I wrote:
Michael Van Canneyt wrote:
...

I would propose to introduce a enumerated

TSQLTransactionStyle =(tsConcurrent,tsReadCommit, etc.);

Then add a TransactionStyle to TSQLTransaction;
This must be mapped by the TSQLConnection when creating the handle.

...

Indeed, this would be more elegant than my solution, but this would require finding some common functionality between various databases (Firebird, PostgreSQL, MySQL, and thinking about others in the future). I.e. something that would be both flexible and yet (at least relatively) portable to all TSQLConnection descendants. I'll look into this tomorrow. Although I may need some help about what transaction modes can be expressed in PostgreSQL and MySQL. I'll see tomorrow what I can do (and where I will need some help :).


1. After reading docs of PostgreSQL and MySQL (I know possibilities of Firebird by heart :), I must admit that I don't see a way to define TSQLTransactionStyle in a way that would be both really flexible and portable to all 3 databases (Firebird, PostgreSQL, MySQL). Every database has an idea of at least two transaction modes like


a) Concurrent (aka "Snapshot")
b) Read committed

... but that's all. Many details seem to be different, especially "read committed" mode has many diffeent variants and possibilities (whether one multirow select can be only partially affected by committing some other transaction ? In Firebird yes, it's always possible, in PostgreSQL you can avoid this. What should happen when uncommitted data is pending ? In Firebird -- many choices: read committed record version, wait for committing or rollback, return immediately with error... PostgreSQL seems to always return committed record version. Anyway -- these are just examples).

I'm waiting for your opinion on this point. How exactly would you propose TSQLTransactionStyle to be defined, and how it should behave when some modes cannot be implemented in some database ?

2. To clear field for our discussion, I'm attaching all my simple corrections to fcl/db/ as a patch to this email. This patch does *not* mess with anything related to changing transaction modes (neither "your" way of TSQLTransactionStyle nor "my" initial way of exposing TSQLTransaction.SQLHandle). In other words, this is the patch that you want to apply now, no matter what is your answer to point 1 above :)

Summary of changes in this patch:
-- Default TIBTransaction.IsolationLevel is changed to ilConcurrent. This makes TIBTransaction more consistent with TIBConnection behaviour, and Delphi's IBX, and Firebird C API (as I explained in one of previous letters).


