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: 45
Committed: Tue Dec 6 10:33:46 2016 UTC (7 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 35998 byte(s)
Log Message:
Committing updates for Release R2-0-0

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     uses IBUtils, FBMessages, FB30Blob, variants, FBArray, FB30Array;
271    
272     { TIBXSQLVAR }
273    
274     procedure TIBXSQLVAR.Changed;
275     begin
276     inherited Changed;
277     TIBXSQLDA(Parent).Changed;
278     end;
279    
280     function TIBXSQLVAR.GetSQLType: cardinal;
281     begin
282     Result := FSQLType;
283     end;
284    
285     function TIBXSQLVAR.GetSubtype: integer;
286     begin
287     Result := FSQLSubType;
288     end;
289    
290     function TIBXSQLVAR.GetAliasName: string;
291     begin
292     with Firebird30ClientAPI do
293     begin
294     result := strpas(TIBXSQLDA(Parent).MetaData.getAlias(StatusIntf,Index));
295     Check4DataBaseError;
296     end;
297     end;
298    
299     function TIBXSQLVAR.GetFieldName: string;
300     begin
301     Result := FFieldName;
302     end;
303    
304     function TIBXSQLVAR.GetOwnerName: string;
305     begin
306     with Firebird30ClientAPI do
307     begin
308     result := strpas(TIBXSQLDA(Parent).MetaData.getOwner(StatusIntf,Index));
309     Check4DataBaseError;
310     end;
311     end;
312    
313     function TIBXSQLVAR.GetRelationName: string;
314     begin
315     Result := FRelationName;
316     end;
317    
318     function TIBXSQLVAR.GetScale: integer;
319     begin
320     Result := FScale;
321     end;
322    
323     function TIBXSQLVAR.GetCharSetID: cardinal;
324     begin
325     result := 0;
326     case SQLType of
327     SQL_VARYING, SQL_TEXT:
328     result := FCharSetID;
329    
330     SQL_BLOB:
331     if (SQLSubType = 1) then
332     result := FCharSetID;
333    
334     SQL_ARRAY:
335     if (FRelationName <> '') and (FFieldName <> '') then
336     result := GetArrayMetaData.GetCharSetID
337     else
338     result := FCharSetID;
339     end;
340     end;
341    
342     function TIBXSQLVAR.GetCodePage: TSystemCodePage;
343     begin
344     result := CP_NONE;
345     with Firebird30ClientAPI do
346     CharSetID2CodePage(GetCharSetID,result);
347     end;
348    
349     function TIBXSQLVAR.GetIsNull: Boolean;
350     begin
351     Result := IsNullable and (FSQLNullIndicator^ = -1);
352     end;
353    
354     function TIBXSQLVAR.GetIsNullable: boolean;
355     begin
356     Result := FSQLNullIndicator <> nil;
357     end;
358    
359     function TIBXSQLVAR.GetSQLData: PChar;
360     begin
361     Result := FSQLData;
362     end;
363    
364     function TIBXSQLVAR.GetDataLength: cardinal;
365     begin
366     Result := FDataLength;
367     end;
368    
369     function TIBXSQLVAR.GetArrayMetaData: IArrayMetaData;
370     begin
371     if GetSQLType <> SQL_ARRAY then
372     IBError(ibxeInvalidDataConversion,[nil]);
373    
374     if FArrayMetaData = nil then
375     FArrayMetaData := TFB30ArrayMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
376     FStatement.GetTransaction as TFB30Transaction,
377     GetRelationName,GetFieldName);
378     Result := FArrayMetaData;
379     end;
380    
381     function TIBXSQLVAR.GetBlobMetaData: IBlobMetaData;
382     begin
383     if GetSQLType <> SQL_BLOB then
384     IBError(ibxeInvalidDataConversion,[nil]);
385    
386     if FBlobMetaData = nil then
387     FBlobMetaData := TFB30BlobMetaData.Create(FStatement.GetAttachment as TFB30Attachment,
388     FStatement.GetTransaction as TFB30Transaction,
389     GetRelationName,GetFieldName,
390     GetSubType);
391     Result := FBlobMetaData;
392     end;
393    
394     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
395     begin
396     if Value then
397     begin
398     IsNullable := true;
399     FNullIndicator := -1;
400     end
401     else
402     if IsNullable then
403     FNullIndicator := 0;
404     end;
405    
406     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
407     begin
408     if Value = IsNullable then Exit;
409     if Value then
410     begin
411     FSQLNullIndicator := @FNullIndicator;
412     FNullIndicator := 0;
413     end
414     else
415     FSQLNullIndicator := nil;
416     end;
417    
418     procedure TIBXSQLVAR.SetSQLData(AValue: PChar; len: cardinal);
419     begin
420     if FOwnsSQLData then
421     FreeMem(FSQLData);
422     FSQLData := AValue;
423     FDataLength := len;
424     FOwnsSQLData := false;
425     end;
426    
427     procedure TIBXSQLVAR.SetScale(aValue: integer);
428     begin
429     FScale := aValue;
430     end;
431    
432     procedure TIBXSQLVAR.SetDataLength(len: cardinal);
433     begin
434     if not FOwnsSQLData then
435     FSQLData := nil;
436     FDataLength := len;
437     with Firebird30ClientAPI do
438     IBAlloc(FSQLData, 0, FDataLength);
439     FOwnsSQLData := true;
440     end;
441    
442     procedure TIBXSQLVAR.SetSQLType(aValue: cardinal);
443     begin
444     FSQLType := aValue;
445     end;
446    
447     procedure TIBXSQLVAR.SetCharSetID(aValue: cardinal);
448     begin
449     FCharSetID := aValue;
450     end;
451    
452     constructor TIBXSQLVAR.Create(aParent: TIBXSQLDA; aIndex: integer);
453     begin
454     inherited Create(aParent,aIndex);
455     FStatement := aParent.Statement;
456     end;
457    
458     procedure TIBXSQLVAR.RowChange;
459     begin
460     inherited;
461     FBlob := nil;
462     FArray := nil;
463     end;
464    
465     procedure TIBXSQLVAR.FreeSQLData;
466     begin
467     if FOwnsSQLData then
468     FreeMem(FSQLData);
469     FSQLData := nil;
470     FOwnsSQLData := true;
471     end;
472    
473     function TIBXSQLVAR.GetAsArray(Array_ID: TISC_QUAD): IArray;
474     begin
475     if SQLType <> SQL_ARRAY then
476     IBError(ibxeInvalidDataConversion,[nil]);
477    
478     if IsNull then
479     Result := nil
480     else
481     begin
482     if FArray = nil then
483     FArray := TFB30Array.Create(FStatement.GetAttachment as TFB30Attachment,
484     TIBXSQLDA(Parent).GetTransaction,
485     GetArrayMetaData,Array_ID);
486     Result := FArray;
487     end;
488     end;
489    
490     function TIBXSQLVAR.GetAsBlob(Blob_ID: TISC_QUAD; BPB: IBPB): IBlob;
491     begin
492     if FBlob <> nil then
493     Result := FBlob
494     else
495     begin
496     if SQLType <> SQL_BLOB then
497     IBError(ibxeInvalidDataConversion, [nil]);
498     if IsNull then
499     Result := nil
500     else
501     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
502     TIBXSQLDA(Parent).GetTransaction,
503     GetBlobMetaData,
504     Blob_ID,BPB);
505     FBlob := Result;
506     end;
507     end;
508    
509     function TIBXSQLVAR.CreateBlob: IBlob;
510     begin
511     Result := TFB30Blob.Create(FStatement.GetAttachment as TFB30Attachment,
512     FStatement.GetTransaction as TFB30Transaction,
513     GetSubType,GetCharSetID,nil);
514     end;
515    
516     { TResultSet }
517    
518     constructor TResultSet.Create(aResults: TIBXOUTPUTSQLDA);
519     begin
520     inherited Create(aResults);
521     FResults := aResults;
522     FCursorSeqNo := aResults.FStatement.FCursorSeqNo;
523     end;
524    
525     destructor TResultSet.Destroy;
526     begin
527     Close;
528     inherited Destroy;
529     end;
530    
531     function TResultSet.FetchNext: boolean;
532     var i: integer;
533     begin
534     CheckActive;
535     Result := FResults.FStatement.FetchNext;
536     if Result then
537     for i := 0 to getCount - 1 do
538     FResults.Column[i].RowChange;
539     end;
540    
541     function TResultSet.GetCursorName: string;
542     begin
543     IBError(ibxeNotSupported,[nil]);
544     Result := '';
545     end;
546    
547     function TResultSet.GetTransaction: ITransaction;
548     begin
549     Result := FResults.FTransaction;
550     end;
551    
552     function TResultSet.IsEof: boolean;
553     begin
554     Result := FResults.FStatement.FEof;
555     end;
556    
557     procedure TResultSet.Close;
558     begin
559     if FCursorSeqNo = FResults.FStatement.FCursorSeqNo then
560     FResults.FStatement.Close;
561     end;
562    
563     { TIBXINPUTSQLDA }
564    
565     function TIBXINPUTSQLDA.GetModified: Boolean;
566     var
567     i: Integer;
568     begin
569     result := False;
570     for i := 0 to FCount - 1 do
571     if Column[i].Modified then
572     begin
573     result := True;
574     exit;
575     end;
576     end;
577    
578     procedure TIBXINPUTSQLDA.FreeMessageBuffer;
579     begin
580     if FCurMetaData <> nil then
581     begin
582     FCurMetaData.release;
583     FCurMetaData := nil;
584     end;
585     if FMessageBuffer <> nil then
586     begin
587     FreeMem(FMessageBuffer);
588     FMessageBuffer := nil;
589     end;
590     FMsgLength := 0;
591     end;
592    
593     function TIBXINPUTSQLDA.GetMessageBuffer: PChar;
594     begin
595     PackBuffer;
596     Result := FMessageBuffer;
597     end;
598    
599     function TIBXINPUTSQLDA.GetMetaData: Firebird.IMessageMetadata;
600     begin
601     PackBuffer;
602     Result := FCurMetaData;
603     end;
604    
605     function TIBXINPUTSQLDA.GetMsgLength: integer;
606     begin
607     PackBuffer;
608     Result := FMsgLength;
609     end;
610    
611     procedure TIBXINPUTSQLDA.PackBuffer;
612     var Builder: Firebird.IMetadataBuilder;
613     i: integer;
614     begin
615     if FMsgLength > 0 then Exit;
616    
617     with Firebird30ClientAPI do
618     begin
619     Builder := inherited MetaData.getBuilder(StatusIntf);
620     Check4DataBaseError;
621     try
622     for i := 0 to Count - 1 do
623     with TIBXSQLVar(Column[i]) do
624     begin
625     Builder.setType(StatusIntf,i,FSQLType);
626     Check4DataBaseError;
627     Builder.setSubType(StatusIntf,i,FSQLSubType);
628     Check4DataBaseError;
629     Builder.setLength(StatusIntf,i,FDataLength);
630     Check4DataBaseError;
631     Builder.setCharSet(StatusIntf,i,GetCharSetID);
632     Check4DataBaseError;
633     Builder.setScale(StatusIntf,i,FScale);
634     Check4DataBaseError;
635     end;
636     FCurMetaData := Builder.getMetadata(StatusIntf);
637     Check4DataBaseError;
638     finally
639     Builder.release;
640     end;
641    
642     FMsgLength := FCurMetaData.getMessageLength(StatusIntf);
643     Check4DataBaseError;
644    
645     IBAlloc(FMessageBuffer,0,FMsgLength);
646    
647     for i := 0 to Count - 1 do
648     with TIBXSQLVar(Column[i]) do
649     begin
650     Move(FSQLData^,(FMessageBuffer + FCurMetaData.getOffset(StatusIntf,i))^,FDataLength);
651     Check4DataBaseError;
652     if IsNullable then
653     begin
654     Move(FNullIndicator,(FMessageBuffer + FCurMetaData.getNullOffset(StatusIntf,i))^,sizeof(FNullIndicator));
655     Check4DataBaseError;
656     end;
657     end;
658     end;
659     end;
660    
661     procedure TIBXINPUTSQLDA.FreeXSQLDA;
662     begin
663     inherited FreeXSQLDA;
664     FreeMessageBuffer;
665     end;
666    
667     constructor TIBXINPUTSQLDA.Create(aStatement: TFB30Statement);
668     begin
669     inherited Create(aStatement);
670     FMessageBuffer := nil;
671     end;
672    
673     destructor TIBXINPUTSQLDA.Destroy;
674     begin
675     FreeMessageBuffer;
676     inherited Destroy;
677     end;
678    
679     procedure TIBXINPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
680     var i: integer;
681     begin
682     FMetaData := aMetaData;
683     with Firebird30ClientAPI do
684     begin
685     Count := metadata.getCount(StatusIntf);
686     Check4DataBaseError;
687     Initialize;
688    
689     for i := 0 to Count - 1 do
690     with TIBXSQLVar(Column[i]) do
691     begin
692     FSQLType := aMetaData.getType(StatusIntf,i);
693     Check4DataBaseError;
694     if FSQLType = SQL_BLOB then
695     begin
696     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
697     Check4DataBaseError;
698     end
699     else
700     FSQLSubType := 0;
701     FDataLength := aMetaData.getLength(StatusIntf,i);
702     Check4DataBaseError;
703     case SQLType of
704     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
705     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT, SQL_BOOLEAN,
706     SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
707     begin
708     if (FDataLength = 0) then
709     { Make sure you get a valid pointer anyway
710     select '' from foo }
711     IBAlloc(FSQLData, 0, 1)
712     else
713     IBAlloc(FSQLData, 0, FDataLength)
714     end;
715     SQL_VARYING: begin
716     IBAlloc(FSQLData, 0, FDataLength + 2);
717     end;
718     else
719     IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
720     end;
721     FNullable := aMetaData.isNullable(StatusIntf,i);
722     FOwnsSQLData := true;
723     Check4DataBaseError;
724     FNullIndicator := -1;
725     if FNullable then
726     FSQLNullIndicator := @FNullIndicator
727     else
728     FSQLNullIndicator := nil;
729     FScale := aMetaData.getScale(StatusIntf,i);
730     Check4DataBaseError;
731     FCharSetID := aMetaData.getCharSet(StatusIntf,i);
732     Check4DataBaseError;
733     end;
734     end;
735     end;
736    
737     procedure TIBXINPUTSQLDA.Changed;
738     begin
739     inherited Changed;
740     FreeMessageBuffer;
741     end;
742    
743     function TIBXINPUTSQLDA.IsInputDataArea: boolean;
744     begin
745     Result := true;
746     end;
747    
748     { TIBXOUTPUTSQLDA }
749    
750     procedure TIBXOUTPUTSQLDA.FreeXSQLDA;
751     begin
752     inherited FreeXSQLDA;
753     FreeMem(FMessageBuffer);
754     FMessageBuffer := nil;
755     FMsgLength := 0;
756     end;
757    
758     procedure TIBXOUTPUTSQLDA.Bind(aMetaData: Firebird.IMessageMetadata);
759     var i: integer;
760     begin
761     FMetaData := aMetaData;
762     with Firebird30ClientAPI do
763     begin
764     Count := metadata.getCount(StatusIntf);
765     Check4DataBaseError;
766     Initialize;
767    
768     FMsgLength := metaData.getMessageLength(StatusIntf);
769     Check4DataBaseError;
770     IBAlloc(FMessageBuffer,0,FMsgLength);
771    
772     for i := 0 to Count - 1 do
773     with TIBXSQLVar(Column[i]) do
774     begin
775     FSQLType := aMetaData.getType(StatusIntf,i);
776     Check4DataBaseError;
777     if FSQLType = SQL_BLOB then
778     begin
779     FSQLSubType := aMetaData.getSubType(StatusIntf,i);
780     Check4DataBaseError;
781     end
782     else
783     FSQLSubType := 0;
784     FBlob := nil;
785     FArray := nil;
786     FSQLData := FMessageBuffer + metaData.getOffset(StatusIntf,i);
787     Check4DataBaseError;
788     FDataLength := aMetaData.getLength(StatusIntf,i);
789     Check4DataBaseError;
790     FRelationName := strpas(aMetaData.getRelation(StatusIntf,i));
791     Check4DataBaseError;
792     FFieldName := strpas(aMetaData.getField(StatusIntf,i));
793     Check4DataBaseError;
794     FNullable := aMetaData.isNullable(StatusIntf,i);
795     Check4DataBaseError;
796     if FNullable then
797     begin
798     FSQLNullIndicator := PShort(FMessageBuffer + aMetaData.getNullOffset(StatusIntf,i));
799     Check4DataBaseError;
800     end
801     else
802     FSQLNullIndicator := nil;
803     FScale := aMetaData.getScale(StatusIntf,i);
804     Check4DataBaseError;
805     FCharSetID := aMetaData.getCharSet(StatusIntf,i);
806     Check4DataBaseError;
807     end;
808     end;
809     SetUniqueRelationName;
810     end;
811    
812     procedure TIBXOUTPUTSQLDA.GetData(index: integer; var aIsNull: boolean;
813     var len: short; var data: PChar);
814     begin
815     with TIBXSQLVAR(Column[index]) do
816     begin
817     aIsNull := FNullable and (FSQLNullIndicator^ = -1);
818     data := FSQLData;
819     len := FDataLength;
820     if not IsNull and (FSQLType = SQL_VARYING) then
821     begin
822     with Firebird30ClientAPI do
823     len := DecodeInteger(data,2);
824     Inc(Data,2);
825     end;
826     end;
827     end;
828    
829     function TIBXOUTPUTSQLDA.IsInputDataArea: boolean;
830     begin
831     Result := false;
832     end;
833    
834     { TIBXSQLDA }
835     constructor TIBXSQLDA.Create(aStatement: TFB30Statement);
836     begin
837     inherited Create;
838     FStatement := aStatement;
839     FSize := 0;
840     // writeln('Creating ',ClassName);
841     end;
842    
843     destructor TIBXSQLDA.Destroy;
844     begin
845     FreeXSQLDA;
846     // writeln('Destroying ',ClassName);
847     inherited Destroy;
848     end;
849    
850     procedure TIBXSQLDA.Changed;
851     begin
852    
853     end;
854    
855     function TIBXSQLDA.CheckStatementStatus(Request: TStatementStatus): boolean;
856     begin
857     Result := false;
858     case Request of
859     ssPrepared:
860     Result := FStatement.IsPrepared;
861    
862     ssExecuteResults:
863     Result :=FStatement.FSingleResults;
864    
865     ssCursorOpen:
866     Result := FStatement.FOpen;
867    
868     ssBOF:
869     Result := FStatement.FBOF;
870    
871     ssEOF:
872     Result := FStatement.FEOF;
873     end;
874     end;
875    
876     function TIBXSQLDA.ColumnsInUseCount: integer;
877     begin
878     Result := FCount;
879     end;
880    
881     function TIBXSQLDA.GetTransaction: TFB30Transaction;
882     begin
883     Result := FStatement.GetTransaction as TFB30Transaction;
884     end;
885    
886     procedure TIBXSQLDA.Initialize;
887     begin
888     if FMetaData <> nil then
889     inherited Initialize;
890     end;
891    
892     function TIBXSQLDA.StateChanged(var ChangeSeqNo: integer): boolean;
893     begin
894     Result := FStatement.ChangeSeqNo <> ChangeSeqNo;
895     if Result then
896     ChangeSeqNo := FStatement.ChangeSeqNo;
897     end;
898    
899     procedure TIBXSQLDA.SetCount(Value: Integer);
900     var
901     i: Integer;
902     begin
903     FCount := Value;
904     if FCount = 0 then
905     FUniqueRelationName := ''
906     else
907     begin
908     SetLength(FColumnList, FCount);
909     for i := FSize to FCount - 1 do
910     FColumnList[i] := TIBXSQLVAR.Create(self,i);
911     FSize := FCount;
912     end;
913     end;
914    
915     function TIBXSQLDA.GetTransactionSeqNo: integer;
916     begin
917     Result := FTransactionSeqNo;
918     end;
919    
920     procedure TIBXSQLDA.FreeXSQLDA;
921     var i: integer;
922     begin
923     if FMetaData <> nil then
924     FMetaData.release;
925     FMetaData := nil;
926     for i := 0 to Count - 1 do
927     TIBXSQLVAR(Column[i]).FreeSQLData;
928     for i := 0 to FSize - 1 do
929     TIBXSQLVAR(Column[i]).Free;
930     SetLength(FColumnList,0);
931     FSize := 0;
932     end;
933    
934     function TIBXSQLDA.GetStatement: IStatement;
935     begin
936     Result := FStatement;
937     end;
938    
939     function TIBXSQLDA.GetPrepareSeqNo: integer;
940     begin
941     Result := FStatement.FPrepareSeqNo;
942     end;
943    
944     { TFB30Statement }
945    
946     procedure TFB30Statement.CheckHandle;
947     begin
948     if FStatementIntf = nil then
949     IBError(ibxeInvalidStatementHandle,[nil]);
950     end;
951    
952     procedure TFB30Statement.GetDSQLInfo(info_request: byte; buffer: ISQLInfoResults
953     );
954     begin
955     with Firebird30ClientAPI, buffer as TSQLInfoResultsBuffer do
956     begin
957     StatementIntf.getInfo(StatusIntf,1,BytePtr(@info_request),
958     GetBufSize, BytePtr(Buffer));
959     Check4DataBaseError;
960     end;
961     end;
962    
963     procedure TFB30Statement.InternalPrepare;
964     begin
965     if FPrepared then
966     Exit;
967     if (FSQL = '') then
968     IBError(ibxeEmptyQuery, [nil]);
969     try
970     CheckTransaction(FTransactionIntf);
971     with Firebird30ClientAPI do
972     begin
973     if FHasParamNames then
974     begin
975     if FProcessedSQL = '' then
976     FSQLParams.PreprocessSQL(FSQL,FGenerateParamNames,FProcessedSQL);
977     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
978     (FTransactionIntf as TFB30Transaction).TransactionIntf,
979     Length(FProcessedSQL),
980     PChar(FProcessedSQL),
981     FSQLDialect,
982     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
983     end
984     else
985     FStatementIntf := (GetAttachment as TFB30Attachment).AttachmentIntf.prepare(StatusIntf,
986     (FTransactionIntf as TFB30Transaction).TransactionIntf,
987     Length(FSQL),
988     PChar(FSQL),
989     FSQLDialect,
990     Firebird.IStatement.PREPARE_PREFETCH_METADATA);
991     Check4DataBaseError;
992     FSQLStatementType := TIBSQLStatementTypes(FStatementIntf.getType(StatusIntf));
993     Check4DataBaseError;
994    
995     { Done getting the type }
996     case FSQLStatementType of
997     SQLGetSegment,
998     SQLPutSegment,
999     SQLStartTransaction:
1000     begin
1001     FreeHandle;
1002     IBError(ibxeNotPermitted, [nil]);
1003     end;
1004     SQLCommit,
1005     SQLRollback,
1006     SQLDDL, SQLSetGenerator,
1007     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
1008     SQLExecProcedure:
1009     begin
1010     {set up input sqlda}
1011     FSQLParams.Bind(FStatementIntf.getInputMetadata(StatusIntf));
1012     Check4DataBaseError;
1013    
1014     {setup output sqlda}
1015     if FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1016     SQLExecProcedure] then
1017     FSQLRecord.Bind(FStatementIntf.getOutputMetadata(StatusIntf));
1018     Check4DataBaseError;
1019     end;
1020     end;
1021     end;
1022     except
1023     on E: Exception do begin
1024     if (FStatementIntf <> nil) then
1025     FreeHandle;
1026     if E is EIBInterBaseError then
1027     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
1028     EIBInterBaseError(E).IBErrorCode,
1029     EIBInterBaseError(E).Message +
1030     sSQLErrorSeparator + FSQL)
1031     else
1032     raise;
1033     end;
1034     end;
1035     FPrepared := true;
1036     FSingleResults := false;
1037     if RetainInterfaces then
1038     begin
1039     SetRetainInterfaces(false);
1040     SetRetainInterfaces(true);
1041     end;
1042     Inc(FPrepareSeqNo);
1043     with GetTransaction as TFB30Transaction do
1044     begin
1045     FSQLParams.FTransactionSeqNo := TransactionSeqNo;
1046     FSQLRecord.FTransactionSeqNo := TransactionSeqNo;
1047     end;
1048     SignalActivity;
1049     Inc(FChangeSeqNo);
1050     end;
1051    
1052     function TFB30Statement.InternalExecute(aTransaction: ITransaction): IResults;
1053     begin
1054     Result := nil;
1055     FBOF := false;
1056     FEOF := false;
1057     FSingleResults := false;
1058     CheckTransaction(aTransaction);
1059     if not FPrepared then
1060     InternalPrepare;
1061     CheckHandle;
1062     if aTransaction <> FTransactionIntf then
1063     AddMonitor(aTransaction as TFB30Transaction);
1064     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1065     IBError(ibxeInterfaceOutofDate,[nil]);
1066    
1067     try
1068     with Firebird30ClientAPI do
1069     case FSQLStatementType of
1070     SQLSelect:
1071     IBError(ibxeIsAExecuteProcedure,[]);
1072    
1073     SQLExecProcedure:
1074     begin
1075     FStatementIntf.execute(StatusIntf,
1076     (aTransaction as TFB30Transaction).TransactionIntf,
1077     FSQLParams.MetaData,
1078     FSQLParams.MessageBuffer,
1079     FSQLRecord.MetaData,
1080     FSQLRecord.MessageBuffer);
1081     Check4DataBaseError;
1082    
1083     Result := TResults.Create(FSQLRecord);
1084     FSingleResults := true;
1085     end
1086     else
1087     FStatementIntf.execute(StatusIntf,
1088     (aTransaction as TFB30Transaction).TransactionIntf,
1089     FSQLParams.MetaData,
1090     FSQLParams.MessageBuffer,
1091     nil,
1092     nil);
1093     Check4DataBaseError;
1094     end;
1095     finally
1096     if aTransaction <> FTransactionIntf then
1097     RemoveMonitor(aTransaction as TFB30Transaction);
1098     end;
1099     FExecTransactionIntf := aTransaction;
1100     SignalActivity;
1101     Inc(FChangeSeqNo);
1102     end;
1103    
1104     function TFB30Statement.InternalOpenCursor(aTransaction: ITransaction
1105     ): IResultSet;
1106     begin
1107     if FSQLStatementType <> SQLSelect then
1108     IBError(ibxeIsASelectStatement,[]);
1109    
1110     CheckTransaction(aTransaction);
1111     if not FPrepared then
1112     InternalPrepare;
1113     CheckHandle;
1114     if aTransaction <> FTransactionIntf then
1115     AddMonitor(aTransaction as TFB30Transaction);
1116     if (FSQLParams.FTransactionSeqNo < (FTransactionIntf as TFB30transaction).TransactionSeqNo) then
1117     IBError(ibxeInterfaceOutofDate,[nil]);
1118    
1119     with Firebird30ClientAPI do
1120     begin
1121     FResultSet := FStatementIntf.openCursor(StatusIntf,
1122     (aTransaction as TFB30Transaction).TransactionIntf,
1123     FSQLParams.MetaData,
1124     FSQLParams.MessageBuffer,
1125     FSQLRecord.MetaData,
1126     0);
1127     Check4DataBaseError;
1128     end;
1129     Inc(FCursorSeqNo);
1130     FSingleResults := false;
1131     FOpen := True;
1132     FExecTransactionIntf := aTransaction;
1133     FBOF := true;
1134     FEOF := false;
1135     FSQLRecord.FTransaction := (aTransaction as TFB30Transaction);
1136     FSQLRecord.FTransactionSeqNo := FSQLRecord.FTransaction.TransactionSeqNo;
1137     Result := TResultSet.Create(FSQLRecord);
1138     SignalActivity;
1139     Inc(FChangeSeqNo);
1140     end;
1141    
1142     procedure TFB30Statement.FreeHandle;
1143     begin
1144     Close;
1145     ReleaseInterfaces;
1146     if FStatementIntf <> nil then
1147     begin
1148     FStatementIntf.release;
1149     FStatementIntf := nil;
1150     FPrepared := false;
1151     end;
1152     end;
1153    
1154     procedure TFB30Statement.InternalClose(Force: boolean);
1155     begin
1156     if (FStatementIntf <> nil) and (SQLStatementType = SQLSelect) and FOpen then
1157     try
1158     with Firebird30ClientAPI do
1159     begin
1160     if FResultSet <> nil then
1161     begin
1162     if FSQLRecord.FTransaction.InTransaction and
1163     (FSQLRecord.FTransactionSeqNo = FSQLRecord.FTransaction.TransactionSeqNo) then
1164     FResultSet.close(StatusIntf)
1165     else
1166     FResultSet.release;
1167     end;
1168     FResultSet := nil;
1169     if not Force then Check4DataBaseError;
1170     end;
1171     finally
1172     if (FSQLRecord.FTransaction <> nil) and (FSQLRecord.FTransaction <> FTransactionIntf) then
1173     RemoveMonitor(FSQLRecord.FTransaction);
1174     FOpen := False;
1175     FExecTransactionIntf := nil;
1176     FSQLRecord.FTransaction := nil;
1177     end;
1178     SignalActivity;
1179     Inc(FChangeSeqNo);
1180     end;
1181    
1182     constructor TFB30Statement.Create(Attachment: TFB30Attachment;
1183     Transaction: ITransaction; sql: string; aSQLDialect: integer);
1184     begin
1185     inherited Create(Attachment,Transaction,sql,aSQLDialect);
1186     FSQLParams := TIBXINPUTSQLDA.Create(self);
1187     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1188     InternalPrepare;
1189     end;
1190    
1191     constructor TFB30Statement.CreateWithParameterNames(
1192     Attachment: TFB30Attachment; Transaction: ITransaction; sql: string;
1193     aSQLDialect: integer; GenerateParamNames: boolean);
1194     begin
1195     inherited CreateWithParameterNames(Attachment,Transaction,sql,aSQLDialect,GenerateParamNames);
1196     FSQLParams := TIBXINPUTSQLDA.Create(self);
1197     FSQLRecord := TIBXOUTPUTSQLDA.Create(self);
1198     InternalPrepare;
1199     end;
1200    
1201     destructor TFB30Statement.Destroy;
1202     begin
1203     inherited Destroy;
1204     if assigned(FSQLParams) then FSQLParams.Free;
1205     if assigned(FSQLRecord) then FSQLRecord.Free;
1206     end;
1207    
1208     function TFB30Statement.FetchNext: boolean;
1209     var fetchResult: integer;
1210     begin
1211     result := false;
1212     if not FOpen then
1213     IBError(ibxeSQLClosed, [nil]);
1214     if FEOF then
1215     IBError(ibxeEOF,[nil]);
1216    
1217     with Firebird30ClientAPI do
1218     begin
1219     { Go to the next record... }
1220     fetchResult := FResultSet.fetchNext(StatusIntf,FSQLRecord.MessageBuffer);
1221     if fetchResult = Firebird.IStatus.RESULT_NO_DATA then
1222     begin
1223     FBOF := false;
1224     FEOF := true;
1225     Exit; {End of File}
1226     end
1227     else
1228     if fetchResult <> Firebird.IStatus.RESULT_OK then
1229     begin
1230     try
1231     IBDataBaseError;
1232     except
1233     Close;
1234     raise;
1235     end;
1236     end
1237     else
1238     begin
1239     FBOF := false;
1240     result := true;
1241     end;
1242     end;
1243     FSQLRecord.RowChange;
1244     SignalActivity;
1245     if FEOF then
1246     Inc(FChangeSeqNo);
1247     end;
1248    
1249     function TFB30Statement.GetSQLParams: ISQLParams;
1250     begin
1251     CheckHandle;
1252     if not HasInterface(0) then
1253     AddInterface(0,TSQLParams.Create(FSQLParams));
1254     Result := TSQLParams(GetInterface(0));
1255     end;
1256    
1257     function TFB30Statement.GetMetaData: IMetaData;
1258     begin
1259     CheckHandle;
1260     if not HasInterface(1) then
1261     AddInterface(1, TMetaData.Create(FSQLRecord));
1262     Result := TMetaData(GetInterface(1));
1263     end;
1264    
1265     function TFB30Statement.GetPlan: String;
1266     begin
1267     CheckHandle;
1268     if (not (FSQLStatementType in [SQLSelect, SQLSelectForUpdate,
1269     {TODO: SQLExecProcedure, }
1270     SQLUpdate, SQLDelete])) then
1271     result := ''
1272     else
1273     with Firebird30ClientAPI do
1274     begin
1275     Result := FStatementIntf.getPlan(StatusIntf,true);
1276     Check4DataBaseError;
1277     end;
1278     end;
1279    
1280     function TFB30Statement.CreateBlob(column: TColumnMetaData): IBlob;
1281     begin
1282     if assigned(column) and (column.SQLType <> SQL_Blob) then
1283     IBError(ibxeNotABlob,[nil]);
1284     Result := TFB30Blob.Create(GetAttachment as TFB30Attachment,
1285     GetTransaction as TFB30Transaction,
1286     column.GetBlobMetaData,nil);
1287     end;
1288    
1289     function TFB30Statement.CreateArray(column: TColumnMetaData): IArray;
1290     begin
1291     if assigned(column) and (column.SQLType <> SQL_ARRAY) then
1292     IBError(ibxeNotAnArray,[nil]);
1293     Result := TFB30Array.Create(GetAttachment as TFB30Attachment,
1294     GetTransaction as TFB30Transaction,
1295     column.GetArrayMetaData);
1296     end;
1297    
1298     procedure TFB30Statement.SetRetainInterfaces(aValue: boolean);
1299     begin
1300     inherited SetRetainInterfaces(aValue);
1301     if HasInterface(1) then
1302     TMetaData(GetInterface(1)).RetainInterfaces := aValue;
1303     if HasInterface(0) then
1304     TSQLParams(GetInterface(0)).RetainInterfaces := aValue;
1305     end;
1306    
1307     function TFB30Statement.IsPrepared: boolean;
1308     begin
1309     Result := FStatementIntf <> nil;
1310     end;
1311    
1312     end.
1313