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 (2 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 52931 byte(s)
Log Message:
Fixed Merged

File Contents

# User Rev Content
1 tony 45 (*
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 tony 56 {$IFDEF MSWINDOWS}
29     {$DEFINE WINDOWS}
30     {$ENDIF}
31 tony 45
32     {$IFDEF FPC}
33 tony 56 {$mode delphi}
34 tony 45 {$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 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
90 tony 45 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 tony 56 FSQLData: PByte; {Address of SQL Data in Message Buffer}
101 tony 45 FSQLNullIndicator: PShort; {Address of null indicator}
102     FDataLength: integer;
103 tony 315 FMetadataSize: integer;
104 tony 45 FNullable: boolean;
105     FScale: integer;
106     FCharSetID: cardinal;
107 tony 56 FRelationName: AnsiString;
108     FFieldName: AnsiString;
109 tony 45
110     protected
111 tony 345 function CanChangeSQLType: boolean;
112 tony 45 function GetSQLType: cardinal; override;
113     function GetSubtype: integer; override;
114 tony 56 function GetAliasName: AnsiString; override;
115     function GetFieldName: AnsiString; override;
116     function GetOwnerName: AnsiString; override;
117     function GetRelationName: AnsiString; override;
118 tony 45 function GetScale: integer; override;
119     function GetCharSetID: cardinal; override;
120     function GetCodePage: TSystemCodePage; override;
121 tony 309 function GetCharSetWidth: integer; override;
122 tony 45 function GetIsNull: Boolean; override;
123     function GetIsNullable: boolean; override;
124 tony 56 function GetSQLData: PByte; override;
125 tony 45 function GetDataLength: cardinal; override;
126 tony 315 function GetSize: cardinal; override;
127 tony 345 function GetAttachment: IAttachment; override;
128     function GetDefaultTextSQLType: cardinal; override;
129 tony 45 procedure SetIsNull(Value: Boolean); override;
130     procedure SetIsNullable(Value: Boolean); override;
131 tony 56 procedure SetSQLData(AValue: PByte; len: cardinal); override;
132 tony 45 procedure SetScale(aValue: integer); override;
133     procedure SetDataLength(len: cardinal); override;
134     procedure SetSQLType(aValue: cardinal); override;
135     procedure SetCharSetID(aValue: cardinal); override;
136 tony 345 procedure SetMetaSize(aValue: cardinal); override;
137 tony 45 public
138     constructor Create(aParent: TIBXSQLDA; aIndex: integer);
139     procedure Changed; override;
140 tony 349 procedure InitColumnMetaData(aMetaData: Firebird.IMessageMetadata);
141 tony 345 procedure ColumnSQLDataInit;
142 tony 45 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 tony 263 protected
160 tony 45 FStatement: TFB30Statement;
161 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
162 tony 45 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 tony 345 function CanChangeMetaData: boolean; override;
177 tony 45 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 tony 56 FMessageBuffer: PByte; {Message Buffer}
187 tony 45 FMsgLength: integer; {Message Buffer length}
188     FCurMetaData: Firebird.IMessageMetadata;
189     procedure FreeMessageBuffer;
190 tony 345 procedure FreeCurMetaData;
191 tony 56 function GetMessageBuffer: PByte;
192 tony 45 function GetMetaData: Firebird.IMessageMetadata;
193     function GetModified: Boolean;
194     function GetMsgLength: integer;
195 tony 68 procedure BuildMetadata;
196 tony 45 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 tony 345 procedure ReInitialise;
205 tony 45 function IsInputDataArea: boolean; override;
206     property MetaData: Firebird.IMessageMetadata read GetMetaData;
207 tony 56 property MessageBuffer: PByte read GetMessageBuffer;
208 tony 45 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 tony 56 FMessageBuffer: PByte; {Message Buffer}
217 tony 45 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 tony 56 var data: PByte); override;
224 tony 45 function IsInputDataArea: boolean; override;
225 tony 56 property MessageBuffer: PByte read FMessageBuffer;
226 tony 45 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 tony 350 procedure RowChange;
236 tony 45 public
237     constructor Create(aResults: TIBXOUTPUTSQLDA);
238     destructor Destroy; override;
239     {IResultSet}
240 tony 350 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 tony 56 function GetCursorName: AnsiString;
247 tony 45 function GetTransaction: ITransaction; override;
248 tony 350 function IsBof: boolean;
249 tony 45 function IsEof: boolean;
250     procedure Close;
251     end;
252    
253 tony 345 { 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 tony 350 TFetchType = (ftNext,ftPrior,ftFirst,ftLast,ftAbsolute,ftRelative);
271    
272 tony 45 { TFB30Statement }
273    
274     TFB30Statement = class(TFBStatement,IStatement)
275     private
276     FStatementIntf: Firebird.IStatement;
277 tony 263 FFirebird30ClientAPI: TFB30ClientAPI;
278 tony 45 FSQLParams: TIBXINPUTSQLDA;
279     FSQLRecord: TIBXOUTPUTSQLDA;
280     FResultSet: Firebird.IResultSet;
281     FCursorSeqNo: integer;
282 tony 350 FCursor: AnsiString;
283 tony 345 FBatch: Firebird.IBatch;
284     FBatchCompletion: IBatchCompletion;
285     FBatchRowCount: integer;
286     FBatchBufferSize: integer;
287     FBatchBufferUsed: integer;
288 tony 45 protected
289 tony 345 procedure CheckChangeBatchRowLimit; override;
290 tony 45 procedure CheckHandle; override;
291 tony 345 procedure CheckBatchModeAvailable;
292 tony 45 procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
293 tony 350 procedure InternalPrepare(CursorName: AnsiString=''); override;
294 tony 45 function InternalExecute(aTransaction: ITransaction): IResults; override;
295 tony 350 function InternalOpenCursor(aTransaction: ITransaction; Scrollable: boolean
296     ): IResultSet; override;
297 tony 263 procedure ProcessSQL(sql: AnsiString; GenerateParamNames: boolean; var processedSQL: AnsiString); override;
298 tony 45 procedure FreeHandle; override;
299     procedure InternalClose(Force: boolean); override;
300 tony 345 function SavePerfStats(var Stats: TPerfStatistics): boolean;
301 tony 45 public
302     constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
303 tony 350 sql: AnsiString; aSQLDialect: integer; CursorName: AnsiString='');
304 tony 45 constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
305 tony 270 sql: AnsiString; aSQLDialect: integer; GenerateParamNames: boolean =false;
306 tony 350 CaseSensitiveParams: boolean=false; CursorName: AnsiString='');
307 tony 45 destructor Destroy; override;
308 tony 350 function Fetch(FetchType: TFetchType; PosOrOffset: integer=0): boolean;
309 tony 45 property StatementIntf: Firebird.IStatement read FStatementIntf;
310    
311     public
312     {IStatement}
313     function GetSQLParams: ISQLParams; override;
314     function GetMetaData: IMetaData; override;
315 tony 56 function GetPlan: AnsiString;
316 tony 45 function IsPrepared: boolean;
317     function CreateBlob(column: TColumnMetaData): IBlob; override;
318     function CreateArray(column: TColumnMetaData): IArray; override;
319     procedure SetRetainInterfaces(aValue: boolean); override;
320 tony 345 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 tony 45 end;
328    
329     implementation
330    
331 tony 68 uses IBUtils, FBMessages, FBBlob, FB30Blob, variants, FBArray, FB30Array;
332 tony 45
333 tony 47 const
334     ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
335    
336 tony 345 { 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 tony 45 { TIBXSQLVAR }
458    
459     procedure TIBXSQLVAR.Changed;
460     begin
461     inherited Changed;
462     TIBXSQLDA(Parent).Changed;
463     end;
464    
465 tony 349 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 tony 345 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 tony 45 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 tony 56 function TIBXSQLVAR.GetAliasName: AnsiString;
539 tony 45 begin
540 tony 263 with FFirebird30ClientAPI do
541 tony 45 begin
542     result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
543     Check4DataBaseError;
544     end;
545     end;
546    
547 tony 56 function TIBXSQLVAR.GetFieldName: AnsiString;
548 tony 45 begin
549     Result := FFieldName;
550     end;
551    
552 tony 56 function TIBXSQLVAR.GetOwnerName: AnsiString;
553 tony 45 begin
554 tony 263 with FFirebird30ClientAPI do
555 tony 45 begin
556     result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
557     Check4DataBaseError;
558     end;
559     end;
560    
561 tony 56 function TIBXSQLVAR.GetRelationName: AnsiString;
562 tony 45 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 tony 345 result := 0; {NONE}
574 tony 45 case SQLType of
575     SQL_VARYING, SQL_TEXT:
576     result := FCharSetID;
577    
578     SQL_BLOB:
579     if (SQLSubType = 1) then
580 tony 345 result := FCharSetID
581     else
582     result := 1; {OCTETS}
583 tony 45
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 tony 60 with Statement.GetAttachment do
596 tony 45 CharSetID2CodePage(GetCharSetID,result);
597     end;
598    
599 tony 309 function TIBXSQLVAR.GetCharSetWidth: integer;
600     begin
601     result := 1;
602     with Statement.GetAttachment DO
603     CharSetWidth(GetCharSetID,result);
604     end;
605    
606 tony 45 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 tony 56 function TIBXSQLVAR.GetSQLData: PByte;
617 tony 45 begin
618     Result := FSQLData;
619     end;
620    
621     function TIBXSQLVAR.GetDataLength: cardinal;
622     begin
623     Result := FDataLength;
624     end;
625    
626 tony 315 function TIBXSQLVAR.GetSize: cardinal;
627     begin
628     Result := FMetadataSize;
629     end;
630    
631 tony 345 function TIBXSQLVAR.GetAttachment: IAttachment;
632     begin
633     Result := FStatement.GetAttachment;
634     end;
635    
636 tony 45 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 tony 47 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
659 tony 45 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 tony 47 Changed;
673 tony 45 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 tony 68 Changed;
686 tony 45 end;
687    
688 tony 56 procedure TIBXSQLVAR.SetSQLData(AValue: PByte; len: cardinal);
689 tony 45 begin
690     if FOwnsSQLData then
691     FreeMem(FSQLData);
692     FSQLData := AValue;
693     FDataLength := len;
694     FOwnsSQLData := false;
695 tony 68 Changed;
696 tony 45 end;
697    
698     procedure TIBXSQLVAR.SetScale(aValue: integer);
699     begin
700     FScale := aValue;
701 tony 68 Changed;
702 tony 45 end;
703    
704     procedure TIBXSQLVAR.SetDataLength(len: cardinal);
705     begin
706     if not FOwnsSQLData then
707     FSQLData := nil;
708     FDataLength := len;
709 tony 263 with FFirebird30ClientAPI do
710 tony 45 IBAlloc(FSQLData, 0, FDataLength);
711     FOwnsSQLData := true;
712 tony 68 Changed;
713 tony 45 end;
714    
715     procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
716     begin
717 tony 345 if (FSQLType <> aValue) and not CanChangeSQLType then
718     IBError(ibxeSQLTypeUnchangeable,[TSQLDataItem.GetSQLTypeName(FSQLType),TSQLDataItem.GetSQLTypeName(aValue)]);
719 tony 45 FSQLType := aValue;
720 tony 68 Changed;
721 tony 45 end;
722    
723     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
724     begin
725     FCharSetID := aValue;
726 tony 68 Changed;
727 tony 45 end;
728    
729 tony 345 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 tony 45 constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
742     begin
743     inherited Create(aParent,aIndex);
744     FStatement := aParent.Statement;
745 tony 263 FFirebird30ClientAPI := aParent.FFirebird30ClientAPI;
746 tony 45 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 tony 350 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 tony 45 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 tony 350 Result := FResults.FStatement.Fetch(ftNext);
832 tony 45 if Result then
833 tony 350 RowChange;
834 tony 45 end;
835    
836 tony 350 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 tony 56 function TResultSet.GetCursorName: AnsiString;
877 tony 45 begin
878 tony 350 Result := FResults.FStatement.FCursor;
879 tony 45 end;
880    
881     function TResultSet.GetTransaction: ITransaction;
882     begin
883     Result := FResults.FTransaction;
884     end;
885    
886 tony 350 function TResultSet.IsBof: boolean;
887     begin
888     Result := FResults.FStatement.FBof;
889     end;
890    
891 tony 45 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 tony 345 procedure TIBXINPUTSQLDA.FreeCurMetaData;
928     begin
929     if FCurMetaData <> nil then
930     begin
931     FCurMetaData.release;
932     FCurMetaData := nil;
933     end;
934     end;
935    
936 tony 56 function TIBXINPUTSQLDA.GetMessageBuffer: PByte;
937 tony 45 begin
938     PackBuffer;
939     Result := FMessageBuffer;
940     end;
941    
942     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
943     begin
944 tony 68 BuildMetadata;
945 tony 45 Result := FCurMetaData;
946     end;
947    
948     function TIBXINPUTSQLDA.GetMsgLength: integer;
949     begin
950     PackBuffer;
951     Result := FMsgLength;
952     end;
953    
954 tony 68 procedure TIBXINPUTSQLDA.BuildMetadata;
955 tony 45 var Builder: Firebird.IMetadataBuilder;
956     i: integer;
957     begin
958 tony 345 if (FCurMetaData = nil) and (Count > 0) then
959 tony 263 with FFirebird30ClientAPI do
960 tony 45 begin
961 tony 345 Builder := FFirebird30ClientAPI.MasterIntf.getMetadataBuilder(StatusIntf,Count);
962 tony 45 Check4DataBaseError;
963     try
964     for i := 0 to Count - 1 do
965     with TIBXSQLVar(Column[i]) do
966     begin
967 tony 345 Builder.setType(StatusIntf,i,FSQLType+1);
968 tony 45 Check4DataBaseError;
969     Builder.setSubType(StatusIntf,i,FSQLSubType);
970     Check4DataBaseError;
971 tony 345 // 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 tony 45 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 tony 68 end;
994     end;
995 tony 45
996 tony 68 procedure TIBXINPUTSQLDA.PackBuffer;
997     var i: integer;
998 tony 345 P: PByte;
999 tony 68 begin
1000     BuildMetadata;
1001    
1002 tony 345 if (FMsgLength = 0) and (FCurMetaData <> nil) then
1003 tony 263 with FFirebird30ClientAPI do
1004 tony 68 begin
1005 tony 45 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 tony 345 P := FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i);
1014     // writeln('Packbuffer: Column ',Name,' Type = ',TSQLDataItem.GetSQLTypeName(FSQLType),' Size = ',GetSize,' DataLength = ',GetDataLength);
1015 tony 68 if not Modified then
1016     IBError(ibxeUninitializedInputParameter,[i,Name]);
1017 tony 47 if IsNull then
1018 tony 345 FillChar(P^,FDataLength,0)
1019 tony 47 else
1020 tony 68 if FSQLData <> nil then
1021 tony 345 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 tony 45 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 tony 345 FreeCurMetaData;
1048 tony 45 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 tony 345 FreeXSQLDA;
1060 tony 45 inherited Destroy;
1061     end;
1062    
1063     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
1064     var i: integer;
1065     begin
1066     FMetaData := aMetaData;
1067 tony 263 with FFirebird30ClientAPI do
1068 tony 45 begin
1069 tony 338 Count := aMetadata.getCount(StatusIntf);
1070 tony 45 Check4DataBaseError;
1071     Initialize;
1072    
1073     for i := 0 to Count - 1 do
1074     with TIBXSQLVar(Column[i]) do
1075     begin
1076 tony 349 InitColumnMetaData(aMetaData);
1077     SaveMetaData;
1078 tony 45 if FNullable then
1079     FSQLNullIndicator := @FNullIndicator
1080     else
1081     FSQLNullIndicator := nil;
1082 tony 345 ColumnSQLDataInit;
1083 tony 45 end;
1084     end;
1085     end;
1086    
1087     procedure TIBXINPUTSQLDA.Changed;
1088     begin
1089     inherited Changed;
1090 tony 345 FreeCurMetaData;
1091 tony 45 FreeMessageBuffer;
1092     end;
1093    
1094 tony 345 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 tony 45 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 tony 263 with FFirebird30ClientAPI do
1122 tony 45 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 tony 349 InitColumnMetaData(aMetaData);
1135 tony 45 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 tony 349 FBlob := nil;
1145     FArray := nil;
1146 tony 45 end;
1147     end;
1148     SetUniqueRelationName;
1149     end;
1150    
1151     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
1152 tony 56 var len: short; var data: PByte);
1153 tony 45 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 tony 263 with FFirebird30ClientAPI do
1162 tony 45 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 tony 263 FFirebird30ClientAPI := aStatement.FFirebird30ClientAPI;
1179 tony 45 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 tony 345 function TIBXSQLDA.CanChangeMetaData: boolean;
1240     begin
1241     Result := FStatement.FBatch = nil;
1242     end;
1243    
1244 tony 45 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 tony 345 FCount := 0;
1276 tony 45 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 tony 345 procedure TFB30Statement.CheckChangeBatchRowLimit;
1293     begin
1294     if IsInBatchMode then
1295     IBError(ibxeInBatchMode,[nil]);
1296     end;
1297    
1298 tony 45 procedure TFB30Statement.CheckHandle;
1299     begin
1300     if FStatementIntf = nil then
1301     IBError(ibxeInvalidStatementHandle,[nil]);
1302     end;
1303    
1304 tony 345 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 tony 45 procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
1317     );
1318     begin
1319 tony 263 with FFirebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
1320 tony 45 begin
1321     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
1322     GetBufSize, BytePtr(Buffer));
1323     Check4DataBaseError;
1324     end;
1325     end;
1326    
1327 tony 350 procedure TFB30Statement.InternalPrepare(CursorName: AnsiString);
1328     var GUID : TGUID;
1329 tony 45 begin
1330     if FPrepared then
1331     Exit;
1332 tony 350
1333     FCursor := CursorName;
1334 tony 45 if (FSQL = '') then
1335     IBError(ibxeEmptyQuery, [nil]);
1336     try
1337     CheckTransaction(FTransactionIntf);
1338 tony 263 with FFirebird30ClientAPI do
1339 tony 45 begin
1340 tony 350 if FCursor = '' then
1341     begin
1342     CreateGuid(GUID);
1343     FCursor := GUIDToString(GUID);
1344     end;
1345    
1346 tony 45 if FHasParamNames then
1347     begin
1348     if FProcessedSQL = '' then
1349 tony 263 ProcessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
1350 tony 45 FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
1351     (FTransactionIntf as TFB30Transaction).TransactionIntf,
1352     Length(FProcessedSQL),
1353 tony 56 PAnsiChar(FProcessedSQL),
1354 tony 45 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 tony 56 PAnsiChar(FSQL),
1362 tony 45 FSQLDialect,
1363     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
1364     Check4DataBaseError;
1365     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1366     Check4DataBaseError;
1367    
1368 tony 350 if FSQLStatementType = SQLSelect then
1369     begin
1370     FStatementIntf.setCursorName(StatusIntf,PAnsiChar(FCursor));
1371     Check4DataBaseError;
1372     end;
1373 tony 45 { 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 tony 315 E.Message := E.Message + sSQLErrorSeparator + FSQL;
1406     raise;
1407 tony 45 end;
1408     end;
1409     FPrepared := true;
1410 tony 350
1411 tony 45 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 tony 345
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 tony 45 begin
1447     Result := nil;
1448 tony 345 FBatchCompletion := nil;
1449 tony 45 FBOF := false;
1450     FEOF := false;
1451     FSingleResults := false;
1452 tony 345 FStatisticsAvailable := false;
1453     if IsInBatchMode then
1454     IBerror(ibxeInBatchMode,[]);
1455 tony 45 CheckTransaction(aTransaction);
1456     if not FPrepared then
1457     InternalPrepare;
1458     CheckHandle;
1459     if aTransaction <> FTransactionIntf then
1460     AddMonitor(aTransaction as TFB30Transaction);
1461 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1462 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1463    
1464 tony 345
1465 tony 45 try
1466 tony 263 with FFirebird30ClientAPI do
1467 tony 45 begin
1468 tony 47 case FSQLStatementType of
1469     SQLSelect:
1470     IBError(ibxeIsAExecuteProcedure,[]);
1471    
1472     SQLExecProcedure:
1473     begin
1474 tony 345 ExecuteQuery(FSQLRecord.MetaData,FSQLRecord.MessageBuffer);
1475 tony 47 Result := TResults.Create(FSQLRecord);
1476     FSingleResults := true;
1477 tony 345 end;
1478    
1479 tony 47 else
1480 tony 345 ExecuteQuery;
1481 tony 47 end;
1482 tony 45 end;
1483     finally
1484     if aTransaction <> FTransactionIntf then
1485     RemoveMonitor(aTransaction as TFB30Transaction);
1486     end;
1487     FExecTransactionIntf := aTransaction;
1488 tony 111 FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1489     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1490 tony 45 SignalActivity;
1491     Inc(FChangeSeqNo);
1492     end;
1493    
1494 tony 350 function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction;
1495     Scrollable: boolean): IResultSet;
1496     var flags: cardinal;
1497 tony 45 begin
1498 tony 350 flags := 0;
1499 tony 45 if FSQLStatementType <> SQLSelect then
1500     IBError(ibxeIsASelectStatement,[]);
1501    
1502 tony 345 FBatchCompletion := nil;
1503     CheckTransaction(aTransaction);
1504 tony 45 if not FPrepared then
1505     InternalPrepare;
1506     CheckHandle;
1507     if aTransaction <> FTransactionIntf then
1508     AddMonitor(aTransaction as TFB30Transaction);
1509 tony 347 if FStaleReferenceChecks and (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1510 tony 45 IBError(ibxeInterfaceOutofDate,[nil]);
1511    
1512 tony 350 if Scrollable then
1513     flags := Firebird.IStatement.CURSOR_TYPE_SCROLLABLE;
1514    
1515 tony 263 with FFirebird30ClientAPI do
1516 tony 45 begin
1517 tony 47 if FCollectStatistics then
1518     begin
1519     UtilIntf.getPerfCounters(StatusIntf,
1520     (GetAttachment as TFB30Attachment).AttachmentIntf,
1521     ISQL_COUNTERS, @FBeforeStats);
1522     Check4DataBaseError;
1523     end;
1524    
1525 tony 45 FResultSet := FStatementIntf.openCursor(StatusIntf,
1526     (aTransaction as TFB30Transaction).TransactionIntf,
1527     FSQLParams.MetaData,
1528     FSQLParams.MessageBuffer,
1529     FSQLRecord.MetaData,
1530 tony 350 flags);
1531 tony 45 Check4DataBaseError;
1532 tony 47
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 tony 45 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 tony 263 procedure TFB30Statement.ProcessSQL(sql: AnsiString; GenerateParamNames: boolean;
1556     var processedSQL: AnsiString);
1557     begin
1558     FSQLParams.PreprocessSQL(sql,GenerateParamNames,processedSQL);
1559     end;
1560    
1561 tony 45 procedure TFB30Statement.FreeHandle;
1562     begin
1563     Close;
1564     ReleaseInterfaces;
1565 tony 345 if FBatch <> nil then
1566     begin
1567     FBatch.release;
1568     FBatch := nil;
1569     end;
1570 tony 45 if FStatementIntf <> nil then
1571     begin
1572     FStatementIntf.release;
1573     FStatementIntf := nil;
1574     FPrepared := false;
1575     end;
1576 tony 350 FCursor := '';
1577 tony 45 end;
1578    
1579     procedure TFB30Statement.InternalClose(Force: boolean);
1580     begin
1581     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1582     try
1583 tony 263 with FFirebird30ClientAPI do
1584 tony 45 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 tony 56 if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> (FTransactionIntf as TFB30Transaction)) then
1598 tony 45 RemoveMonitor(FSQLRecord.FTransaction);
1599     FOpen := False;
1600     FExecTransactionIntf := nil;
1601     FSQLRecord.FTransaction := nil;
1602     end;
1603     SignalActivity;
1604     Inc(FChangeSeqNo);
1605     end;
1606    
1607 tony 345 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 tony 45 constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1622 tony 350 Transaction: ITransaction; sql: AnsiString; aSQLDialect: integer;
1623     CursorName: AnsiString);
1624 tony 45 begin
1625     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1626 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1627 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1628     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1629 tony 350 InternalPrepare(CursorName);
1630 tony 45 end;
1631    
1632     constructor TFB30Statement.CreateWithParameterNames(
1633 tony 56 Attachment: TFB30Attachment; Transaction: ITransaction; sql: AnsiString;
1634 tony 270 aSQLDialect: integer; GenerateParamNames: boolean;
1635 tony 350 CaseSensitiveParams: boolean; CursorName: AnsiString);
1636 tony 45 begin
1637     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1638 tony 263 FFirebird30ClientAPI := Attachment.Firebird30ClientAPI;
1639 tony 45 FSQLParams := TIBXINPUTSQLDA.Create(self);
1640 tony 270 FSQLParams.CaseSensitiveParams := CaseSensitiveParams;
1641 tony 45 FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1642 tony 350 InternalPrepare(CursorName);
1643 tony 45 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 tony 350 function TFB30Statement.Fetch(FetchType: TFetchType; PosOrOffset: integer
1653     ): boolean;
1654 tony 45 var fetchResult: integer;
1655     begin
1656     result := false;
1657     if not FOpen then
1658     IBError(ibxeSQLClosed, [nil]);
1659    
1660 tony 263 with FFirebird30ClientAPI do
1661 tony 45 begin
1662 tony 350 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 tony 45 end;
1676 tony 350
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 tony 45 end;
1703 tony 350
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 tony 209 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 tony 45 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 tony 56 function TFB30Statement.GetPlan: AnsiString;
1745 tony 45 begin
1746     CheckHandle;
1747     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1748     {TODO: SQLExecProcedure, }
1749     SQLUpdate, SQLDelete])) then
1750     result := ''
1751     else
1752 tony 263 with FFirebird30ClientAPI do
1753 tony 45 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 tony 345 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 tony 45 function TFB30Statement.IsPrepared: boolean;
1900     begin
1901     Result := FStatementIntf <> nil;
1902     end;
1903    
1904     end.
1905