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