I'm trying to cross database with insert query in plpgsql, please help me - sql

CREATE OR REPLACE FUNCTION "public"."cross_insert"("p_name" varchar, "p_detail" varchar)
RETURNS SETOF "pg_catalog"."varchar" AS $BODY$
BEGIN
SELECT * FROM public.dblink(
'
host=10.10.10.53
port=5432
user=sassuperuser
password=password10
dbname=blog2
',
'
SELECT * FROM public.funct_insert2(
'''||p_name||''',
'''||p_detail||'''
);
'
);
RETURN query
SELECT ('SUKSES')::character varying;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000

Your code is little bit messy. Try start with reading documentation to PLpgSQL. There are three issues:
PL/pgSQL doesn't allow implicit throwing result of any query. You should to iterate over result (FOR IN SELECT) or you have to save result to variable (SELECT INTO). If you want to do it, then you should to use PERFORM command. So you query should to look like:
PERFORM public.dblink(' .....');
There is not any sense why your function is declared like RETURNS SETOF varchar. Then you have to use RETURN QUERY SELECT 'success'. It is absolutely useless. There is relatively high overhead of this functionality. This should be classic scalar function that returns text or better, that returns void type. The exception is raised on error. So you don't need to returns anything.
Second, don't do this - PostgreSQL has FDW interface. You can use foreign table instead. It will be faster and safer.

Related

PostgreSQL: Why calling function (NUMERIC -> NUMERIC) in SELECT is too expensive?

