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

Comparing ibx/trunk/runtime/IBSQL.pas (file contents):
Revision 5 by tony, Fri Feb 18 16:26:16 2011 UTC vs.
Revision 49 by tony, Thu Feb 2 16:20:12 2017 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   {$Mode Delphi}
37  
38 + {$codepage UTF8}
39 +
40 + (* Define IBXQUERYSTATS to write to stdout a summary of query execution
41 +   statistics each time a query is executed
42 +
43 +   Define IBXQUERYTIME to write to stdout The local execution time for each
44 +   query
45 +   *)
46 +
47 + { $DEFINE IBXQUERYSTATS}
48 + { $DEFINE IBXQUERYTIME}
49 +
50   interface
51  
52   uses
53 < {$IFDEF LINUX }
37 <  baseunix,unix,
38 < {$ELSE}
39 < {$DEFINE HAS_SQLMONITOR}
53 > {$IFDEF WINDOWS }
54    Windows,
55 + {$ELSE}
56 +  baseunix, unix,
57   {$ENDIF}
58 <  SysUtils, Classes, Forms, Controls, IBHeader,
43 <  IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
58 >  SysUtils, Classes, IBExternals, IB, IBDatabase, IBUtils;
59  
60   type
46  TIBSQL = class;
47  TIBXSQLDA = class;
48  
49  { TIBXSQLVAR }
50  TIBXSQLVAR = class(TObject)
51  private
52    FParent: TIBXSQLDA;
53    FSQL: TIBSQL;
54    FIndex: Integer;
55    FModified: Boolean;
56    FName: String;
57    FXSQLVAR: PXSQLVAR;       { Point to the PXSQLVAR in the owner object }
58
59    function AdjustScale(Value: Int64; Scale: Integer): Double;
60    function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
61    function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
62    function GetAsCurrency: Currency;
63    function GetAsInt64: Int64;
64    function GetAsDateTime: TDateTime;
65    function GetAsDouble: Double;
66    function GetAsFloat: Float;
67    function GetAsLong: Long;
68    function GetAsPointer: Pointer;
69    function GetAsQuad: TISC_QUAD;
70    function GetAsShort: Short;
71    function GetAsString: String;
72    function GetAsVariant: Variant;
73    function GetAsXSQLVAR: PXSQLVAR;
74    function GetIsNull: Boolean;
75    function GetIsNullable: Boolean;
76    function GetSize: Integer;
77    function GetSQLType: Integer;
78    procedure SetAsCurrency(Value: Currency);
79    procedure SetAsInt64(Value: Int64);
80    procedure SetAsDate(Value: TDateTime);
81    procedure SetAsTime(Value: TDateTime);
82    procedure SetAsDateTime(Value: TDateTime);
83    procedure SetAsDouble(Value: Double);
84    procedure SetAsFloat(Value: Float);
85    procedure SetAsLong(Value: Long);
86    procedure SetAsPointer(Value: Pointer);
87    procedure SetAsQuad(Value: TISC_QUAD);
88    procedure SetAsShort(Value: Short);
89    procedure SetAsString(Value: String);
90    procedure SetAsVariant(Value: Variant);
91    procedure SetAsXSQLVAR(Value: PXSQLVAR);
92    procedure SetIsNull(Value: Boolean);
93    procedure SetIsNullable(Value: Boolean);
94  public
95    constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
96    procedure Assign(Source: TIBXSQLVAR);
97    procedure LoadFromFile(const FileName: String);
98    procedure LoadFromStream(Stream: TStream);
99    procedure SaveToFile(const FileName: String);
100    procedure SaveToStream(Stream: TStream);
101    property AsDate: TDateTime read GetAsDateTime write SetAsDate;
102    property AsTime: TDateTime read GetAsDateTime write SetAsTime;
103    property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
104    property AsDouble: Double read GetAsDouble write SetAsDouble;
105    property AsFloat: Float read GetAsFloat write SetAsFloat;
106    property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
107    property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
108    property AsInteger: Integer read GetAsLong write SetAsLong;
109    property AsLong: Long read GetAsLong write SetAsLong;
110    property AsPointer: Pointer read GetAsPointer write SetAsPointer;
111    property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
112    property AsShort: Short read GetAsShort write SetAsShort;
113    property AsString: String read GetAsString write SetAsString;
114    property AsVariant: Variant read GetAsVariant write SetAsVariant;
115    property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
116    property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
117    property IsNull: Boolean read GetIsNull write SetIsNull;
118    property IsNullable: Boolean read GetIsNullable write SetIsNullable;
119    property Index: Integer read FIndex;
120    property Modified: Boolean read FModified write FModified;
121    property Name: String read FName;
122    property Size: Integer read GetSize;
123    property SQLType: Integer read GetSQLType;
124    property Value: Variant read GetAsVariant write SetAsVariant;
125  end;
126
127  TIBXSQLVARArray = Array of TIBXSQLVAR;
128
129  { TIBXSQLVAR }
130  TIBXSQLDA = class(TObject)
131  protected
132    FSQL: TIBSQL;
133    FCount: Integer;
134    FNames: TStrings;
135    FSize: Integer;
136    FXSQLDA: PXSQLDA;
137    FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
138    FUniqueRelationName: String;
139    function GetModified: Boolean;
140    function GetNames: String;
141    function GetRecordSize: Integer;
142    function GetXSQLDA: PXSQLDA;
143    function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
144    function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
145    procedure Initialize;
146    procedure SetCount(Value: Integer);
147  public
148    constructor Create(Query: TIBSQL);
149    destructor Destroy; override;
150    procedure AddName(FieldName: String; Idx: Integer);
151    function ByName(Idx: String): TIBXSQLVAR;
152    property AsXSQLDA: PXSQLDA read GetXSQLDA;
153    property Count: Integer read FCount write SetCount;
154    property Modified: Boolean read GetModified;
155    property Names: String read GetNames;
156    property RecordSize: Integer read GetRecordSize;
157    property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
158    property UniqueRelationName: String read FUniqueRelationName;
159  end;
160
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 186 | Line 86 | type
86    { TIBOutputDelimitedFile }
87    TIBOutputDelimitedFile = class(TIBBatchOutput)
88    protected
89 <  {$IFDEF LINUX}
89 >  {$IFDEF UNIX}
90      FHandle: cint;
91    {$ELSE}
92      FHandle: THandle;
# Line 229 | Line 129 | type
129    { TIBOutputRawFile }
130    TIBOutputRawFile = class(TIBBatchOutput)
131    protected
132 <  {$IFDEF LINUX}
132 >  {$IFDEF UNIX}
133      FHandle: cint;
134    {$ELSE}
135      FHandle: THandle;
# Line 243 | Line 143 | type
143    { TIBInputRawFile }
144    TIBInputRawFile = class(TIBBatchInput)
145    protected
146 <   {$IFDEF LINUX}
146 >   {$IFDEF UNIX}
147      FHandle: cint;
148    {$ELSE}
149      FHandle: THandle;
# Line 255 | Line 155 | type
155    end;
156  
157       { TIBSQL }
258  TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
259                  SQLUpdate, SQLDelete, SQLDDL,
260                  SQLGetSegment, SQLPutSegment,
261                  SQLExecProcedure, SQLStartTransaction,
262                  SQLCommit, SQLRollback,
263                  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? }
271 <    FEOF,                          { At EOF? }
272 <    FGoToFirstRecordOnExecute,     { Automatically position record on first record after executing }
273 <    FOpen,                         { Is a cursor open? }
274 <    FPrepared: Boolean;            { Has the query been prepared? }
175 >    FGoToFirstRecordOnExecute: boolean;     { Automatically position record on first record after executing }
176      FRecordCount: Integer;         { How many records have been read so far? }
276    FCursor: String;               { Cursor name...}
277    FHandle: TISC_STMT_HANDLE;     { Once prepared, this accesses the SQL Query }
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 }
283 <    FSQLRecord: TIBXSQLDA;         { The current record }
284 <    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;
288    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;
297    function GetTRHandle: PISC_TR_HANDLE;
298    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);
309    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;
314    function Current: TIBXSQLDA;
208      procedure ExecQuery;
209 <    function FieldByName(FieldName: String): TIBXSQLVAR;
210 <    function ParamByName(ParamName: 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;
323    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;
334 <    property TRHandle: PISC_TR_HANDLE read GetTRHandle;
335 <    property Handle: TISC_STMT_HANDLE read FHandle;
336 <    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 344 | 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, Variants {$IFDEF HAS_SQLMONITOR}, IBSQLMonitor {$ENDIF};
353 <
354 < { TIBXSQLVAR }
355 < constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
356 < begin
357 <  inherited Create;
358 <  FParent := Parent;
359 <  FSQL := Query;
360 < end;
361 <
362 < procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
363 < var
364 <  szBuff: PChar;
365 <  s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
366 <  bSourceBlob, bDestBlob: Boolean;
367 <  iSegs, iMaxSeg, iSize: Long;
368 <  iBlobType: Short;
369 < begin
370 <  szBuff := nil;
371 <  bSourceBlob := True;
372 <  bDestBlob := True;
373 <  s_bhandle := nil;
374 <  d_bhandle := nil;
375 <  try
376 <    if (Source.IsNull) then
377 <    begin
378 <      IsNull := True;
379 <      exit;
380 <    end
381 <    else
382 <      if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
383 <         (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
384 <        exit; { arrays not supported }
385 <    if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
386 <       (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
387 <    begin
388 <      AsXSQLVAR := Source.AsXSQLVAR;
389 <      exit;
390 <    end
391 <    else
392 <      if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
393 <      begin
394 <        szBuff := nil;
395 <        IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
396 <        Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
397 <        bSourceBlob := False;
398 <        iSize := Source.FXSQLVAR^.sqllen;
399 <      end
400 <      else
401 <        if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
402 <          bDestBlob := False;
403 <
404 <    if bSourceBlob then
405 <    begin
406 <      { read the blob }
407 <      Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
408 <        Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
409 <        0, nil), True);
410 <      try
411 <        IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
412 <          iBlobType);
413 <        szBuff := nil;
414 <        IBAlloc(szBuff, 0, iSize);
415 <        IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
416 <      finally
417 <        Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
418 <      end;
419 <    end;
420 <
421 <    if bDestBlob then
422 <    begin
423 <      { write the blob }
424 <      FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
425 <        FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
426 <        0, nil), True);
427 <      try
428 <        IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
429 <      finally
430 <        FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
431 <      end;
432 <    end
433 <    else
434 <    begin
435 <      { just copy the buffer }
436 <      FXSQLVAR.sqltype := SQL_TEXT;
437 <      FXSQLVAR.sqllen := iSize;
438 <      IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
439 <      Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
440 <    end;
441 <  finally
442 <    FreeMem(szBuff);
443 <  end;
444 < end;
445 <
446 < function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
447 < var
448 <  Scaling : Int64;
449 <  i: Integer;
450 <  Val: Double;
451 < begin
452 <  Scaling := 1; Val := Value;
453 <  if Scale > 0 then
454 <  begin
455 <    for i := 1 to Scale do
456 <      Scaling := Scaling * 10;
457 <    result := Val * Scaling;
458 <  end
459 <  else
460 <    if Scale < 0 then
461 <    begin
462 <      for i := -1 downto Scale do
463 <        Scaling := Scaling * 10;
464 <      result := Val / Scaling;
465 <    end
466 <    else
467 <      result := Val;
468 < end;
469 <
470 < function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
471 < var
472 <  Scaling : Int64;
473 <  i: Integer;
474 <  Val: Int64;
475 < begin
476 <  Scaling := 1; Val := Value;
477 <  if Scale > 0 then begin
478 <    for i := 1 to Scale do Scaling := Scaling * 10;
479 <    result := Val * Scaling;
480 <  end else if Scale < 0 then begin
481 <    for i := -1 downto Scale do Scaling := Scaling * 10;
482 <    result := Val div Scaling;
483 <  end else
484 <    result := Val;
485 < end;
486 <
487 < function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
488 < var
489 <  Scaling : Int64;
490 <  i : Integer;
491 <  FractionText, PadText, CurrText: string;
492 < begin
493 <  Result := 0;
494 <  Scaling := 1;
495 <  if Scale > 0 then
496 <  begin
497 <    for i := 1 to Scale do
498 <      Scaling := Scaling * 10;
499 <    result := Value * Scaling;
500 <  end
501 <  else
502 <    if Scale < 0 then
503 <    begin
504 <      for i := -1 downto Scale do
505 <        Scaling := Scaling * 10;
506 <      FractionText := IntToStr(abs(Value mod Scaling));
507 <      for i := Length(FractionText) to -Scale -1 do
508 <        PadText := '0' + PadText;
509 <      if Value < 0 then
510 <        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
511 <      else
512 <        CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
513 <      try
514 <        result := StrToCurr(CurrText);
515 <      except
516 <        on E: Exception do
517 <          IBError(ibxeInvalidDataConversion, [nil]);
518 <      end;
519 <    end
520 <    else
521 <      result := Value;
522 < end;
523 <
524 < function TIBXSQLVAR.GetAsCurrency: Currency;
525 < begin
526 <  result := 0;
527 <  if FSQL.Database.SQLDialect < 3 then
528 <    result := GetAsDouble
529 <  else begin
530 <    if not IsNull then
531 <      case FXSQLVAR^.sqltype and (not 1) of
532 <        SQL_TEXT, SQL_VARYING: begin
533 <          try
534 <            result := StrtoCurr(AsString);
535 <          except
536 <            on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
537 <          end;
538 <        end;
539 <        SQL_SHORT:
540 <          result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
541 <                                      FXSQLVAR^.sqlscale);
542 <        SQL_LONG:
543 <          result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
544 <                                      FXSQLVAR^.sqlscale);
545 <        SQL_INT64:
546 <          result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
547 <                                      FXSQLVAR^.sqlscale);
548 <        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
549 <          result := Trunc(AsDouble);
550 <        else
551 <          IBError(ibxeInvalidDataConversion, [nil]);
552 <      end;
553 <    end;
554 < end;
555 <
556 < function TIBXSQLVAR.GetAsInt64: Int64;
557 < begin
558 <  result := 0;
559 <  if not IsNull then
560 <    case FXSQLVAR^.sqltype and (not 1) of
561 <      SQL_TEXT, SQL_VARYING: begin
562 <        try
563 <          result := StrToInt64(AsString);
564 <        except
565 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
566 <        end;
567 <      end;
568 <      SQL_SHORT:
569 <        result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
570 <                                    FXSQLVAR^.sqlscale);
571 <      SQL_LONG:
572 <        result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
573 <                                    FXSQLVAR^.sqlscale);
574 <      SQL_INT64:
575 <        result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
576 <                                    FXSQLVAR^.sqlscale);
577 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
578 <        result := Trunc(AsDouble);
579 <      else
580 <        IBError(ibxeInvalidDataConversion, [nil]);
581 <    end;
582 < end;
583 <
584 < function TIBXSQLVAR.GetAsDateTime: TDateTime;
585 < var
586 <  tm_date: TCTimeStructure;
587 < begin
588 <  result := 0;
589 <  if not IsNull then
590 <    case FXSQLVAR^.sqltype and (not 1) of
591 <      SQL_TEXT, SQL_VARYING: begin
592 <        try
593 <          result := StrToDate(AsString);
594 <        except
595 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
596 <        end;
597 <      end;
598 <      SQL_TYPE_DATE: begin
599 <        isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
600 <        try
601 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
602 <                               Word(tm_date.tm_mday));
603 <        except
604 <          on E: EConvertError do begin
605 <            IBError(ibxeInvalidDataConversion, [nil]);
606 <          end;
607 <        end;
608 <      end;
609 <      SQL_TYPE_TIME: begin
610 <        isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
611 <        try
612 <          result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
613 <                               Word(tm_date.tm_sec), 0)
614 <        except
615 <          on E: EConvertError do begin
616 <            IBError(ibxeInvalidDataConversion, [nil]);
617 <          end;
618 <        end;
619 <      end;
620 <      SQL_TIMESTAMP: begin
621 <        isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
622 <        try
623 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
624 <                              Word(tm_date.tm_mday));
625 <          if result >= 0 then
626 <            result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
627 <                                          Word(tm_date.tm_sec), 0)
628 <          else
629 <            result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
630 <                                          Word(tm_date.tm_sec), 0)
631 <        except
632 <          on E: EConvertError do begin
633 <            IBError(ibxeInvalidDataConversion, [nil]);
634 <          end;
635 <        end;
636 <      end;
637 <      else
638 <        IBError(ibxeInvalidDataConversion, [nil]);
639 <    end;
640 < end;
641 <
642 < function TIBXSQLVAR.GetAsDouble: Double;
643 < begin
644 <  result := 0;
645 <  if not IsNull then begin
646 <    case FXSQLVAR^.sqltype and (not 1) of
647 <      SQL_TEXT, SQL_VARYING: begin
648 <        try
649 <          result := StrToFloat(AsString);
650 <        except
651 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
652 <        end;
653 <      end;
654 <      SQL_SHORT:
655 <        result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
656 <                              FXSQLVAR^.sqlscale);
657 <      SQL_LONG:
658 <        result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
659 <                              FXSQLVAR^.sqlscale);
660 <      SQL_INT64:
661 <        result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
662 <      SQL_FLOAT:
663 <        result := PFloat(FXSQLVAR^.sqldata)^;
664 <      SQL_DOUBLE, SQL_D_FLOAT:
665 <        result := PDouble(FXSQLVAR^.sqldata)^;
666 <      else
667 <        IBError(ibxeInvalidDataConversion, [nil]);
668 <    end;
669 <    if  FXSQLVAR^.sqlscale <> 0 then
670 <      result :=
671 <        StrToFloat(FloatToStrF(result, fffixed, 15,
672 <                  Abs(FXSQLVAR^.sqlscale) ));
673 <  end;
674 < end;
675 <
676 < function TIBXSQLVAR.GetAsFloat: Float;
677 < begin
678 <  result := 0;
679 <  try
680 <    result := AsDouble;
681 <  except
682 <    on E: EOverflow do
683 <      IBError(ibxeInvalidDataConversion, [nil]);
684 <  end;
685 < end;
686 <
687 < function TIBXSQLVAR.GetAsLong: Long;
688 < begin
689 <  result := 0;
690 <  if not IsNull then
691 <    case FXSQLVAR^.sqltype and (not 1) of
692 <      SQL_TEXT, SQL_VARYING: begin
693 <        try
694 <          result := StrToInt(AsString);
695 <        except
696 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
697 <        end;
698 <      end;
699 <      SQL_SHORT:
700 <        result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
701 <                                    FXSQLVAR^.sqlscale));
702 <      SQL_LONG:
703 <        result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
704 <                                    FXSQLVAR^.sqlscale));
705 <      SQL_INT64:
706 <        result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
707 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
708 <        result := Trunc(AsDouble);
709 <      else
710 <        IBError(ibxeInvalidDataConversion, [nil]);
711 <    end;
712 < end;
713 <
714 < function TIBXSQLVAR.GetAsPointer: Pointer;
715 < begin
716 <  if not IsNull then
717 <    result := FXSQLVAR^.sqldata
718 <  else
719 <    result := nil;
720 < end;
721 <
722 < function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
723 < begin
724 <  result.gds_quad_high := 0;
725 <  result.gds_quad_low := 0;
726 <  if not IsNull then
727 <    case FXSQLVAR^.sqltype and (not 1) of
728 <      SQL_BLOB, SQL_ARRAY, SQL_QUAD:
729 <        result := PISC_QUAD(FXSQLVAR^.sqldata)^;
730 <      else
731 <        IBError(ibxeInvalidDataConversion, [nil]);
732 <    end;
733 < end;
734 <
735 < function TIBXSQLVAR.GetAsShort: Short;
736 < begin
737 <  result := 0;
738 <  try
739 <    result := AsLong;
740 <  except
741 <    on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
742 <  end;
743 < end;
744 <
745 <
746 < function TIBXSQLVAR.GetAsString: String;
747 < var
748 <  sz: PChar;
749 <  str_len: Integer;
750 <  ss: TStringStream;
751 < begin
752 <  result := '';
753 <  { Check null, if so return a default string }
754 <  if not IsNull then
755 <    case FXSQLVar^.sqltype and (not 1) of
756 <      SQL_ARRAY:
757 <        result := '(Array)'; {do not localize}
758 <      SQL_BLOB: begin
759 <        ss := TStringStream.Create('');
760 <        SaveToStream(ss);
761 <        result := ss.DataString;
762 <        ss.Free;
763 <      end;
764 <      SQL_TEXT, SQL_VARYING: begin
765 <        sz := FXSQLVAR^.sqldata;
766 <        if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
767 <          str_len := FXSQLVar^.sqllen
768 <        else begin
769 <          str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
770 <          Inc(sz, 2);
771 <        end;
772 <        SetString(result, sz, str_len);
773 <        if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
774 <          result := TrimRight(result);
775 <      end;
776 <      SQL_TYPE_DATE:
777 <        case FSQL.Database.SQLDialect of
778 <          1 : result := DateTimeToStr(AsDateTime);
779 <          3 : result := DateToStr(AsDateTime);
780 <        end;
781 <      SQL_TYPE_TIME :
782 <        result := TimeToStr(AsDateTime);
783 <      SQL_TIMESTAMP:
784 <        result := DateTimeToStr(AsDateTime);
785 <      SQL_SHORT, SQL_LONG:
786 <        if FXSQLVAR^.sqlscale = 0 then
787 <          result := IntToStr(AsLong)
788 <        else if FXSQLVAR^.sqlscale >= (-4) then
789 <          result := CurrToStr(AsCurrency)
790 <        else
791 <          result := FloatToStr(AsDouble);
792 <      SQL_INT64:
793 <        if FXSQLVAR^.sqlscale = 0 then
794 <          result := IntToStr(AsInt64)
795 <        else if FXSQLVAR^.sqlscale >= (-4) then
796 <          result := CurrToStr(AsCurrency)
797 <        else
798 <          result := FloatToStr(AsDouble);
799 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
800 <        result := FloatToStr(AsDouble);
801 <      else
802 <        IBError(ibxeInvalidDataConversion, [nil]);
803 <    end;
804 < end;
805 <
806 < function TIBXSQLVAR.GetAsVariant: Variant;
807 < begin
808 <  if IsNull then
809 <    result := NULL
810 <  { Check null, if so return a default string }
811 <  else case FXSQLVar^.sqltype and (not 1) of
812 <      SQL_ARRAY:
813 <        result := '(Array)'; {do not localize}
814 <      SQL_BLOB:
815 <        result := '(Blob)'; {do not localize}
816 <      SQL_TEXT, SQL_VARYING:
817 <        result := AsString;
818 <      SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
819 <        result := AsDateTime;
820 <      SQL_SHORT, SQL_LONG:
821 <        if FXSQLVAR^.sqlscale = 0 then
822 <          result := AsLong
823 <        else if FXSQLVAR^.sqlscale >= (-4) then
824 <          result := AsCurrency
825 <        else
826 <          result := AsDouble;
827 <      SQL_INT64:
828 <        if FXSQLVAR^.sqlscale = 0 then
829 <          IBError(ibxeInvalidDataConversion, [nil])
830 <        else if FXSQLVAR^.sqlscale >= (-4) then
831 <          result := AsCurrency
832 <        else
833 <          result := AsDouble;
834 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
835 <        result := AsDouble;
836 <      else
837 <        IBError(ibxeInvalidDataConversion, [nil]);
838 <    end;
839 < end;
840 <
841 < function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
842 < begin
843 <  result := FXSQLVAR;
844 < end;
845 <
846 < function TIBXSQLVAR.GetIsNull: Boolean;
847 < begin
848 <  result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
849 < end;
850 <
851 < function TIBXSQLVAR.GetIsNullable: Boolean;
852 < begin
853 <  result := (FXSQLVAR^.sqltype and 1 = 1);
854 < end;
855 <
856 < procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
857 < var
858 <  fs: TFileStream;
859 < begin
860 <  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
861 <  try
862 <    LoadFromStream(fs);
863 <  finally
864 <    fs.Free;
865 <  end;
866 < end;
867 <
868 < procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
869 < var
870 <  bs: TIBBlobStream;
871 < begin
872 <  bs := TIBBlobStream.Create;
873 <  try
874 <    bs.Mode := bmWrite;
875 <    bs.Database := FSQL.Database;
876 <    bs.Transaction := FSQL.Transaction;
877 <    Stream.Seek(0, soFromBeginning);
878 <    bs.LoadFromStream(Stream);
879 <    bs.Finalize;
880 <    AsQuad := bs.BlobID;
881 <  finally
882 <    bs.Free;
883 <  end;
884 < end;
885 <
886 < procedure TIBXSQLVAR.SaveToFile(const FileName: String);
887 < var
888 <  fs: TFileStream;
889 < begin
890 <  fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
891 <  try
892 <    SaveToStream(fs);
893 <  finally
894 <    fs.Free;
895 <  end;
896 < end;
897 <
898 < procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
899 < var
900 <  bs: TIBBlobStream;
901 < begin
902 <  bs := TIBBlobStream.Create;
903 <  try
904 <    bs.Mode := bmRead;
905 <    bs.Database := FSQL.Database;
906 <    bs.Transaction := FSQL.Transaction;
907 <    bs.BlobID := AsQuad;
908 <    bs.SaveToStream(Stream);
909 <  finally
910 <    bs.Free;
911 <  end;
912 < end;
913 <
914 < function TIBXSQLVAR.GetSize: Integer;
915 < begin
916 <  result := FXSQLVAR^.sqllen;
917 < end;
918 <
919 < function TIBXSQLVAR.GetSQLType: Integer;
920 < begin
921 <  result := FXSQLVAR^.sqltype and (not 1);
922 < end;
923 <
924 < procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
925 < var
926 <  xvar: TIBXSQLVAR;
927 <  i: Integer;
928 < begin
929 <  if FSQL.Database.SQLDialect < 3 then
930 <    AsDouble := Value
931 <  else
932 <  begin
933 <    if IsNullable then
934 <      IsNull := False;
935 <    for i := 0 to FParent.FCount - 1 do
936 <      if FParent.FNames[i] = FName then
937 <      begin
938 <        xvar := FParent[i];
939 <        xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
940 <        xvar.FXSQLVAR^.sqlscale := -4;
941 <        xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
942 <        IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
943 <        PCurrency(xvar.FXSQLVAR^.sqldata)^ := Value;
944 <        xvar.FModified := True;
945 <      end;
946 <  end;
947 < end;
948 <
949 < procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
950 < var
951 <  i: Integer;
952 <  xvar: TIBXSQLVAR;
953 < begin
954 <  if IsNullable then
955 <    IsNull := False;
956 <  for i := 0 to FParent.FCount - 1 do
957 <    if FParent.FNames[i] = FName then
958 <    begin
959 <      xvar := FParent[i];
960 <      xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
961 <      xvar.FXSQLVAR^.sqlscale := 0;
962 <      xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
963 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
964 <      PInt64(xvar.FXSQLVAR^.sqldata)^ := Value;
965 <      xvar.FModified := True;
966 <    end;
967 < end;
968 <
969 < procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
970 < var
971 <  i: Integer;
972 <  tm_date: TCTimeStructure;
973 <  Yr, Mn, Dy: Word;
974 <  xvar: TIBXSQLVAR;
975 < begin
976 <  if FSQL.Database.SQLDialect < 3 then
977 <  begin
978 <    AsDateTime := Value;
979 <    exit;
980 <  end;
981 <  if IsNullable then
982 <    IsNull := False;
983 <  for i := 0 to FParent.FCount - 1 do
984 <    if FParent.FNames[i] = FName then
985 <    begin
986 <      xvar := FParent[i];
987 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_DATE or (xvar.FXSQLVAR^.sqltype and 1);
988 <      DecodeDate(Value, Yr, Mn, Dy);
989 <      with tm_date do begin
990 <        tm_sec := 0;
991 <        tm_min := 0;
992 <        tm_hour := 0;
993 <        tm_mday := Dy;
994 <        tm_mon := Mn - 1;
995 <        tm_year := Yr - 1900;
996 <      end;
997 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
998 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
999 <      isc_encode_sql_date(@tm_date, PISC_DATE(xvar.FXSQLVAR^.sqldata));
1000 <      xvar.FModified := True;
1001 <    end;
1002 < end;
1003 <
1004 < procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1005 < var
1006 <  i: Integer;
1007 <  tm_date: TCTimeStructure;
1008 <  Hr, Mt, S, Ms: Word;
1009 <  xvar: TIBXSQLVAR;
1010 < begin
1011 <  if FSQL.Database.SQLDialect < 3 then
1012 <  begin
1013 <    AsDateTime := Value;
1014 <    exit;
1015 <  end;
1016 <  if IsNullable then
1017 <    IsNull := False;
1018 <  for i := 0 to FParent.FCount - 1 do
1019 <    if FParent.FNames[i] = FName then
1020 <    begin
1021 <      xvar := FParent[i];
1022 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_TIME or (xvar.FXSQLVAR^.sqltype and 1);
1023 <      DecodeTime(Value, Hr, Mt, S, Ms);
1024 <      with tm_date do begin
1025 <        tm_sec := S;
1026 <        tm_min := Mt;
1027 <        tm_hour := Hr;
1028 <        tm_mday := 0;
1029 <        tm_mon := 0;
1030 <        tm_year := 0;
1031 <      end;
1032 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1033 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1034 <      isc_encode_sql_time(@tm_date, PISC_TIME(xvar.FXSQLVAR^.sqldata));
1035 <      xvar.FModified := True;
1036 <    end;
1037 < end;
1038 <
1039 < procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1040 < var
1041 <  i: Integer;
1042 <  tm_date: TCTimeStructure;
1043 <  Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1044 <  xvar: TIBXSQLVAR;
1045 < begin
1046 <  if IsNullable then
1047 <    IsNull := False;
1048 <  for i := 0 to FParent.FCount - 1 do
1049 <    if FParent.FNames[i] = FName then
1050 <    begin
1051 <      xvar := FParent[i];
1052 <      xvar.FXSQLVAR^.sqltype := SQL_TIMESTAMP or (xvar.FXSQLVAR^.sqltype and 1);
1053 <      DecodeDate(Value, Yr, Mn, Dy);
1054 <      DecodeTime(Value, Hr, Mt, S, Ms);
1055 <      with tm_date do begin
1056 <        tm_sec := S;
1057 <        tm_min := Mt;
1058 <        tm_hour := Hr;
1059 <        tm_mday := Dy;
1060 <        tm_mon := Mn - 1;
1061 <        tm_year := Yr - 1900;
1062 <      end;
1063 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1064 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1065 <      isc_encode_date(@tm_date, PISC_QUAD(xvar.FXSQLVAR^.sqldata));
1066 <      xvar.FModified := True;
1067 <    end;
1068 < end;
1069 <
1070 < procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1071 < var
1072 <  i: Integer;
1073 <  xvar: TIBXSQLVAR;
1074 < begin
1075 <  if IsNullable then
1076 <    IsNull := False;
1077 <  for i := 0 to FParent.FCount - 1 do
1078 <    if FParent.FNames[i] = FName then
1079 <    begin
1080 <      xvar := FParent[i];
1081 <      xvar.FXSQLVAR^.sqltype := SQL_DOUBLE or (xvar.FXSQLVAR^.sqltype and 1);
1082 <      xvar.FXSQLVAR^.sqllen := SizeOf(Double);
1083 <      xvar.FXSQLVAR^.sqlscale := 0;
1084 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1085 <      PDouble(xvar.FXSQLVAR^.sqldata)^ := Value;
1086 <      xvar.FModified := True;
1087 <    end;
1088 < end;
1089 <
1090 < procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1091 < var
1092 <  i: Integer;
1093 <  xvar: TIBXSQLVAR;
1094 < begin
1095 <  if IsNullable then
1096 <    IsNull := False;
1097 <  for i := 0 to FParent.FCount - 1 do
1098 <    if FParent.FNames[i] = FName then
1099 <    begin
1100 <      xvar := FParent[i];
1101 <      xvar.FXSQLVAR^.sqltype := SQL_FLOAT or (xvar.FXSQLVAR^.sqltype and 1);
1102 <      xvar.FXSQLVAR^.sqllen := SizeOf(Float);
1103 <      xvar.FXSQLVAR^.sqlscale := 0;
1104 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1105 <      PSingle(xvar.FXSQLVAR^.sqldata)^ := Value;
1106 <      xvar.FModified := True;
1107 <    end;
1108 < end;
1109 <
1110 < procedure TIBXSQLVAR.SetAsLong(Value: Long);
1111 < var
1112 <  i: Integer;
1113 <  xvar: TIBXSQLVAR;
1114 < begin
1115 <  if IsNullable then
1116 <    IsNull := False;
1117 <  for i := 0 to FParent.FCount - 1 do
1118 <    if FParent.FNames[i] = FName then
1119 <    begin
1120 <      xvar := FParent[i];
1121 <      xvar.FXSQLVAR^.sqltype := SQL_LONG or (xvar.FXSQLVAR^.sqltype and 1);
1122 <      xvar.FXSQLVAR^.sqllen := SizeOf(Long);
1123 <      xvar.FXSQLVAR^.sqlscale := 0;
1124 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1125 <      PLong(xvar.FXSQLVAR^.sqldata)^ := Value;
1126 <      xvar.FModified := True;
1127 <    end;
1128 < end;
1129 <
1130 < procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1131 < var
1132 <  i: Integer;
1133 <  xvar: TIBXSQLVAR;
1134 < begin
1135 <  if IsNullable and (Value = nil) then
1136 <    IsNull := True
1137 <  else begin
1138 <    IsNull := False;
1139 <    for i := 0 to FParent.FCount - 1 do
1140 <      if FParent.FNames[i] = FName then
1141 <      begin
1142 <        xvar := FParent[i];
1143 <        xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1144 <        Move(Value^, xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1145 <        xvar.FModified := True;
1146 <      end;
1147 <  end;
1148 < end;
1149 <
1150 < procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1151 < var
1152 <  i: Integer;
1153 <  xvar: TIBXSQLVAR;
1154 < begin
1155 <  if IsNullable then
1156 <    IsNull := False;
1157 <  for i := 0 to FParent.FCount - 1 do
1158 <    if FParent.FNames[i] = FName then
1159 <    begin
1160 <      xvar := FParent[i];
1161 <      if (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1162 <         (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1163 <        IBError(ibxeInvalidDataConversion, [nil]);
1164 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1165 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1166 <      PISC_QUAD(xvar.FXSQLVAR^.sqldata)^ := Value;
1167 <      xvar.FModified := True;
1168 <    end;
1169 < end;
1170 <
1171 < procedure TIBXSQLVAR.SetAsShort(Value: Short);
1172 < var
1173 <  i: Integer;
1174 <  xvar: TIBXSQLVAR;
1175 < begin
1176 <  if IsNullable then
1177 <    IsNull := False;
1178 <  for i := 0 to FParent.FCount - 1 do
1179 <    if FParent.FNames[i] = FName then
1180 <    begin
1181 <      xvar := FParent[i];
1182 <      xvar.FXSQLVAR^.sqltype := SQL_SHORT or (xvar.FXSQLVAR^.sqltype and 1);
1183 <      xvar.FXSQLVAR^.sqllen := SizeOf(Short);
1184 <      xvar.FXSQLVAR^.sqlscale := 0;
1185 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1186 <      PShort(xvar.FXSQLVAR^.sqldata)^ := Value;
1187 <      xvar.FModified := True;
1188 <    end;
1189 < end;
1190 <
1191 < procedure TIBXSQLVAR.SetAsString(Value: String);
1192 < var
1193 <  stype: Integer;
1194 <  ss: TStringStream;
1195 <
1196 <  procedure SetStringValue;
1197 <  var
1198 <    i: Integer;
1199 <    xvar: TIBXSQLVAR;
1200 <  begin
1201 <    for i := 0 to FParent.FCount - 1 do
1202 <      if FParent.FNames[i] = FName then
1203 <      begin
1204 <        xvar := FParent[i];
1205 <        if (xvar.FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1206 <           (xvar.FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1207 <          Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen)
1208 <        else begin
1209 <          xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1210 <          xvar.FXSQLVAR^.sqllen := Length(Value);
1211 <          IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen + 1);
1212 <          if (Length(Value) > 0) then
1213 <            Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1214 <        end;
1215 <        xvar.FModified := True;
1216 <      end;
1217 <  end;
1218 <
1219 < begin
1220 <  if IsNullable then
1221 <    IsNull := False;
1222 <  stype := FXSQLVAR^.sqltype and (not 1);
1223 <  if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1224 <    SetStringValue
1225 <  else begin
1226 <    if (stype = SQL_BLOB) then
1227 <    begin
1228 <      ss := TStringStream.Create(Value);
1229 <      try
1230 <        LoadFromStream(ss);
1231 <      finally
1232 <        ss.Free;
1233 <      end;
1234 <    end
1235 <    else if Value = '' then
1236 <      IsNull := True
1237 <    else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1238 <      (stype = SQL_TYPE_TIME) then
1239 <      SetAsDateTime(StrToDateTime(Value))
1240 <    else
1241 <      SetStringValue;
1242 <  end;
1243 < end;
1244 <
1245 < procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1246 < begin
1247 <  if VarIsNull(Value) then
1248 <    IsNull := True
1249 <  else case VarType(Value) of
1250 <    varEmpty, varNull:
1251 <      IsNull := True;
1252 <    varSmallint, varInteger, varByte:
1253 <      AsLong := Value;
1254 <    varSingle, varDouble:
1255 <      AsDouble := Value;
1256 <    varCurrency:
1257 <      AsCurrency := Value;
1258 <    varBoolean:
1259 <      if Value then
1260 <        AsLong := ISC_TRUE
1261 <      else
1262 <        AsLong := ISC_FALSE;
1263 <    varDate:
1264 <      AsDateTime := Value;
1265 <    varOleStr, varString:
1266 <      AsString := Value;
1267 <    varArray:
1268 <      IBError(ibxeNotSupported, [nil]);
1269 <    varByRef, varDispatch, varError, varUnknown, varVariant:
1270 <      IBError(ibxeNotPermitted, [nil]);
1271 <  end;
1272 < end;
1273 <
1274 < procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1275 < var
1276 <  i: Integer;
1277 <  xvar: TIBXSQLVAR;
1278 <  sqlind: PShort;
1279 <  sqldata: PChar;
1280 <  local_sqllen: Integer;
1281 < begin
1282 <  for i := 0 to FParent.FCount - 1 do
1283 <    if FParent.FNames[i] = FName then
1284 <    begin
1285 <      xvar := FParent[i];
1286 <      sqlind := xvar.FXSQLVAR^.sqlind;
1287 <      sqldata := xvar.FXSQLVAR^.sqldata;
1288 <      Move(Value^, xvar.FXSQLVAR^, SizeOf(TXSQLVAR));
1289 <      xvar.FXSQLVAR^.sqlind := sqlind;
1290 <      xvar.FXSQLVAR^.sqldata := sqldata;
1291 <      if (Value^.sqltype and 1 = 1) then
1292 <      begin
1293 <        if (xvar.FXSQLVAR^.sqlind = nil) then
1294 <          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1295 <        xvar.FXSQLVAR^.sqlind^ := Value^.sqlind^;
1296 <      end
1297 <      else
1298 <        if (xvar.FXSQLVAR^.sqlind <> nil) then
1299 <          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1300 <      if ((xvar.FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1301 <        local_sqllen := xvar.FXSQLVAR^.sqllen + 2
1302 <      else
1303 <        local_sqllen := xvar.FXSQLVAR^.sqllen;
1304 <      FXSQLVAR^.sqlscale := Value^.sqlscale;
1305 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, local_sqllen);
1306 <      Move(Value^.sqldata[0], xvar.FXSQLVAR^.sqldata[0], local_sqllen);
1307 <      xvar.FModified := True;
1308 <    end;
1309 < end;
1310 <
1311 < procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
1312 < var
1313 <  i: Integer;
1314 <  xvar: TIBXSQLVAR;
1315 < begin
1316 <  if Value then
1317 <  begin
1318 <    if not IsNullable then
1319 <      IsNullable := True;
1320 <    for i := 0 to FParent.FCount - 1 do
1321 <      if FParent.FNames[i] = FName then
1322 <      begin
1323 <        xvar := FParent[i];
1324 <        if Assigned(xvar.FXSQLVAR^.sqlind) then
1325 <          xvar.FXSQLVAR^.sqlind^ := -1;
1326 <        xvar.FModified := True;
1327 <      end;
1328 <  end
1329 <  else
1330 <    if ((not Value) and IsNullable) then
1331 <    begin
1332 <      for i := 0 to FParent.FCount - 1 do
1333 <        if FParent.FNames[i] = FName then
1334 <        begin
1335 <          xvar := FParent[i];
1336 <          if Assigned(xvar.FXSQLVAR^.sqlind) then
1337 <            xvar.FXSQLVAR^.sqlind^ := 0;
1338 <          xvar.FModified := True;
1339 <        end;
1340 <    end;
1341 < end;
1342 <
1343 < procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1344 < var
1345 <  i: Integer;
1346 <  xvar: TIBXSQLVAR;
1347 < begin
1348 <  for i := 0 to FParent.FCount - 1 do
1349 <    if FParent.FNames[i] = FName then
1350 <    begin
1351 <      xvar := FParent[i];
1352 <      if (Value <> IsNullable) then
1353 <      begin
1354 <        if Value then
1355 <        begin
1356 <          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype or 1;
1357 <          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1358 <        end
1359 <        else
1360 <        begin
1361 <          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype and (not 1);
1362 <          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1363 <        end;
1364 <      end;
1365 <    end;
1366 < end;
1367 <
1368 < { TIBXSQLDA }
1369 < constructor TIBXSQLDA.Create(Query: TIBSQL);
1370 < begin
1371 <  inherited Create;
1372 <  FSQL := Query;
1373 <  FNames := TStringList.Create;
1374 <  FSize := 0;
1375 <  FUniqueRelationName := '';
1376 < end;
251 >   Variants, IBSQLMonitor, FBMessages, IBCustomDataSet;
252  
253 < destructor TIBXSQLDA.Destroy;
253 > procedure IBAlloc(var P; OldSize, NewSize: Integer);
254   var
255    i: Integer;
256   begin
257 <  FNames.Free;
258 <  if FXSQLDA <> nil then
1384 <  begin
1385 <    for i := 0 to FSize - 1 do
1386 <    begin
1387 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1388 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1389 <      FXSQLVARs[i].Free ;
1390 <    end;
1391 <    FreeMem(FXSQLDA);
1392 <    FXSQLDA := nil;
1393 <    FXSQLVARs := nil;
1394 <  end;
1395 <  inherited Destroy;
1396 < end;
1397 <
1398 < procedure TIBXSQLDA.AddName(FieldName: String; Idx: Integer);
1399 < var
1400 <  fn: String;
1401 < begin
1402 <  fn := FormatIdentifierValue(FSQL.Database.SQLDialect, FieldName);
1403 <  while FNames.Count <= Idx do
1404 <    FNames.Add('');
1405 <  FNames[Idx] := fn;
1406 <  FXSQLVARs[Idx].FName := fn;
1407 <  FXSQLVARs[Idx].FIndex := Idx;
1408 < end;
1409 <
1410 < function TIBXSQLDA.GetModified: Boolean;
1411 < var
1412 <  i: Integer;
1413 < begin
1414 <  result := False;
1415 <  for i := 0 to FCount - 1 do
1416 <    if FXSQLVARs[i].Modified then
1417 <    begin
1418 <      result := True;
1419 <      exit;
1420 <    end;
1421 < end;
1422 <
1423 < function TIBXSQLDA.GetNames: String;
1424 < begin
1425 <  result := FNames.Text;
1426 < end;
1427 <
1428 < function TIBXSQLDA.GetRecordSize: Integer;
1429 < begin
1430 <  result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1431 < end;
1432 <
1433 < function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1434 < begin
1435 <  result := FXSQLDA;
1436 < end;
1437 <
1438 < function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1439 < begin
1440 <  if (Idx < 0) or (Idx >= FCount) then
1441 <    IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1442 <  result := FXSQLVARs[Idx]
1443 < end;
1444 <
1445 < function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1446 < begin
1447 <  result := GetXSQLVARByName(Idx);
1448 <  if result = nil then
1449 <    IBError(ibxeFieldNotFound, [Idx]);
1450 < end;
1451 <
1452 < function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1453 < var
1454 <  s: String;
1455 <  i, Cnt: Integer;
1456 < begin
1457 <  s := FormatIdentifierValue(FSQL.Database.SQLDialect, Idx);
1458 <  i := 0;
1459 <  Cnt := FNames.Count;
1460 <  while (i < Cnt) and (FNames[i] <> s) do Inc(i);
1461 <  if i = Cnt then
1462 <    result := nil
1463 <  else
1464 <    result := GetXSQLVAR(i);
1465 < end;
1466 <
1467 < procedure TIBXSQLDA.Initialize;
1468 < var
1469 <  i, j, j_len: Integer;
1470 <  NamesWereEmpty: Boolean;
1471 <  st: String;
1472 <  bUnique: Boolean;
1473 < begin
1474 <  bUnique := True;
1475 <  NamesWereEmpty := (FNames.Count = 0);
1476 <  if FXSQLDA <> nil then
1477 <  begin
1478 <    for i := 0 to FCount - 1 do
1479 <    begin
1480 <      with FXSQLVARs[i].Data^ do
1481 <      begin
1482 <        if bUnique and (strpas(relname) <> '') then
1483 <        begin
1484 <          if FUniqueRelationName = '' then
1485 <            FUniqueRelationName := strpas(relname)
1486 <          else
1487 <            if strpas(relname) <> FUniqueRelationName then
1488 <            begin
1489 <              FUniqueRelationName := '';
1490 <              bUnique := False;
1491 <            end;
1492 <        end;
1493 <        if NamesWereEmpty then
1494 <        begin
1495 <          st := strpas(aliasname);
1496 <          if st = '' then
1497 <          begin
1498 <            st := 'F_'; {do not localize}
1499 <            aliasname_length := 2;
1500 <            j := 1; j_len := 1;
1501 <            StrPCopy(aliasname, st + IntToStr(j));
1502 <          end
1503 <          else
1504 <          begin
1505 <            StrPCopy(aliasname, st);
1506 <            j := 0; j_len := 0;
1507 <          end;
1508 <          while GetXSQLVARByName(strpas(aliasname)) <> nil do
1509 <          begin
1510 <            Inc(j); j_len := Length(IntToStr(j));
1511 <            if j_len + aliasname_length > 31 then
1512 <              StrPCopy(aliasname,
1513 <                       Copy(st, 1, 31 - j_len) +
1514 <                       IntToStr(j))
1515 <            else
1516 <              StrPCopy(aliasname, st + IntToStr(j));
1517 <          end;
1518 <          Inc(aliasname_length, j_len);
1519 <          AddName(strpas(aliasname), i);
1520 <        end;
1521 <        case sqltype and (not 1) of
1522 <          SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1523 <          SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1524 <          SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1525 <            if (sqllen = 0) then
1526 <              { Make sure you get a valid pointer anyway
1527 <               select '' from foo }
1528 <              IBAlloc(sqldata, 0, 1)
1529 <            else
1530 <              IBAlloc(sqldata, 0, sqllen)
1531 <          end;
1532 <          SQL_VARYING: begin
1533 <            IBAlloc(sqldata, 0, sqllen + 2);
1534 <          end;
1535 <          else
1536 <            IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1537 <        end;
1538 <        if (sqltype and 1 = 1) then
1539 <          IBAlloc(sqlind, 0, SizeOf(Short))
1540 <        else
1541 <          if (sqlind <> nil) then
1542 <            ReallocMem(sqlind, 0);
1543 <      end;
1544 <    end;
1545 <  end;
1546 < end;
1547 <
1548 < procedure TIBXSQLDA.SetCount(Value: Integer);
1549 < var
1550 <  i, OldSize: Integer;
1551 <  p : PXSQLVAR;
1552 < begin
1553 <  FNames.Clear;
1554 <  FCount := Value;
1555 <  if FCount = 0 then
1556 <    FUniqueRelationName := ''
1557 <  else
1558 <  begin
1559 <    if FSize > 0 then
1560 <      OldSize := XSQLDA_LENGTH(FSize)
1561 <    else
1562 <      OldSize := 0;
1563 <    if FCount > FSize then
1564 <    begin
1565 <      IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1566 <      SetLength(FXSQLVARs, FCount);
1567 <      FXSQLDA^.version := SQLDA_VERSION1;
1568 <      p := @FXSQLDA^.sqlvar[0];
1569 <      for i := 0 to FCount - 1 do
1570 <      begin
1571 <        if i >= FSize then
1572 <          FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1573 <        FXSQLVARs[i].FXSQLVAR := p;
1574 <        p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1575 <      end;
1576 <      FSize := FCount;
1577 <    end;
1578 <    if FSize > 0 then
1579 <    begin
1580 <      FXSQLDA^.sqln := Value;
1581 <      FXSQLDA^.sqld := Value;
1582 <    end;
1583 <  end;
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 LINUX}
265 > {$IFDEF UNIX}
266    if FHandle <> -1 then
267       fpclose(FHandle);
268   {$ELSE}
# Line 1603 | Line 278 | end;
278   procedure TIBOutputDelimitedFile.ReadyFile;
279   var
280    i: Integer;
281 <  {$IFDEF LINUX}
281 >  {$IFDEF UNIX}
282    BytesWritten: cint;
283    {$ELSE}
284    BytesWritten: DWORD;
# Line 1614 | Line 289 | begin
289      FColDelimiter := TAB;
290    if FRowDelimiter = '' then
291      FRowDelimiter := CRLF;
292 <  {$IFDEF LINUX}
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,
# Line 1626 | Line 301 | begin
301    begin
302      for i := 0 to Columns.Count - 1 do
303        if i = 0 then
304 <        st := strpas(Columns[i].Data^.aliasname)
304 >        st := Columns[i].GetAliasname
305        else
306 <        st := st + FColDelimiter + strpas(Columns[i].Data^.aliasname);
306 >        st := st + FColDelimiter + Columns[i].GetAliasname;
307      st := st + FRowDelimiter;
308 <    {$IFDEF LINUX}
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, PChar(st[1]), Length(st), BytesWritten, nil);
314 >    WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
315      {$ENDIF}
316    end;
317   end;
# Line 1644 | Line 319 | end;
319   function TIBOutputDelimitedFile.WriteColumns: Boolean;
320   var
321    i: Integer;
322 <  {$IFDEF LINUX}
322 >  {$IFDEF UNIX}
323    BytesWritten: cint;
324    {$ELSE}
325    BytesWritten: DWORD;
# Line 1652 | Line 327 | var
327    st: string;
328   begin
329    result := False;
330 <  {$IFDEF LINUX}
330 >  {$IFDEF UNIX}
331    if FHandle <> -1 then
332    {$ELSE}
333    if FHandle <> 0 then
# Line 1666 | Line 341 | begin
341        st := st + StripString(Columns[i].AsString, FColDelimiter + FRowDelimiter);
342      end;
343      st := st + FRowDelimiter;
344 <  {$IFDEF LINUX}
344 >  {$IFDEF UNIX}
345      BytesWritten := FpWrite(FHandle,st[1],Length(st));
346    {$ELSE}
347      WriteFile(FHandle, st[1], Length(st), BytesWritten, nil);
# Line 1783 | Line 458 | end;
458   { TIBOutputRawFile }
459   destructor TIBOutputRawFile.Destroy;
460   begin
461 < {$IFDEF LINUX}
461 > {$IFDEF UNIX}
462    if FHandle <> -1 then
463       fpclose(FHandle);
464   {$ELSE}
# Line 1798 | Line 473 | end;
473  
474   procedure TIBOutputRawFile.ReadyFile;
475   begin
476 <  {$IFDEF LINUX}
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,
# Line 1818 | Line 493 | begin
493    begin
494      for i := 0 to Columns.Count - 1 do
495      begin
496 <      {$IFDEF LINUX}
497 <      BytesWritten := FpWrite(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].Data^.sqldata^, Columns[i].Data^.sqllen,
499 >      WriteFile(FHandle, Columns[i].GetAsPointer^, Columns[i].GetSize,
500                  BytesWritten, nil);
501        {$ENDIF}
502 <      if BytesWritten <> DWORD(Columns[i].Data^.sqllen) then
502 >      if BytesWritten <> DWORD(Columns[i].GetSize) then
503          exit;
504      end;
505      result := True;
# Line 1834 | Line 509 | end;
509   { TIBInputRawFile }
510   destructor TIBInputRawFile.Destroy;
511   begin
512 < {$IFDEF LINUX}
512 > {$IFDEF UNIX}
513    if FHandle <> -1 then
514       fpclose(FHandle);
515   {$ELSE}
# Line 1850 | Line 525 | var
525    BytesRead: DWord;
526   begin
527    result := False;
528 < {$IFDEF LINUX}
528 > {$IFDEF UNIX}
529    if FHandle <> -1 then
530   {$ELSE}
531    if FHandle <> 0 then
# Line 1858 | Line 533 | begin
533    begin
534      for i := 0 to Params.Count - 1 do
535      begin
536 <      {$IFDEF LINUX}
537 <      BytesRead := FpRead(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].Data^.sqldata^, Params[i].Data^.sqllen);
539 >      ReadFile(FHandle, Params[i].GetAsPointer^, Params[i].GetSize,
540                 BytesRead, nil);
541        {$ENDIF}
542 <      if BytesRead <> DWORD(Params[i].Data^.sqllen) then
542 >      if BytesRead <> DWORD(Params[i].GetSize) then
543          exit;
544      end;
545      result := True;
# Line 1873 | Line 548 | end;
548  
549   procedure TIBInputRawFile.ReadyFile;
550   begin
551 < {$IFDEF LINUX}
551 > {$IFDEF UNIX}
552    if FHandle <> -1 then
553       fpclose(FHandle);
554    FHandle := FpOpen(Filename,O_RdOnly);
# Line 1893 | Line 568 | end;
568   constructor TIBSQL.Create(AOwner: TComponent);
569   begin
570    inherited Create(AOwner);
1896  FIBLoaded := False;
1897  CheckIBLoaded;
1898  FIBLoaded := True;
571    FGenerateParamNames := False;
572    FGoToFirstRecordOnExecute := True;
573    FBase := TIBBase.Create(Self);
574    FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
575    FBase.BeforeTransactionEnd := BeforeTransactionEnd;
1904  FBOF := False;
1905  FEOF := False;
1906  FPrepared := False;
576    FRecordCount := 0;
577    FSQL := TStringList.Create;
578    TStringList(FSQL).OnChanging := SQLChanging;
579 <  FProcessedSQL := TStringList.Create;
1911 <  FHandle := nil;
1912 <  FSQLParams := TIBXSQLDA.Create(self);
1913 <  FSQLRecord := TIBXSQLDA.Create(self);
1914 <  FSQLType := SQLUnknown;
579 >  TStringList(FSQL).OnChange := SQLChanged;
580    FParamCheck := True;
1916  FCursor := Name + RandomString(8);
581    if AOwner is TIBDatabase then
582      Database := TIBDatabase(AOwner)
583    else
# Line 1923 | Line 587 | end;
587  
588   destructor TIBSQL.Destroy;
589   begin
590 <  if FIBLoaded then
591 <  begin
592 <    if (FOpen) then
1929 <      Close;
1930 <    if (FHandle <> nil) then
1931 <      FreeHandle;
1932 <    FSQL.Free;
1933 <    FProcessedSQL.Free;
1934 <    FBase.Free;
1935 <    FSQLParams.Free;
1936 <    FSQLRecord.Free;
1937 <  end;
590 >  FreeHandle;
591 >  FSQL.Free;
592 >  FBase.Free;
593    inherited Destroy;
594   end;
595  
# Line 1942 | Line 597 | procedure TIBSQL.BatchInput(InputObject:
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 1954 | 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 1971 | 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;
1990 var
1991  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(
2000 <              [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2001 <        IBDatabaseError;
2002 <    end;
2003 <  finally
2004 <    FEOF := False;
2005 <    FBOF := False;
2006 <    FOpen := False;
2007 <    FRecordCount := 0;
2008 <  end;
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
2015 <    result := Transaction.Call(ErrCode, RaiseError)
657 >  if FResults <> nil then
658 >    Result := FResults.GetCount
659    else
660 <  if RaiseError and (ErrCode > 0) then
661 <    IBDataBaseError;
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.Current: TIBXSQLDA;
671 > function TIBSQL.GetPrepared: Boolean;
672   begin
673 <  result := FSQLRecord;
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 >    Result := FStatement.GetSQLStatementType;
682 > end;
683 >
684 > procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
685 > begin
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
2029 <    Close;
2030 <    FreeHandle;
2031 <  end;
693 >  FreeHandle;
694   end;
695  
696   procedure TIBSQL.ExecQuery;
697   var
698    fetch_res: ISC_STATUS;
699 +  {$IFDEF IBXQUERYSTATS}
700 +  stats: TPerfCounters;
701 +  {$ENDIF}
702 +  {$IFDEF IBXQUERYTIME}
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,
2062 <                            @FHandle,
2063 <                            Database.SQLDialect,
2064 <                            FSQLParams.AsXSQLDA,
2065 <                            FSQLRecord.AsXSQLDA), False);
2066 <      if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2067 <      begin
2068 <         { Sometimes a prepared stored procedure appears to get
2069 <           off sync on the server ....This code is meant to try
2070 <           to work around the problem simply by "retrying". This
2071 <           need to be reproduced and fixed.
2072 <         }
2073 <        isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2074 <                         PChar(FProcessedSQL.Text), 1, nil);
2075 <        Call(isc_dsql_execute2(StatusVector,
2076 <                            TRHandle,
2077 <                            @FHandle,
2078 <                            Database.SQLDialect,
2079 <                            FSQLParams.AsXSQLDA,
2080 <                            FSQLRecord.AsXSQLDA), True);
2081 <      end;
2082 <    end
2083 <    else
2084 <      Call(isc_dsql_execute(StatusVector,
2085 <                           TRHandle,
2086 <                           @FHandle,
2087 <                           Database.SQLDialect,
2088 <                           FSQLParams.AsXSQLDA), True)
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 <  {$IFDEF HAS_SQLMONITOR}
732 <  if not (csDesigning in ComponentState) then
733 <    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;
2102 < var
2103 <  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]);
2108  result := GetFields(i);
765   end;
766  
767 < function TIBSQL.ParamByName(ParamName: String): TIBXSQLVAR;
767 > function TIBSQL.ParamByName(ParamName: String): ISQLParam;
768   begin
769    Result := Params.ByName(ParamName);
770   end;
771  
772 < function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
772 > function TIBSQL.GetFields(const Idx: Integer): ISQLData;
773   begin
774 <  if (Idx < 0) or (Idx >= FSQLRecord.Count) then
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;
2132 < var
2133 <  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
2147 <        Close;
2148 <        raise;
2149 <      end;
2150 <    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;
815 <  {$IFDEF HAS_SQLMONITOR}
813 >    end
814 >    else
815 >      FEOF := true;
816 >
817      if not (csDesigning in ComponentState) then
818        MonitorHook.SQLFetch(Self);
2158  {$ENDIF}
819    end;
820   end;
821  
822   procedure TIBSQL.FreeHandle;
2163 var
2164  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
2174 <      isc_res :=
2175 <        Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2176 <      if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2177 <        IBDataBaseError;
2178 <    end;
2179 <  finally
2180 <    FPrepared := False;
2181 <    FHandle := nil;
2182 <  end;
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 2187 | Line 836 | begin
836    result := FBase.Database;
837   end;
838  
2190 function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2191 begin
2192  result := FBase.DBHandle;
2193 end;
2194
839   function TIBSQL.GetPlan: String;
2196 var
2197  result_buffer: array[0..16384] of Char;
2198  result_length, i: Integer;
2199  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);
2208 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2209 <                           SizeOf(result_buffer), result_buffer), True);
2210 <    if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2211 <      IBError(ibxeUnknownError, [nil]);
2212 <    result_length := isc_vax_integer(@result_buffer[1], 2);
2213 <    SetString(result, nil, result_length);
2214 <    for i := 1 to result_length do
2215 <      result[i] := result_buffer[i + 2];
2216 <    result := Trim(result);
2217 <  end;
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;
2228 <  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
2236 <      IBDatabaseError;
2237 <    if (result_buffer[0] <> Char(isc_info_sql_records)) then
2238 <      result := -1
2239 <    else
2240 <    case SQLType of
2241 <    SQLUpdate:   Result := isc_vax_integer(@result_buffer[6], 4);
2242 <    SQLDelete:   Result := isc_vax_integer(@result_buffer[13], 4);
2243 <    SQLInsert:   Result := isc_vax_integer(@result_buffer[27], 4);
2244 <    else         Result := -1 ;
2245 <    end ;
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 2258 | Line 877 | begin
877    result := FBase.Transaction;
878   end;
879  
2261 function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2262 begin
2263  result := FBase.TRHandle;
2264 end;
2265
2266 {
2267 Preprocess SQL
2268 Using FSQL, process the typed SQL and put the process SQL
2269 in FProcessedSQL and parameter names in FSQLParams
2270 }
2271 procedure TIBSQL.PreprocessSQL;
2272 var
2273  cCurChar, cNextChar, cQuoteChar: Char;
2274  sSQL, sProcessedSQL, sParamName: String;
2275  i, iLenSQL, iSQLPos: Integer;
2276  iCurState, iCurParamState: Integer;
2277  iParamSuffix: Integer;
2278  slNames: TStrings;
2279
2280 const
2281  DefaultState = 0;
2282  CommentState = 1;
2283  QuoteState = 2;
2284  ParamState = 3;
2285  ParamDefaultState = 0;
2286  ParamQuoteState = 1;
2287
2288  procedure AddToProcessedSQL(cChar: Char);
2289  begin
2290    sProcessedSQL[iSQLPos] := cChar;
2291    Inc(iSQLPos);
2292  end;
2293
2294 begin
2295  slNames := TStringList.Create;
2296  try
2297    { Do some initializations of variables }
2298    iParamSuffix := 0;
2299    cQuoteChar := '''';
2300    sSQL := FSQL.Text;
2301    iLenSQL := Length(sSQL);
2302    SetString(sProcessedSQL, nil, iLenSQL + 1);
2303    i := 1;
2304    iSQLPos := 1;
2305    iCurState := DefaultState;
2306    iCurParamState := ParamDefaultState;
2307    { Now, traverse through the SQL string, character by character,
2308     picking out the parameters and formatting correctly for InterBase }
2309    while (i <= iLenSQL) do begin
2310      { Get the current token and a look-ahead }
2311      cCurChar := sSQL[i];
2312      if i = iLenSQL then
2313        cNextChar := #0
2314      else
2315        cNextChar := sSQL[i + 1];
2316      { Now act based on the current state }
2317      case iCurState of
2318        DefaultState: begin
2319          case cCurChar of
2320            '''', '"': begin
2321              cQuoteChar := cCurChar;
2322              iCurState := QuoteState;
2323            end;
2324            '?', ':': begin
2325              iCurState := ParamState;
2326              AddToProcessedSQL('?');
2327            end;
2328            '/': if (cNextChar = '*') then begin
2329              AddToProcessedSQL(cCurChar);
2330              Inc(i);
2331              iCurState := CommentState;
2332            end;
2333          end;
2334        end;
2335        CommentState: begin
2336          if (cNextChar = #0) then
2337            IBError(ibxeSQLParseError, [SEOFInComment])
2338          else if (cCurChar = '*') then begin
2339            if (cNextChar = '/') then
2340              iCurState := DefaultState;
2341          end;
2342        end;
2343        QuoteState: begin
2344          if cNextChar = #0 then
2345            IBError(ibxeSQLParseError, [SEOFInString])
2346          else if (cCurChar = cQuoteChar) then begin
2347            if (cNextChar = cQuoteChar) then begin
2348              AddToProcessedSQL(cCurChar);
2349              Inc(i);
2350            end else
2351              iCurState := DefaultState;
2352          end;
2353        end;
2354        ParamState:
2355        begin
2356          { collect the name of the parameter }
2357          if iCurParamState = ParamDefaultState then
2358          begin
2359            if cCurChar = '"' then
2360              iCurParamState := ParamQuoteState
2361            else if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2362                sParamName := sParamName + cCurChar
2363            else if FGenerateParamNames then
2364            begin
2365              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2366              Inc(iParamSuffix);
2367              iCurState := DefaultState;
2368              slNames.Add(sParamName);
2369              sParamName := '';
2370            end
2371            else
2372              IBError(ibxeSQLParseError, [SParamNameExpected]);
2373          end
2374          else begin
2375            { determine if Quoted parameter name is finished }
2376            if cCurChar = '"' then
2377            begin
2378              Inc(i);
2379              slNames.Add(sParamName);
2380              SParamName := '';
2381              iCurParamState := ParamDefaultState;
2382              iCurState := DefaultState;
2383            end
2384            else
2385              sParamName := sParamName + cCurChar
2386          end;
2387          { determine if the unquoted parameter name is finished }
2388          if (iCurParamState <> ParamQuoteState) and
2389            (iCurState <> DefaultState) then
2390          begin
2391            if not (cNextChar in ['A'..'Z', 'a'..'z',
2392                                  '0'..'9', '_', '$']) then begin
2393              Inc(i);
2394              iCurState := DefaultState;
2395              slNames.Add(sParamName);
2396              sParamName := '';
2397            end;
2398          end;
2399        end;
2400      end;
2401      if iCurState <> ParamState then
2402        AddToProcessedSQL(sSQL[i]);
2403      Inc(i);
2404    end;
2405    AddToProcessedSQL(#0);
2406    FSQLParams.Count := slNames.Count;
2407    for i := 0 to slNames.Count - 1 do
2408      FSQLParams.AddName(slNames[i], i);
2409    FProcessedSQL.Text := sProcessedSQL;
2410  finally
2411    slNames.Free;
2412  end;
2413 end;
2414
880   procedure TIBSQL.SetDatabase(Value: TIBDatabase);
881   begin
882    FBase.Database := Value;
883   end;
884  
885   procedure TIBSQL.Prepare;
2421 var
2422  stmt_len: Integer;
2423  res_buffer: array[0..7] of Char;
2424  type_item: Char;
886   begin
887    CheckClosed;
888    FBase.CheckDatabase;
889    FBase.CheckTransaction;
890 <  if FPrepared then
890 >  Close;
891 >  if Prepared then
892      exit;
893    if (FSQL.Text = '') then
894      IBError(ibxeEmptyQuery, [nil]);
895 +
896 +  if FStatement <> nil then
897 +    FStatement.Prepare(Transaction.TransactionIntf)
898 +  else
899    if not ParamCheck then
900 <    FProcessedSQL.Text := FSQL.Text
900 >    FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
901    else
902 <    PreprocessSQL;
903 <  if (FProcessedSQL.Text = '') then
904 <    IBError(ibxeEmptyQuery, [nil]);
905 <  try
906 <    Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
907 <                                    @FHandle), True);
908 <    Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
909 <               PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
910 <    { After preparing the statement, query the stmt type and possibly
911 <      create a FSQLRecord "holder" }
912 <    { Get the type of the statement }
2447 <    type_item := Char(isc_info_sql_stmt_type);
2448 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2449 <                         SizeOf(res_buffer), res_buffer), True);
2450 <    if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2451 <      IBError(ibxeUnknownError, [nil]);
2452 <    stmt_len := isc_vax_integer(@res_buffer[1], 2);
2453 <    FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2454 <    { Done getting the type }
2455 <    case FSQLType of
2456 <      SQLGetSegment,
2457 <      SQLPutSegment,
2458 <      SQLStartTransaction: begin
2459 <        FreeHandle;
2460 <        IBError(ibxeNotPermitted, [nil]);
2461 <      end;
2462 <      SQLCommit,
2463 <      SQLRollback,
2464 <      SQLDDL, SQLSetGenerator,
2465 <      SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2466 <      SQLExecProcedure: begin
2467 <        { We already know how many inputs there are, so... }
2468 <        if (FSQLParams.FXSQLDA <> nil) and
2469 <           (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2470 <                                        FSQLParams.FXSQLDA), False) > 0) then
2471 <          IBDataBaseError;
2472 <        FSQLParams.Initialize;
2473 <        if FSQLType in [SQLSelect, SQLSelectForUpdate,
2474 <                        SQLExecProcedure] then begin
2475 <          { Allocate an initial output descriptor (with one column) }
2476 <          FSQLRecord.Count := 1;
2477 <          { Using isc_dsql_describe, get the right size for the columns... }
2478 <          Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2479 <          if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2480 <            FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2481 <            Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2482 <          end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2483 <            FSQLRecord.Count := 0;
2484 <          FSQLRecord.Initialize;
2485 <        end;
2486 <      end;
2487 <    end;
2488 <    FPrepared := True;
2489 <  {$IFDEF HAS_SQLMONITOR}
2490 <    if not (csDesigning in ComponentState) then
902 >    FStatement := Database.Attachment.PrepareWithNamedParameters(
903 >                     Transaction.TransactionIntf,
904 >                     SQL.Text,
905 >                     GenerateParamNames);
906 >  {$IFDEF IBXQUERYSTATS}
907 >  FStatement.EnableStatistics(true);
908 >  {$ENDIF}
909 >  FMetaData := FStatement.GetMetaData;
910 >  FSQLParams := FStatement.GetSQLParams;
911 >  FStatement.SetRetainInterfaces(true);
912 >  if not (csDesigning in ComponentState) then
913        MonitorHook.SQLPrepare(Self);
2492  {$ENDIF}
2493  except
2494    on E: Exception do begin
2495      if (FHandle <> nil) then
2496        FreeHandle;
2497      raise;
2498    end;
2499  end;
914   end;
915  
916   function TIBSQL.GetUniqueRelationName: String;
917   begin
918 <  if FPrepared and (FSQLType = SQLSelect) then
919 <    result := FSQLRecord.UniqueRelationName
918 >  if Prepared and (GetSQLStatementType = SQLSelect) then
919 >    result := FMetaData.GetUniqueRelationName
920    else
921      result := '';
922   end;
# Line 2529 | Line 943 | procedure TIBSQL.SQLChanging(Sender: TOb
943   begin
944    if Assigned(OnSQLChanging) then
945      OnSQLChanging(Self);
946 <  if FHandle <> nil then FreeHandle;
946 >
947 >  FreeHandle;
948   end;
949  
950 < procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
950 > procedure TIBSQL.SQLChanged(Sender: TObject);
951   begin
952 <  if (FOpen) then
953 <    Close;
952 >  if assigned(OnSQLChanged) then
953 >    OnSQLChanged(self);
954 > end;
955 >
956 > procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
957 >  Action: TTransactionAction);
958 > begin
959 >  if not (Owner is TIBCustomDataSet) then
960 >    FreeHandle;
961   end;
962  
963   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines