ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
Revision: 5
Committed: Fri Feb 18 16:26:16 2011 UTC (13 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 71942 byte(s)
Log Message:
Committing updates for Release pre-release

File Contents

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