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 350 by tony, Wed Oct 20 14:58:56 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}
665  
666 <  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]);
543 <
544 <  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;
666 > var slNames: TStrings;
667  
668 <        ArrayDimState:
669 <        begin
670 <          case cCurChar of
671 <          ':',',','0'..'9',' ',#9,#10,#13:
672 <            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;
613 <
614 <        CommentState:
615 <        begin
616 <          if (cNextChar = #0) then
617 <            IBError(ibxeSQLParseError, [SEOFInComment])
618 <          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
1333 <          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 + function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean;
2426 + var i: integer;
2427 +    ds: integer;
2428 + begin
2429 +  Result := false;
2430 +  ds := 0;
2431 +  S := Trim(S);
2432 +  {$IF declared(DefaultFormatSettings)}
2433 +  with DefaultFormatSettings do
2434 +  {$ELSE}
2435 +  {$IF declared(FormatSettings)}
2436 +  with FormatSettings do
2437 +  {$IFEND}
2438 +  {$IFEND}
2439 +  begin
2440 +    {ThousandSeparator not allowed as by Delphi specs}
2441 +    if (ThousandSeparator <> DecimalSeparator) and
2442 +       (Pos(ThousandSeparator, S) <> 0) then
2443 +        Exit;
2444 +
2445 +    for i := length(S) downto 1 do
2446 +    begin
2447 +      if S[i] = AnsiChar(DecimalSeparator) then
2448 +      begin
2449 +          if ds <> 0 then Exit; {only one allowed}
2450 +          ds := i-1;
2451 +          system.Delete(S,i,1);
2452 +      end
2453 +      else
2454 +      if (i > 1) and (S[i] in ['+','-']) then
2455 +        Exit
2456 +      else
2457 +      if not (S[i] in ['0'..'9']) then
2458 +          Exit; {bad character}
2459 +
2460 +    end;
2461 +    if ds = 0 then
2462 +      scale := 0
2463 +    else
2464 +      scale := ds - Length(S);
2465 +    Result := TryStrToInt64(S,Value);
2466 +  end;
2467 + end;
2468 +
2469   var b: IBlob;
2470      dt: TDateTime;
2471 +    timezone: AnsiString;
2472 +    {$ifdef FPC_HAS_TYPE_EXTENDED}
2473 +    FloatValue: Extended;
2474 +    {$else}
2475 +    FloatValue: Double;
2476 +    {$endif}
2477 +    Int64Value: Int64;
2478 +    BCDValue: TBCD;
2479 +    aScale: integer;
2480   begin
2481    CheckActive;
2482    if IsNullable then
2483      IsNull := False;
2484 <  case SQLTYPE of
2484 >  with FFirebirdClientAPI do
2485 >  case getColMetaData.SQLTYPE of
2486    SQL_BOOLEAN:
2487      if AnsiCompareText(Value,STrue) = 0 then
2488        AsBoolean := true
# Line 1872 | Line 2493 | begin
2493        IBError(ibxeInvalidDataConversion,[nil]);
2494  
2495    SQL_BLOB:
2496 +    if Length(Value) < GetAttachment.GetInlineBlobLimit then
2497 +      DoSetString
2498 +    else
2499      begin
2500        Changing;
2501        b := FIBXSQLVAR.CreateBlob;
# Line 1882 | Line 2506 | begin
2506  
2507    SQL_VARYING,
2508    SQL_TEXT:
2509 +    DoSetString;
2510 +
2511 +  SQL_SHORT,
2512 +  SQL_LONG,
2513 +  SQL_INT64:
2514 +    if TryStrToNumeric(Value,Int64Value,aScale) then
2515      begin
2516 <      Changing;
2517 <      FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
2518 <      Changed;
2519 <    end;
2516 >      if aScale = 0 then
2517 >        SetAsInt64(Int64Value)
2518 >      else
2519 >        SetAsNumeric(Int64Value,aScale);
2520 >    end
2521 >    else
2522 >    if TryStrToFloat(Value,FloatValue) then
2523 >      SetAsDouble(FloatValue)
2524 >    else
2525 >      DoSetString;
2526  
2527 <    SQL_SHORT,
2528 <    SQL_LONG,
2529 <    SQL_INT64:
2530 <      SetAsNumeric(AdjustScaleFromCurrency(StrToCurr(Value),GetScale),GetScale);
2531 <
2532 <    SQL_D_FLOAT,
2533 <    SQL_DOUBLE,
2534 <    SQL_FLOAT:
1899 <      SetAsDouble(StrToFloat(Value));
2527 >  SQL_DEC_FIXED,
2528 >  SQL_DEC16,
2529 >  SQL_DEC34,
2530 >  SQL_INT128:
2531 >    if TryStrToBCD(Value,BCDValue) then
2532 >      SetAsBCD(BCDValue)
2533 >    else
2534 >      DoSetString;
2535  
2536 <    SQL_TIMESTAMP:
2536 >  SQL_D_FLOAT,
2537 >  SQL_DOUBLE,
2538 >  SQL_FLOAT:
2539 >    if TryStrToFloat(Value,FloatValue) then
2540 >      SetAsDouble(FloatValue)
2541 >    else
2542 >      DoSetString;
2543 >
2544 >  SQL_TIMESTAMP:
2545        if TryStrToDateTime(Value,dt) then
2546          SetAsDateTime(dt)
2547        else
2548 <        FIBXSQLVar.SetString(Value);
2548 >        DoSetString;
2549  
2550 <    SQL_TYPE_DATE:
2550 >  SQL_TYPE_DATE:
2551        if TryStrToDateTime(Value,dt) then
2552          SetAsDate(dt)
2553        else
2554 <        FIBXSQLVar.SetString(Value);
2554 >        DoSetString;
2555  
2556 <    SQL_TYPE_TIME:
2556 >  SQL_TYPE_TIME:
2557        if TryStrToDateTime(Value,dt) then
2558          SetAsTime(dt)
2559        else
2560 <        FIBXSQLVar.SetString(Value);
2560 >        DoSetString;
2561  
2562 <    else
2563 <      IBError(ibxeInvalidDataConversion,[nil]);
2562 >  SQL_TIMESTAMP_TZ,
2563 >  SQL_TIMESTAMP_TZ_EX:
2564 >      if ParseDateTimeTZString(value,dt,timezone) then
2565 >        SetAsDateTime(dt,timezone)
2566 >      else
2567 >        DoSetString;
2568 >
2569 >  SQL_TIME_TZ,
2570 >  SQL_TIME_TZ_EX:
2571 >      if ParseDateTimeTZString(value,dt,timezone,true) then
2572 >        SetAsTime(dt,GetAttachment.GetTimeZoneServices.GetTimeTZDate,timezone)
2573 >      else
2574 >        DoSetString;
2575 >
2576 >  else
2577 >    IBError(ibxeInvalidDataConversion,[GetSQLTypeName(getColMetaData.SQLTYPE)]);
2578    end;
2579   end;
2580  
# Line 1955 | Line 2612 | begin
2612    IsNull := true;
2613   end;
2614  
2615 + function TSQLParam.getColMetadata: IParamMetaData;
2616 + begin
2617 +  Result := FIBXSQLVAR.getColMetadata;
2618 + end;
2619 +
2620   function TSQLParam.GetModified: boolean;
2621   begin
2622    CheckActive;
# Line 1968 | Line 2630 | begin
2630    Result := inherited GetAsPointer;
2631   end;
2632  
2633 + function TSQLParam.GetAsString: AnsiString;
2634 + var rs: RawByteString;
2635 + begin
2636 +  Result := '';
2637 +  if (SQLType = SQL_VARYING) and not IsNull then
2638 +  {SQLData points to start of string - default is to length word}
2639 +  begin
2640 +    CheckActive;
2641 +    SetString(rs,PAnsiChar(SQLData),DataLength);
2642 +    SetCodePage(rs,GetCodePage,false);
2643 +    Result := rs;
2644 +  end
2645 +  else
2646 +    Result := inherited GetAsString;
2647 + end;
2648 +
2649   procedure TSQLParam.SetName(Value: AnsiString);
2650   begin
2651    CheckActive;
# Line 2159 | Line 2837 | begin
2837    end;
2838   end;
2839  
2840 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZoneID: TFBTimeZoneID);
2841 + var i: integer;
2842 +    OldSQLVar: TSQLVarData;
2843 + begin
2844 +  if FIBXSQLVAR.UniqueName then
2845 +    inherited SetAsTime(AValue,OnDate, aTimeZoneID)
2846 +  else
2847 +  with FIBXSQLVAR.Parent do
2848 +  begin
2849 +    for i := 0 to Count - 1 do
2850 +      if Column[i].Name = Name then
2851 +      begin
2852 +        OldSQLVar := FIBXSQLVAR;
2853 +        FIBXSQLVAR := Column[i];
2854 +        try
2855 +          inherited SetAsTime(AValue,OnDate, aTimeZoneID);
2856 +        finally
2857 +          FIBXSQLVAR := OldSQLVar;
2858 +        end;
2859 +      end;
2860 +  end;
2861 + end;
2862 +
2863 + procedure TSQLParam.SetAsTime(aValue: TDateTime; OnDate: TDateTime; aTimeZone: AnsiString);
2864 + var i: integer;
2865 +    OldSQLVar: TSQLVarData;
2866 + begin
2867 +  if FIBXSQLVAR.UniqueName then
2868 +    inherited SetAsTime(AValue,OnDate,aTimeZone)
2869 +  else
2870 +  with FIBXSQLVAR.Parent do
2871 +  begin
2872 +    for i := 0 to Count - 1 do
2873 +      if Column[i].Name = Name then
2874 +      begin
2875 +        OldSQLVar := FIBXSQLVAR;
2876 +        FIBXSQLVAR := Column[i];
2877 +        try
2878 +          inherited SetAsTime(AValue,OnDate,aTimeZone);
2879 +        finally
2880 +          FIBXSQLVAR := OldSQLVar;
2881 +        end;
2882 +      end;
2883 +  end;
2884 + end;
2885 +
2886 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID);
2887 + begin
2888 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZoneID);
2889 + end;
2890 +
2891 + procedure TSQLParam.SetAsTime(aValue: TDateTime; aTimeZone: AnsiString);
2892 + begin
2893 +  SetAsTime(aValue,GetTimeZoneServices.GetTimeTZDate,aTimeZone);
2894 + end;
2895 +
2896   procedure TSQLParam.SetAsDateTime(AValue: TDateTime);
2897   var i: integer;
2898      OldSQLVar: TSQLVarData;
# Line 2182 | Line 2916 | begin
2916    end;
2917   end;
2918  
2919 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZoneID: TFBTimeZoneID
2920 +  );
2921 + var i: integer;
2922 +    OldSQLVar: TSQLVarData;
2923 + begin
2924 +  if FIBXSQLVAR.UniqueName then
2925 +    inherited SetAsDateTime(AValue,aTimeZoneID)
2926 +  else
2927 +  with FIBXSQLVAR.Parent do
2928 +  begin
2929 +    for i := 0 to Count - 1 do
2930 +      if Column[i].Name = Name then
2931 +      begin
2932 +        OldSQLVar := FIBXSQLVAR;
2933 +        FIBXSQLVAR := Column[i];
2934 +        try
2935 +          inherited SetAsDateTime(AValue,aTimeZoneID);
2936 +        finally
2937 +          FIBXSQLVAR := OldSQLVar;
2938 +        end;
2939 +      end;
2940 +  end;
2941 + end;
2942 +
2943 + procedure TSQLParam.SetAsDateTime(aValue: TDateTime; aTimeZone: AnsiString);
2944 + var i: integer;
2945 +    OldSQLVar: TSQLVarData;
2946 + begin
2947 +  if FIBXSQLVAR.UniqueName then
2948 +    inherited SetAsDateTime(AValue,aTimeZone)
2949 +  else
2950 +  with FIBXSQLVAR.Parent do
2951 +  begin
2952 +    for i := 0 to Count - 1 do
2953 +      if Column[i].Name = Name then
2954 +      begin
2955 +        OldSQLVar := FIBXSQLVAR;
2956 +        FIBXSQLVAR := Column[i];
2957 +        try
2958 +          inherited SetAsDateTime(AValue,aTimeZone);
2959 +        finally
2960 +          FIBXSQLVAR := OldSQLVar;
2961 +        end;
2962 +      end;
2963 +  end;
2964 + end;
2965 +
2966   procedure TSQLParam.SetAsDouble(AValue: Double);
2967   var i: integer;
2968      OldSQLVar: TSQLVarData;
# Line 2362 | Line 3143 | begin
3143    FIBXSQLVAR.SetCharSetID(aValue);
3144   end;
3145  
3146 + procedure TSQLParam.SetAsBcd(aValue: tBCD);
3147 + var i: integer;
3148 +    OldSQLVar: TSQLVarData;
3149 + begin
3150 +  if FIBXSQLVAR.UniqueName then
3151 +    inherited SetAsBcd(AValue)
3152 +  else
3153 +  with FIBXSQLVAR.Parent do
3154 +  begin
3155 +    for i := 0 to Count - 1 do
3156 +      if Column[i].Name = Name then
3157 +      begin
3158 +        OldSQLVar := FIBXSQLVAR;
3159 +        FIBXSQLVAR := Column[i];
3160 +        try
3161 +          inherited SetAsBcd(AValue);
3162 +        finally
3163 +          FIBXSQLVAR := OldSQLVar;
3164 +        end;
3165 +      end;
3166 +  end;
3167 + end;
3168 +
3169   { TMetaData }
3170  
3171   procedure TMetaData.CheckActive;
# Line 2500 | Line 3304 | begin
3304      end;
3305   end;
3306  
3307 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
3308 + begin
3309 +  Result := FSQLParams.CaseSensitiveParams;
3310 + end;
3311 +
3312   { TResults }
3313  
3314   procedure TResults.CheckActive;
# Line 2512 | Line 3321 | begin
3321    if not FResults.CheckStatementStatus(ssPrepared)  then
3322      IBError(ibxeStatementNotPrepared, [nil]);
3323  
3324 <  with GetTransaction as TFBTransaction do
3324 >  with GetTransaction do
3325    if not InTransaction or (FResults.TransactionSeqNo <> FTransactionSeqNo) then
3326      IBError(ibxeInterfaceOutofDate,[nil]);
3327   end;
3328  
3329   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
3330 + var col: TIBSQLData;
3331   begin
3332    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
3333      IBError(ibxeInvalidColumnIndex,[nil]);
3334  
3335    if not HasInterface(aIBXSQLVAR.Index) then
3336      AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
3337 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3337 >  col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
3338 >  col.FTransaction := GetTransaction;
3339 >  Result := col;
3340   end;
3341  
3342   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2581 | Line 3393 | begin
3393    FResults.GetData(index,IsNull, len,data);
3394   end;
3395  
3396 + function TResults.GetStatement: IStatement;
3397 + begin
3398 +  Result := FStatement;
3399 + end;
3400 +
3401   function TResults.GetTransaction: ITransaction;
3402   begin
3403    Result := FStatement.GetTransaction;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines