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: 270
Committed: Fri Jan 18 11:10:37 2019 UTC (5 years, 11 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/fbintf/client/3.0/FB30Statement.pas
File size: 39227 byte(s)
Log Message:
Fixes merged

File Contents

# User Rev Content
1 tony 45 (*
2     * Firebird Interface (fbintf). The fbintf components provide a set of
3     * Pascal language bindings for the Firebird API.
4     *
5     * The contents of this file are subject to the Initial Developer's
6     * Public License Version 1.0 (the "License"); you may not use this
7     * file except in compliance with the License. You may obtain a copy
8     * of the License here:
9     *
10     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
11     *
12     * Software distributed under the License is distributed on an "AS
13     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14     * implied. See the License for the specific language governing rights
15     * and limitations under the License.
16     *
17     * The Initial Developer of the Original Code is Tony Whyman.
18     *
19     * The Original Code is (C) 2016 Tony Whyman, MWA Software
20     * (http://www.mwasoftware.co.uk).
21     *
22     * All Rights Reserved.
23     *
24     * Contributor(s): ______________________________________.
25     *
26     *)
27     unit FB30Statement;
28 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$codepage UTF8}
35     {$interfaces COM}
36     {$ENDIF}
37    
38     {This unit is hacked from IBSQL and contains the code for managing an XSQLDA and
39     SQLVars, along with statement preparation, execution and cursor management.
40     Most of the SQLVar code has been moved to unit FBSQLData. Client access is
41     provided through interface rather than direct access to the XSQLDA and XSQLVar
42     objects.}
43    
44     {
45     Note on reference counted interfaces.
46     ------------------------------------
47    
48     TFB30Statement manages both an input and an output SQLDA through the TIBXINPUTSQLDA
49     and TIBXOUTPUTSQLDA objects. As pure objects, these are explicitly destroyed
50     when the statement is destroyed.
51    
52     However, IResultSet is an interface and is returned when a cursor is opened and
53     has a reference for the TIBXOUTPUTSQLDA. The user may discard their reference
54     to the IStatement while still using the IResultSet. This would be a problem if t
55     he underlying TFB30Statement object and its TIBXOUTPUTSQLDA is destroyed while
56     still leaving the TIBXResultSet object in place. Calls to (e.g.) FetchNext would fail.
57    
58     To avoid this problem, TResultsSet objects have a reference to the IStatement
59     interface of the TFB30Statement object. Thus, as long as these "copies" exist,
60     the owning statement is not destroyed even if the user discards their reference
61     to the statement. Note: the TFB30Statement does not have a reference to the TIBXResultSet
62     interface. This way circular references are avoided.
63    
64     To avoid and IResultSet interface being kept to long and no longer synchronised
65     with the query, each statement includes a prepare sequence number, incremented
66     each time the query is prepared. When the IResultSet interface is created, it
67     noted the current prepare sequence number. Whe an IResult interface is accessed
68     it checks this number against the statement's current prepare sequence number.
69     If not the same, an error is raised.
70    
71     A similar strategy is used for the IMetaData, IResults and ISQLParams interfaces.
72     }
73    
74     interface
75    
76     uses
77     Classes, SysUtils, Firebird, IB, FBStatement, FB30ClientAPI, FB30Transaction,
78     FB30Attachment,IBExternals, FBSQLData, FBOutputBlock, FBActivityMonitor;
79    
80     type
81    
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 270 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false;
260     CaseSensitiveParams: boolean=false);
261 tony 45 destructor Destroy; override;
262     function FetchNext: boolean;
263     property StatementIntf: Firebird.IStatement read FStatementIntf;
264    
265     public
266     {IStatement}
267     function GetSQLParams: ISQLParams; override;
268     function GetMetaData: IMetaData; override;
269 tony 56 function GetPlan: AnsiString;
270 tony 45 function IsPrepared: boolean;
271     function CreateBlob(column: TColumnMetaData): IBlob; override;
272     function CreateArray(column: TColumnMetaData): IArray; override;
273     procedure SetRetainInterfaces(aValue: boolean); override;
274    
275     end;
276    
277     implementation
278    
279 tony 68 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
280 tony 45
281 tony 47 const
282     ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
283    
284 tony 45 { TIBXSQLVAR }
285    
286     procedure TIBXSQLVAR.Changed;
287     begin
288     inherited Changed;
289     TIBXSQLDA(Parent).Changed;
290     end;
291    
292     function TIBXSQLVAR.GetSQLType: cardinal;
293     begin
294     Result := FSQLType;
295     end;
296    
297     function TIBXSQLVAR.GetSubtype: integer;
298     begin
299     Result := FSQLSubType;
300     end;
301    
302 tony 56 function TIBXSQLVAR.GetAliasName: AnsiString;
303 tony 45 begin
304 tony 263 with FFirebird30ClientAPI do
305 tony 45 begin
306     result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
307     Check4DataBaseError;
308     end;
309     end;
310    
311 tony 56 function TIBXSQLVAR.GetFieldName: AnsiString;
312 tony 45 begin
313     Result := FFieldName;
314     end;
315    
316 tony 56 function TIBXSQLVAR.GetOwnerName: AnsiString;
317 tony 45 begin
318 tony 263 with FFirebird30ClientAPI do
319 tony 45 begin
320     result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
321     Check4DataBaseError;
322     end;
323     end;
324    
325 tony 56 function TIBXSQLVAR.GetRelationName: AnsiString;
326 tony 45 begin
327     Result := FRelationName;
328     end;
329    
330     function TIBXSQLVAR.GetScale: integer;
331     begin
332     Result := FScale;
333     end;
334    
335     function TIBXSQLVAR.GetCharSetID: cardinal;
336     begin
337     result := 0;
338     case SQLType of
339     SQL_VARYING, SQL_TEXT:
340     result := FCharSetID;
341    
342     SQL_BLOB:
343     if (SQLSubType = 1) then
344     result := FCharSetID;
345    
346     SQL_ARRAY:
347     if (FRelationName <> '') and (FFieldName <> '') then
348     result := GetArrayMetaData.GetCharSetID
349     else
350     result := FCharSetID;
351     end;
352 tony 60 result := result;
353 tony 45 end;
354    
355     function TIBXSQLVAR.GetCodePage: TSystemCodePage;
356     begin
357     result := CP_NONE;
358 tony 60 with Statement.GetAttachment do
359 tony 45 CharSetID2CodePage(GetCharSetID,result);
360     end;
361    
362     function TIBXSQLVAR.GetIsNull: Boolean;
363     begin
364     Result := IsNullable and (FSQLNullIndicator^ = -1);
365     end;
366    
367     function TIBXSQLVAR.GetIsNullable: boolean;
368     begin
369     Result := FSQLNullIndicator <> nil;
370     end;
371    
372 tony 56 function TIBXSQLVAR.GetSQLData: PByte;
373 tony 45 begin
374     Result := FSQLData;
375     end;
376    
377     function TIBXSQLVAR.GetDataLength: cardinal;
378     begin
379     Result := FDataLength;
380     end;
381    
382     function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
383     begin
384     if GetSQLType <> SQL_ARRAY then
385     IBError(ibxeInvalidDataConversion,[nil]);
386    
387     if FArrayMetaData = nil then
388     FArrayMetaData := TFB30ArrayMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
389     FStatement.GetTransaction as TFB30Transaction,
390     GetRelationName,GetFieldName);
391     Result := FArrayMetaData;
392     end;
393    
394     function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
395     begin
396     if GetSQLType <> SQL_BLOB then
397     IBError(ibxeInvalidDataConversion,[nil]);
398    
399     if FBlobMetaData = nil then
400     FBlobMetaData := TFB30BlobMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
401     FStatement.GetTransaction as TFB30Transaction,
402     GetRelationName,GetFieldName,
403     GetSubType);
404 tony 47 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
405 tony 45 Result := FBlobMetaData;
406     end;
407    
408     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
409     begin
410     if Value then
411     begin
412     IsNullable := true;
413     FNullIndicator := -1;
414     end
415     else
416     if IsNullable then
417     FNullIndicator := 0;
418 tony 47 Changed;
419 tony 45 end;
420    
421     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
422     begin
423     if Value = IsNullable then Exit;
424     if Value then
425     begin
426     FSQLNullIndicator := @FNullIndicator;
427     FNullIndicator := 0;
428     end
429     else
430     FSQLNullIndicator := nil;
431 tony 68 Changed;
432 tony 45 end;
433    
434 tony 56 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
435 tony 45 begin
436     if FOwnsSQLData then
437     FreeMem(FSQLData);
438     FSQLData := AValue;
439     FDataLength := len;
440     FOwnsSQLData := false;
441 tony 68 Changed;
442 tony 45 end;
443    
444     procedure TIBXSQLVAR.SetScale(aValue: integer);
445     begin
446     FScale := aValue;
447 tony 68 Changed;
448 tony 45 end;
449    
450     procedure TIBXSQLVAR.SetDataLength(len: cardinal);
451     begin
452     if not FOwnsSQLData then
453     FSQLData := nil;
454     FDataLength := len;
455 tony 263 with FFirebird30ClientAPI do
456 tony 45 IBAlloc(FSQLData, 0, FDataLength);
457     FOwnsSQLData := true;
458 tony 68 Changed;
459 tony 45 end;
460    
461     procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
462     begin
463     FSQLType := aValue;
464 tony 68 Changed;
465 tony 45 end;
466    
467     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
468     begin
469     FCharSetID := aValue;
470 tony 68 Changed;
471 tony 45 end;
472    
473     constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
474     begin
475     inherited Create(aParent,aIndex);
476     FStatement := aParent.Statement;
477 tony 263 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
478 tony 45 end;
479    
480     procedure TIBXSQLVAR.RowChange;
481     begin
482     inherited;
483     FBlob := nil;
484     FArray := nil;
485     end;
486    
487     procedure TIBXSQLVAR.FreeSQLData;
488     begin
489     if FOwnsSQLData then
490     FreeMem(FSQLData);
491     FSQLData := nil;
492     FOwnsSQLData := true;
493     end;
494    
495     function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
496     begin
497     if SQLType <> SQL_ARRAY then
498     IBError(ibxeInvalidDataConversion,[nil]);
499    
500     if IsNull then
501     Result := nil
502     else
503     begin
504     if FArray = nil then
505     FArray := TFB30Array.Create(FStatement.GetAttachment as TFB30Attachment,
506     TIBXSQLDA(Parent).GetTransaction,
507     GetArrayMetaData,Array_ID);
508     Result := FArray;
509     end;
510     end;
511    
512     function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
513     begin
514     if FBlob <> nil then
515     Result := FBlob
516     else
517     begin
518     if SQLType <> SQL_BLOB then
519     IBError(ibxeInvalidDataConversion, [nil]);
520     if IsNull then
521     Result := nil
522     else
523     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
524     TIBXSQLDA(Parent).GetTransaction,
525     GetBlobMetaData,
526     Blob_ID,BPB);
527     FBlob := Result;
528     end;
529     end;
530    
531     function TIBXSQLVAR.CreateBlob: IBlob;
532     begin
533     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
534     FStatement.GetTransaction as TFB30Transaction,
535     GetSubType,GetCharSetID,nil);
536     end;
537    
538     { TResultSet }
539    
540     constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
541     begin
542     inherited Create(aResults);
543     FResults := aResults;
544     FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
545     end;
546    
547     destructor TResultSet.Destroy;
548     begin
549     Close;
550     inherited Destroy;
551     end;
552    
553     function TResultSet.FetchNext: boolean;
554     var i: integer;
555     begin
556     CheckActive;
557     Result := FResults.FStatement.FetchNext;
558     if Result then
559     for i := 0 to getCount - 1 do
560     FResults.Column[i].RowChange;
561     end;
562    
563 tony 56 function TResultSet.GetCursorName: AnsiString;
564 tony 45 begin
565     IBError(ibxeNotSupported,[nil]);
566     Result := '';
567     end;
568    
569     function TResultSet.GetTransaction: ITransaction;
570     begin
571     Result := FResults.FTransaction;
572     end;
573    
574     function TResultSet.IsEof: boolean;
575     begin
576     Result := FResults.FStatement.FEof;
577     end;
578    
579     procedure TResultSet.Close;
580     begin
581     if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
582     FResults.FStatement.Close;
583     end;
584    
585     { TIBXINPUTSQLDA }
586    
587     function TIBXINPUTSQLDA.GetModified: Boolean;
588     var
589     i: Integer;
590     begin
591     result := False;
592     for i := 0 to FCount - 1 do
593     if Column[i].Modified then
594     begin
595     result := True;
596     exit;
597     end;
598     end;
599    
600     procedure TIBXINPUTSQLDA.FreeMessageBuffer;
601     begin
602     if FCurMetaData <> nil then
603     begin
604     FCurMetaData.release;
605     FCurMetaData := nil;
606     end;
607     if FMessageBuffer <> nil then
608     begin
609     FreeMem(FMessageBuffer);
610     FMessageBuffer := nil;
611     end;
612     FMsgLength := 0;
613     end;
614    
615 tony 56 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
616 tony 45 begin
617     PackBuffer;
618     Result := FMessageBuffer;
619     end;
620    
621     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
622     begin
623 tony 68 BuildMetadata;
624 tony 45 Result := FCurMetaData;
625     end;
626    
627     function TIBXINPUTSQLDA.GetMsgLength: integer;
628     begin
629     PackBuffer;
630     Result := FMsgLength;
631     end;
632    
633 tony 68 procedure TIBXINPUTSQLDA.BuildMetadata;
634 tony 45 var Builder: Firebird.IMetadataBuilder;
635     i: integer;
636     begin
637 tony 68 if FCurMetaData = nil then
638 tony 263 with FFirebird30ClientAPI do
639 tony 45 begin
640     Builder := inherited MetaData.getBuilder(StatusIntf);
641     Check4DataBaseError;
642     try
643     for i := 0 to Count - 1 do
644     with TIBXSQLVar(Column[i]) do
645     begin
646     Builder.setType(StatusIntf,i,FSQLType);
647     Check4DataBaseError;
648     Builder.setSubType(StatusIntf,i,FSQLSubType);
649     Check4DataBaseError;
650     Builder.setLength(StatusIntf,i,FDataLength);
651     Check4DataBaseError;
652     Builder.setCharSet(StatusIntf,i,GetCharSetID);
653     Check4DataBaseError;
654     Builder.setScale(StatusIntf,i,FScale);
655     Check4DataBaseError;
656     end;
657     FCurMetaData := Builder.getMetadata(StatusIntf);
658     Check4DataBaseError;
659     finally
660     Builder.release;
661     end;
662 tony 68 end;
663     end;
664 tony 45
665 tony 68 procedure TIBXINPUTSQLDA.PackBuffer;
666     var i: integer;
667     begin
668     BuildMetadata;
669    
670     if FMsgLength = 0 then
671 tony 263 with FFirebird30ClientAPI do
672 tony 68 begin
673 tony 45 FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
674     Check4DataBaseError;
675    
676     IBAlloc(FMessageBuffer,0,FMsgLength);
677    
678     for i := 0 to Count - 1 do
679     with TIBXSQLVar(Column[i]) do
680     begin
681 tony 68 if not Modified then
682     IBError(ibxeUninitializedInputParameter,[i,Name]);
683    
684 tony 47 if IsNull then
685     FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
686     else
687 tony 68 if FSQLData <> nil then
688 tony 47 Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
689 tony 45 Check4DataBaseError;
690     if IsNullable then
691     begin
692     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
693     Check4DataBaseError;
694     end;
695     end;
696     end;
697     end;
698    
699     procedure TIBXINPUTSQLDA.FreeXSQLDA;
700     begin
701     inherited FreeXSQLDA;
702     FreeMessageBuffer;
703     end;
704    
705     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
706     begin
707     inherited Create(aStatement);
708     FMessageBuffer := nil;
709     end;
710    
711     destructor TIBXINPUTSQLDA.Destroy;
712     begin
713     FreeMessageBuffer;
714     inherited Destroy;
715     end;
716    
717     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
718     var i: integer;
719     begin
720     FMetaData := aMetaData;
721 tony 263 with FFirebird30ClientAPI do
722 tony 45 begin
723     Count := metadata.getCount(StatusIntf);
724     Check4DataBaseError;
725     Initialize;
726    
727     for i := 0 to Count - 1 do
728     with TIBXSQLVar(Column[i]) do
729     begin
730     FSQLType := aMetaData.getType(StatusIntf,i);
731     Check4DataBaseError;
732     if FSQLType = SQL_BLOB then
733     begin
734     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
735     Check4DataBaseError;
736     end
737     else
738     FSQLSubType := 0;
739     FDataLength := aMetaData.getLength(StatusIntf,i);
740     Check4DataBaseError;
741     case SQLType of
742     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
743     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
744     SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
745     begin
746     if (FDataLength = 0) then
747     { Make sure you get a valid pointer anyway
748     select '' from foo }
749     IBAlloc(FSQLData, 0, 1)
750     else
751     IBAlloc(FSQLData, 0, FDataLength)
752     end;
753 tony 47 SQL_VARYING:
754 tony 45 IBAlloc(FSQLData, 0, FDataLength + 2);
755     else
756     IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
757     end;
758     FNullable := aMetaData.isNullable(StatusIntf,i);
759     FOwnsSQLData := true;
760     Check4DataBaseError;
761     FNullIndicator := -1;
762     if FNullable then
763     FSQLNullIndicator := @FNullIndicator
764     else
765     FSQLNullIndicator := nil;
766     FScale := aMetaData.getScale(StatusIntf,i);
767     Check4DataBaseError;
768 tony 60 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
769 tony 45 Check4DataBaseError;
770     end;
771     end;
772     end;
773    
774     procedure TIBXINPUTSQLDA.Changed;
775     begin
776     inherited Changed;
777     FreeMessageBuffer;
778     end;
779    
780     function TIBXINPUTSQLDA.IsInputDataArea: boolean;
781     begin
782     Result := true;
783     end;
784    
785     { TIBXOUTPUTSQLDA }
786    
787     procedure TIBXOUTPUTSQLDA.FreeXSQLDA;
788     begin
789     inherited FreeXSQLDA;
790     FreeMem(FMessageBuffer);
791     FMessageBuffer := nil;
792     FMsgLength := 0;
793     end;
794    
795     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
796     var i: integer;
797     begin
798     FMetaData := aMetaData;
799 tony 263 with FFirebird30ClientAPI do
800 tony 45 begin
801     Count := metadata.getCount(StatusIntf);
802     Check4DataBaseError;
803     Initialize;
804    
805     FMsgLength := metaData.getMessageLength(StatusIntf);
806     Check4DataBaseError;
807     IBAlloc(FMessageBuffer,0,FMsgLength);
808    
809     for i := 0 to Count - 1 do
810     with TIBXSQLVar(Column[i]) do
811     begin
812     FSQLType := aMetaData.getType(StatusIntf,i);
813     Check4DataBaseError;
814     if FSQLType = SQL_BLOB then
815     begin
816     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
817     Check4DataBaseError;
818     end
819     else
820     FSQLSubType := 0;
821     FBlob := nil;
822     FArray := nil;
823     FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
824     Check4DataBaseError;
825     FDataLength := aMetaData.getLength(StatusIntf,i);
826     Check4DataBaseError;
827     FRelationName := strpas(aMetaData.getRelation(StatusIntf,i));
828     Check4DataBaseError;
829     FFieldName := strpas(aMetaData.getField(StatusIntf,i));
830     Check4DataBaseError;
831     FNullable := aMetaData.isNullable(StatusIntf,i);
832     Check4DataBaseError;
833     if FNullable then
834     begin
835     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
836     Check4DataBaseError;
837     end
838     else
839     FSQLNullIndicator := nil;
840     FScale := aMetaData.getScale(StatusIntf,i);
841     Check4DataBaseError;
842 tony 60 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
843 tony 45 Check4DataBaseError;
844     end;
845     end;
846     SetUniqueRelationName;
847     end;
848    
849     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
850 tony 56 var len: short; var data: PByte);
851 tony 45 begin
852     with TIBXSQLVAR(Column[index]) do
853     begin
854     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
855     data := FSQLData;
856     len := FDataLength;
857     if not IsNull and (FSQLType = SQL_VARYING) then
858     begin
859 tony 263 with FFirebird30ClientAPI do
860 tony 45 len := DecodeInteger(data,2);
861     Inc(Data,2);
862     end;
863     end;
864     end;
865    
866     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
867     begin
868     Result := false;
869     end;
870    
871     { TIBXSQLDA }
872     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
873     begin
874     inherited Create;
875     FStatement := aStatement;
876 tony 263 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
877 tony 45 FSize := 0;
878     // writeln('Creating ',ClassName);
879     end;
880    
881     destructor TIBXSQLDA.Destroy;
882     begin
883     FreeXSQLDA;
884     // writeln('Destroying ',ClassName);
885     inherited Destroy;
886     end;
887    
888     procedure TIBXSQLDA.Changed;
889     begin
890    
891     end;
892    
893     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
894     begin
895     Result := false;
896     case Request of
897     ssPrepared:
898     Result := FStatement.IsPrepared;
899    
900     ssExecuteResults:
901     Result :=FStatement.FSingleResults;
902    
903     ssCursorOpen:
904     Result := FStatement.FOpen;
905    
906     ssBOF:
907     Result := FStatement.FBOF;
908    
909     ssEOF:
910     Result := FStatement.FEOF;
911     end;
912     end;
913    
914     function TIBXSQLDA.ColumnsInUseCount: integer;
915     begin
916     Result := FCount;
917     end;
918    
919     function TIBXSQLDA.GetTransaction: TFB30Transaction;
920     begin
921     Result := FStatement.GetTransaction as TFB30Transaction;
922     end;
923    
924     procedure TIBXSQLDA.Initialize;
925     begin
926     if FMetaData <> nil then
927     inherited Initialize;
928     end;
929    
930     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
931     begin
932     Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
933     if Result then
934     ChangeSeqNo := FStatement.ChangeSeqNo;
935     end;
936    
937     procedure TIBXSQLDA.SetCount(Value: Integer);
938     var
939     i: Integer;
940     begin
941     FCount := Value;
942     if FCount = 0 then
943     FUniqueRelationName := ''
944     else
945     begin
946     SetLength(FColumnList, FCount);
947     for i := FSize to FCount - 1 do
948     FColumnList[i] := TIBXSQLVAR.Create(self,i);
949     FSize := FCount;
950     end;
951     end;
952    
953     function TIBXSQLDA.GetTransactionSeqNo: integer;
954     begin
955     Result := FTransactionSeqNo;
956     end;
957    
958     procedure TIBXSQLDA.FreeXSQLDA;
959     var i: integer;
960     begin
961     if FMetaData <> nil then
962     FMetaData.release;
963     FMetaData := nil;
964     for i := 0 to Count - 1 do
965     TIBXSQLVAR(Column[i]).FreeSQLData;
966     for i := 0 to FSize - 1 do
967     TIBXSQLVAR(Column[i]).Free;
968     SetLength(FColumnList,0);
969     FSize := 0;
970     end;
971    
972     function TIBXSQLDA.GetStatement: IStatement;
973     begin
974     Result := FStatement;
975     end;
976    
977     function TIBXSQLDA.GetPrepareSeqNo: integer;
978     begin
979     Result := FStatement.FPrepareSeqNo;
980     end;
981    
982     { TFB30Statement }
983    
984     procedure TFB30Statement.CheckHandle;
985     begin
986     if FStatementIntf = nil then
987     IBError(ibxeInvalidStatementHandle,[nil]);
988     end;
989    
990     procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
991     );
992     begin
993 tony 263 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
994 tony 45 begin
995     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
996     GetBufSize, BytePtr(Buffer));
997     Check4DataBaseError;
998     end;
999     end;
1000    
1001     procedure TFB30Statement.InternalPrepare;
1002     begin
1003     if FPrepared then
1004     Exit;
1005     if (FSQL = '') then
1006     IBError(ibxeEmptyQuery, [nil]);
1007     try
1008     CheckTransaction(FTransactionIntf);
1009 tony 263 with FFirebird30ClientAPI do
1010 tony 45 begin
1011     if FHasParamNames then
1012     begin
1013     if FProcessedSQL = '' then
1014 tony 263 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1015 tony 45 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1016     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1017     Length(FProcessedSQL),
1018 tony 56 PAnsiChar(FProcessedSQL),
1019 tony 45 FSQLDialect,
1020     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1021     end
1022     else
1023     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1024     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1025     Length(FSQL),
1026 tony 56 PAnsiChar(FSQL),
1027 tony 45 FSQLDialect,
1028     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1029     Check4DataBaseError;
1030     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1031     Check4DataBaseError;
1032    
1033     { Done getting the type }
1034     case FSQLStatementType of
1035     SQLGetSegment,
1036     SQLPutSegment,
1037     SQLStartTransaction:
1038     begin
1039     FreeHandle;
1040     IBError(ibxeNotPermitted, [nil]);
1041     end;
1042     SQLCommit,
1043     SQLRollback,
1044     SQLDDL, SQLSetGenerator,
1045     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1046     SQLExecProcedure:
1047     begin
1048     {set up input sqlda}
1049     FSQLParams.Bind(FStatementIntf.getInputMetadata(StatusIntf));
1050     Check4DataBaseError;
1051    
1052     {setup output sqlda}
1053     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1054     SQLExecProcedure] then
1055     FSQLRecord.Bind(FStatementIntf.getOutputMetadata(StatusIntf));
1056     Check4DataBaseError;
1057     end;
1058     end;
1059     end;
1060     except
1061     on E: Exception do begin
1062     if (FStatementIntf <> nil) then
1063     FreeHandle;
1064     if E is EIBInterBaseError then
1065     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
1066     EIBInterBaseError(E).IBErrorCode,
1067     EIBInterBaseError(E).Message +
1068     sSQLErrorSeparator + FSQL)
1069     else
1070     raise;
1071     end;
1072     end;
1073     FPrepared := true;
1074     FSingleResults := false;
1075     if RetainInterfaces then
1076     begin
1077     SetRetainInterfaces(false);
1078     SetRetainInterfaces(true);
1079     end;
1080     Inc(FPrepareSeqNo);
1081     with GetTransaction as TFB30Transaction do
1082     begin
1083     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1084     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1085     end;
1086     SignalActivity;
1087     Inc(FChangeSeqNo);
1088     end;
1089    
1090     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1091     begin
1092     Result := nil;
1093     FBOF := false;
1094     FEOF := false;
1095     FSingleResults := false;
1096     CheckTransaction(aTransaction);
1097     if not FPrepared then
1098     InternalPrepare;
1099     CheckHandle;
1100     if aTransaction <> FTransactionIntf then
1101     AddMonitor(aTransaction as TFB30Transaction);
1102     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1103     IBError(ibxeInterfaceOutofDate,[nil]);
1104    
1105     try
1106 tony 263 with FFirebird30ClientAPI do
1107 tony 45 begin
1108 tony 47 if FCollectStatistics then
1109     begin
1110     UtilIntf.getPerfCounters(StatusIntf,
1111     (GetAttachment as TFB30Attachment).AttachmentIntf,
1112     ISQL_COUNTERS,@FBeforeStats);
1113     Check4DataBaseError;
1114     end;
1115 tony 45
1116 tony 47 case FSQLStatementType of
1117     SQLSelect:
1118     IBError(ibxeIsAExecuteProcedure,[]);
1119    
1120     SQLExecProcedure:
1121     begin
1122     FStatementIntf.execute(StatusIntf,
1123     (aTransaction as TFB30Transaction).TransactionIntf,
1124     FSQLParams.MetaData,
1125     FSQLParams.MessageBuffer,
1126     FSQLRecord.MetaData,
1127     FSQLRecord.MessageBuffer);
1128     Check4DataBaseError;
1129    
1130     Result := TResults.Create(FSQLRecord);
1131     FSingleResults := true;
1132     end
1133     else
1134     FStatementIntf.execute(StatusIntf,
1135     (aTransaction as TFB30Transaction).TransactionIntf,
1136     FSQLParams.MetaData,
1137     FSQLParams.MessageBuffer,
1138     nil,
1139     nil);
1140     Check4DataBaseError;
1141     end;
1142     if FCollectStatistics then
1143     begin
1144     UtilIntf.getPerfCounters(StatusIntf,
1145     (GetAttachment as TFB30Attachment).AttachmentIntf,
1146     ISQL_COUNTERS, @FAfterStats);
1147     Check4DataBaseError;
1148     FStatisticsAvailable := true;
1149     end;
1150 tony 45 end;
1151     finally
1152     if aTransaction <> FTransactionIntf then
1153     RemoveMonitor(aTransaction as TFB30Transaction);
1154     end;
1155     FExecTransactionIntf := aTransaction;
1156 tony 111 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1157     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1158 tony 45 SignalActivity;
1159     Inc(FChangeSeqNo);
1160     end;
1161    
1162     function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction
1163     ): IResultSet;
1164     begin
1165     if FSQLStatementType <> SQLSelect then
1166     IBError(ibxeIsASelectStatement,[]);
1167    
1168     CheckTransaction(aTransaction);
1169     if not FPrepared then
1170     InternalPrepare;
1171     CheckHandle;
1172     if aTransaction <> FTransactionIntf then
1173     AddMonitor(aTransaction as TFB30Transaction);
1174     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1175     IBError(ibxeInterfaceOutofDate,[nil]);
1176    
1177 tony 263 with FFirebird30ClientAPI do
1178 tony 45 begin
1179 tony 47 if FCollectStatistics then
1180     begin
1181     UtilIntf.getPerfCounters(StatusIntf,
1182     (GetAttachment as TFB30Attachment).AttachmentIntf,
1183     ISQL_COUNTERS, @FBeforeStats);
1184     Check4DataBaseError;
1185     end;
1186    
1187 tony 45 FResultSet := FStatementIntf.openCursor(StatusIntf,
1188     (aTransaction as TFB30Transaction).TransactionIntf,
1189     FSQLParams.MetaData,
1190     FSQLParams.MessageBuffer,
1191     FSQLRecord.MetaData,
1192     0);
1193     Check4DataBaseError;
1194 tony 47
1195     if FCollectStatistics then
1196     begin
1197     UtilIntf.getPerfCounters(StatusIntf,
1198     (GetAttachment as TFB30Attachment).AttachmentIntf,
1199     ISQL_COUNTERS,@FAfterStats);
1200     Check4DataBaseError;
1201     FStatisticsAvailable := true;
1202     end;
1203 tony 45 end;
1204     Inc(FCursorSeqNo);
1205     FSingleResults := false;
1206     FOpen := True;
1207     FExecTransactionIntf := aTransaction;
1208     FBOF := true;
1209     FEOF := false;
1210     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1211     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1212     Result := TResultSet.Create(FSQLRecord);
1213     SignalActivity;
1214     Inc(FChangeSeqNo);
1215     end;
1216    
1217 tony 263 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1218     var processedSQL: AnsiString);
1219     begin
1220     FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1221     end;
1222    
1223 tony 45 procedure TFB30Statement.FreeHandle;
1224     begin
1225     Close;
1226     ReleaseInterfaces;
1227     if FStatementIntf <> nil then
1228     begin
1229     FStatementIntf.release;
1230     FStatementIntf := nil;
1231     FPrepared := false;
1232     end;
1233     end;
1234    
1235     procedure TFB30Statement.InternalClose(Force: boolean);
1236     begin
1237     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1238     try
1239 tony 263 with FFirebird30ClientAPI do
1240 tony 45 begin
1241     if FResultSet <> nil then
1242     begin
1243     if FSQLRecord.FTransaction.InTransaction and
1244     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1245     FResultSet.close(StatusIntf)
1246     else
1247     FResultSet.release;
1248     end;
1249     FResultSet := nil;
1250     if not Force then Check4DataBaseError;
1251     end;
1252     finally
1253 tony 56 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1254 tony 45 RemoveMonitor(FSQLRecord.FTransaction);
1255     FOpen := False;
1256     FExecTransactionIntf := nil;
1257     FSQLRecord.FTransaction := nil;
1258     end;
1259     SignalActivity;
1260     Inc(FChangeSeqNo);
1261     end;
1262    
1263     constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1264 tony 56 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1265 tony 45 begin
1266     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1267 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1268 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1269     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1270     InternalPrepare;
1271     end;
1272    
1273     constructor TFB30Statement.CreateWithParameterNames(
1274 tony 56 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1275 tony 270 aSQLDialect: integer; GenerateParamNames: boolean;
1276     CaseSensitiveParams: boolean);
1277 tony 45 begin
1278     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1279 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1280 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1281 tony 270 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1282 tony 45 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1283     InternalPrepare;
1284     end;
1285    
1286     destructor TFB30Statement.Destroy;
1287     begin
1288     inherited Destroy;
1289     if assigned(FSQLParams) then FSQLParams.Free;
1290     if assigned(FSQLRecord) then FSQLRecord.Free;
1291     end;
1292    
1293     function TFB30Statement.FetchNext: boolean;
1294     var fetchResult: integer;
1295     begin
1296     result := false;
1297     if not FOpen then
1298     IBError(ibxeSQLClosed, [nil]);
1299     if FEOF then
1300     IBError(ibxeEOF,[nil]);
1301    
1302 tony 263 with FFirebird30ClientAPI do
1303 tony 45 begin
1304     { Go to the next record... }
1305     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1306     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1307     begin
1308     FBOF := false;
1309     FEOF := true;
1310     Exit; {End of File}
1311     end
1312     else
1313     if fetchResult <> Firebird.IStatus.RESULT_OK then
1314     begin
1315     try
1316     IBDataBaseError;
1317     except
1318     Close;
1319     raise;
1320     end;
1321     end
1322     else
1323     begin
1324     FBOF := false;
1325     result := true;
1326     end;
1327 tony 209 if FCollectStatistics then
1328     begin
1329     UtilIntf.getPerfCounters(StatusIntf,
1330     (GetAttachment as TFB30Attachment).AttachmentIntf,
1331     ISQL_COUNTERS,@FAfterStats);
1332     Check4DataBaseError;
1333     FStatisticsAvailable := true;
1334     end;
1335 tony 45 end;
1336     FSQLRecord.RowChange;
1337     SignalActivity;
1338     if FEOF then
1339     Inc(FChangeSeqNo);
1340     end;
1341    
1342     function TFB30Statement.GetSQLParams: ISQLParams;
1343     begin
1344     CheckHandle;
1345     if not HasInterface(0) then
1346     AddInterface(0,TSQLParams.Create(FSQLParams));
1347     Result := TSQLParams(GetInterface(0));
1348     end;
1349    
1350     function TFB30Statement.GetMetaData: IMetaData;
1351     begin
1352     CheckHandle;
1353     if not HasInterface(1) then
1354     AddInterface(1, TMetaData.Create(FSQLRecord));
1355     Result := TMetaData(GetInterface(1));
1356     end;
1357    
1358 tony 56 function TFB30Statement.GetPlan: AnsiString;
1359 tony 45 begin
1360     CheckHandle;
1361     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1362     {TODO: SQLExecProcedure, }
1363     SQLUpdate, SQLDelete])) then
1364     result := ''
1365     else
1366 tony 263 with FFirebird30ClientAPI do
1367 tony 45 begin
1368     Result := FStatementIntf.getPlan(StatusIntf,true);
1369     Check4DataBaseError;
1370     end;
1371     end;
1372    
1373     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1374     begin
1375     if assigned(column) and (column.SQLType <> SQL_Blob) then
1376     IBError(ibxeNotABlob,[nil]);
1377     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1378     GetTransaction as TFB30Transaction,
1379     column.GetBlobMetaData,nil);
1380     end;
1381    
1382     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1383     begin
1384     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1385     IBError(ibxeNotAnArray,[nil]);
1386     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1387     GetTransaction as TFB30Transaction,
1388     column.GetArrayMetaData);
1389     end;
1390    
1391     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1392     begin
1393     inherited SetRetainInterfaces(aValue);
1394     if HasInterface(1) then
1395     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1396     if HasInterface(0) then
1397     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1398     end;
1399    
1400     function TFB30Statement.IsPrepared: boolean;
1401     begin
1402     Result := FStatementIntf <> nil;
1403     end;
1404    
1405     end.
1406