Getting Error "query has no destination for result data" - postgresql

I trying to select query based on condition using IF ElSE in postgres. Below is my query.
DO
$do$
DECLARE res varchar(50) := 'a';
BEGIN
IF (res = 'a') THEN
SELECT "Name" FROM "TestTable";
ELSE
SELECT "ID" FROM "TestTable";
END IF;
END
$do$
but I am getting following error
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 5 at SQL statement
What I am doing wrong here??

DO purpose is to execute anonymous code block and it doesn't return anything (it returns void, to be specific).
You can execute your SELECT statement afterwards (outside of DO block), or perform an INSERT to temporary table which you need to create beforehand (and this can be done within the block).

Related

Use variable from a previous DO clause

I have a .sql script like this:
DO $$
DECLARE
prev_count := (SELECT count(*) FROM ...);
END$$;
UPDATE [...]
DO $$
DECLARE
cur_count := (SELECT count(*) FROM ...);
BEGIN
ASSERT cur_count = prev_count, 'Mismatch';
END$$;
In which I get some value, modify the database, and expect a new value to match an old value. However, I get errors like this:
psql:migration.sql:163: ERROR: column "prev_count" does not exist
LINE 1: SELECT cur_count = prev_count
^
QUERY: SELECT cur_count = prev_count
CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT
I can't tell if this is a scoping issue because of the anonymous block, and why it's attempting to treat my variables like columns. Any ideas?
According to the manual DO executes an anonymous code block that:
... is treated as though it were the body of a function with no parameters, returning void. It is parsed and executed a single time...
So it is a function that returns VOID. In that sense prev_count only exists in the first DO.
To avoid this, you could create a TEMP table and insert the prev_count in the first DO so you can use it anywhere in the transaction.

Firebird run execute statement inside trigger

