Passing table names in an array
Asked Answered
R

3

2

I need to do the same deletion or purge operation (based on several conditions) on a set of tables. For that I am trying to pass the table names in an array to a function. I am not sure if I am doing it right. Or is there a better way?

I am pasting just a sample example this is not the real function I have written but the basic is same as below:

CREATE OR REPLACE FUNCTION test (tablename text[]) RETURNS int AS
$func$
BEGIN

execute 'delete  * from  '||tablename;
  RETURN 1;
END
$func$ LANGUAGE plpgsql;

But when I call the function I get an error:

select test( {'rajeev1'} );

ERROR:  syntax error at or near "{"
LINE 10: select test( {'rajeev1'} );
                      ^
********** Error **********

ERROR: syntax error at or near "{"
SQL state: 42601
Character: 179
Reparable answered 18/5, 2014 at 15:51 Comment(0)
E
1

You used wrong syntax for text array constant in the function call. But even if it was right, your function is not correct.

If your function has text array as argument you should loop over the array to execute query for each element.

CREATE OR REPLACE FUNCTION test (tablenames text[]) RETURNS int AS
$func$
DECLARE
    tablename text;
BEGIN
    FOREACH tablename IN ARRAY tablenames LOOP
        EXECUTE FORMAT('delete  * from  %s', tablename);
    END LOOP;
    RETURN 1;
END
$func$ LANGUAGE plpgsql;

You can then call the function for several tables at once, not only for one.

SELECT test( '{rajeev1, rajeev2}' );

If you do not need this feature, simply change the argument type to text.

CREATE OR REPLACE FUNCTION test (tablename text) RETURNS int AS
$func$
BEGIN
    EXECUTE format('delete  * from  %s', tablename);
    RETURN 1;
END
$func$ LANGUAGE plpgsql;

SELECT test('rajeev1');

I recommend using the format function.


If you want to execute a function (say purge_this_one_table(tablename)) on a group of tables identified by similar names you can use this construction:

create or replace function purge_all_these_tables(mask text)
returns void language plpgsql 
as $$
declare
    tabname text;
begin
    for tabname in
        select relname 
        from pg_class
        where relkind = 'r' and relname like mask
    loop
        execute format(
            'purge_this_one_table(%s)',
            tabname);
    end loop;
end $$;

select purge_all_these_tables('agg_weekly_%');
Enfield answered 18/5, 2014 at 20:3 Comment(0)
R
2

Array syntax

'{rajeev1, rajeev2}' or ARRAY['rajeev1', 'rajeev2']. Read the manual.

TRUNCATE

Since you are deleting all rows from the tables, consider TRUNCATE instead. Per documentation:

Tip: TRUNCATE is a PostgreSQL extension that provides a faster mechanism to remove all rows from a table.

Be sure to study the details. If TRUNCATE works for you, the whole operation becomes very simple, since the command accepts multiple tables:

TRUNCATE rajeev1, rajeev2, rajeev3, .. 

Dynamic DELETE

Else you need dynamic SQL like you already tried. The scary missing detail: you are completely open to SQL injection and catastrophic syntax errors. Use format() with %I (not %s to sanitize identifiers like table names. Or, better yet in this particular case, use an array of regclass as parameter instead:

CREATE OR REPLACE FUNCTION f_del_all(_tbls regclass)
  RETURNS void AS
$func$
DECLARE
   _tbl regclass;
BEGIN
   FOREACH _tbl IN ARRAY _tbls LOOP
      EXECUTE format('DELETE * FROM %s', _tbl);
   END LOOP;
END
$func$ LANGUAGE plpgsql;

Call:

SELECT f_del_all('{rajeev1,rajeev2,rajeev3}');

Explanation here:
Table name as a PostgreSQL function parameter

Reed answered 19/5, 2014 at 3:18 Comment(0)
E
1

You used wrong syntax for text array constant in the function call. But even if it was right, your function is not correct.

If your function has text array as argument you should loop over the array to execute query for each element.

CREATE OR REPLACE FUNCTION test (tablenames text[]) RETURNS int AS
$func$
DECLARE
    tablename text;
BEGIN
    FOREACH tablename IN ARRAY tablenames LOOP
        EXECUTE FORMAT('delete  * from  %s', tablename);
    END LOOP;
    RETURN 1;
END
$func$ LANGUAGE plpgsql;

You can then call the function for several tables at once, not only for one.

SELECT test( '{rajeev1, rajeev2}' );

If you do not need this feature, simply change the argument type to text.

CREATE OR REPLACE FUNCTION test (tablename text) RETURNS int AS
$func$
BEGIN
    EXECUTE format('delete  * from  %s', tablename);
    RETURN 1;
END
$func$ LANGUAGE plpgsql;

SELECT test('rajeev1');

I recommend using the format function.


If you want to execute a function (say purge_this_one_table(tablename)) on a group of tables identified by similar names you can use this construction:

create or replace function purge_all_these_tables(mask text)
returns void language plpgsql 
as $$
declare
    tabname text;
begin
    for tabname in
        select relname 
        from pg_class
        where relkind = 'r' and relname like mask
    loop
        execute format(
            'purge_this_one_table(%s)',
            tabname);
    end loop;
end $$;

select purge_all_these_tables('agg_weekly_%');
Enfield answered 18/5, 2014 at 20:3 Comment(0)
F
0

It should be:

select test('{rajeev1}');
Folkestone answered 18/5, 2014 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.