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