ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/3.0/FB30Statement.pas
Revision: 371
Committed: Wed Jan 5 15:21:22 2022 UTC (2 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 55713 byte(s)
Log Message:
Beta Release 0.1

File Contents

# User Rev Content
1 tony 45 (*
2     * Firebird Interface (fbintf). The fbintf components provide a set of
3     * Pascal language bindings for the Firebird API.
4     *
5     * The contents of this file are subject to the Initial Developer's
6     * Public License Version 1.0 (the "License"); you may not use this
7     * file except in compliance with the License. You may obtain a copy
8     * of the License here:
9     *
10     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
11     *
12     * Software distributed under the License is distributed on an "AS
13     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14     * implied. See the License for the specific language governing rights
15     * and limitations under the License.
16     *
17     * The Initial Developer of the Original Code is Tony Whyman.
18     *
19     * The Original Code is (C) 2016 Tony Whyman, MWA Software
20     * (http://www.mwasoftware.co.uk).
21     *
22     * All Rights Reserved.
23     *
24     * Contributor(s): ______________________________________.
25     *
26     *)
27     unit FB30Statement;
28 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$codepage UTF8}
35     {$interfaces COM}
36     {$ENDIF}
37    
38     {This unit is hacked from IBSQL and contains the code for managing an XSQLDA and
39     SQLVars, along with statement preparation, execution and cursor management.
40     Most of the SQLVar code has been moved to unit FBSQLData. Client access is
41     provided through interface rather than direct access to the XSQLDA and XSQLVar
42     objects.}
43    
44     {
45     Note on reference counted interfaces.
46     ------------------------------------
47    
48     TFB30Statement manages both an input and an output SQLDA through the TIBXINPUTSQLDA
49     and TIBXOUTPUTSQLDA objects. As pure objects, these are explicitly destroyed
50     when the statement is destroyed.
51    
52     However, IResultSet is an interface and is returned when a cursor is opened and
53     has a reference for the TIBXOUTPUTSQLDA. The user may discard their reference
54     to the IStatement while still using the IResultSet. This would be a problem if t
55     he underlying TFB30Statement object and its TIBXOUTPUTSQLDA is destroyed while
56     still leaving the TIBXResultSet object in place. Calls to (e.g.) FetchNext would fail.
57    
58     To avoid this problem, TResultsSet objects have a reference to the IStatement
59     interface of the TFB30Statement object. Thus, as long as these "copies" exist,
60     the owning statement is not destroyed even if the user discards their reference
61     to the statement. Note: the TFB30Statement does not have a reference to the TIBXResultSet
62     interface. This way circular references are avoided.
63    
64     To avoid and IResultSet interface being kept to long and no longer synchronised
65     with the query, each statement includes a prepare sequence number, incremented
66     each time the query is prepared. When the IResultSet interface is created, it
67     noted the current prepare sequence number. Whe an IResult interface is accessed
68     it checks this number against the statement's current prepare sequence number.
69     If not the same, an error is raised.
70    
71     A similar strategy is used for the IMetaData, IResults and ISQLParams interfaces.
72     }
73    
74     interface
75    
76     uses
77     Classes, SysUtils, Firebird, IB, FBStatement, FB30ClientAPI, FB30Transaction,
78     FB30Attachment,IBExternals, FBSQLData, FBOutputBlock, FBActivityMonitor;
79    
80     type
81     TFB30Statement = class;
82     TIBXSQLDA = class;
83    
84     { TIBXSQLVAR }
85    
86     TIBXSQLVAR = class(TSQLVarData)
87     private
88     FStatement: TFB30Statement;
89 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
90 tony 45 FBlob: IBlob; {Cache references}
91     FNullIndicator: short;
92     FOwnsSQLData: boolean;
93     FBlobMetaData: IBlobMetaData;
94     FArrayMetaData: IArrayMetaData;
95    
96     {SQL Var Type Data}
97     FSQLType: cardinal;
98     FSQLSubType: integer;
99 tony 56 FSQLData: PByte; {Address of SQL Data in Message Buffer}
100 tony 45 FSQLNullIndicator: PShort; {Address of null indicator}
101 tony 371 FDataLength: cardinal;
102     FMetadataSize: cardinal;
103 tony 45 FNullable: boolean;
104     FScale: integer;
105     FCharSetID: cardinal;
106 tony 56 FRelationName: AnsiString;
107     FFieldName: AnsiString;
108 tony 45
109     protected
110 tony 345 function CanChangeSQLType: boolean;
111 tony 45 function GetSQLType: cardinal; override;
112     function GetSubtype: integer; override;
113 tony 56 function GetAliasName: AnsiString; override;
114     function GetFieldName: AnsiString; override;
115     function GetOwnerName: AnsiString; override;
116     function GetRelationName: AnsiString; override;
117 tony 45 function GetScale: integer; override;
118     function GetCharSetID: cardinal; override;
119     function GetIsNull: Boolean; override;
120     function GetIsNullable: boolean; override;
121 tony 56 function GetSQLData: PByte; override;
122 tony 45 function GetDataLength: cardinal; override;
123 tony 315 function GetSize: cardinal; override;
124 tony 345 function GetDefaultTextSQLType: cardinal; override;
125 tony 45 procedure SetIsNull(Value: Boolean); override;
126     procedure SetIsNullable(Value: Boolean); override;
127 tony 371 procedure InternalSetScale(aValue: integer); override;
128     procedure InternalSetDataLength(len: cardinal); override;
129     procedure InternalSetSQLType(aValue: cardinal); override;
130 tony 45 procedure SetCharSetID(aValue: cardinal); override;
131 tony 345 procedure SetMetaSize(aValue: cardinal); override;
132 tony 45 public
133     constructor Create(aParent: TIBXSQLDA; aIndex: integer);
134     procedure Changed; override;
135 tony 349 procedure InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
136 tony 345 procedure ColumnSQLDataInit;
137 tony 45 procedure RowChange; override;
138     procedure FreeSQLData;
139 tony 363 function GetAsArray: IArray; override;
140 tony 45 function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
141     function GetArrayMetaData: IArrayMetaData; override;
142     function GetBlobMetaData: IBlobMetaData; override;
143     function CreateBlob: IBlob; override;
144 tony 371 procedure SetSQLData(AValue: PByte; len: cardinal); override;
145 tony 45 end;
146    
147     { TIBXSQLDA }
148    
149     TIBXSQLDA = class(TSQLDataArea)
150     private
151     FCount: Integer; {Columns in use - may be less than inherited columns}
152     FSize: Integer; {Number of TIBXSQLVARs in column list}
153     FMetaData: Firebird.IMessageMetadata;
154     FTransactionSeqNo: integer;
155 tony 263 protected
156 tony 45 FStatement: TFB30Statement;
157 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
158 tony 371 FMessageBuffer: PByte; {Message Buffer}
159     FMsgLength: integer; {Message Buffer length}
160 tony 45 function GetTransactionSeqNo: integer; override;
161     procedure FreeXSQLDA; virtual;
162     function GetStatement: IStatement; override;
163     function GetPrepareSeqNo: integer; override;
164     procedure SetCount(Value: Integer); override;
165 tony 371 procedure AllocMessageBuffer(len: integer); virtual;
166     procedure FreeMessageBuffer; virtual;
167 tony 45 public
168 tony 371 constructor Create(aStatement: TFB30Statement); overload;
169     constructor Create(api: IFirebirdAPI); overload;
170 tony 45 destructor Destroy; override;
171     procedure Changed; virtual;
172     function CheckStatementStatus(Request: TStatementStatus): boolean; override;
173     function ColumnsInUseCount: integer; override;
174 tony 371 function GetMetaData: Firebird.IMessageMetadata; virtual;
175 tony 45 procedure Initialize; override;
176     function StateChanged(var ChangeSeqNo: integer): boolean; override;
177 tony 345 function CanChangeMetaData: boolean; override;
178 tony 45 property Count: Integer read FCount write SetCount;
179     property Statement: TFB30Statement read FStatement;
180     end;
181    
182     { TIBXINPUTSQLDA }
183    
184     TIBXINPUTSQLDA = class(TIBXSQLDA)
185     private
186     FCurMetaData: Firebird.IMessageMetadata;
187 tony 345 procedure FreeCurMetaData;
188 tony 56 function GetMessageBuffer: PByte;
189 tony 45 function GetModified: Boolean;
190     function GetMsgLength: integer;
191 tony 68 procedure BuildMetadata;
192 tony 371 protected
193 tony 45 procedure PackBuffer;
194     procedure FreeXSQLDA; override;
195     public
196 tony 371 constructor Create(aStatement: TFB30Statement); overload;
197     constructor Create(api: IFirebirdAPI); overload;
198 tony 45 destructor Destroy; override;
199     procedure Bind(aMetaData: Firebird.IMessageMetadata);
200     procedure Changed; override;
201 tony 371 function GetMetaData: Firebird.IMessageMetadata; override;
202 tony 345 procedure ReInitialise;
203 tony 45 function IsInputDataArea: boolean; override;
204 tony 56 property MessageBuffer: PByte read GetMessageBuffer;
205 tony 45 property MsgLength: integer read GetMsgLength;
206     end;
207    
208     { TIBXOUTPUTSQLDA }
209    
210     TIBXOUTPUTSQLDA = class(TIBXSQLDA)
211     private
212     FTransaction: TFB30Transaction; {transaction used to execute the statement}
213     protected
214 tony 371 function GetTransaction: ITransaction; override;
215 tony 45 public
216     procedure Bind(aMetaData: Firebird.IMessageMetadata);
217     procedure GetData(index: integer; var aIsNull: boolean; var len: short;
218 tony 56 var data: PByte); override;
219 tony 45 function IsInputDataArea: boolean; override;
220 tony 56 property MessageBuffer: PByte read FMessageBuffer;
221 tony 45 property MsgLength: integer read FMsgLength;
222     end;
223    
224     { TResultSet }
225    
226     TResultSet = class(TResults,IResultSet)
227     private
228     FResults: TIBXOUTPUTSQLDA;
229     FCursorSeqNo: integer;
230 tony 350 procedure RowChange;
231 tony 45 public
232     constructor Create(aResults: TIBXOUTPUTSQLDA);
233     destructor Destroy; override;
234     {IResultSet}
235 tony 350 function FetchNext: boolean; {fetch next record}
236     function FetchPrior: boolean; {fetch previous record}
237     function FetchFirst:boolean; {fetch first record}
238     function FetchLast: boolean; {fetch last record}
239     function FetchAbsolute(position: Integer): boolean; {fetch record by its absolute position in result set}
240     function FetchRelative(offset: Integer): boolean; {fetch record by position relative to current}
241 tony 56 function GetCursorName: AnsiString;
242 tony 350 function IsBof: boolean;
243 tony 45 function IsEof: boolean;
244     procedure Close;
245     end;
246    
247 tony 345 { TBatchCompletion }
248    
249     TBatchCompletion = class(TInterfaceOwner,IBatchCompletion)
250     private
251     FCompletionState: Firebird.IBatchCompletionState;
252     FFirebird30ClientAPI: TFB30ClientAPI;
253     public
254     constructor Create(api: TFB30ClientAPI; cs: IBatchCompletionState);
255     destructor Destroy; override;
256     {IBatchCompletion}
257     function getErrorStatus(var RowNo: integer; var status: IStatus): boolean;
258     function getTotalProcessed: cardinal;
259     function getState(updateNo: cardinal): TBatchCompletionState;
260     function getStatusMessage(updateNo: cardinal): AnsiString;
261     function getUpdated: integer;
262     end;
263    
264 tony 350 TFetchType = (ftNext,ftPrior,ftFirst,ftLast,ftAbsolute,ftRelative);
265    
266 tony 45 { TFB30Statement }
267    
268     TFB30Statement = class(TFBStatement,IStatement)
269     private
270     FStatementIntf: Firebird.IStatement;
271 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
272 tony 45 FSQLParams: TIBXINPUTSQLDA;
273     FSQLRecord: TIBXOUTPUTSQLDA;
274     FResultSet: Firebird.IResultSet;
275     FCursorSeqNo: integer;
276 tony 350 FCursor: AnsiString;
277 tony 345 FBatch: Firebird.IBatch;
278     FBatchCompletion: IBatchCompletion;
279     FBatchRowCount: integer;
280     FBatchBufferSize: integer;
281     FBatchBufferUsed: integer;
282 tony 45 protected
283 tony 345 procedure CheckChangeBatchRowLimit; override;
284 tony 45 procedure CheckHandle; override;
285 tony 345 procedure CheckBatchModeAvailable;
286 tony 45 procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
287 tony 363 function GetStatementIntf: IStatement; override;
288 tony 350 procedure InternalPrepare(CursorName: AnsiString=''); override;
289 tony 45 function InternalExecute(aTransaction: ITransaction): IResults; override;
290 tony 350 function InternalOpenCursor(aTransaction: ITransaction; Scrollable: boolean
291     ): IResultSet; override;
292 tony 263 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
293 tony 45 procedure FreeHandle; override;
294     procedure InternalClose(Force: boolean); override;
295 tony 345 function SavePerfStats(var Stats: TPerfStatistics): boolean;
296 tony 45 public
297     constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
298 tony 350 sql: AnsiString; aSQLDialect: integer; CursorName: AnsiString='');
299 tony 45 constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
300 tony 270 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false;
301 tony 350 CaseSensitiveParams: boolean=false; CursorName: AnsiString='');
302 tony 45 destructor Destroy; override;
303 tony 350 function Fetch(FetchType: TFetchType; PosOrOffset: integer=0): boolean;
304 tony 45 property StatementIntf: Firebird.IStatement read FStatementIntf;
305 tony 371 property SQLParams: TIBXINPUTSQLDA read FSQLParams;
306     property SQLRecord: TIBXOUTPUTSQLDA read FSQLRecord;
307 tony 45
308     public
309     {IStatement}
310     function GetSQLParams: ISQLParams; override;
311     function GetMetaData: IMetaData; override;
312 tony 56 function GetPlan: AnsiString;
313 tony 45 function IsPrepared: boolean;
314 tony 359 function GetFlags: TStatementFlags; override;
315 tony 45 function CreateBlob(column: TColumnMetaData): IBlob; override;
316     function CreateArray(column: TColumnMetaData): IArray; override;
317     procedure SetRetainInterfaces(aValue: boolean); override;
318 tony 345 function IsInBatchMode: boolean; override;
319     function HasBatchMode: boolean; override;
320     procedure AddToBatch; override;
321     function ExecuteBatch(aTransaction: ITransaction
322     ): IBatchCompletion; override;
323     procedure CancelBatch; override;
324     function GetBatchCompletion: IBatchCompletion; override;
325 tony 45 end;
326    
327     implementation
328    
329 tony 68 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
330 tony 45
331 tony 47 const
332     ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
333    
334 tony 345 { EIBBatchCompletionError }
335    
336     { TBatchCompletion }
337    
338     constructor TBatchCompletion.Create(api: TFB30ClientAPI;
339     cs: IBatchCompletionState);
340     begin
341     inherited Create;
342     FFirebird30ClientAPI := api;
343     FCompletionState := cs;
344     end;
345    
346     destructor TBatchCompletion.Destroy;
347     begin
348     if FCompletionState <> nil then
349     begin
350     FCompletionState.dispose;
351     FCompletionState := nil;
352     end;
353     inherited Destroy;
354     end;
355    
356     function TBatchCompletion.getErrorStatus(var RowNo: integer; var status: IStatus
357     ): boolean;
358     var i: integer;
359     upcount: cardinal;
360     state: integer;
361     FBStatus: Firebird.IStatus;
362     begin
363     Result := false;
364     RowNo := -1;
365     FBStatus := nil;
366     with FFirebird30ClientAPI do
367     begin
368     upcount := FCompletionState.getSize(StatusIntf);
369     Check4DataBaseError;
370     for i := 0 to upcount - 1 do
371     begin
372     state := FCompletionState.getState(StatusIntf,i);
373     if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
374     begin
375     RowNo := i+1;
376     FBStatus := MasterIntf.getStatus;
377     try
378     FCompletionState.getStatus(StatusIntf,FBStatus,i);
379     Check4DataBaseError;
380     except
381     FBStatus.dispose;
382     raise
383     end;
384     status := TFB30StatusObject.Create(FFirebird30ClientAPI,FBStatus,
385     Format(SBatchCompletionError,[RowNo]));
386     status.SetIBDataBaseErrorMessages(GetStatus.GetIBDataBaseErrorMessages);
387     Result := true;
388     break;
389     end;
390     end;
391     end;
392     end;
393    
394     function TBatchCompletion.getTotalProcessed: cardinal;
395     begin
396     with FFirebird30ClientAPI do
397     begin
398     Result := FCompletionState.getsize(StatusIntf);
399     Check4DataBaseError;
400     end;
401     end;
402    
403     function TBatchCompletion.getState(updateNo: cardinal): TBatchCompletionState;
404     var state: integer;
405     begin
406     with FFirebird30ClientAPI do
407     begin
408     state := FCompletionState.getState(StatusIntf,updateNo);
409     Check4DataBaseError;
410     case state of
411     Firebird.IBatchCompletionState.EXECUTE_FAILED:
412     Result := bcExecuteFailed;
413    
414     Firebird.IBatchCompletionState.SUCCESS_NO_INFO:
415     Result := bcSuccessNoInfo;
416    
417     else
418     Result := bcNoMoreErrors;
419     end;
420     end;
421     end;
422    
423     function TBatchCompletion.getStatusMessage(updateNo: cardinal): AnsiString;
424     var status: Firebird.IStatus;
425     begin
426     with FFirebird30ClientAPI do
427     begin
428     status := MasterIntf.getStatus;
429     FCompletionState.getStatus(StatusIntf,status,updateNo);
430     Check4DataBaseError;
431 tony 371 Result := FormatStatus(status);
432 tony 345 end;
433     end;
434    
435     function TBatchCompletion.getUpdated: integer;
436     var i: integer;
437     upcount: cardinal;
438     state: integer;
439     begin
440     Result := 0;
441     with FFirebird30ClientAPI do
442     begin
443     upcount := FCompletionState.getSize(StatusIntf);
444     Check4DataBaseError;
445     for i := 0 to upcount -1 do
446     begin
447     state := FCompletionState.getState(StatusIntf,i);
448     if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
449     break;
450     Inc(Result);
451     end;
452     end;
453     end;
454    
455 tony 45 { TIBXSQLVAR }
456    
457     procedure TIBXSQLVAR.Changed;
458     begin
459     inherited Changed;
460     TIBXSQLDA(Parent).Changed;
461     end;
462    
463 tony 349 procedure TIBXSQLVAR.InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
464     begin
465     with FFirebird30ClientAPI do
466     begin
467     FSQLType := aMetaData.getType(StatusIntf,Index);
468     Check4DataBaseError;
469     if FSQLType = SQL_BLOB then
470     begin
471     FSQLSubType := aMetaData.getSubType(StatusIntf,Index);
472     Check4DataBaseError;
473     end
474     else
475     FSQLSubType := 0;
476     FDataLength := aMetaData.getLength(StatusIntf,Index);
477     Check4DataBaseError;
478     FMetadataSize := FDataLength;
479     FRelationName := strpas(aMetaData.getRelation(StatusIntf,Index));
480     Check4DataBaseError;
481     FFieldName := strpas(aMetaData.getField(StatusIntf,Index));
482     Check4DataBaseError;
483     FNullable := aMetaData.isNullable(StatusIntf,Index);
484     Check4DataBaseError;
485     FScale := aMetaData.getScale(StatusIntf,Index);
486     Check4DataBaseError;
487     FCharSetID := aMetaData.getCharSet(StatusIntf,Index) and $FF;
488     Check4DataBaseError;
489     end;
490     end;
491    
492 tony 345 procedure TIBXSQLVAR.ColumnSQLDataInit;
493     begin
494     FreeSQLData;
495     with FFirebird30ClientAPI do
496     begin
497     case SQLType of
498     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
499     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
500     SQL_LONG, SQL_INT64, SQL_INT128, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT,
501     SQL_TIMESTAMP_TZ, SQL_TIME_TZ, SQL_DEC_FIXED, SQL_DEC16, SQL_DEC34,
502     SQL_TIMESTAMP_TZ_EX, SQL_TIME_TZ_EX:
503     begin
504     if (FDataLength = 0) then
505     { Make sure you get a valid pointer anyway
506     select '' from foo }
507     IBAlloc(FSQLData, 0, 1)
508     else
509     IBAlloc(FSQLData, 0, FDataLength)
510     end;
511     SQL_VARYING:
512     IBAlloc(FSQLData, 0, FDataLength + 2);
513     else
514     IBError(ibxeUnknownSQLDataType, [SQLType and (not 1)])
515     end;
516     FOwnsSQLData := true;
517     FNullIndicator := -1;
518     end;
519     end;
520    
521     function TIBXSQLVAR.CanChangeSQLType: boolean;
522     begin
523     Result := Parent.CanChangeMetaData;
524     end;
525    
526 tony 45 function TIBXSQLVAR.GetSQLType: cardinal;
527     begin
528     Result := FSQLType;
529     end;
530    
531     function TIBXSQLVAR.GetSubtype: integer;
532     begin
533     Result := FSQLSubType;
534     end;
535    
536 tony 56 function TIBXSQLVAR.GetAliasName: AnsiString;
537 tony 371 var metadata: Firebird.IMessageMetadata;
538 tony 45 begin
539 tony 371 metadata := TIBXSQLDA(Parent).GetMetaData;
540     try
541     with FFirebird30ClientAPI do
542     begin
543     result := strpas(metaData.getAlias(StatusIntf,Index));
544     Check4DataBaseError;
545     end;
546     finally
547     metadata.release;
548 tony 45 end;
549     end;
550    
551 tony 56 function TIBXSQLVAR.GetFieldName: AnsiString;
552 tony 45 begin
553     Result := FFieldName;
554     end;
555    
556 tony 56 function TIBXSQLVAR.GetOwnerName: AnsiString;
557 tony 371 var metadata: Firebird.IMessageMetadata;
558 tony 45 begin
559 tony 371 metadata := TIBXSQLDA(Parent).GetMetaData;
560     try
561     with FFirebird30ClientAPI do
562     begin
563     result := strpas(metaData.getOwner(StatusIntf,Index));
564     Check4DataBaseError;
565     end;
566     finally
567     metadata.release;
568 tony 45 end;
569     end;
570    
571 tony 56 function TIBXSQLVAR.GetRelationName: AnsiString;
572 tony 45 begin
573     Result := FRelationName;
574     end;
575    
576     function TIBXSQLVAR.GetScale: integer;
577     begin
578     Result := FScale;
579     end;
580    
581     function TIBXSQLVAR.GetCharSetID: cardinal;
582     begin
583 tony 345 result := 0; {NONE}
584 tony 45 case SQLType of
585     SQL_VARYING, SQL_TEXT:
586     result := FCharSetID;
587    
588     SQL_BLOB:
589     if (SQLSubType = 1) then
590 tony 345 result := FCharSetID
591     else
592     result := 1; {OCTETS}
593 tony 45
594     SQL_ARRAY:
595     if (FRelationName <> '') and (FFieldName <> '') then
596     result := GetArrayMetaData.GetCharSetID
597     else
598     result := FCharSetID;
599     end;
600     end;
601    
602     function TIBXSQLVAR.GetIsNull: Boolean;
603     begin
604     Result := IsNullable and (FSQLNullIndicator^ = -1);
605     end;
606    
607     function TIBXSQLVAR.GetIsNullable: boolean;
608     begin
609     Result := FSQLNullIndicator <> nil;
610     end;
611    
612 tony 56 function TIBXSQLVAR.GetSQLData: PByte;
613 tony 45 begin
614     Result := FSQLData;
615     end;
616    
617     function TIBXSQLVAR.GetDataLength: cardinal;
618     begin
619     Result := FDataLength;
620     end;
621    
622 tony 315 function TIBXSQLVAR.GetSize: cardinal;
623     begin
624     Result := FMetadataSize;
625     end;
626    
627 tony 45 function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
628     begin
629     if GetSQLType <> SQL_ARRAY then
630     IBError(ibxeInvalidDataConversion,[nil]);
631    
632     if FArrayMetaData = nil then
633 tony 371 FArrayMetaData := TFB30ArrayMetaData.Create(GetAttachment as TFB30Attachment,
634     GetTransaction as TFB30Transaction,
635 tony 45 GetRelationName,GetFieldName);
636     Result := FArrayMetaData;
637     end;
638    
639     function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
640     begin
641     if GetSQLType <> SQL_BLOB then
642     IBError(ibxeInvalidDataConversion,[nil]);
643    
644     if FBlobMetaData = nil then
645 tony 371 FBlobMetaData := TFB30BlobMetaData.Create(GetAttachment as TFB30Attachment,
646     GetTransaction as TFB30Transaction,
647 tony 45 GetRelationName,GetFieldName,
648     GetSubType);
649 tony 47 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
650 tony 45 Result := FBlobMetaData;
651     end;
652    
653     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
654     begin
655     if Value then
656     begin
657     IsNullable := true;
658     FNullIndicator := -1;
659     end
660     else
661     if IsNullable then
662     FNullIndicator := 0;
663 tony 47 Changed;
664 tony 45 end;
665    
666     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
667     begin
668     if Value = IsNullable then Exit;
669     if Value then
670     begin
671     FSQLNullIndicator := @FNullIndicator;
672     FNullIndicator := 0;
673     end
674     else
675     FSQLNullIndicator := nil;
676 tony 68 Changed;
677 tony 45 end;
678    
679 tony 56 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
680 tony 45 begin
681     if FOwnsSQLData then
682     FreeMem(FSQLData);
683     FSQLData := AValue;
684     FDataLength := len;
685     FOwnsSQLData := false;
686 tony 68 Changed;
687 tony 45 end;
688    
689 tony 371 procedure TIBXSQLVAR.InternalSetScale(aValue: integer);
690 tony 45 begin
691     FScale := aValue;
692 tony 68 Changed;
693 tony 45 end;
694    
695 tony 371 procedure TIBXSQLVAR.InternalSetDataLength(len: cardinal);
696 tony 45 begin
697     if not FOwnsSQLData then
698     FSQLData := nil;
699     FDataLength := len;
700 tony 263 with FFirebird30ClientAPI do
701 tony 45 IBAlloc(FSQLData, 0, FDataLength);
702     FOwnsSQLData := true;
703 tony 68 Changed;
704 tony 45 end;
705    
706 tony 371 procedure TIBXSQLVAR.InternalSetSQLType(aValue: cardinal);
707 tony 45 begin
708     FSQLType := aValue;
709 tony 68 Changed;
710 tony 45 end;
711    
712     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
713     begin
714     FCharSetID := aValue;
715 tony 68 Changed;
716 tony 45 end;
717    
718 tony 345 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 tony 45 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
731     begin
732     inherited Create(aParent,aIndex);
733     FStatement := aParent.Statement;
734 tony 263 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
735 tony 45 end;
736    
737     procedure TIBXSQLVAR.RowChange;
738     begin
739     inherited;
740     FBlob := nil;
741     end;
742    
743     procedure TIBXSQLVAR.FreeSQLData;
744     begin
745     if FOwnsSQLData then
746     FreeMem(FSQLData);
747     FSQLData := nil;
748     FOwnsSQLData := true;
749     end;
750    
751 tony 363 function TIBXSQLVAR.GetAsArray: IArray;
752 tony 45 begin
753     if SQLType <> SQL_ARRAY then
754     IBError(ibxeInvalidDataConversion,[nil]);
755    
756     if IsNull then
757     Result := nil
758     else
759     begin
760 tony 363 if FArrayIntf = nil then
761 tony 371 FArrayIntf := TFB30Array.Create(GetAttachment as TFB30Attachment,
762     GetTransaction as TFB30Transaction,
763 tony 363 GetArrayMetaData,PISC_QUAD(SQLData)^);
764     Result := FArrayIntf;
765 tony 45 end;
766     end;
767    
768     function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
769     begin
770     if FBlob <> nil then
771     Result := FBlob
772     else
773     begin
774     if SQLType <> SQL_BLOB then
775     IBError(ibxeInvalidDataConversion, [nil]);
776     if IsNull then
777     Result := nil
778     else
779 tony 371 Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
780     GetTransaction as TFB30Transaction,
781 tony 45 GetBlobMetaData,
782     Blob_ID,BPB);
783     FBlob := Result;
784     end;
785     end;
786    
787     function TIBXSQLVAR.CreateBlob: IBlob;
788     begin
789 tony 371 Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
790     GetTransaction as TFB30Transaction,
791 tony 45 GetSubType,GetCharSetID,nil);
792     end;
793    
794     { TResultSet }
795    
796 tony 350 procedure TResultSet.RowChange;
797     var i: integer;
798     begin
799     for i := 0 to getCount - 1 do
800     FResults.Column[i].RowChange;
801     end;
802    
803 tony 45 constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
804     begin
805     inherited Create(aResults);
806     FResults := aResults;
807     FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
808     end;
809    
810     destructor TResultSet.Destroy;
811     begin
812     Close;
813     inherited Destroy;
814     end;
815    
816     function TResultSet.FetchNext: boolean;
817     begin
818     CheckActive;
819 tony 350 Result := FResults.FStatement.Fetch(ftNext);
820 tony 45 if Result then
821 tony 350 RowChange;
822 tony 45 end;
823    
824 tony 350 function TResultSet.FetchPrior: boolean;
825     begin
826     CheckActive;
827     Result := FResults.FStatement.Fetch(ftPrior);
828     if Result then
829     RowChange;
830     end;
831    
832     function TResultSet.FetchFirst: boolean;
833     begin
834     CheckActive;
835     Result := FResults.FStatement.Fetch(ftFirst);
836     if Result then
837     RowChange;
838     end;
839    
840     function TResultSet.FetchLast: boolean;
841     begin
842     CheckActive;
843     Result := FResults.FStatement.Fetch(ftLast);
844     if Result then
845     RowChange;
846     end;
847    
848     function TResultSet.FetchAbsolute(position: Integer): boolean;
849     begin
850     CheckActive;
851     Result := FResults.FStatement.Fetch(ftAbsolute,position);
852     if Result then
853     RowChange;
854     end;
855    
856     function TResultSet.FetchRelative(offset: Integer): boolean;
857     begin
858     CheckActive;
859     Result := FResults.FStatement.Fetch(ftRelative,offset);
860     if Result then
861     RowChange;
862     end;
863    
864 tony 56 function TResultSet.GetCursorName: AnsiString;
865 tony 45 begin
866 tony 350 Result := FResults.FStatement.FCursor;
867 tony 45 end;
868    
869 tony 350 function TResultSet.IsBof: boolean;
870     begin
871     Result := FResults.FStatement.FBof;
872     end;
873    
874 tony 45 function TResultSet.IsEof: boolean;
875     begin
876     Result := FResults.FStatement.FEof;
877     end;
878    
879     procedure TResultSet.Close;
880     begin
881     if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
882     FResults.FStatement.Close;
883     end;
884    
885     { TIBXINPUTSQLDA }
886    
887     function TIBXINPUTSQLDA.GetModified: Boolean;
888     var
889     i: Integer;
890     begin
891     result := False;
892     for i := 0 to FCount - 1 do
893     if Column[i].Modified then
894     begin
895     result := True;
896     exit;
897     end;
898     end;
899    
900 tony 345 procedure TIBXINPUTSQLDA.FreeCurMetaData;
901     begin
902     if FCurMetaData <> nil then
903     begin
904     FCurMetaData.release;
905     FCurMetaData := nil;
906     end;
907     end;
908    
909 tony 56 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
910 tony 45 begin
911     PackBuffer;
912     Result := FMessageBuffer;
913     end;
914    
915     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
916     begin
917 tony 68 BuildMetadata;
918 tony 45 Result := FCurMetaData;
919 tony 371 if Result <> nil then
920     Result.addRef;
921 tony 45 end;
922    
923     function TIBXINPUTSQLDA.GetMsgLength: integer;
924     begin
925     PackBuffer;
926     Result := FMsgLength;
927     end;
928    
929 tony 68 procedure TIBXINPUTSQLDA.BuildMetadata;
930 tony 45 var Builder: Firebird.IMetadataBuilder;
931     i: integer;
932 tony 371 version: NativeInt;
933 tony 45 begin
934 tony 345 if (FCurMetaData = nil) and (Count > 0) then
935 tony 263 with FFirebird30ClientAPI do
936 tony 45 begin
937 tony 345 Builder := FFirebird30ClientAPI.MasterIntf.getMetadataBuilder(StatusIntf,Count);
938 tony 45 Check4DataBaseError;
939     try
940     for i := 0 to Count - 1 do
941     with TIBXSQLVar(Column[i]) do
942     begin
943 tony 371 version := Builder.vtable.version;
944     if version >= 4 then
945     {Firebird 4 or later}
946     begin
947     Builder.setField(StatusIntf,i,PAnsiChar(Name));
948     Check4DataBaseError;
949     Builder.setAlias(StatusIntf,i,PAnsiChar(Name));
950     Check4DataBaseError;
951     end;
952     Builder.setType(StatusIntf,i,FSQLType);
953 tony 45 Check4DataBaseError;
954     Builder.setSubType(StatusIntf,i,FSQLSubType);
955     Check4DataBaseError;
956 tony 345 // writeln('Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
957     if FSQLType = SQL_VARYING then
958     begin
959     {The datalength can be greater than the metadata size when SQLType has been overridden to text}
960     if (GetDataLength > GetSize) and CanChangeMetaData then
961     Builder.setLength(StatusIntf,i,GetDataLength)
962     else
963     Builder.setLength(StatusIntf,i,GetSize)
964     end
965     else
966     Builder.setLength(StatusIntf,i,GetDataLength);
967 tony 45 Check4DataBaseError;
968     Builder.setCharSet(StatusIntf,i,GetCharSetID);
969     Check4DataBaseError;
970     Builder.setScale(StatusIntf,i,FScale);
971     Check4DataBaseError;
972     end;
973     FCurMetaData := Builder.getMetadata(StatusIntf);
974     Check4DataBaseError;
975     finally
976     Builder.release;
977     end;
978 tony 68 end;
979     end;
980 tony 45
981 tony 68 procedure TIBXINPUTSQLDA.PackBuffer;
982     var i: integer;
983 tony 345 P: PByte;
984 tony 371 MsgLen: cardinal;
985 tony 68 begin
986     BuildMetadata;
987    
988 tony 345 if (FMsgLength = 0) and (FCurMetaData <> nil) then
989 tony 263 with FFirebird30ClientAPI do
990 tony 68 begin
991 tony 371 MsgLen := FCurMetaData.getMessageLength(StatusIntf);
992 tony 45 Check4DataBaseError;
993    
994 tony 371 AllocMessageBuffer(MsgLen);
995 tony 45
996     for i := 0 to Count - 1 do
997     with TIBXSQLVar(Column[i]) do
998     begin
999 tony 345 P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
1000     // writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
1001 tony 68 if not Modified then
1002     IBError(ibxeUninitializedInputParameter,[i,Name]);
1003 tony 47 if IsNull then
1004 tony 345 FillChar(P^,FDataLength,0)
1005 tony 47 else
1006 tony 68 if FSQLData <> nil then
1007 tony 345 begin
1008     if SQLType = SQL_VARYING then
1009     begin
1010     EncodeInteger(FDataLength,2,P);
1011     Inc(P,2);
1012     end
1013     else
1014     if (SQLType = SQL_BLOB) and (FStatement.FBatch <> nil) then
1015     begin
1016     FStatement.FBatch.registerBlob(Statusintf,ISC_QUADPtr(FSQLData),ISC_QUADPtr(FSQLData));
1017     Check4DatabaseError;
1018     end;
1019     Move(FSQLData^,P^,FDataLength);
1020     end;
1021 tony 45 if IsNullable then
1022     begin
1023     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
1024     Check4DataBaseError;
1025     end;
1026     end;
1027     end;
1028     end;
1029    
1030     procedure TIBXINPUTSQLDA.FreeXSQLDA;
1031     begin
1032     inherited FreeXSQLDA;
1033 tony 345 FreeCurMetaData;
1034 tony 45 end;
1035    
1036     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
1037     begin
1038     inherited Create(aStatement);
1039     FMessageBuffer := nil;
1040     end;
1041    
1042 tony 371 constructor TIBXINPUTSQLDA.Create(api: IFirebirdAPI);
1043     begin
1044     inherited Create(api);
1045     FMessageBuffer := nil;
1046     end;
1047    
1048 tony 45 destructor TIBXINPUTSQLDA.Destroy;
1049     begin
1050 tony 345 FreeXSQLDA;
1051 tony 45 inherited Destroy;
1052     end;
1053    
1054     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1055     var i: integer;
1056     begin
1057     FMetaData := aMetaData;
1058 tony 371 FMetaData.AddRef;
1059 tony 263 with FFirebird30ClientAPI do
1060 tony 45 begin
1061 tony 338 Count := aMetadata.getCount(StatusIntf);
1062 tony 45 Check4DataBaseError;
1063     Initialize;
1064    
1065     for i := 0 to Count - 1 do
1066     with TIBXSQLVar(Column[i]) do
1067     begin
1068 tony 349 InitColumnMetaData(aMetaData);
1069     SaveMetaData;
1070 tony 45 if FNullable then
1071     FSQLNullIndicator := @FNullIndicator
1072     else
1073     FSQLNullIndicator := nil;
1074 tony 345 ColumnSQLDataInit;
1075 tony 45 end;
1076     end;
1077     end;
1078    
1079     procedure TIBXINPUTSQLDA.Changed;
1080     begin
1081     inherited Changed;
1082 tony 345 FreeCurMetaData;
1083 tony 45 FreeMessageBuffer;
1084     end;
1085    
1086 tony 345 procedure TIBXINPUTSQLDA.ReInitialise;
1087     var i: integer;
1088     begin
1089     FreeMessageBuffer;
1090     for i := 0 to Count - 1 do
1091     TIBXSQLVar(Column[i]).ColumnSQLDataInit;
1092     end;
1093    
1094 tony 45 function TIBXINPUTSQLDA.IsInputDataArea: boolean;
1095     begin
1096     Result := true;
1097     end;
1098    
1099     { TIBXOUTPUTSQLDA }
1100    
1101 tony 371 function TIBXOUTPUTSQLDA.GetTransaction: ITransaction;
1102 tony 45 begin
1103 tony 371 if FTransaction <> nil then
1104     Result := FTransaction
1105     else
1106     Result := inherited GetTransaction;
1107 tony 45 end;
1108    
1109     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1110     var i: integer;
1111 tony 371 MsgLen: cardinal;
1112 tony 45 begin
1113     FMetaData := aMetaData;
1114 tony 371 FMetaData.AddRef;
1115 tony 263 with FFirebird30ClientAPI do
1116 tony 45 begin
1117 tony 371 Count := aMetaData.getCount(StatusIntf);
1118 tony 45 Check4DataBaseError;
1119     Initialize;
1120    
1121 tony 371 MsgLen := aMetaData.getMessageLength(StatusIntf);
1122 tony 45 Check4DataBaseError;
1123 tony 371 AllocMessageBuffer(MsgLen);
1124 tony 45
1125     for i := 0 to Count - 1 do
1126     with TIBXSQLVar(Column[i]) do
1127     begin
1128 tony 349 InitColumnMetaData(aMetaData);
1129 tony 371 FSQLData := FMessageBuffer + aMetaData.getOffset(StatusIntf,i);
1130 tony 45 Check4DataBaseError;
1131     if FNullable then
1132     begin
1133     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
1134     Check4DataBaseError;
1135     end
1136     else
1137     FSQLNullIndicator := nil;
1138 tony 349 FBlob := nil;
1139 tony 363 FArrayIntf := nil;
1140 tony 45 end;
1141     end;
1142     SetUniqueRelationName;
1143     end;
1144    
1145     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
1146 tony 56 var len: short; var data: PByte);
1147 tony 45 begin
1148     with TIBXSQLVAR(Column[index]) do
1149     begin
1150     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
1151     data := FSQLData;
1152     len := FDataLength;
1153     if not IsNull and (FSQLType = SQL_VARYING) then
1154     begin
1155 tony 263 with FFirebird30ClientAPI do
1156 tony 45 len := DecodeInteger(data,2);
1157     Inc(Data,2);
1158     end;
1159     end;
1160     end;
1161    
1162     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
1163     begin
1164     Result := false;
1165     end;
1166    
1167     { TIBXSQLDA }
1168     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
1169     begin
1170     inherited Create;
1171     FStatement := aStatement;
1172 tony 263 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
1173 tony 45 FSize := 0;
1174     // writeln('Creating ',ClassName);
1175     end;
1176    
1177 tony 371 constructor TIBXSQLDA.Create(api: IFirebirdAPI);
1178     begin
1179     inherited Create;
1180     FStatement := nil;
1181     FSize := 0;
1182     FFirebird30ClientAPI := api as TFB30ClientAPI;
1183     end;
1184    
1185 tony 45 destructor TIBXSQLDA.Destroy;
1186     begin
1187     FreeXSQLDA;
1188     // writeln('Destroying ',ClassName);
1189     inherited Destroy;
1190     end;
1191    
1192     procedure TIBXSQLDA.Changed;
1193     begin
1194    
1195     end;
1196    
1197     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
1198     begin
1199     Result := false;
1200 tony 371 if FStatement <> nil then
1201 tony 45 case Request of
1202     ssPrepared:
1203     Result := FStatement.IsPrepared;
1204    
1205     ssExecuteResults:
1206 tony 371 Result := FStatement.FSingleResults;
1207 tony 45
1208     ssCursorOpen:
1209     Result := FStatement.FOpen;
1210    
1211     ssBOF:
1212     Result := FStatement.FBOF;
1213    
1214     ssEOF:
1215     Result := FStatement.FEOF;
1216     end;
1217     end;
1218    
1219     function TIBXSQLDA.ColumnsInUseCount: integer;
1220     begin
1221     Result := FCount;
1222     end;
1223    
1224     procedure TIBXSQLDA.Initialize;
1225     begin
1226     if FMetaData <> nil then
1227     inherited Initialize;
1228     end;
1229    
1230     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
1231     begin
1232 tony 371 Result := (FStatement <> nil) and (FStatement.ChangeSeqNo <> ChangeSeqNo);
1233 tony 45 if Result then
1234     ChangeSeqNo := FStatement.ChangeSeqNo;
1235     end;
1236    
1237 tony 345 function TIBXSQLDA.CanChangeMetaData: boolean;
1238     begin
1239     Result := FStatement.FBatch = nil;
1240     end;
1241    
1242 tony 45 procedure TIBXSQLDA.SetCount(Value: Integer);
1243     var
1244     i: Integer;
1245     begin
1246     FCount := Value;
1247     if FCount = 0 then
1248     FUniqueRelationName := ''
1249     else
1250     begin
1251     SetLength(FColumnList, FCount);
1252     for i := FSize to FCount - 1 do
1253     FColumnList[i] := TIBXSQLVAR.Create(self,i);
1254     FSize := FCount;
1255     end;
1256     end;
1257    
1258 tony 371 procedure TIBXSQLDA.AllocMessageBuffer(len: integer);
1259     begin
1260     with FFirebird30ClientAPI do
1261     IBAlloc(FMessageBuffer,0,len);
1262     FMsgLength := len;
1263     end;
1264    
1265     procedure TIBXSQLDA.FreeMessageBuffer;
1266     begin
1267     if FMessageBuffer <> nil then
1268     begin
1269     FreeMem(FMessageBuffer);
1270     FMessageBuffer := nil;
1271     end;
1272     FMsgLength := 0;
1273     end;
1274    
1275     function TIBXSQLDA.GetMetaData: Firebird.IMessageMetadata;
1276     begin
1277     Result := FMetadata;
1278     if Result <> nil then
1279     Result.addRef;
1280     end;
1281    
1282 tony 45 function TIBXSQLDA.GetTransactionSeqNo: integer;
1283     begin
1284     Result := FTransactionSeqNo;
1285     end;
1286    
1287     procedure TIBXSQLDA.FreeXSQLDA;
1288     var i: integer;
1289     begin
1290     if FMetaData <> nil then
1291     FMetaData.release;
1292     FMetaData := nil;
1293     for i := 0 to Count - 1 do
1294     TIBXSQLVAR(Column[i]).FreeSQLData;
1295     for i := 0 to FSize - 1 do
1296     TIBXSQLVAR(Column[i]).Free;
1297 tony 345 FCount := 0;
1298 tony 45 SetLength(FColumnList,0);
1299     FSize := 0;
1300 tony 371 FreeMessageBuffer;
1301 tony 45 end;
1302    
1303     function TIBXSQLDA.GetStatement: IStatement;
1304     begin
1305     Result := FStatement;
1306     end;
1307    
1308     function TIBXSQLDA.GetPrepareSeqNo: integer;
1309     begin
1310 tony 371 if FStatement = nil then
1311     Result := 0
1312     else
1313     Result := FStatement.FPrepareSeqNo;
1314 tony 45 end;
1315    
1316     { TFB30Statement }
1317    
1318 tony 345 procedure TFB30Statement.CheckChangeBatchRowLimit;
1319     begin
1320     if IsInBatchMode then
1321     IBError(ibxeInBatchMode,[nil]);
1322     end;
1323    
1324 tony 45 procedure TFB30Statement.CheckHandle;
1325     begin
1326     if FStatementIntf = nil then
1327     IBError(ibxeInvalidStatementHandle,[nil]);
1328     end;
1329    
1330 tony 345 procedure TFB30Statement.CheckBatchModeAvailable;
1331     begin
1332     if not HasBatchMode then
1333     IBError(ibxeBatchModeNotSupported,[nil]);
1334     case SQLStatementType of
1335     SQLInsert,
1336     SQLUpdate: {OK};
1337     else
1338     IBError(ibxeInvalidBatchQuery,[GetSQLStatementTypeName]);
1339     end;
1340     end;
1341    
1342 tony 45 procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1343     );
1344     begin
1345 tony 263 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
1346 tony 45 begin
1347     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
1348     GetBufSize, BytePtr(Buffer));
1349     Check4DataBaseError;
1350     end;
1351     end;
1352    
1353 tony 363 function TFB30Statement.GetStatementIntf: IStatement;
1354     begin
1355     Result := self;
1356     end;
1357    
1358 tony 350 procedure TFB30Statement.InternalPrepare(CursorName: AnsiString);
1359     var GUID : TGUID;
1360 tony 371 metadata: Firebird.IMessageMetadata;
1361 tony 45 begin
1362     if FPrepared then
1363     Exit;
1364 tony 350
1365     FCursor := CursorName;
1366 tony 45 if (FSQL = '') then
1367     IBError(ibxeEmptyQuery, [nil]);
1368     try
1369     CheckTransaction(FTransactionIntf);
1370 tony 263 with FFirebird30ClientAPI do
1371 tony 45 begin
1372 tony 350 if FCursor = '' then
1373     begin
1374     CreateGuid(GUID);
1375     FCursor := GUIDToString(GUID);
1376     end;
1377    
1378 tony 45 if FHasParamNames then
1379     begin
1380     if FProcessedSQL = '' then
1381 tony 263 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1382 tony 45 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1383     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1384     Length(FProcessedSQL),
1385 tony 56 PAnsiChar(FProcessedSQL),
1386 tony 45 FSQLDialect,
1387     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1388     end
1389     else
1390     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1391     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1392     Length(FSQL),
1393 tony 56 PAnsiChar(FSQL),
1394 tony 45 FSQLDialect,
1395     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1396     Check4DataBaseError;
1397     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1398     Check4DataBaseError;
1399    
1400 tony 350 if FSQLStatementType = SQLSelect then
1401     begin
1402     FStatementIntf.setCursorName(StatusIntf,PAnsiChar(FCursor));
1403     Check4DataBaseError;
1404     end;
1405 tony 45 { Done getting the type }
1406     case FSQLStatementType of
1407     SQLGetSegment,
1408     SQLPutSegment,
1409     SQLStartTransaction:
1410     begin
1411     FreeHandle;
1412     IBError(ibxeNotPermitted, [nil]);
1413     end;
1414     SQLCommit,
1415     SQLRollback,
1416     SQLDDL, SQLSetGenerator,
1417     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1418     SQLExecProcedure:
1419     begin
1420     {set up input sqlda}
1421 tony 371 metadata := FStatementIntf.getInputMetadata(StatusIntf);
1422 tony 45 Check4DataBaseError;
1423 tony 371 try
1424     FSQLParams.Bind(metadata);
1425     finally
1426     metadata.release;
1427     end;
1428 tony 45
1429     {setup output sqlda}
1430     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1431     SQLExecProcedure] then
1432 tony 371 begin
1433     metadata := FStatementIntf.getOutputMetadata(StatusIntf);
1434     Check4DataBaseError;
1435     try
1436     FSQLRecord.Bind(metadata);
1437     finally
1438     metadata.release;
1439     end;
1440     end;
1441 tony 45 end;
1442     end;
1443     end;
1444     except
1445     on E: Exception do begin
1446     if (FStatementIntf <> nil) then
1447     FreeHandle;
1448     if E is EIBInterBaseError then
1449 tony 315 E.Message := E.Message + sSQLErrorSeparator + FSQL;
1450     raise;
1451 tony 45 end;
1452     end;
1453     FPrepared := true;
1454 tony 350
1455 tony 45 FSingleResults := false;
1456     if RetainInterfaces then
1457     begin
1458     SetRetainInterfaces(false);
1459     SetRetainInterfaces(true);
1460     end;
1461     Inc(FPrepareSeqNo);
1462     with GetTransaction as TFB30Transaction do
1463     begin
1464     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1465     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1466     end;
1467     SignalActivity;
1468     Inc(FChangeSeqNo);
1469     end;
1470    
1471     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1472 tony 345
1473     procedure ExecuteQuery(outMetaData: Firebird.IMessageMetaData=nil; outBuffer: pointer=nil);
1474 tony 371 var inMetadata: Firebird.IMessageMetaData;
1475 tony 345 begin
1476     with FFirebird30ClientAPI do
1477     begin
1478     SavePerfStats(FBeforeStats);
1479 tony 371 inMetadata := FSQLParams.GetMetaData;
1480     try
1481     FStatementIntf.execute(StatusIntf,
1482     (aTransaction as TFB30Transaction).TransactionIntf,
1483     inMetaData,
1484     FSQLParams.MessageBuffer,
1485     outMetaData,
1486     outBuffer);
1487     Check4DataBaseError;
1488     finally
1489     if inMetadata <> nil then
1490     inMetadata.release;
1491     end;
1492 tony 345 FStatisticsAvailable := SavePerfStats(FAfterStats);
1493     end;
1494     end;
1495    
1496 tony 359 var Cursor: IResultSet;
1497 tony 371 outMetadata: Firebird.IMessageMetaData;
1498 tony 345
1499 tony 45 begin
1500     Result := nil;
1501 tony 345 FBatchCompletion := nil;
1502 tony 45 FBOF := false;
1503     FEOF := false;
1504     FSingleResults := false;
1505 tony 345 FStatisticsAvailable := false;
1506     if IsInBatchMode then
1507     IBerror(ibxeInBatchMode,[]);
1508 tony 45 CheckTransaction(aTransaction);
1509     if not FPrepared then
1510     InternalPrepare;
1511     CheckHandle;
1512     if aTransaction <> FTransactionIntf then
1513     AddMonitor(aTransaction as TFB30Transaction);
1514 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1515 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1516    
1517 tony 345
1518 tony 45 try
1519 tony 263 with FFirebird30ClientAPI do
1520 tony 45 begin
1521 tony 47 case FSQLStatementType of
1522     SQLSelect:
1523 tony 359 {e.g. Update...returning with a single row in Firebird 5 and later}
1524     begin
1525     Cursor := InternalOpenCursor(aTransaction,false);
1526     if not Cursor.IsEof then
1527     Cursor.FetchNext;
1528     Result := Cursor; {note only first row}
1529     FSingleResults := true;
1530     end;
1531 tony 47
1532     SQLExecProcedure:
1533     begin
1534 tony 371 outMetadata := FSQLRecord.GetMetaData;
1535     try
1536     ExecuteQuery(outMetadata,FSQLRecord.MessageBuffer);
1537     Result := TResults.Create(FSQLRecord);
1538     FSingleResults := true;
1539     finally
1540     if outMetadata <> nil then
1541     outMetadata.release;
1542     end;
1543 tony 345 end;
1544    
1545 tony 47 else
1546 tony 345 ExecuteQuery;
1547 tony 47 end;
1548 tony 45 end;
1549     finally
1550     if aTransaction <> FTransactionIntf then
1551     RemoveMonitor(aTransaction as TFB30Transaction);
1552     end;
1553     FExecTransactionIntf := aTransaction;
1554 tony 111 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1555     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1556 tony 45 SignalActivity;
1557     Inc(FChangeSeqNo);
1558     end;
1559    
1560 tony 350 function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction;
1561     Scrollable: boolean): IResultSet;
1562     var flags: cardinal;
1563 tony 371 inMetadata,
1564     outMetadata: Firebird.IMessageMetadata;
1565 tony 45 begin
1566 tony 350 flags := 0;
1567 tony 359 if (FSQLStatementType <> SQLSelect) and not (stHasCursor in getFlags) then
1568 tony 45 IBError(ibxeIsASelectStatement,[]);
1569    
1570 tony 345 FBatchCompletion := nil;
1571     CheckTransaction(aTransaction);
1572 tony 45 if not FPrepared then
1573     InternalPrepare;
1574     CheckHandle;
1575     if aTransaction <> FTransactionIntf then
1576     AddMonitor(aTransaction as TFB30Transaction);
1577 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1578 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1579    
1580 tony 350 if Scrollable then
1581     flags := Firebird.IStatement.CURSOR_TYPE_SCROLLABLE;
1582    
1583 tony 263 with FFirebird30ClientAPI do
1584 tony 45 begin
1585 tony 47 if FCollectStatistics then
1586     begin
1587     UtilIntf.getPerfCounters(StatusIntf,
1588     (GetAttachment as TFB30Attachment).AttachmentIntf,
1589     ISQL_COUNTERS, @FBeforeStats);
1590     Check4DataBaseError;
1591     end;
1592    
1593 tony 371 inMetadata := FSQLParams.GetMetaData;
1594     outMetadata := FSQLRecord.GetMetaData;
1595     try
1596     FResultSet := FStatementIntf.openCursor(StatusIntf,
1597 tony 45 (aTransaction as TFB30Transaction).TransactionIntf,
1598 tony 371 inMetaData,
1599 tony 45 FSQLParams.MessageBuffer,
1600 tony 371 outMetaData,
1601 tony 350 flags);
1602 tony 371 Check4DataBaseError;
1603     finally
1604     if inMetadata <> nil then
1605     inMetadata.release;
1606     if outMetadata <> nil then
1607     outMetadata.release;
1608     end;
1609 tony 47
1610     if FCollectStatistics then
1611     begin
1612     UtilIntf.getPerfCounters(StatusIntf,
1613     (GetAttachment as TFB30Attachment).AttachmentIntf,
1614     ISQL_COUNTERS,@FAfterStats);
1615     Check4DataBaseError;
1616     FStatisticsAvailable := true;
1617     end;
1618 tony 45 end;
1619     Inc(FCursorSeqNo);
1620     FSingleResults := false;
1621     FOpen := True;
1622     FExecTransactionIntf := aTransaction;
1623     FBOF := true;
1624     FEOF := false;
1625     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1626     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1627     Result := TResultSet.Create(FSQLRecord);
1628     SignalActivity;
1629     Inc(FChangeSeqNo);
1630     end;
1631    
1632 tony 263 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1633     var processedSQL: AnsiString);
1634     begin
1635     FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1636     end;
1637    
1638 tony 45 procedure TFB30Statement.FreeHandle;
1639     begin
1640     Close;
1641     ReleaseInterfaces;
1642 tony 345 if FBatch <> nil then
1643     begin
1644     FBatch.release;
1645     FBatch := nil;
1646     end;
1647 tony 45 if FStatementIntf <> nil then
1648     begin
1649     FStatementIntf.release;
1650     FStatementIntf := nil;
1651     FPrepared := false;
1652     end;
1653 tony 350 FCursor := '';
1654 tony 45 end;
1655    
1656     procedure TFB30Statement.InternalClose(Force: boolean);
1657     begin
1658     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1659     try
1660 tony 263 with FFirebird30ClientAPI do
1661 tony 45 begin
1662     if FResultSet <> nil then
1663     begin
1664     if FSQLRecord.FTransaction.InTransaction and
1665     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1666     FResultSet.close(StatusIntf)
1667     else
1668     FResultSet.release;
1669     end;
1670     FResultSet := nil;
1671     if not Force then Check4DataBaseError;
1672     end;
1673     finally
1674 tony 56 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1675 tony 45 RemoveMonitor(FSQLRecord.FTransaction);
1676     FOpen := False;
1677     FExecTransactionIntf := nil;
1678     FSQLRecord.FTransaction := nil;
1679     end;
1680     SignalActivity;
1681     Inc(FChangeSeqNo);
1682     end;
1683    
1684 tony 345 function TFB30Statement.SavePerfStats(var Stats: TPerfStatistics): boolean;
1685     begin
1686     Result := false;
1687     if FCollectStatistics then
1688     with FFirebird30ClientAPI do
1689     begin
1690     UtilIntf.getPerfCounters(StatusIntf,
1691     (GetAttachment as TFB30Attachment).AttachmentIntf,
1692     ISQL_COUNTERS, @Stats);
1693     Check4DataBaseError;
1694     Result := true;
1695     end;
1696     end;
1697    
1698 tony 45 constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1699 tony 350 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
1700     CursorName: AnsiString);
1701 tony 45 begin
1702     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1703 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1704 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1705     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1706 tony 350 InternalPrepare(CursorName);
1707 tony 45 end;
1708    
1709     constructor TFB30Statement.CreateWithParameterNames(
1710 tony 56 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1711 tony 270 aSQLDialect: integer; GenerateParamNames: boolean;
1712 tony 350 CaseSensitiveParams: boolean; CursorName: AnsiString);
1713 tony 45 begin
1714     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1715 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1716 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1717 tony 270 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1718 tony 45 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1719 tony 350 InternalPrepare(CursorName);
1720 tony 45 end;
1721    
1722     destructor TFB30Statement.Destroy;
1723     begin
1724     inherited Destroy;
1725     if assigned(FSQLParams) then FSQLParams.Free;
1726     if assigned(FSQLRecord) then FSQLRecord.Free;
1727     end;
1728    
1729 tony 350 function TFB30Statement.Fetch(FetchType: TFetchType; PosOrOffset: integer
1730     ): boolean;
1731 tony 45 var fetchResult: integer;
1732     begin
1733 tony 371 result := false;
1734 tony 45 if not FOpen then
1735     IBError(ibxeSQLClosed, [nil]);
1736    
1737 tony 263 with FFirebird30ClientAPI do
1738 tony 45 begin
1739 tony 350 case FetchType of
1740     ftNext:
1741     begin
1742     if FEOF then
1743     IBError(ibxeEOF,[nil]);
1744     { Go to the next record... }
1745     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1746     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1747     begin
1748     FBOF := false;
1749     FEOF := true;
1750     Exit; {End of File}
1751     end
1752 tony 45 end;
1753 tony 350
1754     ftPrior:
1755     begin
1756     if FBOF then
1757     IBError(ibxeBOF,[nil]);
1758     { Go to the next record... }
1759     fetchResult := FResultSet.fetchPrior(StatusIntf,FSQLRecord.MessageBuffer);
1760     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1761     begin
1762     FBOF := true;
1763     FEOF := false;
1764     Exit; {Top of File}
1765     end
1766     end;
1767    
1768     ftFirst:
1769     fetchResult := FResultSet.fetchFirst(StatusIntf,FSQLRecord.MessageBuffer);
1770    
1771     ftLast:
1772     fetchResult := FResultSet.fetchLast(StatusIntf,FSQLRecord.MessageBuffer);
1773    
1774     ftAbsolute:
1775     fetchResult := FResultSet.fetchAbsolute(StatusIntf,PosOrOffset,FSQLRecord.MessageBuffer);
1776    
1777     ftRelative:
1778     fetchResult := FResultSet.fetchRelative(StatusIntf,PosOrOffset,FSQLRecord.MessageBuffer);
1779 tony 45 end;
1780 tony 350
1781     Check4DataBaseError;
1782     if fetchResult <> Firebird.IStatus.RESULT_OK then
1783     exit; {result = false}
1784    
1785     {Result OK}
1786     FBOF := false;
1787     FEOF := false;
1788     result := true;
1789    
1790 tony 209 if FCollectStatistics then
1791     begin
1792     UtilIntf.getPerfCounters(StatusIntf,
1793     (GetAttachment as TFB30Attachment).AttachmentIntf,
1794     ISQL_COUNTERS,@FAfterStats);
1795     Check4DataBaseError;
1796     FStatisticsAvailable := true;
1797     end;
1798 tony 45 end;
1799     FSQLRecord.RowChange;
1800     SignalActivity;
1801     if FEOF then
1802     Inc(FChangeSeqNo);
1803     end;
1804    
1805     function TFB30Statement.GetSQLParams: ISQLParams;
1806     begin
1807     CheckHandle;
1808     if not HasInterface(0) then
1809     AddInterface(0,TSQLParams.Create(FSQLParams));
1810     Result := TSQLParams(GetInterface(0));
1811     end;
1812    
1813     function TFB30Statement.GetMetaData: IMetaData;
1814     begin
1815     CheckHandle;
1816     if not HasInterface(1) then
1817     AddInterface(1, TMetaData.Create(FSQLRecord));
1818     Result := TMetaData(GetInterface(1));
1819     end;
1820    
1821 tony 56 function TFB30Statement.GetPlan: AnsiString;
1822 tony 45 begin
1823     CheckHandle;
1824     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1825     {TODO: SQLExecProcedure, }
1826     SQLUpdate, SQLDelete])) then
1827     result := ''
1828     else
1829 tony 263 with FFirebird30ClientAPI do
1830 tony 45 begin
1831     Result := FStatementIntf.getPlan(StatusIntf,true);
1832     Check4DataBaseError;
1833     end;
1834     end;
1835    
1836     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1837     begin
1838     if assigned(column) and (column.SQLType <> SQL_Blob) then
1839     IBError(ibxeNotABlob,[nil]);
1840     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1841     GetTransaction as TFB30Transaction,
1842     column.GetBlobMetaData,nil);
1843     end;
1844    
1845     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1846     begin
1847     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1848     IBError(ibxeNotAnArray,[nil]);
1849     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1850     GetTransaction as TFB30Transaction,
1851     column.GetArrayMetaData);
1852     end;
1853    
1854     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1855     begin
1856     inherited SetRetainInterfaces(aValue);
1857     if HasInterface(1) then
1858     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1859     if HasInterface(0) then
1860     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1861     end;
1862    
1863 tony 345 function TFB30Statement.IsInBatchMode: boolean;
1864     begin
1865     Result := FBatch <> nil;
1866     end;
1867    
1868     function TFB30Statement.HasBatchMode: boolean;
1869     begin
1870     Result := GetAttachment.HasBatchMode;
1871     end;
1872    
1873     procedure TFB30Statement.AddToBatch;
1874     var BatchPB: TXPBParameterBlock;
1875 tony 371 inMetadata: Firebird.IMessageMetadata;
1876 tony 345
1877     const SixteenMB = 16 * 1024 * 1024;
1878 tony 371 MB256 = 256* 1024 *1024;
1879 tony 345 begin
1880     FBatchCompletion := nil;
1881     if not FPrepared then
1882     InternalPrepare;
1883     CheckHandle;
1884     CheckBatchModeAvailable;
1885 tony 371 inMetadata := FSQLParams.GetMetaData;
1886     try
1887     with FFirebird30ClientAPI do
1888 tony 345 begin
1889 tony 371 if FBatch = nil then
1890     begin
1891     {Start Batch}
1892     BatchPB := TXPBParameterBlock.Create(FFirebird30ClientAPI,Firebird.IXpbBuilder.BATCH);
1893     with FFirebird30ClientAPI do
1894     try
1895     if FBatchRowLimit = maxint then
1896     FBatchBufferSize := MB256
1897     else
1898     begin
1899     FBatchBufferSize := FBatchRowLimit * inMetadata.getAlignedLength(StatusIntf);
1900     Check4DatabaseError;
1901     if FBatchBufferSize < SixteenMB then
1902     FBatchBufferSize := SixteenMB;
1903     if FBatchBufferSize > MB256 {assumed limit} then
1904     IBError(ibxeBatchBufferSizeTooBig,[FBatchBufferSize]);
1905     end;
1906     BatchPB.insertInt(Firebird.IBatch.TAG_RECORD_COUNTS,1);
1907     BatchPB.insertInt(Firebird.IBatch.TAG_BUFFER_BYTES_SIZE,FBatchBufferSize);
1908     FBatch := FStatementIntf.createBatch(StatusIntf,
1909     inMetadata,
1910     BatchPB.getDataLength,
1911     BatchPB.getBuffer);
1912     Check4DataBaseError;
1913 tony 345
1914 tony 371 finally
1915     BatchPB.Free;
1916     end;
1917     FBatchRowCount := 0;
1918     FBatchBufferUsed := 0;
1919 tony 345 end;
1920    
1921 tony 371 Inc(FBatchRowCount);
1922     Inc(FBatchBufferUsed,inMetadata.getAlignedLength(StatusIntf));
1923     Check4DataBaseError;
1924     if FBatchBufferUsed > FBatchBufferSize then
1925     raise EIBBatchBufferOverflow.Create(Ord(ibxeBatchRowBufferOverflow),
1926     Format(GetErrorMessage(ibxeBatchRowBufferOverflow),
1927     [FBatchRowCount,FBatchBufferSize]));
1928 tony 345
1929 tony 371 FBatch.Add(StatusIntf,1,FSQLParams.GetMessageBuffer);
1930     Check4DataBaseError
1931     end;
1932     finally
1933     if inMetadata <> nil then
1934     inMetadata.release;
1935 tony 345 end;
1936     end;
1937    
1938     function TFB30Statement.ExecuteBatch(aTransaction: ITransaction
1939     ): IBatchCompletion;
1940    
1941     procedure Check4BatchCompletionError(bc: IBatchCompletion);
1942     var status: IStatus;
1943     RowNo: integer;
1944     begin
1945     status := nil;
1946     {Raise an exception if there was an error reported in the BatchCompletion}
1947     if (bc <> nil) and bc.getErrorStatus(RowNo,status) then
1948     raise EIBInterbaseError.Create(status);
1949     end;
1950    
1951     var cs: Firebird.IBatchCompletionState;
1952    
1953     begin
1954     Result := nil;
1955     if FBatch = nil then
1956     IBError(ibxeNotInBatchMode,[]);
1957    
1958     with FFirebird30ClientAPI do
1959     begin
1960     SavePerfStats(FBeforeStats);
1961     if aTransaction = nil then
1962     cs := FBatch.execute(StatusIntf,(FTransactionIntf as TFB30Transaction).TransactionIntf)
1963     else
1964     cs := FBatch.execute(StatusIntf,(aTransaction as TFB30Transaction).TransactionIntf);
1965     Check4DataBaseError;
1966     FBatchCompletion := TBatchCompletion.Create(FFirebird30ClientAPI,cs);
1967     FStatisticsAvailable := SavePerfStats(FAfterStats);
1968     FBatch.release;
1969     FBatch := nil;
1970     Check4BatchCompletionError(FBatchCompletion);
1971     Result := FBatchCompletion;
1972     end;
1973     end;
1974    
1975     procedure TFB30Statement.CancelBatch;
1976     begin
1977     if FBatch = nil then
1978     IBError(ibxeNotInBatchMode,[]);
1979     FBatch.release;
1980     FBatch := nil;
1981     end;
1982    
1983     function TFB30Statement.GetBatchCompletion: IBatchCompletion;
1984     begin
1985     Result := FBatchCompletion;
1986     end;
1987    
1988 tony 45 function TFB30Statement.IsPrepared: boolean;
1989     begin
1990     Result := FStatementIntf <> nil;
1991     end;
1992    
1993 tony 359 function TFB30Statement.GetFlags: TStatementFlags;
1994     var flags: cardinal;
1995     begin
1996     CheckHandle;
1997     Result := [];
1998     with FFirebird30ClientAPI do
1999     begin
2000     flags := FStatementIntf.getFlags(StatusIntf);
2001     Check4DataBaseError;
2002     end;
2003     if flags and Firebird.IStatement.FLAG_HAS_CURSOR <> 0 then
2004     Result := Result + [stHasCursor];
2005     if flags and Firebird.IStatement.FLAG_REPEAT_EXECUTE <> 0 then
2006     Result := Result + [stRepeatExecute];
2007     if flags and Firebird.IStatement.CURSOR_TYPE_SCROLLABLE <> 0 then
2008     Result := Result + [stScrollable];
2009     end;
2010    
2011 tony 45 end.
2012