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

Comparing ibx/trunk/fbintf/client/FBSQLData.pas (file contents):
Revision 263 by tony, Thu Dec 6 15:55:01 2018 UTC vs.
Revision 345 by tony, Mon Aug 23 14:22:29 2021 UTC

# Line 76 | Line 76 | unit FBSQLData;
76    methods are needed for SQL parameters only. The string getters and setters
77    are virtual as SQLVar and Array encodings of string data is different.}
78  
79 { Note on SQL Parameter Names
80  --------------------------------------------
81
82  IBX processes parameter names case insensitive. This does result in some additional
83  overhead due to a call to "AnsiUpperCase". This can be avoided by undefining
84  "UseCaseInSensitiveParamName" below.
85
86 }
87 {$define UseCaseInSensitiveParamName}
79  
80   interface
81  
82   uses
83 <  Classes, SysUtils, IBExternals, IBHeader, IB,  FBActivityMonitor, FBClientAPI;
83 >  Classes, SysUtils, IBExternals, {$IFDEF WINDOWS} Windows, {$ENDIF} IB,  FBActivityMonitor, FBClientAPI,
84 >  FmtBCD;
85  
86   type
87  
88 <  { TSQLDataItem }
88 >   {The IExTimeZoneServices is only available in FB4 and onwards}
89 >
90 >   IExTimeZoneServices = interface(ITimeZoneServices)
91 >   ['{789c2eeb-c4a7-4fed-837e-0cbdef775904}']
92 >   {encode/decode - used to encode/decode the wire protocol}
93 >   procedure EncodeTimestampTZ(timestamp: TDateTime; timezoneID: TFBTimeZoneID;
94 >     bufptr: PByte); overload;
95 >   procedure EncodeTimestampTZ(timestamp: TDateTime; timezone: AnsiString;
96 >       bufptr: PByte); overload;
97 >   procedure EncodeTimeTZ(time: TDateTime; timezoneID: TFBTimeZoneID; OnDate: TDateTime;
98 >     bufptr: PByte); overload;
99 >   procedure EncodeTimeTZ(time: TDateTime; timezone: AnsiString; OnDate: TDateTime;
100 >     bufptr: PByte); overload;
101 >   procedure DecodeTimestampTZ(bufptr: PByte; var timestamp: TDateTime;
102 >     var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
103 >   procedure DecodeTimestampTZ(bufptr: PByte; var timestamp: TDateTime;
104 >     var dstOffset: smallint; var timezone: AnsiString); overload;
105 >   procedure DecodeTimestampTZEx(bufptr: PByte; var timestamp: TDateTime;
106 >     var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
107 >   procedure DecodeTimestampTZEx(bufptr: PByte; var timestamp: TDateTime;
108 >     var dstOffset: smallint; var timezone: AnsiString); overload;
109 >   procedure DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
110 >     var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
111 >   procedure DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
112 >     var dstOffset: smallint; var timezone: AnsiString); overload;
113 >   procedure DecodeTimeTZEx(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
114 >     var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
115 >   procedure DecodeTimeTZEx(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
116 >     var dstOffset: smallint; var timezone: AnsiString); overload;
117 >   end;
118 >
119 >   { TSQLDataItem }
120  
121    TSQLDataItem = class(TFBInterfacedObject)
122    private
123       FFirebirdClientAPI: TFBClientAPI;
124 +     FTimeZoneServices: IExTimeZoneServices;
125       function AdjustScale(Value: Int64; aScale: Integer): Double;
126       function AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
127       function AdjustScaleToCurrency(Value: Int64; aScale: Integer): Currency;
128 +     function GetDateFormatStr(IncludeTime: boolean): AnsiString;
129 +     function GetTimeFormatStr: AnsiString;
130 +     function GetTimestampFormatStr: AnsiString;
131       procedure SetAsInteger(AValue: Integer);
132 +     procedure InternalGetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
133 +       var aTimezone: AnsiString; var aTimeZoneID: TFBTimeZoneID);
134    protected
135       function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
136       function AdjustScaleFromDouble(Value: Double; aScale: Integer): Int64;
137       procedure CheckActive; virtual;
138 +     procedure CheckTZSupport;
139 +     function GetAttachment: IAttachment; virtual; abstract;
140       function GetSQLDialect: integer; virtual; abstract;
141 +     function GetTimeZoneServices: IExTimeZoneServices; virtual;
142       procedure Changed; virtual;
143       procedure Changing; virtual;
144       procedure InternalSetAsString(Value: AnsiString); virtual;
# Line 119 | Line 151 | type
151       procedure SetDataLength(len: cardinal); virtual;
152       procedure SetSQLType(aValue: cardinal); virtual;
153       property DataLength: cardinal read GetDataLength write SetDataLength;
154 <
154 >     property FirebirdClientAPI: TFBClientAPI read FFirebirdClientAPI;
155    public
156       constructor Create(api: TFBClientAPI);
157       function GetSQLType: cardinal; virtual; abstract;
158       function GetSQLTypeName: AnsiString; overload;
159       class function GetSQLTypeName(SQLType: short): AnsiString; overload;
160 +     function GetStrDataLength: short;
161       function GetName: AnsiString; virtual; abstract;
162       function GetScale: integer; virtual; abstract;
163       function GetAsBoolean: boolean;
164       function GetAsCurrency: Currency;
165       function GetAsInt64: Int64;
166 <     function GetAsDateTime: TDateTime;
166 >     function GetAsDateTime: TDateTime; overload;
167 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
168 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
169 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime); overload;
170 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString; OnDate: TDateTime); overload;
171 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
172 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
173 >     function GetAsUTCDateTime: TDateTime;
174       function GetAsDouble: Double;
175       function GetAsFloat: Float;
176       function GetAsLong: Long;
# Line 142 | Line 182 | type
182       function GetIsNullable: boolean; virtual;
183       function GetAsVariant: Variant;
184       function GetModified: boolean; virtual;
185 +     function GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats): integer;
186 +     function GetAsBCD: tBCD;
187 +     function GetSize: cardinal; virtual; abstract;
188 +     function GetCharSetWidth: integer; virtual; abstract;
189       procedure SetAsBoolean(AValue: boolean); virtual;
190       procedure SetAsCurrency(Value: Currency); virtual;
191       procedure SetAsInt64(Value: Int64); virtual;
192       procedure SetAsDate(Value: TDateTime); virtual;
193       procedure SetAsLong(Value: Long); virtual;
194 <     procedure SetAsTime(Value: TDateTime); virtual;
195 <     procedure SetAsDateTime(Value: TDateTime);
194 >     procedure SetAsTime(Value: TDateTime); overload;
195 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime;aTimeZoneID: TFBTimeZoneID); overload;
196 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
197 >     procedure SetAsDateTime(Value: TDateTime); overload;
198 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
199 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
200 >     procedure SetAsUTCDateTime(aUTCTime: TDateTime);
201       procedure SetAsDouble(Value: Double); virtual;
202       procedure SetAsFloat(Value: Float); virtual;
203       procedure SetAsPointer(Value: Pointer);
# Line 157 | Line 206 | type
206       procedure SetAsString(Value: AnsiString); virtual;
207       procedure SetAsVariant(Value: Variant);
208       procedure SetAsNumeric(Value: Int64; aScale: integer);
209 +     procedure SetAsBcd(aValue: tBCD); virtual;
210       procedure SetIsNull(Value: Boolean); virtual;
211       procedure SetIsNullable(Value: Boolean); virtual;
212       procedure SetName(aValue: AnsiString); virtual;
# Line 190 | Line 240 | type
240  
241    TSQLDataArea = class
242    private
243 +    FCaseSensitiveParams: boolean;
244      function GetColumn(index: integer): TSQLVarData;
245      function GetCount: integer;
246    protected
# Line 212 | Line 263 | type
263        var data: PByte); virtual;
264      procedure RowChange;
265      function StateChanged(var ChangeSeqNo: integer): boolean; virtual; abstract;
266 +    property CaseSensitiveParams: boolean read FCaseSensitiveParams
267 +                                            write FCaseSensitiveParams; {Only used when IsInputDataArea true}
268 +    function CanChangeMetaData: boolean; virtual; abstract;
269      property Count: integer read GetCount;
270      property Column[index: integer]: TSQLVarData read GetColumn;
271      property UniqueRelationName: AnsiString read FUniqueRelationName;
# Line 233 | Line 287 | type
287      function GetStatement: IStatement;
288      procedure SetName(AValue: AnsiString);
289    protected
290 +    function GetAttachment: IAttachment; virtual; abstract;
291      function GetSQLType: cardinal; virtual; abstract;
292      function GetSubtype: integer; virtual; abstract;
293      function GetAliasName: AnsiString;  virtual; abstract;
# Line 241 | Line 296 | type
296      function GetRelationName: AnsiString;  virtual; abstract;
297      function GetScale: integer; virtual; abstract;
298      function GetCharSetID: cardinal; virtual; abstract;
299 +    function GetCharSetWidth: integer; virtual; abstract;
300      function GetCodePage: TSystemCodePage; virtual; abstract;
301      function GetIsNull: Boolean;   virtual; abstract;
302      function GetIsNullable: boolean; virtual; abstract;
303      function GetSQLData: PByte;  virtual; abstract;
304 <    function GetDataLength: cardinal; virtual; abstract;
304 >    function GetDataLength: cardinal; virtual; abstract; {current field length}
305 >    function GetSize: cardinal; virtual; abstract; {field length as given by metadata}
306 >    function GetDefaultTextSQLType: cardinal; virtual; abstract;
307      procedure SetIsNull(Value: Boolean); virtual; abstract;
308      procedure SetIsNullable(Value: Boolean);  virtual; abstract;
309      procedure SetSQLData(AValue: PByte; len: cardinal); virtual; abstract;
# Line 253 | Line 311 | type
311      procedure SetDataLength(len: cardinal); virtual; abstract;
312      procedure SetSQLType(aValue: cardinal); virtual; abstract;
313      procedure SetCharSetID(aValue: cardinal); virtual; abstract;
314 +    procedure SetMetaSize(aValue: cardinal); virtual;
315    public
316      constructor Create(aParent: TSQLDataArea; aIndex: integer);
317      procedure SetString(aValue: AnsiString);
# Line 294 | Line 353 | type
353      FIBXSQLVAR: TSQLVarData;
354      FOwner: IUnknown;         {Keep reference to ensure Metadata/statement not discarded}
355      FPrepareSeqNo: integer;
297    FStatement: IStatement;
356      FChangeSeqNo: integer;
357    protected
358      procedure CheckActive; override;
359 +    function GetAttachment: IAttachment; override;
360      function SQLData: PByte; override;
361      function GetDataLength: cardinal; override;
362      function GetCodePage: TSystemCodePage; override;
# Line 306 | Line 365 | type
365      constructor Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
366      destructor Destroy; override;
367      function GetSQLDialect: integer; override;
309    property Statement: IStatement read FStatement;
368  
369    public
370      {IColumnMetaData}
# Line 321 | Line 379 | type
379      function GetScale: integer; override;
380      function getCharSetID: cardinal; override;
381      function GetIsNullable: boolean; override;
382 <    function GetSize: cardinal;
382 >    function GetSize: cardinal; override;
383 >    function GetCharSetWidth: integer; override;
384      function GetArrayMetaData: IArrayMetaData;
385      function GetBlobMetaData: IBlobMetaData;
386 +    function GetStatement: IStatement;
387 +    function GetTransaction: ITransaction; virtual;
388      property Name: AnsiString read GetName;
389      property Size: cardinal read GetSize;
390      property CharSetID: cardinal read getCharSetID;
391      property SQLSubtype: integer read getSubtype;
392      property IsNullable: Boolean read GetIsNullable;
393 +  public
394 +    property Statement: IStatement read GetStatement;
395    end;
396  
397    { TIBSQLData }
398  
399    TIBSQLData = class(TColumnMetaData,ISQLData)
400 +  private
401 +    FTransaction: ITransaction;
402    protected
403      procedure CheckActive; override;
404    public
405 +    function GetTransaction: ITransaction; override;
406      function GetIsNull: Boolean; override;
407      function GetAsArray: IArray;
408      function GetAsBlob: IBlob; overload;
# Line 347 | Line 413 | type
413  
414    { TSQLParam }
415  
416 <  TSQLParam = class(TIBSQLData,ISQLParam)
416 >  TSQLParam = class(TIBSQLData,ISQLParam,ISQLData)
417    protected
418      procedure CheckActive; override;
419      procedure Changed; override;
# Line 359 | Line 425 | type
425      procedure Clear;
426      function GetModified: boolean; override;
427      function GetAsPointer: Pointer;
428 +    function GetAsString: AnsiString; override;
429      procedure SetName(Value: AnsiString); override;
430      procedure SetIsNull(Value: Boolean);  override;
431      procedure SetIsNullable(Value: Boolean); override;
# Line 370 | Line 437 | type
437      procedure SetAsInt64(AValue: Int64);
438      procedure SetAsDate(AValue: TDateTime);
439      procedure SetAsLong(AValue: Long);
440 <    procedure SetAsTime(AValue: TDateTime);
441 <    procedure SetAsDateTime(AValue: TDateTime);
440 >    procedure SetAsTime(AValue: TDateTime); overload;
441 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
442 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
443 >    procedure SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
444 >    procedure SetAsTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
445 >    procedure SetAsDateTime(AValue: TDateTime); overload;
446 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
447 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
448      procedure SetAsDouble(AValue: Double);
449      procedure SetAsFloat(AValue: Float);
450      procedure SetAsPointer(AValue: Pointer);
# Line 381 | Line 454 | type
454      procedure SetAsBlob(aValue: IBlob);
455      procedure SetAsQuad(AValue: TISC_QUAD);
456      procedure SetCharSetID(aValue: cardinal);
457 +    procedure SetAsBcd(aValue: tBCD);
458  
459      property AsBlob: IBlob read GetAsBlob write SetAsBlob;
460      property IsNullable: Boolean read GetIsNullable write SetIsNullable;
# Line 423 | Line 497 | type
497      function getSQLParam(index: integer): ISQLParam;
498      function ByName(Idx: AnsiString): ISQLParam ;
499      function GetModified: Boolean;
500 +    function GetHasCaseSensitiveParams: Boolean;
501    end;
502  
503    { TResults }
# Line 444 | Line 519 | type
519       function ByName(Idx: AnsiString): ISQLData;
520       function getSQLData(index: integer): ISQLData;
521       procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PByte);
522 +     function GetStatement: IStatement;
523       function GetTransaction: ITransaction; virtual;
524       procedure SetRetainInterfaces(aValue: boolean);
525   end;
526  
527   implementation
528  
529 < uses FBMessages, variants, IBUtils, FBTransaction;
454 <
455 < type
456 <
457 <   { TSQLParamProcessor }
458 <
459 <   TSQLParamProcessor = class(TSQLwithNamedParamsTokeniser)
460 <   private
461 <   const
462 <     sIBXParam = 'IBXParam';  {do not localize}
463 <   private
464 <     FInString: AnsiString;
465 <     FIndex: integer;
466 <     function DoExecute(GenerateParamNames: boolean;
467 <       var slNames: TStrings): AnsiString;
468 <   protected
469 <     function GetChar: AnsiChar; override;
470 <   public
471 <     class function Execute(sSQL: AnsiString; GenerateParamNames: boolean;
472 <       var slNames: TStrings): AnsiString;
473 <   end;
474 <
475 < { TSQLParamProcessor }
476 <
477 < function TSQLParamProcessor.DoExecute(GenerateParamNames: boolean;
478 <  var slNames: TStrings): AnsiString;
479 < var token: TSQLTokens;
480 <    iParamSuffix: Integer;
481 < begin
482 <  Result := '';
483 <  iParamSuffix := 0;
484 <
485 <  while not EOF do
486 <  begin
487 <    token := GetNextToken;
488 <    case token of
489 <    sqltParam,
490 <    sqltQuotedParam:
491 <      begin
492 <        Result := Result + '?';
493 <        slNames.Add(TokenText);
494 <      end;
495 <
496 <    sqltPlaceHolder:
497 <      if GenerateParamNames then
498 <      begin
499 <        Inc(iParamSuffix);
500 <        slNames.AddObject(sIBXParam + IntToStr(iParamSuffix),self); //Note local convention
501 <                                            //add pointer to self to mark entry
502 <        Result := Result + '?';
503 <      end
504 <      else
505 <        IBError(ibxeSQLParseError, [SParamNameExpected]);
506 <
507 <    sqltQuotedString:
508 <      Result := Result + '''' + SQLSafeString(TokenText) + '''';
509 <
510 <    sqltIdentifierInDoubleQuotes:
511 <      Result := Result + '"' + StringReplace(TokenText,'"','""',[rfReplaceAll]) + '"';
512 <
513 <    sqltComment:
514 <      Result := Result + '/*' + TokenText + '*/';
515 <
516 <    sqltCommentLine:
517 <      Result := Result + '//' + TokenText + LineEnding;
518 <
519 <    sqltEOL:
520 <      Result := Result + LineEnding;
521 <
522 <    else
523 <      Result := Result + TokenText;
524 <    end;
525 <  end;
526 < end;
527 <
528 < function TSQLParamProcessor.GetChar: AnsiChar;
529 < begin
530 <  if FIndex <= Length(FInString) then
531 <  begin
532 <    Result := FInString[FIndex];
533 <    Inc(FIndex);
534 <  end
535 <  else
536 <    Result := #0;
537 < end;
538 <
539 < class function TSQLParamProcessor.Execute(sSQL: AnsiString;
540 <  GenerateParamNames: boolean; var slNames: TStrings): AnsiString;
541 < begin
542 <  with self.Create do
543 <  try
544 <    FInString := sSQL;
545 <    FIndex := 1;
546 <    Result := DoExecute(GenerateParamNames,slNames);
547 <  finally
548 <    Free;
549 <  end;
550 < end;
551 <
529 > uses FBMessages, variants, IBUtils, FBTransaction, DateUtils;
530  
531   { TSQLDataArea }
532  
# Line 654 | Line 632 | var
632    s: AnsiString;
633    i: Integer;
634   begin
635 <  {$ifdef UseCaseInSensitiveParamName}
636 <   s := AnsiUpperCase(Idx);
637 <  {$else}
635 >  if not IsInputDataArea or not CaseSensitiveParams then
636 >   s := AnsiUpperCase(Idx)
637 >  else
638     s := Idx;
639 <  {$endif}
639 >
640    for i := 0 to Count - 1 do
641      if Column[i].Name = s then
642      begin
# Line 690 | Line 668 | end;
668  
669   procedure TSQLVarData.SetName(AValue: AnsiString);
670   begin
671 <  if FName = AValue then Exit;
694 <  {$ifdef UseCaseInSensitiveParamName}
695 <  if Parent.IsInputDataArea then
671 >  if not Parent.IsInputDataArea or not Parent.CaseSensitiveParams then
672      FName := AnsiUpperCase(AValue)
673    else
698  {$endif}
674      FName := AValue;
675   end;
676  
677 + procedure TSQLVarData.SetMetaSize(aValue: cardinal);
678 + begin
679 +  //Ignore
680 + end;
681 +
682   constructor TSQLVarData.Create(aParent: TSQLDataArea; aIndex: integer);
683   begin
684    inherited Create;
# Line 715 | Line 695 | begin
695     a zero byte when the string is empty, neatly avoiding a nil pointer error.}
696  
697    FVarString := aValue;
698 <  SQLType := SQL_TEXT;
698 >  if SQLType = SQL_BLOB then
699 >    SetMetaSize(GetAttachment.GetInlineBlobLimit);
700 >  SQLType := GetDefaultTextSQLType;
701 >  Scale := 0;
702    SetSQLData(PByte(PAnsiChar(FVarString)),Length(aValue));
703   end;
704  
# Line 876 | Line 859 | begin
859        result := Value;
860   end;
861  
862 + function TSQLDataItem.GetDateFormatStr(IncludeTime: boolean): AnsiString;
863 + begin
864 +  {$IF declared(DefaultFormatSettings)}
865 +  with DefaultFormatSettings do
866 +  {$ELSE}
867 +  {$IF declared(FormatSettings)}
868 +  with FormatSettings do
869 +  {$IFEND}
870 +  {$IFEND}
871 +  case GetSQLDialect of
872 +    1:
873 +      if IncludeTime then
874 +        result := ShortDateFormat + ' ' + LongTimeFormat
875 +      else
876 +        result := ShortDateFormat;
877 +    3:
878 +      result := ShortDateFormat;
879 +  end;
880 + end;
881 +
882 + function TSQLDataItem.GetTimeFormatStr: AnsiString;
883 + begin
884 +  {$IF declared(DefaultFormatSettings)}
885 +  with DefaultFormatSettings do
886 +  {$ELSE}
887 +  {$IF declared(FormatSettings)}
888 +  with FormatSettings do
889 +  {$IFEND}
890 +  {$IFEND}
891 +    Result := 'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';;
892 + end;
893 +
894 + function TSQLDataItem.GetTimestampFormatStr: AnsiString;
895 + begin
896 +  {$IF declared(DefaultFormatSettings)}
897 +  with DefaultFormatSettings do
898 +  {$ELSE}
899 +  {$IF declared(FormatSettings)}
900 +  with FormatSettings do
901 +  {$IFEND}
902 +  {$IFEND}
903 +    Result := ShortDateFormat + ' ' +  'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';
904 + end;
905 +
906   procedure TSQLDataItem.SetAsInteger(AValue: Integer);
907   begin
908    SetAsLong(aValue);
909   end;
910  
911 + procedure TSQLDataItem.InternalGetAsDateTime(var aDateTime: TDateTime;
912 +  var dstOffset: smallint; var aTimezone: AnsiString;
913 +  var aTimeZoneID: TFBTimeZoneID);
914 + begin
915 +  CheckActive;
916 +  aDateTime := 0;
917 +  dstOffset := 0;
918 +  aTimezone := '';
919 +  aTimeZoneID := TimeZoneID_GMT;
920 +  if not IsNull then
921 +    with FFirebirdClientAPI do
922 +    case SQLType of
923 +      SQL_TEXT, SQL_VARYING:
924 +        if not ParseDateTimeTZString(AsString,aDateTime,aTimeZone) then
925 +          IBError(ibxeInvalidDataConversion, [nil]);
926 +      SQL_TYPE_DATE:
927 +        aDateTime := SQLDecodeDate(SQLData);
928 +      SQL_TYPE_TIME:
929 +        aDateTime := SQLDecodeTime(SQLData);
930 +      SQL_TIMESTAMP:
931 +        aDateTime := SQLDecodeDateTime(SQLData);
932 +      SQL_TIMESTAMP_TZ:
933 +        begin
934 +          GetTimeZoneServices.DecodeTimestampTZ(SQLData,aDateTime,dstOffset,aTimeZone);
935 +          aTimeZoneID := PISC_TIMESTAMP_TZ(SQLData)^.time_zone;
936 +        end;
937 +      SQL_TIMESTAMP_TZ_EX:
938 +      begin
939 +        GetTimeZoneServices.DecodeTimestampTZEx(SQLData,aDateTime,dstOffset,aTimeZone);
940 +        aTimeZoneID := PISC_TIMESTAMP_TZ_EX(SQLData)^.time_zone;
941 +      end;
942 +      SQL_TIME_TZ:
943 +        with GetTimeZoneServices do
944 +        begin
945 +          DecodeTimeTZ(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
946 +          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
947 +        end;
948 +      SQL_TIME_TZ_EX:
949 +        with GetTimeZoneServices do
950 +        begin
951 +          DecodeTimeTZEx(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
952 +          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
953 +        end;
954 +      else
955 +        IBError(ibxeInvalidDataConversion, [nil]);
956 +    end;
957 + end;
958 +
959   function TSQLDataItem.AdjustScaleFromCurrency(Value: Currency; aScale: Integer
960    ): Int64;
961   var
# Line 936 | Line 1011 | begin
1011    //Do nothing by default
1012   end;
1013  
1014 + procedure TSQLDataItem.CheckTZSupport;
1015 + begin
1016 +  if not FFirebirdClientAPI.HasTimeZoneSupport then
1017 +    IBError(ibxeNoTimezoneSupport,[]);
1018 + end;
1019 +
1020 + function TSQLDataItem.GetTimeZoneServices: IExTimeZoneServices;
1021 + begin
1022 +  if FTimeZoneServices = nil then
1023 +  begin
1024 +    if not GetAttachment.HasTimeZoneSupport then
1025 +      IBError(ibxeNoTimezoneSupport,[]);
1026 +    GetAttachment.GetTimeZoneServices.QueryInterface(IExTimeZoneServices,FTimeZoneServices);
1027 +  end;
1028 +  Result := FTimeZoneServices;
1029 + end;
1030 +
1031   procedure TSQLDataItem.Changed;
1032   begin
1033    //Do nothing by default
# Line 996 | Line 1088 | begin
1088    SQL_LONG:             Result := 'SQL_LONG';
1089    SQL_SHORT:            Result := 'SQL_SHORT';
1090    SQL_TIMESTAMP:        Result := 'SQL_TIMESTAMP';
1091 +  SQL_TIMESTAMP_TZ:     Result := 'SQL_TIMESTAMP_TZ';
1092 +  SQL_TIMESTAMP_TZ_EX:  Result := 'SQL_TIMESTAMP_TZ_EX';
1093    SQL_BLOB:             Result := 'SQL_BLOB';
1094    SQL_D_FLOAT:          Result := 'SQL_D_FLOAT';
1095    SQL_ARRAY:            Result := 'SQL_ARRAY';
# Line 1003 | Line 1097 | begin
1097    SQL_TYPE_TIME:        Result := 'SQL_TYPE_TIME';
1098    SQL_TYPE_DATE:        Result := 'SQL_TYPE_DATE';
1099    SQL_INT64:            Result := 'SQL_INT64';
1100 +  SQL_TIME_TZ:          Result := 'SQL_TIME_TZ';
1101 +  SQL_TIME_TZ_EX:       Result := 'SQL_TIME_TZ_EX';
1102 +  SQL_DEC_FIXED:        Result := 'SQL_DEC_FIXED';
1103 +  SQL_DEC16:            Result := 'SQL_DEC16';
1104 +  SQL_DEC34:            Result := 'SQL_DEC34';
1105 +  SQL_INT128:           Result := 'SQL_INT128';
1106 +  SQL_NULL:             Result := 'SQL_NULL';
1107 +  SQL_BOOLEAN:          Result := 'SQL_BOOLEAN';
1108    end;
1109   end;
1110  
1111 + function TSQLDataItem.GetStrDataLength: short;
1112 + begin
1113 +  with FFirebirdClientAPI do
1114 +  if SQLType = SQL_VARYING then
1115 +    Result := DecodeInteger(SQLData, 2)
1116 +  else
1117 +    Result := DataLength;
1118 + end;
1119 +
1120   function TSQLDataItem.GetAsBoolean: boolean;
1121   begin
1122    CheckActive;
# Line 1045 | Line 1156 | begin
1156            result := AdjustScaleToCurrency(PInt64(SQLData)^,
1157                                        Scale);
1158          SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1159 <          result := Trunc(AsDouble);
1159 >          result := Round(AsDouble);
1160 >
1161 >        SQL_DEC_FIXED,
1162 >        SQL_DEC16,
1163 >        SQL_DEC34,
1164 >        SQL_INT128:
1165 >          if not BCDToCurr(GetAsBCD,Result) then
1166 >            IBError(ibxeInvalidDataConversion, [nil]);
1167 >
1168          else
1169            IBError(ibxeInvalidDataConversion, [nil]);
1170        end;
# Line 1075 | Line 1194 | begin
1194          result := AdjustScaleToInt64(PInt64(SQLData)^,
1195                                      Scale);
1196        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1197 <        result := Trunc(AsDouble);
1197 >        result := Round(AsDouble);
1198        else
1199          IBError(ibxeInvalidDataConversion, [nil]);
1200      end;
1201   end;
1202  
1203   function TSQLDataItem.GetAsDateTime: TDateTime;
1204 + var aTimezone: AnsiString;
1205 +    aTimeZoneID: TFBTimeZoneID;
1206 +    dstOffset: smallint;
1207 + begin
1208 +  InternalGetAsDateTime(Result,dstOffset,aTimeZone,aTimeZoneID);
1209 + end;
1210 +
1211 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime;
1212 +  var dstOffset: smallint; var aTimezone: AnsiString);
1213 + var aTimeZoneID: TFBTimeZoneID;
1214 + begin
1215 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1216 + end;
1217 +
1218 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
1219 +  var aTimezoneID: TFBTimeZoneID);
1220 + var aTimezone: AnsiString;
1221 + begin
1222 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1223 + end;
1224 +
1225 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1226 +  var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime);
1227 + var aTimeZone: AnsiString;
1228   begin
1229    CheckActive;
1230 <  result := 0;
1230 >  aTime := 0;
1231 >  dstOffset := 0;
1232    if not IsNull then
1233      with FFirebirdClientAPI do
1234      case SQLType of
1235 <      SQL_TEXT, SQL_VARYING: begin
1236 <        try
1237 <          result := StrToDate(AsString);
1238 <        except
1239 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
1235 >      SQL_TIME_TZ:
1236 >        begin
1237 >          GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1238 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1239 >        end;
1240 >      SQL_TIME_TZ_EX:
1241 >        begin
1242 >          GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1243 >          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1244          end;
1245 +    else
1246 +      IBError(ibxeInvalidDataConversion, [nil]);
1247 +    end;
1248 + end;
1249 +
1250 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1251 +  var aTimezone: AnsiString; OnDate: TDateTime);
1252 + begin
1253 +  CheckActive;
1254 +  aTime := 0;
1255 +  dstOffset := 0;
1256 +  if not IsNull then
1257 +    with FFirebirdClientAPI do
1258 +    case SQLType of
1259 +      SQL_TIME_TZ:
1260 +        GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1261 +      SQL_TIME_TZ_EX:
1262 +        GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1263 +    else
1264 +      IBError(ibxeInvalidDataConversion, [nil]);
1265 +    end;
1266 + end;
1267 +
1268 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1269 +  var aTimezoneID: TFBTimeZoneID);
1270 + begin
1271 +  GetAsTime(aTime,dstOffset,aTimeZoneID,GetTimeZoneServices.GetTimeTZDate);
1272 + end;
1273 +
1274 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1275 +  var aTimezone: AnsiString);
1276 + begin
1277 +  GetAsTime(aTime,dstOffset,aTimeZone,GetTimeZoneServices.GetTimeTZDate);
1278 + end;
1279 +
1280 + function TSQLDataItem.GetAsUTCDateTime: TDateTime;
1281 + var aTimezone: AnsiString;
1282 + begin
1283 +  CheckActive;
1284 +  result := 0;
1285 +  aTimezone := '';
1286 +  if not IsNull then
1287 +    with FFirebirdClientAPI do
1288 +    case SQLType of
1289 +      SQL_TEXT, SQL_VARYING:
1290 +      begin
1291 +        if not ParseDateTimeTZString(AsString,Result,aTimeZone) then
1292 +          IBError(ibxeInvalidDataConversion, [nil]);
1293 +        Result := GetTimeZoneServices.LocalTimeToGMT(Result,aTimeZone);
1294        end;
1295        SQL_TYPE_DATE:
1296          result := SQLDecodeDate(SQLData);
1297 <      SQL_TYPE_TIME:
1297 >      SQL_TYPE_TIME,
1298 >      SQL_TIME_TZ,
1299 >      SQL_TIME_TZ_EX:
1300          result := SQLDecodeTime(SQLData);
1301 <      SQL_TIMESTAMP:
1301 >      SQL_TIMESTAMP,
1302 >      SQL_TIMESTAMP_TZ,
1303 >      SQL_TIMESTAMP_TZ_EX:
1304          result := SQLDecodeDateTime(SQLData);
1305        else
1306          IBError(ibxeInvalidDataConversion, [nil]);
1307 <    end;
1307 >      end;
1308   end;
1309  
1310   function TSQLDataItem.GetAsDouble: Double;
# Line 1131 | Line 1332 | begin
1332          result := PFloat(SQLData)^;
1333        SQL_DOUBLE, SQL_D_FLOAT:
1334          result := PDouble(SQLData)^;
1335 +      SQL_DEC_FIXED,
1336 +      SQL_DEC16,
1337 +      SQL_DEC34,
1338 +      SQL_INT128:
1339 +        Result := BCDToDouble(GetAsBCD);
1340        else
1341          IBError(ibxeInvalidDataConversion, [nil]);
1342      end;
# Line 1167 | Line 1373 | begin
1373          end;
1374        end;
1375        SQL_SHORT:
1376 <        result := Trunc(AdjustScale(Int64(PShort(SQLData)^),
1376 >        result := Round(AdjustScale(Int64(PShort(SQLData)^),
1377                                      Scale));
1378        SQL_LONG:
1379 <        result := Trunc(AdjustScale(Int64(PLong(SQLData)^),
1379 >        result := Round(AdjustScale(Int64(PLong(SQLData)^),
1380                                      Scale));
1381        SQL_INT64:
1382 <        result := Trunc(AdjustScale(PInt64(SQLData)^, Scale));
1382 >        result := Round(AdjustScale(PInt64(SQLData)^, Scale));
1383        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1384 <        result := Trunc(AsDouble);
1384 >        result := Round(AsDouble);
1385 >      SQL_DEC_FIXED,
1386 >      SQL_DEC16,
1387 >      SQL_DEC34,
1388 >      SQL_INT128:
1389 >        Result := BCDToInteger(GetAsBCD);
1390        else
1391 <        IBError(ibxeInvalidDataConversion, [nil]);
1391 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1392      end;
1393   end;
1394  
# Line 1215 | Line 1426 | begin
1426    end;
1427   end;
1428  
1429 + {Copied from LazUTF8}
1430 +
1431 + function UTF8CodepointSizeFull(p: PAnsiChar): integer;
1432 + const TopBitSetMask   = $80; {%10000000}
1433 +      Top2BitsSetMask = $C0; {%11000000}
1434 +      Top3BitsSetMask = $E0; {%11100000}
1435 +      Top4BitsSetMask = $F0; {%11110000}
1436 +      Top5BitsSetMask = $F8; {%11111000}
1437 + begin
1438 +  case p^ of
1439 +  #0..#191: // %11000000
1440 +    // regular single byte character (#0 is a character, this is Pascal ;)
1441 +    Result:=1;
1442 +  #192..#223: // p^ and %11100000 = %11000000
1443 +    begin
1444 +      // could be 2 byte character
1445 +      if (ord(p[1]) and Top2BitsSetMask) = TopBitSetMask then
1446 +        Result:=2
1447 +      else
1448 +        Result:=1;
1449 +    end;
1450 +  #224..#239: // p^ and %11110000 = %11100000
1451 +    begin
1452 +      // could be 3 byte character
1453 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1454 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask) then
1455 +        Result:=3
1456 +      else
1457 +        Result:=1;
1458 +    end;
1459 +  #240..#247: // p^ and %11111000 = %11110000
1460 +    begin
1461 +      // could be 4 byte character
1462 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1463 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask)
1464 +      and ((ord(p[3]) and Top2BitsSetMask) = TopBitSetMask) then
1465 +        Result:=4
1466 +      else
1467 +        Result:=1;
1468 +    end;
1469 +  else
1470 +    Result:=1;
1471 +  end;
1472 + end;
1473 +
1474 + {Returns the byte length of a UTF8 string with a fixed charwidth}
1475 +
1476 + function GetStrLen(p: PAnsiChar; FieldWidth, MaxDataLength: cardinal): integer;
1477 + var i: integer;
1478 +    cplen: integer;
1479 +    s: AnsiString;
1480 + begin
1481 +  Result := 0;
1482 +  s := strpas(p);
1483 +  for i := 1 to FieldWidth do
1484 +  begin
1485 +    cplen := UTF8CodepointSizeFull(p);
1486 +    Inc(p,cplen);
1487 +    Inc(Result,cplen);
1488 +    if Result >= MaxDataLength then
1489 +    begin
1490 +      Result := MaxDataLength;
1491 +      Exit;
1492 +    end;
1493 +  end;
1494 + end;
1495  
1496   function TSQLDataItem.GetAsString: AnsiString;
1497   var
1498    sz: PByte;
1499    str_len: Integer;
1500    rs: RawByteString;
1501 +  aTimeZone: AnsiString;
1502 +  aDateTime: TDateTime;
1503 +  dstOffset: smallint;
1504   begin
1505    CheckActive;
1506    result := '';
# Line 1238 | Line 1518 | begin
1518        begin
1519          sz := SQLData;
1520          if (SQLType = SQL_TEXT) then
1521 <          str_len := DataLength
1521 >        begin
1522 >          if GetCodePage = cp_utf8 then
1523 >            str_len := GetStrLen(PAnsiChar(sz),GetSize div GetCharSetWidth,DataLength)
1524 >          else
1525 >            str_len := DataLength
1526 >        end
1527          else begin
1528 <          str_len := DecodeInteger(SQLData, 2);
1528 >          str_len := DecodeInteger(sz, 2);
1529            Inc(sz, 2);
1530          end;
1531          SetString(rs, PAnsiChar(sz), str_len);
1532          SetCodePage(rs,GetCodePage,false);
1533 <        if (SQLType = SQL_TEXT) and (GetCharSetID <> 1) then
1249 <          Result := TrimRight(rs)
1250 <        else
1251 <          Result := rs
1533 >        Result := rs;
1534        end;
1535 +
1536        SQL_TYPE_DATE:
1537 <        case GetSQLDialect of
1255 <          1 : result := DateTimeToStr(AsDateTime);
1256 <          3 : result := DateToStr(AsDateTime);
1257 <        end;
1258 <      SQL_TYPE_TIME :
1259 <        result := TimeToStr(AsDateTime);
1537 >        Result := DateToStr(GetAsDateTime);
1538        SQL_TIMESTAMP:
1539 <      {$IF declared(DefaultFormatSettings)}
1540 <      with DefaultFormatSettings do
1541 <      {$ELSE}
1542 <      {$IF declared(FormatSettings)}
1543 <      with FormatSettings do
1544 <      {$IFEND}
1545 <      {$IFEND}
1546 <        result := FormatDateTime(ShortDateFormat + ' ' +
1547 <                            LongTimeFormat+'.zzz',AsDateTime);
1539 >        Result := FBFormatDateTime(GetTimestampFormatStr,GetAsDateTime);
1540 >      SQL_TYPE_TIME:
1541 >        Result := FBFormatDateTime(GetTimeFormatStr,GetAsDateTime);
1542 >      SQL_TIMESTAMP_TZ,
1543 >      SQL_TIMESTAMP_TZ_EX:
1544 >        with GetAttachment.GetTimeZoneServices do
1545 >        begin
1546 >          if GetTZTextOption = tzGMT then
1547 >            Result := FBFormatDateTime(GetTimestampFormatStr,GetAsUTCDateTime)
1548 >          else
1549 >          begin
1550 >            GetAsDateTime(aDateTime,dstOffset,aTimeZone);
1551 >            if GetTZTextOption = tzOffset then
1552 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1553 >            else
1554 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + aTimeZone;
1555 >          end;
1556 >        end;
1557 >      SQL_TIME_TZ,
1558 >      SQL_TIME_TZ_EX:
1559 >        with GetAttachment.GetTimeZoneServices do
1560 >        begin
1561 >          if GetTZTextOption = tzGMT then
1562 >             Result := FBFormatDateTime(GetTimeFormatStr,GetAsUTCDateTime)
1563 >          else
1564 >          begin
1565 >            GetAsTime(aDateTime,dstOffset,aTimeZone,GetTimeTZDate);
1566 >            if GetTZTextOption = tzOffset then
1567 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1568 >            else
1569 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + aTimeZone;
1570 >          end;
1571 >        end;
1572 >
1573        SQL_SHORT, SQL_LONG:
1574          if Scale = 0 then
1575            result := IntToStr(AsLong)
# Line 1283 | Line 1586 | begin
1586            result := FloatToStr(AsDouble);
1587        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1588          result := FloatToStr(AsDouble);
1589 +
1590 +      SQL_DEC16,
1591 +      SQL_DEC34:
1592 +        result := BCDToStr(GetAsBCD);
1593 +
1594 +      SQL_DEC_FIXED,
1595 +      SQL_INT128:
1596 +        result := Int128ToStr(SQLData,scale);
1597 +
1598        else
1599          IBError(ibxeInvalidDataConversion, [nil]);
1600      end;
# Line 1294 | Line 1606 | begin
1606    Result := false;
1607   end;
1608  
1609 < function TSQLDataItem.getIsNullable: boolean;
1609 > function TSQLDataItem.GetIsNullable: boolean;
1610   begin
1611    CheckActive;
1612    Result := false;
1613   end;
1614  
1615   function TSQLDataItem.GetAsVariant: Variant;
1616 + var ts: TDateTime;
1617 +  dstOffset: smallint;
1618 +    timezone: AnsiString;
1619   begin
1620    CheckActive;
1621    if IsNull then
# Line 1314 | Line 1629 | begin
1629          result := AsString;
1630        SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
1631          result := AsDateTime;
1632 +      SQL_TIMESTAMP_TZ,
1633 +      SQL_TIME_TZ,
1634 +      SQL_TIMESTAMP_TZ_EX,
1635 +      SQL_TIME_TZ_EX:
1636 +        begin
1637 +          GetAsDateTime(ts,dstOffset,timezone);
1638 +          result := VarArrayOf([ts,dstOffset,timezone]);
1639 +        end;
1640        SQL_SHORT, SQL_LONG:
1641          if Scale = 0 then
1642            result := AsLong
# Line 1332 | Line 1655 | begin
1655          result := AsDouble;
1656        SQL_BOOLEAN:
1657          result := AsBoolean;
1658 +      SQL_DEC_FIXED,
1659 +      SQL_DEC16,
1660 +      SQL_DEC34,
1661 +      SQL_INT128:
1662 +        result := VarFmtBCDCreate(GetAsBcd);
1663        else
1664          IBError(ibxeInvalidDataConversion, [nil]);
1665      end;
# Line 1342 | Line 1670 | begin
1670    Result := false;
1671   end;
1672  
1673 + function TSQLDataItem.GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats
1674 +  ): integer;
1675 + begin
1676 +  case DateTimeFormat of
1677 +  dfTimestamp:
1678 +    Result := Length(GetTimestampFormatStr);
1679 +  dfDateTime:
1680 +    Result := Length(GetDateFormatStr(true));
1681 +  dfTime:
1682 +    Result := Length(GetTimeFormatStr);
1683 +  dfTimestampTZ:
1684 +    Result := Length(GetTimestampFormatStr) + 6; {assume time offset format}
1685 +  dfTimeTZ:
1686 +    Result := Length(GetTimeFormatStr)+ 6;
1687 +  else
1688 +    Result := 0;
1689 +  end;end;
1690 +
1691 + function TSQLDataItem.GetAsBCD: tBCD;
1692 +
1693 + begin
1694 +  CheckActive;
1695 +  if IsNull then
1696 +   with Result do
1697 +   begin
1698 +     FillChar(Result,sizeof(Result),0);
1699 +     Precision := 1;
1700 +     exit;
1701 +   end;
1702 +
1703 +  case SQLType of
1704 +  SQL_DEC16,
1705 +  SQL_DEC34:
1706 +    with FFirebirdClientAPI do
1707 +      Result := SQLDecFloatDecode(SQLType,  SQLData);
1708 +
1709 +  SQL_DEC_FIXED,
1710 +  SQL_INT128:
1711 +    with FFirebirdClientAPI do
1712 +      Result := StrToBCD(Int128ToStr(SQLData,scale));
1713 +  else
1714 +    if not CurrToBCD(GetAsCurrency,Result) then
1715 +      IBError(ibxeBadBCDConversion,[]);
1716 +  end;
1717 + end;
1718 +
1719  
1720   procedure TSQLDataItem.SetIsNull(Value: Boolean);
1721   begin
# Line 1430 | Line 1804 | begin
1804    Changed;
1805   end;
1806  
1807 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
1808 + begin
1809 +  CheckActive;
1810 +  CheckTZSupport;
1811 +  if GetSQLDialect < 3 then
1812 +  begin
1813 +    AsDateTime := aValue;
1814 +    exit;
1815 +  end;
1816 +
1817 +  Changing;
1818 +  if IsNullable then
1819 +    IsNull := False;
1820 +
1821 +  SQLType := SQL_TIME_TZ;
1822 +  DataLength := SizeOf(ISC_TIME_TZ);
1823 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZoneID,OnDate,SQLData);
1824 +  Changed;
1825 + end;
1826 +
1827 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
1828 + begin
1829 +  CheckActive;
1830 +  CheckTZSupport;
1831 +  if GetSQLDialect < 3 then
1832 +  begin
1833 +    AsDateTime := aValue;
1834 +    exit;
1835 +  end;
1836 +
1837 +  Changing;
1838 +  if IsNullable then
1839 +    IsNull := False;
1840 +
1841 +  SQLType := SQL_TIME_TZ;
1842 +  DataLength := SizeOf(ISC_TIME_TZ);
1843 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZone,OnDate,SQLData);
1844 +  Changed;
1845 + end;
1846 +
1847   procedure TSQLDataItem.SetAsDateTime(Value: TDateTime);
1848   begin
1849    CheckActive;
# Line 1444 | Line 1858 | begin
1858    Changed;
1859   end;
1860  
1861 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime;
1862 +  aTimeZoneID: TFBTimeZoneID);
1863 + begin
1864 +  CheckActive;
1865 +  CheckTZSupport;
1866 +  if IsNullable then
1867 +    IsNull := False;
1868 +
1869 +  Changing;
1870 +  SQLType := SQL_TIMESTAMP_TZ;
1871 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1872 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZoneID,SQLData);
1873 +  Changed;
1874 + end;
1875 +
1876 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString
1877 +  );
1878 + begin
1879 +  CheckActive;
1880 +  CheckTZSupport;
1881 +  if IsNullable then
1882 +    IsNull := False;
1883 +
1884 +  Changing;
1885 +  SQLType := SQL_TIMESTAMP_TZ;
1886 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1887 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZone,SQLData);
1888 +  Changed;
1889 + end;
1890 +
1891 + procedure TSQLDataItem.SetAsUTCDateTime(aUTCTime: TDateTime);
1892 + begin
1893 +  SetAsDateTime(aUTCTime,TimeZoneID_GMT);
1894 + end;
1895 +
1896   procedure TSQLDataItem.SetAsDouble(Value: Double);
1897   begin
1898    CheckActive;
# Line 1539 | Line 1988 | begin
1988    CheckActive;
1989    if VarIsNull(Value) then
1990      IsNull := True
1991 +  else
1992 +  if VarIsArray(Value) then {must be datetime plus timezone}
1993 +    SetAsDateTime(Value[0],AnsiString(Value[1]))
1994    else case VarType(Value) of
1995      varEmpty, varNull:
1996        IsNull := True;
# Line 1561 | Line 2013 | begin
2013        IBError(ibxeNotSupported, [nil]);
2014      varByRef, varDispatch, varError, varUnknown, varVariant:
2015        IBError(ibxeNotPermitted, [nil]);
2016 +    else
2017 +      if VarIsFmtBCD(Value) then
2018 +        SetAsBCD(VarToBCD(Value))
2019 +      else
2020 +        IBError(ibxeNotSupported, [nil]);
2021    end;
2022   end;
2023  
# Line 1578 | Line 2035 | begin
2035    Changed;
2036   end;
2037  
2038 + procedure TSQLDataItem.SetAsBcd(aValue: tBCD);
2039 + var C: Currency;
2040 + begin
2041 +  CheckActive;
2042 +  Changing;
2043 +  if IsNullable then
2044 +    IsNull := False;
2045 +
2046 +
2047 +  with FFirebirdClientAPI do
2048 +  if aValue.Precision <= 16 then
2049 +  begin
2050 +    if not HasDecFloatSupport then
2051 +      IBError(ibxeDecFloatNotSupported,[]);
2052 +
2053 +    SQLType := SQL_DEC16;
2054 +    DataLength := 8;
2055 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2056 +  end
2057 +  else
2058 +  if aValue.Precision <= 34 then
2059 +  begin
2060 +    if not HasDecFloatSupport then
2061 +      IBError(ibxeDecFloatNotSupported,[]);
2062 +
2063 +    SQLType := SQL_DEC34;
2064 +    DataLength := 16;
2065 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2066 +  end
2067 +  else
2068 +  if aValue.Precision <= 38 then
2069 +  begin
2070 +    if not HasInt128Support then
2071 +      IBError(ibxeInt128NotSupported,[]);
2072 +
2073 +    SQLType := SQL_INT128;
2074 +    DataLength := 16;
2075 +    StrToInt128(scale,BcdToStr(aValue),SQLData);
2076 +  end
2077 +  else
2078 +    IBError(ibxeBCDOverflow,[BCDToStr(aValue)]);
2079 +
2080 +  Changed;
2081 + end;
2082 +
2083   procedure TSQLDataItem.SetAsBoolean(AValue: boolean);
2084   begin
2085    CheckActive;
# Line 1608 | Line 2110 | begin
2110      IBError(ibxeStatementNotPrepared, [nil]);
2111   end;
2112  
2113 + function TColumnMetaData.GetAttachment: IAttachment;
2114 + begin
2115 +  Result := GetStatement.GetAttachment;
2116 + end;
2117 +
2118   function TColumnMetaData.SQLData: PByte;
2119   begin
2120    Result := FIBXSQLVAR.SQLData;
# Line 1712 | Line 2219 | end;
2219   function TColumnMetaData.GetSize: cardinal;
2220   begin
2221    CheckActive;
2222 <  result := FIBXSQLVAR.DataLength;
2222 >  result := FIBXSQLVAR.GetSize;
2223 > end;
2224 >
2225 > function TColumnMetaData.GetCharSetWidth: integer;
2226 > begin
2227 >  CheckActive;
2228 >  result := FIBXSQLVAR.GetCharSetWidth;
2229   end;
2230  
2231   function TColumnMetaData.GetArrayMetaData: IArrayMetaData;
# Line 1727 | Line 2240 | begin
2240    result := FIBXSQLVAR.GetBlobMetaData;
2241   end;
2242  
2243 + function TColumnMetaData.GetStatement: IStatement;
2244 + begin
2245 +  Result := FIBXSQLVAR.GetStatement;
2246 + end;
2247 +
2248 + function TColumnMetaData.GetTransaction: ITransaction;
2249 + begin
2250 +  Result := GetStatement.GetTransaction;
2251 + end;
2252 +
2253   { TIBSQLData }
2254  
2255   procedure TIBSQLData.CheckActive;
# Line 1746 | Line 2269 | begin
2269      IBError(ibxeBOF,[nil]);
2270   end;
2271  
2272 + function TIBSQLData.GetTransaction: ITransaction;
2273 + begin
2274 +  if FTransaction = nil then
2275 +    Result := inherited GetTransaction
2276 +  else
2277 +    Result := FTransaction;
2278 + end;
2279 +
2280   function TIBSQLData.GetIsNull: Boolean;
2281   begin
2282    CheckActive;
# Line 1789 | Line 2320 | end;
2320   { TSQLParam }
2321  
2322   procedure TSQLParam.InternalSetAsString(Value: AnsiString);
2323 +
2324 + procedure DoSetString;
2325 + begin
2326 +  Changing;
2327 +  FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
2328 +  Changed;
2329 + end;
2330 +
2331   var b: IBlob;
2332      dt: TDateTime;
2333 +    CurrValue: Currency;
2334 +    FloatValue: single;
2335 +    timezone: AnsiString;
2336   begin
2337    CheckActive;
2338    if IsNullable then
2339      IsNull := False;
2340 +  with FFirebirdClientAPI do
2341    case SQLTYPE of
2342    SQL_BOOLEAN:
2343      if AnsiCompareText(Value,STrue) = 0 then
# Line 1806 | Line 2349 | begin
2349        IBError(ibxeInvalidDataConversion,[nil]);
2350  
2351    SQL_BLOB:
2352 +    if Length(Value) < GetAttachment.GetInlineBlobLimit then
2353 +      DoSetString
2354 +    else
2355      begin
2356        Changing;
2357        b := FIBXSQLVAR.CreateBlob;
# Line 1816 | Line 2362 | begin
2362  
2363    SQL_VARYING,
2364    SQL_TEXT:
2365 <    begin
1820 <      Changing;
1821 <      FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
1822 <      Changed;
1823 <    end;
2365 >    DoSetString;
2366  
2367      SQL_SHORT,
2368      SQL_LONG,
2369      SQL_INT64:
2370 <      SetAsNumeric(AdjustScaleFromCurrency(StrToCurr(Value),GetScale),GetScale);
2370 >      if TryStrToCurr(Value,CurrValue) then
2371 >        SetAsNumeric(AdjustScaleFromCurrency(CurrValue,GetScale),GetScale)
2372 >      else
2373 >        DoSetString;
2374  
2375      SQL_D_FLOAT,
2376      SQL_DOUBLE,
2377      SQL_FLOAT:
2378 <      SetAsDouble(StrToFloat(Value));
2378 >      if TryStrToFloat(Value,FloatValue) then
2379 >        SetAsDouble(FloatValue)
2380 >      else
2381 >        DoSetString;
2382  
2383      SQL_TIMESTAMP:
2384        if TryStrToDateTime(Value,dt) then
2385          SetAsDateTime(dt)
2386        else
2387 <        FIBXSQLVar.SetString(Value);
2387 >        DoSetString;
2388  
2389      SQL_TYPE_DATE:
2390        if TryStrToDateTime(Value,dt) then
2391          SetAsDate(dt)
2392        else
2393 <        FIBXSQLVar.SetString(Value);
2393 >        DoSetString;
2394  
2395      SQL_TYPE_TIME:
2396        if TryStrToDateTime(Value,dt) then
2397          SetAsTime(dt)
2398        else
2399 <        FIBXSQLVar.SetString(Value);
2399 >        DoSetString;
2400 >
2401 >    SQL_TIMESTAMP_TZ:
2402 >      if ParseDateTimeTZString(value,dt,timezone) then
2403 >        SetAsDateTime(dt,timezone)
2404 >      else
2405 >        DoSetString;
2406 >
2407 >    SQL_TIME_TZ:
2408 >      if ParseDateTimeTZString(value,dt,timezone,true) then
2409 >        SetAsTime(dt,GetAttachment.GetTimeZoneServices.GetTimeTZDate,timezone)
2410 >      else
2411 >        DoSetString;
2412 >
2413 >    SQL_DEC_FIXED,
2414 >    SQL_DEC16,
2415 >    SQL_DEC34,
2416 >    SQL_INT128:
2417 >      SetAsBCD(StrToBCD(Value));
2418  
2419      else
2420 <      IBError(ibxeInvalidDataConversion,[nil]);
2420 >      IBError(ibxeInvalidDataConversion,[GetSQLTypeName(SQLType)]);
2421    end;
2422   end;
2423  
# Line 1902 | Line 2468 | begin
2468    Result := inherited GetAsPointer;
2469   end;
2470  
2471 + function TSQLParam.GetAsString: AnsiString;
2472 + var rs: RawByteString;
2473 + begin
2474 +  Result := '';
2475 +  if (SQLType = SQL_VARYING) and not IsNull then
2476 +  {SQLData points to start of string - default is to length word}
2477 +  begin
2478 +    CheckActive;
2479 +    SetString(rs,PAnsiChar(SQLData),DataLength);
2480 +    SetCodePage(rs,GetCodePage,false);
2481 +    Result := rs;
2482 +  end
2483 +  else
2484 +    Result := inherited GetAsString;
2485 + end;
2486 +
2487   procedure TSQLParam.SetName(Value: AnsiString);
2488   begin
2489    CheckActive;
# Line 2093 | Line 2675 | begin
2675    end;
2676   end;
2677  
2678 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
2679 + var i: integer;
2680 +    OldSQLVar: TSQLVarData;
2681 + begin
2682 +  if FIBXSQLVAR.UniqueName then
2683 +    inherited SetAsTime(AValue,OnDate, aTimeZoneID)
2684 +  else
2685 +  with FIBXSQLVAR.Parent do
2686 +  begin
2687 +    for i := 0 to Count - 1 do
2688 +      if Column[i].Name = Name then
2689 +      begin
2690 +        OldSQLVar := FIBXSQLVAR;
2691 +        FIBXSQLVAR := Column[i];
2692 +        try
2693 +          inherited SetAsTime(AValue,OnDate, aTimeZoneID);
2694 +        finally
2695 +          FIBXSQLVAR := OldSQLVar;
2696 +        end;
2697 +      end;
2698 +  end;
2699 + end;
2700 +
2701 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
2702 + var i: integer;
2703 +    OldSQLVar: TSQLVarData;
2704 + begin
2705 +  if FIBXSQLVAR.UniqueName then
2706 +    inherited SetAsTime(AValue,OnDate,aTimeZone)
2707 +  else
2708 +  with FIBXSQLVAR.Parent do
2709 +  begin
2710 +    for i := 0 to Count - 1 do
2711 +      if Column[i].Name = Name then
2712 +      begin
2713 +        OldSQLVar := FIBXSQLVAR;
2714 +        FIBXSQLVAR := Column[i];
2715 +        try
2716 +          inherited SetAsTime(AValue,OnDate,aTimeZone);
2717 +        finally
2718 +          FIBXSQLVAR := OldSQLVar;
2719 +        end;
2720 +      end;
2721 +  end;
2722 + end;
2723 +
2724 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID);
2725 + begin
2726 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZoneID);
2727 + end;
2728 +
2729 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZone: AnsiString);
2730 + begin
2731 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZone);
2732 + end;
2733 +
2734   procedure TSQLParam.SetAsDateTime(AValue: TDateTime);
2735   var i: integer;
2736      OldSQLVar: TSQLVarData;
# Line 2116 | Line 2754 | begin
2754    end;
2755   end;
2756  
2757 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID
2758 +  );
2759 + var i: integer;
2760 +    OldSQLVar: TSQLVarData;
2761 + begin
2762 +  if FIBXSQLVAR.UniqueName then
2763 +    inherited SetAsDateTime(AValue,aTimeZoneID)
2764 +  else
2765 +  with FIBXSQLVAR.Parent do
2766 +  begin
2767 +    for i := 0 to Count - 1 do
2768 +      if Column[i].Name = Name then
2769 +      begin
2770 +        OldSQLVar := FIBXSQLVAR;
2771 +        FIBXSQLVAR := Column[i];
2772 +        try
2773 +          inherited SetAsDateTime(AValue,aTimeZoneID);
2774 +        finally
2775 +          FIBXSQLVAR := OldSQLVar;
2776 +        end;
2777 +      end;
2778 +  end;
2779 + end;
2780 +
2781 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString);
2782 + var i: integer;
2783 +    OldSQLVar: TSQLVarData;
2784 + begin
2785 +  if FIBXSQLVAR.UniqueName then
2786 +    inherited SetAsDateTime(AValue,aTimeZone)
2787 +  else
2788 +  with FIBXSQLVAR.Parent do
2789 +  begin
2790 +    for i := 0 to Count - 1 do
2791 +      if Column[i].Name = Name then
2792 +      begin
2793 +        OldSQLVar := FIBXSQLVAR;
2794 +        FIBXSQLVAR := Column[i];
2795 +        try
2796 +          inherited SetAsDateTime(AValue,aTimeZone);
2797 +        finally
2798 +          FIBXSQLVAR := OldSQLVar;
2799 +        end;
2800 +      end;
2801 +  end;
2802 + end;
2803 +
2804   procedure TSQLParam.SetAsDouble(AValue: Double);
2805   var i: integer;
2806      OldSQLVar: TSQLVarData;
# Line 2296 | Line 2981 | begin
2981    FIBXSQLVAR.SetCharSetID(aValue);
2982   end;
2983  
2984 + procedure TSQLParam.SetAsBcd(aValue: tBCD);
2985 + var i: integer;
2986 +    OldSQLVar: TSQLVarData;
2987 + begin
2988 +  if FIBXSQLVAR.UniqueName then
2989 +    inherited SetAsBcd(AValue)
2990 +  else
2991 +  with FIBXSQLVAR.Parent do
2992 +  begin
2993 +    for i := 0 to Count - 1 do
2994 +      if Column[i].Name = Name then
2995 +      begin
2996 +        OldSQLVar := FIBXSQLVAR;
2997 +        FIBXSQLVAR := Column[i];
2998 +        try
2999 +          inherited SetAsBcd(AValue);
3000 +        finally
3001 +          FIBXSQLVAR := OldSQLVar;
3002 +        end;
3003 +      end;
3004 +  end;
3005 + end;
3006 +
3007   { TMetaData }
3008  
3009   procedure TMetaData.CheckActive;
# Line 2434 | Line 3142 | begin
3142      end;
3143   end;
3144  
3145 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
3146 + begin
3147 +  Result := FSQLParams.CaseSensitiveParams;
3148 + end;
3149 +
3150   { TResults }
3151  
3152   procedure TResults.CheckActive;
# Line 2446 | Line 3159 | begin
3159    if not FResults.CheckStatementStatus(ssPrepared)  then
3160      IBError(ibxeStatementNotPrepared, [nil]);
3161  
3162 <  with GetTransaction as TFBTransaction do
3162 >  with GetTransaction do
3163    if not InTransaction or (FResults.TransactionSeqNo <> FTransactionSeqNo) then
3164      IBError(ibxeInterfaceOutofDate,[nil]);
3165   end;
3166  
3167   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
3168 + var col: TIBSQLData;
3169   begin
3170    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
3171      IBError(ibxeInvalidColumnIndex,[nil]);
3172  
3173    if not HasInterface(aIBXSQLVAR.Index) then
3174      AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
3175 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3175 >  col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3176 >  col.FTransaction := GetTransaction;
3177 >  Result := col;
3178   end;
3179  
3180   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2515 | Line 3231 | begin
3231    FResults.GetData(index,IsNull, len,data);
3232   end;
3233  
3234 + function TResults.GetStatement: IStatement;
3235 + begin
3236 +  Result := FStatement;
3237 + end;
3238 +
3239   function TResults.GetTransaction: ITransaction;
3240   begin
3241    Result := FStatement.GetTransaction;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines