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

# Content
1 (*
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 {$IFDEF MSWINDOWS}
29 {$DEFINE WINDOWS}
30 {$ENDIF}
31
32 {$IFDEF FPC}
33 {$mode delphi}
34 {$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 FFirebird30ClientAPI: TFB30ClientAPI;
91 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 FSQLData: PByte; {Address of SQL Data in Message Buffer}
102 FSQLNullIndicator: PShort; {Address of null indicator}
103 FDataLength: integer;
104 FNullable: boolean;
105 FScale: integer;
106 FCharSetID: cardinal;
107 FRelationName: AnsiString;
108 FFieldName: AnsiString;
109
110 protected
111 function GetSQLType: cardinal; override;
112 function GetSubtype: integer; override;
113 function GetAliasName: AnsiString; override;
114 function GetFieldName: AnsiString; override;
115 function GetOwnerName: AnsiString; override;
116 function GetRelationName: AnsiString; override;
117 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 function GetSQLData: PByte; override;
123 function GetDataLength: cardinal; override;
124 procedure SetIsNull(Value: Boolean); override;
125 procedure SetIsNullable(Value: Boolean); override;
126 procedure SetSQLData(AValue: PByte; len: cardinal); override;
127 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 protected
153 FStatement: TFB30Statement;
154 FFirebird30ClientAPI: TFB30ClientAPI;
155 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 FMessageBuffer: PByte; {Message Buffer}
179 FMsgLength: integer; {Message Buffer length}
180 FCurMetaData: Firebird.IMessageMetadata;
181 procedure FreeMessageBuffer;
182 function GetMessageBuffer: PByte;
183 function GetMetaData: Firebird.IMessageMetadata;
184 function GetModified: Boolean;
185 function GetMsgLength: integer;
186 procedure BuildMetadata;
187 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 property MessageBuffer: PByte read GetMessageBuffer;
198 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 FMessageBuffer: PByte; {Message Buffer}
207 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 var data: PByte); override;
214 function IsInputDataArea: boolean; override;
215 property MessageBuffer: PByte read FMessageBuffer;
216 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 function GetCursorName: AnsiString;
231 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 FFirebird30ClientAPI: TFB30ClientAPI;
242 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 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
253 procedure FreeHandle; override;
254 procedure InternalClose(Force: boolean); override;
255 public
256 constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
257 sql: AnsiString; aSQLDialect: integer);
258 constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
259 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false);
260 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 function GetPlan: AnsiString;
269 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 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
279
280 const
281 ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
282
283 { 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 function TIBXSQLVAR.GetAliasName: AnsiString;
302 begin
303 with FFirebird30ClientAPI do
304 begin
305 result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
306 Check4DataBaseError;
307 end;
308 end;
309
310 function TIBXSQLVAR.GetFieldName: AnsiString;
311 begin
312 Result := FFieldName;
313 end;
314
315 function TIBXSQLVAR.GetOwnerName: AnsiString;
316 begin
317 with FFirebird30ClientAPI do
318 begin
319 result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
320 Check4DataBaseError;
321 end;
322 end;
323
324 function TIBXSQLVAR.GetRelationName: AnsiString;
325 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 result := result;
352 end;
353
354 function TIBXSQLVAR.GetCodePage: TSystemCodePage;
355 begin
356 result := CP_NONE;
357 with Statement.GetAttachment do
358 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 function TIBXSQLVAR.GetSQLData: PByte;
372 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 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
404 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 Changed;
418 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 Changed;
431 end;
432
433 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
434 begin
435 if FOwnsSQLData then
436 FreeMem(FSQLData);
437 FSQLData := AValue;
438 FDataLength := len;
439 FOwnsSQLData := false;
440 Changed;
441 end;
442
443 procedure TIBXSQLVAR.SetScale(aValue: integer);
444 begin
445 FScale := aValue;
446 Changed;
447 end;
448
449 procedure TIBXSQLVAR.SetDataLength(len: cardinal);
450 begin
451 if not FOwnsSQLData then
452 FSQLData := nil;
453 FDataLength := len;
454 with FFirebird30ClientAPI do
455 IBAlloc(FSQLData, 0, FDataLength);
456 FOwnsSQLData := true;
457 Changed;
458 end;
459
460 procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
461 begin
462 FSQLType := aValue;
463 Changed;
464 end;
465
466 procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
467 begin
468 FCharSetID := aValue;
469 Changed;
470 end;
471
472 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
473 begin
474 inherited Create(aParent,aIndex);
475 FStatement := aParent.Statement;
476 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
477 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 function TResultSet.GetCursorName: AnsiString;
563 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 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
615 begin
616 PackBuffer;
617 Result := FMessageBuffer;
618 end;
619
620 function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
621 begin
622 BuildMetadata;
623 Result := FCurMetaData;
624 end;
625
626 function TIBXINPUTSQLDA.GetMsgLength: integer;
627 begin
628 PackBuffer;
629 Result := FMsgLength;
630 end;
631
632 procedure TIBXINPUTSQLDA.BuildMetadata;
633 var Builder: Firebird.IMetadataBuilder;
634 i: integer;
635 begin
636 if FCurMetaData = nil then
637 with FFirebird30ClientAPI do
638 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 end;
662 end;
663
664 procedure TIBXINPUTSQLDA.PackBuffer;
665 var i: integer;
666 begin
667 BuildMetadata;
668
669 if FMsgLength = 0 then
670 with FFirebird30ClientAPI do
671 begin
672 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 if not Modified then
681 IBError(ibxeUninitializedInputParameter,[i,Name]);
682
683 if IsNull then
684 FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
685 else
686 if FSQLData <> nil then
687 Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
688 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 with FFirebird30ClientAPI do
721 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 SQL_VARYING:
753 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 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
768 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 with FFirebird30ClientAPI do
799 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 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
842 Check4DataBaseError;
843 end;
844 end;
845 SetUniqueRelationName;
846 end;
847
848 procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
849 var len: short; var data: PByte);
850 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 with FFirebird30ClientAPI do
859 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 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
876 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 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
993 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 with FFirebird30ClientAPI do
1009 begin
1010 if FHasParamNames then
1011 begin
1012 if FProcessedSQL = '' then
1013 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1014 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1015 (FTransactionIntf as TFB30Transaction).TransactionIntf,
1016 Length(FProcessedSQL),
1017 PAnsiChar(FProcessedSQL),
1018 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 PAnsiChar(FSQL),
1026 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 with FFirebird30ClientAPI do
1106 begin
1107 if FCollectStatistics then
1108 begin
1109 UtilIntf.getPerfCounters(StatusIntf,
1110 (GetAttachment as TFB30Attachment).AttachmentIntf,
1111 ISQL_COUNTERS,@FBeforeStats);
1112 Check4DataBaseError;
1113 end;
1114
1115 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 end;
1150 finally
1151 if aTransaction <> FTransactionIntf then
1152 RemoveMonitor(aTransaction as TFB30Transaction);
1153 end;
1154 FExecTransactionIntf := aTransaction;
1155 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1156 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1157 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 with FFirebird30ClientAPI do
1177 begin
1178 if FCollectStatistics then
1179 begin
1180 UtilIntf.getPerfCounters(StatusIntf,
1181 (GetAttachment as TFB30Attachment).AttachmentIntf,
1182 ISQL_COUNTERS, @FBeforeStats);
1183 Check4DataBaseError;
1184 end;
1185
1186 FResultSet := FStatementIntf.openCursor(StatusIntf,
1187 (aTransaction as TFB30Transaction).TransactionIntf,
1188 FSQLParams.MetaData,
1189 FSQLParams.MessageBuffer,
1190 FSQLRecord.MetaData,
1191 0);
1192 Check4DataBaseError;
1193
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 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 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1217 var processedSQL: AnsiString);
1218 begin
1219 FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1220 end;
1221
1222 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 with FFirebird30ClientAPI do
1239 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 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1253 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 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1264 begin
1265 inherited Create(Attachment,Transaction,sql,aSQLDialect);
1266 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1267 FSQLParams := TIBXINPUTSQLDA.Create(self);
1268 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1269 InternalPrepare;
1270 end;
1271
1272 constructor TFB30Statement.CreateWithParameterNames(
1273 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1274 aSQLDialect: integer; GenerateParamNames: boolean);
1275 begin
1276 inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1277 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1278 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 with FFirebird30ClientAPI do
1300 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 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 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 function TFB30Statement.GetPlan: AnsiString;
1356 begin
1357 CheckHandle;
1358 if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1359 {TODO: SQLExecProcedure, }
1360 SQLUpdate, SQLDelete])) then
1361 result := ''
1362 else
1363 with FFirebird30ClientAPI do
1364 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