14

I am trying to run this function in PostrgeSQL:

CREATE OR REPLACE FUNCTION create_partition_and_insert()
RETURNS trigger AS
$BODY$
DECLARE
partition VARCHAR(25);
_date text;
BEGIN
EXECUTE 'SELECT REPLACE(' || quote_literal(NEW.date) || ',''-'',''_'') into _date';
partition := TG_RELNAME || '_' || _date || ‘p’;
IF NOT EXISTS(SELECT relname FROM pg_class WHERE relname=partition) THEN
RAISE NOTICE 'A partition has been created %',partition;
EXECUTE 'CREATE TABLE ' || partition || ' (check (date = ''' || NEW.date || ''')) INHERITS (' || TG_RELNAME || ');';
END IF;
EXECUTE 'INSERT INTO ' || partition || ' SELECT(' || TG_RELNAME || ' ' || quote_literal(NEW) || ').*;';
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql;

But on running this I get this as error:

ERROR:  EXECUTE of SELECT ... INTO is not implemented
HINT:  You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.
CONTEXT:  PL/pgSQL function create_partition_and_insert() line 6 at EXECUTE statement

I have no clue why this is happening and none of the solutions provided online are helping me here.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
shivams
  • 2,597
  • 6
  • 25
  • 47

3 Answers3

28

Instead of

execute 'select 1 into i'    -- error

you should use

execute 'select 1' into i
klin
  • 112,967
  • 15
  • 204
  • 232
4

The error message is the least of the problems in this function. Consider a complete rewrite.

Assuming the column date is actually data type date:

CREATE OR REPLACE FUNCTION create_partition_and_insert()
  RETURNS trigger AS
$func$
DECLARE
   _partition text := quote_ident(TG_RELNAME
                               || to_char(NEW.date,'_YYYY_MM_DD"p"'));
BEGIN
   IF NOT EXISTS(SELECT relname FROM pg_class WHERE relname = _partition) THEN
      EXECUTE format('CREATE TABLE %s (CHECK (date = %L)) INHERITS (%I);'
                   , _partition, NEW.date, TG_RELNAME);

      RAISE NOTICE 'A new partition has been created: %', _partition;
   END IF;

   EXECUTE format('INSERT INTO %s SELECT ($1).*', _partition)
   USING NEW;

   RETURN NULL;
END
$func$  LANGUAGE plpgsql;

Major points

  • You don't need dynamic SQL in your first statement at all (the source or your syntax error). Actually, you don't need the whole statement nor the variable. I radically simplified the name concatenation. Details for to_char() in the manual.

  • Ignoring the typographical quotes in ‘p’ - those are probably just c/p artefacts.

  • Assignments are comparatively expensive in plpgsql. Adapt your programming style and reduce variables and assignments.

  • Don't convert the whole row to its text representation, concatenate and then cast it back. That's needlessly expensive, convoluted and error-prone. Pass the value directly in a USING clause to EXECUTE like demonstrated.

  • Don't raise the notice before it's done. RAISE is not rolled back in the case of an exception leading to potentially misleading log entries.

  • If you have more than one schema in your database (which is common), your code is still unsafe. You need to schema-qualify table names or SET the search_path for the function.

Related answers with more details:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

PostgreSQL official documentation (as on 12 July 2022) clearly calls out that as below:

SELECT INTO is not currently supported within EXECUTE; instead, execute a plain SELECT command and specify INTO as part of the EXECUTE itself.

Something like below worked for me.

v_query_string = 'SELECT count(*) FROM foo';
EXECUTE v_query_string into v_count;
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Jeremy Caney Jul 14 '22 at 02:09