View Issue Details

IDProjectCategoryView StatusLast Update
0021594FPCDatabasepublic2018-08-08 07:03
ReporterMartin SchreiberAssigned ToMichael Van Canneyt 
PrioritynormalSeveritycrashReproducibilityalways
Status resolvedResolutionfixed 
PlatformOSwin32OS Version
Product Version2.6.1Product Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0021594: Wrong tdatalink.ffirstrecord.
Descriptiontdatalink.destroy() triggers tdataset.recalcbuflistsize(), there is no update of ffirstrecord of existing datalinks.

Testresult:
"
recno: 50 field: 49 firstrec: 6 activerec: 13 19 buffercount: 20 activerec_0: 36
 activerec_13: 49 activerec_9: 45
recno: 50 field: 49 firstrec: 6 activerec: 7 buffercount: 14 activerec_0: 42 act
iverec_13: 0An unhandled exception occurred at $004200D5 :
EAccessViolation : Access violation
  $004200D5 GETFIELDISNULL, line 209 of memds.pp
  $00421492 TMEMDATASET__GETFIELDDATA, line 718 of memds.pp
  $00423FE0 TDATASET__GETFIELDDATA, line 608 of dataset.inc
  $00428FE8 TFIELD__GETDATA, line 578 of fields.inc
  $00428F19 TFIELD__GETDATA, line 563 of fields.inc
  $0042BA4D TLONGINTFIELD__GETVALUE, line 1440 of fields.inc
  $0042B7D4 TLONGINTFIELD__GETASLONGINT, line 1383 of fields.inc
  $00428AB7 TFIELD__GETASINTEGER, line 464 of fields.inc
  $0040195A LOGTEST, line 38 of firstrecord.pas
  $00401B5F main, line 66 of firstrecord.pas
"

Testresult without heaptrace:
"
recno: 50 field: 49 firstrec: 6 activerec: 13 19 buffercount: 20 activerec_0: 36
 activerec_13: 49 activerec_9: 45
recno: 50 field: 49 firstrec: 6 activerec: 7 buffercount: 14 activerec_0: 42 act
iverec_13: 0 activerec_9: 0
"
TagsNo tags attached.
Fixed in Revision39471
FPCOldBugId
FPCTarget
Attached Files
  • firstrecord.pas (1,618 bytes)
    program firstrecord;
    {$ifdef FPC}{$mode objfpc}{$h+}{$endif}
    {$ifdef mswindows}{$apptype console}{$endif}
    uses
     heaptrc,sysutils,memds,db,classes;
     
    type
     tdatasetcracker = class(TPersistent)
      private
        FFirstRecord,
        FBufferCount : Integer;
     end;
     tdataset1 = class(tdataset);
     
    var
     ds1: tmemdataset;
     dso1: tdatasource;
     dl1,dl2: tdatalink;
     int1: integer;
     
    procedure logtest;
    var
     int1: integer;
    begin
     write('recno: ',ds1.recno,' field: ',ds1.fields[0].asinteger,
                ' firstrec: ', tdatasetcracker(dl1).ffirstrecord,
                 ' activerec: ',dl1.activerecord);
     if dl2 <> nil then begin
      write(' ',dl2.activerecord);
     end;
     write(' buffercount: ',tdataset1(ds1).buffercount);
     int1:= dl1.activerecord;
     dl1.activerecord:= 0;
     write(' activerec_0: ',ds1.fields[0].asinteger);
     dl1.activerecord:= 13;
     write(' activerec_13: ',ds1.fields[0].asinteger);
     dl1.activerecord:= 9;
     write(' activerec_9: ',ds1.fields[0].asinteger);
     writeln;
     dl1.activerecord:= int1;
     flush(output);
    end;
     
    begin
     ds1:= tmemdataset.create(nil);
     dso1:= tdatasource.create(nil);
     dl1:= tdatalink.create;
     dl2:= tdatalink.create;
     try
      with ds1 do begin
       fielddefs.add('field1',ftinteger);
       createtable;
       active:= true;
       for int1:= 0 to 49 do begin
        appendrecord([int1]);
       end;
      end;
      dso1.dataset:= ds1;
      dl1.datasource:= dso1;
      dl2.datasource:= dso1;
      dl1.buffercount:= 14;
      dl2.buffercount:= 20;
      ds1.moveby(30);
      logtest;
      freeandnil(dl2);
      logtest;
     finally
      ds1.free;
      dso1.free;
      dl1.free;
      dl2.free;
     end;
    end.
    
    firstrecord.pas (1,618 bytes)
  • dsrc.diff (448 bytes)
    Index: packages/fcl-db/src/base/datasource.inc
    ===================================================================
    --- packages/fcl-db/src/base/datasource.inc	(revision 39552)
    +++ packages/fcl-db/src/base/datasource.inc	(working copy)
    @@ -609,8 +609,8 @@
         End;
       If ADataset<>Nil Then
         begin
    +    FDataSet:=ADataset;
         ADataset.RegisterDatasource(Self);
    -    FDataSet:=ADataset;
         ProcessEvent(deUpdateState,0);
         End;
     end;
    
    dsrc.diff (448 bytes)
  • dsrc_calcrange.diff (506 bytes)
    Index: packages/fcl-db/src/base/datasource.inc
    ===================================================================
    --- packages/fcl-db/src/base/datasource.inc	(revision 39552)
    +++ packages/fcl-db/src/base/datasource.inc	(working copy)
    @@ -73,6 +73,8 @@
     var
         aMax, aMin: integer;
     begin
    +  if (csLoading in FDataSource.ComponentState) or (Dataset=nil) then
    +    exit;
       aMin:= DataSet.FActiveRecord - FBufferCount + 1;
       If aMin < 0 Then aMin:= 0;
       aMax:= Dataset.FBufferCount - FBufferCount;
    
    dsrc_calcrange.diff (506 bytes)

