ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/2.5/FB25Statement.pas
Revision: 56
Committed: Mon Mar 6 10:20:02 2017 UTC (7 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 36935 byte(s)
Log Message:
Committing updates for Trunk

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 FBlob: IBlob; {Cache references}
125 FArray: IArray;
126 FNullIndicator: short;
127 FOwnsSQLData: boolean;
128 FBlobMetaData: IBlobMetaData;
129 FArrayMetaData: IArrayMetaData;
130 FXSQLVAR: PXSQLVAR; { Points to the PXSQLVAR in the owner object }
131 protected
132 function GetSQLType: cardinal; override;
133 function GetSubtype: integer; override;
134 function GetAliasName: AnsiString; override;
135 function GetFieldName: AnsiString; override;
136 function GetOwnerName: AnsiString; override;
137 function GetRelationName: AnsiString; override;
138 function GetScale: integer; override;
139 function GetCharSetID: cardinal; override;
140 function GetCodePage: TSystemCodePage; override;
141 function GetIsNull: Boolean; override;
142 function GetIsNullable: boolean; override;
143 function GetSQLData: PByte; override;
144 function GetDataLength: cardinal; override;
145 procedure SetIsNull(Value: Boolean); override;
146 procedure SetIsNullable(Value: Boolean); override;
147 procedure SetSQLData(AValue: PByte; len: cardinal); override;
148 procedure SetScale(aValue: integer); override;
149 procedure SetDataLength(len: cardinal); override;
150 procedure SetSQLType(aValue: cardinal); override;
151 procedure SetCharSetID(aValue: cardinal); override;
152 public
153 constructor Create(aParent: TIBXSQLDA; aIndex: integer);
154 procedure FreeSQLData;
155 procedure RowChange; override;
156 function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
157 function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
158 function GetArrayMetaData: IArrayMetaData; override;
159 function GetBlobMetaData: IBlobMetaData; override;
160 function CreateBlob: IBlob; override;
161 procedure Initialize; override;
162
163 property Statement: TFB25Statement read FStatement;
164 end;
165
166 TIBXINPUTSQLDA = class;
167
168 { TIBXSQLDA }
169
170 TIBXSQLDA = class(TSQLDataArea)
171 private
172 FCount: Integer; {Columns in use - may be less than inherited columns}
173 FSize: Integer; {Number of TIBXSQLVARs in column list}
174 FXSQLDA: PXSQLDA;
175 FTransactionSeqNo: integer;
176 function GetRecordSize: Integer;
177 function GetXSQLDA: PXSQLDA;
178 protected
179 FStatement: TFB25Statement;
180 function GetTransactionSeqNo: integer; override;
181 procedure FreeXSQLDA;
182 function GetStatement: IStatement; override;
183 function GetPrepareSeqNo: integer; override;
184 procedure SetCount(Value: Integer); override;
185 public
186 constructor Create(aStatement: TFB25Statement);
187 destructor Destroy; override;
188 function CheckStatementStatus(Request: TStatementStatus): boolean; override;
189 function ColumnsInUseCount: integer; override;
190 function GetTransaction: TFB25Transaction; virtual;
191 procedure Initialize; override;
192 function StateChanged(var ChangeSeqNo: integer): boolean; override;
193 property AsXSQLDA: PXSQLDA read GetXSQLDA;
194 property Count: Integer read FCount write SetCount;
195 property RecordSize: Integer read GetRecordSize;
196 property Statement: TFB25Statement read FStatement;
197 end;
198
199 { TIBXINPUTSQLDA }
200
201 TIBXINPUTSQLDA = class(TIBXSQLDA)
202 public
203 procedure Bind;
204 function IsInputDataArea: boolean; override;
205 end;
206
207
208 { TIBXOUTPUTSQLDA }
209
210 TIBXOUTPUTSQLDA = class(TIBXSQLDA)
211 private
212 FTransaction: TFB25Transaction; {transaction used to execute the statement}
213 public
214 procedure Bind;
215 function GetTransaction: TFB25Transaction; override;
216 procedure GetData(index: integer; var aIsNull: boolean; var len: short;
217 var data: PByte); override;
218 function IsInputDataArea: boolean; override;
219 end;
220
221 { TResultSet }
222
223 TResultSet = class(TResults,IResultSet)
224 private
225 FResults: TIBXOUTPUTSQLDA;
226 FCursorSeqNo: integer;
227 public
228 constructor Create(aResults: TIBXOUTPUTSQLDA);
229 destructor Destroy; override;
230 {IResultSet}
231 function FetchNext: boolean;
232 function GetCursorName: AnsiString;
233 function GetTransaction: ITransaction; override;
234 function IsEof: boolean;
235 procedure Close;
236 end;
237
238 { TFB25Statement }
239
240 TFB25Statement = class(TFBStatement,IStatement)
241 private
242 FDBHandle: TISC_DB_HANDLE;
243 FHandle: TISC_STMT_HANDLE;
244 FSQLParams: TIBXINPUTSQLDA;
245 FSQLRecord: TIBXOUTPUTSQLDA;
246 FCursor: AnsiString; { Cursor name...}
247 FCursorSeqNo: integer;
248 procedure GetPerfCounters(var counters: TPerfStatistics);
249 protected
250 procedure CheckHandle; override;
251 procedure GetDsqlInfo(info_request: byte; buffer: ISQLInfoResults); override;
252 procedure InternalPrepare; override;
253 function InternalExecute(aTransaction: ITransaction): IResults; override;
254 function InternalOpenCursor(aTransaction: ITransaction): IResultSet; override;
255 procedure FreeHandle; override;
256 procedure InternalClose(Force: boolean); override;
257 public
258 constructor Create(Attachment: TFB25Attachment; Transaction: ITransaction;
259 sql: AnsiString; aSQLDialect: integer);
260 constructor CreateWithParameterNames(Attachment: TFB25Attachment;
261 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean);
262 destructor Destroy; override;
263 function FetchNext: boolean;
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 property Handle: TISC_STMT_HANDLE read FHandle;
275
276 end;
277
278 implementation
279
280 uses IBUtils, FBMessages, FBBlob, FB25Blob, variants, IBErrorCodes, FBArray, FB25Array
281 {$IFDEF UNIX}, BaseUnix {$ENDIF};
282
283
284 { TIBXSQLVAR }
285
286 function TIBXSQLVAR.GetSQLType: cardinal;
287 begin
288 result := FXSQLVAR^.sqltype and (not 1);
289 end;
290
291 function TIBXSQLVAR.GetSubtype: integer;
292 begin
293 if GetSQLType = SQL_BLOB then
294 result := FXSQLVAR^.sqlsubtype
295 else
296 result := 0;
297 end;
298
299 function TIBXSQLVAR.GetAliasName: AnsiString;
300 begin
301 result := strpas(FXSQLVAR^.aliasname);
302 end;
303
304 function TIBXSQLVAR.GetFieldName: AnsiString;
305 begin
306 result := strpas(FXSQLVAR^.sqlname);
307 end;
308
309 function TIBXSQLVAR.GetOwnerName: AnsiString;
310 begin
311 result := strpas(FXSQLVAR^.ownname);
312 end;
313
314 function TIBXSQLVAR.GetRelationName: AnsiString;
315 begin
316 result := strpas(FXSQLVAR^.relname);
317 end;
318
319 function TIBXSQLVAR.GetScale: integer;
320 begin
321 if GetSQLType = SQL_BLOB then
322 result := 0
323 else
324 result := FXSQLVAR^.sqlscale;
325 end;
326
327 function TIBXSQLVAR.GetCharSetID: cardinal;
328 begin
329 result := 0;
330 case SQLType of
331 SQL_VARYING, SQL_TEXT:
332 {see http://firebirdsql.org/rlsnotesh/rlsnotes210.html}
333 result := FXSQLVAR^.sqlsubtype and $FF;
334
335 SQL_BLOB:
336 if (SQLSubType = 1) then
337 {see http://firebirdsql.org/rlsnotesh/rlsnotes210.html}
338 result := FXSQLVAR^.sqlscale;
339
340 SQL_ARRAY:
341 if (GetRelationName <> '') and (GetFieldName <> '') then
342 result := GetArrayMetaData.GetCharSetID;
343 end;
344 end;
345
346 function TIBXSQLVAR.GetCodePage: TSystemCodePage;
347 begin
348 result := CP_NONE;
349 with FirebirdClientAPI do
350 CharSetID2CodePage(GetCharSetID,result);
351 end;
352
353 function TIBXSQLVAR.GetIsNull: Boolean;
354 begin
355 result := IsNullable and (FNullIndicator = -1);
356 end;
357
358 function TIBXSQLVAR.GetIsNullable: boolean;
359 begin
360 result := (FXSQLVAR^.sqltype and 1 = 1);
361 end;
362
363 function TIBXSQLVAR.GetSQLData: PByte;
364 begin
365 Result := FXSQLVAR^.sqldata;
366 end;
367
368 function TIBXSQLVAR.GetDataLength: cardinal;
369 begin
370 Result := FXSQLVAR^.sqllen;
371 end;
372
373 function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
374 begin
375 if GetSQLType <> SQL_ARRAY then
376 IBError(ibxeInvalidDataConversion,[nil]);
377
378 if FArrayMetaData = nil then
379 FArrayMetaData := TFB25ArrayMetaData.Create(FStatement.GetAttachment as TFB25Attachment,
380 FStatement.GetTransaction as TFB25Transaction,
381 GetRelationName,GetFieldName);
382 Result := FArrayMetaData;
383 end;
384
385 function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
386 begin
387 if GetSQLType <> SQL_BLOB then
388 IBError(ibxeInvalidDataConversion,[nil]);
389
390 if FBlobMetaData = nil then
391 FBlobMetaData := TFB25BlobMetaData.Create(FStatement.GetAttachment as TFB25Attachment,
392 FStatement.GetTransaction as TFB25Transaction,
393 GetRelationName,GetFieldName,GetSubType);
394 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
395 Result := FBlobMetaData;
396 end;
397
398 function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
399 begin
400 if SQLType <> SQL_ARRAY then
401 IBError(ibxeInvalidDataConversion,[nil]);
402
403 if IsNull then
404 Result := nil
405 else
406 begin
407 if FArray = nil then
408 FArray := TFB25Array.Create(FStatement.GetAttachment as TFB25Attachment,
409 TIBXSQLDA(Parent).GetTransaction,
410 GetArrayMetaData,Array_ID);
411 Result := FArray;
412 end;
413 end;
414
415 function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
416 begin
417 if FBlob <> nil then
418 Result := FBlob
419 else
420 begin
421 if SQLType <> SQL_BLOB then
422 IBError(ibxeInvalidDataConversion, [nil]);
423 if IsNull then
424 Result := nil
425 else
426 Result := TFB25Blob.Create(FStatement.GetAttachment as TFB25Attachment,
427 TIBXSQLDA(Parent).GetTransaction,
428 GetBlobMetaData,
429 Blob_ID,BPB);
430 FBlob := Result;
431 end;
432 end;
433
434 function TIBXSQLVAR.CreateBlob: IBlob;
435 begin
436 Result := TFB25Blob.Create(FStatement.GetAttachment as TFB25Attachment,
437 FStatement.GetTransaction as TFB25Transaction,GetSubType,GetCharSetID,nil);
438 end;
439
440 procedure TIBXSQLVAR.Initialize;
441 begin
442 inherited Initialize;
443 FOwnsSQLData := true;
444 with FirebirdClientAPI, FXSQLVar^ do
445 begin
446 case sqltype and (not 1) of
447 SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
448 SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
449 SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
450 if (sqllen = 0) then
451 { Make sure you get a valid pointer anyway
452 select '' from foo }
453 IBAlloc(sqldata, 0, 1)
454 else
455 IBAlloc(sqldata, 0, sqllen)
456 end;
457 SQL_VARYING: begin
458 IBAlloc(sqldata, 0, sqllen + 2);
459 end;
460 else
461 IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
462 end;
463 if (sqltype and 1 = 1) then
464 begin
465 sqlInd := @FNullIndicator;
466 FNullIndicator := -1;
467 end
468 else
469 sqlInd := nil;
470 end;
471 end;
472
473 procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
474 begin
475 if Value then
476 begin
477 IsNullable := true;
478 FNullIndicator := -1;
479 Changed;
480 end
481 else
482 if ((not Value) and IsNullable) then
483 begin
484 FNullIndicator := 0;
485 Changed;
486 end;
487 end;
488
489 procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
490 begin
491 if (Value <> IsNullable) then
492 begin
493 if Value then
494 begin
495 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype or 1;
496 FNullIndicator := 0;
497 FXSQLVAR^.sqlInd := @FNullIndicator;
498 end
499 else
500 begin
501 FXSQLVAR^.sqltype := FXSQLVAR^.sqltype and (not 1);
502 FXSQLVAR^.sqlind := nil;
503 end;
504 end;
505 end;
506
507 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
508 begin
509 if FOwnsSQLData then
510 FreeMem(FXSQLVAR^.sqldata);
511 FXSQLVAR^.sqldata := AValue;
512 FXSQLVAR^.sqllen := len;
513 FOwnsSQLData := false;
514 end;
515
516 procedure TIBXSQLVAR.SetScale(aValue: integer);
517 begin
518 FXSQLVAR^.sqlscale := aValue;
519 end;
520
521 procedure TIBXSQLVAR.SetDataLength(len: cardinal);
522 begin
523 if not FOwnsSQLData then
524 FXSQLVAR^.sqldata := nil;
525 FXSQLVAR^.sqllen := len;
526 with FirebirdClientAPI do
527 IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
528 FOwnsSQLData := true;
529 end;
530
531 procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
532 begin
533 FXSQLVAR^.sqltype := aValue or (FXSQLVAR^.sqltype and 1);
534 end;
535
536 procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
537 begin
538 if aValue <> GetCharSetID then
539 case SQLType of
540 SQL_VARYING, SQL_TEXT:
541 FXSQLVAR^.sqlsubtype := (aValue and $FF) or (FXSQLVAR^.sqlsubtype and not $FF);
542
543 SQL_BLOB,
544 SQL_ARRAY:
545 IBError(ibxeInvalidDataConversion,[nil]);
546 end;
547 end;
548
549 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
550 begin
551 inherited Create(aParent,aIndex);
552 FStatement := aParent.Statement;
553 end;
554
555 procedure TIBXSQLVAR.FreeSQLData;
556 begin
557 if FOwnsSQLData then
558 FreeMem(FXSQLVAR^.sqldata);
559 FXSQLVAR^.sqldata := nil;
560 FOwnsSQLData := true;
561 end;
562
563 procedure TIBXSQLVAR.RowChange;
564 begin
565 inherited RowChange;
566 FBlob := nil;
567 FArray := nil;
568 end;
569
570
571 { TResultSet }
572
573 constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
574 begin
575 inherited Create(aResults);
576 FResults := aResults;
577 FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
578 end;
579
580 destructor TResultSet.Destroy;
581 begin
582 Close;
583 inherited Destroy;
584 end;
585
586 function TResultSet.FetchNext: boolean;
587 var i: integer;
588 begin
589 CheckActive;
590 Result := FResults.FStatement.FetchNext;
591 if Result then
592 for i := 0 to getCount - 1 do
593 FResults.Column[i].RowChange;
594 end;
595
596 function TResultSet.GetCursorName: AnsiString;
597 begin
598 Result := FResults.FStatement.FCursor;
599 end;
600
601 function TResultSet.GetTransaction: ITransaction;
602 begin
603 Result := FResults.GetTransaction;
604 end;
605
606 function TResultSet.IsEof: boolean;
607 begin
608 Result := FResults.FStatement.FEof;
609 end;
610
611 procedure TResultSet.Close;
612 begin
613 if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
614 FResults.FStatement.Close;
615 end;
616
617 { TIBXINPUTSQLDA }
618
619 procedure TIBXINPUTSQLDA.Bind;
620 begin
621 if Count = 0 then
622 Count := 1;
623 with Firebird25ClientAPI do
624 begin
625 if (FXSQLDA <> nil) then
626 if isc_dsql_describe_bind(StatusVector, @(FStatement.Handle), FStatement.SQLDialect,
627 FXSQLDA) > 0 then
628 IBDataBaseError;
629
630 if FXSQLDA^.sqld > FXSQLDA^.sqln then
631 begin
632 Count := FXSQLDA^.sqld;
633 if isc_dsql_describe_bind(StatusVector, @(FStatement.Handle), FStatement.SQLDialect,
634 FXSQLDA) > 0 then
635 IBDataBaseError;
636 end
637 else
638 if FXSQLDA^.sqld = 0 then
639 Count := 0;
640 end;
641 Initialize;
642 end;
643
644 function TIBXINPUTSQLDA.IsInputDataArea: boolean;
645 begin
646 Result := true;
647 end;
648
649 { TIBXOUTPUTSQLDA }
650
651 procedure TIBXOUTPUTSQLDA.Bind;
652 begin
653 { Allocate an initial output descriptor (with one column) }
654 Count := 1;
655 with Firebird25ClientAPI do
656 begin
657 { Using isc_dsql_describe, get the right size for the columns... }
658 if isc_dsql_describe(StatusVector, @(FStatement.Handle), FStatement.SQLDialect, FXSQLDA) > 0 then
659 IBDataBaseError;
660
661 if FXSQLDA^.sqld > FXSQLDA^.sqln then
662 begin
663 Count := FXSQLDA^.sqld;
664 if isc_dsql_describe(StatusVector, @(FStatement.Handle), FStatement.SQLDialect, FXSQLDA) > 0 then
665 IBDataBaseError;
666 end
667 else
668 if FXSQLDA^.sqld = 0 then
669 Count := 0;
670 end;
671 Initialize;
672 SetUniqueRelationName;
673 end;
674
675 function TIBXOUTPUTSQLDA.GetTransaction: TFB25Transaction;
676 begin
677 Result := FTransaction;
678 end;
679
680 procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull:boolean; var len: short;
681 var data: PByte);
682 begin
683 with TIBXSQLVAR(Column[index]), FXSQLVAR^ do
684 begin
685 aIsNull := (sqltype and 1 = 1) and (FNullIndicator = -1);
686 data := sqldata;
687 len := sqllen;
688 if not IsNull and ((sqltype and (not 1)) = SQL_VARYING) then
689 begin
690 with FirebirdClientAPI do
691 len := DecodeInteger(data,2);
692 Inc(data,2);
693 end;
694 end;
695 end;
696
697 function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
698 begin
699 Result := false;
700 end;
701
702 { TIBXSQLDA }
703 constructor TIBXSQLDA.Create(aStatement: TFB25Statement);
704 begin
705 inherited Create;
706 FStatement := aStatement;
707 FSize := 0;
708 // writeln('Creating ',ClassName);
709 end;
710
711 destructor TIBXSQLDA.Destroy;
712 begin
713 FreeXSQLDA;
714 // writeln('Destroying ',ClassName);
715 inherited Destroy;
716 end;
717
718 function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
719 begin
720 Result := false;
721 case Request of
722 ssPrepared:
723 Result := FStatement.IsPrepared;
724
725 ssExecuteResults:
726 Result :=FStatement.FSingleResults;
727
728 ssCursorOpen:
729 Result := FStatement.FOpen;
730
731 ssBOF:
732 Result := FStatement.FBOF;
733
734 ssEOF:
735 Result := FStatement.FEOF;
736 end;
737 end;
738
739 function TIBXSQLDA.ColumnsInUseCount: integer;
740 begin
741 Result := FCount;
742 end;
743
744 function TIBXSQLDA.GetRecordSize: Integer;
745 begin
746 result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
747 end;
748
749 function TIBXSQLDA.GetXSQLDA: PXSQLDA;
750 begin
751 result := FXSQLDA;
752 end;
753
754 function TIBXSQLDA.GetTransactionSeqNo: integer;
755 begin
756 Result := FTransactionSeqNo;
757 end;
758
759 procedure TIBXSQLDA.Initialize;
760 begin
761 if FXSQLDA <> nil then
762 inherited Initialize;
763 end;
764
765 function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
766 begin
767 Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
768 if Result then
769 ChangeSeqNo := FStatement.ChangeSeqNo;
770 end;
771
772 function TIBXSQLDA.GetTransaction: TFB25Transaction;
773 begin
774 Result := FStatement.GetTransaction as TFB25Transaction;
775 end;
776
777 procedure TIBXSQLDA.SetCount(Value: Integer);
778 var
779 i, OldSize: Integer;
780 p : PXSQLVAR;
781 begin
782 FCount := Value;
783 if FCount = 0 then
784 FUniqueRelationName := ''
785 else
786 begin
787 if FSize > 0 then
788 OldSize := XSQLDA_LENGTH(FSize)
789 else
790 OldSize := 0;
791 if Count > FSize then
792 begin
793 Firebird25ClientAPI.IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(Count));
794 SetLength(FColumnList, FCount);
795 FXSQLDA^.version := SQLDA_VERSION1;
796 p := @FXSQLDA^.sqlvar[0];
797 for i := 0 to Count - 1 do
798 begin
799 if i >= FSize then
800 FColumnList[i] := TIBXSQLVAR.Create(self,i);
801 TIBXSQLVAR(Column[i]).FXSQLVAR := p;
802 p := Pointer(PAnsiChar(p) + sizeof(FXSQLDA^.sqlvar));
803 end;
804 FSize := inherited Count;
805 end;
806 if FSize > 0 then
807 begin
808 FXSQLDA^.sqln := Value;
809 FXSQLDA^.sqld := Value;
810 end;
811 end;
812 end;
813
814 procedure TIBXSQLDA.FreeXSQLDA;
815 var i: integer;
816 begin
817 if FXSQLDA <> nil then
818 begin
819 // writeln('SQLDA Cleanup');
820 for i := 0 to Count - 1 do
821 TIBXSQLVAR(Column[i]).FreeSQLData;
822 FreeMem(FXSQLDA);
823 FXSQLDA := nil;
824 end;
825 for i := 0 to FSize - 1 do
826 TIBXSQLVAR(Column[i]).Free;
827 SetLength(FColumnList,0);
828 FSize := 0;
829 end;
830
831 function TIBXSQLDA.GetStatement: IStatement;
832 begin
833 Result := FStatement;
834 end;
835
836 function TIBXSQLDA.GetPrepareSeqNo: integer;
837 begin
838 Result := FStatement.FPrepareSeqNo;
839 end;
840
841 { TFB25Statement }
842
843 procedure TFB25Statement.GetPerfCounters(var counters: TPerfStatistics);
844 var DBInfo: IDBInformation;
845 i: integer;
846 {$IFDEF UNIX}
847 times: tms;
848 {$ENDIF}
849 begin
850 {$IFDEF UNIX}
851 FpTimes(times);
852 counters[psUserTime] := times.tms_utime;
853 {$ELSE}
854 counters[psUserTime] := 0;
855 {$ENDIF}
856 counters[psRealTime] := TimeStampToMSecs(DateTimeToTimeStamp(Now));
857
858 DBInfo := GetAttachment.GetDBInformation([isc_info_reads,isc_info_writes,
859 isc_info_fetches, isc_info_num_buffers, isc_info_current_memory,
860 isc_info_max_memory]);
861 if DBInfo <> nil then
862 begin
863 for i := 0 to DBInfo.Count - 1 do
864 with DBInfo[i] do
865 case getItemType of
866 isc_info_reads:
867 counters[psReads] := AsInteger;
868 isc_info_writes:
869 counters[psWrites] := AsInteger;
870 isc_info_fetches:
871 counters[psFetches] := AsInteger;
872 isc_info_num_buffers:
873 counters[psBuffers] := AsInteger;
874 isc_info_current_memory:
875 counters[psCurrentMemory] := AsInteger;
876 isc_info_max_memory:
877 counters[psMaxMemory] := AsInteger;
878 end;
879 end;
880 end;
881
882 procedure TFB25Statement.CheckHandle;
883 begin
884 if FHandle = nil then
885 IBError(ibxeInvalidStatementHandle,[nil]);
886 end;
887
888 procedure TFB25Statement.GetDsqlInfo(info_request: byte; buffer: ISQLInfoResults
889 );
890 begin
891 with Firebird25ClientAPI, buffer as TSQLInfoResultsBuffer do
892 if isc_dsql_sql_info(StatusVector, @(FHandle), 1, @info_request,
893 GetBufSize, Buffer) > 0 then
894 IBDatabaseError;
895 end;
896
897 procedure TFB25Statement.InternalPrepare;
898 var
899 RB: ISQLInfoResults;
900 TRHandle: TISC_TR_HANDLE;
901 begin
902 if FPrepared then
903 Exit;
904 if (FSQL = '') then
905 IBError(ibxeEmptyQuery, [nil]);
906 try
907 CheckTransaction(FTransactionIntf);
908 with Firebird25ClientAPI do
909 begin
910 Call(isc_dsql_alloc_statement2(StatusVector, @(FDBHandle),
911 @FHandle), True);
912 TRHandle := (FTransactionIntf as TFB25Transaction).Handle;
913 if FHasParamNames then
914 begin
915 if FProcessedSQL = '' then
916 FSQLParams.PreprocessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
917 Call(isc_dsql_prepare(StatusVector, @(TRHandle), @FHandle, 0,
918 PAnsiChar(FProcessedSQL), FSQLDialect, nil), True);
919 end
920 else
921 Call(isc_dsql_prepare(StatusVector, @(TRHandle), @FHandle, 0,
922 PAnsiChar(FSQL), FSQLDialect, nil), True);
923 end;
924 { After preparing the statement, query the stmt type and possibly
925 create a FSQLRecord "holder" }
926 { Get the type of the statement }
927 RB := GetDsqlInfo(isc_info_sql_stmt_type);
928 if RB.Count > 0 then
929 FSQLStatementType := TIBSQLStatementTypes(RB[0].GetAsInteger)
930 else
931 FSQLStatementType := SQLUnknown;
932
933 case FSQLStatementType of
934 SQLGetSegment,
935 SQLPutSegment,
936 SQLStartTransaction: begin
937 FreeHandle;
938 IBError(ibxeNotPermitted, [nil]);
939 end;
940 SQLCommit,
941 SQLRollback,
942 SQLDDL, SQLSetGenerator,
943 SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
944 SQLExecProcedure:
945 begin
946 {set up input sqlda}
947 FSQLParams.Bind;
948
949 {setup output sqlda}
950 if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
951 SQLExecProcedure] then
952 FSQLRecord.Bind;
953 end;
954 end;
955 except
956 on E: Exception do begin
957 if (FHandle <> nil) then
958 FreeHandle;
959 if E is EIBInterBaseError then
960 raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
961 EIBInterBaseError(E).IBErrorCode,
962 EIBInterBaseError(E).Message +
963 sSQLErrorSeparator + FSQL)
964 else
965 raise;
966 end;
967 end;
968 FPrepared := true;
969 FSingleResults := false;
970 if RetainInterfaces then
971 begin
972 SetRetainInterfaces(false);
973 SetRetainInterfaces(true);
974 end;
975 Inc(FPrepareSeqNo);
976 Inc(FChangeSeqNo);
977 with FTransactionIntf as TFB25Transaction do
978 begin
979 FSQLParams.FTransactionSeqNo := TransactionSeqNo;
980 FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
981 end;
982 end;
983
984 function TFB25Statement.InternalExecute(aTransaction: ITransaction): IResults;
985 var TRHandle: TISC_TR_HANDLE;
986 begin
987 Result := nil;
988 FBOF := false;
989 FEOF := false;
990 FSingleResults := false;
991 CheckTransaction(aTransaction);
992 if not FPrepared then
993 InternalPrepare;
994 CheckHandle;
995 if aTransaction <> FTransactionIntf then
996 AddMonitor(aTransaction as TFB25Transaction);
997 if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB25transaction).TransactionSeqNo) then
998 IBError(ibxeInterfaceOutofDate,[nil]);
999
1000 try
1001 TRHandle := (aTransaction as TFB25Transaction).Handle;
1002 with Firebird25ClientAPI do
1003 begin
1004 if FCollectStatistics then
1005 GetPerfCounters(FBeforeStats);
1006
1007 case FSQLStatementType of
1008 SQLSelect:
1009 IBError(ibxeIsAExecuteProcedure,[]);
1010
1011 SQLExecProcedure:
1012 begin
1013 Call(isc_dsql_execute2(StatusVector,
1014 @(TRHandle),
1015 @FHandle,
1016 SQLDialect,
1017 FSQLParams.AsXSQLDA,
1018 FSQLRecord.AsXSQLDA), True);
1019 Result := TResults.Create(FSQLRecord);
1020 FSingleResults := true;
1021 end
1022 else
1023 Call(isc_dsql_execute(StatusVector,
1024 @(TRHandle),
1025 @FHandle,
1026 SQLDialect,
1027 FSQLParams.AsXSQLDA), True);
1028
1029 end;
1030 if FCollectStatistics then
1031 begin
1032 GetPerfCounters(FAfterStats);
1033 FStatisticsAvailable := true;
1034 end;
1035 end;
1036 finally
1037 if aTransaction <> FTransactionIntf then
1038 RemoveMonitor(aTransaction as TFB25Transaction);
1039 end;
1040 FExecTransactionIntf := aTransaction;
1041 Inc(FChangeSeqNo);
1042 end;
1043
1044 function TFB25Statement.InternalOpenCursor(aTransaction: ITransaction
1045 ): IResultSet;
1046 var TRHandle: TISC_TR_HANDLE;
1047 GUID : TGUID;
1048 begin
1049 if FSQLStatementType <> SQLSelect then
1050 IBError(ibxeIsASelectStatement,[]);
1051
1052 CheckTransaction(aTransaction);
1053 if not FPrepared then
1054 InternalPrepare;
1055 CheckHandle;
1056 if aTransaction <> FTransactionIntf then
1057 AddMonitor(aTransaction as TFB25Transaction);
1058 if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB25transaction).TransactionSeqNo) then
1059 IBError(ibxeInterfaceOutofDate,[nil]);
1060
1061 with Firebird25ClientAPI do
1062 begin
1063 if FCollectStatistics then
1064 GetPerfCounters(FBeforeStats);
1065
1066 TRHandle := (aTransaction as TFB25Transaction).Handle;
1067 Call(isc_dsql_execute2(StatusVector,
1068 @(TRHandle),
1069 @FHandle,
1070 SQLDialect,
1071 FSQLParams.AsXSQLDA,
1072 nil), True);
1073 if FCursor = '' then
1074 begin
1075 CreateGuid(GUID);
1076 FCursor := GUIDToString(GUID);
1077 Call(
1078 isc_dsql_set_cursor_name(StatusVector, @FHandle, PAnsiChar(FCursor), 0),
1079 True);
1080 end;
1081
1082 if FCollectStatistics then
1083 begin
1084 GetPerfCounters(FAfterStats);
1085 FStatisticsAvailable := true;
1086 end;
1087 end;
1088 Inc(FCursorSeqNo);
1089 FSingleResults := false;
1090 FOpen := True;
1091 FExecTransactionIntf := aTransaction;
1092 FBOF := true;
1093 FEOF := false;
1094 FSQLRecord.FTransaction := aTransaction as TFB25Transaction;
1095 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1096 Result := TResultSet.Create(FSQLRecord);
1097 Inc(FChangeSeqNo);
1098 end;
1099
1100 procedure TFB25Statement.FreeHandle;
1101 var
1102 isc_res: ISC_STATUS;
1103 begin
1104 Close;
1105 ReleaseInterfaces;
1106 try
1107 if FHandle <> nil then
1108 with Firebird25ClientAPI do
1109 begin
1110 isc_res :=
1111 Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
1112 if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
1113 IBDataBaseError;
1114 end;
1115 finally
1116 FHandle := nil;
1117 FCursor := '';
1118 FPrepared := false;
1119 end;
1120 end;
1121
1122 procedure TFB25Statement.InternalClose(Force: boolean);
1123 var
1124 isc_res: ISC_STATUS;
1125 begin
1126 if (FHandle <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1127 try
1128 with Firebird25ClientAPI do
1129 begin
1130 isc_res := Call(
1131 isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
1132 False);
1133 if not Force and (StatusVector^ = 1) and (isc_res > 0) and
1134 not getStatus.CheckStatusVector(
1135 [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
1136 IBDatabaseError;
1137 end;
1138 finally
1139 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB25Transaction)) then
1140 RemoveMonitor(FSQLRecord.FTransaction);
1141 FOpen := False;
1142 FExecTransactionIntf := nil;
1143 FSQLRecord.FTransaction := nil;
1144 Inc(FChangeSeqNo);
1145 end;
1146 end;
1147
1148 constructor TFB25Statement.Create(Attachment: TFB25Attachment;
1149 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1150 begin
1151 inherited Create(Attachment,Transaction,sql,aSQLDialect);
1152 FDBHandle := Attachment.Handle;
1153 FSQLParams := TIBXINPUTSQLDA.Create(self);
1154 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1155 InternalPrepare;
1156 end;
1157
1158 constructor TFB25Statement.CreateWithParameterNames(Attachment: TFB25Attachment;
1159 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
1160 GenerateParamNames: boolean);
1161 begin
1162 inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1163 FDBHandle := Attachment.Handle;
1164 FSQLParams := TIBXINPUTSQLDA.Create(self);
1165 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1166 InternalPrepare;
1167 end;
1168
1169 destructor TFB25Statement.Destroy;
1170 begin
1171 inherited Destroy;
1172 if assigned(FSQLParams) then FSQLParams.Free;
1173 if assigned(FSQLRecord) then FSQLRecord.Free;
1174 end;
1175
1176 function TFB25Statement.FetchNext: boolean;
1177 var
1178 fetch_res: ISC_STATUS;
1179 begin
1180 result := false;
1181 if not FOpen then
1182 IBError(ibxeSQLClosed, [nil]);
1183 if FEOF then
1184 IBError(ibxeEOF,[nil]);
1185
1186 with Firebird25ClientAPI do
1187 begin
1188 { Go to the next record... }
1189 fetch_res :=
1190 Call(isc_dsql_fetch(StatusVector, @FHandle, SQLDialect, FSQLRecord.AsXSQLDA), False);
1191 if (fetch_res = 100) or (getStatus.CheckStatusVector([isc_dsql_cursor_err])) then
1192 begin
1193 FBOF := false;
1194 FEOF := true;
1195 Exit; {End of File}
1196 end
1197 else
1198 if (fetch_res > 0) then
1199 begin
1200 try
1201 IBDataBaseError;
1202 except
1203 Close;
1204 raise;
1205 end;
1206 end
1207 else
1208 begin
1209 FBOF := false;
1210 result := true;
1211 end;
1212 end;
1213 FSQLRecord.RowChange;
1214 if FEOF then
1215 Inc(FChangeSeqNo);
1216 end;
1217
1218 function TFB25Statement.GetSQLParams: ISQLParams;
1219 begin
1220 CheckHandle;
1221 if not HasInterface(0) then
1222 AddInterface(0,TSQLParams.Create(FSQLParams));
1223 Result := TSQLParams(GetInterface(0));
1224 end;
1225
1226 function TFB25Statement.GetMetaData: IMetaData;
1227 begin
1228 CheckHandle;
1229 if not HasInterface(1) then
1230 AddInterface(1, TMetaData.Create(FSQLRecord));
1231 Result := TMetaData(GetInterface(1));
1232 end;
1233
1234 function TFB25Statement.GetPlan: AnsiString;
1235 var
1236 RB: ISQLInfoResults;
1237 begin
1238 if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1239 {TODO: SQLExecProcedure, }
1240 SQLUpdate, SQLDelete])) then
1241 result := ''
1242 else
1243 begin
1244 RB := TSQLInfoResultsBuffer.Create(4*4096);
1245 GetDsqlInfo(isc_info_sql_get_plan,RB);
1246 if RB.Count > 0 then
1247 Result := RB[0].GetAsString;
1248 end;
1249 end;
1250
1251 function TFB25Statement.CreateBlob(column: TColumnMetaData): IBlob;
1252 begin
1253 if assigned(column) and (column.SQLType <> SQL_Blob) then
1254 IBError(ibxeNotABlob,[nil]);
1255 Result := TFB25Blob.Create(GetAttachment as TFB25Attachment,FExecTransactionIntf as TFB25Transaction,
1256 column.GetBlobMetaData,nil);
1257 end;
1258
1259 function TFB25Statement.CreateArray(column: TColumnMetaData): IArray;
1260 begin
1261 if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1262 IBError(ibxeNotAnArray,[nil]);
1263 Result := TFB25Array.Create(GetAttachment as TFB25Attachment,FExecTransactionIntf as TFB25Transaction,
1264 column.GetArrayMetaData);
1265 end;
1266
1267 procedure TFB25Statement.SetRetainInterfaces(aValue: boolean);
1268 begin
1269 inherited SetRetainInterfaces(aValue);
1270 if HasInterface(1) then
1271 TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1272 if HasInterface(0) then
1273 TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1274 end;
1275
1276 function TFB25Statement.IsPrepared: boolean;
1277 begin
1278 Result := FHandle <> nil;
1279 end;
1280
1281 end.
1282