I wrote a procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE') that generates part of code that i want to execute (some long if statement)
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
and I want to call it in trigger, then add some code and run it all together.
The code is:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
declare variable besedilo_primerjave varchar(5000);
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
execute procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE')
returning_values :besedilo_primerjave;
execute statement besedilo_primerjave || ' THEN BEGIN INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
''MERILA_STRANKE'',
''ID_MERILA_STRANKE'',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END';
end
END^
SET TERM ; ^
Now when I run the update and trigger triggers I get this error:
SQL Message : -104 Invalid token
Engine Code : 335544569 Engine Message : Dynamic SQL Error SQL
error code = -104 Token unknown - line 1, column 1 IF
On the other hand if I write it like this:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
THEN BEGIN
INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
'MERILA_STRANKE',
'ID_MERILA_STRANKE',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END
end
END^
SET TERM ; ^
It works as it should. I do not understand why it doesn't run if is more or less the same code.
As I also mentioned in your previous question, execute statement cannot be used to execute snippets of PSQL (procedural SQL) like that, it can only execute normal DSQL (dynamic SQL). And as it doesn't understand PSQL, you get the "token unknown - if" error, because if is not valid in DSQL.
execute statement is equivalent to executing SQL yourself from a query tool or application (it uses the same API), you can't use if there either.
There is a loophole by using an execute block statement, but that still would not allow you to gain access to the NEW (or OLD) trigger context variables unless explicitly passed as parameters, which would negate most of the usefulness of dynamically generated code in this context.
The only real solution is to write the trigger and not do it dynamically, maybe using a code generator (I'm not sure if any exist, otherwise you need to write that yourself).

how to run multiple updated statements in postgresql

I tried to run this script with the parameters in place but it keeps throwing syntax errors.Is there anything wrong with the syntax.Also what is the correct way to call this function.I require an output that tells me the update statement was executed successfully. I tried "select function_name(schema_name.TABLE_NAME);".Let me add that I am a beginner and am open to any kind of feedback. will also provide more details if necessary.
CREATE OR REPLACE FUNCTION function_name (TABLE_NAME IN character varying)
RETURNS text AS $SQLQuery$
DECLARE SQLQuery text;
BEGIN
SQLQuery =
' UPDATE '|| TABLE_NAME || ' SET column1=''0''
WHERE column1 is null;' ||
' UPDATE '|| TABLE_NAME || ' SET column2='value'
WHERE column2=''different value'';' ||
--multiple update statements later
Execute SQLQuery;
Return SQLQuery;
END;
$SQLQuery$
LANGUAGE plpgsql;
Update:
this is the error i am getting when i call the test function
ERROR: missing FROM-clause entry for table "schema_name"
LINE 2: select test_function(schema_name.TABLE_NAME);
^
********** Error **********
ERROR: missing FROM-clause entry for table "schema_name"
SQL state: 42P01
it is reading the function as a table?
I have also received syntax errors saying
EXECUTE column does not exist or that the function does not exist
even though i just declared it.
To use single quotes inside a siting constant, you must escape them by doubling them.
Instead of
' SET column1='0''
you'll have to write
' SET column1=''0'''
smth like:
CREATE OR REPLACE FUNCTION function_name (schema_name text,TABLE_NAME IN character varying)
RETURNS text AS $SQLQuery$
DECLARE
c int;
rtn text :='';
BEGIN
execute format(' UPDATE %I.%I SET column1=''0'' WHERE column1 is null;',schema_name,TABLE_NAME);
get diagnostics c = row_count;
raise info '%', 'affected: '||c;
rtn = rtn + 'affected: '||c||chr(10);
--repeat above construct for multiple update statement
return rtn;
END;
$SQLQuery$
LANGUAGE plpgsql;
and advises. I'm novice like you, but I learned to follow several rules, that help me:
with dynamic sql use format to avoid sql injection
don't overcomplicate things (eg the functionality you are looking for is inside UPDATE statement already - check the output. If you want to check the resulting row use, UPDATE ... RETURNING * construct.
practice is good, but reading concepts is precious.
In your POST select function_name(schema_name.TABLE_NAME); would not work, because you use schema_name.TABLE_NAME without quotes, but even if you put them, your function is vulnerable - what will happen if you run select function_name(';drop sometable;--');?..
You are trying to pass SQL Identifier, but your function takes string as parameter instead. You should change it to something like:
select test_function('schema_name.TABLE_NAME');
You can try that function below as base for whatever you are trying to do.
/* You need to split table and schema name
or you might get errors when using names that aren't lower case.
This: 'public.TEST1' would be translated to: "public.TEST1"
that is different table from public.test1
*/
CREATE OR REPLACE FUNCTION multi_update_stuff(schema_name varchar, table_name varchar)
/* We will return set of multiple columns. One possible method is to return table.
First column shows executed query, second if it returned no errors (true)
*/
RETURNS TABLE(SQLQuery text, result boolean)
AS $body$
DECLARE
/* Declare arroy of queries that we will iterate and execute later.
We use format() to build query from template and fill it with values.
%1$I can be described as "put first value here and treat it as object identifier"
%3$L can be described as "put third value here and treat it as SQL literal"
*/
SQLQueries text[] := array[
/* First query */
format('UPDATE %1$I.%2$I SET column1 = %3$L WHERE column1 is null;',
schema_name, table_name, '0'),
/* Second query */
format('UPDATE %1$I.%2$I SET column2 = %3$L WHERE column2 = %4$L;',
schema_name, table_name, 'value', 'different value'),
/* Third query, to see error free result */
'SELECT 1'];
BEGIN
/* Iterate our array */
FOREACH SQLQuery IN ARRAY SQLQueries
LOOP
/* Start transaction block */
BEGIN
EXECUTE SQLQuery;
result := true;
/* Catch error if any */
EXCEPTION
WHEN others THEN
result := false;
END;
/* Return row with whatever is assigned to variables listed in RETURNS.
In this case SQLQuery was already assigned by FOREACH.
*/
RETURN NEXT;
END LOOP;
END;
$body$
LANGUAGE plpgsql;
SELECT * FROM multi_update_stuff('schema_name', 'TABLE_NAME')

Executing string queries from function

I have a table called rezultz with 1 row only containing valid SQL queries like: CREATE TRIGGER ... I'm trying to execute them with this function called get_all_rezultz() but it doesn't seem to work,any ideas why?
CREATE OR REPLACE FUNCTION get_all_rezultz() RETURNS SETOF rezultz AS
$BODY$
DECLARE
r rezultz%rowtype;
BEGIN
FOR r IN
SELECT * FROM rezultz
LOOP
-- can do some processing here
RETURN QUERY EXECUTE r; -- return current row of SELECT
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
SELECT * FROM get_all_rezultz();
Here is the error I get :
NOTICE: identifier "CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON userman FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); " will be truncated to "CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE O"
CONTEXT: SQL statement "("CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON userman FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ")"
PL/pgSQL function get_all_rezultz() line 10 at RETURN QUERY
ERROR: syntax error at or near ""CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON userman FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ""
LINE 1: ("CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPD...
^
QUERY: ("CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON userman FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ")
CONTEXT: PL/pgSQL function get_all_rezultz() line 10 at RETURN QUERY
********** Error **********
ERROR: syntax error at or near ""CREATE TRIGGER userman_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON userman FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func(); ""
SQL state: 42601
Context: PL/pgSQL function get_all_rezultz() line 10 at RETURN QUERY
The variable r is a record containing multiple columns. It's not a scalar value (e.g. a string).
So you need to use the column name in the execute statement. Assuming the column in the table is called sql_statement you need to use:
RETURN QUERY EXECUTE r.sql_statement;
However, this will still not work because the select statement stored in that table will most definitely not return a results that is SETOF rezultz it returns a SETOF mytable if the query is select * from mytable.
You would need to specify RETURNS SETOF record but then you need to specify the column names and structure of the result when calling the function.
And even then this won't work because a function still only return a single result, not multiple results from multiple queries - which will happen if rezultzcontains more then one row.
If your SQL statements aren't actually SELECT statements as you have claimed, you will need to use EXECUTE you can't use RETURN QUERY for a CREATE TRIGGER statement.

pgsql sql functions sequential execution

If I have these two Postgres function definitions saved in two seperate .sql files:
CREATE OR REPLACE FUNCTION column_exists(tablename text, colname text) RETURNS boolean AS
$BODY$
DECLARE
q text;
field_name text;
onerow record;
BEGIN
q = 'SELECT column_name FROM information_schema.columns WHERE table_name='''||tablename||''' AND table_schema =''public''';
FOR onerow IN EXECUTE q
LOOP
field_name := onerow.column_name;
IF ((field_name = colname)) then
RETURN true;
END IF;
END LOOP;
RETURN false;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION correct_col_names() RETURNS VOID AS
$BODY$
DECLARE
q boolean;
BEGIN
-- rename name column to Name
select column_exists('National_Parks', 'name') as q;
IF q = TRUE THEN
alter table "National_Parks"
rename column name to "Name";
END IF;
-- remance descriptio column to description
select column_exists('National_Parks', 'descriptio') as q;
IF q = TRUE THEN
alter table "Natioanl_Parks"
rename column descriptio to "Description";
END IF;
END
$BODY$
LANGUAGE plpgsql
What is the syntax I need to use to call the sequentially, say in another script? I tried
select correct_col_names()
and this returns the following error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "correct_col_names" line 7 at SQL statement
********** Error **********
ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Context: PL/pgSQL function "correct_col_names" line 7 at SQL statement
TIA.
The problem is that you have SELECT statements that aren't doing anything with the data. Your
select column_exists('National_Parks', 'name') as q;
should be
select column_exists('National_Parks', 'name') INTO q;
The as simply aliases the result as "q" for that query, it doesn't actually put it into the q variable.
Your syntax for calling the functions (select correct_col_names()) is correct for SQL. Once you fix the two errors in that function, it should work.
However, if you were to try select correct_col_names() inside another PL/PGSQL function, you would get the same error, because the select statement isn't actually doing anything with the results. perform correct_col_names() would run without error, because PERFORM is PL/PGSQL syntax for calling something when you don't want to save the result.