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: 349
Committed: Mon Oct 18 08:39:40 2021 UTC (3 years, 2 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/fbintf/client/3.0/FB30Statement.pas
File size: 49828 byte(s)
Log Message:
FIxes Merged

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