Solved

DBE : DbiGetNextRecord()

Posted on 1997-08-26
10
724 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
10 Comments
 
LVL 8

Author Comment

by:ZifNab
ID: 1342547
Edited text of question
0
 
LVL 8

Author Comment

by:ZifNab
ID: 1342548
Adjusted points to 100
0
 
LVL 1

Accepted Solution

by:
arh earned 100 total points
ID: 1342549
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 8

Author Comment

by:ZifNab
ID: 1342550
Can you give me a clear and working example? Please

Thanks,

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

Author Comment

by:ZifNab
ID: 1342551
{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
 
LVL 1

Expert Comment

by:arh
ID: 1342552
{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
ID: 1342553
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
ID: 1342554
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
ID: 1342555
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
ID: 1342556
Thanks for the comment and answer!

Have fun,
c.u. ZifNab;
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Run video youtube webbrowse 10 86
URL for downloading Google Chrome for Win XP 2 284
Graphics32 under Delphi 10.1 Berlin 2 186
Browsing a TTreeView in Delphi 5 49
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…
Attackers love to prey on accounts that have privileges. Reducing privileged accounts and protecting privileged accounts therefore is paramount. Users, groups, and service accounts need to be protected to help protect the entire Active Directory …

751 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