ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/3.0/FB30Statement.pas
Revision: 263
Committed: Thu Dec 6 15:55:01 2018 UTC (5 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 39096 byte(s)
Log Message:
Release 2.3.2 committed

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    
82     TFB30Statement = class;
83     TIBXSQLDA = class;
84    
85     { TIBXSQLVAR }
86    
87     TIBXSQLVAR = class(TSQLVarData)
88     private
89     FStatement: TFB30Statement;
90 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
91 tony 45 FBlob: IBlob; {Cache references}
92     FArray: IArray;
93     FNullIndicator: short;
94     FOwnsSQLData: boolean;
95     FBlobMetaData: IBlobMetaData;
96     FArrayMetaData: IArrayMetaData;
97    
98     {SQL Var Type Data}
99     FSQLType: cardinal;
100     FSQLSubType: integer;
101 tony 56 FSQLData: PByte; {Address of SQL Data in Message Buffer}
102 tony 45 FSQLNullIndicator: PShort; {Address of null indicator}
103     FDataLength: integer;
104     FNullable: boolean;
105     FScale: integer;
106     FCharSetID: cardinal;
107 tony 56 FRelationName: AnsiString;
108     FFieldName: AnsiString;
109 tony 45
110     protected
111     function GetSQLType: cardinal; override;
112     function GetSubtype: integer; override;
113 tony 56 function GetAliasName: AnsiString; override;
114     function GetFieldName: AnsiString; override;
115     function GetOwnerName: AnsiString; override;
116     function GetRelationName: AnsiString; override;
117 tony 45 function GetScale: integer; override;
118     function GetCharSetID: cardinal; override;
119     function GetCodePage: TSystemCodePage; override;
120     function GetIsNull: Boolean; override;
121     function GetIsNullable: boolean; override;
122 tony 56 function GetSQLData: PByte; override;
123 tony 45 function GetDataLength: cardinal; override;
124     procedure SetIsNull(Value: Boolean); override;
125     procedure SetIsNullable(Value: Boolean); override;
126 tony 56 procedure SetSQLData(AValue: PByte; len: cardinal); override;
127 tony 45 procedure SetScale(aValue: integer); override;
128     procedure SetDataLength(len: cardinal); override;
129     procedure SetSQLType(aValue: cardinal); override;
130     procedure SetCharSetID(aValue: cardinal); override;
131    
132     public
133     constructor Create(aParent: TIBXSQLDA; aIndex: integer);
134     procedure Changed; override;
135     procedure RowChange; override;
136     procedure FreeSQLData;
137     function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
138     function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
139     function GetArrayMetaData: IArrayMetaData; override;
140     function GetBlobMetaData: IBlobMetaData; override;
141     function CreateBlob: IBlob; override;
142     end;
143    
144     { TIBXSQLDA }
145    
146     TIBXSQLDA = class(TSQLDataArea)
147     private
148     FCount: Integer; {Columns in use - may be less than inherited columns}
149     FSize: Integer; {Number of TIBXSQLVARs in column list}
150     FMetaData: Firebird.IMessageMetadata;
151     FTransactionSeqNo: integer;
152 tony 263 protected
153 tony 45 FStatement: TFB30Statement;
154 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
155 tony 45 function GetTransactionSeqNo: integer; override;
156     procedure FreeXSQLDA; virtual;
157     function GetStatement: IStatement; override;
158     function GetPrepareSeqNo: integer; override;
159     procedure SetCount(Value: Integer); override;
160     public
161     constructor Create(aStatement: TFB30Statement);
162     destructor Destroy; override;
163     procedure Changed; virtual;
164     function CheckStatementStatus(Request: TStatementStatus): boolean; override;
165     function ColumnsInUseCount: integer; override;
166     function GetTransaction: TFB30Transaction; virtual;
167     procedure Initialize; override;
168     function StateChanged(var ChangeSeqNo: integer): boolean; override;
169     property MetaData: Firebird.IMessageMetadata read FMetaData;
170     property Count: Integer read FCount write SetCount;
171     property Statement: TFB30Statement read FStatement;
172     end;
173    
174     { TIBXINPUTSQLDA }
175    
176     TIBXINPUTSQLDA = class(TIBXSQLDA)
177     private
178 tony 56 FMessageBuffer: PByte; {Message Buffer}
179 tony 45 FMsgLength: integer; {Message Buffer length}
180     FCurMetaData: Firebird.IMessageMetadata;
181     procedure FreeMessageBuffer;
182 tony 56 function GetMessageBuffer: PByte;
183 tony 45 function GetMetaData: Firebird.IMessageMetadata;
184     function GetModified: Boolean;
185     function GetMsgLength: integer;
186 tony 68 procedure BuildMetadata;
187 tony 45 procedure PackBuffer;
188     protected
189     procedure FreeXSQLDA; override;
190     public
191     constructor Create(aStatement: TFB30Statement);
192     destructor Destroy; override;
193     procedure Bind(aMetaData: Firebird.IMessageMetadata);
194     procedure Changed; override;
195     function IsInputDataArea: boolean; override;
196     property MetaData: Firebird.IMessageMetadata read GetMetaData;
197 tony 56 property MessageBuffer: PByte read GetMessageBuffer;
198 tony 45 property MsgLength: integer read GetMsgLength;
199     end;
200    
201     { TIBXOUTPUTSQLDA }
202    
203     TIBXOUTPUTSQLDA = class(TIBXSQLDA)
204     private
205     FTransaction: TFB30Transaction; {transaction used to execute the statement}
206 tony 56 FMessageBuffer: PByte; {Message Buffer}
207 tony 45 FMsgLength: integer; {Message Buffer length}
208     protected
209     procedure FreeXSQLDA; override;
210     public
211     procedure Bind(aMetaData: Firebird.IMessageMetadata);
212     procedure GetData(index: integer; var aIsNull: boolean; var len: short;
213 tony 56 var data: PByte); override;
214 tony 45 function IsInputDataArea: boolean; override;
215 tony 56 property MessageBuffer: PByte read FMessageBuffer;
216 tony 45 property MsgLength: integer read FMsgLength;
217     end;
218    
219     { TResultSet }
220    
221     TResultSet = class(TResults,IResultSet)
222     private
223     FResults: TIBXOUTPUTSQLDA;
224     FCursorSeqNo: integer;
225     public
226     constructor Create(aResults: TIBXOUTPUTSQLDA);
227     destructor Destroy; override;
228     {IResultSet}
229     function FetchNext: boolean;
230 tony 56 function GetCursorName: AnsiString;
231 tony 45 function GetTransaction: ITransaction; override;
232     function IsEof: boolean;
233     procedure Close;
234     end;
235    
236     { TFB30Statement }
237    
238     TFB30Statement = class(TFBStatement,IStatement)
239     private
240     FStatementIntf: Firebird.IStatement;
241 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
242 tony 45 FSQLParams: TIBXINPUTSQLDA;
243     FSQLRecord: TIBXOUTPUTSQLDA;
244     FResultSet: Firebird.IResultSet;
245     FCursorSeqNo: integer;
246     protected
247     procedure CheckHandle; override;
248     procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
249     procedure InternalPrepare; override;
250     function InternalExecute(aTransaction: ITransaction): IResults; override;
251     function InternalOpenCursor(aTransaction: ITransaction): IResultSet; override;
252 tony 263 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
253 tony 45 procedure FreeHandle; override;
254     procedure InternalClose(Force: boolean); override;
255     public
256     constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
257 tony 56 sql: AnsiString; aSQLDialect: integer);
258 tony 45 constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
259 tony 56 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false);
260 tony 45 destructor Destroy; override;
261     function FetchNext: boolean;
262     property StatementIntf: Firebird.IStatement read FStatementIntf;
263    
264     public
265     {IStatement}
266     function GetSQLParams: ISQLParams; override;
267     function GetMetaData: IMetaData; override;
268 tony 56 function GetPlan: AnsiString;
269 tony 45 function IsPrepared: boolean;
270     function CreateBlob(column: TColumnMetaData): IBlob; override;
271     function CreateArray(column: TColumnMetaData): IArray; override;
272     procedure SetRetainInterfaces(aValue: boolean); override;
273    
274     end;
275    
276     implementation
277    
278 tony 68 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
279 tony 45
280 tony 47 const
281     ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
282    
283 tony 45 { TIBXSQLVAR }
284    
285     procedure TIBXSQLVAR.Changed;
286     begin
287     inherited Changed;
288     TIBXSQLDA(Parent).Changed;
289     end;
290    
291     function TIBXSQLVAR.GetSQLType: cardinal;
292     begin
293     Result := FSQLType;
294     end;
295    
296     function TIBXSQLVAR.GetSubtype: integer;
297     begin
298     Result := FSQLSubType;
299     end;
300    
301 tony 56 function TIBXSQLVAR.GetAliasName: AnsiString;
302 tony 45 begin
303 tony 263 with FFirebird30ClientAPI do
304 tony 45 begin
305     result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
306     Check4DataBaseError;
307     end;
308     end;
309    
310 tony 56 function TIBXSQLVAR.GetFieldName: AnsiString;
311 tony 45 begin
312     Result := FFieldName;
313     end;
314    
315 tony 56 function TIBXSQLVAR.GetOwnerName: AnsiString;
316 tony 45 begin
317 tony 263 with FFirebird30ClientAPI do
318 tony 45 begin
319     result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
320     Check4DataBaseError;
321     end;
322     end;
323    
324 tony 56 function TIBXSQLVAR.GetRelationName: AnsiString;
325 tony 45 begin
326     Result := FRelationName;
327     end;
328    
329     function TIBXSQLVAR.GetScale: integer;
330     begin
331     Result := FScale;
332     end;
333    
334     function TIBXSQLVAR.GetCharSetID: cardinal;
335     begin
336     result := 0;
337     case SQLType of
338     SQL_VARYING, SQL_TEXT:
339     result := FCharSetID;
340    
341     SQL_BLOB:
342     if (SQLSubType = 1) then
343     result := FCharSetID;
344    
345     SQL_ARRAY:
346     if (FRelationName <> '') and (FFieldName <> '') then
347     result := GetArrayMetaData.GetCharSetID
348     else
349     result := FCharSetID;
350     end;
351 tony 60 result := result;
352 tony 45 end;
353    
354     function TIBXSQLVAR.GetCodePage: TSystemCodePage;
355     begin
356     result := CP_NONE;
357 tony 60 with Statement.GetAttachment do
358 tony 45 CharSetID2CodePage(GetCharSetID,result);
359     end;
360    
361     function TIBXSQLVAR.GetIsNull: Boolean;
362     begin
363     Result := IsNullable and (FSQLNullIndicator^ = -1);
364     end;
365    
366     function TIBXSQLVAR.GetIsNullable: boolean;
367     begin
368     Result := FSQLNullIndicator <> nil;
369     end;
370    
371 tony 56 function TIBXSQLVAR.GetSQLData: PByte;
372 tony 45 begin
373     Result := FSQLData;
374     end;
375    
376     function TIBXSQLVAR.GetDataLength: cardinal;
377     begin
378     Result := FDataLength;
379     end;
380    
381     function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
382     begin
383     if GetSQLType <> SQL_ARRAY then
384     IBError(ibxeInvalidDataConversion,[nil]);
385    
386     if FArrayMetaData = nil then
387     FArrayMetaData := TFB30ArrayMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
388     FStatement.GetTransaction as TFB30Transaction,
389     GetRelationName,GetFieldName);
390     Result := FArrayMetaData;
391     end;
392    
393     function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
394     begin
395     if GetSQLType <> SQL_BLOB then
396     IBError(ibxeInvalidDataConversion,[nil]);
397    
398     if FBlobMetaData = nil then
399     FBlobMetaData := TFB30BlobMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
400     FStatement.GetTransaction as TFB30Transaction,
401     GetRelationName,GetFieldName,
402     GetSubType);
403 tony 47 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
404 tony 45 Result := FBlobMetaData;
405     end;
406    
407     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
408     begin
409     if Value then
410     begin
411     IsNullable := true;
412     FNullIndicator := -1;
413     end
414     else
415     if IsNullable then
416     FNullIndicator := 0;
417 tony 47 Changed;
418 tony 45 end;
419    
420     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
421     begin
422     if Value = IsNullable then Exit;
423     if Value then
424     begin
425     FSQLNullIndicator := @FNullIndicator;
426     FNullIndicator := 0;
427     end
428     else
429     FSQLNullIndicator := nil;
430 tony 68 Changed;
431 tony 45 end;
432    
433 tony 56 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
434 tony 45 begin
435     if FOwnsSQLData then
436     FreeMem(FSQLData);
437     FSQLData := AValue;
438     FDataLength := len;
439     FOwnsSQLData := false;
440 tony 68 Changed;
441 tony 45 end;
442    
443     procedure TIBXSQLVAR.SetScale(aValue: integer);
444     begin
445     FScale := aValue;
446 tony 68 Changed;
447 tony 45 end;
448    
449     procedure TIBXSQLVAR.SetDataLength(len: cardinal);
450     begin
451     if not FOwnsSQLData then
452     FSQLData := nil;
453     FDataLength := len;
454 tony 263 with FFirebird30ClientAPI do
455 tony 45 IBAlloc(FSQLData, 0, FDataLength);
456     FOwnsSQLData := true;
457 tony 68 Changed;
458 tony 45 end;
459    
460     procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
461     begin
462     FSQLType := aValue;
463 tony 68 Changed;
464 tony 45 end;
465    
466     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
467     begin
468     FCharSetID := aValue;
469 tony 68 Changed;
470 tony 45 end;
471    
472     constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
473     begin
474     inherited Create(aParent,aIndex);
475     FStatement := aParent.Statement;
476 tony 263 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
477 tony 45 end;
478    
479     procedure TIBXSQLVAR.RowChange;
480     begin
481     inherited;
482     FBlob := nil;
483     FArray := nil;
484     end;
485    
486     procedure TIBXSQLVAR.FreeSQLData;
487     begin
488     if FOwnsSQLData then
489     FreeMem(FSQLData);
490     FSQLData := nil;
491     FOwnsSQLData := true;
492     end;
493    
494     function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
495     begin
496     if SQLType <> SQL_ARRAY then
497     IBError(ibxeInvalidDataConversion,[nil]);
498    
499     if IsNull then
500     Result := nil
501     else
502     begin
503     if FArray = nil then
504     FArray := TFB30Array.Create(FStatement.GetAttachment as TFB30Attachment,
505     TIBXSQLDA(Parent).GetTransaction,
506     GetArrayMetaData,Array_ID);
507     Result := FArray;
508     end;
509     end;
510    
511     function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
512     begin
513     if FBlob <> nil then
514     Result := FBlob
515     else
516     begin
517     if SQLType <> SQL_BLOB then
518     IBError(ibxeInvalidDataConversion, [nil]);
519     if IsNull then
520     Result := nil
521     else
522     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
523     TIBXSQLDA(Parent).GetTransaction,
524     GetBlobMetaData,
525     Blob_ID,BPB);
526     FBlob := Result;
527     end;
528     end;
529    
530     function TIBXSQLVAR.CreateBlob: IBlob;
531     begin
532     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
533     FStatement.GetTransaction as TFB30Transaction,
534     GetSubType,GetCharSetID,nil);
535     end;
536    
537     { TResultSet }
538    
539     constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
540     begin
541     inherited Create(aResults);
542     FResults := aResults;
543     FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
544     end;
545    
546     destructor TResultSet.Destroy;
547     begin
548     Close;
549     inherited Destroy;
550     end;
551    
552     function TResultSet.FetchNext: boolean;
553     var i: integer;
554     begin
555     CheckActive;
556     Result := FResults.FStatement.FetchNext;
557     if Result then
558     for i := 0 to getCount - 1 do
559     FResults.Column[i].RowChange;
560     end;
561    
562 tony 56 function TResultSet.GetCursorName: AnsiString;
563 tony 45 begin
564     IBError(ibxeNotSupported,[nil]);
565     Result := '';
566     end;
567    
568     function TResultSet.GetTransaction: ITransaction;
569     begin
570     Result := FResults.FTransaction;
571     end;
572    
573     function TResultSet.IsEof: boolean;
574     begin
575     Result := FResults.FStatement.FEof;
576     end;
577    
578     procedure TResultSet.Close;
579     begin
580     if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
581     FResults.FStatement.Close;
582     end;
583    
584     { TIBXINPUTSQLDA }
585    
586     function TIBXINPUTSQLDA.GetModified: Boolean;
587     var
588     i: Integer;
589     begin
590     result := False;
591     for i := 0 to FCount - 1 do
592     if Column[i].Modified then
593     begin
594     result := True;
595     exit;
596     end;
597     end;
598    
599     procedure TIBXINPUTSQLDA.FreeMessageBuffer;
600     begin
601     if FCurMetaData <> nil then
602     begin
603     FCurMetaData.release;
604     FCurMetaData := nil;
605     end;
606     if FMessageBuffer <> nil then
607     begin
608     FreeMem(FMessageBuffer);
609     FMessageBuffer := nil;
610     end;
611     FMsgLength := 0;
612     end;
613    
614 tony 56 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
615 tony 45 begin
616     PackBuffer;
617     Result := FMessageBuffer;
618     end;
619    
620     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
621     begin
622 tony 68 BuildMetadata;
623 tony 45 Result := FCurMetaData;
624     end;
625    
626     function TIBXINPUTSQLDA.GetMsgLength: integer;
627     begin
628     PackBuffer;
629     Result := FMsgLength;
630     end;
631    
632 tony 68 procedure TIBXINPUTSQLDA.BuildMetadata;
633 tony 45 var Builder: Firebird.IMetadataBuilder;
634     i: integer;
635     begin
636 tony 68 if FCurMetaData = nil then
637 tony 263 with FFirebird30ClientAPI do
638 tony 45 begin
639     Builder := inherited MetaData.getBuilder(StatusIntf);
640     Check4DataBaseError;
641     try
642     for i := 0 to Count - 1 do
643     with TIBXSQLVar(Column[i]) do
644     begin
645     Builder.setType(StatusIntf,i,FSQLType);
646     Check4DataBaseError;
647     Builder.setSubType(StatusIntf,i,FSQLSubType);
648     Check4DataBaseError;
649     Builder.setLength(StatusIntf,i,FDataLength);
650     Check4DataBaseError;
651     Builder.setCharSet(StatusIntf,i,GetCharSetID);
652     Check4DataBaseError;
653     Builder.setScale(StatusIntf,i,FScale);
654     Check4DataBaseError;
655     end;
656     FCurMetaData := Builder.getMetadata(StatusIntf);
657     Check4DataBaseError;
658     finally
659     Builder.release;
660     end;
661 tony 68 end;
662     end;
663 tony 45
664 tony 68 procedure TIBXINPUTSQLDA.PackBuffer;
665     var i: integer;
666     begin
667     BuildMetadata;
668    
669     if FMsgLength = 0 then
670 tony 263 with FFirebird30ClientAPI do
671 tony 68 begin
672 tony 45 FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
673     Check4DataBaseError;
674    
675     IBAlloc(FMessageBuffer,0,FMsgLength);
676    
677     for i := 0 to Count - 1 do
678     with TIBXSQLVar(Column[i]) do
679     begin
680 tony 68 if not Modified then
681     IBError(ibxeUninitializedInputParameter,[i,Name]);
682    
683 tony 47 if IsNull then
684     FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
685     else
686 tony 68 if FSQLData <> nil then
687 tony 47 Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
688 tony 45 Check4DataBaseError;
689     if IsNullable then
690     begin
691     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
692     Check4DataBaseError;
693     end;
694     end;
695     end;
696     end;
697    
698     procedure TIBXINPUTSQLDA.FreeXSQLDA;
699     begin
700     inherited FreeXSQLDA;
701     FreeMessageBuffer;
702     end;
703    
704     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
705     begin
706     inherited Create(aStatement);
707     FMessageBuffer := nil;
708     end;
709    
710     destructor TIBXINPUTSQLDA.Destroy;
711     begin
712     FreeMessageBuffer;
713     inherited Destroy;
714     end;
715    
716     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
717     var i: integer;
718     begin
719     FMetaData := aMetaData;
720 tony 263 with FFirebird30ClientAPI do
721 tony 45 begin
722     Count := metadata.getCount(StatusIntf);
723     Check4DataBaseError;
724     Initialize;
725    
726     for i := 0 to Count - 1 do
727     with TIBXSQLVar(Column[i]) do
728     begin
729     FSQLType := aMetaData.getType(StatusIntf,i);
730     Check4DataBaseError;
731     if FSQLType = SQL_BLOB then
732     begin
733     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
734     Check4DataBaseError;
735     end
736     else
737     FSQLSubType := 0;
738     FDataLength := aMetaData.getLength(StatusIntf,i);
739     Check4DataBaseError;
740     case SQLType of
741     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
742     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
743     SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
744     begin
745     if (FDataLength = 0) then
746     { Make sure you get a valid pointer anyway
747     select '' from foo }
748     IBAlloc(FSQLData, 0, 1)
749     else
750     IBAlloc(FSQLData, 0, FDataLength)
751     end;
752 tony 47 SQL_VARYING:
753 tony 45 IBAlloc(FSQLData, 0, FDataLength + 2);
754     else
755     IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
756     end;
757     FNullable := aMetaData.isNullable(StatusIntf,i);
758     FOwnsSQLData := true;
759     Check4DataBaseError;
760     FNullIndicator := -1;
761     if FNullable then
762     FSQLNullIndicator := @FNullIndicator
763     else
764     FSQLNullIndicator := nil;
765     FScale := aMetaData.getScale(StatusIntf,i);
766     Check4DataBaseError;
767 tony 60 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
768 tony 45 Check4DataBaseError;
769     end;
770     end;
771     end;
772    
773     procedure TIBXINPUTSQLDA.Changed;
774     begin
775     inherited Changed;
776     FreeMessageBuffer;
777     end;
778    
779     function TIBXINPUTSQLDA.IsInputDataArea: boolean;
780     begin
781     Result := true;
782     end;
783    
784     { TIBXOUTPUTSQLDA }
785    
786     procedure TIBXOUTPUTSQLDA.FreeXSQLDA;
787     begin
788     inherited FreeXSQLDA;
789     FreeMem(FMessageBuffer);
790     FMessageBuffer := nil;
791     FMsgLength := 0;
792     end;
793    
794     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
795     var i: integer;
796     begin
797     FMetaData := aMetaData;
798 tony 263 with FFirebird30ClientAPI do
799 tony 45 begin
800     Count := metadata.getCount(StatusIntf);
801     Check4DataBaseError;
802     Initialize;
803    
804     FMsgLength := metaData.getMessageLength(StatusIntf);
805     Check4DataBaseError;
806     IBAlloc(FMessageBuffer,0,FMsgLength);
807    
808     for i := 0 to Count - 1 do
809     with TIBXSQLVar(Column[i]) do
810     begin
811     FSQLType := aMetaData.getType(StatusIntf,i);
812     Check4DataBaseError;
813     if FSQLType = SQL_BLOB then
814     begin
815     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
816     Check4DataBaseError;
817     end
818     else
819     FSQLSubType := 0;
820     FBlob := nil;
821     FArray := nil;
822     FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
823     Check4DataBaseError;
824     FDataLength := aMetaData.getLength(StatusIntf,i);
825     Check4DataBaseError;
826     FRelationName := strpas(aMetaData.getRelation(StatusIntf,i));
827     Check4DataBaseError;
828     FFieldName := strpas(aMetaData.getField(StatusIntf,i));
829     Check4DataBaseError;
830     FNullable := aMetaData.isNullable(StatusIntf,i);
831     Check4DataBaseError;
832     if FNullable then
833     begin
834     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
835     Check4DataBaseError;
836     end
837     else
838     FSQLNullIndicator := nil;
839     FScale := aMetaData.getScale(StatusIntf,i);
840     Check4DataBaseError;
841 tony 60 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
842 tony 45 Check4DataBaseError;
843     end;
844     end;
845     SetUniqueRelationName;
846     end;
847    
848     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
849 tony 56 var len: short; var data: PByte);
850 tony 45 begin
851     with TIBXSQLVAR(Column[index]) do
852     begin
853     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
854     data := FSQLData;
855     len := FDataLength;
856     if not IsNull and (FSQLType = SQL_VARYING) then
857     begin
858 tony 263 with FFirebird30ClientAPI do
859 tony 45 len := DecodeInteger(data,2);
860     Inc(Data,2);
861     end;
862     end;
863     end;
864    
865     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
866     begin
867     Result := false;
868     end;
869    
870     { TIBXSQLDA }
871     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
872     begin
873     inherited Create;
874     FStatement := aStatement;
875 tony 263 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
876 tony 45 FSize := 0;
877     // writeln('Creating ',ClassName);
878     end;
879    
880     destructor TIBXSQLDA.Destroy;
881     begin
882     FreeXSQLDA;
883     // writeln('Destroying ',ClassName);
884     inherited Destroy;
885     end;
886    
887     procedure TIBXSQLDA.Changed;
888     begin
889    
890     end;
891    
892     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
893     begin
894     Result := false;
895     case Request of
896     ssPrepared:
897     Result := FStatement.IsPrepared;
898    
899     ssExecuteResults:
900     Result :=FStatement.FSingleResults;
901    
902     ssCursorOpen:
903     Result := FStatement.FOpen;
904    
905     ssBOF:
906     Result := FStatement.FBOF;
907    
908     ssEOF:
909     Result := FStatement.FEOF;
910     end;
911     end;
912    
913     function TIBXSQLDA.ColumnsInUseCount: integer;
914     begin
915     Result := FCount;
916     end;
917    
918     function TIBXSQLDA.GetTransaction: TFB30Transaction;
919     begin
920     Result := FStatement.GetTransaction as TFB30Transaction;
921     end;
922    
923     procedure TIBXSQLDA.Initialize;
924     begin
925     if FMetaData <> nil then
926     inherited Initialize;
927     end;
928    
929     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
930     begin
931     Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
932     if Result then
933     ChangeSeqNo := FStatement.ChangeSeqNo;
934     end;
935    
936     procedure TIBXSQLDA.SetCount(Value: Integer);
937     var
938     i: Integer;
939     begin
940     FCount := Value;
941     if FCount = 0 then
942     FUniqueRelationName := ''
943     else
944     begin
945     SetLength(FColumnList, FCount);
946     for i := FSize to FCount - 1 do
947     FColumnList[i] := TIBXSQLVAR.Create(self,i);
948     FSize := FCount;
949     end;
950     end;
951    
952     function TIBXSQLDA.GetTransactionSeqNo: integer;
953     begin
954     Result := FTransactionSeqNo;
955     end;
956    
957     procedure TIBXSQLDA.FreeXSQLDA;
958     var i: integer;
959     begin
960     if FMetaData <> nil then
961     FMetaData.release;
962     FMetaData := nil;
963     for i := 0 to Count - 1 do
964     TIBXSQLVAR(Column[i]).FreeSQLData;
965     for i := 0 to FSize - 1 do
966     TIBXSQLVAR(Column[i]).Free;
967     SetLength(FColumnList,0);
968     FSize := 0;
969     end;
970    
971     function TIBXSQLDA.GetStatement: IStatement;
972     begin
973     Result := FStatement;
974     end;
975    
976     function TIBXSQLDA.GetPrepareSeqNo: integer;
977     begin
978     Result := FStatement.FPrepareSeqNo;
979     end;
980    
981     { TFB30Statement }
982    
983     procedure TFB30Statement.CheckHandle;
984     begin
985     if FStatementIntf = nil then
986     IBError(ibxeInvalidStatementHandle,[nil]);
987     end;
988    
989     procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
990     );
991     begin
992 tony 263 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
993 tony 45 begin
994     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
995     GetBufSize, BytePtr(Buffer));
996     Check4DataBaseError;
997     end;
998     end;
999    
1000     procedure TFB30Statement.InternalPrepare;
1001     begin
1002     if FPrepared then
1003     Exit;
1004     if (FSQL = '') then
1005     IBError(ibxeEmptyQuery, [nil]);
1006     try
1007     CheckTransaction(FTransactionIntf);
1008 tony 263 with FFirebird30ClientAPI do
1009 tony 45 begin
1010     if FHasParamNames then
1011     begin
1012     if FProcessedSQL = '' then
1013 tony 263 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1014 tony 45 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1015     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1016     Length(FProcessedSQL),
1017 tony 56 PAnsiChar(FProcessedSQL),
1018 tony 45 FSQLDialect,
1019     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1020     end
1021     else
1022     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1023     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1024     Length(FSQL),
1025 tony 56 PAnsiChar(FSQL),
1026 tony 45 FSQLDialect,
1027     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1028     Check4DataBaseError;
1029     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1030     Check4DataBaseError;
1031    
1032     { Done getting the type }
1033     case FSQLStatementType of
1034     SQLGetSegment,
1035     SQLPutSegment,
1036     SQLStartTransaction:
1037     begin
1038     FreeHandle;
1039     IBError(ibxeNotPermitted, [nil]);
1040     end;
1041     SQLCommit,
1042     SQLRollback,
1043     SQLDDL, SQLSetGenerator,
1044     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1045     SQLExecProcedure:
1046     begin
1047     {set up input sqlda}
1048     FSQLParams.Bind(FStatementIntf.getInputMetadata(StatusIntf));
1049     Check4DataBaseError;
1050    
1051     {setup output sqlda}
1052     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1053     SQLExecProcedure] then
1054     FSQLRecord.Bind(FStatementIntf.getOutputMetadata(StatusIntf));
1055     Check4DataBaseError;
1056     end;
1057     end;
1058     end;
1059     except
1060     on E: Exception do begin
1061     if (FStatementIntf <> nil) then
1062     FreeHandle;
1063     if E is EIBInterBaseError then
1064     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
1065     EIBInterBaseError(E).IBErrorCode,
1066     EIBInterBaseError(E).Message +
1067     sSQLErrorSeparator + FSQL)
1068     else
1069     raise;
1070     end;
1071     end;
1072     FPrepared := true;
1073     FSingleResults := false;
1074     if RetainInterfaces then
1075     begin
1076     SetRetainInterfaces(false);
1077     SetRetainInterfaces(true);
1078     end;
1079     Inc(FPrepareSeqNo);
1080     with GetTransaction as TFB30Transaction do
1081     begin
1082     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1083     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1084     end;
1085     SignalActivity;
1086     Inc(FChangeSeqNo);
1087     end;
1088    
1089     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1090     begin
1091     Result := nil;
1092     FBOF := false;
1093     FEOF := false;
1094     FSingleResults := false;
1095     CheckTransaction(aTransaction);
1096     if not FPrepared then
1097     InternalPrepare;
1098     CheckHandle;
1099     if aTransaction <> FTransactionIntf then
1100     AddMonitor(aTransaction as TFB30Transaction);
1101     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1102     IBError(ibxeInterfaceOutofDate,[nil]);
1103    
1104     try
1105 tony 263 with FFirebird30ClientAPI do
1106 tony 45 begin
1107 tony 47 if FCollectStatistics then
1108     begin
1109     UtilIntf.getPerfCounters(StatusIntf,
1110     (GetAttachment as TFB30Attachment).AttachmentIntf,
1111     ISQL_COUNTERS,@FBeforeStats);
1112     Check4DataBaseError;
1113     end;
1114 tony 45
1115 tony 47 case FSQLStatementType of
1116     SQLSelect:
1117     IBError(ibxeIsAExecuteProcedure,[]);
1118    
1119     SQLExecProcedure:
1120     begin
1121     FStatementIntf.execute(StatusIntf,
1122     (aTransaction as TFB30Transaction).TransactionIntf,
1123     FSQLParams.MetaData,
1124     FSQLParams.MessageBuffer,
1125     FSQLRecord.MetaData,
1126     FSQLRecord.MessageBuffer);
1127     Check4DataBaseError;
1128    
1129     Result := TResults.Create(FSQLRecord);
1130     FSingleResults := true;
1131     end
1132     else
1133     FStatementIntf.execute(StatusIntf,
1134     (aTransaction as TFB30Transaction).TransactionIntf,
1135     FSQLParams.MetaData,
1136     FSQLParams.MessageBuffer,
1137     nil,
1138     nil);
1139     Check4DataBaseError;
1140     end;
1141     if FCollectStatistics then
1142     begin
1143     UtilIntf.getPerfCounters(StatusIntf,
1144     (GetAttachment as TFB30Attachment).AttachmentIntf,
1145     ISQL_COUNTERS, @FAfterStats);
1146     Check4DataBaseError;
1147     FStatisticsAvailable := true;
1148     end;
1149 tony 45 end;
1150     finally
1151     if aTransaction <> FTransactionIntf then
1152     RemoveMonitor(aTransaction as TFB30Transaction);
1153     end;
1154     FExecTransactionIntf := aTransaction;
1155 tony 111 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1156     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1157 tony 45 SignalActivity;
1158     Inc(FChangeSeqNo);
1159     end;
1160    
1161     function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction
1162     ): IResultSet;
1163     begin
1164     if FSQLStatementType <> SQLSelect then
1165     IBError(ibxeIsASelectStatement,[]);
1166    
1167     CheckTransaction(aTransaction);
1168     if not FPrepared then
1169     InternalPrepare;
1170     CheckHandle;
1171     if aTransaction <> FTransactionIntf then
1172     AddMonitor(aTransaction as TFB30Transaction);
1173     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1174     IBError(ibxeInterfaceOutofDate,[nil]);
1175    
1176 tony 263 with FFirebird30ClientAPI do
1177 tony 45 begin
1178 tony 47 if FCollectStatistics then
1179     begin
1180     UtilIntf.getPerfCounters(StatusIntf,
1181     (GetAttachment as TFB30Attachment).AttachmentIntf,
1182     ISQL_COUNTERS, @FBeforeStats);
1183     Check4DataBaseError;
1184     end;
1185    
1186 tony 45 FResultSet := FStatementIntf.openCursor(StatusIntf,
1187     (aTransaction as TFB30Transaction).TransactionIntf,
1188     FSQLParams.MetaData,
1189     FSQLParams.MessageBuffer,
1190     FSQLRecord.MetaData,
1191     0);
1192     Check4DataBaseError;
1193 tony 47
1194     if FCollectStatistics then
1195     begin
1196     UtilIntf.getPerfCounters(StatusIntf,
1197     (GetAttachment as TFB30Attachment).AttachmentIntf,
1198     ISQL_COUNTERS,@FAfterStats);
1199     Check4DataBaseError;
1200     FStatisticsAvailable := true;
1201     end;
1202 tony 45 end;
1203     Inc(FCursorSeqNo);
1204     FSingleResults := false;
1205     FOpen := True;
1206     FExecTransactionIntf := aTransaction;
1207     FBOF := true;
1208     FEOF := false;
1209     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1210     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1211     Result := TResultSet.Create(FSQLRecord);
1212     SignalActivity;
1213     Inc(FChangeSeqNo);
1214     end;
1215    
1216 tony 263 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1217     var processedSQL: AnsiString);
1218     begin
1219     FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1220     end;
1221    
1222 tony 45 procedure TFB30Statement.FreeHandle;
1223     begin
1224     Close;
1225     ReleaseInterfaces;
1226     if FStatementIntf <> nil then
1227     begin
1228     FStatementIntf.release;
1229     FStatementIntf := nil;
1230     FPrepared := false;
1231     end;
1232     end;
1233    
1234     procedure TFB30Statement.InternalClose(Force: boolean);
1235     begin
1236     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1237     try
1238 tony 263 with FFirebird30ClientAPI do
1239 tony 45 begin
1240     if FResultSet <> nil then
1241     begin
1242     if FSQLRecord.FTransaction.InTransaction and
1243     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1244     FResultSet.close(StatusIntf)
1245     else
1246     FResultSet.release;
1247     end;
1248     FResultSet := nil;
1249     if not Force then Check4DataBaseError;
1250     end;
1251     finally
1252 tony 56 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1253 tony 45 RemoveMonitor(FSQLRecord.FTransaction);
1254     FOpen := False;
1255     FExecTransactionIntf := nil;
1256     FSQLRecord.FTransaction := nil;
1257     end;
1258     SignalActivity;
1259     Inc(FChangeSeqNo);
1260     end;
1261    
1262     constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1263 tony 56 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1264 tony 45 begin
1265     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1266 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1267 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1268     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1269     InternalPrepare;
1270     end;
1271    
1272     constructor TFB30Statement.CreateWithParameterNames(
1273 tony 56 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1274 tony 45 aSQLDialect: integer; GenerateParamNames: boolean);
1275     begin
1276     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1277 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1278 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1279     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1280     InternalPrepare;
1281     end;
1282    
1283     destructor TFB30Statement.Destroy;
1284     begin
1285     inherited Destroy;
1286     if assigned(FSQLParams) then FSQLParams.Free;
1287     if assigned(FSQLRecord) then FSQLRecord.Free;
1288     end;
1289    
1290     function TFB30Statement.FetchNext: boolean;
1291     var fetchResult: integer;
1292     begin
1293     result := false;
1294     if not FOpen then
1295     IBError(ibxeSQLClosed, [nil]);
1296     if FEOF then
1297     IBError(ibxeEOF,[nil]);
1298    
1299 tony 263 with FFirebird30ClientAPI do
1300 tony 45 begin
1301     { Go to the next record... }
1302     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1303     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1304     begin
1305     FBOF := false;
1306     FEOF := true;
1307     Exit; {End of File}
1308     end
1309     else
1310     if fetchResult <> Firebird.IStatus.RESULT_OK then
1311     begin
1312     try
1313     IBDataBaseError;
1314     except
1315     Close;
1316     raise;
1317     end;
1318     end
1319     else
1320     begin
1321     FBOF := false;
1322     result := true;
1323     end;
1324 tony 209 if FCollectStatistics then
1325     begin
1326     UtilIntf.getPerfCounters(StatusIntf,
1327     (GetAttachment as TFB30Attachment).AttachmentIntf,
1328     ISQL_COUNTERS,@FAfterStats);
1329     Check4DataBaseError;
1330     FStatisticsAvailable := true;
1331     end;
1332 tony 45 end;
1333     FSQLRecord.RowChange;
1334     SignalActivity;
1335     if FEOF then
1336     Inc(FChangeSeqNo);
1337     end;
1338    
1339     function TFB30Statement.GetSQLParams: ISQLParams;
1340     begin
1341     CheckHandle;
1342     if not HasInterface(0) then
1343     AddInterface(0,TSQLParams.Create(FSQLParams));
1344     Result := TSQLParams(GetInterface(0));
1345     end;
1346    
1347     function TFB30Statement.GetMetaData: IMetaData;
1348     begin
1349     CheckHandle;
1350     if not HasInterface(1) then
1351     AddInterface(1, TMetaData.Create(FSQLRecord));
1352     Result := TMetaData(GetInterface(1));
1353     end;
1354    
1355 tony 56 function TFB30Statement.GetPlan: AnsiString;
1356 tony 45 begin
1357     CheckHandle;
1358     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1359     {TODO: SQLExecProcedure, }
1360     SQLUpdate, SQLDelete])) then
1361     result := ''
1362     else
1363 tony 263 with FFirebird30ClientAPI do
1364 tony 45 begin
1365     Result := FStatementIntf.getPlan(StatusIntf,true);
1366     Check4DataBaseError;
1367     end;
1368     end;
1369    
1370     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1371     begin
1372     if assigned(column) and (column.SQLType <> SQL_Blob) then
1373     IBError(ibxeNotABlob,[nil]);
1374     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1375     GetTransaction as TFB30Transaction,
1376     column.GetBlobMetaData,nil);
1377     end;
1378    
1379     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1380     begin
1381     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1382     IBError(ibxeNotAnArray,[nil]);
1383     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1384     GetTransaction as TFB30Transaction,
1385     column.GetArrayMetaData);
1386     end;
1387    
1388     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1389     begin
1390     inherited SetRetainInterfaces(aValue);
1391     if HasInterface(1) then
1392     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1393     if HasInterface(0) then
1394     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1395     end;
1396    
1397     function TFB30Statement.IsPrepared: boolean;
1398     begin
1399     Result := FStatementIntf <> nil;
1400     end;
1401    
1402     end.
1403