Thread: Out of memory error

Out of memory error

From
aditya desai
Date:
Hi,
In a trigger function I am creating a temp table . When an update on a table is executed for say 10k rows. I get the below error.

ERROR: out of shared memory
HINT:You might need to increase max_locks_per_transaction
CONTEXT: SQL Statement "created temp table changedinfo(colName varchar(100), oldValue varchar(4000), newValue varchar(4000)

Current value of  max_locks_per_transaction is 64. Do I have to increase this?

Regards,
Aditya.

Re: Out of memory error

From
Tom Lane
Date:
aditya desai <admad123@gmail.com> writes:
> In a trigger function I am creating a temp table . When an update on a
> table is executed for say 10k rows. I get the below error.

> ERROR: out of shared memory
> HINT:You might need to increase max_locks_per_transaction
> CONTEXT: SQL Statement "created temp table changedinfo(colName
> varchar(100), oldValue varchar(4000), newValue varchar(4000)

[ raised eyebrow ... ]  If you are concerned about performance,
I'd start by not creating a temp table per row of the outer update.
That's costing probably 100x to 1000x as much as the row update itself.

            regards, tom lane



Re: Out of memory error

From
aditya desai
Date:
Thanks Tom. However I could not find any solution to achieve the given requirement. I have to take all values in the temp table and assign it to an array variable to pass it to the audit procedure as shown below. Can you please advise ? 

CREATE OR REPLACE FUNCTION call_insert_info(
    
) RETURNS void AS $$
    DECLARE
        v_message r_log_message[];
OLDVALUE1 varchar(4000);
    BEGIN
            drop table if exists changedinfo
    create temp table changedinfo(colName varchar(100), oldValue varchar(4000), newValue varchar(4000));
            insert into changed infot select 'empName', OLD.empName, NEW.empName from employee;
            insert into changed infot select 'location', OLD.location, NEW.location from employee;
            
        
v_message:=   array(select '(' || columname || ',' || oldvalue || ',' || newvalue ||')' from changedinfo);
        perform insert_info(v_message);
        raise notice '%',v_message;
    END;
$$ LANGUAGE plpgsql;

Regards,
AD.


On Wed, Nov 24, 2021 at 11:22 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
aditya desai <admad123@gmail.com> writes:
> In a trigger function I am creating a temp table . When an update on a
> table is executed for say 10k rows. I get the below error.

> ERROR: out of shared memory
> HINT:You might need to increase max_locks_per_transaction
> CONTEXT: SQL Statement "created temp table changedinfo(colName
> varchar(100), oldValue varchar(4000), newValue varchar(4000)

[ raised eyebrow ... ]  If you are concerned about performance,
I'd start by not creating a temp table per row of the outer update.
That's costing probably 100x to 1000x as much as the row update itself.

                        regards, tom lane

Re: Out of memory error

From
Thomas Kellerer
Date:
aditya desai schrieb am 24.11.2021 um 07:25:
> Thanks Tom. However I could not find any solution to achieve the given requirement. I have to take all values in the
temptable and assign it to an array variable to pass it to the audit procedure as shown below. Can you please advise ?  
>
> CREATE OR REPLACE FUNCTION call_insert_info(
>     
> ) RETURNS void AS $$
>     DECLARE
>         v_message r_log_message[];
> OLDVALUE1 varchar(4000);
>     BEGIN
>             drop table if exists changedinfo
>     create temp table changedinfo(colName varchar(100), oldValue varchar(4000), newValue varchar(4000));
>             insert into changed infot select 'empName', OLD.empName, NEW.empName from employee;
>             insert into changed infot select 'location', OLD.location, NEW.location from employee;
>             
>         
> v_message:=   array(select '(' || columname || ',' || oldvalue || ',' || newvalue ||')' from changedinfo);
>         perform insert_info(v_message);
>         raise notice '%',v_message;
>     END;
> $$ LANGUAGE plpgsql;


You don't need a temp table for that. You can create the array directly from the new and old records:

    v_message := array[concat_ws(',', 'empName', old.empname, new.empname), concat_ws(',', 'location', old.location,
new.location)];

Although nowadays I would probably pass such an "structure" as JSON though, not as a comma separated list.




Re: Out of memory error

From
aditya desai
Date:
Ok. Let me try this. Thanks!!

On Wed, Nov 24, 2021 at 12:01 PM Thomas Kellerer <shammat@gmx.net> wrote:
aditya desai schrieb am 24.11.2021 um 07:25:
> Thanks Tom. However I could not find any solution to achieve the given requirement. I have to take all values in the temp table and assign it to an array variable to pass it to the audit procedure as shown below. Can you please advise ? 
>
> CREATE OR REPLACE FUNCTION call_insert_info(
>     
> ) RETURNS void AS $$
>     DECLARE
>         v_message r_log_message[];
> OLDVALUE1 varchar(4000);
>     BEGIN
>             drop table if exists changedinfo
>     create temp table changedinfo(colName varchar(100), oldValue varchar(4000), newValue varchar(4000));
>             insert into changed infot select 'empName', OLD.empName, NEW.empName from employee;
>             insert into changed infot select 'location', OLD.location, NEW.location from employee;
>             
>         
> v_message:=   array(select '(' || columname || ',' || oldvalue || ',' || newvalue ||')' from changedinfo);
>         perform insert_info(v_message);
>         raise notice '%',v_message;
>     END;
> $$ LANGUAGE plpgsql;


You don't need a temp table for that. You can create the array directly from the new and old records:

    v_message := array[concat_ws(',', 'empName', old.empname, new.empname), concat_ws(',', 'location', old.location, new.location)];

Although nowadays I would probably pass such an "structure" as JSON though, not as a comma separated list.



Re: Out of memory error

From
Michael Lewis
Date:
It seems like that function has some syntax errors, and also doesn't do what you want since I presume the "from employee" bit would mean you get many rows inserted into that temp table for all the existing data and not the one row you are operating on at the moment the trigger fires.

It is worth noting also that if bulk operations are at all common for this table then writing this as an after statement trigger will likely be helpful for performance.

For full context, we'd need to see how the function insert_info is defined.

Re: Out of memory error

From
aditya desai
Date:
H Michael,
Please see insert_info function below. Also r_log_message is composite data type and it's definition is also given below.

CREATE OR REPLACE FUNCTION insert_info(
    info_array  r_log_message[]
) RETURNS varchar AS $$
    DECLARE
        info_element  r_log_message;
    BEGIN
        FOREACH info_element IN ARRAY info_array
        LOOP
            INSERT INTO testaditya(
                columname,
                oldvalue,
                newvalue
            ) VALUES(
                info_element.column_name,
                info_element.oldvalue,
                info_element.newvalue
            );
        END LOOP;
        RETURN 'OK';
    END;
$$ LANGUAGE plpgsql;


postgres=# \d r_log_message;
                 Composite type "public.r_log_message"
   Column    |          Type           | Collation | Nullable | Default
-------------+-------------------------+-----------+----------+---------
 column_name | character varying(30)   |           |          |
 oldvalue    | character varying(4000) |           |          |
 newvalue    | character varying(4000) |           |          |

Regards,
Aditya.



On Wed, Nov 24, 2021 at 12:16 PM Michael Lewis <mlewis@entrata.com> wrote:
It seems like that function has some syntax errors, and also doesn't do what you want since I presume the "from employee" bit would mean you get many rows inserted into that temp table for all the existing data and not the one row you are operating on at the moment the trigger fires.

It is worth noting also that if bulk operations are at all common for this table then writing this as an after statement trigger will likely be helpful for performance.

For full context, we'd need to see how the function insert_info is defined.

Re: Out of memory error

From
aditya desai
Date:
Hi Thomas,
v_message is of composite data type r_log_message and it's definition is as shown below.

postgres=# \d r_log_message;
                 Composite type "public.r_log_message"
   Column    |          Type           | Collation | Nullable | Default
-------------+-------------------------+-----------+----------+---------
 column_name | character varying(30)   |           |          |
 oldvalue    | character varying(4000) |           |          |
 newvalue    | character varying(4000) |           |          |

Regards,
Aditya.

