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 1 by tony, Mon Jul 31 16:43:00 2000 UTC vs.
Revision 139 by tony, Wed Jan 24 16:16:29 2018 UTC

# Line 24 | Line 24
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 < interface
36 > {$Mode Delphi}
37  
38 < uses
34 <  Windows, SysUtils, Classes, Forms, Controls, IBHeader,
35 <  IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
38 > {$codepage UTF8}
39  
40 < type
41 <  TIBSQL = class;
39 <  TIBXSQLDA = class;
40 <  
41 <  { TIBXSQLVAR }
42 <  TIBXSQLVAR = class(TObject)
43 <  private
44 <    FParent: TIBXSQLDA;
45 <    FSQL: TIBSQL;
46 <    FIndex: Integer;
47 <    FModified: Boolean;
48 <    FName: String;
49 <    FXSQLVAR: PXSQLVAR;       { Point to the PXSQLVAR in the owner object }
50 <
51 <    function AdjustScale(Value: Int64; Scale: Integer): Double;
52 <    function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
53 <    function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
54 <    function GetAsCurrency: Currency;
55 <    function GetAsInt64: Int64;
56 <    function GetAsDateTime: TDateTime;
57 <    function GetAsDouble: Double;
58 <    function GetAsFloat: Float;
59 <    function GetAsLong: Long;
60 <    function GetAsPointer: Pointer;
61 <    function GetAsQuad: TISC_QUAD;
62 <    function GetAsShort: Short;
63 <    function GetAsString: String;
64 <    function GetAsVariant: Variant;
65 <    function GetAsXSQLVAR: PXSQLVAR;
66 <    function GetIsNull: Boolean;
67 <    function GetIsNullable: Boolean;
68 <    function GetSize: Integer;
69 <    function GetSQLType: Integer;
70 <    procedure SetAsCurrency(Value: Currency);
71 <    procedure SetAsInt64(Value: Int64);
72 <    procedure SetAsDate(Value: TDateTime);
73 <    procedure SetAsTime(Value: TDateTime);
74 <    procedure SetAsDateTime(Value: TDateTime);
75 <    procedure SetAsDouble(Value: Double);
76 <    procedure SetAsFloat(Value: Float);
77 <    procedure SetAsLong(Value: Long);
78 <    procedure SetAsPointer(Value: Pointer);
79 <    procedure SetAsQuad(Value: TISC_QUAD);
80 <    procedure SetAsShort(Value: Short);
81 <    procedure SetAsString(Value: String);
82 <    procedure SetAsVariant(Value: Variant);
83 <    procedure SetAsXSQLVAR(Value: PXSQLVAR);
84 <    procedure SetIsNull(Value: Boolean);
85 <    procedure SetIsNullable(Value: Boolean);
86 <  public
87 <    constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
88 <    procedure Assign(Source: TIBXSQLVAR);
89 <    procedure LoadFromFile(const FileName: String);
90 <    procedure LoadFromStream(Stream: TStream);
91 <    procedure SaveToFile(const FileName: String);
92 <    procedure SaveToStream(Stream: TStream);
93 <    property AsDate: TDateTime read GetAsDateTime write SetAsDate;
94 <    property AsTime: TDateTime read GetAsDateTime write SetAsTime;
95 <    property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
96 <    property AsDouble: Double read GetAsDouble write SetAsDouble;
97 <    property AsFloat: Float read GetAsFloat write SetAsFloat;
98 <    property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
99 <    property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
100 <    property AsInteger: Integer read GetAsLong write SetAsLong;
101 <    property AsLong: Long read GetAsLong write SetAsLong;
102 <    property AsPointer: Pointer read GetAsPointer write SetAsPointer;
103 <    property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
104 <    property AsShort: Short read GetAsShort write SetAsShort;
105 <    property AsString: String read GetAsString write SetAsString;
106 <    property AsVariant: Variant read GetAsVariant write SetAsVariant;
107 <    property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
108 <    property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
109 <    property IsNull: Boolean read GetIsNull write SetIsNull;
110 <    property IsNullable: Boolean read GetIsNullable write SetIsNullable;
111 <    property Index: Integer read FIndex;
112 <    property Modified: Boolean read FModified write FModified;
113 <    property Name: String read FName;
114 <    property Size: Integer read GetSize;
115 <    property SQLType: Integer read GetSQLType;
116 <    property Value: Variant read GetAsVariant write SetAsVariant;
117 <  end;
40 > (* Define IBXQUERYSTATS to write to stdout a summary of query execution
41 >   statistics each time a query is executed
42  
43 <  TIBXSQLVARArray = Array of TIBXSQLVAR;
43 >   Define IBXQUERYTIME to write to stdout The local execution time for each
44 >   query
45 >   *)
46  
47 <  { TIBXSQLVAR }
48 <  TIBXSQLDA = class(TObject)
49 <  protected
50 <    FSQL: TIBSQL;
125 <    FCount: Integer;
126 <    FNames: TStrings;
127 <    FSize: Integer;
128 <    FXSQLDA: PXSQLDA;
129 <    FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
130 <    FUniqueRelationName: String;
131 <    function GetModified: Boolean;
132 <    function GetNames: String;
133 <    function GetRecordSize: Integer;
134 <    function GetXSQLDA: PXSQLDA;
135 <    function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
136 <    function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
137 <    procedure Initialize;
138 <    procedure SetCount(Value: Integer);
139 <  public
140 <    constructor Create(Query: TIBSQL);
141 <    destructor Destroy; override;
142 <    procedure AddName(FieldName: String; Idx: Integer);
143 <    function ByName(Idx: String): TIBXSQLVAR;
144 <    property AsXSQLDA: PXSQLDA read GetXSQLDA;
145 <    property Count: Integer read FCount write SetCount;
146 <    property Modified: Boolean read GetModified;
147 <    property Names: String read GetNames;
148 <    property RecordSize: Integer read GetRecordSize;
149 <    property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
150 <    property UniqueRelationName: String read FUniqueRelationName;
151 <  end;
47 > { $DEFINE IBXQUERYSTATS}
48 > { $DEFINE IBXQUERYTIME}
49 >
50 > interface
51  
52 + uses
53 + {$IFDEF WINDOWS }
54 +  Windows,
55 + {$ELSE}
56 +  baseunix, unix,
57 + {$ENDIF}
58 +  SysUtils, Classes, IBExternals, IB, IBDatabase, IBUtils;
59 +
60 + type
61    { TIBBatch }
62  
63    TIBBatch = class(TObject)
64    protected
65      FFilename: String;
66 <    FColumns: TIBXSQLDA;
67 <    FParams: TIBXSQLDA;
66 >    FColumns: IResults;
67 >    FParams: ISQLParams;
68    public
69      procedure ReadyFile; virtual; abstract;
70 <    property Columns: TIBXSQLDA read FColumns;
70 >    property Columns: IResults read FColumns;
71      property Filename: String read FFilename write FFilename;
72 <    property Params: TIBXSQLDA read FParams;
72 >    property Params: ISQLParams read FParams;
73    end;
74  
75    TIBBatchInput = class(TIBBatch)
# Line 178 | Line 86 | type
86    { TIBOutputDelimitedFile }
87    TIBOutputDelimitedFile = class(TIBBatchOutput)
88    protected
89 +  {$IFDEF UNIX}
90 +    FHandle: cint;
91 +  {$ELSE}
92      FHandle: THandle;
93 +  {$ENDIF}
94      FOutputTitles: Boolean;
95      FColDelimiter,
96      FRowDelimiter: string;
# Line 217 | Line 129 | type
129    { TIBOutputRawFile }
130    TIBOutputRawFile = class(TIBBatchOutput)
131    protected
132 +  {$IFDEF UNIX}
133 +    FHandle: cint;
134 +  {$ELSE}
135      FHandle: THandle;
136 +  {$ENDIF}
137    public
138      destructor Destroy; override;
139      procedure ReadyFile; override;
# Line 227 | Line 143 | type
143    { TIBInputRawFile }
144    TIBInputRawFile = class(TIBBatchInput)
145    protected
146 +   {$IFDEF UNIX}
147 +    FHandle: cint;
148 +  {$ELSE}
149      FHandle: THandle;
150 +  {$ENDIF}
151    public
152      destructor Destroy; override;
153      function ReadParameters: Boolean; override;
# Line 235 | Line 155 | type
155    end;
156  
157       { TIBSQL }
238  TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
239                  SQLUpdate, SQLDelete, SQLDDL,
240                  SQLGetSegment, SQLPutSegment,
241                  SQLExecProcedure, SQLStartTransaction,
242                  SQLCommit, SQLRollback,
243                  SQLSelectForUpdate, SQLSetGenerator);
158  
159    TIBSQL = class(TComponent)
160    private
161 <    FIBLoaded: Boolean;
161 >    FMetaData: IMetaData;
162 >    FSQLParams: ISQLParams;
163 >    FStatement: IStatement;
164 >    FOnSQLChanged: TNotifyEvent;
165 >    FUniqueParamNames: Boolean;
166 >    FBOF: boolean;
167 >    FEOF: boolean;
168 >    function GetFieldCount: integer;
169 >    function GetOpen: Boolean;
170 >    function GetPrepared: Boolean;
171 >    function GetSQLStatementType: TIBSQLStatementTypes;
172 >    procedure SetUniqueParamNames(AValue: Boolean);
173    protected
174      FBase: TIBBase;
175 <    FBOF,                          { At BOF? }
251 <    FEOF,                          { At EOF? }
252 <    FGoToFirstRecordOnExecute,     { Automatically position record on first record after executing }
253 <    FOpen,                         { Is a cursor open? }
254 <    FPrepared: Boolean;            { Has the query been prepared? }
175 >    FGoToFirstRecordOnExecute: boolean;     { Automatically position record on first record after executing }
176      FRecordCount: Integer;         { How many records have been read so far? }
256    FCursor: String;               { Cursor name...}
257    FHandle: TISC_STMT_HANDLE;     { Once prepared, this accesses the SQL Query }
177      FOnSQLChanging: TNotifyEvent;  { Call this when the SQL is changing }
178      FSQL: TStrings;                { SQL Query (by user) }
179      FParamCheck: Boolean;          { Check for parameters? (just like TQuery) }
180 <    FProcessedSQL: TStrings;       { SQL Query (pre-processed for param labels) }
181 <    FSQLParams,                    { Any parameters to the query }
263 <    FSQLRecord: TIBXSQLDA;         { The current record }
264 <    FSQLType: TIBSQLTypes;         { Select, update, delete, insert, create, alter, etc...}
180 >    FResults: IResults;            {Single row results from exec}
181 >    FResultSet: IResultSet;        {Multi-row results from open cursor}
182      FGenerateParamNames: Boolean;  { Auto generate param names ?}
183      procedure DoBeforeDatabaseDisconnect(Sender: TObject);
184      function GetDatabase: TIBDatabase;
268    function GetDBHandle: PISC_DB_HANDLE;
185      function GetEOF: Boolean;
186 <    function GetFields(const Idx: Integer): TIBXSQLVAR;
186 >    function GetFields(const Idx: Integer): ISQLData;
187      function GetFieldIndex(FieldName: String): Integer;
188      function GetPlan: String;
189      function GetRecordCount: Integer;
190      function GetRowsAffected: Integer;
191 <    function GetSQLParams: TIBXSQLDA;
191 >    function GetSQLParams: ISQLParams;
192      function GetTransaction: TIBTransaction;
277    function GetTRHandle: PISC_TR_HANDLE;
278    procedure PreprocessSQL;
193      procedure SetDatabase(Value: TIBDatabase);
194      procedure SetSQL(Value: TStrings);
195      procedure SetTransaction(Value: TIBTransaction);
196      procedure SQLChanging(Sender: TObject);
197 <    procedure BeforeTransactionEnd(Sender: TObject);
197 >    procedure SQLChanged(Sender: TObject);
198 >    procedure BeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
199    public
200      constructor Create(AOwner: TComponent); override;
201      destructor Destroy; override;
202      procedure BatchInput(InputObject: TIBBatchInput);
203      procedure BatchOutput(OutputObject: TIBBatchOutput);
289    function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
204      procedure CheckClosed;           { raise error if query is not closed. }
205      procedure CheckOpen;             { raise error if query is not open.}
206      procedure CheckValidStatement;   { raise error if statement is invalid.}
207      procedure Close;
294    function Current: TIBXSQLDA;
208      procedure ExecQuery;
209 <    function FieldByName(FieldName: String): TIBXSQLVAR;
209 >    function HasField(FieldName: String): boolean;
210 >    function FieldByName(FieldName: String): ISQLData;
211 >    function ParamByName(ParamName: String): ISQLParam;
212      procedure FreeHandle;
213 <    function Next: TIBXSQLDA;
213 >    function Next: boolean;
214      procedure Prepare;
215      function GetUniqueRelationName: String;
216      property Bof: Boolean read FBOF;
302    property DBHandle: PISC_DB_HANDLE read GetDBHandle;
217      property Eof: Boolean read GetEOF;
218 <    property Fields[const Idx: Integer]: TIBXSQLVAR read GetFields;
218 >    property Current: IResults read FResults;
219 >    property Fields[const Idx: Integer]: ISQLData read GetFields; default;
220      property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
221 <    property Open: Boolean read FOpen;
222 <    property Params: TIBXSQLDA read GetSQLParams;
221 >    property FieldCount: integer read GetFieldCount;
222 >    property Open: Boolean read GetOpen;
223 >    property Params: ISQLParams read GetSQLParams;
224      property Plan: String read GetPlan;
225 <    property Prepared: Boolean read FPrepared;
225 >    property Prepared: Boolean read GetPrepared;
226      property RecordCount: Integer read GetRecordCount;
227      property RowsAffected: Integer read GetRowsAffected;
228 <    property SQLType: TIBSQLTypes read FSQLType;
313 <    property TRHandle: PISC_TR_HANDLE read GetTRHandle;
314 <    property Handle: TISC_STMT_HANDLE read FHandle;
315 <    property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
228 >    property SQLStatementType: TIBSQLStatementTypes read GetSQLStatementType;
229      property UniqueRelationName: String read GetUniqueRelationName;
230 +    property Statement: IStatement read FStatement;
231 +    property MetaData: IMetaData read FMetaData;
232    published
233      property Database: TIBDatabase read GetDatabase write SetDatabase;
234 +    property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
235 +    property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
236      property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
237                                                 write FGoToFirstRecordOnExecute
238                                                 default True;
# Line 323 | Line 240 | type
240      property SQL: TStrings read FSQL write SetSQL;
241      property Transaction: TIBTransaction read GetTransaction write SetTransaction;
242      property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
243 +    property OnSQLChanged: TNotifyEvent read FOnSQLChanged write FOnSQLChanged;
244    end;
245  
246 + procedure IBAlloc(var P; OldSize, NewSize: Integer);
247 +
248   implementation
249  
250   uses
251 <  IBIntf, IBBlob, IBSQLMonitor;
332 <
333 < { TIBXSQLVAR }
334 < constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
335 < begin
336 <  inherited Create;
337 <  FParent := Parent;
338 <  FSQL := Query;
339 < end;
340 <
341 < procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
342 < var
343 <  szBuff: PChar;
344 <  s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
345 <  bSourceBlob, bDestBlob: Boolean;
346 <  iSegs, iMaxSeg, iSize: Long;
347 <  iBlobType: Short;
348 < begin
349 <  szBuff := nil;
350 <  bSourceBlob := True;
351 <  bDestBlob := True;
352 <  s_bhandle := nil;
353 <  d_bhandle := nil;
354 <  try
355 <    if (Source.IsNull) then
356 <    begin
357 <      IsNull := True;
358 <      exit;
359 <    end
360 <    else
361 <      if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
362 <         (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
363 <        exit; { arrays not supported }
364 <    if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
365 <       (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
366 <    begin
367 <      AsXSQLVAR := Source.AsXSQLVAR;
368 <      exit;
369 <    end
370 <    else
371 <      if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
372 <      begin
373 <        szBuff := nil;
374 <        IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
375 <        Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
376 <        bSourceBlob := False;
377 <        iSize := Source.FXSQLVAR^.sqllen;
378 <      end
379 <      else
380 <        if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
381 <          bDestBlob := False;
382 <
383 <    if bSourceBlob then
384 <    begin
385 <      { read the blob }
386 <      Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
387 <        Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
388 <        0, nil), True);
389 <      try
390 <        IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
391 <          iBlobType);
392 <        szBuff := nil;
393 <        IBAlloc(szBuff, 0, iSize);
394 <        IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
395 <      finally
396 <        Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
397 <      end;
398 <    end;
399 <
400 <    if bDestBlob then
401 <    begin
402 <      { write the blob }
403 <      FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
404 <        FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
405 <        0, nil), True);
406 <      try
407 <        IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
408 <      finally
409 <        FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
410 <      end;
411 <    end
412 <    else
413 <    begin
414 <      { just copy the buffer }
415 <      FXSQLVAR.sqltype := SQL_TEXT;
416 <      FXSQLVAR.sqllen := iSize;
417 <      IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
418 <      Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
419 <    end;
420 <  finally
421 <    FreeMem(szBuff);
422 <  end;
423 < end;
424 <
425 < function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
426 < var
427 <  Scaling, i: Integer;
428 <  Val: Double;
429 < begin
430 <  Scaling := 1; Val := Value;
431 <  if Scale > 0 then
432 <  begin
433 <    for i := 1 to Scale do
434 <      Scaling := Scaling * 10;
435 <    result := Val * Scaling;
436 <  end
437 <  else
438 <    if Scale < 0 then
439 <    begin
440 <      for i := -1 downto Scale do
441 <        Scaling := Scaling * 10;
442 <      result := Val / Scaling;
443 <    end
444 <    else
445 <      result := Val;
446 < end;
447 <
448 < function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
449 < var
450 <  Scaling, i: Integer;
451 <  Val: Int64;
452 < begin
453 <  Scaling := 1; Val := Value;
454 <  if Scale > 0 then begin
455 <    for i := 1 to Scale do Scaling := Scaling * 10;
456 <    result := Val * Scaling;
457 <  end else if Scale < 0 then begin
458 <    for i := -1 downto Scale do Scaling := Scaling * 10;
459 <    result := Val div Scaling;
460 <  end else
461 <    result := Val;
462 < end;
463 <
464 < function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
465 < var
466 <  Scaling, i : Integer;
467 <  FractionText, PadText, CurrText: string;
468 < begin
469 <  result := Value;
470 <  Scaling := 1;
471 <  if Scale > 0 then
472 <  begin
473 <    for i := 1 to Scale do
474 <      Scaling := Scaling * 10;
475 <    result := Value * Scaling;
476 <  end
477 <  else
478 <    if Scale < 0 then
479 <    begin
480 <      for i := -1 downto Scale do
481 <        Scaling := Scaling * 10;
482 <      FractionText := IntToStr(abs(Value mod Scaling));
483 <      for i := Length(FractionText) to -Scale -1 do
484 <        PadText := '0' + PadText;
485 <      if Value < 0 then
486 <        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
487 <      else
488 <        CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
489 <      try
490 <        result := StrToCurr(CurrText);
491 <      except
492 <        on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
493 <      end;
494 <    end;
495 < end;
496 <
497 < function TIBXSQLVAR.GetAsCurrency: Currency;
498 < begin
499 <  result := 0;
500 <  if FSQL.Database.SQLDialect < 3 then
501 <    result := GetAsDouble
502 <  else begin
503 <    if not IsNull then
504 <      case FXSQLVAR^.sqltype and (not 1) of
505 <        SQL_TEXT, SQL_VARYING: begin
506 <          try
507 <            result := StrtoCurr(AsString);
508 <          except
509 <            on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
510 <          end;
511 <        end;
512 <        SQL_SHORT:
513 <          result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
514 <                                      FXSQLVAR^.sqlscale);
515 <        SQL_LONG:
516 <          result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
517 <                                      FXSQLVAR^.sqlscale);
518 <        SQL_INT64:
519 <          result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
520 <                                      FXSQLVAR^.sqlscale);
521 <        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
522 <          result := Trunc(AsDouble);
523 <        else
524 <          IBError(ibxeInvalidDataConversion, [nil]);
525 <      end;
526 <    end;
527 < end;
528 <
529 < function TIBXSQLVAR.GetAsInt64: Int64;
530 < begin
531 <  result := 0;
532 <  if not IsNull then
533 <    case FXSQLVAR^.sqltype and (not 1) of
534 <      SQL_TEXT, SQL_VARYING: begin
535 <        try
536 <          result := StrToInt64(AsString);
537 <        except
538 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
539 <        end;
540 <      end;
541 <      SQL_SHORT:
542 <        result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
543 <                                    FXSQLVAR^.sqlscale);
544 <      SQL_LONG:
545 <        result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
546 <                                    FXSQLVAR^.sqlscale);
547 <      SQL_INT64:
548 <        result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
549 <                                    FXSQLVAR^.sqlscale);
550 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
551 <        result := Trunc(AsDouble);
552 <      else
553 <        IBError(ibxeInvalidDataConversion, [nil]);
554 <    end;
555 < end;
556 <
557 < function TIBXSQLVAR.GetAsDateTime: TDateTime;
558 < var
559 <  tm_date: TCTimeStructure;
560 < begin
561 <  result := 0;
562 <  if not IsNull then
563 <    case FXSQLVAR^.sqltype and (not 1) of
564 <      SQL_TEXT, SQL_VARYING: begin
565 <        try
566 <          result := StrToDate(AsString);
567 <        except
568 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
569 <        end;
570 <      end;
571 <      SQL_TYPE_DATE: begin
572 <        isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
573 <        try
574 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
575 <                               Word(tm_date.tm_mday));
576 <        except
577 <          on E: EConvertError do begin
578 <            IBError(ibxeInvalidDataConversion, [nil]);
579 <          end;
580 <        end;
581 <      end;
582 <      SQL_TYPE_TIME: begin
583 <        isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
584 <        try
585 <          result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
586 <                               Word(tm_date.tm_sec), 0)
587 <        except
588 <          on E: EConvertError do begin
589 <            IBError(ibxeInvalidDataConversion, [nil]);
590 <          end;
591 <        end;
592 <      end;
593 <      SQL_TIMESTAMP: begin
594 <        isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
595 <        try
596 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
597 <                              Word(tm_date.tm_mday));
598 <          if result >= 0 then
599 <            result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
600 <                                          Word(tm_date.tm_sec), 0)
601 <          else
602 <            result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
603 <                                          Word(tm_date.tm_sec), 0)
604 <        except
605 <          on E: EConvertError do begin
606 <            IBError(ibxeInvalidDataConversion, [nil]);
607 <          end;
608 <        end;
609 <      end;
610 <      else
611 <        IBError(ibxeInvalidDataConversion, [nil]);
612 <    end;
613 < end;
614 <
615 < function TIBXSQLVAR.GetAsDouble: Double;
616 < begin
617 <  result := 0;
618 <  if not IsNull then begin
619 <    case FXSQLVAR^.sqltype and (not 1) of
620 <      SQL_TEXT, SQL_VARYING: begin
621 <        try
622 <          result := StrToFloat(AsString);
623 <        except
624 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
625 <        end;
626 <      end;
627 <      SQL_SHORT:
628 <        result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
629 <                              FXSQLVAR^.sqlscale);
630 <      SQL_LONG:
631 <        result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
632 <                              FXSQLVAR^.sqlscale);
633 <      SQL_INT64:
634 <        result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
635 <      SQL_FLOAT:
636 <        result := PFloat(FXSQLVAR^.sqldata)^;
637 <      SQL_DOUBLE, SQL_D_FLOAT:
638 <        result := PDouble(FXSQLVAR^.sqldata)^;
639 <      else
640 <        IBError(ibxeInvalidDataConversion, [nil]);
641 <    end;
642 <    if  FXSQLVAR^.sqlscale <> 0 then
643 <      result :=
644 <        StrToFloat(FloatToStrF(result, fffixed, 15,
645 <                  Abs(FXSQLVAR^.sqlscale) ));
646 <  end;
647 < end;
648 <
649 < function TIBXSQLVAR.GetAsFloat: Float;
650 < begin
651 <  result := 0;
652 <  try
653 <    result := AsDouble;
654 <  except
655 <    on E: EOverflow do
656 <      IBError(ibxeInvalidDataConversion, [nil]);
657 <  end;
658 < end;
659 <
660 < function TIBXSQLVAR.GetAsLong: Long;
661 < begin
662 <  result := 0;
663 <  if not IsNull then
664 <    case FXSQLVAR^.sqltype and (not 1) of
665 <      SQL_TEXT, SQL_VARYING: begin
666 <        try
667 <          result := StrToInt(AsString);
668 <        except
669 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
670 <        end;
671 <      end;
672 <      SQL_SHORT:
673 <        result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
674 <                                    FXSQLVAR^.sqlscale));
675 <      SQL_LONG:
676 <        result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
677 <                                    FXSQLVAR^.sqlscale));
678 <      SQL_INT64:
679 <        result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
680 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
681 <        result := Trunc(AsDouble);
682 <      else
683 <        IBError(ibxeInvalidDataConversion, [nil]);
684 <    end;
685 < end;
686 <
687 < function TIBXSQLVAR.GetAsPointer: Pointer;
688 < begin
689 <  if not IsNull then
690 <    result := FXSQLVAR^.sqldata
691 <  else
692 <    result := nil;
693 < end;
694 <
695 < function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
696 < begin
697 <  result.gds_quad_high := 0;
698 <  result.gds_quad_low := 0;
699 <  if not IsNull then
700 <    case FXSQLVAR^.sqltype and (not 1) of
701 <      SQL_BLOB, SQL_ARRAY, SQL_QUAD:
702 <        result := PISC_QUAD(FXSQLVAR^.sqldata)^;
703 <      else
704 <        IBError(ibxeInvalidDataConversion, [nil]);
705 <    end;
706 < end;
707 <
708 < function TIBXSQLVAR.GetAsShort: Short;
709 < begin
710 <  result := 0;
711 <  try
712 <    result := AsLong;
713 <  except
714 <    on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
715 <  end;
716 < end;
717 <
718 <
719 < function TIBXSQLVAR.GetAsString: String;
720 < var
721 <  sz: PChar;
722 <  str_len: Integer;
723 <  ss: TStringStream;
724 < begin
725 <  result := '';
726 <  { Check null, if so return a default string }
727 <  if not IsNull then
728 <    case FXSQLVar^.sqltype and (not 1) of
729 <      SQL_ARRAY:
730 <        result := '(Array)'; {do not localize}
731 <      SQL_BLOB: begin
732 <        ss := TStringStream.Create('');
733 <        SaveToStream(ss);
734 <        result := ss.DataString;
735 <        ss.Free;
736 <      end;
737 <      SQL_TEXT, SQL_VARYING: begin
738 <        sz := FXSQLVAR^.sqldata;
739 <        if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
740 <          str_len := FXSQLVar^.sqllen
741 <        else begin
742 <          str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
743 <          Inc(sz, 2);
744 <        end;
745 <        SetString(result, sz, str_len);
746 <        if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
747 <          result := TrimRight(result);
748 <      end;
749 <      SQL_TYPE_DATE:
750 <        case FSQL.Database.SQLDialect of
751 <          1 : result := DateTimeToStr(AsDateTime);
752 <          3 : result := DateToStr(AsDateTime);
753 <        end;
754 <      SQL_TYPE_TIME :
755 <        result := TimeToStr(AsDateTime);
756 <      SQL_TIMESTAMP:
757 <        result := DateTimeToStr(AsDateTime);
758 <      SQL_SHORT, SQL_LONG:
759 <        if FXSQLVAR^.sqlscale = 0 then
760 <          result := IntToStr(AsLong)
761 <        else if FXSQLVAR^.sqlscale >= (-4) then
762 <          result := CurrToStr(AsCurrency)
763 <        else
764 <          result := FloatToStr(AsDouble);
765 <      SQL_INT64:
766 <        if FXSQLVAR^.sqlscale = 0 then
767 <          result := IntToStr(AsInt64)
768 <        else if FXSQLVAR^.sqlscale >= (-4) then
769 <          result := CurrToStr(AsCurrency)
770 <        else
771 <          result := FloatToStr(AsDouble);
772 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
773 <        result := FloatToStr(AsDouble);
774 <      else
775 <        IBError(ibxeInvalidDataConversion, [nil]);
776 <    end;
777 < end;
778 <
779 < function TIBXSQLVAR.GetAsVariant: Variant;
780 < begin
781 <  if IsNull then
782 <    result := NULL
783 <  { Check null, if so return a default string }
784 <  else case FXSQLVar^.sqltype and (not 1) of
785 <      SQL_ARRAY:
786 <        result := '(Array)'; {do not localize}
787 <      SQL_BLOB:
788 <        result := '(Blob)'; {do not localize}
789 <      SQL_TEXT, SQL_VARYING:
790 <        result := AsString;
791 <      SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
792 <        result := AsDateTime;
793 <      SQL_SHORT, SQL_LONG:
794 <        if FXSQLVAR^.sqlscale = 0 then
795 <          result := AsLong
796 <        else if FXSQLVAR^.sqlscale >= (-4) then
797 <          result := AsCurrency
798 <        else
799 <          result := AsDouble;
800 <      SQL_INT64:
801 <        if FXSQLVAR^.sqlscale = 0 then
802 <          IBError(ibxeInvalidDataConversion, [nil])
803 <        else if FXSQLVAR^.sqlscale >= (-4) then
804 <          result := AsCurrency
805 <        else
806 <          result := AsDouble;
807 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
808 <        result := AsDouble;
809 <      else
810 <        IBError(ibxeInvalidDataConversion, [nil]);
811 <    end;
812 < end;
813 <
814 < function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
815 < begin
816 <  result := FXSQLVAR;
817 < end;
818 <
819 < function TIBXSQLVAR.GetIsNull: Boolean;
820 < begin
821 <  result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
822 < end;
823 <
824 < function TIBXSQLVAR.GetIsNullable: Boolean;
825 < begin
826 <  result := (FXSQLVAR^.sqltype and 1 = 1);
827 < end;
828 <
829 < procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
830 < var
831 <  fs: TFileStream;
832 < begin
833 <  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
834 <  try
835 <    LoadFromStream(fs);
836 <  finally
837 <    fs.Free;
838 <  end;
839 < end;
840 <
841 < procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
842 < var
843 <  bs: TIBBlobStream;
844 < begin
845 <  bs := TIBBlobStream.Create;
846 <  try
847 <    bs.Mode := bmWrite;
848 <    bs.Database := FSQL.Database;
849 <    bs.Transaction := FSQL.Transaction;
850 <    Stream.Seek(0, soFromBeginning);
851 <    bs.LoadFromStream(Stream);
852 <    bs.Finalize;
853 <    AsQuad := bs.BlobID;
854 <  finally
855 <    bs.Free;
856 <  end;
857 < end;
858 <
859 < procedure TIBXSQLVAR.SaveToFile(const FileName: String);
860 < var
861 <  fs: TFileStream;
862 < begin
863 <  fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
864 <  try
865 <    SaveToStream(fs);
866 <  finally
867 <    fs.Free;
868 <  end;
869 < end;
870 <
871 < procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
872 < var
873 <  bs: TIBBlobStream;
874 < begin
875 <  bs := TIBBlobStream.Create;
876 <  try
877 <    bs.Mode := bmRead;
878 <    bs.Database := FSQL.Database;
879 <    bs.Transaction := FSQL.Transaction;
880 <    bs.BlobID := AsQuad;
881 <    bs.SaveToStream(Stream);
882 <  finally
883 <    bs.Free;
884 <  end;
885 < end;
886 <
887 < function TIBXSQLVAR.GetSize: Integer;
888 < begin
889 <  result := FXSQLVAR^.sqllen;
890 < end;
891 <
892 < function TIBXSQLVAR.GetSQLType: Integer;
893 < begin
894 <  result := FXSQLVAR^.sqltype and (not 1);
895 < end;
896 <
897 < procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
898 < var
899 <  xvar: TIBXSQLVAR;
900 <  i: Integer;
901 < begin
902 <  if FSQL.Database.SQLDialect < 3 then
903 <    AsDouble := Value
904 <  else
905 <  begin
906 <    if IsNullable then
907 <      IsNull := False;
908 <    for i := 0 to FParent.FCount - 1 do
909 <      if FParent.FNames[i] = FName then
910 <      begin
911 <        xvar := FParent[i];
912 <        xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
913 <        xvar.FXSQLVAR^.sqlscale := -4;
914 <        xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
915 <        IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
916 <        PCurrency(xvar.FXSQLVAR^.sqldata)^ := Value;
917 <        xvar.FModified := True;
918 <      end;
919 <  end;
920 < end;
921 <
922 < procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
923 < var
924 <  i: Integer;
925 <  xvar: TIBXSQLVAR;
926 < begin
927 <  if IsNullable then
928 <    IsNull := False;
929 <  for i := 0 to FParent.FCount - 1 do
930 <    if FParent.FNames[i] = FName then
931 <    begin
932 <      xvar := FParent[i];
933 <      xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
934 <      xvar.FXSQLVAR^.sqlscale := 0;
935 <      xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
936 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
937 <      PInt64(xvar.FXSQLVAR^.sqldata)^ := Value;
938 <      xvar.FModified := True;
939 <    end;
940 < end;
941 <
942 < procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
943 < var
944 <  i: Integer;
945 <  tm_date: TCTimeStructure;
946 <  Yr, Mn, Dy: Word;
947 <  xvar: TIBXSQLVAR;
948 < begin
949 <  if FSQL.Database.SQLDialect < 3 then
950 <  begin
951 <    AsDateTime := Value;
952 <    exit;
953 <  end;
954 <  if IsNullable then
955 <    IsNull := False;
956 <  for i := 0 to FParent.FCount - 1 do
957 <    if FParent.FNames[i] = FName then
958 <    begin
959 <      xvar := FParent[i];
960 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_DATE or (xvar.FXSQLVAR^.sqltype and 1);
961 <      DecodeDate(Value, Yr, Mn, Dy);
962 <      with tm_date do begin
963 <        tm_sec := 0;
964 <        tm_min := 0;
965 <        tm_hour := 0;
966 <        tm_mday := Dy;
967 <        tm_mon := Mn - 1;
968 <        tm_year := Yr - 1900;
969 <      end;
970 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
971 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
972 <      isc_encode_sql_date(@tm_date, PISC_DATE(xvar.FXSQLVAR^.sqldata));
973 <      xvar.FModified := True;
974 <    end;
975 < end;
976 <
977 < procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
978 < var
979 <  i: Integer;
980 <  tm_date: TCTimeStructure;
981 <  Hr, Mt, S, Ms: Word;
982 <  xvar: TIBXSQLVAR;
983 < begin
984 <  if FSQL.Database.SQLDialect < 3 then
985 <  begin
986 <    AsDateTime := Value;
987 <    exit;
988 <  end;
989 <  if IsNullable then
990 <    IsNull := False;
991 <  for i := 0 to FParent.FCount - 1 do
992 <    if FParent.FNames[i] = FName then
993 <    begin
994 <      xvar := FParent[i];
995 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_TIME or (xvar.FXSQLVAR^.sqltype and 1);
996 <      DecodeTime(Value, Hr, Mt, S, Ms);
997 <      with tm_date do begin
998 <        tm_sec := S;
999 <        tm_min := Mt;
1000 <        tm_hour := Hr;
1001 <        tm_mday := 0;
1002 <        tm_mon := 0;
1003 <        tm_year := 0;
1004 <      end;
1005 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1006 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1007 <      isc_encode_sql_time(@tm_date, PISC_TIME(xvar.FXSQLVAR^.sqldata));
1008 <      xvar.FModified := True;
1009 <    end;
1010 < end;
1011 <
1012 < procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1013 < var
1014 <  i: Integer;
1015 <  tm_date: TCTimeStructure;
1016 <  Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1017 <  xvar: TIBXSQLVAR;
1018 < begin
1019 <  if IsNullable then
1020 <    IsNull := False;
1021 <  for i := 0 to FParent.FCount - 1 do
1022 <    if FParent.FNames[i] = FName then
1023 <    begin
1024 <      xvar := FParent[i];
1025 <      xvar.FXSQLVAR^.sqltype := SQL_TIMESTAMP or (xvar.FXSQLVAR^.sqltype and 1);
1026 <      DecodeDate(Value, Yr, Mn, Dy);
1027 <      DecodeTime(Value, Hr, Mt, S, Ms);
1028 <      with tm_date do begin
1029 <        tm_sec := S;
1030 <        tm_min := Mt;
1031 <        tm_hour := Hr;
1032 <        tm_mday := Dy;
1033 <        tm_mon := Mn - 1;
1034 <        tm_year := Yr - 1900;
1035 <      end;
1036 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1037 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1038 <      isc_encode_date(@tm_date, PISC_QUAD(xvar.FXSQLVAR^.sqldata));
1039 <      xvar.FModified := True;
1040 <    end;
1041 < end;
1042 <
1043 < procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1044 < var
1045 <  i: Integer;
1046 <  xvar: TIBXSQLVAR;
1047 < begin
1048 <  if IsNullable then
1049 <    IsNull := False;
1050 <  for i := 0 to FParent.FCount - 1 do
1051 <    if FParent.FNames[i] = FName then
1052 <    begin
1053 <      xvar := FParent[i];
1054 <      xvar.FXSQLVAR^.sqltype := SQL_DOUBLE or (xvar.FXSQLVAR^.sqltype and 1);
1055 <      xvar.FXSQLVAR^.sqllen := SizeOf(Double);
1056 <      xvar.FXSQLVAR^.sqlscale := 0;
1057 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1058 <      PDouble(xvar.FXSQLVAR^.sqldata)^ := Value;
1059 <      xvar.FModified := True;
1060 <    end;
1061 < end;
1062 <
1063 < procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1064 < var
1065 <  i: Integer;
1066 <  xvar: TIBXSQLVAR;
1067 < begin
1068 <  if IsNullable then
1069 <    IsNull := False;
1070 <  for i := 0 to FParent.FCount - 1 do
1071 <    if FParent.FNames[i] = FName then
1072 <    begin
1073 <      xvar := FParent[i];
1074 <      xvar.FXSQLVAR^.sqltype := SQL_FLOAT or (xvar.FXSQLVAR^.sqltype and 1);
1075 <      xvar.FXSQLVAR^.sqllen := SizeOf(Float);
1076 <      xvar.FXSQLVAR^.sqlscale := 0;
1077 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1078 <      PSingle(xvar.FXSQLVAR^.sqldata)^ := Value;
1079 <      xvar.FModified := True;
1080 <    end;
1081 < end;
1082 <
1083 < procedure TIBXSQLVAR.SetAsLong(Value: Long);
1084 < var
1085 <  i: Integer;
1086 <  xvar: TIBXSQLVAR;
1087 < begin
1088 <  if IsNullable then
1089 <    IsNull := False;
1090 <  for i := 0 to FParent.FCount - 1 do
1091 <    if FParent.FNames[i] = FName then
1092 <    begin
1093 <      xvar := FParent[i];
1094 <      xvar.FXSQLVAR^.sqltype := SQL_LONG or (xvar.FXSQLVAR^.sqltype and 1);
1095 <      xvar.FXSQLVAR^.sqllen := SizeOf(Long);
1096 <      xvar.FXSQLVAR^.sqlscale := 0;
1097 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1098 <      PLong(xvar.FXSQLVAR^.sqldata)^ := Value;
1099 <      xvar.FModified := True;
1100 <    end;
1101 < end;
1102 <
1103 < procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1104 < var
1105 <  i: Integer;
1106 <  xvar: TIBXSQLVAR;
1107 < begin
1108 <  if IsNullable and (Value = nil) then
1109 <    IsNull := True
1110 <  else begin
1111 <    IsNull := False;
1112 <    for i := 0 to FParent.FCount - 1 do
1113 <      if FParent.FNames[i] = FName then
1114 <      begin
1115 <        xvar := FParent[i];
1116 <        xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1117 <        Move(Value^, xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1118 <        xvar.FModified := True;
1119 <      end;
1120 <  end;
1121 < end;
1122 <
1123 < procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1124 < var
1125 <  i: Integer;
1126 <  xvar: TIBXSQLVAR;
1127 < begin
1128 <  if IsNullable then
1129 <    IsNull := False;
1130 <  for i := 0 to FParent.FCount - 1 do
1131 <    if FParent.FNames[i] = FName then
1132 <    begin
1133 <      xvar := FParent[i];
1134 <      if (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1135 <         (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1136 <        IBError(ibxeInvalidDataConversion, [nil]);
1137 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1138 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1139 <      PISC_QUAD(xvar.FXSQLVAR^.sqldata)^ := Value;
1140 <      xvar.FModified := True;
1141 <    end;
1142 < end;
1143 <
1144 < procedure TIBXSQLVAR.SetAsShort(Value: Short);
1145 < var
1146 <  i: Integer;
1147 <  xvar: TIBXSQLVAR;
1148 < begin
1149 <  if IsNullable then
1150 <    IsNull := False;
1151 <  for i := 0 to FParent.FCount - 1 do
1152 <    if FParent.FNames[i] = FName then
1153 <    begin
1154 <      xvar := FParent[i];
1155 <      xvar.FXSQLVAR^.sqltype := SQL_SHORT or (xvar.FXSQLVAR^.sqltype and 1);
1156 <      xvar.FXSQLVAR^.sqllen := SizeOf(Short);
1157 <      xvar.FXSQLVAR^.sqlscale := 0;
1158 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1159 <      PShort(xvar.FXSQLVAR^.sqldata)^ := Value;
1160 <      xvar.FModified := True;
1161 <    end;
1162 < end;
1163 <
1164 < procedure TIBXSQLVAR.SetAsString(Value: String);
1165 < var
1166 <  stype: Integer;
1167 <  ss: TStringStream;
1168 <
1169 <  procedure SetStringValue;
1170 <  var
1171 <    i: Integer;
1172 <    xvar: TIBXSQLVAR;
1173 <  begin
1174 <    for i := 0 to FParent.FCount - 1 do
1175 <      if FParent.FNames[i] = FName then
1176 <      begin
1177 <        xvar := FParent[i];
1178 <        if (xvar.FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1179 <           (xvar.FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1180 <          Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen)
1181 <        else begin
1182 <          xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1183 <          xvar.FXSQLVAR^.sqllen := Length(Value);
1184 <          IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen + 1);
1185 <          if (Length(Value) > 0) then
1186 <            Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1187 <        end;
1188 <        xvar.FModified := True;
1189 <      end;
1190 <  end;
1191 <
1192 < begin
1193 <  if IsNullable then
1194 <    IsNull := False;
1195 <  stype := FXSQLVAR^.sqltype and (not 1);
1196 <  if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1197 <    SetStringValue
1198 <  else begin
1199 <    if (stype = SQL_BLOB) then
1200 <    begin
1201 <      ss := TStringStream.Create(Value);
1202 <      try
1203 <        LoadFromStream(ss);
1204 <      finally
1205 <        ss.Free;
1206 <      end;
1207 <    end
1208 <    else if Value = '' then
1209 <      IsNull := True
1210 <    else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1211 <      (stype = SQL_TYPE_TIME) then
1212 <      SetAsDateTime(StrToDateTime(Value))
1213 <    else
1214 <      SetStringValue;
1215 <  end;
1216 < end;
1217 <
1218 < procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1219 < begin
1220 <  if VarIsNull(Value) then
1221 <    IsNull := True
1222 <  else case VarType(Value) of
1223 <    varEmpty, varNull:
1224 <      IsNull := True;
1225 <    varSmallint, varInteger, varByte:
1226 <      AsLong := Value;
1227 <    varSingle, varDouble:
1228 <      AsDouble := Value;
1229 <    varCurrency:
1230 <      AsCurrency := Value;
1231 <    varBoolean:
1232 <      if Value then
1233 <        AsLong := ISC_TRUE
1234 <      else
1235 <        AsLong := ISC_FALSE;
1236 <    varDate:
1237 <      AsDateTime := Value;
1238 <    varOleStr, varString:
1239 <      AsString := Value;
1240 <    varArray:
1241 <      IBError(ibxeNotSupported, [nil]);
1242 <    varByRef, varDispatch, varError, varUnknown, varVariant:
1243 <      IBError(ibxeNotPermitted, [nil]);
1244 <  end;
1245 < end;
1246 <
1247 < procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1248 < var
1249 <  i: Integer;
1250 <  xvar: TIBXSQLVAR;
1251 <  sqlind: PShort;
1252 <  sqldata: PChar;
1253 <  local_sqllen: Integer;
1254 < begin
1255 <  for i := 0 to FParent.FCount - 1 do
1256 <    if FParent.FNames[i] = FName then
1257 <    begin
1258 <      xvar := FParent[i];
1259 <      sqlind := xvar.FXSQLVAR^.sqlind;
1260 <      sqldata := xvar.FXSQLVAR^.sqldata;
1261 <      Move(Value^, xvar.FXSQLVAR^, SizeOf(TXSQLVAR));
1262 <      xvar.FXSQLVAR^.sqlind := sqlind;
1263 <      xvar.FXSQLVAR^.sqldata := sqldata;
1264 <      if (Value^.sqltype and 1 = 1) then
1265 <      begin
1266 <        if (xvar.FXSQLVAR^.sqlind = nil) then
1267 <          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1268 <        xvar.FXSQLVAR^.sqlind^ := Value^.sqlind^;
1269 <      end
1270 <      else
1271 <        if (xvar.FXSQLVAR^.sqlind <> nil) then
1272 <          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1273 <      if ((xvar.FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1274 <        local_sqllen := xvar.FXSQLVAR^.sqllen + 2
1275 <      else
1276 <        local_sqllen := xvar.FXSQLVAR^.sqllen;
1277 <      FXSQLVAR^.sqlscale := Value^.sqlscale;
1278 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, local_sqllen);
1279 <      Move(Value^.sqldata[0], xvar.FXSQLVAR^.sqldata[0], local_sqllen);
1280 <      xvar.FModified := True;
1281 <    end;
1282 < end;
251 >   Variants, IBSQLMonitor, FBMessages, IBCustomDataSet;
252  
253 < procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
253 > procedure IBAlloc(var P; OldSize, NewSize: Integer);
254   var
255    i: Integer;
1287  xvar: TIBXSQLVAR;
1288 begin
1289  if Value then
1290  begin
1291    if not IsNullable then
1292      IsNullable := True;
1293    for i := 0 to FParent.FCount - 1 do
1294      if FParent.FNames[i] = FName then
1295      begin
1296        xvar := FParent[i];
1297        xvar.FXSQLVAR^.sqlind^ := -1;
1298        xvar.FModified := True;
1299      end;
1300  end else if ((not Value) and IsNullable) then
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        xvar.FXSQLVAR^.sqlind^ := 0;
1307        xvar.FModified := True;
1308      end;
1309  end;
1310 end;
1311
1312 procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1313 var
1314  i: Integer;
1315  xvar: TIBXSQLVAR;
1316 begin
1317  for i := 0 to FParent.FCount - 1 do
1318    if FParent.FNames[i] = FName then
1319    begin
1320      xvar := FParent[i];
1321      if (Value <> IsNullable) then
1322      begin
1323        if Value then
1324        begin
1325          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype or 1;
1326          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1327        end
1328        else
1329        begin
1330          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype and (not 1);
1331          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1332        end;
1333      end;
1334    end;
1335 end;
1336
1337 { TIBXSQLDA }
1338 constructor TIBXSQLDA.Create(Query: TIBSQL);
1339 begin
1340  inherited Create;
1341  FSQL := Query;
1342  FNames := TStringList.Create;
1343  FSize := 0;
1344  FUniqueRelationName := '';
1345 end;
1346
1347 destructor TIBXSQLDA.Destroy;
1348 var
1349  i: Integer;
1350 begin
1351  FNames.Free;
1352  if FXSQLDA <> nil then
1353  begin
1354    for i := 0 to FSize - 1 do
1355    begin
1356      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1357      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1358      FXSQLVARs[i].Free ;
1359    end;
1360    FreeMem(FXSQLDA);
1361    FXSQLDA := nil;
1362    FXSQLVARs := nil;
1363  end;
1364  inherited;
1365 end;
1366
1367 procedure TIBXSQLDA.AddName(FieldName: String; Idx: Integer);
1368 var
1369  fn: String;
1370 begin
1371  fn := FormatIdentifierValue(FSQL.Database.SQLDialect, FieldName);
1372  while FNames.Count <= Idx do
1373    FNames.Add('');
1374  FNames[Idx] := fn;
1375  FXSQLVARs[Idx].FName := fn;
1376  FXSQLVARs[Idx].FIndex := Idx;
1377 end;
1378
1379 function TIBXSQLDA.GetModified: Boolean;
1380 var
1381  i: Integer;
1382 begin
1383  result := False;
1384  for i := 0 to FCount - 1 do
1385    if FXSQLVARs[i].Modified then
1386    begin
1387      result := True;
1388      exit;
1389    end;
1390 end;
1391
1392 function TIBXSQLDA.GetNames: String;
1393 begin
1394  result := FNames.Text;
1395 end;
1396
1397 function TIBXSQLDA.GetRecordSize: Integer;
1398 begin
1399  result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1400 end;
1401
1402 function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1403 begin
1404  result := FXSQLDA;
1405 end;
1406
1407 function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1408 begin
1409  if (Idx < 0) or (Idx >= FCount) then
1410    IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1411  result := FXSQLVARs[Idx]
1412 end;
1413
1414 function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1415 begin
1416  result := GetXSQLVARByName(Idx);
1417  if result = nil then
1418    IBError(ibxeFieldNotFound, [Idx]);
1419 end;
1420
1421 function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1422 var
1423  s: String;
1424  i, Cnt: Integer;
1425 begin
1426  s := FormatIdentifierValue(FSQL.Database.SQLDialect, Idx);
1427  i := 0;
1428  Cnt := FNames.Count;
1429  while (i < Cnt) and (FNames[i] <> s) do Inc(i);
1430  if i = Cnt then
1431    result := nil
1432  else
1433    result := GetXSQLVAR(i);
1434 end;
1435
1436 procedure TIBXSQLDA.Initialize;
1437 var
1438  i, j, j_len: Integer;
1439  NamesWereEmpty: Boolean;
1440  st: String;
1441  bUnique: Boolean;
1442 begin
1443  bUnique := True;
1444  NamesWereEmpty := (FNames.Count = 0);
1445  if FXSQLDA <> nil then begin
1446    for i := 0 to FCount - 1 do begin
1447      with FXSQLVARs[i].Data^ do begin
1448        if bUnique and (String(relname) <> '') then
1449        begin
1450          if FUniqueRelationName = '' then
1451            FUniqueRelationName := String(relname)
1452          else if String(relname) <> FUniqueRelationName then
1453          begin
1454            FUniqueRelationName := '';
1455            bUnique := False;
1456          end;
1457        end;
1458        if NamesWereEmpty then begin
1459          st := String(aliasname);
1460          if st = '' then begin
1461            st := 'F_'; {do not localize}
1462            aliasname_length := 2;
1463            j := 1; j_len := 1;
1464            StrPCopy(aliasname, st + IntToStr(j));
1465          end else begin
1466            StrPCopy(aliasname, st);
1467            j := 0; j_len := 0;
1468          end;
1469          while GetXSQLVARByName(String(aliasname)) <> nil do begin
1470            Inc(j); j_len := Length(IntToStr(j));
1471            if j_len + aliasname_length > 31 then
1472              StrPCopy(aliasname,
1473                       Copy(st, 1, 31 - j_len) +
1474                       IntToStr(j))
1475            else
1476              StrPCopy(aliasname, st + IntToStr(j));
1477          end;
1478          Inc(aliasname_length, j_len);
1479          AddName(String(aliasname), i);
1480        end;
1481        case sqltype and (not 1) of
1482          SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1483          SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1484          SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1485            if (sqllen = 0) then
1486              { Make sure you get a valid pointer anyway
1487               select '' from foo }
1488              IBAlloc(sqldata, 0, 1)
1489            else
1490              IBAlloc(sqldata, 0, sqllen)
1491          end;
1492          SQL_VARYING: begin
1493            IBAlloc(sqldata, 0, sqllen + 2);
1494          end;
1495          else
1496            IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1497        end;
1498        if (sqltype and 1 = 1) then
1499          IBAlloc(sqlind, 0, SizeOf(Short))
1500        else
1501          if (sqlind <> nil) then
1502            ReallocMem(sqlind, 0);
1503      end;
1504    end;
1505  end;
1506 end;
1507
1508 procedure TIBXSQLDA.SetCount(Value: Integer);
1509 var
1510  i, OldSize: Integer;
1511  p : PXSQLVAR;
256   begin
257 <  FNames.Clear;
258 <  FCount := Value;
1515 <  if FCount = 0 then
1516 <    FUniqueRelationName := ''
1517 <  else
1518 <  begin
1519 <    if FSize > 0 then
1520 <      OldSize := XSQLDA_LENGTH(FSize)
1521 <    else
1522 <      OldSize := 0;
1523 <    if FCount > FSize then
1524 <    begin
1525 <      IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1526 <      SetLength(FXSQLVARs, FCount);
1527 <      FXSQLDA^.version := SQLDA_VERSION1;
1528 <      p := @FXSQLDA^.sqlvar[0];
1529 <      for i := 0 to FCount - 1 do
1530 <      begin
1531 <        if i >= FSize then
1532 <          FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1533 <        FXSQLVARs[i].FXSQLVAR := p;
1534 <        p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1535 < //        FNames.Add('');
1536 <      end;
1537 <      FSize := FCount;
1538 <    end;
1539 <    if FSize > 0 then
1540 <    begin
1541 <      FXSQLDA^.sqln := Value;
1542 <      FXSQLDA^.sqld := Value;
1543 <    end;
1544 <  end;
257 >  ReallocMem(Pointer(P), NewSize);
258 >  for i := OldSize to NewSize - 1 do PChar(P)[i] := #0;
259   end;
260  
261   { TIBOutputDelimitedFile }
262  
263   destructor TIBOutputDelimitedFile.Destroy;
264   begin
265 + {$IFDEF UNIX}
266 +  if FHandle <> -1 then
267 +     fpclose(FHandle);
268 + {$ELSE}
269    if FHandle <> 0 then
270    begin
271      FlushFileBuffers(FHandle);
272      CloseHandle(FHandle);
273    end;
274 + {$ENDIF}
275    inherited Destroy;
276   end;
277  
278   procedure TIBOutputDelimitedFile.ReadyFile;
279   var
280    i: Integer;
281 +  {$IFDEF UNIX}
282 +  BytesWritten: cint;
283 +  {$ELSE}
284    BytesWritten: DWORD;
285 +  {$ENDIF}
286    st: string;
287   begin
288    if FColDelimiter = '' then
289      FColDelimiter := TAB;
290    if FRowDelimiter = '' then
291      FRowDelimiter := CRLF;
292 +  {$IFDEF UNIX}
293 +  FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
294 +  {$ELSE}
295    FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
296                          FILE_ATTRIBUTE_NORMAL, 0);
297    if FHandle = INVALID_HANDLE_VALUE then
298      FHandle := 0;
299 +  {$ENDIF}
300    if FOutputTitles then
301    begin
302      for i := 0 to Columns.Count - 1 do
303        if i = 0 then
304 <        st := string(Columns[i].Data^.aliasname)
304 >        st := Columns[i].GetAliasname
305        else
306 <        st := st + FColDelimiter + string(Columns[i].Data^.aliasname);
306 >        st := st + FColDelimiter + Columns[i].GetAliasname;
307      st := st + FRowDelimiter;
308 +    {$IFDEF UNIX}
309 +    if FHandle <> -1 then
310 +       BytesWritten := FpWrite(FHandle,st[1],Length(st));
311 +    if BytesWritten = -1 then
312 +       raise Exception.Create('File Write Error');
313 +    {$ELSE}
314      WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
315 +    {$ENDIF}
316    end;
317   end;
318  
319   function TIBOutputDelimitedFile.WriteColumns: Boolean;
320   var
321    i: Integer;
322 +  {$IFDEF UNIX}
323 +  BytesWritten: cint;
324 +  {$ELSE}
325    BytesWritten: DWORD;
326 +  {$ENDIF}
327    st: string;
328   begin
329    result := False;
330 +  {$IFDEF UNIX}
331 +  if FHandle <> -1 then
332 +  {$ELSE}
333    if FHandle <> 0 then
334 +  {$ENDIF}
335    begin
336      st := '';
337      for i := 0 to Columns.Count - 1 do
# Line 1599 | Line 341 | begin
341        st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
342      end;
343      st := st + FRowDelimiter;
344 +  {$IFDEF UNIX}
345 +    BytesWritten := FpWrite(FHandle,st[1],Length(st));
346 +  {$ELSE}
347      WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
348 +  {$ENDIF}
349      if BytesWritten = DWORD(Length(st)) then
350        result := True;
351    end
# Line 1712 | Line 458 | end;
458   { TIBOutputRawFile }
459   destructor TIBOutputRawFile.Destroy;
460   begin
461 + {$IFDEF UNIX}
462 +  if FHandle <> -1 then
463 +     fpclose(FHandle);
464 + {$ELSE}
465    if FHandle <> 0 then
466    begin
467      FlushFileBuffers(FHandle);
468      CloseHandle(FHandle);
469    end;
470 + {$ENDIF}
471    inherited Destroy;
472   end;
473  
474   procedure TIBOutputRawFile.ReadyFile;
475   begin
476 +  {$IFDEF UNIX}
477 +  FHandle := FpOpen(Filename,O_WrOnly or O_Creat);
478 +  {$ELSE}
479    FHandle := CreateFile(PChar(Filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
480                          FILE_ATTRIBUTE_NORMAL, 0);
481    if FHandle = INVALID_HANDLE_VALUE then
482      FHandle := 0;
483 +  {$ENDIF}
484   end;
485  
486   function TIBOutputRawFile.WriteColumns: Boolean;
# Line 1738 | Line 493 | begin
493    begin
494      for i := 0 to Columns.Count - 1 do
495      begin
496 <      WriteFile(FHandle, Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen,
496 >      {$IFDEF UNIX}
497 >      BytesWritten := FpWrite(FHandle,Columns[i].GetAsPointer^, Columns[i].GetSize);
498 >      {$ELSE}
499 >      WriteFile(FHandle, Columns[i].GetAsPointer^, Columns[i].GetSize,
500                  BytesWritten, nil);
501 <      if BytesWritten <> DWORD(Columns[i].Data^.sqllen) then
501 >      {$ENDIF}
502 >      if BytesWritten <> DWORD(Columns[i].GetSize) then
503          exit;
504      end;
505      result := True;
# Line 1750 | Line 509 | end;
509   { TIBInputRawFile }
510   destructor TIBInputRawFile.Destroy;
511   begin
512 + {$IFDEF UNIX}
513 +  if FHandle <> -1 then
514 +     fpclose(FHandle);
515 + {$ELSE}
516    if FHandle <> 0 then
517      CloseHandle(FHandle);
518 <  inherited;
518 > {$ENDIF}
519 >  inherited Destroy;
520   end;
521  
522   function TIBInputRawFile.ReadParameters: Boolean;
# Line 1761 | Line 525 | var
525    BytesRead: DWord;
526   begin
527    result := False;
528 + {$IFDEF UNIX}
529 +  if FHandle <> -1 then
530 + {$ELSE}
531    if FHandle <> 0 then
532 + {$ENDIF}
533    begin
534      for i := 0 to Params.Count - 1 do
535      begin
536 <      ReadFile(FHandle, Params[i].Data^.sqldata^, Params[i].Data^.sqllen,
536 >      {$IFDEF UNIX}
537 >      BytesRead := FpRead(FHandle,Params[i].GetAsPointer^,Params[i].GetSize);
538 >      {$ELSE}
539 >      ReadFile(FHandle, Params[i].GetAsPointer^, Params[i].GetSize,
540                 BytesRead, nil);
541 <      if BytesRead <> DWORD(Params[i].Data^.sqllen) then
541 >      {$ENDIF}
542 >      if BytesRead <> DWORD(Params[i].GetSize) then
543          exit;
544      end;
545      result := True;
# Line 1776 | Line 548 | end;
548  
549   procedure TIBInputRawFile.ReadyFile;
550   begin
551 + {$IFDEF UNIX}
552 +  if FHandle <> -1 then
553 +     fpclose(FHandle);
554 +  FHandle := FpOpen(Filename,O_RdOnly);
555 +  if FHandle = -1 then
556 +     raise Exception.CreateFmt('Unable to open file %s',[Filename]);
557 + {$ELSE}
558    if FHandle <> 0 then
559      CloseHandle(FHandle);
560    FHandle := CreateFile(PChar(Filename), GENERIC_READ, 0, nil, OPEN_EXISTING,
561                          FILE_FLAG_SEQUENTIAL_SCAN, 0);
562    if FHandle = INVALID_HANDLE_VALUE then
563      FHandle := 0;
564 + {$ENDIF}
565   end;
566  
567   { TIBSQL }
568   constructor TIBSQL.Create(AOwner: TComponent);
569   begin
570    inherited Create(AOwner);
1791  FIBLoaded := False;
1792  CheckIBLoaded;
1793  FIBLoaded := True;
571    FGenerateParamNames := False;
572    FGoToFirstRecordOnExecute := True;
573    FBase := TIBBase.Create(Self);
574    FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
575    FBase.BeforeTransactionEnd := BeforeTransactionEnd;
1799  FBOF := False;
1800  FEOF := False;
1801  FPrepared := False;
576    FRecordCount := 0;
577    FSQL := TStringList.Create;
578    TStringList(FSQL).OnChanging := SQLChanging;
579 <  FProcessedSQL := TStringList.Create;
1806 <  FHandle := nil;
1807 <  FSQLParams := TIBXSQLDA.Create(self);
1808 <  FSQLRecord := TIBXSQLDA.Create(self);
1809 <  FSQLType := SQLUnknown;
579 >  TStringList(FSQL).OnChange := SQLChanged;
580    FParamCheck := True;
1811  FCursor := Name + RandomString(8);
581    if AOwner is TIBDatabase then
582      Database := TIBDatabase(AOwner)
583    else
# Line 1818 | Line 587 | end;
587  
588   destructor TIBSQL.Destroy;
589   begin
590 <  if FIBLoaded then
591 <  begin
592 <    if (FOpen) then
593 <      Close;
1825 <    if (FHandle <> nil) then
1826 <      FreeHandle;
1827 <    FSQL.Free;
1828 <    FProcessedSQL.Free;
1829 <    FBase.Free;
1830 <    FSQLParams.Free;
1831 <    FSQLRecord.Free;
1832 <  end;
1833 <  inherited;
590 >  FreeHandle;
591 >  FSQL.Free;
592 >  FBase.Free;
593 >  inherited Destroy;
594   end;
595  
596   procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
597   begin
598    if not Prepared then
599      Prepare;
600 <  InputObject.FParams := Self.FSQLParams;
600 >  InputObject.FParams := Self.GetSQLParams;
601    InputObject.ReadyFile;
602 <  if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
602 >  if GetSQLStatementType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
603      while InputObject.ReadParameters do
604        ExecQuery;
605   end;
# Line 1849 | Line 609 | begin
609    CheckClosed;
610    if not Prepared then
611      Prepare;
612 <  if FSQLType = SQLSelect then begin
612 >  if GetSQLStatementType = SQLSelect then begin
613      try
614        ExecQuery;
615 <      OutputObject.FColumns := Self.FSQLRecord;
615 >      OutputObject.FColumns := Self.FResults;
616        OutputObject.ReadyFile;
617        if not FGoToFirstRecordOnExecute then
618          Next;
# Line 1866 | Line 626 | end;
626  
627   procedure TIBSQL.CheckClosed;
628   begin
629 <  if FOpen then IBError(ibxeSQLOpen, [nil]);
629 >  if FResultSet <> nil  then IBError(ibxeSQLOpen, [nil]);
630   end;
631  
632   procedure TIBSQL.CheckOpen;
633   begin
634 <  if not FOpen then IBError(ibxeSQLClosed, [nil]);
634 >  if FResultSet = nil then IBError(ibxeSQLClosed, [nil]);
635   end;
636  
637   procedure TIBSQL.CheckValidStatement;
638   begin
639    FBase.CheckTransaction;
640 <  if (FHandle = nil) then
640 >  if (FStatement = nil) then
641      IBError(ibxeInvalidStatementHandle, [nil]);
642   end;
643  
644   procedure TIBSQL.Close;
1885 var
1886  isc_res: ISC_STATUS;
645   begin
646 <  try
647 <    if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then begin
648 <      isc_res := Call(
649 <                   isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
650 <                   False);
651 <      if (StatusVector^ = 1) and (isc_res > 0) and
652 <        not CheckStatusVector(
1895 <              [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
1896 <        IBDatabaseError;
1897 <    end;
1898 <  finally
1899 <    FEOF := False;
1900 <    FBOF := False;
1901 <    FOpen := False;
1902 <    FRecordCount := 0;
1903 <  end;
646 >  if FResults <> nil then
647 >    FResults.SetRetainInterfaces(false);
648 >  FResultSet := nil;
649 >  FResults := nil;
650 >  FBOF := false;
651 >  FEOF := false;
652 >  FRecordCount := 0;
653   end;
654  
655 < function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
655 > function TIBSQL.GetFieldCount: integer;
656   begin
657 <  result := 0;
658 < if Transaction <> nil then
659 <    result := Transaction.Call(ErrCode, RaiseError)
657 >  if FResults <> nil then
658 >    Result := FResults.GetCount
659 >  else
660 >  if FMetaData <> nil then
661 >    Result := FMetaData.GetCount
662 >  else
663 >    Result := 0;
664 > end;
665 >
666 > function TIBSQL.GetOpen: Boolean;
667 > begin
668 >  Result := FResultSet <> nil;
669 > end;
670 >
671 > function TIBSQL.GetPrepared: Boolean;
672 > begin
673 >  Result := (FStatement <> nil) and FStatement.IsPrepared;
674 > end;
675 >
676 > function TIBSQL.GetSQLStatementType: TIBSQLStatementTypes;
677 > begin
678 >  if FStatement = nil then
679 >    Result := SQLUnknown
680    else
681 <  if RaiseError and (ErrCode > 0) then
1913 <    IBDataBaseError;
681 >    Result := FStatement.GetSQLStatementType;
682   end;
683  
684 < function TIBSQL.Current: TIBXSQLDA;
684 > procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
685   begin
686 <  result := FSQLRecord;
686 >  if FUniqueParamNames = AValue then Exit;
687 >  FreeHandle;
688 >  FUniqueParamNames := AValue;
689   end;
690  
691   procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
692   begin
693 <  if (FHandle <> nil) then begin
1924 <    Close;
1925 <    FreeHandle;
1926 <  end;
693 >  FreeHandle;
694   end;
695  
696   procedure TIBSQL.ExecQuery;
697 +  {$IFDEF IBXQUERYSTATS}
698   var
699 <  fetch_res: ISC_STATUS;
699 >  stats: TPerfCounters;
700 >  {$ENDIF}
701 >  {$IFDEF IBXQUERYTIME}
702 > var
703 >  tmsecs: comp;
704 >  {$ENDIF}
705   begin
706    CheckClosed;
707    if not Prepared then Prepare;
708    CheckValidStatement;
709 <  case FSQLType of
710 <    SQLSelect: begin
711 <      Call(isc_dsql_execute2(StatusVector,
712 <                            TRHandle,
713 <                            @FHandle,
714 <                            Database.SQLDialect,
715 <                            FSQLParams.AsXSQLDA,
716 <                            nil), True);
717 <      Call(
718 <        isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
719 <        True);
720 <      FOpen := True;
721 <      FBOF := True;
722 <      FEOF := False;
723 <      FRecordCount := 0;
724 <      if FGoToFirstRecordOnExecute then
725 <        Next;
726 <    end;
727 <    SQLExecProcedure: begin
728 <      fetch_res := Call(isc_dsql_execute2(StatusVector,
729 <                            TRHandle,
1957 <                            @FHandle,
1958 <                            Database.SQLDialect,
1959 <                            FSQLParams.AsXSQLDA,
1960 <                            FSQLRecord.AsXSQLDA), False);
1961 <      if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
1962 <      begin
1963 <         { Sometimes a prepared stored procedure appears to get
1964 <           off sync on the server ....This code is meant to try
1965 <           to work around the problem simply by "retrying". This
1966 <           need to be reproduced and fixed.
1967 <         }
1968 <        isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
1969 <                         PChar(FProcessedSQL.Text), 1, nil);
1970 <        Call(isc_dsql_execute2(StatusVector,
1971 <                            TRHandle,
1972 <                            @FHandle,
1973 <                            Database.SQLDialect,
1974 <                            FSQLParams.AsXSQLDA,
1975 <                            FSQLRecord.AsXSQLDA), True);
1976 <      end;
1977 <    end
1978 <    else
1979 <      Call(isc_dsql_execute(StatusVector,
1980 <                           TRHandle,
1981 <                           @FHandle,
1982 <                           Database.SQLDialect,
1983 <                           FSQLParams.AsXSQLDA), True)
709 >  {$IFDEF IBXQUERYTIME}
710 >  tmsecs := TimeStampToMSecs(DateTimeToTimeStamp(Now));
711 >  {$ENDIF}
712 >  if SQLStatementType = SQLSelect then
713 >  begin
714 >    FResultSet := FStatement.OpenCursor;
715 >    FResults := FResultSet;
716 >    FResults.SetRetainInterfaces(true);
717 >    FBOF := True;
718 >    FEOF := False;
719 >    FRecordCount := 0;
720 >    if not (csDesigning in ComponentState) then
721 >      MonitorHook.SQLExecute(Self);
722 >    if FGoToFirstRecordOnExecute then
723 >      Next;
724 >  end
725 >  else
726 >  begin
727 >    FResults := FStatement.Execute;
728 >    if not (csDesigning in ComponentState) then
729 >      MonitorHook.SQLExecute(Self);
730    end;
731 <  if not (csDesigning in ComponentState) then
732 <    MonitorHook.SQLExecute(Self);
731 >  {$IFDEF IBXQUERYTIME}
732 >  writeln('Executing ',FStatement.GetSQLText,
733 >    ' Response time= ',Format('%f msecs',[TimeStampToMSecs(DateTimeToTimeStamp(Now)) - tmsecs]));
734 >  {$ENDIF}
735 >  {$IFDEF IBXQUERYSTATS}
736 >  if FStatement.GetPerfStatistics(stats) then
737 >    writeln('Executing ',FStatement.GetSQLText,
738 >    ' Elapsed time= ', FormatFloat('#0.000',stats[psRealTime]/1000),' sec');
739 >  {$ENDIF}
740 >  FBase.DoAfterExecQuery(self);
741 > end;
742 >
743 > function TIBSQL.HasField(FieldName: String): boolean;
744 > begin
745 >  if FResults = nil then
746 >    IBError(ibxeNoFieldAccess,[nil]);
747 >
748 >  Result := FResults.ByName(FieldName) <> nil;
749   end;
750  
751   function TIBSQL.GetEOF: Boolean;
752   begin
753 <  result := FEOF or not FOpen;
753 >  result := FEOF or (FResultSet = nil);
754   end;
755  
756 < function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
1995 < var
1996 <  i: Integer;
756 > function TIBSQL.FieldByName(FieldName: String): ISQLData;
757   begin
758 <  i := GetFieldIndex(FieldName);
759 <  if (i < 0) then
758 >  if FResults = nil then
759 >    IBError(ibxeNoFieldAccess,[nil]);
760 >
761 >  Result := FResults.ByName(FieldName);
762 >
763 >  if Result = nil then
764      IBError(ibxeFieldNotFound, [FieldName]);
2001  result := GetFields(i);
765   end;
766  
767 < function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
767 > function TIBSQL.ParamByName(ParamName: String): ISQLParam;
768   begin
769 <  if (Idx < 0) or (Idx >= FSQLRecord.Count) then
769 >  Result := Params.ByName(ParamName);
770 > end;
771 >
772 > function TIBSQL.GetFields(const Idx: Integer): ISQLData;
773 > begin
774 >  if FResults = nil then
775 >    IBError(ibxeNoFieldAccess,[nil]);
776 >
777 >  if (Idx < 0) or (Idx >= FResults.GetCount) then
778      IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
779 <  result := FSQLRecord[Idx];
779 >  result := FResults[Idx];
780   end;
781  
782   function TIBSQL.GetFieldIndex(FieldName: String): Integer;
783 + var Field: IColumnMetaData;
784   begin
785 <  if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
785 >  if FMetaData = nil then
786 >    IBError(ibxeNoFieldAccess,[nil]);
787 >
788 >  Field := FMetaData.ByName(FieldName);
789 >
790 >  if Field = nil then
791      result := -1
792    else
793 <    result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
793 >    result := Field.GetIndex;
794   end;
795  
796 < function TIBSQL.Next: TIBXSQLDA;
2020 < var
2021 <  fetch_res: ISC_STATUS;
796 > function TIBSQL.Next: boolean;
797   begin
798 <  result := nil;
799 <  if not FEOF then begin
798 >  result := false;
799 >  if not FEOF then
800 >  begin
801      CheckOpen;
802 <    { Go to the next record... }
803 <    fetch_res :=
804 <      Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
805 <    if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then begin
806 <      FEOF := True;
807 <    end else if (fetch_res > 0) then begin
808 <      try
809 <        IBDataBaseError;
810 <      except
2035 <        Close;
2036 <        raise;
2037 <      end;
2038 <    end else begin
802 >    try
803 >      Result := FResultSet.FetchNext;
804 >    except
805 >      Close;
806 >      raise;
807 >    end;
808 >
809 >    if Result then
810 >    begin
811        Inc(FRecordCount);
812        FBOF := False;
813 <      result := FSQLRecord;
814 <    end;
813 >    end
814 >    else
815 >      FEOF := true;
816 >
817      if not (csDesigning in ComponentState) then
818        MonitorHook.SQLFetch(Self);
819    end;
820   end;
821  
822   procedure TIBSQL.FreeHandle;
2049 var
2050  isc_res: ISC_STATUS;
823   begin
824 <  try
825 <    { The following two lines merely set the SQLDA count
826 <     variable FCount to 0, but do not deallocate
827 <     That way the allocations can be reused for
828 <     a new query sring in the same SQL instance }
829 <    FSQLRecord.Count := 0;
830 <    FSQLParams.Count := 0;
831 <    if FHandle <> nil then begin
2060 <      isc_res :=
2061 <        Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2062 <      if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2063 <        IBDataBaseError;
2064 <    end;
2065 <  finally
2066 <    FPrepared := False;
2067 <    FHandle := nil;
2068 <  end;
824 >  if FStatement <> nil then
825 >    FStatement.SetRetainInterfaces(false);
826 >  Close;
827 >  FStatement := nil;
828 >  FResults := nil;
829 >  FResultSet := nil;
830 >  FMetaData := nil;
831 >  FSQLParams := nil;
832   end;
833  
834   function TIBSQL.GetDatabase: TIBDatabase;
# Line 2073 | Line 836 | begin
836    result := FBase.Database;
837   end;
838  
2076 function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2077 begin
2078  result := FBase.DBHandle;
2079 end;
2080
839   function TIBSQL.GetPlan: String;
2082 var
2083  result_buffer: array[0..16384] of Char;
2084  result_length, i: Integer;
2085  info_request: Char;
840   begin
841    if (not Prepared) or
842 <     (not (FSQLType in [SQLSelect, SQLSelectForUpdate,
842 >     (not (GetSQLStatementType in [SQLSelect, SQLSelectForUpdate,
843         {TODO: SQLExecProcedure, }
844         SQLUpdate, SQLDelete])) then
845      result := ''
846 <  else begin
847 <    info_request := Char(isc_info_sql_get_plan);
2094 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2095 <                           SizeOf(result_buffer), result_buffer), True);
2096 <    if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2097 <      IBError(ibxeUnknownError, [nil]);
2098 <    result_length := isc_vax_integer(@result_buffer[1], 2);
2099 <    SetString(result, nil, result_length);
2100 <    for i := 1 to result_length do
2101 <      result[i] := result_buffer[i + 2];
2102 <    result := Trim(result);
2103 <  end;
846 >  else
847 >    Result := FStatement.GetPlan;
848   end;
849  
850   function TIBSQL.GetRecordCount: Integer;
851   begin
852 <  result := FRecordCount;
852 >  Result := FRecordCount;
853   end;
854  
855 < function TIBSQL.GetRowsAffected: integer;
855 > function TIBSQL.GetRowsAffected: Integer;
856   var
857 <  result_buffer: array[0..1048] of Char;
2114 <  info_request: Char;
857 >  SelectCount, InsertCount, UpdateCount, DeleteCount: integer;
858   begin
859    if not Prepared then
860 <    result := -1
861 <  else begin
862 <    info_request := Char(isc_info_sql_records);
863 <    if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
864 <                         SizeOf(result_buffer), result_buffer) > 0 then
2122 <      IBDatabaseError;
2123 <    if (result_buffer[0] <> Char(isc_info_sql_records)) then
2124 <      result := -1
2125 <    else
2126 <    case SQLType of
2127 <    SQLUpdate:   Result := isc_vax_integer(@result_buffer[6], 4);
2128 <    SQLDelete:   Result := isc_vax_integer(@result_buffer[13], 4);
2129 <    SQLInsert:   Result := isc_vax_integer(@result_buffer[27], 4);
2130 <    else         Result := -1 ;
2131 <    end ;
860 >    Result := -1
861 >  else
862 >  begin
863 >    FStatement.GetRowsAffected(SelectCount, InsertCount, UpdateCount, DeleteCount);
864 >    Result := InsertCount + UpdateCount + DeleteCount;
865    end;
866   end;
867  
868 < function TIBSQL.GetSQLParams: TIBXSQLDA;
868 > function TIBSQL.GetSQLParams: ISQLParams;
869   begin
870    if not Prepared then
871      Prepare;
872 <  result := FSQLParams;
872 >  result := Statement.SQLParams;
873   end;
874  
875   function TIBSQL.GetTransaction: TIBTransaction;
# Line 2144 | Line 877 | begin
877    result := FBase.Transaction;
878   end;
879  
2147 function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2148 begin
2149  result := FBase.TRHandle;
2150 end;
2151
2152 {
2153 Preprocess SQL
2154 Using FSQL, process the typed SQL and put the process SQL
2155 in FProcessedSQL and parameter names in FSQLParams
2156 }
2157 procedure TIBSQL.PreprocessSQL;
2158 var
2159  cCurChar, cNextChar, cQuoteChar: Char;
2160  sSQL, sProcessedSQL, sParamName: String;
2161  i, iLenSQL, iSQLPos: Integer;
2162  iCurState, iCurParamState: Integer;
2163  iParamSuffix: Integer;
2164  slNames: TStrings;
2165
2166 const
2167  DefaultState = 0;
2168  CommentState = 1;
2169  QuoteState = 2;
2170  ParamState = 3;
2171  ParamDefaultState = 0;
2172  ParamQuoteState = 1;
2173
2174  procedure AddToProcessedSQL(cChar: Char);
2175  begin
2176    sProcessedSQL[iSQLPos] := cChar;
2177    Inc(iSQLPos);
2178  end;
2179
2180 begin
2181  slNames := TStringList.Create;
2182  try
2183    { Do some initializations of variables }
2184    iParamSuffix := 0;
2185    cQuoteChar := '''';
2186    sSQL := FSQL.Text;
2187    iLenSQL := Length(sSQL);
2188    SetString(sProcessedSQL, nil, iLenSQL + 1);
2189    i := 1;
2190    iSQLPos := 1;
2191    iCurState := DefaultState;
2192    iCurParamState := ParamDefaultState;
2193    { Now, traverse through the SQL string, character by character,
2194     picking out the parameters and formatting correctly for InterBase }
2195    while (i <= iLenSQL) do begin
2196      { Get the current token and a look-ahead }
2197      cCurChar := sSQL[i];
2198      if i = iLenSQL then
2199        cNextChar := #0
2200      else
2201        cNextChar := sSQL[i + 1];
2202      { Now act based on the current state }
2203      case iCurState of
2204        DefaultState: begin
2205          case cCurChar of
2206            '''', '"': begin
2207              cQuoteChar := cCurChar;
2208              iCurState := QuoteState;
2209            end;
2210            '?', ':': begin
2211              iCurState := ParamState;
2212              AddToProcessedSQL('?');
2213            end;
2214            '/': if (cNextChar = '*') then begin
2215              AddToProcessedSQL(cCurChar);
2216              Inc(i);
2217              iCurState := CommentState;
2218            end;
2219          end;
2220        end;
2221        CommentState: begin
2222          if (cNextChar = #0) then
2223            IBError(ibxeSQLParseError, [SEOFInComment])
2224          else if (cCurChar = '*') then begin
2225            if (cNextChar = '/') then
2226              iCurState := DefaultState;
2227          end;
2228        end;
2229        QuoteState: begin
2230          if cNextChar = #0 then
2231            IBError(ibxeSQLParseError, [SEOFInString])
2232          else if (cCurChar = cQuoteChar) then begin
2233            if (cNextChar = cQuoteChar) then begin
2234              AddToProcessedSQL(cCurChar);
2235              Inc(i);
2236            end else
2237              iCurState := DefaultState;
2238          end;
2239        end;
2240        ParamState:
2241        begin
2242          { collect the name of the parameter }
2243          if iCurParamState = ParamDefaultState then
2244          begin
2245            if cCurChar = '"' then
2246              iCurParamState := ParamQuoteState
2247            else if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2248                sParamName := sParamName + cCurChar
2249            else if FGenerateParamNames then
2250            begin
2251              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2252              Inc(iParamSuffix);
2253              iCurState := DefaultState;
2254              slNames.Add(sParamName);
2255              sParamName := '';
2256            end
2257            else
2258              IBError(ibxeSQLParseError, [SParamNameExpected]);
2259          end
2260          else begin
2261            { determine if Quoted parameter name is finished }
2262            if cCurChar = '"' then
2263            begin
2264              Inc(i);
2265              slNames.Add(sParamName);
2266              SParamName := '';
2267              iCurParamState := ParamDefaultState;
2268              iCurState := DefaultState;
2269            end
2270            else
2271              sParamName := sParamName + cCurChar
2272          end;
2273          { determine if the unquoted parameter name is finished }
2274          if (iCurParamState <> ParamQuoteState) and
2275            (iCurState <> DefaultState) then
2276          begin
2277            if not (cNextChar in ['A'..'Z', 'a'..'z',
2278                                  '0'..'9', '_', '$']) then begin
2279              Inc(i);
2280              iCurState := DefaultState;
2281              slNames.Add(sParamName);
2282              sParamName := '';
2283            end;
2284          end;
2285        end;
2286      end;
2287      if iCurState <> ParamState then
2288        AddToProcessedSQL(sSQL[i]);
2289      Inc(i);
2290    end;
2291    AddToProcessedSQL(#0);
2292    FSQLParams.Count := slNames.Count;
2293    for i := 0 to slNames.Count - 1 do
2294      FSQLParams.AddName(slNames[i], i);
2295    FProcessedSQL.Text := sProcessedSQL;
2296  finally
2297    slNames.Free;
2298  end;
2299 end;
2300
880   procedure TIBSQL.SetDatabase(Value: TIBDatabase);
881   begin
882 +  if Value = FBase.Database then Exit;
883    FBase.Database := Value;
884 +  FreeHandle;
885   end;
886  
887   procedure TIBSQL.Prepare;
2307 var
2308  stmt_len: Integer;
2309  res_buffer: array[0..7] of Char;
2310  type_item: Char;
888   begin
889    CheckClosed;
890    FBase.CheckDatabase;
891    FBase.CheckTransaction;
892 <  if FPrepared then
892 >  Close;
893 >  if Prepared then
894      exit;
895    if (FSQL.Text = '') then
896      IBError(ibxeEmptyQuery, [nil]);
897 +
898 +  if FStatement <> nil then
899 +    FStatement.Prepare(Transaction.TransactionIntf)
900 +  else
901    if not ParamCheck then
902 <    FProcessedSQL.Text := FSQL.Text
902 >    FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
903    else
904 <    PreprocessSQL;
905 <  if (FProcessedSQL.Text = '') then
906 <    IBError(ibxeEmptyQuery, [nil]);
907 <  try
908 <    Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
909 <                                    @FHandle), True);
910 <    Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
911 <               PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
912 <    { After preparing the statement, query the stmt type and possibly
913 <      create a FSQLRecord "holder" }
914 <    { Get the type of the statement }
2333 <    type_item := Char(isc_info_sql_stmt_type);
2334 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2335 <                         SizeOf(res_buffer), res_buffer), True);
2336 <    if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2337 <      IBError(ibxeUnknownError, [nil]);
2338 <    stmt_len := isc_vax_integer(@res_buffer[1], 2);
2339 <    FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2340 <    { Done getting the type }
2341 <    case FSQLType of
2342 <      SQLGetSegment,
2343 <      SQLPutSegment,
2344 <      SQLStartTransaction: begin
2345 <        FreeHandle;
2346 <        IBError(ibxeNotPermitted, [nil]);
2347 <      end;
2348 <      SQLCommit,
2349 <      SQLRollback,
2350 <      SQLDDL, SQLSetGenerator,
2351 <      SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2352 <      SQLExecProcedure: begin
2353 <        { We already know how many inputs there are, so... }
2354 <        if (FSQLParams.FXSQLDA <> nil) and
2355 <           (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2356 <                                        FSQLParams.FXSQLDA), False) > 0) then
2357 <          IBDataBaseError;
2358 <        FSQLParams.Initialize;
2359 <        if FSQLType in [SQLSelect, SQLSelectForUpdate,
2360 <                        SQLExecProcedure] then begin
2361 <          { Allocate an initial output descriptor (with one column) }
2362 <          FSQLRecord.Count := 1;
2363 <          { Using isc_dsql_describe, get the right size for the columns... }
2364 <          Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2365 <          if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2366 <            FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2367 <            Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2368 <          end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2369 <            FSQLRecord.Count := 0;
2370 <          FSQLRecord.Initialize;
2371 <        end;
2372 <      end;
2373 <    end;
2374 <    FPrepared := True;
2375 <    if not (csDesigning in ComponentState) then
904 >    FStatement := Database.Attachment.PrepareWithNamedParameters(
905 >                     Transaction.TransactionIntf,
906 >                     SQL.Text,
907 >                     GenerateParamNames);
908 >  {$IFDEF IBXQUERYSTATS}
909 >  FStatement.EnableStatistics(true);
910 >  {$ENDIF}
911 >  FMetaData := FStatement.GetMetaData;
912 >  FSQLParams := FStatement.GetSQLParams;
913 >  FStatement.SetRetainInterfaces(true);
914 >  if not (csDesigning in ComponentState) then
915        MonitorHook.SQLPrepare(Self);
2377  except
2378    on E: Exception do begin
2379      if (FHandle <> nil) then
2380        FreeHandle;
2381      raise;
2382    end;
2383  end;
916   end;
917  
918   function TIBSQL.GetUniqueRelationName: String;
919   begin
920 <  if FPrepared and (FSQLType = SQLSelect) then
921 <    result := FSQLRecord.UniqueRelationName
920 >  if Prepared and (GetSQLStatementType = SQLSelect) then
921 >    result := FMetaData.GetUniqueRelationName
922    else
923      result := '';
924   end;
# Line 2406 | Line 938 | end;
938  
939   procedure TIBSQL.SetTransaction(Value: TIBTransaction);
940   begin
941 +  if FBase.Transaction = Value then Exit;
942 +  FreeHandle;
943    FBase.Transaction := Value;
944   end;
945  
# Line 2413 | Line 947 | procedure TIBSQL.SQLChanging(Sender: TOb
947   begin
948    if Assigned(OnSQLChanging) then
949      OnSQLChanging(Self);
950 <  if FHandle <> nil then FreeHandle;
950 >
951 >  FreeHandle;
952   end;
953  
954 < procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
954 > procedure TIBSQL.SQLChanged(Sender: TObject);
955   begin
956 <  if (FOpen) then
957 <    Close;
956 >  if assigned(OnSQLChanged) then
957 >    OnSQLChanged(self);
958 > end;
959 >
960 > procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
961 >  Action: TTransactionAction);
962 > begin
963 >  if not (Owner is TIBCustomDataSet) then
964 >    FreeHandle;
965   end;
966  
967   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines