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

Comparing ibx/trunk/fbintf/client/3.0/FB30Statement.pas (file contents):
Revision 344 by tony, Wed Jun 9 12:07:56 2021 UTC vs.
Revision 345 by tony, Mon Aug 23 14:22:29 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 109 | 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 124 | Line 124 | type
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 131 | 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 ColumnSQLDataInit;
141      procedure RowChange; override;
142      procedure FreeSQLData;
143      function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
# Line 169 | Line 172 | type
172      function GetTransaction: TFB30Transaction; virtual;
173      procedure Initialize; override;
174      function StateChanged(var ChangeSeqNo: integer): boolean; override;
175 +    function CanChangeMetaData: boolean; override;
176      property MetaData: Firebird.IMessageMetadata read FMetaData;
177      property Count: Integer read FCount write SetCount;
178      property Statement: TFB30Statement read FStatement;
# Line 182 | Line 186 | type
186      FMsgLength: integer; {Message Buffer length}
187      FCurMetaData: Firebird.IMessageMetadata;
188      procedure FreeMessageBuffer;
189 +    procedure FreeCurMetaData;
190      function GetMessageBuffer: PByte;
191      function GetMetaData: Firebird.IMessageMetadata;
192      function GetModified: Boolean;
# Line 195 | Line 200 | type
200      destructor Destroy; override;
201      procedure Bind(aMetaData: Firebird.IMessageMetadata);
202      procedure Changed; override;
203 +    procedure ReInitialise;
204      function IsInputDataArea: boolean; override;
205      property MetaData: Firebird.IMessageMetadata read GetMetaData;
206      property MessageBuffer: PByte read GetMessageBuffer;
# Line 236 | Line 242 | type
242      procedure Close;
243    end;
244  
245 +  { TBatchCompletion }
246 +
247 +  TBatchCompletion = class(TInterfaceOwner,IBatchCompletion)
248 +  private
249 +    FCompletionState: Firebird.IBatchCompletionState;
250 +    FFirebird30ClientAPI: TFB30ClientAPI;
251 +  public
252 +    constructor Create(api: TFB30ClientAPI; cs: IBatchCompletionState);
253 +    destructor Destroy; override;
254 +    {IBatchCompletion}
255 +    function getErrorStatus(var RowNo: integer; var status: IStatus): boolean;
256 +    function getTotalProcessed: cardinal;
257 +    function getState(updateNo: cardinal): TBatchCompletionState;
258 +    function getStatusMessage(updateNo: cardinal): AnsiString;
259 +    function getUpdated: integer;
260 +  end;
261 +
262    { TFB30Statement }
263  
264    TFB30Statement = class(TFBStatement,IStatement)
# Line 246 | Line 269 | type
269      FSQLRecord: TIBXOUTPUTSQLDA;
270      FResultSet: Firebird.IResultSet;
271      FCursorSeqNo: integer;
272 +    FBatch: Firebird.IBatch;
273 +    FBatchCompletion: IBatchCompletion;
274 +    FBatchRowCount: integer;
275 +    FBatchBufferSize: integer;
276 +    FBatchBufferUsed: integer;
277    protected
278 +    procedure CheckChangeBatchRowLimit; override;
279      procedure CheckHandle; override;
280 +    procedure CheckBatchModeAvailable;
281      procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
282      procedure InternalPrepare; override;
283      function InternalExecute(aTransaction: ITransaction): IResults; override;
# Line 255 | Line 285 | type
285      procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
286      procedure FreeHandle; override;
287      procedure InternalClose(Force: boolean); override;
288 +    function SavePerfStats(var Stats: TPerfStatistics): boolean;
289    public
290      constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
291        sql: AnsiString; aSQLDialect: integer);
# Line 274 | Line 305 | type
305      function CreateBlob(column: TColumnMetaData): IBlob; override;
306      function CreateArray(column: TColumnMetaData): IArray; override;
307      procedure SetRetainInterfaces(aValue: boolean); override;
308 <
308 >    function IsInBatchMode: boolean; override;
309 >    function HasBatchMode: boolean; override;
310 >    procedure AddToBatch; override;
311 >    function ExecuteBatch(aTransaction: ITransaction
312 >      ): IBatchCompletion; override;
313 >    procedure CancelBatch; override;
314 >    function GetBatchCompletion: IBatchCompletion; override;
315   end;
316  
317   implementation
# Line 284 | Line 321 | uses IBUtils, FBMessages, FBBlob, FB30Bl
321   const
322    ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
323  
324 + { EIBBatchCompletionError }
325 +
326 + { TBatchCompletion }
327 +
328 + constructor TBatchCompletion.Create(api: TFB30ClientAPI;
329 +  cs: IBatchCompletionState);
330 + begin
331 +  inherited Create;
332 +  FFirebird30ClientAPI := api;
333 +  FCompletionState := cs;
334 + end;
335 +
336 + destructor TBatchCompletion.Destroy;
337 + begin
338 +  if FCompletionState <> nil then
339 +  begin
340 +    FCompletionState.dispose;
341 +    FCompletionState := nil;
342 +  end;
343 +  inherited Destroy;
344 + end;
345 +
346 + function TBatchCompletion.getErrorStatus(var RowNo: integer; var status: IStatus
347 +  ): boolean;
348 + var i: integer;
349 +  upcount: cardinal;
350 +  state: integer;
351 +  FBStatus: Firebird.IStatus;
352 + begin
353 +  Result := false;
354 +  RowNo := -1;
355 +  FBStatus := nil;
356 +  with FFirebird30ClientAPI do
357 +  begin
358 +    upcount := FCompletionState.getSize(StatusIntf);
359 +    Check4DataBaseError;
360 +    for i := 0 to upcount - 1 do
361 +    begin
362 +      state := FCompletionState.getState(StatusIntf,i);
363 +      if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
364 +      begin
365 +        RowNo := i+1;
366 +        FBStatus := MasterIntf.getStatus;
367 +        try
368 +          FCompletionState.getStatus(StatusIntf,FBStatus,i);
369 +          Check4DataBaseError;
370 +        except
371 +          FBStatus.dispose;
372 +          raise
373 +        end;
374 +        status := TFB30StatusObject.Create(FFirebird30ClientAPI,FBStatus,
375 +                      Format(SBatchCompletionError,[RowNo]));
376 +        status.SetIBDataBaseErrorMessages(GetStatus.GetIBDataBaseErrorMessages);
377 +        Result := true;
378 +        break;
379 +      end;
380 +    end;
381 +  end;
382 + end;
383 +
384 + function TBatchCompletion.getTotalProcessed: cardinal;
385 + begin
386 +  with FFirebird30ClientAPI do
387 +  begin
388 +    Result := FCompletionState.getsize(StatusIntf);
389 +    Check4DataBaseError;
390 +  end;
391 + end;
392 +
393 + function TBatchCompletion.getState(updateNo: cardinal): TBatchCompletionState;
394 + var state: integer;
395 + begin
396 +  with FFirebird30ClientAPI do
397 +  begin
398 +    state := FCompletionState.getState(StatusIntf,updateNo);
399 +    Check4DataBaseError;
400 +    case state of
401 +      Firebird.IBatchCompletionState.EXECUTE_FAILED:
402 +        Result := bcExecuteFailed;
403 +
404 +      Firebird.IBatchCompletionState.SUCCESS_NO_INFO:
405 +        Result := bcSuccessNoInfo;
406 +
407 +     else
408 +        Result := bcNoMoreErrors;
409 +    end;
410 +  end;
411 + end;
412 +
413 + function TBatchCompletion.getStatusMessage(updateNo: cardinal): AnsiString;
414 + var status: Firebird.IStatus;
415 + begin
416 +  with FFirebird30ClientAPI do
417 +  begin
418 +    status := MasterIntf.getStatus;
419 +    FCompletionState.getStatus(StatusIntf,status,updateNo);
420 +    Check4DataBaseError;
421 +    Result := FormatFBStatus(status);
422 +  end;
423 + end;
424 +
425 + function TBatchCompletion.getUpdated: integer;
426 + var i: integer;
427 +    upcount: cardinal;
428 +    state: integer;
429 + begin
430 +  Result := 0;
431 +  with FFirebird30ClientAPI do
432 +  begin
433 +    upcount := FCompletionState.getSize(StatusIntf);
434 +    Check4DataBaseError;
435 +    for i := 0 to upcount -1  do
436 +    begin
437 +      state := FCompletionState.getState(StatusIntf,i);
438 +      if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
439 +          break;
440 +      Inc(Result);
441 +    end;
442 +  end;
443 + end;
444 +
445   { TIBXSQLVAR }
446  
447   procedure TIBXSQLVAR.Changed;
# Line 292 | Line 450 | begin
450    TIBXSQLDA(Parent).Changed;
451   end;
452  
453 + procedure TIBXSQLVAR.ColumnSQLDataInit;
454 + begin
455 +  FreeSQLData;
456 +  with FFirebird30ClientAPI do
457 +  begin
458 +    case SQLType of
459 +      SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
460 +      SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
461 +      SQL_LONG, SQL_INT64, SQL_INT128, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT,
462 +      SQL_TIMESTAMP_TZ, SQL_TIME_TZ, SQL_DEC_FIXED, SQL_DEC16, SQL_DEC34,
463 +      SQL_TIMESTAMP_TZ_EX, SQL_TIME_TZ_EX:
464 +      begin
465 +        if (FDataLength = 0) then
466 +          { Make sure you get a valid pointer anyway
467 +           select '' from foo }
468 +          IBAlloc(FSQLData, 0, 1)
469 +        else
470 +          IBAlloc(FSQLData, 0, FDataLength)
471 +      end;
472 +      SQL_VARYING:
473 +        IBAlloc(FSQLData, 0, FDataLength + 2);
474 +     else
475 +        IBError(ibxeUnknownSQLDataType, [SQLType and (not 1)])
476 +    end;
477 +    FOwnsSQLData := true;
478 +    FNullIndicator := -1;
479 +  end;
480 + end;
481 +
482 + function TIBXSQLVAR.CanChangeSQLType: boolean;
483 + begin
484 +  Result := Parent.CanChangeMetaData;
485 + end;
486 +
487   function TIBXSQLVAR.GetSQLType: cardinal;
488   begin
489    Result := FSQLType;
# Line 337 | Line 529 | end;
529  
530   function TIBXSQLVAR.GetCharSetID: cardinal;
531   begin
532 <  result := 0;
532 >  result := 0; {NONE}
533    case SQLType of
534    SQL_VARYING, SQL_TEXT:
535        result := FCharSetID;
536  
537    SQL_BLOB:
538      if (SQLSubType = 1) then
539 <      result := FCharSetID;
539 >      result := FCharSetID
540 >    else
541 >      result := 1; {OCTETS}
542  
543    SQL_ARRAY:
544      if (FRelationName <> '') and (FFieldName <> '') then
# Line 352 | Line 546 | begin
546      else
547        result := FCharSetID;
548    end;
355  result := result;
549   end;
550  
551   function TIBXSQLVAR.GetCodePage: TSystemCodePage;
# Line 394 | Line 587 | begin
587    Result := FMetadataSize;
588   end;
589  
590 + function TIBXSQLVAR.GetAttachment: IAttachment;
591 + begin
592 +  Result := FStatement.GetAttachment;
593 + end;
594 +
595   function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
596   begin
597    if GetSQLType <> SQL_ARRAY then
# Line 475 | Line 673 | end;
673  
674   procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
675   begin
676 +  if (FSQLType <> aValue) and not CanChangeSQLType then
677 +    IBError(ibxeSQLTypeUnchangeable,[TSQLDataItem.GetSQLTypeName(FSQLType),TSQLDataItem.GetSQLTypeName(aValue)]);
678    FSQLType := aValue;
679    Changed;
680   end;
# Line 485 | Line 685 | begin
685    Changed;
686   end;
687  
688 + procedure TIBXSQLVAR.SetMetaSize(aValue: cardinal);
689 + begin
690 +  if (aValue > FMetaDataSize) and not CanChangeSQLType then
691 +    IBError(ibxeCannotIncreaseMetadatasize,[FMetaDataSize,aValue]);
692 +  FMetaDataSize := aValue;
693 + end;
694 +
695 + function TIBXSQLVAR.GetDefaultTextSQLType: cardinal;
696 + begin
697 +  Result := SQL_VARYING;
698 + end;
699 +
700   constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
701   begin
702    inherited Create(aParent,aIndex);
# Line 614 | Line 826 | end;
826  
827   procedure TIBXINPUTSQLDA.FreeMessageBuffer;
828   begin
617  if FCurMetaData <> nil then
618  begin
619    FCurMetaData.release;
620    FCurMetaData := nil;
621  end;
829    if FMessageBuffer <> nil then
830    begin
831      FreeMem(FMessageBuffer);
# Line 627 | Line 834 | begin
834    FMsgLength := 0;
835   end;
836  
837 + procedure TIBXINPUTSQLDA.FreeCurMetaData;
838 + begin
839 +  if FCurMetaData <> nil then
840 +  begin
841 +    FCurMetaData.release;
842 +    FCurMetaData := nil;
843 +  end;
844 + end;
845 +
846   function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
847   begin
848    PackBuffer;
# Line 649 | Line 865 | procedure TIBXINPUTSQLDA.BuildMetadata;
865   var Builder: Firebird.IMetadataBuilder;
866      i: integer;
867   begin
868 <  if FCurMetaData = nil then
868 >  if (FCurMetaData = nil) and (Count > 0) then
869    with FFirebird30ClientAPI do
870    begin
871 <    Builder := inherited MetaData.getBuilder(StatusIntf);
871 >    Builder := FFirebird30ClientAPI.MasterIntf.getMetadataBuilder(StatusIntf,Count);
872      Check4DataBaseError;
873      try
874        for i := 0 to Count - 1 do
875        with TIBXSQLVar(Column[i]) do
876        begin
877 <        Builder.setType(StatusIntf,i,FSQLType);
877 >        Builder.setType(StatusIntf,i,FSQLType+1);
878          Check4DataBaseError;
879          Builder.setSubType(StatusIntf,i,FSQLSubType);
880          Check4DataBaseError;
881 <        Builder.setLength(StatusIntf,i,FDataLength);
881 > //        writeln('Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
882 >        if FSQLType = SQL_VARYING then
883 >        begin
884 >          {The datalength can be greater than the metadata size when SQLType has been overridden to text}
885 >          if (GetDataLength > GetSize) and CanChangeMetaData then
886 >            Builder.setLength(StatusIntf,i,GetDataLength)
887 >          else
888 >            Builder.setLength(StatusIntf,i,GetSize)
889 >        end
890 >        else
891 >          Builder.setLength(StatusIntf,i,GetDataLength);
892          Check4DataBaseError;
893          Builder.setCharSet(StatusIntf,i,GetCharSetID);
894          Check4DataBaseError;
# Line 679 | Line 905 | end;
905  
906   procedure TIBXINPUTSQLDA.PackBuffer;
907   var i: integer;
908 +    P: PByte;
909   begin
910    BuildMetadata;
911  
912 <  if FMsgLength = 0 then
912 >  if (FMsgLength = 0) and (FCurMetaData <> nil) then
913    with FFirebird30ClientAPI do
914    begin
915      FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
# Line 693 | Line 920 | begin
920      for i := 0 to Count - 1 do
921      with TIBXSQLVar(Column[i]) do
922      begin
923 +      P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
924 + //     writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
925        if not Modified then
926          IBError(ibxeUninitializedInputParameter,[i,Name]);
698
927        if IsNull then
928 <        FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
928 >        FillChar(P^,FDataLength,0)
929        else
930        if FSQLData <> nil then
931 <        Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
932 <      Check4DataBaseError;
931 >      begin
932 >        if SQLType = SQL_VARYING then
933 >        begin
934 >            EncodeInteger(FDataLength,2,P);
935 >            Inc(P,2);
936 >        end
937 >        else
938 >        if (SQLType = SQL_BLOB) and (FStatement.FBatch <> nil) then
939 >        begin
940 >          FStatement.FBatch.registerBlob(Statusintf,ISC_QUADPtr(FSQLData),ISC_QUADPtr(FSQLData));
941 >          Check4DatabaseError;
942 >        end;
943 >        Move(FSQLData^,P^,FDataLength);
944 >      end;
945        if IsNullable then
946        begin
947          Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
# Line 714 | Line 954 | end;
954   procedure TIBXINPUTSQLDA.FreeXSQLDA;
955   begin
956    inherited FreeXSQLDA;
957 +  FreeCurMetaData;
958    FreeMessageBuffer;
959   end;
960  
# Line 725 | Line 966 | end;
966  
967   destructor TIBXINPUTSQLDA.Destroy;
968   begin
969 <  FreeMessageBuffer;
969 >  FreeXSQLDA;
970    inherited Destroy;
971   end;
972  
# Line 752 | Line 993 | begin
993        else
994          FSQLSubType := 0;
995        FDataLength := aMetaData.getLength(StatusIntf,i);
755      FMetadataSize := FDataLength;
996        Check4DataBaseError;
997 <      case SQLType of
758 <        SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
759 <        SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
760 <        SQL_LONG, SQL_INT64, SQL_INT128, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT,
761 <        SQL_TIMESTAMP_TZ, SQL_TIME_TZ, SQL_DEC_FIXED, SQL_DEC16, SQL_DEC34,
762 <        SQL_TIMESTAMP_TZ_EX, SQL_TIME_TZ_EX:
763 <        begin
764 <          if (FDataLength = 0) then
765 <            { Make sure you get a valid pointer anyway
766 <             select '' from foo }
767 <            IBAlloc(FSQLData, 0, 1)
768 <          else
769 <            IBAlloc(FSQLData, 0, FDataLength)
770 <        end;
771 <        SQL_VARYING:
772 <          IBAlloc(FSQLData, 0, FDataLength + 2);
773 <       else
774 <          IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
775 <      end;
997 >      FMetadataSize := FDataLength;
998        FNullable := aMetaData.isNullable(StatusIntf,i);
777      FOwnsSQLData := true;
999        Check4DataBaseError;
779      FNullIndicator := -1;
1000        if FNullable then
1001          FSQLNullIndicator := @FNullIndicator
1002        else
# Line 785 | Line 1005 | begin
1005        Check4DataBaseError;
1006        FCharSetID :=  aMetaData.getCharSet(StatusIntf,i) and $FF;
1007        Check4DataBaseError;
1008 +      ColumnSQLDataInit;
1009      end;
1010    end;
1011   end;
# Line 792 | Line 1013 | end;
1013   procedure TIBXINPUTSQLDA.Changed;
1014   begin
1015    inherited Changed;
1016 +  FreeCurMetaData;
1017    FreeMessageBuffer;
1018   end;
1019  
1020 + procedure TIBXINPUTSQLDA.ReInitialise;
1021 + var i: integer;
1022 + begin
1023 +  FreeMessageBuffer;
1024 +  for i := 0 to Count - 1 do
1025 +    TIBXSQLVar(Column[i]).ColumnSQLDataInit;
1026 + end;
1027 +
1028   function TIBXINPUTSQLDA.IsInputDataArea: boolean;
1029   begin
1030    Result := true;
# Line 953 | Line 1183 | begin
1183      ChangeSeqNo := FStatement.ChangeSeqNo;
1184   end;
1185  
1186 + function TIBXSQLDA.CanChangeMetaData: boolean;
1187 + begin
1188 +  Result := FStatement.FBatch = nil;
1189 + end;
1190 +
1191   procedure TIBXSQLDA.SetCount(Value: Integer);
1192   var
1193    i: Integer;
# Line 984 | Line 1219 | begin
1219      TIBXSQLVAR(Column[i]).FreeSQLData;
1220    for i := 0 to FSize - 1  do
1221      TIBXSQLVAR(Column[i]).Free;
1222 +  FCount := 0;
1223    SetLength(FColumnList,0);
1224    FSize := 0;
1225   end;
# Line 1000 | Line 1236 | end;
1236  
1237   { TFB30Statement }
1238  
1239 + procedure TFB30Statement.CheckChangeBatchRowLimit;
1240 + begin
1241 +  if IsInBatchMode then
1242 +    IBError(ibxeInBatchMode,[nil]);
1243 + end;
1244 +
1245   procedure TFB30Statement.CheckHandle;
1246   begin
1247    if FStatementIntf = nil then
1248      IBError(ibxeInvalidStatementHandle,[nil]);
1249   end;
1250  
1251 + procedure TFB30Statement.CheckBatchModeAvailable;
1252 + begin
1253 +  if not HasBatchMode then
1254 +    IBError(ibxeBatchModeNotSupported,[nil]);
1255 +  case SQLStatementType of
1256 +  SQLInsert,
1257 +  SQLUpdate: {OK};
1258 +  else
1259 +     IBError(ibxeInvalidBatchQuery,[GetSQLStatementTypeName]);
1260 +  end;
1261 + end;
1262 +
1263   procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1264    );
1265   begin
# Line 1103 | Line 1357 | begin
1357   end;
1358  
1359   function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1360 +
1361 +  procedure ExecuteQuery(outMetaData: Firebird.IMessageMetaData=nil; outBuffer: pointer=nil);
1362 +  begin
1363 +    with FFirebird30ClientAPI do
1364 +    begin
1365 +      SavePerfStats(FBeforeStats);
1366 +      FStatementIntf.execute(StatusIntf,
1367 +                             (aTransaction as TFB30Transaction).TransactionIntf,
1368 +                             FSQLParams.MetaData,
1369 +                             FSQLParams.MessageBuffer,
1370 +                             outMetaData,
1371 +                             outBuffer);
1372 +      Check4DataBaseError;
1373 +      FStatisticsAvailable := SavePerfStats(FAfterStats);
1374 +    end;
1375 +  end;
1376 +
1377 +
1378   begin
1379    Result := nil;
1380 +  FBatchCompletion := nil;
1381    FBOF := false;
1382    FEOF := false;
1383    FSingleResults := false;
1384 +  FStatisticsAvailable := false;
1385 +  if IsInBatchMode then
1386 +    IBerror(ibxeInBatchMode,[]);
1387    CheckTransaction(aTransaction);
1388    if not FPrepared then
1389      InternalPrepare;
# Line 1117 | Line 1393 | begin
1393    if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1394      IBError(ibxeInterfaceOutofDate,[nil]);
1395  
1396 +
1397    try
1398      with FFirebird30ClientAPI do
1399      begin
1123      if FCollectStatistics then
1124      begin
1125        UtilIntf.getPerfCounters(StatusIntf,
1126                      (GetAttachment as TFB30Attachment).AttachmentIntf,
1127                      ISQL_COUNTERS,@FBeforeStats);
1128        Check4DataBaseError;
1129      end;
1130
1400        case FSQLStatementType of
1401        SQLSelect:
1402          IBError(ibxeIsAExecuteProcedure,[]);
1403  
1404        SQLExecProcedure:
1405        begin
1406 <        FStatementIntf.execute(StatusIntf,
1138 <                               (aTransaction as TFB30Transaction).TransactionIntf,
1139 <                               FSQLParams.MetaData,
1140 <                               FSQLParams.MessageBuffer,
1141 <                               FSQLRecord.MetaData,
1142 <                               FSQLRecord.MessageBuffer);
1143 <        Check4DataBaseError;
1144 <
1406 >        ExecuteQuery(FSQLRecord.MetaData,FSQLRecord.MessageBuffer);
1407          Result := TResults.Create(FSQLRecord);
1408          FSingleResults := true;
1147      end
1148      else
1149        FStatementIntf.execute(StatusIntf,
1150                               (aTransaction as TFB30Transaction).TransactionIntf,
1151                               FSQLParams.MetaData,
1152                               FSQLParams.MessageBuffer,
1153                               nil,
1154                               nil);
1155        Check4DataBaseError;
1409        end;
1410 <      if FCollectStatistics then
1411 <      begin
1412 <        UtilIntf.getPerfCounters(StatusIntf,
1160 <                  (GetAttachment as TFB30Attachment).AttachmentIntf,
1161 <                  ISQL_COUNTERS, @FAfterStats);
1162 <        Check4DataBaseError;
1163 <        FStatisticsAvailable := true;
1410 >
1411 >      else
1412 >        ExecuteQuery;
1413        end;
1414      end;
1415    finally
# Line 1180 | Line 1429 | begin
1429    if FSQLStatementType <> SQLSelect then
1430     IBError(ibxeIsASelectStatement,[]);
1431  
1432 < CheckTransaction(aTransaction);
1432 >  FBatchCompletion := nil;
1433 >  CheckTransaction(aTransaction);
1434    if not FPrepared then
1435      InternalPrepare;
1436    CheckHandle;
# Line 1239 | Line 1489 | procedure TFB30Statement.FreeHandle;
1489   begin
1490    Close;
1491    ReleaseInterfaces;
1492 +  if FBatch <> nil then
1493 +  begin
1494 +    FBatch.release;
1495 +    FBatch := nil;
1496 +  end;
1497    if FStatementIntf <> nil then
1498    begin
1499      FStatementIntf.release;
# Line 1275 | Line 1530 | begin
1530    Inc(FChangeSeqNo);
1531   end;
1532  
1533 + function TFB30Statement.SavePerfStats(var Stats: TPerfStatistics): boolean;
1534 + begin
1535 +  Result := false;
1536 +  if FCollectStatistics then
1537 +  with FFirebird30ClientAPI do
1538 +  begin
1539 +    UtilIntf.getPerfCounters(StatusIntf,
1540 +              (GetAttachment as TFB30Attachment).AttachmentIntf,
1541 +              ISQL_COUNTERS, @Stats);
1542 +    Check4DataBaseError;
1543 +    Result := true;
1544 +  end;
1545 + end;
1546 +
1547   constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1548    Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1549   begin
# Line 1412 | Line 1681 | begin
1681      TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1682   end;
1683  
1684 + function TFB30Statement.IsInBatchMode: boolean;
1685 + begin
1686 +  Result := FBatch <> nil;
1687 + end;
1688 +
1689 + function TFB30Statement.HasBatchMode: boolean;
1690 + begin
1691 +  Result := GetAttachment.HasBatchMode;
1692 + end;
1693 +
1694 + procedure TFB30Statement.AddToBatch;
1695 + var BatchPB: TXPBParameterBlock;
1696 +
1697 + const SixteenMB = 16 * 1024 * 1024;
1698 + begin
1699 +  FBatchCompletion := nil;
1700 +  if not FPrepared then
1701 +    InternalPrepare;
1702 +  CheckHandle;
1703 +  CheckBatchModeAvailable;
1704 +  with FFirebird30ClientAPI do
1705 +  begin
1706 +    if FBatch = nil then
1707 +    begin
1708 +      {Start Batch}
1709 +      BatchPB := TXPBParameterBlock.Create(FFirebird30ClientAPI,Firebird.IXpbBuilder.BATCH);
1710 +      with FFirebird30ClientAPI do
1711 +      try
1712 +        FBatchBufferSize := FBatchRowLimit * FSQLParams.MetaData.getAlignedLength(StatusIntf);
1713 +        Check4DatabaseError;
1714 +        if FBatchBufferSize < SixteenMB then
1715 +          FBatchBufferSize := SixteenMB;
1716 +        if FBatchBufferSize > 256 * 1024 *1024 {assumed limit} then
1717 +          IBError(ibxeBatchBufferSizeTooBig,[FBatchBufferSize]);
1718 +
1719 +        BatchPB.insertInt(Firebird.IBatch.TAG_RECORD_COUNTS,1);
1720 +        BatchPB.insertInt(Firebird.IBatch.TAG_BUFFER_BYTES_SIZE,FBatchBufferSize);
1721 +        FBatch := FStatementIntf.createBatch(StatusIntf,
1722 +                                             FSQLParams.MetaData,
1723 +                                             BatchPB.getDataLength,
1724 +                                             BatchPB.getBuffer);
1725 +        Check4DataBaseError;
1726 +
1727 +      finally
1728 +        BatchPB.Free;
1729 +      end;
1730 +      FBatchRowCount := 0;
1731 +      FBatchBufferUsed := 0;
1732 +    end;
1733 +
1734 +    Inc(FBatchRowCount);
1735 +    Inc(FBatchBufferUsed,FSQLParams.MetaData.getAlignedLength(StatusIntf));
1736 +    Check4DataBaseError;
1737 +    if FBatchBufferUsed > FBatchBufferSize then
1738 +      raise EIBBatchBufferOverflow.Create(Ord(ibxeBatchRowBufferOverflow),
1739 +                              Format(GetErrorMessage(ibxeBatchRowBufferOverflow),
1740 +                              [FBatchRowCount,FBatchBufferSize]));
1741 +
1742 +    FBatch.Add(StatusIntf,1,FSQLParams.GetMessageBuffer);
1743 +      Check4DataBaseError
1744 +  end;
1745 + end;
1746 +
1747 + function TFB30Statement.ExecuteBatch(aTransaction: ITransaction
1748 +  ): IBatchCompletion;
1749 +
1750 + procedure Check4BatchCompletionError(bc: IBatchCompletion);
1751 + var status: IStatus;
1752 +    RowNo: integer;
1753 + begin
1754 +  status := nil;
1755 +  {Raise an exception if there was an error reported in the BatchCompletion}
1756 +  if (bc <> nil) and bc.getErrorStatus(RowNo,status) then
1757 +    raise EIBInterbaseError.Create(status);
1758 + end;
1759 +
1760 + var cs: Firebird.IBatchCompletionState;
1761 +
1762 + begin
1763 +  Result := nil;
1764 +  if FBatch = nil then
1765 +    IBError(ibxeNotInBatchMode,[]);
1766 +
1767 +  with FFirebird30ClientAPI do
1768 +  begin
1769 +    SavePerfStats(FBeforeStats);
1770 +    if aTransaction = nil then
1771 +      cs := FBatch.execute(StatusIntf,(FTransactionIntf as TFB30Transaction).TransactionIntf)
1772 +    else
1773 +      cs := FBatch.execute(StatusIntf,(aTransaction as TFB30Transaction).TransactionIntf);
1774 +    Check4DataBaseError;
1775 +    FBatchCompletion := TBatchCompletion.Create(FFirebird30ClientAPI,cs);
1776 +    FStatisticsAvailable := SavePerfStats(FAfterStats);
1777 +    FBatch.release;
1778 +    FBatch := nil;
1779 +    Check4BatchCompletionError(FBatchCompletion);
1780 +    Result := FBatchCompletion;
1781 +  end;
1782 + end;
1783 +
1784 + procedure TFB30Statement.CancelBatch;
1785 + begin
1786 +  if FBatch = nil then
1787 +    IBError(ibxeNotInBatchMode,[]);
1788 +  FBatch.release;
1789 +  FBatch := nil;
1790 + end;
1791 +
1792 + function TFB30Statement.GetBatchCompletion: IBatchCompletion;
1793 + begin
1794 +  Result := FBatchCompletion;
1795 + end;
1796 +
1797   function TFB30Statement.IsPrepared: boolean;
1798   begin
1799    Result := FStatementIntf <> nil;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines