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

Comparing ibx/trunk/fbintf/client/FBSQLData.pas (file contents):
Revision 59 by tony, Mon Mar 13 09:51:56 2017 UTC vs.
Revision 349 by tony, Mon Oct 18 08:39:40 2021 UTC

# Line 76 | Line 76 | unit FBSQLData;
76    methods are needed for SQL parameters only. The string getters and setters
77    are virtual as SQLVar and Array encodings of string data is different.}
78  
79 { $define ALLOWDIALECT3PARAMNAMES}
80
81 {$ifndef ALLOWDIALECT3PARAMNAMES}
82
83 { Note on SQL Dialects and SQL Parameter Names
84  --------------------------------------------
85
86  Even when dialect 3 quoted format parameter names are not supported, IBX still processes
87  parameter names case insensitive. This does result in some additional overhead
88  due to a call to "AnsiUpperCase". This can be avoided by undefining
89  "UseCaseInSensitiveParamName" below.
90
91  Note: do not define "UseCaseSensitiveParamName" when "ALLOWDIALECT3PARAMNAMES"
92  is defined. This will not give a useful result.
93 }
94 {$define UseCaseInSensitiveParamName}
95 {$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 +     FFirebirdClientAPI: TFBClientAPI;
124 +     FTimeZoneServices: IExTimeZoneServices;
125       function AdjustScale(Value: Int64; aScale: Integer): Double;
126       function AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
127       function AdjustScaleToCurrency(Value: Int64; aScale: Integer): Currency;
128 +     function GetDateFormatStr(IncludeTime: boolean): AnsiString;
129 +     function GetTimeFormatStr: AnsiString;
130 +     function GetTimestampFormatStr: AnsiString;
131       procedure SetAsInteger(AValue: Integer);
132 +     procedure InternalGetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
133 +       var aTimezone: AnsiString; var aTimeZoneID: TFBTimeZoneID);
134    protected
135       function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
136       function AdjustScaleFromDouble(Value: Double; aScale: Integer): Int64;
137       procedure CheckActive; virtual;
138 +     procedure CheckTZSupport;
139 +     function GetAttachment: IAttachment; virtual; abstract;
140       function GetSQLDialect: integer; virtual; abstract;
141 +     function GetTimeZoneServices: IExTimeZoneServices; virtual;
142       procedure Changed; virtual;
143       procedure Changing; virtual;
144       procedure InternalSetAsString(Value: AnsiString); virtual;
# Line 126 | Line 151 | type
151       procedure SetDataLength(len: cardinal); virtual;
152       procedure SetSQLType(aValue: cardinal); virtual;
153       property DataLength: cardinal read GetDataLength write SetDataLength;
154 <
154 >     property FirebirdClientAPI: TFBClientAPI read FFirebirdClientAPI;
155    public
156 <     function GetSQLType: cardinal; virtual; abstract;
156 >     constructor Create(api: TFBClientAPI);
157 >     function GetSQLType: cardinal; virtual; abstract; {Current Field Data SQL Type}
158       function GetSQLTypeName: AnsiString; overload;
159 <     class function GetSQLTypeName(SQLType: short): AnsiString; overload;
159 >     class function GetSQLTypeName(SQLType: cardinal): AnsiString; overload;
160 >     function GetStrDataLength: short;
161       function GetName: AnsiString; virtual; abstract;
162 <     function GetScale: integer; virtual; abstract;
162 >     function GetScale: integer; virtual; abstract; {Current Field Data scale}
163       function GetAsBoolean: boolean;
164       function GetAsCurrency: Currency;
165       function GetAsInt64: Int64;
166 <     function GetAsDateTime: TDateTime;
166 >     function GetAsDateTime: TDateTime; overload;
167 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
168 >     procedure GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
169 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime); overload;
170 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString; OnDate: TDateTime); overload;
171 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezoneID: TFBTimeZoneID); overload;
172 >     procedure GetAsTime(var aTime: TDateTime; var dstOffset: smallint; var aTimezone: AnsiString); overload;
173 >     function GetAsUTCDateTime: TDateTime;
174       function GetAsDouble: Double;
175       function GetAsFloat: Float;
176       function GetAsLong: Long;
# Line 145 | Line 179 | type
179       function GetAsShort: short;
180       function GetAsString: AnsiString; virtual;
181       function GetIsNull: Boolean; virtual;
182 <     function getIsNullable: boolean; virtual;
182 >     function GetIsNullable: boolean; virtual;
183       function GetAsVariant: Variant;
184       function GetModified: boolean; virtual;
185 +     function GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats): integer;
186 +     function GetAsBCD: tBCD;
187 +     function GetSize: cardinal; virtual; abstract;
188 +     function GetCharSetWidth: integer; virtual; abstract;
189       procedure SetAsBoolean(AValue: boolean); virtual;
190       procedure SetAsCurrency(Value: Currency); virtual;
191       procedure SetAsInt64(Value: Int64); virtual;
192       procedure SetAsDate(Value: TDateTime); virtual;
193       procedure SetAsLong(Value: Long); virtual;
194 <     procedure SetAsTime(Value: TDateTime); virtual;
195 <     procedure SetAsDateTime(Value: TDateTime);
194 >     procedure SetAsTime(Value: TDateTime); overload;
195 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime;aTimeZoneID: TFBTimeZoneID); overload;
196 >     procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
197 >     procedure SetAsDateTime(Value: TDateTime); overload;
198 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
199 >     procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
200 >     procedure SetAsUTCDateTime(aUTCTime: TDateTime);
201       procedure SetAsDouble(Value: Double); virtual;
202       procedure SetAsFloat(Value: Float); virtual;
203       procedure SetAsPointer(Value: Pointer);
# Line 163 | Line 206 | type
206       procedure SetAsString(Value: AnsiString); virtual;
207       procedure SetAsVariant(Value: Variant);
208       procedure SetAsNumeric(Value: Int64; aScale: integer);
209 +     procedure SetAsBcd(aValue: tBCD); virtual;
210       procedure SetIsNull(Value: Boolean); virtual;
211       procedure SetIsNullable(Value: Boolean); virtual;
212       procedure SetName(aValue: AnsiString); virtual;
# Line 196 | Line 240 | type
240  
241    TSQLDataArea = class
242    private
243 +    FCaseSensitiveParams: boolean;
244      function GetColumn(index: integer): TSQLVarData;
245      function GetCount: integer;
246    protected
# Line 218 | Line 263 | type
263        var data: PByte); virtual;
264      procedure RowChange;
265      function StateChanged(var ChangeSeqNo: integer): boolean; virtual; abstract;
266 +    property CaseSensitiveParams: boolean read FCaseSensitiveParams
267 +                                            write FCaseSensitiveParams; {Only used when IsInputDataArea true}
268 +    function CanChangeMetaData: boolean; virtual; abstract;
269      property Count: integer read GetCount;
270      property Column[index: integer]: TSQLVarData read GetColumn;
271      property UniqueRelationName: AnsiString read FUniqueRelationName;
# Line 236 | Line 284 | type
284      FModified: boolean;
285      FUniqueName: boolean;
286      FVarString: RawByteString;
287 +    FColMetaData: IParamMetaData;
288      function GetStatement: IStatement;
289      procedure SetName(AValue: AnsiString);
290    protected
291 +    function GetAttachment: IAttachment; virtual; abstract;
292      function GetSQLType: cardinal; virtual; abstract;
293      function GetSubtype: integer; virtual; abstract;
294      function GetAliasName: AnsiString;  virtual; abstract;
# Line 247 | Line 297 | type
297      function GetRelationName: AnsiString;  virtual; abstract;
298      function GetScale: integer; virtual; abstract;
299      function GetCharSetID: cardinal; virtual; abstract;
300 +    function GetCharSetWidth: integer; virtual; abstract;
301      function GetCodePage: TSystemCodePage; virtual; abstract;
302      function GetIsNull: Boolean;   virtual; abstract;
303      function GetIsNullable: boolean; virtual; abstract;
304      function GetSQLData: PByte;  virtual; abstract;
305 <    function GetDataLength: cardinal; virtual; abstract;
305 >    function GetDataLength: cardinal; virtual; abstract; {current field length}
306 >    function GetSize: cardinal; virtual; abstract; {field length as given by metadata}
307 >    function GetDefaultTextSQLType: cardinal; virtual; abstract;
308      procedure SetIsNull(Value: Boolean); virtual; abstract;
309      procedure SetIsNullable(Value: Boolean);  virtual; abstract;
310      procedure SetSQLData(AValue: PByte; len: cardinal); virtual; abstract;
# Line 259 | Line 312 | type
312      procedure SetDataLength(len: cardinal); virtual; abstract;
313      procedure SetSQLType(aValue: cardinal); virtual; abstract;
314      procedure SetCharSetID(aValue: cardinal); virtual; abstract;
315 +    procedure SetMetaSize(aValue: cardinal); virtual;
316    public
317      constructor Create(aParent: TSQLDataArea; aIndex: integer);
318      procedure SetString(aValue: AnsiString);
# Line 269 | Line 323 | type
323      function CreateBlob: IBlob; virtual; abstract;
324      function GetArrayMetaData: IArrayMetaData; virtual; abstract;
325      function GetBlobMetaData: IBlobMetaData; virtual; abstract;
326 +    function getColMetadata: IParamMetaData;
327      procedure Initialize; virtual;
328 +    procedure SaveMetaData;
329  
330    public
331      property AliasName: AnsiString read GetAliasName;
# Line 300 | Line 356 | type
356      FIBXSQLVAR: TSQLVarData;
357      FOwner: IUnknown;         {Keep reference to ensure Metadata/statement not discarded}
358      FPrepareSeqNo: integer;
303    FStatement: IStatement;
359      FChangeSeqNo: integer;
360    protected
361      procedure CheckActive; override;
362 +    function GetAttachment: IAttachment; override;
363      function SQLData: PByte; override;
364      function GetDataLength: cardinal; override;
365      function GetCodePage: TSystemCodePage; override;
# Line 312 | Line 368 | type
368      constructor Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
369      destructor Destroy; override;
370      function GetSQLDialect: integer; override;
315    property Statement: IStatement read FStatement;
371  
372    public
373      {IColumnMetaData}
# Line 327 | Line 382 | type
382      function GetScale: integer; override;
383      function getCharSetID: cardinal; override;
384      function GetIsNullable: boolean; override;
385 <    function GetSize: cardinal;
385 >    function GetSize: cardinal; override;
386 >    function GetCharSetWidth: integer; override;
387      function GetArrayMetaData: IArrayMetaData;
388      function GetBlobMetaData: IBlobMetaData;
389 +    function GetStatement: IStatement;
390 +    function GetTransaction: ITransaction; virtual;
391      property Name: AnsiString read GetName;
392      property Size: cardinal read GetSize;
393      property CharSetID: cardinal read getCharSetID;
394      property SQLSubtype: integer read getSubtype;
395      property IsNullable: Boolean read GetIsNullable;
396 +  public
397 +    property Statement: IStatement read GetStatement;
398    end;
399  
400    { TIBSQLData }
401  
402    TIBSQLData = class(TColumnMetaData,ISQLData)
403 +  private
404 +    FTransaction: ITransaction;
405    protected
406      procedure CheckActive; override;
407    public
408 +    function GetTransaction: ITransaction; override;
409      function GetIsNull: Boolean; override;
410      function GetAsArray: IArray;
411      function GetAsBlob: IBlob; overload;
# Line 351 | Line 414 | type
414      property AsBlob: IBlob read GetAsBlob;
415   end;
416  
417 +  { TSQLParamMetaData }
418 +
419 +  TSQLParamMetaData = class(TFBInterfacedObject,IParamMetaData)
420 +  private
421 +    FSQLType: cardinal;
422 +    FSQLSubType: integer;
423 +    FScale: integer;
424 +    FCharSetID: cardinal;
425 +    FNullable: boolean;
426 +    FSize: cardinal;
427 +    FCodePage: TSystemCodePage;
428 +  public
429 +    constructor Create(src: TSQLVarData);
430 +    {IParamMetaData}
431 +    function GetSQLType: cardinal;
432 +    function GetSQLTypeName: AnsiString;
433 +    function getSubtype: integer;
434 +    function getScale: integer;
435 +    function getCharSetID: cardinal;
436 +    function getCodePage: TSystemCodePage;
437 +    function getIsNullable: boolean;
438 +    function GetSize: cardinal;
439 +    property SQLType: cardinal read GetSQLType;
440 +  end;
441 +
442    { TSQLParam }
443  
444 <  TSQLParam = class(TIBSQLData,ISQLParam)
444 >  TSQLParam = class(TIBSQLData,ISQLParam,ISQLData)
445    protected
446      procedure CheckActive; override;
447      procedure Changed; override;
# Line 363 | Line 451 | type
451      procedure SetSQLType(aValue: cardinal); override;
452    public
453      procedure Clear;
454 +    function getColMetadata: IParamMetaData;
455      function GetModified: boolean; override;
456      function GetAsPointer: Pointer;
457 +    function GetAsString: AnsiString; override;
458      procedure SetName(Value: AnsiString); override;
459      procedure SetIsNull(Value: Boolean);  override;
460      procedure SetIsNullable(Value: Boolean); override;
# Line 376 | Line 466 | type
466      procedure SetAsInt64(AValue: Int64);
467      procedure SetAsDate(AValue: TDateTime);
468      procedure SetAsLong(AValue: Long);
469 <    procedure SetAsTime(AValue: TDateTime);
470 <    procedure SetAsDateTime(AValue: TDateTime);
469 >    procedure SetAsTime(AValue: TDateTime); overload;
470 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
471 >    procedure SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString); overload;
472 >    procedure SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
473 >    procedure SetAsTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
474 >    procedure SetAsDateTime(AValue: TDateTime); overload;
475 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID); overload;
476 >    procedure SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString); overload;
477      procedure SetAsDouble(AValue: Double);
478      procedure SetAsFloat(AValue: Float);
479      procedure SetAsPointer(AValue: Pointer);
# Line 387 | Line 483 | type
483      procedure SetAsBlob(aValue: IBlob);
484      procedure SetAsQuad(AValue: TISC_QUAD);
485      procedure SetCharSetID(aValue: cardinal);
486 +    procedure SetAsBcd(aValue: tBCD);
487  
488      property AsBlob: IBlob read GetAsBlob write SetAsBlob;
489      property IsNullable: Boolean read GetIsNullable write SetIsNullable;
# Line 429 | Line 526 | type
526      function getSQLParam(index: integer): ISQLParam;
527      function ByName(Idx: AnsiString): ISQLParam ;
528      function GetModified: Boolean;
529 +    function GetHasCaseSensitiveParams: Boolean;
530    end;
531  
532    { TResults }
# Line 450 | Line 548 | type
548       function ByName(Idx: AnsiString): ISQLData;
549       function getSQLData(index: integer): ISQLData;
550       procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PByte);
551 +     function GetStatement: IStatement;
552       function GetTransaction: ITransaction; virtual;
553       procedure SetRetainInterfaces(aValue: boolean);
554   end;
555  
556   implementation
557  
558 < uses FBMessages, FBClientAPI, variants, IBUtils, FBTransaction;
558 > uses FBMessages, variants, IBUtils, FBTransaction, DateUtils;
559 >
560 > { TSQLParamMetaData }
561 >
562 > constructor TSQLParamMetaData.Create(src: TSQLVarData);
563 > begin
564 >  inherited Create;
565 >  FSQLType := src.GetSQLType;
566 >  FSQLSubType := src.getSubtype;
567 >  FScale := src.GetScale;
568 >  FCharSetID := src.getCharSetID;
569 >  FNullable := src.GetIsNullable;
570 >  FSize := src.GetSize;
571 >  FCodePage := src.GetCodePage;
572 > end;
573 >
574 > function TSQLParamMetaData.GetSQLType: cardinal;
575 > begin
576 >  Result := FSQLType;
577 > end;
578 >
579 > function TSQLParamMetaData.GetSQLTypeName: AnsiString;
580 > begin
581 >  Result := TSQLDataItem.GetSQLTypeName(FSQLType);
582 > end;
583 >
584 > function TSQLParamMetaData.getSubtype: integer;
585 > begin
586 >  Result := FSQLSubType;
587 > end;
588 >
589 > function TSQLParamMetaData.getScale: integer;
590 > begin
591 >  Result := FScale;
592 > end;
593 >
594 > function TSQLParamMetaData.getCharSetID: cardinal;
595 > begin
596 >  Result := FCharSetID;
597 > end;
598  
599 + function TSQLParamMetaData.getCodePage: TSystemCodePage;
600 + begin
601 +  Result :=  FCodePage;
602 + end;
603 +
604 + function TSQLParamMetaData.getIsNullable: boolean;
605 + begin
606 +  Result :=  FNullable;
607 + end;
608 +
609 + function TSQLParamMetaData.GetSize: cardinal;
610 + begin
611 +  Result := FSize;
612 + end;
613  
614   { TSQLDataArea }
615  
# Line 510 | Line 662 | end;
662  
663   procedure TSQLDataArea.PreprocessSQL(sSQL: AnsiString; GenerateParamNames: boolean;
664    var sProcessedSQL: AnsiString);
513 var
514  cCurChar, cNextChar, cQuoteChar: AnsiChar;
515  sParamName: AnsiString;
516  j, i, iLenSQL, iSQLPos: Integer;
517  iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
518  iParamSuffix: Integer;
519  slNames: TStrings;
520  StrBuffer: PByte;
521  found: boolean;
522
523 const
524  DefaultState = 0;
525  CommentState = 1;
526  QuoteState = 2;
527  ParamState = 3;
528  ArrayDimState = 4;
529 {$ifdef ALLOWDIALECT3PARAMNAMES}
530  ParamDefaultState = 0;
531  ParamQuoteState = 1;
532  {$endif}
533
534  procedure AddToProcessedSQL(cChar: AnsiChar);
535  begin
536    StrBuffer[iSQLPos] := byte(cChar);
537    Inc(iSQLPos);
538  end;
539
540 begin
541  if not IsInputDataArea then
542    IBError(ibxeNotPermitted,[nil]);
665  
666 <  sParamName := '';
545 <  iLenSQL := Length(sSQL);
546 <  GetMem(StrBuffer,iLenSQL + 1);
547 <  slNames := TStringList.Create;
548 <  try
549 <    { Do some initializations of variables }
550 <    iParamSuffix := 0;
551 <    cQuoteChar := '''';
552 <    i := 1;
553 <    iSQLPos := 0;
554 <    iCurState := DefaultState;
555 <    {$ifdef ALLOWDIALECT3PARAMNAMES}
556 <    iCurParamState := ParamDefaultState;
557 <    {$endif}
558 <    { Now, traverse through the SQL string, character by character,
559 <     picking out the parameters and formatting correctly for InterBase }
560 <    while (i <= iLenSQL) do begin
561 <      { Get the current token and a look-ahead }
562 <      cCurChar := sSQL[i];
563 <      if i = iLenSQL then
564 <        cNextChar := #0
565 <      else
566 <        cNextChar := sSQL[i + 1];
567 <      { Now act based on the current state }
568 <      case iCurState of
569 <        DefaultState:
570 <        begin
571 <          case cCurChar of
572 <            '''', '"':
573 <            begin
574 <              cQuoteChar := cCurChar;
575 <              iCurState := QuoteState;
576 <            end;
577 <            '?', ':':
578 <            begin
579 <              iCurState := ParamState;
580 <              AddToProcessedSQL('?');
581 <            end;
582 <            '/': if (cNextChar = '*') then
583 <            begin
584 <              AddToProcessedSQL(cCurChar);
585 <              Inc(i);
586 <              iCurState := CommentState;
587 <            end;
588 <            '[':
589 <            begin
590 <              AddToProcessedSQL(cCurChar);
591 <              Inc(i);
592 <              iCurState := ArrayDimState;
593 <            end;
594 <          end;
595 <        end;
596 <
597 <        ArrayDimState:
598 <        begin
599 <          case cCurChar of
600 <          ':',',','0'..'9',' ',#9,#10,#13:
601 <            begin
602 <              AddToProcessedSQL(cCurChar);
603 <              Inc(i);
604 <            end;
605 <          else
606 <            begin
607 <              AddToProcessedSQL(cCurChar);
608 <              Inc(i);
609 <              iCurState := DefaultState;
610 <            end;
611 <          end;
612 <        end;
666 > var slNames: TStrings;
667  
668 <        CommentState:
669 <        begin
670 <          if (cNextChar = #0) then
671 <            IBError(ibxeSQLParseError, [SEOFInComment])
672 <          else if (cCurChar = '*') then begin
619 <            if (cNextChar = '/') then
620 <              iCurState := DefaultState;
621 <          end;
622 <        end;
623 <        QuoteState: begin
624 <          if cNextChar = #0 then
625 <            IBError(ibxeSQLParseError, [SEOFInString])
626 <          else if (cCurChar = cQuoteChar) then begin
627 <            if (cNextChar = cQuoteChar) then begin
628 <              AddToProcessedSQL(cCurChar);
629 <              Inc(i);
630 <            end else
631 <              iCurState := DefaultState;
632 <          end;
633 <        end;
634 <        ParamState:
635 <        begin
636 <          { collect the name of the parameter }
637 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
638 <          if iCurParamState = ParamDefaultState then
639 <          begin
640 <            if cCurChar = '"' then
641 <              iCurParamState := ParamQuoteState
642 <            else
643 <            {$endif}
644 <            if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
645 <                sParamName := sParamName + cCurChar
646 <            else if GenerateParamNames then
647 <            begin
648 <              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
649 <              Inc(iParamSuffix);
650 <              iCurState := DefaultState;
651 <              slNames.AddObject(sParamName,self); //Note local convention
652 <                                                  //add pointer to self to mark entry
653 <              sParamName := '';
654 <            end
655 <            else
656 <              IBError(ibxeSQLParseError, [SParamNameExpected]);
657 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
658 <          end
659 <          else begin
660 <            { determine if Quoted parameter name is finished }
661 <            if cCurChar = '"' then
662 <            begin
663 <              Inc(i);
664 <              slNames.Add(sParamName);
665 <              SParamName := '';
666 <              iCurParamState := ParamDefaultState;
667 <              iCurState := DefaultState;
668 <            end
669 <            else
670 <              sParamName := sParamName + cCurChar
671 <          end;
672 <          {$endif}
673 <          { determine if the unquoted parameter name is finished }
674 <          if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
675 <            (iCurState <> DefaultState) then
676 <          begin
677 <            if not (cNextChar in ['A'..'Z', 'a'..'z',
678 <                                  '0'..'9', '_', '$']) then begin
679 <              Inc(i);
680 <              iCurState := DefaultState;
681 <              slNames.Add(sParamName);
682 <              sParamName := '';
683 <            end;
684 <          end;
685 <        end;
686 <      end;
687 <      if (iCurState <> ParamState) and (i <= iLenSQL) then
688 <        AddToProcessedSQL(sSQL[i]);
689 <      Inc(i);
690 <    end;
691 <    AddToProcessedSQL(#0);
692 <    sProcessedSQL := strpas(PAnsiChar(StrBuffer));
668 >  procedure SetColumnNames(slNames: TStrings);
669 >  var i, j: integer;
670 >      found: boolean;
671 >  begin
672 >    found := false;
673      SetCount(slNames.Count);
674      for i := 0 to slNames.Count - 1 do
675      begin
# Line 710 | Line 690 | begin
690          Column[i].UniqueName := not found;
691        end;
692      end;
693 +  end;
694 +
695 + begin
696 +  if not IsInputDataArea then
697 +    IBError(ibxeNotPermitted,[nil]);
698 +
699 +  slNames := TStringList.Create;
700 +  try
701 +    sProcessedSQL := TSQLParamProcessor.Execute(sSQL,GenerateParamNames,slNames);
702 +    SetColumnNames(slNames);
703    finally
704      slNames.Free;
715    FreeMem(StrBuffer);
705    end;
706   end;
707  
# Line 726 | Line 715 | var
715    s: AnsiString;
716    i: Integer;
717   begin
718 <  {$ifdef UseCaseInSensitiveParamName}
719 <   s := AnsiUpperCase(Idx);
720 <  {$else}
718 >  if not IsInputDataArea or not CaseSensitiveParams then
719 >   s := AnsiUpperCase(Idx)
720 >  else
721     s := Idx;
722 <  {$endif}
722 >
723    for i := 0 to Count - 1 do
724      if Column[i].Name = s then
725      begin
# Line 762 | Line 751 | end;
751  
752   procedure TSQLVarData.SetName(AValue: AnsiString);
753   begin
754 <  if FName = AValue then Exit;
766 <  {$ifdef UseCaseInSensitiveParamName}
767 <  if Parent.IsInputDataArea then
754 >  if not Parent.IsInputDataArea or not Parent.CaseSensitiveParams then
755      FName := AnsiUpperCase(AValue)
756    else
770  {$endif}
757      FName := AValue;
758   end;
759  
760 + procedure TSQLVarData.SetMetaSize(aValue: cardinal);
761 + begin
762 +  //Ignore
763 + end;
764 +
765 + procedure TSQLVarData.SaveMetaData;
766 + begin
767 +  FColMetaData := TSQLParamMetaData.Create(self);
768 + end;
769 +
770   constructor TSQLVarData.Create(aParent: TSQLDataArea; aIndex: integer);
771   begin
772    inherited Create;
# Line 787 | Line 783 | begin
783     a zero byte when the string is empty, neatly avoiding a nil pointer error.}
784  
785    FVarString := aValue;
786 <  SQLType := SQL_TEXT;
786 >  if SQLType = SQL_BLOB then
787 >    SetMetaSize(GetAttachment.GetInlineBlobLimit);
788 >  SQLType := GetDefaultTextSQLType;
789 >  Scale := 0;
790    SetSQLData(PByte(PAnsiChar(FVarString)),Length(aValue));
791   end;
792  
# Line 802 | Line 801 | begin
801    FVarString := '';
802   end;
803  
804 + function TSQLVarData.getColMetadata: IParamMetaData;
805 + begin
806 +  Result := FColMetaData;
807 + end;
808 +
809   procedure TSQLVarData.Initialize;
810  
811    function FindVarByName(idx: AnsiString; limit: integer): TSQLVarData;
# Line 948 | Line 952 | begin
952        result := Value;
953   end;
954  
955 + function TSQLDataItem.GetDateFormatStr(IncludeTime: boolean): AnsiString;
956 + begin
957 +  {$IF declared(DefaultFormatSettings)}
958 +  with DefaultFormatSettings do
959 +  {$ELSE}
960 +  {$IF declared(FormatSettings)}
961 +  with FormatSettings do
962 +  {$IFEND}
963 +  {$IFEND}
964 +  case GetSQLDialect of
965 +    1:
966 +      if IncludeTime then
967 +        result := ShortDateFormat + ' ' + LongTimeFormat
968 +      else
969 +        result := ShortDateFormat;
970 +    3:
971 +      result := ShortDateFormat;
972 +  end;
973 + end;
974 +
975 + function TSQLDataItem.GetTimeFormatStr: AnsiString;
976 + begin
977 +  {$IF declared(DefaultFormatSettings)}
978 +  with DefaultFormatSettings do
979 +  {$ELSE}
980 +  {$IF declared(FormatSettings)}
981 +  with FormatSettings do
982 +  {$IFEND}
983 +  {$IFEND}
984 +    Result := 'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';;
985 + end;
986 +
987 + function TSQLDataItem.GetTimestampFormatStr: AnsiString;
988 + begin
989 +  {$IF declared(DefaultFormatSettings)}
990 +  with DefaultFormatSettings do
991 +  {$ELSE}
992 +  {$IF declared(FormatSettings)}
993 +  with FormatSettings do
994 +  {$IFEND}
995 +  {$IFEND}
996 +    Result := ShortDateFormat + ' ' +  'hh' + TimeSeparator + 'nn' + TimeSeparator + 'ss' + '.zzzz';
997 + end;
998 +
999   procedure TSQLDataItem.SetAsInteger(AValue: Integer);
1000   begin
1001    SetAsLong(aValue);
1002   end;
1003  
1004 + procedure TSQLDataItem.InternalGetAsDateTime(var aDateTime: TDateTime;
1005 +  var dstOffset: smallint; var aTimezone: AnsiString;
1006 +  var aTimeZoneID: TFBTimeZoneID);
1007 + begin
1008 +  CheckActive;
1009 +  aDateTime := 0;
1010 +  dstOffset := 0;
1011 +  aTimezone := '';
1012 +  aTimeZoneID := TimeZoneID_GMT;
1013 +  if not IsNull then
1014 +    with FFirebirdClientAPI do
1015 +    case SQLType of
1016 +      SQL_TEXT, SQL_VARYING:
1017 +        if not ParseDateTimeTZString(AsString,aDateTime,aTimeZone) then
1018 +          IBError(ibxeInvalidDataConversion, [nil]);
1019 +      SQL_TYPE_DATE:
1020 +        aDateTime := SQLDecodeDate(SQLData);
1021 +      SQL_TYPE_TIME:
1022 +        aDateTime := SQLDecodeTime(SQLData);
1023 +      SQL_TIMESTAMP:
1024 +        aDateTime := SQLDecodeDateTime(SQLData);
1025 +      SQL_TIMESTAMP_TZ:
1026 +        begin
1027 +          GetTimeZoneServices.DecodeTimestampTZ(SQLData,aDateTime,dstOffset,aTimeZone);
1028 +          aTimeZoneID := PISC_TIMESTAMP_TZ(SQLData)^.time_zone;
1029 +        end;
1030 +      SQL_TIMESTAMP_TZ_EX:
1031 +      begin
1032 +        GetTimeZoneServices.DecodeTimestampTZEx(SQLData,aDateTime,dstOffset,aTimeZone);
1033 +        aTimeZoneID := PISC_TIMESTAMP_TZ_EX(SQLData)^.time_zone;
1034 +      end;
1035 +      SQL_TIME_TZ:
1036 +        with GetTimeZoneServices do
1037 +        begin
1038 +          DecodeTimeTZ(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1039 +          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1040 +        end;
1041 +      SQL_TIME_TZ_EX:
1042 +        with GetTimeZoneServices do
1043 +        begin
1044 +          DecodeTimeTZEx(SQLData,GetTimeTZDate,aDateTime,dstOffset,aTimeZone);
1045 +          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1046 +        end;
1047 +      else
1048 +        IBError(ibxeInvalidDataConversion, [nil]);
1049 +    end;
1050 + end;
1051 +
1052   function TSQLDataItem.AdjustScaleFromCurrency(Value: Currency; aScale: Integer
1053    ): Int64;
1054   var
# Line 1001 | Line 1097 | begin
1097    end
1098    else
1099      result := trunc(Value);
1100 + //  writeln('Adjusted ',Value,' to ',Result);
1101   end;
1102  
1103   procedure TSQLDataItem.CheckActive;
# Line 1008 | Line 1105 | begin
1105    //Do nothing by default
1106   end;
1107  
1108 + procedure TSQLDataItem.CheckTZSupport;
1109 + begin
1110 +  if not FFirebirdClientAPI.HasTimeZoneSupport then
1111 +    IBError(ibxeNoTimezoneSupport,[]);
1112 + end;
1113 +
1114 + function TSQLDataItem.GetTimeZoneServices: IExTimeZoneServices;
1115 + begin
1116 +  if FTimeZoneServices = nil then
1117 +  begin
1118 +    if not GetAttachment.HasTimeZoneSupport then
1119 +      IBError(ibxeNoTimezoneSupport,[]);
1120 +    GetAttachment.GetTimeZoneServices.QueryInterface(IExTimeZoneServices,FTimeZoneServices);
1121 +  end;
1122 +  Result := FTimeZoneServices;
1123 + end;
1124 +
1125   procedure TSQLDataItem.Changed;
1126   begin
1127    //Do nothing by default
# Line 1046 | Line 1160 | begin
1160     //Do nothing by default
1161   end;
1162  
1163 + constructor TSQLDataItem.Create(api: TFBClientAPI);
1164 + begin
1165 +  inherited Create;
1166 +  FFirebirdClientAPI := api;
1167 + end;
1168 +
1169   function TSQLDataItem.GetSQLTypeName: AnsiString;
1170   begin
1171    Result := GetSQLTypeName(GetSQLType);
1172   end;
1173  
1174 < class function TSQLDataItem.GetSQLTypeName(SQLType: short): AnsiString;
1174 > class function TSQLDataItem.GetSQLTypeName(SQLType: cardinal): AnsiString;
1175   begin
1176    Result := 'Unknown';
1177    case SQLType of
# Line 1062 | Line 1182 | begin
1182    SQL_LONG:             Result := 'SQL_LONG';
1183    SQL_SHORT:            Result := 'SQL_SHORT';
1184    SQL_TIMESTAMP:        Result := 'SQL_TIMESTAMP';
1185 +  SQL_TIMESTAMP_TZ:     Result := 'SQL_TIMESTAMP_TZ';
1186 +  SQL_TIMESTAMP_TZ_EX:  Result := 'SQL_TIMESTAMP_TZ_EX';
1187    SQL_BLOB:             Result := 'SQL_BLOB';
1188    SQL_D_FLOAT:          Result := 'SQL_D_FLOAT';
1189    SQL_ARRAY:            Result := 'SQL_ARRAY';
# Line 1069 | Line 1191 | begin
1191    SQL_TYPE_TIME:        Result := 'SQL_TYPE_TIME';
1192    SQL_TYPE_DATE:        Result := 'SQL_TYPE_DATE';
1193    SQL_INT64:            Result := 'SQL_INT64';
1194 +  SQL_TIME_TZ:          Result := 'SQL_TIME_TZ';
1195 +  SQL_TIME_TZ_EX:       Result := 'SQL_TIME_TZ_EX';
1196 +  SQL_DEC_FIXED:        Result := 'SQL_DEC_FIXED';
1197 +  SQL_DEC16:            Result := 'SQL_DEC16';
1198 +  SQL_DEC34:            Result := 'SQL_DEC34';
1199 +  SQL_INT128:           Result := 'SQL_INT128';
1200 +  SQL_NULL:             Result := 'SQL_NULL';
1201 +  SQL_BOOLEAN:          Result := 'SQL_BOOLEAN';
1202    end;
1203   end;
1204  
1205 + function TSQLDataItem.GetStrDataLength: short;
1206 + begin
1207 +  with FFirebirdClientAPI do
1208 +  if SQLType = SQL_VARYING then
1209 +    Result := DecodeInteger(SQLData, 2)
1210 +  else
1211 +    Result := DataLength;
1212 + end;
1213 +
1214   function TSQLDataItem.GetAsBoolean: boolean;
1215   begin
1216    CheckActive;
# Line 1111 | Line 1250 | begin
1250            result := AdjustScaleToCurrency(PInt64(SQLData)^,
1251                                        Scale);
1252          SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1253 <          result := Trunc(AsDouble);
1253 >          result := Round(AsDouble);
1254 >
1255 >        SQL_DEC_FIXED,
1256 >        SQL_DEC16,
1257 >        SQL_DEC34,
1258 >        SQL_INT128:
1259 >          if not BCDToCurr(GetAsBCD,Result) then
1260 >            IBError(ibxeInvalidDataConversion, [nil]);
1261 >
1262          else
1263            IBError(ibxeInvalidDataConversion, [nil]);
1264        end;
# Line 1141 | Line 1288 | begin
1288          result := AdjustScaleToInt64(PInt64(SQLData)^,
1289                                      Scale);
1290        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1291 <        result := Trunc(AsDouble);
1291 >        result := Round(AsDouble);
1292        else
1293          IBError(ibxeInvalidDataConversion, [nil]);
1294      end;
1295   end;
1296  
1297   function TSQLDataItem.GetAsDateTime: TDateTime;
1298 + var aTimezone: AnsiString;
1299 +    aTimeZoneID: TFBTimeZoneID;
1300 +    dstOffset: smallint;
1301 + begin
1302 +  InternalGetAsDateTime(Result,dstOffset,aTimeZone,aTimeZoneID);
1303 + end;
1304 +
1305 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime;
1306 +  var dstOffset: smallint; var aTimezone: AnsiString);
1307 + var aTimeZoneID: TFBTimeZoneID;
1308 + begin
1309 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1310 + end;
1311 +
1312 + procedure TSQLDataItem.GetAsDateTime(var aDateTime: TDateTime; var dstOffset: smallint;
1313 +  var aTimezoneID: TFBTimeZoneID);
1314 + var aTimezone: AnsiString;
1315 + begin
1316 +  InternalGetAsDateTime(aDateTime,dstOffset,aTimeZone,aTimeZoneID);
1317 + end;
1318 +
1319 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1320 +  var aTimezoneID: TFBTimeZoneID; OnDate: TDateTime);
1321 + var aTimeZone: AnsiString;
1322   begin
1323    CheckActive;
1324 <  result := 0;
1324 >  aTime := 0;
1325 >  dstOffset := 0;
1326    if not IsNull then
1327 <    with FirebirdClientAPI do
1327 >    with FFirebirdClientAPI do
1328      case SQLType of
1329 <      SQL_TEXT, SQL_VARYING: begin
1330 <        try
1331 <          result := StrToDate(AsString);
1332 <        except
1161 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
1329 >      SQL_TIME_TZ:
1330 >        begin
1331 >          GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1332 >          aTimeZoneID := PISC_TIME_TZ(SQLData)^.time_zone;
1333          end;
1334 +      SQL_TIME_TZ_EX:
1335 +        begin
1336 +          GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1337 +          aTimeZoneID := PISC_TIME_TZ_EX(SQLData)^.time_zone;
1338 +        end;
1339 +    else
1340 +      IBError(ibxeInvalidDataConversion, [nil]);
1341 +    end;
1342 + end;
1343 +
1344 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1345 +  var aTimezone: AnsiString; OnDate: TDateTime);
1346 + begin
1347 +  CheckActive;
1348 +  aTime := 0;
1349 +  dstOffset := 0;
1350 +  if not IsNull then
1351 +    with FFirebirdClientAPI do
1352 +    case SQLType of
1353 +      SQL_TIME_TZ:
1354 +        GetTimeZoneServices.DecodeTimeTZ(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1355 +      SQL_TIME_TZ_EX:
1356 +        GetTimeZoneServices.DecodeTimeTZEx(SQLData,OnDate,aTime,dstOffset,aTimeZone);
1357 +    else
1358 +      IBError(ibxeInvalidDataConversion, [nil]);
1359 +    end;
1360 + end;
1361 +
1362 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1363 +  var aTimezoneID: TFBTimeZoneID);
1364 + begin
1365 +  GetAsTime(aTime,dstOffset,aTimeZoneID,GetTimeZoneServices.GetTimeTZDate);
1366 + end;
1367 +
1368 + procedure TSQLDataItem.GetAsTime(var aTime: TDateTime; var dstOffset: smallint;
1369 +  var aTimezone: AnsiString);
1370 + begin
1371 +  GetAsTime(aTime,dstOffset,aTimeZone,GetTimeZoneServices.GetTimeTZDate);
1372 + end;
1373 +
1374 + function TSQLDataItem.GetAsUTCDateTime: TDateTime;
1375 + var aTimezone: AnsiString;
1376 + begin
1377 +  CheckActive;
1378 +  result := 0;
1379 +  aTimezone := '';
1380 +  if not IsNull then
1381 +    with FFirebirdClientAPI do
1382 +    case SQLType of
1383 +      SQL_TEXT, SQL_VARYING:
1384 +      begin
1385 +        if not ParseDateTimeTZString(AsString,Result,aTimeZone) then
1386 +          IBError(ibxeInvalidDataConversion, [nil]);
1387 +        Result := GetTimeZoneServices.LocalTimeToGMT(Result,aTimeZone);
1388        end;
1389        SQL_TYPE_DATE:
1390          result := SQLDecodeDate(SQLData);
1391 <      SQL_TYPE_TIME:
1391 >      SQL_TYPE_TIME,
1392 >      SQL_TIME_TZ,
1393 >      SQL_TIME_TZ_EX:
1394          result := SQLDecodeTime(SQLData);
1395 <      SQL_TIMESTAMP:
1395 >      SQL_TIMESTAMP,
1396 >      SQL_TIMESTAMP_TZ,
1397 >      SQL_TIMESTAMP_TZ_EX:
1398          result := SQLDecodeDateTime(SQLData);
1399        else
1400          IBError(ibxeInvalidDataConversion, [nil]);
1401 <    end;
1401 >      end;
1402   end;
1403  
1404   function TSQLDataItem.GetAsDouble: Double;
# Line 1197 | Line 1426 | begin
1426          result := PFloat(SQLData)^;
1427        SQL_DOUBLE, SQL_D_FLOAT:
1428          result := PDouble(SQLData)^;
1429 +      SQL_DEC_FIXED,
1430 +      SQL_DEC16,
1431 +      SQL_DEC34,
1432 +      SQL_INT128:
1433 +        Result := BCDToDouble(GetAsBCD);
1434        else
1435          IBError(ibxeInvalidDataConversion, [nil]);
1436      end;
# Line 1233 | Line 1467 | begin
1467          end;
1468        end;
1469        SQL_SHORT:
1470 <        result := Trunc(AdjustScale(Int64(PShort(SQLData)^),
1470 >        result := Round(AdjustScale(Int64(PShort(SQLData)^),
1471                                      Scale));
1472        SQL_LONG:
1473 <        result := Trunc(AdjustScale(Int64(PLong(SQLData)^),
1473 >        result := Round(AdjustScale(Int64(PLong(SQLData)^),
1474                                      Scale));
1475        SQL_INT64:
1476 <        result := Trunc(AdjustScale(PInt64(SQLData)^, Scale));
1476 >        result := Round(AdjustScale(PInt64(SQLData)^, Scale));
1477        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1478 <        result := Trunc(AsDouble);
1478 >        result := Round(AsDouble);
1479 >      SQL_DEC_FIXED,
1480 >      SQL_DEC16,
1481 >      SQL_DEC34,
1482 >      SQL_INT128:
1483 >        Result := BCDToInteger(GetAsBCD);
1484        else
1485 <        IBError(ibxeInvalidDataConversion, [nil]);
1485 >        IBError(ibxeInvalidDataConversion, [GetSQLTypeName]);
1486      end;
1487   end;
1488  
# Line 1281 | Line 1520 | begin
1520    end;
1521   end;
1522  
1523 + {Copied from LazUTF8}
1524 +
1525 + function UTF8CodepointSizeFull(p: PAnsiChar): integer;
1526 + const TopBitSetMask   = $80; {%10000000}
1527 +      Top2BitsSetMask = $C0; {%11000000}
1528 +      Top3BitsSetMask = $E0; {%11100000}
1529 +      Top4BitsSetMask = $F0; {%11110000}
1530 +      Top5BitsSetMask = $F8; {%11111000}
1531 + begin
1532 +  case p^ of
1533 +  #0..#191: // %11000000
1534 +    // regular single byte character (#0 is a character, this is Pascal ;)
1535 +    Result:=1;
1536 +  #192..#223: // p^ and %11100000 = %11000000
1537 +    begin
1538 +      // could be 2 byte character
1539 +      if (ord(p[1]) and Top2BitsSetMask) = TopBitSetMask then
1540 +        Result:=2
1541 +      else
1542 +        Result:=1;
1543 +    end;
1544 +  #224..#239: // p^ and %11110000 = %11100000
1545 +    begin
1546 +      // could be 3 byte character
1547 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1548 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask) then
1549 +        Result:=3
1550 +      else
1551 +        Result:=1;
1552 +    end;
1553 +  #240..#247: // p^ and %11111000 = %11110000
1554 +    begin
1555 +      // could be 4 byte character
1556 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1557 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask)
1558 +      and ((ord(p[3]) and Top2BitsSetMask) = TopBitSetMask) then
1559 +        Result:=4
1560 +      else
1561 +        Result:=1;
1562 +    end;
1563 +  else
1564 +    Result:=1;
1565 +  end;
1566 + end;
1567 +
1568 + {Returns the byte length of a UTF8 string with a fixed charwidth}
1569 +
1570 + function GetStrLen(p: PAnsiChar; FieldWidth, MaxDataLength: cardinal): integer;
1571 + var i: integer;
1572 +    cplen: integer;
1573 +    s: AnsiString;
1574 + begin
1575 +  Result := 0;
1576 +  s := strpas(p);
1577 +  for i := 1 to FieldWidth do
1578 +  begin
1579 +    cplen := UTF8CodepointSizeFull(p);
1580 +    Inc(p,cplen);
1581 +    Inc(Result,cplen);
1582 +    if Result >= MaxDataLength then
1583 +    begin
1584 +      Result := MaxDataLength;
1585 +      Exit;
1586 +    end;
1587 +  end;
1588 + end;
1589  
1590   function TSQLDataItem.GetAsString: AnsiString;
1591   var
1592    sz: PByte;
1593    str_len: Integer;
1594    rs: RawByteString;
1595 +  aTimeZone: AnsiString;
1596 +  aDateTime: TDateTime;
1597 +  dstOffset: smallint;
1598   begin
1599    CheckActive;
1600    result := '';
1601    { Check null, if so return a default string }
1602    if not IsNull then
1603 <  with FirebirdClientAPI do
1603 >  with FFirebirdClientAPI do
1604      case SQLType of
1605        SQL_BOOLEAN:
1606          if AsBoolean then
# Line 1304 | Line 1612 | begin
1612        begin
1613          sz := SQLData;
1614          if (SQLType = SQL_TEXT) then
1615 <          str_len := DataLength
1615 >        begin
1616 >          if GetCodePage = cp_utf8 then
1617 >            str_len := GetStrLen(PAnsiChar(sz),GetSize div GetCharSetWidth,DataLength)
1618 >          else
1619 >            str_len := DataLength
1620 >        end
1621          else begin
1622 <          str_len := DecodeInteger(SQLData, 2);
1622 >          str_len := DecodeInteger(sz, 2);
1623            Inc(sz, 2);
1624          end;
1625          SetString(rs, PAnsiChar(sz), str_len);
1626          SetCodePage(rs,GetCodePage,false);
1627 <        if (SQLType = SQL_TEXT) and (GetCharSetID <> 1) then
1315 <          Result := TrimRight(rs)
1316 <        else
1317 <          Result := rs
1627 >        Result := rs;
1628        end;
1629 +
1630        SQL_TYPE_DATE:
1631 <        case GetSQLDialect of
1321 <          1 : result := DateTimeToStr(AsDateTime);
1322 <          3 : result := DateToStr(AsDateTime);
1323 <        end;
1324 <      SQL_TYPE_TIME :
1325 <        result := TimeToStr(AsDateTime);
1631 >        Result := DateToStr(GetAsDateTime);
1632        SQL_TIMESTAMP:
1633 <      {$IF declared(DefaultFormatSettings)}
1634 <      with DefaultFormatSettings do
1635 <      {$ELSE}
1636 <      {$IF declared(FormatSettings)}
1637 <      with FormatSettings do
1638 <      {$IFEND}
1639 <      {$IFEND}
1640 <        result := FormatDateTime(ShortDateFormat + ' ' +
1641 <                            LongTimeFormat+'.zzz',AsDateTime);
1633 >        Result := FBFormatDateTime(GetTimestampFormatStr,GetAsDateTime);
1634 >      SQL_TYPE_TIME:
1635 >        Result := FBFormatDateTime(GetTimeFormatStr,GetAsDateTime);
1636 >      SQL_TIMESTAMP_TZ,
1637 >      SQL_TIMESTAMP_TZ_EX:
1638 >        with GetAttachment.GetTimeZoneServices do
1639 >        begin
1640 >          if GetTZTextOption = tzGMT then
1641 >            Result := FBFormatDateTime(GetTimestampFormatStr,GetAsUTCDateTime)
1642 >          else
1643 >          begin
1644 >            GetAsDateTime(aDateTime,dstOffset,aTimeZone);
1645 >            if GetTZTextOption = tzOffset then
1646 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1647 >            else
1648 >              Result := FBFormatDateTime(GetTimestampFormatStr,aDateTime) + ' ' + aTimeZone;
1649 >          end;
1650 >        end;
1651 >      SQL_TIME_TZ,
1652 >      SQL_TIME_TZ_EX:
1653 >        with GetAttachment.GetTimeZoneServices do
1654 >        begin
1655 >          if GetTZTextOption = tzGMT then
1656 >             Result := FBFormatDateTime(GetTimeFormatStr,GetAsUTCDateTime)
1657 >          else
1658 >          begin
1659 >            GetAsTime(aDateTime,dstOffset,aTimeZone,GetTimeTZDate);
1660 >            if GetTZTextOption = tzOffset then
1661 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + FormatTimeZoneOffset(dstOffset)
1662 >            else
1663 >              Result := FBFormatDateTime(GetTimeFormatStr,aDateTime) + ' ' + aTimeZone;
1664 >          end;
1665 >        end;
1666 >
1667        SQL_SHORT, SQL_LONG:
1668          if Scale = 0 then
1669            result := IntToStr(AsLong)
# Line 1349 | Line 1680 | begin
1680            result := FloatToStr(AsDouble);
1681        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
1682          result := FloatToStr(AsDouble);
1683 +
1684 +      SQL_DEC16,
1685 +      SQL_DEC34:
1686 +        result := BCDToStr(GetAsBCD);
1687 +
1688 +      SQL_DEC_FIXED,
1689 +      SQL_INT128:
1690 +        result := Int128ToStr(SQLData,scale);
1691 +
1692        else
1693          IBError(ibxeInvalidDataConversion, [nil]);
1694      end;
# Line 1360 | Line 1700 | begin
1700    Result := false;
1701   end;
1702  
1703 < function TSQLDataItem.getIsNullable: boolean;
1703 > function TSQLDataItem.GetIsNullable: boolean;
1704   begin
1705    CheckActive;
1706    Result := false;
1707   end;
1708  
1709   function TSQLDataItem.GetAsVariant: Variant;
1710 + var ts: TDateTime;
1711 +  dstOffset: smallint;
1712 +    timezone: AnsiString;
1713   begin
1714    CheckActive;
1715    if IsNull then
# Line 1380 | Line 1723 | begin
1723          result := AsString;
1724        SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
1725          result := AsDateTime;
1726 +      SQL_TIMESTAMP_TZ,
1727 +      SQL_TIME_TZ,
1728 +      SQL_TIMESTAMP_TZ_EX,
1729 +      SQL_TIME_TZ_EX:
1730 +        begin
1731 +          GetAsDateTime(ts,dstOffset,timezone);
1732 +          result := VarArrayOf([ts,dstOffset,timezone]);
1733 +        end;
1734        SQL_SHORT, SQL_LONG:
1735          if Scale = 0 then
1736            result := AsLong
# Line 1398 | Line 1749 | begin
1749          result := AsDouble;
1750        SQL_BOOLEAN:
1751          result := AsBoolean;
1752 +      SQL_DEC_FIXED,
1753 +      SQL_DEC16,
1754 +      SQL_DEC34,
1755 +      SQL_INT128:
1756 +        result := VarFmtBCDCreate(GetAsBcd);
1757        else
1758          IBError(ibxeInvalidDataConversion, [nil]);
1759      end;
# Line 1408 | Line 1764 | begin
1764    Result := false;
1765   end;
1766  
1767 + function TSQLDataItem.GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats
1768 +  ): integer;
1769 + begin
1770 +  case DateTimeFormat of
1771 +  dfTimestamp:
1772 +    Result := Length(GetTimestampFormatStr);
1773 +  dfDateTime:
1774 +    Result := Length(GetDateFormatStr(true));
1775 +  dfTime:
1776 +    Result := Length(GetTimeFormatStr);
1777 +  dfTimestampTZ:
1778 +    Result := Length(GetTimestampFormatStr) + 6; {assume time offset format}
1779 +  dfTimeTZ:
1780 +    Result := Length(GetTimeFormatStr)+ 6;
1781 +  else
1782 +    Result := 0;
1783 +  end;end;
1784 +
1785 + function TSQLDataItem.GetAsBCD: tBCD;
1786 +
1787 + begin
1788 +  CheckActive;
1789 +  if IsNull then
1790 +   with Result do
1791 +   begin
1792 +     FillChar(Result,sizeof(Result),0);
1793 +     Precision := 1;
1794 +     exit;
1795 +   end;
1796 +
1797 +  case SQLType of
1798 +  SQL_DEC16,
1799 +  SQL_DEC34:
1800 +    with FFirebirdClientAPI do
1801 +      Result := SQLDecFloatDecode(SQLType,  SQLData);
1802 +
1803 +  SQL_DEC_FIXED,
1804 +  SQL_INT128:
1805 +    with FFirebirdClientAPI do
1806 +      Result := StrToBCD(Int128ToStr(SQLData,scale));
1807 +  else
1808 +    if not CurrToBCD(GetAsCurrency,Result) then
1809 +      IBError(ibxeBadBCDConversion,[]);
1810 +  end;
1811 + end;
1812 +
1813  
1814   procedure TSQLDataItem.SetIsNull(Value: Boolean);
1815   begin
# Line 1471 | Line 1873 | begin
1873  
1874    SQLType := SQL_TYPE_DATE;
1875    DataLength := SizeOf(ISC_DATE);
1876 <  with FirebirdClientAPI do
1876 >  with FFirebirdClientAPI do
1877      SQLEncodeDate(Value,SQLData);
1878    Changed;
1879   end;
# Line 1491 | Line 1893 | begin
1893  
1894    SQLType := SQL_TYPE_TIME;
1895    DataLength := SizeOf(ISC_TIME);
1896 <  with FirebirdClientAPI do
1896 >  with FFirebirdClientAPI do
1897      SQLEncodeTime(Value,SQLData);
1898    Changed;
1899   end;
1900  
1901 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
1902 + begin
1903 +  CheckActive;
1904 +  CheckTZSupport;
1905 +  if GetSQLDialect < 3 then
1906 +  begin
1907 +    AsDateTime := aValue;
1908 +    exit;
1909 +  end;
1910 +
1911 +  Changing;
1912 +  if IsNullable then
1913 +    IsNull := False;
1914 +
1915 +  SQLType := SQL_TIME_TZ;
1916 +  DataLength := SizeOf(ISC_TIME_TZ);
1917 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZoneID,OnDate,SQLData);
1918 +  Changed;
1919 + end;
1920 +
1921 + procedure TSQLDataItem.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
1922 + begin
1923 +  CheckActive;
1924 +  CheckTZSupport;
1925 +  if GetSQLDialect < 3 then
1926 +  begin
1927 +    AsDateTime := aValue;
1928 +    exit;
1929 +  end;
1930 +
1931 +  Changing;
1932 +  if IsNullable then
1933 +    IsNull := False;
1934 +
1935 +  SQLType := SQL_TIME_TZ;
1936 +  DataLength := SizeOf(ISC_TIME_TZ);
1937 +  GetTimeZoneServices.EncodeTimeTZ(aValue, aTimeZone,OnDate,SQLData);
1938 +  Changed;
1939 + end;
1940 +
1941   procedure TSQLDataItem.SetAsDateTime(Value: TDateTime);
1942   begin
1943    CheckActive;
# Line 1505 | Line 1947 | begin
1947    Changing;
1948    SQLType := SQL_TIMESTAMP;
1949    DataLength := SizeOf(ISC_TIME) + sizeof(ISC_DATE);
1950 <  with FirebirdClientAPI do
1950 >  with FFirebirdClientAPI do
1951      SQLEncodeDateTime(Value,SQLData);
1952    Changed;
1953   end;
1954  
1955 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime;
1956 +  aTimeZoneID: TFBTimeZoneID);
1957 + begin
1958 +  CheckActive;
1959 +  CheckTZSupport;
1960 +  if IsNullable then
1961 +    IsNull := False;
1962 +
1963 +  Changing;
1964 +  SQLType := SQL_TIMESTAMP_TZ;
1965 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1966 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZoneID,SQLData);
1967 +  Changed;
1968 + end;
1969 +
1970 + procedure TSQLDataItem.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString
1971 +  );
1972 + begin
1973 +  CheckActive;
1974 +  CheckTZSupport;
1975 +  if IsNullable then
1976 +    IsNull := False;
1977 +
1978 +  Changing;
1979 +  SQLType := SQL_TIMESTAMP_TZ;
1980 +  DataLength := SizeOf(ISC_TIMESTAMP_TZ);
1981 +  GetTimeZoneServices.EncodeTimestampTZ(aValue,aTimeZone,SQLData);
1982 +  Changed;
1983 + end;
1984 +
1985 + procedure TSQLDataItem.SetAsUTCDateTime(aUTCTime: TDateTime);
1986 + begin
1987 +  SetAsDateTime(aUTCTime,TimeZoneID_GMT);
1988 + end;
1989 +
1990   procedure TSQLDataItem.SetAsDouble(Value: Double);
1991   begin
1992    CheckActive;
# Line 1605 | Line 2082 | begin
2082    CheckActive;
2083    if VarIsNull(Value) then
2084      IsNull := True
2085 +  else
2086 +  if VarIsArray(Value) then {must be datetime plus timezone}
2087 +    SetAsDateTime(Value[0],AnsiString(Value[1]))
2088    else case VarType(Value) of
2089      varEmpty, varNull:
2090        IsNull := True;
# Line 1627 | 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  
# Line 1644 | Line 2129 | begin
2129    Changed;
2130   end;
2131  
2132 + procedure TSQLDataItem.SetAsBcd(aValue: tBCD);
2133 + var C: Currency;
2134 + begin
2135 +  CheckActive;
2136 +  Changing;
2137 +  if IsNullable then
2138 +    IsNull := False;
2139 +
2140 +
2141 +  with FFirebirdClientAPI do
2142 +  if aValue.Precision <= 16 then
2143 +  begin
2144 +    if not HasDecFloatSupport then
2145 +      IBError(ibxeDecFloatNotSupported,[]);
2146 +
2147 +    SQLType := SQL_DEC16;
2148 +    DataLength := 8;
2149 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2150 +  end
2151 +  else
2152 +  if aValue.Precision <= 34 then
2153 +  begin
2154 +    if not HasDecFloatSupport then
2155 +      IBError(ibxeDecFloatNotSupported,[]);
2156 +
2157 +    SQLType := SQL_DEC34;
2158 +    DataLength := 16;
2159 +    SQLDecFloatEncode(aValue,SQLType,SQLData);
2160 +  end
2161 +  else
2162 +  if aValue.Precision <= 38 then
2163 +  begin
2164 +    if not HasInt128Support then
2165 +      IBError(ibxeInt128NotSupported,[]);
2166 +
2167 +    SQLType := SQL_INT128;
2168 +    DataLength := 16;
2169 +    StrToInt128(scale,BcdToStr(aValue),SQLData);
2170 +  end
2171 +  else
2172 +    IBError(ibxeBCDOverflow,[BCDToStr(aValue)]);
2173 +
2174 +  Changed;
2175 + end;
2176 +
2177   procedure TSQLDataItem.SetAsBoolean(AValue: boolean);
2178   begin
2179    CheckActive;
# Line 1674 | Line 2204 | begin
2204      IBError(ibxeStatementNotPrepared, [nil]);
2205   end;
2206  
2207 + function TColumnMetaData.GetAttachment: IAttachment;
2208 + begin
2209 +  Result := GetStatement.GetAttachment;
2210 + end;
2211 +
2212   function TColumnMetaData.SQLData: PByte;
2213   begin
2214    Result := FIBXSQLVAR.SQLData;
# Line 1691 | Line 2226 | end;
2226  
2227   constructor TColumnMetaData.Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
2228   begin
2229 <  inherited Create;
2229 >  inherited Create(aIBXSQLVAR.GetStatement.GetAttachment.getFirebirdAPI as TFBClientAPI);
2230    FIBXSQLVAR := aIBXSQLVAR;
2231    FOwner := aOwner;
2232    FPrepareSeqNo := FIBXSQLVAR.Parent.PrepareSeqNo;
# Line 1778 | Line 2313 | end;
2313   function TColumnMetaData.GetSize: cardinal;
2314   begin
2315    CheckActive;
2316 <  result := FIBXSQLVAR.DataLength;
2316 >  result := FIBXSQLVAR.GetSize;
2317 > end;
2318 >
2319 > function TColumnMetaData.GetCharSetWidth: integer;
2320 > begin
2321 >  CheckActive;
2322 >  result := FIBXSQLVAR.GetCharSetWidth;
2323   end;
2324  
2325   function TColumnMetaData.GetArrayMetaData: IArrayMetaData;
# Line 1793 | Line 2334 | begin
2334    result := FIBXSQLVAR.GetBlobMetaData;
2335   end;
2336  
2337 + function TColumnMetaData.GetStatement: IStatement;
2338 + begin
2339 +  Result := FIBXSQLVAR.GetStatement;
2340 + end;
2341 +
2342 + function TColumnMetaData.GetTransaction: ITransaction;
2343 + begin
2344 +  Result := GetStatement.GetTransaction;
2345 + end;
2346 +
2347   { TIBSQLData }
2348  
2349   procedure TIBSQLData.CheckActive;
# Line 1812 | Line 2363 | begin
2363      IBError(ibxeBOF,[nil]);
2364   end;
2365  
2366 + function TIBSQLData.GetTransaction: ITransaction;
2367 + begin
2368 +  if FTransaction = nil then
2369 +    Result := inherited GetTransaction
2370 +  else
2371 +    Result := FTransaction;
2372 + end;
2373 +
2374   function TIBSQLData.GetIsNull: Boolean;
2375   begin
2376    CheckActive;
# Line 1855 | Line 2414 | end;
2414   { TSQLParam }
2415  
2416   procedure TSQLParam.InternalSetAsString(Value: AnsiString);
2417 +
2418 + procedure DoSetString;
2419 + begin
2420 +  Changing;
2421 +  FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
2422 +  Changed;
2423 + end;
2424 +
2425   var b: IBlob;
2426      dt: TDateTime;
2427 +    timezone: AnsiString;
2428 +    FloatValue: Double;
2429 +    Int64Value: Int64;
2430 +    BCDValue: TBCD;
2431 +    aScale: integer;
2432   begin
2433    CheckActive;
2434    if IsNullable then
2435      IsNull := False;
2436 <  case SQLTYPE of
2436 >  with FFirebirdClientAPI do
2437 >  case getColMetaData.SQLTYPE of
2438    SQL_BOOLEAN:
2439      if AnsiCompareText(Value,STrue) = 0 then
2440        AsBoolean := true
# Line 1872 | Line 2445 | begin
2445        IBError(ibxeInvalidDataConversion,[nil]);
2446  
2447    SQL_BLOB:
2448 +    if Length(Value) < GetAttachment.GetInlineBlobLimit then
2449 +      DoSetString
2450 +    else
2451      begin
2452        Changing;
2453        b := FIBXSQLVAR.CreateBlob;
# Line 1882 | Line 2458 | begin
2458  
2459    SQL_VARYING,
2460    SQL_TEXT:
2461 +    DoSetString;
2462 +
2463 +  SQL_SHORT,
2464 +  SQL_LONG,
2465 +  SQL_INT64:
2466 +    {If the string contains an integer then convert and set directly}
2467 +    if TryStrToInt64(Value,Int64Value) then
2468 +      SetAsInt64(Int64Value)
2469 +    else
2470 +    if getColMetaData.getScale = 0 then {integer expected but non-integer string}
2471      begin
2472 <      Changing;
2473 <      FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
2474 <      Changed;
2475 <    end;
2472 >      if TryStrToFloat(Value,FloatValue) then
2473 >        {truncate it if the column is limited to an integer}
2474 >        SetAsInt64(Trunc(FloatValue))
2475 >      else
2476 >        DoSetString;
2477 >    end
2478 >    else
2479 >    if TryStrToFloat(Value,FloatValue) then
2480 >    begin
2481 >      aScale := getColMetaData.getScale;
2482 >      {Set as int64 with adjusted scale}
2483 >      SetAsNumeric(AdjustScaleFromDouble(FloatValue,aScale),aScale)
2484 >    end
2485 >    else
2486 >      DoSetString;
2487 >
2488 >  SQL_DEC_FIXED,
2489 >  SQL_DEC16,
2490 >  SQL_DEC34,
2491 >  SQL_INT128:
2492 >    if TryStrToBCD(Value,BCDValue) then
2493 >      SetAsBCD(BCDValue)
2494 >    else
2495 >      DoSetString;
2496  
2497 <    SQL_SHORT,
2498 <    SQL_LONG,
2499 <    SQL_INT64:
2500 <      SetAsNumeric(AdjustScaleFromCurrency(StrToCurr(Value),GetScale),GetScale);
2501 <
2502 <    SQL_D_FLOAT,
2503 <    SQL_DOUBLE,
1898 <    SQL_FLOAT:
1899 <      SetAsDouble(StrToFloat(Value));
2497 >  SQL_D_FLOAT,
2498 >  SQL_DOUBLE,
2499 >  SQL_FLOAT:
2500 >    if TryStrToFloat(Value,FloatValue) then
2501 >      SetAsDouble(FloatValue)
2502 >    else
2503 >      DoSetString;
2504  
2505 <    SQL_TIMESTAMP:
2505 >  SQL_TIMESTAMP:
2506        if TryStrToDateTime(Value,dt) then
2507          SetAsDateTime(dt)
2508        else
2509 <        FIBXSQLVar.SetString(Value);
2509 >        DoSetString;
2510  
2511 <    SQL_TYPE_DATE:
2511 >  SQL_TYPE_DATE:
2512        if TryStrToDateTime(Value,dt) then
2513          SetAsDate(dt)
2514        else
2515 <        FIBXSQLVar.SetString(Value);
2515 >        DoSetString;
2516  
2517 <    SQL_TYPE_TIME:
2517 >  SQL_TYPE_TIME:
2518        if TryStrToDateTime(Value,dt) then
2519          SetAsTime(dt)
2520        else
2521 <        FIBXSQLVar.SetString(Value);
2521 >        DoSetString;
2522  
2523 <    else
2524 <      IBError(ibxeInvalidDataConversion,[nil]);
2523 >  SQL_TIMESTAMP_TZ,
2524 >  SQL_TIMESTAMP_TZ_EX:
2525 >      if ParseDateTimeTZString(value,dt,timezone) then
2526 >        SetAsDateTime(dt,timezone)
2527 >      else
2528 >        DoSetString;
2529 >
2530 >  SQL_TIME_TZ,
2531 >  SQL_TIME_TZ_EX:
2532 >      if ParseDateTimeTZString(value,dt,timezone,true) then
2533 >        SetAsTime(dt,GetAttachment.GetTimeZoneServices.GetTimeTZDate,timezone)
2534 >      else
2535 >        DoSetString;
2536 >
2537 >  else
2538 >    IBError(ibxeInvalidDataConversion,[GetSQLTypeName(getColMetaData.SQLTYPE)]);
2539    end;
2540   end;
2541  
# Line 1955 | Line 2573 | begin
2573    IsNull := true;
2574   end;
2575  
2576 + function TSQLParam.getColMetadata: IParamMetaData;
2577 + begin
2578 +  Result := FIBXSQLVAR.getColMetadata;
2579 + end;
2580 +
2581   function TSQLParam.GetModified: boolean;
2582   begin
2583    CheckActive;
# Line 1968 | Line 2591 | begin
2591    Result := inherited GetAsPointer;
2592   end;
2593  
2594 + function TSQLParam.GetAsString: AnsiString;
2595 + var rs: RawByteString;
2596 + begin
2597 +  Result := '';
2598 +  if (SQLType = SQL_VARYING) and not IsNull then
2599 +  {SQLData points to start of string - default is to length word}
2600 +  begin
2601 +    CheckActive;
2602 +    SetString(rs,PAnsiChar(SQLData),DataLength);
2603 +    SetCodePage(rs,GetCodePage,false);
2604 +    Result := rs;
2605 +  end
2606 +  else
2607 +    Result := inherited GetAsString;
2608 + end;
2609 +
2610   procedure TSQLParam.SetName(Value: AnsiString);
2611   begin
2612    CheckActive;
# Line 2159 | Line 2798 | begin
2798    end;
2799   end;
2800  
2801 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
2802 + var i: integer;
2803 +    OldSQLVar: TSQLVarData;
2804 + begin
2805 +  if FIBXSQLVAR.UniqueName then
2806 +    inherited SetAsTime(AValue,OnDate, aTimeZoneID)
2807 +  else
2808 +  with FIBXSQLVAR.Parent do
2809 +  begin
2810 +    for i := 0 to Count - 1 do
2811 +      if Column[i].Name = Name then
2812 +      begin
2813 +        OldSQLVar := FIBXSQLVAR;
2814 +        FIBXSQLVAR := Column[i];
2815 +        try
2816 +          inherited SetAsTime(AValue,OnDate, aTimeZoneID);
2817 +        finally
2818 +          FIBXSQLVAR := OldSQLVar;
2819 +        end;
2820 +      end;
2821 +  end;
2822 + end;
2823 +
2824 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
2825 + var i: integer;
2826 +    OldSQLVar: TSQLVarData;
2827 + begin
2828 +  if FIBXSQLVAR.UniqueName then
2829 +    inherited SetAsTime(AValue,OnDate,aTimeZone)
2830 +  else
2831 +  with FIBXSQLVAR.Parent do
2832 +  begin
2833 +    for i := 0 to Count - 1 do
2834 +      if Column[i].Name = Name then
2835 +      begin
2836 +        OldSQLVar := FIBXSQLVAR;
2837 +        FIBXSQLVAR := Column[i];
2838 +        try
2839 +          inherited SetAsTime(AValue,OnDate,aTimeZone);
2840 +        finally
2841 +          FIBXSQLVAR := OldSQLVar;
2842 +        end;
2843 +      end;
2844 +  end;
2845 + end;
2846 +
2847 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID);
2848 + begin
2849 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZoneID);
2850 + end;
2851 +
2852 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZone: AnsiString);
2853 + begin
2854 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZone);
2855 + end;
2856 +
2857   procedure TSQLParam.SetAsDateTime(AValue: TDateTime);
2858   var i: integer;
2859      OldSQLVar: TSQLVarData;
# Line 2182 | Line 2877 | begin
2877    end;
2878   end;
2879  
2880 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID
2881 +  );
2882 + var i: integer;
2883 +    OldSQLVar: TSQLVarData;
2884 + begin
2885 +  if FIBXSQLVAR.UniqueName then
2886 +    inherited SetAsDateTime(AValue,aTimeZoneID)
2887 +  else
2888 +  with FIBXSQLVAR.Parent do
2889 +  begin
2890 +    for i := 0 to Count - 1 do
2891 +      if Column[i].Name = Name then
2892 +      begin
2893 +        OldSQLVar := FIBXSQLVAR;
2894 +        FIBXSQLVAR := Column[i];
2895 +        try
2896 +          inherited SetAsDateTime(AValue,aTimeZoneID);
2897 +        finally
2898 +          FIBXSQLVAR := OldSQLVar;
2899 +        end;
2900 +      end;
2901 +  end;
2902 + end;
2903 +
2904 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString);
2905 + var i: integer;
2906 +    OldSQLVar: TSQLVarData;
2907 + begin
2908 +  if FIBXSQLVAR.UniqueName then
2909 +    inherited SetAsDateTime(AValue,aTimeZone)
2910 +  else
2911 +  with FIBXSQLVAR.Parent do
2912 +  begin
2913 +    for i := 0 to Count - 1 do
2914 +      if Column[i].Name = Name then
2915 +      begin
2916 +        OldSQLVar := FIBXSQLVAR;
2917 +        FIBXSQLVAR := Column[i];
2918 +        try
2919 +          inherited SetAsDateTime(AValue,aTimeZone);
2920 +        finally
2921 +          FIBXSQLVAR := OldSQLVar;
2922 +        end;
2923 +      end;
2924 +  end;
2925 + end;
2926 +
2927   procedure TSQLParam.SetAsDouble(AValue: Double);
2928   var i: integer;
2929      OldSQLVar: TSQLVarData;
# Line 2362 | Line 3104 | begin
3104    FIBXSQLVAR.SetCharSetID(aValue);
3105   end;
3106  
3107 + procedure TSQLParam.SetAsBcd(aValue: tBCD);
3108 + var i: integer;
3109 +    OldSQLVar: TSQLVarData;
3110 + begin
3111 +  if FIBXSQLVAR.UniqueName then
3112 +    inherited SetAsBcd(AValue)
3113 +  else
3114 +  with FIBXSQLVAR.Parent do
3115 +  begin
3116 +    for i := 0 to Count - 1 do
3117 +      if Column[i].Name = Name then
3118 +      begin
3119 +        OldSQLVar := FIBXSQLVAR;
3120 +        FIBXSQLVAR := Column[i];
3121 +        try
3122 +          inherited SetAsBcd(AValue);
3123 +        finally
3124 +          FIBXSQLVAR := OldSQLVar;
3125 +        end;
3126 +      end;
3127 +  end;
3128 + end;
3129 +
3130   { TMetaData }
3131  
3132   procedure TMetaData.CheckActive;
# Line 2500 | Line 3265 | begin
3265      end;
3266   end;
3267  
3268 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
3269 + begin
3270 +  Result := FSQLParams.CaseSensitiveParams;
3271 + end;
3272 +
3273   { TResults }
3274  
3275   procedure TResults.CheckActive;
# Line 2512 | Line 3282 | begin
3282    if not FResults.CheckStatementStatus(ssPrepared)  then
3283      IBError(ibxeStatementNotPrepared, [nil]);
3284  
3285 <  with GetTransaction as TFBTransaction do
3285 >  with GetTransaction do
3286    if not InTransaction or (FResults.TransactionSeqNo <> FTransactionSeqNo) then
3287      IBError(ibxeInterfaceOutofDate,[nil]);
3288   end;
3289  
3290   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
3291 + var col: TIBSQLData;
3292   begin
3293    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
3294      IBError(ibxeInvalidColumnIndex,[nil]);
3295  
3296    if not HasInterface(aIBXSQLVAR.Index) then
3297      AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
3298 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3298 >  col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3299 >  col.FTransaction := GetTransaction;
3300 >  Result := col;
3301   end;
3302  
3303   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2581 | Line 3354 | begin
3354    FResults.GetData(index,IsNull, len,data);
3355   end;
3356  
3357 + function TResults.GetStatement: IStatement;
3358 + begin
3359 +  Result := FStatement;
3360 + end;
3361 +
3362   function TResults.GetTransaction: ITransaction;
3363   begin
3364    Result := FStatement.GetTransaction;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines