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 45 by tony, Tue Dec 6 10:33:46 2016 UTC vs.
ibx/branches/udr/client/FBSQLData.pas (file contents), Revision 372 by tony, Wed Jan 5 16:20:22 2022 UTC

# Line 60 | Line 60
60   {                                                                        }
61   {************************************************************************}
62   unit FBSQLData;
63 + {$IFDEF MSWINDOWS}
64 + {$DEFINE WINDOWS}
65 + {$ENDIF}
66  
67   {$IFDEF FPC}
68 < {$mode objfpc}{$H+}
68 > {$mode delphi}
69   {$codepage UTF8}
70   {$interfaces COM}
71   {$ENDIF}
# Line 73 | 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  
76 { $define ALLOWDIALECT3PARAMNAMES}
77
78 {$ifndef ALLOWDIALECT3PARAMNAMES}
79
80 { Note on SQL Dialects and SQL Parameter Names
81  --------------------------------------------
82
83  Even when dialect 3 quoted format parameter names are not supported, IBX still processes
84  parameter names case insensitive. This does result in some additional overhead
85  due to a call to "AnsiUpperCase". This can be avoided by undefining
86  "UseCaseInSensitiveParamName" below.
87
88  Note: do not define "UseCaseSensitiveParamName" when "ALLOWDIALECT3PARAMNAMES"
89  is defined. This will not give a useful result.
90 }
91 {$define UseCaseInSensitiveParamName}
92 {$endif}
79  
80   interface
81  
82   uses
83 <  Classes, SysUtils, IBExternals, IBHeader, IB,  FBActivityMonitor;
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 <     function AdjustScale(Value: Int64; aScale: Integer): Double;
124 <     function AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
125 <     function AdjustScaleToCurrency(Value: Int64; aScale: Integer): Currency;
123 >     FFirebirdClientAPI: TFBClientAPI;
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
110     function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
111     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: String); virtual;
141 <     function SQLData: PChar; virtual; abstract;
140 >     procedure InternalSetAsString(Value: AnsiString); virtual;
141 >     function SQLData: PByte; virtual; abstract;
142       function GetDataLength: cardinal; virtual; abstract;
143       function GetCodePage: TSystemCodePage; virtual; abstract;
144 <     function Transliterate(s: string; CodePage: TSystemCodePage): RawByteString;
144 >     function getCharSetID: cardinal; virtual; abstract;
145 >     function Transliterate(s: AnsiString; CodePage: TSystemCodePage): RawByteString;
146       procedure SetScale(aValue: integer); virtual;
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 <     function GetSQLType: cardinal; virtual; abstract;
153 <     function GetSQLTypeName: string; overload;
154 <     class function GetSQLTypeName(SQLType: short): string; overload;
155 <     function GetName: string; virtual; abstract;
156 <     function GetScale: integer; virtual; abstract;
152 >     constructor Create(api: TFBClientAPI);
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: 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; {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;
175       function GetAsPointer: Pointer;
176       function GetAsQuad: TISC_QUAD;
177       function GetAsShort: short;
178 <     function GetAsString: String; virtual;
178 >     function GetAsString: AnsiString; virtual;
179 >     function GetAsNumeric: IFBNumeric;
180       function GetIsNull: Boolean; virtual;
181 <     function getIsNullable: 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);
203       procedure SetAsQuad(Value: TISC_QUAD);
204       procedure SetAsShort(Value: short); virtual;
205 <     procedure SetAsString(Value: String); virtual;
205 >     procedure SetAsString(Value: AnsiString); virtual;
206       procedure SetAsVariant(Value: Variant);
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: string); virtual;
211 >     procedure SetName(aValue: AnsiString); virtual;
212       property AsDate: TDateTime read GetAsDateTime write SetAsDate;
213       property AsBoolean:boolean read GetAsBoolean write SetAsBoolean;
214       property AsTime: TDateTime read GetAsDateTime write SetAsTime;
# Line 174 | Line 222 | type
222       property AsPointer: Pointer read GetAsPointer write SetAsPointer;
223       property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
224       property AsShort: short read GetAsShort write SetAsShort;
225 <     property AsString: String read GetAsString write SetAsString;
225 >     property AsString: AnsiString read GetAsString write SetAsString;
226       property AsVariant: Variant read GetAsVariant write SetAsVariant;
227       property Modified: Boolean read getModified;
228       property IsNull: Boolean read GetIsNull write SetIsNull;
# Line 191 | Line 239 | type
239  
240    TSQLDataArea = class
241    private
242 +    FCaseSensitiveParams: boolean;
243      function GetColumn(index: integer): TSQLVarData;
244      function GetCount: integer;
245    protected
246 <    FUniqueRelationName: string;
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 204 | Line 255 | type
255    public
256      procedure Initialize; virtual;
257      function IsInputDataArea: boolean; virtual; abstract; {Input to Database}
258 <    procedure PreprocessSQL(sSQL: string; GenerateParamNames: boolean;
259 <      var sProcessedSQL: string);
258 >    procedure PreprocessSQL(sSQL: AnsiString; GenerateParamNames: boolean;
259 >      var sProcessedSQL: AnsiString);
260      function ColumnsInUseCount: integer; virtual;
261 <    function ColumnByName(Idx: string): TSQLVarData;
261 >    function ColumnByName(Idx: AnsiString): TSQLVarData;
262      function CheckStatementStatus(Request: TStatementStatus): boolean; virtual; abstract;
263      procedure GetData(index: integer; var IsNull: boolean; var len: short;
264 <      var data: PChar); virtual;
264 >      var data: PByte); virtual;
265      procedure RowChange;
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;
272 <    property UniqueRelationName: string read FUniqueRelationName;
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 226 | Line 282 | type
282    TSQLVarData = class
283    private
284      FParent: TSQLDataArea;
285 <    FName: string;
285 >    FName: AnsiString;
286      FIndex: integer;
287      FModified: boolean;
288      FUniqueName: boolean;
289      FVarString: RawByteString;
290 +    FColMetaData: IParamMetaData;
291      function GetStatement: IStatement;
292 <    procedure SetName(AValue: string);
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: string;  virtual; abstract;
300 <    function GetFieldName: string; virtual; abstract;
301 <    function GetOwnerName: string;  virtual; abstract;
302 <    function GetRelationName: string;  virtual; abstract;
299 >    function GetAliasName: AnsiString;  virtual; abstract;
300 >    function GetFieldName: AnsiString; virtual; abstract;
301 >    function GetOwnerName: AnsiString;  virtual; abstract;
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: PChar;  virtual; abstract;
310 <    function GetDataLength: cardinal; virtual; abstract;
309 >    function GetSQLData: PByte;  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: PChar; 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;
318 >    procedure SetSQLData(AValue: PByte; len: 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 <    procedure SetString(aValue: string);
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: string read GetAliasName;
342 <    property FieldName: string read GetFieldName;
343 <    property OwnerName: string read GetOwnerName;
344 <    property RelationName: string read GetRelationName;
341 >    property AliasName: AnsiString read GetAliasName;
342 >    property FieldName: AnsiString read GetFieldName;
343 >    property OwnerName: AnsiString read GetOwnerName;
344 >    property RelationName: AnsiString read GetRelationName;
345      property Parent: TSQLDataArea read FParent;
346      property Index: integer read FIndex;
347 <    property Name: string read FName write SetName;
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: PChar read GetSQLData;
352 >    property SQLData: PByte read GetSQLData;
353      property DataLength: cardinal read GetDataLength write SetDataLength;
354      property IsNull: Boolean read GetIsNull write SetIsNull;
355      property IsNullable: Boolean read GetIsNullable write SetIsNullable;
# Line 290 | 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;
298    FStatement: IStatement;
370      FChangeSeqNo: integer;
371    protected
372      procedure CheckActive; override;
373 <    function SQLData: PChar; override;
373 >    function SQLData: PByte; override;
374      function GetDataLength: cardinal; override;
375      function GetCodePage: TSystemCodePage; override;
376  
# Line 307 | 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}
385      function GetIndex: integer;
386      function GetSQLType: cardinal; override;
387      function getSubtype: integer;
388 <    function getRelationName: string;
389 <    function getOwnerName: string;
390 <    function getSQLName: string;    {Name of the column}
391 <    function getAliasName: string;  {Alias Name of column or Column Name if not alias}
392 <    function GetName: string; override;      {Disambiguated uppercase Field Name}
388 >    function getRelationName: AnsiString;
389 >    function getOwnerName: AnsiString;
390 >    function getSQLName: AnsiString;    {Name of the column}
391 >    function getAliasName: AnsiString;  {Alias Name of column or Column Name if not alias}
392 >    function GetName: AnsiString; override;      {Disambiguated uppercase Field Name}
393      function GetScale: integer; override;
394 <    function getCharSetID: cardinal;
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 <    property Name: string read GetName;
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 342 | Line 419 | type
419      function GetAsArray: IArray;
420      function GetAsBlob: IBlob; overload;
421      function GetAsBlob(BPB: IBPB): IBlob; overload;
422 <    function GetAsString: String; override;
422 >    function GetAsString: AnsiString; override;
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;
457 <    procedure InternalSetAsString(Value: String); override;
457 >    procedure InternalSetAsString(Value: AnsiString); override;
458      procedure SetScale(aValue: integer); override;
459      procedure SetDataLength(len: cardinal); override;
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 <    procedure SetName(Value: string); override;
467 >    function GetAsString: AnsiString; override;
468 >    procedure SetName(Value: AnsiString); override;
469      procedure SetIsNull(Value: Boolean);  override;
470      procedure SetIsNullable(Value: Boolean); override;
471      procedure SetAsArray(anArray: IArray);
# Line 371 | 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);
490      procedure SetAsShort(AValue: Short);
491 <    procedure SetAsString(AValue: String); override;
491 >    procedure SetAsString(AValue: AnsiString); override;
492      procedure SetAsVariant(AValue: Variant);
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 400 | Line 513 | type
513      destructor Destroy; override;
514    public
515      {IMetaData}
516 <    function GetUniqueRelationName: string;
516 >    function GetUniqueRelationName: AnsiString;
517      function getCount: integer;
518      function getColumnMetaData(index: integer): IColumnMetaData;
519 <    function ByName(Idx: String): IColumnMetaData;
519 >    function ByName(Idx: AnsiString): IColumnMetaData;
520    end;
521  
522    { TSQLParams }
# Line 422 | Line 535 | type
535      {ISQLParams}
536      function getCount: integer;
537      function getSQLParam(index: integer): ISQLParam;
538 <    function ByName(Idx: String): ISQLParam ;
538 >    function ByName(Idx: AnsiString): ISQLParam ;
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 442 | Line 560 | type
560       constructor Create(aResults: TSQLDataArea);
561        {IResults}
562       function getCount: integer;
563 <     function ByName(Idx: String): ISQLData;
563 >     function ByName(Idx: AnsiString): ISQLData;
564       function getSQLData(index: integer): ISQLData;
565 <     procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PChar);
566 <     function GetTransaction: ITransaction; virtual;
565 >     procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PByte);
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, FBClientAPI, variants, IBUtils, FBTransaction;
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 467 | 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;
657    bUnique: Boolean;
658 <  RelationName: string;
658 >  RelationName: AnsiString;
659   begin
660    bUnique := True;
661    for i := 0 to ColumnsInUseCount - 1 do
# Line 502 | Line 686 | begin
686      Column[i].Initialize;
687   end;
688  
689 < procedure TSQLDataArea.PreprocessSQL(sSQL: string; GenerateParamNames: boolean;
690 <  var sProcessedSQL: string);
507 < var
508 <  cCurChar, cNextChar, cQuoteChar: Char;
509 <  sParamName: String;
510 <  j, i, iLenSQL, iSQLPos: Integer;
511 <  iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
512 <  iParamSuffix: Integer;
513 <  slNames: TStrings;
514 <  StrBuffer: PChar;
515 <  found: boolean;
516 <
517 < const
518 <  DefaultState = 0;
519 <  CommentState = 1;
520 <  QuoteState = 2;
521 <  ParamState = 3;
522 < {$ifdef ALLOWDIALECT3PARAMNAMES}
523 <  ParamDefaultState = 0;
524 <  ParamQuoteState = 1;
525 <  {$endif}
689 > procedure TSQLDataArea.PreprocessSQL(sSQL: AnsiString; GenerateParamNames: boolean;
690 >  var sProcessedSQL: AnsiString);
691  
692 <  procedure AddToProcessedSQL(cChar: Char);
528 <  begin
529 <    StrBuffer[iSQLPos] := cChar;
530 <    Inc(iSQLPos);
531 <  end;
532 <
533 < begin
534 <  if not IsInputDataArea then
535 <    IBError(ibxeNotPermitted,[nil]);
692 > var slNames: TStrings;
693  
694 <  sParamName := '';
695 <  iLenSQL := Length(sSQL);
696 <  GetMem(StrBuffer,iLenSQL + 1);
697 <  slNames := TStringList.Create;
698 <  try
542 <    { Do some initializations of variables }
543 <    iParamSuffix := 0;
544 <    cQuoteChar := '''';
545 <    i := 1;
546 <    iSQLPos := 0;
547 <    iCurState := DefaultState;
548 <    {$ifdef ALLOWDIALECT3PARAMNAMES}
549 <    iCurParamState := ParamDefaultState;
550 <    {$endif}
551 <    { Now, traverse through the SQL string, character by character,
552 <     picking out the parameters and formatting correctly for InterBase }
553 <    while (i <= iLenSQL) do begin
554 <      { Get the current token and a look-ahead }
555 <      cCurChar := sSQL[i];
556 <      if i = iLenSQL then
557 <        cNextChar := #0
558 <      else
559 <        cNextChar := sSQL[i + 1];
560 <      { Now act based on the current state }
561 <      case iCurState of
562 <        DefaultState: begin
563 <          case cCurChar of
564 <            '''', '"': begin
565 <              cQuoteChar := cCurChar;
566 <              iCurState := QuoteState;
567 <            end;
568 <            '?', ':': begin
569 <              iCurState := ParamState;
570 <              AddToProcessedSQL('?');
571 <            end;
572 <            '/': if (cNextChar = '*') then begin
573 <              AddToProcessedSQL(cCurChar);
574 <              Inc(i);
575 <              iCurState := CommentState;
576 <            end;
577 <          end;
578 <        end;
579 <        CommentState: begin
580 <          if (cNextChar = #0) then
581 <            IBError(ibxeSQLParseError, [SEOFInComment])
582 <          else if (cCurChar = '*') then begin
583 <            if (cNextChar = '/') then
584 <              iCurState := DefaultState;
585 <          end;
586 <        end;
587 <        QuoteState: begin
588 <          if cNextChar = #0 then
589 <            IBError(ibxeSQLParseError, [SEOFInString])
590 <          else if (cCurChar = cQuoteChar) then begin
591 <            if (cNextChar = cQuoteChar) then begin
592 <              AddToProcessedSQL(cCurChar);
593 <              Inc(i);
594 <            end else
595 <              iCurState := DefaultState;
596 <          end;
597 <        end;
598 <        ParamState:
599 <        begin
600 <          { collect the name of the parameter }
601 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
602 <          if iCurParamState = ParamDefaultState then
603 <          begin
604 <            if cCurChar = '"' then
605 <              iCurParamState := ParamQuoteState
606 <            else
607 <            {$endif}
608 <            if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
609 <                sParamName := sParamName + cCurChar
610 <            else if GenerateParamNames then
611 <            begin
612 <              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
613 <              Inc(iParamSuffix);
614 <              iCurState := DefaultState;
615 <              slNames.AddObject(sParamName,self); //Note local convention
616 <                                                  //add pointer to self to mark entry
617 <              sParamName := '';
618 <            end
619 <            else
620 <              IBError(ibxeSQLParseError, [SParamNameExpected]);
621 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
622 <          end
623 <          else begin
624 <            { determine if Quoted parameter name is finished }
625 <            if cCurChar = '"' then
626 <            begin
627 <              Inc(i);
628 <              slNames.Add(sParamName);
629 <              SParamName := '';
630 <              iCurParamState := ParamDefaultState;
631 <              iCurState := DefaultState;
632 <            end
633 <            else
634 <              sParamName := sParamName + cCurChar
635 <          end;
636 <          {$endif}
637 <          { determine if the unquoted parameter name is finished }
638 <          if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
639 <            (iCurState <> DefaultState) then
640 <          begin
641 <            if not (cNextChar in ['A'..'Z', 'a'..'z',
642 <                                  '0'..'9', '_', '$']) then begin
643 <              Inc(i);
644 <              iCurState := DefaultState;
645 <              slNames.Add(sParamName);
646 <              sParamName := '';
647 <            end;
648 <          end;
649 <        end;
650 <      end;
651 <      if (iCurState <> ParamState) and (i <= iLenSQL) then
652 <        AddToProcessedSQL(sSQL[i]);
653 <      Inc(i);
654 <    end;
655 <    AddToProcessedSQL(#0);
656 <    sProcessedSQL := strpas(StrBuffer);
694 >  procedure SetColumnNames(slNames: TStrings);
695 >  var i, j: integer;
696 >      found: boolean;
697 >  begin
698 >    found := false;
699      SetCount(slNames.Count);
700      for i := 0 to slNames.Count - 1 do
701      begin
# Line 674 | Line 716 | begin
716          Column[i].UniqueName := not found;
717        end;
718      end;
719 +  end;
720 +
721 + begin
722 +  if not IsInputDataArea then
723 +    IBError(ibxeNotPermitted,[nil]);
724 +
725 +  slNames := TStringList.Create;
726 +  try
727 +    sProcessedSQL := TSQLParamProcessor.Execute(sSQL,GenerateParamNames,slNames);
728 +    SetColumnNames(slNames);
729    finally
730      slNames.Free;
679    FreeMem(StrBuffer);
731    end;
732   end;
733  
# Line 685 | Line 736 | begin
736    Result := Count;
737   end;
738  
739 < function TSQLDataArea.ColumnByName(Idx: string): TSQLVarData;
739 > function TSQLDataArea.ColumnByName(Idx: AnsiString): TSQLVarData;
740   var
741 <  s: String;
741 >  s: AnsiString;
742    i: Integer;
743   begin
744 <  {$ifdef UseCaseInSensitiveParamName}
745 <   s := AnsiUpperCase(Idx);
746 <  {$else}
744 >  if not IsInputDataArea or not CaseSensitiveParams then
745 >   s := AnsiUpperCase(Idx)
746 >  else
747     s := Idx;
748 <  {$endif}
748 >
749    for i := 0 to Count - 1 do
750      if Column[i].Name = s then
751      begin
# Line 705 | Line 756 | begin
756   end;
757  
758   procedure TSQLDataArea.GetData(index: integer; var IsNull: boolean;
759 <  var len: short; var data: PChar);
759 >  var len: short; var data: PByte);
760   begin
761    //Do Nothing
762   end;
# Line 724 | Line 775 | begin
775    Result := FParent.Statement;
776   end;
777  
778 < procedure TSQLVarData.SetName(AValue: string);
778 > procedure TSQLVarData.SetName(AValue: AnsiString);
779   begin
780 <  if FName = AValue then Exit;
730 <  {$ifdef UseCaseInSensitiveParamName}
731 <  if Parent.IsInputDataArea then
780 >  if not Parent.IsInputDataArea or not Parent.CaseSensitiveParams then
781      FName := AnsiUpperCase(AValue)
782    else
734  {$endif}
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 743 | Line 854 | begin
854    FUniqueName := true;
855   end;
856  
857 < procedure TSQLVarData.SetString(aValue: string);
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
865     value, a reference is kept in FVarString and a pointer to it placed in the
866 <   SQLVar. This avoids string copies. Note that PChar is guaranteed to point to
866 >   SQLVar. This avoids string copies. Note that PAnsiChar is guaranteed to point to
867     a zero byte when the string is empty, neatly avoiding a nil pointer error.}
868  
869    FVarString := aValue;
870 <  SQLType := SQL_TEXT;
871 <  SetSQLData(PChar(FVarString),Length(aValue));
870 >  if SQLType = SQL_BLOB then
871 >    SetMetaSize(GetAttachment.GetInlineBlobLimit);
872 >  if CanChangeMetaData then
873 >    SQLType := GetDefaultTextSQLType
874 >  else
875 >  if Length(aValue) > DataLength then
876 >    IBError(ibxeStringOverflow,[Length(aValue),DataLength]);
877 >  Scale := 0;
878 >  if  (SQLType <> SQL_VARYING) and (SQLType <> SQL_TEXT) then
879 >    IBError(ibxeUnableTosetaTextType,[Index,Name,TSQLDataItem.GetSQLTypeName(SQLType)]);
880 >  SetSQLData(PByte(PAnsiChar(FVarString)),Length(aValue));
881   end;
882  
883   procedure TSQLVarData.Changed;
# Line 762 | Line 887 | end;
887  
888   procedure TSQLVarData.RowChange;
889   begin
890 +  FArrayIntf := nil;
891    FModified := false;
892    FVarString := '';
893   end;
894  
895 + function TSQLVarData.getColMetadata: IParamMetaData;
896 + begin
897 +  Result := FColMetaData;
898 + end;
899 +
900   procedure TSQLVarData.Initialize;
901  
902 <  function FindVarByName(idx: string; limit: integer): TSQLVarData;
902 >  function FindVarByName(idx: AnsiString; limit: integer): TSQLVarData;
903    var
904      k: integer;
905    begin
# Line 783 | Line 914 | procedure TSQLVarData.Initialize;
914  
915   var
916    j, j_len: Integer;
917 <  st: String;
918 <  sBaseName: string;
917 >  st: AnsiString;
918 >  sBaseName: AnsiString;
919   begin
920    RowChange;
921  
# Line 825 | Line 956 | end;
956  
957   {TSQLDataItem}
958  
959 < function TSQLDataItem.AdjustScale(Value: Int64; aScale: Integer): Double;
829 < var
830 <  Scaling : Int64;
831 <  i: Integer;
832 <  Val: Double;
959 > function TSQLDataItem.GetDateFormatStr(IncludeTime: boolean): AnsiString;
960   begin
961 <  Scaling := 1; Val := Value;
962 <  if aScale > 0 then
963 <  begin
964 <    for i := 1 to aScale do
965 <      Scaling := Scaling * 10;
966 <    result := Val * Scaling;
967 <  end
968 <  else
969 <    if aScale < 0 then
970 <    begin
971 <      for i := -1 downto aScale do
972 <        Scaling := Scaling * 10;
973 <      result := Val / Scaling;
974 <    end
975 <    else
976 <      result := Val;
961 >  {$IF declared(DefaultFormatSettings)}
962 >  with DefaultFormatSettings do
963 >  {$ELSE}
964 >  {$IF declared(FormatSettings)}
965 >  with FormatSettings do
966 >  {$IFEND}
967 >  {$IFEND}
968 >  case GetSQLDialect of
969 >    1:
970 >      if IncludeTime then
971 >        result := ShortDateFormat + ' ' + LongTimeFormat
972 >      else
973 >        result := ShortDateFormat;
974 >    3:
975 >      result := ShortDateFormat;
976 >  end;
977   end;
978  
979 < function TSQLDataItem.AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
980 < var
981 <  Scaling : Int64;
982 <  i: Integer;
983 <  Val: Int64;
979 > function TSQLDataItem.GetTimeFormatStr: AnsiString;
980 > begin
981 >  {$IF declared(DefaultFormatSettings)}
982 >  with DefaultFormatSettings do
983 >  {$ELSE}
984 >  {$IF declared(FormatSettings)}
985 >  with FormatSettings do
986 >  {$IFEND}
987 >  {$IFEND}
988 >    Result := 'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';;
989 > end;
990 >
991 > function TSQLDataItem.GetTimestampFormatStr: AnsiString;
992 > begin
993 >  {$IF declared(DefaultFormatSettings)}
994 >  with DefaultFormatSettings do
995 >  {$ELSE}
996 >  {$IF declared(FormatSettings)}
997 >  with FormatSettings do
998 >  {$IFEND}
999 >  {$IFEND}
1000 >    Result := ShortDateFormat + ' ' +  'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';
1001 > end;
1002 >
1003 > procedure TSQLDataItem.SetAsInteger(AValue: Integer);
1004   begin
1005 <  Scaling := 1; Val := Value;
859 <  if aScale > 0 then begin
860 <    for i := 1 to aScale do Scaling := Scaling * 10;
861 <    result := Val * Scaling;
862 <  end else if aScale < 0 then begin
863 <    for i := -1 downto aScale do Scaling := Scaling * 10;
864 <    result := Val div Scaling;
865 <  end else
866 <    result := Val;
1005 >  SetAsLong(aValue);
1006   end;
1007  
1008 < function TSQLDataItem.AdjustScaleToCurrency(Value: Int64; aScale: Integer
1009 <  ): Currency;
1010 < var
872 <  Scaling : Int64;
873 <  i : Integer;
874 <  FractionText, PadText, CurrText: string;
1008 > procedure TSQLDataItem.InternalGetAsDateTime(var aDateTime: TDateTime;
1009 >  var dstOffset: smallint; var aTimezone: AnsiString;
1010 >  var aTimeZoneID: TFBTimeZoneID);
1011   begin
1012 <  Result := 0;
1013 <  Scaling := 1;
1014 <  PadText := '';
1015 <  if aScale > 0 then
1016 <  begin
1017 <    for i := 1 to aScale do
1018 <      Scaling := Scaling * 10;
1019 <    result := Value * Scaling;
1020 <  end
1021 <  else
886 <    if aScale < 0 then
887 <    begin
888 <      for i := -1 downto aScale do
889 <        Scaling := Scaling * 10;
890 <      FractionText := IntToStr(abs(Value mod Scaling));
891 <      for i := Length(FractionText) to -aScale -1 do
892 <        PadText := '0' + PadText;
893 <      if Value < 0 then
894 <        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DefaultFormatSettings.DecimalSeparator + PadText + FractionText
895 <      else
896 <        CurrText := IntToStr(Abs(Value div Scaling)) + DefaultFormatSettings.DecimalSeparator + PadText + FractionText;
897 <      try
898 <        result := StrToCurr(CurrText);
899 <      except
900 <        on E: Exception do
1012 >  CheckActive;
1013 >  aDateTime := 0;
1014 >  dstOffset := 0;
1015 >  aTimezone := '';
1016 >  aTimeZoneID := TimeZoneID_GMT;
1017 >  if not IsNull then
1018 >    with FFirebirdClientAPI do
1019 >    case SQLType of
1020 >      SQL_TEXT, SQL_VARYING:
1021 >        if not ParseDateTimeTZString(AsString,aDateTime,aTimeZone) then
1022            IBError(ibxeInvalidDataConversion, [nil]);
1023 +      SQL_TYPE_DATE:
1024 +        aDateTime := SQLDecodeDate(SQLData);
1025 +      SQL_TYPE_TIME:
1026 +        aDateTime := SQLDecodeTime(SQLData);
1027 +      SQL_TIMESTAMP:
1028 +        aDateTime := SQLDecodeDateTime(SQLData);
1029 +      SQL_TIMESTAMP_TZ:
1030 +        begin
1031 +          GetTimeZoneServices.DecodeTimestampTZ(SQLData,aDateTime,dstOffset,aTimeZone);
1032 +          aTimeZoneID := PISC_TIMESTAMP_TZ(SQLData)^.time_zone;
1033 +        end;
1034 +      SQL_TIMESTAMP_TZ_EX:
1035 +      begin
1036 +        GetTimeZoneServices.DecodeTimestampTZEx(SQLData,aDateTime,dstOffset,aTimeZone);
1037 +        aTimeZoneID := PISC_TIMESTAMP_TZ_EX(SQLData)^.time_zone;
1038        end;
1039 <    end
1040 <    else
1041 <      result := Value;
1039 >      SQL_TIME_TZ:
1040 >        with GetTimeZoneServices do
1041 >        begin
1042 >          DecodeTimeTZ(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1043 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1044 >        end;
1045 >      SQL_TIME_TZ_EX:
1046 >        with GetTimeZoneServices do
1047 >        begin
1048 >          DecodeTimeTZEx(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1049 >          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1050 >        end;
1051 >      else
1052 >        IBError(ibxeInvalidDataConversion, [nil]);
1053 >    end;
1054   end;
1055  
1056 < procedure TSQLDataItem.SetAsInteger(AValue: Integer);
1056 > procedure TSQLDataItem.CheckActive;
1057   begin
1058 <  SetAsLong(aValue);
1058 >  //Do nothing by default
1059   end;
1060  
1061 < function TSQLDataItem.AdjustScaleFromCurrency(Value: Currency; aScale: Integer
914 <  ): Int64;
915 < var
916 <  Scaling : Int64;
917 <  i : Integer;
1061 > procedure TSQLDataItem.CheckTZSupport;
1062   begin
1063 <  Result := 0;
1064 <  Scaling := 1;
921 <  if aScale < 0 then
922 <  begin
923 <    for i := -1 downto aScale do
924 <      Scaling := Scaling * 10;
925 <    result := trunc(Value * Scaling);
926 <  end
927 <  else
928 <  if aScale > 0 then
929 <  begin
930 <    for i := 1 to aScale do
931 <       Scaling := Scaling * 10;
932 <    result := trunc(Value / Scaling);
933 <  end
934 <  else
935 <    result := trunc(Value);
1063 >  if not FFirebirdClientAPI.HasTimeZoneSupport then
1064 >    IBError(ibxeNoTimezoneSupport,[]);
1065   end;
1066  
1067 < function TSQLDataItem.AdjustScaleFromDouble(Value: Double; aScale: Integer
939 <  ): Int64;
940 < var
941 <  Scaling : Int64;
942 <  i : Integer;
1067 > function TSQLDataItem.GetTimeZoneServices: IExTimeZoneServices;
1068   begin
1069 <  Result := 0;
945 <  Scaling := 1;
946 <  if aScale < 0 then
947 <  begin
948 <    for i := -1 downto aScale do
949 <      Scaling := Scaling * 10;
950 <    result := trunc(Value * Scaling);
951 <  end
952 <  else
953 <  if aScale > 0 then
1069 >  if FTimeZoneServices = nil then
1070    begin
1071 <    for i := 1 to aScale do
1072 <       Scaling := Scaling * 10;
1073 <    result := trunc(Value / Scaling);
1074 <  end
1075 <  else
960 <    result := trunc(Value);
961 < end;
962 <
963 < procedure TSQLDataItem.CheckActive;
964 < begin
965 <  //Do nothing by default
1071 >    if not GetAttachment.HasTimeZoneSupport then
1072 >      IBError(ibxeNoTimezoneSupport,[]);
1073 >    GetAttachment.GetTimeZoneServices.QueryInterface(IExTimeZoneServices,FTimeZoneServices);
1074 >  end;
1075 >  Result := FTimeZoneServices;
1076   end;
1077  
1078   procedure TSQLDataItem.Changed;
# Line 975 | Line 1085 | begin
1085    //Do nothing by default
1086   end;
1087  
1088 < procedure TSQLDataItem.InternalSetAsString(Value: String);
1088 > procedure TSQLDataItem.InternalSetAsString(Value: AnsiString);
1089   begin
1090    //Do nothing by default
1091   end;
1092  
1093 < function TSQLDataItem.Transliterate(s: string; CodePage: TSystemCodePage
1093 > function TSQLDataItem.Transliterate(s: AnsiString; CodePage: TSystemCodePage
1094    ): RawByteString;
1095   begin
1096    Result := s;
# Line 1003 | Line 1113 | begin
1113     //Do nothing by default
1114   end;
1115  
1116 < function TSQLDataItem.GetSQLTypeName: string;
1116 > constructor TSQLDataItem.Create(api: TFBClientAPI);
1117 > begin
1118 >  inherited Create;
1119 >  FFirebirdClientAPI := api;
1120 > end;
1121 >
1122 > function TSQLDataItem.CanChangeMetaData: boolean;
1123 > begin
1124 >  Result := false;
1125 > end;
1126 >
1127 > function TSQLDataItem.GetSQLTypeName: AnsiString;
1128   begin
1129    Result := GetSQLTypeName(GetSQLType);
1130   end;
1131  
1132 < class function TSQLDataItem.GetSQLTypeName(SQLType: short): string;
1132 > class function TSQLDataItem.GetSQLTypeName(SQLType: cardinal): AnsiString;
1133   begin
1134    Result := 'Unknown';
1135    case SQLType of
# Line 1019 | Line 1140 | begin
1140    SQL_LONG:             Result := 'SQL_LONG';
1141    SQL_SHORT:            Result := 'SQL_SHORT';
1142    SQL_TIMESTAMP:        Result := 'SQL_TIMESTAMP';
1143 +  SQL_TIMESTAMP_TZ:     Result := 'SQL_TIMESTAMP_TZ';
1144 +  SQL_TIMESTAMP_TZ_EX:  Result := 'SQL_TIMESTAMP_TZ_EX';
1145    SQL_BLOB:             Result := 'SQL_BLOB';
1146    SQL_D_FLOAT:          Result := 'SQL_D_FLOAT';
1147    SQL_ARRAY:            Result := 'SQL_ARRAY';
# Line 1026 | Line 1149 | begin
1149    SQL_TYPE_TIME:        Result := 'SQL_TYPE_TIME';
1150    SQL_TYPE_DATE:        Result := 'SQL_TYPE_DATE';
1151    SQL_INT64:            Result := 'SQL_INT64';
1152 +  SQL_TIME_TZ:          Result := 'SQL_TIME_TZ';
1153 +  SQL_TIME_TZ_EX:       Result := 'SQL_TIME_TZ_EX';
1154 +  SQL_DEC_FIXED:        Result := 'SQL_DEC_FIXED';
1155 +  SQL_DEC16:            Result := 'SQL_DEC16';
1156 +  SQL_DEC34:            Result := 'SQL_DEC34';
1157 +  SQL_INT128:           Result := 'SQL_INT128';
1158 +  SQL_NULL:             Result := 'SQL_NULL';
1159 +  SQL_BOOLEAN:          Result := 'SQL_BOOLEAN';
1160    end;
1161   end;
1162  
1163 + function TSQLDataItem.GetStrDataLength: short;
1164 + begin
1165 +  with FFirebirdClientAPI do
1166 +  if SQLType = SQL_VARYING then
1167 +    Result := DecodeInteger(SQLData, 2)
1168 +  else
1169 +    Result := DataLength;
1170 + end;
1171 +
1172   function TSQLDataItem.GetAsBoolean: boolean;
1173   begin
1174    CheckActive;
# Line 1059 | Line 1199 | begin
1199            end;
1200          end;
1201          SQL_SHORT:
1202 <          result := AdjustScaleToCurrency(Int64(PShort(SQLData)^),
1203 <                                      Scale);
1202 >          result := NumericFromRawValues(Int64(PShort(SQLData)^),
1203 >                                      Scale).getAsCurrency;
1204          SQL_LONG:
1205 <          result := AdjustScaleToCurrency(Int64(PLong(SQLData)^),
1206 <                                      Scale);
1205 >          result := NumericFromRawValues(Int64(PLong(SQLData)^),
1206 >                                      Scale).getAsCurrency;
1207          SQL_INT64:
1208 <          result := AdjustScaleToCurrency(PInt64(SQLData)^,
1209 <                                      Scale);
1208 >          result := NumericFromRawValues(PInt64(SQLData)^,
1209 >                                      Scale).getAsCurrency;
1210          SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1211 <          result := Trunc(AsDouble);
1211 >          result := Round(AsDouble);
1212 >
1213 >        SQL_DEC_FIXED,
1214 >        SQL_DEC16,
1215 >        SQL_DEC34,
1216 >        SQL_INT128:
1217 >          if not BCDToCurr(GetAsBCD,Result) then
1218 >            IBError(ibxeInvalidDataConversion, [nil]);
1219 >
1220          else
1221            IBError(ibxeInvalidDataConversion, [nil]);
1222        end;
# Line 1089 | Line 1237 | begin
1237          end;
1238        end;
1239        SQL_SHORT:
1240 <        result := AdjustScaleToInt64(Int64(PShort(SQLData)^),
1241 <                                    Scale);
1240 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1241 >                                    Scale).getAsInt64;
1242        SQL_LONG:
1243 <        result := AdjustScaleToInt64(Int64(PLong(SQLData)^),
1244 <                                    Scale);
1243 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1244 >                                    Scale).getAsInt64;
1245        SQL_INT64:
1246 <        result := AdjustScaleToInt64(PInt64(SQLData)^,
1247 <                                    Scale);
1246 >        result := NumericFromRawValues(PInt64(SQLData)^,
1247 >                                    Scale).getAsInt64;
1248        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1249 <        result := Trunc(AsDouble);
1249 >        result := Round(AsDouble);
1250        else
1251          IBError(ibxeInvalidDataConversion, [nil]);
1252      end;
1253   end;
1254  
1255   function TSQLDataItem.GetAsDateTime: TDateTime;
1256 + var aTimezone: AnsiString;
1257 +    aTimeZoneID: TFBTimeZoneID;
1258 +    dstOffset: smallint;
1259 + begin
1260 +  InternalGetAsDateTime(Result,dstOffset,aTimeZone,aTimeZoneID);
1261 + end;
1262 +
1263 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime;
1264 +  var dstOffset: smallint; var aTimezone: AnsiString);
1265 + var aTimeZoneID: TFBTimeZoneID;
1266 + begin
1267 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1268 + end;
1269 +
1270 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
1271 +  var aTimezoneID: TFBTimeZoneID);
1272 + var aTimezone: AnsiString;
1273 + begin
1274 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1275 + end;
1276 +
1277 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1278 +  var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime);
1279 + var aTimeZone: AnsiString;
1280   begin
1281    CheckActive;
1282 <  result := 0;
1282 >  aTime := 0;
1283 >  dstOffset := 0;
1284    if not IsNull then
1285 <    with FirebirdClientAPI do
1285 >    with FFirebirdClientAPI do
1286      case SQLType of
1287 <      SQL_TEXT, SQL_VARYING: begin
1288 <        try
1289 <          result := StrToDate(AsString);
1290 <        except
1118 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
1287 >      SQL_TIME_TZ:
1288 >        begin
1289 >          GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1290 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1291          end;
1292 +      SQL_TIME_TZ_EX:
1293 +        begin
1294 +          GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1295 +          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1296 +        end;
1297 +    else
1298 +      IBError(ibxeInvalidDataConversion, [nil]);
1299 +    end;
1300 + end;
1301 +
1302 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1303 +  var aTimezone: AnsiString; OnDate: TDateTime);
1304 + begin
1305 +  CheckActive;
1306 +  aTime := 0;
1307 +  dstOffset := 0;
1308 +  if not IsNull then
1309 +    with FFirebirdClientAPI do
1310 +    case SQLType of
1311 +      SQL_TIME_TZ:
1312 +        GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1313 +      SQL_TIME_TZ_EX:
1314 +        GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1315 +    else
1316 +      IBError(ibxeInvalidDataConversion, [nil]);
1317 +    end;
1318 + end;
1319 +
1320 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1321 +  var aTimezoneID: TFBTimeZoneID);
1322 + begin
1323 +  GetAsTime(aTime,dstOffset,aTimeZoneID,GetTimeZoneServices.GetTimeTZDate);
1324 + end;
1325 +
1326 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1327 +  var aTimezone: AnsiString);
1328 + begin
1329 +  GetAsTime(aTime,dstOffset,aTimeZone,GetTimeZoneServices.GetTimeTZDate);
1330 + end;
1331 +
1332 + function TSQLDataItem.GetAsUTCDateTime: TDateTime;
1333 + var aTimezone: AnsiString;
1334 + begin
1335 +  CheckActive;
1336 +  result := 0;
1337 +  aTimezone := '';
1338 +  if not IsNull then
1339 +    with FFirebirdClientAPI do
1340 +    case SQLType of
1341 +      SQL_TEXT, SQL_VARYING:
1342 +      begin
1343 +        if not ParseDateTimeTZString(AsString,Result,aTimeZone) then
1344 +          IBError(ibxeInvalidDataConversion, [nil]);
1345 +        Result := GetTimeZoneServices.LocalTimeToGMT(Result,aTimeZone);
1346        end;
1347        SQL_TYPE_DATE:
1348          result := SQLDecodeDate(SQLData);
1349 <      SQL_TYPE_TIME:
1349 >      SQL_TYPE_TIME,
1350 >      SQL_TIME_TZ,
1351 >      SQL_TIME_TZ_EX:
1352          result := SQLDecodeTime(SQLData);
1353 <      SQL_TIMESTAMP:
1353 >      SQL_TIMESTAMP,
1354 >      SQL_TIMESTAMP_TZ,
1355 >      SQL_TIMESTAMP_TZ_EX:
1356          result := SQLDecodeDateTime(SQLData);
1357        else
1358          IBError(ibxeInvalidDataConversion, [nil]);
1359 <    end;
1359 >      end;
1360   end;
1361  
1362   function TSQLDataItem.GetAsDouble: Double;
# Line 1143 | Line 1373 | begin
1373          end;
1374        end;
1375        SQL_SHORT:
1376 <        result := AdjustScale(Int64(PShort(SQLData)^),
1377 <                              Scale);
1376 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1377 >                              Scale).getAsDouble;
1378        SQL_LONG:
1379 <        result := AdjustScale(Int64(PLong(SQLData)^),
1380 <                              Scale);
1379 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1380 >                              Scale).getAsDouble;
1381        SQL_INT64:
1382 <        result := AdjustScale(PInt64(SQLData)^, Scale);
1382 >        result := NumericFromRawValues(PInt64(SQLData)^, Scale).getAsDouble;
1383        SQL_FLOAT:
1384          result := PFloat(SQLData)^;
1385        SQL_DOUBLE, SQL_D_FLOAT:
1386          result := PDouble(SQLData)^;
1387 +      SQL_DEC_FIXED,
1388 +      SQL_DEC16,
1389 +      SQL_DEC34,
1390 +      SQL_INT128:
1391 +        Result := BCDToDouble(GetAsBCD);
1392        else
1393          IBError(ibxeInvalidDataConversion, [nil]);
1394      end;
# Line 1190 | Line 1425 | begin
1425          end;
1426        end;
1427        SQL_SHORT:
1428 <        result := Trunc(AdjustScale(Int64(PShort(SQLData)^),
1429 <                                    Scale));
1428 >        result := NumericFromRawValues(Int64(PShort(SQLData)^),
1429 >                                    Scale).getAsInteger;
1430        SQL_LONG:
1431 <        result := Trunc(AdjustScale(Int64(PLong(SQLData)^),
1432 <                                    Scale));
1431 >        result := NumericFromRawValues(Int64(PLong(SQLData)^),
1432 >                                    Scale).getAsInteger;
1433        SQL_INT64:
1434 <        result := Trunc(AdjustScale(PInt64(SQLData)^, Scale));
1434 >        result := NumericFromRawValues(PInt64(SQLData)^, Scale).getAsInteger;
1435 >
1436        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1437 <        result := Trunc(AsDouble);
1437 >        result := Round(AsDouble);
1438 >      SQL_DEC_FIXED,
1439 >      SQL_DEC16,
1440 >      SQL_DEC34,
1441 >      SQL_INT128:
1442 >        Result := BCDToInteger(GetAsBCD);
1443        else
1444 <        IBError(ibxeInvalidDataConversion, [nil]);
1444 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1445      end;
1446   end;
1447  
# Line 1238 | Line 1479 | begin
1479    end;
1480   end;
1481  
1482 + {Copied from LazUTF8}
1483 +
1484 + function UTF8CodepointSizeFull(p: PAnsiChar): integer;
1485 + const TopBitSetMask   = $80; {%10000000}
1486 +      Top2BitsSetMask = $C0; {%11000000}
1487 +      Top3BitsSetMask = $E0; {%11100000}
1488 +      Top4BitsSetMask = $F0; {%11110000}
1489 +      Top5BitsSetMask = $F8; {%11111000}
1490 + begin
1491 +  case p^ of
1492 +  #0..#191: // %11000000
1493 +    // regular single byte character (#0 is a character, this is Pascal ;)
1494 +    Result:=1;
1495 +  #192..#223: // p^ and %11100000 = %11000000
1496 +    begin
1497 +      // could be 2 byte character
1498 +      if (ord(p[1]) and Top2BitsSetMask) = TopBitSetMask then
1499 +        Result:=2
1500 +      else
1501 +        Result:=1;
1502 +    end;
1503 +  #224..#239: // p^ and %11110000 = %11100000
1504 +    begin
1505 +      // could be 3 byte character
1506 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1507 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask) then
1508 +        Result:=3
1509 +      else
1510 +        Result:=1;
1511 +    end;
1512 +  #240..#247: // p^ and %11111000 = %11110000
1513 +    begin
1514 +      // could be 4 byte character
1515 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1516 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask)
1517 +      and ((ord(p[3]) and Top2BitsSetMask) = TopBitSetMask) then
1518 +        Result:=4
1519 +      else
1520 +        Result:=1;
1521 +    end;
1522 +  else
1523 +    Result:=1;
1524 +  end;
1525 + end;
1526 +
1527 + {Returns the byte length of a UTF8 string with a fixed charwidth}
1528  
1529 < function TSQLDataItem.GetAsString: String;
1529 > function GetStrLen(p: PAnsiChar; FieldWidth, MaxDataLength: cardinal): integer;
1530 > var i: integer;
1531 >    cplen: integer;
1532 > begin
1533 >  Result := 0;
1534 >  for i := 1 to FieldWidth do
1535 >  begin
1536 >    cplen := UTF8CodepointSizeFull(p);
1537 >    Inc(p,cplen);
1538 >    Inc(Result,cplen);
1539 >    if Result >= MaxDataLength then
1540 >    begin
1541 >      Result := MaxDataLength;
1542 >      Exit;
1543 >    end;
1544 >  end;
1545 > end;
1546 >
1547 > function TSQLDataItem.GetAsString: AnsiString;
1548   var
1549 <  sz: PChar;
1549 >  sz: PByte;
1550    str_len: Integer;
1551    rs: RawByteString;
1552 +  aTimeZone: AnsiString;
1553 +  aDateTime: TDateTime;
1554 +  dstOffset: smallint;
1555   begin
1556    CheckActive;
1557    result := '';
1558    { Check null, if so return a default string }
1559    if not IsNull then
1560 <  with FirebirdClientAPI do
1560 >  with FFirebirdClientAPI do
1561      case SQLType of
1562        SQL_BOOLEAN:
1563          if AsBoolean then
# Line 1261 | Line 1569 | begin
1569        begin
1570          sz := SQLData;
1571          if (SQLType = SQL_TEXT) then
1572 <          str_len := DataLength
1572 >        begin
1573 >          if GetCodePage = cp_utf8 then
1574 >            str_len := GetStrLen(PAnsiChar(sz),GetSize div GetCharSetWidth,DataLength)
1575 >          else
1576 >            str_len := DataLength
1577 >        end
1578          else begin
1579 <          str_len := DecodeInteger(SQLData, 2);
1579 >          str_len := DecodeInteger(sz, 2);
1580            Inc(sz, 2);
1581          end;
1582 <        SetString(rs, sz, str_len);
1582 >        SetString(rs, PAnsiChar(sz), str_len);
1583          SetCodePage(rs,GetCodePage,false);
1584 <        Result := Trim(rs);
1584 >        Result := rs;
1585        end;
1586 +
1587        SQL_TYPE_DATE:
1588 <        case GetSQLDialect of
1275 <          1 : result := DateTimeToStr(AsDateTime);
1276 <          3 : result := DateToStr(AsDateTime);
1277 <        end;
1278 <      SQL_TYPE_TIME :
1279 <        result := TimeToStr(AsDateTime);
1588 >        Result := DateToStr(GetAsDateTime);
1589        SQL_TIMESTAMP:
1590 <        result := FormatDateTime(FormatSettings.ShortDateFormat + ' ' +
1591 <                            FormatSettings.LongTimeFormat+'.zzz',AsDateTime);
1590 >        Result := FBFormatDateTime(GetTimestampFormatStr,GetAsDateTime);
1591 >      SQL_TYPE_TIME:
1592 >        Result := FBFormatDateTime(GetTimeFormatStr,GetAsDateTime);
1593 >      SQL_TIMESTAMP_TZ,
1594 >      SQL_TIMESTAMP_TZ_EX:
1595 >        with GetAttachment.GetTimeZoneServices do
1596 >        begin
1597 >          if GetTZTextOption = tzGMT then
1598 >            Result := FBFormatDateTime(GetTimestampFormatStr,GetAsUTCDateTime)
1599 >          else
1600 >          begin
1601 >            GetAsDateTime(aDateTime,dstOffset,aTimeZone);
1602 >            if GetTZTextOption = tzOffset then
1603 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1604 >            else
1605 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + aTimeZone;
1606 >          end;
1607 >        end;
1608 >      SQL_TIME_TZ,
1609 >      SQL_TIME_TZ_EX:
1610 >        with GetAttachment.GetTimeZoneServices do
1611 >        begin
1612 >          if GetTZTextOption = tzGMT then
1613 >             Result := FBFormatDateTime(GetTimeFormatStr,GetAsUTCDateTime)
1614 >          else
1615 >          begin
1616 >            GetAsTime(aDateTime,dstOffset,aTimeZone,GetTimeTZDate);
1617 >            if GetTZTextOption = tzOffset then
1618 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1619 >            else
1620 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + aTimeZone;
1621 >          end;
1622 >        end;
1623 >
1624        SQL_SHORT, SQL_LONG:
1625          if Scale = 0 then
1626            result := IntToStr(AsLong)
# Line 1296 | Line 1637 | begin
1637            result := FloatToStr(AsDouble);
1638        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1639          result := FloatToStr(AsDouble);
1640 +
1641 +      SQL_DEC16,
1642 +      SQL_DEC34:
1643 +        result := BCDToStr(GetAsBCD);
1644 +
1645 +      SQL_DEC_FIXED,
1646 +      SQL_INT128:
1647 +        result := Int128ToStr(SQLData,scale);
1648 +
1649        else
1650 <        IBError(ibxeInvalidDataConversion, [nil]);
1650 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1651      end;
1652   end;
1653  
1654 + function TSQLDataItem.GetAsNumeric: IFBNumeric;
1655 + var aValue: Int64;
1656 + begin
1657 +  case SQLType of
1658 +   SQL_TEXT, SQL_VARYING:
1659 +     Result := NewNumeric(GetAsString);
1660 +
1661 +   SQL_SHORT:
1662 +     Result := NumericFromRawValues(PShort(SQLData)^, Scale);
1663 +
1664 +   SQL_LONG:
1665 +     Result := NumericFromRawValues(PLong(SQLData)^, Scale);
1666 +
1667 +   SQL_INT64:
1668 +     Result := NumericFromRawValues(PInt64(SQLData)^, Scale);
1669 +
1670 +   SQL_DEC16,
1671 +   SQL_DEC34,
1672 +   SQL_DEC_FIXED,
1673 +   SQL_INT128:
1674 +     Result := NewNumeric(GetAsBCD);
1675 +
1676 +   else
1677 +     IBError(ibxeInvalidDataConversion, [nil]);
1678 +  end;
1679 + end;
1680 +
1681   function TSQLDataItem.GetIsNull: Boolean;
1682   begin
1683    CheckActive;
1684    Result := false;
1685   end;
1686  
1687 < function TSQLDataItem.getIsNullable: boolean;
1687 > function TSQLDataItem.GetIsNullable: boolean;
1688   begin
1689    CheckActive;
1690    Result := false;
1691   end;
1692  
1693   function TSQLDataItem.GetAsVariant: Variant;
1694 + var ts: TDateTime;
1695 +  dstOffset: smallint;
1696 +    timezone: AnsiString;
1697   begin
1698    CheckActive;
1699    if IsNull then
# Line 1327 | Line 1707 | begin
1707          result := AsString;
1708        SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
1709          result := AsDateTime;
1710 +      SQL_TIMESTAMP_TZ,
1711 +      SQL_TIME_TZ,
1712 +      SQL_TIMESTAMP_TZ_EX,
1713 +      SQL_TIME_TZ_EX:
1714 +        begin
1715 +          GetAsDateTime(ts,dstOffset,timezone);
1716 +          result := VarArrayOf([ts,dstOffset,timezone]);
1717 +        end;
1718        SQL_SHORT, SQL_LONG:
1719          if Scale = 0 then
1720            result := AsLong
# Line 1345 | Line 1733 | begin
1733          result := AsDouble;
1734        SQL_BOOLEAN:
1735          result := AsBoolean;
1736 +      SQL_DEC_FIXED,
1737 +      SQL_DEC16,
1738 +      SQL_DEC34,
1739 +      SQL_INT128:
1740 +        result := VarFmtBCDCreate(GetAsBcd);
1741        else
1742          IBError(ibxeInvalidDataConversion, [nil]);
1743      end;
# Line 1355 | Line 1748 | begin
1748    Result := false;
1749   end;
1750  
1751 + function TSQLDataItem.GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats
1752 +  ): integer;
1753 + begin
1754 +  case DateTimeFormat of
1755 +  dfTimestamp:
1756 +    Result := Length(GetTimestampFormatStr);
1757 +  dfDateTime:
1758 +    Result := Length(GetDateFormatStr(true));
1759 +  dfTime:
1760 +    Result := Length(GetTimeFormatStr);
1761 +  dfTimestampTZ:
1762 +    Result := Length(GetTimestampFormatStr) + 6; {assume time offset format}
1763 +  dfTimeTZ:
1764 +    Result := Length(GetTimeFormatStr)+ 6;
1765 +  else
1766 +    Result := 0;
1767 +  end;end;
1768 +
1769 + function TSQLDataItem.GetAsBCD: tBCD;
1770 +
1771 + begin
1772 +  CheckActive;
1773 +  if IsNull then
1774 +   with Result do
1775 +   begin
1776 +     FillChar(Result,sizeof(Result),0);
1777 +     Precision := 1;
1778 +     exit;
1779 +   end;
1780 +
1781 +  case SQLType of
1782 +  SQL_DEC16,
1783 +  SQL_DEC34:
1784 +    with FFirebirdClientAPI do
1785 +      Result := SQLDecFloatDecode(SQLType,  SQLData);
1786 +
1787 +  SQL_DEC_FIXED,
1788 +  SQL_INT128:
1789 +    with FFirebirdClientAPI do
1790 +      Result := StrToBCD(Int128ToStr(SQLData,scale));
1791 +  else
1792 +    if not CurrToBCD(GetAsCurrency,Result) then
1793 +      IBError(ibxeBadBCDConversion,[]);
1794 +  end;
1795 + end;
1796 +
1797  
1798   procedure TSQLDataItem.SetIsNull(Value: Boolean);
1799   begin
# Line 1366 | Line 1805 | begin
1805    //ignore unless overridden
1806   end;
1807  
1808 < procedure TSQLDataItem.SetName(aValue: string);
1808 > procedure TSQLDataItem.SetName(aValue: AnsiString);
1809   begin
1810    //ignore unless overridden
1811   end;
# Line 1377 | Line 1816 | begin
1816    if GetSQLDialect < 3 then
1817      AsDouble := Value
1818    else
1819 +  if not CanChangeMetaData and ((SQLType <> SQL_INT64) or (Scale <> -4)) then
1820 +    SetAsNumeric(NewNumeric(Value))
1821 +  else
1822    begin
1823      Changing;
1824      if IsNullable then
# Line 1392 | Line 1834 | end;
1834   procedure TSQLDataItem.SetAsInt64(Value: Int64);
1835   begin
1836    CheckActive;
1837 <  Changing;
1838 <  if IsNullable then
1839 <    IsNull := False;
1837 >  if not CanChangeMetaData and ((SQLType <> SQL_INT64) or (Scale <> 0)) then
1838 >    SetAsNumeric(NewNumeric(Value))
1839 >  else
1840 >  begin
1841 >    Changing;
1842 >    if IsNullable then
1843 >      IsNull := False;
1844  
1845 <  SQLType := SQL_INT64;
1846 <  Scale := 0;
1847 <  DataLength := SizeOf(Int64);
1848 <  PInt64(SQLData)^ := Value;
1849 <  Changed;
1845 >    SQLType := SQL_INT64;
1846 >    Scale := 0;
1847 >    DataLength := SizeOf(Int64);
1848 >    PInt64(SQLData)^ := Value;
1849 >    Changed;
1850 >  end;
1851   end;
1852  
1853   procedure TSQLDataItem.SetAsDate(Value: TDateTime);
# Line 1418 | Line 1865 | begin
1865  
1866    SQLType := SQL_TYPE_DATE;
1867    DataLength := SizeOf(ISC_DATE);
1868 <  with FirebirdClientAPI do
1868 >  with FFirebirdClientAPI do
1869      SQLEncodeDate(Value,SQLData);
1870    Changed;
1871   end;
# Line 1438 | Line 1885 | begin
1885  
1886    SQLType := SQL_TYPE_TIME;
1887    DataLength := SizeOf(ISC_TIME);
1888 <  with FirebirdClientAPI do
1888 >  with FFirebirdClientAPI do
1889      SQLEncodeTime(Value,SQLData);
1890    Changed;
1891   end;
1892  
1893 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
1894 + begin
1895 +  CheckActive;
1896 +  CheckTZSupport;
1897 +  if GetSQLDialect < 3 then
1898 +  begin
1899 +    AsDateTime := aValue;
1900 +    exit;
1901 +  end;
1902 +
1903 +  Changing;
1904 +  if IsNullable then
1905 +    IsNull := False;
1906 +
1907 +  SQLType := SQL_TIME_TZ;
1908 +  DataLength := SizeOf(ISC_TIME_TZ);
1909 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZoneID,OnDate,SQLData);
1910 +  Changed;
1911 + end;
1912 +
1913 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
1914 + begin
1915 +  CheckActive;
1916 +  CheckTZSupport;
1917 +  if GetSQLDialect < 3 then
1918 +  begin
1919 +    AsDateTime := aValue;
1920 +    exit;
1921 +  end;
1922 +
1923 +  Changing;
1924 +  if IsNullable then
1925 +    IsNull := False;
1926 +
1927 +  SQLType := SQL_TIME_TZ;
1928 +  DataLength := SizeOf(ISC_TIME_TZ);
1929 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZone,OnDate,SQLData);
1930 +  Changed;
1931 + end;
1932 +
1933   procedure TSQLDataItem.SetAsDateTime(Value: TDateTime);
1934   begin
1935    CheckActive;
# Line 1451 | Line 1938 | begin
1938  
1939    Changing;
1940    SQLType := SQL_TIMESTAMP;
1941 <  DataLength := SizeOf(TISC_QUAD);
1942 <  with FirebirdClientAPI do
1941 >  DataLength := SizeOf(ISC_TIME) + sizeof(ISC_DATE);
1942 >  with FFirebirdClientAPI do
1943      SQLEncodeDateTime(Value,SQLData);
1944    Changed;
1945   end;
1946  
1947 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime;
1948 +  aTimeZoneID: TFBTimeZoneID);
1949 + begin
1950 +  CheckActive;
1951 +  CheckTZSupport;
1952 +  if IsNullable then
1953 +    IsNull := False;
1954 +
1955 +  Changing;
1956 +  SQLType := SQL_TIMESTAMP_TZ;
1957 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1958 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZoneID,SQLData);
1959 +  Changed;
1960 + end;
1961 +
1962 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString
1963 +  );
1964 + begin
1965 +  CheckActive;
1966 +  CheckTZSupport;
1967 +  if IsNullable then
1968 +    IsNull := False;
1969 +
1970 +  Changing;
1971 +  SQLType := SQL_TIMESTAMP_TZ;
1972 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1973 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZone,SQLData);
1974 +  Changed;
1975 + end;
1976 +
1977 + procedure TSQLDataItem.SetAsUTCDateTime(aUTCTime: TDateTime);
1978 + begin
1979 +  SetAsDateTime(aUTCTime,TimeZoneID_GMT);
1980 + end;
1981 +
1982   procedure TSQLDataItem.SetAsDouble(Value: Double);
1983   begin
1984    CheckActive;
# Line 1488 | Line 2010 | end;
2010   procedure TSQLDataItem.SetAsLong(Value: Long);
2011   begin
2012    CheckActive;
2013 <  if IsNullable then
2014 <    IsNull := False;
2013 >  if not CanChangeMetaData and ((SQLType <> SQL_LONG) or (Scale <> 0)) then
2014 >    SetAsNumeric(NewNumeric(Value))
2015 >  else
2016 >  begin
2017 >    if IsNullable then
2018 >      IsNull := False;
2019  
2020 <  Changing;
2021 <  SQLType := SQL_LONG;
2022 <  DataLength := SizeOf(Long);
2023 <  Scale := 0;
2024 <  PLong(SQLData)^ := Value;
2025 <  Changed;
2020 >    Changing;
2021 >    SQLType := SQL_LONG;
2022 >    DataLength := SizeOf(Long);
2023 >    Scale := 0;
2024 >    PLong(SQLData)^ := Value;
2025 >    Changed;
2026 >  end;
2027   end;
2028  
2029   procedure TSQLDataItem.SetAsPointer(Value: Pointer);
# Line 1531 | Line 2058 | end;
2058   procedure TSQLDataItem.SetAsShort(Value: short);
2059   begin
2060    CheckActive;
2061 <  Changing;
2062 <  if IsNullable then
2063 <    IsNull := False;
2061 >  if not CanChangeMetaData and ((SQLType <> SQL_SHORT) or (Scale <> 0)) then
2062 >    SetAsNumeric(NewNumeric(Value))
2063 >  else
2064 >  begin
2065 >    Changing;
2066 >    if IsNullable then
2067 >      IsNull := False;
2068  
2069 <  SQLType := SQL_SHORT;
2070 <  DataLength := SizeOf(Short);
2071 <  Scale := 0;
2072 <  PShort(SQLData)^ := Value;
2073 <  Changed;
2069 >    SQLType := SQL_SHORT;
2070 >    DataLength := SizeOf(Short);
2071 >    Scale := 0;
2072 >    PShort(SQLData)^ := Value;
2073 >    Changed;
2074 >  end;
2075   end;
2076  
2077 < procedure TSQLDataItem.SetAsString(Value: String);
2077 > procedure TSQLDataItem.SetAsString(Value: AnsiString);
2078   begin
2079    InternalSetAsString(Value);
2080   end;
# Line 1552 | Line 2084 | begin
2084    CheckActive;
2085    if VarIsNull(Value) then
2086      IsNull := True
2087 +  else
2088 +  if VarIsArray(Value) then {must be datetime plus timezone}
2089 +    SetAsDateTime(Value[0],AnsiString(Value[1]))
2090    else case VarType(Value) of
2091      varEmpty, varNull:
2092        IsNull := True;
2093      varSmallint, varInteger, varByte,
2094 <      varWord, varShortInt:
2095 <      AsLong := Value;
1561 <    varInt64:
1562 <      AsInt64 := Value;
2094 >      varWord, varShortInt, varInt64:
2095 >        SetAsNumeric(NewNumeric(Int64(Value)));
2096      varSingle, varDouble:
2097        AsDouble := Value;
2098      varCurrency:
2099 <      AsCurrency := Value;
2099 >      SetAsNumeric(NewNumeric(Currency(Value)));
2100      varBoolean:
2101        AsBoolean := Value;
2102      varDate:
# Line 1574 | Line 2107 | begin
2107        IBError(ibxeNotSupported, [nil]);
2108      varByRef, varDispatch, varError, varUnknown, varVariant:
2109        IBError(ibxeNotPermitted, [nil]);
2110 +    else
2111 +      if VarIsFmtBCD(Value) then
2112 +        SetAsBCD(VarToBCD(Value))
2113 +      else
2114 +        IBError(ibxeNotSupported, [nil]);
2115 +  end;
2116 + end;
2117 +
2118 + procedure TSQLDataItem.SetAsNumeric(Value: IFBNumeric);
2119 + begin
2120 +  CheckActive;
2121 +  Changing;
2122 +  if IsNullable then
2123 +    IsNull := False;
2124 +
2125 +  if CanChangeMetadata then
2126 +  begin
2127 +    {Restore original values}
2128 +    SQLType := getColMetadata.GetSQLType;
2129 +    Scale := getColMetadata.getScale;
2130 +    SetDataLength(getColMetadata.GetSize);
2131 +  end;
2132 +
2133 +  with FFirebirdClientAPI do
2134 +  case GetSQLType of
2135 +  SQL_LONG:
2136 +      PLong(SQLData)^ := SafeInteger(Value.clone(Scale).getRawValue);
2137 +  SQL_SHORT:
2138 +    PShort(SQLData)^ := SafeSmallInt(Value.clone(Scale).getRawValue);
2139 +  SQL_INT64:
2140 +    PInt64(SQLData)^ := Value.clone(Scale).getRawValue;
2141 +  SQL_TEXT, SQL_VARYING:
2142 +   SetAsString(Value.getAsString);
2143 +  SQL_D_FLOAT,
2144 +  SQL_DOUBLE:
2145 +    PDouble(SQLData)^ := Value.getAsDouble;
2146 +  SQL_FLOAT:
2147 +    PSingle(SQLData)^ := Value.getAsDouble;
2148 +  SQL_DEC_FIXED,
2149 +  SQL_DEC16,
2150 +  SQL_DEC34:
2151 +     SQLDecFloatEncode(Value.getAsBCD,SQLType,SQLData);
2152 +  SQL_INT128:
2153 +    StrToInt128(Scale,Value.getAsString,SQLData);
2154 +  else
2155 +    IBError(ibxeInvalidDataConversion, [nil]);
2156 +  end;
2157 +  Changed;
2158 + end;
2159 +
2160 + procedure TSQLDataItem.SetAsBcd(aValue: tBCD);
2161 + begin
2162 +  CheckActive;
2163 +  Changing;
2164 +  if IsNullable then
2165 +    IsNull := False;
2166 +
2167 +  if not CanChangeMetaData then
2168 +  begin
2169 +    SetAsNumeric(NewNumeric(aValue));
2170 +    Exit;
2171    end;
2172 +
2173 +  with FFirebirdClientAPI do
2174 +  if aValue.Precision <= 16 then
2175 +  begin
2176 +    if not HasDecFloatSupport then
2177 +      IBError(ibxeDecFloatNotSupported,[]);
2178 +
2179 +    SQLType := SQL_DEC16;
2180 +    DataLength := 8;
2181 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2182 +  end
2183 +  else
2184 +  if aValue.Precision <= 34 then
2185 +  begin
2186 +    if not HasDecFloatSupport then
2187 +      IBError(ibxeDecFloatNotSupported,[]);
2188 +
2189 +    SQLType := SQL_DEC34;
2190 +    DataLength := 16;
2191 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2192 +  end
2193 +  else
2194 +  if aValue.Precision <= 38 then
2195 +  begin
2196 +    if not HasInt128Support then
2197 +      IBError(ibxeInt128NotSupported,[]);
2198 +
2199 +    SQLType := SQL_INT128;
2200 +    DataLength := 16;
2201 +    StrToInt128(scale,BcdToStr(aValue),SQLData);
2202 +  end
2203 +  else
2204 +    IBError(ibxeBCDOverflow,[BCDToStr(aValue)]);
2205 +
2206 +  Changed;
2207   end;
2208  
2209   procedure TSQLDataItem.SetAsBoolean(AValue: boolean);
# Line 1607 | Line 2236 | begin
2236      IBError(ibxeStatementNotPrepared, [nil]);
2237   end;
2238  
2239 < function TColumnMetaData.SQLData: PChar;
2239 > function TColumnMetaData.GetAttachment: IAttachment;
2240 > begin
2241 >  Result := FIBXSQLVAR.GetAttachment;
2242 > end;
2243 >
2244 > function TColumnMetaData.SQLData: PByte;
2245   begin
2246    Result := FIBXSQLVAR.SQLData;
2247   end;
# Line 1624 | Line 2258 | end;
2258  
2259   constructor TColumnMetaData.Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
2260   begin
2261 <  inherited Create;
2261 >  inherited Create(aIBXSQLVAR.GetAttachment.getFirebirdAPI as TFBClientAPI);
2262    FIBXSQLVAR := aIBXSQLVAR;
2263    FOwner := aOwner;
2264    FPrepareSeqNo := FIBXSQLVAR.Parent.PrepareSeqNo;
# Line 1640 | Line 2274 | end;
2274  
2275   function TColumnMetaData.GetSQLDialect: integer;
2276   begin
2277 <  Result := FIBXSQLVAR.Statement.GetSQLDialect;
2277 >  Result := FIBXSQLVAR.GetAttachment.GetSQLDialect;
2278 > end;
2279 >
2280 > function TColumnMetaData.getColMetadata: IParamMetaData;
2281 > begin
2282 >  Result := self;
2283   end;
2284  
2285   function TColumnMetaData.GetIndex: integer;
# Line 1660 | Line 2299 | begin
2299    result := FIBXSQLVAR.SQLSubtype;
2300   end;
2301  
2302 < function TColumnMetaData.getRelationName: string;
2302 > function TColumnMetaData.getRelationName: AnsiString;
2303   begin
2304    CheckActive;
2305     result :=  FIBXSQLVAR.RelationName;
2306   end;
2307  
2308 < function TColumnMetaData.getOwnerName: string;
2308 > function TColumnMetaData.getOwnerName: AnsiString;
2309   begin
2310    CheckActive;
2311    result :=  FIBXSQLVAR.OwnerName;
2312   end;
2313  
2314 < function TColumnMetaData.getSQLName: string;
2314 > function TColumnMetaData.getSQLName: AnsiString;
2315   begin
2316    CheckActive;
2317    result :=  FIBXSQLVAR.FieldName;
2318   end;
2319  
2320 < function TColumnMetaData.getAliasName: string;
2320 > function TColumnMetaData.getAliasName: AnsiString;
2321   begin
2322    CheckActive;
2323    result := FIBXSQLVAR.AliasName;
2324   end;
2325  
2326 < function TColumnMetaData.GetName: string;
2326 > function TColumnMetaData.GetName: AnsiString;
2327   begin
2328    CheckActive;
2329    Result := FIBXSQLVAR. Name;
# Line 1711 | Line 2350 | end;
2350   function TColumnMetaData.GetSize: cardinal;
2351   begin
2352    CheckActive;
2353 <  result := FIBXSQLVAR.DataLength;
2353 >  result := FIBXSQLVAR.GetSize;
2354 > end;
2355 >
2356 > function TColumnMetaData.GetCharSetWidth: integer;
2357 > begin
2358 >  CheckActive;
2359 >  result := FIBXSQLVAR.GetCharSetWidth;
2360   end;
2361  
2362   function TColumnMetaData.GetArrayMetaData: IArrayMetaData;
# Line 1726 | Line 2371 | begin
2371    result := FIBXSQLVAR.GetBlobMetaData;
2372   end;
2373  
2374 + function TColumnMetaData.GetStatement: IStatement;
2375 + begin
2376 +  Result := FIBXSQLVAR.GetStatement;
2377 + end;
2378 +
2379 + function TColumnMetaData.GetTransaction: ITransaction;
2380 + begin
2381 +  Result := FIBXSQLVAR.GetTransaction;
2382 + end;
2383 +
2384   { TIBSQLData }
2385  
2386   procedure TIBSQLData.CheckActive;
# Line 1754 | Line 2409 | end;
2409   function TIBSQLData.GetAsArray: IArray;
2410   begin
2411    CheckActive;
2412 <  result := FIBXSQLVAR.GetAsArray(AsQuad);
2412 >  result := FIBXSQLVAR.GetAsArray;
2413   end;
2414  
2415   function TIBSQLData.GetAsBlob: IBlob;
# Line 1769 | Line 2424 | begin
2424    result := FIBXSQLVAR.GetAsBlob(AsQuad,BPB);
2425   end;
2426  
2427 < function TIBSQLData.GetAsString: String;
2427 > function TIBSQLData.GetAsString: AnsiString;
2428   begin
2429    CheckActive;
2430    Result := '';
# Line 1777 | Line 2432 | begin
2432    if not IsNull then
2433    case SQLType of
2434      SQL_ARRAY:
2435 <      result := '(Array)'; {do not localize}
2435 >      result := SArray;
2436      SQL_BLOB:
2437 <      Result := Trim(FIBXSQLVAR.GetAsBlob(AsQuad,nil).GetAsString);
2437 >      Result := FIBXSQLVAR.GetAsBlob(AsQuad,nil).GetAsString;
2438      else
2439        Result := inherited GetAsString;
2440    end;
# Line 1787 | Line 2442 | end;
2442  
2443   { TSQLParam }
2444  
2445 < procedure TSQLParam.InternalSetAsString(Value: String);
2445 > procedure TSQLParam.InternalSetAsString(Value: AnsiString);
2446 >
2447 > procedure DoSetString;
2448 > begin
2449 >  Changing;
2450 >  FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
2451 >  Changed;
2452 > end;
2453 >
2454   var b: IBlob;
2455 +    dt: TDateTime;
2456 +    timezone: AnsiString;
2457 +    Int64Value: Int64;
2458 +    BCDValue: TBCD;
2459 +    aScale: integer;
2460   begin
2461    CheckActive;
2462    if IsNullable then
2463      IsNull := False;
2464 +  with FFirebirdClientAPI do
2465    case SQLTYPE of
2466    SQL_BOOLEAN:
2467 <    if CompareText(Value,STrue) = 0 then
2467 >    if AnsiCompareText(Value,STrue) = 0 then
2468        AsBoolean := true
2469      else
2470 <    if CompareText(Value,SFalse) = 0 then
2470 >    if AnsiCompareText(Value,SFalse) = 0 then
2471        AsBoolean := false
2472      else
2473        IBError(ibxeInvalidDataConversion,[nil]);
2474  
2475    SQL_BLOB:
2476 +    if Length(Value) < GetAttachment.GetInlineBlobLimit then
2477 +      DoSetString
2478 +    else
2479      begin
2480        Changing;
2481        b := FIBXSQLVAR.CreateBlob;
# Line 1814 | Line 2486 | begin
2486  
2487    SQL_VARYING,
2488    SQL_TEXT:
2489 <    begin
1818 <      Changing;
1819 <      FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
1820 <      Changed;
1821 <    end;
2489 >    DoSetString;
2490  
2491 <    SQL_SHORT,
2492 <    SQL_LONG,
2493 <    SQL_INT64:
2494 <      SetAsInt64(StrToInt(Value));
2495 <
2496 <    SQL_D_FLOAT,
2497 <    SQL_DOUBLE,
1830 <    SQL_FLOAT:
1831 <      SetAsDouble(StrToFloat(Value));
2491 >  SQL_SHORT,
2492 >  SQL_LONG,
2493 >  SQL_INT64:
2494 >    if TryStrToNumeric(Value,Int64Value,aScale) then
2495 >      SetAsNumeric(NumericFromRawValues(Int64Value,aScale))
2496 >    else
2497 >      DoSetString;
2498  
2499 <    SQL_TIMESTAMP:
2500 <      SetAsDateTime(StrToDateTime(Value));
2499 >  SQL_DEC_FIXED,
2500 >  SQL_DEC16,
2501 >  SQL_DEC34,
2502 >  SQL_INT128:
2503 >    if TryStrToBCD(Value,BCDValue) then
2504 >      SetAsNumeric(NewNumeric(BCDValue))
2505 >    else
2506 >      DoSetString;
2507  
2508 <    SQL_TYPE_DATE:
2509 <      SetAsDate(StrToDateTime(Value));
2508 >  SQL_D_FLOAT,
2509 >  SQL_DOUBLE,
2510 >  SQL_FLOAT:
2511 >    if TryStrToNumeric(Value,Int64Value,aScale) then
2512 >      SetAsNumeric(NumericFromRawValues(Int64Value,AScale))
2513 >    else
2514 >      DoSetString;
2515  
2516 <    SQL_TYPE_TIME:
2517 <      SetAsTime(StrToDateTime(Value));
2516 >  SQL_TIMESTAMP:
2517 >      if TryStrToDateTime(Value,dt) then
2518 >        SetAsDateTime(dt)
2519 >      else
2520 >        DoSetString;
2521  
2522 <    else
2523 <      IBError(ibxeInvalidDataConversion,[nil]);
2522 >  SQL_TYPE_DATE:
2523 >      if TryStrToDateTime(Value,dt) then
2524 >        SetAsDate(dt)
2525 >      else
2526 >        DoSetString;
2527 >
2528 >  SQL_TYPE_TIME:
2529 >      if TryStrToDateTime(Value,dt) then
2530 >        SetAsTime(dt)
2531 >      else
2532 >        DoSetString;
2533 >
2534 >  SQL_TIMESTAMP_TZ,
2535 >  SQL_TIMESTAMP_TZ_EX:
2536 >      if ParseDateTimeTZString(value,dt,timezone) then
2537 >        SetAsDateTime(dt,timezone)
2538 >      else
2539 >        DoSetString;
2540 >
2541 >  SQL_TIME_TZ,
2542 >  SQL_TIME_TZ_EX:
2543 >      if ParseDateTimeTZString(value,dt,timezone,true) then
2544 >        SetAsTime(dt,GetAttachment.GetTimeZoneServices.GetTimeTZDate,timezone)
2545 >      else
2546 >        DoSetString;
2547 >
2548 >  else
2549 >    IBError(ibxeInvalidDataConversion,[GetSQLTypeName(getColMetaData.SQLTYPE)]);
2550    end;
2551   end;
2552  
# Line 1878 | Line 2584 | begin
2584    IsNull := true;
2585   end;
2586  
2587 + function TSQLParam.CanChangeMetaData: boolean;
2588 + begin
2589 +  Result := FIBXSQLVAR.CanChangeMetaData;
2590 + end;
2591 +
2592 + function TSQLParam.getColMetadata: IParamMetaData;
2593 + begin
2594 +  Result := FIBXSQLVAR.getColMetadata;
2595 + end;
2596 +
2597   function TSQLParam.GetModified: boolean;
2598   begin
2599    CheckActive;
# Line 1891 | Line 2607 | begin
2607    Result := inherited GetAsPointer;
2608   end;
2609  
2610 < procedure TSQLParam.SetName(Value: string);
2610 > function TSQLParam.GetAsString: AnsiString;
2611 > var rs: RawByteString;
2612 > begin
2613 >  Result := '';
2614 >  if (SQLType = SQL_VARYING) and not IsNull then
2615 >  {SQLData points to start of string - default is to length word}
2616 >  begin
2617 >    CheckActive;
2618 >    SetString(rs,PAnsiChar(SQLData),DataLength);
2619 >    SetCodePage(rs,GetCodePage,false);
2620 >    Result := rs;
2621 >  end
2622 >  else
2623 >    Result := inherited GetAsString;
2624 > end;
2625 >
2626 > procedure TSQLParam.SetName(Value: AnsiString);
2627   begin
2628    CheckActive;
2629    FIBXSQLVAR.Name := Value;
# Line 1936 | Line 2668 | begin
2668    if not FIBXSQLVAR.UniqueName then
2669      IBError(ibxeDuplicateParamName,[FIBXSQLVAR.Name]);
2670  
2671 +  FIBXSQLVAR.SetArray(anArray); {save array interface}
2672    SetAsQuad(AnArray.GetArrayID);
2673   end;
2674  
# Line 2082 | Line 2815 | begin
2815    end;
2816   end;
2817  
2818 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
2819 + var i: integer;
2820 +    OldSQLVar: TSQLVarData;
2821 + begin
2822 +  if FIBXSQLVAR.UniqueName then
2823 +    inherited SetAsTime(AValue,OnDate, aTimeZoneID)
2824 +  else
2825 +  with FIBXSQLVAR.Parent do
2826 +  begin
2827 +    for i := 0 to Count - 1 do
2828 +      if Column[i].Name = Name then
2829 +      begin
2830 +        OldSQLVar := FIBXSQLVAR;
2831 +        FIBXSQLVAR := Column[i];
2832 +        try
2833 +          inherited SetAsTime(AValue,OnDate, aTimeZoneID);
2834 +        finally
2835 +          FIBXSQLVAR := OldSQLVar;
2836 +        end;
2837 +      end;
2838 +  end;
2839 + end;
2840 +
2841 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
2842 + var i: integer;
2843 +    OldSQLVar: TSQLVarData;
2844 + begin
2845 +  if FIBXSQLVAR.UniqueName then
2846 +    inherited SetAsTime(AValue,OnDate,aTimeZone)
2847 +  else
2848 +  with FIBXSQLVAR.Parent do
2849 +  begin
2850 +    for i := 0 to Count - 1 do
2851 +      if Column[i].Name = Name then
2852 +      begin
2853 +        OldSQLVar := FIBXSQLVAR;
2854 +        FIBXSQLVAR := Column[i];
2855 +        try
2856 +          inherited SetAsTime(AValue,OnDate,aTimeZone);
2857 +        finally
2858 +          FIBXSQLVAR := OldSQLVar;
2859 +        end;
2860 +      end;
2861 +  end;
2862 + end;
2863 +
2864 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID);
2865 + begin
2866 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZoneID);
2867 + end;
2868 +
2869 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZone: AnsiString);
2870 + begin
2871 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZone);
2872 + end;
2873 +
2874   procedure TSQLParam.SetAsDateTime(AValue: TDateTime);
2875   var i: integer;
2876      OldSQLVar: TSQLVarData;
# Line 2105 | Line 2894 | begin
2894    end;
2895   end;
2896  
2897 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID
2898 +  );
2899 + var i: integer;
2900 +    OldSQLVar: TSQLVarData;
2901 + begin
2902 +  if FIBXSQLVAR.UniqueName then
2903 +    inherited SetAsDateTime(AValue,aTimeZoneID)
2904 +  else
2905 +  with FIBXSQLVAR.Parent do
2906 +  begin
2907 +    for i := 0 to Count - 1 do
2908 +      if Column[i].Name = Name then
2909 +      begin
2910 +        OldSQLVar := FIBXSQLVAR;
2911 +        FIBXSQLVAR := Column[i];
2912 +        try
2913 +          inherited SetAsDateTime(AValue,aTimeZoneID);
2914 +        finally
2915 +          FIBXSQLVAR := OldSQLVar;
2916 +        end;
2917 +      end;
2918 +  end;
2919 + end;
2920 +
2921 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString);
2922 + var i: integer;
2923 +    OldSQLVar: TSQLVarData;
2924 + begin
2925 +  if FIBXSQLVAR.UniqueName then
2926 +    inherited SetAsDateTime(AValue,aTimeZone)
2927 +  else
2928 +  with FIBXSQLVAR.Parent do
2929 +  begin
2930 +    for i := 0 to Count - 1 do
2931 +      if Column[i].Name = Name then
2932 +      begin
2933 +        OldSQLVar := FIBXSQLVAR;
2934 +        FIBXSQLVAR := Column[i];
2935 +        try
2936 +          inherited SetAsDateTime(AValue,aTimeZone);
2937 +        finally
2938 +          FIBXSQLVAR := OldSQLVar;
2939 +        end;
2940 +      end;
2941 +  end;
2942 + end;
2943 +
2944   procedure TSQLParam.SetAsDouble(AValue: Double);
2945   var i: integer;
2946      OldSQLVar: TSQLVarData;
# Line 2197 | Line 3033 | begin
3033    end;
3034   end;
3035  
3036 < procedure TSQLParam.SetAsString(AValue: String);
3036 > procedure TSQLParam.SetAsString(AValue: AnsiString);
3037   var i: integer;
3038      OldSQLVar: TSQLVarData;
3039   begin
# Line 2285 | Line 3121 | begin
3121    FIBXSQLVAR.SetCharSetID(aValue);
3122   end;
3123  
3124 + procedure TSQLParam.SetAsBcd(aValue: tBCD);
3125 + var i: integer;
3126 +    OldSQLVar: TSQLVarData;
3127 + begin
3128 +  if FIBXSQLVAR.UniqueName then
3129 +    inherited SetAsBcd(AValue)
3130 +  else
3131 +  with FIBXSQLVAR.Parent do
3132 +  begin
3133 +    for i := 0 to Count - 1 do
3134 +      if Column[i].Name = Name then
3135 +      begin
3136 +        OldSQLVar := FIBXSQLVAR;
3137 +        FIBXSQLVAR := Column[i];
3138 +        try
3139 +          inherited SetAsBcd(AValue);
3140 +        finally
3141 +          FIBXSQLVAR := OldSQLVar;
3142 +        end;
3143 +      end;
3144 +  end;
3145 + end;
3146 +
3147 + procedure TSQLParam.SetAsNumeric(aValue: IFBNumeric);
3148 + var i: integer;
3149 +    OldSQLVar: TSQLVarData;
3150 + begin
3151 +  if FIBXSQLVAR.UniqueName then
3152 +    inherited SetAsNumeric(AValue)
3153 +  else
3154 +  with FIBXSQLVAR.Parent do
3155 +  begin
3156 +    for i := 0 to Count - 1 do
3157 +      if Column[i].Name = Name then
3158 +      begin
3159 +        OldSQLVar := FIBXSQLVAR;
3160 +        FIBXSQLVAR := Column[i];
3161 +        try
3162 +          inherited SetAsNumeric(AValue);
3163 +        finally
3164 +          FIBXSQLVAR := OldSQLVar;
3165 +        end;
3166 +      end;
3167 +  end;
3168 + end;
3169 +
3170   { TMetaData }
3171  
3172   procedure TMetaData.CheckActive;
# Line 2306 | Line 3188 | end;
3188  
3189   destructor TMetaData.Destroy;
3190   begin
3191 <  (FStatement as TInterfaceOwner).Remove(self);
3191 >  if FStatement <> nil then
3192 >    (FStatement as TInterfaceOwner).Remove(self);
3193    inherited Destroy;
3194   end;
3195  
3196 < function TMetaData.GetUniqueRelationName: string;
3196 > function TMetaData.GetUniqueRelationName: AnsiString;
3197   begin
3198    CheckActive;
3199    Result := FMetaData.UniqueRelationName;
# Line 2338 | Line 3221 | begin
3221    end;
3222   end;
3223  
3224 < function TMetaData.ByName(Idx: String): IColumnMetaData;
3224 > function TMetaData.ByName(Idx: AnsiString): IColumnMetaData;
3225   var aIBXSQLVAR: TSQLVarData;
3226   begin
3227    CheckActive;
# Line 2372 | Line 3255 | end;
3255  
3256   destructor TSQLParams.Destroy;
3257   begin
3258 <  (FStatement as TInterfaceOwner).Remove(self);
3258 >  if FStatement <> nil then
3259 >    (FStatement as TInterfaceOwner).Remove(self);
3260    inherited Destroy;
3261   end;
3262  
# Line 2398 | Line 3282 | begin
3282    end;
3283   end;
3284  
3285 < function TSQLParams.ByName(Idx: String): ISQLParam;
3285 > function TSQLParams.ByName(Idx: AnsiString): ISQLParam;
3286   var aIBXSQLVAR: TSQLVarData;
3287   begin
3288    CheckActive;
# Line 2423 | Line 3307 | begin
3307      end;
3308   end;
3309  
3310 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
3311 + begin
3312 +  Result := FSQLParams.CaseSensitiveParams;
3313 + end;
3314 +
3315 + function TSQLParams.GetStatement: IStatement;
3316 + begin
3317 +  Result := FSQLParams.GetStatement;
3318 + end;
3319 +
3320 + function TSQLParams.GetTransaction: ITransaction;
3321 + begin
3322 +  Result := FSQLParams.GetTransaction;
3323 + end;
3324 +
3325 + function TSQLParams.GetAttachment: IAttachment;
3326 + begin
3327 +  Result := FSQLParams.GetAttachment;
3328 + end;
3329 +
3330 + procedure TSQLParams.Clear;
3331 + var i: integer;
3332 + begin
3333 +  for i := 0 to getCount - 1 do
3334 +    getSQLParam(i).Clear;
3335 + end;
3336 +
3337   { TResults }
3338  
3339   procedure TResults.CheckActive;
# Line 2435 | Line 3346 | begin
3346    if not FResults.CheckStatementStatus(ssPrepared)  then
3347      IBError(ibxeStatementNotPrepared, [nil]);
3348  
3349 <  with GetTransaction as TFBTransaction do
3349 >  with GetTransaction do
3350    if not InTransaction or (FResults.TransactionSeqNo <> FTransactionSeqNo) then
3351      IBError(ibxeInterfaceOutofDate,[nil]);
3352   end;
3353  
3354   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
3355 + var col: TIBSQLData;
3356   begin
3357    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
3358      IBError(ibxeInvalidColumnIndex,[nil]);
3359  
3360    if not HasInterface(aIBXSQLVAR.Index) then
3361 <    AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
3362 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3361 >  begin
3362 >    col := TIBSQLData.Create(self,aIBXSQLVAR);
3363 >    AddInterface(aIBXSQLVAR.Index, col);
3364 >  end
3365 >  else
3366 >    col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3367 >  Result := col;
3368   end;
3369  
3370   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2466 | Line 3383 | begin
3383    Result := FResults.Count;
3384   end;
3385  
3386 < function TResults.ByName(Idx: String): ISQLData;
3386 > function TResults.ByName(Idx: AnsiString): ISQLData;
3387   var col: TSQLVarData;
3388   begin
3389    Result := nil;
# Line 2498 | Line 3415 | begin
3415   end;
3416  
3417   procedure TResults.GetData(index: integer; var IsNull: boolean; var len: short;
3418 <  var data: PChar);
3418 >  var data: PByte);
3419   begin
3420    CheckActive;
3421    FResults.GetData(index,IsNull, len,data);
3422   end;
3423  
3424 + function TResults.GetStatement: IStatement;
3425 + begin
3426 +  Result := FStatement;
3427 + end;
3428 +
3429   function TResults.GetTransaction: ITransaction;
3430   begin
3431 <  Result := FStatement.GetTransaction;
3431 >  Result := FResults.GetTransaction;
3432 > end;
3433 >
3434 > function TResults.GetAttachment: IAttachment;
3435 > begin
3436 >  Result := FResults.GetAttachment;
3437   end;
3438  
3439   procedure TResults.SetRetainInterfaces(aValue: boolean);
# Line 2514 | Line 3441 | begin
3441    RetainInterfaces := aValue;
3442   end;
3443  
2517
3444   end.
3445  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines