ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 23
Committed: Fri Mar 13 10:26:52 2015 UTC (9 years, 1 month ago) by tony
Content type: text/x-pascal
File size: 78492 byte(s)
Log Message:
Committing updates for Release R1-2-1

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