Activities

2012-03-29 11:29

 

firstrecord.pas (1,618 bytes)
program firstrecord;
{$ifdef FPC}{$mode objfpc}{$h+}{$endif}
{$ifdef mswindows}{$apptype console}{$endif}
uses
 heaptrc,sysutils,memds,db,classes;
 
type
 tdatasetcracker = class(TPersistent)
  private
    FFirstRecord,
    FBufferCount : Integer;
 end;
 tdataset1 = class(tdataset);
 
var
 ds1: tmemdataset;
 dso1: tdatasource;
 dl1,dl2: tdatalink;
 int1: integer;
 
procedure logtest;
var
 int1: integer;
begin
 write('recno: ',ds1.recno,' field: ',ds1.fields[0].asinteger,
            ' firstrec: ', tdatasetcracker(dl1).ffirstrecord,
             ' activerec: ',dl1.activerecord);
 if dl2 <> nil then begin
  write(' ',dl2.activerecord);
 end;
 write(' buffercount: ',tdataset1(ds1).buffercount);
 int1:= dl1.activerecord;
 dl1.activerecord:= 0;
 write(' activerec_0: ',ds1.fields[0].asinteger);
 dl1.activerecord:= 13;
 write(' activerec_13: ',ds1.fields[0].asinteger);
 dl1.activerecord:= 9;
 write(' activerec_9: ',ds1.fields[0].asinteger);
 writeln;
 dl1.activerecord:= int1;
 flush(output);
end;
 
begin
 ds1:= tmemdataset.create(nil);
 dso1:= tdatasource.create(nil);
 dl1:= tdatalink.create;
 dl2:= tdatalink.create;
 try
  with ds1 do begin
   fielddefs.add('field1',ftinteger);
   createtable;
   active:= true;
   for int1:= 0 to 49 do begin
    appendrecord([int1]);
   end;
  end;
  dso1.dataset:= ds1;
  dl1.datasource:= dso1;
  dl2.datasource:= dso1;
  dl1.buffercount:= 14;
  dl2.buffercount:= 20;
  ds1.moveby(30);
  logtest;
  freeandnil(dl2);
  logtest;
 finally
  ds1.free;
  dso1.free;
  dl1.free;
  dl2.free;
 end;
end.
firstrecord.pas (1,618 bytes)

LacaK

2014-03-06 12:39

developer   ~0073508

Last edited: 2014-03-06 12:41

View 3 revisions

I can confirm, that FFirstRecord of 1st TDataLink is not updated properly, when 2nd TDataLink is destroyed.

When 2nd datalink is destroyed, TDatasource.UnregisterDataLink is called, which calls DataSet.RecalcBufListSize (where Dataset.BufferCount is updated from 20 to 14)

DataSet.RecalcBufListSize iterates through all datasources and all datalinks and calculates new BufferCount for DataSet.

May be that in DataSet.RecalcBufListSize should be added call to TDataLink.CalcRange (for all datalinks) if new BufferCount will be <> of old one ?

Michael Van Canneyt

2018-07-18 14:57

administrator   ~0109556

Fixed using suggestion of Laco

Jesus Reyes

2018-08-08 04:08

developer   ~0109940

Last edited: 2018-08-08 04:59

View 2 revisions

There is a problem with this patch.

