Postgres dblink stored procedure call with specifying transaction level - postgresql

I want to use dblink in PL/pgSQL stored procedure in such way:
PERFORM dblink_exec('myconn', 'BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE');
PERFORM dblink_exec('myconn', 'SELECT another_stored_procedure()');
PERFORM dblink_exec('myconn', 'COMMIT');
but I got an error in runtime:
ERROR: statement returning results not allowed
CONTEXT: SQL statement "SELECT dblink_exec('myconn', 'select another_stored_procedure()')"
so execution fails, although I tried to get the desired result in different ways.
UPDATE 1:
I know that stored procedures in postgresql are transactional.
I'm using dblink for the autonomous transactions functionality to use it on the same server.
The matter is that the default level of transactions on my server
is "read commited" but sometimes I need to start transactions with another level, e.g. "serializable".
So I need to execute stored procedure in autonomous transaction with explicit transaction level specifying.
And as far as I know dblink allows that, but I failed to find any useful info about dblink or dblink_exec functions which are suitable for my situation.

I assume, you have connected with another PostgreSQL server at the other end.
You need to call the dblink() function to execute statements, which has result(s), and not dblink_exec(). (Even if your function on the other end has returns void -- in that case, you could get a single NULL from calling that function in a SELECT.)
Also, you might not need transaction management:
Are PostgreSQL functions transactional?
In short, you need to execute:
-- PERFORM dblink_exec('myconn', 'BEGIN ...');
-- if you need explicit transaction management
PERFORM * FROM dblink('myconn', 'SELECT another_stored_procedure()') alias(col text);
-- PERFORM dblink_exec('myconn', 'COMMIT');

Related

postgreSQL transaction with condition

I'm trying to translate an SQL Server transaction from a textbook into PostgreSQL. The original transaction is
BEGIN TRANSACTION
INSERT INTO Customers(cust_id,cust_name) VALUES('1000000010','Toys Emporium');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders(order_num,order_date,cust_id) VALUES(20100,'1999/12/1','1000000010');
IF ##ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
[additional inserts with same rollback omitted]
COMMIT TRANSACTION
But I keep getting response
ERROR: syntax error at or near "IF"
LINE 1: IF ##ERROR <> 0 THEN
^
PostgreSQL has the best architecture for developing SQL codes. So, inside the function, you can not use transaction commit or start. Transactions work only inside the procedures. But, you can create your best ACID structure only by using functions. Because the function begin keyword is equivalent to start transaction command and the end keyword of the function is equivalent to commit transaction command. One function = One transaction. But if you need to use one transaction for inserting data into two tables, you can use your insert functions for the same tables inside the one function. In this variant, your main function will be your main transaction. Inside the main function, if one of the internal functions will have an exception so, other internal functions will be rollbacked. For example, we use sub-functions and sub-sub functions inside the main function. If one of the sub-sub functions will have exceptions then your main function will be rollbacked. Using this mechanism you will control your transactions fully, without any problems.
I wrote your query in PostgreSQL, Example:
begin transaction;
INSERT INTO Customers(cust_id,cust_name) VALUES('1000000010','Toys Emporium');
savepoint StartOrder;
commit;
INSERT INTO Orders(order_num,order_date,cust_id) VALUES(20100,'1999/12/1','1000000010');
commit;
exception when others then
rollback to StartOrder;
commit;

How to not execute INSERT in read-only transaction

Postgres server is in hot standbuy mode.
Asynchronou streaming binary replication is used.
Command like
INSERT INTO logfile (logdate) values (current_date)
Causes error
cannot execute INSERT in a read-only transaction.
Maybe it should be changed to
INSERT INTO logfile (logdate)
SELECT current_date
WHERE ???
What where condition should used ?
It should work starting at Postgres 9.0
If direct where clause is not possible, maybe some plpgsql function can used in where.
Maybe
show transaction_read_only
result should captured or some function can used.
Alternately application can determine if database is read-only in startup. Should show transaction_read_only result used for this.
Running INSERT on a standby server is not possible in pure (non-procedural) SQL because when the server is in standby mode, all the data-modification queries are rejected in planning phase, before it's executed.
It's possible with conditionals in PL/PgSQL.
DO $code$
BEGIN
IF NOT pg_is_in_recovery() THEN
INSERT INTO logfile (logdate) VALUES (current_date);
END IF;
END;
$code$;
However, it's probably not recommended - it's usually better to test pg_is_in_recovery() once (in application code) and then act accordingly.
I'm using pg_is_in_recovery() system function instead of transaction_read_only GUC because it's not exactly the same thing. But if you prefer that, please use:
SELECT current_setting('transaction_read_only')::bool
More info: DO command, conditionals in PL/PgSQL, system information functions.

