Browse Source

pqconnection: fix connection leak from empty transaction. Issue #41482

Ondrej Pokorny 1 month ago
parent
commit
78caba769c
1 changed files with 25 additions and 12 deletions
  1. 25 12
      packages/fcl-db/src/sqldb/postgres/pqconnection.pp

+ 25 - 12
packages/fcl-db/src/sqldb/postgres/pqconnection.pp

@@ -59,9 +59,10 @@ type
     FConnected: Boolean;
     FCOnnection: TPQConnection;
     FDBName : String;
-    FActive : Boolean;
-    FUsed: Boolean;
+    FActive : Boolean; // within BEGIN - COMMIT/ROLLBACK
+    FUsed: Boolean; // is currently used in a transaction and cannot be obtained from handle pool
     function GetConnected: Boolean;
+    function GetHasOpenCursors: Boolean; // has open cursors
   protected
     FNativeConn: PPGConn;
     FCursorList  : TThreadList;
@@ -86,6 +87,7 @@ type
     Property NativeConn : PPGConn Read FNativeConn;
     Property Active : Boolean Read Factive;
     Property Used : Boolean Read FUsed Write FUsed;
+    Property HasOpenCursors : Boolean Read GetHasOpenCursors;
     Property Connected : Boolean Read GetConnected;
   end;
 
@@ -323,6 +325,20 @@ begin
   Result:=FNativeConn<>Nil;
 end;
 
+function TPGHandle.GetHasOpenCursors: Boolean;
+
+Var
+  L : TList;
+
+begin
+  L:=FCursorList.LockList;
+  try
+    Result:=L.Count>0;
+  finally
+    FCursorList.UnlockList;
+  end;
+end;
+
 procedure TPGHandle.RegisterCursor(Cursor: TPQCursor);
 begin
   if Cursor.handle=Self then
@@ -350,15 +366,9 @@ begin
   {$ENDIF}
   Cursor.Handle:=Nil;
   FCursorList.Remove(Cursor);
-  L:=FCursorList.LockList;
-  try
-    Used:=L.Count>0;
-    {$IFDEF PQDEBUG}
-    Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, ']  unregistering cursor ',PtrInt(Cursor),'. Handle still used: ',Used);
-    {$ENDIF}
-  finally
-    FCursorList.UnlockList;
-  end;
+  {$IFDEF PQDEBUG}
+  Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, ']  unregistering cursor ',PtrInt(Cursor),'. Handle still used: ',Used);
+  {$ENDIF}
 end;
 
 procedure TPGHandle.UnprepareStatement(Cursor: TPQCursor; Force : Boolean);
@@ -595,6 +605,7 @@ var
 
 begin
   tr := (trans as TPQTransactionHandle).Handle as TPGHandle;
+  tr.Used:=False; // handle can be reused after rollback (even if it failed)
   TR.RollBack;
   result := true;
 end;
@@ -605,6 +616,7 @@ var
 begin
   tr := (trans as TPQTransactionHandle).Handle;
   tr.Commit;
+  tr.Used:=False; // handle can be reused after successful commit
   Result:=True;
 end;
 
@@ -641,7 +653,7 @@ begin
     while (i<L.Count) do
       begin
       T:=TPGHandle(L[i]);
-      if Not (T.Connected and T.Used) then
+      if Not (T.Used or T.HasOpenCursors) then // existing handle must not be used and must not have open cursors
         break
       else
         T:=Nil;
@@ -815,6 +827,7 @@ procedure TPGHandle.Commit;
 
 begin
   Exec('COMMIT',True,SErrCommitFailed);
+  FActive:=False; // handle is not active after successful COMMIT
 end;
 
 procedure TPGHandle.Reset;