DBE : DbiGetNextRecord()

Sorry it's BDE instead of DBE (I always mix the B&D)

Hi all, hope someone can help me :

I'm trying to iterate through a database with DbiGetNextRecord() (I've got a reason for not using Next!)

Here's the code I use for it :
{Background information : It's put in a dataaware component}

try
 fdatalink.dataset.UpdateCursorPos;
 check(dbisettobegin(FDatalink.dataset.handle));
 while not FDatalink.dataSet.EOF do begin
 {my CODE}
 status := dbigetnextrecord(fdatalink.dataset.handle,dbinolock,nil,nil)
;
 fdatalink.dataset.UpdateCursorPos;
 end;
finally
 fdatalink.dataset.CursorPosChanged;
end;
if status =dbierr_none then begin
 {my code}
 {fdatalink.dataset.resync([]); } with resync it works, but I don't want to use resync!  
end;

Now ..., the problem that occured :
It doesn't go the next record! If I ask information about the record (e.g. fdatalink.Field.AsVariant) I always get the same information!

The Questions :
* Does anybody know what I'm doing wrong?
* Which  function/procedure do I have to do to go to the next record?
* When to use UpdateCursorPos & CursorPosChanged

Please give a simple example.

I know I'm almost on the right track here.

c.u. ZifNab;
LVL 8
ZifNabAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ZifNabAuthor Commented:
Edited text of question
0
ZifNabAuthor Commented:
Adjusted points to 100
0
arhCommented:
Look at the source of UpdateCursorPos from db.pas:

procedure TDataSet.UpdateCursorPos;
begin
  if FRecordCount > 0 then SetCurrentRecord(FActiveRecord);
end;

It restores BDE cursor to position according to the dataset active record, and effect of your dbigetnetxrecord call is lost.
Advice: you shouldn't call UpdateCursorPos from inside your loop.
And after you have finished with cursor, you should make it sync with where dataset think it is. If you want to restore pos to what it was before your activity, call UpdateCursorPos. If you whant to
leave it as it is now, call CursorPosChanged.
And yes, docs for UpdateCursorPos is misleading.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

ZifNabAuthor Commented:
Can you give me a clear and working example? Please

Thanks,

Have fun,
c.u. ZifNab;
0
ZifNabAuthor Commented:
{Background information for code example}

The component is data-aware : you've to give a field and datasource.

What I want to do is :

Iterate through the table given by the datasource with dbigetnextrecord and while iterating making some calculations on the field. {e.g. sum of all the same field of the whole table} So I also have to get the information of each record.

Information of record : I thought you also could find this with dbiGetNextRecord. By defining a pRecBuf. But that doesn't seem to work.

pRecBuf       Type: pBYTE       (Output) Pointer to the client buffer that receives the record data. Optional. If NULL, no data is returned.


Can you give me an example what does all this?

Thanks already,
Have fun,
c.u. ZifNab;
0
arhCommented:
{hope that will do. tried to keep this as simple as possible.
 I tested this on oracle table containing two columns, one of type
 number and the other of type char(100). When you click the button,
 max value in the selected DBGrid column is shown.
 I think this code can be moved to the component without problems.}

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, DB, Grids, DBGrids, DBTables,
  dbitypes;

type
  TForm1 = class(TForm)
    Table1: TTable;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

procedure show_max_values( fd: PFldDesc );
procedure process_record( cur: HDBICur; fd: PFldDesc; rec_buf: pchar );
procedure iterate_dataset( ds: TDataSet; field_name: string );

implementation

{$R *.DFM}

uses dbiprocs;

procedure TForm1.Button1Click(Sender: TObject);
begin
  iterate_dataset( DBGrid1.DataSource.DataSet, DBGrid1.SelectedField.FieldName );
end;

procedure rcheck( r: dbiResult );
begin
  if r<>DBIERR_NONE then
    raise EDBEngineError.create( r );
end;

var
  max_str: array[0..256] of char;
  max_num: double;

{ iterate thru dataset, calculate and show max value of given field}
procedure iterate_dataset( ds: TDataSet; field_name: string );
 var
  cur: HDBICur;
  cur_props: CurProps;
  fld_desc: PFldDesc;
  fld_desc_size: integer;
  rec_buf: PCHAR;
  rec_buf_size: integer;
  i: integer;
  fd: PFldDesc;
  r: dbiResult;
begin
  cur:=ds.Handle;
  { set up record buffer }
  rcheck( dbiGetCursorProps( cur, cur_props ) );
  rec_buf_size:=cur_props.iRecBufSize;
  getmem( rec_buf, rec_buf_size );
  try
    { get desc for all fields }
    fld_desc_size:=cur_props.iFields*sizeof( FldDesc );
    getmem( fld_desc, fld_desc_size );
    try
      rcheck( dbiGetFieldDescs( cur, fld_desc ) );
      { find field by name }
      fd:=fld_desc;
      i:=1;
      while (i<=cur_props.iFields) and (strpas(fd^.szName)<>field_name) do
        inc( fd );
      if i>cur_props.iFields then
        raise Exception.Create( 'Field not found' );
      { set initial values }
      max_num:=0;
      max_str[0]:=#0;
      { iterate }
      rcheck( dbiSetToBegin( cur ) );
      r:=dbiGetNextRecord( cur, dbiNOLOCK, rec_buf, nil );
      while r=DBIERR_NONE do begin
        process_record( cur, fd, rec_buf );
        r:=dbiGetNextRecord( cur, dbiNOLOCK, rec_buf, nil );
      end;
      if r<>DBIERR_EOF then
        raise EDBEngineError.Create( r );
      { show result }
      show_max_values( fd );
      { clean up }
      ds.UpdateCursorPos;
    finally
      freemem( fld_desc, fld_desc_size );
    end;
  finally
    freemem( rec_buf, rec_buf_size );
  end;
end;

procedure process_record( cur: HDBICur; fd: PFldDesc; rec_buf: pchar );
var
  is_null: bool;
  num_buf: record
    case integer of
      0: (i: integer);
      1: (w: word);
      2: (l: longint);
      3: (d: double);
  end;
  str: array[0..256] of char;
  num: double;
begin
  { get field value }
  case fd^.iFldType of
    fldBOOL, fldINT16, fldUINT16, fldINT32, fldFLOAT:
      begin
        rcheck( dbiGetField( cur, fd^.iFldNum, rec_buf, @num_buf, is_null ) );
        if not is_null then begin
          case fd^.iFldType of
            fldBOOL, fldINT16:
              num:=num_buf.i;
            fldUINT16:
              num:=num_buf.w;
            fldINT32:
              num:=num_buf.l;
            fldFLOAT:
              num:=num_buf.d;
            else
              raise Exception.Create( 'Can''t happen' );
          end;
        end;
      end;
    fldZSTRING:
      begin
        if fd^.iLen>sizeof( str ) then { str should better be allocated by getmem }
          raise Exception.Create( 'String field too long - can''t handle' );
        rcheck( dbiGetField( cur, fd^.iFldNum, rec_buf, @str[0], is_null ) );
      end;
    else
      raise Exception.Create( 'Unsupported field type' );
  end;
  { process field value }
  if not is_null then begin
    case fd^.iFldType of
      fldBOOL, fldINT16, fldUINT16, fldINT32, fldFLOAT:
        if num>max_num then
          max_num:=num;
      fldZSTRING:
        if stricomp( str, max_str )>0 then
          strplcopy( max_str, str, sizeof( max_str ) );
      else
        raise Exception.Create( 'Can''t happen' );
    end;
  end;
end;

procedure show_max_values( fd: PFldDesc );
begin
  case fd^.iFldType of
    fldBOOL, fldINT16, fldUINT16, fldINT32, fldFLOAT:
      MessageDlg( 'Max numeric field value is: '+FloatToStr( max_num ),
                  mtInformation, [mbOK], 0 );
    fldZSTRING:
      MessageDlg( 'Max string field value is: '+strpas( max_str ),
                  mtInformation, [mbOK], 0 );
    else
      raise Exception.Create( 'Unsupported field type' );
  end;
end;

end.

0
arhCommented:
What a mess happened with formatting.
And as always there is a bug.
Code that finds field by name should be:

{ find field by name }
fd:=fld_desc;
i:=1;
while (i<=cur_props.iFields) and strpas(fd^.szName)<>field_name) do begin
 inc( fd );
 inc( i );
end;

The example was working anyhow, so i didn't notice it
for the first time.
0
ZifNabAuthor Commented:
Woow! That's more then I wanted, but it's brilliant!!
You're one heck of a programmer, great, thank's a lot! It will take a few days before I understand the code, but I'll learn a lot. Hope I can still ask you some explanation if I'm really struck with the code.

Question : Maybe you know too. Is it possible with a dataware component to detect if the range of a table has changed. Let say making the component aware for range changes and this without changing or making other components (e.g. like making a descendant of Ttable.)
0
arhCommented:
There is a BDE function, dbiRegisterCallback, which allows
you to have your function (so-called callback) called when
specific event occurs inside BDE. One of event types
is cbTableChanged. This may be what you want. There are
several limitations: callback function may not call BDE,
may not yield to windows, and cbTableChanged only works with
paradox tables. And I can't give you example, because i've
never worked with paradox (neither DBF) files from Delphi.
  And if table is changed by user from another computer in network (or, i guess, another instance of BDE program running on same computer), there is no way to detect it other then
re-read table.
0
ZifNabAuthor Commented:
Thanks for the comment and answer!

Have fun,
c.u. ZifNab;
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.