Insert values in a loop and see the progress postgresql [duplicate]

I have Postgresql Function which has to INSERT about 1.5 million data into a table. What I want is I want to see the table getting populated with every one records insertion. Currently what is happening when I am trying with say about 1000 records, the get gets populated only after the complete function gets executed. If I stop the function half way through, no data gets populated. How can I make the record committed even if I stop after certain number of records have been inserted?
This can be done using dblink. I showed an example with one insert being committed you will need to add your while loop logic and commit every loop. You can http://www.postgresql.org/docs/9.3/static/contrib-dblink-connect.html
CREATE OR REPLACE FUNCTION log_the_dancing(ip_dance_entry text)
RETURNS INT AS
$BODY$
DECLARE
BEGIN
PERFORM dblink_connect('dblink_trans','dbname=sandbox port=5433 user=postgres');
PERFORM dblink('dblink_trans','INSERT INTO dance_log(dance_entry) SELECT ' || '''' || ip_dance_entry || '''');
PERFORM dblink('dblink_trans','COMMIT;');
PERFORM dblink_disconnect('dblink_trans');
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION log_the_dancing(ip_dance_entry text)
OWNER TO postgres;
BEGIN TRANSACTION;
select log_the_dancing('The Flamingo');
select log_the_dancing('Break Dance');
select log_the_dancing('Cha Cha');
ROLLBACK TRANSACTION;
--Show records committed even though we rolled back outer transaction
select *
from dance_log;
What you're asking for is generally called an autonomous transaction.
PostgreSQL does not support autonomous transactions at this time (9.4).
To properly support them it really needs stored procedures, not just the user-defined functions it currently supports. It's also very complicated to implement autonomous tx's in PostgreSQL for a variety of internal reasons related to its session and process model.
For now, use dblink as suggested by Bob.
If you have the flexibility to change from function to procedure, from PostgreSQL 12 onwards you can do internal commits if you use procedures instead of functions, invoked by CALL command. Therefore your function will be changed to a procedure and invoked with CALL command: e.g:
CREATE PROCEDURE transaction_test2()
LANGUAGE plpgsql
AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT * FROM test2 ORDER BY x LOOP
INSERT INTO test1 (a) VALUES (r.x);
COMMIT;
END LOOP;
END;
$$;
CALL transaction_test2();
More details about transaction management regarding Postgres are available here: https://www.postgresql.org/docs/12/plpgsql-transactions.html
For Postgresql 9.5 or newer you can use dynamic background workers provided by pg_background extension. It creates autonomous transaction. Please, refer the github page of the extension. The sollution is better then db_link. There is a complete guide on Autonomous transaction support in PostgreSQL. There is a third way to start autonomous transaction in Postgres, but some patching neede. Please see Peter's Eisentraut patch proposal for OracleDB-style transactions.

Calling functions with exec instead of select

Is the default way of calling a function select * from my_function()?
I ask because I have built a function that doesn't return anything, just inserts data into a table and (coming from a SQL Server background) it "feels" strange to call it with select * from...
I was expecting something like exec my_function()
use PERFORM statement - http://www.postgresql.org/docs/current/static/plpgsql-statements.html
Sometimes it is useful to evaluate an expression or SELECT query but
discard the result, for example when calling a function that has
side-effects but no useful result value. To do this in PL/pgSQL, use
the PERFORM statement
so it's just
DO $$ BEGIN
PERFORM my_function();
END $$;
PostgreSQL 11:
PostgreSQL 11 supports true stored procedures as pointed out by #AbdisamadKhalif . They support in-procedure transaction control.
Older versions:
Yes, that's the standard way, and yes it's weird.
Usually you'd write such functions as stored procedures and invoke them with the CALL or EXECUTE command. PostgreSQL does not support true stored procedures (multiple result sets, autonomous transactions, and all that) though, only sql-callable user-defined functions.
So the workaround is to SELECT function_name() using the PostgreSQL extension syntax that omits FROM, or SELECT 1 FROM function_name(); to be (somewhat) more standard.
The ODBC driver, JDBC driver, etc understand the {call func_name()} escape syntax and automatically translate it to an underlying SELECT.
You will use from when the function returns a set. If the function returns void just do
select my_function();

Are PostgreSQL functions transactional?

Is a PostgreSQL function such as the following automatically transactional?
CREATE OR REPLACE FUNCTION refresh_materialized_view(name)
RETURNS integer AS
$BODY$
DECLARE
_table_name ALIAS FOR $1;
_entry materialized_views%ROWTYPE;
_result INT;
BEGIN
EXECUTE 'TRUNCATE TABLE ' || _table_name;
UPDATE materialized_views
SET last_refresh = CURRENT_TIMESTAMP
WHERE table_name = _table_name;
RETURN 1;
END
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
In other words, if an error occurs during the execution of the function, will any changes be rolled back? If this isn't the default behavior, how can I make the function transactional?
PostgreSQL 12 update: there is limited support for top-level PROCEDUREs that can do transaction control. You still cannot manage transactions in regular SQL-callable functions, so the below remains true except when using the new top-level procedures.
Functions are part of the transaction they're called from. Their effects are rolled back if the transaction rolls back. Their work commits if the transaction commits. Any BEGIN ... EXCEPT blocks within the function operate like (and under the hood use) savepoints like the SAVEPOINT and ROLLBACK TO SAVEPOINT SQL statements.
The function either succeeds in its entirety or fails in its entirety, barring BEGIN ... EXCEPT error handling. If an error is raised within the function and not handled, the transaction calling the function is aborted. Aborted transactions cannot commit, and if they try to commit the COMMIT is treated as ROLLBACK, same as for any other transaction in error. Observe:
regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR: division by zero
regress=# COMMIT;
ROLLBACK
See how the transaction, which is in the error state due to the zero division, rolls back on COMMIT?
If you call a function without an explicit surounding transaction the rules are exactly the same as for any other Pg statement:
BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;
(where COMMIT will fail if the SELECT raised an error).
PostgreSQL does not (yet) support autonomous transactions in functions, where the procedure/function could commit/rollback independently of the calling transaction. This can be simulated using a new session via dblink.
BUT, things that aren't transactional or are imperfectly transactional exist in PostgreSQL. If it has non-transactional behaviour in a normal BEGIN; do stuff; COMMIT; block, it has non-transactional behaviour in a function too. For example, nextval and setval, TRUNCATE, etc.
As my knowledge of PostgreSQL is less deeper than Craig Ringer´s I will try to give a shorter answer: Yes.
If you execute a function that has an error in it, none of the steps will impact in the database.
Also, if you execute a query in PgAdmin the same happen.
For example, if you execute in a query:
update your_table yt set column1 = 10 where yt.id=20;
select anything_that_do_not_exists;
The update in the row, id = 20 of your_table will not be saved in the database.
UPDATE Sep - 2018
To clarify the concept I have made a little example with non-transactional function nextval.
First, let´s create a sequence:
create sequence test_sequence start 100;
Then, let´s execute:
update your_table yt set column1 = 10 where yt.id=20;
select nextval('test_sequence');
select anything_that_do_not_exists;
Now, if we open another query and execute
select nextval('test_sequence');
We will get 101 because the first value (100) was used in the latter query (that is because the sequences are not transactional) although the update was not committed.
https://www.postgresql.org/docs/current/static/plpgsql-structure.html
It is important not to confuse the use of BEGIN/END for grouping statements in PL/pgSQL with the similarly-named SQL commands for transaction control. PL/pgSQL's BEGIN/END are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction established by an outer query — they cannot start or commit that transaction, since there would be no context for them to execute in. However, a block containing an EXCEPTION clause effectively forms a subtransaction that can be rolled back without affecting the outer transaction. For more about that see Section 39.6.6.
In the function level, it is not transnational. In other words, each statement in the function belongs to a single transaction, which is the default db auto commit value. Auto commit is true by default. But anyway, you have to call the function using
select schemaName.functionName()
The above statement 'select schemaName.functionName()' is a single transaction, let's name the transaction T1, and so the all the statements in the function belong to the transaction T1. In this way, the function is in a single transaction.
Postgres 14 update: All statements written in between the BEGIN and END block of a Procedure/Function is executed in a single transaction. Thus, any errors arising while execution of this block will cause automatic roll back of the transaction.
Additionally, the ATOMIC Transaction including triggers as well.