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: 390
Committed: Sat Jan 22 16:15:12 2022 UTC (2 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 55878 byte(s)
Log Message:
In Firebird 3 and later API: the status vector is now a thread var

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

Properties

Name Value
svn:eol-style native