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

Comparing ibx/trunk/fbintf/client/3.0/FB30Statement.pas (file contents):
Revision 263 by tony, Thu Dec 6 15:55:01 2018 UTC vs.
Revision 349 by tony, Mon Oct 18 08:39:40 2021 UTC

# Line 78 | Line 78 | uses
78    FB30Attachment,IBExternals, FBSQLData, FBOutputBlock, FBActivityMonitor;
79  
80   type
81
81    TFB30Statement = class;
82    TIBXSQLDA = class;
83  
# Line 101 | Line 100 | type
100      FSQLData: PByte; {Address of SQL Data in Message Buffer}
101      FSQLNullIndicator: PShort; {Address of null indicator}
102      FDataLength: integer;
103 +    FMetadataSize: integer;
104      FNullable: boolean;
105      FScale: integer;
106      FCharSetID: cardinal;
# Line 108 | Line 108 | type
108      FFieldName: AnsiString;
109  
110      protected
111 +     function CanChangeSQLType: boolean;
112       function GetSQLType: cardinal; override;
113       function GetSubtype: integer; override;
114       function GetAliasName: AnsiString;  override;
# Line 117 | Line 118 | type
118       function GetScale: integer; override;
119       function GetCharSetID: cardinal; override;
120       function GetCodePage: TSystemCodePage; override;
121 +     function GetCharSetWidth: integer; override;
122       function GetIsNull: Boolean;   override;
123       function GetIsNullable: boolean; override;
124       function GetSQLData: PByte;  override;
125       function GetDataLength: cardinal; override;
126 +     function GetSize: cardinal; override;
127 +     function GetAttachment: IAttachment; override;
128 +     function GetDefaultTextSQLType: cardinal; override;
129       procedure SetIsNull(Value: Boolean); override;
130       procedure SetIsNullable(Value: Boolean);  override;
131       procedure SetSQLData(AValue: PByte; len: cardinal); override;
# Line 128 | Line 133 | type
133       procedure SetDataLength(len: cardinal); override;
134       procedure SetSQLType(aValue: cardinal); override;
135       procedure SetCharSetID(aValue: cardinal); override;
136 <
136 >     procedure SetMetaSize(aValue: cardinal); override;
137    public
138      constructor Create(aParent: TIBXSQLDA; aIndex: integer);
139      procedure Changed; override;
140 +    procedure InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
141 +    procedure ColumnSQLDataInit;
142      procedure RowChange; override;
143      procedure FreeSQLData;
144      function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
# Line 166 | Line 173 | type
173      function GetTransaction: TFB30Transaction; virtual;
174      procedure Initialize; override;
175      function StateChanged(var ChangeSeqNo: integer): boolean; override;
176 +    function CanChangeMetaData: boolean; override;
177      property MetaData: Firebird.IMessageMetadata read FMetaData;
178      property Count: Integer read FCount write SetCount;
179      property Statement: TFB30Statement read FStatement;
# Line 179 | Line 187 | type
187      FMsgLength: integer; {Message Buffer length}
188      FCurMetaData: Firebird.IMessageMetadata;
189      procedure FreeMessageBuffer;
190 +    procedure FreeCurMetaData;
191      function GetMessageBuffer: PByte;
192      function GetMetaData: Firebird.IMessageMetadata;
193      function GetModified: Boolean;
# Line 192 | Line 201 | type
201      destructor Destroy; override;
202      procedure Bind(aMetaData: Firebird.IMessageMetadata);
203      procedure Changed; override;
204 +    procedure ReInitialise;
205      function IsInputDataArea: boolean; override;
206      property MetaData: Firebird.IMessageMetadata read GetMetaData;
207      property MessageBuffer: PByte read GetMessageBuffer;
# Line 233 | Line 243 | type
243      procedure Close;
244    end;
245  
246 +  { TBatchCompletion }
247 +
248 +  TBatchCompletion = class(TInterfaceOwner,IBatchCompletion)
249 +  private
250 +    FCompletionState: Firebird.IBatchCompletionState;
251 +    FFirebird30ClientAPI: TFB30ClientAPI;
252 +  public
253 +    constructor Create(api: TFB30ClientAPI; cs: IBatchCompletionState);
254 +    destructor Destroy; override;
255 +    {IBatchCompletion}
256 +    function getErrorStatus(var RowNo: integer; var status: IStatus): boolean;
257 +    function getTotalProcessed: cardinal;
258 +    function getState(updateNo: cardinal): TBatchCompletionState;
259 +    function getStatusMessage(updateNo: cardinal): AnsiString;
260 +    function getUpdated: integer;
261 +  end;
262 +
263    { TFB30Statement }
264  
265    TFB30Statement = class(TFBStatement,IStatement)
# Line 243 | Line 270 | type
270      FSQLRecord: TIBXOUTPUTSQLDA;
271      FResultSet: Firebird.IResultSet;
272      FCursorSeqNo: integer;
273 +    FBatch: Firebird.IBatch;
274 +    FBatchCompletion: IBatchCompletion;
275 +    FBatchRowCount: integer;
276 +    FBatchBufferSize: integer;
277 +    FBatchBufferUsed: integer;
278    protected
279 +    procedure CheckChangeBatchRowLimit; override;
280      procedure CheckHandle; override;
281 +    procedure CheckBatchModeAvailable;
282      procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
283      procedure InternalPrepare; override;
284      function InternalExecute(aTransaction: ITransaction): IResults; override;
# Line 252 | Line 286 | type
286      procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
287      procedure FreeHandle; override;
288      procedure InternalClose(Force: boolean); override;
289 +    function SavePerfStats(var Stats: TPerfStatistics): boolean;
290    public
291      constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
292        sql: AnsiString; aSQLDialect: integer);
293      constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
294 <      sql: AnsiString;  aSQLDialect: integer; GenerateParamNames: boolean =false);
294 >      sql: AnsiString;  aSQLDialect: integer; GenerateParamNames: boolean =false;
295 >      CaseSensitiveParams: boolean=false);
296      destructor Destroy; override;
297      function FetchNext: boolean;
298      property StatementIntf: Firebird.IStatement read FStatementIntf;
# Line 270 | Line 306 | type
306      function CreateBlob(column: TColumnMetaData): IBlob; override;
307      function CreateArray(column: TColumnMetaData): IArray; override;
308      procedure SetRetainInterfaces(aValue: boolean); override;
309 <
309 >    function IsInBatchMode: boolean; override;
310 >    function HasBatchMode: boolean; override;
311 >    procedure AddToBatch; override;
312 >    function ExecuteBatch(aTransaction: ITransaction
313 >      ): IBatchCompletion; override;
314 >    procedure CancelBatch; override;
315 >    function GetBatchCompletion: IBatchCompletion; override;
316   end;
317  
318   implementation
# Line 280 | Line 322 | uses IBUtils, FBMessages, FBBlob, FB30Bl
322   const
323    ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
324  
325 + { EIBBatchCompletionError }
326 +
327 + { TBatchCompletion }
328 +
329 + constructor TBatchCompletion.Create(api: TFB30ClientAPI;
330 +  cs: IBatchCompletionState);
331 + begin
332 +  inherited Create;
333 +  FFirebird30ClientAPI := api;
334 +  FCompletionState := cs;
335 + end;
336 +
337 + destructor TBatchCompletion.Destroy;
338 + begin
339 +  if FCompletionState <> nil then
340 +  begin
341 +    FCompletionState.dispose;
342 +    FCompletionState := nil;
343 +  end;
344 +  inherited Destroy;
345 + end;
346 +
347 + function TBatchCompletion.getErrorStatus(var RowNo: integer; var status: IStatus
348 +  ): boolean;
349 + var i: integer;
350 +  upcount: cardinal;
351 +  state: integer;
352 +  FBStatus: Firebird.IStatus;
353 + begin
354 +  Result := false;
355 +  RowNo := -1;
356 +  FBStatus := nil;
357 +  with FFirebird30ClientAPI do
358 +  begin
359 +    upcount := FCompletionState.getSize(StatusIntf);
360 +    Check4DataBaseError;
361 +    for i := 0 to upcount - 1 do
362 +    begin
363 +      state := FCompletionState.getState(StatusIntf,i);
364 +      if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
365 +      begin
366 +        RowNo := i+1;
367 +        FBStatus := MasterIntf.getStatus;
368 +        try
369 +          FCompletionState.getStatus(StatusIntf,FBStatus,i);
370 +          Check4DataBaseError;
371 +        except
372 +          FBStatus.dispose;
373 +          raise
374 +        end;
375 +        status := TFB30StatusObject.Create(FFirebird30ClientAPI,FBStatus,
376 +                      Format(SBatchCompletionError,[RowNo]));
377 +        status.SetIBDataBaseErrorMessages(GetStatus.GetIBDataBaseErrorMessages);
378 +        Result := true;
379 +        break;
380 +      end;
381 +    end;
382 +  end;
383 + end;
384 +
385 + function TBatchCompletion.getTotalProcessed: cardinal;
386 + begin
387 +  with FFirebird30ClientAPI do
388 +  begin
389 +    Result := FCompletionState.getsize(StatusIntf);
390 +    Check4DataBaseError;
391 +  end;
392 + end;
393 +
394 + function TBatchCompletion.getState(updateNo: cardinal): TBatchCompletionState;
395 + var state: integer;
396 + begin
397 +  with FFirebird30ClientAPI do
398 +  begin
399 +    state := FCompletionState.getState(StatusIntf,updateNo);
400 +    Check4DataBaseError;
401 +    case state of
402 +      Firebird.IBatchCompletionState.EXECUTE_FAILED:
403 +        Result := bcExecuteFailed;
404 +
405 +      Firebird.IBatchCompletionState.SUCCESS_NO_INFO:
406 +        Result := bcSuccessNoInfo;
407 +
408 +     else
409 +        Result := bcNoMoreErrors;
410 +    end;
411 +  end;
412 + end;
413 +
414 + function TBatchCompletion.getStatusMessage(updateNo: cardinal): AnsiString;
415 + var status: Firebird.IStatus;
416 + begin
417 +  with FFirebird30ClientAPI do
418 +  begin
419 +    status := MasterIntf.getStatus;
420 +    FCompletionState.getStatus(StatusIntf,status,updateNo);
421 +    Check4DataBaseError;
422 +    Result := FormatFBStatus(status);
423 +  end;
424 + end;
425 +
426 + function TBatchCompletion.getUpdated: integer;
427 + var i: integer;
428 +    upcount: cardinal;
429 +    state: integer;
430 + begin
431 +  Result := 0;
432 +  with FFirebird30ClientAPI do
433 +  begin
434 +    upcount := FCompletionState.getSize(StatusIntf);
435 +    Check4DataBaseError;
436 +    for i := 0 to upcount -1  do
437 +    begin
438 +      state := FCompletionState.getState(StatusIntf,i);
439 +      if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
440 +          break;
441 +      Inc(Result);
442 +    end;
443 +  end;
444 + end;
445 +
446   { TIBXSQLVAR }
447  
448   procedure TIBXSQLVAR.Changed;
# Line 288 | Line 451 | begin
451    TIBXSQLDA(Parent).Changed;
452   end;
453  
454 + procedure TIBXSQLVAR.InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
455 + begin
456 +  with FFirebird30ClientAPI do
457 +  begin
458 +    FSQLType := aMetaData.getType(StatusIntf,Index);
459 +    Check4DataBaseError;
460 +    if FSQLType = SQL_BLOB then
461 +    begin
462 +      FSQLSubType := aMetaData.getSubType(StatusIntf,Index);
463 +      Check4DataBaseError;
464 +    end
465 +    else
466 +      FSQLSubType := 0;
467 +    FDataLength := aMetaData.getLength(StatusIntf,Index);
468 +    Check4DataBaseError;
469 +    FMetadataSize := FDataLength;
470 +    FRelationName := strpas(aMetaData.getRelation(StatusIntf,Index));
471 +    Check4DataBaseError;
472 +    FFieldName := strpas(aMetaData.getField(StatusIntf,Index));
473 +    Check4DataBaseError;
474 +    FNullable := aMetaData.isNullable(StatusIntf,Index);
475 +    Check4DataBaseError;
476 +    FScale := aMetaData.getScale(StatusIntf,Index);
477 +    Check4DataBaseError;
478 +    FCharSetID :=  aMetaData.getCharSet(StatusIntf,Index) and $FF;
479 +    Check4DataBaseError;
480 +  end;
481 + end;
482 +
483 + procedure TIBXSQLVAR.ColumnSQLDataInit;
484 + begin
485 +  FreeSQLData;
486 +  with FFirebird30ClientAPI do
487 +  begin
488 +    case SQLType of
489 +      SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
490 +      SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
491 +      SQL_LONG, SQL_INT64, SQL_INT128, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT,
492 +      SQL_TIMESTAMP_TZ, SQL_TIME_TZ, SQL_DEC_FIXED, SQL_DEC16, SQL_DEC34,
493 +      SQL_TIMESTAMP_TZ_EX, SQL_TIME_TZ_EX:
494 +      begin
495 +        if (FDataLength = 0) then
496 +          { Make sure you get a valid pointer anyway
497 +           select '' from foo }
498 +          IBAlloc(FSQLData, 0, 1)
499 +        else
500 +          IBAlloc(FSQLData, 0, FDataLength)
501 +      end;
502 +      SQL_VARYING:
503 +        IBAlloc(FSQLData, 0, FDataLength + 2);
504 +     else
505 +        IBError(ibxeUnknownSQLDataType, [SQLType and (not 1)])
506 +    end;
507 +    FOwnsSQLData := true;
508 +    FNullIndicator := -1;
509 +  end;
510 + end;
511 +
512 + function TIBXSQLVAR.CanChangeSQLType: boolean;
513 + begin
514 +  Result := Parent.CanChangeMetaData;
515 + end;
516 +
517   function TIBXSQLVAR.GetSQLType: cardinal;
518   begin
519    Result := FSQLType;
# Line 333 | Line 559 | end;
559  
560   function TIBXSQLVAR.GetCharSetID: cardinal;
561   begin
562 <  result := 0;
562 >  result := 0; {NONE}
563    case SQLType of
564    SQL_VARYING, SQL_TEXT:
565        result := FCharSetID;
566  
567    SQL_BLOB:
568      if (SQLSubType = 1) then
569 <      result := FCharSetID;
569 >      result := FCharSetID
570 >    else
571 >      result := 1; {OCTETS}
572  
573    SQL_ARRAY:
574      if (FRelationName <> '') and (FFieldName <> '') then
# Line 348 | Line 576 | begin
576      else
577        result := FCharSetID;
578    end;
351  result := result;
579   end;
580  
581   function TIBXSQLVAR.GetCodePage: TSystemCodePage;
# Line 358 | Line 585 | begin
585       CharSetID2CodePage(GetCharSetID,result);
586   end;
587  
588 + function TIBXSQLVAR.GetCharSetWidth: integer;
589 + begin
590 +  result := 1;
591 +  with Statement.GetAttachment DO
592 +    CharSetWidth(GetCharSetID,result);
593 + end;
594 +
595   function TIBXSQLVAR.GetIsNull: Boolean;
596   begin
597    Result := IsNullable and (FSQLNullIndicator^ = -1);
# Line 378 | Line 612 | begin
612    Result := FDataLength;
613   end;
614  
615 + function TIBXSQLVAR.GetSize: cardinal;
616 + begin
617 +  Result := FMetadataSize;
618 + end;
619 +
620 + function TIBXSQLVAR.GetAttachment: IAttachment;
621 + begin
622 +  Result := FStatement.GetAttachment;
623 + end;
624 +
625   function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
626   begin
627    if GetSQLType <> SQL_ARRAY then
# Line 459 | Line 703 | end;
703  
704   procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
705   begin
706 +  if (FSQLType <> aValue) and not CanChangeSQLType then
707 +    IBError(ibxeSQLTypeUnchangeable,[TSQLDataItem.GetSQLTypeName(FSQLType),TSQLDataItem.GetSQLTypeName(aValue)]);
708    FSQLType := aValue;
709    Changed;
710   end;
# Line 469 | Line 715 | begin
715    Changed;
716   end;
717  
718 + procedure TIBXSQLVAR.SetMetaSize(aValue: cardinal);
719 + begin
720 +  if (aValue > FMetaDataSize) and not CanChangeSQLType then
721 +    IBError(ibxeCannotIncreaseMetadatasize,[FMetaDataSize,aValue]);
722 +  FMetaDataSize := aValue;
723 + end;
724 +
725 + function TIBXSQLVAR.GetDefaultTextSQLType: cardinal;
726 + begin
727 +  Result := SQL_VARYING;
728 + end;
729 +
730   constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
731   begin
732    inherited Create(aParent,aIndex);
# Line 598 | Line 856 | end;
856  
857   procedure TIBXINPUTSQLDA.FreeMessageBuffer;
858   begin
601  if FCurMetaData <> nil then
602  begin
603    FCurMetaData.release;
604    FCurMetaData := nil;
605  end;
859    if FMessageBuffer <> nil then
860    begin
861      FreeMem(FMessageBuffer);
# Line 611 | Line 864 | begin
864    FMsgLength := 0;
865   end;
866  
867 + procedure TIBXINPUTSQLDA.FreeCurMetaData;
868 + begin
869 +  if FCurMetaData <> nil then
870 +  begin
871 +    FCurMetaData.release;
872 +    FCurMetaData := nil;
873 +  end;
874 + end;
875 +
876   function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
877   begin
878    PackBuffer;
# Line 633 | Line 895 | procedure TIBXINPUTSQLDA.BuildMetadata;
895   var Builder: Firebird.IMetadataBuilder;
896      i: integer;
897   begin
898 <  if FCurMetaData = nil then
898 >  if (FCurMetaData = nil) and (Count > 0) then
899    with FFirebird30ClientAPI do
900    begin
901 <    Builder := inherited MetaData.getBuilder(StatusIntf);
901 >    Builder := FFirebird30ClientAPI.MasterIntf.getMetadataBuilder(StatusIntf,Count);
902      Check4DataBaseError;
903      try
904        for i := 0 to Count - 1 do
905        with TIBXSQLVar(Column[i]) do
906        begin
907 <        Builder.setType(StatusIntf,i,FSQLType);
907 >        Builder.setType(StatusIntf,i,FSQLType+1);
908          Check4DataBaseError;
909          Builder.setSubType(StatusIntf,i,FSQLSubType);
910          Check4DataBaseError;
911 <        Builder.setLength(StatusIntf,i,FDataLength);
911 > //        writeln('Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
912 >        if FSQLType = SQL_VARYING then
913 >        begin
914 >          {The datalength can be greater than the metadata size when SQLType has been overridden to text}
915 >          if (GetDataLength > GetSize) and CanChangeMetaData then
916 >            Builder.setLength(StatusIntf,i,GetDataLength)
917 >          else
918 >            Builder.setLength(StatusIntf,i,GetSize)
919 >        end
920 >        else
921 >          Builder.setLength(StatusIntf,i,GetDataLength);
922          Check4DataBaseError;
923          Builder.setCharSet(StatusIntf,i,GetCharSetID);
924          Check4DataBaseError;
# Line 663 | Line 935 | end;
935  
936   procedure TIBXINPUTSQLDA.PackBuffer;
937   var i: integer;
938 +    P: PByte;
939   begin
940    BuildMetadata;
941  
942 <  if FMsgLength = 0 then
942 >  if (FMsgLength = 0) and (FCurMetaData <> nil) then
943    with FFirebird30ClientAPI do
944    begin
945      FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
# Line 677 | Line 950 | begin
950      for i := 0 to Count - 1 do
951      with TIBXSQLVar(Column[i]) do
952      begin
953 +      P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
954 + //     writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
955        if not Modified then
956          IBError(ibxeUninitializedInputParameter,[i,Name]);
682
957        if IsNull then
958 <        FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
958 >        FillChar(P^,FDataLength,0)
959        else
960        if FSQLData <> nil then
961 <        Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
962 <      Check4DataBaseError;
961 >      begin
962 >        if SQLType = SQL_VARYING then
963 >        begin
964 >            EncodeInteger(FDataLength,2,P);
965 >            Inc(P,2);
966 >        end
967 >        else
968 >        if (SQLType = SQL_BLOB) and (FStatement.FBatch <> nil) then
969 >        begin
970 >          FStatement.FBatch.registerBlob(Statusintf,ISC_QUADPtr(FSQLData),ISC_QUADPtr(FSQLData));
971 >          Check4DatabaseError;
972 >        end;
973 >        Move(FSQLData^,P^,FDataLength);
974 >      end;
975        if IsNullable then
976        begin
977          Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
# Line 698 | Line 984 | end;
984   procedure TIBXINPUTSQLDA.FreeXSQLDA;
985   begin
986    inherited FreeXSQLDA;
987 +  FreeCurMetaData;
988    FreeMessageBuffer;
989   end;
990  
# Line 709 | Line 996 | end;
996  
997   destructor TIBXINPUTSQLDA.Destroy;
998   begin
999 <  FreeMessageBuffer;
999 >  FreeXSQLDA;
1000    inherited Destroy;
1001   end;
1002  
# Line 719 | Line 1006 | begin
1006    FMetaData := aMetaData;
1007    with FFirebird30ClientAPI do
1008    begin
1009 <    Count := metadata.getCount(StatusIntf);
1009 >    Count := aMetadata.getCount(StatusIntf);
1010      Check4DataBaseError;
1011      Initialize;
1012  
1013      for i := 0 to Count - 1 do
1014      with TIBXSQLVar(Column[i]) do
1015      begin
1016 <      FSQLType := aMetaData.getType(StatusIntf,i);
1017 <      Check4DataBaseError;
731 <      if FSQLType = SQL_BLOB then
732 <      begin
733 <        FSQLSubType := aMetaData.getSubType(StatusIntf,i);
734 <        Check4DataBaseError;
735 <      end
736 <      else
737 <        FSQLSubType := 0;
738 <      FDataLength := aMetaData.getLength(StatusIntf,i);
739 <      Check4DataBaseError;
740 <      case SQLType of
741 <        SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
742 <        SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
743 <        SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
744 <        begin
745 <          if (FDataLength = 0) then
746 <            { Make sure you get a valid pointer anyway
747 <             select '' from foo }
748 <            IBAlloc(FSQLData, 0, 1)
749 <          else
750 <            IBAlloc(FSQLData, 0, FDataLength)
751 <        end;
752 <        SQL_VARYING:
753 <          IBAlloc(FSQLData, 0, FDataLength + 2);
754 <       else
755 <          IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
756 <      end;
757 <      FNullable := aMetaData.isNullable(StatusIntf,i);
758 <      FOwnsSQLData := true;
759 <      Check4DataBaseError;
760 <      FNullIndicator := -1;
1016 >      InitColumnMetaData(aMetaData);
1017 >      SaveMetaData;
1018        if FNullable then
1019          FSQLNullIndicator := @FNullIndicator
1020        else
1021          FSQLNullIndicator := nil;
1022 <      FScale := aMetaData.getScale(StatusIntf,i);
766 <      Check4DataBaseError;
767 <      FCharSetID :=  aMetaData.getCharSet(StatusIntf,i) and $FF;
768 <      Check4DataBaseError;
1022 >      ColumnSQLDataInit;
1023      end;
1024    end;
1025   end;
# Line 773 | Line 1027 | end;
1027   procedure TIBXINPUTSQLDA.Changed;
1028   begin
1029    inherited Changed;
1030 +  FreeCurMetaData;
1031    FreeMessageBuffer;
1032   end;
1033  
1034 + procedure TIBXINPUTSQLDA.ReInitialise;
1035 + var i: integer;
1036 + begin
1037 +  FreeMessageBuffer;
1038 +  for i := 0 to Count - 1 do
1039 +    TIBXSQLVar(Column[i]).ColumnSQLDataInit;
1040 + end;
1041 +
1042   function TIBXINPUTSQLDA.IsInputDataArea: boolean;
1043   begin
1044    Result := true;
# Line 808 | Line 1071 | begin
1071      for i := 0 to Count - 1 do
1072      with TIBXSQLVar(Column[i]) do
1073      begin
1074 <      FSQLType := aMetaData.getType(StatusIntf,i);
812 <      Check4DataBaseError;
813 <      if FSQLType = SQL_BLOB then
814 <      begin
815 <        FSQLSubType := aMetaData.getSubType(StatusIntf,i);
816 <        Check4DataBaseError;
817 <      end
818 <      else
819 <        FSQLSubType := 0;
820 <      FBlob := nil;
821 <      FArray := nil;
1074 >      InitColumnMetaData(aMetaData);
1075        FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
1076        Check4DataBaseError;
824      FDataLength := aMetaData.getLength(StatusIntf,i);
825      Check4DataBaseError;
826      FRelationName := strpas(aMetaData.getRelation(StatusIntf,i));
827      Check4DataBaseError;
828      FFieldName := strpas(aMetaData.getField(StatusIntf,i));
829      Check4DataBaseError;
830      FNullable := aMetaData.isNullable(StatusIntf,i);
831      Check4DataBaseError;
1077        if FNullable then
1078        begin
1079          FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
# Line 836 | Line 1081 | begin
1081        end
1082        else
1083          FSQLNullIndicator := nil;
1084 <      FScale := aMetaData.getScale(StatusIntf,i);
1085 <      Check4DataBaseError;
841 <      FCharSetID :=  aMetaData.getCharSet(StatusIntf,i) and $FF;
842 <      Check4DataBaseError;
1084 >      FBlob := nil;
1085 >      FArray := nil;
1086      end;
1087    end;
1088    SetUniqueRelationName;
# Line 933 | Line 1176 | begin
1176      ChangeSeqNo := FStatement.ChangeSeqNo;
1177   end;
1178  
1179 + function TIBXSQLDA.CanChangeMetaData: boolean;
1180 + begin
1181 +  Result := FStatement.FBatch = nil;
1182 + end;
1183 +
1184   procedure TIBXSQLDA.SetCount(Value: Integer);
1185   var
1186    i: Integer;
# Line 964 | Line 1212 | begin
1212      TIBXSQLVAR(Column[i]).FreeSQLData;
1213    for i := 0 to FSize - 1  do
1214      TIBXSQLVAR(Column[i]).Free;
1215 +  FCount := 0;
1216    SetLength(FColumnList,0);
1217    FSize := 0;
1218   end;
# Line 980 | Line 1229 | end;
1229  
1230   { TFB30Statement }
1231  
1232 + procedure TFB30Statement.CheckChangeBatchRowLimit;
1233 + begin
1234 +  if IsInBatchMode then
1235 +    IBError(ibxeInBatchMode,[nil]);
1236 + end;
1237 +
1238   procedure TFB30Statement.CheckHandle;
1239   begin
1240    if FStatementIntf = nil then
1241      IBError(ibxeInvalidStatementHandle,[nil]);
1242   end;
1243  
1244 + procedure TFB30Statement.CheckBatchModeAvailable;
1245 + begin
1246 +  if not HasBatchMode then
1247 +    IBError(ibxeBatchModeNotSupported,[nil]);
1248 +  case SQLStatementType of
1249 +  SQLInsert,
1250 +  SQLUpdate: {OK};
1251 +  else
1252 +     IBError(ibxeInvalidBatchQuery,[GetSQLStatementTypeName]);
1253 +  end;
1254 + end;
1255 +
1256   procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1257    );
1258   begin
# Line 1061 | Line 1328 | begin
1328        if (FStatementIntf <> nil) then
1329          FreeHandle;
1330        if E is EIBInterBaseError then
1331 <        raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
1332 <                                       EIBInterBaseError(E).IBErrorCode,
1066 <                                       EIBInterBaseError(E).Message +
1067 <                                       sSQLErrorSeparator + FSQL)
1068 <      else
1069 <        raise;
1331 >        E.Message := E.Message + sSQLErrorSeparator + FSQL;
1332 >      raise;
1333      end;
1334    end;
1335    FPrepared := true;
# Line 1087 | Line 1350 | begin
1350   end;
1351  
1352   function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1353 +
1354 +  procedure ExecuteQuery(outMetaData: Firebird.IMessageMetaData=nil; outBuffer: pointer=nil);
1355 +  begin
1356 +    with FFirebird30ClientAPI do
1357 +    begin
1358 +      SavePerfStats(FBeforeStats);
1359 +      FStatementIntf.execute(StatusIntf,
1360 +                             (aTransaction as TFB30Transaction).TransactionIntf,
1361 +                             FSQLParams.MetaData,
1362 +                             FSQLParams.MessageBuffer,
1363 +                             outMetaData,
1364 +                             outBuffer);
1365 +      Check4DataBaseError;
1366 +      FStatisticsAvailable := SavePerfStats(FAfterStats);
1367 +    end;
1368 +  end;
1369 +
1370 +
1371   begin
1372    Result := nil;
1373 +  FBatchCompletion := nil;
1374    FBOF := false;
1375    FEOF := false;
1376    FSingleResults := false;
1377 +  FStatisticsAvailable := false;
1378 +  if IsInBatchMode then
1379 +    IBerror(ibxeInBatchMode,[]);
1380    CheckTransaction(aTransaction);
1381    if not FPrepared then
1382      InternalPrepare;
1383    CheckHandle;
1384    if aTransaction <> FTransactionIntf then
1385      AddMonitor(aTransaction as TFB30Transaction);
1386 <  if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1386 >  if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1387      IBError(ibxeInterfaceOutofDate,[nil]);
1388  
1389 +
1390    try
1391      with FFirebird30ClientAPI do
1392      begin
1107      if FCollectStatistics then
1108      begin
1109        UtilIntf.getPerfCounters(StatusIntf,
1110                      (GetAttachment as TFB30Attachment).AttachmentIntf,
1111                      ISQL_COUNTERS,@FBeforeStats);
1112        Check4DataBaseError;
1113      end;
1114
1393        case FSQLStatementType of
1394        SQLSelect:
1395          IBError(ibxeIsAExecuteProcedure,[]);
1396  
1397        SQLExecProcedure:
1398        begin
1399 <        FStatementIntf.execute(StatusIntf,
1122 <                               (aTransaction as TFB30Transaction).TransactionIntf,
1123 <                               FSQLParams.MetaData,
1124 <                               FSQLParams.MessageBuffer,
1125 <                               FSQLRecord.MetaData,
1126 <                               FSQLRecord.MessageBuffer);
1127 <        Check4DataBaseError;
1128 <
1399 >        ExecuteQuery(FSQLRecord.MetaData,FSQLRecord.MessageBuffer);
1400          Result := TResults.Create(FSQLRecord);
1401          FSingleResults := true;
1131      end
1132      else
1133        FStatementIntf.execute(StatusIntf,
1134                               (aTransaction as TFB30Transaction).TransactionIntf,
1135                               FSQLParams.MetaData,
1136                               FSQLParams.MessageBuffer,
1137                               nil,
1138                               nil);
1139        Check4DataBaseError;
1402        end;
1403 <      if FCollectStatistics then
1404 <      begin
1405 <        UtilIntf.getPerfCounters(StatusIntf,
1144 <                  (GetAttachment as TFB30Attachment).AttachmentIntf,
1145 <                  ISQL_COUNTERS, @FAfterStats);
1146 <        Check4DataBaseError;
1147 <        FStatisticsAvailable := true;
1403 >
1404 >      else
1405 >        ExecuteQuery;
1406        end;
1407      end;
1408    finally
# Line 1164 | Line 1422 | begin
1422    if FSQLStatementType <> SQLSelect then
1423     IBError(ibxeIsASelectStatement,[]);
1424  
1425 < CheckTransaction(aTransaction);
1425 >  FBatchCompletion := nil;
1426 >  CheckTransaction(aTransaction);
1427    if not FPrepared then
1428      InternalPrepare;
1429    CheckHandle;
1430    if aTransaction <> FTransactionIntf then
1431      AddMonitor(aTransaction as TFB30Transaction);
1432 <  if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1432 >  if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1433      IBError(ibxeInterfaceOutofDate,[nil]);
1434  
1435   with FFirebird30ClientAPI do
# Line 1223 | Line 1482 | procedure TFB30Statement.FreeHandle;
1482   begin
1483    Close;
1484    ReleaseInterfaces;
1485 +  if FBatch <> nil then
1486 +  begin
1487 +    FBatch.release;
1488 +    FBatch := nil;
1489 +  end;
1490    if FStatementIntf <> nil then
1491    begin
1492      FStatementIntf.release;
# Line 1259 | Line 1523 | begin
1523    Inc(FChangeSeqNo);
1524   end;
1525  
1526 + function TFB30Statement.SavePerfStats(var Stats: TPerfStatistics): boolean;
1527 + begin
1528 +  Result := false;
1529 +  if FCollectStatistics then
1530 +  with FFirebird30ClientAPI do
1531 +  begin
1532 +    UtilIntf.getPerfCounters(StatusIntf,
1533 +              (GetAttachment as TFB30Attachment).AttachmentIntf,
1534 +              ISQL_COUNTERS, @Stats);
1535 +    Check4DataBaseError;
1536 +    Result := true;
1537 +  end;
1538 + end;
1539 +
1540   constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1541    Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1542   begin
# Line 1271 | Line 1549 | end;
1549  
1550   constructor TFB30Statement.CreateWithParameterNames(
1551    Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1552 <  aSQLDialect: integer; GenerateParamNames: boolean);
1552 >  aSQLDialect: integer; GenerateParamNames: boolean;
1553 >  CaseSensitiveParams: boolean);
1554   begin
1555    inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1556    FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1557    FSQLParams := TIBXINPUTSQLDA.Create(self);
1558 +  FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1559    FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1560    InternalPrepare;
1561   end;
# Line 1394 | Line 1674 | begin
1674      TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1675   end;
1676  
1677 + function TFB30Statement.IsInBatchMode: boolean;
1678 + begin
1679 +  Result := FBatch <> nil;
1680 + end;
1681 +
1682 + function TFB30Statement.HasBatchMode: boolean;
1683 + begin
1684 +  Result := GetAttachment.HasBatchMode;
1685 + end;
1686 +
1687 + procedure TFB30Statement.AddToBatch;
1688 + var BatchPB: TXPBParameterBlock;
1689 +
1690 + const SixteenMB = 16 * 1024 * 1024;
1691 + begin
1692 +  FBatchCompletion := nil;
1693 +  if not FPrepared then
1694 +    InternalPrepare;
1695 +  CheckHandle;
1696 +  CheckBatchModeAvailable;
1697 +  with FFirebird30ClientAPI do
1698 +  begin
1699 +    if FBatch = nil then
1700 +    begin
1701 +      {Start Batch}
1702 +      BatchPB := TXPBParameterBlock.Create(FFirebird30ClientAPI,Firebird.IXpbBuilder.BATCH);
1703 +      with FFirebird30ClientAPI do
1704 +      try
1705 +        FBatchBufferSize := FBatchRowLimit * FSQLParams.MetaData.getAlignedLength(StatusIntf);
1706 +        Check4DatabaseError;
1707 +        if FBatchBufferSize < SixteenMB then
1708 +          FBatchBufferSize := SixteenMB;
1709 +        if FBatchBufferSize > 256 * 1024 *1024 {assumed limit} then
1710 +          IBError(ibxeBatchBufferSizeTooBig,[FBatchBufferSize]);
1711 +
1712 +        BatchPB.insertInt(Firebird.IBatch.TAG_RECORD_COUNTS,1);
1713 +        BatchPB.insertInt(Firebird.IBatch.TAG_BUFFER_BYTES_SIZE,FBatchBufferSize);
1714 +        FBatch := FStatementIntf.createBatch(StatusIntf,
1715 +                                             FSQLParams.MetaData,
1716 +                                             BatchPB.getDataLength,
1717 +                                             BatchPB.getBuffer);
1718 +        Check4DataBaseError;
1719 +
1720 +      finally
1721 +        BatchPB.Free;
1722 +      end;
1723 +      FBatchRowCount := 0;
1724 +      FBatchBufferUsed := 0;
1725 +    end;
1726 +
1727 +    Inc(FBatchRowCount);
1728 +    Inc(FBatchBufferUsed,FSQLParams.MetaData.getAlignedLength(StatusIntf));
1729 +    Check4DataBaseError;
1730 +    if FBatchBufferUsed > FBatchBufferSize then
1731 +      raise EIBBatchBufferOverflow.Create(Ord(ibxeBatchRowBufferOverflow),
1732 +                              Format(GetErrorMessage(ibxeBatchRowBufferOverflow),
1733 +                              [FBatchRowCount,FBatchBufferSize]));
1734 +
1735 +    FBatch.Add(StatusIntf,1,FSQLParams.GetMessageBuffer);
1736 +      Check4DataBaseError
1737 +  end;
1738 + end;
1739 +
1740 + function TFB30Statement.ExecuteBatch(aTransaction: ITransaction
1741 +  ): IBatchCompletion;
1742 +
1743 + procedure Check4BatchCompletionError(bc: IBatchCompletion);
1744 + var status: IStatus;
1745 +    RowNo: integer;
1746 + begin
1747 +  status := nil;
1748 +  {Raise an exception if there was an error reported in the BatchCompletion}
1749 +  if (bc <> nil) and bc.getErrorStatus(RowNo,status) then
1750 +    raise EIBInterbaseError.Create(status);
1751 + end;
1752 +
1753 + var cs: Firebird.IBatchCompletionState;
1754 +
1755 + begin
1756 +  Result := nil;
1757 +  if FBatch = nil then
1758 +    IBError(ibxeNotInBatchMode,[]);
1759 +
1760 +  with FFirebird30ClientAPI do
1761 +  begin
1762 +    SavePerfStats(FBeforeStats);
1763 +    if aTransaction = nil then
1764 +      cs := FBatch.execute(StatusIntf,(FTransactionIntf as TFB30Transaction).TransactionIntf)
1765 +    else
1766 +      cs := FBatch.execute(StatusIntf,(aTransaction as TFB30Transaction).TransactionIntf);
1767 +    Check4DataBaseError;
1768 +    FBatchCompletion := TBatchCompletion.Create(FFirebird30ClientAPI,cs);
1769 +    FStatisticsAvailable := SavePerfStats(FAfterStats);
1770 +    FBatch.release;
1771 +    FBatch := nil;
1772 +    Check4BatchCompletionError(FBatchCompletion);
1773 +    Result := FBatchCompletion;
1774 +  end;
1775 + end;
1776 +
1777 + procedure TFB30Statement.CancelBatch;
1778 + begin
1779 +  if FBatch = nil then
1780 +    IBError(ibxeNotInBatchMode,[]);
1781 +  FBatch.release;
1782 +  FBatch := nil;
1783 + end;
1784 +
1785 + function TFB30Statement.GetBatchCompletion: IBatchCompletion;
1786 + begin
1787 +  Result := FBatchCompletion;
1788 + end;
1789 +
1790   function TFB30Statement.IsPrepared: boolean;
1791   begin
1792    Result := FStatementIntf <> nil;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines