ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/fbintf/client/2.5/FB25Statement.pas
Revision: 363
Committed: Tue Dec 7 13:30:05 2021 UTC (2 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 40441 byte(s)
Log Message:
add fbintf

File Contents

# Content
1 (*
2 * Firebird Interface (fbintf). The fbintf components provide a set of
3 * Pascal language bindings for the Firebird API. Although predominantly
4 * a new development they include source code taken from IBX and may be
5 * considered a derived product. This software thus also includes the copyright
6 * notice and license conditions from IBX.
7 *
8 * Except for those parts dervied from IBX, contents of this file are subject
9 * to the Initial Developer's Public License Version 1.0 (the "License"); you
10 * may not use this file except in compliance with the License. You may obtain a
11 * copy of the License here:
12 *
13 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
14 *
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing rights
18 * and limitations under the License.
19 *
20 * The Initial Developer of the Original Code is Tony Whyman.
21 *
22 * The Original Code is (C) 2016 Tony Whyman, MWA Software
23 * (http://www.mwasoftware.co.uk).
24 *
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 *)
30 {************************************************************************}
31 { }
32 { Borland Delphi Visual Component Library }
33 { InterBase Express core components }
34 { }
35 { Copyright (c) 1998-2000 Inprise Corporation }
36 { }
37 { InterBase Express is based in part on the product }
38 { Free IB Components, written by Gregory H. Deatz for }
39 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
40 { Free IB Components is used under license. }
41 { }
42 { The contents of this file are subject to the InterBase }
43 { Public License Version 1.0 (the "License"); you may not }
44 { use this file except in compliance with the License. You }
45 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
46 { Software distributed under the License is distributed on }
47 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
48 { express or implied. See the License for the specific language }
49 { governing rights and limitations under the License. }
50 { The Original Code was created by InterBase Software Corporation }
51 { and its successors. }
52 { Portions created by Inprise Corporation are Copyright (C) Inprise }
53 { Corporation. All Rights Reserved. }
54 { Contributor(s): Jeff Overcash }
55 { }
56 { IBX For Lazarus (Firebird Express) }
57 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
58 { Portions created by MWA Software are copyright McCallum Whyman }
59 { Associates Ltd 2011 - 2015 }
60 { }
61 {************************************************************************}
62 unit FB25Statement;
63 {$IFDEF MSWINDOWS}
64 {$DEFINE WINDOWS}
65 {$ENDIF}
66
67 {$IFDEF FPC}
68 {$mode delphi}
69 {$codepage UTF8}
70 {$interfaces COM}
71 {$ENDIF}
72
73 {This unit is hacked from IBSQL and contains the code for managing an XSQLDA and
74 SQLVars, along with statement preparation, execution and cursor management.
75 Most of the SQLVar code has been moved to unit FBSQLData. Client access is
76 provided through interface rather than direct access to the XSQLDA and XSQLVar
77 objects.}
78
79 {
80 Note on reference counted interfaces.
81 ------------------------------------
82
83 TFB25Statement manages both an input and an output SQLDA through the TIBXINPUTSQLDA
84 and TIBXOUTPUTSQLDA objects. As pure objects, these are explicitly destroyed
85 when the statement is destroyed.
86
87 However, IResultSet is an interface and is returned when a cursor is opened and
88 has a reference for the TIBXOUTPUTSQLDA. The user may discard their reference
89 to the IStatement while still using the IResultSet. This would be a problem if t
90 he underlying TFB25Statement object and its TIBXOUTPUTSQLDA is destroyed while
91 still leaving the TIBXResultSet object in place. Calls to (e.g.) FetchNext would fail.
92
93 To avoid this problem, TResultsSet objects have a reference to the IStatement
94 interface of the TFB25Statement object. Thus, as long as these "copies" exist,
95 the owning statement is not destroyed even if the user discards their reference
96 to the statement. Note: the TFB25Statement does not have a reference to the TIBXResultSet
97 interface. This way circular references are avoided.
98
99 To avoid an IResultSet interface being kept too long and no longer synchronised
100 with the query, each statement includes a prepare sequence number, incremented
101 each time the query is prepared. When the IResultSet interface is created, it
102 noted the current prepare sequence number. Whe an IResult interface is accessed
103 it checks this number against the statement's current prepare sequence number.
104 If not the same, an error is raised.
105
106 A similar strategy is used for the IMetaData, IResults and ISQLParams interfaces.
107 }
108
109 interface
110
111 uses
112 Classes, SysUtils, IB, FBClientAPI, FB25ClientAPI, FB25Transaction, FB25Attachment,
113 IBHeader, IBExternals, FBSQLData, FBOutputBlock, FBStatement, FBActivityMonitor;
114
115 type
116 TFB25Statement = class;
117 TIBXSQLDA = class;
118
119 { TIBXSQLVAR }
120
121 TIBXSQLVAR = class(TSQLVarData)
122 private
123 FStatement: TFB25Statement;
124 FFirebird25ClientAPI: TFB25ClientAPI;
125 FBlob: IBlob; {Cache references}
126 FNullIndicator: short;
127 FOwnsSQLData: boolean;
128 FBlobMetaData: IBlobMetaData;
129 FArrayMetaData: IArrayMetaData;
130 FMetadataSize: short; {size of field from metadata}
131 FXSQLVAR: PXSQLVAR; { Points to the PXSQLVAR in the owner object }
132 protected
133 function GetSQLType: cardinal; override;
134 function GetSubtype: integer; override;
135 function GetAliasName: AnsiString; override;
136 function GetFieldName: AnsiString; override;
137 function GetOwnerName: AnsiString; override;
138 function GetRelationName: AnsiString; override;
139 function GetScale: integer; override;
140 function GetCharSetID: cardinal; override;
141 function GetCodePage: TSystemCodePage; override;
142 function GetCharSetWidth: integer; override;
143 function GetIsNull: Boolean; override;
144 function GetIsNullable: boolean; override;
145 function GetSQLData: PByte; override;
146 function GetDataLength: cardinal; override;
147 function GetSize: cardinal; override;
148 function GetAttachment: IAttachment; override;
149 function GetDefaultTextSQLType: cardinal; override;
150 procedure SetIsNull(Value: Boolean); override;
151 procedure SetIsNullable(Value: Boolean); override;
152 procedure SetSQLData(AValue: PByte; len: cardinal); override;
153 procedure SetScale(aValue: integer); override;
154 procedure SetDataLength(len: cardinal); override;
155 procedure SetSQLType(aValue: cardinal); override;
156 procedure SetCharSetID(aValue: cardinal); override;
157 public
158 constructor Create(aParent: TIBXSQLDA; aIndex: integer);
159 procedure FreeSQLData;
160 procedure RowChange; override;
161 function GetAsArray: IArray; override;
162 function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
163 function GetArrayMetaData: IArrayMetaData; override;
164 function GetBlobMetaData: IBlobMetaData; override;
165 function CreateBlob: IBlob; override;
166 procedure Initialize; override;
167
168 property Statement: TFB25Statement read FStatement;
169 property SQLType: cardinal read GetSQLType write SetSQLType;
170 end;
171
172 TIBXINPUTSQLDA = class;
173
174 { TIBXSQLDA }
175
176 TIBXSQLDA = class(TSQLDataArea)
177 private
178 FCount: Integer; {Columns in use - may be less than inherited columns}
179 FSize: Integer; {Number of TIBXSQLVARs in column list}
180 FXSQLDA: PXSQLDA;
181 FTransactionSeqNo: integer;
182 function GetRecordSize: Integer;
183 function GetXSQLDA: PXSQLDA;
184 protected
185 FStatement: TFB25Statement;
186 FFirebird25ClientAPI: TFB25ClientAPI;
187 function GetTransactionSeqNo: integer; override;
188 procedure FreeXSQLDA;
189 function GetStatement: IStatement; override;
190 function GetPrepareSeqNo: integer; override;
191 procedure SetCount(Value: Integer); override;
192 public
193 constructor Create(aStatement: TFB25Statement);
194 destructor Destroy; override;
195 function CanChangeMetaData: boolean; override;
196 function CheckStatementStatus(Request: TStatementStatus): boolean; override;
197 function ColumnsInUseCount: integer; override;
198 function GetTransaction: TFB25Transaction; virtual;
199 procedure Initialize; override;
200 function StateChanged(var ChangeSeqNo: integer): boolean; override;
201 property AsXSQLDA: PXSQLDA read GetXSQLDA;
202 property Count: Integer read FCount write SetCount;
203 property RecordSize: Integer read GetRecordSize;
204 property Statement: TFB25Statement read FStatement;
205 end;
206
207 { TIBXINPUTSQLDA }
208
209 TIBXINPUTSQLDA = class(TIBXSQLDA)
210 public
211 procedure Bind;
212 function IsInputDataArea: boolean; override;
213 end;
214
215
216 { TIBXOUTPUTSQLDA }
217
218 TIBXOUTPUTSQLDA = class(TIBXSQLDA)
219 private
220 FTransaction: TFB25Transaction; {transaction used to execute the statement}
221 public
222 procedure Bind;
223 function GetTransaction: TFB25Transaction; override;
224 procedure GetData(index: integer; var aIsNull: boolean; var len: short;
225 var data: PByte); override;
226 function IsInputDataArea: boolean; override;
227 end;
228
229 { TResultSet }
230
231 TResultSet = class(TResults,IResultSet)
232 private
233 FResults: TIBXOUTPUTSQLDA;
234 FCursorSeqNo: integer;
235 public
236 constructor Create(aResults: TIBXOUTPUTSQLDA);
237 destructor Destroy; override;
238 {IResultSet}
239 function FetchNext: boolean; {fetch next record}
240 function FetchPrior: boolean; {fetch previous record}
241 function FetchFirst:boolean; {fetch first record}
242 function FetchLast: boolean; {fetch last record}
243 function FetchAbsolute(position: Integer): boolean; {fetch record by its absolute position in result set}
244 function FetchRelative(offset: Integer): boolean; {fetch record by position relative to current}
245 function GetCursorName: AnsiString;
246 function GetTransaction: ITransaction; override;
247 function IsEof: boolean;
248 function IsBof: boolean;
249 procedure Close;
250 end;
251
252 { TFB25Statement }
253
254 TFB25Statement = class(TFBStatement,IStatement)
255 private
256 FDBHandle: TISC_DB_HANDLE;
257 FHandle: TISC_STMT_HANDLE;
258 FFirebird25ClientAPI: TFB25ClientAPI;
259 FSQLParams: TIBXINPUTSQLDA;
260 FSQLRecord: TIBXOUTPUTSQLDA;
261 FCursor: AnsiString; { Cursor name...}
262 FCursorSeqNo: integer;
263 procedure GetPerfCounters(var counters: TPerfStatistics);
264 protected
265 procedure CheckHandle; override;
266 procedure GetDsqlInfo(info_request: byte; buffer: ISQLInfoResults); override;
267 function GetStatementIntf: IStatement; override;
268 procedure InternalPrepare(CursorName: AnsiString=''); override;
269 function InternalExecute(aTransaction: ITransaction): IResults; override;
270 function InternalOpenCursor(aTransaction: ITransaction; Scrollable: boolean): IResultSet; override;
271 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
272 procedure FreeHandle; override;
273 procedure InternalClose(Force: boolean); override;
274 public
275 constructor Create(Attachment: TFB25Attachment; Transaction: ITransaction;
276 sql: AnsiString; aSQLDialect: integer; CursorName: AnsiString='');
277 constructor CreateWithParameterNames(Attachment: TFB25Attachment;
278 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean;
279 CaseSensitiveParams: boolean=false; CursorName: AnsiString='');
280 destructor Destroy; override;
281 function FetchNext: boolean;
282
283 public
284 {IStatement}
285 function GetSQLParams: ISQLParams; override;
286 function GetMetaData: IMetaData; override;
287 function GetPlan: AnsiString;
288 function IsPrepared: boolean;
289 function CreateBlob(column: TColumnMetaData): IBlob; override;
290 function CreateArray(column: TColumnMetaData): IArray; override;
291 procedure SetRetainInterfaces(aValue: boolean); override;
292 property Handle: TISC_STMT_HANDLE read FHandle;
293
294 end;
295
296 implementation
297
298 uses IBUtils, FBMessages, FBBlob, FB25Blob, variants, IBErrorCodes, FBArray, FB25Array
299 {$IFDEF UNIX}, BaseUnix {$ENDIF};
300
301
302 { TIBXSQLVAR }
303
304 function TIBXSQLVAR.GetSQLType: cardinal;
305 begin
306 result := FXSQLVAR^.sqltype and (not 1);
307 end;
308
309 function TIBXSQLVAR.GetSubtype: integer;
310 begin
311 if GetSQLType = SQL_BLOB then
312 result := FXSQLVAR^.sqlsubtype
313 else
314 result := 0;
315 end;
316
317 function TIBXSQLVAR.GetAliasName: AnsiString;
318 begin
319 result := strpas(FXSQLVAR^.aliasname);
320 end;
321
322 function TIBXSQLVAR.GetFieldName: AnsiString;
323 begin
324 result := strpas(FXSQLVAR^.sqlname);
325 end;
326
327 function TIBXSQLVAR.GetOwnerName: AnsiString;
328 begin
329 result := strpas(FXSQLVAR^.ownname);
330 end;
331
332 function TIBXSQLVAR.GetRelationName: AnsiString;
333 begin
334 result := strpas(FXSQLVAR^.relname);
335 end;
336
337 function TIBXSQLVAR.GetScale: integer;
338 begin
339 if GetSQLType = SQL_BLOB then
340 result := 0
341 else
342 result := FXSQLVAR^.sqlscale;
343 end;
344
345 function TIBXSQLVAR.GetCharSetID: cardinal;
346 begin
347 result := 0;
348 case SQLType of
349 SQL_VARYING, SQL_TEXT:
350 {see http://firebirdsql.org/rlsnotesh/rlsnotes210.html}
351 result := FXSQLVAR^.sqlsubtype and $FF;
352
353 SQL_BLOB:
354 if (SQLSubType = 1) then
355 {see http://firebirdsql.org/rlsnotesh/rlsnotes210.html}
356 result := FXSQLVAR^.sqlscale and $FF;
357
358 SQL_ARRAY:
359 if (GetRelationName <> '') and (GetFieldName <> '') then
360 result := GetArrayMetaData.GetCharSetID;
361 end;
362 end;
363
364 function TIBXSQLVAR.GetCodePage: TSystemCodePage;
365 begin
366 result := CP_NONE;
367 with Statement.GetAttachment do
368 CharSetID2CodePage(GetCharSetID,result);
369 end;
370
371 function TIBXSQLVAR.GetCharSetWidth: integer;
372 begin
373 result := 1;
374 with Statement.GetAttachment DO
375 CharSetWidth(GetCharSetID,result);
376 end;
377
378 function TIBXSQLVAR.GetIsNull: Boolean;
379 begin
380 result := IsNullable and (FNullIndicator = -1);
381 end;
382
383 function TIBXSQLVAR.GetIsNullable: boolean;
384 begin
385 result := (FXSQLVAR^.sqltype and 1 = 1);
386 end;
387
388 function TIBXSQLVAR.GetSQLData: PByte;
389 begin
390 Result := FXSQLVAR^.sqldata;
391 end;
392
393 function TIBXSQLVAR.GetDataLength: cardinal;
394 begin
395 Result := FXSQLVAR^.sqllen;
396 end;
397
398 function TIBXSQLVAR.GetSize: cardinal;
399 begin
400 Result := FMetadataSize;
401 end;
402
403 function TIBXSQLVAR.GetAttachment: IAttachment;
404 begin
405 Result := FStatement.GetAttachment;
406 end;
407
408 function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
409 begin
410 if GetSQLType <> SQL_ARRAY then
411 IBError(ibxeInvalidDataConversion,[nil]);
412
413 if FArrayMetaData = nil then
414 FArrayMetaData := TFB25ArrayMetaData.Create(FStatement.GetAttachment as TFB25Attachment,
415 FStatement.GetTransaction as TFB25Transaction,
416 GetRelationName,GetFieldName);
417 Result := FArrayMetaData;
418 end;
419
420 function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
421 begin
422 if GetSQLType <> SQL_BLOB then
423 IBError(ibxeInvalidDataConversion,[nil]);
424
425 if FBlobMetaData = nil then
426 FBlobMetaData := TFB25BlobMetaData.Create(FStatement.GetAttachment as TFB25Attachment,
427 FStatement.GetTransaction as TFB25Transaction,
428 GetRelationName,GetFieldName,GetSubType);
429 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
430 Result := FBlobMetaData;
431 end;
432
433 function TIBXSQLVAR.GetAsArray: IArray;
434 begin
435 if SQLType <> SQL_ARRAY then
436 IBError(ibxeInvalidDataConversion,[nil]);
437
438 if IsNull then
439 Result := nil
440 else
441 begin
442 if FArrayIntf = nil then
443 FArrayIntf := TFB25Array.Create(FStatement.GetAttachment as TFB25Attachment,
444 TIBXSQLDA(Parent).GetTransaction,
445 GetArrayMetaData,PISC_QUAD(SQLData)^);
446 Result := FArrayIntf;
447 end;
448 end;
449
450 function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
451 begin
452 if FBlob <> nil then
453 Result := FBlob
454 else
455 begin
456 if SQLType <> SQL_BLOB then
457 IBError(ibxeInvalidDataConversion, [nil]);
458 if IsNull then
459 Result := nil
460 else
461 Result := TFB25Blob.Create(FStatement.GetAttachment as TFB25Attachment,
462 TIBXSQLDA(Parent).GetTransaction,
463 GetBlobMetaData,
464 Blob_ID,BPB);
465 FBlob := Result;
466 end;
467 end;
468
469 function TIBXSQLVAR.CreateBlob: IBlob;
470 begin
471 Result := TFB25Blob.Create(FStatement.GetAttachment as TFB25Attachment,
472 FStatement.GetTransaction as TFB25Transaction,GetSubType,GetCharSetID,nil);
473 end;
474
475 procedure TIBXSQLVAR.Initialize;
476 begin
477 inherited Initialize;
478 FOwnsSQLData := true;
479 with FFirebird25ClientAPI, FXSQLVar^ do
480 begin
481 FMetadataSize := sqllen;
482 case sqltype and (not 1) of
483 SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
484 SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
485 SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
486 if (sqllen = 0) then
487 { Make sure you get a valid pointer anyway
488 select '' from foo }
489 IBAlloc(sqldata, 0, 1)
490 else
491 IBAlloc(sqldata, 0, sqllen)
492 end;
493 SQL_VARYING: begin
494 IBAlloc(sqldata, 0, sqllen + 2);
495 end;
496 else
497 IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
498 end;
499 if (sqltype and 1 = 1) then
500 begin
501 sqlInd := @FNullIndicator;
502 FNullIndicator := -1;
503 end
504 else
505 sqlInd := nil;
506 end;
507 SaveMetaData;
508 end;
509
510 procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
511 begin
512 if Value then
513 begin
514 IsNullable := true;
515 FNullIndicator := -1;
516 Changed;
517 end
518 else
519 if ((not Value) and IsNullable) then
520 begin
521 FNullIndicator := 0;
522 Changed;
523 end;
524 end;
525
526 procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
527 begin
528 if (Value <> IsNullable) then
529 begin
530 if Value then
531 begin
532 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype or 1;
533 FNullIndicator := 0;
534 FXSQLVAR^.sqlInd := @FNullIndicator;
535 end
536 else
537 begin
538 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype and (not 1);
539 FXSQLVAR^.sqlind := nil;
540 end;
541 end;
542 Changed;
543 end;
544
545 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
546 begin
547 if FOwnsSQLData then
548 FreeMem(FXSQLVAR^.sqldata);
549 FXSQLVAR^.sqldata := AValue;
550 FXSQLVAR^.sqllen := len;
551 FOwnsSQLData := false;
552 Changed;
553 end;
554
555 procedure TIBXSQLVAR.SetScale(aValue: integer);
556 begin
557 FXSQLVAR^.sqlscale := aValue;
558 Changed;
559 end;
560
561 procedure TIBXSQLVAR.SetDataLength(len: cardinal);
562 begin
563 if not FOwnsSQLData then
564 FXSQLVAR^.sqldata := nil;
565 FXSQLVAR^.sqllen := len;
566 with FFirebird25ClientAPI do
567 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
568 FOwnsSQLData := true;
569 Changed;
570 end;
571
572 procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
573 begin
574 FXSQLVAR^.sqltype := aValue or (FXSQLVAR^.sqltype and 1);
575 Changed;
576 end;
577
578 procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
579 begin
580 if aValue <> GetCharSetID then
581 begin
582 case SQLType of
583 SQL_VARYING, SQL_TEXT:
584 FXSQLVAR^.sqlsubtype := (aValue and $FF) or (FXSQLVAR^.sqlsubtype and not $FF);
585
586 SQL_BLOB,
587 SQL_ARRAY:
588 IBError(ibxeInvalidDataConversion,[nil]);
589 end;
590 Changed;
591 end;
592 end;
593
594 function TIBXSQLVAR.GetDefaultTextSQLType: cardinal;
595 begin
596 Result := SQL_TEXT;
597 end;
598
599 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
600 begin
601 inherited Create(aParent,aIndex);
602 FStatement := aParent.Statement;
603 FFirebird25ClientAPI := aParent.FFirebird25ClientAPI;
604 end;
605
606 procedure TIBXSQLVAR.FreeSQLData;
607 begin
608 if FOwnsSQLData then
609 FreeMem(FXSQLVAR^.sqldata);
610 FXSQLVAR^.sqldata := nil;
611 FOwnsSQLData := true;
612 end;
613
614 procedure TIBXSQLVAR.RowChange;
615 begin
616 inherited RowChange;
617 FBlob := nil;
618 end;
619
620
621 { TResultSet }
622
623 constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
624 begin
625 inherited Create(aResults);
626 FResults := aResults;
627 FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
628 end;
629
630 destructor TResultSet.Destroy;
631 begin
632 Close;
633 inherited Destroy;
634 end;
635
636 function TResultSet.FetchNext: boolean;
637 var i: integer;
638 begin
639 CheckActive;
640 Result := FResults.FStatement.FetchNext;
641 if Result then
642 for i := 0 to getCount - 1 do
643 FResults.Column[i].RowChange;
644 end;
645
646 function TResultSet.FetchPrior: boolean;
647 begin
648 IBError(ibxeNoScrollableCursors,[]);
649 end;
650
651 function TResultSet.FetchFirst: boolean;
652 begin
653 IBError(ibxeNoScrollableCursors,[]);
654 end;
655
656 function TResultSet.FetchLast: boolean;
657 begin
658 IBError(ibxeNoScrollableCursors,[]);
659 end;
660
661 function TResultSet.FetchAbsolute(position: Integer): boolean;
662 begin
663 IBError(ibxeNoScrollableCursors,[]);
664 end;
665
666 function TResultSet.FetchRelative(offset: Integer): boolean;
667 begin
668 IBError(ibxeNoScrollableCursors,[]);
669 end;
670
671 function TResultSet.GetCursorName: AnsiString;
672 begin
673 Result := FResults.FStatement.FCursor;
674 end;
675
676 function TResultSet.GetTransaction: ITransaction;
677 begin
678 Result := FResults.GetTransaction;
679 end;
680
681 function TResultSet.IsEof: boolean;
682 begin
683 Result := FResults.FStatement.FEof;
684 end;
685
686 function TResultSet.IsBof: boolean;
687 begin
688 Result := FResults.FStatement.FBof;
689 end;
690
691 procedure TResultSet.Close;
692 begin
693 if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
694 FResults.FStatement.Close;
695 end;
696
697 { TIBXINPUTSQLDA }
698
699 procedure TIBXINPUTSQLDA.Bind;
700 begin
701 if Count = 0 then
702 Count := 1;
703 with FFirebird25ClientAPI do
704 begin
705 if (FXSQLDA <> nil) then
706 if isc_dsql_describe_bind(StatusVector, @(FStatement.Handle), FStatement.SQLDialect,
707 FXSQLDA) > 0 then
708 IBDataBaseError;
709
710 if FXSQLDA^.sqld > FXSQLDA^.sqln then
711 begin
712 Count := FXSQLDA^.sqld;
713 if isc_dsql_describe_bind(StatusVector, @(FStatement.Handle), FStatement.SQLDialect,
714 FXSQLDA) > 0 then
715 IBDataBaseError;
716 end
717 else
718 if FXSQLDA^.sqld = 0 then
719 Count := 0;
720 end;
721 Initialize;
722 end;
723
724 function TIBXINPUTSQLDA.IsInputDataArea: boolean;
725 begin
726 Result := true;
727 end;
728
729 { TIBXOUTPUTSQLDA }
730
731 procedure TIBXOUTPUTSQLDA.Bind;
732 begin
733 { Allocate an initial output descriptor (with one column) }
734 Count := 1;
735 with FFirebird25ClientAPI do
736 begin
737 { Using isc_dsql_describe, get the right size for the columns... }
738 if isc_dsql_describe(StatusVector, @(FStatement.Handle), FStatement.SQLDialect, FXSQLDA) > 0 then
739 IBDataBaseError;
740
741 if FXSQLDA^.sqld > FXSQLDA^.sqln then
742 begin
743 Count := FXSQLDA^.sqld;
744 if isc_dsql_describe(StatusVector, @(FStatement.Handle), FStatement.SQLDialect, FXSQLDA) > 0 then
745 IBDataBaseError;
746 end
747 else
748 if FXSQLDA^.sqld = 0 then
749 Count := 0;
750 end;
751 Initialize;
752 SetUniqueRelationName;
753 end;
754
755 function TIBXOUTPUTSQLDA.GetTransaction: TFB25Transaction;
756 begin
757 Result := FTransaction;
758 end;
759
760 procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull:boolean; var len: short;
761 var data: PByte);
762 begin
763 with TIBXSQLVAR(Column[index]), FXSQLVAR^ do
764 begin
765 aIsNull := (sqltype and 1 = 1) and (FNullIndicator = -1);
766 data := sqldata;
767 len := sqllen;
768 if not IsNull and ((sqltype and (not 1)) = SQL_VARYING) then
769 begin
770 with FFirebird25ClientAPI do
771 len := DecodeInteger(data,2);
772 Inc(data,2);
773 end;
774 end;
775 end;
776
777 function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
778 begin
779 Result := false;
780 end;
781
782 { TIBXSQLDA }
783 constructor TIBXSQLDA.Create(aStatement: TFB25Statement);
784 begin
785 inherited Create;
786 FStatement := aStatement;
787 FFirebird25ClientAPI := aStatement.FFirebird25ClientAPI;
788 FSize := 0;
789 // writeln('Creating ',ClassName);
790 end;
791
792 destructor TIBXSQLDA.Destroy;
793 begin
794 FreeXSQLDA;
795 // writeln('Destroying ',ClassName);
796 inherited Destroy;
797 end;
798
799 function TIBXSQLDA.CanChangeMetaData: boolean;
800 begin
801 Result := true;
802 end;
803
804 function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
805 begin
806 Result := false;
807 case Request of
808 ssPrepared:
809 Result := FStatement.IsPrepared;
810
811 ssExecuteResults:
812 Result :=FStatement.FSingleResults;
813
814 ssCursorOpen:
815 Result := FStatement.FOpen;
816
817 ssBOF:
818 Result := FStatement.FBOF;
819
820 ssEOF:
821 Result := FStatement.FEOF;
822 end;
823 end;
824
825 function TIBXSQLDA.ColumnsInUseCount: integer;
826 begin
827 Result := FCount;
828 end;
829
830 function TIBXSQLDA.GetRecordSize: Integer;
831 begin
832 result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
833 end;
834
835 function TIBXSQLDA.GetXSQLDA: PXSQLDA;
836 begin
837 result := FXSQLDA;
838 end;
839
840 function TIBXSQLDA.GetTransactionSeqNo: integer;
841 begin
842 Result := FTransactionSeqNo;
843 end;
844
845 procedure TIBXSQLDA.Initialize;
846 begin
847 if FXSQLDA <> nil then
848 inherited Initialize;
849 end;
850
851 function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
852 begin
853 Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
854 if Result then
855 ChangeSeqNo := FStatement.ChangeSeqNo;
856 end;
857
858 function TIBXSQLDA.GetTransaction: TFB25Transaction;
859 begin
860 Result := FStatement.GetTransaction as TFB25Transaction;
861 end;
862
863 procedure TIBXSQLDA.SetCount(Value: Integer);
864 var
865 i, OldSize: Integer;
866 p : PXSQLVAR;
867 begin
868 FCount := Value;
869 if FCount = 0 then
870 FUniqueRelationName := ''
871 else
872 begin
873 if FSize > 0 then
874 OldSize := XSQLDA_LENGTH(FSize)
875 else
876 OldSize := 0;
877 if Count > FSize then
878 begin
879 FFirebird25ClientAPI.IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(Count));
880 SetLength(FColumnList, FCount);
881 FXSQLDA^.version := SQLDA_VERSION1;
882 p := @FXSQLDA^.sqlvar[0];
883 for i := 0 to Count - 1 do
884 begin
885 if i >= FSize then
886 FColumnList[i] := TIBXSQLVAR.Create(self,i);
887 TIBXSQLVAR(Column[i]).FXSQLVAR := p;
888 p := Pointer(PAnsiChar(p) + sizeof(FXSQLDA^.sqlvar));
889 end;
890 FSize := inherited Count;
891 end;
892 if FSize > 0 then
893 begin
894 FXSQLDA^.sqln := Value;
895 FXSQLDA^.sqld := Value;
896 end;
897 end;
898 end;
899
900 procedure TIBXSQLDA.FreeXSQLDA;
901 var i: integer;
902 begin
903 if FXSQLDA <> nil then
904 begin
905 // writeln('SQLDA Cleanup');
906 for i := 0 to Count - 1 do
907 TIBXSQLVAR(Column[i]).FreeSQLData;
908 FreeMem(FXSQLDA);
909 FXSQLDA := nil;
910 end;
911 for i := 0 to FSize - 1 do
912 TIBXSQLVAR(Column[i]).Free;
913 SetLength(FColumnList,0);
914 FSize := 0;
915 end;
916
917 function TIBXSQLDA.GetStatement: IStatement;
918 begin
919 Result := FStatement;
920 end;
921
922 function TIBXSQLDA.GetPrepareSeqNo: integer;
923 begin
924 Result := FStatement.FPrepareSeqNo;
925 end;
926
927 { TFB25Statement }
928
929 procedure TFB25Statement.GetPerfCounters(var counters: TPerfStatistics);
930 var DBInfo: IDBInformation;
931 i: integer;
932 {$IFDEF UNIX}
933 times: tms;
934 {$ENDIF}
935 begin
936 {$IFDEF UNIX}
937 FpTimes(times);
938 counters[psUserTime] := times.tms_utime;
939 {$ELSE}
940 counters[psUserTime] := 0;
941 {$ENDIF}
942 counters[psRealTime] := TimeStampToMSecs(DateTimeToTimeStamp(Now));
943
944 DBInfo := GetAttachment.GetDBInformation([isc_info_reads,isc_info_writes,
945 isc_info_fetches, isc_info_num_buffers, isc_info_current_memory,
946 isc_info_max_memory]);
947 if DBInfo <> nil then
948 begin
949 for i := 0 to DBInfo.Count - 1 do
950 with DBInfo[i] do
951 case getItemType of
952 isc_info_reads:
953 counters[psReads] := AsInteger;
954 isc_info_writes:
955 counters[psWrites] := AsInteger;
956 isc_info_fetches:
957 counters[psFetches] := AsInteger;
958 isc_info_num_buffers:
959 counters[psBuffers] := AsInteger;
960 isc_info_current_memory:
961 counters[psCurrentMemory] := AsInteger;
962 isc_info_max_memory:
963 counters[psMaxMemory] := AsInteger;
964 end;
965 end;
966 end;
967
968 procedure TFB25Statement.CheckHandle;
969 begin
970 if FHandle = nil then
971 IBError(ibxeInvalidStatementHandle,[nil]);
972 end;
973
974 procedure TFB25Statement.GetDsqlInfo(info_request: byte; buffer: ISQLInfoResults
975 );
976 begin
977 with FFirebird25ClientAPI, buffer as TSQLInfoResultsBuffer do
978 if isc_dsql_sql_info(StatusVector, @(FHandle), 1, @info_request,
979 GetBufSize, Buffer) > 0 then
980 IBDatabaseError;
981 end;
982
983 function TFB25Statement.GetStatementIntf: IStatement;
984 begin
985 Result := self;
986 end;
987
988 procedure TFB25Statement.InternalPrepare(CursorName: AnsiString);
989 var
990 GUID: TGUID;
991 RB: ISQLInfoResults;
992 TRHandle: TISC_TR_HANDLE;
993 begin
994 if FPrepared then
995 Exit;
996
997 if (FSQL = '') then
998 IBError(ibxeEmptyQuery, [nil]);
999
1000 FCursor := CursorName;
1001 if FCursor = '' then
1002 begin
1003 CreateGuid(GUID);
1004 FCursor := GUIDToString(GUID);
1005 end;
1006
1007 try
1008 CheckTransaction(FTransactionIntf);
1009 with FFirebird25ClientAPI do
1010 begin
1011 Call(isc_dsql_alloc_statement2(StatusVector, @(FDBHandle),
1012 @FHandle), True);
1013 TRHandle := (FTransactionIntf as TFB25Transaction).Handle;
1014 if FHasParamNames then
1015 begin
1016 if FProcessedSQL = '' then
1017 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1018 Call(isc_dsql_prepare(StatusVector, @(TRHandle), @FHandle, 0,
1019 PAnsiChar(FProcessedSQL), FSQLDialect, nil), True);
1020 end
1021 else
1022 Call(isc_dsql_prepare(StatusVector, @(TRHandle), @FHandle, 0,
1023 PAnsiChar(FSQL), FSQLDialect, nil), True);
1024 end;
1025
1026 { After preparing the statement, query the stmt type and possibly
1027 create a FSQLRecord "holder" }
1028 { Get the type of the statement }
1029 RB := GetDsqlInfo(isc_info_sql_stmt_type);
1030 if RB.Count > 0 then
1031 FSQLStatementType := TIBSQLStatementTypes(RB[0].GetAsInteger)
1032 else
1033 FSQLStatementType := SQLUnknown;
1034
1035 if FSQLStatementType = SQLSelect then
1036 with FFirebird25ClientAPI do
1037 Call(
1038 isc_dsql_set_cursor_name(StatusVector, @FHandle, PAnsiChar(FCursor), 0),
1039 True);
1040
1041 case FSQLStatementType of
1042 SQLGetSegment,
1043 SQLPutSegment,
1044 SQLStartTransaction: begin
1045 FreeHandle;
1046 IBError(ibxeNotPermitted, [nil]);
1047 end;
1048 SQLCommit,
1049 SQLRollback,
1050 SQLDDL, SQLSetGenerator,
1051 SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1052 SQLExecProcedure:
1053 begin
1054 {set up input sqlda}
1055 FSQLParams.Bind;
1056
1057 {setup output sqlda}
1058 if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1059 SQLExecProcedure] then
1060 FSQLRecord.Bind;
1061 end;
1062 end;
1063 except
1064 on E: Exception do begin
1065 if (FHandle <> nil) then
1066 FreeHandle;
1067 if E is EIBInterBaseError then
1068 E.Message := E.Message + sSQLErrorSeparator + FSQL;
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 Inc(FChangeSeqNo);
1081 with FTransactionIntf as TFB25Transaction do
1082 begin
1083 FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1084 FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1085 end;
1086 end;
1087
1088 function TFB25Statement.InternalExecute(aTransaction: ITransaction): IResults;
1089 var TRHandle: TISC_TR_HANDLE;
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 TFB25Transaction);
1101 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB25transaction).TransactionSeqNo) then
1102 IBError(ibxeInterfaceOutofDate,[nil]);
1103
1104 try
1105 TRHandle := (aTransaction as TFB25Transaction).Handle;
1106 with FFirebird25ClientAPI do
1107 begin
1108 if FCollectStatistics then
1109 GetPerfCounters(FBeforeStats);
1110
1111 case FSQLStatementType of
1112 SQLSelect:
1113 IBError(ibxeIsAExecuteProcedure,[]);
1114
1115 SQLExecProcedure:
1116 begin
1117 Call(isc_dsql_execute2(StatusVector,
1118 @(TRHandle),
1119 @FHandle,
1120 SQLDialect,
1121 FSQLParams.AsXSQLDA,
1122 FSQLRecord.AsXSQLDA), True);
1123 Result := TResults.Create(FSQLRecord);
1124 FSingleResults := true;
1125 end
1126 else
1127 Call(isc_dsql_execute(StatusVector,
1128 @(TRHandle),
1129 @FHandle,
1130 SQLDialect,
1131 FSQLParams.AsXSQLDA), True);
1132
1133 end;
1134 if FCollectStatistics then
1135 begin
1136 GetPerfCounters(FAfterStats);
1137 FStatisticsAvailable := true;
1138 end;
1139 end;
1140 finally
1141 if aTransaction <> FTransactionIntf then
1142 RemoveMonitor(aTransaction as TFB25Transaction);
1143 end;
1144 FExecTransactionIntf := aTransaction;
1145 FSQLRecord.FTransaction := aTransaction as TFB25Transaction;
1146 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1147 Inc(FChangeSeqNo);
1148 end;
1149
1150 function TFB25Statement.InternalOpenCursor(aTransaction: ITransaction;
1151 Scrollable: boolean): IResultSet;
1152 var TRHandle: TISC_TR_HANDLE;
1153 begin
1154 if FSQLStatementType <> SQLSelect then
1155 IBError(ibxeIsASelectStatement,[]);
1156
1157 if Scrollable then
1158 IBError(ibxeNoScrollableCursors,[]);
1159
1160 CheckTransaction(aTransaction);
1161 if not FPrepared then
1162 InternalPrepare;
1163 CheckHandle;
1164 if aTransaction <> FTransactionIntf then
1165 AddMonitor(aTransaction as TFB25Transaction);
1166 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB25transaction).TransactionSeqNo) then
1167 IBError(ibxeInterfaceOutofDate,[nil]);
1168
1169 with FFirebird25ClientAPI do
1170 begin
1171 if FCollectStatistics then
1172 GetPerfCounters(FBeforeStats);
1173
1174 TRHandle := (aTransaction as TFB25Transaction).Handle;
1175 Call(isc_dsql_execute2(StatusVector,
1176 @(TRHandle),
1177 @FHandle,
1178 SQLDialect,
1179 FSQLParams.AsXSQLDA,
1180 nil), True);
1181
1182 if FCollectStatistics then
1183 begin
1184 GetPerfCounters(FAfterStats);
1185 FStatisticsAvailable := true;
1186 end;
1187 end;
1188 Inc(FCursorSeqNo);
1189 FSingleResults := false;
1190 FOpen := True;
1191 FExecTransactionIntf := aTransaction;
1192 FBOF := true;
1193 FEOF := false;
1194 FSQLRecord.FTransaction := aTransaction as TFB25Transaction;
1195 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1196 Result := TResultSet.Create(FSQLRecord);
1197 Inc(FChangeSeqNo);
1198 end;
1199
1200 procedure TFB25Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1201 var processedSQL: AnsiString);
1202 begin
1203 FSQLParams.PreprocessSQL(sql,GenerateParamNames, processedSQL);
1204 end;
1205
1206 procedure TFB25Statement.FreeHandle;
1207 var
1208 isc_res: ISC_STATUS;
1209 begin
1210 Close;
1211 ReleaseInterfaces;
1212 try
1213 if FHandle <> nil then
1214 with FFirebird25ClientAPI do
1215 begin
1216 isc_res :=
1217 Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
1218 if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
1219 IBDataBaseError;
1220 end;
1221 finally
1222 FHandle := nil;
1223 FCursor := '';
1224 FPrepared := false;
1225 end;
1226 end;
1227
1228 procedure TFB25Statement.InternalClose(Force: boolean);
1229 var
1230 isc_res: ISC_STATUS;
1231 begin
1232 if (FHandle <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1233 try
1234 with FFirebird25ClientAPI do
1235 begin
1236 isc_res := Call(
1237 isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
1238 False);
1239 if not Force and (StatusVector^ = 1) and (isc_res > 0) and
1240 not getStatus.CheckStatusVector(
1241 [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
1242 IBDatabaseError;
1243 end;
1244 finally
1245 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB25Transaction)) then
1246 RemoveMonitor(FSQLRecord.FTransaction);
1247 FOpen := False;
1248 FExecTransactionIntf := nil;
1249 FSQLRecord.FTransaction := nil;
1250 Inc(FChangeSeqNo);
1251 end;
1252 end;
1253
1254 constructor TFB25Statement.Create(Attachment: TFB25Attachment;
1255 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
1256 CursorName: AnsiString);
1257 begin
1258 inherited Create(Attachment,Transaction,sql,aSQLDialect);
1259 FDBHandle := Attachment.Handle;
1260 FFirebird25ClientAPI := Attachment.Firebird25ClientAPI;
1261 OnDatabaseError := FFirebird25ClientAPI.IBDataBaseError;
1262 FSQLParams := TIBXINPUTSQLDA.Create(self);
1263 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1264 InternalPrepare(CursorName);
1265 end;
1266
1267 constructor TFB25Statement.CreateWithParameterNames(
1268 Attachment: TFB25Attachment; Transaction: ITransaction; sql: AnsiString;
1269 aSQLDialect: integer; GenerateParamNames: boolean;
1270 CaseSensitiveParams: boolean; CursorName: AnsiString);
1271 begin
1272 inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1273 FDBHandle := Attachment.Handle;
1274 FFirebird25ClientAPI := Attachment.Firebird25ClientAPI;
1275 OnDatabaseError := FFirebird25ClientAPI.IBDataBaseError;
1276 FSQLParams := TIBXINPUTSQLDA.Create(self);
1277 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1278 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1279 InternalPrepare(CursorName);
1280 end;
1281
1282 destructor TFB25Statement.Destroy;
1283 begin
1284 inherited Destroy;
1285 if assigned(FSQLParams) then FSQLParams.Free;
1286 if assigned(FSQLRecord) then FSQLRecord.Free;
1287 end;
1288
1289 function TFB25Statement.FetchNext: boolean;
1290 var
1291 fetch_res: ISC_STATUS;
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 FFirebird25ClientAPI do
1300 begin
1301 { Go to the next record... }
1302 fetch_res :=
1303 Call(isc_dsql_fetch(StatusVector, @FHandle, SQLDialect, FSQLRecord.AsXSQLDA), False);
1304 if (fetch_res = 100) or (getStatus.CheckStatusVector([isc_dsql_cursor_err])) then
1305 begin
1306 FBOF := false;
1307 FEOF := true;
1308 Exit; {End of File}
1309 end
1310 else
1311 if (fetch_res > 0) then
1312 begin
1313 try
1314 IBDataBaseError;
1315 except
1316 Close;
1317 raise;
1318 end;
1319 end
1320 else
1321 begin
1322 FBOF := false;
1323 result := true;
1324 end;
1325 if FCollectStatistics then
1326 begin
1327 GetPerfCounters(FAfterStats);
1328 FStatisticsAvailable := true;
1329 end;
1330 end;
1331 FSQLRecord.RowChange;
1332 if FEOF then
1333 Inc(FChangeSeqNo);
1334 end;
1335
1336 function TFB25Statement.GetSQLParams: ISQLParams;
1337 begin
1338 CheckHandle;
1339 if not HasInterface(0) then
1340 AddInterface(0,TSQLParams.Create(FSQLParams));
1341 Result := TSQLParams(GetInterface(0));
1342 end;
1343
1344 function TFB25Statement.GetMetaData: IMetaData;
1345 begin
1346 CheckHandle;
1347 if not HasInterface(1) then
1348 AddInterface(1, TMetaData.Create(FSQLRecord));
1349 Result := TMetaData(GetInterface(1));
1350 end;
1351
1352 function TFB25Statement.GetPlan: AnsiString;
1353 var
1354 RB: ISQLInfoResults;
1355 begin
1356 if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1357 {TODO: SQLExecProcedure, }
1358 SQLUpdate, SQLDelete])) then
1359 result := ''
1360 else
1361 begin
1362 RB := TSQLInfoResultsBuffer.Create(FFirebird25ClientAPI,4*4096);
1363 GetDsqlInfo(isc_info_sql_get_plan,RB);
1364 if RB.Count > 0 then
1365 Result := RB[0].GetAsString;
1366 end;
1367 end;
1368
1369 function TFB25Statement.CreateBlob(column: TColumnMetaData): IBlob;
1370 begin
1371 if assigned(column) and (column.SQLType <> SQL_Blob) then
1372 IBError(ibxeNotABlob,[nil]);
1373 Result := TFB25Blob.Create(GetAttachment as TFB25Attachment,FExecTransactionIntf as TFB25Transaction,
1374 column.GetBlobMetaData,nil);
1375 end;
1376
1377 function TFB25Statement.CreateArray(column: TColumnMetaData): IArray;
1378 begin
1379 if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1380 IBError(ibxeNotAnArray,[nil]);
1381 Result := TFB25Array.Create(GetAttachment as TFB25Attachment,FExecTransactionIntf as TFB25Transaction,
1382 column.GetArrayMetaData);
1383 end;
1384
1385 procedure TFB25Statement.SetRetainInterfaces(aValue: boolean);
1386 begin
1387 inherited SetRetainInterfaces(aValue);
1388 if HasInterface(1) then
1389 TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1390 if HasInterface(0) then
1391 TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1392 end;
1393
1394 function TFB25Statement.IsPrepared: boolean;
1395 begin
1396 Result := FHandle <> nil;
1397 end;
1398
1399 end.
1400