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: 47
Committed: Mon Jan 9 15:31:51 2017 UTC (7 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 37446 byte(s)
Log Message:
Committing updates for Release R2-0-1

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