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