1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 1999-2014 by Michael Van Canneyt, member of the
4    Free Pascal development team
5
6    Dataset implementation
7
8    See the file COPYING.FPC, included in this distribution,
9    for details about the copyright.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 **********************************************************************}
16{ ---------------------------------------------------------------------
17    TDataSet
18  ---------------------------------------------------------------------}
19
20Const
21  DefaultBufferCount = 10;
22
23constructor TDataSet.Create(AOwner: TComponent);
24
25begin
26  Inherited Create(AOwner);
27  FFieldDefs:=FieldDefsClass.Create(Self);
28  FFieldList:=FieldsClass.Create(Self);
29  FDataSources:=TFPList.Create;
30  FConstraints:=TCheckConstraints.Create(Self);
31
32// FBuffer must be allocated on create, to make Activebuffer return nil
33  ReAllocMem(FBuffers,SizeOf(TRecordBuffer));
34//  pointer(FBuffers^) := nil;
35  FBuffers[0] := nil;
36  FActiveRecord := 0;
37  FBufferCount := -1;
38  FEOF := True;
39  FBOF := True;
40  FIsUniDirectional := False;
41  FAutoCalcFields := True;
42end;
43
44
45
46destructor TDataSet.Destroy;
47
48var
49  i: Integer;
50
51begin
52  Active:=False;
53  FFieldDefs.Free;
54  FFieldList.Free;
55  While MyDatasourceCount>0 do
56    MyDataSources[MyDatasourceCount - 1].DataSet:=Nil;
57  FDatasources.Free;
58  for i := 0 to FBufferCount do
59    FreeRecordBuffer(FBuffers[i]);
60  FConstraints.Free;
61  FreeMem(FBuffers);
62  Inherited Destroy;
63end;
64
65// This procedure must be called when the first record is made/read
66procedure TDataSet.ActivateBuffers;
67
68begin
69  FBOF:=False;
70  FEOF:=False;
71  FActiveRecord:=0;
72end;
73
74procedure TDataSet.UpdateFieldDefs;
75
76begin
77  //!! To be implemented
78end;
79
80procedure TDataSet.BindFields(Binding: Boolean);
81
82var i, FieldIndex: Integer;
83    FieldDef: TFieldDef;
84    Field: TField;
85
86begin
87  { FieldNo is set to -1 for calculated/lookup fields, to 0 for unbound field
88    and for bound fields it is set to FieldDef.FieldNo }
89  FCalcFieldsSize := 0;
90  FBlobFieldCount := 0;
91  for i := 0 to Fields.Count - 1 do
92    begin
93    Field := Fields[i];
94    Field.FFieldDef := Nil;
95    if not Binding then
96      Field.FFieldNo := 0
97    else if Field.FieldKind in [fkCalculated, fkLookup] then
98      begin
99      Field.FFieldNo := -1;
100      Field.FOffset := FCalcFieldsSize;
101      Inc(FCalcFieldsSize, Field.DataSize + 1);
102      end
103    else
104      begin
105      FieldIndex := FieldDefs.IndexOf(Field.FieldName);
106      if FieldIndex = -1 then
107        DatabaseErrorFmt(SFieldNotFound,[Field.FieldName],Self)
108      else
109        begin
110        FieldDef := FieldDefs[FieldIndex];
111        Field.FFieldDef := FieldDef;
112        Field.FFieldNo := FieldDef.FieldNo;
113        if FieldDef.InternalCalcField then
114          FInternalCalcFields := True;
115        if Field.IsBlob then
116          begin
117          Field.FSize := FieldDef.Size;
118          Field.FOffset := FBlobFieldCount;
119          Inc(FBlobFieldCount);
120          end;
121        // synchronize CodePage between TFieldDef and TField
122        // character data in record buffer and field buffer should have same CodePage
123        if Field is TStringField then
124          TStringField(Field).FCodePage := FieldDef.FCodePage
125        else if Field is TMemoField then
126          TMemoField(Field).FCodePage := FieldDef.FCodePage;
127        end;
128      end;
129    Field.Bind(Binding);
130    end;
131end;
132
133function TDataSet.BookmarkAvailable: Boolean;
134
135Const BookmarkStates = [dsBrowse,dsEdit,dsInsert];
136
137begin
138  Result:=(Not IsEmpty) and  not FIsUniDirectional and (State in BookmarkStates)
139          and (getBookMarkFlag(ActiveBuffer)=bfCurrent);
140end;
141
142procedure TDataSet.CalculateFields(Buffer: TRecordBuffer);
143var
144  i: Integer;
145  OldState: TDatasetState;
146begin
147  FCalcBuffer := Buffer;
148  if FState <> dsInternalCalc then
149  begin
150    OldState := FState;
151    FState := dsCalcFields;
152    try
153      ClearCalcFields(FCalcBuffer);
154      if not IsUniDirectional then
155        for i := 0 to FFieldList.Count - 1 do
156          if FFieldList[i].FieldKind = fkLookup then
157            FFieldList[i].CalcLookupValue;
158    finally
159      DoOnCalcFields;
160      FState := OldState;
161    end;
162  end;
163end;
164
165procedure TDataSet.CheckActive;
166
167begin
168  If Not Active then
169    DataBaseError(SInactiveDataset);
170end;
171
172procedure TDataSet.CheckInactive;
173
174begin
175  If Active then
176    DataBaseError(SActiveDataset);
177end;
178
179procedure TDataSet.ClearBuffers;
180
181begin
182  FRecordCount:=0;
183  FActiveRecord:=0;
184  FCurrentRecord:=-1;
185  FBOF:=True;
186  FEOF:=True;
187end;
188
189procedure TDataSet.ClearCalcFields(Buffer: TRecordBuffer);
190
191begin
192  // Empty
193end;
194
195procedure TDataSet.CloseBlob(Field: TField);
196
197begin
198  //!! To be implemented
199end;
200
201procedure TDataSet.CloseCursor;
202
203begin
204  FreeFieldBuffers;
205  ClearBuffers;
206  SetBufListSize(0);
207  Fields.ClearFieldDefs;
208  InternalClose;
209  FInternalOpenComplete := False;
210end;
211
212procedure TDataSet.CreateFields;
213
214Var I : longint;
215
216begin
217{$ifdef DSDebug}
218  Writeln ('Creating fields');
219  Writeln ('Count : ',fielddefs.Count);
220  For I:=0 to FieldDefs.Count-1 do
221    Writeln('Def ',I,' : ',Fielddefs.items[i].Name,'(',Fielddefs.items[i].FieldNo,')');
222{$endif}
223  For I:=0 to FieldDefs.Count-1 do
224    With FieldDefs.Items[I] do
225      If DataType<>ftUnknown then
226        begin
227        {$ifdef DSDebug}
228        Writeln('About to create field ',FieldDefs.Items[i].Name);
229        {$endif}
230        CreateField(self);
231        end;
232end;
233
234procedure TDataSet.DataEvent(Event: TDataEvent; Info: Ptrint);
235
236  procedure HandleFieldChange(aField: TField);
237  begin
238    if aField.FieldKind in [fkData, fkInternalCalc] then
239      SetModified(True);
240
241    if State <> dsSetKey then begin
242      if aField.FieldKind = fkData then begin
243        if FInternalCalcFields then
244          RefreshInternalCalcFields(ActiveBuffer)
245        else if FAutoCalcFields and (FCalcFieldsSize <> 0) then
246          CalculateFields(ActiveBuffer);
247      end;
248
249      aField.Change;
250    end;
251  end;
252
253  procedure HandleScrollOrChange;
254  begin
255    if State <> dsInsert then
256      UpdateCursorPos;
257  end;
258
259var
260  i: Integer;
261begin
262  case Event of
263    deFieldChange   : HandleFieldChange(TField(Info));
264    deDataSetChange,
265    deDataSetScroll : HandleScrollOrChange;
266    deLayoutChange  : FEnableControlsEvent:=deLayoutChange;
267  end;
268
269  if not ControlsDisabled and (FState <> dsBlockRead) then begin
270    for i := 0 to MyDataSourceCount - 1 do
271      MyDataSources[i].ProcessEvent(Event, Info);
272  end;
273end;
274
275procedure TDataSet.DestroyFields;
276
277begin
278  FFieldList.Clear;
279end;
280
281procedure TDataSet.DoAfterCancel;
282
283begin
284 If assigned(FAfterCancel) then
285   FAfterCancel(Self);
286end;
287
288procedure TDataSet.DoAfterClose;
289
290begin
291 If assigned(FAfterClose) and not (csDestroying in ComponentState) then
292   FAfterClose(Self);
293end;
294
295procedure TDataSet.DoAfterDelete;
296
297begin
298 If assigned(FAfterDelete) then
299   FAfterDelete(Self);
300end;
301
302procedure TDataSet.DoAfterEdit;
303
304begin
305 If assigned(FAfterEdit) then
306   FAfterEdit(Self);
307end;
308
309procedure TDataSet.DoAfterInsert;
310
311begin
312 If assigned(FAfterInsert) then
313   FAfterInsert(Self);
314end;
315
316procedure TDataSet.DoAfterOpen;
317
318begin
319 If assigned(FAfterOpen) then
320   FAfterOpen(Self);
321end;
322
323procedure TDataSet.DoAfterPost;
324
325begin
326 If assigned(FAfterPost) then
327   FAfterPost(Self);
328end;
329
330procedure TDataSet.DoAfterScroll;
331
332begin
333 If assigned(FAfterScroll) then
334   FAfterScroll(Self);
335end;
336
337procedure TDataSet.DoAfterRefresh;
338
339begin
340 If assigned(FAfterRefresh) then
341   FAfterRefresh(Self);
342end;
343
344procedure TDataSet.DoBeforeCancel;
345
346begin
347 If assigned(FBeforeCancel) then
348   FBeforeCancel(Self);
349end;
350
351procedure TDataSet.DoBeforeClose;
352
353begin
354 If assigned(FBeforeClose) and not (csDestroying in ComponentState) then
355   FBeforeClose(Self);
356end;
357
358procedure TDataSet.DoBeforeDelete;
359
360begin
361 If assigned(FBeforeDelete) then
362   FBeforeDelete(Self);
363end;
364
365procedure TDataSet.DoBeforeEdit;
366
367begin
368 If assigned(FBeforeEdit) then
369   FBeforeEdit(Self);
370end;
371
372procedure TDataSet.DoBeforeInsert;
373
374begin
375 If assigned(FBeforeInsert) then
376   FBeforeInsert(Self);
377end;
378
379procedure TDataSet.DoBeforeOpen;
380
381begin
382 If assigned(FBeforeOpen) then
383   FBeforeOpen(Self);
384end;
385
386procedure TDataSet.DoBeforePost;
387
388begin
389 If assigned(FBeforePost) then
390   FBeforePost(Self);
391end;
392
393procedure TDataSet.DoBeforeScroll;
394
395begin
396 If assigned(FBeforeScroll) then
397   FBeforeScroll(Self);
398end;
399
400procedure TDataSet.DoBeforeRefresh;
401
402begin
403 If assigned(FBeforeRefresh) then
404   FBeforeRefresh(Self);
405end;
406
407procedure TDataSet.DoInternalOpen;
408
409begin
410  InternalOpen;
411  FInternalOpenComplete := True;
412{$ifdef dsdebug}
413  Writeln ('Calling internal open');
414{$endif}
415{$ifdef dsdebug}
416  Writeln ('Calling RecalcBufListSize');
417{$endif}
418  FRecordCount := 0;
419  RecalcBufListSize;
420  FBOF := True;
421  FEOF := (FRecordCount = 0);
422end;
423
424procedure TDataSet.DoOnCalcFields;
425
426begin
427 If Assigned(FOnCalcfields) then
428   FOnCalcFields(Self);
429end;
430
431procedure TDataSet.DoOnNewRecord;
432
433begin
434 If assigned(FOnNewRecord) then
435   FOnNewRecord(Self);
436end;
437
438function TDataSet.FieldByNumber(FieldNo: Longint): TField;
439
440begin
441  Result:=FFieldList.FieldByNumber(FieldNo);
442end;
443
444function TDataSet.FindRecord(Restart, GoForward: Boolean): Boolean;
445
446begin
447  //!! To be implemented
448end;
449
450procedure TDataSet.FreeFieldBuffers;
451
452Var I : longint;
453
454begin
455  For I:=0 to FFieldList.Count-1 do
456    FFieldList[i].FreeBuffers;
457end;
458
459function TDataSet.GetBookmarkStr: TBookmarkStr;
460
461begin
462  Result:='';
463  If BookMarkAvailable then
464    begin
465    SetLength(Result,FBookMarkSize);
466    GetBookMarkData(ActiveBuffer,Pointer(Result));
467    end
468end;
469
470function TDataSet.GetBuffer(Index: longint): TRecordBuffer;
471
472begin
473  Result:=FBuffers[Index];
474end;
475
476function TDataSet.GetDatasourceCount: Integer;
477begin
478  Result:=FDataSources.Count;
479end;
480
481function TDataSet.GetDatasources(aIndex : integer): TDatasource;
482begin
483  Result:=TDatasource(FDataSources[aIndex]);
484end;
485
486procedure TDataSet.GetCalcFields(Buffer: TRecordBuffer);
487
488begin
489  if (FCalcFieldsSize > 0) or FInternalCalcFields then
490    CalculateFields(Buffer);
491end;
492
493function TDataSet.GetCanModify: Boolean;
494
495begin
496  Result:= not FIsUnidirectional;
497end;
498
499procedure TDataSet.GetChildren(Proc: TGetChildProc; Root: TComponent);
500
501var
502 I: Integer;
503 Field: TField;
504
505begin
506 for I := 0 to Fields.Count - 1 do begin
507   Field := Fields[I];
508   if (Field.Owner = Root) then
509     Proc(Field);
510 end;
511end;
512
513function TDataSet.GetDataSource: TDataSource;
514begin
515  Result:=nil;
516end;
517
518function TDataSet.GetRecordSize: Word;
519begin
520  Result := 0;
521end;
522
523procedure TDataSet.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
524begin
525  // empty stub
526end;
527
528procedure TDataSet.InternalDelete;
529begin
530  // empty stub
531end;
532
533procedure TDataSet.InternalFirst;
534begin
535  // empty stub
536end;
537
538procedure TDataSet.InternalGotoBookmark(ABookmark: Pointer);
539begin
540  // empty stub
541end;
542
543function TDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
544
545begin
546  Result := False;
547end;
548
549procedure TDataSet.DataConvert(aField: TField; aSource, aDest: Pointer;
550  aToNative: Boolean);
551
552var
553  DT : TFieldType;
554
555begin
556  DT := aField.DataType;
557  if aToNative then
558    begin
559    case DT of
560      ftDate, ftTime, ftDateTime: TDateTimeRec(aDest^) := DateTimeToDateTimeRec(DT, TDateTime(aSource^));
561      ftTimeStamp               : TTimeStamp(aDest^) := TTimeStamp(aSource^);
562      ftBCD                     : TBCD(aDest^) := CurrToBCD(Currency(aSource^));
563      ftFMTBCD                  : TBcd(aDest^) := TBcd(aSource^);
564  // See notes from mantis bug-report 8204 for more information
565  //    ftBytes                   : ;
566  //    ftVarBytes                : ;
567      ftWideString              : StrCopy(PWideChar(aDest), PWideChar(aSource));
568      end
569    end
570  else
571    begin
572    case DT of
573      ftDate, ftTime, ftDateTime: TDateTime(aDest^) := DateTimeRecToDateTime(DT, TDateTimeRec(aSource^));
574      ftTimeStamp               : TTimeStamp(aDest^) := TTimeStamp(aSource^);
575      ftBCD                     : BCDToCurr(TBCD(aSource^),Currency(aDest^));
576      ftFMTBCD                  : TBcd(aDest^) := TBcd(aSource^);
577  //    ftBytes                   : ;
578  //    ftVarBytes                : ;
579      ftWideString              : StrCopy(PWideChar(aDest), PWideChar(aSource));
580      end
581    end
582end;
583
584function TDataSet.GetFieldData(Field: TField; Buffer: Pointer;
585  NativeFormat: Boolean): Boolean;
586
587Var
588  AStatBuffer : Array[0..dsMaxStringSize] of Char;
589  ADynBuffer : pchar;
590
591begin
592  If NativeFormat then
593    Result:=GetFieldData(Field, Buffer)
594  else
595    begin
596    if Field.DataSize <= dsMaxStringSize then
597      begin
598      Result := GetfieldData(Field, @AStatBuffer);
599      if Result then DataConvert(Field,@AStatBuffer,Buffer,False);
600      end
601    else
602      begin
603      GetMem(ADynBuffer,Field.DataSize);
604      try
605        Result := GetfieldData(Field, ADynBuffer);
606        if Result then DataConvert(Field,ADynBuffer,Buffer,False);
607      finally
608        FreeMem(ADynBuffer);
609        end;
610      end;
611    end;
612end;
613
614Function DateTimeRecToDateTime(DT: TFieldType; Data: TDateTimeRec): TDateTime;
615
616var
617  TS: TTimeStamp;
618
619begin
620  TS.Date:=0;
621  TS.Time:=0;
622  case DT of
623    ftDate: TS.Date := Data.Date;
624    ftTime: With TS do
625              begin
626              Time := Data.Time;
627              Date := DateDelta;
628              end;
629  else
630    try
631      TS:=MSecsToTimeStamp(trunc(Data.DateTime));
632    except
633    end;
634  end;
635  Result:=TimeStampToDateTime(TS);
636end;
637
638Function DateTimeToDateTimeRec(DT: TFieldType; Data: TDateTime): TDateTimeRec;
639
640var
641  TS : TTimeStamp;
642
643begin
644  TS:=DateTimeToTimeStamp(Data);
645  With Result do
646    case DT of
647      ftDate:
648        Date:=TS.Date;
649      ftTime:
650        Time:=TS.Time;
651    else
652      DateTime:=TimeStampToMSecs(TS);
653    end;
654end;
655
656procedure TDataSet.SetFieldData(Field: TField; Buffer: Pointer);
657
658begin
659// empty procedure
660end;
661
662procedure TDataSet.SetFieldData(Field: TField; Buffer: Pointer;
663  NativeFormat: Boolean);
664
665Var
666  AStatBuffer : Array[0..dsMaxStringSize] of Char;
667  ADynBuffer : pchar;
668
669begin
670  if NativeFormat then
671    SetFieldData(Field, Buffer)
672  else
673    begin
674    if Field.DataSize <= dsMaxStringSize then
675      begin
676      DataConvert(Field,Buffer,@AStatBuffer,True);
677      SetfieldData(Field, @AStatBuffer);
678      end
679    else
680      begin
681      GetMem(ADynBuffer,Field.DataSize);
682      try
683        DataConvert(Field,Buffer,@AStatBuffer,True);
684        SetfieldData(Field, @AStatBuffer);
685      finally
686        FreeMem(ADynBuffer);
687        end;
688      end;
689    end;
690end;
691
692function TDataSet.GetField(Index: Longint): TField;
693
694begin
695  Result:=FFIeldList[index];
696end;
697
698function TDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
699
700begin
701  Result := DefaultFieldClasses[FieldType];
702end;
703
704function TDataSet.GetIsIndexField(Field: TField): Boolean;
705
706begin
707  Result:=False;
708end;
709
710function TDataSet.GetIndexDefs(IndexDefs: TIndexDefs; IndexTypes: TIndexOptions
711  ): TIndexDefs;
712
713var i,f : integer;
714    IndexFields : TStrings;
715
716begin
717  IndexDefs.Update;
718  Result := TIndexDefs.Create(Self);
719  Result.Assign(IndexDefs);
720  i := 0;
721  IndexFields := TStringList.Create;
722  while i < result.Count do
723    begin
724    if (not ((IndexTypes = []) and (result[i].Options = []))) and
725       ((IndexTypes * result[i].Options) = []) then
726      begin
727      result.Delete(i);
728      dec(i);
729      end
730    else
731      begin
732      ExtractStrings([';'],[' '],pchar(result[i].Fields),Indexfields);
733      for f := 0 to IndexFields.Count-1 do if FindField(Indexfields[f]) = nil then
734        begin
735        result.Delete(i);
736        dec(i);
737        break;
738        end;
739      end;
740    inc(i);
741    end;
742  IndexFields.Free;
743end;
744
745function TDataSet.GetNextRecord: Boolean;
746
747  procedure ExchangeBuffers(var buf1,buf2 : pointer);
748
749  var tempbuf : pointer;
750
751  begin
752    tempbuf := buf1;
753    buf1 := buf2;
754    buf2 := tempbuf;
755  end;
756
757begin
758{$ifdef dsdebug}
759  Writeln ('Getting next record. Internal RecordCount : ',FRecordCount);
760{$endif}
761  If FRecordCount>0 Then SetCurrentRecord(FRecordCount-1);
762  Result:=GetRecord(FBuffers[FBufferCount],gmNext,True)=grOK;
763
764  if Result then
765    begin
766      If FRecordCount=0 then ActivateBuffers;
767      if FRecordCount=FBufferCount then
768        ShiftBuffersBackward
769      else
770        begin
771          Inc(FRecordCount);
772          FCurrentRecord:=FRecordCount - 1;
773          ExchangeBuffers(FBuffers[FCurrentRecord],FBuffers[FBufferCount]);
774        end;
775    end
776  else
777    CursorPosChanged;
778{$ifdef dsdebug}
779  Writeln ('Result getting next record : ',Result);
780{$endif}
781end;
782
783function TDataSet.GetNextRecords: Longint;
784
785begin
786  Result:=0;
787{$ifdef dsdebug}
788  Writeln ('Getting next record(s), need :',FBufferCount);
789{$endif}
790  While (FRecordCount<FBufferCount) and GetNextRecord do
791    Inc(Result);
792{$ifdef dsdebug}
793  Writeln ('Result Getting next record(S), GOT :',RESULT);
794{$endif}
795end;
796
797function TDataSet.GetPriorRecord: Boolean;
798
799begin
800{$ifdef dsdebug}
801  Writeln ('GetPriorRecord: Getting previous record');
802{$endif}
803  CheckBiDirectional;
804  If FRecordCount>0 Then SetCurrentRecord(0);
805  Result:=GetRecord(FBuffers[FBufferCount],gmPrior,True)=grOK;
806  if Result then
807    begin
808      If FRecordCount=0 then ActivateBuffers;
809      ShiftBuffersForward;
810
811      if FRecordCount<FBufferCount then
812        Inc(FRecordCount);
813    end
814  else
815    CursorPosChanged;
816{$ifdef dsdebug}
817  Writeln ('Result getting prior record : ',Result);
818{$endif}
819end;
820
821function TDataSet.GetPriorRecords: Longint;
822
823begin
824  Result:=0;
825{$ifdef dsdebug}
826  Writeln ('Getting previous record(s), need :',FBufferCount);
827{$endif}
828  While (FRecordCount<FBufferCount) and GetPriorRecord do
829    Inc(Result);
830end;
831
832function TDataSet.GetRecNo: Longint;
833
834begin
835  Result := -1;
836end;
837
838function TDataSet.GetRecordCount: Longint;
839
840begin
841  Result := -1;
842end;
843
844procedure TDataSet.InitFieldDefs;
845
846begin
847  if IsCursorOpen then
848    InternalInitFieldDefs
849  else
850    begin
851    try
852      OpenCursor(True);
853    finally
854      CloseCursor;
855      end;
856    end;
857end;
858
859procedure TDataSet.SetBlockReadSize(AValue: Integer);
860begin
861  // the state is changed even when setting the same BlockReadSize (follows Delphi behavior)
862  // e.g., state is dsBrowse and BlockReadSize is 1. Setting BlockReadSize to 1 will change state to dsBlockRead
863  FBlockReadSize := AValue;
864  if AValue > 0 then
865  begin
866    CheckActive;
867    SetState(dsBlockRead);
868  end
869  else
870  begin
871    //update state only when in dsBlockRead
872    if FState = dsBlockRead then
873      SetState(dsBrowse);
874  end;
875end;
876
877procedure TDataSet.SetFieldDefs(AFieldDefs: TFieldDefs);
878
879begin
880  Fields.ClearFieldDefs;
881  FFieldDefs.Assign(AFieldDefs);
882end;
883
884procedure TDataSet.DoInsertAppendRecord(const Values: array of const; DoAppend : boolean);
885var i : integer;
886    ValuesSize : integer;
887begin
888  ValuesSize:=Length(Values);
889  if ValuesSize>FieldCount then DatabaseError(STooManyFields,self);
890  if DoAppend then
891    Append
892  else
893    Insert;
894
895  for i := 0 to ValuesSize-1 do
896    Fields[i].AssignValue(Values[i]);
897  Post;
898
899end;
900
901procedure TDataSet.InitFieldDefsFromFields;
902var i : integer;
903
904begin
905  if FieldDefs.Count = 0 then
906    begin
907    FieldDefs.BeginUpdate;
908    try
909      for i := 0 to Fields.Count-1 do with Fields[i] do
910        if not (FieldKind in [fkCalculated,fkLookup]) then // Do not add fielddefs for calculated/lookup fields.
911          begin
912          FFieldDef:=FieldDefs.FieldDefClass.Create(FieldDefs,FieldName,DataType,Size,Required,FieldDefs.Count+1);
913          with FFieldDef do
914            begin
915            if Required then Attributes := Attributes + [faRequired];
916            if ReadOnly then Attributes := Attributes + [faReadOnly];
917            if DataType = ftBCD then Precision := (Fields[i] as TBCDField).Precision
918            else if DataType = ftFMTBcd then Precision := (Fields[i] as TFMTBCDField).Precision;
919            end;
920          end;
921    finally
922      FieldDefs.EndUpdate;
923      end;
924    end;
925end;
926
927procedure TDataSet.InitRecord(Buffer: TRecordBuffer);
928
929begin
930  InternalInitRecord(Buffer);
931  ClearCalcFields(Buffer);
932end;
933
934procedure TDataSet.InternalCancel;
935
936begin
937  //!! To be implemented
938end;
939
940procedure TDataSet.InternalEdit;
941
942begin
943  //!! To be implemented
944end;
945
946procedure TDataSet.InternalRefresh;
947
948begin
949  //!! To be implemented
950end;
951
952procedure TDataSet.OpenCursor(InfoQuery: Boolean);
953
954begin
955  if InfoQuery then
956    InternalInitFieldDefs
957  else if State <> dsOpening then
958    DoInternalOpen;
959end;
960
961procedure TDataSet.OpenCursorcomplete;
962begin
963  try
964    if FState = dsOpening then DoInternalOpen
965  finally
966    if FInternalOpenComplete then
967      begin
968      SetState(dsBrowse);
969      DoAfterOpen;
970      if not IsEmpty then
971        DoAfterScroll;
972      end
973    else
974      begin
975      SetState(dsInactive);
976      CloseCursor;
977      end;
978  end;
979end;
980
981procedure TDataSet.RefreshInternalCalcFields(Buffer: TRecordBuffer);
982
983begin
984  //!! To be implemented
985end;
986
987function TDataSet.SetTempState(const Value: TDataSetState): TDataSetState;
988
989begin
990  result := FState;
991  FState := value;
992  inc(FDisableControlsCount);
993end;
994
995procedure TDataSet.RestoreState(const Value: TDataSetState);
996
997begin
998  FState := value;
999  dec(FDisableControlsCount);
1000end;
1001
1002function TDataSet.GetActive: boolean;
1003
1004begin
1005  result := (FState <> dsInactive) and (FState <> dsOpening);
1006end;
1007
1008procedure TDataSet.InternalHandleException;
1009
1010begin
1011  if assigned(classes.ApplicationHandleException) then
1012    classes.ApplicationHandleException(self)
1013  else
1014    ShowException(ExceptObject,ExceptAddr);
1015end;
1016
1017procedure TDataSet.InternalInitRecord(Buffer: TRecordBuffer);
1018begin
1019  // empty stub
1020end;
1021
1022procedure TDataSet.InternalLast;
1023begin
1024  // empty stub
1025end;
1026
1027procedure TDataSet.InternalPost;
1028
1029  Procedure CheckRequiredFields;
1030
1031  Var I : longint;
1032
1033  begin
1034    For I:=0 to FFieldList.Count-1 do
1035      With FFieldList[i] do
1036        // Required fields that are NOT autoinc !! Autoinc cannot be set !!
1037        if Required and not ReadOnly and
1038           (FieldKind=fkData) and Not (DataType=ftAutoInc) and IsNull then
1039          DatabaseErrorFmt(SNeedField,[DisplayName],Self);
1040  end;
1041
1042begin
1043  CheckRequiredFields;
1044end;
1045
1046procedure TDataSet.InternalSetToRecord(Buffer: TRecordBuffer);
1047begin
1048  // empty stub
1049end;
1050
1051procedure TDataSet.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
1052begin
1053  // empty stub
1054end;
1055
1056procedure TDataSet.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
1057begin
1058  // empty stub
1059end;
1060
1061procedure TDataSet.SetUniDirectional(const Value: Boolean);
1062begin
1063  FIsUniDirectional := Value;
1064end;
1065
1066class function TDataSet.FieldDefsClass: TFieldDefsClass;
1067begin
1068  Result:=TFieldDefs;
1069end;
1070
1071class function TDataSet.FieldsClass: TFieldsClass;
1072begin
1073  Result:=TFields;
1074end;
1075
1076procedure TDataSet.SetActive(Value: Boolean);
1077
1078begin
1079  if value and (Fstate = dsInactive) then
1080    begin
1081    if csLoading in ComponentState then
1082      begin
1083      FOpenAfterRead := true;
1084      exit;
1085      end
1086    else
1087      begin
1088      DoBeforeOpen;
1089      FEnableControlsEvent:=deLayoutChange;
1090      FInternalCalcFields:=False;
1091      try
1092        FDefaultFields:=FieldCount=0;
1093        OpenCursor(False);
1094      finally
1095        if FState <> dsOpening then OpenCursorComplete;
1096        end;
1097      end;
1098    FModified:=False;
1099    end
1100  else if not value and (Fstate <> dsinactive) then
1101    begin
1102    DoBeforeClose;
1103    SetState(dsInactive);
1104    CloseCursor;
1105    DoAfterClose;
1106    FModified:=False;
1107    end
1108end;
1109
1110procedure TDataSet.Loaded;
1111
1112begin
1113  inherited;
1114  try
1115    if FOpenAfterRead then SetActive(true);
1116  except
1117    if csDesigning in Componentstate then
1118      InternalHandleException
1119    else
1120      raise;
1121  end;
1122end;
1123
1124
1125procedure TDataSet.RecalcBufListSize;
1126
1127var
1128  i, j, ABufferCount: Integer;
1129  DataLink: TDataLink;
1130
1131begin
1132{$ifdef dsdebug}
1133  Writeln('Recalculating buffer list size - check cursor');
1134{$endif}
1135  If Not IsCursorOpen Then
1136    Exit;
1137{$ifdef dsdebug}
1138  Writeln('Recalculating buffer list size');
1139{$endif}
1140  if IsUniDirectional then
1141    ABufferCount := 1
1142  else
1143    ABufferCount := DefaultBufferCount;
1144
1145  for i := 0 to MyDataSourceCount - 1 do
1146    for j := 0 to MyDataSources[i].DataLinkCount - 1 do
1147      begin
1148      DataLink:=MyDataSources[i].DataLink[j];
1149      if ABufferCount<DataLink.BufferCount then
1150        ABufferCount:=DataLink.BufferCount;
1151      end;
1152
1153  If (FBufferCount=ABufferCount) Then
1154    exit;
1155
1156{$ifdef dsdebug}
1157  Writeln('Setting buffer list size');
1158{$endif}
1159
1160  SetBufListSize(ABufferCount);
1161{$ifdef dsdebug}
1162  Writeln('Getting next buffers');
1163{$endif}
1164  GetNextRecords;
1165  if (FRecordCount < FBufferCount) and not IsUniDirectional then
1166    begin
1167    FActiveRecord := FActiveRecord + GetPriorRecords;
1168    CursorPosChanged;
1169    end;
1170{$Ifdef dsDebug}
1171  WriteLn(
1172    'SetBufferCount: FActiveRecord=',FActiveRecord,
1173    ' FCurrentRecord=',FCurrentRecord,
1174    ' FBufferCount= ',FBufferCount,
1175    ' FRecordCount=',FRecordCount);
1176{$Endif}
1177  for i := 0 to MyDataSourceCount - 1 do
1178    for j := 0 to MyDataSources[i].DataLinkCount - 1 do
1179       MyDataSources[i].DataLink[j].CalcRange;
1180end;
1181
1182procedure TDataSet.SetBookmarkStr(const Value: TBookmarkStr);
1183
1184begin
1185  GotoBookMark(Pointer(Value))
1186end;
1187
1188procedure TDataSet.SetBufListSize(Value: Longint);
1189
1190Var I : longint;
1191
1192begin
1193  if Value = 0 then Value := -1;
1194{$ifdef dsdebug}
1195  Writeln ('SetBufListSize: ',Value);
1196{$endif}
1197  If Value=FBufferCount Then
1198    exit;
1199  If Value>FBufferCount then
1200    begin
1201{$ifdef dsdebug}
1202    Writeln ('   Reallocating memory :',(Value+1)*SizeOf(TRecordBuffer));
1203{$endif}
1204    ReAllocMem(FBuffers,(Value+1)*SizeOf(TRecordBuffer));
1205{$ifdef dsdebug}
1206    Writeln ('   Filling memory :',(Value+1-FBufferCount)*SizeOf(TRecordBuffer));
1207{$endif}
1208    Inc(FBufferCount); // Cause FBuffers[FBufferCount] is already allocated
1209    FillChar(FBuffers[FBufferCount],(Value+1-FBufferCount)*SizeOf(TRecordBuffer),#0);
1210{$ifdef dsdebug}
1211    Writeln ('   Filled memory');
1212{$endif}
1213    Try
1214{$ifdef dsdebug}
1215      Writeln ('   Assigning buffers : ',(Value)*SizeOf(TRecordBuffer));
1216{$endif}
1217      For I:=FBufferCount to Value do
1218        FBuffers[i]:=AllocRecordBuffer;
1219{$ifdef dsdebug}
1220      Writeln ('   Assigned buffers (FBufferCount:',FBufferCount,') : ',(Value)*SizeOf(TRecordBuffer));
1221{$endif}
1222    except
1223      I:=FBufferCount;
1224      While (I<(Value+1)) do
1225        begin
1226        FreeRecordBuffer(FBuffers[i]);
1227        Inc(i);
1228        end;
1229      raise;
1230    end;
1231    end
1232  else
1233    begin
1234{$ifdef dsdebug}
1235    Writeln ('   Freeing buffers :',FBufferCount-Value);
1236{$endif}
1237    if (value > -1) and (FActiveRecord>Value-1) then
1238      begin
1239      for i := 0 to (FActiveRecord-Value) do
1240        ShiftBuffersBackward;
1241      FActiveRecord := Value -1;
1242      end;
1243
1244    If Assigned(FBuffers) then
1245      begin
1246      For I:=Value+1 to FBufferCount do
1247        FreeRecordBuffer(FBuffers[i]);
1248      // FBuffer must stay allocated, to make sure that Activebuffer returns nil
1249      if Value = -1 then
1250        begin
1251        ReAllocMem(FBuffers,SizeOf(TRecordBuffer));
1252        FBuffers[0] := nil;
1253        end
1254      else
1255        ReAllocMem(FBuffers,(Value+1)*SizeOf(TRecordBuffer));
1256      end;
1257    end;
1258  FBufferCount:=Value;
1259  If Value=-1 then
1260    Value:=0;
1261  if FRecordCount > Value then FRecordCount := Value;
1262{$ifdef dsdebug}
1263  Writeln ('   SetBufListSize: Final FBufferCount=',FBufferCount);
1264{$endif}
1265end;
1266
1267procedure TDataSet.SetChildOrder(Component: TComponent; Order: Longint);
1268
1269var
1270  Field: TField;
1271begin
1272  Field := Component as TField;
1273  if Fields.IndexOf(Field) >= 0 then
1274    Field.Index := Order;
1275end;
1276
1277procedure TDataSet.SetCurrentRecord(Index: Longint);
1278
1279begin
1280  If FCurrentRecord<>Index then
1281    begin
1282{$ifdef DSdebug}
1283    Writeln ('Setting current record to: ',index);
1284{$endif}
1285    if not FIsUniDirectional then Case GetBookMarkFlag(FBuffers[Index]) of
1286      bfCurrent : InternalSetToRecord(FBuffers[Index]);
1287      bfBOF : InternalFirst;
1288      bfEOF : InternalLast;
1289      end;
1290    FCurrentRecord:=Index;
1291    end;
1292end;
1293
1294procedure TDataSet.SetDefaultFields(const Value: Boolean);
1295begin
1296  FDefaultFields := Value;
1297end;
1298
1299procedure TDataSet.SetField(Index: Longint; Value: TField);
1300
1301begin
1302  //!! To be implemented
1303end;
1304
1305procedure TDataSet.CheckBiDirectional;
1306
1307begin
1308  if FIsUniDirectional then DataBaseError(SUniDirectional);
1309end;
1310
1311procedure TDataSet.SetFilterOptions(Value: TFilterOptions);
1312
1313begin
1314  CheckBiDirectional;
1315  FFilterOptions := Value;
1316end;
1317
1318procedure TDataSet.SetFilterText(const Value: string);
1319
1320begin
1321  FFilterText := value;
1322end;
1323
1324procedure TDataSet.SetFiltered(Value: Boolean);
1325
1326begin
1327  if Value then CheckBiDirectional;
1328  FFiltered := value;
1329end;
1330
1331procedure TDataSet.SetFound(const Value: Boolean);
1332begin
1333  FFound := Value;
1334end;
1335
1336procedure TDataSet.SetModified(Value: Boolean);
1337
1338begin
1339  FModified := value;
1340end;
1341
1342procedure TDataSet.SetName(const Value: TComponentName);
1343
1344  function CheckName(const FieldName: string): string;
1345
1346  var i,j: integer;
1347
1348  begin
1349    Result := FieldName;
1350    i := 0;
1351    j := 0;
1352    // Check if fieldname exists.
1353    while (i < Fields.Count) do
1354      if Not SameText(Result,Fields[i].Name) then
1355        inc(i)
1356      else
1357        begin
1358        inc(j);
1359        Result := FieldName + IntToStr(j);
1360        i := 0;
1361        end;
1362    // Check if component with the same name exists.
1363    if Assigned(Owner) then
1364      While Owner.FindComponent(Result)<>Nil do
1365        begin
1366        Inc(J);
1367        Result := FieldName + IntToStr(j);
1368        end;
1369  end;
1370
1371var
1372  i: integer;
1373  OldName, OldFieldName: string;
1374
1375begin
1376  if Self.Name = Value then Exit;
1377  OldName := Self.Name;
1378  inherited SetName(Value);
1379  if (csDesigning in ComponentState) then
1380    for i := 0 to Fields.Count - 1 do begin
1381      OldFieldName := OldName + Fields[i].FieldName;
1382      if Copy(Fields[i].Name, 1, Length(OldFieldName)) = OldFieldName then
1383        Fields[i].Name := CheckName(Value + Fields[i].FieldName);
1384    end;
1385end;
1386
1387procedure TDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent);
1388
1389begin
1390  CheckBiDirectional;
1391  FOnFilterRecord := Value;
1392end;
1393
1394procedure TDataSet.SetRecNo(Value: Longint);
1395
1396begin
1397  //!! To be implemented
1398end;
1399
1400procedure TDataSet.SetState(Value: TDataSetState);
1401
1402begin
1403  If Value<>FState then
1404    begin
1405    FState:=Value;
1406    if Value=dsBrowse then
1407      FModified:=false;
1408    DataEvent(deUpdateState,0);
1409    end;
1410end;
1411
1412function TDataSet.TempBuffer: TRecordBuffer;
1413
1414begin
1415  Result := FBuffers[FRecordCount];
1416end;
1417
1418procedure TDataSet.UpdateIndexDefs;
1419
1420begin
1421  // Empty Abstract
1422end;
1423
1424function TDataSet.AllocRecordBuffer: TRecordBuffer;
1425begin
1426  Result := nil;
1427end;
1428
1429procedure TDataSet.FreeRecordBuffer(var Buffer: TRecordBuffer);
1430begin
1431  // empty stub
1432end;
1433
1434procedure TDataSet.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
1435begin
1436  // empty stub
1437end;
1438
1439function TDataSet.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
1440begin
1441  Result := bfCurrent;
1442end;
1443
1444function TDataSet.ControlsDisabled: Boolean;
1445
1446begin
1447  Result := (FDisableControlsCount > 0);
1448end;
1449
1450function TDataSet.ActiveBuffer: TRecordBuffer;
1451
1452begin
1453{$ifdef dsdebug}
1454  Writeln ('Active buffer requested. Returning record number:',ActiveRecord);
1455{$endif}
1456  Result:=FBuffers[FActiveRecord];
1457end;
1458
1459procedure TDataSet.Append;
1460
1461begin
1462  DoInsertAppend(True);
1463end;
1464
1465procedure TDataSet.InternalInsert;
1466
1467begin
1468  //!! To be implemented
1469end;
1470
1471procedure TDataSet.AppendRecord(const Values: array of const);
1472
1473begin
1474  DoInsertAppendRecord(Values,True);
1475end;
1476
1477function TDataSet.BookmarkValid(ABookmark: TBookmark): Boolean;
1478{
1479  Should be overridden by descendant objects.
1480}
1481begin
1482  Result:=False
1483end;
1484
1485procedure TDataSet.Cancel;
1486
1487begin
1488  If State in [dsEdit,dsInsert] then
1489    begin
1490    DataEvent(deCheckBrowseMode,0);
1491    DoBeforeCancel;
1492    UpdateCursorPos;
1493    InternalCancel;
1494    FreeFieldBuffers;
1495    if (State = dsInsert) and (FRecordCount = 1) then
1496      begin
1497      FEOF := true;
1498      FBOF := true;
1499      FRecordCount := 0;
1500      InitRecord(ActiveBuffer);
1501      SetState(dsBrowse);
1502      DataEvent(deDatasetChange,0);
1503      end
1504    else
1505      begin
1506      SetState(dsBrowse);
1507      SetCurrentRecord(FActiveRecord);
1508      resync([]);
1509      end;
1510    DoAfterCancel;
1511    end;
1512end;
1513
1514procedure TDataSet.CheckBrowseMode;
1515
1516begin
1517  CheckActive;
1518  DataEvent(deCheckBrowseMode,0);
1519  Case State of
1520    dsEdit,dsInsert: begin
1521      UpdateRecord;
1522      If Modified then Post else Cancel;
1523    end;
1524    dsSetKey: Post;
1525  end;
1526end;
1527
1528procedure TDataSet.ClearFields;
1529
1530
1531begin
1532  if not (State in dsEditModes) then
1533    DatabaseError(SNotEditing, Self);
1534  DataEvent(deCheckBrowseMode, 0);
1535  FreeFieldBuffers;
1536  InternalInitRecord(ActiveBuffer);
1537  if State <> dsSetKey then GetCalcFields(ActiveBuffer);
1538  DataEvent(deRecordChange, 0);
1539end;
1540
1541procedure TDataSet.Close;
1542
1543begin
1544  Active:=False;
1545end;
1546
1547function TDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
1548
1549begin
1550  Result:=0;
1551end;
1552
1553function TDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode
1554  ): TStream;
1555
1556
1557begin
1558  Result:=Nil;
1559end;
1560
1561procedure TDataSet.CursorPosChanged;
1562
1563
1564begin
1565  FCurrentRecord:=-1;
1566end;
1567
1568procedure TDataSet.Delete;
1569
1570begin
1571  If Not CanModify then
1572    DatabaseError(SDatasetReadOnly,Self);
1573  If IsEmpty then
1574    DatabaseError(SDatasetEmpty,Self);
1575  if State in [dsInsert] then
1576  begin
1577    Cancel;
1578  end else begin
1579    DataEvent(deCheckBrowseMode,0);
1580{$ifdef dsdebug}
1581    writeln ('Delete: checking required fields');
1582{$endif}
1583    DoBeforeDelete;
1584    DoBeforeScroll;
1585    If Not TryDoing(@InternalDelete,OnDeleteError) then exit;
1586{$ifdef dsdebug}
1587    writeln ('Delete: Internaldelete succeeded');
1588{$endif}
1589    FreeFieldBuffers;
1590    SetState(dsBrowse);
1591{$ifdef dsdebug}
1592    writeln ('Delete: Browse mode set');
1593{$endif}
1594    SetCurrentRecord(FActiveRecord);
1595    Resync([]);
1596    DoAfterDelete;
1597    DoAfterScroll;
1598  end;
1599end;
1600
1601procedure TDataSet.DisableControls;
1602
1603
1604begin
1605  If FDisableControlsCount=0 then
1606    begin
1607    { Save current state,
1608      needed to detect change of state when enabling controls.
1609    }
1610    FDisableControlsState:=FState;
1611    FEnableControlsEvent:=deDatasetChange;
1612    end;
1613  Inc(FDisableControlsCount);
1614end;
1615
1616procedure TDataSet.DoInsertAppend(DoAppend: Boolean);
1617
1618
1619  procedure DoInsert(DoAppend : Boolean);
1620
1621  Var BookBeforeInsert : TBookmark;
1622      TempBuf : pointer;
1623
1624  begin
1625  // need to scroll up al buffers after current one,
1626  // but copy current bookmark to insert buffer.
1627  If FRecordCount > 0 then
1628    BookBeforeInsert:=Bookmark;
1629
1630  if not DoAppend then
1631    begin
1632    if FRecordCount > 0 then
1633      begin
1634      TempBuf := FBuffers[FBufferCount];
1635      move(FBuffers[FActiveRecord],FBuffers[FActiveRecord+1],(FBufferCount-FActiveRecord)*sizeof(FBuffers[0]));
1636      FBuffers[FActiveRecord]:=TempBuf;
1637      end;
1638    end
1639  else if FRecordCount=FBufferCount then
1640    ShiftBuffersBackward
1641  else
1642    begin
1643    if FRecordCount>0 then
1644      inc(FActiveRecord);
1645    end;
1646
1647  // Active buffer is now edit buffer. Initialize.
1648  InitRecord(FBuffers[FActiveRecord]);
1649  cursorposchanged;
1650
1651  // Put bookmark in edit buffer.
1652  if FRecordCount=0 then
1653    SetBookmarkFlag(ActiveBuffer,bfEOF)
1654  else
1655    begin
1656    fBOF := false;
1657    // 29:01:05, JvdS: Why is this here?!? It can result in records with the same bookmark-data?
1658    // I would say that the 'internalinsert' should do this. But I don't know how Tdbf handles it
1659
1660    // 1-apr-06, JvdS: It just sets the bookmark of the newly inserted record to the place
1661    // where the record should be inserted. So it is ok.
1662    if FRecordCount > 0 then
1663      begin
1664      SetBookMarkData(ActiveBuffer,pointer(BookBeforeInsert));
1665      FreeBookmark(BookBeforeInsert);
1666      end;
1667    end;
1668
1669  InternalInsert;
1670
1671  // update buffer count.
1672  If FRecordCount<FBufferCount then
1673    Inc(FRecordCount);
1674  end;
1675
1676begin
1677  CheckBrowseMode;
1678  If Not CanModify then
1679    DatabaseError(SDatasetReadOnly,Self);
1680  DoBeforeInsert;
1681  DoBeforeScroll;
1682  If Not DoAppend then
1683    begin
1684{$ifdef dsdebug}
1685    Writeln ('going to insert mode');
1686{$endif}
1687    DoInsert(false);
1688    end
1689  else
1690    begin
1691{$ifdef dsdebug}
1692    Writeln ('going to append mode');
1693{$endif}
1694    ClearBuffers;
1695    InternalLast;
1696    GetPriorRecords;
1697    if FRecordCount>0 then
1698      FActiveRecord:=FRecordCount-1;
1699    DoInsert(True);
1700    SetBookmarkFlag(ActiveBuffer,bfEOF);
1701    FBOF :=False;
1702    FEOF := true;
1703    end;
1704  SetState(dsInsert);
1705  try
1706    DoOnNewRecord;
1707  except
1708    SetCurrentRecord(FActiveRecord);
1709    resync([]);
1710    raise;
1711  end;
1712  // mark as not modified.
1713  FModified:=False;
1714  // Final events.
1715  DataEvent(deDatasetChange,0);
1716  DoAfterInsert;
1717  DoAfterScroll;
1718{$ifdef dsdebug}
1719  Writeln ('Done with append');
1720{$endif}
1721end;
1722
1723procedure TDataSet.Edit;
1724
1725begin
1726  If State in [dsEdit,dsInsert] then exit;
1727  CheckBrowseMode;
1728  If Not CanModify then
1729    DatabaseError(SDatasetReadOnly,Self);
1730  If FRecordCount = 0 then
1731    begin
1732    Append;
1733    Exit;
1734    end;
1735  DoBeforeEdit;
1736  If Not TryDoing(@InternalEdit,OnEditError) then exit;
1737  GetCalcFields(ActiveBuffer);
1738  SetState(dsEdit);
1739  DataEvent(deRecordChange,0);
1740  DoAfterEdit;
1741end;
1742
1743procedure TDataSet.EnableControls;
1744
1745
1746begin
1747  if FDisableControlsCount > 0 then
1748    Dec(FDisableControlsCount);
1749
1750  if FDisableControlsCount = 0 then begin
1751    if FState <> FDisableControlsState then
1752      DataEvent(deUpdateState, 0);
1753
1754    if (FState <> dsInactive) and (FDisableControlsState <> dsInactive) then
1755      DataEvent(FEnableControlsEvent, 0);
1756  end;
1757end;
1758
1759function TDataSet.FieldByName(const FieldName: string): TField;
1760
1761
1762begin
1763  Result:=FindField(FieldName);
1764  If Result=Nil then
1765    DatabaseErrorFmt(SFieldNotFound,[FieldName],Self);
1766end;
1767
1768function TDataSet.FindField(const FieldName: string): TField;
1769
1770
1771begin
1772  Result:=FFieldList.FindField(FieldName);
1773end;
1774
1775function TDataSet.FindFirst: Boolean;
1776
1777
1778begin
1779  Result:=False;
1780end;
1781
1782function TDataSet.FindLast: Boolean;
1783
1784
1785begin
1786  Result:=False;
1787end;
1788
1789function TDataSet.FindNext: Boolean;
1790
1791
1792begin
1793  Result:=False;
1794end;
1795
1796function TDataSet.FindPrior: Boolean;
1797
1798
1799begin
1800  Result:=False;
1801end;
1802
1803procedure TDataSet.First;
1804
1805
1806begin
1807  CheckBrowseMode;
1808  DoBeforeScroll;
1809  if not FIsUniDirectional then
1810    ClearBuffers
1811  else if not FBof then
1812    begin
1813    Active := False;
1814    Active := True;
1815    end;
1816  try
1817    InternalFirst;
1818    if not FIsUniDirectional then GetNextRecords;
1819  finally
1820    FBOF:=True;
1821    DataEvent(deDatasetChange,0);
1822    DoAfterScroll;
1823    end;
1824end;
1825
1826procedure TDataSet.FreeBookmark(ABookmark: TBookmark);
1827
1828
1829begin
1830  {$ifdef noautomatedbookmark}
1831   FreeMem(ABookMark,FBookMarkSize);
1832  {$endif}
1833end;
1834
1835function TDataSet.GetBookmark: TBookmark;
1836
1837
1838begin
1839  if BookmarkAvailable then
1840    begin
1841    {$ifdef noautomatedbookmark}
1842      GetMem (Result,FBookMarkSize);
1843    {$else}
1844      setlength(Result,FBookMarkSize);
1845    {$endif}
1846    GetBookMarkdata(ActiveBuffer,pointer(Result));
1847    end
1848  else
1849    Result:=Nil;
1850end;
1851
1852function TDataSet.GetCurrentRecord(Buffer: TRecordBuffer): Boolean;
1853
1854
1855begin
1856  Result:=False;
1857end;
1858
1859procedure TDataSet.GetFieldList(List: TList; const FieldNames: string);
1860
1861var
1862  F: TField;
1863  N: String;
1864  StrPos: Integer;
1865
1866begin
1867  if (FieldNames = '') or (List = nil) then
1868    Exit;
1869  StrPos := 1;
1870  repeat
1871    N := ExtractFieldName(FieldNames, StrPos);
1872    F := FieldByName(N);
1873    List.Add(F);
1874  until StrPos > Length(FieldNames);
1875end;
1876
1877procedure TDataSet.GetFieldNames(List: TStrings);
1878
1879
1880begin
1881  FFieldList.GetFieldNames(List);
1882end;
1883
1884procedure TDataSet.GotoBookmark(const ABookmark: TBookmark);
1885
1886
1887begin
1888  If Assigned(ABookMark) then
1889    begin
1890    CheckBrowseMode;
1891    DoBeforeScroll;
1892    InternalGotoBookMark(pointer(ABookMark));
1893    Resync([rmExact,rmCenter]);
1894    DoAfterScroll;
1895    end;
1896end;
1897
1898procedure TDataSet.Insert;
1899
1900begin
1901  DoInsertAppend(False);
1902end;
1903
1904procedure TDataSet.InsertRecord(const Values: array of const);
1905
1906begin
1907  DoInsertAppendRecord(Values,False);
1908end;
1909
1910function TDataSet.IsEmpty: Boolean;
1911
1912begin
1913  Result:=(fBof and fEof) and
1914          (not (State = dsInsert)); // After an insert on an empty dataset, both fBof and fEof are true
1915end;
1916
1917function TDataSet.IsLinkedTo(ADataSource: TDataSource): Boolean;
1918
1919begin
1920//!! Not tested, I never used nested DS
1921  if (ADataSource = nil) or (ADataSource.Dataset = nil) then begin
1922    Result := False
1923  end else if ADataSource.Dataset = Self then begin
1924    Result := True;
1925  end else begin
1926    Result := ADataSource.Dataset.IsLinkedTo(ADataSource.Dataset.DataSource);
1927  end;
1928//!! DataSetField not implemented
1929end;
1930
1931function TDataSet.IsSequenced: Boolean;
1932
1933begin
1934  Result := True;
1935end;
1936
1937procedure TDataSet.Last;
1938
1939begin
1940  CheckBiDirectional;
1941  CheckBrowseMode;
1942  DoBeforeScroll;
1943  ClearBuffers;
1944  try
1945    InternalLast;
1946    GetPriorRecords;
1947    if FRecordCount>0 then
1948      FActiveRecord:=FRecordCount-1
1949  finally
1950    FEOF:=true;
1951    DataEvent(deDataSetChange, 0);
1952    DoAfterScroll;
1953    end;
1954end;
1955
1956function TDataSet.MoveBy(Distance: Longint): Longint;
1957Var
1958  TheResult: Integer;
1959
1960  Function ScrollForward : Integer;
1961  begin
1962    Result:=0;
1963{$ifdef dsdebug}
1964    Writeln('Scrolling forward : ',Distance);
1965    Writeln('Active buffer : ',FActiveRecord);
1966    Writeln('RecordCount   : ',FRecordCount);
1967    WriteLn('BufferCount   : ',FBufferCount);
1968{$endif}
1969    FBOF:=False;
1970    While (Distance>0) and not FEOF do
1971      begin
1972      If FActiveRecord<FRecordCount-1 then
1973        begin
1974        Inc(FActiveRecord);
1975        Dec(Distance);
1976        Inc(TheResult); //Inc(Result);
1977        end
1978      else
1979        begin
1980{$ifdef dsdebug}
1981       Writeln('Moveby : need next record');
1982{$endif}
1983        If GetNextRecord then
1984          begin
1985          Dec(Distance);
1986          Dec(Result);
1987          Inc(TheResult); //Inc(Result);
1988          end
1989        else
1990          FEOF:=true;
1991        end;
1992      end
1993  end;
1994
1995  Function ScrollBackward : Integer;
1996  begin
1997    CheckBiDirectional;
1998    Result:=0;
1999{$ifdef dsdebug}
2000    Writeln('Scrolling backward : ',Abs(Distance));
2001    Writeln('Active buffer : ',FActiveRecord);
2002    Writeln('RecordCunt    : ',FRecordCount);
2003    WriteLn('BufferCount   : ',FBufferCount);
2004{$endif}
2005    FEOF:=False;
2006    While (Distance<0) and not FBOF do
2007      begin
2008      If FActiveRecord>0 then
2009        begin
2010        Dec(FActiveRecord);
2011        Inc(Distance);
2012        Dec(TheResult); //Dec(Result);
2013        end
2014      else
2015        begin
2016       {$ifdef dsdebug}
2017       Writeln('Moveby : need next record');
2018       {$endif}
2019        If GetPriorRecord then
2020          begin
2021          Inc(Distance);
2022          Inc(Result);
2023          Dec(TheResult); //Dec(Result);
2024          end
2025        else
2026          FBOF:=true;
2027        end;
2028      end
2029  end;
2030
2031Var
2032  Scrolled : Integer;
2033
2034begin
2035  CheckBrowseMode;
2036  Result:=0; TheResult:=0;
2037  DoBeforeScroll;
2038  If (Distance = 0) or
2039     ((Distance>0) and FEOF) or
2040     ((Distance<0) and FBOF) then
2041    exit;
2042  Try
2043    Scrolled := 0;
2044    If Distance>0 then
2045      Scrolled:=ScrollForward
2046    else
2047      Scrolled:=ScrollBackward;
2048  finally
2049{$ifdef dsdebug}
2050    WriteLn('ActiveRecord=', FActiveRecord,' FEOF=',FEOF,' FBOF=',FBOF);
2051{$Endif}
2052    DataEvent(deDatasetScroll,Scrolled);
2053    DoAfterScroll;
2054    Result:=TheResult;
2055  end;
2056end;
2057
2058procedure TDataSet.Next;
2059
2060begin
2061  if BlockReadSize>0 then
2062    BlockReadNext
2063  else
2064    MoveBy(1);
2065end;
2066
2067procedure TDataSet.BlockReadNext;
2068begin
2069  MoveBy(1);
2070end;
2071
2072procedure TDataSet.Open;
2073
2074begin
2075  Active:=True;
2076end;
2077
2078procedure TDataSet.Post;
2079
2080begin
2081  UpdateRecord;
2082  if State in [dsEdit,dsInsert] then
2083    begin
2084    DataEvent(deCheckBrowseMode,0);
2085{$ifdef dsdebug}
2086    writeln ('Post: checking required fields');
2087{$endif}
2088    DoBeforePost;
2089    If Not TryDoing(@InternalPost,OnPostError) then exit;
2090    cursorposchanged;
2091{$ifdef dsdebug}
2092    writeln ('Post: Internalpost succeeded');
2093{$endif}
2094    FreeFieldBuffers;
2095// First set the state to dsBrowse, then the Resync, to prevent the calling of
2096// the deDatasetChange event, while the state is still 'editable', while the db isn't
2097    SetState(dsBrowse);
2098    Resync([]);
2099{$ifdef dsdebug}
2100    writeln ('Post: Browse mode set');
2101{$endif}
2102    DoAfterPost;
2103    end
2104  else if State<>dsSetKey then
2105    DatabaseErrorFmt(SNotEditing, [Name], Self);
2106end;
2107
2108procedure TDataSet.Prior;
2109
2110begin
2111  MoveBy(-1);
2112end;
2113
2114procedure TDataSet.Refresh;
2115
2116begin
2117  CheckbrowseMode;
2118  DoBeforeRefresh;
2119  UpdateCursorPos;
2120  InternalRefresh;
2121{ SetCurrentRecord is called by UpdateCursorPos already, so as long as
2122  InternalRefresh doesn't do strange things this should be ok. }
2123//  SetCurrentRecord(FActiveRecord);
2124  Resync([]);
2125  DoAfterRefresh;
2126end;
2127
2128procedure TDataSet.RegisterDataSource(ADataSource: TDataSource);
2129
2130begin
2131  FDataSources.Add(ADataSource);
2132  RecalcBufListSize;
2133end;
2134
2135
2136procedure TDataSet.Resync(Mode: TResyncMode);
2137
2138var i,count : integer;
2139
2140begin
2141  // See if we can find the requested record.
2142{$ifdef dsdebug}
2143    Writeln ('Resync called');
2144{$endif}
2145  if FIsUnidirectional then Exit;
2146// place the cursor of the underlying dataset to the active record
2147//  SetCurrentRecord(FActiveRecord);
2148
2149// Now look if the data on the current cursor of the underlying dataset is still available
2150  If GetRecord(FBuffers[0],gmCurrent,False)<>grOk Then
2151// If that fails and rmExact is set, then raise an exception
2152    If rmExact in Mode then
2153      DatabaseError(SNoSuchRecord,Self)
2154// else, if rmexact is not set, try to fetch the next  or prior record in the underlying dataset
2155    else if (GetRecord(FBuffers[0],gmNext,True)<>grOk) and
2156            (GetRecord(FBuffers[0],gmPrior,True)<>grOk) then
2157      begin
2158{$ifdef dsdebug}
2159      Writeln ('Resync: fuzzy resync');
2160{$endif}
2161      // nothing found, invalidate buffer and bail out.
2162      ClearBuffers;
2163      DataEvent(deDatasetChange,0);
2164      exit;
2165      end;
2166  FCurrentRecord := 0;
2167  FEOF := false;
2168  FBOF := false;
2169
2170// If we've arrived here, FBuffer[0] is the current record
2171  If (rmCenter in Mode) then
2172    count := (FRecordCount div 2)
2173  else
2174    count := FActiveRecord;
2175  i := 0;
2176  FRecordCount := 1;
2177  FActiveRecord := 0;
2178
2179// Fill the buffers before the active record
2180  while (i < count) and GetPriorRecord do
2181    inc(i);
2182  FActiveRecord := i;
2183// Fill the rest of the buffer
2184  GetNextRecords;
2185// If the buffer is not full yet, try to fetch some more prior records
2186  if FRecordCount < FBufferCount then inc(FActiveRecord,getpriorrecords);
2187// That's all folks!
2188  DataEvent(deDatasetChange,0);
2189end;
2190
2191procedure TDataSet.SetFields(const Values: array of const);
2192
2193Var I  : longint;
2194begin
2195  For I:=0 to high(Values) do
2196    Fields[I].AssignValue(Values[I]);
2197end;
2198
2199function TDataSet.Translate(Src, Dest: PChar; ToOem: Boolean): Integer;
2200
2201begin
2202  strcopy(dest,src);
2203  Result:=StrLen(dest);
2204end;
2205
2206function TDataSet.TryDoing(P: TDataOperation; Ev: TDatasetErrorEvent): Boolean;
2207
2208Var Retry : TDataAction;
2209
2210begin
2211{$ifdef dsdebug}
2212  Writeln ('Trying to do');
2213  If P=Nil then writeln ('Procedure to call is nil !!!');
2214{$endif dsdebug}
2215  Result:=True;
2216  Retry:=daRetry;
2217  while Retry=daRetry do
2218    Try
2219{$ifdef dsdebug}
2220      Writeln ('Trying : updatecursorpos');
2221{$endif dsdebug}
2222      UpdateCursorPos;
2223{$ifdef dsdebug}
2224      Writeln ('Trying to do it');
2225{$endif dsdebug}
2226      P;
2227      exit;
2228    except
2229      On E : EDatabaseError do
2230        begin
2231        retry:=daFail;
2232        If Assigned(Ev) then
2233          Ev(Self,E,Retry);
2234        Case Retry of
2235          daFail : Raise;
2236          daAbort : Abort;
2237        end;
2238        end;
2239    else
2240      Raise;
2241    end;
2242{$ifdef dsdebug}
2243  Writeln ('Exit Trying to do');
2244{$endif dsdebug}
2245end;
2246
2247procedure TDataSet.UpdateCursorPos;
2248
2249begin
2250  If FRecordCount>0 then
2251    SetCurrentRecord(FActiveRecord);
2252end;
2253
2254procedure TDataSet.UpdateRecord;
2255
2256begin
2257  if not (State in dsEditModes) then
2258    DatabaseErrorFmt(SNotEditing, [Name], Self);
2259  DataEvent(deUpdateRecord, 0);
2260end;
2261
2262function TDataSet.UpdateStatus: TUpdateStatus;
2263
2264begin
2265  Result:=usUnmodified;
2266end;
2267
2268procedure TDataSet.RemoveField(Field: TField);
2269
2270begin
2271  //!! To be implemented
2272end;
2273
2274procedure TDataSet.SetConstraints(Value: TCheckConstraints);
2275begin
2276  FConstraints.Assign(Value);
2277end;
2278
2279function TDataSet.GetfieldCount: Integer;
2280
2281begin
2282  Result:=FFieldList.Count;
2283end;
2284
2285procedure TDataSet.ShiftBuffersBackward;
2286
2287var TempBuf : pointer;
2288
2289begin
2290  TempBuf := FBuffers[0];
2291  move(FBuffers[1],FBuffers[0],(FBufferCount)*sizeof(FBuffers[0]));
2292  FBuffers[BufferCount]:=TempBuf;
2293end;
2294
2295procedure TDataSet.ShiftBuffersForward;
2296
2297var TempBuf : pointer;
2298
2299begin
2300  TempBuf := FBuffers[FBufferCount];
2301  move(FBuffers[0],FBuffers[1],(FBufferCount)*sizeof(FBuffers[0]));
2302  FBuffers[0]:=TempBuf;
2303end;
2304
2305function TDataSet.GetFieldValues(const FieldName: string): Variant;
2306
2307var i: Integer;
2308    FieldList: TList;
2309begin
2310  FieldList := TList.Create;
2311  try
2312    GetFieldList(FieldList, FieldName);
2313    if FieldList.Count>1 then begin
2314      Result := VarArrayCreate([0, FieldList.Count - 1], varVariant);
2315      for i := 0 to FieldList.Count - 1 do
2316        Result[i] := TField(FieldList[i]).Value;
2317    end else
2318      Result := FieldByName(FieldName).Value;
2319  finally
2320    FieldList.Free;
2321  end;
2322end;
2323
2324procedure TDataSet.SetFieldValues(const FieldName: string; Value: Variant);
2325
2326var
2327  i, l, h : Integer;
2328  FieldList: TList;
2329begin
2330  if VarIsArray(Value) then begin
2331    FieldList := TList.Create;
2332    try
2333      GetFieldList(FieldList, FieldName);
2334      l := VarArrayLowBound(Value, 1);
2335      h := VarArrayHighBound(Value, 1);
2336      if (FieldList.Count = 1) and (l < h) then
2337        // Allow for a field type that can deal with an array
2338        FieldByName(FieldName).Value := Value
2339      else
2340        for i := 0 to FieldList.Count - 1 do
2341          TField(FieldList[i]).Value := Value[l+i];
2342    finally
2343      FieldList.Free;
2344    end;
2345  end else
2346    FieldByName(FieldName).Value := Value;
2347end;
2348
2349function TDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
2350  Options: TLocateOptions): boolean;
2351
2352begin
2353  CheckBiDirectional;
2354  Result := False;
2355end;
2356
2357function TDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
2358  const ResultFields: string): Variant;
2359
2360begin
2361  CheckBiDirectional;
2362  Result := Null;
2363end;
2364
2365
2366procedure TDataSet.UnRegisterDataSource(ADataSource: TDataSource);
2367
2368begin
2369  FDataSources.Remove(ADataSource);
2370end;
2371
2372{------------------------------------------------------------------------------}
2373{ IProviderSupport methods}
2374
2375procedure TDataSet.PSEndTransaction(Commit: Boolean);
2376begin
2377  DatabaseError('Provider support not available', Self);
2378end;
2379
2380procedure TDataSet.PSExecute;
2381begin
2382  DatabaseError('Provider support not available', Self);
2383end;
2384
2385function TDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams;
2386  ResultSet: Pointer): Integer;
2387begin
2388  Result := 0;
2389  DatabaseError('Provider support not available', Self);
2390end;
2391
2392procedure TDataSet.PSGetAttributes(List: TList);
2393begin
2394  DatabaseError('Provider support not available', Self);
2395end;
2396
2397function TDataSet.PSGetCommandText: string;
2398begin
2399  Result := '';
2400  DatabaseError('Provider support not available', Self);
2401end;
2402
2403function TDataSet.PSGetCommandType: TPSCommandType;
2404begin
2405  Result := ctUnknown;
2406  DatabaseError('Provider support not available', Self);
2407end;
2408
2409function TDataSet.PSGetDefaultOrder: TIndexDef;
2410begin
2411  Result := nil;
2412  //DatabaseError('Provider support not available', Self);
2413end;
2414
2415function TDataSet.PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs;
2416begin
2417  Result := nil;
2418  DatabaseError('Provider support not available', Self);
2419end;
2420
2421function TDataSet.PSGetKeyFields: string;
2422begin
2423  Result := '';
2424  DatabaseError('Provider support not available', Self);
2425end;
2426
2427function TDataSet.PSGetParams: TParams;
2428begin
2429  Result := nil;
2430  DatabaseError('Provider support not available', Self);
2431end;
2432
2433function TDataSet.PSGetQuoteChar: string;
2434begin
2435  Result := '';
2436  DatabaseError('Provider support not available', Self);
2437end;
2438
2439function TDataSet.PSGetTableName: string;
2440begin
2441  Result := '';
2442  DatabaseError('Provider support not available', Self);
2443end;
2444
2445function TDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError
2446  ): EUpdateError;
2447begin
2448  if Prev <> nil then
2449    Result := EUpdateError.Create(E.Message, '', 0, Prev.ErrorCode, E)
2450  else
2451    Result := EUpdateError.Create(E.Message, '', 0, 0, E)
2452end;
2453
2454function TDataSet.PSInTransaction: Boolean;
2455begin
2456  Result := False;
2457  DatabaseError('Provider support not available', Self);
2458end;
2459
2460function TDataSet.PSIsSQLBased: Boolean;
2461begin
2462  Result := False;
2463  DatabaseError('Provider support not available', Self);
2464end;
2465
2466function TDataSet.PSIsSQLSupported: Boolean;
2467begin
2468  Result := False;
2469  DatabaseError('Provider support not available', Self);
2470end;
2471
2472procedure TDataSet.PSReset;
2473begin
2474  //DatabaseError('Provider support not available', Self);
2475end;
2476
2477procedure TDataSet.PSSetCommandText(const CommandText: string);
2478begin
2479  DatabaseError('Provider support not available', Self);
2480end;
2481
2482procedure TDataSet.PSSetParams(AParams: TParams);
2483begin
2484  DatabaseError('Provider support not available', Self);
2485end;
2486
2487procedure TDataSet.PSStartTransaction;
2488begin
2489  DatabaseError('Provider support not available', Self);
2490end;
2491
2492function TDataSet.PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet
2493  ): Boolean;
2494begin
2495  Result := False;
2496  DatabaseError('Provider support not available', Self);
2497end;
2498
2499{------------------------------------------------------------------------------}
2500
2501operator Enumerator(ADataSet: TDataSet): TDataSetEnumerator;
2502begin
2503 Result:=TDataSetEnumerator.Create(ADataSet);
2504end;
2505
2506constructor TDataSetEnumerator.Create(ADataSet: TDataSet);
2507begin
2508  inherited Create;
2509  FDataSet:=ADataSet;
2510  FBOF:=True;
2511  FDataSet.First;
2512end;
2513
2514function TDataSetEnumerator.GetCurrent: TFields;
2515begin
2516  Result := FDataSet.Fields;
2517end;
2518
2519function TDataSetEnumerator.MoveNext: Boolean;
2520
2521begin
2522  if FBOF then
2523    FBOF:=False
2524  else
2525    FDataSet.Next;
2526  Result:=not FDataSet.EOF;
2527end;
2528