ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 21
Committed: Thu Feb 26 10:33:34 2015 UTC (9 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 77327 byte(s)
Log Message:
Committing updates for Release R1-2-0

File Contents

# User Rev Content
1 tony 19 {************************************************************************}
2     { }
3     { Borland Delphi Visual Component Library }
4     { InterBase Express core components }
5     { }
6     { Copyright (c) 1998-2000 Inprise Corporation }
7     { }
8     { InterBase Express is based in part on the product }
9     { Free IB Components, written by Gregory H. Deatz for }
10     { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11     { Free IB Components is used under license. }
12     { }
13     { The contents of this file are subject to the InterBase }
14     { Public License Version 1.0 (the "License"); you may not }
15     { use this file except in compliance with the License. You }
16     { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17     { Software distributed under the License is distributed on }
18     { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19     { express or implied. See the License for the specific language }
20     { governing rights and limitations under the License. }
21     { The Original Code was created by InterBase Software Corporation }
22     { and its successors. }
23     { Portions created by Inprise Corporation are Copyright (C) Inprise }
24     { Corporation. All Rights Reserved. }
25     { Contributor(s): Jeff Overcash }
26     { }
27     { IBX For Lazarus (Firebird Express) }
28     { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29     { Portions created by MWA Software are copyright McCallum Whyman }
30     { Associates Ltd 2011 - 2014 }
31     { }
32     {************************************************************************}
33    
34     unit IBSQL;
35    
36     {$Mode Delphi}
37    
38     { IBSQL param names in dialect 3 quoted format (e.g. :"MyParam") are by default disabled.
39    
40     Dialect 3 quoted format parameter names represent a significant overhead and are of
41     limited value - especially for users that use only TIBSQL or TIBCustomDataset
42     descendents. They were previously used internally by IBX to simplify SQL generation
43     for TTable components in Master/Slave relationships which are linked by
44     Dialect 3 names. They were also generated by TStoredProc when the original
45     parameter names are quoted.
46    
47     However, for some users they do cause a big processing overhead. The TTable/TStoredProc
48     code has been re-written so that they are no required by IBX internally.
49     The code to support quoted parameter names is now subject to conditional compilation.
50     To enable support, ALLOWDIALECT3PARAMNAMES should be defined when IBX is compiled.
51    
52     Hint: deleting the space between the brace and the dollar sign below
53    
54     }
55    
56     { $define ALLOWDIALECT3PARAMNAMES}
57    
58     {$ifndef ALLOWDIALECT3PARAMNAMES}
59    
60     { Even when dialect 3 quoted format parameter names are not supported, IBX still processes
61     parameter names case insensitive. This does result in some additional overhead
62     due to a call to "AnsiUpperCase". This can be avoided by undefining
63     "UseCaseSensitiveParamName" below.
64    
65     Note: do not define "UseCaseSensitiveParamName" when "ALLOWDIALECT3PARAMNAMES"
66     is defined. This will not give a useful result.
67     }
68     {$define UseCaseSensitiveParamName}
69     {$endif}
70    
71     interface
72    
73     uses
74     {$IFDEF WINDOWS }
75     Windows,
76     {$ELSE}
77     baseunix, unix,
78     {$ENDIF}
79     SysUtils, Classes, Forms, Controls, IBHeader,
80     IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
81    
82 tony 21 const
83     sSQLErrorSeparator = ' When Executing: ';
84    
85 tony 19 type
86     TIBSQL = class;
87     TIBXSQLDA = class;
88    
89     { TIBXSQLVAR }
90     TIBXSQLVAR = class(TObject)
91     private
92     FParent: TIBXSQLDA;
93     FSQL: TIBSQL;
94     FIndex: Integer;
95     FModified: Boolean;
96     FName: String;
97     FUniqueName: boolean;
98     FXSQLVAR: PXSQLVAR; { Point to the PXSQLVAR in the owner object }
99    
100     function AdjustScale(Value: Int64; Scale: Integer): Double;
101     function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
102     function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
103     function GetAsCurrency: Currency;
104     function GetAsInt64: Int64;
105     function GetAsDateTime: TDateTime;
106     function GetAsDouble: Double;
107     function GetAsFloat: Float;
108     function GetAsLong: Long;
109     function GetAsPointer: Pointer;
110     function GetAsQuad: TISC_QUAD;
111     function GetAsShort: Short;
112     function GetAsString: String;
113     function GetAsVariant: Variant;
114     function GetAsXSQLVAR: PXSQLVAR;
115     function GetIsNull: Boolean;
116     function GetIsNullable: Boolean;
117     function GetSize: Integer;
118     function GetSQLType: Integer;
119     procedure SetAsCurrency(Value: Currency);
120     procedure SetAsInt64(Value: Int64);
121     procedure SetAsDate(Value: TDateTime);
122     procedure SetAsTime(Value: TDateTime);
123     procedure SetAsDateTime(Value: TDateTime);
124     procedure SetAsDouble(Value: Double);
125     procedure SetAsFloat(Value: Float);
126     procedure SetAsLong(Value: Long);
127     procedure SetAsPointer(Value: Pointer);
128     procedure SetAsQuad(Value: TISC_QUAD);
129     procedure SetAsShort(Value: Short);
130     procedure SetAsString(Value: String);
131     procedure SetAsVariant(Value: Variant);
132     procedure SetAsXSQLVAR(Value: PXSQLVAR);
133     procedure SetIsNull(Value: Boolean);
134     procedure SetIsNullable(Value: Boolean);
135     procedure xSetAsCurrency(Value: Currency);
136     procedure xSetAsInt64(Value: Int64);
137     procedure xSetAsDate(Value: TDateTime);
138     procedure xSetAsTime(Value: TDateTime);
139     procedure xSetAsDateTime(Value: TDateTime);
140     procedure xSetAsDouble(Value: Double);
141     procedure xSetAsFloat(Value: Float);
142     procedure xSetAsLong(Value: Long);
143     procedure xSetAsPointer(Value: Pointer);
144     procedure xSetAsQuad(Value: TISC_QUAD);
145     procedure xSetAsShort(Value: Short);
146     procedure xSetAsString(Value: String);
147     procedure xSetAsVariant(Value: Variant);
148     procedure xSetAsXSQLVAR(Value: PXSQLVAR);
149     procedure xSetIsNull(Value: Boolean);
150     procedure xSetIsNullable(Value: Boolean);
151     public
152     constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
153     procedure Assign(Source: TIBXSQLVAR);
154     procedure Clear;
155     procedure LoadFromFile(const FileName: String);
156     procedure LoadFromStream(Stream: TStream);
157     procedure SaveToFile(const FileName: String);
158     procedure SaveToStream(Stream: TStream);
159     property AsDate: TDateTime read GetAsDateTime write SetAsDate;
160     property AsTime: TDateTime read GetAsDateTime write SetAsTime;
161     property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
162     property AsDouble: Double read GetAsDouble write SetAsDouble;
163     property AsFloat: Float read GetAsFloat write SetAsFloat;
164     property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
165     property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
166     property AsInteger: Integer read GetAsLong write SetAsLong;
167     property AsLong: Long read GetAsLong write SetAsLong;
168     property AsPointer: Pointer read GetAsPointer write SetAsPointer;
169     property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
170     property AsShort: Short read GetAsShort write SetAsShort;
171     property AsString: String read GetAsString write SetAsString;
172     property AsVariant: Variant read GetAsVariant write SetAsVariant;
173     property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
174     property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
175     property IsNull: Boolean read GetIsNull write SetIsNull;
176     property IsNullable: Boolean read GetIsNullable write SetIsNullable;
177     property Index: Integer read FIndex;
178     property Modified: Boolean read FModified write FModified;
179     property Name: String read FName;
180     property Size: Integer read GetSize;
181     property SQLType: Integer read GetSQLType;
182     property Value: Variant read GetAsVariant write SetAsVariant;
183     end;
184    
185     TIBXSQLVARArray = Array of TIBXSQLVAR;
186    
187     TIBXSQLDAType = (daInput,daOutput);
188    
189     { TIBXSQLDA }
190    
191     TIBXSQLDA = class(TObject)
192     protected
193     FSQL: TIBSQL;
194     FCount: Integer;
195     FSize: Integer;
196     FInputSQLDA: boolean;
197     FXSQLDA: PXSQLDA;
198     FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
199     FUniqueRelationName: String;
200     function GetModified: Boolean;
201     function GetRecordSize: Integer;
202     function GetXSQLDA: PXSQLDA;
203     function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
204     function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
205     procedure Initialize;
206     procedure SetCount(Value: Integer);
207     public
208     constructor Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
209     destructor Destroy; override;
210     procedure SetParamName(FieldName: String; Idx: Integer; UniqueName: boolean = false);
211     function ByName(Idx: String): TIBXSQLVAR;
212     property AsXSQLDA: PXSQLDA read GetXSQLDA;
213     property Count: Integer read FCount write SetCount;
214     property Modified: Boolean read GetModified;
215     property RecordSize: Integer read GetRecordSize;
216     property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
217     property UniqueRelationName: String read FUniqueRelationName;
218     end;
219    
220     { TIBBatch }
221    
222     TIBBatch = class(TObject)
223     protected
224     FFilename: String;
225     FColumns: TIBXSQLDA;
226     FParams: TIBXSQLDA;
227     public
228     procedure ReadyFile; virtual; abstract;
229     property Columns: TIBXSQLDA read FColumns;
230     property Filename: String read FFilename write FFilename;
231     property Params: TIBXSQLDA read FParams;
232     end;
233    
234     TIBBatchInput = class(TIBBatch)
235     public
236     function ReadParameters: Boolean; virtual; abstract;
237     end;
238    
239     TIBBatchOutput = class(TIBBatch)
240     public
241     function WriteColumns: Boolean; virtual; abstract;
242     end;
243    
244    
245     { TIBOutputDelimitedFile }
246     TIBOutputDelimitedFile = class(TIBBatchOutput)
247     protected
248     {$IFDEF UNIX}
249     FHandle: cint;
250     {$ELSE}
251     FHandle: THandle;
252     {$ENDIF}
253     FOutputTitles: Boolean;
254     FColDelimiter,
255     FRowDelimiter: string;
256     public
257     destructor Destroy; override;
258     procedure ReadyFile; override;
259     function WriteColumns: Boolean; override;
260     property ColDelimiter: string read FColDelimiter write FColDelimiter;
261     property OutputTitles: Boolean read FOutputTitles
262     write FOutputTitles;
263     property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
264     end;
265    
266     { TIBInputDelimitedFile }
267     TIBInputDelimitedFile = class(TIBBatchInput)
268     protected
269     FColDelimiter,
270     FRowDelimiter: string;
271     FEOF: Boolean;
272     FFile: TFileStream;
273     FLookAhead: Char;
274     FReadBlanksAsNull: Boolean;
275     FSkipTitles: Boolean;
276     public
277     destructor Destroy; override;
278     function GetColumn(var Col: string): Integer;
279     function ReadParameters: Boolean; override;
280     procedure ReadyFile; override;
281     property ColDelimiter: string read FColDelimiter write FColDelimiter;
282     property ReadBlanksAsNull: Boolean read FReadBlanksAsNull
283     write FReadBlanksAsNull;
284     property RowDelimiter: string read FRowDelimiter write FRowDelimiter;
285     property SkipTitles: Boolean read FSkipTitles write FSkipTitles;
286     end;
287    
288     { TIBOutputRawFile }
289     TIBOutputRawFile = class(TIBBatchOutput)
290     protected
291     {$IFDEF UNIX}
292     FHandle: cint;
293     {$ELSE}
294     FHandle: THandle;
295     {$ENDIF}
296     public
297     destructor Destroy; override;
298     procedure ReadyFile; override;
299     function WriteColumns: Boolean; override;
300     end;
301    
302     { TIBInputRawFile }
303     TIBInputRawFile = class(TIBBatchInput)
304     protected
305     {$IFDEF UNIX}
306     FHandle: cint;
307     {$ELSE}
308     FHandle: THandle;
309     {$ENDIF}
310     public
311     destructor Destroy; override;
312     function ReadParameters: Boolean; override;
313     procedure ReadyFile; override;
314     end;
315    
316     { TIBSQL }
317     TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
318     SQLUpdate, SQLDelete, SQLDDL,
319     SQLGetSegment, SQLPutSegment,
320     SQLExecProcedure, SQLStartTransaction,
321     SQLCommit, SQLRollback,
322     SQLSelectForUpdate, SQLSetGenerator);
323    
324     TIBSQL = class(TComponent)
325     private
326     FIBLoaded: Boolean;
327     FUniqueParamNames: Boolean;
328     function GetFieldCount: integer;
329     procedure SetUniqueParamNames(AValue: Boolean);
330     protected
331     FBase: TIBBase;
332     FBOF, { At BOF? }
333     FEOF, { At EOF? }
334     FGoToFirstRecordOnExecute, { Automatically position record on first record after executing }
335     FOpen, { Is a cursor open? }
336     FPrepared: Boolean; { Has the query been prepared? }
337     FRecordCount: Integer; { How many records have been read so far? }
338     FCursor: String; { Cursor name...}
339     FHandle: TISC_STMT_HANDLE; { Once prepared, this accesses the SQL Query }
340     FOnSQLChanging: TNotifyEvent; { Call this when the SQL is changing }
341     FSQL: TStrings; { SQL Query (by user) }
342     FParamCheck: Boolean; { Check for parameters? (just like TQuery) }
343     FProcessedSQL: TStrings; { SQL Query (pre-processed for param labels) }
344     FSQLParams, { Any parameters to the query }
345     FSQLRecord: TIBXSQLDA; { The current record }
346     FSQLType: TIBSQLTypes; { Select, update, delete, insert, create, alter, etc...}
347     FGenerateParamNames: Boolean; { Auto generate param names ?}
348     procedure DoBeforeDatabaseDisconnect(Sender: TObject);
349     function GetDatabase: TIBDatabase;
350     function GetDBHandle: PISC_DB_HANDLE;
351     function GetEOF: Boolean;
352     function GetFields(const Idx: Integer): TIBXSQLVAR;
353     function GetFieldIndex(FieldName: String): Integer;
354     function GetPlan: String;
355     function GetRecordCount: Integer;
356     function GetRowsAffected: Integer;
357     function GetSQLParams: TIBXSQLDA;
358     function GetTransaction: TIBTransaction;
359     function GetTRHandle: PISC_TR_HANDLE;
360     procedure PreprocessSQL;
361     procedure SetDatabase(Value: TIBDatabase);
362     procedure SetSQL(Value: TStrings);
363     procedure SetTransaction(Value: TIBTransaction);
364     procedure SQLChanging(Sender: TObject);
365     procedure BeforeTransactionEnd(Sender: TObject);
366     public
367     constructor Create(AOwner: TComponent); override;
368     destructor Destroy; override;
369     procedure BatchInput(InputObject: TIBBatchInput);
370     procedure BatchOutput(OutputObject: TIBBatchOutput);
371     function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
372     procedure CheckClosed; { raise error if query is not closed. }
373     procedure CheckOpen; { raise error if query is not open.}
374     procedure CheckValidStatement; { raise error if statement is invalid.}
375     procedure Close;
376     function Current: TIBXSQLDA;
377     procedure ExecQuery;
378     function FieldByName(FieldName: String): TIBXSQLVAR;
379     function ParamByName(ParamName: String): TIBXSQLVAR;
380     procedure FreeHandle;
381     function Next: TIBXSQLDA;
382     procedure Prepare;
383     function GetUniqueRelationName: String;
384     property Bof: Boolean read FBOF;
385     property DBHandle: PISC_DB_HANDLE read GetDBHandle;
386     property Eof: Boolean read GetEOF;
387     property Fields[const Idx: Integer]: TIBXSQLVAR read GetFields;
388     property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
389     property FieldCount: integer read GetFieldCount;
390     property Open: Boolean read FOpen;
391     property Params: TIBXSQLDA read GetSQLParams;
392     property Plan: String read GetPlan;
393     property Prepared: Boolean read FPrepared;
394     property RecordCount: Integer read GetRecordCount;
395     property RowsAffected: Integer read GetRowsAffected;
396     property SQLType: TIBSQLTypes read FSQLType;
397     property TRHandle: PISC_TR_HANDLE read GetTRHandle;
398     property Handle: TISC_STMT_HANDLE read FHandle;
399     property UniqueRelationName: String read GetUniqueRelationName;
400     published
401     property Database: TIBDatabase read GetDatabase write SetDatabase;
402     property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
403     property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
404     property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
405     write FGoToFirstRecordOnExecute
406     default True;
407     property ParamCheck: Boolean read FParamCheck write FParamCheck;
408     property SQL: TStrings read FSQL write SetSQL;
409     property Transaction: TIBTransaction read GetTransaction write SetTransaction;
410     property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
411     end;
412    
413     implementation
414    
415     uses
416     IBIntf, IBBlob, Variants , IBSQLMonitor;
417    
418     { TIBXSQLVAR }
419     constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
420     begin
421     inherited Create;
422     FParent := Parent;
423     FSQL := Query;
424     end;
425    
426     procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
427     var
428     szBuff: PChar;
429     s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
430     bSourceBlob, bDestBlob: Boolean;
431     iSegs: Int64;
432     iMaxSeg: Int64;
433     iSize: Int64;
434     iBlobType: Short;
435     begin
436     szBuff := nil;
437     bSourceBlob := True;
438     bDestBlob := True;
439     s_bhandle := nil;
440     d_bhandle := nil;
441     try
442     if (Source.IsNull) then
443     begin
444     IsNull := True;
445     exit;
446     end
447     else
448     if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
449     (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
450     exit; { arrays not supported }
451     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
452     (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
453     begin
454     AsXSQLVAR := Source.AsXSQLVAR;
455     exit;
456     end
457     else
458     if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
459     begin
460     szBuff := nil;
461     IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
462     Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
463     bSourceBlob := False;
464     iSize := Source.FXSQLVAR^.sqllen;
465     end
466     else
467     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
468     bDestBlob := False;
469    
470     if bSourceBlob then
471     begin
472     { read the blob }
473     Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
474     Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
475     0, nil), True);
476     try
477     IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
478     iBlobType);
479     szBuff := nil;
480     IBAlloc(szBuff, 0, iSize);
481     IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
482     finally
483     Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
484     end;
485     end;
486    
487     if bDestBlob then
488     begin
489     { write the blob }
490     FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
491     FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
492     0, nil), True);
493     try
494     IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
495     isNull := false
496     finally
497     FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
498     end;
499     end
500     else
501     begin
502     { just copy the buffer }
503     FXSQLVAR.sqltype := SQL_TEXT;
504     FXSQLVAR.sqllen := iSize;
505     IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
506     Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
507     end;
508     finally
509     FreeMem(szBuff);
510     end;
511     end;
512    
513     function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
514     var
515     Scaling : Int64;
516     i: Integer;
517     Val: Double;
518     begin
519     Scaling := 1; Val := Value;
520     if Scale > 0 then
521     begin
522     for i := 1 to Scale do
523     Scaling := Scaling * 10;
524     result := Val * Scaling;
525     end
526     else
527     if Scale < 0 then
528     begin
529     for i := -1 downto Scale do
530     Scaling := Scaling * 10;
531     result := Val / Scaling;
532     end
533     else
534     result := Val;
535     end;
536    
537     function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
538     var
539     Scaling : Int64;
540     i: Integer;
541     Val: Int64;
542     begin
543     Scaling := 1; Val := Value;
544     if Scale > 0 then begin
545     for i := 1 to Scale do Scaling := Scaling * 10;
546     result := Val * Scaling;
547     end else if Scale < 0 then begin
548     for i := -1 downto Scale do Scaling := Scaling * 10;
549     result := Val div Scaling;
550     end else
551     result := Val;
552     end;
553    
554     function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
555     var
556     Scaling : Int64;
557     i : Integer;
558     FractionText, PadText, CurrText: string;
559     begin
560     Result := 0;
561     Scaling := 1;
562     if Scale > 0 then
563     begin
564     for i := 1 to Scale do
565     Scaling := Scaling * 10;
566     result := Value * Scaling;
567     end
568     else
569     if Scale < 0 then
570     begin
571     for i := -1 downto Scale do
572     Scaling := Scaling * 10;
573     FractionText := IntToStr(abs(Value mod Scaling));
574     for i := Length(FractionText) to -Scale -1 do
575     PadText := '0' + PadText;
576     if Value < 0 then
577     CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
578     else
579     CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
580     try
581     result := StrToCurr(CurrText);
582     except
583     on E: Exception do
584     IBError(ibxeInvalidDataConversion, [nil]);
585     end;
586     end
587     else
588     result := Value;
589     end;
590    
591     function TIBXSQLVAR.GetAsCurrency: Currency;
592     begin
593     result := 0;
594     if FSQL.Database.SQLDialect < 3 then
595     result := GetAsDouble
596     else begin
597     if not IsNull then
598     case FXSQLVAR^.sqltype and (not 1) of
599     SQL_TEXT, SQL_VARYING: begin
600     try
601     result := StrtoCurr(AsString);
602     except
603     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
604     end;
605     end;
606     SQL_SHORT:
607     result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
608     FXSQLVAR^.sqlscale);
609     SQL_LONG:
610     result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
611     FXSQLVAR^.sqlscale);
612     SQL_INT64:
613     result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
614     FXSQLVAR^.sqlscale);
615     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
616     result := Trunc(AsDouble);
617     else
618     IBError(ibxeInvalidDataConversion, [nil]);
619     end;
620     end;
621     end;
622    
623     function TIBXSQLVAR.GetAsInt64: Int64;
624     begin
625     result := 0;
626     if not IsNull then
627     case FXSQLVAR^.sqltype and (not 1) of
628     SQL_TEXT, SQL_VARYING: begin
629     try
630     result := StrToInt64(AsString);
631     except
632     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
633     end;
634     end;
635     SQL_SHORT:
636     result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
637     FXSQLVAR^.sqlscale);
638     SQL_LONG:
639     result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
640     FXSQLVAR^.sqlscale);
641     SQL_INT64:
642     result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
643     FXSQLVAR^.sqlscale);
644     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
645     result := Trunc(AsDouble);
646     else
647     IBError(ibxeInvalidDataConversion, [nil]);
648     end;
649     end;
650    
651     function TIBXSQLVAR.GetAsDateTime: TDateTime;
652     var
653     tm_date: TCTimeStructure;
654     msecs: word;
655     begin
656     result := 0;
657     if not IsNull then
658     case FXSQLVAR^.sqltype and (not 1) of
659     SQL_TEXT, SQL_VARYING: begin
660     try
661     result := StrToDate(AsString);
662     except
663     on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
664     end;
665     end;
666     SQL_TYPE_DATE: begin
667     isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
668     try
669     result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
670     Word(tm_date.tm_mday));
671     except
672     on E: EConvertError do begin
673     IBError(ibxeInvalidDataConversion, [nil]);
674     end;
675     end;
676     end;
677     SQL_TYPE_TIME: begin
678     isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
679     try
680     msecs := (PISC_TIME(FXSQLVAR^.sqldata)^ mod 10000) div 10;
681     result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
682     Word(tm_date.tm_sec), msecs)
683     except
684     on E: EConvertError do begin
685     IBError(ibxeInvalidDataConversion, [nil]);
686     end;
687     end;
688     end;
689     SQL_TIMESTAMP: begin
690     isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
691     try
692     result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
693     Word(tm_date.tm_mday));
694     msecs := (PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time mod 10000) div 10;
695     if result >= 0 then
696     result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
697     Word(tm_date.tm_sec), msecs)
698     else
699     result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
700     Word(tm_date.tm_sec), msecs)
701     except
702     on E: EConvertError do begin
703     IBError(ibxeInvalidDataConversion, [nil]);
704     end;
705     end;
706     end;
707     else
708     IBError(ibxeInvalidDataConversion, [nil]);
709     end;
710     end;
711    
712     function TIBXSQLVAR.GetAsDouble: Double;
713     begin
714     result := 0;
715     if not IsNull then begin
716     case FXSQLVAR^.sqltype and (not 1) of
717     SQL_TEXT, SQL_VARYING: begin
718     try
719     result := StrToFloat(AsString);
720     except
721     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
722     end;
723     end;
724     SQL_SHORT:
725     result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
726     FXSQLVAR^.sqlscale);
727     SQL_LONG:
728     result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
729     FXSQLVAR^.sqlscale);
730     SQL_INT64:
731     result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
732     SQL_FLOAT:
733     result := PFloat(FXSQLVAR^.sqldata)^;
734     SQL_DOUBLE, SQL_D_FLOAT:
735     result := PDouble(FXSQLVAR^.sqldata)^;
736     else
737     IBError(ibxeInvalidDataConversion, [nil]);
738     end;
739     if FXSQLVAR^.sqlscale <> 0 then
740     result :=
741     StrToFloat(FloatToStrF(result, fffixed, 15,
742     Abs(FXSQLVAR^.sqlscale) ));
743     end;
744     end;
745    
746     function TIBXSQLVAR.GetAsFloat: Float;
747     begin
748     result := 0;
749     try
750     result := AsDouble;
751     except
752     on E: EOverflow do
753     IBError(ibxeInvalidDataConversion, [nil]);
754     end;
755     end;
756    
757     function TIBXSQLVAR.GetAsLong: Long;
758     begin
759     result := 0;
760     if not IsNull then
761     case FXSQLVAR^.sqltype and (not 1) of
762     SQL_TEXT, SQL_VARYING: begin
763     try
764     result := StrToInt(AsString);
765     except
766     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
767     end;
768     end;
769     SQL_SHORT:
770     result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
771     FXSQLVAR^.sqlscale));
772     SQL_LONG:
773     result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
774     FXSQLVAR^.sqlscale));
775     SQL_INT64:
776     result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
777     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
778     result := Trunc(AsDouble);
779     else
780     IBError(ibxeInvalidDataConversion, [nil]);
781     end;
782     end;
783    
784     function TIBXSQLVAR.GetAsPointer: Pointer;
785     begin
786     if not IsNull then
787     result := FXSQLVAR^.sqldata
788     else
789     result := nil;
790     end;
791    
792     function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
793     begin
794     result.gds_quad_high := 0;
795     result.gds_quad_low := 0;
796     if not IsNull then
797     case FXSQLVAR^.sqltype and (not 1) of
798     SQL_BLOB, SQL_ARRAY, SQL_QUAD:
799     result := PISC_QUAD(FXSQLVAR^.sqldata)^;
800     else
801     IBError(ibxeInvalidDataConversion, [nil]);
802     end;
803     end;
804    
805     function TIBXSQLVAR.GetAsShort: Short;
806     begin
807     result := 0;
808     try
809     result := AsLong;
810     except
811     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
812     end;
813     end;
814    
815    
816     function TIBXSQLVAR.GetAsString: String;
817     var
818     sz: PChar;
819     str_len: Integer;
820     ss: TStringStream;
821     begin
822     result := '';
823     { Check null, if so return a default string }
824     if not IsNull then
825     case FXSQLVar^.sqltype and (not 1) of
826     SQL_ARRAY:
827     result := '(Array)'; {do not localize}
828     SQL_BLOB: begin
829     ss := TStringStream.Create('');
830     try
831     SaveToStream(ss);
832     result := ss.DataString;
833     finally
834     ss.Free;
835     end;
836     end;
837     SQL_TEXT, SQL_VARYING: begin
838     sz := FXSQLVAR^.sqldata;
839     if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
840     str_len := FXSQLVar^.sqllen
841     else begin
842     str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
843     Inc(sz, 2);
844     end;
845     SetString(result, sz, str_len);
846     if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
847     result := TrimRight(result);
848     end;
849     SQL_TYPE_DATE:
850     case FSQL.Database.SQLDialect of
851     1 : result := DateTimeToStr(AsDateTime);
852     3 : result := DateToStr(AsDateTime);
853     end;
854     SQL_TYPE_TIME :
855     result := TimeToStr(AsDateTime);
856     SQL_TIMESTAMP:
857     result := DateTimeToStr(AsDateTime);
858     SQL_SHORT, SQL_LONG:
859     if FXSQLVAR^.sqlscale = 0 then
860     result := IntToStr(AsLong)
861     else if FXSQLVAR^.sqlscale >= (-4) then
862     result := CurrToStr(AsCurrency)
863     else
864     result := FloatToStr(AsDouble);
865     SQL_INT64:
866     if FXSQLVAR^.sqlscale = 0 then
867     result := IntToStr(AsInt64)
868     else if FXSQLVAR^.sqlscale >= (-4) then
869     result := CurrToStr(AsCurrency)
870     else
871     result := FloatToStr(AsDouble);
872     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
873     result := FloatToStr(AsDouble);
874     else
875     IBError(ibxeInvalidDataConversion, [nil]);
876     end;
877     end;
878    
879     function TIBXSQLVAR.GetAsVariant: Variant;
880     begin
881     if IsNull then
882     result := NULL
883     { Check null, if so return a default string }
884     else case FXSQLVar^.sqltype and (not 1) of
885     SQL_ARRAY:
886     result := '(Array)'; {do not localize}
887     SQL_BLOB:
888     result := '(Blob)'; {do not localize}
889     SQL_TEXT, SQL_VARYING:
890     result := AsString;
891     SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
892     result := AsDateTime;
893     SQL_SHORT, SQL_LONG:
894     if FXSQLVAR^.sqlscale = 0 then
895     result := AsLong
896     else if FXSQLVAR^.sqlscale >= (-4) then
897     result := AsCurrency
898     else
899     result := AsDouble;
900     SQL_INT64:
901     if FXSQLVAR^.sqlscale = 0 then
902 tony 21 result := AsInt64
903 tony 19 else if FXSQLVAR^.sqlscale >= (-4) then
904     result := AsCurrency
905     else
906     result := AsDouble;
907     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
908     result := AsDouble;
909     else
910     IBError(ibxeInvalidDataConversion, [nil]);
911     end;
912     end;
913    
914     function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
915     begin
916     result := FXSQLVAR;
917     end;
918    
919     function TIBXSQLVAR.GetIsNull: Boolean;
920     begin
921     result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
922     end;
923    
924     function TIBXSQLVAR.GetIsNullable: Boolean;
925     begin
926     result := (FXSQLVAR^.sqltype and 1 = 1);
927     end;
928    
929     procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
930     var
931     fs: TFileStream;
932     begin
933     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
934     try
935     LoadFromStream(fs);
936     finally
937     fs.Free;
938     end;
939     end;
940    
941     procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
942     var
943     bs: TIBBlobStream;
944     begin
945     bs := TIBBlobStream.Create;
946     try
947     bs.Mode := bmWrite;
948     bs.Database := FSQL.Database;
949     bs.Transaction := FSQL.Transaction;
950     Stream.Seek(0, soFromBeginning);
951     bs.LoadFromStream(Stream);
952     bs.Finalize;
953     AsQuad := bs.BlobID;
954     finally
955     bs.Free;
956     end;
957     end;
958    
959     procedure TIBXSQLVAR.SaveToFile(const FileName: String);
960     var
961     fs: TFileStream;
962     begin
963     fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
964     try
965     SaveToStream(fs);
966     finally
967     fs.Free;
968     end;
969     end;
970    
971     procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
972     var
973     bs: TIBBlobStream;
974     begin
975     bs := TIBBlobStream.Create;
976     try
977     bs.Mode := bmRead;
978     bs.Database := FSQL.Database;
979     bs.Transaction := FSQL.Transaction;
980     bs.BlobID := AsQuad;
981     bs.SaveToStream(Stream);
982     finally
983     bs.Free;
984     end;
985     end;
986    
987     function TIBXSQLVAR.GetSize: Integer;
988     begin
989     result := FXSQLVAR^.sqllen;
990     end;
991    
992     function TIBXSQLVAR.GetSQLType: Integer;
993     begin
994     result := FXSQLVAR^.sqltype and (not 1);
995     end;
996    
997     procedure TIBXSQLVAR.xSetAsCurrency(Value: Currency);
998     begin
999     if IsNullable then
1000     IsNull := False;
1001     FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1002     FXSQLVAR^.sqlscale := -4;
1003     FXSQLVAR^.sqllen := SizeOf(Int64);
1004     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1005     PCurrency(FXSQLVAR^.sqldata)^ := Value;
1006     FModified := True;
1007     end;
1008    
1009     procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
1010     var
1011     i: Integer;
1012     begin
1013     if FSQL.Database.SQLDialect < 3 then
1014     AsDouble := Value
1015     else
1016     begin
1017    
1018     if FUniqueName then
1019     xSetAsCurrency(Value)
1020     else
1021     for i := 0 to FParent.FCount - 1 do
1022     if FParent[i].FName = FName then
1023     FParent[i].xSetAsCurrency(Value);
1024     end;
1025     end;
1026    
1027     procedure TIBXSQLVAR.xSetAsInt64(Value: Int64);
1028     begin
1029     if IsNullable then
1030     IsNull := False;
1031    
1032     FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1033     FXSQLVAR^.sqlscale := 0;
1034     FXSQLVAR^.sqllen := SizeOf(Int64);
1035     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1036     PInt64(FXSQLVAR^.sqldata)^ := Value;
1037     FModified := True;
1038     end;
1039    
1040     procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
1041     var
1042     i: Integer;
1043     begin
1044     if FUniqueName then
1045     xSetAsInt64(Value)
1046     else
1047     for i := 0 to FParent.FCount - 1 do
1048     if FParent[i].FName = FName then
1049     FParent[i].xSetAsInt64(Value);
1050     end;
1051    
1052     procedure TIBXSQLVAR.xSetAsDate(Value: TDateTime);
1053     var
1054     tm_date: TCTimeStructure;
1055     Yr, Mn, Dy: Word;
1056     begin
1057     if IsNullable then
1058     IsNull := False;
1059    
1060     FXSQLVAR^.sqltype := SQL_TYPE_DATE or (FXSQLVAR^.sqltype and 1);
1061     DecodeDate(Value, Yr, Mn, Dy);
1062     with tm_date do begin
1063     tm_sec := 0;
1064     tm_min := 0;
1065     tm_hour := 0;
1066     tm_mday := Dy;
1067     tm_mon := Mn - 1;
1068     tm_year := Yr - 1900;
1069     end;
1070     FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
1071     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1072     isc_encode_sql_date(@tm_date, PISC_DATE(FXSQLVAR^.sqldata));
1073     FModified := True;
1074     end;
1075    
1076     procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
1077     var
1078     i: Integer;
1079     begin
1080     if FSQL.Database.SQLDialect < 3 then
1081     begin
1082     AsDateTime := Value;
1083     exit;
1084     end;
1085    
1086     if FUniqueName then
1087     xSetAsDate(Value)
1088     else
1089     for i := 0 to FParent.FCount - 1 do
1090     if FParent[i].FName = FName then
1091     FParent[i].xSetAsDate(Value);
1092     end;
1093    
1094     procedure TIBXSQLVAR.xSetAsTime(Value: TDateTime);
1095     var
1096     tm_date: TCTimeStructure;
1097     Hr, Mt, S, Ms: Word;
1098     begin
1099     if IsNullable then
1100     IsNull := False;
1101    
1102     FXSQLVAR^.sqltype := SQL_TYPE_TIME or (FXSQLVAR^.sqltype and 1);
1103     DecodeTime(Value, Hr, Mt, S, Ms);
1104     with tm_date do begin
1105     tm_sec := S;
1106     tm_min := Mt;
1107     tm_hour := Hr;
1108     tm_mday := 0;
1109     tm_mon := 0;
1110     tm_year := 0;
1111     end;
1112     FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1113     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1114     isc_encode_sql_time(@tm_date, PISC_TIME(FXSQLVAR^.sqldata));
1115     if Ms > 0 then
1116     Inc(PISC_TIME(FXSQLVAR^.sqldata)^,Ms*10);
1117     FModified := True;
1118     end;
1119    
1120     procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1121     var
1122     i: Integer;
1123     begin
1124     if FSQL.Database.SQLDialect < 3 then
1125     begin
1126     AsDateTime := Value;
1127     exit;
1128     end;
1129    
1130     if FUniqueName then
1131     xSetAsTime(Value)
1132     else
1133     for i := 0 to FParent.FCount - 1 do
1134     if FParent[i].FName = FName then
1135     FParent[i].xSetAsTime(Value);
1136     end;
1137    
1138     procedure TIBXSQLVAR.xSetAsDateTime(Value: TDateTime);
1139     var
1140     tm_date: TCTimeStructure;
1141     Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1142     begin
1143     if IsNullable then
1144     IsNull := False;
1145    
1146     FXSQLVAR^.sqltype := SQL_TIMESTAMP or (FXSQLVAR^.sqltype and 1);
1147     DecodeDate(Value, Yr, Mn, Dy);
1148     DecodeTime(Value, Hr, Mt, S, Ms);
1149     with tm_date do begin
1150     tm_sec := S;
1151     tm_min := Mt;
1152     tm_hour := Hr;
1153     tm_mday := Dy;
1154     tm_mon := Mn - 1;
1155     tm_year := Yr - 1900;
1156     end;
1157     FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1158     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1159     isc_encode_date(@tm_date, PISC_QUAD(FXSQLVAR^.sqldata));
1160     if Ms > 0 then
1161     Inc(PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time,Ms*10);
1162     FModified := True;
1163     end;
1164    
1165     procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1166     var
1167     i: Integer;
1168     begin
1169     if FUniqueName then
1170     xSetAsDateTime(value)
1171     else
1172     for i := 0 to FParent.FCount - 1 do
1173     if FParent[i].FName = FName then
1174     FParent[i].xSetAsDateTime(Value);
1175     end;
1176    
1177     procedure TIBXSQLVAR.xSetAsDouble(Value: Double);
1178     begin
1179     if IsNullable then
1180     IsNull := False;
1181    
1182     FXSQLVAR^.sqltype := SQL_DOUBLE or (FXSQLVAR^.sqltype and 1);
1183     FXSQLVAR^.sqllen := SizeOf(Double);
1184     FXSQLVAR^.sqlscale := 0;
1185     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1186     PDouble(FXSQLVAR^.sqldata)^ := Value;
1187     FModified := True;
1188     end;
1189    
1190     procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1191     var
1192     i: Integer;
1193     begin
1194     if FUniqueName then
1195     xSetAsDouble(Value)
1196     else
1197     for i := 0 to FParent.FCount - 1 do
1198     if FParent[i].FName = FName then
1199     FParent[i].xSetAsDouble(Value);
1200     end;
1201    
1202     procedure TIBXSQLVAR.xSetAsFloat(Value: Float);
1203     begin
1204     if IsNullable then
1205     IsNull := False;
1206    
1207     FXSQLVAR^.sqltype := SQL_FLOAT or (FXSQLVAR^.sqltype and 1);
1208     FXSQLVAR^.sqllen := SizeOf(Float);
1209     FXSQLVAR^.sqlscale := 0;
1210     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1211     PSingle(FXSQLVAR^.sqldata)^ := Value;
1212     FModified := True;
1213     end;
1214    
1215     procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1216     var
1217     i: Integer;
1218     begin
1219     if FUniqueName then
1220     xSetAsFloat(Value)
1221     else
1222     for i := 0 to FParent.FCount - 1 do
1223     if FParent[i].FName = FName then
1224     FParent[i].xSetAsFloat(Value);
1225     end;
1226    
1227     procedure TIBXSQLVAR.xSetAsLong(Value: Long);
1228     begin
1229     if IsNullable then
1230     IsNull := False;
1231    
1232     FXSQLVAR^.sqltype := SQL_LONG or (FXSQLVAR^.sqltype and 1);
1233     FXSQLVAR^.sqllen := SizeOf(Long);
1234     FXSQLVAR^.sqlscale := 0;
1235     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1236     PLong(FXSQLVAR^.sqldata)^ := Value;
1237     FModified := True;
1238     end;
1239    
1240     procedure TIBXSQLVAR.SetAsLong(Value: Long);
1241     var
1242     i: Integer;
1243     begin
1244     if FUniqueName then
1245     xSetAsLong(Value)
1246     else
1247     for i := 0 to FParent.FCount - 1 do
1248     if FParent[i].FName = FName then
1249     FParent[i].xSetAsLong(Value);
1250     end;
1251    
1252     procedure TIBXSQLVAR.xSetAsPointer(Value: Pointer);
1253     begin
1254     if IsNullable and (Value = nil) then
1255     IsNull := True
1256     else begin
1257     IsNull := False;
1258     FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1259     Move(Value^, FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1260     end;
1261     FModified := True;
1262     end;
1263    
1264     procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1265     var
1266     i: Integer;
1267     begin
1268     if FUniqueName then
1269     xSetAsPointer(Value)
1270     else
1271     for i := 0 to FParent.FCount - 1 do
1272     if FParent[i].FName = FName then
1273     FParent[i].xSetAsPointer(Value);
1274     end;
1275    
1276     procedure TIBXSQLVAR.xSetAsQuad(Value: TISC_QUAD);
1277     begin
1278     if IsNullable then
1279     IsNull := False;
1280     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1281     (FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1282     IBError(ibxeInvalidDataConversion, [nil]);
1283     FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1284     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1285     PISC_QUAD(FXSQLVAR^.sqldata)^ := Value;
1286     FModified := True;
1287     end;
1288    
1289     procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1290     var
1291     i: Integer;
1292     begin
1293     if FUniqueName then
1294     xSetAsQuad(Value)
1295     else
1296     for i := 0 to FParent.FCount - 1 do
1297     if FParent[i].FName = FName then
1298     FParent[i].xSetAsQuad(Value);
1299     end;
1300    
1301     procedure TIBXSQLVAR.xSetAsShort(Value: Short);
1302     begin
1303     if IsNullable then
1304     IsNull := False;
1305    
1306     FXSQLVAR^.sqltype := SQL_SHORT or (FXSQLVAR^.sqltype and 1);
1307     FXSQLVAR^.sqllen := SizeOf(Short);
1308     FXSQLVAR^.sqlscale := 0;
1309     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1310     PShort(FXSQLVAR^.sqldata)^ := Value;
1311     FModified := True;
1312     end;
1313    
1314     procedure TIBXSQLVAR.SetAsShort(Value: Short);
1315     var
1316     i: Integer;
1317     begin
1318     if FUniqueName then
1319     xSetAsShort(Value)
1320     else
1321     for i := 0 to FParent.FCount - 1 do
1322     if FParent[i].FName = FName then
1323     FParent[i].xSetAsShort(Value);
1324     end;
1325    
1326     procedure TIBXSQLVAR.xSetAsString(Value: String);
1327     var
1328     stype: Integer;
1329     ss: TStringStream;
1330    
1331     procedure SetStringValue;
1332     var
1333     i: Integer;
1334     begin
1335     if (FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1336     (FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1337     Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen)
1338     else begin
1339     FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1340     FXSQLVAR^.sqllen := Length(Value);
1341     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen + 1);
1342     if (Length(Value) > 0) then
1343     Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1344     end;
1345     FModified := True;
1346     end;
1347    
1348     begin
1349     if IsNullable then
1350     IsNull := False;
1351    
1352     stype := FXSQLVAR^.sqltype and (not 1);
1353     if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1354     SetStringValue
1355     else begin
1356     if (stype = SQL_BLOB) then
1357     begin
1358     ss := TStringStream.Create(Value);
1359     try
1360     LoadFromStream(ss);
1361     finally
1362     ss.Free;
1363     end;
1364     end
1365     else if Value = '' then
1366     IsNull := True
1367     else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1368     (stype = SQL_TYPE_TIME) then
1369     xSetAsDateTime(StrToDateTime(Value))
1370     else
1371     SetStringValue;
1372     end;
1373     end;
1374    
1375     procedure TIBXSQLVAR.SetAsString(Value: String);
1376     var
1377     i: integer;
1378     begin
1379     if FUniqueName then
1380     xSetAsString(Value)
1381     else
1382     for i := 0 to FParent.FCount - 1 do
1383     if FParent[i].FName = FName then
1384     FParent[i].xSetAsString(Value);
1385     end;
1386    
1387     procedure TIBXSQLVAR.xSetAsVariant(Value: Variant);
1388     begin
1389     if VarIsNull(Value) then
1390     IsNull := True
1391     else case VarType(Value) of
1392     varEmpty, varNull:
1393     IsNull := True;
1394     varSmallint, varInteger, varByte,
1395     varWord, varShortInt:
1396     AsLong := Value;
1397     varInt64:
1398     AsInt64 := Value;
1399     varSingle, varDouble:
1400     AsDouble := Value;
1401     varCurrency:
1402     AsCurrency := Value;
1403     varBoolean:
1404     if Value then
1405     AsLong := ISC_TRUE
1406     else
1407     AsLong := ISC_FALSE;
1408     varDate:
1409     AsDateTime := Value;
1410     varOleStr, varString:
1411     AsString := Value;
1412     varArray:
1413     IBError(ibxeNotSupported, [nil]);
1414     varByRef, varDispatch, varError, varUnknown, varVariant:
1415     IBError(ibxeNotPermitted, [nil]);
1416     end;
1417     end;
1418    
1419     procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1420     var
1421     i: integer;
1422     begin
1423     if FUniqueName then
1424     xSetAsVariant(Value)
1425     else
1426     for i := 0 to FParent.FCount - 1 do
1427     if FParent[i].FName = FName then
1428     FParent[i].xSetAsVariant(Value);
1429     end;
1430    
1431     procedure TIBXSQLVAR.xSetAsXSQLVAR(Value: PXSQLVAR);
1432     var
1433     sqlind: PShort;
1434     sqldata: PChar;
1435     local_sqllen: Integer;
1436     begin
1437     sqlind := FXSQLVAR^.sqlind;
1438     sqldata := FXSQLVAR^.sqldata;
1439     Move(Value^, FXSQLVAR^, SizeOf(TXSQLVAR));
1440     FXSQLVAR^.sqlind := sqlind;
1441     FXSQLVAR^.sqldata := sqldata;
1442     if (Value^.sqltype and 1 = 1) then
1443     begin
1444     if (FXSQLVAR^.sqlind = nil) then
1445     IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1446     FXSQLVAR^.sqlind^ := Value^.sqlind^;
1447     end
1448     else
1449     if (FXSQLVAR^.sqlind <> nil) then
1450     ReallocMem(FXSQLVAR^.sqlind, 0);
1451     if ((FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1452     local_sqllen := FXSQLVAR^.sqllen + 2
1453     else
1454     local_sqllen := FXSQLVAR^.sqllen;
1455     FXSQLVAR^.sqlscale := Value^.sqlscale;
1456     IBAlloc(FXSQLVAR^.sqldata, 0, local_sqllen);
1457     Move(Value^.sqldata[0], FXSQLVAR^.sqldata[0], local_sqllen);
1458     FModified := True;
1459     end;
1460    
1461     procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1462     var
1463     i: Integer;
1464     begin
1465     if FUniqueName then
1466     xSetAsXSQLVAR(Value)
1467     else
1468     for i := 0 to FParent.FCount - 1 do
1469     if FParent[i].FName = FName then
1470     FParent[i].xSetAsXSQLVAR(Value);
1471     end;
1472    
1473     procedure TIBXSQLVAR.xSetIsNull(Value: Boolean);
1474     begin
1475     if Value then
1476     begin
1477     if not IsNullable then
1478     IsNullable := True;
1479    
1480     if Assigned(FXSQLVAR^.sqlind) then
1481     FXSQLVAR^.sqlind^ := -1;
1482     FModified := True;
1483     end
1484     else
1485     if ((not Value) and IsNullable) then
1486     begin
1487     if Assigned(FXSQLVAR^.sqlind) then
1488     FXSQLVAR^.sqlind^ := 0;
1489     FModified := True;
1490     end;
1491     end;
1492    
1493     procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
1494     var
1495     i: Integer;
1496     begin
1497     if FUniqueName then
1498     xSetIsNull(Value)
1499     else
1500     for i := 0 to FParent.FCount - 1 do
1501     if FParent[i].FName = FName then
1502     FParent[i].xSetIsNull(Value);
1503     end;
1504    
1505     procedure TIBXSQLVAR.xSetIsNullable(Value: Boolean);
1506     begin
1507     if (Value <> IsNullable) then
1508     begin
1509     if Value then
1510     begin
1511     FXSQLVAR^.sqltype := FXSQLVAR^.sqltype or 1;
1512     IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1513     end
1514     else
1515     begin
1516     FXSQLVAR^.sqltype := FXSQLVAR^.sqltype and (not 1);
1517     ReallocMem(FXSQLVAR^.sqlind, 0);
1518     end;
1519     end;
1520     end;
1521    
1522     procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1523     var
1524     i: Integer;
1525     begin
1526     if FUniqueName then
1527     xSetIsNullable(Value)
1528     else
1529     for i := 0 to FParent.FCount - 1 do
1530     if FParent[i].FName = FName then
1531     FParent[i].xSetIsNullable(Value);
1532     end;
1533    
1534     procedure TIBXSQLVAR.Clear;
1535     begin
1536     IsNull := true;
1537     end;
1538    
1539    
1540     { TIBXSQLDA }
1541     constructor TIBXSQLDA.Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
1542     begin
1543     inherited Create;
1544     FSQL := Query;
1545     FSize := 0;
1546     FUniqueRelationName := '';
1547     FInputSQLDA := sqldaType = daInput;
1548     end;
1549    
1550     destructor TIBXSQLDA.Destroy;
1551     var
1552     i: Integer;
1553     begin
1554     if FXSQLDA <> nil then
1555     begin
1556     for i := 0 to FSize - 1 do
1557     begin
1558     FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1559     FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1560     FXSQLVARs[i].Free ;
1561     end;
1562     FreeMem(FXSQLDA);
1563     FXSQLDA := nil;
1564     FXSQLVARs := nil;
1565     end;
1566     inherited Destroy;
1567     end;
1568    
1569     procedure TIBXSQLDA.SetParamName(FieldName: String; Idx: Integer;
1570     UniqueName: boolean);
1571     var
1572     fn: string;
1573     begin
1574     {$ifdef UseCaseSensitiveParamName}
1575     FXSQLVARs[Idx].FName := AnsiUpperCase(FieldName);
1576     {$else}
1577     FXSQLVARs[Idx].FName := FieldName;
1578     {$endif}
1579     FXSQLVARs[Idx].FIndex := Idx;
1580     FXSQLVARs[Idx].FUniqueName := UniqueName
1581     end;
1582    
1583     function TIBXSQLDA.GetModified: Boolean;
1584     var
1585     i: Integer;
1586     begin
1587     result := False;
1588     for i := 0 to FCount - 1 do
1589     if FXSQLVARs[i].Modified then
1590     begin
1591     result := True;
1592     exit;
1593     end;
1594     end;
1595    
1596     function TIBXSQLDA.GetRecordSize: Integer;
1597     begin
1598     result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1599     end;
1600    
1601     function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1602     begin
1603     result := FXSQLDA;
1604     end;
1605    
1606     function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1607     begin
1608     if (Idx < 0) or (Idx >= FCount) then
1609     IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1610     result := FXSQLVARs[Idx]
1611     end;
1612    
1613     function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1614     begin
1615     result := GetXSQLVARByName(Idx);
1616     if result = nil then
1617     IBError(ibxeFieldNotFound, [Idx]);
1618     end;
1619    
1620     function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1621     var
1622     s: String;
1623     i: Integer;
1624     begin
1625     {$ifdef ALLOWDIALECT3PARAMNAMES}
1626     s := FormatIdentifierValueNC(FSQL.Database.SQLDialect, Idx);
1627     {$else}
1628     {$ifdef UseCaseSensitiveParamName}
1629     s := AnsiUpperCase(Idx);
1630     {$else}
1631     s := Idx;
1632     {$endif}
1633     {$endif}
1634     for i := 0 to FCount - 1 do
1635     if Vars[i].FName = s then
1636     begin
1637     Result := FXSQLVARs[i];
1638     Exit;
1639     end;
1640     Result := nil;
1641     end;
1642    
1643     procedure TIBXSQLDA.Initialize;
1644    
1645     function VarByName(idx: string; limit: integer): TIBXSQLVAR;
1646     var
1647     k: integer;
1648     begin
1649     for k := 0 to limit do
1650     if FXSQLVARs[k].FName = idx then
1651     begin
1652     Result := FXSQLVARs[k];
1653     Exit;
1654     end;
1655     Result := nil;
1656     end;
1657    
1658     var
1659     i, j, j_len: Integer;
1660     st: String;
1661     bUnique: Boolean;
1662     sBaseName: string;
1663     begin
1664     bUnique := True;
1665     if FXSQLDA <> nil then
1666     begin
1667     for i := 0 to FCount - 1 do
1668     begin
1669     with FXSQLVARs[i].Data^ do
1670     begin
1671    
1672     {First get the unique relation name, if any}
1673    
1674     if bUnique and (strpas(relname) <> '') then
1675     begin
1676     if FUniqueRelationName = '' then
1677     FUniqueRelationName := strpas(relname)
1678     else
1679     if strpas(relname) <> FUniqueRelationName then
1680     begin
1681     FUniqueRelationName := '';
1682     bUnique := False;
1683     end;
1684     end;
1685    
1686     {If an output SQLDA then copy the aliasnames to the FName list. Ensure
1687     that they are all upper case only and disambiguated.
1688     }
1689    
1690     if not FInputSQLDA then
1691     begin
1692     st := Space2Underscore(AnsiUppercase(strpas(aliasname)));
1693     if st = '' then
1694     begin
1695     sBaseName := 'F_'; {do not localize}
1696     aliasname_length := 2;
1697     j := 1; j_len := 1;
1698     st := sBaseName + IntToStr(j);
1699     end
1700     else
1701     begin
1702     j := 0; j_len := 0;
1703     sBaseName := st;
1704     end;
1705    
1706     {Look for other columns with the same name and make unique}
1707    
1708     while VarByName(st,i-1) <> nil do
1709     begin
1710     Inc(j);
1711     j_len := Length(IntToStr(j));
1712     if j_len + Length(sBaseName) > 31 then
1713     st := Copy(sBaseName, 1, 31 - j_len) + IntToStr(j)
1714     else
1715     st := sBaseName + IntToStr(j);
1716     end;
1717    
1718     FXSQLVARs[i].FName := st;
1719     end;
1720    
1721     {Finally initialise the XSQLVAR}
1722    
1723     FXSQLVARs[i].FIndex := i;
1724    
1725     case sqltype and (not 1) of
1726     SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1727     SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1728     SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1729     if (sqllen = 0) then
1730     { Make sure you get a valid pointer anyway
1731     select '' from foo }
1732     IBAlloc(sqldata, 0, 1)
1733     else
1734     IBAlloc(sqldata, 0, sqllen)
1735     end;
1736     SQL_VARYING: begin
1737     IBAlloc(sqldata, 0, sqllen + 2);
1738     end;
1739     else
1740     IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1741     end;
1742     if (sqltype and 1 = 1) then
1743     IBAlloc(sqlind, 0, SizeOf(Short))
1744     else
1745     if (sqlind <> nil) then
1746     ReallocMem(sqlind, 0);
1747     end;
1748     end;
1749     end;
1750     end;
1751    
1752     procedure TIBXSQLDA.SetCount(Value: Integer);
1753     var
1754     i, OldSize: Integer;
1755     p : PXSQLVAR;
1756     begin
1757     FCount := Value;
1758     if FCount = 0 then
1759     FUniqueRelationName := ''
1760     else
1761     begin
1762     if FSize > 0 then
1763     OldSize := XSQLDA_LENGTH(FSize)
1764     else
1765     OldSize := 0;
1766     if FCount > FSize then
1767     begin
1768     IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1769     SetLength(FXSQLVARs, FCount);
1770     FXSQLDA^.version := SQLDA_VERSION1;
1771     p := @FXSQLDA^.sqlvar[0];
1772     for i := 0 to FCount - 1 do
1773     begin
1774     if i >= FSize then
1775     FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1776     FXSQLVARs[i].FXSQLVAR := p;
1777     p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1778     end;
1779     FSize := FCount;
1780     end;
1781     if FSize > 0 then
1782     begin
1783     FXSQLDA^.sqln := Value;
1784     FXSQLDA^.sqld := Value;
1785     end;
1786     end;
1787     end;
1788    
1789     { TIBOutputDelimitedFile }
1790    
1791     destructor TIBOutputDelimitedFile.Destroy;
1792     begin
1793     {$IFDEF UNIX}
1794     if FHandle <> -1 then
1795     fpclose(FHandle);
1796     {$ELSE}
1797     if FHandle <> 0 then
1798     begin
1799     FlushFileBuffers(FHandle);
1800     CloseHandle(FHandle);
1801     end;
1802     {$ENDIF}
1803     inherited Destroy;
1804     end;
1805    
1806     procedure TIBOutputDelimitedFile.ReadyFile;
1807     var
1808     i: Integer;
1809     {$IFDEF UNIX}
1810     BytesWritten: cint;
1811     {$ELSE}
1812     BytesWritten: DWORD;
1813     {$ENDIF}
1814     st: string;
1815     begin
1816     if FColDelimiter = '' then
1817     FColDelimiter := TAB;
1818     if FRowDelimiter = '' then
1819     FRowDelimiter := CRLF;
1820     {$IFDEF UNIX}
1821     FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
1822     {$ELSE}
1823     FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
1824     FILE_ATTRIBUTE_NORMAL, 0);
1825     if FHandle = INVALID_HANDLE_VALUE then
1826     FHandle := 0;
1827     {$ENDIF}
1828     if FOutputTitles then
1829     begin
1830     for i := 0 to Columns.Count - 1 do
1831     if i = 0 then
1832     st := strpas(Columns[i].Data^.aliasname)
1833     else
1834     st := st + FColDelimiter + strpas(Columns[i].Data^.aliasname);
1835     st := st + FRowDelimiter;
1836     {$IFDEF UNIX}
1837     if FHandle <> -1 then
1838     BytesWritten := FpWrite(FHandle,st[1],Length(st));
1839     if BytesWritten = -1 then
1840     raise Exception.Create('File Write Error');
1841     {$ELSE}
1842     WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
1843     {$ENDIF}
1844     end;
1845     end;
1846    
1847     function TIBOutputDelimitedFile.WriteColumns: Boolean;
1848     var
1849     i: Integer;
1850     {$IFDEF UNIX}
1851     BytesWritten: cint;
1852     {$ELSE}
1853     BytesWritten: DWORD;
1854     {$ENDIF}
1855     st: string;
1856     begin
1857     result := False;
1858     {$IFDEF UNIX}
1859     if FHandle <> -1 then
1860     {$ELSE}
1861     if FHandle <> 0 then
1862     {$ENDIF}
1863     begin
1864     st := '';
1865     for i := 0 to Columns.Count - 1 do
1866     begin
1867     if i > 0 then
1868     st := st + FColDelimiter;
1869     st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
1870     end;
1871     st := st + FRowDelimiter;
1872     {$IFDEF UNIX}
1873     BytesWritten := FpWrite(FHandle,st[1],Length(st));
1874     {$ELSE}
1875     WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
1876     {$ENDIF}
1877     if BytesWritten = DWORD(Length(st)) then
1878     result := True;
1879     end
1880     end;
1881    
1882     { TIBInputDelimitedFile }
1883    
1884     destructor TIBInputDelimitedFile.Destroy;
1885     begin
1886     FFile.Free;
1887     inherited Destroy;
1888     end;
1889    
1890     function TIBInputDelimitedFile.GetColumn(var Col: string): Integer;
1891     var
1892     c: Char;
1893     BytesRead: Integer;
1894    
1895     procedure ReadInput;
1896     begin
1897     if FLookAhead <> NULL_TERMINATOR then
1898     begin
1899     c := FLookAhead;
1900     BytesRead := 1;
1901     FLookAhead := NULL_TERMINATOR;
1902     end else
1903     BytesRead := FFile.Read(c, 1);
1904     end;
1905    
1906     procedure CheckCRLF(Delimiter: string);
1907     begin
1908     if (c = CR) and (Pos(LF, Delimiter) > 0) then {mbcs ok}
1909     begin
1910     BytesRead := FFile.Read(c, 1);
1911     if (BytesRead = 1) and (c <> #10) then
1912     FLookAhead := c
1913     end;
1914     end;
1915    
1916     begin
1917     Col := '';
1918     result := 0;
1919     ReadInput;
1920     while BytesRead <> 0 do begin
1921     if Pos(c, FColDelimiter) > 0 then {mbcs ok}
1922     begin
1923     CheckCRLF(FColDelimiter);
1924     result := 1;
1925     break;
1926     end else if Pos(c, FRowDelimiter) > 0 then {mbcs ok}
1927     begin
1928     CheckCRLF(FRowDelimiter);
1929     result := 2;
1930     break;
1931     end else
1932     Col := Col + c;
1933     ReadInput;
1934     end;
1935     end;
1936    
1937     function TIBInputDelimitedFile.ReadParameters: Boolean;
1938     var
1939     i, curcol: Integer;
1940     Col: string;
1941     begin
1942     result := False;
1943     if not FEOF then begin
1944     curcol := 0;
1945     repeat
1946     i := GetColumn(Col);
1947     if (i = 0) then
1948     FEOF := True;
1949     if (curcol < Params.Count) then
1950     begin
1951     try
1952     if (Col = '') and
1953     (ReadBlanksAsNull) then
1954     Params[curcol].IsNull := True
1955     else
1956     Params[curcol].AsString := Col;
1957     Inc(curcol);
1958     except
1959     on E: Exception do begin
1960     if not (FEOF and (curcol = Params.Count)) then
1961     raise;
1962     end;
1963     end;
1964     end;
1965     until (FEOF) or (i = 2);
1966     result := ((FEOF) and (curcol = Params.Count)) or
1967     (not FEOF);
1968     end;
1969     end;
1970    
1971     procedure TIBInputDelimitedFile.ReadyFile;
1972     begin
1973     if FColDelimiter = '' then
1974     FColDelimiter := TAB;
1975     if FRowDelimiter = '' then
1976     FRowDelimiter := CRLF;
1977     FLookAhead := NULL_TERMINATOR;
1978     FEOF := False;
1979     if FFile <> nil then
1980     FFile.Free;
1981     FFile := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyWrite);
1982     if FSkipTitles then
1983     ReadParameters;
1984     end;
1985    
1986     { TIBOutputRawFile }
1987     destructor TIBOutputRawFile.Destroy;
1988     begin
1989     {$IFDEF UNIX}
1990     if FHandle <> -1 then
1991     fpclose(FHandle);
1992     {$ELSE}
1993     if FHandle <> 0 then
1994     begin
1995     FlushFileBuffers(FHandle);
1996     CloseHandle(FHandle);
1997     end;
1998     {$ENDIF}
1999     inherited Destroy;
2000     end;
2001    
2002     procedure TIBOutputRawFile.ReadyFile;
2003     begin
2004     {$IFDEF UNIX}
2005     FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
2006     {$ELSE}
2007     FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
2008     FILE_ATTRIBUTE_NORMAL, 0);
2009     if FHandle = INVALID_HANDLE_VALUE then
2010     FHandle := 0;
2011     {$ENDIF}
2012     end;
2013    
2014     function TIBOutputRawFile.WriteColumns: Boolean;
2015     var
2016     i: Integer;
2017     BytesWritten: DWord;
2018     begin
2019     result := False;
2020     if FHandle <> 0 then
2021     begin
2022     for i := 0 to Columns.Count - 1 do
2023     begin
2024     {$IFDEF UNIX}
2025     BytesWritten := FpWrite(FHandle,Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen);
2026     {$ELSE}
2027     WriteFile(FHandle, Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen,
2028     BytesWritten, nil);
2029     {$ENDIF}
2030     if BytesWritten <> DWORD(Columns[i].Data^.sqllen) then
2031     exit;
2032     end;
2033     result := True;
2034     end;
2035     end;
2036    
2037     { TIBInputRawFile }
2038     destructor TIBInputRawFile.Destroy;
2039     begin
2040     {$IFDEF UNIX}
2041     if FHandle <> -1 then
2042     fpclose(FHandle);
2043     {$ELSE}
2044     if FHandle <> 0 then
2045     CloseHandle(FHandle);
2046     {$ENDIF}
2047     inherited Destroy;
2048     end;
2049    
2050     function TIBInputRawFile.ReadParameters: Boolean;
2051     var
2052     i: Integer;
2053     BytesRead: DWord;
2054     begin
2055     result := False;
2056     {$IFDEF UNIX}
2057     if FHandle <> -1 then
2058     {$ELSE}
2059     if FHandle <> 0 then
2060     {$ENDIF}
2061     begin
2062     for i := 0 to Params.Count - 1 do
2063     begin
2064     {$IFDEF UNIX}
2065     BytesRead := FpRead(FHandle,Params[i].Data^.sqldata^,Params[i].Data^.sqllen);
2066     {$ELSE}
2067     ReadFile(FHandle, Params[i].Data^.sqldata^, Params[i].Data^.sqllen,
2068     BytesRead, nil);
2069     {$ENDIF}
2070     if BytesRead <> DWORD(Params[i].Data^.sqllen) then
2071     exit;
2072     end;
2073     result := True;
2074     end;
2075     end;
2076    
2077     procedure TIBInputRawFile.ReadyFile;
2078     begin
2079     {$IFDEF UNIX}
2080     if FHandle <> -1 then
2081     fpclose(FHandle);
2082     FHandle := FpOpen(Filename,O_RdOnly);
2083     if FHandle = -1 then
2084     raise Exception.CreateFmt('Unable to open file %s',[Filename]);
2085     {$ELSE}
2086     if FHandle <> 0 then
2087     CloseHandle(FHandle);
2088     FHandle := CreateFile(PChar(Filename), GENERIC_READ, 0, nil, OPEN_EXISTING,
2089     FILE_FLAG_SEQUENTIAL_SCAN, 0);
2090     if FHandle = INVALID_HANDLE_VALUE then
2091     FHandle := 0;
2092     {$ENDIF}
2093     end;
2094    
2095     { TIBSQL }
2096     constructor TIBSQL.Create(AOwner: TComponent);
2097     begin
2098     inherited Create(AOwner);
2099     FIBLoaded := False;
2100     CheckIBLoaded;
2101     FIBLoaded := True;
2102     FGenerateParamNames := False;
2103     FGoToFirstRecordOnExecute := True;
2104     FBase := TIBBase.Create(Self);
2105     FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
2106     FBase.BeforeTransactionEnd := BeforeTransactionEnd;
2107     FBOF := False;
2108     FEOF := False;
2109     FPrepared := False;
2110     FRecordCount := 0;
2111     FSQL := TStringList.Create;
2112     TStringList(FSQL).OnChanging := SQLChanging;
2113     FProcessedSQL := TStringList.Create;
2114     FHandle := nil;
2115     FSQLParams := TIBXSQLDA.Create(self,daInput);
2116     FSQLRecord := TIBXSQLDA.Create(self,daOutput);
2117     FSQLType := SQLUnknown;
2118     FParamCheck := True;
2119     FCursor := Name + RandomString(8);
2120     if AOwner is TIBDatabase then
2121     Database := TIBDatabase(AOwner)
2122     else
2123     if AOwner is TIBTransaction then
2124     Transaction := TIBTransaction(AOwner);
2125     end;
2126    
2127     destructor TIBSQL.Destroy;
2128     begin
2129     if FIBLoaded then
2130     begin
2131     if (FOpen) then
2132     Close;
2133     if (FHandle <> nil) then
2134     FreeHandle;
2135     FSQL.Free;
2136     FProcessedSQL.Free;
2137     FBase.Free;
2138     FSQLParams.Free;
2139     FSQLRecord.Free;
2140     end;
2141     inherited Destroy;
2142     end;
2143    
2144     procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
2145     begin
2146     if not Prepared then
2147     Prepare;
2148     InputObject.FParams := Self.FSQLParams;
2149     InputObject.ReadyFile;
2150     if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
2151     while InputObject.ReadParameters do
2152     ExecQuery;
2153     end;
2154    
2155     procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
2156     begin
2157     CheckClosed;
2158     if not Prepared then
2159     Prepare;
2160     if FSQLType = SQLSelect then begin
2161     try
2162     ExecQuery;
2163     OutputObject.FColumns := Self.FSQLRecord;
2164     OutputObject.ReadyFile;
2165     if not FGoToFirstRecordOnExecute then
2166     Next;
2167     while (not Eof) and (OutputObject.WriteColumns) do
2168     Next;
2169     finally
2170     Close;
2171     end;
2172     end;
2173     end;
2174    
2175     procedure TIBSQL.CheckClosed;
2176     begin
2177     if FOpen then IBError(ibxeSQLOpen, [nil]);
2178     end;
2179    
2180     procedure TIBSQL.CheckOpen;
2181     begin
2182     if not FOpen then IBError(ibxeSQLClosed, [nil]);
2183     end;
2184    
2185     procedure TIBSQL.CheckValidStatement;
2186     begin
2187     FBase.CheckTransaction;
2188     if (FHandle = nil) then
2189     IBError(ibxeInvalidStatementHandle, [nil]);
2190     end;
2191    
2192     procedure TIBSQL.Close;
2193     var
2194     isc_res: ISC_STATUS;
2195     begin
2196     try
2197     if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then begin
2198     isc_res := Call(
2199     isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
2200     False);
2201     if (StatusVector^ = 1) and (isc_res > 0) and
2202     not CheckStatusVector(
2203     [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2204     IBDatabaseError;
2205     end;
2206     finally
2207     FEOF := False;
2208     FBOF := False;
2209     FOpen := False;
2210     FRecordCount := 0;
2211     end;
2212     end;
2213    
2214     function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
2215     begin
2216     result := 0;
2217     if Transaction <> nil then
2218     result := Transaction.Call(ErrCode, RaiseError)
2219     else
2220     if RaiseError and (ErrCode > 0) then
2221     IBDataBaseError;
2222     end;
2223    
2224     function TIBSQL.Current: TIBXSQLDA;
2225     begin
2226     result := FSQLRecord;
2227     end;
2228    
2229     function TIBSQL.GetFieldCount: integer;
2230     begin
2231     Result := FSQLRecord.Count
2232     end;
2233    
2234     procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
2235     begin
2236     if FUniqueParamNames = AValue then Exit;
2237     FreeHandle;
2238     FUniqueParamNames := AValue;
2239     end;
2240    
2241     procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
2242     begin
2243     if (FHandle <> nil) then begin
2244     Close;
2245     FreeHandle;
2246     end;
2247     end;
2248    
2249     procedure TIBSQL.ExecQuery;
2250     var
2251     fetch_res: ISC_STATUS;
2252     begin
2253     CheckClosed;
2254     if not Prepared then Prepare;
2255     CheckValidStatement;
2256     case FSQLType of
2257     SQLSelect: begin
2258     Call(isc_dsql_execute2(StatusVector,
2259     TRHandle,
2260     @FHandle,
2261     Database.SQLDialect,
2262     FSQLParams.AsXSQLDA,
2263     nil), True);
2264     Call(
2265     isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
2266     True);
2267     FOpen := True;
2268     FBOF := True;
2269     FEOF := False;
2270     FRecordCount := 0;
2271     if FGoToFirstRecordOnExecute then
2272     Next;
2273     end;
2274     SQLExecProcedure: begin
2275     fetch_res := Call(isc_dsql_execute2(StatusVector,
2276     TRHandle,
2277     @FHandle,
2278     Database.SQLDialect,
2279     FSQLParams.AsXSQLDA,
2280     FSQLRecord.AsXSQLDA), True);
2281     (* if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2282     begin
2283     { Sometimes a prepared stored procedure appears to get
2284     off sync on the server ....This code is meant to try
2285     to work around the problem simply by "retrying". This
2286     need to be reproduced and fixed.
2287     }
2288     isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2289     PChar(FProcessedSQL.Text), 1, nil);
2290     Call(isc_dsql_execute2(StatusVector,
2291     TRHandle,
2292     @FHandle,
2293     Database.SQLDialect,
2294     FSQLParams.AsXSQLDA,
2295     FSQLRecord.AsXSQLDA), True);
2296     end; *)
2297     end
2298     else
2299     Call(isc_dsql_execute(StatusVector,
2300     TRHandle,
2301     @FHandle,
2302     Database.SQLDialect,
2303     FSQLParams.AsXSQLDA), True)
2304     end;
2305     if not (csDesigning in ComponentState) then
2306     MonitorHook.SQLExecute(Self);
2307     end;
2308    
2309     function TIBSQL.GetEOF: Boolean;
2310     begin
2311     result := FEOF or not FOpen;
2312     end;
2313    
2314     function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
2315     var
2316     i: Integer;
2317     begin
2318     i := GetFieldIndex(FieldName);
2319     if (i < 0) then
2320     IBError(ibxeFieldNotFound, [FieldName]);
2321     result := GetFields(i);
2322     end;
2323    
2324     function TIBSQL.ParamByName(ParamName: String): TIBXSQLVAR;
2325     begin
2326     Result := Params.ByName(ParamName);
2327     end;
2328    
2329     function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
2330     begin
2331     if (Idx < 0) or (Idx >= FSQLRecord.Count) then
2332     IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
2333     result := FSQLRecord[Idx];
2334     end;
2335    
2336     function TIBSQL.GetFieldIndex(FieldName: String): Integer;
2337     begin
2338     if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
2339     result := -1
2340     else
2341     result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
2342     end;
2343    
2344     function TIBSQL.Next: TIBXSQLDA;
2345     var
2346     fetch_res: ISC_STATUS;
2347     begin
2348     result := nil;
2349     if not FEOF then begin
2350     CheckOpen;
2351     { Go to the next record... }
2352     fetch_res :=
2353     Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
2354     if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then begin
2355     FEOF := True;
2356     end else if (fetch_res > 0) then begin
2357     try
2358     IBDataBaseError;
2359     except
2360     Close;
2361     raise;
2362     end;
2363     end else begin
2364     Inc(FRecordCount);
2365     FBOF := False;
2366     result := FSQLRecord;
2367     end;
2368     if not (csDesigning in ComponentState) then
2369     MonitorHook.SQLFetch(Self);
2370     end;
2371     end;
2372    
2373     procedure TIBSQL.FreeHandle;
2374     var
2375     isc_res: ISC_STATUS;
2376     begin
2377     try
2378     { The following two lines merely set the SQLDA count
2379     variable FCount to 0, but do not deallocate
2380     That way the allocations can be reused for
2381     a new query sring in the same SQL instance }
2382     FSQLRecord.Count := 0;
2383     FSQLParams.Count := 0;
2384     if FHandle <> nil then begin
2385     isc_res :=
2386     Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2387     if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2388     IBDataBaseError;
2389     end;
2390     finally
2391     FPrepared := False;
2392     FHandle := nil;
2393     end;
2394     end;
2395    
2396     function TIBSQL.GetDatabase: TIBDatabase;
2397     begin
2398     result := FBase.Database;
2399     end;
2400    
2401     function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2402     begin
2403     result := FBase.DBHandle;
2404     end;
2405    
2406     function TIBSQL.GetPlan: String;
2407     var
2408     result_buffer: array[0..16384] of Char;
2409     result_length, i: Integer;
2410     info_request: Char;
2411     begin
2412     if (not Prepared) or
2413     (not (FSQLType in [SQLSelect, SQLSelectForUpdate,
2414     {TODO: SQLExecProcedure, }
2415     SQLUpdate, SQLDelete])) then
2416     result := ''
2417     else begin
2418     info_request := Char(isc_info_sql_get_plan);
2419     Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2420     SizeOf(result_buffer), result_buffer), True);
2421     if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2422     IBError(ibxeUnknownError, [nil]);
2423     result_length := isc_vax_integer(@result_buffer[1], 2);
2424     SetString(result, nil, result_length);
2425     for i := 1 to result_length do
2426     result[i] := result_buffer[i + 2];
2427     result := Trim(result);
2428     end;
2429     end;
2430    
2431     function TIBSQL.GetRecordCount: Integer;
2432     begin
2433     result := FRecordCount;
2434     end;
2435    
2436     function TIBSQL.GetRowsAffected: Integer;
2437     var
2438     result_buffer: array[0..1048] of Char;
2439     info_request: Char;
2440     begin
2441     if not Prepared then
2442     result := -1
2443     else begin
2444     info_request := Char(isc_info_sql_records);
2445     if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
2446     SizeOf(result_buffer), result_buffer) > 0 then
2447     IBDatabaseError;
2448     if (result_buffer[0] <> Char(isc_info_sql_records)) then
2449     result := -1
2450     else
2451     case SQLType of
2452     SQLUpdate: Result := isc_vax_integer(@result_buffer[6], 4);
2453     SQLDelete: Result := isc_vax_integer(@result_buffer[13], 4);
2454     SQLInsert: Result := isc_vax_integer(@result_buffer[27], 4);
2455     else Result := -1 ;
2456     end ;
2457     end;
2458     end;
2459    
2460     function TIBSQL.GetSQLParams: TIBXSQLDA;
2461     begin
2462     if not Prepared then
2463     Prepare;
2464     result := FSQLParams;
2465     end;
2466    
2467     function TIBSQL.GetTransaction: TIBTransaction;
2468     begin
2469     result := FBase.Transaction;
2470     end;
2471    
2472     function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2473     begin
2474     result := FBase.TRHandle;
2475     end;
2476    
2477     {
2478     Preprocess SQL
2479     Using FSQL, process the typed SQL and put the process SQL
2480     in FProcessedSQL and parameter names in FSQLParams
2481     }
2482     procedure TIBSQL.PreprocessSQL;
2483     var
2484     cCurChar, cNextChar, cQuoteChar: Char;
2485     sSQL, sProcessedSQL, sParamName: String;
2486     i, iLenSQL, iSQLPos: Integer;
2487     iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
2488     iParamSuffix: Integer;
2489     slNames: TStrings;
2490    
2491     const
2492     DefaultState = 0;
2493     CommentState = 1;
2494     QuoteState = 2;
2495     ParamState = 3;
2496     {$ifdef ALLOWDIALECT3PARAMNAMES}
2497     ParamDefaultState = 0;
2498     ParamQuoteState = 1;
2499     {$endif}
2500    
2501     procedure AddToProcessedSQL(cChar: Char);
2502     begin
2503     sProcessedSQL[iSQLPos] := cChar;
2504     Inc(iSQLPos);
2505     end;
2506    
2507     begin
2508     slNames := TStringList.Create;
2509     try
2510     { Do some initializations of variables }
2511     iParamSuffix := 0;
2512     cQuoteChar := '''';
2513     sSQL := FSQL.Text;
2514     iLenSQL := Length(sSQL);
2515     SetString(sProcessedSQL, nil, iLenSQL + 1);
2516     i := 1;
2517     iSQLPos := 1;
2518     iCurState := DefaultState;
2519     {$ifdef ALLOWDIALECT3PARAMNAMES}
2520     iCurParamState := ParamDefaultState;
2521     {$endif}
2522     { Now, traverse through the SQL string, character by character,
2523     picking out the parameters and formatting correctly for InterBase }
2524     while (i <= iLenSQL) do begin
2525     { Get the current token and a look-ahead }
2526     cCurChar := sSQL[i];
2527     if i = iLenSQL then
2528     cNextChar := #0
2529     else
2530     cNextChar := sSQL[i + 1];
2531     { Now act based on the current state }
2532     case iCurState of
2533     DefaultState: begin
2534     case cCurChar of
2535     '''', '"': begin
2536     cQuoteChar := cCurChar;
2537     iCurState := QuoteState;
2538     end;
2539     '?', ':': begin
2540     iCurState := ParamState;
2541     AddToProcessedSQL('?');
2542     end;
2543     '/': if (cNextChar = '*') then begin
2544     AddToProcessedSQL(cCurChar);
2545     Inc(i);
2546     iCurState := CommentState;
2547     end;
2548     end;
2549     end;
2550     CommentState: begin
2551     if (cNextChar = #0) then
2552     IBError(ibxeSQLParseError, [SEOFInComment])
2553     else if (cCurChar = '*') then begin
2554     if (cNextChar = '/') then
2555     iCurState := DefaultState;
2556     end;
2557     end;
2558     QuoteState: begin
2559     if cNextChar = #0 then
2560     IBError(ibxeSQLParseError, [SEOFInString])
2561     else if (cCurChar = cQuoteChar) then begin
2562     if (cNextChar = cQuoteChar) then begin
2563     AddToProcessedSQL(cCurChar);
2564     Inc(i);
2565     end else
2566     iCurState := DefaultState;
2567     end;
2568     end;
2569     ParamState:
2570     begin
2571     { collect the name of the parameter }
2572     {$ifdef ALLOWDIALECT3PARAMNAMES}
2573     if iCurParamState = ParamDefaultState then
2574     begin
2575     if cCurChar = '"' then
2576     iCurParamState := ParamQuoteState
2577     else
2578     {$endif}
2579     if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2580     sParamName := sParamName + cCurChar
2581     else if FGenerateParamNames then
2582     begin
2583     sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2584     Inc(iParamSuffix);
2585     iCurState := DefaultState;
2586     slNames.AddObject(sParamName,self); //Note local convention
2587     //add pointer to self to mark entry
2588     sParamName := '';
2589     end
2590     else
2591     IBError(ibxeSQLParseError, [SParamNameExpected]);
2592     {$ifdef ALLOWDIALECT3PARAMNAMES}
2593     end
2594     else begin
2595     { determine if Quoted parameter name is finished }
2596     if cCurChar = '"' then
2597     begin
2598     Inc(i);
2599     slNames.Add(sParamName);
2600     SParamName := '';
2601     iCurParamState := ParamDefaultState;
2602     iCurState := DefaultState;
2603     end
2604     else
2605     sParamName := sParamName + cCurChar
2606     end;
2607     {$endif}
2608     { determine if the unquoted parameter name is finished }
2609     if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
2610     (iCurState <> DefaultState) then
2611     begin
2612     if not (cNextChar in ['A'..'Z', 'a'..'z',
2613     '0'..'9', '_', '$']) then begin
2614     Inc(i);
2615     iCurState := DefaultState;
2616     slNames.Add(sParamName);
2617     sParamName := '';
2618     end;
2619     end;
2620     end;
2621     end;
2622     if iCurState <> ParamState then
2623     AddToProcessedSQL(sSQL[i]);
2624     Inc(i);
2625     end;
2626     AddToProcessedSQL(#0);
2627     FSQLParams.Count := slNames.Count;
2628     for i := 0 to slNames.Count - 1 do
2629     FSQLParams.SetParamName(slNames[i], i,FUniqueParamNames or (slNames.Objects[i] <> nil));
2630     FProcessedSQL.Text := sProcessedSQL;
2631     finally
2632     slNames.Free;
2633     end;
2634     end;
2635    
2636     procedure TIBSQL.SetDatabase(Value: TIBDatabase);
2637     begin
2638     FBase.Database := Value;
2639     end;
2640    
2641     procedure TIBSQL.Prepare;
2642     var
2643     stmt_len: Integer;
2644     res_buffer: array[0..7] of Char;
2645     type_item: Char;
2646     begin
2647     CheckClosed;
2648     FBase.CheckDatabase;
2649     FBase.CheckTransaction;
2650     if FPrepared then
2651     exit;
2652     if (FSQL.Text = '') then
2653     IBError(ibxeEmptyQuery, [nil]);
2654     if not ParamCheck then
2655     FProcessedSQL.Text := FSQL.Text
2656     else
2657     PreprocessSQL;
2658     if (FProcessedSQL.Text = '') then
2659     IBError(ibxeEmptyQuery, [nil]);
2660     try
2661     Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
2662     @FHandle), True);
2663     Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2664     PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
2665     { After preparing the statement, query the stmt type and possibly
2666     create a FSQLRecord "holder" }
2667     { Get the type of the statement }
2668     type_item := Char(isc_info_sql_stmt_type);
2669     Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2670     SizeOf(res_buffer), res_buffer), True);
2671     if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2672     IBError(ibxeUnknownError, [nil]);
2673     stmt_len := isc_vax_integer(@res_buffer[1], 2);
2674     FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2675     { Done getting the type }
2676     case FSQLType of
2677     SQLGetSegment,
2678     SQLPutSegment,
2679     SQLStartTransaction: begin
2680     FreeHandle;
2681     IBError(ibxeNotPermitted, [nil]);
2682     end;
2683     SQLCommit,
2684     SQLRollback,
2685     SQLDDL, SQLSetGenerator,
2686     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2687     SQLExecProcedure: begin
2688     { We already know how many inputs there are, so... }
2689     if (FSQLParams.FXSQLDA <> nil) and
2690     (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2691     FSQLParams.FXSQLDA), False) > 0) then
2692     IBDataBaseError;
2693     FSQLParams.Initialize;
2694     if FSQLType in [SQLSelect, SQLSelectForUpdate,
2695     SQLExecProcedure] then begin
2696     { Allocate an initial output descriptor (with one column) }
2697     FSQLRecord.Count := 1;
2698     { Using isc_dsql_describe, get the right size for the columns... }
2699     Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2700     if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2701     FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2702     Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2703     end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2704     FSQLRecord.Count := 0;
2705     FSQLRecord.Initialize;
2706     end;
2707     end;
2708     end;
2709     FPrepared := True;
2710     if not (csDesigning in ComponentState) then
2711     MonitorHook.SQLPrepare(Self);
2712     except
2713     on E: Exception do begin
2714     if (FHandle <> nil) then
2715     FreeHandle;
2716 tony 21 if E is EIBInterBaseError then
2717     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
2718     EIBInterBaseError(E).IBErrorCode,
2719     EIBInterBaseError(E).Message +
2720     sSQLErrorSeparator + FProcessedSQL.Text)
2721     else
2722     raise;
2723 tony 19 end;
2724     end;
2725     end;
2726    
2727     function TIBSQL.GetUniqueRelationName: String;
2728     begin
2729     if FPrepared and (FSQLType = SQLSelect) then
2730     result := FSQLRecord.UniqueRelationName
2731     else
2732     result := '';
2733     end;
2734    
2735     procedure TIBSQL.SetSQL(Value: TStrings);
2736     begin
2737     if FSQL.Text <> Value.Text then
2738     begin
2739     FSQL.BeginUpdate;
2740     try
2741     FSQL.Assign(Value);
2742     finally
2743     FSQL.EndUpdate;
2744     end;
2745     end;
2746     end;
2747    
2748     procedure TIBSQL.SetTransaction(Value: TIBTransaction);
2749     begin
2750     FBase.Transaction := Value;
2751     end;
2752    
2753     procedure TIBSQL.SQLChanging(Sender: TObject);
2754     begin
2755     if Assigned(OnSQLChanging) then
2756     OnSQLChanging(Self);
2757     if FHandle <> nil then FreeHandle;
2758     end;
2759    
2760     procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
2761     begin
2762     if (FOpen) then
2763     Close;
2764     end;
2765    
2766     end.