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

Comparing:
ibx/trunk/runtime/nongui/IBCustomDataSet.pas (file contents), Revision 311 by tony, Mon Aug 24 09:32:58 2020 UTC vs.
ibx/branches/journaling/runtime/nongui/IBCustomDataSet.pas (file contents), Revision 363 by tony, Tue Dec 7 13:30:05 2021 UTC

# Line 53 | Line 53 | uses
53   {$IFDEF UNIX}
54    unix,
55   {$ENDIF}
56 <  SysUtils, Classes, IBDatabase, IBExternals, IB,  IBSQL, Db,
57 <  IBUtils, IBBlob, IBSQLParser, IBDatabaseInfo, IBTypes;
56 >  SysUtils, Classes, IBDatabase, IBExternals, IBInternals, IB,  IBSQL, Db,
57 >  IBUtils, IBBlob, IBSQLParser, IBDatabaseInfo;
58  
59   type
60    TIBCustomDataSet = class;
# Line 88 | Line 88 | type
88  
89    { TIBArray }
90  
91 <  {Wrapper class to support array cache in TIBCustomDataset and event handling}
91 >  {Wrapper class to support array cache in TIBCustomDataSet and event handling}
92  
93    TIBArray = class
94    private
# Line 243 | Line 243 | type
243       property CodePage: TSystemCodePage read FFCodePage write FFCodePage;
244     end;
245  
246 +   PIBBufferedDateTimeWithTimeZone = ^TIBBufferedDateTimeWithTimeZone;
247 +   TIBBufferedDateTimeWithTimeZone = packed record
248 +     Timestamp: TDateTime;
249 +     dstOffset: smallint;
250 +     TimeZoneID: ISC_USHORT;
251 +   end;
252 +
253 +   { TIBDateTimeField }
254 +
255 +   {It seems wrong to make this a subclass of TTimeField and not TDateTimField.
256 +    However, the rationale is backwards compatibility for applications that
257 +    may want to coerce a TField to a TTimeField. If this is to work then
258 +    TIBTimeField has to descend from TTimeField. Hence the declation. As
259 +    TTimeField also descends from TDateTimeField this should not result in any
260 +    problems - unless someone makes a drastic change to TTimeField.}
261 +
262 +   TIBDateTimeField = class(TTimeField)
263 +   private
264 +     FHasTimeZone: boolean;
265 +     FTimeZoneServices: ITimeZoneServices;
266 +     function GetTimeZoneServices: ITimeZoneServices;
267 +     function GetDateTimeBuffer(var aBuffer: TIBBufferedDateTimeWithTimeZone): boolean;
268 +     function GetTimeZoneID: TFBTimeZoneID;
269 +     function GetTimeZoneName: string;
270 +     procedure SetTimeZoneID(aValue: TFBTimeZoneID);
271 +     procedure SetTimeZoneName(AValue: string);
272 +   protected
273 +     procedure Bind(Binding: Boolean); override;
274 +     function GetAsDateTime: TDateTime; override;
275 +     function GetAsVariant: variant; override;
276 +     function GetDataSize: Integer; override;
277 +     procedure GetText(var theText: string; ADisplayText: Boolean); override;
278 +     procedure SetAsDateTime(AValue: TDateTime); override;
279 +     procedure SetAsString(const AValue: string); override;
280 +     procedure SetVarValue(const AValue: Variant); override;
281 +   public
282 +     constructor Create(AOwner: TComponent); override;
283 +     function GetAsDateTimeTZ(var aDateTime: TDateTime; var dstOffset: smallint;
284 +                              var aTimeZoneID: TFBTimeZoneID): boolean; overload;
285 +     function GetAsDateTimeTZ(var aDateTime: TDateTime; var dstOffset: smallint;
286 +                              var aTimeZone: string): boolean; overload;
287 +     function GetAsUTCDateTime: TDateTime;
288 +     procedure SetAsDateTimeTZ(aDateTime: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
289 +     procedure SetAsDateTimeTZ(aDateTime: TDateTime; aTimeZone: string); overload;
290 +     property TimeZoneName: string read GetTimeZoneName write SetTimeZoneName;
291 +     property TimeZoneID: TFBTimeZoneID read GetTimeZoneID;
292 +   published
293 +     property HasTimeZone: boolean read FHasTimeZone;
294 +   end;
295 +
296 +   { TIBTimeField }
297 +
298 +   TIBTimeField = class(TIBDateTimeField)
299 +   public
300 +     constructor Create(AOwner: TComponent); override;
301 +   end;
302 +
303    { TIBDataLink }
304  
305    TIBDataLink = class(TDetailDataLink)
306    private
307      FDataSet: TIBCustomDataSet;
308      FDelayTimerValue: integer;
309 <    FTimer: TIBTimerInf;
309 >    FTimer: IIBTimerInf;
310      procedure HandleRefreshTimer(Sender: TObject);
311      procedure SetDelayTimerValue(AValue: integer);
312    protected
# Line 400 | Line 457 | type
457      FArrayCacheOffset: integer;
458      FAutoCommit: TIBAutoCommit;
459      FCaseSensitiveParameterNames: boolean;
460 +    FDefaultTZDate: TDateTime;
461      FEnableStatistics: boolean;
462      FGenerateParamNames: Boolean;
463      FGeneratorField: TIBGenerator;
# Line 441 | Line 499 | type
499      FRecordCount: Integer;
500      FRecordSize: Integer;
501      FDataSetCloseAction: TDataSetCloseAction;
502 +    FTZTextOption: TTZTextOptions;
503      FSQLFiltered: boolean;
504      FSQLFilterParams: TStrings;
505      FUniDirectional: Boolean;
# Line 478 | Line 537 | type
537      procedure InitModelBuffer(Qry: TIBSQL; Buffer: PChar);
538      function GetSelectStmtIntf: IStatement;
539      procedure SetCaseSensitiveParameterNames(AValue: boolean);
540 +    procedure SetDefaultTZDate(AValue: TDateTime);
541      procedure SetSQLFiltered(AValue: boolean);
542      procedure SetSQLFilterParams(AValue: TStrings);
543      procedure SetUpdateMode(const Value: TUpdateMode);
# Line 633 | Line 693 | type
693      function IsCursorOpen: Boolean; override;
694      procedure Loaded; override;
695      procedure ReQuery;
696 +    procedure ResetBufferCache;
697      procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
698      procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
699      procedure SetCachedUpdates(Value: Boolean);
# Line 656 | Line 717 | type
717      property QModify: TIBSQL read FQModify;
718      property StatementType: TIBSQLStatementTypes read GetStatementType;
719      property SelectStmtHandle: IStatement read GetSelectStmtIntf;
720 +    property Parser: TSelectSQLParser read GetParser;
721 +    property BaseSQLSelect: TStrings read FBaseSQLSelect;
722  
723      {Likely to be made published by descendant classes}
724      property CaseSensitiveParameterNames: boolean read FCaseSensitiveParameterNames
# Line 671 | Line 734 | type
734      property ModifySQL: TStrings read GetModifySQL write SetModifySQL;
735      property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll;
736      property ParamCheck: Boolean read FParamCheck write FParamCheck default True;
737 <    property Parser: TSelectSQLParser read GetParser;
675 <    property BaseSQLSelect: TStrings read FBaseSQLSelect;
737 >    property TZTextOption: TTZTextOptions read FTZTextOption write FTZTextOption;
738      property SQLFiltered: boolean read FSQLFiltered write SetSQLFiltered;
739      property SQLFilterParams: TStrings read FSQLFilterParams write SetSQLFilterParams;
740  
# Line 736 | Line 798 | type
798      property MasterDetailDelay: integer read GetMasterDetailDelay write SetMasterDetailDelay;
799      property DataSetCloseAction: TDataSetCloseAction
800                 read FDataSetCloseAction write FDataSetCloseAction;
801 +    property DefaultTZDate: TDateTime read FDefaultTZDate write SetDefaultTZDate;
802  
803    public
804      {Performance Statistics}
# Line 839 | Line 902 | type
902      property UniDirectional;
903      property Filtered;
904      property DataSetCloseAction;
905 +    property TZTextOption;
906 +    property DefaultTZDate;
907      property SQLFiltered;
908      property SQLFilterParams;
909  
# Line 909 | Line 974 | type
974      FCharacterSetName: RawByteString;
975      FCharacterSetSize: integer;
976      FCodePage: TSystemCodePage;
977 +    FHasTimeZone: boolean;
978      FIdentityColumn: boolean;
979      FRelationName: string;
980      FDataSize: integer;
# Line 921 | Line 987 | type
987      property ArrayDimensions: integer read FArrayDimensions write FArrayDimensions;
988      property ArrayBounds: TArrayBounds read FArrayBounds write FArrayBounds;
989      property IdentityColumn: boolean read FIdentityColumn write FIdentityColumn default false;
990 +    property HasTimeZone: boolean read FHasTimeZone write FHasTimeZone default false;
991    end;
992  
993   const
# Line 928 | Line 995 | const
995      nil,                { ftUnknown }
996      TIBStringField,     { ftString }
997      TIBSmallintField,   { ftSmallint }
998 <    TIBIntegerField,      { ftInteger }
998 >    TIBIntegerField,    { ftInteger }
999      TWordField,         { ftWord }
1000      TBooleanField,      { ftBoolean }
1001      TFloatField,        { ftFloat }
1002      TCurrencyField,     { ftCurrency }
1003      TIBBCDField,        { ftBCD }
1004      TDateField,         { ftDate }
1005 <    TTimeField,         { ftTime }
1006 <    TDateTimeField,     { ftDateTime }
1005 >    TIBTimeField,       { ftTime }
1006 >    TIBDateTimeField,   { ftDateTime }
1007      TBytesField,        { ftBytes }
1008      TVarBytesField,     { ftVarBytes }
1009      TAutoIncField,      { ftAutoInc }
# Line 949 | Line 1016 | const
1016      TBlobField,         { ftTypedBinary }
1017      nil,                { ftCursor }
1018      TStringField,       { ftFixedChar }
1019 <    nil,    { ftWideString }
1020 <    TIBLargeIntField,     { ftLargeInt }
1021 <    nil,          { ftADT }
1022 <    TIBArrayField,        { ftArray }
1023 <    nil,    { ftReference }
1024 <    nil,     { ftDataSet }
958 <    TBlobField,         { ftOraBlob }
959 <    TMemoField,         { ftOraClob }
960 <    TVariantField,      { ftVariant }
961 <    nil,    { ftInterface }
962 <    nil,     { ftIDispatch }
963 <    TGuidField,        { ftGuid }
964 <    TDateTimeField,    {ftTimestamp}
965 <    TIBBCDField,       {ftFMTBcd}
966 <    nil,  {ftFixedWideChar}
967 <    nil);   {ftWideMemo}
968 < (*
969 <    TADTField,          { ftADT }
970 <    TArrayField,        { ftArray }
971 <    TReferenceField,    { ftReference }
972 <    TDataSetField,     { ftDataSet }
1019 >    nil,                { ftWideString }
1020 >    TIBLargeIntField,   { ftLargeInt }
1021 >    nil,                { ftADT }
1022 >    TIBArrayField,      { ftArray }
1023 >    nil,                { ftReference }
1024 >    nil,                { ftDataSet }
1025      TBlobField,         { ftOraBlob }
1026      TMemoField,         { ftOraClob }
1027      TVariantField,      { ftVariant }
1028 <    TInterfaceField,    { ftInterface }
1029 <    TIDispatchField,     { ftIDispatch }
1030 <    TGuidField);        { ftGuid } *)
1028 >    nil,                { ftInterface }
1029 >    nil,                { ftIDispatch }
1030 >    TGuidField,         { ftGuid }
1031 >    TIBDateTimeField,   { ftTimestamp }
1032 >    TFmtBCDField,       { ftFMTBcd }
1033 >    nil,                { ftFixedWideChar }
1034 >    nil                 { ftWideMemo }
1035 > {$IF declared(ftOraTimeStamp)}
1036 >    {These six extra elements were added to the FPC fixes_3_2 branch in Q3 2021}
1037 >    ,
1038 >    nil,                {ftOraTimeStamp}
1039 >    nil,                {ftOraInterval}
1040 >    nil,                {ftLongWord}
1041 >    nil,                {ftShortint}
1042 >    nil,                {ftByte}
1043 >    nil                 {ftExtended}
1044 > {$IFEND}
1045 >    );
1046   (*var
1047    CreateProviderProc: function(DataSet: TIBCustomDataSet): IProvider = nil;*)
1048  
1049   implementation
1050  
1051 < uses Variants, FmtBCD, LazUTF8, IBMessages, IBQuery;
1051 > uses Variants, FmtBCD, LazUTF8, IBMessages, IBQuery, DateUtils, dbconst;
1052  
1053   type
1054  
# Line 1043 | Line 1110 | type
1110      Result := str;
1111    end;
1112  
1113 + { TIBDateTimeField }
1114 +
1115 + function TIBDateTimeField.GetTimeZoneName: string;
1116 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1117 + begin
1118 +  if GetDateTimeBuffer(aBuffer) then
1119 +    Result := GetTimeZoneServices.TimeZoneID2TimeZoneName(aBuffer.TimeZoneID)
1120 +  else
1121 +    Result := '';
1122 + end;
1123 +
1124 + function TIBDateTimeField.GetTimeZoneServices: ITimeZoneServices;
1125 + begin
1126 +  if (FTimeZoneServices = nil) and
1127 +     (DataSet <> nil) and ((DataSet as TIBCustomDataSet).Database <> nil)
1128 +      and ((DataSet as TIBCustomDataSet).Database.attachment <> nil) then
1129 +    FTimeZoneServices := (DataSet as TIBCustomDataSet).Database.attachment.GetTimeZoneServices;
1130 +  Result := FTimeZoneServices;
1131 + end;
1132 +
1133 + function TIBDateTimeField.GetDateTimeBuffer(
1134 +  var aBuffer: TIBBufferedDateTimeWithTimeZone): boolean;
1135 + begin
1136 +  Result := HasTimeZone;
1137 +  if Result then
1138 +    Result := GetData(@aBuffer,False);
1139 + end;
1140 +
1141 + function TIBDateTimeField.GetTimeZoneID: TFBTimeZoneID;
1142 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1143 + begin
1144 +  if GetDateTimeBuffer(aBuffer) then
1145 +    Result := aBuffer.TimeZoneID
1146 +  else
1147 +    Result := TimeZoneID_GMT;
1148 + end;
1149 +
1150 + procedure TIBDateTimeField.SetTimeZoneID(aValue: TFBTimeZoneID);
1151 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1152 + begin
1153 +  if GetDateTimeBuffer(aBuffer) then
1154 +    SetAsDateTimeTZ(aBuffer.Timestamp,aValue)
1155 + end;
1156 +
1157 + procedure TIBDateTimeField.SetTimeZoneName(AValue: string);
1158 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1159 + begin
1160 +  if GetDateTimeBuffer(aBuffer) then
1161 +    SetAsDateTimeTZ(aBuffer.Timestamp,aValue)
1162 + end;
1163 +
1164 + procedure TIBDateTimeField.Bind(Binding: Boolean);
1165 + var IBFieldDef: TIBFieldDef;
1166 + begin
1167 +  inherited Bind(Binding);
1168 +  if Binding and (FieldDef <> nil) then
1169 +  begin
1170 +    IBFieldDef := FieldDef as TIBFieldDef;
1171 +    FHasTimeZone := IBFieldDef.HasTimeZone;
1172 +  end;
1173 + end;
1174 +
1175 + function TIBDateTimeField.GetAsDateTime: TDateTime;
1176 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1177 + begin
1178 +  if GetDateTimeBuffer(aBuffer) then
1179 +    Result := aBuffer.Timestamp
1180 +  else
1181 +    Result := inherited GetAsDateTime;
1182 + end;
1183 +
1184 + function TIBDateTimeField.GetAsVariant: variant;
1185 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1186 + begin
1187 +  if GetDateTimeBuffer(aBuffer) then
1188 +    with aBuffer do
1189 +      Result := VarArrayOf([Timestamp,dstOffset,TimeZoneID])
1190 +  else
1191 +    Result := inherited GetAsVariant;
1192 + end;
1193 +
1194 + function TIBDateTimeField.GetDataSize: Integer;
1195 + begin
1196 +  if HasTimeZone then
1197 +    Result := sizeof(TIBBufferedDateTimeWithTimeZone)
1198 +  else
1199 +    Result := inherited GetDataSize;
1200 + end;
1201 +
1202 + procedure TIBDateTimeField.GetText(var theText: string; ADisplayText: Boolean);
1203 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1204 +    F: string;
1205 + begin
1206 +  if Dataset = nil then
1207 +    DatabaseErrorFmt(SNoDataset,[FieldName]);
1208 +
1209 +  if GetDateTimeBuffer(aBuffer) then
1210 +    {$if declared(DefaultFormatSettings)}
1211 +    with DefaultFormatSettings do
1212 +    {$else}
1213 +    {$if declared(FormatSettings)}
1214 +    with FormatSettings do
1215 +    {$ifend}
1216 +    {$ifend}
1217 +  begin
1218 +    if ADisplayText and (Length(DisplayFormat) <> 0) then
1219 +      F := DisplayFormat
1220 +    else
1221 +      Case DataType of
1222 +       ftTime : F := LongTimeFormat;
1223 +       ftDate : F := ShortDateFormat;
1224 +      else
1225 +       F := ShortDateFormat + ' ' + LongTimeFormat;
1226 +      end;
1227 +
1228 +    with aBuffer do
1229 +    case (DataSet as TIBCustomDataSet).TZTextOption of
1230 +    tzOffset:
1231 +      TheText := FBFormatDateTime(F,timestamp) + ' ' + FormatTimeZoneOffset(dstOffset);
1232 +    tzGMT:
1233 +      TheText := FBFormatDateTime(F,IncMinute(Timestamp,-dstOffset));
1234 +    tzOriginalID:
1235 +      TheText := FBFormatDateTime(F,timestamp) + ' ' + GetTimeZoneServices.TimeZoneID2TimeZoneName(TimeZoneID);
1236 +    end;
1237 +  end
1238 +  else
1239 +    inherited GetText(theText, ADisplayText);
1240 + end;
1241 +
1242 + procedure TIBDateTimeField.SetAsDateTime(AValue: TDateTime);
1243 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1244 + begin
1245 +  if GetDateTimeBuffer(aBuffer) then
1246 +    SetAsDateTimeTZ(AValue,aBuffer.TimeZoneID)
1247 +  else
1248 +    inherited SetAsDateTime(AValue)
1249 + end;
1250 +
1251 + procedure TIBDateTimeField.SetAsString(const AValue: string);
1252 + var aDateTime: TDateTime;
1253 +    aTimeZone: AnsiString;
1254 + begin
1255 +  if AValue = '' then
1256 +    Clear
1257 +  else
1258 +  if ParseDateTimeTZString(AValue,aDateTime,aTimeZone,DataType=ftTime) then
1259 +  begin
1260 +    if not HasTimeZone or (aTimeZone = '') then
1261 +      SetAsDateTime(aDateTime)
1262 +    else
1263 +      SetAsDateTimeTZ(aDateTime,aTimeZone);
1264 +  end
1265 +  else
1266 +    IBError(ibxeBadDateTimeTZString,[AValue]);
1267 + end;
1268 +
1269 + procedure TIBDateTimeField.SetVarValue(const AValue: Variant);
1270 + begin
1271 +  if HasTimeZone and VarIsArray(AValue)then
1272 +      SetAsDateTimeTZ(AValue[0],string(AValue[2]))
1273 +  else
1274 +    inherited SetVarValue(AValue);
1275 + end;
1276 +
1277 + constructor TIBDateTimeField.Create(AOwner: TComponent);
1278 + begin
1279 +  inherited Create(AOwner);
1280 +  SetDataType(ftDateTime);
1281 + end;
1282 +
1283 + function TIBDateTimeField.GetAsDateTimeTZ(var aDateTime: TDateTime;
1284 +  var dstOffset: smallint; var aTimeZoneID: TFBTimeZoneID): boolean;
1285 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1286 + begin
1287 +  Result := GetDateTimeBuffer(aBuffer);
1288 +  if Result then
1289 +  begin
1290 +    aDateTime := aBuffer.Timestamp;
1291 +    dstOffset := aBuffer.dstOffset;
1292 +    aTimeZoneID := aBuffer.TimeZoneID;
1293 +  end
1294 +  else
1295 +    aDateTime := inherited GetAsDateTime
1296 + end;
1297 +
1298 + function TIBDateTimeField.GetAsDateTimeTZ(var aDateTime: TDateTime;
1299 +  var dstOffset: smallint; var aTimeZone: string): boolean;
1300 + var aTimeZoneID: TFBTimeZoneID;
1301 + begin
1302 +  Result := GetAsDateTimeTZ(aDateTime,dstOffset,aTimeZoneID);
1303 +  if Result then
1304 +    aTimeZone := GetTimeZoneServices.TimeZoneID2TimeZoneName(aTimeZoneID);
1305 + end;
1306 +
1307 + function TIBDateTimeField.GetAsUTCDateTime: TDateTime;
1308 + var aBuffer: TIBBufferedDateTimeWithTimeZone;
1309 + begin
1310 +  if GetDateTimeBuffer(aBuffer) then
1311 +    Result := IncMinute(aBuffer.timestamp,-aBuffer.dstOffset)
1312 +  else
1313 +    Result := inherited GetAsDateTime;
1314 + end;
1315 +
1316 + procedure TIBDateTimeField.SetAsDateTimeTZ(aDateTime: TDateTime;
1317 +  aTimeZoneID: TFBTimeZoneID);
1318 + var DateTimeBuffer: TIBBufferedDateTimeWithTimeZone;
1319 + begin
1320 +  if HasTimeZone then
1321 +  begin
1322 +    DateTimeBuffer.Timestamp := aDateTime;
1323 +    DateTimeBuffer.dstOffset := GetTimeZoneServices.GetEffectiveOffsetMins(aDateTime,aTimeZoneID);
1324 +    DateTimeBuffer.TimeZoneID := aTimeZoneID;
1325 +    SetData(@DateTimeBuffer,False);
1326 +  end
1327 +  else
1328 +    inherited SetAsDateTime(aDateTime);
1329 + end;
1330 +
1331 + procedure TIBDateTimeField.SetAsDateTimeTZ(aDateTime: TDateTime;
1332 +  aTimeZone: string);
1333 + begin
1334 +  if HasTimeZone then
1335 +    SetAsDateTimeTZ(aDateTime,GetTimeZoneServices.TimeZoneName2TimeZoneID(aTimeZone))
1336 +  else
1337 +    inherited SetAsDateTime(aDateTime);
1338 + end;
1339 +
1340 + { TIBTimeField }
1341 +
1342 + constructor TIBTimeField.Create(AOwner: TComponent);
1343 + begin
1344 +  inherited Create(AOwner);
1345 +  SetDataType(ftTime);
1346 + end;
1347 +
1348   { TIBParserDataSet }
1349  
1350   procedure TIBParserDataSet.DoBeforeOpen;
# Line 1552 | Line 1854 | begin
1854    FDataLink := TIBDataLink.Create(Self);
1855    FQDelete := TIBSQL.Create(Self);
1856    FQDelete.OnSQLChanging := SQLChanging;
1857 <  FQDelete.GoToFirstRecordOnExecute := False;
1857 >  FQDelete.GoToFirstRecordOnExecute := True;
1858    FQInsert := TIBSQL.Create(Self);
1859    FQInsert.OnSQLChanging := SQLChanging;
1860 <  FQInsert.GoToFirstRecordOnExecute := False;
1860 >  FQInsert.GoToFirstRecordOnExecute := true;
1861    FQRefresh := TIBSQL.Create(Self);
1862    FQRefresh.OnSQLChanging := SQLChanging;
1863    FQRefresh.GoToFirstRecordOnExecute := False;
# Line 1565 | Line 1867 | begin
1867    FQSelect.GoToFirstRecordOnExecute := False;
1868    FQModify := TIBSQL.Create(Self);
1869    FQModify.OnSQLChanging := SQLChanging;
1870 <  FQModify.GoToFirstRecordOnExecute := False;
1870 >  FQModify.GoToFirstRecordOnExecute := True;  {In Firebird 5, Update..Returning returns a cursor}
1871    FUpdateRecordTypes := [cusUnmodified, cusModified, cusInserted];
1872    FParamCheck := True;
1873    FGenerateParamNames := False;
# Line 1586 | Line 1888 | begin
1888      if AOwner is TIBTransaction then
1889        Transaction := TIBTransaction(AOwner);
1890    FBaseSQLSelect := TStringList.Create;
1891 +  FTZTextOption := tzOffset;
1892 +  FDefaultTZDate := EncodeDate(2020,1,1);
1893    FSQLFilterParams := TStringList.Create;
1894    TStringList(FSQLFilterParams).OnChange :=  HandleSQLFilterParamsChanged;
1895   end;
# Line 1695 | Line 1999 | var
1999  
2000    procedure UpdateUsingOnUpdateRecord;
2001    begin
1698    UpdateAction := uaFail;
2002      try
2003        FOnUpdateRecord(Self, UpdateKind, UpdateAction);
2004      except
2005        on E: Exception do
2006        begin
2007 +        UpdateAction := uaFail;
2008          if (E is EDatabaseError) and Assigned(FOnUpdateError) then
2009 <          FOnUpdateError(Self, EIBError(E), UpdateKind, UpdateAction);
1706 <        if UpdateAction = uaFail then
1707 <            raise;
2009 >          FOnUpdateError(Self, EDatabaseError(E), UpdateKind, UpdateAction);
2010        end;
2011      end;
2012    end;
# Line 1713 | Line 2015 | var
2015    begin
2016      try
2017        FUpdateObject.Apply(UpdateKind,PChar(Buffer));
2018 <      ResetBufferUpdateStatus;
2018 >      UpdateAction := uaApplied;
2019      except
2020        on E: Exception do
2021 +      begin
2022 +        UpdateAction := uaFail;
2023          if (E is EDatabaseError) and Assigned(FOnUpdateError) then
2024 <          FOnUpdateError(Self, EIBError(E), UpdateKind, UpdateAction);
2024 >          FOnUpdateError(Self, EDatabaseError(E), UpdateKind, UpdateAction);
2025 >      end;
2026      end;
2027    end;
2028  
# Line 1732 | Line 2037 | var
2037          cusDeleted:
2038            InternalDeleteRecord(FQDelete, Buffer);
2039        end;
2040 +      UpdateAction := uaApplied;
2041      except
2042 <      on E: EIBError do begin
2042 >      on E: Exception do begin
2043          UpdateAction := uaFail;
2044 <        if Assigned(FOnUpdateError) then
2045 <          FOnUpdateError(Self, E, UpdateKind, UpdateAction);
1740 <        case UpdateAction of
1741 <          uaFail: raise;
1742 <          uaAbort: SysUtils.Abort;
1743 <          uaSkip: bRecordsSkipped := True;
1744 <        end;
2044 >        if (E is EDatabaseError) and Assigned(FOnUpdateError) then
2045 >          FOnUpdateError(Self, EDatabaseError(E), UpdateKind, UpdateAction);
2046        end;
2047      end;
2048    end;
# Line 1763 | Line 2064 | begin
2064        Buffer := PRecordData(GetActiveBuf);
2065        GetUpdateKind;
2066        UpdateAction := uaApply;
2067 <      if Assigned(FUpdateObject) or Assigned(FOnUpdateRecord) then
2067 >      if (Assigned(FOnUpdateRecord)) then
2068 >        UpdateUsingOnUpdateRecord;
2069 >      if UpdateAction = uaApply then
2070        begin
2071 <        if (Assigned(FOnUpdateRecord)) then
2072 <          UpdateUsingOnUpdateRecord
2071 >        if Assigned(FUpdateObject) then
2072 >          UpdateUsingUpdateObject
2073          else
2074 <          if Assigned(FUpdateObject) then
1772 <            UpdateUsingUpdateObject;
1773 <        case UpdateAction of
1774 <          uaFail:
1775 <            IBError(ibxeUserAbort, [nil]);
1776 <          uaAbort:
1777 <            SysUtils.Abort;
1778 <          uaApplied:
1779 <            ResetBufferUpdateStatus;
1780 <          uaSkip:
1781 <            bRecordsSkipped := True;
1782 <          uaRetry:
1783 <            Continue;
1784 <        end;
2074 >          UpdateUsingInternalquery;
2075        end;
2076 <      if (not Assigned(FUpdateObject)) and (UpdateAction = UaApply) then
2077 <      begin
2078 <        UpdateUsingInternalquery;
2079 <        UpdateAction := uaApplied;
2076 >
2077 >      case UpdateAction of
2078 >        uaFail:
2079 >          IBError(ibxeUserAbort, [nil]);
2080 >        uaAbort:
2081 >          SysUtils.Abort;
2082 >        uaApplied:
2083 >          ResetBufferUpdateStatus;
2084 >        uaSkip:
2085 >          bRecordsSkipped := True;
2086 >        uaRetry:
2087 >          Continue;
2088        end;
2089 +
2090        Next;
2091      end;
2092      FUpdatesPending := bRecordsSkipped;
# Line 1961 | Line 2260 | var
2260    Buff: PRecordData;
2261   begin
2262    Buff := PRecordData(GetActiveBuf);
2263 <  result := (FQModify.SQL.Text <> '') or
2264 <    (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukModify).Text <> '')) or
2263 >  result := (Trim(FQModify.SQL.Text) <> '') or
2264 >    (Assigned(FUpdateObject) and (Trim(FUpdateObject.GetSQL(ukModify).Text) <> '')) or
2265      ((Buff <> nil) and (Buff^.rdCachedUpdateStatus = cusInserted) and
2266        (FCachedUpdates));
2267   end;
2268  
2269   function TIBCustomDataSet.CanInsert: Boolean;
2270   begin
2271 <  result := (FQInsert.SQL.Text <> '') or
2272 <    (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukInsert).Text <> ''));
2271 >  result := (Trim(FQInsert.SQL.Text) <> '') or
2272 >    (Assigned(FUpdateObject) and (Trim(FUpdateObject.GetSQL(ukInsert).Text) <> ''));
2273   end;
2274  
2275   function TIBCustomDataSet.CanDelete: Boolean;
2276   begin
2277 <  if (FQDelete.SQL.Text <> '') or
2278 <    (Assigned(FUpdateObject) and (FUpdateObject.GetSQL(ukDelete).Text <> '')) then
2277 >  if (Trim(FQDelete.SQL.Text) <> '') or
2278 >    (Assigned(FUpdateObject) and (Trim(FUpdateObject.GetSQL(ukDelete).Text) <> '')) then
2279      result := True
2280    else
2281      result := False;
# Line 1984 | Line 2283 | end;
2283  
2284   function TIBCustomDataSet.CanRefresh: Boolean;
2285   begin
2286 <  result := (FQRefresh.SQL.Text <> '') or
2287 <    (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> ''));
2286 >  result := (Trim(FQRefresh.SQL.Text) <> '') or
2287 >    (Assigned(FUpdateObject) and (Trim(FUpdateObject.RefreshSQL.Text) <> ''));
2288   end;
2289  
2290   procedure TIBCustomDataSet.CheckEditState;
# Line 2152 | Line 2451 | begin
2451          SQL_TYPE_DATE,
2452          SQL_TYPE_TIME:
2453            fdDataSize := SizeOf(TDateTime);
2454 +        SQL_TIMESTAMP_TZ,
2455 +        SQL_TIMESTAMP_TZ_EX,
2456 +        SQL_TIME_TZ,
2457 +        SQL_TIME_TZ_EX:
2458 +          fdDataSize := SizeOf(TIBBufferedDateTimeWithTimeZone);
2459          SQL_SHORT, SQL_LONG:
2460          begin
2461            if (fdDataScale = 0) then
# Line 2179 | Line 2483 | begin
2483          SQL_VARYING,
2484          SQL_TEXT,
2485          SQL_BLOB:
2486 <          fdCodePage := Qry.Metadata[i].getCodePage;
2486 >          fdCodePage := colMetadata.getCodePage;
2487 >        SQL_DEC16,
2488 >        SQL_DEC34,
2489 >        SQL_DEC_FIXED,
2490 >        SQL_INT128:
2491 >          fdDataSize := sizeof(tBCD);
2492          end;
2493          fdDataOfs := FRecordSize;
2494          Inc(FRecordSize, fdDataSize);
# Line 2193 | Line 2502 | end;
2502   procedure TIBCustomDataSet.UpdateRecordFromQuery(QryResults: IResults;
2503    Buffer: PChar);
2504   var i, j: integer;
2505 +    pda: PArrayDataArray;
2506 +    pbd: PBlobDataArray;
2507   begin
2508 +  { Make sure blob cache is empty }
2509 +  pbd := PBlobDataArray(Buffer + FBlobCacheOffset);
2510 +  pda := PArrayDataArray(Buffer + FArrayCacheOffset);
2511 +  for i := 0 to BlobFieldCount - 1 do
2512 +    pbd^[i] := nil;
2513 +  for i := 0 to ArrayFieldCount - 1 do
2514 +    pda^[i] := nil;
2515 +
2516    for i := 0 to QryResults.Count - 1 do
2517    begin
2518      j := GetFieldPosition(QryResults[i].GetAliasName);
# Line 2212 | Line 2531 | procedure TIBCustomDataSet.ColumnDataToB
2531                 ColumnIndex, FieldIndex: integer; Buffer: PChar);
2532   var
2533    LocalData: PByte;
2534 <  LocalDate: TDateTime;
2216 <  LocalDouble: Double;
2217 <  LocalInt: Integer;
2218 <  LocalBool: wordBool;
2219 <  LocalInt64: Int64;
2220 <  LocalCurrency: Currency;
2534 >  BufPtr: PByte;
2535    ColData: ISQLData;
2536   begin
2537    LocalData := nil;
2538    with PRecordData(Buffer)^.rdFields[FieldIndex], FFieldColumns^[FieldIndex] do
2539    begin
2540      QryResults.GetData(ColumnIndex,fdIsNull,fdDataLength,LocalData);
2541 +    BufPtr := PByte(Buffer + fdDataOfs);
2542      if not fdIsNull then
2543      begin
2544        ColData := QryResults[ColumnIndex];
# Line 2231 | Line 2546 | begin
2546          SQL_TYPE_DATE,
2547          SQL_TYPE_TIME,
2548          SQL_TIMESTAMP:
2234        begin
2549            {This is an IBX native format and not the TDataset approach. See also GetFieldData}
2550 <          LocalDate := ColData.AsDateTime;
2551 <          LocalData := PByte(@LocalDate);
2550 >          PDateTime(BufPtr)^ := ColData.AsDateTime;
2551 >
2552 >        SQL_TIMESTAMP_TZ,
2553 >        SQL_TIMESTAMP_TZ_EX:
2554 >        begin
2555 >          with PIBBufferedDateTimeWithTimeZone(Bufptr)^ do
2556 >            ColData.GetAsDateTime(Timestamp,dstOffset,TimeZoneID);
2557 >        end;
2558 >
2559 >        SQL_TIME_TZ,
2560 >        SQL_TIME_TZ_EX:
2561 >        begin
2562 >          with PIBBufferedDateTimeWithTimeZone(Bufptr)^ do
2563 >            ColData.GetAsTime(Timestamp, dstOffset,TimeZoneID, DefaultTZDate);
2564          end;
2565          SQL_SHORT, SQL_LONG:
2566          begin
2567            if (fdDataScale = 0) then
2568 <          begin
2243 <            LocalInt := ColData.AsLong;
2244 <            LocalData := PByte(@LocalInt);
2245 <          end
2568 >            PInteger(BufPtr)^ := ColData.AsLong
2569            else
2570            if (fdDataScale >= (-4)) then
2571 <          begin
2249 <            LocalCurrency := ColData.AsCurrency;
2250 <            LocalData := PByte(@LocalCurrency);
2251 <          end
2571 >            PCurrency(BufPtr)^ := ColData.AsCurrency
2572            else
2573 <          begin
2254 <           LocalDouble := ColData.AsDouble;
2255 <           LocalData := PByte(@LocalDouble);
2256 <          end;
2573 >           PDouble(BufPtr)^ := ColData.AsDouble;
2574          end;
2575          SQL_INT64:
2576          begin
2577            if (fdDataScale = 0) then
2578 <          begin
2262 <            LocalInt64 := ColData.AsInt64;
2263 <            LocalData := PByte(@LocalInt64);
2264 <          end
2578 >            PInt64(BufPtr)^ := ColData.AsInt64
2579            else
2580            if (fdDataScale >= (-4)) then
2581 <          begin
2582 <            LocalCurrency := ColData.AsCurrency;
2583 <            LocalData := PByte(@LocalCurrency);
2270 <            end
2271 <            else
2272 <            begin
2273 <              LocalDouble := ColData.AsDouble;
2274 <              LocalData := PByte(@LocalDouble);
2275 <            end
2581 >            PCurrency(BufPtr)^ := ColData.AsCurrency
2582 >          else
2583 >            PDouble(BufPtr)^ := ColData.AsDouble;
2584          end;
2585 +
2586          SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
2587 <        begin
2588 <          LocalDouble := ColData.AsDouble;
2280 <          LocalData := PByte(@LocalDouble);
2281 <        end;
2587 >          PDouble(BufPtr)^ := ColData.AsDouble;
2588 >
2589          SQL_BOOLEAN:
2590 <        begin
2591 <          LocalBool := ColData.AsBoolean;
2592 <          LocalData := PByte(@LocalBool);
2593 <        end;
2594 <      end;
2590 >          system.PBoolean(BufPtr)^ := ColData.AsBoolean;
2591 >
2592 >        SQL_DEC16,
2593 >        SQL_DEC34,
2594 >        SQL_DEC_FIXED,
2595 >        SQL_INT128:
2596 >          pBCD(BufPtr)^ := ColData.GetAsBCD;
2597  
2289      if fdDataType = SQL_VARYING then
2290        Move(LocalData^, Buffer[fdDataOfs], fdDataLength)
2598        else
2599 <        Move(LocalData^, Buffer[fdDataOfs], fdDataSize)
2599 >        begin
2600 >          if fdDataType = SQL_VARYING then
2601 >            Move(LocalData^, BufPtr^, fdDataLength)
2602 >          else
2603 >            Move(LocalData^, BufPtr^, fdDataSize)
2604 >        end;
2605 >      end; {case}
2606      end
2607      else {Null column}
2608      if fdDataType = SQL_VARYING then
2609 <      FillChar(Buffer[fdDataOfs],fdDataLength,0)
2609 >      FillChar(BufPtr^,fdDataLength,0)
2610      else
2611 <      FillChar(Buffer[fdDataOfs],fdDataSize,0);
2611 >      FillChar(BufPtr^,fdDataSize,0);
2612    end;
2613   end;
2614  
# Line 2637 | Line 2950 | begin
2950      begin
2951        if Buff <> nil then
2952        begin
2953 <        if (Assigned(FUpdateObject) and (FUpdateObject.RefreshSQL.Text <> '')) then
2953 >        if (Assigned(FUpdateObject) and (Trim(FUpdateObject.RefreshSQL.Text) <> '')) then
2954          begin
2955            Qry := TIBSQL.Create(self);
2956            Qry.Database := Database;
# Line 2891 | Line 3204 | end;
3204  
3205   procedure TIBCustomDataSet.SetInternalSQLParams(Params: ISQLParams; Buffer: Pointer);
3206   var
3207 <  i, j: Integer;
3208 <  cr, data: PChar;
3207 >  i, j, arr: Integer;
3208 >  cr, data: PByte;
3209    fn: string;
3210    st: RawByteString;
3211    OldBuffer: Pointer;
3212    Param: ISQLParam;
3213 +  pda: PArrayDataArray;
3214   begin
3215    if (Buffer = nil) then
3216      IBError(ibxeBufferNotSet, [nil]);
# Line 2904 | Line 3218 | begin
3218      InternalPrepare;
3219    OldBuffer := nil;
3220    try
3221 +    pda := PArrayDataArray(PChar(Buffer) + FArrayCacheOffset);
3222 +    arr := 0;
3223      for i := 0 to Params.GetCount - 1 do
3224      begin
3225        Param := Params[i];
# Line 2942 | Line 3258 | begin
3258              case fdDataType of
3259                SQL_TEXT, SQL_VARYING:
3260                begin
3261 <                SetString(st, data, fdDataLength);
3261 >                SetString(st, PAnsiChar(data), fdDataLength);
3262                  SetCodePage(st,fdCodePage,false);
3263                  Param.AsString := st;
3264                end;
# Line 2968 | Line 3284 | begin
3284                else
3285                  Param.AsDouble := PDouble(data)^;
3286              end;
3287 <            SQL_BLOB, SQL_ARRAY, SQL_QUAD:
3287 >            SQL_BLOB, SQL_QUAD:
3288                Param.AsQuad := PISC_QUAD(data)^;
3289 +            SQL_ARRAY:
3290 +              begin
3291 +                if pda[arr] = nil then
3292 +                  Param.AsQuad := PISC_QUAD(data)^
3293 +                else
3294 +                  Param.AsArray := pda[arr].ArrayIntf;
3295 +                Inc(arr);
3296 +              end;
3297              SQL_TYPE_DATE,
3298              SQL_TYPE_TIME,
3299              SQL_TIMESTAMP:
3300              {This is an IBX native format and not the TDataset approach. See also SetFieldData}
3301                Param.AsDateTime := PDateTime(data)^;
3302 +            SQL_TIMESTAMP_TZ_EX,
3303 +            SQL_TIMESTAMP_TZ:
3304 +              with PIBBufferedDateTimeWithTimeZone(data)^ do
3305 +                Param.SetAsDateTime(Timestamp,TimeZoneID);
3306 +            SQL_TIME_TZ_EX,
3307 +            SQL_TIME_TZ:
3308 +              with PIBBufferedDateTimeWithTimeZone(data)^ do
3309 +                Param.SetAsTime(Timestamp,DefaultTZDate,TimeZoneID);
3310              SQL_BOOLEAN:
3311                Param.AsBoolean := PWordBool(data)^;
3312 +            SQL_DEC16,
3313 +            SQL_DEC34,
3314 +            SQL_DEC_FIXED,
3315 +            SQL_INT128:
3316 +              Param.AsBCD := pBCD(data)^;
3317 +            else
3318 +              IBError(ibxeUnknownSQLType,[fdDataType]);
3319            end;
3320          end;
3321        end;
# Line 3232 | Line 3571 | end;
3571   procedure TIBCustomDataSet.ReadRecordCache(RecordNumber: Integer; Buffer: PChar;
3572                                            ReadOldBuffer: Boolean);
3573   begin
3574 +  if RecordNumber = -1 then
3575 +    Exit; {nothing to do}
3576    if FUniDirectional then
3577      RecordNumber := RecordNumber mod UniCache;
3578    if (ReadOldBuffer) then
# Line 3397 | Line 3738 | end;
3738   procedure TIBCustomDataSet.SetArrayIntf(AnArray: IArray; Field: TIBArrayField);
3739   var Buff: PChar;
3740      pda: PArrayDataArray;
3741 +    MappedFieldPos: integer;
3742   begin
3743    if (Field = nil) or (Field.DataSet <> self) then
3744      IBError(ibxFieldNotinDataSet,[Field.Name,Name]);
3745    Buff := GetActiveBuf;
3746    if Buff <> nil then
3747 +  with PRecordData(Buff)^ do
3748    begin
3749      AdjustRecordOnInsert(Buff);
3750 <    pda := PArrayDataArray(Buff + FArrayCacheOffset);
3751 <    pda^[Field.FCacheOffset].FArray := AnArray;
3752 <    WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3750 >    MappedFieldPos := FMappedFieldPosition[Field.FieldNo - 1];
3751 >    if (MappedFieldPos > 0) and
3752 >       (MappedFieldPos <= rdFieldCount) then
3753 >    begin
3754 >      rdFields[MappedFieldPos].fdIsNull := AnArray = nil;
3755 >      pda := PArrayDataArray(Buff + FArrayCacheOffset);
3756 >      if pda^[Field.FCacheOffset] = nil then
3757 >      begin
3758 >        if not rdFields[MappedFieldPos].fdIsNull then
3759 >        begin
3760 >          pda^[Field.FCacheOffset] := TIBArray.Create(Field,AnArray);
3761 >          FArrayList.Add(pda^[Field.FCacheOffset]);
3762 >        end
3763 >      end
3764 >      else
3765 >        pda^[Field.FCacheOffset].FArray := AnArray;
3766 >      WriteRecordCache(PRecordData(Buff)^.rdRecordNumber, Pointer(Buff));
3767 >    end;
3768    end;
3769   end;
3770  
# Line 3613 | Line 3971 | end;
3971  
3972   function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: Pointer): Boolean;
3973   var
3974 <  Buff, Data: PChar;
3974 >  Buff: PChar;
3975 >  Data: PByte;
3976    CurrentRecord: PRecordData;
3977   begin
3978    result := False;
# Line 3640 | Line 3999 | begin
3999      result := not fdIsNull;
4000      if result and (Buffer <> nil) then
4001        begin
4002 <        Data := Buff + fdDataOfs;
4002 >        Data := PByte(Buff) + fdDataOfs;
4003          if (fdDataType = SQL_VARYING) or (fdDataType = SQL_TEXT) then
4004          begin
4005            if fdDataLength <= Field.DataSize then
# Line 3860 | Line 4219 | begin
4219    if FDidActivate then
4220      DeactivateTransaction;
4221    FQSelect.Close;
4222 <  ClearBlobCache;
3864 <  ClearArrayCache;
4222 >  ResetBufferCache;
4223    FreeRecordBuffer(FModelBuffer);
4224    FreeRecordBuffer(FOldBuffer);
4225    FCurrentRecord := -1;
4226    FOpen := False;
3869  FRecordCount := 0;
3870  FDeletedRecords := 0;
4227    FRecordSize := 0;
3872  FBPos := 0;
3873  FOBPos := 0;
3874  FCacheSize := 0;
3875  FOldCacheSize := 0;
3876  FBEnd := 0;
3877  FOBEnd := 0;
3878  FreeMem(FBufferCache);
3879  FBufferCache := nil;
4228    FreeMem(FFieldColumns);
4229    FFieldColumns := nil;
3882  FreeMem(FOldBufferCache);
3883  FOldBufferCache := nil;
4230    BindFields(False);
4231    ResetParser;
4232    if DefaultFields then DestroyFields;
# Line 3948 | Line 4294 | begin
4294   procedure TIBCustomDataSet.FieldDefsFromQuery(SourceQuery: TIBSQL);
4295   const
4296    DefaultSQL = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
4297 <               'F.RDB$DEFAULT_VALUE, Trim(R.RDB$FIELD_NAME) as RDB$FIELD_NAME ' + {do not localize}
4297 >               'F.RDB$DEFAULT_VALUE,  R.RDB$FIELD_NAME ' + {do not localize}
4298                 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
4299                 'where R.RDB$RELATION_NAME = :RELATION ' +  {do not localize}
4300                 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
# Line 3956 | Line 4302 | const
4302                 '     (not F.RDB$DEFAULT_VALUE is NULL)) '; {do not localize}
4303  
4304    DefaultSQLODS12 = 'Select F.RDB$COMPUTED_BLR, ' + {do not localize}
4305 <               'F.RDB$DEFAULT_VALUE, Trim(R.RDB$FIELD_NAME) as RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
4305 >               'F.RDB$DEFAULT_VALUE, R.RDB$FIELD_NAME, R.RDB$IDENTITY_TYPE ' + {do not localize}
4306                 'from RDB$RELATION_FIELDS R, RDB$FIELDS F ' + {do not localize}
4307                 'where R.RDB$RELATION_NAME = :RELATION ' +  {do not localize}
4308                 'and R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME '+ {do not localize}
# Line 3981 | Line 4327 | var
4327    aArrayDimensions: integer;
4328    aArrayBounds: TArrayBounds;
4329    ArrayMetaData: IArrayMetaData;
4330 +  FieldHasTimeZone: boolean;
4331  
4332    function Add_Node(Relation, Field : String) : TRelationNode;
4333    var
# Line 4000 | Line 4347 | var
4347      while not Query.Eof do
4348      begin
4349        FField := TFieldNode.Create;
4350 <      FField.FieldName := Query.Fields[2].AsString;
4350 >      FField.FieldName := TrimRight(Query.Fields[2].AsString);
4351        FField.DEFAULT_VALUE := not Query.Fields[1].IsNull;
4352        FField.COMPUTED_BLR := not Query.Fields[0].IsNull;
4353        FField.IDENTITY_COLUMN := (Query.FieldCount > 3) and not Query.Fields[3].IsNull;
# Line 4133 | Line 4480 | begin
4480          FieldDataSize := GetSize;
4481          FieldPrecision := 0;
4482          FieldNullable := IsNullable;
4483 +        FieldHasTimeZone := false;
4484          CharSetSize := 0;
4485          CharSetName := '';
4486          FieldCodePage := CP_NONE;
# Line 4181 | Line 4529 | begin
4529                FieldType := ftFloat
4530              else
4531              begin
4532 <              FieldType := ftFMTBCD;
4532 >              FieldType := ftBCD;
4533                FieldPrecision := 9;
4534                FieldSize := -getScale;
4535              end;
# Line 4203 | Line 4551 | begin
4551            SQL_TIMESTAMP: FieldType := ftDateTime;
4552            SQL_TYPE_TIME: FieldType := ftTime;
4553            SQL_TYPE_DATE: FieldType := ftDate;
4554 +          SQL_TIMESTAMP_TZ,
4555 +          SQL_TIMESTAMP_TZ_EX:
4556 +            begin
4557 +              FieldType := ftDateTime;
4558 +              FieldHasTimeZone := true;
4559 +            end;
4560 +          SQL_TIME_TZ,
4561 +          SQL_TIME_TZ_EX:
4562 +            begin
4563 +              FieldType := ftTime;
4564 +              FieldHasTimeZone := true;
4565 +            end;
4566            SQL_BLOB:
4567            begin
4568              FieldSize := sizeof (TISC_QUAD);
# Line 4230 | Line 4590 | begin
4590            end;
4591            SQL_BOOLEAN:
4592               FieldType:= ftBoolean;
4593 +
4594 +          SQL_DEC16:
4595 +            begin
4596 +              FieldType := ftFmtBCD;
4597 +              FieldPrecision := 16;
4598 +              FieldSize := 4; {For conversions from currency type}
4599 +            end;
4600 +
4601 +          SQL_DEC34:
4602 +          begin
4603 +            FieldType := ftFmtBCD;
4604 +            FieldPrecision := 34;
4605 +            FieldSize := 4; {For conversions from currency type}
4606 +          end;
4607 +
4608 +          SQL_DEC_FIXED,
4609 +          SQL_INT128:
4610 +          begin
4611 +            FieldType := ftFmtBCD;
4612 +            FieldPrecision := 38;
4613 +            FieldSize := -getScale; {For conversions from currency type}
4614 +          end;
4615 +
4616            else
4617              FieldType := ftUnknown;
4618          end;
# Line 4253 | Line 4636 | begin
4636              CodePage := FieldCodePage;
4637              ArrayDimensions := aArrayDimensions;
4638              ArrayBounds := aArrayBounds;
4639 +            HasTimezone := FieldHasTimeZone;
4640              if (FieldName <> '') and (RelationName <> '') then
4641              begin
4642                IdentityColumn := Is_IDENTITY_COLUMN(RelationName, FieldName);
# Line 4351 | Line 4735 | begin
4735            ftDate:
4736              cur_param.AsDate := cur_field.AsDateTime;
4737            ftTime:
4738 <            cur_param.AsTime := cur_field.AsDateTime;
4738 >            if (cur_field is TIBDateTimeField) and TIBDateTimeField(cur_field).HasTimeZone
4739 >              and (cur_param.GetSQLType = SQL_TIME_TZ) then
4740 >              cur_param.SetAsTime(cur_Field.asDateTime,DefaultTZDate,TIBDateTimeField(cur_field).TimeZoneID)
4741 >            else
4742 >              cur_param.AsTime := cur_field.AsDateTime;
4743            ftDateTime:
4744 <            cur_param.AsDateTime := cur_field.AsDateTime;
4744 >          begin
4745 >            if (cur_field is TIBDateTimeField) and TIBDateTimeField(cur_field).HasTimeZone
4746 >              and (cur_param.GetSQLType = SQL_TIMESTAMP_TZ) then
4747 >              cur_param.SetAsDateTime(cur_field.AsDateTime,TIBDateTimeField(cur_field).TimeZoneID)
4748 >            else
4749 >              cur_param.AsDateTime := cur_field.AsDateTime;
4750 >          end;
4751            ftBlob, ftMemo:
4752            begin
4753              s := nil;
# Line 4367 | Line 4761 | begin
4761            end;
4762            ftArray:
4763              cur_param.AsArray := TIBArrayField(cur_field).ArrayIntf;
4764 +          ftFmtBCD:
4765 +            cur_param.AsBCD := TFmtBCDField(cur_field).AsBCD;
4766            else
4767              IBError(ibxeNotSupported, [nil]);
4768          end;
# Line 4392 | Line 4788 | begin
4788    First;
4789   end;
4790  
4791 + procedure TIBCustomDataSet.ResetBufferCache;
4792 + begin
4793 +  ClearBlobCache;
4794 +  ClearArrayCache;
4795 +  FRecordCount := 0;
4796 +  FDeletedRecords := 0;
4797 +  FBPos := 0;
4798 +  FOBPos := 0;
4799 +  FCacheSize := 0;
4800 +  FOldCacheSize := 0;
4801 +  FBEnd := 0;
4802 +  FOBEnd := 0;
4803 +  FreeMem(FBufferCache);
4804 +  FBufferCache := nil;
4805 +  FreeMem(FOldBufferCache);
4806 +  FOldBufferCache := nil;
4807 + end;
4808 +
4809   procedure TIBCustomDataSet.InternalOpen;
4810  
4811    function RecordDataLength(n: Integer): Long;
# Line 4599 | Line 5013 | end;
5013  
5014   procedure TIBCustomDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
5015   begin
5016 <  PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
5016 >  if Data <> nil then
5017 >    PRecordData(Buffer)^.rdRecordNumber := PInteger(Data)^;
5018   end;
5019  
5020   procedure TIBCustomDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
# Line 4811 | Line 5226 | begin
5226      FQSelect.CaseSensitiveParameterNames := AValue;
5227   end;
5228  
5229 + procedure TIBCustomDataSet.SetDefaultTZDate(AValue: TDateTime);
5230 + begin
5231 +  FDefaultTZDate := DateOf(AValue);
5232 + end;
5233 +
5234   procedure TIBCustomDataSet.SetSQLFiltered(AValue: boolean);
5235   begin
5236    if FSQLFiltered = AValue then Exit;
# Line 5299 | Line 5719 | end;
5719  
5720   procedure TIBGenerator.SetQuerySQL;
5721   begin
5722 <  if Database <> nil then
5722 >  if (Database <> nil) and (FGeneratorName <> '') then
5723      FQuery.SQL.Text := Format('Select Gen_ID(%s,%d) From RDB$Database',
5724        [QuoteIdentifierIfNeeded(Database.SQLDialect,FGeneratorName),Increment]);
5725   end;
# Line 5362 | Line 5782 | begin
5782      Owner.FieldByName(FFieldName).AsInteger := GetNextValue;
5783   end;
5784  
5785 + initialization
5786 +  RegisterClasses([TIBArrayField,TIBStringField,TIBBCDField,
5787 +                   TIBSmallintField,TIBIntegerField,TIBLargeIntField,
5788 +                   TIBMemoField, TIBDateTimeField, TIBTimeField]);
5789 +
5790  
5791   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines