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: 362
Committed: Tue Dec 7 13:27:39 2021 UTC (3 years ago) by tony
Content type: text/x-pascal
Original Path: ibx/branches/journaling/fbintf/client/3.0/FB30Statement.pas
File size: 53842 byte(s)
Log Message:
initiate test release

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