ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/3.0/FB30Statement.pas
Revision: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (8 years ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/fbintf/client/3.0/FB30Statement.pas
File size: 35998 byte(s)
Log Message:
Committing updates for Release R2-0-0

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