Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

DBE : DbiGetNextRecord()

Posted on 1997-08-26
10
Medium Priority
?
757 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
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 200 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
Independent Software Vendors: 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

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Integration Management Part 2
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Suggested Courses

877 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