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

Comparing ibx/trunk/fbintf/client/FBSQLData.pas (file contents):
Revision 56 by tony, Mon Mar 6 10:20:02 2017 UTC vs.
Revision 308 by tony, Sat Jul 18 10:26:30 2020 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, IBHeader, {$IFDEF WINDOWS} Windows, {$ENDIF} IB,  FBActivityMonitor, FBClientAPI;
84  
85   type
86  
# Line 105 | Line 88 | type
88  
89    TSQLDataItem = class(TFBInterfacedObject)
90    private
91 +     FFirebirdClientAPI: TFBClientAPI;
92       function AdjustScale(Value: Int64; aScale: Integer): Double;
93       function AdjustScaleToInt64(Value: Int64; aScale: Integer): Int64;
94       function AdjustScaleToCurrency(Value: Int64; aScale: Integer): Currency;
95 +     function GetTimestampFormatStr: AnsiString;
96 +     function GetDateFormatStr(IncludeTime: boolean): AnsiString;
97 +     function GetTimeFormatStr: AnsiString;
98       procedure SetAsInteger(AValue: Integer);
99    protected
100       function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
# Line 128 | Line 115 | type
115       property DataLength: cardinal read GetDataLength write SetDataLength;
116  
117    public
118 +     constructor Create(api: TFBClientAPI);
119       function GetSQLType: cardinal; virtual; abstract;
120       function GetSQLTypeName: AnsiString; overload;
121       class function GetSQLTypeName(SQLType: short): AnsiString; overload;
122 +     function GetStrDataLength: short;
123       function GetName: AnsiString; virtual; abstract;
124       function GetScale: integer; virtual; abstract;
125       function GetAsBoolean: boolean;
# Line 145 | Line 134 | type
134       function GetAsShort: short;
135       function GetAsString: AnsiString; virtual;
136       function GetIsNull: Boolean; virtual;
137 <     function getIsNullable: boolean; virtual;
137 >     function GetIsNullable: boolean; virtual;
138       function GetAsVariant: Variant;
139       function GetModified: boolean; virtual;
140 +     function GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats): integer;
141 +     function GetSize: cardinal; virtual; abstract;
142       procedure SetAsBoolean(AValue: boolean); virtual;
143       procedure SetAsCurrency(Value: Currency); virtual;
144       procedure SetAsInt64(Value: Int64); virtual;
# Line 162 | Line 153 | type
153       procedure SetAsShort(Value: short); virtual;
154       procedure SetAsString(Value: AnsiString); virtual;
155       procedure SetAsVariant(Value: Variant);
156 +     procedure SetAsNumeric(Value: Int64; aScale: integer);
157       procedure SetIsNull(Value: Boolean); virtual;
158       procedure SetIsNullable(Value: Boolean); virtual;
159       procedure SetName(aValue: AnsiString); virtual;
# Line 195 | Line 187 | type
187  
188    TSQLDataArea = class
189    private
190 +    FCaseSensitiveParams: boolean;
191      function GetColumn(index: integer): TSQLVarData;
192      function GetCount: integer;
193    protected
# Line 217 | Line 210 | type
210        var data: PByte); virtual;
211      procedure RowChange;
212      function StateChanged(var ChangeSeqNo: integer): boolean; virtual; abstract;
213 +    property CaseSensitiveParams: boolean read FCaseSensitiveParams
214 +                                            write FCaseSensitiveParams; {Only used when IsInputDataArea true}
215      property Count: integer read GetCount;
216      property Column[index: integer]: TSQLVarData read GetColumn;
217      property UniqueRelationName: AnsiString read FUniqueRelationName;
# Line 299 | Line 294 | type
294      FIBXSQLVAR: TSQLVarData;
295      FOwner: IUnknown;         {Keep reference to ensure Metadata/statement not discarded}
296      FPrepareSeqNo: integer;
302    FStatement: IStatement;
297      FChangeSeqNo: integer;
298    protected
299      procedure CheckActive; override;
# Line 311 | Line 305 | type
305      constructor Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
306      destructor Destroy; override;
307      function GetSQLDialect: integer; override;
314    property Statement: IStatement read FStatement;
308  
309    public
310      {IColumnMetaData}
# Line 326 | Line 319 | type
319      function GetScale: integer; override;
320      function getCharSetID: cardinal; override;
321      function GetIsNullable: boolean; override;
322 <    function GetSize: cardinal;
322 >    function GetSize: cardinal; override;
323      function GetArrayMetaData: IArrayMetaData;
324      function GetBlobMetaData: IBlobMetaData;
325 +    function GetStatement: IStatement;
326 +    function GetTransaction: ITransaction; virtual;
327      property Name: AnsiString read GetName;
328      property Size: cardinal read GetSize;
329      property CharSetID: cardinal read getCharSetID;
330      property SQLSubtype: integer read getSubtype;
331      property IsNullable: Boolean read GetIsNullable;
332 +  public
333 +    property Statement: IStatement read GetStatement;
334    end;
335  
336    { TIBSQLData }
337  
338    TIBSQLData = class(TColumnMetaData,ISQLData)
339 +  private
340 +    FTransaction: ITransaction;
341    protected
342      procedure CheckActive; override;
343    public
344 +    function GetTransaction: ITransaction; override;
345      function GetIsNull: Boolean; override;
346      function GetAsArray: IArray;
347      function GetAsBlob: IBlob; overload;
# Line 428 | Line 428 | type
428      function getSQLParam(index: integer): ISQLParam;
429      function ByName(Idx: AnsiString): ISQLParam ;
430      function GetModified: Boolean;
431 +    function GetHasCaseSensitiveParams: Boolean;
432    end;
433  
434    { TResults }
# Line 449 | Line 450 | type
450       function ByName(Idx: AnsiString): ISQLData;
451       function getSQLData(index: integer): ISQLData;
452       procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PByte);
453 +     function GetStatement: IStatement;
454       function GetTransaction: ITransaction; virtual;
455       procedure SetRetainInterfaces(aValue: boolean);
456   end;
457  
458   implementation
459  
460 < uses FBMessages, FBClientAPI, variants, IBUtils, FBTransaction;
459 <
460 > uses FBMessages, variants, IBUtils, FBTransaction, DateUtils;
461  
462   { TSQLDataArea }
463  
# Line 509 | Line 510 | end;
510  
511   procedure TSQLDataArea.PreprocessSQL(sSQL: AnsiString; GenerateParamNames: boolean;
512    var sProcessedSQL: AnsiString);
512 var
513  cCurChar, cNextChar, cQuoteChar: AnsiChar;
514  sParamName: AnsiString;
515  j, i, iLenSQL, iSQLPos: Integer;
516  iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
517  iParamSuffix: Integer;
518  slNames: TStrings;
519  StrBuffer: PByte;
520  found: boolean;
521
522 const
523  DefaultState = 0;
524  CommentState = 1;
525  QuoteState = 2;
526  ParamState = 3;
527  ArrayDimState = 4;
528 {$ifdef ALLOWDIALECT3PARAMNAMES}
529  ParamDefaultState = 0;
530  ParamQuoteState = 1;
531  {$endif}
532
533  procedure AddToProcessedSQL(cChar: AnsiChar);
534  begin
535    StrBuffer[iSQLPos] := byte(cChar);
536    Inc(iSQLPos);
537  end;
538
539 begin
540  if not IsInputDataArea then
541    IBError(ibxeNotPermitted,[nil]);
542
543  sParamName := '';
544  iLenSQL := Length(sSQL);
545  GetMem(StrBuffer,iLenSQL + 1);
546  slNames := TStringList.Create;
547  try
548    { Do some initializations of variables }
549    iParamSuffix := 0;
550    cQuoteChar := '''';
551    i := 1;
552    iSQLPos := 0;
553    iCurState := DefaultState;
554    {$ifdef ALLOWDIALECT3PARAMNAMES}
555    iCurParamState := ParamDefaultState;
556    {$endif}
557    { Now, traverse through the SQL string, character by character,
558     picking out the parameters and formatting correctly for InterBase }
559    while (i <= iLenSQL) do begin
560      { Get the current token and a look-ahead }
561      cCurChar := sSQL[i];
562      if i = iLenSQL then
563        cNextChar := #0
564      else
565        cNextChar := sSQL[i + 1];
566      { Now act based on the current state }
567      case iCurState of
568        DefaultState:
569        begin
570          case cCurChar of
571            '''', '"':
572            begin
573              cQuoteChar := cCurChar;
574              iCurState := QuoteState;
575            end;
576            '?', ':':
577            begin
578              iCurState := ParamState;
579              AddToProcessedSQL('?');
580            end;
581            '/': if (cNextChar = '*') then
582            begin
583              AddToProcessedSQL(cCurChar);
584              Inc(i);
585              iCurState := CommentState;
586            end;
587            '[':
588            begin
589              AddToProcessedSQL(cCurChar);
590              Inc(i);
591              iCurState := ArrayDimState;
592            end;
593          end;
594        end;
513  
514 <        ArrayDimState:
597 <        begin
598 <          case cCurChar of
599 <          ':',',','0'..'9',' ',#9,#10,#13:
600 <            begin
601 <              AddToProcessedSQL(cCurChar);
602 <              Inc(i);
603 <            end;
604 <          else
605 <            begin
606 <              AddToProcessedSQL(cCurChar);
607 <              Inc(i);
608 <              iCurState := DefaultState;
609 <            end;
610 <          end;
611 <        end;
514 > var slNames: TStrings;
515  
516 <        CommentState:
517 <        begin
518 <          if (cNextChar = #0) then
519 <            IBError(ibxeSQLParseError, [SEOFInComment])
520 <          else if (cCurChar = '*') then begin
618 <            if (cNextChar = '/') then
619 <              iCurState := DefaultState;
620 <          end;
621 <        end;
622 <        QuoteState: begin
623 <          if cNextChar = #0 then
624 <            IBError(ibxeSQLParseError, [SEOFInString])
625 <          else if (cCurChar = cQuoteChar) then begin
626 <            if (cNextChar = cQuoteChar) then begin
627 <              AddToProcessedSQL(cCurChar);
628 <              Inc(i);
629 <            end else
630 <              iCurState := DefaultState;
631 <          end;
632 <        end;
633 <        ParamState:
634 <        begin
635 <          { collect the name of the parameter }
636 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
637 <          if iCurParamState = ParamDefaultState then
638 <          begin
639 <            if cCurChar = '"' then
640 <              iCurParamState := ParamQuoteState
641 <            else
642 <            {$endif}
643 <            if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
644 <                sParamName := sParamName + cCurChar
645 <            else if GenerateParamNames then
646 <            begin
647 <              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
648 <              Inc(iParamSuffix);
649 <              iCurState := DefaultState;
650 <              slNames.AddObject(sParamName,self); //Note local convention
651 <                                                  //add pointer to self to mark entry
652 <              sParamName := '';
653 <            end
654 <            else
655 <              IBError(ibxeSQLParseError, [SParamNameExpected]);
656 <          {$ifdef ALLOWDIALECT3PARAMNAMES}
657 <          end
658 <          else begin
659 <            { determine if Quoted parameter name is finished }
660 <            if cCurChar = '"' then
661 <            begin
662 <              Inc(i);
663 <              slNames.Add(sParamName);
664 <              SParamName := '';
665 <              iCurParamState := ParamDefaultState;
666 <              iCurState := DefaultState;
667 <            end
668 <            else
669 <              sParamName := sParamName + cCurChar
670 <          end;
671 <          {$endif}
672 <          { determine if the unquoted parameter name is finished }
673 <          if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
674 <            (iCurState <> DefaultState) then
675 <          begin
676 <            if not (cNextChar in ['A'..'Z', 'a'..'z',
677 <                                  '0'..'9', '_', '$']) then begin
678 <              Inc(i);
679 <              iCurState := DefaultState;
680 <              slNames.Add(sParamName);
681 <              sParamName := '';
682 <            end;
683 <          end;
684 <        end;
685 <      end;
686 <      if (iCurState <> ParamState) and (i <= iLenSQL) then
687 <        AddToProcessedSQL(sSQL[i]);
688 <      Inc(i);
689 <    end;
690 <    AddToProcessedSQL(#0);
691 <    sProcessedSQL := strpas(PAnsiChar(StrBuffer));
516 >  procedure SetColumnNames(slNames: TStrings);
517 >  var i, j: integer;
518 >      found: boolean;
519 >  begin
520 >    found := false;
521      SetCount(slNames.Count);
522      for i := 0 to slNames.Count - 1 do
523      begin
# Line 709 | Line 538 | begin
538          Column[i].UniqueName := not found;
539        end;
540      end;
541 +  end;
542 +
543 + begin
544 +  if not IsInputDataArea then
545 +    IBError(ibxeNotPermitted,[nil]);
546 +
547 +  slNames := TStringList.Create;
548 +  try
549 +    sProcessedSQL := TSQLParamProcessor.Execute(sSQL,GenerateParamNames,slNames);
550 +    SetColumnNames(slNames);
551    finally
552      slNames.Free;
714    FreeMem(StrBuffer);
553    end;
554   end;
555  
# Line 725 | Line 563 | var
563    s: AnsiString;
564    i: Integer;
565   begin
566 <  {$ifdef UseCaseInSensitiveParamName}
567 <   s := AnsiUpperCase(Idx);
568 <  {$else}
566 >  if not IsInputDataArea or not CaseSensitiveParams then
567 >   s := AnsiUpperCase(Idx)
568 >  else
569     s := Idx;
570 <  {$endif}
570 >
571    for i := 0 to Count - 1 do
572      if Column[i].Name = s then
573      begin
# Line 761 | Line 599 | end;
599  
600   procedure TSQLVarData.SetName(AValue: AnsiString);
601   begin
602 <  if FName = AValue then Exit;
765 <  {$ifdef UseCaseInSensitiveParamName}
766 <  if Parent.IsInputDataArea then
602 >  if not Parent.IsInputDataArea or not Parent.CaseSensitiveParams then
603      FName := AnsiUpperCase(AValue)
604    else
769  {$endif}
605      FName := AValue;
606   end;
607  
# Line 787 | Line 622 | begin
622  
623    FVarString := aValue;
624    SQLType := SQL_TEXT;
625 +  Scale := 0;
626    SetSQLData(PByte(PAnsiChar(FVarString)),Length(aValue));
627   end;
628  
# Line 947 | Line 783 | begin
783        result := Value;
784   end;
785  
786 + function TSQLDataItem.GetDateFormatStr(IncludeTime: boolean): AnsiString;
787 + begin
788 +  {$IF declared(DefaultFormatSettings)}
789 +  with DefaultFormatSettings do
790 +  {$ELSE}
791 +  {$IF declared(FormatSettings)}
792 +  with FormatSettings do
793 +  {$IFEND}
794 +  {$IFEND}
795 +  case GetSQLDialect of
796 +    1:
797 +      if IncludeTime then
798 +        result := ShortDateFormat + ' ' + LongTimeFormat
799 +      else
800 +        result := ShortDateFormat;
801 +    3:
802 +      result := ShortDateFormat;
803 +  end;
804 + end;
805 +
806 + function TSQLDataItem.GetTimeFormatStr: AnsiString;
807 + begin
808 +  {$IF declared(DefaultFormatSettings)}
809 +  with DefaultFormatSettings do
810 +  {$ELSE}
811 +  {$IF declared(FormatSettings)}
812 +  with FormatSettings do
813 +  {$IFEND}
814 +  {$IFEND}
815 +    Result := LongTimeFormat;
816 + end;
817 +
818 + function TSQLDataItem.GetTimestampFormatStr: AnsiString;
819 + begin
820 +  {$IF declared(DefaultFormatSettings)}
821 +  with DefaultFormatSettings do
822 +  {$ELSE}
823 +  {$IF declared(FormatSettings)}
824 +  with FormatSettings do
825 +  {$IFEND}
826 +  {$IFEND}
827 +    Result := ShortDateFormat + ' ' +  LongTimeFormat + '.zzz';
828 + end;
829 +
830   procedure TSQLDataItem.SetAsInteger(AValue: Integer);
831   begin
832    SetAsLong(aValue);
# Line 1045 | Line 925 | begin
925     //Do nothing by default
926   end;
927  
928 + constructor TSQLDataItem.Create(api: TFBClientAPI);
929 + begin
930 +  inherited Create;
931 +  FFirebirdClientAPI := api;
932 + end;
933 +
934   function TSQLDataItem.GetSQLTypeName: AnsiString;
935   begin
936    Result := GetSQLTypeName(GetSQLType);
# Line 1071 | Line 957 | begin
957    end;
958   end;
959  
960 + function TSQLDataItem.GetStrDataLength: short;
961 + begin
962 +  with FFirebirdClientAPI do
963 +  if SQLType = SQL_VARYING then
964 +    Result := DecodeInteger(SQLData, 2)
965 +  else
966 +    Result := DataLength;
967 + end;
968 +
969   function TSQLDataItem.GetAsBoolean: boolean;
970   begin
971    CheckActive;
# Line 1151 | Line 1046 | begin
1046    CheckActive;
1047    result := 0;
1048    if not IsNull then
1049 <    with FirebirdClientAPI do
1049 >    with FFirebirdClientAPI do
1050      case SQLType of
1051        SQL_TEXT, SQL_VARYING: begin
1052          try
# Line 1280 | Line 1175 | begin
1175    end;
1176   end;
1177  
1178 + {Copied from LazUTF8}
1179 +
1180 + function UTF8CodepointSizeFull(p: PAnsiChar): integer;
1181 + const TopBitSetMask   = $8000; {%10000000}
1182 +      Top2BitsSetMask = $C000; {%11000000}
1183 +      Top3BitsSetMask = $E000; {%11100000}
1184 +      Top4BitsSetMask = $F000; {%11110000}
1185 +      Top5BitsSetMask = $F800; {%11111000}
1186 + begin
1187 +  case p^ of
1188 +  #0..#191: // %11000000
1189 +    // regular single byte character (#0 is a character, this is Pascal ;)
1190 +    Result:=1;
1191 +  #192..#223: // p^ and %11100000 = %11000000
1192 +    begin
1193 +      // could be 2 byte character
1194 +      if (ord(p[1]) and Top2BitsSetMask) = TopBitSetMask then
1195 +        Result:=2
1196 +      else
1197 +        Result:=1;
1198 +    end;
1199 +  #224..#239: // p^ and %11110000 = %11100000
1200 +    begin
1201 +      // could be 3 byte character
1202 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1203 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask) then
1204 +        Result:=3
1205 +      else
1206 +        Result:=1;
1207 +    end;
1208 +  #240..#247: // p^ and %11111000 = %11110000
1209 +    begin
1210 +      // could be 4 byte character
1211 +      if ((ord(p[1]) and Top2BitsSetMask) = TopBitSetMask)
1212 +      and ((ord(p[2]) and Top2BitsSetMask) = TopBitSetMask)
1213 +      and ((ord(p[3]) and Top2BitsSetMask) = TopBitSetMask) then
1214 +        Result:=4
1215 +      else
1216 +        Result:=1;
1217 +    end;
1218 +  else
1219 +    Result:=1;
1220 +  end;
1221 + end;
1222 +
1223 + {Returns the byte length of a UTF8 string with a fixed charwidth}
1224 +
1225 + function GetStrLen(p: PAnsiChar; CharWidth, MaxDataLength: cardinal): integer;
1226 + var i: integer;
1227 +    cplen: integer;
1228 + begin
1229 +  Result := 0;
1230 +  for i := 1 to CharWidth do
1231 +  begin
1232 +    cplen := UTF8CodepointSizeFull(p);
1233 +    Inc(p,cplen);
1234 +    Inc(Result,cplen);
1235 +    if Result >= MaxDataLength then
1236 +    begin
1237 +      Result := MaxDataLength;
1238 +      Exit;
1239 +    end;
1240 +  end;
1241 + end;
1242  
1243   function TSQLDataItem.GetAsString: AnsiString;
1244   var
# Line 1291 | Line 1250 | begin
1250    result := '';
1251    { Check null, if so return a default string }
1252    if not IsNull then
1253 <  with FirebirdClientAPI do
1253 >  with FFirebirdClientAPI do
1254      case SQLType of
1255        SQL_BOOLEAN:
1256          if AsBoolean then
# Line 1303 | Line 1262 | begin
1262        begin
1263          sz := SQLData;
1264          if (SQLType = SQL_TEXT) then
1265 <          str_len := DataLength
1265 >        begin
1266 >          if GetCodePage = cp_utf8 then
1267 >            str_len := GetStrLen(PAnsiChar(sz),GetSize,DataLength)
1268 >          else
1269 >            str_len := DataLength
1270 >        end
1271          else begin
1272 <          str_len := DecodeInteger(SQLData, 2);
1272 >          str_len := DecodeInteger(sz, 2);
1273            Inc(sz, 2);
1274          end;
1275          SetString(rs, PAnsiChar(sz), str_len);
1276          SetCodePage(rs,GetCodePage,false);
1277 <        if (SQLType = SQL_TEXT) and (GetCharSetID <> 1) then
1314 <          Result := TrimRight(rs)
1315 <        else
1316 <          Result := rs
1277 >        Result := rs;
1278        end;
1279        SQL_TYPE_DATE:
1280 <        case GetSQLDialect of
1320 <          1 : result := DateTimeToStr(AsDateTime);
1321 <          3 : result := DateToStr(AsDateTime);
1322 <        end;
1280 >        result := FormatDateTime(GetDateFormatStr(TimeOf(AsDateTime)<>0),AsDateTime);
1281        SQL_TYPE_TIME :
1282 <        result := TimeToStr(AsDateTime);
1282 >        result := FormatDateTime(GetTimeFormatStr,AsDateTime);
1283        SQL_TIMESTAMP:
1284 <      {$IF declared(DefaultFormatSettings)}
1327 <      with DefaultFormatSettings do
1328 <      {$ELSE}
1329 <      {$IF declared(FormatSettings)}
1330 <      with FormatSettings do
1331 <      {$IFEND}
1332 <      {$IFEND}
1333 <        result := FormatDateTime(ShortDateFormat + ' ' +
1334 <                            LongTimeFormat+'.zzz',AsDateTime);
1284 >        result := FormatDateTime(GetTimestampFormatStr,AsDateTime);
1285        SQL_SHORT, SQL_LONG:
1286          if Scale = 0 then
1287            result := IntToStr(AsLong)
# Line 1359 | Line 1309 | begin
1309    Result := false;
1310   end;
1311  
1312 < function TSQLDataItem.getIsNullable: boolean;
1312 > function TSQLDataItem.GetIsNullable: boolean;
1313   begin
1314    CheckActive;
1315    Result := false;
# Line 1407 | Line 1357 | begin
1357    Result := false;
1358   end;
1359  
1360 + function TSQLDataItem.GetDateTimeStrLength(DateTimeFormat: TIBDateTimeFormats
1361 +  ): integer;
1362 + begin
1363 +  case DateTimeFormat of
1364 +  dfTimestamp:
1365 +    Result := Length(GetTimestampFormatStr);
1366 +  dfDateTime:
1367 +    Result := Length(GetDateFormatStr(true));
1368 +  dfTime:
1369 +    Result := Length(GetTimeFormatStr);
1370 +  else
1371 +    Result := 0;
1372 +  end;
1373 + end;
1374 +
1375  
1376   procedure TSQLDataItem.SetIsNull(Value: Boolean);
1377   begin
# Line 1470 | Line 1435 | begin
1435  
1436    SQLType := SQL_TYPE_DATE;
1437    DataLength := SizeOf(ISC_DATE);
1438 <  with FirebirdClientAPI do
1438 >  with FFirebirdClientAPI do
1439      SQLEncodeDate(Value,SQLData);
1440    Changed;
1441   end;
# Line 1490 | Line 1455 | begin
1455  
1456    SQLType := SQL_TYPE_TIME;
1457    DataLength := SizeOf(ISC_TIME);
1458 <  with FirebirdClientAPI do
1458 >  with FFirebirdClientAPI do
1459      SQLEncodeTime(Value,SQLData);
1460    Changed;
1461   end;
# Line 1504 | Line 1469 | begin
1469    Changing;
1470    SQLType := SQL_TIMESTAMP;
1471    DataLength := SizeOf(ISC_TIME) + sizeof(ISC_DATE);
1472 <  with FirebirdClientAPI do
1472 >  with FFirebirdClientAPI do
1473      SQLEncodeDateTime(Value,SQLData);
1474    Changed;
1475   end;
# Line 1629 | Line 1594 | begin
1594    end;
1595   end;
1596  
1597 + procedure TSQLDataItem.SetAsNumeric(Value: Int64; aScale: integer);
1598 + begin
1599 +  CheckActive;
1600 +  Changing;
1601 +  if IsNullable then
1602 +    IsNull := False;
1603 +
1604 +  SQLType := SQL_INT64;
1605 +  Scale := aScale;
1606 +  DataLength := SizeOf(Int64);
1607 +  PInt64(SQLData)^ := Value;
1608 +  Changed;
1609 + end;
1610 +
1611   procedure TSQLDataItem.SetAsBoolean(AValue: boolean);
1612   begin
1613    CheckActive;
# Line 1676 | Line 1655 | end;
1655  
1656   constructor TColumnMetaData.Create(aOwner: IUnknown; aIBXSQLVAR: TSQLVarData);
1657   begin
1658 <  inherited Create;
1658 >  inherited Create(aIBXSQLVAR.GetStatement.GetAttachment.getFirebirdAPI as TFBClientAPI);
1659    FIBXSQLVAR := aIBXSQLVAR;
1660    FOwner := aOwner;
1661    FPrepareSeqNo := FIBXSQLVAR.Parent.PrepareSeqNo;
# Line 1778 | Line 1757 | begin
1757    result := FIBXSQLVAR.GetBlobMetaData;
1758   end;
1759  
1760 + function TColumnMetaData.GetStatement: IStatement;
1761 + begin
1762 +  Result := FIBXSQLVAR.GetStatement;
1763 + end;
1764 +
1765 + function TColumnMetaData.GetTransaction: ITransaction;
1766 + begin
1767 +  Result := GetStatement.GetTransaction;
1768 + end;
1769 +
1770   { TIBSQLData }
1771  
1772   procedure TIBSQLData.CheckActive;
# Line 1797 | Line 1786 | begin
1786      IBError(ibxeBOF,[nil]);
1787   end;
1788  
1789 + function TIBSQLData.GetTransaction: ITransaction;
1790 + begin
1791 +  if FTransaction = nil then
1792 +    Result := inherited GetTransaction
1793 +  else
1794 +    Result := FTransaction;
1795 + end;
1796 +
1797   function TIBSQLData.GetIsNull: Boolean;
1798   begin
1799    CheckActive;
# Line 1840 | Line 1837 | end;
1837   { TSQLParam }
1838  
1839   procedure TSQLParam.InternalSetAsString(Value: AnsiString);
1840 +
1841 + procedure DoSetString;
1842 + begin
1843 +  Changing;
1844 +  FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
1845 +  Changed;
1846 + end;
1847 +
1848   var b: IBlob;
1849 +    dt: TDateTime;
1850 +    CurrValue: Currency;
1851 +    FloatValue: single;
1852   begin
1853    CheckActive;
1854    if IsNullable then
# Line 1866 | Line 1874 | begin
1874  
1875    SQL_VARYING,
1876    SQL_TEXT:
1877 <    begin
1870 <      Changing;
1871 <      FIBXSQLVar.SetString(Transliterate(Value,GetCodePage));
1872 <      Changed;
1873 <    end;
1877 >    DoSetString;
1878  
1879      SQL_SHORT,
1880      SQL_LONG,
1881      SQL_INT64:
1882 <      SetAsInt64(StrToInt(Value));
1882 >      if TryStrToCurr(Value,CurrValue) then
1883 >        SetAsNumeric(AdjustScaleFromCurrency(CurrValue,GetScale),GetScale)
1884 >      else
1885 >        DoSetString;
1886  
1887      SQL_D_FLOAT,
1888      SQL_DOUBLE,
1889      SQL_FLOAT:
1890 <      SetAsDouble(StrToFloat(Value));
1890 >      if TryStrToFloat(Value,FloatValue) then
1891 >        SetAsDouble(FloatValue)
1892 >      else
1893 >        DoSetString;
1894  
1895      SQL_TIMESTAMP:
1896 <      SetAsDateTime(StrToDateTime(Value));
1896 >      if TryStrToDateTime(Value,dt) then
1897 >        SetAsDateTime(dt)
1898 >      else
1899 >        DoSetString;
1900  
1901      SQL_TYPE_DATE:
1902 <      SetAsDate(StrToDateTime(Value));
1902 >      if TryStrToDateTime(Value,dt) then
1903 >        SetAsDate(dt)
1904 >      else
1905 >        DoSetString;
1906  
1907      SQL_TYPE_TIME:
1908 <      SetAsTime(StrToDateTime(Value));
1908 >      if TryStrToDateTime(Value,dt) then
1909 >        SetAsTime(dt)
1910 >      else
1911 >        DoSetString;
1912  
1913      else
1914        IBError(ibxeInvalidDataConversion,[nil]);
# Line 2475 | Line 2494 | begin
2494      end;
2495   end;
2496  
2497 + function TSQLParams.GetHasCaseSensitiveParams: Boolean;
2498 + begin
2499 +  Result := FSQLParams.CaseSensitiveParams;
2500 + end;
2501 +
2502   { TResults }
2503  
2504   procedure TResults.CheckActive;
# Line 2493 | Line 2517 | begin
2517   end;
2518  
2519   function TResults.GetISQLData(aIBXSQLVAR: TSQLVarData): ISQLData;
2520 + var col: TIBSQLData;
2521   begin
2522    if (aIBXSQLVAR.Index < 0) or (aIBXSQLVAR.Index >= getCount) then
2523      IBError(ibxeInvalidColumnIndex,[nil]);
2524  
2525    if not HasInterface(aIBXSQLVAR.Index) then
2526      AddInterface(aIBXSQLVAR.Index, TIBSQLData.Create(self,aIBXSQLVAR));
2527 <  Result := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
2527 >  col := TIBSQLData(GetInterface(aIBXSQLVAR.Index));
2528 >  col.FTransaction := GetTransaction;
2529 >  Result := col;
2530   end;
2531  
2532   constructor TResults.Create(aResults: TSQLDataArea);
# Line 2556 | Line 2583 | begin
2583    FResults.GetData(index,IsNull, len,data);
2584   end;
2585  
2586 + function TResults.GetStatement: IStatement;
2587 + begin
2588 +  Result := FStatement;
2589 + end;
2590 +
2591   function TResults.GetTransaction: ITransaction;
2592   begin
2593    Result := FStatement.GetTransaction;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines