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: 394
Committed: Sat Feb 12 23:26:48 2022 UTC (2 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 55922 byte(s)
Log Message:
Use FieldNames for param names

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

Properties

Name Value
svn:eol-style native