I have a query with a long select part with many cases when statement and I tried to put them to function for clarity, but the query start running longer than before.
So, for testing, I create one simple table
CREATE TABLE t1 AS
select random()::numeric from generate_series(1, 5000000, 1)
and one very simple function
create funtion f1 (i NUMERIC)
returns NUMERIC
LANGUAGE plpgsql
AS
$body$
begin
return i+1
end
$body$
and running two queries:
1: SELECT random, random+1 from T1
2: SELECT random, f1(random) from T1
and I don't know why the first is running two times faster than the second, so please help.
Sorry for my English :(
That is because the function f1 has to be called for every row, and calling a PL/pgSQL function is quite expensive.
If you define it as an SQL function, it could be inlined, which would be much cheaper:
FREATE FUNCTION f1(i NUMERIC) RETURNS numeric
LANGUAGE sql AS
'SELECT i + 1';

Writing dynamic SQL safely, with an example function that estimates rows in any view

Last week I wrote myself a client side pivot table generator. Included on the screen is a list of tables and views. Along the way, I found it helpful to have an estimate of the number of rows in a selected table or view. It turns out to be easy to get an estimate of a table count, but I didn't know how to get an estimate of the rows in a view. Today, I was reading
COUNT(*) MADE FAST
Laurenz Albe's blog post ends with this tidy piece of clever:
CREATE FUNCTION row_estimator(query text) RETURNS bigint
LANGUAGE plpgsql AS
$$DECLARE
plan jsonb;
BEGIN
EXECUTE 'EXPLAIN (FORMAT JSON) ' || query INTO plan;
RETURN (plan->0->'Plan'->>'Plan Rows')::bigint;
END;$$;
That. Is. Nice. I realized that this would work as a view estimator, so I wrote up (read "hacked together") a function:
DROP FUNCTION IF EXISTS api.view_count_estimate (text, text);
CREATE OR REPLACE FUNCTION api.view_count_estimate (
schema_name text,
view_name text)
RETURNS BIGINT
AS $$
DECLARE
plan jsonb;
query text;
BEGIN
EXECUTE
'select definition
from pg_views
where schemaname = $1 and
viewname = $2'
USING schema_name,view_name
INTO query;
EXECUTE
'EXPLAIN (FORMAT JSON) ' || query
INTO plan;
RETURN (plan->0->'Plan'->>'Plan Rows')::bigint;
END;
$$ LANGUAGE plpgsql;
ALTER FUNCTION api.view_count_estimate(text, text) OWNER TO user_change_structure;
This brings me to one of the areas I'm a bit nervous about in Postgres: Creating dynamic SQL safely. I'm not really clear about the magic regclass castings, or if I should be using something like quote_ident() above. Is the built SQL with the USING list safe? I don't see how it could be.
I'm using Postgres 11.4.x.
As Laurenz said, your current code is perfectly safe, but given the amount of paranoia around SQL injection nowadays, it's worth elaborating.
Consider the naive version of your function:
CREATE FUNCTION api.view_count_estimate(schema_name text, view_name text) RETURNS BIGINT
AS $$
DECLARE result BIGINT;
BEGIN
EXECUTE 'SELECT COUNT(*) FROM ' || schema_name || '.' || view_name INTO result;
RETURN result;
END
$$ LANGUAGE plpgsql;
This is obviously wide open to SQL injection, but can be easily secured in a couple of ways. The most straightforward is to use quote_ident():
EXECUTE 'SELECT COUNT(*) FROM ' || quote_ident(schema_name) || '.' || quote_ident(view_name)
INTO result;
This guarantees that the concatenated strings are syntactically valid identifiers, by double-quoting them if they contain any symbols, whitespace, or uppercase characters, so there is no risk of the user input being interpreted as an unwanted SQL expression or keyword (though it has the potential downside of making your function inputs case-sensitive).
A much more succinct alternative to quote_ident() is to use the equivalent %I format specifier:
EXECUTE format('SELECT COUNT(*) FROM %I.%I', schema_name, view_name) INTO result;
There is also a %L specifier for embedding string literals, equivalent to the quote_literal() function.
An even nicer approach is to pass view references via a regclass parameter:
CREATE FUNCTION api.view_count_estimate(view_id regclass) RETURNS BIGINT
AS $$
DECLARE result BIGINT;
BEGIN
EXECUTE 'SELECT COUNT(*) FROM ' || view_id::text INTO result;
RETURN result;
END
$$ LANGUAGE plpgsql;
The "magic" behind the regclass type is actually pretty straightforward; the value itself is just the integer primary key of the pg_class table, and it behaves like an integer in most respects, but the casts to and from string values will query pg_class to find the name of the table, following the same quoting rules as quote_ident() and respecting the current schema search path.
In other words, you can call the function with
SELECT view_count_estimate('my_view')
or
SELECT view_count_estimate('"public"."my_view"')
or
SELECT view_count_estimate('public.My_View')
...and the regclass conversion will resolve the identifier just as it would in a query (while the text cast within the function will quote/qualify the identifier as needed).
But all of this is only necessary if you need to substitute an identifier into your query. If you just need to substitute values into a query (as is the case in your function) then a parameterised query (e.g. EXECUTE ... USING) is completely safe. Query parameterisation is not simple string substitution; it maintains a clear separation between code and data (in fact, the entire SQL statement is parsed, and all identifiers resolved, before the parameter values are even considered), so there is no possibility of SQL injection.
In fact, since all of the variables in this case are simple parameters, you don't need a dynamic query at all; your function can just run the query directly, without the EXECUTE:
SELECT definition
FROM pg_views
WHERE schemaname = schema_name
AND viewname = view_name
INTO query;
Under the hood, PL/pgSQL will build exactly the same parameterised query (i.e. ...WHERE schemaname = $1 AND viewname = $2) and bind the parameters to your function inputs. This approach has the added benefit of only preparing the query the first time you call the function within a session, and just re-binding the parameter values on subsequent calls.
Actually, in this case you don't really need the query at all. The pg_get_viewdef() function will return the view definition for a given regclass, so the whole thing can be reduced to:
CREATE FUNCTION api.view_count_estimate(view_id regclass) RETURNS bigint
AS $$
DECLARE
plan jsonb;
BEGIN
EXECUTE 'EXPLAIN (FORMAT JSON) ' || pg_get_viewdef(view_id) INTO plan;
RETURN (plan->0->'Plan'->>'Plan Rows')::bigint;
END;
$$ LANGUAGE plpgsql;
Your function is safe from SQL injection.

PL/pgSQL function to return the output of various SELECT queries from different database

I have found this very interesting article: Refactor a PL/pgSQL function to return the output of various SELECT queries
from Erwin Brandstetter which describes how to return all columns of various tables with only one function:
CREATE OR REPLACE FUNCTION data_of(_table_name anyelement, _where_part text)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_table_name)::text || ' WHERE ' || _where_part;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM data_of(NULL::tablename,'1=1 LIMIT 1');
This works pretty well. I need a very similar solution but for getting data from a table on a different database via dblink. That means the call NULL::tablename will fail since the table does not exists on the database where the call is made. I wonder how to make this work. Any try to connect inside of the function via dblink to a different database failed to get the result of NULL::tablename. It seems the polymorph function needs a polymorph parameter which creates the return type of the function implicit.
I would appreciate very much if anybody could help me.
Thanks a lot
Kind regards
Brian
it seems this request is more difficult to explain than I thought it is. Here is a second try with a test setup:
Database 1
First we create a test table with some data on database 1:
CREATE TABLE db1_test
(
id integer NOT NULL,
txt text
)
WITH (
OIDS=TRUE
);
INSERT INTO db1_test (id, txt) VALUES(1,'one');
INSERT INTO db1_test (id, txt) VALUES(2,'two');
INSERT INTO db1_test (id, txt) VALUES(3,'three');
Now we create the polymorph function on database 1:
-- create a polymorph function with a polymorph parameter "_table_name" on database 1
-- the return type is set implicit by calling the function "data_of" with the parameter "NULL::[tablename]" and a where part
CREATE OR REPLACE FUNCTION data_of(_table_name anyelement, _where_part text)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_table_name)::text || ' WHERE ' || _where_part;
END
$func$ LANGUAGE plpgsql;
Now we make test call if everything works as aspected on database 1
SELECT * FROM data_of(NULL::db1_test, 'id=2');
It works. Please notice I do NOT specify any columns of the table db1_test. Now we switch over to database 2.
Database 2
Here I need to make exactly the same call to data_of from database 1 as before and although WITHOUT knowing the columns of the selected table at call time. Unfortunatly this is not gonna work, the only call which works is something like that:
SELECT
*
FROM dblink('dbname=[database1] port=[port] user=[user] password=[password]'::text, 'SELECT * FROM data_of(NULL::db1_test, \'id=2\')'::text)
t1(id integer, txt text);
Conclusion
This call works, but as you can see, I need to specify at least once how all the columns look like from the table I want to select. I am looking for any way to bypass this and make it possible to make a call WITHOUT knowing all of the columns from the table on database 1.
Final goal
My final goal is to create a function in database 2 which looks like
SELECT * from data_of_dblink('table_name','where_part')
and which calls internaly data_of() on database1 to make it possible to select a table on a different database with a where part as parameter. It should work like a static view but with the possiblity to pass a where part as parameter.
I am extremly open for suggestions.
Thanks a lot
Brian

