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: 270
Committed: Fri Jan 18 11:10:37 2019 UTC (5 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 39227 byte(s)
Log Message:
Fixes merged

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 CaseSensitiveParams: boolean=false);
261 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 function GetPlan: AnsiString;
270 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 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
280
281 const
282 ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
283
284 { 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 function TIBXSQLVAR.GetAliasName: AnsiString;
303 begin
304 with FFirebird30ClientAPI do
305 begin
306 result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
307 Check4DataBaseError;
308 end;
309 end;
310
311 function TIBXSQLVAR.GetFieldName: AnsiString;
312 begin
313 Result := FFieldName;
314 end;
315
316 function TIBXSQLVAR.GetOwnerName: AnsiString;
317 begin
318 with FFirebird30ClientAPI do
319 begin
320 result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
321 Check4DataBaseError;
322 end;
323 end;
324
325 function TIBXSQLVAR.GetRelationName: AnsiString;
326 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 result := result;
353 end;
354
355 function TIBXSQLVAR.GetCodePage: TSystemCodePage;
356 begin
357 result := CP_NONE;
358 with Statement.GetAttachment do
359 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 function TIBXSQLVAR.GetSQLData: PByte;
373 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 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
405 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 Changed;
419 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 Changed;
432 end;
433
434 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
435 begin
436 if FOwnsSQLData then
437 FreeMem(FSQLData);
438 FSQLData := AValue;
439 FDataLength := len;
440 FOwnsSQLData := false;
441 Changed;
442 end;
443
444 procedure TIBXSQLVAR.SetScale(aValue: integer);
445 begin
446 FScale := aValue;
447 Changed;
448 end;
449
450 procedure TIBXSQLVAR.SetDataLength(len: cardinal);
451 begin
452 if not FOwnsSQLData then
453 FSQLData := nil;
454 FDataLength := len;
455 with FFirebird30ClientAPI do
456 IBAlloc(FSQLData, 0, FDataLength);
457 FOwnsSQLData := true;
458 Changed;
459 end;
460
461 procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
462 begin
463 FSQLType := aValue;
464 Changed;
465 end;
466
467 procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
468 begin
469 FCharSetID := aValue;
470 Changed;
471 end;
472
473 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
474 begin
475 inherited Create(aParent,aIndex);
476 FStatement := aParent.Statement;
477 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
478 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 function TResultSet.GetCursorName: AnsiString;
564 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 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
616 begin
617 PackBuffer;
618 Result := FMessageBuffer;
619 end;
620
621 function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
622 begin
623 BuildMetadata;
624 Result := FCurMetaData;
625 end;
626
627 function TIBXINPUTSQLDA.GetMsgLength: integer;
628 begin
629 PackBuffer;
630 Result := FMsgLength;
631 end;
632
633 procedure TIBXINPUTSQLDA.BuildMetadata;
634 var Builder: Firebird.IMetadataBuilder;
635 i: integer;
636 begin
637 if FCurMetaData = nil then
638 with FFirebird30ClientAPI do
639 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 end;
663 end;
664
665 procedure TIBXINPUTSQLDA.PackBuffer;
666 var i: integer;
667 begin
668 BuildMetadata;
669
670 if FMsgLength = 0 then
671 with FFirebird30ClientAPI do
672 begin
673 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 if not Modified then
682 IBError(ibxeUninitializedInputParameter,[i,Name]);
683
684 if IsNull then
685 FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
686 else
687 if FSQLData <> nil then
688 Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
689 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 with FFirebird30ClientAPI do
722 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 SQL_VARYING:
754 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 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
769 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 with FFirebird30ClientAPI do
800 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 FCharSetID := aMetaData.getCharSet(StatusIntf,i) and $FF;
843 Check4DataBaseError;
844 end;
845 end;
846 SetUniqueRelationName;
847 end;
848
849 procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
850 var len: short; var data: PByte);
851 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 with FFirebird30ClientAPI do
860 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 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
877 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 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
994 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 with FFirebird30ClientAPI do
1010 begin
1011 if FHasParamNames then
1012 begin
1013 if FProcessedSQL = '' then
1014 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1015 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1016 (FTransactionIntf as TFB30Transaction).TransactionIntf,
1017 Length(FProcessedSQL),
1018 PAnsiChar(FProcessedSQL),
1019 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 PAnsiChar(FSQL),
1027 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 with FFirebird30ClientAPI do
1107 begin
1108 if FCollectStatistics then
1109 begin
1110 UtilIntf.getPerfCounters(StatusIntf,
1111 (GetAttachment as TFB30Attachment).AttachmentIntf,
1112 ISQL_COUNTERS,@FBeforeStats);
1113 Check4DataBaseError;
1114 end;
1115
1116 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 end;
1151 finally
1152 if aTransaction <> FTransactionIntf then
1153 RemoveMonitor(aTransaction as TFB30Transaction);
1154 end;
1155 FExecTransactionIntf := aTransaction;
1156 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1157 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1158 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 with FFirebird30ClientAPI do
1178 begin
1179 if FCollectStatistics then
1180 begin
1181 UtilIntf.getPerfCounters(StatusIntf,
1182 (GetAttachment as TFB30Attachment).AttachmentIntf,
1183 ISQL_COUNTERS, @FBeforeStats);
1184 Check4DataBaseError;
1185 end;
1186
1187 FResultSet := FStatementIntf.openCursor(StatusIntf,
1188 (aTransaction as TFB30Transaction).TransactionIntf,
1189 FSQLParams.MetaData,
1190 FSQLParams.MessageBuffer,
1191 FSQLRecord.MetaData,
1192 0);
1193 Check4DataBaseError;
1194
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 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 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1218 var processedSQL: AnsiString);
1219 begin
1220 FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1221 end;
1222
1223 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 with FFirebird30ClientAPI do
1240 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 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1254 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 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1265 begin
1266 inherited Create(Attachment,Transaction,sql,aSQLDialect);
1267 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1268 FSQLParams := TIBXINPUTSQLDA.Create(self);
1269 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1270 InternalPrepare;
1271 end;
1272
1273 constructor TFB30Statement.CreateWithParameterNames(
1274 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1275 aSQLDialect: integer; GenerateParamNames: boolean;
1276 CaseSensitiveParams: boolean);
1277 begin
1278 inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1279 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1280 FSQLParams := TIBXINPUTSQLDA.Create(self);
1281 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1282 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 with FFirebird30ClientAPI do
1303 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 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 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 function TFB30Statement.GetPlan: AnsiString;
1359 begin
1360 CheckHandle;
1361 if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1362 {TODO: SQLExecProcedure, }
1363 SQLUpdate, SQLDelete])) then
1364 result := ''
1365 else
1366 with FFirebird30ClientAPI do
1367 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