Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2015-03

Suggestion: TDataSetEnumerator

11 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

silvioprog

Hello,

What do you think about a TDataSetEnumerator class in the DB unit? Something like this:

=== begin code ===

  TDataSetEnumerator = class
  private
    FPosition: Integer;
    FDataSet: TDataSet;
    function GetCurrent: TDataSet;
  public
    constructor Create(ADataSet: TDataSet);
    function MoveNext: Boolean;
    property Current: TDataSet read GetCurrent;
  end;
  TSQLQuery = Class(TCustomSQLQuery)
  ...
  public
    function GetEnumerator: TDataSetEnumerator;
  end;
...
constructor TDataSetEnumerator.Create(ADataSet: TDataSet);
begin
  inherited Create;
  FDataSet := ADataSet;
  FPosition := -1;
  FDataSet.MoveBy(0);
end;
function TDataSetEnumerator.GetCurrent: TDataSet;
begin
  Result := FDataSet;
end;
function TDataSetEnumerator.MoveNext: Boolean;
begin
  Inc(FPosition);
  if FPosition = FDataSet.RecordCount then
    Exit(False);
  FDataSet.MoveBy(FPosition);
  Result := True;
end;

...

function TSQLQuery.GetEnumerator: TDataSetEnumerator;
begin
  Result := TDataSetEnumerator.Create(Self);
end;

=== end code ===

So, after implementing this feature, you could use something like this:

=== begin code ===

    q.Open;
    for r in q do
      for f in r.Fields do
        WriteLn(f.AsString);

=== end code ===

Instead of this:

=== begin code ===

    q.Open;
    q.First;
    while not q.EOF do
    begin
      for f in q.Fields do
        WriteLn(f.AsString);
      q.Next;
    end;

=== end code ===

Thank you!

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

Michael Van Canneyt
to silvioprog
silvioprog wrote
Hello, What do you think about a TDataSetEnumerator class in the DB unit? Something like this:

Good idea. I never implemented it because I didn't know what to return. But maybe returning the dataset itself is not a bad idea.

If you implement it using the enumerator operator, you can implement and test it yourself without needing to change the db unit. Once you're satisfied, we can transfer the code to the db unit.

Michael.

Reply by email

Constantine Yannakopoulos
to silvioprog: What do you think about a TDataSetEnumerator class in the DB unit? Something like this:…
silvioprog wrote 6 lines
What do you think about a TDataSetEnumerator class in the DB unit? Something like this: === begin code ===
function TDataSetEnumerator.MoveNext: Boolean;
Show quoted text 8 lines
begin Inc(FPosition); if FPosition = FDataSet.RecordCount then Exit(False); FDataSet.MoveBy(FPosition); Result := True; end;

I'm not sure for FPC but in Delphi there are datasets that do not fully implement the properties .RecNo and .RecordCount, server cursor datasets for example always return -1 if memory serves. IMHO the .MoveNext method should be written in such a way as to not use .RecordCount but only .Eof which is the only property that is guaranteed to always work correctly in all datasets.

​ ​ ​Constantine​

Reply by email

silvioprog
to Michael Van Canneyt: Good idea. I never implemented it because I didn't know what to return. But maybe…
Michael Van Canneyt wrote 11 lines
On Wed, 4 Mar 2015, silvioprog wrote: > Hello, > What do you think about a TDataSetEnumerator class in the DB unit? > Something like this: > Good idea. I never implemented it because I didn't know what to return. But maybe returning the dataset itself is not a bad idea.

=)

The first idea was sent from Gilson Nunes via Lazarus-BR group, but he was iterating in the recno, so I suggested him to iterate on the dataset cursor, because most of the lists use their own item as iterator. =)

If you implement it using the enumerator operator, you can implement and test it yourself without needing to change the db unit.

Using the 'operator' keyword? I'll try a test. (it will be my first usage of this keyword hehe)

Once you're satisfied, we can transfer the code to the db unit.

Very good. I'll make some tests and send a patch to bugtracker.

Thank you!

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

silvioprog
to Constantine Yannakopoulos: function TDataSetEnumerator.MoveNext: Boolean; I'm not sure for FPC but in Delphi there…

On Thu, Mar 5, 2015 at 6:48 AM, Constantine Yannakopoulos <

