PostgreSQL column "exists" does not exist - postgresql

I have this query for a function in postgresql
EXECUTE FORMAT('SELECT * FROM riesgo.%I WHERE codigo = %L', TG_TABLE_NAME, NEW.codigo) INTO _colvar;
IF EXISTS _colvar THEN
UPDATE riesgo.ro_agresion SET
(nombre,creado_por,valor,activo) =
(NEW.nombre,NEW.creado_por,NEW.valor,NEW.activo)
WHERE codigo = NEW.codigo;
ELSE
RETURN NEW;
END IF;
RETURN NULL;
and I'm getting this
ERROR: column "exists" does not exist
LINE 1: SELECT EXISTS _colvar

The explanation for the confusing error can be found in the manual for pl/pgsql Expressions, which explains:
When you write a PL/pgSQL statement IF expression THEN ... PL/pgSQL will evaluate the expression by feeding a query like SELECT expression to the main SQL engine
So in your case, the expression is being translated to SELECT EXISTS _colvar, which looks to the query parser like a column named "EXISTS" being given an alias _colvar".
To fix it, you need something that would be valid in a select list. For instance:
IF _colvar IS NOT NULL ...

Related

Select query by declare variable on postgresql

I am new working postgresql and pgadmin4.I write a very simple query.
I have a table called PgFinalLocationsTable on public schema.In my table there are a few filed.One of these filed is UserName .I want to declare a variable and finally do select on my table according this variable like below:
DO $$
DECLARE myvar text default 'sa';
BEGIN
select * from public."PgFinalLocationsTable" where "UserName" = myvar;
END $$;
But why i got these message:
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 4 at SQL statement
SQL state: 42601
It is a simple query!!!
After googling and see post on stack i have changed my query like this:
CREATE OR REPLACE FUNCTION fun(text myvar) RETURNS text AS $$
--DECLARE myvar text;
BEGIN
select * from public."PgFinalLocationsTable" where "UserName" = myvar;
END;
$$ language plpgsql;
select fun('sa');
I want to return all my fields and i do not want to use plpgsql.I want to use PostgreSQL. In any case i got this error:
ERROR: type myvar does not exist
SQL state: 42704
What is the problem on my first query and second query?Should i have make a function for select query when i want to pass a variable?
I do all stuff because i want to create this sql query:
"IF (NOT EXISTS(SELECT 1 FROM [dbo].[{0}] WHERE [UserId] = #UserId And [DeviceId] = #DeviceId)) " +
"BEGIN " +
"INSERT INTO [dbo].[{0}]([Id], [Location], [Timestamp], [UserId], [DeviceId], [AllowDomains], [Topic], [UserName], [FirstName], [LastName], [JobLocationName], [LocationId], [AppVersion], [AppName]) " +
"VALUES(#Id, GEOGRAPHY::Point(#X, #Y, 4326), #Timestamp, #UserId, #DeviceId, #AllowDomains, #Topic, #UserName, #FirstName, #LastName, #JobLocationName, #LocationId, #AppVersion, #AppName) " +
"END "
You don't understand to DO command well. DO command is anonymous function without declaration, and because it has not declared an output, then is not possible any other result than debug stream.
so your first example has not sense in PostgreSQL. Result of unbind queries in MSSQL is returned as result of MS SQL procedure. Nothing similar is possible in PostgreSQL. PostgreSQL knows only functions, that can returns scalar value, composite value or relation (only one). When you are coming from MS SQL, the best what you can, try to forgot almost all knowleadge from MS SQL.
ERROR: type myvar does not exist
SQL state: 42704
This bug is clean - you switch variable name and type name - really type myvar doesn't exist.
Some function that returns table can looks like:
CREATE OR REPLACE FUNCTION fx1(myvar text)
RETURNS SETOF public."PgFinalLocationsTable" AS $$
BEGIN
RETURN QUERY SELECT * FROM public."PgFinalLocationsTable" WHERE "UserName" = myvar;
END;
$$ LANGUAGE plpgsql;
or you can use a SQL language only
CREATE OR REPLACE FUNCTION fx1(myvar text)
RETURNS SETOF public."PgFinalLocationsTable" AS $$
SELECT * FROM public."PgFinalLocationsTable" WHERE "UserName" = $1;
$$ LANGUAGE sql;
Because PostgreSQL doesn't support unbind queries, then doesn't allow it. You should to use RETURN QUERY command - in PLpgSQL language.
Because programming with stored procedures is really different between PostgreSQL and MSSQL (MSSQL is not similar to any other), please, try to read documentation - it is not bad https://www.postgresql.org/docs/current/static/plpgsql.html
Your function can looks in Postgres like (I don't know used types)
CREATE OR REPLACE FUNCTION fx("_UserId" int,
"_DeviceId" int,
"_X" int,
"_Y" int,
...
BEGIN
IF NOT EXISTS(SELECT * FROM /* I don't know what [{0}] means */
WHERE "UserId" = "_UserId" AND "DeviceId" = "_DeviceId")
THEN
INSERT INTO ..
END IF;
END;
$$ LANGUAGE plpgsql;
Probably your fragment can be solved without procedural extension by INSERT INTO ON CONFLICT DO NOTHING command https://www.postgresql.org/docs/current/static/sql-insert.html - what is better.
Note - using case sensitive identifiers is short way to hell.

How to syntax dynamic insert in plpgsql

I was wondering if there is a way to syntax an INSERT using EXECUTE in plpgsql.
Suppose I want to have a dynamic insert with mutliple IFs. I try to create something like the following
CREATE FUNCTION __a_inj(creator text) RETURNS integer
AS $query$
DECLARE ii ALIAS FOR $1;
BEGIN
EXECUTE'
INSERT INTO deleteme(name) VALUES($1) RETURNING id'
USING creator;
return ii;
END;
$query$
LANGUAGE plpgsql;
I call it with select __a_inj('drop table deleteme;--'); and get
ERROR: invalid input syntax for integer: "drop table deleteme;--"
CONTEXT: PL/pgSQL function __a_inj(text) while casting return value
to function's return type SQL state: 22P02
If I replace the INSERT line with INSERT INTO deleteme(name) VALUES($1) RETURNING id into ii' I get
ERROR: syntax error at or near "into" LINE 2: ... INSERT INTO
deleteme(name) VALUES($1) RETURNING id into ii
If the function is
CREATE FUNCTION __a_inj(creator text) RETURNS integer
AS $query$
DECLARE ii ALIAS FOR $1;
BEGIN
RETURN EXECUTE'
INSERT INTO deleteme(name) VALUES($1) RETURNING id into ii'
USING creator;
--return ii;
END;
$query$
LANGUAGE plpgsql;
I get
ERROR: syntax error at or near "USING" LINE 8: USING creator;
How can I have a dynamic INSERT with EXECUTE
OR
I guess there is no need to syntax one. Just use ifs to create the INTO and values part and then use a simple INSERT that is also strong against SQL injection attacks ?
Both statements are dynamic SQL (EXECUTE), but the second one has a syntax error (there is no INTO clause in SQL). You get the error in the first statement because the string you insert cannot be converted to an integer.
You are safe from SQL injection if you use the USING clause, because that uses a statement with parameters. SQL injection can only happen if you concatenate a user input string with an SQL statement.

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')

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.

PostgreSQL - Writing dynamic sql in stored procedure that returns a result set

How can I write a stored procedure that contains a dynamically built SQL statement that returns a result set? Here is my sample code:
CREATE OR REPLACE FUNCTION reporting.report_get_countries_new (
starts_with varchar,
ends_with varchar
)
RETURNS TABLE (
country_id integer,
country_name varchar
) AS
$body$
DECLARE
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
sql VARCHAR;
BEGIN
sql = 'SELECT * FROM lookups.countries WHERE lookups.countries.country_name >= ' || starts_with ;
IF ends_with IS NOT NULL THEN
sql = sql || ' AND lookups.countries.country_name <= ' || ends_with ;
END IF;
RETURN QUERY EXECUTE sql;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;
This code returns an error:
ERROR: syntax error at or near "RETURN"
LINE 1: RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE o...
^
QUERY: RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE omnipay_lookups.countries.country_name >= r
CONTEXT: PL/pgSQL function "report_get_countries_new" line 14 at EXECUTE statement
I have tried other ways instead of this:
RETURN QUERY EXECUTE sql;
Way 1:
RETURN EXECUTE sql;
Way 2:
sql = 'RETURN QUERY SELECT * FROM....
/*later*/
EXECUTE sql;
In all cases without success.
Ultimately I want to write a stored procedure that contains a dynamic sql statement and that returns the result set from the dynamic sql statement.
There is room for improvements:
CREATE OR REPLACE FUNCTION report_get_countries_new (starts_with text
, ends_with text = NULL)
RETURNS SETOF lookups.countries AS
$func$
DECLARE
sql text := 'SELECT * FROM lookups.countries WHERE country_name >= $1';
BEGIN
IF ends_with IS NOT NULL THEN
sql := sql || ' AND country_name <= $2';
END IF;
RETURN QUERY EXECUTE sql
USING starts_with, ends_with;
END
$func$ LANGUAGE plpgsql;
-- the rest is default settings
Major points
PostgreSQL 8.4 introduced the USING clause for EXECUTE, which is useful for several reasons. Recap in the manual:
The command string can use parameter values, which are referenced in
the command as $1, $2, etc. These symbols refer to values supplied in
the USING clause. This method is often preferable to inserting data
values into the command string as text: it avoids run-time overhead of
converting the values to text and back, and it is much less prone to
SQL-injection attacks since there is no need for quoting or escaping.
IOW, it is safer and faster than building a query string with text representation of parameters, even when sanitized with quote_literal().
Note that $1, $2 in the query string refer to the supplied values in the USING clause, not to the function parameters.
While you return SELECT * FROM lookups.countries, you can simplify the RETURN declaration like demonstrated:
RETURNS SETOF lookups.countries
In PostgreSQL there is a composite type defined for every table automatically. Use it. The effect is that the function depends on the type and you get an error message if you try to alter the table. Drop & recreate the function in such a case.
This may or may not be desirable - generally it is! You want to be made aware of side effects if you alter tables. The way you have it, your function would break silently and raise an exception on it's next call.
If you provide an explicit default for the second parameter in the declaration like demonstrated, you can (but don't have to) simplify the call in case you don't want to set an upper bound with ends_with.
SELECT * FROM report_get_countries_new('Zaire');
instead of:
SELECT * FROM report_get_countries_new('Zaire', NULL);
Be aware of function overloading in this context.
Don't quote the language name 'plpgsql' even if that's tolerated (for now). It's an identifier.
You can assign a variable at declaration time. Saves an extra step.
Parameters are named in the header. Drop the nonsensical lines:
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
Use quote_literal() to avoid SQL injection (!!!) and fix your quoting problem:
CREATE OR REPLACE FUNCTION report_get_countries_new (
starts_with varchar,
ends_with varchar
)
RETURNS TABLE (
country_id integer,
country_name varchar
) AS
$body$
DECLARE
starts_with ALIAS FOR $1;
ends_with ALIAS FOR $2;
sql VARCHAR;
BEGIN
sql := 'SELECT * FROM lookups.countries WHERE lookups.countries.country_name ' || quote_literal(starts_with) ;
IF ends_with IS NOT NULL THEN
sql := sql || ' AND lookups.countries.country_name <= ' || quote_literal(ends_with) ;
END IF;
RETURN QUERY EXECUTE sql;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;
This is tested in version 9.1, works fine.