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

Comparing:
ibx/trunk/fbintf/client/FBSQLData.pas (file contents), Revision 270 by tony, Fri Jan 18 11:10:37 2019 UTC vs.
ibx/branches/udr/client/FBSQLData.pas (file contents), Revision 381 by tony, Sat Jan 15 00:06:22 2022 UTC

# Line 80 | Line 80 | unit FBSQLData;
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 <     function AdjustScale(Value: Int64; aScale: Integer): Double;
93 <     function AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
94 <     function AdjustScaleToCurrency(Value: Int64; aScale: Integer): Currency;
95 <     function GetTimestampFormatStr: AnsiString;
124 >     FTimeZoneServices: IExTimeZoneServices;
125       function GetDateFormatStr(IncludeTime: boolean): AnsiString;
126       function GetTimeFormatStr: AnsiString;
127 +     function GetTimestampFormatStr: AnsiString;
128       procedure SetAsInteger(AValue: Integer);
129 +     procedure InternalGetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
130 +       var aTimezone: AnsiString; var aTimeZoneID: TFBTimeZoneID);
131    protected
100     function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
101     function AdjustScaleFromDouble(Value: Double; aScale: Integer): Int64;
132       procedure CheckActive; virtual;
133 +     procedure CheckTZSupport;
134 +     function GetAttachment: IAttachment; virtual; abstract;
135 +     function GetTransaction: ITransaction; virtual; abstract;
136       function GetSQLDialect: integer; virtual; abstract;
137 +     function GetTimeZoneServices: IExTimeZoneServices; virtual;
138       procedure Changed; virtual;
139       procedure Changing; virtual;
140       procedure InternalSetAsString(Value: AnsiString); virtual;
# Line 113 | Line 147 | type
147       procedure SetDataLength(len: cardinal); virtual;
148       procedure SetSQLType(aValue: cardinal); virtual;
149       property DataLength: cardinal read GetDataLength write SetDataLength;
150 <
150 >     property FirebirdClientAPI: TFBClientAPI read FFirebirdClientAPI;
151    public
152       constructor Create(api: TFBClientAPI);
153 <     function GetSQLType: cardinal; virtual; abstract;
153 >     function CanChangeMetaData: boolean; virtual;
154 >     function GetSQLType: cardinal; virtual; abstract; {Current Field Data SQL Type}
155       function GetSQLTypeName: AnsiString; overload;
156 <     class function GetSQLTypeName(SQLType: short): AnsiString; overload;
156 >     class function GetSQLTypeName(SQLType: cardinal): AnsiString; overload;
157 >     function GetStrDataLength: short;
158 >     function getColMetadata: IParamMetaData; virtual; abstract;
159       function GetName: AnsiString; virtual; abstract;
160 <     function GetScale: integer; virtual; abstract;
160 >     function GetScale: integer; virtual; abstract; {Current Field Data scale}
161       function GetAsBoolean: boolean;
162       function GetAsCurrency: Currency;
163       function GetAsInt64: Int64;
164 <     function GetAsDateTime: TDateTime;
164 >     function GetAsDateTime: TDateTime; overload;
165 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
166 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
167 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime); overload;
168 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString; OnDate: TDateTime); overload;
169 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
170 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
171 >     function GetAsUTCDateTime: TDateTime;
172       function GetAsDouble: Double;
173       function GetAsFloat: Float;
174       function GetAsLong: Long;
# Line 132 | Line 176 | type
176       function GetAsQuad: TISC_QUAD;
177       function GetAsShort: short;
178       function GetAsString: AnsiString; virtual;
179 +     function GetAsNumeric: IFBNumeric;
180       function GetIsNull: Boolean; virtual;
181       function GetIsNullable: boolean; virtual;
182       function GetAsVariant: Variant;
183       function GetModified: boolean; virtual;
184       function GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats): integer;
185 +     function GetAsBCD: tBCD;
186 +     function GetSize: cardinal; virtual; abstract;
187 +     function GetCharSetWidth: integer; virtual; abstract;
188       procedure SetAsBoolean(AValue: boolean); virtual;
189       procedure SetAsCurrency(Value: Currency); virtual;
190       procedure SetAsInt64(Value: Int64); virtual;
191       procedure SetAsDate(Value: TDateTime); virtual;
192       procedure SetAsLong(Value: Long); virtual;
193 <     procedure SetAsTime(Value: TDateTime); virtual;
194 <     procedure SetAsDateTime(Value: TDateTime);
193 >     procedure SetAsTime(Value: TDateTime); overload;
194 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime;aTimeZoneID: TFBTimeZoneID); overload;
195 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
196 >     procedure SetAsDateTime(Value: TDateTime); overload;
197 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
198 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
199 >     procedure SetAsUTCDateTime(aUTCTime: TDateTime);
200       procedure SetAsDouble(Value: Double); virtual;
201       procedure SetAsFloat(Value: Float); virtual;
202       procedure SetAsPointer(Value: Pointer);
# Line 151 | Line 204 | type
204       procedure SetAsShort(Value: short); virtual;
205       procedure SetAsString(Value: AnsiString); virtual;
206       procedure SetAsVariant(Value: Variant);
207 <     procedure SetAsNumeric(Value: Int64; aScale: integer);
207 >     procedure SetAsNumeric(Value: IFBNumeric); virtual;
208 >     procedure SetAsBcd(aValue: tBCD); virtual;
209       procedure SetIsNull(Value: Boolean); virtual;
210       procedure SetIsNullable(Value: Boolean); virtual;
211       procedure SetName(aValue: AnsiString); virtual;
# Line 192 | Line 246 | type
246      FUniqueRelationName: AnsiString;
247      FColumnList: array of TSQLVarData;
248      function GetStatement: IStatement; virtual; abstract;
249 +    function GetAttachment: IAttachment; virtual;
250 +    function GetTransaction: ITransaction; virtual;
251      function GetPrepareSeqNo: integer; virtual; abstract;
252      function GetTransactionSeqNo: integer; virtual; abstract;
253      procedure SetCount(aValue: integer); virtual; abstract;
# Line 210 | Line 266 | type
266      function StateChanged(var ChangeSeqNo: integer): boolean; virtual; abstract;
267      property CaseSensitiveParams: boolean read FCaseSensitiveParams
268                                              write FCaseSensitiveParams; {Only used when IsInputDataArea true}
269 +    function CanChangeMetaData: boolean; virtual; abstract;
270      property Count: integer read GetCount;
271 <    property Column[index: integer]: TSQLVarData read GetColumn;
271 >    property Column[index: integer]: TSQLVarData read GetColumn; default;
272      property UniqueRelationName: AnsiString read FUniqueRelationName;
273      property Statement: IStatement read GetStatement;
274 +    property Attachment: IAttachment read GetAttachment;
275      property PrepareSeqNo: integer read GetPrepareSeqNo;
276 +    property Transaction: ITransaction read GetTransaction;
277      property TransactionSeqNo: integer read GetTransactionSeqNo;
278    end;
279  
# Line 228 | Line 287 | type
287      FModified: boolean;
288      FUniqueName: boolean;
289      FVarString: RawByteString;
290 +    FColMetaData: IParamMetaData;
291      function GetStatement: IStatement;
292      procedure SetName(AValue: AnsiString);
293    protected
294 +    FArrayIntf: IArray;
295 +    function GetAttachment: IAttachment;
296 +    function GetTransaction: ITransaction;
297      function GetSQLType: cardinal; virtual; abstract;
298      function GetSubtype: integer; virtual; abstract;
299      function GetAliasName: AnsiString;  virtual; abstract;
# Line 239 | Line 302 | type
302      function GetRelationName: AnsiString;  virtual; abstract;
303      function GetScale: integer; virtual; abstract;
304      function GetCharSetID: cardinal; virtual; abstract;
305 <    function GetCodePage: TSystemCodePage; virtual; abstract;
305 >    function GetCharSetWidth: integer;
306 >    function GetCodePage: TSystemCodePage;
307      function GetIsNull: Boolean;   virtual; abstract;
308      function GetIsNullable: boolean; virtual; abstract;
309      function GetSQLData: PByte;  virtual; abstract;
310 <    function GetDataLength: cardinal; virtual; abstract;
310 >    function GetDataLength: cardinal; virtual; abstract; {current field length}
311 >    function GetSize: cardinal; virtual; abstract; {field length as given by metadata}
312 >    function GetDefaultTextSQLType: cardinal; virtual; abstract;
313 >    procedure InternalSetSQLType(aValue: cardinal); virtual; abstract;
314 >    procedure InternalSetScale(aValue: integer); virtual; abstract;
315 >    procedure InternalSetDataLength(len: cardinal); virtual; abstract;
316      procedure SetIsNull(Value: Boolean); virtual; abstract;
317      procedure SetIsNullable(Value: Boolean);  virtual; abstract;
318      procedure SetSQLData(AValue: PByte; len: cardinal); virtual; abstract;
319 <    procedure SetScale(aValue: integer); virtual; abstract;
320 <    procedure SetDataLength(len: cardinal); virtual; abstract;
321 <    procedure SetSQLType(aValue: cardinal); virtual; abstract;
319 >    procedure SetScale(aValue: integer);
320 >    procedure SetDataLength(len: cardinal);
321 >    procedure SetSQLType(aValue: cardinal);
322      procedure SetCharSetID(aValue: cardinal); virtual; abstract;
323 +    procedure SetMetaSize(aValue: cardinal); virtual;
324    public
325      constructor Create(aParent: TSQLDataArea; aIndex: integer);
326 +    function CanChangeMetaData: boolean;
327      procedure SetString(aValue: AnsiString);
328      procedure Changed; virtual;
329      procedure RowChange; virtual;
330 <    function GetAsArray(Array_ID: TISC_QUAD): IArray; virtual; abstract;
330 >    function GetAsArray: IArray; virtual; abstract;
331      function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; virtual; abstract;
332      function CreateBlob: IBlob; virtual; abstract;
333      function GetArrayMetaData: IArrayMetaData; virtual; abstract;
334      function GetBlobMetaData: IBlobMetaData; virtual; abstract;
335 +    function getColMetadata: IParamMetaData;
336      procedure Initialize; virtual;
337 +    procedure SaveMetaData;
338 +    procedure SetArray(AValue: IArray);
339  
340    public
341      property AliasName: AnsiString read GetAliasName;
# Line 272 | Line 346 | type
346      property Index: integer read FIndex;
347      property Name: AnsiString read FName write SetName;
348      property CharSetID: cardinal read GetCharSetID write SetCharSetID;
349 +    property CodePage: TSystemCodePage read GetCodePage;
350      property SQLType: cardinal read GetSQLType write SetSQLType;
351      property SQLSubtype: integer read GetSubtype;
352      property SQLData: PByte read GetSQLData;
# Line 287 | Line 362 | type
362  
363    { TColumnMetaData }
364  
365 <  TColumnMetaData = class(TSQLDataItem,IColumnMetaData)
365 >  TColumnMetaData = class(TSQLDataItem,IColumnMetaData,IParamMetaData)
366    private
367      FIBXSQLVAR: TSQLVarData;
368      FOwner: IUnknown;         {Keep reference to ensure Metadata/statement not discarded}
369      FPrepareSeqNo: integer;
295    FStatement: IStatement;
370      FChangeSeqNo: integer;
371    protected
372      procedure CheckActive; override;
# Line 304 | Line 378 | type
378      constructor Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
379      destructor Destroy; override;
380      function GetSQLDialect: integer; override;
381 <    property Statement: IStatement read FStatement;
381 >    function getColMetadata: IParamMetaData; override;
382  
383    public
384      {IColumnMetaData}
# Line 319 | Line 393 | type
393      function GetScale: integer; override;
394      function getCharSetID: cardinal; override;
395      function GetIsNullable: boolean; override;
396 <    function GetSize: cardinal;
396 >    function GetSize: cardinal; override;
397 >    function GetCharSetWidth: integer; override;
398      function GetArrayMetaData: IArrayMetaData;
399      function GetBlobMetaData: IBlobMetaData;
400 +    function GetStatement: IStatement;
401 +    function GetTransaction: ITransaction; override;
402 +    function GetAttachment: IAttachment; override;
403      property Name: AnsiString read GetName;
404      property Size: cardinal read GetSize;
405      property CharSetID: cardinal read getCharSetID;
406      property SQLSubtype: integer read getSubtype;
407      property IsNullable: Boolean read GetIsNullable;
408 +  public
409 +    property Statement: IStatement read GetStatement;
410    end;
411  
412    { TIBSQLData }
# Line 343 | Line 423 | type
423      property AsBlob: IBlob read GetAsBlob;
424   end;
425  
426 +  { TSQLParamMetaData }
427 +
428 +  TSQLParamMetaData = class(TFBInterfacedObject,IParamMetaData)
429 +  private
430 +    FSQLType: cardinal;
431 +    FSQLSubType: integer;
432 +    FScale: integer;
433 +    FCharSetID: cardinal;
434 +    FNullable: boolean;
435 +    FSize: cardinal;
436 +    FCodePage: TSystemCodePage;
437 +  public
438 +    constructor Create(src: TSQLVarData);
439 +    {IParamMetaData}
440 +    function GetSQLType: cardinal;
441 +    function GetSQLTypeName: AnsiString;
442 +    function getSubtype: integer;
443 +    function getScale: integer;
444 +    function getCharSetID: cardinal;
445 +    function getCodePage: TSystemCodePage;
446 +    function getIsNullable: boolean;
447 +    function GetSize: cardinal;
448 +    property SQLType: cardinal read GetSQLType;
449 +  end;
450 +
451    { TSQLParam }
452  
453 <  TSQLParam = class(TIBSQLData,ISQLParam)
453 >  TSQLParam = class(TIBSQLData,ISQLParam,ISQLData)
454    protected
455      procedure CheckActive; override;
456      procedure Changed; override;
# Line 355 | Line 460 | type
460      procedure SetSQLType(aValue: cardinal); override;
461    public
462      procedure Clear;
463 +    function CanChangeMetaData: boolean; override;
464 +    function getColMetadata: IParamMetaData; override;
465      function GetModified: boolean; override;
466      function GetAsPointer: Pointer;
467 +    function GetAsString: AnsiString; override;
468      procedure SetName(Value: AnsiString); override;
469      procedure SetIsNull(Value: Boolean);  override;
470      procedure SetIsNullable(Value: Boolean); override;
# Line 368 | Line 476 | type
476      procedure SetAsInt64(AValue: Int64);
477      procedure SetAsDate(AValue: TDateTime);
478      procedure SetAsLong(AValue: Long);
479 <    procedure SetAsTime(AValue: TDateTime);
480 <    procedure SetAsDateTime(AValue: TDateTime);
479 >    procedure SetAsTime(AValue: TDateTime); overload;
480 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
481 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
482 >    procedure SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
483 >    procedure SetAsTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
484 >    procedure SetAsDateTime(AValue: TDateTime); overload;
485 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
486 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
487      procedure SetAsDouble(AValue: Double);
488      procedure SetAsFloat(AValue: Float);
489      procedure SetAsPointer(AValue: Pointer);
# Line 379 | Line 493 | type
493      procedure SetAsBlob(aValue: IBlob);
494      procedure SetAsQuad(AValue: TISC_QUAD);
495      procedure SetCharSetID(aValue: cardinal);
496 +    procedure SetAsBcd(aValue: tBCD);
497 +    procedure SetAsNumeric(aValue: IFBNumeric);
498  
499      property AsBlob: IBlob read GetAsBlob write SetAsBlob;
500      property IsNullable: Boolean read GetIsNullable write SetIsNullable;
# Line 419 | Line 535 | type
535      {ISQLParams}
536      function getCount: integer;
537      function getSQLParam(index: integer): ISQLParam;
538 <    function ByName(Idx: AnsiString): ISQLParam ;
538 >    function ByName(Idx: AnsiString): ISQLParam ; virtual;
539      function GetModified: Boolean;
540 +    function GetHasCaseSensitiveParams: Boolean;
541 +    function GetStatement: IStatement;
542 +    function GetTransaction: ITransaction;
543 +    function GetAttachment: IAttachment;
544 +    procedure Clear;
545    end;
546  
547    { TResults }
# Line 439 | Line 560 | type
560       constructor Create(aResults: TSQLDataArea);
561        {IResults}
562       function getCount: integer;
563 <     function ByName(Idx: AnsiString): ISQLData;
563 >     function ByName(Idx: AnsiString): ISQLData; virtual;
564       function getSQLData(index: integer): ISQLData;
565       procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PByte);
566 <     function GetTransaction: ITransaction; virtual;
566 >     function GetStatement: IStatement;
567 >     function GetTransaction: ITransaction;
568 >     function GetAttachment: IAttachment;
569       procedure SetRetainInterfaces(aValue: boolean);
570   end;
571  
572   implementation
573  
574 < uses FBMessages, variants, IBUtils, FBTransaction, DateUtils;
574 > uses FBMessages, variants, IBUtils, FBTransaction, FBNumeric, DateUtils;
575 >
576 > { TSQLParamMetaData }
577 >
578 > constructor TSQLParamMetaData.Create(src: TSQLVarData);
579 > begin
580 >  inherited Create;
581 >  FSQLType := src.GetSQLType;
582 >  FSQLSubType := src.getSubtype;
583 >  FScale := src.GetScale;
584 >  FCharSetID := src.getCharSetID;
585 >  FNullable := src.GetIsNullable;
586 >  FSize := src.GetSize;
587 >  FCodePage := src.GetCodePage;
588 > end;
589 >
590 > function TSQLParamMetaData.GetSQLType: cardinal;
591 > begin
592 >  Result := FSQLType;
593 > end;
594 >
595 > function TSQLParamMetaData.GetSQLTypeName: AnsiString;
596 > begin
597 >  Result := TSQLDataItem.GetSQLTypeName(FSQLType);
598 > end;
599 >
600 > function TSQLParamMetaData.getSubtype: integer;
601 > begin
602 >  Result := FSQLSubType;
603 > end;
604 >
605 > function TSQLParamMetaData.getScale: integer;
606 > begin
607 >  Result := FScale;
608 > end;
609 >
610 > function TSQLParamMetaData.getCharSetID: cardinal;
611 > begin
612 >  Result := FCharSetID;
613 > end;
614 >
615 > function TSQLParamMetaData.getCodePage: TSystemCodePage;
616 > begin
617 >  Result :=  FCodePage;
618 > end;
619 >
620 > function TSQLParamMetaData.getIsNullable: boolean;
621 > begin
622 >  Result :=  FNullable;
623 > end;
624 >
625 > function TSQLParamMetaData.GetSize: cardinal;
626 > begin
627 >  Result := FSize;
628 > end;
629  
630   { TSQLDataArea }
631  
# Line 464 | Line 641 | begin
641    Result := Length(FColumnList);
642   end;
643  
644 + function TSQLDataArea.GetTransaction: ITransaction;
645 + begin
646 +  Result := GetStatement.GetTransaction;
647 + end;
648 +
649 + function TSQLDataArea.GetAttachment: IAttachment;
650 + begin
651 +  Result := GetStatement.GetAttachment;
652 + end;
653 +
654   procedure TSQLDataArea.SetUniqueRelationName;
655   var
656    i: Integer;
# Line 596 | Line 783 | begin
783      FName := AValue;
784   end;
785  
786 + function TSQLVarData.GetAttachment: IAttachment;
787 + begin
788 +  Result := Parent.Attachment;
789 + end;
790 +
791 + function TSQLVarData.GetTransaction: ITransaction;
792 + begin
793 +  Result := Parent.Transaction;
794 + end;
795 +
796 + function TSQLVarData.GetCharSetWidth: integer;
797 + begin
798 +  result := 1;
799 +  GetAttachment.CharSetWidth(GetCharSetID,result);
800 + end;
801 +
802 + function TSQLVarData.GetCodePage: TSystemCodePage;
803 + begin
804 +  result := CP_NONE;
805 +  GetAttachment.CharSetID2CodePage(GetCharSetID,result);
806 + end;
807 +
808 + procedure TSQLVarData.SetScale(aValue: integer);
809 + begin
810 +  if aValue = Scale then
811 +    Exit;
812 +  if not CanChangeMetaData  then
813 +    IBError(ibxeScaleCannotBeChanged,[]);
814 +  InternalSetScale(aValue);
815 + end;
816 +
817 + procedure TSQLVarData.SetDataLength(len: cardinal);
818 + begin
819 +  if len = DataLength then
820 +    Exit;
821 +  InternalSetDataLength(len);
822 + end;
823 +
824 + procedure TSQLVarData.SetSQLType(aValue: cardinal);
825 + begin
826 +  if aValue = SQLType then
827 +    Exit;
828 +  if not CanChangeMetaData then
829 +    IBError(ibxeSQLTypeUnchangeable,[TSQLDataItem.GetSQLTypeName(SQLType),
830 +                                          TSQLDataItem.GetSQLTypeName(aValue)]);
831 +  InternalSetSQLType(aValue);
832 + end;
833 +
834 + procedure TSQLVarData.SetMetaSize(aValue: cardinal);
835 + begin
836 +  //Ignore
837 + end;
838 +
839 + procedure TSQLVarData.SaveMetaData;
840 + begin
841 +  FColMetaData := TSQLParamMetaData.Create(self);
842 + end;
843 +
844 + procedure TSQLVarData.SetArray(AValue: IArray);
845 + begin
846 +  FArrayIntf := AValue;
847 + end;
848 +
849   constructor TSQLVarData.Create(aParent: TSQLDataArea; aIndex: integer);
850   begin
851    inherited Create;
# Line 604 | Line 854 | begin
854    FUniqueName := true;
855   end;
856  
857 + function TSQLVarData.CanChangeMetaData: boolean;
858 + begin
859 +  Result := Parent.CanChangeMetaData;
860 + end;
861 +
862   procedure TSQLVarData.SetString(aValue: AnsiString);
863   begin
864    {we take full advantage here of reference counted strings. When setting a string
# Line 612 | Line 867 | begin
867     a zero byte when the string is empty, neatly avoiding a nil pointer error.}
868  
869    FVarString := aValue;
870 <  SQLType := SQL_TEXT;
870 >  if SQLType = SQL_BLOB then
871 >    SetMetaSize(GetAttachment.GetInlineBlobLimit);
872 >  if CanChangeMetaData then
873 >    SQLType := GetDefaultTextSQLType;
874    Scale := 0;
875 +  if  (SQLType <> SQL_VARYING) and (SQLType <> SQL_TEXT) then
876 +    IBError(ibxeUnableTosetaTextType,[Index,Name,TSQLDataItem.GetSQLTypeName(SQLType)]);
877 +  if not CanChangeMetaData and (Length(aValue) > GetSize) then
878 +    IBError(ibxeStringOverflow,[Length(aValue),DataLength]);
879    SetSQLData(PByte(PAnsiChar(FVarString)),Length(aValue));
880   end;
881  
# Line 624 | Line 886 | end;
886  
887   procedure TSQLVarData.RowChange;
888   begin
889 +  FArrayIntf := nil;
890    FModified := false;
891    FVarString := '';
892   end;
893  
894 + function TSQLVarData.getColMetadata: IParamMetaData;
895 + begin
896 +  Result := FColMetaData;
897 + end;
898 +
899   procedure TSQLVarData.Initialize;
900  
901    function FindVarByName(idx: AnsiString; limit: integer): TSQLVarData;
# Line 687 | Line 955 | end;
955  
956   {TSQLDataItem}
957  
690 function TSQLDataItem.AdjustScale(Value: Int64; aScale: Integer): Double;
691 var
692  Scaling : Int64;
693  i: Integer;
694  Val: Double;
695 begin
696  Scaling := 1; Val := Value;
697  if aScale > 0 then
698  begin
699    for i := 1 to aScale do
700      Scaling := Scaling * 10;
701    result := Val * Scaling;
702  end
703  else
704    if aScale < 0 then
705    begin
706      for i := -1 downto aScale do
707        Scaling := Scaling * 10;
708      result := Val / Scaling;
709    end
710    else
711      result := Val;
712 end;
713
714 function TSQLDataItem.AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
715 var
716  Scaling : Int64;
717  i: Integer;
718  Val: Int64;
719 begin
720  Scaling := 1; Val := Value;
721  if aScale > 0 then begin
722    for i := 1 to aScale do Scaling := Scaling * 10;
723    result := Val * Scaling;
724  end else if aScale < 0 then begin
725    for i := -1 downto aScale do Scaling := Scaling * 10;
726    result := Val div Scaling;
727  end else
728    result := Val;
729 end;
730
731 function TSQLDataItem.AdjustScaleToCurrency(Value: Int64; aScale: Integer
732  ): Currency;
733 var
734  Scaling : Int64;
735  i : Integer;
736  FractionText, PadText, CurrText: AnsiString;
737 begin
738  Result := 0;
739  Scaling := 1;
740  PadText := '';
741  if aScale > 0 then
742  begin
743    for i := 1 to aScale do
744      Scaling := Scaling * 10;
745    result := Value * Scaling;
746  end
747  else
748    if aScale < 0 then
749    begin
750      for i := -1 downto aScale do
751        Scaling := Scaling * 10;
752      FractionText := IntToStr(abs(Value mod Scaling));
753      for i := Length(FractionText) to -aScale -1 do
754        PadText := '0' + PadText;
755      {$IF declared(DefaultFormatSettings)}
756      with DefaultFormatSettings do
757      {$ELSE}
758      {$IF declared(FormatSettings)}
759      with FormatSettings do
760      {$IFEND}
761      {$IFEND}
762      if Value < 0 then
763        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
764      else
765        CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
766      try
767        result := StrToCurr(CurrText);
768      except
769        on E: Exception do
770          IBError(ibxeInvalidDataConversion, [nil]);
771      end;
772    end
773    else
774      result := Value;
775 end;
776
958   function TSQLDataItem.GetDateFormatStr(IncludeTime: boolean): AnsiString;
959   begin
960    {$IF declared(DefaultFormatSettings)}
# Line 803 | Line 984 | begin
984    with FormatSettings do
985    {$IFEND}
986    {$IFEND}
987 <    Result := LongTimeFormat;
987 >    Result := 'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';;
988   end;
989  
990   function TSQLDataItem.GetTimestampFormatStr: AnsiString;
# Line 815 | Line 996 | begin
996    with FormatSettings do
997    {$IFEND}
998    {$IFEND}
999 <    Result := ShortDateFormat + ' ' +  LongTimeFormat + '.zzz';
999 >    Result := ShortDateFormat + ' ' +  'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';
1000   end;
1001  
1002   procedure TSQLDataItem.SetAsInteger(AValue: Integer);
# Line 823 | Line 1004 | begin
1004    SetAsLong(aValue);
1005   end;
1006  
1007 < function TSQLDataItem.AdjustScaleFromCurrency(Value: Currency; aScale: Integer
1008 <  ): Int64;
1009 < var
829 <  Scaling : Int64;
830 <  i : Integer;
1007 > procedure TSQLDataItem.InternalGetAsDateTime(var aDateTime: TDateTime;
1008 >  var dstOffset: smallint; var aTimezone: AnsiString;
1009 >  var aTimeZoneID: TFBTimeZoneID);
1010   begin
1011 <  Result := 0;
1012 <  Scaling := 1;
1013 <  if aScale < 0 then
1014 <  begin
1015 <    for i := -1 downto aScale do
1016 <      Scaling := Scaling * 10;
1017 <    result := trunc(Value * Scaling);
1018 <  end
1019 <  else
1020 <  if aScale > 0 then
1021 <  begin
1022 <    for i := 1 to aScale do
1023 <       Scaling := Scaling * 10;
1024 <    result := trunc(Value / Scaling);
1025 <  end
1026 <  else
1027 <    result := trunc(Value);
1011 >  CheckActive;
1012 >  aDateTime := 0;
1013 >  dstOffset := 0;
1014 >  aTimezone := '';
1015 >  aTimeZoneID := TimeZoneID_GMT;
1016 >  if not IsNull then
1017 >    with FFirebirdClientAPI do
1018 >    case SQLType of
1019 >      SQL_TEXT, SQL_VARYING:
1020 >        if not ParseDateTimeTZString(AsString,aDateTime,aTimeZone) then
1021 >          IBError(ibxeInvalidDataConversion, [nil]);
1022 >      SQL_TYPE_DATE:
1023 >        aDateTime := SQLDecodeDate(SQLData);
1024 >      SQL_TYPE_TIME:
1025 >        aDateTime := SQLDecodeTime(SQLData);
1026 >      SQL_TIMESTAMP:
1027 >        aDateTime := SQLDecodeDateTime(SQLData);
1028 >      SQL_TIMESTAMP_TZ:
1029 >        begin
1030 >          GetTimeZoneServices.DecodeTimestampTZ(SQLData,aDateTime,dstOffset,aTimeZone);
1031 >          aTimeZoneID := PISC_TIMESTAMP_TZ(SQLData)^.time_zone;
1032 >        end;
1033 >      SQL_TIMESTAMP_TZ_EX:
1034 >      begin
1035 >        GetTimeZoneServices.DecodeTimestampTZEx(SQLData,aDateTime,dstOffset,aTimeZone);
1036 >        aTimeZoneID := PISC_TIMESTAMP_TZ_EX(SQLData)^.time_zone;
1037 >      end;
1038 >      SQL_TIME_TZ:
1039 >        with GetTimeZoneServices do
1040 >        begin
1041 >          DecodeTimeTZ(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1042 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1043 >        end;
1044 >      SQL_TIME_TZ_EX:
1045 >        with GetTimeZoneServices do
1046 >        begin
1047 >          DecodeTimeTZEx(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1048 >          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1049 >        end;
1050 >      else
1051 >        IBError(ibxeInvalidDataConversion, [nil]);
1052 >    end;
1053   end;
1054  
1055 < function TSQLDataItem.AdjustScaleFromDouble(Value: Double; aScale: Integer
852 <  ): Int64;
853 < var
854 <  Scaling : Int64;
855 <  i : Integer;
1055 > procedure TSQLDataItem.CheckActive;
1056   begin
1057 <  Result := 0;
858 <  Scaling := 1;
859 <  if aScale < 0 then
860 <  begin
861 <    for i := -1 downto aScale do
862 <      Scaling := Scaling * 10;
863 <    result := trunc(Value * Scaling);
864 <  end
865 <  else
866 <  if aScale > 0 then
867 <  begin
868 <    for i := 1 to aScale do
869 <       Scaling := Scaling * 10;
870 <    result := trunc(Value / Scaling);
871 <  end
872 <  else
873 <    result := trunc(Value);
1057 >  //Do nothing by default
1058   end;
1059  
1060 < procedure TSQLDataItem.CheckActive;
1060 > procedure TSQLDataItem.CheckTZSupport;
1061   begin
1062 <  //Do nothing by default
1062 >  if not FFirebirdClientAPI.HasTimeZoneSupport then
1063 >    IBError(ibxeNoTimezoneSupport,[]);
1064 > end;
1065 >
1066 > function TSQLDataItem.GetTimeZoneServices: IExTimeZoneServices;
1067 > begin
1068 >  if FTimeZoneServices = nil then
1069 >  begin
1070 >    if not GetAttachment.HasTimeZoneSupport then
1071 >      IBError(ibxeNoTimezoneSupport,[]);
1072 >    GetAttachment.GetTimeZoneServices.QueryInterface(IExTimeZoneServices,FTimeZoneServices);
1073 >  end;
1074 >  Result := FTimeZoneServices;
1075   end;
1076  
1077   procedure TSQLDataItem.Changed;
# Line 922 | Line 1118 | begin
1118    FFirebirdClientAPI := api;
1119   end;
1120  
1121 + function TSQLDataItem.CanChangeMetaData: boolean;
1122 + begin
1123 +  Result := false;
1124 + end;
1125 +
1126   function TSQLDataItem.GetSQLTypeName: AnsiString;
1127   begin
1128    Result := GetSQLTypeName(GetSQLType);
1129   end;
1130  
1131 < class function TSQLDataItem.GetSQLTypeName(SQLType: short): AnsiString;
1131 > class function TSQLDataItem.GetSQLTypeName(SQLType: cardinal): AnsiString;
1132   begin
1133    Result := 'Unknown';
1134    case SQLType of
# Line 938 | Line 1139 | begin
1139    SQL_LONG:             Result := 'SQL_LONG';
1140    SQL_SHORT:            Result := 'SQL_SHORT';
1141    SQL_TIMESTAMP:        Result := 'SQL_TIMESTAMP';
1142 +  SQL_TIMESTAMP_TZ:     Result := 'SQL_TIMESTAMP_TZ';
1143 +  SQL_TIMESTAMP_TZ_EX:  Result := 'SQL_TIMESTAMP_TZ_EX';
1144    SQL_BLOB:             Result := 'SQL_BLOB';
1145    SQL_D_FLOAT:          Result := 'SQL_D_FLOAT';
1146    SQL_ARRAY:            Result := 'SQL_ARRAY';
# Line 945 | Line 1148 | begin
1148    SQL_TYPE_TIME:        Result := 'SQL_TYPE_TIME';
1149    SQL_TYPE_DATE:        Result := 'SQL_TYPE_DATE';
1150    SQL_INT64:            Result := 'SQL_INT64';
1151 +  SQL_TIME_TZ:          Result := 'SQL_TIME_TZ';
1152 +  SQL_TIME_TZ_EX:       Result := 'SQL_TIME_TZ_EX';
1153 +  SQL_DEC_FIXED:        Result := 'SQL_DEC_FIXED';
1154 +  SQL_DEC16:            Result := 'SQL_DEC16';
1155 +  SQL_DEC34:            Result := 'SQL_DEC34';
1156 +  SQL_INT128:           Result := 'SQL_INT128';
1157 +  SQL_NULL:             Result := 'SQL_NULL';
1158 +  SQL_BOOLEAN:          Result := 'SQL_BOOLEAN';
1159    end;
1160   end;
1161  
1162 + function TSQLDataItem.GetStrDataLength: short;
1163 + begin
1164 +  with FFirebirdClientAPI do
1165 +  if SQLType = SQL_VARYING then
1166 +    Result := DecodeInteger(SQLData, 2)
1167 +  else
1168 +    Result := DataLength;
1169 + end;
1170 +
1171   function TSQLDataItem.GetAsBoolean: boolean;
1172   begin
1173    CheckActive;
# Line 978 | Line 1198 | begin
1198            end;
1199          end;
1200          SQL_SHORT:
1201 <          result := AdjustScaleToCurrency(Int64(PShort(SQLData)^),
1202 <                                      Scale);
1201 >          result := NumericFromRawValues(Int64(PShort(SQLData)^),
1202 >                                      Scale).getAsCurrency;
1203          SQL_LONG:
1204 <          result := AdjustScaleToCurrency(Int64(PLong(SQLData)^),
1205 <                                      Scale);
1204 >          result := NumericFromRawValues(Int64(PLong(SQLData)^),
1205 >                                      Scale).getAsCurrency;
1206          SQL_INT64:
1207 <          result := AdjustScaleToCurrency(PInt64(SQLData)^,
1208 <                                      Scale);
1207 >          result := NumericFromRawValues(PInt64(SQLData)^,
1208 >                                      Scale).getAsCurrency;
1209          SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1210 <          result := Trunc(AsDouble);
1210 >          result := Round(AsDouble);
1211 >
1212 >        SQL_DEC_FIXED,
1213 >        SQL_DEC16,
1214 >        SQL_DEC34,
1215 >        SQL_INT128:
1216 >          if not BCDToCurr(GetAsBCD,Result) then
1217 >            IBError(ibxeInvalidDataConversion, [nil]);
1218 >
1219          else
1220            IBError(ibxeInvalidDataConversion, [nil]);
1221        end;
# Line 1008 | Line 1236 | begin
1236          end;
1237        end;
1238        SQL_SHORT:
1239 <        result := AdjustScaleToInt64(Int64(PShort(SQLData)^),
1240 <                                    Scale);
1239 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1240 >                                    Scale).getAsInt64;
1241        SQL_LONG:
1242 <        result := AdjustScaleToInt64(Int64(PLong(SQLData)^),
1243 <                                    Scale);
1242 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1243 >                                    Scale).getAsInt64;
1244        SQL_INT64:
1245 <        result := AdjustScaleToInt64(PInt64(SQLData)^,
1246 <                                    Scale);
1245 >        result := NumericFromRawValues(PInt64(SQLData)^,
1246 >                                    Scale).getAsInt64;
1247        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1248 <        result := Trunc(AsDouble);
1248 >        result := Round(AsDouble);
1249        else
1250          IBError(ibxeInvalidDataConversion, [nil]);
1251      end;
1252   end;
1253  
1254   function TSQLDataItem.GetAsDateTime: TDateTime;
1255 + var aTimezone: AnsiString;
1256 +    aTimeZoneID: TFBTimeZoneID;
1257 +    dstOffset: smallint;
1258 + begin
1259 +  InternalGetAsDateTime(Result,dstOffset,aTimeZone,aTimeZoneID);
1260 + end;
1261 +
1262 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime;
1263 +  var dstOffset: smallint; var aTimezone: AnsiString);
1264 + var aTimeZoneID: TFBTimeZoneID;
1265 + begin
1266 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1267 + end;
1268 +
1269 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
1270 +  var aTimezoneID: TFBTimeZoneID);
1271 + var aTimezone: AnsiString;
1272 + begin
1273 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1274 + end;
1275 +
1276 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1277 +  var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime);
1278 + var aTimeZone: AnsiString;
1279   begin
1280    CheckActive;
1281 <  result := 0;
1281 >  aTime := 0;
1282 >  dstOffset := 0;
1283    if not IsNull then
1284      with FFirebirdClientAPI do
1285      case SQLType of
1286 <      SQL_TEXT, SQL_VARYING: begin
1287 <        try
1288 <          result := StrToDate(AsString);
1289 <        except
1290 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
1286 >      SQL_TIME_TZ:
1287 >        begin
1288 >          GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1289 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1290 >        end;
1291 >      SQL_TIME_TZ_EX:
1292 >        begin
1293 >          GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1294 >          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1295          end;
1296 +    else
1297 +      IBError(ibxeInvalidDataConversion, [nil]);
1298 +    end;
1299 + end;
1300 +
1301 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1302 +  var aTimezone: AnsiString; OnDate: TDateTime);
1303 + begin
1304 +  CheckActive;
1305 +  aTime := 0;
1306 +  dstOffset := 0;
1307 +  if not IsNull then
1308 +    with FFirebirdClientAPI do
1309 +    case SQLType of
1310 +      SQL_TIME_TZ:
1311 +        GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1312 +      SQL_TIME_TZ_EX:
1313 +        GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1314 +    else
1315 +      IBError(ibxeInvalidDataConversion, [nil]);
1316 +    end;
1317 + end;
1318 +
1319 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1320 +  var aTimezoneID: TFBTimeZoneID);
1321 + begin
1322 +  GetAsTime(aTime,dstOffset,aTimeZoneID,GetTimeZoneServices.GetTimeTZDate);
1323 + end;
1324 +
1325 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1326 +  var aTimezone: AnsiString);
1327 + begin
1328 +  GetAsTime(aTime,dstOffset,aTimeZone,GetTimeZoneServices.GetTimeTZDate);
1329 + end;
1330 +
1331 + function TSQLDataItem.GetAsUTCDateTime: TDateTime;
1332 + var aTimezone: AnsiString;
1333 + begin
1334 +  CheckActive;
1335 +  result := 0;
1336 +  aTimezone := '';
1337 +  if not IsNull then
1338 +    with FFirebirdClientAPI do
1339 +    case SQLType of
1340 +      SQL_TEXT, SQL_VARYING:
1341 +      begin
1342 +        if not ParseDateTimeTZString(AsString,Result,aTimeZone) then
1343 +          IBError(ibxeInvalidDataConversion, [nil]);
1344 +        Result := GetTimeZoneServices.LocalTimeToGMT(Result,aTimeZone);
1345        end;
1346        SQL_TYPE_DATE:
1347          result := SQLDecodeDate(SQLData);
1348 <      SQL_TYPE_TIME:
1348 >      SQL_TYPE_TIME,
1349 >      SQL_TIME_TZ,
1350 >      SQL_TIME_TZ_EX:
1351          result := SQLDecodeTime(SQLData);
1352 <      SQL_TIMESTAMP:
1352 >      SQL_TIMESTAMP,
1353 >      SQL_TIMESTAMP_TZ,
1354 >      SQL_TIMESTAMP_TZ_EX:
1355          result := SQLDecodeDateTime(SQLData);
1356        else
1357          IBError(ibxeInvalidDataConversion, [nil]);
1358 <    end;
1358 >      end;
1359   end;
1360  
1361   function TSQLDataItem.GetAsDouble: Double;
# Line 1062 | Line 1372 | begin
1372          end;
1373        end;
1374        SQL_SHORT:
1375 <        result := AdjustScale(Int64(PShort(SQLData)^),
1376 <                              Scale);
1375 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1376 >                              Scale).getAsDouble;
1377        SQL_LONG:
1378 <        result := AdjustScale(Int64(PLong(SQLData)^),
1379 <                              Scale);
1378 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1379 >                              Scale).getAsDouble;
1380        SQL_INT64:
1381 <        result := AdjustScale(PInt64(SQLData)^, Scale);
1381 >        result := NumericFromRawValues(PInt64(SQLData)^, Scale).getAsDouble;
1382        SQL_FLOAT:
1383          result := PFloat(SQLData)^;
1384        SQL_DOUBLE, SQL_D_FLOAT:
1385          result := PDouble(SQLData)^;
1386 +      SQL_DEC_FIXED,
1387 +      SQL_DEC16,
1388 +      SQL_DEC34,
1389 +      SQL_INT128:
1390 +        Result := BCDToDouble(GetAsBCD);
1391        else
1392          IBError(ibxeInvalidDataConversion, [nil]);
1393      end;
# Line 1109 | Line 1424 | begin
1424          end;
1425        end;
1426        SQL_SHORT:
1427 <        result := Trunc(AdjustScale(Int64(PShort(SQLData)^),
1428 <                                    Scale));
1427 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1428 >                                    Scale).getAsInteger;
1429        SQL_LONG:
1430 <        result := Trunc(AdjustScale(Int64(PLong(SQLData)^),
1431 <                                    Scale));
1430 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1431 >                                    Scale).getAsInteger;
1432        SQL_INT64:
1433 <        result := Trunc(AdjustScale(PInt64(SQLData)^, Scale));
1433 >        result := NumericFromRawValues(PInt64(SQLData)^, Scale).getAsInteger;
1434 >
1435        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1436 <        result := Trunc(AsDouble);
1436 >        result := Round(AsDouble);
1437 >      SQL_DEC_FIXED,
1438 >      SQL_DEC16,
1439 >      SQL_DEC34,
1440 >      SQL_INT128:
1441 >        Result := BCDToInteger(GetAsBCD);
1442        else
1443 <        IBError(ibxeInvalidDataConversion, [nil]);
1443 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1444      end;
1445   end;
1446  
# Line 1157 | Line 1478 | begin
1478    end;
1479   end;
1480  
1481 + {Copied from LazUTF8}
1482 +
1483 + function UTF8CodepointSizeFull(p: PAnsiChar): integer;
1484 + const TopBitSetMask   = $80; {%10000000}
1485 +      Top2BitsSetMask = $C0; {%11000000}
1486 +      Top3BitsSetMask = $E0; {%11100000}
1487 +      Top4BitsSetMask = $F0; {%11110000}
1488 +      Top5BitsSetMask = $F8; {%11111000}
1489 + begin
1490 +  case p^ of
1491 +  #0..#191: // %11000000
1492 +    // regular single byte character (#0 is a character, this is Pascal ;)
1493 +    Result:=1;
1494 +  #192..#223: // p^ and %11100000 = %11000000
1495 +    begin
1496 +      // could be 2 byte character
1497 +      if (ord(p[1]) and Top2BitsSetMask) = TopBitSetMask then
1498 +        Result:=2
1499 +      else
1500 +        Result:=1;
1501 +    end;
1502 +  #224..#239: // p^ and %11110000 = %11100000
1503 +    begin
1504 +      // could be 3 byte character
1505 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1506 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask) then
1507 +        Result:=3
1508 +      else
1509 +        Result:=1;
1510 +    end;
1511 +  #240..#247: // p^ and %11111000 = %11110000
1512 +    begin
1513 +      // could be 4 byte character
1514 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1515 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask)
1516 +      and ((ord(p[3]) and Top2BitsSetMask) = TopBitSetMask) then
1517 +        Result:=4
1518 +      else
1519 +        Result:=1;
1520 +    end;
1521 +  else
1522 +    Result:=1;
1523 +  end;
1524 + end;
1525 +
1526 + {Returns the byte length of a UTF8 string with a fixed charwidth}
1527 +
1528 + function GetStrLen(p: PAnsiChar; FieldWidth, MaxDataLength: cardinal): integer;
1529 + var i: integer;
1530 +    cplen: integer;
1531 + begin
1532 +  Result := 0;
1533 +  for i := 1 to FieldWidth do
1534 +  begin
1535 +    cplen := UTF8CodepointSizeFull(p);
1536 +    Inc(p,cplen);
1537 +    Inc(Result,cplen);
1538 +    if Result >= MaxDataLength then
1539 +    begin
1540 +      Result := MaxDataLength;
1541 +      Exit;
1542 +    end;
1543 +  end;
1544 + end;
1545  
1546   function TSQLDataItem.GetAsString: AnsiString;
1547   var
1548    sz: PByte;
1549    str_len: Integer;
1550    rs: RawByteString;
1551 +  aTimeZone: AnsiString;
1552 +  aDateTime: TDateTime;
1553 +  dstOffset: smallint;
1554   begin
1555    CheckActive;
1556    result := '';
# Line 1180 | Line 1568 | begin
1568        begin
1569          sz := SQLData;
1570          if (SQLType = SQL_TEXT) then
1571 <          str_len := DataLength
1571 >        begin
1572 >          if GetCodePage = cp_utf8 then
1573 >            str_len := GetStrLen(PAnsiChar(sz),GetSize div GetCharSetWidth,DataLength)
1574 >          else
1575 >            str_len := DataLength
1576 >        end
1577          else begin
1578 <          str_len := DecodeInteger(SQLData, 2);
1578 >          str_len := DecodeInteger(sz, 2);
1579            Inc(sz, 2);
1580          end;
1581          SetString(rs, PAnsiChar(sz), str_len);
1582          SetCodePage(rs,GetCodePage,false);
1583 <        if (SQLType = SQL_TEXT) and (GetCharSetID <> 1) then
1191 <          Result := TrimRight(rs)
1192 <        else
1193 <          Result := rs
1583 >        Result := rs;
1584        end;
1585 +
1586        SQL_TYPE_DATE:
1587 <        result := FormatDateTime(GetDateFormatStr(TimeOf(AsDateTime)<>0),AsDateTime);
1197 <      SQL_TYPE_TIME :
1198 <        result := FormatDateTime(GetTimeFormatStr,AsDateTime);
1587 >        Result := DateToStr(GetAsDateTime);
1588        SQL_TIMESTAMP:
1589 <        result := FormatDateTime(GetTimestampFormatStr,AsDateTime);
1589 >        Result := FBFormatDateTime(GetTimestampFormatStr,GetAsDateTime);
1590 >      SQL_TYPE_TIME:
1591 >        Result := FBFormatDateTime(GetTimeFormatStr,GetAsDateTime);
1592 >      SQL_TIMESTAMP_TZ,
1593 >      SQL_TIMESTAMP_TZ_EX:
1594 >        with GetAttachment.GetTimeZoneServices do
1595 >        begin
1596 >          if GetTZTextOption = tzGMT then
1597 >            Result := FBFormatDateTime(GetTimestampFormatStr,GetAsUTCDateTime)
1598 >          else
1599 >          begin
1600 >            GetAsDateTime(aDateTime,dstOffset,aTimeZone);
1601 >            if GetTZTextOption = tzOffset then
1602 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1603 >            else
1604 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + aTimeZone;
1605 >          end;
1606 >        end;
1607 >      SQL_TIME_TZ,
1608 >      SQL_TIME_TZ_EX:
1609 >        with GetAttachment.GetTimeZoneServices do
1610 >        begin
1611 >          if GetTZTextOption = tzGMT then
1612 >             Result := FBFormatDateTime(GetTimeFormatStr,GetAsUTCDateTime)
1613 >          else
1614 >          begin
1615 >            GetAsTime(aDateTime,dstOffset,aTimeZone,GetTimeTZDate);
1616 >            if GetTZTextOption = tzOffset then
1617 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1618 >            else
1619 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + aTimeZone;
1620 >          end;
1621 >        end;
1622 >
1623        SQL_SHORT, SQL_LONG:
1624          if Scale = 0 then
1625            result := IntToStr(AsLong)
# Line 1214 | Line 1636 | begin
1636            result := FloatToStr(AsDouble);
1637        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1638          result := FloatToStr(AsDouble);
1639 +
1640 +      SQL_DEC16,
1641 +      SQL_DEC34:
1642 +        result := BCDToStr(GetAsBCD);
1643 +
1644 +      SQL_DEC_FIXED,
1645 +      SQL_INT128:
1646 +        result := Int128ToStr(SQLData,scale);
1647 +
1648        else
1649 <        IBError(ibxeInvalidDataConversion, [nil]);
1649 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1650      end;
1651   end;
1652  
1653 + function TSQLDataItem.GetAsNumeric: IFBNumeric;
1654 + var aValue: Int64;
1655 + begin
1656 +  case SQLType of
1657 +   SQL_TEXT, SQL_VARYING:
1658 +     Result := StrToNumeric(GetAsString);
1659 +
1660 +   SQL_SHORT:
1661 +     Result := NumericFromRawValues(PShort(SQLData)^, Scale);
1662 +
1663 +   SQL_LONG:
1664 +     Result := NumericFromRawValues(PLong(SQLData)^, Scale);
1665 +
1666 +   SQL_INT64:
1667 +     Result := NumericFromRawValues(PInt64(SQLData)^, Scale);
1668 +
1669 +   SQL_DEC16,
1670 +   SQL_DEC34,
1671 +   SQL_DEC_FIXED,
1672 +   SQL_INT128:
1673 +     Result := BCDToNumeric(GetAsBCD);
1674 +
1675 +   else
1676 +     IBError(ibxeInvalidDataConversion, [nil]);
1677 +  end;
1678 + end;
1679 +
1680   function TSQLDataItem.GetIsNull: Boolean;
1681   begin
1682    CheckActive;
# Line 1232 | Line 1690 | begin
1690   end;
1691  
1692   function TSQLDataItem.GetAsVariant: Variant;
1693 + var ts: TDateTime;
1694 +  dstOffset: smallint;
1695 +    timezone: AnsiString;
1696   begin
1697    CheckActive;
1698    if IsNull then
# Line 1245 | Line 1706 | begin
1706          result := AsString;
1707        SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
1708          result := AsDateTime;
1709 +      SQL_TIMESTAMP_TZ,
1710 +      SQL_TIME_TZ,
1711 +      SQL_TIMESTAMP_TZ_EX,
1712 +      SQL_TIME_TZ_EX:
1713 +        begin
1714 +          GetAsDateTime(ts,dstOffset,timezone);
1715 +          result := VarArrayOf([ts,dstOffset,timezone]);
1716 +        end;
1717        SQL_SHORT, SQL_LONG:
1718          if Scale = 0 then
1719            result := AsLong
# Line 1263 | Line 1732 | begin
1732          result := AsDouble;
1733        SQL_BOOLEAN:
1734          result := AsBoolean;
1735 +      SQL_DEC_FIXED,
1736 +      SQL_DEC16,
1737 +      SQL_DEC34,
1738 +      SQL_INT128:
1739 +        result := VarFmtBCDCreate(GetAsBcd);
1740        else
1741          IBError(ibxeInvalidDataConversion, [nil]);
1742      end;
# Line 1283 | Line 1757 | begin
1757      Result := Length(GetDateFormatStr(true));
1758    dfTime:
1759      Result := Length(GetTimeFormatStr);
1760 +  dfTimestampTZ:
1761 +    Result := Length(GetTimestampFormatStr) + 6; {assume time offset format}
1762 +  dfTimeTZ:
1763 +    Result := Length(GetTimeFormatStr)+ 6;
1764    else
1765      Result := 0;
1766 +  end;end;
1767 +
1768 + function TSQLDataItem.GetAsBCD: tBCD;
1769 +
1770 + begin
1771 +  CheckActive;
1772 +  if IsNull then
1773 +   with Result do
1774 +   begin
1775 +     FillChar(Result,sizeof(Result),0);
1776 +     Precision := 1;
1777 +     exit;
1778 +   end;
1779 +
1780 +  case SQLType of
1781 +  SQL_DEC16,
1782 +  SQL_DEC34:
1783 +    with FFirebirdClientAPI do
1784 +      Result := SQLDecFloatDecode(SQLType,  SQLData);
1785 +
1786 +  SQL_DEC_FIXED,
1787 +  SQL_INT128:
1788 +    with FFirebirdClientAPI do
1789 +      Result := StrToBCD(Int128ToStr(SQLData,scale));
1790 +  else
1791 +    if not CurrToBCD(GetAsCurrency,Result) then
1792 +      IBError(ibxeBadBCDConversion,[]);
1793    end;
1794   end;
1795  
# Line 1310 | Line 1815 | begin
1815    if GetSQLDialect < 3 then
1816      AsDouble := Value
1817    else
1818 +  if not CanChangeMetaData and ((SQLType <> SQL_INT64) or (Scale <> -4)) then
1819 +    SetAsNumeric(CurrToNumeric(Value))
1820 +  else
1821    begin
1822      Changing;
1823      if IsNullable then
# Line 1325 | Line 1833 | end;
1833   procedure TSQLDataItem.SetAsInt64(Value: Int64);
1834   begin
1835    CheckActive;
1836 <  Changing;
1837 <  if IsNullable then
1838 <    IsNull := False;
1836 >  if not CanChangeMetaData and ((SQLType <> SQL_INT64) or (Scale <> 0)) then
1837 >    SetAsNumeric(IntToNumeric(Value))
1838 >  else
1839 >  begin
1840 >    Changing;
1841 >    if IsNullable then
1842 >      IsNull := False;
1843  
1844 <  SQLType := SQL_INT64;
1845 <  Scale := 0;
1846 <  DataLength := SizeOf(Int64);
1847 <  PInt64(SQLData)^ := Value;
1848 <  Changed;
1844 >    SQLType := SQL_INT64;
1845 >    Scale := 0;
1846 >    DataLength := SizeOf(Int64);
1847 >    PInt64(SQLData)^ := Value;
1848 >    Changed;
1849 >  end;
1850   end;
1851  
1852   procedure TSQLDataItem.SetAsDate(Value: TDateTime);
# Line 1376 | Line 1889 | begin
1889    Changed;
1890   end;
1891  
1892 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
1893 + begin
1894 +  CheckActive;
1895 +  CheckTZSupport;
1896 +  if GetSQLDialect < 3 then
1897 +  begin
1898 +    AsDateTime := aValue;
1899 +    exit;
1900 +  end;
1901 +
1902 +  Changing;
1903 +  if IsNullable then
1904 +    IsNull := False;
1905 +
1906 +  SQLType := SQL_TIME_TZ;
1907 +  DataLength := SizeOf(ISC_TIME_TZ);
1908 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZoneID,OnDate,SQLData);
1909 +  Changed;
1910 + end;
1911 +
1912 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
1913 + begin
1914 +  CheckActive;
1915 +  CheckTZSupport;
1916 +  if GetSQLDialect < 3 then
1917 +  begin
1918 +    AsDateTime := aValue;
1919 +    exit;
1920 +  end;
1921 +
1922 +  Changing;
1923 +  if IsNullable then
1924 +    IsNull := False;
1925 +
1926 +  SQLType := SQL_TIME_TZ;
1927 +  DataLength := SizeOf(ISC_TIME_TZ);
1928 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZone,OnDate,SQLData);
1929 +  Changed;
1930 + end;
1931 +
1932   procedure TSQLDataItem.SetAsDateTime(Value: TDateTime);
1933   begin
1934    CheckActive;
# Line 1390 | Line 1943 | begin
1943    Changed;
1944   end;
1945  
1946 < procedure TSQLDataItem.SetAsDouble(Value: Double);
1946 > procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime;
1947 >  aTimeZoneID: TFBTimeZoneID);
1948   begin
1949    CheckActive;
1950 +  CheckTZSupport;
1951    if IsNullable then
1952      IsNull := False;
1953  
1954    Changing;
1955 <  SQLType := SQL_DOUBLE;
1956 <  DataLength := SizeOf(Double);
1957 <  Scale := 0;
1403 <  PDouble(SQLData)^ := Value;
1955 >  SQLType := SQL_TIMESTAMP_TZ;
1956 >  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1957 >  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZoneID,SQLData);
1958    Changed;
1959   end;
1960  
1961 < procedure TSQLDataItem.SetAsFloat(Value: Float);
1961 > procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString
1962 >  );
1963   begin
1964    CheckActive;
1965 +  CheckTZSupport;
1966    if IsNullable then
1967      IsNull := False;
1968  
1969    Changing;
1970 <  SQLType := SQL_FLOAT;
1971 <  DataLength := SizeOf(Float);
1972 <  Scale := 0;
1417 <  PSingle(SQLData)^ := Value;
1970 >  SQLType := SQL_TIMESTAMP_TZ;
1971 >  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1972 >  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZone,SQLData);
1973    Changed;
1974   end;
1975  
1976 + procedure TSQLDataItem.SetAsUTCDateTime(aUTCTime: TDateTime);
1977 + begin
1978 +  SetAsDateTime(aUTCTime,TimeZoneID_GMT);
1979 + end;
1980 +
1981 + procedure TSQLDataItem.SetAsDouble(Value: Double);
1982 + begin
1983 +  CheckActive;
1984 +  if not CanChangeMetaData and (SQLType <> SQL_DOUBLE) then
1985 +    SetAsNumeric(DoubleToNumeric(Value))
1986 +  else
1987 +  begin
1988 +    if IsNullable then
1989 +      IsNull := False;
1990 +
1991 +    Changing;
1992 +    SQLType := SQL_DOUBLE;
1993 +    DataLength := SizeOf(Double);
1994 +    Scale := 0;
1995 +    PDouble(SQLData)^ := Value;
1996 +    Changed;
1997 +  end;
1998 + end;
1999 +
2000 + procedure TSQLDataItem.SetAsFloat(Value: Float);
2001 + begin
2002 +  CheckActive;
2003 +  if not CanChangeMetaData and (SQLType <> SQL_FLOAT) then
2004 +    SetAsNumeric(DoubleToNumeric(Value))
2005 +  else
2006 +  begin
2007 +    if IsNullable then
2008 +      IsNull := False;
2009 +
2010 +    Changing;
2011 +    SQLType := SQL_FLOAT;
2012 +    DataLength := SizeOf(Float);
2013 +    Scale := 0;
2014 +    PSingle(SQLData)^ := Value;
2015 +    Changed;
2016 +  end;
2017 + end;
2018 +
2019   procedure TSQLDataItem.SetAsLong(Value: Long);
2020   begin
2021    CheckActive;
2022 <  if IsNullable then
2023 <    IsNull := False;
2022 >  if not CanChangeMetaData and ((SQLType <> SQL_LONG) or (Scale <> 0)) then
2023 >    SetAsNumeric(IntToNumeric(Value))
2024 >  else
2025 >  begin
2026 >    if IsNullable then
2027 >      IsNull := False;
2028  
2029 <  Changing;
2030 <  SQLType := SQL_LONG;
2031 <  DataLength := SizeOf(Long);
2032 <  Scale := 0;
2033 <  PLong(SQLData)^ := Value;
2034 <  Changed;
2029 >    Changing;
2030 >    SQLType := SQL_LONG;
2031 >    DataLength := SizeOf(Long);
2032 >    Scale := 0;
2033 >    PLong(SQLData)^ := Value;
2034 >    Changed;
2035 >  end;
2036   end;
2037  
2038   procedure TSQLDataItem.SetAsPointer(Value: Pointer);
# Line 1464 | Line 2067 | end;
2067   procedure TSQLDataItem.SetAsShort(Value: short);
2068   begin
2069    CheckActive;
2070 <  Changing;
2071 <  if IsNullable then
2072 <    IsNull := False;
2070 >  if not CanChangeMetaData and ((SQLType <> SQL_SHORT) or (Scale <> 0)) then
2071 >    SetAsNumeric(IntToNumeric(Value))
2072 >  else
2073 >  begin
2074 >    Changing;
2075 >    if IsNullable then
2076 >      IsNull := False;
2077  
2078 <  SQLType := SQL_SHORT;
2079 <  DataLength := SizeOf(Short);
2080 <  Scale := 0;
2081 <  PShort(SQLData)^ := Value;
2082 <  Changed;
2078 >    SQLType := SQL_SHORT;
2079 >    DataLength := SizeOf(Short);
2080 >    Scale := 0;
2081 >    PShort(SQLData)^ := Value;
2082 >    Changed;
2083 >  end;
2084   end;
2085  
2086   procedure TSQLDataItem.SetAsString(Value: AnsiString);
# Line 1485 | Line 2093 | begin
2093    CheckActive;
2094    if VarIsNull(Value) then
2095      IsNull := True
2096 +  else
2097 +  if VarIsArray(Value) then {must be datetime plus timezone}
2098 +    SetAsDateTime(Value[0],AnsiString(Value[1]))
2099    else case VarType(Value) of
2100      varEmpty, varNull:
2101        IsNull := True;
2102 <    varSmallint, varInteger, varByte,
2103 <      varWord, varShortInt:
2104 <      AsLong := Value;
1494 <    varInt64:
1495 <      AsInt64 := Value;
2102 >    varSmallint, varInteger, varByte, varLongWord,
2103 >      varWord, varShortInt, varInt64:
2104 >        SetAsNumeric(IntToNumeric(Int64(Value)));
2105      varSingle, varDouble:
2106        AsDouble := Value;
2107      varCurrency:
2108 <      AsCurrency := Value;
2108 >      SetAsNumeric(CurrToNumeric(Currency(Value)));
2109      varBoolean:
2110        AsBoolean := Value;
2111      varDate:
# Line 1507 | Line 2116 | begin
2116        IBError(ibxeNotSupported, [nil]);
2117      varByRef, varDispatch, varError, varUnknown, varVariant:
2118        IBError(ibxeNotPermitted, [nil]);
2119 +    else
2120 +      if VarIsFmtBCD(Value) then
2121 +        SetAsBCD(VarToBCD(Value))
2122 +      else
2123 +        IBError(ibxeNotSupported, [nil]);
2124    end;
2125   end;
2126  
2127 < procedure TSQLDataItem.SetAsNumeric(Value: Int64; aScale: integer);
2127 > procedure TSQLDataItem.SetAsNumeric(Value: IFBNumeric);
2128   begin
2129    CheckActive;
2130    Changing;
2131    if IsNullable then
2132      IsNull := False;
2133  
2134 <  SQLType := SQL_INT64;
2135 <  Scale := aScale;
2136 <  DataLength := SizeOf(Int64);
2137 <  PInt64(SQLData)^ := Value;
2134 >  if CanChangeMetadata then
2135 >  begin
2136 >    {Restore original values}
2137 >    SQLType := getColMetadata.GetSQLType;
2138 >    Scale := getColMetadata.getScale;
2139 >    SetDataLength(getColMetadata.GetSize);
2140 >  end;
2141 >
2142 >  with FFirebirdClientAPI do
2143 >  case GetSQLType of
2144 >  SQL_LONG:
2145 >      PLong(SQLData)^ := SafeInteger(Value.AdjustScaleTo(Scale).getRawValue);
2146 >  SQL_SHORT:
2147 >    PShort(SQLData)^ := SafeSmallInt(Value.AdjustScaleTo(Scale).getRawValue);
2148 >  SQL_INT64:
2149 >    PInt64(SQLData)^ := Value.AdjustScaleTo(Scale).getRawValue;
2150 >  SQL_TEXT, SQL_VARYING:
2151 >   SetAsString(Value.getAsString);
2152 >  SQL_D_FLOAT,
2153 >  SQL_DOUBLE:
2154 >    PDouble(SQLData)^ := Value.getAsDouble;
2155 >  SQL_FLOAT:
2156 >    PSingle(SQLData)^ := Value.getAsDouble;
2157 >  SQL_DEC_FIXED,
2158 >  SQL_DEC16,
2159 >  SQL_DEC34:
2160 >     SQLDecFloatEncode(Value.getAsBCD,SQLType,SQLData);
2161 >  SQL_INT128:
2162 >    StrToInt128(Scale,Value.getAsString,SQLData);
2163 >  else
2164 >    IBError(ibxeInvalidDataConversion, [nil]);
2165 >  end;
2166 >  Changed;
2167 > end;
2168 >
2169 > procedure TSQLDataItem.SetAsBcd(aValue: tBCD);
2170 > begin
2171 >  CheckActive;
2172 >  Changing;
2173 >  if IsNullable then
2174 >    IsNull := False;
2175 >
2176 >  if not CanChangeMetaData then
2177 >  begin
2178 >    SetAsNumeric(BCDToNumeric(aValue));
2179 >    Exit;
2180 >  end;
2181 >
2182 >  with FFirebirdClientAPI do
2183 >  if aValue.Precision <= 16 then
2184 >  begin
2185 >    if not HasDecFloatSupport then
2186 >      IBError(ibxeDecFloatNotSupported,[]);
2187 >
2188 >    SQLType := SQL_DEC16;
2189 >    DataLength := 8;
2190 >    SQLDecFloatEncode(aValue,SQLType,SQLData);
2191 >  end
2192 >  else
2193 >  if aValue.Precision <= 34 then
2194 >  begin
2195 >    if not HasDecFloatSupport then
2196 >      IBError(ibxeDecFloatNotSupported,[]);
2197 >
2198 >    SQLType := SQL_DEC34;
2199 >    DataLength := 16;
2200 >    SQLDecFloatEncode(aValue,SQLType,SQLData);
2201 >  end
2202 >  else
2203 >  if aValue.Precision <= 38 then
2204 >  begin
2205 >    if not HasInt128Support then
2206 >      IBError(ibxeInt128NotSupported,[]);
2207 >
2208 >    SQLType := SQL_INT128;
2209 >    DataLength := 16;
2210 >    StrToInt128(scale,BcdToStr(aValue),SQLData);
2211 >  end
2212 >  else
2213 >    IBError(ibxeBCDOverflow,[BCDToStr(aValue)]);
2214 >
2215    Changed;
2216   end;
2217  
# Line 1554 | Line 2245 | begin
2245      IBError(ibxeStatementNotPrepared, [nil]);
2246   end;
2247  
2248 + function TColumnMetaData.GetAttachment: IAttachment;
2249 + begin
2250 +  Result := FIBXSQLVAR.GetAttachment;
2251 + end;
2252 +
2253   function TColumnMetaData.SQLData: PByte;
2254   begin
2255    Result := FIBXSQLVAR.SQLData;
# Line 1571 | Line 2267 | end;
2267  
2268   constructor TColumnMetaData.Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
2269   begin
2270 <  inherited Create(aIBXSQLVAR.GetStatement.GetAttachment.getFirebirdAPI as TFBClientAPI);
2270 >  inherited Create(aIBXSQLVAR.GetAttachment.getFirebirdAPI as TFBClientAPI);
2271    FIBXSQLVAR := aIBXSQLVAR;
2272    FOwner := aOwner;
2273    FPrepareSeqNo := FIBXSQLVAR.Parent.PrepareSeqNo;
# Line 1587 | Line 2283 | end;
2283  
2284   function TColumnMetaData.GetSQLDialect: integer;
2285   begin
2286 <  Result := FIBXSQLVAR.Statement.GetSQLDialect;
2286 >  Result := FIBXSQLVAR.GetAttachment.GetSQLDialect;
2287 > end;
2288 >
2289 > function TColumnMetaData.getColMetadata: IParamMetaData;
2290 > begin
2291 >  Result := self;
2292   end;
2293  
2294   function TColumnMetaData.GetIndex: integer;
# Line 1658 | Line 2359 | end;
2359   function TColumnMetaData.GetSize: cardinal;
2360   begin
2361    CheckActive;
2362 <  result := FIBXSQLVAR.DataLength;
2362 >  result := FIBXSQLVAR.GetSize;
2363 > end;
2364 >
2365 > function TColumnMetaData.GetCharSetWidth: integer;
2366 > begin
2367 >  CheckActive;
2368 >  result := FIBXSQLVAR.GetCharSetWidth;
2369   end;
2370  
2371   function TColumnMetaData.GetArrayMetaData: IArrayMetaData;
# Line 1673 | Line 2380 | begin
2380    result := FIBXSQLVAR.GetBlobMetaData;
2381   end;
2382  
2383 + function TColumnMetaData.GetStatement: IStatement;
2384 + begin
2385 +  Result := FIBXSQLVAR.GetStatement;
2386 + end;
2387 +
2388 + function TColumnMetaData.GetTransaction: ITransaction;
2389 + begin
2390 +  Result := FIBXSQLVAR.GetTransaction;
2391 + end;
2392 +
2393   { TIBSQLData }
2394  
2395   procedure TIBSQLData.CheckActive;
# Line 1701 | Line 2418 | end;
2418   function TIBSQLData.GetAsArray: IArray;
2419   begin
2420    CheckActive;
2421 <  result := FIBXSQLVAR.GetAsArray(AsQuad);
2421 >  result := FIBXSQLVAR.GetAsArray;
2422   end;
2423  
2424   function TIBSQLData.GetAsBlob: IBlob;
# Line 1745 | Line 2462 | end;
2462  
2463   var b: IBlob;
2464      dt: TDateTime;
2465 <    CurrValue: Currency;
2466 <    FloatValue: single;
2465 >    timezone: AnsiString;
2466 >    Int64Value: Int64;
2467 >    BCDValue: TBCD;
2468 >    aScale: integer;
2469   begin
2470    CheckActive;
2471    if IsNullable then
2472      IsNull := False;
2473 +  with FFirebirdClientAPI do
2474    case SQLTYPE of
2475    SQL_BOOLEAN:
2476      if AnsiCompareText(Value,STrue) = 0 then
# Line 1762 | Line 2482 | begin
2482        IBError(ibxeInvalidDataConversion,[nil]);
2483  
2484    SQL_BLOB:
2485 +    if Length(Value) < GetAttachment.GetInlineBlobLimit then
2486 +      DoSetString
2487 +    else
2488      begin
2489        Changing;
2490        b := FIBXSQLVAR.CreateBlob;
# Line 1774 | Line 2497 | begin
2497    SQL_TEXT:
2498      DoSetString;
2499  
2500 <    SQL_SHORT,
2501 <    SQL_LONG,
2502 <    SQL_INT64:
2503 <      if TryStrToCurr(Value,CurrValue) then
2504 <        SetAsNumeric(AdjustScaleFromCurrency(CurrValue,GetScale),GetScale)
2505 <      else
2506 <        DoSetString;
2500 >  SQL_SHORT,
2501 >  SQL_LONG,
2502 >  SQL_INT64:
2503 >    if TryStrToNumeric(Value,Int64Value,aScale) then
2504 >      SetAsNumeric(NumericFromRawValues(Int64Value,aScale))
2505 >    else
2506 >      DoSetString;
2507  
2508 <    SQL_D_FLOAT,
2509 <    SQL_DOUBLE,
2510 <    SQL_FLOAT:
2511 <      if TryStrToFloat(Value,FloatValue) then
2512 <        SetAsDouble(FloatValue)
2513 <      else
2514 <        DoSetString;
2508 >  SQL_DEC_FIXED,
2509 >  SQL_DEC16,
2510 >  SQL_DEC34,
2511 >  SQL_INT128:
2512 >    if TryStrToBCD(Value,BCDValue) then
2513 >      SetAsNumeric(BCDToNumeric(BCDValue))
2514 >    else
2515 >      DoSetString;
2516 >
2517 >  SQL_D_FLOAT,
2518 >  SQL_DOUBLE,
2519 >  SQL_FLOAT:
2520 >    if TryStrToNumeric(Value,Int64Value,aScale) then
2521 >      SetAsNumeric(NumericFromRawValues(Int64Value,AScale))
2522 >    else
2523 >      DoSetString;
2524  
2525 <    SQL_TIMESTAMP:
2525 >  SQL_TIMESTAMP:
2526        if TryStrToDateTime(Value,dt) then
2527          SetAsDateTime(dt)
2528        else
2529          DoSetString;
2530  
2531 <    SQL_TYPE_DATE:
2531 >  SQL_TYPE_DATE:
2532        if TryStrToDateTime(Value,dt) then
2533          SetAsDate(dt)
2534        else
2535          DoSetString;
2536  
2537 <    SQL_TYPE_TIME:
2537 >  SQL_TYPE_TIME:
2538        if TryStrToDateTime(Value,dt) then
2539          SetAsTime(dt)
2540        else
2541          DoSetString;
2542  
2543 <    else
2544 <      IBError(ibxeInvalidDataConversion,[nil]);
2543 >  SQL_TIMESTAMP_TZ,
2544 >  SQL_TIMESTAMP_TZ_EX:
2545 >      if ParseDateTimeTZString(value,dt,timezone) then
2546 >        SetAsDateTime(dt,timezone)
2547 >      else
2548 >        DoSetString;
2549 >
2550 >  SQL_TIME_TZ,
2551 >  SQL_TIME_TZ_EX:
2552 >      if ParseDateTimeTZString(value,dt,timezone,true) then
2553 >        SetAsTime(dt,GetAttachment.GetTimeZoneServices.GetTimeTZDate,timezone)
2554 >      else
2555 >        DoSetString;
2556 >
2557 >  else
2558 >    IBError(ibxeInvalidDataConversion,[GetSQLTypeName(getColMetaData.SQLTYPE)]);
2559    end;
2560   end;
2561  
# Line 1847 | Line 2593 | begin
2593    IsNull := true;
2594   end;
2595  
2596 + function TSQLParam.CanChangeMetaData: boolean;
2597 + begin
2598 +  Result := FIBXSQLVAR.CanChangeMetaData;
2599 + end;
2600 +
2601 + function TSQLParam.getColMetadata: IParamMetaData;
2602 + begin
2603 +  Result := FIBXSQLVAR.getColMetadata;
2604 + end;
2605 +
2606   function TSQLParam.GetModified: boolean;
2607   begin
2608    CheckActive;
# Line 1860 | Line 2616 | begin
2616    Result := inherited GetAsPointer;
2617   end;
2618  
2619 + function TSQLParam.GetAsString: AnsiString;
2620 + var rs: RawByteString;
2621 + begin
2622 +  Result := '';
2623 +  if (SQLType = SQL_VARYING) and not IsNull then
2624 +  {SQLData points to start of string - default is to length word}
2625 +  begin
2626 +    CheckActive;
2627 +    SetString(rs,PAnsiChar(SQLData),DataLength);
2628 +    SetCodePage(rs,GetCodePage,false);
2629 +    Result := rs;
2630 +  end
2631 +  else
2632 +    Result := inherited GetAsString;
2633 + end;
2634 +
2635   procedure TSQLParam.SetName(Value: AnsiString);
2636   begin
2637    CheckActive;
# Line 1905 | Line 2677 | begin
2677    if not FIBXSQLVAR.UniqueName then
2678      IBError(ibxeDuplicateParamName,[FIBXSQLVAR.Name]);
2679  
2680 +  FIBXSQLVAR.SetArray(anArray); {save array interface}
2681    SetAsQuad(AnArray.GetArrayID);
2682   end;
2683  
# Line 2051 | Line 2824 | begin
2824    end;
2825   end;
2826  
2827 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
2828 + var i: integer;
2829 +    OldSQLVar: TSQLVarData;
2830 + begin
2831 +  if FIBXSQLVAR.UniqueName then
2832 +    inherited SetAsTime(AValue,OnDate, aTimeZoneID)
2833 +  else
2834 +  with FIBXSQLVAR.Parent do
2835 +  begin
2836 +    for i := 0 to Count - 1 do
2837 +      if Column[i].Name = Name then
2838 +      begin
2839 +        OldSQLVar := FIBXSQLVAR;
2840 +        FIBXSQLVAR := Column[i];
2841 +        try
2842 +          inherited SetAsTime(AValue,OnDate, aTimeZoneID);
2843 +        finally
2844 +          FIBXSQLVAR := OldSQLVar;
2845 +        end;
2846 +      end;
2847 +  end;
2848 + end;
2849 +
2850 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
2851 + var i: integer;
2852 +    OldSQLVar: TSQLVarData;
2853 + begin
2854 +  if FIBXSQLVAR.UniqueName then
2855 +    inherited SetAsTime(AValue,OnDate,aTimeZone)
2856 +  else
2857 +  with FIBXSQLVAR.Parent do
2858 +  begin
2859 +    for i := 0 to Count - 1 do
2860 +      if Column[i].Name = Name then
2861 +      begin
2862 +        OldSQLVar := FIBXSQLVAR;
2863 +        FIBXSQLVAR := Column[i];
2864 +        try
2865 +          inherited SetAsTime(AValue,OnDate,aTimeZone);
2866 +        finally
2867 +          FIBXSQLVAR := OldSQLVar;
2868 +        end;
2869 +      end;
2870 +  end;
2871 + end;
2872 +
2873 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID);
2874 + begin
2875 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZoneID);
2876 + end;
2877 +
2878 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZone: AnsiString);
2879 + begin
2880 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZone);
2881 + end;
2882 +
2883   procedure TSQLParam.SetAsDateTime(AValue: TDateTime);
2884   var i: integer;
2885      OldSQLVar: TSQLVarData;
# Line 2074 | Line 2903 | begin
2903    end;
2904   end;
2905  
2906 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID
2907 +  );
2908 + var i: integer;
2909 +    OldSQLVar: TSQLVarData;
2910 + begin
2911 +  if FIBXSQLVAR.UniqueName then
2912 +    inherited SetAsDateTime(AValue,aTimeZoneID)
2913 +  else
2914 +  with FIBXSQLVAR.Parent do
2915 +  begin
2916 +    for i := 0 to Count - 1 do
2917 +      if Column[i].Name = Name then
2918 +      begin
2919 +        OldSQLVar := FIBXSQLVAR;
2920 +        FIBXSQLVAR := Column[i];
2921 +        try
2922 +          inherited SetAsDateTime(AValue,aTimeZoneID);
2923 +        finally
2924 +          FIBXSQLVAR := OldSQLVar;
2925 +        end;
2926 +      end;
2927 +  end;
2928 + end;
2929 +
2930 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString);
2931 + var i: integer;
2932 +    OldSQLVar: TSQLVarData;
2933 + begin
2934 +  if FIBXSQLVAR.UniqueName then
2935 +    inherited SetAsDateTime(AValue,aTimeZone)
2936 +  else
2937 +  with FIBXSQLVAR.Parent do
2938 +  begin
2939 +    for i := 0 to Count - 1 do
2940 +      if Column[i].Name = Name then
2941 +      begin
2942 +        OldSQLVar := FIBXSQLVAR;
2943 +        FIBXSQLVAR := Column[i];
2944 +        try
2945 +          inherited SetAsDateTime(AValue,aTimeZone);
2946 +        finally
2947 +          FIBXSQLVAR := OldSQLVar;
2948 +        end;
2949 +      end;
2950 +  end;
2951 + end;
2952 +
2953   procedure TSQLParam.SetAsDouble(AValue: Double);
2954   var i: integer;
2955      OldSQLVar: TSQLVarData;
# Line 2254 | Line 3130 | begin
3130    FIBXSQLVAR.SetCharSetID(aValue);
3131   end;
3132  
3133 + procedure TSQLParam.SetAsBcd(aValue: tBCD);
3134 + var i: integer;
3135 +    OldSQLVar: TSQLVarData;
3136 + begin
3137 +  if FIBXSQLVAR.UniqueName then
3138 +    inherited SetAsBcd(AValue)
3139 +  else
3140 +  with FIBXSQLVAR.Parent do
3141 +  begin
3142 +    for i := 0 to Count - 1 do
3143 +      if Column[i].Name = Name then
3144 +      begin
3145 +        OldSQLVar := FIBXSQLVAR;
3146 +        FIBXSQLVAR := Column[i];
3147 +        try
3148 +          inherited SetAsBcd(AValue);
3149 +        finally
3150 +          FIBXSQLVAR := OldSQLVar;
3151 +        end;
3152 +      end;
3153 +  end;
3154 + end;
3155 +
3156 + procedure TSQLParam.SetAsNumeric(aValue: IFBNumeric);
3157 + var i: integer;
3158 +    OldSQLVar: TSQLVarData;
3159 + begin
3160 +  if FIBXSQLVAR.UniqueName then
3161 +    inherited SetAsNumeric(AValue)
3162 +  else
3163 +  with FIBXSQLVAR.Parent do
3164 +  begin
3165 +    for i := 0 to Count - 1 do
3166 +      if Column[i].Name = Name then
3167 +      begin
3168 +        OldSQLVar := FIBXSQLVAR;
3169 +        FIBXSQLVAR := Column[i];
3170 +        try
3171 +          inherited SetAsNumeric(AValue);
3172 +        finally
3173 +          FIBXSQLVAR := OldSQLVar;
3174 +        end;
3175 +      end;
3176 +  end;
3177 + end;
3178 +
3179   { TMetaData }
3180  
3181   procedure TMetaData.CheckActive;
# Line 2275 | Line 3197 | end;
3197  
3198   destructor TMetaData.Destroy;
3199   begin
3200 <  (FStatement as TInterfaceOwner).Remove(self);
3200 >  if FStatement <> nil then
3201 >    (FStatement as TInterfaceOwner).Remove(self);
3202    inherited Destroy;
3203   end;
3204  
# Line 2341 | Line 3264 | end;
3264  
3265   destructor TSQLParams.Destroy;
3266   begin
3267 <  (FStatement as TInterfaceOwner).Remove(self);
3267 >  if FStatement <> nil then
3268 >    (FStatement as TInterfaceOwner).Remove(self);
3269    inherited Destroy;
3270   end;
3271  
# Line 2392 | Line 3316 | begin
3316      end;
3317   end;
3318  
3319 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
3320 + begin
3321 +  Result := FSQLParams.CaseSensitiveParams;
3322 + end;
3323 +
3324 + function TSQLParams.GetStatement: IStatement;
3325 + begin
3326 +  Result := FSQLParams.GetStatement;
3327 + end;
3328 +
3329 + function TSQLParams.GetTransaction: ITransaction;
3330 + begin
3331 +  Result := FSQLParams.GetTransaction;
3332 + end;
3333 +
3334 + function TSQLParams.GetAttachment: IAttachment;
3335 + begin
3336 +  Result := FSQLParams.GetAttachment;
3337 + end;
3338 +
3339 + procedure TSQLParams.Clear;
3340 + var i: integer;
3341 + begin
3342 +  for i := 0 to getCount - 1 do
3343 +    getSQLParam(i).Clear;
3344 + end;
3345 +
3346   { TResults }
3347  
3348   procedure TResults.CheckActive;
# Line 2404 | Line 3355 | begin
3355    if not FResults.CheckStatementStatus(ssPrepared)  then
3356      IBError(ibxeStatementNotPrepared, [nil]);
3357  
3358 <  with GetTransaction as TFBTransaction do
3358 >  with GetTransaction do
3359    if not InTransaction or (FResults.TransactionSeqNo <> FTransactionSeqNo) then
3360      IBError(ibxeInterfaceOutofDate,[nil]);
3361   end;
3362  
3363   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
3364 + var col: TIBSQLData;
3365   begin
3366    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
3367      IBError(ibxeInvalidColumnIndex,[nil]);
3368  
3369    if not HasInterface(aIBXSQLVAR.Index) then
3370 <    AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
3371 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3370 >  begin
3371 >    col := TIBSQLData.Create(self,aIBXSQLVAR);
3372 >    AddInterface(aIBXSQLVAR.Index, col);
3373 >  end
3374 >  else
3375 >    col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3376 >  Result := col;
3377   end;
3378  
3379   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2473 | Line 3430 | begin
3430    FResults.GetData(index,IsNull, len,data);
3431   end;
3432  
3433 + function TResults.GetStatement: IStatement;
3434 + begin
3435 +  Result := FStatement;
3436 + end;
3437 +
3438   function TResults.GetTransaction: ITransaction;
3439   begin
3440 <  Result := FStatement.GetTransaction;
3440 >  Result := FResults.GetTransaction;
3441 > end;
3442 >
3443 > function TResults.GetAttachment: IAttachment;
3444 > begin
3445 >  Result := FResults.GetAttachment;
3446   end;
3447  
3448   procedure TResults.SetRetainInterfaces(aValue: boolean);

Comparing:
ibx/trunk/fbintf/client/FBSQLData.pas (property svn:eol-style), Revision 270 by tony, Fri Jan 18 11:10:37 2019 UTC vs.
ibx/branches/udr/client/FBSQLData.pas (property svn:eol-style), Revision 381 by tony, Sat Jan 15 00:06:22 2022 UTC

# Line 0 | Line 1
1 + native

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines