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