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: 384
Committed: Mon Jan 17 09:52:58 2022 UTC (2 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 55916 byte(s)
Log Message:
Ensure null idicator set to not null for not null columns

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 384 aNullIndicator: short;
986 tony 68 begin
987     BuildMetadata;
988    
989 tony 345 if (FMsgLength = 0) and (FCurMetaData <> nil) then
990 tony 263 with FFirebird30ClientAPI do
991 tony 68 begin
992 tony 371 MsgLen := FCurMetaData.getMessageLength(StatusIntf);
993 tony 45 Check4DataBaseError;
994    
995 tony 371 AllocMessageBuffer(MsgLen);
996 tony 45
997     for i := 0 to Count - 1 do
998     with TIBXSQLVar(Column[i]) do
999     begin
1000 tony 345 P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
1001     // writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
1002 tony 68 if not Modified then
1003     IBError(ibxeUninitializedInputParameter,[i,Name]);
1004 tony 47 if IsNull then
1005 tony 345 FillChar(P^,FDataLength,0)
1006 tony 47 else
1007 tony 68 if FSQLData <> nil then
1008 tony 345 begin
1009     if SQLType = SQL_VARYING then
1010     begin
1011     EncodeInteger(FDataLength,2,P);
1012     Inc(P,2);
1013     end
1014     else
1015     if (SQLType = SQL_BLOB) and (FStatement.FBatch <> nil) then
1016     begin
1017     FStatement.FBatch.registerBlob(Statusintf,ISC_QUADPtr(FSQLData),ISC_QUADPtr(FSQLData));
1018     Check4DatabaseError;
1019     end;
1020     Move(FSQLData^,P^,FDataLength);
1021     end;
1022 tony 45 if IsNullable then
1023     begin
1024     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
1025     Check4DataBaseError;
1026 tony 384 end
1027     else
1028     begin
1029     aNullIndicator := 0;
1030     Move(aNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(aNullIndicator));
1031 tony 45 end;
1032     end;
1033     end;
1034     end;
1035    
1036     procedure TIBXINPUTSQLDA.FreeXSQLDA;
1037     begin
1038     inherited FreeXSQLDA;
1039 tony 345 FreeCurMetaData;
1040 tony 45 end;
1041    
1042     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
1043     begin
1044     inherited Create(aStatement);
1045     FMessageBuffer := nil;
1046     end;
1047    
1048 tony 371 constructor TIBXINPUTSQLDA.Create(api: IFirebirdAPI);
1049     begin
1050     inherited Create(api);
1051     FMessageBuffer := nil;
1052     end;
1053    
1054 tony 45 destructor TIBXINPUTSQLDA.Destroy;
1055     begin
1056 tony 345 FreeXSQLDA;
1057 tony 45 inherited Destroy;
1058     end;
1059    
1060     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1061     var i: integer;
1062     begin
1063     FMetaData := aMetaData;
1064 tony 371 FMetaData.AddRef;
1065 tony 263 with FFirebird30ClientAPI do
1066 tony 45 begin
1067 tony 338 Count := aMetadata.getCount(StatusIntf);
1068 tony 45 Check4DataBaseError;
1069     Initialize;
1070    
1071     for i := 0 to Count - 1 do
1072     with TIBXSQLVar(Column[i]) do
1073     begin
1074 tony 349 InitColumnMetaData(aMetaData);
1075     SaveMetaData;
1076 tony 45 if FNullable then
1077     FSQLNullIndicator := @FNullIndicator
1078     else
1079     FSQLNullIndicator := nil;
1080 tony 345 ColumnSQLDataInit;
1081 tony 45 end;
1082     end;
1083     end;
1084    
1085     procedure TIBXINPUTSQLDA.Changed;
1086     begin
1087     inherited Changed;
1088 tony 345 FreeCurMetaData;
1089 tony 45 FreeMessageBuffer;
1090     end;
1091    
1092 tony 345 procedure TIBXINPUTSQLDA.ReInitialise;
1093     var i: integer;
1094     begin
1095     FreeMessageBuffer;
1096     for i := 0 to Count - 1 do
1097     TIBXSQLVar(Column[i]).ColumnSQLDataInit;
1098     end;
1099    
1100 tony 45 function TIBXINPUTSQLDA.IsInputDataArea: boolean;
1101     begin
1102     Result := true;
1103     end;
1104    
1105     { TIBXOUTPUTSQLDA }
1106    
1107 tony 371 function TIBXOUTPUTSQLDA.GetTransaction: ITransaction;
1108 tony 45 begin
1109 tony 371 if FTransaction <> nil then
1110     Result := FTransaction
1111     else
1112     Result := inherited GetTransaction;
1113 tony 45 end;
1114    
1115     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1116     var i: integer;
1117 tony 371 MsgLen: cardinal;
1118 tony 45 begin
1119     FMetaData := aMetaData;
1120 tony 371 FMetaData.AddRef;
1121 tony 263 with FFirebird30ClientAPI do
1122 tony 45 begin
1123 tony 371 Count := aMetaData.getCount(StatusIntf);
1124 tony 45 Check4DataBaseError;
1125     Initialize;
1126    
1127 tony 371 MsgLen := aMetaData.getMessageLength(StatusIntf);
1128 tony 45 Check4DataBaseError;
1129 tony 371 AllocMessageBuffer(MsgLen);
1130 tony 45
1131     for i := 0 to Count - 1 do
1132     with TIBXSQLVar(Column[i]) do
1133     begin
1134 tony 349 InitColumnMetaData(aMetaData);
1135 tony 371 FSQLData := FMessageBuffer + aMetaData.getOffset(StatusIntf,i);
1136 tony 45 Check4DataBaseError;
1137     if FNullable then
1138     begin
1139     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
1140     Check4DataBaseError;
1141     end
1142     else
1143     FSQLNullIndicator := nil;
1144 tony 349 FBlob := nil;
1145 tony 363 FArrayIntf := nil;
1146 tony 45 end;
1147     end;
1148     SetUniqueRelationName;
1149     end;
1150    
1151     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
1152 tony 56 var len: short; var data: PByte);
1153 tony 45 begin
1154     with TIBXSQLVAR(Column[index]) do
1155     begin
1156     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
1157     data := FSQLData;
1158     len := FDataLength;
1159     if not IsNull and (FSQLType = SQL_VARYING) then
1160     begin
1161 tony 263 with FFirebird30ClientAPI do
1162 tony 45 len := DecodeInteger(data,2);
1163     Inc(Data,2);
1164     end;
1165     end;
1166     end;
1167    
1168     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
1169     begin
1170     Result := false;
1171     end;
1172    
1173     { TIBXSQLDA }
1174     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
1175     begin
1176     inherited Create;
1177     FStatement := aStatement;
1178 tony 263 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
1179 tony 45 FSize := 0;
1180     // writeln('Creating ',ClassName);
1181     end;
1182    
1183 tony 371 constructor TIBXSQLDA.Create(api: IFirebirdAPI);
1184     begin
1185     inherited Create;
1186     FStatement := nil;
1187     FSize := 0;
1188     FFirebird30ClientAPI := api as TFB30ClientAPI;
1189     end;
1190    
1191 tony 45 destructor TIBXSQLDA.Destroy;
1192     begin
1193     FreeXSQLDA;
1194     // writeln('Destroying ',ClassName);
1195     inherited Destroy;
1196     end;
1197    
1198     procedure TIBXSQLDA.Changed;
1199     begin
1200    
1201     end;
1202    
1203     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
1204     begin
1205     Result := false;
1206 tony 371 if FStatement <> nil then
1207 tony 45 case Request of
1208     ssPrepared:
1209     Result := FStatement.IsPrepared;
1210    
1211     ssExecuteResults:
1212 tony 371 Result := FStatement.FSingleResults;
1213 tony 45
1214     ssCursorOpen:
1215     Result := FStatement.FOpen;
1216    
1217     ssBOF:
1218     Result := FStatement.FBOF;
1219    
1220     ssEOF:
1221     Result := FStatement.FEOF;
1222     end;
1223     end;
1224    
1225     function TIBXSQLDA.ColumnsInUseCount: integer;
1226     begin
1227     Result := FCount;
1228     end;
1229    
1230     procedure TIBXSQLDA.Initialize;
1231     begin
1232     if FMetaData <> nil then
1233     inherited Initialize;
1234     end;
1235    
1236     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
1237     begin
1238 tony 371 Result := (FStatement <> nil) and (FStatement.ChangeSeqNo <> ChangeSeqNo);
1239 tony 45 if Result then
1240     ChangeSeqNo := FStatement.ChangeSeqNo;
1241     end;
1242    
1243 tony 345 function TIBXSQLDA.CanChangeMetaData: boolean;
1244     begin
1245     Result := FStatement.FBatch = nil;
1246     end;
1247    
1248 tony 45 procedure TIBXSQLDA.SetCount(Value: Integer);
1249     var
1250     i: Integer;
1251     begin
1252     FCount := Value;
1253     if FCount = 0 then
1254     FUniqueRelationName := ''
1255     else
1256     begin
1257     SetLength(FColumnList, FCount);
1258     for i := FSize to FCount - 1 do
1259     FColumnList[i] := TIBXSQLVAR.Create(self,i);
1260     FSize := FCount;
1261     end;
1262     end;
1263    
1264 tony 371 procedure TIBXSQLDA.AllocMessageBuffer(len: integer);
1265     begin
1266     with FFirebird30ClientAPI do
1267     IBAlloc(FMessageBuffer,0,len);
1268     FMsgLength := len;
1269     end;
1270    
1271     procedure TIBXSQLDA.FreeMessageBuffer;
1272     begin
1273     if FMessageBuffer <> nil then
1274     begin
1275     FreeMem(FMessageBuffer);
1276     FMessageBuffer := nil;
1277     end;
1278     FMsgLength := 0;
1279     end;
1280    
1281     function TIBXSQLDA.GetMetaData: Firebird.IMessageMetadata;
1282     begin
1283     Result := FMetadata;
1284     if Result <> nil then
1285     Result.addRef;
1286     end;
1287    
1288 tony 45 function TIBXSQLDA.GetTransactionSeqNo: integer;
1289     begin
1290     Result := FTransactionSeqNo;
1291     end;
1292    
1293     procedure TIBXSQLDA.FreeXSQLDA;
1294     var i: integer;
1295     begin
1296     if FMetaData <> nil then
1297     FMetaData.release;
1298     FMetaData := nil;
1299     for i := 0 to Count - 1 do
1300     TIBXSQLVAR(Column[i]).FreeSQLData;
1301     for i := 0 to FSize - 1 do
1302     TIBXSQLVAR(Column[i]).Free;
1303 tony 345 FCount := 0;
1304 tony 45 SetLength(FColumnList,0);
1305     FSize := 0;
1306 tony 371 FreeMessageBuffer;
1307 tony 45 end;
1308    
1309     function TIBXSQLDA.GetStatement: IStatement;
1310     begin
1311     Result := FStatement;
1312     end;
1313    
1314     function TIBXSQLDA.GetPrepareSeqNo: integer;
1315     begin
1316 tony 371 if FStatement = nil then
1317     Result := 0
1318     else
1319     Result := FStatement.FPrepareSeqNo;
1320 tony 45 end;
1321    
1322     { TFB30Statement }
1323    
1324 tony 345 procedure TFB30Statement.CheckChangeBatchRowLimit;
1325     begin
1326     if IsInBatchMode then
1327     IBError(ibxeInBatchMode,[nil]);
1328     end;
1329    
1330 tony 45 procedure TFB30Statement.CheckHandle;
1331     begin
1332     if FStatementIntf = nil then
1333     IBError(ibxeInvalidStatementHandle,[nil]);
1334     end;
1335    
1336 tony 345 procedure TFB30Statement.CheckBatchModeAvailable;
1337     begin
1338     if not HasBatchMode then
1339     IBError(ibxeBatchModeNotSupported,[nil]);
1340     case SQLStatementType of
1341     SQLInsert,
1342     SQLUpdate: {OK};
1343     else
1344     IBError(ibxeInvalidBatchQuery,[GetSQLStatementTypeName]);
1345     end;
1346     end;
1347    
1348 tony 45 procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1349     );
1350     begin
1351 tony 263 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
1352 tony 45 begin
1353     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
1354     GetBufSize, BytePtr(Buffer));
1355     Check4DataBaseError;
1356     end;
1357     end;
1358    
1359 tony 363 function TFB30Statement.GetStatementIntf: IStatement;
1360     begin
1361     Result := self;
1362     end;
1363    
1364 tony 350 procedure TFB30Statement.InternalPrepare(CursorName: AnsiString);
1365     var GUID : TGUID;
1366 tony 371 metadata: Firebird.IMessageMetadata;
1367 tony 45 begin
1368     if FPrepared then
1369     Exit;
1370 tony 350
1371     FCursor := CursorName;
1372 tony 45 if (FSQL = '') then
1373     IBError(ibxeEmptyQuery, [nil]);
1374     try
1375     CheckTransaction(FTransactionIntf);
1376 tony 263 with FFirebird30ClientAPI do
1377 tony 45 begin
1378 tony 350 if FCursor = '' then
1379     begin
1380     CreateGuid(GUID);
1381     FCursor := GUIDToString(GUID);
1382     end;
1383    
1384 tony 45 if FHasParamNames then
1385     begin
1386     if FProcessedSQL = '' then
1387 tony 263 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1388 tony 45 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1389     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1390     Length(FProcessedSQL),
1391 tony 56 PAnsiChar(FProcessedSQL),
1392 tony 45 FSQLDialect,
1393     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1394     end
1395     else
1396     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1397     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1398     Length(FSQL),
1399 tony 56 PAnsiChar(FSQL),
1400 tony 45 FSQLDialect,
1401     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1402     Check4DataBaseError;
1403     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1404     Check4DataBaseError;
1405    
1406 tony 350 if FSQLStatementType = SQLSelect then
1407     begin
1408     FStatementIntf.setCursorName(StatusIntf,PAnsiChar(FCursor));
1409     Check4DataBaseError;
1410     end;
1411 tony 45 { Done getting the type }
1412     case FSQLStatementType of
1413     SQLGetSegment,
1414     SQLPutSegment,
1415     SQLStartTransaction:
1416     begin
1417     FreeHandle;
1418     IBError(ibxeNotPermitted, [nil]);
1419     end;
1420     SQLCommit,
1421     SQLRollback,
1422     SQLDDL, SQLSetGenerator,
1423     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1424     SQLExecProcedure:
1425     begin
1426     {set up input sqlda}
1427 tony 371 metadata := FStatementIntf.getInputMetadata(StatusIntf);
1428 tony 45 Check4DataBaseError;
1429 tony 371 try
1430     FSQLParams.Bind(metadata);
1431     finally
1432     metadata.release;
1433     end;
1434 tony 45
1435     {setup output sqlda}
1436     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1437     SQLExecProcedure] then
1438 tony 371 begin
1439     metadata := FStatementIntf.getOutputMetadata(StatusIntf);
1440     Check4DataBaseError;
1441     try
1442     FSQLRecord.Bind(metadata);
1443     finally
1444     metadata.release;
1445     end;
1446     end;
1447 tony 45 end;
1448     end;
1449     end;
1450     except
1451     on E: Exception do begin
1452     if (FStatementIntf <> nil) then
1453     FreeHandle;
1454     if E is EIBInterBaseError then
1455 tony 315 E.Message := E.Message + sSQLErrorSeparator + FSQL;
1456     raise;
1457 tony 45 end;
1458     end;
1459     FPrepared := true;
1460 tony 350
1461 tony 45 FSingleResults := false;
1462     if RetainInterfaces then
1463     begin
1464     SetRetainInterfaces(false);
1465     SetRetainInterfaces(true);
1466     end;
1467     Inc(FPrepareSeqNo);
1468     with GetTransaction as TFB30Transaction do
1469     begin
1470     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1471     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1472     end;
1473     SignalActivity;
1474     Inc(FChangeSeqNo);
1475     end;
1476    
1477     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1478 tony 345
1479     procedure ExecuteQuery(outMetaData: Firebird.IMessageMetaData=nil; outBuffer: pointer=nil);
1480 tony 371 var inMetadata: Firebird.IMessageMetaData;
1481 tony 345 begin
1482     with FFirebird30ClientAPI do
1483     begin
1484     SavePerfStats(FBeforeStats);
1485 tony 371 inMetadata := FSQLParams.GetMetaData;
1486     try
1487     FStatementIntf.execute(StatusIntf,
1488     (aTransaction as TFB30Transaction).TransactionIntf,
1489     inMetaData,
1490     FSQLParams.MessageBuffer,
1491     outMetaData,
1492     outBuffer);
1493     Check4DataBaseError;
1494     finally
1495     if inMetadata <> nil then
1496     inMetadata.release;
1497     end;
1498 tony 345 FStatisticsAvailable := SavePerfStats(FAfterStats);
1499     end;
1500     end;
1501    
1502 tony 359 var Cursor: IResultSet;
1503 tony 371 outMetadata: Firebird.IMessageMetaData;
1504 tony 345
1505 tony 45 begin
1506     Result := nil;
1507 tony 345 FBatchCompletion := nil;
1508 tony 45 FBOF := false;
1509     FEOF := false;
1510     FSingleResults := false;
1511 tony 345 FStatisticsAvailable := false;
1512     if IsInBatchMode then
1513     IBerror(ibxeInBatchMode,[]);
1514 tony 45 CheckTransaction(aTransaction);
1515     if not FPrepared then
1516     InternalPrepare;
1517     CheckHandle;
1518     if aTransaction <> FTransactionIntf then
1519     AddMonitor(aTransaction as TFB30Transaction);
1520 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1521 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1522    
1523 tony 345
1524 tony 45 try
1525 tony 263 with FFirebird30ClientAPI do
1526 tony 45 begin
1527 tony 47 case FSQLStatementType of
1528     SQLSelect:
1529 tony 359 {e.g. Update...returning with a single row in Firebird 5 and later}
1530     begin
1531     Cursor := InternalOpenCursor(aTransaction,false);
1532     if not Cursor.IsEof then
1533     Cursor.FetchNext;
1534     Result := Cursor; {note only first row}
1535     FSingleResults := true;
1536     end;
1537 tony 47
1538     SQLExecProcedure:
1539     begin
1540 tony 371 outMetadata := FSQLRecord.GetMetaData;
1541     try
1542     ExecuteQuery(outMetadata,FSQLRecord.MessageBuffer);
1543     Result := TResults.Create(FSQLRecord);
1544     FSingleResults := true;
1545     finally
1546     if outMetadata <> nil then
1547     outMetadata.release;
1548     end;
1549 tony 345 end;
1550    
1551 tony 47 else
1552 tony 345 ExecuteQuery;
1553 tony 47 end;
1554 tony 45 end;
1555     finally
1556     if aTransaction <> FTransactionIntf then
1557     RemoveMonitor(aTransaction as TFB30Transaction);
1558     end;
1559     FExecTransactionIntf := aTransaction;
1560 tony 111 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1561     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1562 tony 45 SignalActivity;
1563     Inc(FChangeSeqNo);
1564     end;
1565    
1566 tony 350 function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction;
1567     Scrollable: boolean): IResultSet;
1568     var flags: cardinal;
1569 tony 371 inMetadata,
1570     outMetadata: Firebird.IMessageMetadata;
1571 tony 45 begin
1572 tony 350 flags := 0;
1573 tony 359 if (FSQLStatementType <> SQLSelect) and not (stHasCursor in getFlags) then
1574 tony 45 IBError(ibxeIsASelectStatement,[]);
1575    
1576 tony 345 FBatchCompletion := nil;
1577     CheckTransaction(aTransaction);
1578 tony 45 if not FPrepared then
1579     InternalPrepare;
1580     CheckHandle;
1581     if aTransaction <> FTransactionIntf then
1582     AddMonitor(aTransaction as TFB30Transaction);
1583 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1584 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1585    
1586 tony 350 if Scrollable then
1587     flags := Firebird.IStatement.CURSOR_TYPE_SCROLLABLE;
1588    
1589 tony 263 with FFirebird30ClientAPI do
1590 tony 45 begin
1591 tony 47 if FCollectStatistics then
1592     begin
1593     UtilIntf.getPerfCounters(StatusIntf,
1594     (GetAttachment as TFB30Attachment).AttachmentIntf,
1595     ISQL_COUNTERS, @FBeforeStats);
1596     Check4DataBaseError;
1597     end;
1598    
1599 tony 371 inMetadata := FSQLParams.GetMetaData;
1600     outMetadata := FSQLRecord.GetMetaData;
1601     try
1602     FResultSet := FStatementIntf.openCursor(StatusIntf,
1603 tony 45 (aTransaction as TFB30Transaction).TransactionIntf,
1604 tony 371 inMetaData,
1605 tony 45 FSQLParams.MessageBuffer,
1606 tony 371 outMetaData,
1607 tony 350 flags);
1608 tony 371 Check4DataBaseError;
1609     finally
1610     if inMetadata <> nil then
1611     inMetadata.release;
1612     if outMetadata <> nil then
1613     outMetadata.release;
1614     end;
1615 tony 47
1616     if FCollectStatistics then
1617     begin
1618     UtilIntf.getPerfCounters(StatusIntf,
1619     (GetAttachment as TFB30Attachment).AttachmentIntf,
1620     ISQL_COUNTERS,@FAfterStats);
1621     Check4DataBaseError;
1622     FStatisticsAvailable := true;
1623     end;
1624 tony 45 end;
1625     Inc(FCursorSeqNo);
1626     FSingleResults := false;
1627     FOpen := True;
1628     FExecTransactionIntf := aTransaction;
1629     FBOF := true;
1630     FEOF := false;
1631     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1632     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1633     Result := TResultSet.Create(FSQLRecord);
1634     SignalActivity;
1635     Inc(FChangeSeqNo);
1636     end;
1637    
1638 tony 263 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1639     var processedSQL: AnsiString);
1640     begin
1641     FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1642     end;
1643    
1644 tony 45 procedure TFB30Statement.FreeHandle;
1645     begin
1646     Close;
1647     ReleaseInterfaces;
1648 tony 345 if FBatch <> nil then
1649     begin
1650     FBatch.release;
1651     FBatch := nil;
1652     end;
1653 tony 45 if FStatementIntf <> nil then
1654     begin
1655     FStatementIntf.release;
1656     FStatementIntf := nil;
1657     FPrepared := false;
1658     end;
1659 tony 350 FCursor := '';
1660 tony 45 end;
1661    
1662     procedure TFB30Statement.InternalClose(Force: boolean);
1663     begin
1664     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1665     try
1666 tony 263 with FFirebird30ClientAPI do
1667 tony 45 begin
1668     if FResultSet <> nil then
1669     begin
1670     if FSQLRecord.FTransaction.InTransaction and
1671     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1672     FResultSet.close(StatusIntf)
1673     else
1674     FResultSet.release;
1675     end;
1676     FResultSet := nil;
1677     if not Force then Check4DataBaseError;
1678     end;
1679     finally
1680 tony 56 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1681 tony 45 RemoveMonitor(FSQLRecord.FTransaction);
1682     FOpen := False;
1683     FExecTransactionIntf := nil;
1684     FSQLRecord.FTransaction := nil;
1685     end;
1686     SignalActivity;
1687     Inc(FChangeSeqNo);
1688     end;
1689    
1690 tony 345 function TFB30Statement.SavePerfStats(var Stats: TPerfStatistics): boolean;
1691     begin
1692     Result := false;
1693     if FCollectStatistics then
1694     with FFirebird30ClientAPI do
1695     begin
1696     UtilIntf.getPerfCounters(StatusIntf,
1697     (GetAttachment as TFB30Attachment).AttachmentIntf,
1698     ISQL_COUNTERS, @Stats);
1699     Check4DataBaseError;
1700     Result := true;
1701     end;
1702     end;
1703    
1704 tony 45 constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1705 tony 350 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
1706     CursorName: AnsiString);
1707 tony 45 begin
1708     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1709 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1710 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1711     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1712 tony 350 InternalPrepare(CursorName);
1713 tony 45 end;
1714    
1715     constructor TFB30Statement.CreateWithParameterNames(
1716 tony 56 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1717 tony 270 aSQLDialect: integer; GenerateParamNames: boolean;
1718 tony 350 CaseSensitiveParams: boolean; CursorName: AnsiString);
1719 tony 45 begin
1720     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1721 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1722 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1723 tony 270 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1724 tony 45 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1725 tony 350 InternalPrepare(CursorName);
1726 tony 45 end;
1727    
1728     destructor TFB30Statement.Destroy;
1729     begin
1730     inherited Destroy;
1731     if assigned(FSQLParams) then FSQLParams.Free;
1732     if assigned(FSQLRecord) then FSQLRecord.Free;
1733     end;
1734    
1735 tony 350 function TFB30Statement.Fetch(FetchType: TFetchType; PosOrOffset: integer
1736     ): boolean;
1737 tony 45 var fetchResult: integer;
1738     begin
1739 tony 371 result := false;
1740 tony 45 if not FOpen then
1741     IBError(ibxeSQLClosed, [nil]);
1742    
1743 tony 263 with FFirebird30ClientAPI do
1744 tony 45 begin
1745 tony 350 case FetchType of
1746     ftNext:
1747     begin
1748     if FEOF then
1749     IBError(ibxeEOF,[nil]);
1750     { Go to the next record... }
1751     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1752     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1753     begin
1754     FBOF := false;
1755     FEOF := true;
1756     Exit; {End of File}
1757     end
1758 tony 45 end;
1759 tony 350
1760     ftPrior:
1761     begin
1762     if FBOF then
1763     IBError(ibxeBOF,[nil]);
1764     { Go to the next record... }
1765     fetchResult := FResultSet.fetchPrior(StatusIntf,FSQLRecord.MessageBuffer);
1766     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1767     begin
1768     FBOF := true;
1769     FEOF := false;
1770     Exit; {Top of File}
1771     end
1772     end;
1773    
1774     ftFirst:
1775     fetchResult := FResultSet.fetchFirst(StatusIntf,FSQLRecord.MessageBuffer);
1776    
1777     ftLast:
1778     fetchResult := FResultSet.fetchLast(StatusIntf,FSQLRecord.MessageBuffer);
1779    
1780     ftAbsolute:
1781     fetchResult := FResultSet.fetchAbsolute(StatusIntf,PosOrOffset,FSQLRecord.MessageBuffer);
1782    
1783     ftRelative:
1784     fetchResult := FResultSet.fetchRelative(StatusIntf,PosOrOffset,FSQLRecord.MessageBuffer);
1785 tony 45 end;
1786 tony 350
1787     Check4DataBaseError;
1788     if fetchResult <> Firebird.IStatus.RESULT_OK then
1789     exit; {result = false}
1790    
1791     {Result OK}
1792     FBOF := false;
1793     FEOF := false;
1794     result := true;
1795    
1796 tony 209 if FCollectStatistics then
1797     begin
1798     UtilIntf.getPerfCounters(StatusIntf,
1799     (GetAttachment as TFB30Attachment).AttachmentIntf,
1800     ISQL_COUNTERS,@FAfterStats);
1801     Check4DataBaseError;
1802     FStatisticsAvailable := true;
1803     end;
1804 tony 45 end;
1805     FSQLRecord.RowChange;
1806     SignalActivity;
1807     if FEOF then
1808     Inc(FChangeSeqNo);
1809     end;
1810    
1811     function TFB30Statement.GetSQLParams: ISQLParams;
1812     begin
1813     CheckHandle;
1814     if not HasInterface(0) then
1815     AddInterface(0,TSQLParams.Create(FSQLParams));
1816     Result := TSQLParams(GetInterface(0));
1817     end;
1818    
1819     function TFB30Statement.GetMetaData: IMetaData;
1820     begin
1821     CheckHandle;
1822     if not HasInterface(1) then
1823     AddInterface(1, TMetaData.Create(FSQLRecord));
1824     Result := TMetaData(GetInterface(1));
1825     end;
1826    
1827 tony 56 function TFB30Statement.GetPlan: AnsiString;
1828 tony 45 begin
1829     CheckHandle;
1830     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1831     {TODO: SQLExecProcedure, }
1832     SQLUpdate, SQLDelete])) then
1833     result := ''
1834     else
1835 tony 263 with FFirebird30ClientAPI do
1836 tony 45 begin
1837     Result := FStatementIntf.getPlan(StatusIntf,true);
1838     Check4DataBaseError;
1839     end;
1840     end;
1841    
1842     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1843     begin
1844     if assigned(column) and (column.SQLType <> SQL_Blob) then
1845     IBError(ibxeNotABlob,[nil]);
1846     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1847     GetTransaction as TFB30Transaction,
1848     column.GetBlobMetaData,nil);
1849     end;
1850    
1851     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1852     begin
1853     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1854     IBError(ibxeNotAnArray,[nil]);
1855     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1856     GetTransaction as TFB30Transaction,
1857     column.GetArrayMetaData);
1858     end;
1859    
1860     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1861     begin
1862     inherited SetRetainInterfaces(aValue);
1863     if HasInterface(1) then
1864     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1865     if HasInterface(0) then
1866     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1867     end;
1868    
1869 tony 345 function TFB30Statement.IsInBatchMode: boolean;
1870     begin
1871     Result := FBatch <> nil;
1872     end;
1873    
1874     function TFB30Statement.HasBatchMode: boolean;
1875     begin
1876     Result := GetAttachment.HasBatchMode;
1877     end;
1878    
1879     procedure TFB30Statement.AddToBatch;
1880     var BatchPB: TXPBParameterBlock;
1881 tony 371 inMetadata: Firebird.IMessageMetadata;
1882 tony 345
1883     const SixteenMB = 16 * 1024 * 1024;
1884 tony 371 MB256 = 256* 1024 *1024;
1885 tony 345 begin
1886     FBatchCompletion := nil;
1887     if not FPrepared then
1888     InternalPrepare;
1889     CheckHandle;
1890     CheckBatchModeAvailable;
1891 tony 371 inMetadata := FSQLParams.GetMetaData;
1892     try
1893     with FFirebird30ClientAPI do
1894 tony 345 begin
1895 tony 371 if FBatch = nil then
1896     begin
1897     {Start Batch}
1898     BatchPB := TXPBParameterBlock.Create(FFirebird30ClientAPI,Firebird.IXpbBuilder.BATCH);
1899     with FFirebird30ClientAPI do
1900     try
1901     if FBatchRowLimit = maxint then
1902     FBatchBufferSize := MB256
1903     else
1904     begin
1905     FBatchBufferSize := FBatchRowLimit * inMetadata.getAlignedLength(StatusIntf);
1906     Check4DatabaseError;
1907     if FBatchBufferSize < SixteenMB then
1908     FBatchBufferSize := SixteenMB;
1909     if FBatchBufferSize > MB256 {assumed limit} then
1910     IBError(ibxeBatchBufferSizeTooBig,[FBatchBufferSize]);
1911     end;
1912     BatchPB.insertInt(Firebird.IBatch.TAG_RECORD_COUNTS,1);
1913     BatchPB.insertInt(Firebird.IBatch.TAG_BUFFER_BYTES_SIZE,FBatchBufferSize);
1914     FBatch := FStatementIntf.createBatch(StatusIntf,
1915     inMetadata,
1916     BatchPB.getDataLength,
1917     BatchPB.getBuffer);
1918     Check4DataBaseError;
1919 tony 345
1920 tony 371 finally
1921     BatchPB.Free;
1922     end;
1923     FBatchRowCount := 0;
1924     FBatchBufferUsed := 0;
1925 tony 345 end;
1926    
1927 tony 371 Inc(FBatchRowCount);
1928     Inc(FBatchBufferUsed,inMetadata.getAlignedLength(StatusIntf));
1929     Check4DataBaseError;
1930     if FBatchBufferUsed > FBatchBufferSize then
1931     raise EIBBatchBufferOverflow.Create(Ord(ibxeBatchRowBufferOverflow),
1932     Format(GetErrorMessage(ibxeBatchRowBufferOverflow),
1933     [FBatchRowCount,FBatchBufferSize]));
1934 tony 345
1935 tony 371 FBatch.Add(StatusIntf,1,FSQLParams.GetMessageBuffer);
1936     Check4DataBaseError
1937     end;
1938     finally
1939     if inMetadata <> nil then
1940     inMetadata.release;
1941 tony 345 end;
1942     end;
1943    
1944     function TFB30Statement.ExecuteBatch(aTransaction: ITransaction
1945     ): IBatchCompletion;
1946    
1947     procedure Check4BatchCompletionError(bc: IBatchCompletion);
1948     var status: IStatus;
1949     RowNo: integer;
1950     begin
1951     status := nil;
1952     {Raise an exception if there was an error reported in the BatchCompletion}
1953     if (bc <> nil) and bc.getErrorStatus(RowNo,status) then
1954     raise EIBInterbaseError.Create(status);
1955     end;
1956    
1957     var cs: Firebird.IBatchCompletionState;
1958    
1959     begin
1960     Result := nil;
1961     if FBatch = nil then
1962     IBError(ibxeNotInBatchMode,[]);
1963    
1964     with FFirebird30ClientAPI do
1965     begin
1966     SavePerfStats(FBeforeStats);
1967     if aTransaction = nil then
1968     cs := FBatch.execute(StatusIntf,(FTransactionIntf as TFB30Transaction).TransactionIntf)
1969     else
1970     cs := FBatch.execute(StatusIntf,(aTransaction as TFB30Transaction).TransactionIntf);
1971     Check4DataBaseError;
1972     FBatchCompletion := TBatchCompletion.Create(FFirebird30ClientAPI,cs);
1973     FStatisticsAvailable := SavePerfStats(FAfterStats);
1974     FBatch.release;
1975     FBatch := nil;
1976     Check4BatchCompletionError(FBatchCompletion);
1977     Result := FBatchCompletion;
1978     end;
1979     end;
1980    
1981     procedure TFB30Statement.CancelBatch;
1982     begin
1983     if FBatch = nil then
1984     IBError(ibxeNotInBatchMode,[]);
1985     FBatch.release;
1986     FBatch := nil;
1987     end;
1988    
1989     function TFB30Statement.GetBatchCompletion: IBatchCompletion;
1990     begin
1991     Result := FBatchCompletion;
1992     end;
1993    
1994 tony 45 function TFB30Statement.IsPrepared: boolean;
1995     begin
1996     Result := FStatementIntf <> nil;
1997     end;
1998    
1999 tony 359 function TFB30Statement.GetFlags: TStatementFlags;
2000     var flags: cardinal;
2001     begin
2002     CheckHandle;
2003     Result := [];
2004     with FFirebird30ClientAPI do
2005     begin
2006     flags := FStatementIntf.getFlags(StatusIntf);
2007     Check4DataBaseError;
2008     end;
2009     if flags and Firebird.IStatement.FLAG_HAS_CURSOR <> 0 then
2010     Result := Result + [stHasCursor];
2011     if flags and Firebird.IStatement.FLAG_REPEAT_EXECUTE <> 0 then
2012     Result := Result + [stRepeatExecute];
2013     if flags and Firebird.IStatement.CURSOR_TYPE_SCROLLABLE <> 0 then
2014     Result := Result + [stScrollable];
2015     end;
2016    
2017 tony 45 end.
2018    

Properties

Name Value
svn:eol-style native