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: 349
Committed: Mon Oct 18 08:39:40 2021 UTC (3 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 49828 byte(s)
Log Message:
FIxes Merged

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 TFB30Statement = class;
82 TIBXSQLDA = class;
83
84 { TIBXSQLVAR }
85
86 TIBXSQLVAR = class(TSQLVarData)
87 private
88 FStatement: TFB30Statement;
89 FFirebird30ClientAPI: TFB30ClientAPI;
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 FMetadataSize: integer;
104 FNullable: boolean;
105 FScale: integer;
106 FCharSetID: cardinal;
107 FRelationName: AnsiString;
108 FFieldName: AnsiString;
109
110 protected
111 function CanChangeSQLType: boolean;
112 function GetSQLType: cardinal; override;
113 function GetSubtype: integer; override;
114 function GetAliasName: AnsiString; override;
115 function GetFieldName: AnsiString; override;
116 function GetOwnerName: AnsiString; override;
117 function GetRelationName: AnsiString; override;
118 function GetScale: integer; override;
119 function GetCharSetID: cardinal; override;
120 function GetCodePage: TSystemCodePage; override;
121 function GetCharSetWidth: integer; override;
122 function GetIsNull: Boolean; override;
123 function GetIsNullable: boolean; override;
124 function GetSQLData: PByte; override;
125 function GetDataLength: cardinal; override;
126 function GetSize: cardinal; override;
127 function GetAttachment: IAttachment; override;
128 function GetDefaultTextSQLType: cardinal; override;
129 procedure SetIsNull(Value: Boolean); override;
130 procedure SetIsNullable(Value: Boolean); override;
131 procedure SetSQLData(AValue: PByte; len: cardinal); override;
132 procedure SetScale(aValue: integer); override;
133 procedure SetDataLength(len: cardinal); override;
134 procedure SetSQLType(aValue: cardinal); override;
135 procedure SetCharSetID(aValue: cardinal); override;
136 procedure SetMetaSize(aValue: cardinal); override;
137 public
138 constructor Create(aParent: TIBXSQLDA; aIndex: integer);
139 procedure Changed; override;
140 procedure InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
141 procedure ColumnSQLDataInit;
142 procedure RowChange; override;
143 procedure FreeSQLData;
144 function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
145 function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
146 function GetArrayMetaData: IArrayMetaData; override;
147 function GetBlobMetaData: IBlobMetaData; override;
148 function CreateBlob: IBlob; override;
149 end;
150
151 { TIBXSQLDA }
152
153 TIBXSQLDA = class(TSQLDataArea)
154 private
155 FCount: Integer; {Columns in use - may be less than inherited columns}
156 FSize: Integer; {Number of TIBXSQLVARs in column list}
157 FMetaData: Firebird.IMessageMetadata;
158 FTransactionSeqNo: integer;
159 protected
160 FStatement: TFB30Statement;
161 FFirebird30ClientAPI: TFB30ClientAPI;
162 function GetTransactionSeqNo: integer; override;
163 procedure FreeXSQLDA; virtual;
164 function GetStatement: IStatement; override;
165 function GetPrepareSeqNo: integer; override;
166 procedure SetCount(Value: Integer); override;
167 public
168 constructor Create(aStatement: TFB30Statement);
169 destructor Destroy; override;
170 procedure Changed; virtual;
171 function CheckStatementStatus(Request: TStatementStatus): boolean; override;
172 function ColumnsInUseCount: integer; override;
173 function GetTransaction: TFB30Transaction; virtual;
174 procedure Initialize; override;
175 function StateChanged(var ChangeSeqNo: integer): boolean; override;
176 function CanChangeMetaData: boolean; override;
177 property MetaData: Firebird.IMessageMetadata read FMetaData;
178 property Count: Integer read FCount write SetCount;
179 property Statement: TFB30Statement read FStatement;
180 end;
181
182 { TIBXINPUTSQLDA }
183
184 TIBXINPUTSQLDA = class(TIBXSQLDA)
185 private
186 FMessageBuffer: PByte; {Message Buffer}
187 FMsgLength: integer; {Message Buffer length}
188 FCurMetaData: Firebird.IMessageMetadata;
189 procedure FreeMessageBuffer;
190 procedure FreeCurMetaData;
191 function GetMessageBuffer: PByte;
192 function GetMetaData: Firebird.IMessageMetadata;
193 function GetModified: Boolean;
194 function GetMsgLength: integer;
195 procedure BuildMetadata;
196 procedure PackBuffer;
197 protected
198 procedure FreeXSQLDA; override;
199 public
200 constructor Create(aStatement: TFB30Statement);
201 destructor Destroy; override;
202 procedure Bind(aMetaData: Firebird.IMessageMetadata);
203 procedure Changed; override;
204 procedure ReInitialise;
205 function IsInputDataArea: boolean; override;
206 property MetaData: Firebird.IMessageMetadata read GetMetaData;
207 property MessageBuffer: PByte read GetMessageBuffer;
208 property MsgLength: integer read GetMsgLength;
209 end;
210
211 { TIBXOUTPUTSQLDA }
212
213 TIBXOUTPUTSQLDA = class(TIBXSQLDA)
214 private
215 FTransaction: TFB30Transaction; {transaction used to execute the statement}
216 FMessageBuffer: PByte; {Message Buffer}
217 FMsgLength: integer; {Message Buffer length}
218 protected
219 procedure FreeXSQLDA; override;
220 public
221 procedure Bind(aMetaData: Firebird.IMessageMetadata);
222 procedure GetData(index: integer; var aIsNull: boolean; var len: short;
223 var data: PByte); override;
224 function IsInputDataArea: boolean; override;
225 property MessageBuffer: PByte read FMessageBuffer;
226 property MsgLength: integer read FMsgLength;
227 end;
228
229 { TResultSet }
230
231 TResultSet = class(TResults,IResultSet)
232 private
233 FResults: TIBXOUTPUTSQLDA;
234 FCursorSeqNo: integer;
235 public
236 constructor Create(aResults: TIBXOUTPUTSQLDA);
237 destructor Destroy; override;
238 {IResultSet}
239 function FetchNext: boolean;
240 function GetCursorName: AnsiString;
241 function GetTransaction: ITransaction; override;
242 function IsEof: boolean;
243 procedure Close;
244 end;
245
246 { TBatchCompletion }
247
248 TBatchCompletion = class(TInterfaceOwner,IBatchCompletion)
249 private
250 FCompletionState: Firebird.IBatchCompletionState;
251 FFirebird30ClientAPI: TFB30ClientAPI;
252 public
253 constructor Create(api: TFB30ClientAPI; cs: IBatchCompletionState);
254 destructor Destroy; override;
255 {IBatchCompletion}
256 function getErrorStatus(var RowNo: integer; var status: IStatus): boolean;
257 function getTotalProcessed: cardinal;
258 function getState(updateNo: cardinal): TBatchCompletionState;
259 function getStatusMessage(updateNo: cardinal): AnsiString;
260 function getUpdated: integer;
261 end;
262
263 { TFB30Statement }
264
265 TFB30Statement = class(TFBStatement,IStatement)
266 private
267 FStatementIntf: Firebird.IStatement;
268 FFirebird30ClientAPI: TFB30ClientAPI;
269 FSQLParams: TIBXINPUTSQLDA;
270 FSQLRecord: TIBXOUTPUTSQLDA;
271 FResultSet: Firebird.IResultSet;
272 FCursorSeqNo: integer;
273 FBatch: Firebird.IBatch;
274 FBatchCompletion: IBatchCompletion;
275 FBatchRowCount: integer;
276 FBatchBufferSize: integer;
277 FBatchBufferUsed: integer;
278 protected
279 procedure CheckChangeBatchRowLimit; override;
280 procedure CheckHandle; override;
281 procedure CheckBatchModeAvailable;
282 procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
283 procedure InternalPrepare; override;
284 function InternalExecute(aTransaction: ITransaction): IResults; override;
285 function InternalOpenCursor(aTransaction: ITransaction): IResultSet; override;
286 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
287 procedure FreeHandle; override;
288 procedure InternalClose(Force: boolean); override;
289 function SavePerfStats(var Stats: TPerfStatistics): boolean;
290 public
291 constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
292 sql: AnsiString; aSQLDialect: integer);
293 constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
294 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false;
295 CaseSensitiveParams: boolean=false);
296 destructor Destroy; override;
297 function FetchNext: boolean;
298 property StatementIntf: Firebird.IStatement read FStatementIntf;
299
300 public
301 {IStatement}
302 function GetSQLParams: ISQLParams; override;
303 function GetMetaData: IMetaData; override;
304 function GetPlan: AnsiString;
305 function IsPrepared: boolean;
306 function CreateBlob(column: TColumnMetaData): IBlob; override;
307 function CreateArray(column: TColumnMetaData): IArray; override;
308 procedure SetRetainInterfaces(aValue: boolean); override;
309 function IsInBatchMode: boolean; override;
310 function HasBatchMode: boolean; override;
311 procedure AddToBatch; override;
312 function ExecuteBatch(aTransaction: ITransaction
313 ): IBatchCompletion; override;
314 procedure CancelBatch; override;
315 function GetBatchCompletion: IBatchCompletion; override;
316 end;
317
318 implementation
319
320 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
321
322 const
323 ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
324
325 { EIBBatchCompletionError }
326
327 { TBatchCompletion }
328
329 constructor TBatchCompletion.Create(api: TFB30ClientAPI;
330 cs: IBatchCompletionState);
331 begin
332 inherited Create;
333 FFirebird30ClientAPI := api;
334 FCompletionState := cs;
335 end;
336
337 destructor TBatchCompletion.Destroy;
338 begin
339 if FCompletionState <> nil then
340 begin
341 FCompletionState.dispose;
342 FCompletionState := nil;
343 end;
344 inherited Destroy;
345 end;
346
347 function TBatchCompletion.getErrorStatus(var RowNo: integer; var status: IStatus
348 ): boolean;
349 var i: integer;
350 upcount: cardinal;
351 state: integer;
352 FBStatus: Firebird.IStatus;
353 begin
354 Result := false;
355 RowNo := -1;
356 FBStatus := nil;
357 with FFirebird30ClientAPI do
358 begin
359 upcount := FCompletionState.getSize(StatusIntf);
360 Check4DataBaseError;
361 for i := 0 to upcount - 1 do
362 begin
363 state := FCompletionState.getState(StatusIntf,i);
364 if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
365 begin
366 RowNo := i+1;
367 FBStatus := MasterIntf.getStatus;
368 try
369 FCompletionState.getStatus(StatusIntf,FBStatus,i);
370 Check4DataBaseError;
371 except
372 FBStatus.dispose;
373 raise
374 end;
375 status := TFB30StatusObject.Create(FFirebird30ClientAPI,FBStatus,
376 Format(SBatchCompletionError,[RowNo]));
377 status.SetIBDataBaseErrorMessages(GetStatus.GetIBDataBaseErrorMessages);
378 Result := true;
379 break;
380 end;
381 end;
382 end;
383 end;
384
385 function TBatchCompletion.getTotalProcessed: cardinal;
386 begin
387 with FFirebird30ClientAPI do
388 begin
389 Result := FCompletionState.getsize(StatusIntf);
390 Check4DataBaseError;
391 end;
392 end;
393
394 function TBatchCompletion.getState(updateNo: cardinal): TBatchCompletionState;
395 var state: integer;
396 begin
397 with FFirebird30ClientAPI do
398 begin
399 state := FCompletionState.getState(StatusIntf,updateNo);
400 Check4DataBaseError;
401 case state of
402 Firebird.IBatchCompletionState.EXECUTE_FAILED:
403 Result := bcExecuteFailed;
404
405 Firebird.IBatchCompletionState.SUCCESS_NO_INFO:
406 Result := bcSuccessNoInfo;
407
408 else
409 Result := bcNoMoreErrors;
410 end;
411 end;
412 end;
413
414 function TBatchCompletion.getStatusMessage(updateNo: cardinal): AnsiString;
415 var status: Firebird.IStatus;
416 begin
417 with FFirebird30ClientAPI do
418 begin
419 status := MasterIntf.getStatus;
420 FCompletionState.getStatus(StatusIntf,status,updateNo);
421 Check4DataBaseError;
422 Result := FormatFBStatus(status);
423 end;
424 end;
425
426 function TBatchCompletion.getUpdated: integer;
427 var i: integer;
428 upcount: cardinal;
429 state: integer;
430 begin
431 Result := 0;
432 with FFirebird30ClientAPI do
433 begin
434 upcount := FCompletionState.getSize(StatusIntf);
435 Check4DataBaseError;
436 for i := 0 to upcount -1 do
437 begin
438 state := FCompletionState.getState(StatusIntf,i);
439 if state = Firebird.IBatchCompletionState.EXECUTE_FAILED then
440 break;
441 Inc(Result);
442 end;
443 end;
444 end;
445
446 { TIBXSQLVAR }
447
448 procedure TIBXSQLVAR.Changed;
449 begin
450 inherited Changed;
451 TIBXSQLDA(Parent).Changed;
452 end;
453
454 procedure TIBXSQLVAR.InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
455 begin
456 with FFirebird30ClientAPI do
457 begin
458 FSQLType := aMetaData.getType(StatusIntf,Index);
459 Check4DataBaseError;
460 if FSQLType = SQL_BLOB then
461 begin
462 FSQLSubType := aMetaData.getSubType(StatusIntf,Index);
463 Check4DataBaseError;
464 end
465 else
466 FSQLSubType := 0;
467 FDataLength := aMetaData.getLength(StatusIntf,Index);
468 Check4DataBaseError;
469 FMetadataSize := FDataLength;
470 FRelationName := strpas(aMetaData.getRelation(StatusIntf,Index));
471 Check4DataBaseError;
472 FFieldName := strpas(aMetaData.getField(StatusIntf,Index));
473 Check4DataBaseError;
474 FNullable := aMetaData.isNullable(StatusIntf,Index);
475 Check4DataBaseError;
476 FScale := aMetaData.getScale(StatusIntf,Index);
477 Check4DataBaseError;
478 FCharSetID := aMetaData.getCharSet(StatusIntf,Index) and $FF;
479 Check4DataBaseError;
480 end;
481 end;
482
483 procedure TIBXSQLVAR.ColumnSQLDataInit;
484 begin
485 FreeSQLData;
486 with FFirebird30ClientAPI do
487 begin
488 case SQLType of
489 SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
490 SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
491 SQL_LONG, SQL_INT64, SQL_INT128, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT,
492 SQL_TIMESTAMP_TZ, SQL_TIME_TZ, SQL_DEC_FIXED, SQL_DEC16, SQL_DEC34,
493 SQL_TIMESTAMP_TZ_EX, SQL_TIME_TZ_EX:
494 begin
495 if (FDataLength = 0) then
496 { Make sure you get a valid pointer anyway
497 select '' from foo }
498 IBAlloc(FSQLData, 0, 1)
499 else
500 IBAlloc(FSQLData, 0, FDataLength)
501 end;
502 SQL_VARYING:
503 IBAlloc(FSQLData, 0, FDataLength + 2);
504 else
505 IBError(ibxeUnknownSQLDataType, [SQLType and (not 1)])
506 end;
507 FOwnsSQLData := true;
508 FNullIndicator := -1;
509 end;
510 end;
511
512 function TIBXSQLVAR.CanChangeSQLType: boolean;
513 begin
514 Result := Parent.CanChangeMetaData;
515 end;
516
517 function TIBXSQLVAR.GetSQLType: cardinal;
518 begin
519 Result := FSQLType;
520 end;
521
522 function TIBXSQLVAR.GetSubtype: integer;
523 begin
524 Result := FSQLSubType;
525 end;
526
527 function TIBXSQLVAR.GetAliasName: AnsiString;
528 begin
529 with FFirebird30ClientAPI do
530 begin
531 result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
532 Check4DataBaseError;
533 end;
534 end;
535
536 function TIBXSQLVAR.GetFieldName: AnsiString;
537 begin
538 Result := FFieldName;
539 end;
540
541 function TIBXSQLVAR.GetOwnerName: AnsiString;
542 begin
543 with FFirebird30ClientAPI do
544 begin
545 result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
546 Check4DataBaseError;
547 end;
548 end;
549
550 function TIBXSQLVAR.GetRelationName: AnsiString;
551 begin
552 Result := FRelationName;
553 end;
554
555 function TIBXSQLVAR.GetScale: integer;
556 begin
557 Result := FScale;
558 end;
559
560 function TIBXSQLVAR.GetCharSetID: cardinal;
561 begin
562 result := 0; {NONE}
563 case SQLType of
564 SQL_VARYING, SQL_TEXT:
565 result := FCharSetID;
566
567 SQL_BLOB:
568 if (SQLSubType = 1) then
569 result := FCharSetID
570 else
571 result := 1; {OCTETS}
572
573 SQL_ARRAY:
574 if (FRelationName <> '') and (FFieldName <> '') then
575 result := GetArrayMetaData.GetCharSetID
576 else
577 result := FCharSetID;
578 end;
579 end;
580
581 function TIBXSQLVAR.GetCodePage: TSystemCodePage;
582 begin
583 result := CP_NONE;
584 with Statement.GetAttachment do
585 CharSetID2CodePage(GetCharSetID,result);
586 end;
587
588 function TIBXSQLVAR.GetCharSetWidth: integer;
589 begin
590 result := 1;
591 with Statement.GetAttachment DO
592 CharSetWidth(GetCharSetID,result);
593 end;
594
595 function TIBXSQLVAR.GetIsNull: Boolean;
596 begin
597 Result := IsNullable and (FSQLNullIndicator^ = -1);
598 end;
599
600 function TIBXSQLVAR.GetIsNullable: boolean;
601 begin
602 Result := FSQLNullIndicator <> nil;
603 end;
604
605 function TIBXSQLVAR.GetSQLData: PByte;
606 begin
607 Result := FSQLData;
608 end;
609
610 function TIBXSQLVAR.GetDataLength: cardinal;
611 begin
612 Result := FDataLength;
613 end;
614
615 function TIBXSQLVAR.GetSize: cardinal;
616 begin
617 Result := FMetadataSize;
618 end;
619
620 function TIBXSQLVAR.GetAttachment: IAttachment;
621 begin
622 Result := FStatement.GetAttachment;
623 end;
624
625 function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
626 begin
627 if GetSQLType <> SQL_ARRAY then
628 IBError(ibxeInvalidDataConversion,[nil]);
629
630 if FArrayMetaData = nil then
631 FArrayMetaData := TFB30ArrayMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
632 FStatement.GetTransaction as TFB30Transaction,
633 GetRelationName,GetFieldName);
634 Result := FArrayMetaData;
635 end;
636
637 function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
638 begin
639 if GetSQLType <> SQL_BLOB then
640 IBError(ibxeInvalidDataConversion,[nil]);
641
642 if FBlobMetaData = nil then
643 FBlobMetaData := TFB30BlobMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
644 FStatement.GetTransaction as TFB30Transaction,
645 GetRelationName,GetFieldName,
646 GetSubType);
647 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
648 Result := FBlobMetaData;
649 end;
650
651 procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
652 begin
653 if Value then
654 begin
655 IsNullable := true;
656 FNullIndicator := -1;
657 end
658 else
659 if IsNullable then
660 FNullIndicator := 0;
661 Changed;
662 end;
663
664 procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
665 begin
666 if Value = IsNullable then Exit;
667 if Value then
668 begin
669 FSQLNullIndicator := @FNullIndicator;
670 FNullIndicator := 0;
671 end
672 else
673 FSQLNullIndicator := nil;
674 Changed;
675 end;
676
677 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
678 begin
679 if FOwnsSQLData then
680 FreeMem(FSQLData);
681 FSQLData := AValue;
682 FDataLength := len;
683 FOwnsSQLData := false;
684 Changed;
685 end;
686
687 procedure TIBXSQLVAR.SetScale(aValue: integer);
688 begin
689 FScale := aValue;
690 Changed;
691 end;
692
693 procedure TIBXSQLVAR.SetDataLength(len: cardinal);
694 begin
695 if not FOwnsSQLData then
696 FSQLData := nil;
697 FDataLength := len;
698 with FFirebird30ClientAPI do
699 IBAlloc(FSQLData, 0, FDataLength);
700 FOwnsSQLData := true;
701 Changed;
702 end;
703
704 procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
705 begin
706 if (FSQLType <> aValue) and not CanChangeSQLType then
707 IBError(ibxeSQLTypeUnchangeable,[TSQLDataItem.GetSQLTypeName(FSQLType),TSQLDataItem.GetSQLTypeName(aValue)]);
708 FSQLType := aValue;
709 Changed;
710 end;
711
712 procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
713 begin
714 FCharSetID := aValue;
715 Changed;
716 end;
717
718 procedure TIBXSQLVAR.SetMetaSize(aValue: cardinal);
719 begin
720 if (aValue > FMetaDataSize) and not CanChangeSQLType then
721 IBError(ibxeCannotIncreaseMetadatasize,[FMetaDataSize,aValue]);
722 FMetaDataSize := aValue;
723 end;
724
725 function TIBXSQLVAR.GetDefaultTextSQLType: cardinal;
726 begin
727 Result := SQL_VARYING;
728 end;
729
730 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
731 begin
732 inherited Create(aParent,aIndex);
733 FStatement := aParent.Statement;
734 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
735 end;
736
737 procedure TIBXSQLVAR.RowChange;
738 begin
739 inherited;
740 FBlob := nil;
741 FArray := nil;
742 end;
743
744 procedure TIBXSQLVAR.FreeSQLData;
745 begin
746 if FOwnsSQLData then
747 FreeMem(FSQLData);
748 FSQLData := nil;
749 FOwnsSQLData := true;
750 end;
751
752 function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
753 begin
754 if SQLType <> SQL_ARRAY then
755 IBError(ibxeInvalidDataConversion,[nil]);
756
757 if IsNull then
758 Result := nil
759 else
760 begin
761 if FArray = nil then
762 FArray := TFB30Array.Create(FStatement.GetAttachment as TFB30Attachment,
763 TIBXSQLDA(Parent).GetTransaction,
764 GetArrayMetaData,Array_ID);
765 Result := FArray;
766 end;
767 end;
768
769 function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
770 begin
771 if FBlob <> nil then
772 Result := FBlob
773 else
774 begin
775 if SQLType <> SQL_BLOB then
776 IBError(ibxeInvalidDataConversion, [nil]);
777 if IsNull then
778 Result := nil
779 else
780 Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
781 TIBXSQLDA(Parent).GetTransaction,
782 GetBlobMetaData,
783 Blob_ID,BPB);
784 FBlob := Result;
785 end;
786 end;
787
788 function TIBXSQLVAR.CreateBlob: IBlob;
789 begin
790 Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
791 FStatement.GetTransaction as TFB30Transaction,
792 GetSubType,GetCharSetID,nil);
793 end;
794
795 { TResultSet }
796
797 constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
798 begin
799 inherited Create(aResults);
800 FResults := aResults;
801 FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
802 end;
803
804 destructor TResultSet.Destroy;
805 begin
806 Close;
807 inherited Destroy;
808 end;
809
810 function TResultSet.FetchNext: boolean;
811 var i: integer;
812 begin
813 CheckActive;
814 Result := FResults.FStatement.FetchNext;
815 if Result then
816 for i := 0 to getCount - 1 do
817 FResults.Column[i].RowChange;
818 end;
819
820 function TResultSet.GetCursorName: AnsiString;
821 begin
822 IBError(ibxeNotSupported,[nil]);
823 Result := '';
824 end;
825
826 function TResultSet.GetTransaction: ITransaction;
827 begin
828 Result := FResults.FTransaction;
829 end;
830
831 function TResultSet.IsEof: boolean;
832 begin
833 Result := FResults.FStatement.FEof;
834 end;
835
836 procedure TResultSet.Close;
837 begin
838 if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
839 FResults.FStatement.Close;
840 end;
841
842 { TIBXINPUTSQLDA }
843
844 function TIBXINPUTSQLDA.GetModified: Boolean;
845 var
846 i: Integer;
847 begin
848 result := False;
849 for i := 0 to FCount - 1 do
850 if Column[i].Modified then
851 begin
852 result := True;
853 exit;
854 end;
855 end;
856
857 procedure TIBXINPUTSQLDA.FreeMessageBuffer;
858 begin
859 if FMessageBuffer <> nil then
860 begin
861 FreeMem(FMessageBuffer);
862 FMessageBuffer := nil;
863 end;
864 FMsgLength := 0;
865 end;
866
867 procedure TIBXINPUTSQLDA.FreeCurMetaData;
868 begin
869 if FCurMetaData <> nil then
870 begin
871 FCurMetaData.release;
872 FCurMetaData := nil;
873 end;
874 end;
875
876 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
877 begin
878 PackBuffer;
879 Result := FMessageBuffer;
880 end;
881
882 function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
883 begin
884 BuildMetadata;
885 Result := FCurMetaData;
886 end;
887
888 function TIBXINPUTSQLDA.GetMsgLength: integer;
889 begin
890 PackBuffer;
891 Result := FMsgLength;
892 end;
893
894 procedure TIBXINPUTSQLDA.BuildMetadata;
895 var Builder: Firebird.IMetadataBuilder;
896 i: integer;
897 begin
898 if (FCurMetaData = nil) and (Count > 0) then
899 with FFirebird30ClientAPI do
900 begin
901 Builder := FFirebird30ClientAPI.MasterIntf.getMetadataBuilder(StatusIntf,Count);
902 Check4DataBaseError;
903 try
904 for i := 0 to Count - 1 do
905 with TIBXSQLVar(Column[i]) do
906 begin
907 Builder.setType(StatusIntf,i,FSQLType+1);
908 Check4DataBaseError;
909 Builder.setSubType(StatusIntf,i,FSQLSubType);
910 Check4DataBaseError;
911 // writeln('Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
912 if FSQLType = SQL_VARYING then
913 begin
914 {The datalength can be greater than the metadata size when SQLType has been overridden to text}
915 if (GetDataLength > GetSize) and CanChangeMetaData then
916 Builder.setLength(StatusIntf,i,GetDataLength)
917 else
918 Builder.setLength(StatusIntf,i,GetSize)
919 end
920 else
921 Builder.setLength(StatusIntf,i,GetDataLength);
922 Check4DataBaseError;
923 Builder.setCharSet(StatusIntf,i,GetCharSetID);
924 Check4DataBaseError;
925 Builder.setScale(StatusIntf,i,FScale);
926 Check4DataBaseError;
927 end;
928 FCurMetaData := Builder.getMetadata(StatusIntf);
929 Check4DataBaseError;
930 finally
931 Builder.release;
932 end;
933 end;
934 end;
935
936 procedure TIBXINPUTSQLDA.PackBuffer;
937 var i: integer;
938 P: PByte;
939 begin
940 BuildMetadata;
941
942 if (FMsgLength = 0) and (FCurMetaData <> nil) then
943 with FFirebird30ClientAPI do
944 begin
945 FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
946 Check4DataBaseError;
947
948 IBAlloc(FMessageBuffer,0,FMsgLength);
949
950 for i := 0 to Count - 1 do
951 with TIBXSQLVar(Column[i]) do
952 begin
953 P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
954 // writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
955 if not Modified then
956 IBError(ibxeUninitializedInputParameter,[i,Name]);
957 if IsNull then
958 FillChar(P^,FDataLength,0)
959 else
960 if FSQLData <> nil then
961 begin
962 if SQLType = SQL_VARYING then
963 begin
964 EncodeInteger(FDataLength,2,P);
965 Inc(P,2);
966 end
967 else
968 if (SQLType = SQL_BLOB) and (FStatement.FBatch <> nil) then
969 begin
970 FStatement.FBatch.registerBlob(Statusintf,ISC_QUADPtr(FSQLData),ISC_QUADPtr(FSQLData));
971 Check4DatabaseError;
972 end;
973 Move(FSQLData^,P^,FDataLength);
974 end;
975 if IsNullable then
976 begin
977 Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
978 Check4DataBaseError;
979 end;
980 end;
981 end;
982 end;
983
984 procedure TIBXINPUTSQLDA.FreeXSQLDA;
985 begin
986 inherited FreeXSQLDA;
987 FreeCurMetaData;
988 FreeMessageBuffer;
989 end;
990
991 constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
992 begin
993 inherited Create(aStatement);
994 FMessageBuffer := nil;
995 end;
996
997 destructor TIBXINPUTSQLDA.Destroy;
998 begin
999 FreeXSQLDA;
1000 inherited Destroy;
1001 end;
1002
1003 procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1004 var i: integer;
1005 begin
1006 FMetaData := aMetaData;
1007 with FFirebird30ClientAPI do
1008 begin
1009 Count := aMetadata.getCount(StatusIntf);
1010 Check4DataBaseError;
1011 Initialize;
1012
1013 for i := 0 to Count - 1 do
1014 with TIBXSQLVar(Column[i]) do
1015 begin
1016 InitColumnMetaData(aMetaData);
1017 SaveMetaData;
1018 if FNullable then
1019 FSQLNullIndicator := @FNullIndicator
1020 else
1021 FSQLNullIndicator := nil;
1022 ColumnSQLDataInit;
1023 end;
1024 end;
1025 end;
1026
1027 procedure TIBXINPUTSQLDA.Changed;
1028 begin
1029 inherited Changed;
1030 FreeCurMetaData;
1031 FreeMessageBuffer;
1032 end;
1033
1034 procedure TIBXINPUTSQLDA.ReInitialise;
1035 var i: integer;
1036 begin
1037 FreeMessageBuffer;
1038 for i := 0 to Count - 1 do
1039 TIBXSQLVar(Column[i]).ColumnSQLDataInit;
1040 end;
1041
1042 function TIBXINPUTSQLDA.IsInputDataArea: boolean;
1043 begin
1044 Result := true;
1045 end;
1046
1047 { TIBXOUTPUTSQLDA }
1048
1049 procedure TIBXOUTPUTSQLDA.FreeXSQLDA;
1050 begin
1051 inherited FreeXSQLDA;
1052 FreeMem(FMessageBuffer);
1053 FMessageBuffer := nil;
1054 FMsgLength := 0;
1055 end;
1056
1057 procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1058 var i: integer;
1059 begin
1060 FMetaData := aMetaData;
1061 with FFirebird30ClientAPI do
1062 begin
1063 Count := metadata.getCount(StatusIntf);
1064 Check4DataBaseError;
1065 Initialize;
1066
1067 FMsgLength := metaData.getMessageLength(StatusIntf);
1068 Check4DataBaseError;
1069 IBAlloc(FMessageBuffer,0,FMsgLength);
1070
1071 for i := 0 to Count - 1 do
1072 with TIBXSQLVar(Column[i]) do
1073 begin
1074 InitColumnMetaData(aMetaData);
1075 FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
1076 Check4DataBaseError;
1077 if FNullable then
1078 begin
1079 FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
1080 Check4DataBaseError;
1081 end
1082 else
1083 FSQLNullIndicator := nil;
1084 FBlob := nil;
1085 FArray := nil;
1086 end;
1087 end;
1088 SetUniqueRelationName;
1089 end;
1090
1091 procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
1092 var len: short; var data: PByte);
1093 begin
1094 with TIBXSQLVAR(Column[index]) do
1095 begin
1096 aIsNull := FNullable and (FSQLNullIndicator^ = -1);
1097 data := FSQLData;
1098 len := FDataLength;
1099 if not IsNull and (FSQLType = SQL_VARYING) then
1100 begin
1101 with FFirebird30ClientAPI do
1102 len := DecodeInteger(data,2);
1103 Inc(Data,2);
1104 end;
1105 end;
1106 end;
1107
1108 function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
1109 begin
1110 Result := false;
1111 end;
1112
1113 { TIBXSQLDA }
1114 constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
1115 begin
1116 inherited Create;
1117 FStatement := aStatement;
1118 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
1119 FSize := 0;
1120 // writeln('Creating ',ClassName);
1121 end;
1122
1123 destructor TIBXSQLDA.Destroy;
1124 begin
1125 FreeXSQLDA;
1126 // writeln('Destroying ',ClassName);
1127 inherited Destroy;
1128 end;
1129
1130 procedure TIBXSQLDA.Changed;
1131 begin
1132
1133 end;
1134
1135 function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
1136 begin
1137 Result := false;
1138 case Request of
1139 ssPrepared:
1140 Result := FStatement.IsPrepared;
1141
1142 ssExecuteResults:
1143 Result :=FStatement.FSingleResults;
1144
1145 ssCursorOpen:
1146 Result := FStatement.FOpen;
1147
1148 ssBOF:
1149 Result := FStatement.FBOF;
1150
1151 ssEOF:
1152 Result := FStatement.FEOF;
1153 end;
1154 end;
1155
1156 function TIBXSQLDA.ColumnsInUseCount: integer;
1157 begin
1158 Result := FCount;
1159 end;
1160
1161 function TIBXSQLDA.GetTransaction: TFB30Transaction;
1162 begin
1163 Result := FStatement.GetTransaction as TFB30Transaction;
1164 end;
1165
1166 procedure TIBXSQLDA.Initialize;
1167 begin
1168 if FMetaData <> nil then
1169 inherited Initialize;
1170 end;
1171
1172 function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
1173 begin
1174 Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
1175 if Result then
1176 ChangeSeqNo := FStatement.ChangeSeqNo;
1177 end;
1178
1179 function TIBXSQLDA.CanChangeMetaData: boolean;
1180 begin
1181 Result := FStatement.FBatch = nil;
1182 end;
1183
1184 procedure TIBXSQLDA.SetCount(Value: Integer);
1185 var
1186 i: Integer;
1187 begin
1188 FCount := Value;
1189 if FCount = 0 then
1190 FUniqueRelationName := ''
1191 else
1192 begin
1193 SetLength(FColumnList, FCount);
1194 for i := FSize to FCount - 1 do
1195 FColumnList[i] := TIBXSQLVAR.Create(self,i);
1196 FSize := FCount;
1197 end;
1198 end;
1199
1200 function TIBXSQLDA.GetTransactionSeqNo: integer;
1201 begin
1202 Result := FTransactionSeqNo;
1203 end;
1204
1205 procedure TIBXSQLDA.FreeXSQLDA;
1206 var i: integer;
1207 begin
1208 if FMetaData <> nil then
1209 FMetaData.release;
1210 FMetaData := nil;
1211 for i := 0 to Count - 1 do
1212 TIBXSQLVAR(Column[i]).FreeSQLData;
1213 for i := 0 to FSize - 1 do
1214 TIBXSQLVAR(Column[i]).Free;
1215 FCount := 0;
1216 SetLength(FColumnList,0);
1217 FSize := 0;
1218 end;
1219
1220 function TIBXSQLDA.GetStatement: IStatement;
1221 begin
1222 Result := FStatement;
1223 end;
1224
1225 function TIBXSQLDA.GetPrepareSeqNo: integer;
1226 begin
1227 Result := FStatement.FPrepareSeqNo;
1228 end;
1229
1230 { TFB30Statement }
1231
1232 procedure TFB30Statement.CheckChangeBatchRowLimit;
1233 begin
1234 if IsInBatchMode then
1235 IBError(ibxeInBatchMode,[nil]);
1236 end;
1237
1238 procedure TFB30Statement.CheckHandle;
1239 begin
1240 if FStatementIntf = nil then
1241 IBError(ibxeInvalidStatementHandle,[nil]);
1242 end;
1243
1244 procedure TFB30Statement.CheckBatchModeAvailable;
1245 begin
1246 if not HasBatchMode then
1247 IBError(ibxeBatchModeNotSupported,[nil]);
1248 case SQLStatementType of
1249 SQLInsert,
1250 SQLUpdate: {OK};
1251 else
1252 IBError(ibxeInvalidBatchQuery,[GetSQLStatementTypeName]);
1253 end;
1254 end;
1255
1256 procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1257 );
1258 begin
1259 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
1260 begin
1261 StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
1262 GetBufSize, BytePtr(Buffer));
1263 Check4DataBaseError;
1264 end;
1265 end;
1266
1267 procedure TFB30Statement.InternalPrepare;
1268 begin
1269 if FPrepared then
1270 Exit;
1271 if (FSQL = '') then
1272 IBError(ibxeEmptyQuery, [nil]);
1273 try
1274 CheckTransaction(FTransactionIntf);
1275 with FFirebird30ClientAPI do
1276 begin
1277 if FHasParamNames then
1278 begin
1279 if FProcessedSQL = '' then
1280 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1281 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1282 (FTransactionIntf as TFB30Transaction).TransactionIntf,
1283 Length(FProcessedSQL),
1284 PAnsiChar(FProcessedSQL),
1285 FSQLDialect,
1286 Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1287 end
1288 else
1289 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1290 (FTransactionIntf as TFB30Transaction).TransactionIntf,
1291 Length(FSQL),
1292 PAnsiChar(FSQL),
1293 FSQLDialect,
1294 Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1295 Check4DataBaseError;
1296 FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1297 Check4DataBaseError;
1298
1299 { Done getting the type }
1300 case FSQLStatementType of
1301 SQLGetSegment,
1302 SQLPutSegment,
1303 SQLStartTransaction:
1304 begin
1305 FreeHandle;
1306 IBError(ibxeNotPermitted, [nil]);
1307 end;
1308 SQLCommit,
1309 SQLRollback,
1310 SQLDDL, SQLSetGenerator,
1311 SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1312 SQLExecProcedure:
1313 begin
1314 {set up input sqlda}
1315 FSQLParams.Bind(FStatementIntf.getInputMetadata(StatusIntf));
1316 Check4DataBaseError;
1317
1318 {setup output sqlda}
1319 if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1320 SQLExecProcedure] then
1321 FSQLRecord.Bind(FStatementIntf.getOutputMetadata(StatusIntf));
1322 Check4DataBaseError;
1323 end;
1324 end;
1325 end;
1326 except
1327 on E: Exception do begin
1328 if (FStatementIntf <> nil) then
1329 FreeHandle;
1330 if E is EIBInterBaseError then
1331 E.Message := E.Message + sSQLErrorSeparator + FSQL;
1332 raise;
1333 end;
1334 end;
1335 FPrepared := true;
1336 FSingleResults := false;
1337 if RetainInterfaces then
1338 begin
1339 SetRetainInterfaces(false);
1340 SetRetainInterfaces(true);
1341 end;
1342 Inc(FPrepareSeqNo);
1343 with GetTransaction as TFB30Transaction do
1344 begin
1345 FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1346 FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1347 end;
1348 SignalActivity;
1349 Inc(FChangeSeqNo);
1350 end;
1351
1352 function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1353
1354 procedure ExecuteQuery(outMetaData: Firebird.IMessageMetaData=nil; outBuffer: pointer=nil);
1355 begin
1356 with FFirebird30ClientAPI do
1357 begin
1358 SavePerfStats(FBeforeStats);
1359 FStatementIntf.execute(StatusIntf,
1360 (aTransaction as TFB30Transaction).TransactionIntf,
1361 FSQLParams.MetaData,
1362 FSQLParams.MessageBuffer,
1363 outMetaData,
1364 outBuffer);
1365 Check4DataBaseError;
1366 FStatisticsAvailable := SavePerfStats(FAfterStats);
1367 end;
1368 end;
1369
1370
1371 begin
1372 Result := nil;
1373 FBatchCompletion := nil;
1374 FBOF := false;
1375 FEOF := false;
1376 FSingleResults := false;
1377 FStatisticsAvailable := false;
1378 if IsInBatchMode then
1379 IBerror(ibxeInBatchMode,[]);
1380 CheckTransaction(aTransaction);
1381 if not FPrepared then
1382 InternalPrepare;
1383 CheckHandle;
1384 if aTransaction <> FTransactionIntf then
1385 AddMonitor(aTransaction as TFB30Transaction);
1386 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1387 IBError(ibxeInterfaceOutofDate,[nil]);
1388
1389
1390 try
1391 with FFirebird30ClientAPI do
1392 begin
1393 case FSQLStatementType of
1394 SQLSelect:
1395 IBError(ibxeIsAExecuteProcedure,[]);
1396
1397 SQLExecProcedure:
1398 begin
1399 ExecuteQuery(FSQLRecord.MetaData,FSQLRecord.MessageBuffer);
1400 Result := TResults.Create(FSQLRecord);
1401 FSingleResults := true;
1402 end;
1403
1404 else
1405 ExecuteQuery;
1406 end;
1407 end;
1408 finally
1409 if aTransaction <> FTransactionIntf then
1410 RemoveMonitor(aTransaction as TFB30Transaction);
1411 end;
1412 FExecTransactionIntf := aTransaction;
1413 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1414 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1415 SignalActivity;
1416 Inc(FChangeSeqNo);
1417 end;
1418
1419 function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction
1420 ): IResultSet;
1421 begin
1422 if FSQLStatementType <> SQLSelect then
1423 IBError(ibxeIsASelectStatement,[]);
1424
1425 FBatchCompletion := nil;
1426 CheckTransaction(aTransaction);
1427 if not FPrepared then
1428 InternalPrepare;
1429 CheckHandle;
1430 if aTransaction <> FTransactionIntf then
1431 AddMonitor(aTransaction as TFB30Transaction);
1432 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1433 IBError(ibxeInterfaceOutofDate,[nil]);
1434
1435 with FFirebird30ClientAPI do
1436 begin
1437 if FCollectStatistics then
1438 begin
1439 UtilIntf.getPerfCounters(StatusIntf,
1440 (GetAttachment as TFB30Attachment).AttachmentIntf,
1441 ISQL_COUNTERS, @FBeforeStats);
1442 Check4DataBaseError;
1443 end;
1444
1445 FResultSet := FStatementIntf.openCursor(StatusIntf,
1446 (aTransaction as TFB30Transaction).TransactionIntf,
1447 FSQLParams.MetaData,
1448 FSQLParams.MessageBuffer,
1449 FSQLRecord.MetaData,
1450 0);
1451 Check4DataBaseError;
1452
1453 if FCollectStatistics then
1454 begin
1455 UtilIntf.getPerfCounters(StatusIntf,
1456 (GetAttachment as TFB30Attachment).AttachmentIntf,
1457 ISQL_COUNTERS,@FAfterStats);
1458 Check4DataBaseError;
1459 FStatisticsAvailable := true;
1460 end;
1461 end;
1462 Inc(FCursorSeqNo);
1463 FSingleResults := false;
1464 FOpen := True;
1465 FExecTransactionIntf := aTransaction;
1466 FBOF := true;
1467 FEOF := false;
1468 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1469 FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1470 Result := TResultSet.Create(FSQLRecord);
1471 SignalActivity;
1472 Inc(FChangeSeqNo);
1473 end;
1474
1475 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1476 var processedSQL: AnsiString);
1477 begin
1478 FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1479 end;
1480
1481 procedure TFB30Statement.FreeHandle;
1482 begin
1483 Close;
1484 ReleaseInterfaces;
1485 if FBatch <> nil then
1486 begin
1487 FBatch.release;
1488 FBatch := nil;
1489 end;
1490 if FStatementIntf <> nil then
1491 begin
1492 FStatementIntf.release;
1493 FStatementIntf := nil;
1494 FPrepared := false;
1495 end;
1496 end;
1497
1498 procedure TFB30Statement.InternalClose(Force: boolean);
1499 begin
1500 if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1501 try
1502 with FFirebird30ClientAPI do
1503 begin
1504 if FResultSet <> nil then
1505 begin
1506 if FSQLRecord.FTransaction.InTransaction and
1507 (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1508 FResultSet.close(StatusIntf)
1509 else
1510 FResultSet.release;
1511 end;
1512 FResultSet := nil;
1513 if not Force then Check4DataBaseError;
1514 end;
1515 finally
1516 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1517 RemoveMonitor(FSQLRecord.FTransaction);
1518 FOpen := False;
1519 FExecTransactionIntf := nil;
1520 FSQLRecord.FTransaction := nil;
1521 end;
1522 SignalActivity;
1523 Inc(FChangeSeqNo);
1524 end;
1525
1526 function TFB30Statement.SavePerfStats(var Stats: TPerfStatistics): boolean;
1527 begin
1528 Result := false;
1529 if FCollectStatistics then
1530 with FFirebird30ClientAPI do
1531 begin
1532 UtilIntf.getPerfCounters(StatusIntf,
1533 (GetAttachment as TFB30Attachment).AttachmentIntf,
1534 ISQL_COUNTERS, @Stats);
1535 Check4DataBaseError;
1536 Result := true;
1537 end;
1538 end;
1539
1540 constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1541 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer);
1542 begin
1543 inherited Create(Attachment,Transaction,sql,aSQLDialect);
1544 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1545 FSQLParams := TIBXINPUTSQLDA.Create(self);
1546 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1547 InternalPrepare;
1548 end;
1549
1550 constructor TFB30Statement.CreateWithParameterNames(
1551 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1552 aSQLDialect: integer; GenerateParamNames: boolean;
1553 CaseSensitiveParams: boolean);
1554 begin
1555 inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1556 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1557 FSQLParams := TIBXINPUTSQLDA.Create(self);
1558 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1559 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1560 InternalPrepare;
1561 end;
1562
1563 destructor TFB30Statement.Destroy;
1564 begin
1565 inherited Destroy;
1566 if assigned(FSQLParams) then FSQLParams.Free;
1567 if assigned(FSQLRecord) then FSQLRecord.Free;
1568 end;
1569
1570 function TFB30Statement.FetchNext: boolean;
1571 var fetchResult: integer;
1572 begin
1573 result := false;
1574 if not FOpen then
1575 IBError(ibxeSQLClosed, [nil]);
1576 if FEOF then
1577 IBError(ibxeEOF,[nil]);
1578
1579 with FFirebird30ClientAPI do
1580 begin
1581 { Go to the next record... }
1582 fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1583 if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1584 begin
1585 FBOF := false;
1586 FEOF := true;
1587 Exit; {End of File}
1588 end
1589 else
1590 if fetchResult <> Firebird.IStatus.RESULT_OK then
1591 begin
1592 try
1593 IBDataBaseError;
1594 except
1595 Close;
1596 raise;
1597 end;
1598 end
1599 else
1600 begin
1601 FBOF := false;
1602 result := true;
1603 end;
1604 if FCollectStatistics then
1605 begin
1606 UtilIntf.getPerfCounters(StatusIntf,
1607 (GetAttachment as TFB30Attachment).AttachmentIntf,
1608 ISQL_COUNTERS,@FAfterStats);
1609 Check4DataBaseError;
1610 FStatisticsAvailable := true;
1611 end;
1612 end;
1613 FSQLRecord.RowChange;
1614 SignalActivity;
1615 if FEOF then
1616 Inc(FChangeSeqNo);
1617 end;
1618
1619 function TFB30Statement.GetSQLParams: ISQLParams;
1620 begin
1621 CheckHandle;
1622 if not HasInterface(0) then
1623 AddInterface(0,TSQLParams.Create(FSQLParams));
1624 Result := TSQLParams(GetInterface(0));
1625 end;
1626
1627 function TFB30Statement.GetMetaData: IMetaData;
1628 begin
1629 CheckHandle;
1630 if not HasInterface(1) then
1631 AddInterface(1, TMetaData.Create(FSQLRecord));
1632 Result := TMetaData(GetInterface(1));
1633 end;
1634
1635 function TFB30Statement.GetPlan: AnsiString;
1636 begin
1637 CheckHandle;
1638 if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1639 {TODO: SQLExecProcedure, }
1640 SQLUpdate, SQLDelete])) then
1641 result := ''
1642 else
1643 with FFirebird30ClientAPI do
1644 begin
1645 Result := FStatementIntf.getPlan(StatusIntf,true);
1646 Check4DataBaseError;
1647 end;
1648 end;
1649
1650 function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1651 begin
1652 if assigned(column) and (column.SQLType <> SQL_Blob) then
1653 IBError(ibxeNotABlob,[nil]);
1654 Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1655 GetTransaction as TFB30Transaction,
1656 column.GetBlobMetaData,nil);
1657 end;
1658
1659 function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1660 begin
1661 if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1662 IBError(ibxeNotAnArray,[nil]);
1663 Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1664 GetTransaction as TFB30Transaction,
1665 column.GetArrayMetaData);
1666 end;
1667
1668 procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1669 begin
1670 inherited SetRetainInterfaces(aValue);
1671 if HasInterface(1) then
1672 TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1673 if HasInterface(0) then
1674 TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1675 end;
1676
1677 function TFB30Statement.IsInBatchMode: boolean;
1678 begin
1679 Result := FBatch <> nil;
1680 end;
1681
1682 function TFB30Statement.HasBatchMode: boolean;
1683 begin
1684 Result := GetAttachment.HasBatchMode;
1685 end;
1686
1687 procedure TFB30Statement.AddToBatch;
1688 var BatchPB: TXPBParameterBlock;
1689
1690 const SixteenMB = 16 * 1024 * 1024;
1691 begin
1692 FBatchCompletion := nil;
1693 if not FPrepared then
1694 InternalPrepare;
1695 CheckHandle;
1696 CheckBatchModeAvailable;
1697 with FFirebird30ClientAPI do
1698 begin
1699 if FBatch = nil then
1700 begin
1701 {Start Batch}
1702 BatchPB := TXPBParameterBlock.Create(FFirebird30ClientAPI,Firebird.IXpbBuilder.BATCH);
1703 with FFirebird30ClientAPI do
1704 try
1705 FBatchBufferSize := FBatchRowLimit * FSQLParams.MetaData.getAlignedLength(StatusIntf);
1706 Check4DatabaseError;
1707 if FBatchBufferSize < SixteenMB then
1708 FBatchBufferSize := SixteenMB;
1709 if FBatchBufferSize > 256 * 1024 *1024 {assumed limit} then
1710 IBError(ibxeBatchBufferSizeTooBig,[FBatchBufferSize]);
1711
1712 BatchPB.insertInt(Firebird.IBatch.TAG_RECORD_COUNTS,1);
1713 BatchPB.insertInt(Firebird.IBatch.TAG_BUFFER_BYTES_SIZE,FBatchBufferSize);
1714 FBatch := FStatementIntf.createBatch(StatusIntf,
1715 FSQLParams.MetaData,
1716 BatchPB.getDataLength,
1717 BatchPB.getBuffer);
1718 Check4DataBaseError;
1719
1720 finally
1721 BatchPB.Free;
1722 end;
1723 FBatchRowCount := 0;
1724 FBatchBufferUsed := 0;
1725 end;
1726
1727 Inc(FBatchRowCount);
1728 Inc(FBatchBufferUsed,FSQLParams.MetaData.getAlignedLength(StatusIntf));
1729 Check4DataBaseError;
1730 if FBatchBufferUsed > FBatchBufferSize then
1731 raise EIBBatchBufferOverflow.Create(Ord(ibxeBatchRowBufferOverflow),
1732 Format(GetErrorMessage(ibxeBatchRowBufferOverflow),
1733 [FBatchRowCount,FBatchBufferSize]));
1734
1735 FBatch.Add(StatusIntf,1,FSQLParams.GetMessageBuffer);
1736 Check4DataBaseError
1737 end;
1738 end;
1739
1740 function TFB30Statement.ExecuteBatch(aTransaction: ITransaction
1741 ): IBatchCompletion;
1742
1743 procedure Check4BatchCompletionError(bc: IBatchCompletion);
1744 var status: IStatus;
1745 RowNo: integer;
1746 begin
1747 status := nil;
1748 {Raise an exception if there was an error reported in the BatchCompletion}
1749 if (bc <> nil) and bc.getErrorStatus(RowNo,status) then
1750 raise EIBInterbaseError.Create(status);
1751 end;
1752
1753 var cs: Firebird.IBatchCompletionState;
1754
1755 begin
1756 Result := nil;
1757 if FBatch = nil then
1758 IBError(ibxeNotInBatchMode,[]);
1759
1760 with FFirebird30ClientAPI do
1761 begin
1762 SavePerfStats(FBeforeStats);
1763 if aTransaction = nil then
1764 cs := FBatch.execute(StatusIntf,(FTransactionIntf as TFB30Transaction).TransactionIntf)
1765 else
1766 cs := FBatch.execute(StatusIntf,(aTransaction as TFB30Transaction).TransactionIntf);
1767 Check4DataBaseError;
1768 FBatchCompletion := TBatchCompletion.Create(FFirebird30ClientAPI,cs);
1769 FStatisticsAvailable := SavePerfStats(FAfterStats);
1770 FBatch.release;
1771 FBatch := nil;
1772 Check4BatchCompletionError(FBatchCompletion);
1773 Result := FBatchCompletion;
1774 end;
1775 end;
1776
1777 procedure TFB30Statement.CancelBatch;
1778 begin
1779 if FBatch = nil then
1780 IBError(ibxeNotInBatchMode,[]);
1781 FBatch.release;
1782 FBatch := nil;
1783 end;
1784
1785 function TFB30Statement.GetBatchCompletion: IBatchCompletion;
1786 begin
1787 Result := FBatchCompletion;
1788 end;
1789
1790 function TFB30Statement.IsPrepared: boolean;
1791 begin
1792 Result := FStatementIntf <> nil;
1793 end;
1794
1795 end.
1796