Input table for PL/pgSQL function

I would like to use a plpgsql function with a table and several columns as input parameter. The idea is to split the table in chunks and do something with each part.
I tried the following function:
CREATE OR REPLACE FUNCTION my_func(Integer)
RETURNS SETOF my_part
AS $$
DECLARE
out my_part;
BEGIN
FOR i IN 0..$1 LOOP
FOR out IN
SELECT * FROM my_func2(SELECT * FROM table1 WHERE id = i)
LOOP
RETURN NEXT out;
END LOOP;
END LOOP;
RETURN;
END;
$$
LANGUAGE plpgsql;
my_func2() is the function that does some work on each smaller part.
CREATE or REPLACE FUNCTION my_func2(table1)
RETURNS SETOF my_part2 AS
$$
BEGIN
RETURN QUERY
SELECT * FROM table1;
END
$$
LANGUAGE plpgsql;
If I run:
SELECT * FROM my_func(99);
I guess I should receive the first 99 IDs processed for each id.
But it says there is an error for the following line:
SELECT * FROM my_func2(select * from table1 where id = i)
The error is:
The subquery is only allowed to return one column
Why does this happen? Is there an easy way to fix this?
There are multiple misconceptions here. Study the basics before you try advanced magic.
Postgres does not have "table variables". You can only pass 1 column or row at a time to a function. Use a temporary table or a refcursor (like commented by #Daniel) to pass a whole table. The syntax is invalid in multiple places, so it's unclear whether that's what you are actually trying.
Even if it is: it would probably be better to process one row at a time or rethink your approach and use a set-based operation (plain SQL) instead of passing cursors.
The data types my_part and my_part2 are undefined in your question. May be a shortcoming of the question or a problem in the test case.
You seem to expect that the table name table1 in the function body of my_func2() refers to the function parameter of the same (type!) name, but this is fundamentally wrong in at least two ways:
You can only pass values. A table name is an identifier, not a value. You would need to build a query string dynamically and execute it with EXECUTE in a plpgsql function. Try a search, many related answers her on SO. Then again, that may also not be what you wanted.
table1 in CREATE or REPLACE FUNCTION my_func2(table1) is a type name, not a parameter name. It means your function expects a value of the type table1. Obviously, you have a table of the same name, so it's supposed to be the associated row type.
The RETURN type of my_func2() must match what you actually return. Since you are returning SELECT * FROM table1, make that RETURNS SETOF table1.
It can just be a simple SQL function.
All of that put together:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Note the parentheses, which are essential for decomposing a row type. Per documentation:
The parentheses are required here to show that compositecol is a column name not a table name
But there is more ...
Don't use out as variable name, it's a keyword of the CREATE FUNCTION statement.
The syntax of your main query my_func() is more like psudo-code. Too much doesn't add up.
Proof of concept
Demo table:
CREATE TABLE table1(table1_id serial PRIMARY KEY, txt text);
INSERT INTO table1(txt) VALUES ('a'),('b'),('c'),('d'),('e'),('f'),('g');
Helper function:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Main function:
CREATE OR REPLACE FUNCTION my_func(int)
RETURNS SETOF table1 AS
$func$
DECLARE
rec table1;
BEGIN
FOR i IN 0..$1 LOOP
FOR rec IN
SELECT * FROM table1 WHERE table1_id = i
LOOP
RETURN QUERY
SELECT * FROM my_func2(rec);
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM my_func(99);
SQL Fiddle.
But it's really just a a proof of concept. Nothing useful, yet.
As the error log is telling you.. you can return only one column in a subquery, so you have to change it to
SELECT my_func2(SELECT Specific_column_you_need FROM hasval WHERE wid = i)
a possible solution can be that you pass to funct2 the primary key of the table your funct2 needs and then you can obtain the whole table by making the SELECT * inside the function

Writing a Procedure in PostgreSQL 9.1

I am trying to write a procedure (function) in Postgres 9.1 to just select all data from a view and return it (all columns) so our application can use it. The intent is to have the application make a call to the procedure whenever it wants the data. My problem is that I cannot seem to get the syntax right to have it return any data. I have written procedures in MySQL, SQL Server and Oracle but never in postgres and it is quite different. Once it works is the best way to call it to just use select "function-name"? Any help would be appreciated.
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record AS
$BODY$
declare times record;
begin
select * into times from fg.fn_dy_dest_dep_arr;
return;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION fg.get_flight_times()
OWNER TO jason;
GRANT EXECUTE ON FUNCTION fg.get_flight_times() TO fltrakr;
Inside a PL/pgSQL function you need to use RETURN NEXT or RETURN QUERY as documented in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#AEN54092
User RETURNS setof record is no longer necessary in 9.1, using returns table is a lot easier. If you only want to return the result of SELECT statement, you can use SQL as well, which makes the function even easier:
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS table (col1 integer, col2 text, col3 text)
$BODY$
select col1, col2, col3 from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
Note that in this case, you have to specify the column definition returned by your function at creation time. With that version you just use:
select * from fg.get_flight_times();
When using returns setof record you don't need to specify the columns at creation time, but you will be forced to do so when selecting from the function (which I find more complicated, as this needs to be done everytime the function is used):
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record
$BODY$
select * from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
And this is used like this:
select *
from fg.get_flight_times() f(col1 integer, col2 text, col3 text);
Much more complicated in my opinion (because the caller needs to know the structure and datatypes of the columns)
Do read the manual, everything I have written here is documented there as well (I know it only because I have already read the manual...)