Solved

DBE : DbiGetNextRecord()

Posted on 1997-08-26
10
671 Views
Last Modified: 2012-08-14
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;
0
Comment
Question by:ZifNab
  • 6
  • 4
10 Comments
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
Edited text of question
0
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
Adjusted points to 100
0
 
LVL 1

Accepted Solution

by:
arh earned 100 total points
Comment Utility
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
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
Can you give me a clear and working example? Please

Thanks,

Have fun,
c.u. ZifNab;
0
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
{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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 1

Expert Comment

by:arh
Comment Utility
{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
 
LVL 1

Expert Comment

by:arh
Comment Utility
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
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
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
 
LVL 1

Expert Comment

by:arh
Comment Utility
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
 
LVL 8

Author Comment

by:ZifNab
Comment Utility
Thanks for the comment and answer!

Have fun,
c.u. ZifNab;
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now