BookmarkValid in version 6.7.13

Discussion of open issues, suggestions and bugs regarding SDAC (SQL Server Data Access Components) for Delphi, C++Builder, Lazarus (and FPC)
Post Reply
Romano
Posts: 42
Joined: Tue 10 Feb 2009 11:21

BookmarkValid in version 6.7.13

Post by Romano » Sat 27 Apr 2013 09:55

Hi,
I looked at your solution for BookmarkValid.
I'm not sure that it is ok. If I compare it with conditions in SetToBookmark and CompareBookmarks I think that it could be write in another way...

Code: Select all

procedure TMemData.SetToBookmark(Bookmark: PRecBookmark);
...
  if (Bookmark.RefreshIteration = FRefreshIteration) and
    (IntPtr(Bookmark.Item) <> nil)
  then begin
....
  end;

// Set by order
  inherited;
end;

Code: Select all

function TMemData.CompareBookmarks(Bookmark1, Bookmark2: PRecBookmark): integer;
...
    if Bookmark1.RefreshIteration = Bookmark2.RefreshIteration then
      if Bookmark1.Item = Bookmark2.Item then begin
...
        if (IntPtr(Bookmark1.Item) <> nil) and (IntPtr(Bookmark2.Item) <> nil) then
          try // for freed item
            if Bookmark1.Item.Order >= Bookmark2.Item.Order then
              if Bookmark1.Item.Order = Bookmark2.Item.Order then
...
// Compare by order
  Result := inherited CompareBookmarks(Bookmark1, Bookmark2);
end;
I think that the right condition in BookmarkValid is:

Code: Select all

function TMemData.BookmarkValid(Bookmark: PRecBookmark): boolean;
begin
  if IntPtr(Bookmark) <> nil then
    begin
      if (Bookmark.RefreshIteration = FRefreshIteration) and (IntPtr(Bookmark.Item) <> nil) then
        Result := (Bookmark.Item.Order <= FRecordCount)
      else
        Result := (Bookmark.Order <> -1) and (Bookmark.Order <= FRecordCount);
    end
  else
    Result := False;

  if Result and Filtered then
    Result := not OmitRecord(Bookmark.Item);
end;
Maybe I'm wrong:-), but we have problem with BookamrkValid in combination with grid from Developer Express and the right solution is crucial for as and our customers...

Thanks
Roman Krupicka

AndreyZ

Re: BookmarkValid in version 6.7.13

Post by AndreyZ » Mon 29 Apr 2013 10:39

Hello,

Please describe in details the problem you encountered with using SDAC and grid from DevExpress.

Romano
Posts: 42
Joined: Tue 10 Feb 2009 11:21

Re: BookmarkValid in version 6.7.13

Post by Romano » Mon 29 Apr 2013 13:19

Hi,
problem was with Access Violation Error. You have fixed it in 6.7.13. I was curious how you fixed it. That's all:-).

But if I see condition for BookmarkValid I have question - is the condition right?
You compare RefreshIteration and Item at first in SetToBookmark and CompareBookmarks. And after it Order:

Code: Select all

procedure TMemData.SetToBookmark(Bookmark: PRecBookmark);
...
  if (Bookmark.RefreshIteration = FRefreshIteration) and
    (IntPtr(Bookmark.Item) <> nil)
  then begin
....
  end;

// Set by order
  inherited;
end;
But in BookmarkValid you compare Order at first and if it is False, you compare RefreshIteration and Item. I think that the condition should be vice versa...

Code: Select all

  if IntPtr(Bookmark) <> nil then
    Result := ((Bookmark.Order <> -1) and (Bookmark.Order <= FRecordCount)) or
      ((Bookmark.RefreshIteration = FRefreshIteration) and (IntPtr(Bookmark.Item) <> nil) and (Bookmark.Item.Order <= FRecordCount))
  else
    Result := False;
Your condition:
1. step - ORDER
Result := ((Bookmark.Order <> -1) and (Bookmark.Order <= FRecordCount))
or

2. step - RefreshIteration and Item
((Bookmark.RefreshIteration = FRefreshIteration) and (IntPtr(Bookmark.Item) <> nil) and (Bookmark.Item.Order <= FRecordCount))

My suggestion:

Code: Select all

function TMemData.BookmarkValid(Bookmark: PRecBookmark): boolean;
begin
  if IntPtr(Bookmark) <> nil then
    begin
      // 1. step - RefreshIteration and Item
      if (Bookmark.RefreshIteration = FRefreshIteration) and 
         (IntPtr(Bookmark.Item) <> nil) then
        Result := (Bookmark.Item.Order <= FRecordCount)
      else
        // 2. step - ORDER
        Result := (Bookmark.Order <> -1) and (Bookmark.Order <= FRecordCount);
    end
  else
    Result := False;
