Solved

DBE : DbiGetNextRecord()

Posted on 1997-08-26
10
699 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 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
Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

 
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

What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Best Firemonkey component pack 1 105
Simple Delphi Question 9 90
Performance of SQL statement 37 112
Firemonkey android show image from resource ? 1 42
A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

823 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