[fpc-devel] PostgreSQL SQLdb transactions

Chris Dryburgh cdryburghca at netscape.net
Tue Nov 4 19:33:22 CET 2014


Hi

In PostgreSQL it is considered poor practice to have long running idle 
transactions.
https://encrypted.google.com/#q=postgresql+idle+in+transaction

Long running idle transactions are common with SQLdb and PostgreSQL. In 
FPC 2.6.4 and I think the development version a open connection has a 
open transaction that remains open for as long as a dataset is open. 
Transactions are only needed in read-write situations that require 
multiple queries. Single queries automatically start and commit there 
own transaction. I propose only sending a ‘begin’ transaction command 
when a query is sent or when a non-select query is sent. This would mean 
changes to the pqconnection.pp file.

|TPQConnection.StartdbTransaction, TPQConnection.RollBackRetaining,
and TPQConnection.CommitRetaining
...
-   res := PQexec(tr.PGConn, ‘BEGIN’);
-   CheckResultError(res,tr.PGConn,sErrTransactionFailed);
-   PQclear(res);

|

The ‘begin’ transaction command can now be sent in PrepareStatement or 
Execute before a query is run. According to PostgreSQL documentation 
beginning a transaction when there is already one running or committing 
one when one is not running generates a warning but causes no harm. If 
this warning causes an issue then I'm sure a serverTransactionRunning 
flag can be created.

If transactions are opened for all queries.

|  procedure TPQConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
or procedure TPQConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams);
...
      if LogEvent(detPrepare) then
         Log(detPrepare,S);
+     res := PQexec(tr.PGConn, 'BEGIN');
+     CheckResultError(res,tr.PGConn,sErrTransactionFailed);
+     PQclear(res);
       res := PQexec(tr.PGConn,pchar(s));
       CheckResultError(res,nil,SErrPrepareFailed);
|


If transactions are limited to write only situations.

|  procedure TPQConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
|||or procedure TPQConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams);
|
...
      if LogEvent(detPrepare) then
         Log(detPrepare,S);
+      if (FStatementType in [stInsert,stUpdate,stDelete]) then
+       begin
+         res := PQexec(tr.PGConn, 'BEGIN');
+         CheckResultError(res,tr.PGConn,sErrTransactionFailed);
+         PQclear(res);
+        end;
       res := PQexec(tr.PGConn,pchar(s));
       CheckResultError(res,nil,SErrPrepareFailed);
|


The end result should be that server transactions only open when needed. 
Users would likely commit write transactions quickly to save data to the 
database. Read-only transactions might be left in a open state but can 
be closed by an aware developer when not needed. To eliminate open 
read-only transactions an option is to never open a transaction for 
select queries which would mean a overhead for the server opening and 
closing transactions for each query. What do others here think?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20141104/14523500/attachment.html>


More information about the fpc-devel mailing list