Show quoted text 27 lines
wrote: On Thu, Mar 5, 2015 at 2:11 AM, silvioprog wrote: > What do you think about a TDataSetEnumerator class in the DB unit? > Something like this: > > === begin code === > > function TDataSetEnumerator.MoveNext: Boolean; > begin > Inc(FPosition); > if FPosition = FDataSet.RecordCount then > Exit(False); > FDataSet.MoveBy(FPosition); > Result := True; > end; > I'm not sure for FPC but in Delphi there are datasets that do not fully implement the properties .RecNo and .RecordCount, server cursor datasets for example always return -1 if memory serves. IMHO the .MoveNext method should be written in such a way as to not use .RecordCount but only .Eof which is the only property that is guaranteed to always work correctly in all datasets.

Thanks for the information.

So we can change it to (FCL have no a .MoveNext, so I used the .Next method):

=== begin code ===

  TDataSetEnumerator = class
  private
    FDataSet: TDataSet;
    FFirstDone: Boolean;
    function GetCurrent: TDataSet;
  public
    constructor Create(ADataSet: TDataSet);
    function MoveNext: Boolean;
    property Current: TDataSet read GetCurrent;
  end;

...

constructor TDataSetEnumerator.Create(ADataSet: TDataSet);
begin
  inherited Create;
  FDataSet := ADataSet;
  FDataSet.First;
end;
function TDataSetEnumerator.GetCurrent: TDataSet;
begin
  Result := FDataSet;
end;
function TDataSetEnumerator.MoveNext: Boolean;
begin
  if FFirstDone then
    FDataSet.Next
  else
    FFirstDone := True;
  Result := not FDataSet.EOF;
end;

=== end code ===

Thank you!

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

Michael Van Canneyt
to silvioprog
silvioprog wrote 44 lines
On Thu, Mar 5, 2015 at 6:48 AM, Constantine Yannakopoulos wrote: On Thu, Mar 5, 2015 at 2:11 AM, silvioprog wrote: What do you think about a TDataSetEnumerator class in the DB unit? Something like this: === begin code ===   function TDataSetEnumerator.MoveNext: Boolean;begin   Inc(FPosition);   if FPosition = FDataSet.RecordCount then     Exit(False);   FDataSet.MoveBy(FPosition);   Result := True; end; I'm not sure for FPC but in Delphi there are datasets that do not fully implement the properties .RecNo and .RecordCount, server cursor datasets for example always return -1 if memory serves. IMHO the .MoveNext method should be written in such a way as to not use .RecordCount but only .Eof which is the only property that is guaranteed to always work correctly in all datasets. Thanks for the information. So we can change it to (FCL have no a .MoveNext, so I used the .Next method): === begin code ===   TDataSetEnumerator = class   private     FDataSet: TDataSet;     FFirstDone: Boolean;     function GetCurrent: TDataSet;   public     constructor Create(ADataSet: TDataSet);     function MoveNext: Boolean;     property Current: TDataSet read GetCurrent;   end; ... constructor TDataSetEnumerator.Create(ADataSet: TDataSet); begin   inherited Create;   FDataSet := ADataSet;   FDataSet.First;

You should check here for unidirectional datasets. They do not support .first. For unidirectional datasets you can only go from the current point forward.

Michael.

Reply by email

LacaK
to Michael Van Canneyt
Show quoted text 5 lines
You should check here for unidirectional datasets. They do not support .first. For unidirectional datasets you can only go from the current point forward.

