Dataset.Refresh and BookmarkValid problem

The forum for ZeosLib 7.2 Report problems. Ask for help, post proposals for the new version and Zeoslib 7.2 features here. This is a forum that will be edited once the 7.2.x version goes into RC/stable!!

My personal intention for 7.2 is to speed up the internals as optimal a possible for all IDE's. Hope you can help?! Have fun with testing 7.2
Post Reply
louis
Expert Boarder
Expert Boarder
Posts: 107
Joined: 02.01.2009, 19:41

Dataset.Refresh and BookmarkValid problem

Post by louis »

Hello,
How I notify to all user in the network that a record is deleted by another user?

Before to modify a record i need to verify if thar record still exists then with this procedure I try to avoid that an user try modify the record deleted by another user in the network:

Code: Select all

procedure TMyDataSet.BeforeEdit(Dataset: TDataset);
const
  ErrMsg = 'The record was deleted by another user.';
var
  BM: TBookmark;
begin
  BM := DataSet.GetBookmark;
  DataSet.Refresh;
  if DataSet.BookmarkValid(BM) then
  begin
    DataSet.GotoBookmark(BM);
    DataSet.FreeBookmark(BM);
  end
  else raise Exception.Create(ErrMsg);
end;
But this procedure not work correctly because if the record deleted by another user is previous of current record Dataset.BookmarkValid(BM) is ever false and block editing of current record. If the record deleted by anhoter user is subsequent of current record Dataset.BookmarkValid(BM) is ever true and not cause problem.

What modify i need made to works fine?

Thanks
User avatar
aehimself
Zeos Dev Team
Zeos Dev Team
Posts: 765
Joined: 18.11.2018, 17:37
Location: Hungary

Re: Dataset.Refresh and BookmarkValid problem

Post by aehimself »

As far as I remember all bookmarks become invalid if you close and reopen (refresh) a dataset.
You can solve what you are looking for is taking the value of all index fields prior and use TDataSet.Locate on the same fields after the refresh.

P.s.: depending on your Delphi version FreeBookMark is no longer needed as it's a TBytes array and gets cleaned up automatically. If it's an older version, you should wrap it with a Try...Finally block to avoid memory leaks.
Delphi 12.1, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmariadb.dll 3.3.8
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.13
- MSSQL 2012, 2019; sybdb.dll FreeTDS_2435
- SQLite 3.45.2
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1918
Joined: 17.01.2011, 14:17

Re: Dataset.Refresh and BookmarkValid problem

Post by marsupilami »

aehimself wrote: 22.09.2023, 08:38 As far as I remember all bookmarks become invalid if you close and reopen (refresh) a dataset.
You can solve what you are looking for is taking the value of all index fields prior and use TDataSet.Locate on the same fields after the refresh.
This is correct. Bookmarks are only meant to be used while a dataset is open. After closing a dataset, they become invalid. Also there is no link between a specific record in the database and the book mark in a dataset. The same record may get assigned a different bookmark each time.

A note on the use of "Refresh": As far as I know it only refreshes the current record.

Regading updates of deleted records: I may be wrong, but Zeos checks the number of affected records after an update gets sent to the database. So if you update a record that was already deleted you should get the infamous "0 record(s) updated. Only one record should have been updated." error.
louis
Expert Boarder
Expert Boarder
Posts: 107
Joined: 02.01.2009, 19:41

Re: Dataset.Refresh and BookmarkValid problem

Post by louis »

marsupilami wrote: 22.09.2023, 10:05 A note on the use of "Refresh": As far as I know it only refreshes the current record.
Refresh refreshes whole table.
marsupilami wrote: 22.09.2023, 10:05 You can solve what you are looking for is taking the value of all index fields prior and use TDataSet.Locate on the same fields after the refresh.
Unfortunately I'm in a Delphi DBAction and i don't know the name of table to extract their primary key.

Bookmarks are not safe for my needs but RDB$DB_KEY seems safe to me:
It is persistent in the tables until backup/restore and is unique in the table. Sure, it can be reused after the garbage collector cleans up the data, but when i need DB_KEY there is ever a live transaction that the GC can't clean up then:

Before modifying a record I can try to locate a record that contains an RDB$DB_KEY to see if it still exists.

Now do I have to edit all queries by adding ", RDB$.DB_KEY" or is there a function in Zeos that returns the DBKey of each record like the "RecNo" tables in Paradox?

Thanks
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1918
Joined: 17.01.2011, 14:17

Re: Dataset.Refresh and BookmarkValid problem

Post by marsupilami »

louis wrote: 25.09.2023, 11:59 Bookmarks are not safe for my needs but RDB$DB_KEY seems safe to me:
It is persistent in the tables until backup/restore and is unique in the table. Sure, it can be reused after the garbage collector cleans up the data, but when i need DB_KEY there is ever a live transaction that the GC can't clean up then:

Before modifying a record I can try to locate a record that contains an RDB$DB_KEY to see if it still exists.
You might want to be careful. This is what IBPhoenix has to say about the life time of RDB$DB_KEY:
wrote:The duration of db_keys is a point that's not very clear in the manuals unless you read with sharp eye the API Reference Guide. By default, a db_key is valid only for the duration of the current transaction. After you use Commit or Rollback, the db_key values you had are inaccurate. If you are using auto-commit, you still can use db_keys because this mode uses an implicit CommitRetaining, that means the transaction context is retained, so garbage collection cannot proceed and hence, the db_keys used remain valid until you do a "hard" Commit.
So - if you see changes to the database, you also might see the RDB$DB_KEY value change if a record changes - because the adddress in the database changes.
louis wrote: 25.09.2023, 11:59 Now do I have to edit all queries by adding ", RDB$.DB_KEY" or is there a function in Zeos that returns the DBKey of each record like the "RecNo" tables in Paradox?
There is no magic bullet here. Zeos doesn't automagically extend your query to include RDB$DB_KEY. Even if it did, there would be ambiguities as soon as you do a join. So if you want to use RDB$DB_KEY, you will have to extend your queries.
Another option might be to determine the primary key for the data you have. TZQuery.DbcResultSet.GetMetadata.GetTableName() will help you in identifying the table(s) for your current query. Use TZSQLMetadata to fetch the primary key of that table. Then use that information for checking if the record still exists.

Something like this can get you started:

Code: Select all

uses ZAbstractRODataset, ZDBCIntfs;

function GetTableName(Query: TZAbstractRODataset): String;
var
  MD: IZResultSetMetadata;
  TableName: String;
  x: Integer;
begin
  MD := Query.DbcResultSet.GetMetadata;
  TableName := '';
  for x := FirstDbcIndex to MD.GetColumnCount - FirstDbcIndex do begin
    if TableName = '' then begin; //no table name found yet
      TableName := MD.GetTableName(x);
    end else begin // table name found. Make sure, it is only one table.
      if (TableName <> MD.GetTableName(x)) and (MD.GetTableName(x) <> '') then
        raise Exception.Create('Cannot determine primary key. More than one table name found in Query: ' + TableName + ' and ' + MD.GetTableName(x));
    end;
  end;
end;
Post Reply