TDataLink.CalcRange assumes that the datasource's dataset is set which is not always true, as for example when loading a form with a datasource that links to a dataset in a datamodule, in this case while loading the form and the reader is fixing the references, the dataset will be assigned to the datasource, but first the datasource is assigned to the new dataset's list of datasources and then a call to the new dataset's RecalcBufListSize will be done, after that the datasource.dataset is set.

Now there comes the 'if'. IF the first time the form is loaded the linked dataset is opened and is in the initial state, I mean, only the original 10 records are loaded then there may be no problems because TDatalink.CalcRange is not called *IF* the dataset datasources links are just TFieldDatalinks or datalinks which doesn't increase the buffer count usage *THEN* the patched code is simply not used because the buffercount is the same as the default (the condition lacaK has mentioned I guess). The problem comes on the second form load, because if the first load made the buffer count increase the new code will be used as the required buffers will be different than the available buffers. If this happen, then an access violation will be raised as at this point the dataset is not yet set for loading datasource.

I think there are at least two possible fixes, 1. assign the dataset before ADataset.RegisterDatasource(Self). Or, check that the datasource is not loading in TDataLink.CalcRange or check dataset is not nil. Both solutions seem to work, attached.

Jesus Reyes

2018-08-08 04:58

developer  

dsrc.diff (448 bytes)
Index: packages/fcl-db/src/base/datasource.inc
===================================================================
--- packages/fcl-db/src/base/datasource.inc	(revision 39552)
+++ packages/fcl-db/src/base/datasource.inc	(working copy)
@@ -609,8 +609,8 @@
     End;
   If ADataset<>Nil Then
     begin
+    FDataSet:=ADataset;
     ADataset.RegisterDatasource(Self);
-    FDataSet:=ADataset;
     ProcessEvent(deUpdateState,0);
     End;
 end;
dsrc.diff (448 bytes)

Jesus Reyes

2018-08-08 04:58

developer  

dsrc_calcrange.diff (506 bytes)
Index: packages/fcl-db/src/base/datasource.inc
===================================================================
--- packages/fcl-db/src/base/datasource.inc	(revision 39552)
+++ packages/fcl-db/src/base/datasource.inc	(working copy)
@@ -73,6 +73,8 @@
 var
     aMax, aMin: integer;
 begin
+  if (csLoading in FDataSource.ComponentState) or (Dataset=nil) then
+    exit;
   aMin:= DataSet.FActiveRecord - FBufferCount + 1;
   If aMin < 0 Then aMin:= 0;
   aMax:= Dataset.FBufferCount - FBufferCount;
dsrc_calcrange.diff (506 bytes)

Michael Van Canneyt

2018-08-08 07:03

administrator   ~0109941

Fixed using slightly modified version of both patches. rev 39592

Issue History

Date Modified Username Field Change
2012-03-29 11:29 Martin Schreiber New Issue
2012-03-29 11:29 Martin Schreiber Status new => assigned
2012-03-29 11:29 Martin Schreiber Assigned To => Joost van der Sluis
2012-03-29 11:29 Martin Schreiber File Added: firstrecord.pas
2014-03-06 12:39 LacaK Note Added: 0073508
2014-03-06 12:40 LacaK Note Edited: 0073508 View Revisions
2014-03-06 12:41 LacaK Note Edited: 0073508 View Revisions
2018-07-18 14:57 Michael Van Canneyt Fixed in Revision => 39471
2018-07-18 14:57 Michael Van Canneyt Note Added: 0109556
2018-07-18 14:57 Michael Van Canneyt Status assigned => resolved
2018-07-18 14:57 Michael Van Canneyt Fixed in Version => 3.1.1
2018-07-18 14:57 Michael Van Canneyt Resolution open => fixed
2018-07-18 14:57 Michael Van Canneyt Assigned To Joost van der Sluis => Michael Van Canneyt
2018-07-18 14:57 Michael Van Canneyt Target Version => 3.2.0
2018-08-08 04:08 Jesus Reyes Note Added: 0109940
2018-08-08 04:08 Jesus Reyes Status resolved => feedback
2018-08-08 04:08 Jesus Reyes Resolution fixed => reopened
2018-08-08 04:58 Jesus Reyes File Added: dsrc.diff
2018-08-08 04:58 Jesus Reyes File Added: dsrc_calcrange.diff
2018-08-08 04:59 Jesus Reyes Note Edited: 0109940 View Revisions
2018-08-08 07:03 Michael Van Canneyt Note Added: 0109941
2018-08-08 07:03 Michael Van Canneyt Status feedback => resolved
2018-08-08 07:03 Michael Van Canneyt Resolution reopened => fixed