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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines