ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQL.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBSQL.pas (file contents):
Revision 5 by tony, Fri Feb 18 16:26:16 2011 UTC vs.
Revision 17 by tony, Sat Dec 28 19:22:24 2013 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines