Browse Source

fcl-db:
- base: added new TDataSetState: dsRefreshFields used in refreshing field between local TBufDataSet and remote DB during process of applying updates.
- mssql: implemented RefreshLastInsertID + test

git-svn-id: trunk@29443 -

lacak 10 years ago
parent
commit
9fba59b3bc

+ 6 - 5
packages/fcl-db/src/base/bufdataset.pas

@@ -2104,9 +2104,10 @@ end;
 function TCustomBufDataset.GetCurrentBuffer: TRecordBuffer;
 begin
   case State of
-    dsFilter:     Result := FFilterBuffer;
-    dsCalcFields: Result := CalcBuffer;
-    else          Result := ActiveBuffer;
+    dsFilter:        Result := FFilterBuffer;
+    dsCalcFields:    Result := CalcBuffer;
+    dsRefreshFields: Result := FCurrentIndex.CurrentBuffer
+    else             Result := ActiveBuffer;
   end;
 end;
 
@@ -2155,7 +2156,7 @@ begin
     begin
     Inc(CurrBuff, GetRecordSize + Field.Offset);
     Result := Boolean(CurrBuff^);
-    if result and assigned(Buffer) then
+    if Result and assigned(Buffer) then
       begin
       inc(CurrBuff);
       Move(CurrBuff^, Buffer^, Field.DataSize);
@@ -2180,7 +2181,7 @@ begin
   CurrBuff := GetCurrentBuffer;
   If Field.FieldNo > 0 then // If =-1, then calculated/lookup field or =0 unbound field
     begin
-    if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
+    if Field.ReadOnly and not (State in [dsSetKey, dsFilter, dsRefreshFields]) then
       DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);	
     if State in [dsEdit, dsInsert, dsNewValue] then
       Field.Validate(Buffer);	

+ 2 - 2
packages/fcl-db/src/base/db.pas

@@ -48,7 +48,7 @@ type
 
   TDataSetState = (dsInactive, dsBrowse, dsEdit, dsInsert, dsSetKey,
     dsCalcFields, dsFilter, dsNewValue, dsOldValue, dsCurValue, dsBlockRead,
-    dsInternalCalc, dsOpening);
+    dsInternalCalc, dsOpening, dsRefreshFields);
 
   TDataEvent = (deFieldChange, deRecordChange, deDataSetChange,
     deDataSetScroll, deLayoutChange, deUpdateRecord, deUpdateState,
@@ -2157,7 +2157,7 @@ const
 
   dsEditModes = [dsEdit, dsInsert, dsSetKey];
   dsWriteModes = [dsEdit, dsInsert, dsSetKey, dsCalcFields, dsFilter,
-    dsNewValue, dsInternalCalc];
+                  dsNewValue, dsInternalCalc, dsRefreshFields];
   // Correct list of all field types that are BLOB types.
   // Please use this instead of checking TBlobType which will give
   // incorrect results

+ 17 - 1
packages/fcl-db/src/sqldb/mssql/mssqlconn.pp

@@ -96,6 +96,7 @@ type
     // - Statement execution
     procedure Execute(cursor:TSQLCursor; ATransaction:TSQLTransaction; AParams:TParams); override;
     function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
+    function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): boolean; override;
     // - Result retrieving
     procedure AddFieldDefs(cursor:TSQLCursor; FieldDefs:TFieldDefs); override;
     function Fetch(cursor:TSQLCursor):boolean; override;
@@ -315,7 +316,7 @@ end;
 constructor TMSSQLConnection.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
-  FConnOptions := FConnOptions + [sqSupportEmptyDatabaseName, sqEscapeRepeat];
+  FConnOptions := [sqSupportEmptyDatabaseName, sqEscapeRepeat, sqImplicitTransaction, sqLastInsertID];
   //FieldNameQuoteChars:=DoubleQuotes; //default
   Ftds := DBTDS_UNKNOWN;
 end;
@@ -659,6 +660,21 @@ begin
     Result := inherited RowsAffected(cursor);
 end;
 
+function TMSSQLConnection.RefreshLastInsertID(Query: TCustomSQLQuery; Field: TField): boolean;
+var Identity: int64;
+begin
+  // global variable @@IDENTITY is NUMERIC(38,0)
+  Result:=False;
+  if dbcmd(FDBProc, 'SELECT @@IDENTITY') = FAIL then Exit;
+  if dbsqlexec(FDBProc) = FAIL then Exit;
+  if dbresults(FDBProc) = FAIL then Exit;
+  if dbnextrow(FDBProc) = FAIL then Exit;
+  if dbconvert(FDBProc, dbcoltype(FDBProc,1), dbdata(FDBProc,1), -1, SYBINT8, @Identity, sizeof(Identity)) = -1 then Exit;
+  // by default identity columns are ReadOnly
+  Field.AsLargeInt := Identity;
+  Result:=True;
+end;
+
 function TMSSQLConnection.TranslateFldType(SQLDataType: integer): TFieldType;
 begin
   case SQLDataType of

+ 4 - 2
packages/fcl-db/src/sqldb/sqldb.pp

@@ -2644,7 +2644,9 @@ begin
   DoRefresh:=(UpdateKind in [ukModify,ukInsert]) and NeedRefreshRecord(UpdateKind);
   if assigned(LastIDField) or DoRefresh then
     begin
-    S:=SetTempState(dsNewValue);
+    // updates fields directly in record buffer of TBufDataSet
+    //   TDataSet buffers are resynchronized at end of ApplyUpdates process
+    S:=SetTempState(dsRefreshFields);
     try
       RecordRefreshed:=False;
       if assigned(LastIDField) then
@@ -2656,7 +2658,7 @@ begin
     end;
     if RecordRefreshed then
       // Active buffer is updated, move to record.
-      ActiveBufferToRecord;
+      //ActiveBufferToRecord;
     end;
 end;
 

+ 6 - 4
packages/fcl-db/tests/testsqldb.pas

@@ -574,6 +574,8 @@ begin
     case SQLServerType of
       ssMySQL:
         datatype := 'integer auto_increment';
+      ssMSSQL, ssSybase:
+        datatype := 'integer identity';
       ssSQLite:
         datatype := 'integer';
       else
@@ -590,18 +592,18 @@ begin
     Open;
     Insert;
     FieldByName('f').AsString:='a';
-    Post;
+    Post;  // #1 record
     Append;
     FieldByName('f').AsString:='b';
-    Post;
+    Post;  // #2 record
     AssertTrue('ID field is not null after Post', FieldByName('id').IsNull);
-    First;
+    First; // #1 record
     ApplyUpdates(0);
     AssertTrue('ID field is still null after ApplyUpdates', Not FieldByName('id').IsNull);
     // Should be 1 after the table was created, but this is not guaranteed... So we just test positive values.
     id := FieldByName('id').AsLargeInt;
     AssertTrue('ID field has not positive value', id>0);
-    Next;
+    Next;  // #2 record
     AssertTrue('Next ID value is not greater than previous', FieldByName('id').AsLargeInt>id);
     end;
 end;