ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBCustomDataSet.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBCustomDataSet.pas (file contents):
Revision 19 by tony, Mon Jul 7 13:00:15 2014 UTC vs.
Revision 23 by tony, Fri Mar 13 10:26:52 2015 UTC

# Line 27 | Line 27
27   {    IBX For Lazarus (Firebird Express)                                  }
28   {    Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29   {    Portions created by MWA Software are copyright McCallum Whyman      }
30 < {    Associates Ltd 2011                                                 }
30 > {    Associates Ltd 2011 - 2015                                                }
31   {                                                                        }
32   {************************************************************************}
33  
34   unit IBCustomDataSet;
35  
36 + {$R-}
37 +
38   {$Mode Delphi}
39  
40   {$IFDEF DELPHI}
# Line 49 | Line 51 | uses
51   {$ENDIF}
52    SysUtils, Classes, Forms, Controls, IBDatabase,
53    IBExternals, IB, IBHeader,  IBSQL, Db,
54 <  IBUtils, IBBlob;
54 >  IBUtils, IBBlob, IBSQLParser;
55  
56   const
57    BufferCacheSize    =  1000;  { Allocate cache in this many record chunks}
# Line 185 | Line 187 | type
187      property ApplyOnEvent: TIBGeneratorApplyOnEvent read FApplyOnEvent write FApplyOnEvent;
188    end;
189  
190 +  TIBAutoCommit = (acDisabled, acCommitRetaining);
191 +
192    { TIBCustomDataSet }
193    TIBUpdateAction = (uaFail, uaAbort, uaSkip, uaRetry, uaApply, uaApplied);
194  
# Line 198 | Line 202 | type
202  
203    TIBCustomDataSet = class(TDataset)
204    private
205 +    FAutoCommit: TIBAutoCommit;
206      FGenerateParamNames: Boolean;
207      FGeneratorField: TIBGenerator;
208      FNeedsRefresh: Boolean;
# Line 252 | Line 257 | type
257      FAfterTransactionEnd,
258      FTransactionFree: TNotifyEvent;
259      FAliasNameMap: array of string;
260 <
260 >    FAliasNameList: array of string;
261 >    FBaseSQLSelect: TStrings;
262 >    FParser: TSelectSQLParser;
263      function GetSelectStmtHandle: TISC_STMT_HANDLE;
264      procedure SetUpdateMode(const Value: TUpdateMode);
265      procedure SetUpdateObject(Value: TIBDataSetUpdateObject);
# Line 285 | Line 292 | type
292      function GetModifySQL: TStrings;
293      function GetTransaction: TIBTransaction;
294      function GetTRHandle: PISC_TR_HANDLE;
295 +    function GetParser: TSelectSQLParser;
296      procedure InternalDeleteRecord(Qry: TIBSQL; Buff: Pointer); virtual;
297      function InternalLocate(const KeyFields: string; const KeyValues: Variant;
298                              Options: TLocateOptions): Boolean; virtual;
# Line 323 | Line 331 | type
331      procedure DeactivateTransaction;
332      procedure CheckDatasetClosed;
333      procedure CheckDatasetOpen;
334 +    function CreateParser: TSelectSQLParser; virtual;
335      procedure FieldDefsFromQuery(SourceQuery: TIBSQL);
336      function GetActiveBuf: PChar;
337      procedure InternalBatchInput(InputObject: TIBBatchInput); virtual;
# Line 356 | Line 365 | type
365      procedure ClearCalcFields(Buffer: PChar); override;
366      function AllocRecordBuffer: PChar; override;
367      procedure DoBeforeDelete; override;
368 +    procedure DoAfterDelete; override;
369      procedure DoBeforeEdit; override;
370      procedure DoBeforeInsert; override;
371      procedure DoAfterInsert; override;
372 +    procedure DoBeforeOpen; override;
373      procedure DoBeforePost; override;
374 +    procedure DoAfterPost; override;
375      procedure FreeRecordBuffer(var Buffer: PChar); override;
376      procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
377      function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
# Line 373 | Line 385 | type
385                         DoCheck: Boolean): TGetResult; override;
386      function GetRecordCount: Integer; override;
387      function GetRecordSize: Word; override;
388 +    procedure InternalAutoCommit;
389      procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
390      procedure InternalCancel; override;
391      procedure InternalClose; override;
# Line 390 | Line 403 | type
403      procedure InternalSetFieldData(Field: TField; Buffer: Pointer); virtual;
404      procedure InternalSetToRecord(Buffer: PChar); override;
405      function IsCursorOpen: Boolean; override;
406 +    procedure Loaded; override;
407      procedure ReQuery;
408      procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
409      procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
# Line 403 | Line 417 | type
417  
418    protected
419      {Likely to be made public by descendant classes}
420 +    property AutoCommit: TIBAutoCommit read FAutoCommit write FAutoCommit default acDisabled;
421      property SQLParams: TIBXSQLDA read GetSQLParams;
422      property Params: TIBXSQLDA read GetSQLParams;
423      property InternalPrepared: Boolean read FInternalPrepared;
# Line 426 | Line 441 | type
441      property ModifySQL: TStrings read GetModifySQL write SetModifySQL;
442      property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll;
443      property ParamCheck: Boolean read FParamCheck write FParamCheck default True;
444 +    property Parser: TSelectSQLParser read GetParser;
445 +    property BaseSQLSelect: TStrings read FBaseSQLSelect;
446  
447      property BeforeDatabaseDisconnect: TNotifyEvent read FBeforeDatabaseDisconnect
448                                                   write FBeforeDatabaseDisconnect;
# Line 446 | Line 463 | type
463      procedure ApplyUpdates;
464      function CachedUpdateStatus: TCachedUpdateStatus;
465      procedure CancelUpdates;
466 +    function GetFieldPosition(AliasName: string): integer;
467      procedure FetchAll;
468      function LocateNext(const KeyFields: string; const KeyValues: Variant;
469                          Options: TLocateOptions): Boolean;
470      procedure RecordModified(Value: Boolean);
471      procedure RevertRecord;
472      procedure Undelete;
473 +    procedure ResetParser;
474 +    function HasParser: boolean;
475  
476      { TDataSet support methods }
477      function BookmarkValid(Bookmark: TBookmark): Boolean; override;
# Line 514 | Line 534 | type
534                                                     write FOnUpdateRecord;
535    end;
536  
537 <  TIBDataSet = class(TIBCustomDataSet)
537 >  TIBParserDataSet = class(TIBCustomDataSet)
538 >  public
539 >    property Parser;
540 >  end;
541 >
542 >  TIBDataSet = class(TIBParserDataSet)
543    private
544      function GetPrepared: Boolean;
545  
# Line 539 | Line 564 | type
564      property QModify;
565      property StatementType;
566      property SelectStmtHandle;
567 +    property BaseSQLSelect;
568  
569    published
570      { TIBCustomDataSet }
571 +    property AutoCommit;
572      property BufferChunks;
573      property CachedUpdates;
574      property DeleteSQL;
# Line 869 | Line 896 | begin
896    FParamCheck := True;
897    FGenerateParamNames := False;
898    FForcedRefresh := False;
899 +  FAutoCommit:= acDisabled;
900    {Bookmark Size is Integer for IBX}
901    BookmarkSize := SizeOf(Integer);
902    FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
# Line 882 | Line 910 | begin
910    else
911      if AOwner is TIBTransaction then
912        Transaction := TIBTransaction(AOwner);
913 +  FBaseSQLSelect := TStringList.Create;
914   end;
915  
916   destructor TIBCustomDataSet.Destroy;
# Line 902 | Line 931 | begin
931      FOldCacheSize := 0;
932      FMappedFieldPosition := nil;
933    end;
934 +  if assigned(FBaseSQLSelect) then FBaseSQLSelect.Free;
935 +  if assigned(FParser) then FParser.Free;
936    inherited Destroy;
937   end;
938  
# Line 1145 | Line 1176 | begin
1176    end;
1177   end;
1178  
1179 + function TIBCustomDataSet.GetFieldPosition(AliasName: string): integer;
1180 + var i: integer;
1181 +    Prepared: boolean;
1182 + begin
1183 +  Result := 0;
1184 +  Prepared := FInternalPrepared;
1185 +  if not Prepared then
1186 +    InternalPrepare;
1187 +  try
1188 +    for i := 0 to Length(FAliasNameList) - 1 do
1189 +      if FAliasNameList[i] = AliasName then
1190 +      begin
1191 +        Result := i + 1;
1192 +        Exit
1193 +      end;
1194 +  finally
1195 +    if not Prepared then
1196 +      InternalUnPrepare;
1197 +  end;
1198 + end;
1199 +
1200   procedure TIBCustomDataSet.ActivateConnection;
1201   begin
1202    if not Assigned(Database) then
# Line 1205 | Line 1257 | begin
1257      IBError(ibxeDatasetClosed, [nil]);
1258   end;
1259  
1260 + function TIBCustomDataSet.CreateParser: TSelectSQLParser;
1261 + begin
1262 +  Result := TSelectSQLParser.Create(self,FBaseSQLSelect);
1263 +  Result.OnSQLChanging := SQLChanging
1264 + end;
1265 +
1266   procedure TIBCustomDataSet.CheckNotUniDirectional;
1267   begin
1268    if UniDirectional then
# Line 1349 | Line 1407 | var
1407    LocalData: Pointer;
1408    LocalDate, LocalDouble: Double;
1409    LocalInt: Integer;
1410 +  LocalBool: wordBool;
1411    LocalInt64: Int64;
1412    LocalCurrency: Currency;
1413    FieldsLoaded: Integer;
# Line 1493 | Line 1552 | begin
1552              end;
1553            end;
1554          end;
1555 +        SQL_BOOLEAN:
1556 +        begin
1557 +          LocalBool:= false;
1558 +          rdFields[j].fdDataSize := SizeOf(wordBool);
1559 +          if RecordNumber >= 0 then
1560 +            LocalBool := Qry.Current[i].AsBoolean;
1561 +          LocalData := PChar(@LocalBool);
1562 +        end;
1563          else { SQL_TEXT, SQL_BLOB, SQL_ARRAY, SQL_QUAD }
1564          begin
1565            rdFields[j].fdDataSize := Qry.Current[i].Data^.sqllen;
# Line 1768 | Line 1835 | var
1835    ofs: DWORD;
1836    Qry: TIBSQL;
1837   begin
1838 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
1838 >  if Assigned(Database) and not Database.SQLHourGlass then
1839 >     SetCursor := False
1840 >  else
1841 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
1842    if SetCursor then
1843      Screen.Cursor := crHourGlass;
1844    try
# Line 1892 | Line 1962 | begin
1962    if FInternalPrepared then
1963      Exit;
1964    DidActivate := False;
1965 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
1965 >  if Assigned(Database) and not Database.SQLHourGlass then
1966 >    SetCursor := False
1967 >  else
1968 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
1969    if SetCursor then
1970      Screen.Cursor := crHourGlass;
1971    try
# Line 1900 | Line 1973 | begin
1973      DidActivate := ActivateTransaction;
1974      FBase.CheckDatabase;
1975      FBase.CheckTransaction;
1976 +    if HasParser and (FParser.SQLText <> FQSelect.SQL.Text) then
1977 +      FQSelect.SQL.Text := FParser.SQLText;
1978 + //   writeln( FQSelect.SQL.Text);
1979      if FQSelect.SQL.Text <> '' then
1980      begin
1981        if not FQSelect.Prepared then
# Line 2119 | Line 2195 | begin
2195              SQL_TIMESTAMP:
2196                Qry.Params[i].AsDateTime :=
2197                         TimeStampToDateTime(MSecsToTimeStamp(trunc(PDouble(data)^)));
2198 +            SQL_BOOLEAN:
2199 +              Qry.Params[i].AsBoolean := PWordBool(data)^;
2200            end;
2201          end;
2202        end;
# Line 2144 | Line 2222 | begin
2222    begin
2223      Disconnect;
2224      FQSelect.SQL.Assign(Value);
2225 +    FBaseSQLSelect.assign(Value);
2226    end;
2227   end;
2228  
# Line 2207 | Line 2286 | end;
2286  
2287   procedure TIBCustomDataSet.SQLChanging(Sender: TObject);
2288   begin
2289 <  if FOpen then
2290 <    InternalClose;
2289 >  Active := false;
2290 > {  if FOpen then
2291 >    InternalClose;}
2292    if FInternalPrepared then
2293      InternalUnPrepare;
2294    FieldDefs.Clear;
2295 <  FieldDefs.Updated := false
2295 >  FieldDefs.Updated := false;
2296   end;
2297  
2298   { I can "undelete" uninserted records (make them "inserted" again).
# Line 2470 | Line 2550 | begin
2550    inherited DoBeforeDelete;
2551   end;
2552  
2553 + procedure TIBCustomDataSet.DoAfterDelete;
2554 + begin
2555 +  inherited DoAfterDelete;
2556 +  InternalAutoCommit;
2557 + end;
2558 +
2559   procedure TIBCustomDataSet.DoBeforeEdit;
2560   var
2561    Buff: PRecordData;
# Line 2498 | Line 2584 | begin
2584    inherited DoAfterInsert;
2585   end;
2586  
2587 + procedure TIBCustomDataSet.DoBeforeOpen;
2588 + begin
2589 +  if assigned(FParser) then
2590 +     FParser.Reset;
2591 +  DataEvent(deCheckBrowseMode,1); {Conventional use to report getting ready to prepare}
2592 +  inherited DoBeforeOpen;
2593 +  DataEvent(deCheckBrowseMode,2); {Conventional use to report the right time to set parameters}
2594 + end;
2595 +
2596   procedure TIBCustomDataSet.DoBeforePost;
2597   begin
2598    inherited DoBeforePost;
# Line 2506 | Line 2601 | begin
2601       GeneratorField.Apply
2602   end;
2603  
2604 + procedure TIBCustomDataSet.DoAfterPost;
2605 + begin
2606 +  inherited DoAfterPost;
2607 +  InternalAutoCommit;
2608 + end;
2609 +
2610   procedure TIBCustomDataSet.FetchAll;
2611   var
2612    SetCursor: Boolean;
# Line 2515 | Line 2616 | var
2616    CurBookmark: string;
2617    {$ENDIF}
2618   begin
2619 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
2619 >  if Assigned(Database) and not Database.SQLHourGlass then
2620 >    SetCursor := False
2621 >  else
2622 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
2623    if SetCursor then
2624      Screen.Cursor := crHourGlass;
2625    try
# Line 2780 | Line 2884 | begin
2884    result := FRecordBufferSize;
2885   end;
2886  
2887 + procedure TIBCustomDataSet.InternalAutoCommit;
2888 + begin
2889 +  with Transaction do
2890 +    if InTransaction and (FAutoCommit = acCommitRetaining) then
2891 +    begin
2892 +      if CachedUpdates then ApplyUpdates;
2893 +      CommitRetaining;
2894 +    end;
2895 + end;
2896 +
2897   procedure TIBCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
2898   begin
2899    CheckEditState;
# Line 2859 | Line 2973 | var
2973    Buff: PChar;
2974    SetCursor: Boolean;
2975   begin
2976 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
2976 >  if Assigned(Database) and not Database.SQLHourGlass then
2977 >    SetCursor := False
2978 >  else
2979 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
2980    if SetCursor then
2981      Screen.Cursor := crHourGlass;
2982    try
# Line 3045 | Line 3162 | begin
3162      Query.SQL.Text := DefaultSQL;
3163      Query.Prepare;
3164      SetLength(FAliasNameMap, SourceQuery.Current.Count);
3165 +    SetLength(FAliasNameList, SourceQuery.Current.Count);
3166      for i := 0 to SourceQuery.Current.Count - 1 do
3167        with SourceQuery.Current[i].Data^ do
3168        begin
# Line 3053 | Line 3171 | begin
3171          SetString(DBAliasName, aliasname, aliasname_length);
3172          SetString(RelationName, relname, relname_length);
3173          SetString(FieldName, sqlname, sqlname_length);
3174 +        FAliasNameList[i] := DBAliasName;
3175          FieldSize := 0;
3176          FieldPrecision := 0;
3177          FieldNullable := SourceQuery.Current[i].IsNullable;
# Line 3130 | Line 3249 | begin
3249              FieldSize := sizeof (TISC_QUAD);
3250              FieldType := ftUnknown;
3251            end;
3252 +          SQL_BOOLEAN:
3253 +             FieldType:= ftBoolean;
3254            else
3255              FieldType := ftUnknown;
3256          end;
# Line 3226 | Line 3347 | begin
3347          else case cur_field.DataType of
3348            ftString:
3349              cur_param.AsString := cur_field.AsString;
3350 <          ftBoolean, ftSmallint, ftWord:
3350 >          ftBoolean:
3351 >            cur_param.AsBoolean := cur_field.AsBoolean;
3352 >          ftSmallint, ftWord:
3353              cur_param.AsShort := cur_field.AsInteger;
3354            ftInteger:
3355              cur_param.AsLong := cur_field.AsInteger;
# Line 3288 | Line 3411 | var
3411    end;
3412  
3413   begin
3414 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3414 >  if Assigned(Database) and not Database.SQLHourGlass then
3415 >    SetCursor := False
3416 >  else
3417 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3418    if SetCursor then
3419      Screen.Cursor := crHourGlass;
3420    try
# Line 3363 | Line 3489 | var
3489    SetCursor: Boolean;
3490    bInserting: Boolean;
3491   begin
3492 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3492 >  if Assigned(Database) and not Database.SQLHourGlass then
3493 >    SetCursor := False
3494 >  else
3495 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3496    if SetCursor then
3497      Screen.Cursor := crHourGlass;
3498    try
# Line 3424 | Line 3553 | begin
3553    result := FOpen;
3554   end;
3555  
3556 + procedure TIBCustomDataSet.Loaded;
3557 + begin
3558 +  if assigned(FQSelect) then
3559 +    FBaseSQLSelect.assign(FQSelect.SQL);
3560 +  inherited Loaded;
3561 + end;
3562 +
3563   function TIBCustomDataSet.Locate(const KeyFields: string; const KeyValues: Variant;
3564                                   Options: TLocateOptions): Boolean;
3565   var
# Line 3506 | Line 3642 | end;
3642   procedure TIBCustomDataSet.InternalSetFieldData(Field: TField; Buffer: Pointer);
3643   var
3644    Buff, TmpBuff: PChar;
3645 +  MappedFieldPos: integer;
3646   begin
3647    Buff := GetActiveBuf;
3648    if Field.FieldNo < 0 then
# Line 3522 | Line 3659 | begin
3659      begin
3660        { If inserting, Adjust record position }
3661        AdjustRecordOnInsert(Buff);
3662 <      if (FMappedFieldPosition[Field.FieldNo - 1] > 0) and
3663 <         (FMappedFieldPosition[Field.FieldNo - 1] <= rdFieldCount) then
3662 >      MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
3663 >      if (MappedFieldPos > 0) and
3664 >         (MappedFieldPos <= rdFieldCount) then
3665        begin
3666          Field.Validate(Buffer);
3667          if (Buffer = nil) or
3668             (Field is TIBStringField) and (PChar(Buffer)[0] = #0) then
3669 <          rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdIsNull := True
3669 >          rdFields[MappedFieldPos].fdIsNull := True
3670          else begin
3671 <          Move(Buffer^, Buff[rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdDataOfs],
3672 <                 rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdDataSize);
3673 <          if (rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdDataType = SQL_TEXT) or
3674 <             (rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdDataType = SQL_VARYING) then
3675 <            rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdDataLength := StrLen(PChar(Buffer));
3676 <          rdFields[FMappedFieldPosition[Field.FieldNo - 1]].fdIsNull := False;
3671 >          Move(Buffer^, Buff[rdFields[MappedFieldPos].fdDataOfs],
3672 >                 rdFields[MappedFieldPos].fdDataSize);
3673 >          if (rdFields[MappedFieldPos].fdDataType = SQL_TEXT) or
3674 >             (rdFields[MappedFieldPos].fdDataType = SQL_VARYING) then
3675 >            rdFields[MappedFieldPos].fdDataLength := StrLen(PChar(Buffer));
3676 >          rdFields[MappedFieldPos].fdIsNull := False;
3677            if rdUpdateStatus = usUnmodified then
3678            begin
3679              if CachedUpdates then
# Line 3632 | Line 3770 | begin
3770      FieldDefs.Clear;
3771      FieldDefs.Updated := false;
3772      FInternalPrepared := False;
3773 +    Setlength(FAliasNameList,0);
3774    end;
3775   end;
3776  
# Line 3641 | Line 3780 | var
3780    SetCursor: Boolean;
3781   begin
3782    DidActivate := False;
3783 <  SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3783 >  if Assigned(Database) and not Database.SQLHourGlass then
3784 >    SetCursor := False
3785 >  else
3786 >    SetCursor := (GetCurrentThreadID = MainThreadID) and (Screen.Cursor = crDefault);
3787    if SetCursor then
3788      Screen.Cursor := crHourGlass;
3789    try
# Line 3670 | Line 3812 | begin
3812    Result := FQSelect.Handle;
3813   end;
3814  
3815 + function TIBCustomDataSet.GetParser: TSelectSQLParser;
3816 + begin
3817 +  if not assigned(FParser) then
3818 +    FParser := CreateParser;
3819 +  Result := FParser
3820 + end;
3821 +
3822 + procedure TIBCustomDataSet.ResetParser;
3823 + begin
3824 +  if assigned(FParser) then
3825 +  begin
3826 +    FParser.Free;
3827 +    FParser := nil;
3828 +    SQLChanging(nil)
3829 +  end;
3830 + end;
3831 +
3832 + function TIBCustomDataSet.HasParser: boolean;
3833 + begin
3834 +  Result := not (csDesigning in ComponentState) and (FParser <> nil)
3835 + end;
3836 +
3837   procedure TIBCustomDataSet.SetGenerateParamNames(AValue: Boolean);
3838   begin
3839    if FGenerateParamNames = AValue then Exit;
# Line 4084 | Line 4248 | begin
4248      Owner.FieldByName(FFieldName).AsInteger := GetNextValue(Owner.Database,Owner.Transaction);
4249   end;
4250  
4251 < end.
4251 > end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines