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: 47
Committed: Mon Jan 9 15:31:51 2017 UTC (7 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 37446 byte(s)
Log Message:
Committing updates for Release R2-0-1

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    
29     {$IFDEF FPC}
30     {$mode objfpc}{$H+}
31     {$codepage UTF8}
32     {$interfaces COM}
33     {$ENDIF}
34    
35     {This unit is hacked from IBSQL and contains the code for managing an XSQLDA and
36     SQLVars, along with statement preparation, execution and cursor management.
37     Most of the SQLVar code has been moved to unit FBSQLData. Client access is
38     provided through interface rather than direct access to the XSQLDA and XSQLVar
39     objects.}
40    
41     {
42     Note on reference counted interfaces.
43     ------------------------------------
44    
45     TFB30Statement manages both an input and an output SQLDA through the TIBXINPUTSQLDA
46     and TIBXOUTPUTSQLDA objects. As pure objects, these are explicitly destroyed
47     when the statement is destroyed.
48    
49     However, IResultSet is an interface and is returned when a cursor is opened and
50     has a reference for the TIBXOUTPUTSQLDA. The user may discard their reference
51     to the IStatement while still using the IResultSet. This would be a problem if t
52     he underlying TFB30Statement object and its TIBXOUTPUTSQLDA is destroyed while
53     still leaving the TIBXResultSet object in place. Calls to (e.g.) FetchNext would fail.
54    
55     To avoid this problem, TResultsSet objects have a reference to the IStatement
56     interface of the TFB30Statement object. Thus, as long as these "copies" exist,
57     the owning statement is not destroyed even if the user discards their reference
58     to the statement. Note: the TFB30Statement does not have a reference to the TIBXResultSet
59     interface. This way circular references are avoided.
60    
61     To avoid and IResultSet interface being kept to long and no longer synchronised
62     with the query, each statement includes a prepare sequence number, incremented
63     each time the query is prepared. When the IResultSet interface is created, it
64     noted the current prepare sequence number. Whe an IResult interface is accessed
65     it checks this number against the statement's current prepare sequence number.
66     If not the same, an error is raised.
67    
68     A similar strategy is used for the IMetaData, IResults and ISQLParams interfaces.
69     }
70    
71     interface
72    
73     uses
74     Classes, SysUtils, Firebird, IB, FBStatement, FB30ClientAPI, FB30Transaction,
75     FB30Attachment,IBExternals, FBSQLData, FBOutputBlock, FBActivityMonitor;
76    
77     type
78    
79     TFB30Statement = class;
80     TIBXSQLDA = class;
81    
82     { TIBXSQLVAR }
83    
84     TIBXSQLVAR = class(TSQLVarData)
85     private
86     FStatement: TFB30Statement;
87     FBlob: IBlob; {Cache references}
88     FArray: IArray;
89     FNullIndicator: short;
90     FOwnsSQLData: boolean;
91     FBlobMetaData: IBlobMetaData;
92     FArrayMetaData: IArrayMetaData;
93    
94     {SQL Var Type Data}
95     FSQLType: cardinal;
96     FSQLSubType: integer;
97     FSQLData: PChar; {Address of SQL Data in Message Buffer}
98     FSQLNullIndicator: PShort; {Address of null indicator}
99     FDataLength: integer;
100     FNullable: boolean;
101     FScale: integer;
102     FCharSetID: cardinal;
103     FRelationName: string;
104     FFieldName: string;
105    
106     protected
107     function GetSQLType: cardinal; override;
108     function GetSubtype: integer; override;
109     function GetAliasName: string; override;
110     function GetFieldName: string; override;
111     function GetOwnerName: string; override;
112     function GetRelationName: string; override;
113     function GetScale: integer; override;
114     function GetCharSetID: cardinal; override;
115     function GetCodePage: TSystemCodePage; override;
116     function GetIsNull: Boolean; override;
117     function GetIsNullable: boolean; override;
118     function GetSQLData: PChar; override;
119     function GetDataLength: cardinal; override;
120     procedure SetIsNull(Value: Boolean); override;
121     procedure SetIsNullable(Value: Boolean); override;
122     procedure SetSQLData(AValue: PChar; len: cardinal); override;
123     procedure SetScale(aValue: integer); override;
124     procedure SetDataLength(len: cardinal); override;
125     procedure SetSQLType(aValue: cardinal); override;
126     procedure SetCharSetID(aValue: cardinal); override;
127    
128     public
129     constructor Create(aParent: TIBXSQLDA; aIndex: integer);
130     procedure Changed; override;
131     procedure RowChange; override;
132     procedure FreeSQLData;
133     function GetAsArray(Array_ID: TISC_QUAD): IArray; override;
134     function GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob; override;
135     function GetArrayMetaData: IArrayMetaData; override;
136     function GetBlobMetaData: IBlobMetaData; override;
137     function CreateBlob: IBlob; override;
138     end;
139    
140     { TIBXSQLDA }
141    
142     TIBXSQLDA = class(TSQLDataArea)
143     private
144     FCount: Integer; {Columns in use - may be less than inherited columns}
145     FSize: Integer; {Number of TIBXSQLVARs in column list}
146     FMetaData: Firebird.IMessageMetadata;
147     FTransactionSeqNo: integer;
148     protected
149     FStatement: TFB30Statement;
150     function GetTransactionSeqNo: integer; override;
151     procedure FreeXSQLDA; virtual;
152     function GetStatement: IStatement; override;
153     function GetPrepareSeqNo: integer; override;
154     procedure SetCount(Value: Integer); override;
155     public
156     constructor Create(aStatement: TFB30Statement);
157     destructor Destroy; override;
158     procedure Changed; virtual;
159     function CheckStatementStatus(Request: TStatementStatus): boolean; override;
160     function ColumnsInUseCount: integer; override;
161     function GetTransaction: TFB30Transaction; virtual;
162     procedure Initialize; override;
163     function StateChanged(var ChangeSeqNo: integer): boolean; override;
164     property MetaData: Firebird.IMessageMetadata read FMetaData;
165     property Count: Integer read FCount write SetCount;
166     property Statement: TFB30Statement read FStatement;
167     end;
168    
169     { TIBXINPUTSQLDA }
170    
171     TIBXINPUTSQLDA = class(TIBXSQLDA)
172     private
173     FMessageBuffer: PChar; {Message Buffer}
174     FMsgLength: integer; {Message Buffer length}
175     FCurMetaData: Firebird.IMessageMetadata;
176     procedure FreeMessageBuffer;
177     function GetMessageBuffer: PChar;
178     function GetMetaData: Firebird.IMessageMetadata;
179     function GetModified: Boolean;
180     function GetMsgLength: integer;
181     procedure PackBuffer;
182     protected
183     procedure FreeXSQLDA; override;
184     public
185     constructor Create(aStatement: TFB30Statement);
186     destructor Destroy; override;
187     procedure Bind(aMetaData: Firebird.IMessageMetadata);
188     procedure Changed; override;
189     function IsInputDataArea: boolean; override;
190     property MetaData: Firebird.IMessageMetadata read GetMetaData;
191     property MessageBuffer: PChar read GetMessageBuffer;
192     property MsgLength: integer read GetMsgLength;
193     end;
194    
195     { TIBXOUTPUTSQLDA }
196    
197     TIBXOUTPUTSQLDA = class(TIBXSQLDA)
198     private
199     FTransaction: TFB30Transaction; {transaction used to execute the statement}
200     FMessageBuffer: PChar; {Message Buffer}
201     FMsgLength: integer; {Message Buffer length}
202     protected
203     procedure FreeXSQLDA; override;
204     public
205     procedure Bind(aMetaData: Firebird.IMessageMetadata);
206     procedure GetData(index: integer; var aIsNull: boolean; var len: short;
207     var data: PChar); override;
208     function IsInputDataArea: boolean; override;
209     property MessageBuffer: PChar read FMessageBuffer;
210     property MsgLength: integer read FMsgLength;
211     end;
212    
213     { TResultSet }
214    
215     TResultSet = class(TResults,IResultSet)
216     private
217     FResults: TIBXOUTPUTSQLDA;
218     FCursorSeqNo: integer;
219     public
220     constructor Create(aResults: TIBXOUTPUTSQLDA);
221     destructor Destroy; override;
222     {IResultSet}
223     function FetchNext: boolean;
224     function GetCursorName: string;
225     function GetTransaction: ITransaction; override;
226     function IsEof: boolean;
227     procedure Close;
228     end;
229    
230     { TFB30Statement }
231    
232     TFB30Statement = class(TFBStatement,IStatement)
233     private
234     FStatementIntf: Firebird.IStatement;
235     FSQLParams: TIBXINPUTSQLDA;
236     FSQLRecord: TIBXOUTPUTSQLDA;
237     FResultSet: Firebird.IResultSet;
238     FCursorSeqNo: integer;
239     protected
240     procedure CheckHandle; override;
241     procedure GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults); override;
242     procedure InternalPrepare; override;
243     function InternalExecute(aTransaction: ITransaction): IResults; override;
244     function InternalOpenCursor(aTransaction: ITransaction): IResultSet; override;
245     procedure FreeHandle; override;
246     procedure InternalClose(Force: boolean); override;
247     public
248     constructor Create(Attachment: TFB30Attachment; Transaction: ITransaction;
249     sql: string; aSQLDialect: integer);
250     constructor CreateWithParameterNames(Attachment: TFB30Attachment; Transaction: ITransaction;
251     sql: string; aSQLDialect: integer; GenerateParamNames: boolean =false);
252     destructor Destroy; override;
253     function FetchNext: boolean;
254     property StatementIntf: Firebird.IStatement read FStatementIntf;
255    
256     public
257     {IStatement}
258     function GetSQLParams: ISQLParams; override;
259     function GetMetaData: IMetaData; override;
260     function GetPlan: String;
261     function IsPrepared: boolean;
262     function CreateBlob(column: TColumnMetaData): IBlob; override;
263     function CreateArray(column: TColumnMetaData): IArray; override;
264     procedure SetRetainInterfaces(aValue: boolean); override;
265    
266     end;
267    
268     implementation
269    
270 tony 47 uses IBUtils, FBMessages, FBBLob, FB30Blob, variants, FBArray, FB30Array;
271 tony 45
272 tony 47 const
273     ISQL_COUNTERS = 'CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches';
274    
275 tony 45 { TIBXSQLVAR }
276    
277     procedure TIBXSQLVAR.Changed;
278     begin
279     inherited Changed;
280     TIBXSQLDA(Parent).Changed;
281     end;
282    
283     function TIBXSQLVAR.GetSQLType: cardinal;
284     begin
285     Result := FSQLType;
286     end;
287    
288     function TIBXSQLVAR.GetSubtype: integer;
289     begin
290     Result := FSQLSubType;
291     end;
292    
293     function TIBXSQLVAR.GetAliasName: string;
294     begin
295     with Firebird30ClientAPI do
296     begin
297     result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
298     Check4DataBaseError;
299     end;
300     end;
301    
302     function TIBXSQLVAR.GetFieldName: string;
303     begin
304     Result := FFieldName;
305     end;
306    
307     function TIBXSQLVAR.GetOwnerName: string;
308     begin
309     with Firebird30ClientAPI do
310     begin
311     result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
312     Check4DataBaseError;
313     end;
314     end;
315    
316     function TIBXSQLVAR.GetRelationName: string;
317     begin
318     Result := FRelationName;
319     end;
320    
321     function TIBXSQLVAR.GetScale: integer;
322     begin
323     Result := FScale;
324     end;
325    
326     function TIBXSQLVAR.GetCharSetID: cardinal;
327     begin
328     result := 0;
329     case SQLType of
330     SQL_VARYING, SQL_TEXT:
331     result := FCharSetID;
332    
333     SQL_BLOB:
334     if (SQLSubType = 1) then
335     result := FCharSetID;
336    
337     SQL_ARRAY:
338     if (FRelationName <> '') and (FFieldName <> '') then
339     result := GetArrayMetaData.GetCharSetID
340     else
341     result := FCharSetID;
342     end;
343     end;
344    
345     function TIBXSQLVAR.GetCodePage: TSystemCodePage;
346     begin
347     result := CP_NONE;
348     with Firebird30ClientAPI do
349     CharSetID2CodePage(GetCharSetID,result);
350     end;
351    
352     function TIBXSQLVAR.GetIsNull: Boolean;
353     begin
354     Result := IsNullable and (FSQLNullIndicator^ = -1);
355     end;
356    
357     function TIBXSQLVAR.GetIsNullable: boolean;
358     begin
359     Result := FSQLNullIndicator <> nil;
360     end;
361    
362     function TIBXSQLVAR.GetSQLData: PChar;
363     begin
364     Result := FSQLData;
365     end;
366    
367     function TIBXSQLVAR.GetDataLength: cardinal;
368     begin
369     Result := FDataLength;
370     end;
371    
372     function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
373     begin
374     if GetSQLType <> SQL_ARRAY then
375     IBError(ibxeInvalidDataConversion,[nil]);
376    
377     if FArrayMetaData = nil then
378     FArrayMetaData := TFB30ArrayMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
379     FStatement.GetTransaction as TFB30Transaction,
380     GetRelationName,GetFieldName);
381     Result := FArrayMetaData;
382     end;
383    
384     function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
385     begin
386     if GetSQLType <> SQL_BLOB then
387     IBError(ibxeInvalidDataConversion,[nil]);
388    
389     if FBlobMetaData = nil then
390     FBlobMetaData := TFB30BlobMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
391     FStatement.GetTransaction as TFB30Transaction,
392     GetRelationName,GetFieldName,
393     GetSubType);
394 tony 47 (FBlobMetaData as TFBBlobMetaData).SetCharSetID(GetCharSetID);
395 tony 45 Result := FBlobMetaData;
396     end;
397    
398     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
399     begin
400     if Value then
401     begin
402     IsNullable := true;
403     FNullIndicator := -1;
404     end
405     else
406     if IsNullable then
407     FNullIndicator := 0;
408 tony 47 Changed;
409 tony 45 end;
410    
411     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
412     begin
413     if Value = IsNullable then Exit;
414     if Value then
415     begin
416     FSQLNullIndicator := @FNullIndicator;
417     FNullIndicator := 0;
418     end
419     else
420     FSQLNullIndicator := nil;
421     end;
422    
423     procedure TIBXSQLVAR.SetSQLData(AValue: PChar; len: cardinal);
424     begin
425     if FOwnsSQLData then
426     FreeMem(FSQLData);
427     FSQLData := AValue;
428     FDataLength := len;
429     FOwnsSQLData := false;
430     end;
431    
432     procedure TIBXSQLVAR.SetScale(aValue: integer);
433     begin
434     FScale := aValue;
435     end;
436    
437     procedure TIBXSQLVAR.SetDataLength(len: cardinal);
438     begin
439     if not FOwnsSQLData then
440     FSQLData := nil;
441     FDataLength := len;
442     with Firebird30ClientAPI do
443     IBAlloc(FSQLData, 0, FDataLength);
444     FOwnsSQLData := true;
445     end;
446    
447     procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
448     begin
449     FSQLType := aValue;
450     end;
451    
452     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
453     begin
454     FCharSetID := aValue;
455     end;
456    
457     constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
458     begin
459     inherited Create(aParent,aIndex);
460     FStatement := aParent.Statement;
461     end;
462    
463     procedure TIBXSQLVAR.RowChange;
464     begin
465     inherited;
466     FBlob := nil;
467     FArray := nil;
468     end;
469    
470     procedure TIBXSQLVAR.FreeSQLData;
471     begin
472     if FOwnsSQLData then
473     FreeMem(FSQLData);
474     FSQLData := nil;
475     FOwnsSQLData := true;
476     end;
477    
478     function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
479     begin
480     if SQLType <> SQL_ARRAY then
481     IBError(ibxeInvalidDataConversion,[nil]);
482    
483     if IsNull then
484     Result := nil
485     else
486     begin
487     if FArray = nil then
488     FArray := TFB30Array.Create(FStatement.GetAttachment as TFB30Attachment,
489     TIBXSQLDA(Parent).GetTransaction,
490     GetArrayMetaData,Array_ID);
491     Result := FArray;
492     end;
493     end;
494    
495     function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
496     begin
497     if FBlob <> nil then
498     Result := FBlob
499     else
500     begin
501     if SQLType <> SQL_BLOB then
502     IBError(ibxeInvalidDataConversion, [nil]);
503     if IsNull then
504     Result := nil
505     else
506     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
507     TIBXSQLDA(Parent).GetTransaction,
508     GetBlobMetaData,
509     Blob_ID,BPB);
510     FBlob := Result;
511     end;
512     end;
513    
514     function TIBXSQLVAR.CreateBlob: IBlob;
515     begin
516     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
517     FStatement.GetTransaction as TFB30Transaction,
518     GetSubType,GetCharSetID,nil);
519     end;
520    
521     { TResultSet }
522    
523     constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
524     begin
525     inherited Create(aResults);
526     FResults := aResults;
527     FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
528     end;
529    
530     destructor TResultSet.Destroy;
531     begin
532     Close;
533     inherited Destroy;
534     end;
535    
536     function TResultSet.FetchNext: boolean;
537     var i: integer;
538     begin
539     CheckActive;
540     Result := FResults.FStatement.FetchNext;
541     if Result then
542     for i := 0 to getCount - 1 do
543     FResults.Column[i].RowChange;
544     end;
545    
546     function TResultSet.GetCursorName: string;
547     begin
548     IBError(ibxeNotSupported,[nil]);
549     Result := '';
550     end;
551    
552     function TResultSet.GetTransaction: ITransaction;
553     begin
554     Result := FResults.FTransaction;
555     end;
556    
557     function TResultSet.IsEof: boolean;
558     begin
559     Result := FResults.FStatement.FEof;
560     end;
561    
562     procedure TResultSet.Close;
563     begin
564     if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
565     FResults.FStatement.Close;
566     end;
567    
568     { TIBXINPUTSQLDA }
569    
570     function TIBXINPUTSQLDA.GetModified: Boolean;
571     var
572     i: Integer;
573     begin
574     result := False;
575     for i := 0 to FCount - 1 do
576     if Column[i].Modified then
577     begin
578     result := True;
579     exit;
580     end;
581     end;
582    
583     procedure TIBXINPUTSQLDA.FreeMessageBuffer;
584     begin
585     if FCurMetaData <> nil then
586     begin
587     FCurMetaData.release;
588     FCurMetaData := nil;
589     end;
590     if FMessageBuffer <> nil then
591     begin
592     FreeMem(FMessageBuffer);
593     FMessageBuffer := nil;
594     end;
595     FMsgLength := 0;
596     end;
597    
598     function TIBXINPUTSQLDA.GetMessageBuffer: PChar;
599     begin
600     PackBuffer;
601     Result := FMessageBuffer;
602     end;
603    
604     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
605     begin
606     PackBuffer;
607     Result := FCurMetaData;
608     end;
609    
610     function TIBXINPUTSQLDA.GetMsgLength: integer;
611     begin
612     PackBuffer;
613     Result := FMsgLength;
614     end;
615    
616     procedure TIBXINPUTSQLDA.PackBuffer;
617     var Builder: Firebird.IMetadataBuilder;
618     i: integer;
619     begin
620     if FMsgLength > 0 then Exit;
621    
622     with Firebird30ClientAPI do
623     begin
624     Builder := inherited MetaData.getBuilder(StatusIntf);
625     Check4DataBaseError;
626     try
627     for i := 0 to Count - 1 do
628     with TIBXSQLVar(Column[i]) do
629     begin
630     Builder.setType(StatusIntf,i,FSQLType);
631     Check4DataBaseError;
632     Builder.setSubType(StatusIntf,i,FSQLSubType);
633     Check4DataBaseError;
634     Builder.setLength(StatusIntf,i,FDataLength);
635     Check4DataBaseError;
636     Builder.setCharSet(StatusIntf,i,GetCharSetID);
637     Check4DataBaseError;
638     Builder.setScale(StatusIntf,i,FScale);
639     Check4DataBaseError;
640     end;
641     FCurMetaData := Builder.getMetadata(StatusIntf);
642     Check4DataBaseError;
643     finally
644     Builder.release;
645     end;
646    
647     FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
648     Check4DataBaseError;
649    
650     IBAlloc(FMessageBuffer,0,FMsgLength);
651    
652     for i := 0 to Count - 1 do
653     with TIBXSQLVar(Column[i]) do
654     begin
655 tony 47 if IsNull then
656     FillChar((FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength,0)
657     else
658     Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
659 tony 45 Check4DataBaseError;
660     if IsNullable then
661     begin
662     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
663     Check4DataBaseError;
664     end;
665     end;
666     end;
667     end;
668    
669     procedure TIBXINPUTSQLDA.FreeXSQLDA;
670     begin
671     inherited FreeXSQLDA;
672     FreeMessageBuffer;
673     end;
674    
675     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
676     begin
677     inherited Create(aStatement);
678     FMessageBuffer := nil;
679     end;
680    
681     destructor TIBXINPUTSQLDA.Destroy;
682     begin
683     FreeMessageBuffer;
684     inherited Destroy;
685     end;
686    
687     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
688     var i: integer;
689     begin
690     FMetaData := aMetaData;
691     with Firebird30ClientAPI do
692     begin
693     Count := metadata.getCount(StatusIntf);
694     Check4DataBaseError;
695     Initialize;
696    
697     for i := 0 to Count - 1 do
698     with TIBXSQLVar(Column[i]) do
699     begin
700     FSQLType := aMetaData.getType(StatusIntf,i);
701     Check4DataBaseError;
702     if FSQLType = SQL_BLOB then
703     begin
704     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
705     Check4DataBaseError;
706     end
707     else
708     FSQLSubType := 0;
709     FDataLength := aMetaData.getLength(StatusIntf,i);
710     Check4DataBaseError;
711     case SQLType of
712     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
713     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
714     SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
715     begin
716     if (FDataLength = 0) then
717     { Make sure you get a valid pointer anyway
718     select '' from foo }
719     IBAlloc(FSQLData, 0, 1)
720     else
721     IBAlloc(FSQLData, 0, FDataLength)
722     end;
723 tony 47 SQL_VARYING:
724 tony 45 IBAlloc(FSQLData, 0, FDataLength + 2);
725     else
726     IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
727     end;
728     FNullable := aMetaData.isNullable(StatusIntf,i);
729     FOwnsSQLData := true;
730     Check4DataBaseError;
731     FNullIndicator := -1;
732     if FNullable then
733     FSQLNullIndicator := @FNullIndicator
734     else
735     FSQLNullIndicator := nil;
736     FScale := aMetaData.getScale(StatusIntf,i);
737     Check4DataBaseError;
738     FCharSetID := aMetaData.getCharSet(StatusIntf,i);
739     Check4DataBaseError;
740     end;
741     end;
742     end;
743    
744     procedure TIBXINPUTSQLDA.Changed;
745     begin
746     inherited Changed;
747     FreeMessageBuffer;
748     end;
749    
750     function TIBXINPUTSQLDA.IsInputDataArea: boolean;
751     begin
752     Result := true;
753     end;
754    
755     { TIBXOUTPUTSQLDA }
756    
757     procedure TIBXOUTPUTSQLDA.FreeXSQLDA;
758     begin
759     inherited FreeXSQLDA;
760     FreeMem(FMessageBuffer);
761     FMessageBuffer := nil;
762     FMsgLength := 0;
763     end;
764    
765     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
766     var i: integer;
767     begin
768     FMetaData := aMetaData;
769     with Firebird30ClientAPI do
770     begin
771     Count := metadata.getCount(StatusIntf);
772     Check4DataBaseError;
773     Initialize;
774    
775     FMsgLength := metaData.getMessageLength(StatusIntf);
776     Check4DataBaseError;
777     IBAlloc(FMessageBuffer,0,FMsgLength);
778    
779     for i := 0 to Count - 1 do
780     with TIBXSQLVar(Column[i]) do
781     begin
782     FSQLType := aMetaData.getType(StatusIntf,i);
783     Check4DataBaseError;
784     if FSQLType = SQL_BLOB then
785     begin
786     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
787     Check4DataBaseError;
788     end
789     else
790     FSQLSubType := 0;
791     FBlob := nil;
792     FArray := nil;
793     FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
794     Check4DataBaseError;
795     FDataLength := aMetaData.getLength(StatusIntf,i);
796     Check4DataBaseError;
797     FRelationName := strpas(aMetaData.getRelation(StatusIntf,i));
798     Check4DataBaseError;
799     FFieldName := strpas(aMetaData.getField(StatusIntf,i));
800     Check4DataBaseError;
801     FNullable := aMetaData.isNullable(StatusIntf,i);
802     Check4DataBaseError;
803     if FNullable then
804     begin
805     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
806     Check4DataBaseError;
807     end
808     else
809     FSQLNullIndicator := nil;
810     FScale := aMetaData.getScale(StatusIntf,i);
811     Check4DataBaseError;
812     FCharSetID := aMetaData.getCharSet(StatusIntf,i);
813     Check4DataBaseError;
814     end;
815     end;
816     SetUniqueRelationName;
817     end;
818    
819     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
820     var len: short; var data: PChar);
821     begin
822     with TIBXSQLVAR(Column[index]) do
823     begin
824     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
825     data := FSQLData;
826     len := FDataLength;
827     if not IsNull and (FSQLType = SQL_VARYING) then
828     begin
829     with Firebird30ClientAPI do
830     len := DecodeInteger(data,2);
831     Inc(Data,2);
832     end;
833     end;
834     end;
835    
836     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
837     begin
838     Result := false;
839     end;
840    
841     { TIBXSQLDA }
842     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
843     begin
844     inherited Create;
845     FStatement := aStatement;
846     FSize := 0;
847     // writeln('Creating ',ClassName);
848     end;
849    
850     destructor TIBXSQLDA.Destroy;
851     begin
852     FreeXSQLDA;
853     // writeln('Destroying ',ClassName);
854     inherited Destroy;
855     end;
856    
857     procedure TIBXSQLDA.Changed;
858     begin
859    
860     end;
861    
862     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
863     begin
864     Result := false;
865     case Request of
866     ssPrepared:
867     Result := FStatement.IsPrepared;
868    
869     ssExecuteResults:
870     Result :=FStatement.FSingleResults;
871    
872     ssCursorOpen:
873     Result := FStatement.FOpen;
874    
875     ssBOF:
876     Result := FStatement.FBOF;
877    
878     ssEOF:
879     Result := FStatement.FEOF;
880     end;
881     end;
882    
883     function TIBXSQLDA.ColumnsInUseCount: integer;
884     begin
885     Result := FCount;
886     end;
887    
888     function TIBXSQLDA.GetTransaction: TFB30Transaction;
889     begin
890     Result := FStatement.GetTransaction as TFB30Transaction;
891     end;
892    
893     procedure TIBXSQLDA.Initialize;
894     begin
895     if FMetaData <> nil then
896     inherited Initialize;
897     end;
898    
899     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
900     begin
901     Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
902     if Result then
903     ChangeSeqNo := FStatement.ChangeSeqNo;
904     end;
905    
906     procedure TIBXSQLDA.SetCount(Value: Integer);
907     var
908     i: Integer;
909     begin
910     FCount := Value;
911     if FCount = 0 then
912     FUniqueRelationName := ''
913     else
914     begin
915     SetLength(FColumnList, FCount);
916     for i := FSize to FCount - 1 do
917     FColumnList[i] := TIBXSQLVAR.Create(self,i);
918     FSize := FCount;
919     end;
920     end;
921    
922     function TIBXSQLDA.GetTransactionSeqNo: integer;
923     begin
924     Result := FTransactionSeqNo;
925     end;
926    
927     procedure TIBXSQLDA.FreeXSQLDA;
928     var i: integer;
929     begin
930     if FMetaData <> nil then
931     FMetaData.release;
932     FMetaData := nil;
933     for i := 0 to Count - 1 do
934     TIBXSQLVAR(Column[i]).FreeSQLData;
935     for i := 0 to FSize - 1 do
936     TIBXSQLVAR(Column[i]).Free;
937     SetLength(FColumnList,0);
938     FSize := 0;
939     end;
940    
941     function TIBXSQLDA.GetStatement: IStatement;
942     begin
943     Result := FStatement;
944     end;
945    
946     function TIBXSQLDA.GetPrepareSeqNo: integer;
947     begin
948     Result := FStatement.FPrepareSeqNo;
949     end;
950    
951     { TFB30Statement }
952    
953     procedure TFB30Statement.CheckHandle;
954     begin
955     if FStatementIntf = nil then
956     IBError(ibxeInvalidStatementHandle,[nil]);
957     end;
958    
959     procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
960     );
961     begin
962     with Firebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
963     begin
964     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
965     GetBufSize, BytePtr(Buffer));
966     Check4DataBaseError;
967     end;
968     end;
969    
970     procedure TFB30Statement.InternalPrepare;
971     begin
972     if FPrepared then
973     Exit;
974     if (FSQL = '') then
975     IBError(ibxeEmptyQuery, [nil]);
976     try
977     CheckTransaction(FTransactionIntf);
978     with Firebird30ClientAPI do
979     begin
980     if FHasParamNames then
981     begin
982     if FProcessedSQL = '' then
983     FSQLParams.PreprocessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
984     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
985     (FTransactionIntf as TFB30Transaction).TransactionIntf,
986     Length(FProcessedSQL),
987     PChar(FProcessedSQL),
988     FSQLDialect,
989     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
990     end
991     else
992     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
993     (FTransactionIntf as TFB30Transaction).TransactionIntf,
994     Length(FSQL),
995     PChar(FSQL),
996     FSQLDialect,
997     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
998     Check4DataBaseError;
999     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
1000     Check4DataBaseError;
1001    
1002     { Done getting the type }
1003     case FSQLStatementType of
1004     SQLGetSegment,
1005     SQLPutSegment,
1006     SQLStartTransaction:
1007     begin
1008     FreeHandle;
1009     IBError(ibxeNotPermitted, [nil]);
1010     end;
1011     SQLCommit,
1012     SQLRollback,
1013     SQLDDL, SQLSetGenerator,
1014     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1015     SQLExecProcedure:
1016     begin
1017     {set up input sqlda}
1018     FSQLParams.Bind(FStatementIntf.getInputMetadata(StatusIntf));
1019     Check4DataBaseError;
1020    
1021     {setup output sqlda}
1022     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1023     SQLExecProcedure] then
1024     FSQLRecord.Bind(FStatementIntf.getOutputMetadata(StatusIntf));
1025     Check4DataBaseError;
1026     end;
1027     end;
1028     end;
1029     except
1030     on E: Exception do begin
1031     if (FStatementIntf <> nil) then
1032     FreeHandle;
1033     if E is EIBInterBaseError then
1034     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
1035     EIBInterBaseError(E).IBErrorCode,
1036     EIBInterBaseError(E).Message +
1037     sSQLErrorSeparator + FSQL)
1038     else
1039     raise;
1040     end;
1041     end;
1042     FPrepared := true;
1043     FSingleResults := false;
1044     if RetainInterfaces then
1045     begin
1046     SetRetainInterfaces(false);
1047     SetRetainInterfaces(true);
1048     end;
1049     Inc(FPrepareSeqNo);
1050     with GetTransaction as TFB30Transaction do
1051     begin
1052     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1053     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1054     end;
1055     SignalActivity;
1056     Inc(FChangeSeqNo);
1057     end;
1058    
1059     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1060     begin
1061     Result := nil;
1062     FBOF := false;
1063     FEOF := false;
1064     FSingleResults := false;
1065     CheckTransaction(aTransaction);
1066     if not FPrepared then
1067     InternalPrepare;
1068     CheckHandle;
1069     if aTransaction <> FTransactionIntf then
1070     AddMonitor(aTransaction as TFB30Transaction);
1071     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1072     IBError(ibxeInterfaceOutofDate,[nil]);
1073    
1074     try
1075     with Firebird30ClientAPI do
1076     begin
1077 tony 47 if FCollectStatistics then
1078     begin
1079     UtilIntf.getPerfCounters(StatusIntf,
1080     (GetAttachment as TFB30Attachment).AttachmentIntf,
1081     ISQL_COUNTERS,@FBeforeStats);
1082     Check4DataBaseError;
1083     end;
1084 tony 45
1085 tony 47 case FSQLStatementType of
1086     SQLSelect:
1087     IBError(ibxeIsAExecuteProcedure,[]);
1088    
1089     SQLExecProcedure:
1090     begin
1091     FStatementIntf.execute(StatusIntf,
1092     (aTransaction as TFB30Transaction).TransactionIntf,
1093     FSQLParams.MetaData,
1094     FSQLParams.MessageBuffer,
1095     FSQLRecord.MetaData,
1096     FSQLRecord.MessageBuffer);
1097     Check4DataBaseError;
1098    
1099     Result := TResults.Create(FSQLRecord);
1100     FSingleResults := true;
1101     end
1102     else
1103     FStatementIntf.execute(StatusIntf,
1104     (aTransaction as TFB30Transaction).TransactionIntf,
1105     FSQLParams.MetaData,
1106     FSQLParams.MessageBuffer,
1107     nil,
1108     nil);
1109     Check4DataBaseError;
1110     end;
1111     if FCollectStatistics then
1112     begin
1113     UtilIntf.getPerfCounters(StatusIntf,
1114     (GetAttachment as TFB30Attachment).AttachmentIntf,
1115     ISQL_COUNTERS, @FAfterStats);
1116     Check4DataBaseError;
1117     FStatisticsAvailable := true;
1118     end;
1119 tony 45 end;
1120     finally
1121     if aTransaction <> FTransactionIntf then
1122     RemoveMonitor(aTransaction as TFB30Transaction);
1123     end;
1124     FExecTransactionIntf := aTransaction;
1125     SignalActivity;
1126     Inc(FChangeSeqNo);
1127     end;
1128    
1129     function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction
1130     ): IResultSet;
1131     begin
1132     if FSQLStatementType <> SQLSelect then
1133     IBError(ibxeIsASelectStatement,[]);
1134    
1135     CheckTransaction(aTransaction);
1136     if not FPrepared then
1137     InternalPrepare;
1138     CheckHandle;
1139     if aTransaction <> FTransactionIntf then
1140     AddMonitor(aTransaction as TFB30Transaction);
1141     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1142     IBError(ibxeInterfaceOutofDate,[nil]);
1143    
1144     with Firebird30ClientAPI do
1145     begin
1146 tony 47 if FCollectStatistics then
1147     begin
1148     UtilIntf.getPerfCounters(StatusIntf,
1149     (GetAttachment as TFB30Attachment).AttachmentIntf,
1150     ISQL_COUNTERS, @FBeforeStats);
1151     Check4DataBaseError;
1152     end;
1153    
1154 tony 45 FResultSet := FStatementIntf.openCursor(StatusIntf,
1155     (aTransaction as TFB30Transaction).TransactionIntf,
1156     FSQLParams.MetaData,
1157     FSQLParams.MessageBuffer,
1158     FSQLRecord.MetaData,
1159     0);
1160     Check4DataBaseError;
1161 tony 47
1162     if FCollectStatistics then
1163     begin
1164     UtilIntf.getPerfCounters(StatusIntf,
1165     (GetAttachment as TFB30Attachment).AttachmentIntf,
1166     ISQL_COUNTERS,@FAfterStats);
1167     Check4DataBaseError;
1168     FStatisticsAvailable := true;
1169     end;
1170 tony 45 end;
1171     Inc(FCursorSeqNo);
1172     FSingleResults := false;
1173     FOpen := True;
1174     FExecTransactionIntf := aTransaction;
1175     FBOF := true;
1176     FEOF := false;
1177     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1178     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1179     Result := TResultSet.Create(FSQLRecord);
1180     SignalActivity;
1181     Inc(FChangeSeqNo);
1182     end;
1183    
1184     procedure TFB30Statement.FreeHandle;
1185     begin
1186     Close;
1187     ReleaseInterfaces;
1188     if FStatementIntf <> nil then
1189     begin
1190     FStatementIntf.release;
1191     FStatementIntf := nil;
1192     FPrepared := false;
1193     end;
1194     end;
1195    
1196     procedure TFB30Statement.InternalClose(Force: boolean);
1197     begin
1198     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1199     try
1200     with Firebird30ClientAPI do
1201     begin
1202     if FResultSet <> nil then
1203     begin
1204     if FSQLRecord.FTransaction.InTransaction and
1205     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1206     FResultSet.close(StatusIntf)
1207     else
1208     FResultSet.release;
1209     end;
1210     FResultSet := nil;
1211     if not Force then Check4DataBaseError;
1212     end;
1213     finally
1214     if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> FTransactionIntf) then
1215     RemoveMonitor(FSQLRecord.FTransaction);
1216     FOpen := False;
1217     FExecTransactionIntf := nil;
1218     FSQLRecord.FTransaction := nil;
1219     end;
1220     SignalActivity;
1221     Inc(FChangeSeqNo);
1222     end;
1223    
1224     constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1225     Transaction: ITransaction; sql: string; aSQLDialect: integer);
1226     begin
1227     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1228     FSQLParams := TIBXINPUTSQLDA.Create(self);
1229     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1230     InternalPrepare;
1231     end;
1232    
1233     constructor TFB30Statement.CreateWithParameterNames(
1234     Attachment: TFB30Attachment; Transaction: ITransaction; sql: string;
1235     aSQLDialect: integer; GenerateParamNames: boolean);
1236     begin
1237     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1238     FSQLParams := TIBXINPUTSQLDA.Create(self);
1239     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1240     InternalPrepare;
1241     end;
1242    
1243     destructor TFB30Statement.Destroy;
1244     begin
1245     inherited Destroy;
1246     if assigned(FSQLParams) then FSQLParams.Free;
1247     if assigned(FSQLRecord) then FSQLRecord.Free;
1248     end;
1249    
1250     function TFB30Statement.FetchNext: boolean;
1251     var fetchResult: integer;
1252     begin
1253     result := false;
1254     if not FOpen then
1255     IBError(ibxeSQLClosed, [nil]);
1256     if FEOF then
1257     IBError(ibxeEOF,[nil]);
1258    
1259     with Firebird30ClientAPI do
1260     begin
1261     { Go to the next record... }
1262     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1263     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1264     begin
1265     FBOF := false;
1266     FEOF := true;
1267     Exit; {End of File}
1268     end
1269     else
1270     if fetchResult <> Firebird.IStatus.RESULT_OK then
1271     begin
1272     try
1273     IBDataBaseError;
1274     except
1275     Close;
1276     raise;
1277     end;
1278     end
1279     else
1280     begin
1281     FBOF := false;
1282     result := true;
1283     end;
1284     end;
1285     FSQLRecord.RowChange;
1286     SignalActivity;
1287     if FEOF then
1288     Inc(FChangeSeqNo);
1289     end;
1290    
1291     function TFB30Statement.GetSQLParams: ISQLParams;
1292     begin
1293     CheckHandle;
1294     if not HasInterface(0) then
1295     AddInterface(0,TSQLParams.Create(FSQLParams));
1296     Result := TSQLParams(GetInterface(0));
1297     end;
1298    
1299     function TFB30Statement.GetMetaData: IMetaData;
1300     begin
1301     CheckHandle;
1302     if not HasInterface(1) then
1303     AddInterface(1, TMetaData.Create(FSQLRecord));
1304     Result := TMetaData(GetInterface(1));
1305     end;
1306    
1307     function TFB30Statement.GetPlan: String;
1308     begin
1309     CheckHandle;
1310     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1311     {TODO: SQLExecProcedure, }
1312     SQLUpdate, SQLDelete])) then
1313     result := ''
1314     else
1315     with Firebird30ClientAPI do
1316     begin
1317     Result := FStatementIntf.getPlan(StatusIntf,true);
1318     Check4DataBaseError;
1319     end;
1320     end;
1321    
1322     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1323     begin
1324     if assigned(column) and (column.SQLType <> SQL_Blob) then
1325     IBError(ibxeNotABlob,[nil]);
1326     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1327     GetTransaction as TFB30Transaction,
1328     column.GetBlobMetaData,nil);
1329     end;
1330    
1331     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1332     begin
1333     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1334     IBError(ibxeNotAnArray,[nil]);
1335     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1336     GetTransaction as TFB30Transaction,
1337     column.GetArrayMetaData);
1338     end;
1339    
1340     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1341     begin
1342     inherited SetRetainInterfaces(aValue);
1343     if HasInterface(1) then
1344     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1345     if HasInterface(0) then
1346     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1347     end;
1348    
1349     function TFB30Statement.IsPrepared: boolean;
1350     begin
1351     Result := FStatementIntf <> nil;
1352     end;
1353    
1354     end.
1355