AFAIK we support First. We have also test for it.
Btw Delphi:
"The only supported navigation methods are the First <http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TDataSet.First> and Next
<http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TDataSet.Next> methods."
(http://docwiki.embarcadero.com/RADStudio/XE7/en/Using_dbExpress_Datasets)

"*TSQLDataSet* is a unidirectional dataset. Unlike other datasets, unidirectional datasets do not buffer multiple records in memory. Because of this, you can only navigate using the First and Next methods. " (http://docwiki.embarcadero.com/Libraries/XE7/en/Data.SqlExpr.TSQLDataSet)

http://docwiki.embarcadero.com/RADStudio/XE7/en/Navigating_Datasets http://docwiki.embarcadero.com/RADStudio/XE7/en/Using_the_First_and_Last_Methods

-Laco.

Reply by email

Michael Van Canneyt
to LacaK
LacaK wrote 6 lines
> You should check here for unidirectional datasets. They do not support > .first. > For unidirectional datasets you can only go from the current point forward. > AFAIK we support First. We have also test for it.

Ah :)

Great, in that case no changes are needed.

Thanks for correcting me !

Michael.

Reply by email

silvioprog
to LacaK
LacaK wrote 68 lines
What if TDataSetEnumerator.Current will return TFields instead of TDataSet which enumerates? Example: procedure IterateDataSet; var DataSet, DS: TDataSet; Flds: TFields; F: TField; begin for Flds in DataSet do for F in Flds do // DataSet can be still accessed by Flds.DataSet writeln(F.AsString,'|'); end; I know that it is more or less same ... it depends what will be most common usage scenario. In one case it will be better have DataSet in another Fields. When iterating DataSet IMO only Fields, Recno, Bookmark are usable properties which changes on each itteration and can be practically used inside loop. But I think, that most common scenario will be use only Fields inside loop. In both cases user must define "control variable" either of TDataSet type or TFields type. (*** Example implementation ***) type TDataSetEnumerator = class private FDataSet: TDataSet; FBOF: Boolean; function GetCurrent: TFields; public &nb sp; constructor Create(ADataSet: TDataSet); function MoveNext: Boolean; property Current: TFields read GetCurrent; end; operator Enumerator(ADataSet: TDataSet): TDataSetEnumerator; begin Result := TDataSetEnumerator.Create(ADataSet); end; constructor TDataSetEnumerator.Create(ADataSet: TDataSet); begin inherited Create; FDataSet := ADataSet; FBOF := True; FDataSet.First; end; function TDataSetEnumerator.GetCurrent: TFields; begin Result := FDataSet.Fields; end; function TDataSetEnumerator.MoveNext: Boolean; begin if FBOF then FBOF := False else FDataSet.Next; Result := not FDataSet.EOF; end; (* *** *) -Laco.

Very nice. I open a new issue about that (
http://bugs.freepascal.org/view.php?id=27644). =)

I have a question. Can I use two or more enum operators in a same unit?

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

silvioprog
to silvioprog
silvioprog wrote 77 lines
On Sat, Mar 7, 2015 at 4:27 AM, LacaK wrote: > What if TDataSetEnumerator.Current will return TFields instead of > TDataSet which enumerates? > > Example: > procedure IterateDataSet; > var DataSet, DS: TDataSet; > Flds: TFields; > F: TField; > begin > for Flds in DataSet do > for F in Flds do // DataSet can be still accessed by Flds.DataSet > writeln(F.AsString,'|'); > end; > > I know that it is more or less same ... it depends what will be most > common usage scenario. > In one case it will be better have DataSet in another Fields. > When iterating DataSet IMO only Fields, Recno, Bookmark are usable > properties which changes on each itteration and can be practically used > inside loop. > But I think, that most common scenario will be use only Fields inside > loop. > In both cases user must define "control variable" either of TDataSet type > or TFields type. > > (*** Example implementation ***) > type > TDataSetEnumerator = class > private > FDataSet: TDataSet; > FBOF: Boolean; > function GetCurrent: TFields; > public > &nb sp; constructor Create(ADataSet: TDataSet); > function MoveNext: Boolean; > property Current: TFields read GetCurrent; > end; > > operator Enumerator(ADataSet: TDataSet): TDataSetEnumerator; > begin > Result := TDataSetEnumerator.Create(ADataSet); > end; > > constructor TDataSetEnumerator.Create(ADataSet: TDataSet); > begin > inherited Create; > FDataSet := ADataSet; > FBOF := True; > FDataSet.First; > end; > > function TDataSetEnumerator.GetCurrent: TFields; > begin > Result := FDataSet.Fields; > end; > > function TDataSetEnumerator.MoveNext: Boolean; > begin > if FBOF then > FBOF := False > else > FDataSet.Next; > Result := not FDataSet.EOF; > end; > > (* *** *) > > -Laco. > Very nice. I open a new issue about that ( http://bugs.freepascal.org/view.php?id=27644). =) I have a question. Can I use two or more enum operators in a same unit?

It was applied in trunk. Thanks guys! (y)

Silvio Clécio My public projects - github.com/silvioprog

Reply by email