On Wed, Nov 24, 2021 at 12:01 PM Thomas Kellerer <shammat@gmx.net> wrote:
aditya desai schrieb am 24.11.2021 um 07:25:
> Thanks Tom. However I could not find any solution to achieve the given requirement. I have to take all values in the temp table and assign it to an array variable to pass it to the audit procedure as shown below. Can you please advise ? 
>
> CREATE OR REPLACE FUNCTION call_insert_info(
>     
> ) RETURNS void AS $$
>     DECLARE
>         v_message r_log_message[];
> OLDVALUE1 varchar(4000);
>     BEGIN
>             drop table if exists changedinfo
>     create temp table changedinfo(colName varchar(100), oldValue varchar(4000), newValue varchar(4000));
>             insert into changed infot select 'empName', OLD.empName, NEW.empName from employee;
>             insert into changed infot select 'location', OLD.location, NEW.location from employee;
>             
>         
> v_message:=   array(select '(' || columname || ',' || oldvalue || ',' || newvalue ||')' from changedinfo);
>         perform insert_info(v_message);
>         raise notice '%',v_message;
>     END;
> $$ LANGUAGE plpgsql;


You don't need a temp table for that. You can create the array directly from the new and old records:

    v_message := array[concat_ws(',', 'empName', old.empname, new.empname), concat_ws(',', 'location', old.location, new.location)];

Although nowadays I would probably pass such an "structure" as JSON though, not as a comma separated list.



Re: Out of memory error

From
Thomas Kellerer
Date:
aditya desai schrieb am 24.11.2021 um 08:35:
> Hi Thomas,
> v_message is of composite data type r_log_message and it's definition is as shown below.
>
> postgres=# \d r_log_message;
>                  Composite type "public.r_log_message"
>    Column    |          Type           | Collation | Nullable | Default
> -------------+-------------------------+-----------+----------+---------
>  column_name | character varying(30)   |           |          |
>  oldvalue    | character varying(4000) |           |          |
>  newvalue    | character varying(4000) |           |          |
>
> Regards,
> Aditya.

Sorry, didn't see that.

Then you need to create records of that type in the array:

   v_message := array[('empName', old.empname, new.empname)::r_log_message, ('location', old.location,
new.location)::r_log_message];

or an array of that type:

   v_message := array[('empName', old.empname, new.empname), ('location', old.location,
new.location)]::r_log_message[];


Btw: why don't you use `text` instead of varchar(4000).



Re: Out of memory error

From
Thomas Kellerer
Date:
aditya desai schrieb am 24.11.2021 um 08:31:
> H Michael,
> Please see insert_info function below. Also r_log_message is composite data type and it's definition is also given
below.
>
> CREATE OR REPLACE FUNCTION insert_info(
>     info_array  r_log_message[]
> ) RETURNS varchar AS $$
>     DECLARE
>         info_element  r_log_message;
>     BEGIN
>         FOREACH info_element IN ARRAY info_array
>         LOOP
>             INSERT INTO testaditya(
>                 columname,
>                 oldvalue,
>                 newvalue
>             ) VALUES(
>                 info_element.column_name,
>                 info_element.oldvalue,
>                 info_element.newvalue
>             );
>         END LOOP;
>         RETURN 'OK';
>     END;
> $$ LANGUAGE plpgsql;

You don't need a loop for that. This can be done more efficiently using unnest()


    INSERT INTO testaditya(columname,oldvalue,newvalue)
    select u.*
    from unnest(info_array) as u;






Re: Out of memory error

From
aditya desai
Date:
Thanks Thomas!  Sorry to say this but ,this was migrated from Oracle to PG  :) and the app team just wants to keep the data type as it is  :(

On Wed, Nov 24, 2021 at 5:40 PM Thomas Kellerer <shammat@gmx.net> wrote:
aditya desai schrieb am 24.11.2021 um 08:35:
> Hi Thomas,
> v_message is of composite data type r_log_message and it's definition is as shown below.
>
> postgres=# \d r_log_message;
>                  Composite type "public.r_log_message"
>    Column    |          Type           | Collation | Nullable | Default
> -------------+-------------------------+-----------+----------+---------
>  column_name | character varying(30)   |           |          |
>  oldvalue    | character varying(4000) |           |          |
>  newvalue    | character varying(4000) |           |          |
>
> Regards,
> Aditya.

Sorry, didn't see that.

Then you need to create records of that type in the array:

   v_message := array[('empName', old.empname, new.empname)::r_log_message, ('location', old.location, new.location)::r_log_message];

or an array of that type:

   v_message := array[('empName', old.empname, new.empname), ('location', old.location, new.location)]::r_log_message[];


Btw: why don't you use `text` instead of varchar(4000).