ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 79414 byte(s)
Log Message:
Committing updates for Release R1-3-2

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 tony 31 SysUtils, Classes, IBHeader,
80 tony 19 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 tony 35 FOnSQLChanged: TNotifyEvent;
332 tony 19 FUniqueParamNames: Boolean;
333     function GetFieldCount: integer;
334     procedure SetUniqueParamNames(AValue: Boolean);
335     protected
336     FBase: TIBBase;
337     FBOF, { At BOF? }
338     FEOF, { At EOF? }
339     FGoToFirstRecordOnExecute, { Automatically position record on first record after executing }
340     FOpen, { Is a cursor open? }
341     FPrepared: Boolean; { Has the query been prepared? }
342     FRecordCount: Integer; { How many records have been read so far? }
343     FCursor: String; { Cursor name...}
344     FHandle: TISC_STMT_HANDLE; { Once prepared, this accesses the SQL Query }
345     FOnSQLChanging: TNotifyEvent; { Call this when the SQL is changing }
346     FSQL: TStrings; { SQL Query (by user) }
347     FParamCheck: Boolean; { Check for parameters? (just like TQuery) }
348     FProcessedSQL: TStrings; { SQL Query (pre-processed for param labels) }
349     FSQLParams, { Any parameters to the query }
350     FSQLRecord: TIBXSQLDA; { The current record }
351     FSQLType: TIBSQLTypes; { Select, update, delete, insert, create, alter, etc...}
352     FGenerateParamNames: Boolean; { Auto generate param names ?}
353     procedure DoBeforeDatabaseDisconnect(Sender: TObject);
354     function GetDatabase: TIBDatabase;
355     function GetDBHandle: PISC_DB_HANDLE;
356     function GetEOF: Boolean;
357     function GetFields(const Idx: Integer): TIBXSQLVAR;
358     function GetFieldIndex(FieldName: String): Integer;
359     function GetPlan: String;
360     function GetRecordCount: Integer;
361     function GetRowsAffected: Integer;
362     function GetSQLParams: TIBXSQLDA;
363     function GetTransaction: TIBTransaction;
364     function GetTRHandle: PISC_TR_HANDLE;
365     procedure PreprocessSQL;
366     procedure SetDatabase(Value: TIBDatabase);
367     procedure SetSQL(Value: TStrings);
368     procedure SetTransaction(Value: TIBTransaction);
369     procedure SQLChanging(Sender: TObject);
370 tony 35 procedure SQLChanged(Sender: TObject);
371 tony 27 procedure BeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
372 tony 19 public
373     constructor Create(AOwner: TComponent); override;
374     destructor Destroy; override;
375     procedure BatchInput(InputObject: TIBBatchInput);
376     procedure BatchOutput(OutputObject: TIBBatchOutput);
377     function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
378     procedure CheckClosed; { raise error if query is not closed. }
379     procedure CheckOpen; { raise error if query is not open.}
380     procedure CheckValidStatement; { raise error if statement is invalid.}
381     procedure Close;
382     function Current: TIBXSQLDA;
383     procedure ExecQuery;
384     function FieldByName(FieldName: String): TIBXSQLVAR;
385     function ParamByName(ParamName: String): TIBXSQLVAR;
386     procedure FreeHandle;
387     function Next: TIBXSQLDA;
388     procedure Prepare;
389     function GetUniqueRelationName: String;
390     property Bof: Boolean read FBOF;
391     property DBHandle: PISC_DB_HANDLE read GetDBHandle;
392     property Eof: Boolean read GetEOF;
393     property Fields[const Idx: Integer]: TIBXSQLVAR read GetFields;
394     property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
395     property FieldCount: integer read GetFieldCount;
396     property Open: Boolean read FOpen;
397     property Params: TIBXSQLDA read GetSQLParams;
398     property Plan: String read GetPlan;
399     property Prepared: Boolean read FPrepared;
400     property RecordCount: Integer read GetRecordCount;
401     property RowsAffected: Integer read GetRowsAffected;
402     property SQLType: TIBSQLTypes read FSQLType;
403     property TRHandle: PISC_TR_HANDLE read GetTRHandle;
404     property Handle: TISC_STMT_HANDLE read FHandle;
405     property UniqueRelationName: String read GetUniqueRelationName;
406     published
407     property Database: TIBDatabase read GetDatabase write SetDatabase;
408     property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
409     property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
410     property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
411     write FGoToFirstRecordOnExecute
412     default True;
413     property ParamCheck: Boolean read FParamCheck write FParamCheck;
414     property SQL: TStrings read FSQL write SetSQL;
415     property Transaction: TIBTransaction read GetTransaction write SetTransaction;
416     property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
417 tony 35 property OnSQLChanged: TNotifyEvent read FOnSQLChanged write FOnSQLChanged;
418 tony 19 end;
419    
420     implementation
421    
422     uses
423     IBIntf, IBBlob, Variants , IBSQLMonitor;
424    
425     { TIBXSQLVAR }
426     constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
427     begin
428     inherited Create;
429     FParent := Parent;
430     FSQL := Query;
431     end;
432    
433     procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
434     var
435     szBuff: PChar;
436     s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
437     bSourceBlob, bDestBlob: Boolean;
438     iSegs: Int64;
439     iMaxSeg: Int64;
440     iSize: Int64;
441     iBlobType: Short;
442     begin
443     szBuff := nil;
444     bSourceBlob := True;
445     bDestBlob := True;
446     s_bhandle := nil;
447     d_bhandle := nil;
448     try
449     if (Source.IsNull) then
450     begin
451     IsNull := True;
452     exit;
453     end
454     else
455     if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
456     (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
457     exit; { arrays not supported }
458     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
459     (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
460     begin
461     AsXSQLVAR := Source.AsXSQLVAR;
462     exit;
463     end
464     else
465     if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
466     begin
467     szBuff := nil;
468     IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
469     Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
470     bSourceBlob := False;
471     iSize := Source.FXSQLVAR^.sqllen;
472     end
473     else
474     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
475     bDestBlob := False;
476    
477     if bSourceBlob then
478     begin
479     { read the blob }
480     Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
481     Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
482     0, nil), True);
483     try
484     IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
485     iBlobType);
486     szBuff := nil;
487     IBAlloc(szBuff, 0, iSize);
488     IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
489     finally
490     Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
491     end;
492     end;
493    
494     if bDestBlob then
495     begin
496     { write the blob }
497     FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
498     FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
499     0, nil), True);
500     try
501     IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
502     isNull := false
503     finally
504     FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
505     end;
506     end
507     else
508     begin
509     { just copy the buffer }
510     FXSQLVAR.sqltype := SQL_TEXT;
511     FXSQLVAR.sqllen := iSize;
512     IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
513     Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
514     end;
515     finally
516     FreeMem(szBuff);
517     end;
518     end;
519    
520     function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
521     var
522     Scaling : Int64;
523     i: Integer;
524     Val: Double;
525     begin
526     Scaling := 1; Val := Value;
527     if Scale > 0 then
528     begin
529     for i := 1 to Scale do
530     Scaling := Scaling * 10;
531     result := Val * Scaling;
532     end
533     else
534     if Scale < 0 then
535     begin
536     for i := -1 downto Scale do
537     Scaling := Scaling * 10;
538     result := Val / Scaling;
539     end
540     else
541     result := Val;
542     end;
543    
544     function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
545     var
546     Scaling : Int64;
547     i: Integer;
548     Val: Int64;
549     begin
550     Scaling := 1; Val := Value;
551     if Scale > 0 then begin
552     for i := 1 to Scale do Scaling := Scaling * 10;
553     result := Val * Scaling;
554     end else if Scale < 0 then begin
555     for i := -1 downto Scale do Scaling := Scaling * 10;
556     result := Val div Scaling;
557     end else
558     result := Val;
559     end;
560    
561     function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
562     var
563     Scaling : Int64;
564     i : Integer;
565     FractionText, PadText, CurrText: string;
566     begin
567     Result := 0;
568     Scaling := 1;
569     if Scale > 0 then
570     begin
571     for i := 1 to Scale do
572     Scaling := Scaling * 10;
573     result := Value * Scaling;
574     end
575     else
576     if Scale < 0 then
577     begin
578     for i := -1 downto Scale do
579     Scaling := Scaling * 10;
580     FractionText := IntToStr(abs(Value mod Scaling));
581     for i := Length(FractionText) to -Scale -1 do
582     PadText := '0' + PadText;
583     if Value < 0 then
584     CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
585     else
586     CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
587     try
588     result := StrToCurr(CurrText);
589     except
590     on E: Exception do
591     IBError(ibxeInvalidDataConversion, [nil]);
592     end;
593     end
594     else
595     result := Value;
596     end;
597    
598 tony 23 function TIBXSQLVAR.GetAsBoolean: boolean;
599     begin
600     result := false;
601     if not IsNull then
602     begin
603     if FXSQLVAR^.sqltype and (not 1) = SQL_BOOLEAN then
604     result := PByte(FXSQLVAR^.sqldata)^ = ISC_TRUE
605     else
606     IBError(ibxeInvalidDataConversion, [nil]);
607     end
608     end;
609    
610 tony 19 function TIBXSQLVAR.GetAsCurrency: Currency;
611     begin
612     result := 0;
613     if FSQL.Database.SQLDialect < 3 then
614     result := GetAsDouble
615     else begin
616     if not IsNull then
617     case FXSQLVAR^.sqltype and (not 1) of
618     SQL_TEXT, SQL_VARYING: begin
619     try
620     result := StrtoCurr(AsString);
621     except
622     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
623     end;
624     end;
625     SQL_SHORT:
626     result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
627     FXSQLVAR^.sqlscale);
628     SQL_LONG:
629     result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
630     FXSQLVAR^.sqlscale);
631     SQL_INT64:
632     result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
633     FXSQLVAR^.sqlscale);
634     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
635     result := Trunc(AsDouble);
636     else
637     IBError(ibxeInvalidDataConversion, [nil]);
638     end;
639     end;
640     end;
641    
642     function TIBXSQLVAR.GetAsInt64: Int64;
643     begin
644     result := 0;
645     if not IsNull then
646     case FXSQLVAR^.sqltype and (not 1) of
647     SQL_TEXT, SQL_VARYING: begin
648     try
649     result := StrToInt64(AsString);
650     except
651     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
652     end;
653     end;
654     SQL_SHORT:
655     result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
656     FXSQLVAR^.sqlscale);
657     SQL_LONG:
658     result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
659     FXSQLVAR^.sqlscale);
660     SQL_INT64:
661     result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
662     FXSQLVAR^.sqlscale);
663     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
664     result := Trunc(AsDouble);
665     else
666     IBError(ibxeInvalidDataConversion, [nil]);
667     end;
668     end;
669    
670     function TIBXSQLVAR.GetAsDateTime: TDateTime;
671     var
672     tm_date: TCTimeStructure;
673     msecs: word;
674     begin
675     result := 0;
676     if not IsNull then
677     case FXSQLVAR^.sqltype and (not 1) of
678     SQL_TEXT, SQL_VARYING: begin
679     try
680     result := StrToDate(AsString);
681     except
682     on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
683     end;
684     end;
685     SQL_TYPE_DATE: begin
686     isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
687     try
688     result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
689     Word(tm_date.tm_mday));
690     except
691     on E: EConvertError do begin
692     IBError(ibxeInvalidDataConversion, [nil]);
693     end;
694     end;
695     end;
696     SQL_TYPE_TIME: begin
697     isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
698     try
699     msecs := (PISC_TIME(FXSQLVAR^.sqldata)^ mod 10000) div 10;
700     result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
701     Word(tm_date.tm_sec), msecs)
702     except
703     on E: EConvertError do begin
704     IBError(ibxeInvalidDataConversion, [nil]);
705     end;
706     end;
707     end;
708     SQL_TIMESTAMP: begin
709     isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
710     try
711     result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
712     Word(tm_date.tm_mday));
713     msecs := (PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time mod 10000) div 10;
714     if result >= 0 then
715     result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
716     Word(tm_date.tm_sec), msecs)
717     else
718     result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
719     Word(tm_date.tm_sec), msecs)
720     except
721     on E: EConvertError do begin
722     IBError(ibxeInvalidDataConversion, [nil]);
723     end;
724     end;
725     end;
726     else
727     IBError(ibxeInvalidDataConversion, [nil]);
728     end;
729     end;
730    
731     function TIBXSQLVAR.GetAsDouble: Double;
732     begin
733     result := 0;
734     if not IsNull then begin
735     case FXSQLVAR^.sqltype and (not 1) of
736     SQL_TEXT, SQL_VARYING: begin
737     try
738     result := StrToFloat(AsString);
739     except
740     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
741     end;
742     end;
743     SQL_SHORT:
744     result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
745     FXSQLVAR^.sqlscale);
746     SQL_LONG:
747     result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
748     FXSQLVAR^.sqlscale);
749     SQL_INT64:
750     result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
751     SQL_FLOAT:
752     result := PFloat(FXSQLVAR^.sqldata)^;
753     SQL_DOUBLE, SQL_D_FLOAT:
754     result := PDouble(FXSQLVAR^.sqldata)^;
755     else
756     IBError(ibxeInvalidDataConversion, [nil]);
757     end;
758     if FXSQLVAR^.sqlscale <> 0 then
759     result :=
760     StrToFloat(FloatToStrF(result, fffixed, 15,
761     Abs(FXSQLVAR^.sqlscale) ));
762     end;
763     end;
764    
765     function TIBXSQLVAR.GetAsFloat: Float;
766     begin
767     result := 0;
768     try
769     result := AsDouble;
770     except
771     on E: EOverflow do
772     IBError(ibxeInvalidDataConversion, [nil]);
773     end;
774     end;
775    
776     function TIBXSQLVAR.GetAsLong: Long;
777     begin
778     result := 0;
779     if not IsNull then
780     case FXSQLVAR^.sqltype and (not 1) of
781     SQL_TEXT, SQL_VARYING: begin
782     try
783     result := StrToInt(AsString);
784     except
785     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
786     end;
787     end;
788     SQL_SHORT:
789     result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
790     FXSQLVAR^.sqlscale));
791     SQL_LONG:
792     result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
793     FXSQLVAR^.sqlscale));
794     SQL_INT64:
795     result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
796     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
797     result := Trunc(AsDouble);
798     else
799     IBError(ibxeInvalidDataConversion, [nil]);
800     end;
801     end;
802    
803     function TIBXSQLVAR.GetAsPointer: Pointer;
804     begin
805     if not IsNull then
806     result := FXSQLVAR^.sqldata
807     else
808     result := nil;
809     end;
810    
811     function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
812     begin
813     result.gds_quad_high := 0;
814     result.gds_quad_low := 0;
815     if not IsNull then
816     case FXSQLVAR^.sqltype and (not 1) of
817     SQL_BLOB, SQL_ARRAY, SQL_QUAD:
818     result := PISC_QUAD(FXSQLVAR^.sqldata)^;
819     else
820     IBError(ibxeInvalidDataConversion, [nil]);
821     end;
822     end;
823    
824     function TIBXSQLVAR.GetAsShort: Short;
825     begin
826     result := 0;
827     try
828     result := AsLong;
829     except
830     on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
831     end;
832     end;
833    
834    
835     function TIBXSQLVAR.GetAsString: String;
836     var
837     sz: PChar;
838     str_len: Integer;
839     ss: TStringStream;
840     begin
841     result := '';
842     { Check null, if so return a default string }
843     if not IsNull then
844     case FXSQLVar^.sqltype and (not 1) of
845     SQL_ARRAY:
846     result := '(Array)'; {do not localize}
847     SQL_BLOB: begin
848     ss := TStringStream.Create('');
849     try
850     SaveToStream(ss);
851     result := ss.DataString;
852     finally
853     ss.Free;
854     end;
855     end;
856     SQL_TEXT, SQL_VARYING: begin
857     sz := FXSQLVAR^.sqldata;
858     if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
859     str_len := FXSQLVar^.sqllen
860     else begin
861     str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
862     Inc(sz, 2);
863     end;
864     SetString(result, sz, str_len);
865     if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
866     result := TrimRight(result);
867     end;
868     SQL_TYPE_DATE:
869     case FSQL.Database.SQLDialect of
870     1 : result := DateTimeToStr(AsDateTime);
871     3 : result := DateToStr(AsDateTime);
872     end;
873     SQL_TYPE_TIME :
874     result := TimeToStr(AsDateTime);
875     SQL_TIMESTAMP:
876     result := DateTimeToStr(AsDateTime);
877     SQL_SHORT, SQL_LONG:
878     if FXSQLVAR^.sqlscale = 0 then
879     result := IntToStr(AsLong)
880     else if FXSQLVAR^.sqlscale >= (-4) then
881     result := CurrToStr(AsCurrency)
882     else
883     result := FloatToStr(AsDouble);
884     SQL_INT64:
885     if FXSQLVAR^.sqlscale = 0 then
886     result := IntToStr(AsInt64)
887     else if FXSQLVAR^.sqlscale >= (-4) then
888     result := CurrToStr(AsCurrency)
889     else
890     result := FloatToStr(AsDouble);
891     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
892     result := FloatToStr(AsDouble);
893     else
894     IBError(ibxeInvalidDataConversion, [nil]);
895     end;
896     end;
897    
898     function TIBXSQLVAR.GetAsVariant: Variant;
899     begin
900     if IsNull then
901     result := NULL
902     { Check null, if so return a default string }
903     else case FXSQLVar^.sqltype and (not 1) of
904     SQL_ARRAY:
905     result := '(Array)'; {do not localize}
906     SQL_BLOB:
907     result := '(Blob)'; {do not localize}
908     SQL_TEXT, SQL_VARYING:
909     result := AsString;
910     SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
911     result := AsDateTime;
912     SQL_SHORT, SQL_LONG:
913     if FXSQLVAR^.sqlscale = 0 then
914     result := AsLong
915     else if FXSQLVAR^.sqlscale >= (-4) then
916     result := AsCurrency
917     else
918     result := AsDouble;
919     SQL_INT64:
920     if FXSQLVAR^.sqlscale = 0 then
921 tony 21 result := AsInt64
922 tony 19 else if FXSQLVAR^.sqlscale >= (-4) then
923     result := AsCurrency
924     else
925     result := AsDouble;
926     SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
927     result := AsDouble;
928 tony 23 SQL_BOOLEAN:
929     result := AsBoolean;
930 tony 19 else
931     IBError(ibxeInvalidDataConversion, [nil]);
932     end;
933     end;
934    
935     function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
936     begin
937     result := FXSQLVAR;
938     end;
939    
940     function TIBXSQLVAR.GetIsNull: Boolean;
941     begin
942     result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
943     end;
944    
945     function TIBXSQLVAR.GetIsNullable: Boolean;
946     begin
947     result := (FXSQLVAR^.sqltype and 1 = 1);
948     end;
949    
950     procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
951     var
952     fs: TFileStream;
953     begin
954     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
955     try
956     LoadFromStream(fs);
957     finally
958     fs.Free;
959     end;
960     end;
961    
962     procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
963     var
964     bs: TIBBlobStream;
965     begin
966     bs := TIBBlobStream.Create;
967     try
968     bs.Mode := bmWrite;
969     bs.Database := FSQL.Database;
970     bs.Transaction := FSQL.Transaction;
971     Stream.Seek(0, soFromBeginning);
972     bs.LoadFromStream(Stream);
973     bs.Finalize;
974     AsQuad := bs.BlobID;
975     finally
976     bs.Free;
977     end;
978     end;
979    
980     procedure TIBXSQLVAR.SaveToFile(const FileName: String);
981     var
982     fs: TFileStream;
983     begin
984     fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
985     try
986     SaveToStream(fs);
987     finally
988     fs.Free;
989     end;
990     end;
991    
992     procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
993     var
994     bs: TIBBlobStream;
995     begin
996     bs := TIBBlobStream.Create;
997     try
998     bs.Mode := bmRead;
999     bs.Database := FSQL.Database;
1000     bs.Transaction := FSQL.Transaction;
1001     bs.BlobID := AsQuad;
1002     bs.SaveToStream(Stream);
1003     finally
1004     bs.Free;
1005     end;
1006     end;
1007    
1008     function TIBXSQLVAR.GetSize: Integer;
1009     begin
1010     result := FXSQLVAR^.sqllen;
1011     end;
1012    
1013     function TIBXSQLVAR.GetSQLType: Integer;
1014     begin
1015     result := FXSQLVAR^.sqltype and (not 1);
1016     end;
1017    
1018 tony 23 procedure TIBXSQLVAR.SetAsBoolean(AValue: boolean);
1019     var
1020     i: Integer;
1021     begin
1022     if FUniqueName then
1023     xSetAsBoolean(AValue)
1024     else
1025     for i := 0 to FParent.FCount - 1 do
1026     if FParent[i].FName = FName then
1027     FParent[i].xSetAsBoolean(AValue);
1028     end;
1029    
1030 tony 19 procedure TIBXSQLVAR.xSetAsCurrency(Value: Currency);
1031     begin
1032     if IsNullable then
1033     IsNull := False;
1034     FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1035     FXSQLVAR^.sqlscale := -4;
1036     FXSQLVAR^.sqllen := SizeOf(Int64);
1037     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1038     PCurrency(FXSQLVAR^.sqldata)^ := Value;
1039     FModified := True;
1040     end;
1041    
1042     procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
1043     var
1044     i: Integer;
1045     begin
1046     if FSQL.Database.SQLDialect < 3 then
1047     AsDouble := Value
1048     else
1049     begin
1050    
1051     if FUniqueName then
1052     xSetAsCurrency(Value)
1053     else
1054     for i := 0 to FParent.FCount - 1 do
1055     if FParent[i].FName = FName then
1056     FParent[i].xSetAsCurrency(Value);
1057     end;
1058     end;
1059    
1060     procedure TIBXSQLVAR.xSetAsInt64(Value: Int64);
1061     begin
1062     if IsNullable then
1063     IsNull := False;
1064    
1065     FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1066     FXSQLVAR^.sqlscale := 0;
1067     FXSQLVAR^.sqllen := SizeOf(Int64);
1068     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1069     PInt64(FXSQLVAR^.sqldata)^ := Value;
1070     FModified := True;
1071     end;
1072    
1073     procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
1074     var
1075     i: Integer;
1076     begin
1077     if FUniqueName then
1078     xSetAsInt64(Value)
1079     else
1080     for i := 0 to FParent.FCount - 1 do
1081     if FParent[i].FName = FName then
1082     FParent[i].xSetAsInt64(Value);
1083     end;
1084    
1085     procedure TIBXSQLVAR.xSetAsDate(Value: TDateTime);
1086     var
1087     tm_date: TCTimeStructure;
1088     Yr, Mn, Dy: Word;
1089     begin
1090     if IsNullable then
1091     IsNull := False;
1092    
1093     FXSQLVAR^.sqltype := SQL_TYPE_DATE or (FXSQLVAR^.sqltype and 1);
1094     DecodeDate(Value, Yr, Mn, Dy);
1095     with tm_date do begin
1096     tm_sec := 0;
1097     tm_min := 0;
1098     tm_hour := 0;
1099     tm_mday := Dy;
1100     tm_mon := Mn - 1;
1101     tm_year := Yr - 1900;
1102     end;
1103     FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
1104     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1105     isc_encode_sql_date(@tm_date, PISC_DATE(FXSQLVAR^.sqldata));
1106     FModified := True;
1107     end;
1108    
1109     procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
1110     var
1111     i: Integer;
1112     begin
1113     if FSQL.Database.SQLDialect < 3 then
1114     begin
1115     AsDateTime := Value;
1116     exit;
1117     end;
1118    
1119     if FUniqueName then
1120     xSetAsDate(Value)
1121     else
1122     for i := 0 to FParent.FCount - 1 do
1123     if FParent[i].FName = FName then
1124     FParent[i].xSetAsDate(Value);
1125     end;
1126    
1127     procedure TIBXSQLVAR.xSetAsTime(Value: TDateTime);
1128     var
1129     tm_date: TCTimeStructure;
1130     Hr, Mt, S, Ms: Word;
1131     begin
1132     if IsNullable then
1133     IsNull := False;
1134    
1135     FXSQLVAR^.sqltype := SQL_TYPE_TIME or (FXSQLVAR^.sqltype and 1);
1136     DecodeTime(Value, Hr, Mt, S, Ms);
1137     with tm_date do begin
1138     tm_sec := S;
1139     tm_min := Mt;
1140     tm_hour := Hr;
1141     tm_mday := 0;
1142     tm_mon := 0;
1143     tm_year := 0;
1144     end;
1145     FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1146     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1147     isc_encode_sql_time(@tm_date, PISC_TIME(FXSQLVAR^.sqldata));
1148     if Ms > 0 then
1149     Inc(PISC_TIME(FXSQLVAR^.sqldata)^,Ms*10);
1150     FModified := True;
1151     end;
1152    
1153     procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1154     var
1155     i: Integer;
1156     begin
1157     if FSQL.Database.SQLDialect < 3 then
1158     begin
1159     AsDateTime := Value;
1160     exit;
1161     end;
1162    
1163     if FUniqueName then
1164     xSetAsTime(Value)
1165     else
1166     for i := 0 to FParent.FCount - 1 do
1167     if FParent[i].FName = FName then
1168     FParent[i].xSetAsTime(Value);
1169     end;
1170    
1171     procedure TIBXSQLVAR.xSetAsDateTime(Value: TDateTime);
1172     var
1173     tm_date: TCTimeStructure;
1174     Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1175     begin
1176     if IsNullable then
1177     IsNull := False;
1178    
1179     FXSQLVAR^.sqltype := SQL_TIMESTAMP or (FXSQLVAR^.sqltype and 1);
1180     DecodeDate(Value, Yr, Mn, Dy);
1181     DecodeTime(Value, Hr, Mt, S, Ms);
1182     with tm_date do begin
1183     tm_sec := S;
1184     tm_min := Mt;
1185     tm_hour := Hr;
1186     tm_mday := Dy;
1187     tm_mon := Mn - 1;
1188     tm_year := Yr - 1900;
1189     end;
1190     FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1191     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1192     isc_encode_date(@tm_date, PISC_QUAD(FXSQLVAR^.sqldata));
1193     if Ms > 0 then
1194     Inc(PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time,Ms*10);
1195     FModified := True;
1196     end;
1197    
1198     procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1199     var
1200     i: Integer;
1201     begin
1202     if FUniqueName then
1203     xSetAsDateTime(value)
1204     else
1205     for i := 0 to FParent.FCount - 1 do
1206     if FParent[i].FName = FName then
1207     FParent[i].xSetAsDateTime(Value);
1208     end;
1209    
1210     procedure TIBXSQLVAR.xSetAsDouble(Value: Double);
1211     begin
1212     if IsNullable then
1213     IsNull := False;
1214    
1215     FXSQLVAR^.sqltype := SQL_DOUBLE or (FXSQLVAR^.sqltype and 1);
1216     FXSQLVAR^.sqllen := SizeOf(Double);
1217     FXSQLVAR^.sqlscale := 0;
1218     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1219     PDouble(FXSQLVAR^.sqldata)^ := Value;
1220     FModified := True;
1221     end;
1222    
1223     procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1224     var
1225     i: Integer;
1226     begin
1227     if FUniqueName then
1228     xSetAsDouble(Value)
1229     else
1230     for i := 0 to FParent.FCount - 1 do
1231     if FParent[i].FName = FName then
1232     FParent[i].xSetAsDouble(Value);
1233     end;
1234    
1235     procedure TIBXSQLVAR.xSetAsFloat(Value: Float);
1236     begin
1237     if IsNullable then
1238     IsNull := False;
1239    
1240     FXSQLVAR^.sqltype := SQL_FLOAT or (FXSQLVAR^.sqltype and 1);
1241     FXSQLVAR^.sqllen := SizeOf(Float);
1242     FXSQLVAR^.sqlscale := 0;
1243     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1244     PSingle(FXSQLVAR^.sqldata)^ := Value;
1245     FModified := True;
1246     end;
1247    
1248     procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1249     var
1250     i: Integer;
1251     begin
1252     if FUniqueName then
1253     xSetAsFloat(Value)
1254     else
1255     for i := 0 to FParent.FCount - 1 do
1256     if FParent[i].FName = FName then
1257     FParent[i].xSetAsFloat(Value);
1258     end;
1259    
1260     procedure TIBXSQLVAR.xSetAsLong(Value: Long);
1261     begin
1262     if IsNullable then
1263     IsNull := False;
1264    
1265     FXSQLVAR^.sqltype := SQL_LONG or (FXSQLVAR^.sqltype and 1);
1266     FXSQLVAR^.sqllen := SizeOf(Long);
1267     FXSQLVAR^.sqlscale := 0;
1268     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1269     PLong(FXSQLVAR^.sqldata)^ := Value;
1270     FModified := True;
1271     end;
1272    
1273     procedure TIBXSQLVAR.SetAsLong(Value: Long);
1274     var
1275     i: Integer;
1276     begin
1277     if FUniqueName then
1278     xSetAsLong(Value)
1279     else
1280     for i := 0 to FParent.FCount - 1 do
1281     if FParent[i].FName = FName then
1282     FParent[i].xSetAsLong(Value);
1283     end;
1284    
1285     procedure TIBXSQLVAR.xSetAsPointer(Value: Pointer);
1286     begin
1287     if IsNullable and (Value = nil) then
1288     IsNull := True
1289     else begin
1290     IsNull := False;
1291     FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1292     Move(Value^, FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1293     end;
1294     FModified := True;
1295     end;
1296    
1297     procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1298     var
1299     i: Integer;
1300     begin
1301     if FUniqueName then
1302     xSetAsPointer(Value)
1303     else
1304     for i := 0 to FParent.FCount - 1 do
1305     if FParent[i].FName = FName then
1306     FParent[i].xSetAsPointer(Value);
1307     end;
1308    
1309     procedure TIBXSQLVAR.xSetAsQuad(Value: TISC_QUAD);
1310     begin
1311     if IsNullable then
1312     IsNull := False;
1313     if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1314     (FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1315     IBError(ibxeInvalidDataConversion, [nil]);
1316     FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1317     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1318     PISC_QUAD(FXSQLVAR^.sqldata)^ := Value;
1319     FModified := True;
1320     end;
1321    
1322     procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1323     var
1324     i: Integer;
1325     begin
1326     if FUniqueName then
1327     xSetAsQuad(Value)
1328     else
1329     for i := 0 to FParent.FCount - 1 do
1330     if FParent[i].FName = FName then
1331     FParent[i].xSetAsQuad(Value);
1332     end;
1333    
1334     procedure TIBXSQLVAR.xSetAsShort(Value: Short);
1335     begin
1336     if IsNullable then
1337     IsNull := False;
1338    
1339     FXSQLVAR^.sqltype := SQL_SHORT or (FXSQLVAR^.sqltype and 1);
1340     FXSQLVAR^.sqllen := SizeOf(Short);
1341     FXSQLVAR^.sqlscale := 0;
1342     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1343     PShort(FXSQLVAR^.sqldata)^ := Value;
1344     FModified := True;
1345     end;
1346    
1347     procedure TIBXSQLVAR.SetAsShort(Value: Short);
1348     var
1349     i: Integer;
1350     begin
1351     if FUniqueName then
1352     xSetAsShort(Value)
1353     else
1354     for i := 0 to FParent.FCount - 1 do
1355     if FParent[i].FName = FName then
1356     FParent[i].xSetAsShort(Value);
1357     end;
1358    
1359     procedure TIBXSQLVAR.xSetAsString(Value: String);
1360     var
1361     stype: Integer;
1362     ss: TStringStream;
1363    
1364     procedure SetStringValue;
1365     var
1366     i: Integer;
1367     begin
1368     if (FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1369     (FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1370     Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen)
1371     else begin
1372     FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1373     FXSQLVAR^.sqllen := Length(Value);
1374     IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen + 1);
1375     if (Length(Value) > 0) then
1376     Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1377     end;
1378     FModified := True;
1379     end;
1380    
1381     begin
1382     if IsNullable then
1383     IsNull := False;
1384    
1385     stype := FXSQLVAR^.sqltype and (not 1);
1386     if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1387     SetStringValue
1388     else begin
1389     if (stype = SQL_BLOB) then
1390     begin
1391     ss := TStringStream.Create(Value);
1392     try
1393     LoadFromStream(ss);
1394     finally
1395     ss.Free;
1396     end;
1397     end
1398     else if Value = '' then
1399     IsNull := True
1400     else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1401     (stype = SQL_TYPE_TIME) then
1402     xSetAsDateTime(StrToDateTime(Value))
1403     else
1404     SetStringValue;
1405     end;
1406     end;
1407    
1408     procedure TIBXSQLVAR.SetAsString(Value: String);
1409     var
1410     i: integer;
1411     begin
1412     if FUniqueName then
1413     xSetAsString(Value)
1414     else
1415     for i := 0 to FParent.FCount - 1 do
1416     if FParent[i].FName = FName then
1417     FParent[i].xSetAsString(Value);
1418     end;
1419    
1420     procedure TIBXSQLVAR.xSetAsVariant(Value: Variant);
1421     begin
1422     if VarIsNull(Value) then
1423     IsNull := True
1424     else case VarType(Value) of
1425     varEmpty, varNull:
1426     IsNull := True;
1427     varSmallint, varInteger, varByte,
1428     varWord, varShortInt:
1429     AsLong := Value;
1430     varInt64:
1431     AsInt64 := Value;
1432     varSingle, varDouble:
1433     AsDouble := Value;
1434     varCurrency:
1435     AsCurrency := Value;
1436     varBoolean:
1437 tony 25 AsBoolean := Value;
1438 tony 19 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 tony 35 TStringList(FSQL).OnChange := SQLChanged;
2160 tony 19 FProcessedSQL := TStringList.Create;
2161     FHandle := nil;
2162     FSQLParams := TIBXSQLDA.Create(self,daInput);
2163     FSQLRecord := TIBXSQLDA.Create(self,daOutput);
2164     FSQLType := SQLUnknown;
2165     FParamCheck := True;
2166 tony 31 FCursor := HexStr(self); //Name + RandomString(8);
2167 tony 19 if AOwner is TIBDatabase then
2168     Database := TIBDatabase(AOwner)
2169     else
2170     if AOwner is TIBTransaction then
2171     Transaction := TIBTransaction(AOwner);
2172     end;
2173    
2174     destructor TIBSQL.Destroy;
2175     begin
2176     if FIBLoaded then
2177     begin
2178     if (FOpen) then
2179     Close;
2180     if (FHandle <> nil) then
2181     FreeHandle;
2182     FSQL.Free;
2183     FProcessedSQL.Free;
2184     FBase.Free;
2185     FSQLParams.Free;
2186     FSQLRecord.Free;
2187     end;
2188     inherited Destroy;
2189     end;
2190    
2191     procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
2192     begin
2193     if not Prepared then
2194     Prepare;
2195     InputObject.FParams := Self.FSQLParams;
2196     InputObject.ReadyFile;
2197     if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
2198     while InputObject.ReadParameters do
2199     ExecQuery;
2200     end;
2201    
2202     procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
2203     begin
2204     CheckClosed;
2205     if not Prepared then
2206     Prepare;
2207     if FSQLType = SQLSelect then begin
2208     try
2209     ExecQuery;
2210     OutputObject.FColumns := Self.FSQLRecord;
2211     OutputObject.ReadyFile;
2212     if not FGoToFirstRecordOnExecute then
2213     Next;
2214     while (not Eof) and (OutputObject.WriteColumns) do
2215     Next;
2216     finally
2217     Close;
2218     end;
2219     end;
2220     end;
2221    
2222     procedure TIBSQL.CheckClosed;
2223     begin
2224     if FOpen then IBError(ibxeSQLOpen, [nil]);
2225     end;
2226    
2227     procedure TIBSQL.CheckOpen;
2228     begin
2229     if not FOpen then IBError(ibxeSQLClosed, [nil]);
2230     end;
2231    
2232     procedure TIBSQL.CheckValidStatement;
2233     begin
2234     FBase.CheckTransaction;
2235     if (FHandle = nil) then
2236     IBError(ibxeInvalidStatementHandle, [nil]);
2237     end;
2238    
2239     procedure TIBSQL.Close;
2240     var
2241     isc_res: ISC_STATUS;
2242     begin
2243     try
2244     if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then begin
2245     isc_res := Call(
2246     isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
2247     False);
2248     if (StatusVector^ = 1) and (isc_res > 0) and
2249     not CheckStatusVector(
2250     [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2251     IBDatabaseError;
2252     end;
2253     finally
2254     FEOF := False;
2255     FBOF := False;
2256     FOpen := False;
2257     FRecordCount := 0;
2258     end;
2259     end;
2260    
2261     function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
2262     begin
2263     result := 0;
2264     if Transaction <> nil then
2265     result := Transaction.Call(ErrCode, RaiseError)
2266     else
2267     if RaiseError and (ErrCode > 0) then
2268     IBDataBaseError;
2269     end;
2270    
2271     function TIBSQL.Current: TIBXSQLDA;
2272     begin
2273     result := FSQLRecord;
2274     end;
2275    
2276     function TIBSQL.GetFieldCount: integer;
2277     begin
2278     Result := FSQLRecord.Count
2279     end;
2280    
2281     procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
2282     begin
2283     if FUniqueParamNames = AValue then Exit;
2284     FreeHandle;
2285     FUniqueParamNames := AValue;
2286     end;
2287    
2288     procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
2289     begin
2290     if (FHandle <> nil) then begin
2291     Close;
2292     FreeHandle;
2293     end;
2294     end;
2295    
2296     procedure TIBSQL.ExecQuery;
2297     var
2298     fetch_res: ISC_STATUS;
2299     begin
2300     CheckClosed;
2301     if not Prepared then Prepare;
2302     CheckValidStatement;
2303     case FSQLType of
2304     SQLSelect: begin
2305     Call(isc_dsql_execute2(StatusVector,
2306     TRHandle,
2307     @FHandle,
2308     Database.SQLDialect,
2309     FSQLParams.AsXSQLDA,
2310     nil), True);
2311     Call(
2312     isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
2313     True);
2314     FOpen := True;
2315     FBOF := True;
2316     FEOF := False;
2317     FRecordCount := 0;
2318 tony 33 if not (csDesigning in ComponentState) then
2319     MonitorHook.SQLExecute(Self);
2320 tony 19 if FGoToFirstRecordOnExecute then
2321     Next;
2322     end;
2323     SQLExecProcedure: begin
2324     fetch_res := Call(isc_dsql_execute2(StatusVector,
2325     TRHandle,
2326     @FHandle,
2327     Database.SQLDialect,
2328     FSQLParams.AsXSQLDA,
2329     FSQLRecord.AsXSQLDA), True);
2330 tony 33 if not (csDesigning in ComponentState) then
2331     MonitorHook.SQLExecute(Self);
2332 tony 19 (* if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2333     begin
2334     { Sometimes a prepared stored procedure appears to get
2335     off sync on the server ....This code is meant to try
2336     to work around the problem simply by "retrying". This
2337     need to be reproduced and fixed.
2338     }
2339     isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2340     PChar(FProcessedSQL.Text), 1, nil);
2341     Call(isc_dsql_execute2(StatusVector,
2342     TRHandle,
2343     @FHandle,
2344     Database.SQLDialect,
2345     FSQLParams.AsXSQLDA,
2346     FSQLRecord.AsXSQLDA), True);
2347     end; *)
2348     end
2349     else
2350     Call(isc_dsql_execute(StatusVector,
2351     TRHandle,
2352     @FHandle,
2353     Database.SQLDialect,
2354 tony 27 FSQLParams.AsXSQLDA), True);
2355 tony 33 if not (csDesigning in ComponentState) then
2356     MonitorHook.SQLExecute(Self);
2357 tony 19 end;
2358 tony 27 FBase.DoAfterExecQuery(self);
2359     // writeln('Rows Affected = ',RowsAffected);
2360 tony 19 end;
2361    
2362     function TIBSQL.GetEOF: Boolean;
2363     begin
2364     result := FEOF or not FOpen;
2365     end;
2366    
2367     function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
2368     var
2369     i: Integer;
2370     begin
2371     i := GetFieldIndex(FieldName);
2372     if (i < 0) then
2373     IBError(ibxeFieldNotFound, [FieldName]);
2374     result := GetFields(i);
2375     end;
2376    
2377     function TIBSQL.ParamByName(ParamName: String): TIBXSQLVAR;
2378     begin
2379     Result := Params.ByName(ParamName);
2380     end;
2381    
2382     function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
2383     begin
2384     if (Idx < 0) or (Idx >= FSQLRecord.Count) then
2385     IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
2386     result := FSQLRecord[Idx];
2387     end;
2388    
2389     function TIBSQL.GetFieldIndex(FieldName: String): Integer;
2390     begin
2391     if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
2392     result := -1
2393     else
2394     result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
2395     end;
2396    
2397     function TIBSQL.Next: TIBXSQLDA;
2398     var
2399     fetch_res: ISC_STATUS;
2400     begin
2401     result := nil;
2402     if not FEOF then begin
2403     CheckOpen;
2404     { Go to the next record... }
2405     fetch_res :=
2406     Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
2407     if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then begin
2408     FEOF := True;
2409     end else if (fetch_res > 0) then begin
2410     try
2411     IBDataBaseError;
2412     except
2413     Close;
2414     raise;
2415     end;
2416     end else begin
2417     Inc(FRecordCount);
2418     FBOF := False;
2419     result := FSQLRecord;
2420     end;
2421     if not (csDesigning in ComponentState) then
2422     MonitorHook.SQLFetch(Self);
2423     end;
2424     end;
2425    
2426     procedure TIBSQL.FreeHandle;
2427     var
2428     isc_res: ISC_STATUS;
2429     begin
2430     try
2431     { The following two lines merely set the SQLDA count
2432     variable FCount to 0, but do not deallocate
2433     That way the allocations can be reused for
2434     a new query sring in the same SQL instance }
2435     FSQLRecord.Count := 0;
2436     FSQLParams.Count := 0;
2437     if FHandle <> nil then begin
2438     isc_res :=
2439     Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2440     if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2441     IBDataBaseError;
2442     end;
2443     finally
2444     FPrepared := False;
2445     FHandle := nil;
2446     end;
2447     end;
2448    
2449     function TIBSQL.GetDatabase: TIBDatabase;
2450     begin
2451     result := FBase.Database;
2452     end;
2453    
2454     function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2455     begin
2456     result := FBase.DBHandle;
2457     end;
2458    
2459     function TIBSQL.GetPlan: String;
2460     var
2461     result_buffer: array[0..16384] of Char;
2462     result_length, i: Integer;
2463     info_request: Char;
2464     begin
2465     if (not Prepared) or
2466     (not (FSQLType in [SQLSelect, SQLSelectForUpdate,
2467     {TODO: SQLExecProcedure, }
2468     SQLUpdate, SQLDelete])) then
2469     result := ''
2470     else begin
2471 tony 27 info_request := isc_info_sql_get_plan;
2472 tony 19 Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2473     SizeOf(result_buffer), result_buffer), True);
2474 tony 27 if (result_buffer[0] <> isc_info_sql_get_plan) then
2475 tony 19 IBError(ibxeUnknownError, [nil]);
2476     result_length := isc_vax_integer(@result_buffer[1], 2);
2477     SetString(result, nil, result_length);
2478     for i := 1 to result_length do
2479     result[i] := result_buffer[i + 2];
2480     result := Trim(result);
2481     end;
2482     end;
2483    
2484     function TIBSQL.GetRecordCount: Integer;
2485     begin
2486     result := FRecordCount;
2487     end;
2488    
2489     function TIBSQL.GetRowsAffected: Integer;
2490     var
2491     info_request: Char;
2492 tony 27 RB: TResultBuffer;
2493 tony 19 begin
2494     if not Prepared then
2495     result := -1
2496     else begin
2497 tony 27 RB := TResultBuffer.Create;
2498     try
2499     info_request := isc_info_sql_records;
2500     if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
2501     RB.Size, RB.buffer) > 0 then
2502     IBDatabaseError;
2503     case SQLType of
2504     SQLInsert, SQLUpdate: {Covers Insert or Update as well as individual update}
2505     Result := RB.GetValue(isc_info_sql_records, isc_info_req_insert_count)+
2506     RB.GetValue(isc_info_sql_records, isc_info_req_update_count);
2507     SQLDelete:
2508     Result := RB.GetValue(isc_info_sql_records, isc_info_req_delete_count);
2509     SQLExecProcedure:
2510     Result := RB.GetValue(isc_info_sql_records, isc_info_req_insert_count) +
2511     RB.GetValue(isc_info_sql_records, isc_info_req_update_count) +
2512     RB.GetValue(isc_info_sql_records, isc_info_req_delete_count);
2513     else
2514     Result := 0;
2515     end;
2516     finally
2517     RB.Free;
2518     end;
2519 tony 19 end;
2520     end;
2521    
2522     function TIBSQL.GetSQLParams: TIBXSQLDA;
2523     begin
2524     if not Prepared then
2525     Prepare;
2526     result := FSQLParams;
2527     end;
2528    
2529     function TIBSQL.GetTransaction: TIBTransaction;
2530     begin
2531     result := FBase.Transaction;
2532     end;
2533    
2534     function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2535     begin
2536     result := FBase.TRHandle;
2537     end;
2538    
2539     {
2540     Preprocess SQL
2541     Using FSQL, process the typed SQL and put the process SQL
2542     in FProcessedSQL and parameter names in FSQLParams
2543     }
2544     procedure TIBSQL.PreprocessSQL;
2545     var
2546     cCurChar, cNextChar, cQuoteChar: Char;
2547     sSQL, sProcessedSQL, sParamName: String;
2548     i, iLenSQL, iSQLPos: Integer;
2549     iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
2550     iParamSuffix: Integer;
2551     slNames: TStrings;
2552    
2553     const
2554     DefaultState = 0;
2555     CommentState = 1;
2556     QuoteState = 2;
2557     ParamState = 3;
2558     {$ifdef ALLOWDIALECT3PARAMNAMES}
2559     ParamDefaultState = 0;
2560     ParamQuoteState = 1;
2561     {$endif}
2562    
2563     procedure AddToProcessedSQL(cChar: Char);
2564     begin
2565     sProcessedSQL[iSQLPos] := cChar;
2566     Inc(iSQLPos);
2567     end;
2568    
2569     begin
2570 tony 27 sParamName := '';
2571 tony 19 slNames := TStringList.Create;
2572     try
2573     { Do some initializations of variables }
2574     iParamSuffix := 0;
2575     cQuoteChar := '''';
2576     sSQL := FSQL.Text;
2577     iLenSQL := Length(sSQL);
2578     SetString(sProcessedSQL, nil, iLenSQL + 1);
2579     i := 1;
2580     iSQLPos := 1;
2581     iCurState := DefaultState;
2582     {$ifdef ALLOWDIALECT3PARAMNAMES}
2583     iCurParamState := ParamDefaultState;
2584     {$endif}
2585     { Now, traverse through the SQL string, character by character,
2586     picking out the parameters and formatting correctly for InterBase }
2587     while (i <= iLenSQL) do begin
2588     { Get the current token and a look-ahead }
2589     cCurChar := sSQL[i];
2590     if i = iLenSQL then
2591     cNextChar := #0
2592     else
2593     cNextChar := sSQL[i + 1];
2594     { Now act based on the current state }
2595     case iCurState of
2596     DefaultState: begin
2597     case cCurChar of
2598     '''', '"': begin
2599     cQuoteChar := cCurChar;
2600     iCurState := QuoteState;
2601     end;
2602     '?', ':': begin
2603     iCurState := ParamState;
2604     AddToProcessedSQL('?');
2605     end;
2606     '/': if (cNextChar = '*') then begin
2607     AddToProcessedSQL(cCurChar);
2608     Inc(i);
2609     iCurState := CommentState;
2610     end;
2611     end;
2612     end;
2613     CommentState: begin
2614     if (cNextChar = #0) then
2615     IBError(ibxeSQLParseError, [SEOFInComment])
2616     else if (cCurChar = '*') then begin
2617     if (cNextChar = '/') then
2618     iCurState := DefaultState;
2619     end;
2620     end;
2621     QuoteState: begin
2622     if cNextChar = #0 then
2623     IBError(ibxeSQLParseError, [SEOFInString])
2624     else if (cCurChar = cQuoteChar) then begin
2625     if (cNextChar = cQuoteChar) then begin
2626     AddToProcessedSQL(cCurChar);
2627     Inc(i);
2628     end else
2629     iCurState := DefaultState;
2630     end;
2631     end;
2632     ParamState:
2633     begin
2634     { collect the name of the parameter }
2635     {$ifdef ALLOWDIALECT3PARAMNAMES}
2636     if iCurParamState = ParamDefaultState then
2637     begin
2638     if cCurChar = '"' then
2639     iCurParamState := ParamQuoteState
2640     else
2641     {$endif}
2642     if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2643     sParamName := sParamName + cCurChar
2644     else if FGenerateParamNames then
2645     begin
2646     sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2647     Inc(iParamSuffix);
2648     iCurState := DefaultState;
2649     slNames.AddObject(sParamName,self); //Note local convention
2650     //add pointer to self to mark entry
2651     sParamName := '';
2652     end
2653     else
2654     IBError(ibxeSQLParseError, [SParamNameExpected]);
2655     {$ifdef ALLOWDIALECT3PARAMNAMES}
2656     end
2657     else begin
2658     { determine if Quoted parameter name is finished }
2659     if cCurChar = '"' then
2660     begin
2661     Inc(i);
2662     slNames.Add(sParamName);
2663     SParamName := '';
2664     iCurParamState := ParamDefaultState;
2665     iCurState := DefaultState;
2666     end
2667     else
2668     sParamName := sParamName + cCurChar
2669     end;
2670     {$endif}
2671     { determine if the unquoted parameter name is finished }
2672     if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
2673     (iCurState <> DefaultState) then
2674     begin
2675     if not (cNextChar in ['A'..'Z', 'a'..'z',
2676     '0'..'9', '_', '$']) then begin
2677     Inc(i);
2678     iCurState := DefaultState;
2679     slNames.Add(sParamName);
2680     sParamName := '';
2681     end;
2682     end;
2683     end;
2684     end;
2685     if iCurState <> ParamState then
2686     AddToProcessedSQL(sSQL[i]);
2687     Inc(i);
2688     end;
2689     AddToProcessedSQL(#0);
2690     FSQLParams.Count := slNames.Count;
2691     for i := 0 to slNames.Count - 1 do
2692     FSQLParams.SetParamName(slNames[i], i,FUniqueParamNames or (slNames.Objects[i] <> nil));
2693     FProcessedSQL.Text := sProcessedSQL;
2694     finally
2695     slNames.Free;
2696     end;
2697     end;
2698    
2699     procedure TIBSQL.SetDatabase(Value: TIBDatabase);
2700     begin
2701     FBase.Database := Value;
2702     end;
2703    
2704     procedure TIBSQL.Prepare;
2705     var
2706     stmt_len: Integer;
2707     res_buffer: array[0..7] of Char;
2708     type_item: Char;
2709     begin
2710     CheckClosed;
2711     FBase.CheckDatabase;
2712     FBase.CheckTransaction;
2713     if FPrepared then
2714     exit;
2715     if (FSQL.Text = '') then
2716     IBError(ibxeEmptyQuery, [nil]);
2717     if not ParamCheck then
2718     FProcessedSQL.Text := FSQL.Text
2719     else
2720     PreprocessSQL;
2721     if (FProcessedSQL.Text = '') then
2722     IBError(ibxeEmptyQuery, [nil]);
2723     try
2724     Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
2725     @FHandle), True);
2726     Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2727     PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
2728     { After preparing the statement, query the stmt type and possibly
2729     create a FSQLRecord "holder" }
2730     { Get the type of the statement }
2731 tony 27 type_item := isc_info_sql_stmt_type;
2732 tony 19 Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2733     SizeOf(res_buffer), res_buffer), True);
2734 tony 27 if (res_buffer[0] <> isc_info_sql_stmt_type) then
2735 tony 19 IBError(ibxeUnknownError, [nil]);
2736     stmt_len := isc_vax_integer(@res_buffer[1], 2);
2737     FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2738     { Done getting the type }
2739     case FSQLType of
2740     SQLGetSegment,
2741     SQLPutSegment,
2742     SQLStartTransaction: begin
2743     FreeHandle;
2744     IBError(ibxeNotPermitted, [nil]);
2745     end;
2746     SQLCommit,
2747     SQLRollback,
2748     SQLDDL, SQLSetGenerator,
2749     SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2750     SQLExecProcedure: begin
2751     { We already know how many inputs there are, so... }
2752     if (FSQLParams.FXSQLDA <> nil) and
2753     (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2754     FSQLParams.FXSQLDA), False) > 0) then
2755     IBDataBaseError;
2756     FSQLParams.Initialize;
2757     if FSQLType in [SQLSelect, SQLSelectForUpdate,
2758     SQLExecProcedure] then begin
2759     { Allocate an initial output descriptor (with one column) }
2760     FSQLRecord.Count := 1;
2761     { Using isc_dsql_describe, get the right size for the columns... }
2762     Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2763     if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2764     FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2765     Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2766     end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2767     FSQLRecord.Count := 0;
2768     FSQLRecord.Initialize;
2769     end;
2770     end;
2771     end;
2772     FPrepared := True;
2773     if not (csDesigning in ComponentState) then
2774     MonitorHook.SQLPrepare(Self);
2775     except
2776     on E: Exception do begin
2777     if (FHandle <> nil) then
2778     FreeHandle;
2779 tony 21 if E is EIBInterBaseError then
2780     raise EIBInterBaseError.Create(EIBInterBaseError(E).SQLCode,
2781     EIBInterBaseError(E).IBErrorCode,
2782     EIBInterBaseError(E).Message +
2783     sSQLErrorSeparator + FProcessedSQL.Text)
2784     else
2785     raise;
2786 tony 19 end;
2787     end;
2788     end;
2789    
2790     function TIBSQL.GetUniqueRelationName: String;
2791     begin
2792     if FPrepared and (FSQLType = SQLSelect) then
2793     result := FSQLRecord.UniqueRelationName
2794     else
2795     result := '';
2796     end;
2797    
2798     procedure TIBSQL.SetSQL(Value: TStrings);
2799     begin
2800     if FSQL.Text <> Value.Text then
2801     begin
2802     FSQL.BeginUpdate;
2803     try
2804     FSQL.Assign(Value);
2805     finally
2806     FSQL.EndUpdate;
2807     end;
2808     end;
2809     end;
2810    
2811     procedure TIBSQL.SetTransaction(Value: TIBTransaction);
2812     begin
2813     FBase.Transaction := Value;
2814     end;
2815    
2816     procedure TIBSQL.SQLChanging(Sender: TObject);
2817     begin
2818     if Assigned(OnSQLChanging) then
2819     OnSQLChanging(Self);
2820     if FHandle <> nil then FreeHandle;
2821     end;
2822    
2823 tony 35 procedure TIBSQL.SQLChanged(Sender: TObject);
2824     begin
2825     if assigned(OnSQLChanged) then
2826     OnSQLChanged(self);
2827     end;
2828    
2829 tony 27 procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
2830     Action: TTransactionAction);
2831 tony 19 begin
2832     if (FOpen) then
2833     Close;
2834     end;
2835    
2836     end.