end;
Regards
Roman Krupicka

colek
Posts: 3
Joined: Wed 10 Apr 2013 05:54

Re: BookmarkValid in version 6.7.13

Post by colek » Tue 30 Apr 2013 06:02

I agree with the colleague Roman. BookmarkValid does not return the correct value in some cases.

Please, look at the example (just click on the Button2).

Regards
Martin Kolek

Project1

Code: Select all

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Unit1

Code: Select all

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, MemDS, DBAccess, MSAccess;

type
  TForm1 = class(TForm)
    Connection: TMSConnection;
    Query: TMSQuery;
    Button2: TButton;
    Label3: TLabel;
    lPocet2: TLabel;
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  MemData;

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);

  function __GetQueryStr(PocetZaznamu: integer): string;

    function __RandomChar: string;
    begin
      Result := Chr(65 + Random(26));
    end;

    function __RandomWord(PocetPismen: integer): string;
    var
      i: integer;
    begin
      Result := '';
      for i := 0 to PocetPismen - 1 do
        Result := Result + __RandomChar;
    end;

  var
    i: integer;
  begin
    Randomize;
    Result := 'SELECT * FROM (';
    for i := 0 to PocetZaznamu - 1 do
    begin
      if i = 0 then
        Result := Result +
          Format(
            '(SELECT %d AS Field1, ''%s'' AS Field2, ''%s'' AS Field3, ''%s'' AS Field4)',
            [Random(1000),
             __RandomWord(3),
             __RandomWord(2),
             __RandomWord(1)])
      else
        Result := Result +
          Format(
            '(SELECT %d, ''%s'', ''%s'', ''%s'')',
            [Random(1000),
             __RandomWord(3),
             __RandomWord(2),
             __RandomWord(1)]);
      if i < (PocetZaznamu - 1) then
        Result := Result + ' UNION ALL'#13
    end;
    Result := Result + ') t';
  end;

var
  B: TBookmark;
begin
  Connection.Connect;

  Query.SQL.Text := __GetQueryStr(10);
  Query.Open;

  Query.Last;
  B := Query.Bookmark;

  Query.Close;
  Query.SQL.Text := __GetQueryStr(100);
  Query.Open;

  if Query.BookmarkValid(B) then
    ShowMessage('Ooops, invalid bookmark is valid!');

  lPocet2.Caption := IntToStr(StrToIntDef(lPocet2.Caption, 0) + 1);
  lPocet2.Refresh;
end;

end.

AndreyZ

Re: BookmarkValid in version 6.7.13

Post by AndreyZ » Tue 30 Apr 2013 08:54

To colek:
It is a correct behaviour that BookmarkValid returns True in your example, because there is a valid record the B bookmark points to. The code that Romano suggests will return the same result.

To Romano:
We think that our code is correct. If you encounter any situations when BookmarkValid returns incorrect value, please describe it in details (particularly, why you think the value is incorrect).

Romano
Posts: 42
Joined: Tue 10 Feb 2009 11:21

Re: BookmarkValid in version 6.7.13

Post by Romano » Tue 07 May 2013 12:03

Hi,
question is what is it bookmark...
I think that bookmark is unique identification of row in query.
So if I close a query I have all its bookmarks invalid.
In your code - if Bookmark.RefreshIteration <> FRefreshIteration you continue to condition for Bookmark.Order. And if Order is less then record count then Bookmark is valid. I think that if Bookmark.RefreshIteration <> FRefreshIteration the bookmark is invalid...
That's all... :)

Regards
Roman Krupicka

AndreyZ

Re: BookmarkValid in version 6.7.13

Post by AndreyZ » Fri 10 May 2013 14:06

That is not right. Roughly speaking, bookmark is like a number of a particular row. So, when you close and open the same dataset, bookmarks remain valid. Moreover, you can use bookmark of one dataset for another dataset (maybe not the correct thing to do, but you can do this if you like). You can check this using any other components, for example, ADO. Here is an example:

Code: Select all

var
  bm: TBookmark;
begin
  ADOQuery1.SQL.Text := 'select * from table1';
  ADOQuery1.Open;
  bm := ADOQuery1.Bookmark;

  ADOQuery2.SQL.Text := 'select * from table2';
  ADOQuery2.Open;
  if ADOQuery2.BookmarkValid(bm) then // bm is a valid bookmark
    ADOQuery2.GotoBookmark(bm);
end;

Post Reply