Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Database    [ Add a report in this area ]  
Report #:  136496   Status: Reported
TIBTransaction. Savepoints is not worked
Project:  Delphi Build #:  17.0.4770.56661
Version:    17.0 Submitted By:   Anton Shchyrov
Report Type:  Issue Date Reported:  4/8/2016 8:37:17 AM
Severity:    Commonly encountered problem Last Updated: 4/8/2016 8:40:14 AM
Platform:    All platforms Internal Tracking #:  
Resolution: None  Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
TIBTransaction component have three methods for work with savepoints

procedure ReleaseSavepoint(Name: AnsiString);
procedure RollbackSavepoint(Name: AnsiString);
procedure StartSavepoint(Name: AnsiString);

Each method call API function from gds library and convert AnsiString parameter Name to PAnsiChar.

procedure TIBTransaction.ReleaseSavepoint(Name: AnsiString);
begin
  CheckInTransaction;
  Call(FDatabases[0].GDSLibrary.isc_release_savepoint(StatusVector, @FHandle,
                               @Name), False);
end;

procedure TIBTransaction.RollbackSavepoint(Name: AnsiString);
begin
  CheckInTransaction;
  Call(FDatabases[0].GDSLibrary.isc_rollback_savepoint(StatusVector, @FHandle,
                               @Name, 0), False);
end;

procedure TIBTransaction.StartSavepoint(Name: AnsiString);
begin
  CheckInTransaction;
  Call(FDatabases[0].GDSLibrary.isc_start_savepoint(StatusVector, @FHandle,
                               @Name), False);
end;

But the conversion is going wrong. Instead explicit cast PAnsiChar(Name) in methods gets the address of a variable Name. As a result, the parameter passed to the API function contains garbage.

Also the second parameter passed to the function call "false", the methods do not report erroneous operation
Steps to Reproduce:
1) Put to form three components: IBDatabase, IBTransaction, IBDataSet.

2) Execute code
// Insert new record
IBDataSet1.Insert;
IBDataSet1.Fields[0].AsInteger := 10;
IBDataSet1.Post;
// Editing record
IBDataSet1.Transaction.StartSavepoint('Test');
IBDataSet1.Edit;
IBDataSet1.Fields[0].AsInteger := 20;
IBDataSet1.Post;
IBDataSet1.Transaction.RollbackSavepoint('Test');
IBDataSet1.Refresh;
// Must have 10, but showing 20
ShowMessage(IntToStr(IBDataSet1.Fields[0].AsInteger));
Workarounds
Write helper

TIBXTransactionHelper = class helper for TIBTransaction
  procedure ReleaseSavepointFix(const AName: AnsiString);
  procedure RollbackSavepointFix(const AName: AnsiString);
  procedure StartSavepointFix(const AName: AnsiString);
end;

procedure TIBXTransactionHelper.ReleaseSavepointFix(const AName: AnsiString);
begin
  CheckInTransaction;
  Call(Databases[0].GDSLibrary.isc_release_savepoint(StatusVector, @Handle,
                               PAnsiChar(AName)), True);
end;

procedure TIBXTransactionHelper.RollbackSavepointFix(const AName: AnsiString);
begin
  CheckInTransaction;
  Call(Databases[0].GDSLibrary.isc_rollback_savepoint(StatusVector, @Handle,
                               PAnsiChar(AName), 0), True);
end;

procedure TIBXTransactionHelper.StartSavepointFix(const AName: AnsiString);
begin
  CheckInTransaction;
  Call(Databases[0].GDSLibrary.isc_start_savepoint(StatusVector, @Handle,
                               PAnsiChar(AName)), True);
end;
Attachment
None
Comments

None

Server Response from: ETNACODE01