-- In units Interbase and IBConnection, SQLVar[...].AliasName is used everywhere, instead of SQLVar[...].SQLName. As I explained in previous letter "[fpc-devel] Fix to IBConnection field names", using AliasName is just better than SQLName. Consider e.g. `select count(*) from ...', that has one field with SQLName = '' but AliasName = 'COUNT'. Also, developer writing SQL statememt has full control over what alias name will be assigned for the field.

-- Fix for Sqldb unit that used in one place FieldByName while FindField + manual check was required. This is described in detail in comments inside patch.

-- TIBDatabase should allow setting Transaction from non-nil to nil (TIBDatabase must anyway be prepared everywhere to handle the case when Transaction = nil), and also TIBDatabase should automatically set it's Transaction property to nil when Transaction instance is destroyed (using FreeNotification). I rearranged TIBDatabase.SetTransaction to make it (in my opinion) cleaner.

Michalis.
cvs diff: Diffing .
cvs diff: Diffing dbase
cvs diff: Diffing interbase
Index: interbase/interbase.pp
===================================================================
RCS file: /FPC/CVS/fpc/fcl/db/interbase/interbase.pp,v
retrieving revision 1.16
diff -u -r1.16 interbase.pp
--- interbase/interbase.pp	17 Mar 2005 09:02:17 -0000	1.16
+++ interbase/interbase.pp	20 Mar 2005 03:13:15 -0000
@@ -52,6 +52,8 @@
     procedure SetDBDialect;
     procedure SetTransaction(Value : TIBTransaction);
   protected
+    procedure Notification(AComponent: TComponent; 
+      Operation: TOperation); override;
     function GetHandle : pointer; virtual;
       { This procedure makes connection to Interbase server internally.
         Is visible only by descendants, in application programming
@@ -180,7 +182,7 @@
     property AccessMode: TAccessMode
       read FAccessMode write FAccessMode default amReadWrite;
     property IsolationLevel: TIsolationLevel
-      read FIsolationLevel write FIsolationLevel default ilReadCommitted;
+      read FIsolationLevel write FIsolationLevel default ilConcurrent;
     property LockResolution: TLockResolution
       read FLockResolution write FLockResolution default lrWait;
     property TableReservation: TTableReservation
@@ -383,22 +385,24 @@
 
 procedure TIBDatabase.SetTransaction(Value : TIBTransaction);
 begin
-  if FTransaction = nil then
+  if Value <> FTransaction then
   begin
+    if FTransaction <> nil then
+    begin
+      if FTransaction.Active then
+        raise EInterBaseError.Create(
+          'Cannot assign transaction while old transaction active!');
+      FTransaction.RemoveFreeNotification(Self);
+    end;
+
     FTransaction := Value;
-    if Assigned(FTransaction) then
-      FTransaction.Database := Self;
-    exit;
-  end;
 
-  if (Value <> FTransaction) and (Value <> nil) then
-    if (not FTransaction.Active) then
+    if FTransaction <> nil then
     begin
-      FTransaction := Value;
       FTransaction.Database := Self;
-    end
-    else
-      raise EInterBaseError.Create('Cannot assign transaction while old transaction active!');
+      FTransaction.FreeNotification(Self);
+    end;
+  end;
 end;
 
 function TIBDatabase.GetHandle: pointer;
@@ -468,6 +472,14 @@
   inherited Destroy;
 end;
 
+procedure TIBDatabase.Notification(AComponent: TComponent; 
+  Operation: TOperation); 
+begin
+  inherited;
+  if (AComponent = FTransaction) and (Operation = opRemove) then
+    FTransaction := nil;
+end;
+
 { TIBTransaction }
 
 procedure TIBTransaction.SetActive(Value : boolean);
@@ -573,7 +585,7 @@
 constructor TIBTransaction.Create(AOwner : TComponent);
 begin
   inherited Create(AOwner);
-  FIsolationLevel := ilReadCommitted;
+  FIsolationLevel := ilConcurrent;
 end;
 
 destructor TIBTransaction.Destroy;
@@ -990,7 +1002,7 @@
   for x := 0 to FSQLDA^.SQLD - 1 do
   begin
     {$R-}
-    if (Field.FieldName = FSQLDA^.SQLVar[x].SQLName) then
+    if (Field.FieldName = FSQLDA^.SQLVar[x].AliasName) then
     begin
       Result := not PFieldDataPrefix(CurrBuff)^.IsNull;
 
@@ -1138,7 +1150,7 @@
     begin
       TranslateFldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen, lenset,
         TransType, TransLen);
-      TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].SQLName, TransType,
+      TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].AliasName, TransType,
         TransLen, False, (x + 1));
     end;
     {$R+}
cvs diff: Diffing memds
cvs diff: Diffing mysql
cvs diff: Diffing odbc
cvs diff: Diffing sdf
cvs diff: Diffing sqldb
Index: sqldb/sqldb.pp
===================================================================
RCS file: /FPC/CVS/fpc/fcl/db/sqldb/sqldb.pp,v
retrieving revision 1.14
diff -u -r1.14 sqldb.pp
--- sqldb/sqldb.pp	14 Feb 2005 17:13:12 -0000	1.14
+++ sqldb/sqldb.pp	20 Mar 2005 03:13:16 -0000
@@ -618,8 +618,21 @@
               begin
               // Todo: If there is more then one field in the key, that must be parsed
               s := indexdefs[tel].fields;
-              F := fieldbyname(s);
-              F.ProviderFlags := F.ProviderFlags + [pfInKey];
+              F := FindField(s);
+              
+              { Using FindField and checking is F<>nil 
+                (instead of using FieldByName) is *not* only a workaround
+                for this "Todo: If there is more then one field ..." above.
+                
+                Even if S consists of only one field, there still is
+                a chance that this field is not available in our dataset.
+                Consider e.g. when I do `select Xyz from SomeTable'
+                where SomeTable is defined as 
+                `(Foo integer primary key, Xyz integer)'.
+                Then S above will be 'Foo', but there is no field 'Foo'
+                in my dataset ! }
+              if F <> nil then
+                F.ProviderFlags := F.ProviderFlags + [pfInKey];
               end;
             end;
           end;
cvs diff: Diffing sqldb/interbase
Index: sqldb/interbase/ibconnection.pp
===================================================================
RCS file: /FPC/CVS/fpc/fcl/db/sqldb/interbase/ibconnection.pp,v
retrieving revision 1.12
diff -u -r1.12 ibconnection.pp
--- sqldb/interbase/ibconnection.pp	14 Feb 2005 17:13:12 -0000	1.12
+++ sqldb/interbase/ibconnection.pp	20 Mar 2005 03:13:16 -0000
@@ -488,7 +488,7 @@
       begin
       TranslateFldType(SQLDA^.SQLVar[x].SQLType, SQLDA^.SQLVar[x].SQLLen, SQLDA^.SQLVar[x].SQLScale,
         lenset, TransType, TransLen);
-      FD := TFieldDef.Create(FieldDefs, SQLDA^.SQLVar[x].SQLName, TransType,
+      FD := TFieldDef.Create(FieldDefs, SQLDA^.SQLVar[x].AliasName, TransType,
          TransLen, False, (x + 1));
       if TransType = ftBCD then FD.precision := SQLDA^.SQLVar[x].SQLLen;
       FD.DisplayName := SQLDA^.SQLVar[x].AliasName;
cvs diff: Diffing sqldb/mysql
cvs diff: Diffing sqldb/postgres
cvs diff: Diffing sqlite
cvs diff: Diffing tests
_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply via email to