I'm trying to create a stored procedure that will create a select statement.
My procedure looks like below.
CREATE OR REPLACE PROCEDURE record_example()
LANGUAGE plpgsql
AS $$
DECLARE
unload_query text;
BEGIN
unload_query := 'query = ('''select * from my_table''')';
insert into query values (unload_query);
END;
$$;
But its throwing error. Im not able to close the single quote properly.
LINE 1: SELECT 'query = ('''select * from my_table''')'
^
QUERY: SELECT 'query = ('''select * from my_table''')'
CONTEXT: SQL statement in PL/PgSQL function "record_example" near line 5
Expected output:
Unload query = query = (select * from my table)
If you want the result of a query inside a string, you need to use the string concatenation operator. Also, subqueries must be surrounded by parentheses.
unload_query := 'query = (''' || (SELECT * FROM my_table) || ''')';
This will fail if the query returns more than a single row.
If you want the literal string in query, you have too many quotes:
unload_query := 'query = (''select * from my_table'')';
Related
I've got a result from my function
EXECUTE format('SELECT ARRAY (SELECT tvmid from "%s".tvmtable order by tvmid)', operatorName) INTO tvms;
that gives mi array of tvms in format {1,2,3}. I need it to have a String divided with , so I'm trying like
SELECT ARRAY_TO_STRING(tvms, ",") INTO res;
but when I'm executing my function I got error
ERROR: column "," does not exist
If it helps here is my whole function
create or replace function getTVMList(operatorName varchar)
returns varchar as $$
declare
tvms varchar[];
res varchar;
begin
EXECUTE format('SELECT ARRAY (SELECT tvmid from "%s".tvmtable order by tvmid)', operatorName) INTO tvms;
SELECT ARRAY_TO_STRING(tvms, ",") INTO res;
return res;
end;
$$
language plpgsql;
Double quotes reference a column name.
You have to put the comma into single quotes instead of double quotes: ','
SELECT ARRAY_TO_STRING(tvms, ',') INTO res;
I getting error like below: [42883] ERROR: operator does not exist: text || integer[] Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts. I cannot fix this problem and tried so many times.
My Code:
DECLARE
arr_operators integer[1,2];
BEGIN
query1 := 'SELECT * FROM dist.' || _rec1.table_name || ' WHERE operator_id = ANY (''' || arr_operators || ''');';
FOR _rec IN EXECUTE query1 LOOP
END LOOP;
I think problem is happening when I am makin query string.But when I use this statement in query directly lik below is working well:
FOR _rec1 IN (SELECT * FROM dist.sirdarya WHERE id = any (arr_operators)) LOOP
INSERT INTO dist.justt(column1,column2) VALUES (_rec1.id,_rec1.msisdn);
END LOOP;
Any help is appreciated.
I suggest unnesting an array instead of concatenating string which could lead to SQL Injection:
SELECT *
FROM table_name
WHERE operator_id IN (SELECT * FROM unnest(arr_operators));
db<>fiddle demo
This part is particulary dangerous:
query1 := 'SELECT * FROM dist.' || _rec1.table_name
What if table name is let's say: ;DROP DATABASE ...;--?
It could be rewritten as:
query1 := FORMAT('SELECT * FROM dist.%I ...', _rec1.table_name);
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')
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.
i have a storerd procedure like below,
CREATE FUNCTION select_transactions3(text, text, int)
RETURNS SETOF transactions AS
$body$
DECLARE
rec transactions%ROWTYPE;
BEGIN
FOR rec IN (SELECT invoice_no, trans_date FROM transactions WHERE $1 = $2 limit $3 )
LOOP
RETURN NEXT rec;
END LOOP;
END;
$body$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
when i execute query like this :
select * from select_transactions3("invoice_no", '1103300105472',10);
or
select * from select_transactions3(invoice_no, '1103300105472',10);
it getting error like this :
ERROR: column "invoice_no" does not exist
but when i try execute with one colon like this :
select * from select_transactions3('invoice_no', '1103300105472',10);
the result is no row.
how i can get the data like this :
invoice_no | trans_date
---------------+-------------------------
1103300105472 | 2011-03-30 12:25:35.694
thanks .
UPDATE : If we want a certain column of table that we want to show
CREATE FUNCTION select_to_transactions14(_col character varying, _val character varying, _limit int)
RETURNS SETOF RECORD AS
$$
DECLARE
rec record;
BEGIN
FOR rec IN EXECUTE 'SELECT invoice_no, amount FROM transactions
WHERE ' || _col || ' = $1 LIMIT $2' USING _val, _limit LOOP
RETURN NEXT rec;
END LOOP;
END;
$$ LANGUAGE plpgsql;
to get the result :
SELECT * FROM select_to_transactions14( 'invoice_no', '1103300105472',1)
as ("invoice_no" varchar(125), "amount" numeric(12,2));
Your function could look like this:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS SETOF transactions AS
$BODY$
BEGIN
RETURN QUERY EXECUTE '
SELECT *
FROM transactions
WHERE ' || quote_ident(_col) || ' = $1
LIMIT $2'
USING _val, _limit;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
IN PostgreSQL 9.1 or later that's simpler with format()
...
RETURN QUERY EXECUTE format('
SELECT *
FROM transactions
WHERE %I = $1
LIMIT $2', _col)
USING _val, _limit;
...
%I escapes identifiers like quote_ident().
Major points:
You were bumping into the limitation of dynamic SQL that you cannot use parameters for identifiers. You have to build the query string with the column name and then execute it.
You can do that with values though. I demonstrate the use of the USING clause for EXECUTE. Also note the use of quote_ident(): prevents SQL injection and certain syntax errors.
I also largely simplified your function. [RETURN QUERY EXECUTE][3] makes your code shorter and faster. No need to loop if all you do is return the row.
I use named IN parameters, so you don't get confused with the $-notation in the query string. $1 and $2 inside the query string refer to the values provided in the USING clause, not to the input parameters.
I change to SELECT * as you have to return the whole row to match the declared return type anyway.
Last but not least: Be sure to consider what the manual has to say about functions declared SECURITY DEFINER.
RETURN TYPE
If you don't want to return the whole row, one convenient possibility is:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS TABLE (invoice_no varchar(125), amount numeric(12,2) AS ...
Then you don't have to provide a column definition list with every call and can simplify to:
SELECT * FROM select_to_transactions3('invoice_no', '1103300105472', 1);
You can query all databases from the server and sort them according to your own database.
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tableName';