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