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 7 by tony, Sun Aug 5 18:28:19 2012 UTC vs.
Revision 45 by tony, Tue Dec 6 10:33:46 2016 UTC

# Line 27 | Line 27
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                                                 }
30 > {    Associates Ltd 2011 - 2014                                                }
31   {                                                                        }
32   {************************************************************************}
33  
# Line 35 | Line 35 | unit IBSQL;
35  
36   {$Mode Delphi}
37  
38 + {$codepage UTF8}
39 +
40   interface
41  
42   uses
# Line 43 | Line 45 | uses
45   {$ELSE}
46    baseunix, unix,
47   {$ENDIF}
48 <  SysUtils, Classes, Forms, Controls, IBHeader,
47 <  IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
48 >  SysUtils, Classes, IBExternals, IB, IBDatabase, IBUtils;
49  
50   type
50  TIBSQL = class;
51  TIBXSQLDA = class;
52  
53  { TIBXSQLVAR }
54  TIBXSQLVAR = class(TObject)
55  private
56    FParent: TIBXSQLDA;
57    FSQL: TIBSQL;
58    FIndex: Integer;
59    FModified: Boolean;
60    FName: String;
61    FXSQLVAR: PXSQLVAR;       { Point to the PXSQLVAR in the owner object }
62
63    function AdjustScale(Value: Int64; Scale: Integer): Double;
64    function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
65    function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
66    function GetAsCurrency: Currency;
67    function GetAsInt64: Int64;
68    function GetAsDateTime: TDateTime;
69    function GetAsDouble: Double;
70    function GetAsFloat: Float;
71    function GetAsLong: Long;
72    function GetAsPointer: Pointer;
73    function GetAsQuad: TISC_QUAD;
74    function GetAsShort: Short;
75    function GetAsString: String;
76    function GetAsVariant: Variant;
77    function GetAsXSQLVAR: PXSQLVAR;
78    function GetIsNull: Boolean;
79    function GetIsNullable: Boolean;
80    function GetSize: Integer;
81    function GetSQLType: Integer;
82    procedure SetAsCurrency(Value: Currency);
83    procedure SetAsInt64(Value: Int64);
84    procedure SetAsDate(Value: TDateTime);
85    procedure SetAsTime(Value: TDateTime);
86    procedure SetAsDateTime(Value: TDateTime);
87    procedure SetAsDouble(Value: Double);
88    procedure SetAsFloat(Value: Float);
89    procedure SetAsLong(Value: Long);
90    procedure SetAsPointer(Value: Pointer);
91    procedure SetAsQuad(Value: TISC_QUAD);
92    procedure SetAsShort(Value: Short);
93    procedure SetAsString(Value: String);
94    procedure SetAsVariant(Value: Variant);
95    procedure SetAsXSQLVAR(Value: PXSQLVAR);
96    procedure SetIsNull(Value: Boolean);
97    procedure SetIsNullable(Value: Boolean);
98  public
99    constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
100    procedure Assign(Source: TIBXSQLVAR);
101    procedure Clear;
102    procedure LoadFromFile(const FileName: String);
103    procedure LoadFromStream(Stream: TStream);
104    procedure SaveToFile(const FileName: String);
105    procedure SaveToStream(Stream: TStream);
106    property AsDate: TDateTime read GetAsDateTime write SetAsDate;
107    property AsTime: TDateTime read GetAsDateTime write SetAsTime;
108    property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
109    property AsDouble: Double read GetAsDouble write SetAsDouble;
110    property AsFloat: Float read GetAsFloat write SetAsFloat;
111    property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
112    property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
113    property AsInteger: Integer read GetAsLong write SetAsLong;
114    property AsLong: Long read GetAsLong write SetAsLong;
115    property AsPointer: Pointer read GetAsPointer write SetAsPointer;
116    property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
117    property AsShort: Short read GetAsShort write SetAsShort;
118    property AsString: String read GetAsString write SetAsString;
119    property AsVariant: Variant read GetAsVariant write SetAsVariant;
120    property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
121    property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
122    property IsNull: Boolean read GetIsNull write SetIsNull;
123    property IsNullable: Boolean read GetIsNullable write SetIsNullable;
124    property Index: Integer read FIndex;
125    property Modified: Boolean read FModified write FModified;
126    property Name: String read FName;
127    property Size: Integer read GetSize;
128    property SQLType: Integer read GetSQLType;
129    property Value: Variant read GetAsVariant write SetAsVariant;
130  end;
131
132  TIBXSQLVARArray = Array of TIBXSQLVAR;
133
134  { TIBXSQLVAR }
135  TIBXSQLDA = class(TObject)
136  protected
137    FSQL: TIBSQL;
138    FCount: Integer;
139    FNames: TStrings;
140    FSize: Integer;
141    FXSQLDA: PXSQLDA;
142    FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
143    FUniqueRelationName: String;
144    function GetModified: Boolean;
145    function GetNames: String;
146    function GetRecordSize: Integer;
147    function GetXSQLDA: PXSQLDA;
148    function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
149    function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
150    procedure Initialize;
151    procedure SetCount(Value: Integer);
152  public
153    constructor Create(Query: TIBSQL);
154    destructor Destroy; override;
155    procedure AddName(FieldName: String; Idx: Integer);
156    function ByName(Idx: String): TIBXSQLVAR;
157    property AsXSQLDA: PXSQLDA read GetXSQLDA;
158    property Count: Integer read FCount write SetCount;
159    property Modified: Boolean read GetModified;
160    property Names: String read GetNames;
161    property RecordSize: Integer read GetRecordSize;
162    property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
163    property UniqueRelationName: String read FUniqueRelationName;
164  end;
165
51    { TIBBatch }
52  
53    TIBBatch = class(TObject)
54    protected
55      FFilename: String;
56 <    FColumns: TIBXSQLDA;
57 <    FParams: TIBXSQLDA;
56 >    FColumns: IResults;
57 >    FParams: ISQLParams;
58    public
59      procedure ReadyFile; virtual; abstract;
60 <    property Columns: TIBXSQLDA read FColumns;
60 >    property Columns: IResults read FColumns;
61      property Filename: String read FFilename write FFilename;
62 <    property Params: TIBXSQLDA read FParams;
62 >    property Params: ISQLParams read FParams;
63    end;
64  
65    TIBBatchInput = class(TIBBatch)
# Line 260 | Line 145 | type
145    end;
146  
147       { TIBSQL }
263  TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
264                  SQLUpdate, SQLDelete, SQLDDL,
265                  SQLGetSegment, SQLPutSegment,
266                  SQLExecProcedure, SQLStartTransaction,
267                  SQLCommit, SQLRollback,
268                  SQLSelectForUpdate, SQLSetGenerator);
148  
149    TIBSQL = class(TComponent)
150    private
151 <    FIBLoaded: Boolean;
151 >    FMetaData: IMetaData;
152 >    FSQLParams: ISQLParams;
153 >    FStatement: IStatement;
154 >    FOnSQLChanged: TNotifyEvent;
155 >    FUniqueParamNames: Boolean;
156 >    FBOF: boolean;
157 >    FEOF: boolean;
158      function GetFieldCount: integer;
159 +    function GetOpen: Boolean;
160 +    function GetPrepared: Boolean;
161 +    function GetSQLStatementType: TIBSQLStatementTypes;
162 +    procedure SetUniqueParamNames(AValue: Boolean);
163    protected
164      FBase: TIBBase;
165 <    FBOF,                          { At BOF? }
277 <    FEOF,                          { At EOF? }
278 <    FGoToFirstRecordOnExecute,     { Automatically position record on first record after executing }
279 <    FOpen,                         { Is a cursor open? }
280 <    FPrepared: Boolean;            { Has the query been prepared? }
165 >    FGoToFirstRecordOnExecute: boolean;     { Automatically position record on first record after executing }
166      FRecordCount: Integer;         { How many records have been read so far? }
282    FCursor: String;               { Cursor name...}
283    FHandle: TISC_STMT_HANDLE;     { Once prepared, this accesses the SQL Query }
167      FOnSQLChanging: TNotifyEvent;  { Call this when the SQL is changing }
168      FSQL: TStrings;                { SQL Query (by user) }
169      FParamCheck: Boolean;          { Check for parameters? (just like TQuery) }
170 <    FProcessedSQL: TStrings;       { SQL Query (pre-processed for param labels) }
171 <    FSQLParams,                    { Any parameters to the query }
289 <    FSQLRecord: TIBXSQLDA;         { The current record }
290 <    FSQLType: TIBSQLTypes;         { Select, update, delete, insert, create, alter, etc...}
170 >    FResults: IResults;            {Single row results from exec}
171 >    FResultSet: IResultSet;        {Multi-row results from open cursor}
172      FGenerateParamNames: Boolean;  { Auto generate param names ?}
173      procedure DoBeforeDatabaseDisconnect(Sender: TObject);
174      function GetDatabase: TIBDatabase;
294    function GetDBHandle: PISC_DB_HANDLE;
175      function GetEOF: Boolean;
176 <    function GetFields(const Idx: Integer): TIBXSQLVAR;
176 >    function GetFields(const Idx: Integer): ISQLData;
177      function GetFieldIndex(FieldName: String): Integer;
178      function GetPlan: String;
179      function GetRecordCount: Integer;
180      function GetRowsAffected: Integer;
181 <    function GetSQLParams: TIBXSQLDA;
181 >    function GetSQLParams: ISQLParams;
182      function GetTransaction: TIBTransaction;
303    function GetTRHandle: PISC_TR_HANDLE;
304    procedure PreprocessSQL;
183      procedure SetDatabase(Value: TIBDatabase);
184      procedure SetSQL(Value: TStrings);
185      procedure SetTransaction(Value: TIBTransaction);
186      procedure SQLChanging(Sender: TObject);
187 <    procedure BeforeTransactionEnd(Sender: TObject);
187 >    procedure SQLChanged(Sender: TObject);
188 >    procedure BeforeTransactionEnd(Sender: TObject; Action: TTransactionAction);
189    public
190      constructor Create(AOwner: TComponent); override;
191      destructor Destroy; override;
192      procedure BatchInput(InputObject: TIBBatchInput);
193      procedure BatchOutput(OutputObject: TIBBatchOutput);
315    function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
194      procedure CheckClosed;           { raise error if query is not closed. }
195      procedure CheckOpen;             { raise error if query is not open.}
196      procedure CheckValidStatement;   { raise error if statement is invalid.}
197      procedure Close;
320    function Current: TIBXSQLDA;
198      procedure ExecQuery;
199 <    function FieldByName(FieldName: String): TIBXSQLVAR;
200 <    function ParamByName(ParamName: String): TIBXSQLVAR;
199 >    function HasField(FieldName: String): boolean;
200 >    function FieldByName(FieldName: String): ISQLData;
201 >    function ParamByName(ParamName: String): ISQLParam;
202      procedure FreeHandle;
203 <    function Next: TIBXSQLDA;
203 >    function Next: boolean;
204      procedure Prepare;
205      function GetUniqueRelationName: String;
206      property Bof: Boolean read FBOF;
329    property DBHandle: PISC_DB_HANDLE read GetDBHandle;
207      property Eof: Boolean read GetEOF;
208 <    property Fields[const Idx: Integer]: TIBXSQLVAR read GetFields;
208 >    property Current: IResults read FResults;
209 >    property Fields[const Idx: Integer]: ISQLData read GetFields; default;
210      property FieldIndex[FieldName: String]: Integer read GetFieldIndex;
211      property FieldCount: integer read GetFieldCount;
212 <    property Open: Boolean read FOpen;
213 <    property Params: TIBXSQLDA read GetSQLParams;
212 >    property Open: Boolean read GetOpen;
213 >    property Params: ISQLParams read GetSQLParams;
214      property Plan: String read GetPlan;
215 <    property Prepared: Boolean read FPrepared;
215 >    property Prepared: Boolean read GetPrepared;
216      property RecordCount: Integer read GetRecordCount;
217      property RowsAffected: Integer read GetRowsAffected;
218 <    property SQLType: TIBSQLTypes read FSQLType;
341 <    property TRHandle: PISC_TR_HANDLE read GetTRHandle;
342 <    property Handle: TISC_STMT_HANDLE read FHandle;
343 <    property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
218 >    property SQLStatementType: TIBSQLStatementTypes read GetSQLStatementType;
219      property UniqueRelationName: String read GetUniqueRelationName;
220 +    property Statement: IStatement read FStatement;
221 +    property MetaData: IMetaData read FMetaData;
222    published
223      property Database: TIBDatabase read GetDatabase write SetDatabase;
224 +    property GenerateParamNames: Boolean read FGenerateParamNames write FGenerateParamNames;
225 +    property UniqueParamNames: Boolean read FUniqueParamNames write SetUniqueParamNames;
226      property GoToFirstRecordOnExecute: Boolean read FGoToFirstRecordOnExecute
227                                                 write FGoToFirstRecordOnExecute
228                                                 default True;
# Line 351 | Line 230 | type
230      property SQL: TStrings read FSQL write SetSQL;
231      property Transaction: TIBTransaction read GetTransaction write SetTransaction;
232      property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
233 +    property OnSQLChanged: TNotifyEvent read FOnSQLChanged write FOnSQLChanged;
234    end;
235  
236 + procedure IBAlloc(var P; OldSize, NewSize: Integer);
237 +
238   implementation
239  
240   uses
241 <  IBIntf, IBBlob, Variants , IBSQLMonitor;
360 <
361 < { TIBXSQLVAR }
362 < constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
363 < begin
364 <  inherited Create;
365 <  FParent := Parent;
366 <  FSQL := Query;
367 < end;
368 <
369 < procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
370 < var
371 <  szBuff: PChar;
372 <  s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
373 <  bSourceBlob, bDestBlob: Boolean;
374 <  iSegs: Int64;
375 <  iMaxSeg: Int64;
376 <  iSize: Int64;
377 <  iBlobType: Short;
378 < begin
379 <  szBuff := nil;
380 <  bSourceBlob := True;
381 <  bDestBlob := True;
382 <  s_bhandle := nil;
383 <  d_bhandle := nil;
384 <  try
385 <    if (Source.IsNull) then
386 <    begin
387 <      IsNull := True;
388 <      exit;
389 <    end
390 <    else
391 <      if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
392 <         (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
393 <        exit; { arrays not supported }
394 <    if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
395 <       (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
396 <    begin
397 <      AsXSQLVAR := Source.AsXSQLVAR;
398 <      exit;
399 <    end
400 <    else
401 <      if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
402 <      begin
403 <        szBuff := nil;
404 <        IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
405 <        Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
406 <        bSourceBlob := False;
407 <        iSize := Source.FXSQLVAR^.sqllen;
408 <      end
409 <      else
410 <        if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
411 <          bDestBlob := False;
412 <
413 <    if bSourceBlob then
414 <    begin
415 <      { read the blob }
416 <      Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
417 <        Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
418 <        0, nil), True);
419 <      try
420 <        IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
421 <          iBlobType);
422 <        szBuff := nil;
423 <        IBAlloc(szBuff, 0, iSize);
424 <        IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
425 <      finally
426 <        Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
427 <      end;
428 <    end;
429 <
430 <    if bDestBlob then
431 <    begin
432 <      { write the blob }
433 <      FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
434 <        FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
435 <        0, nil), True);
436 <      try
437 <        IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
438 <        isNull := false
439 <      finally
440 <        FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
441 <      end;
442 <    end
443 <    else
444 <    begin
445 <      { just copy the buffer }
446 <      FXSQLVAR.sqltype := SQL_TEXT;
447 <      FXSQLVAR.sqllen := iSize;
448 <      IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
449 <      Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
450 <    end;
451 <  finally
452 <    FreeMem(szBuff);
453 <  end;
454 < end;
455 <
456 < function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
457 < var
458 <  Scaling : Int64;
459 <  i: Integer;
460 <  Val: Double;
461 < begin
462 <  Scaling := 1; Val := Value;
463 <  if Scale > 0 then
464 <  begin
465 <    for i := 1 to Scale do
466 <      Scaling := Scaling * 10;
467 <    result := Val * Scaling;
468 <  end
469 <  else
470 <    if Scale < 0 then
471 <    begin
472 <      for i := -1 downto Scale do
473 <        Scaling := Scaling * 10;
474 <      result := Val / Scaling;
475 <    end
476 <    else
477 <      result := Val;
478 < end;
479 <
480 < function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
481 < var
482 <  Scaling : Int64;
483 <  i: Integer;
484 <  Val: Int64;
485 < begin
486 <  Scaling := 1; Val := Value;
487 <  if Scale > 0 then begin
488 <    for i := 1 to Scale do Scaling := Scaling * 10;
489 <    result := Val * Scaling;
490 <  end else if Scale < 0 then begin
491 <    for i := -1 downto Scale do Scaling := Scaling * 10;
492 <    result := Val div Scaling;
493 <  end else
494 <    result := Val;
495 < end;
496 <
497 < function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
498 < var
499 <  Scaling : Int64;
500 <  i : Integer;
501 <  FractionText, PadText, CurrText: string;
502 < begin
503 <  Result := 0;
504 <  Scaling := 1;
505 <  if Scale > 0 then
506 <  begin
507 <    for i := 1 to Scale do
508 <      Scaling := Scaling * 10;
509 <    result := Value * Scaling;
510 <  end
511 <  else
512 <    if Scale < 0 then
513 <    begin
514 <      for i := -1 downto Scale do
515 <        Scaling := Scaling * 10;
516 <      FractionText := IntToStr(abs(Value mod Scaling));
517 <      for i := Length(FractionText) to -Scale -1 do
518 <        PadText := '0' + PadText;
519 <      if Value < 0 then
520 <        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
521 <      else
522 <        CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
523 <      try
524 <        result := StrToCurr(CurrText);
525 <      except
526 <        on E: Exception do
527 <          IBError(ibxeInvalidDataConversion, [nil]);
528 <      end;
529 <    end
530 <    else
531 <      result := Value;
532 < end;
533 <
534 < function TIBXSQLVAR.GetAsCurrency: Currency;
535 < begin
536 <  result := 0;
537 <  if FSQL.Database.SQLDialect < 3 then
538 <    result := GetAsDouble
539 <  else begin
540 <    if not IsNull then
541 <      case FXSQLVAR^.sqltype and (not 1) of
542 <        SQL_TEXT, SQL_VARYING: begin
543 <          try
544 <            result := StrtoCurr(AsString);
545 <          except
546 <            on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
547 <          end;
548 <        end;
549 <        SQL_SHORT:
550 <          result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
551 <                                      FXSQLVAR^.sqlscale);
552 <        SQL_LONG:
553 <          result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
554 <                                      FXSQLVAR^.sqlscale);
555 <        SQL_INT64:
556 <          result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
557 <                                      FXSQLVAR^.sqlscale);
558 <        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
559 <          result := Trunc(AsDouble);
560 <        else
561 <          IBError(ibxeInvalidDataConversion, [nil]);
562 <      end;
563 <    end;
564 < end;
565 <
566 < function TIBXSQLVAR.GetAsInt64: Int64;
567 < begin
568 <  result := 0;
569 <  if not IsNull then
570 <    case FXSQLVAR^.sqltype and (not 1) of
571 <      SQL_TEXT, SQL_VARYING: begin
572 <        try
573 <          result := StrToInt64(AsString);
574 <        except
575 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
576 <        end;
577 <      end;
578 <      SQL_SHORT:
579 <        result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
580 <                                    FXSQLVAR^.sqlscale);
581 <      SQL_LONG:
582 <        result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
583 <                                    FXSQLVAR^.sqlscale);
584 <      SQL_INT64:
585 <        result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
586 <                                    FXSQLVAR^.sqlscale);
587 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
588 <        result := Trunc(AsDouble);
589 <      else
590 <        IBError(ibxeInvalidDataConversion, [nil]);
591 <    end;
592 < end;
593 <
594 < function TIBXSQLVAR.GetAsDateTime: TDateTime;
595 < var
596 <  tm_date: TCTimeStructure;
597 <  msecs: word;
598 < begin
599 <  result := 0;
600 <  if not IsNull then
601 <    case FXSQLVAR^.sqltype and (not 1) of
602 <      SQL_TEXT, SQL_VARYING: begin
603 <        try
604 <          result := StrToDate(AsString);
605 <        except
606 <          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
607 <        end;
608 <      end;
609 <      SQL_TYPE_DATE: begin
610 <        isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
611 <        try
612 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
613 <                               Word(tm_date.tm_mday));
614 <        except
615 <          on E: EConvertError do begin
616 <            IBError(ibxeInvalidDataConversion, [nil]);
617 <          end;
618 <        end;
619 <      end;
620 <      SQL_TYPE_TIME: begin
621 <        isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
622 <        try
623 <          msecs :=  (PISC_TIME(FXSQLVAR^.sqldata)^ mod 10000) div 10;
624 <          result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
625 <                               Word(tm_date.tm_sec), msecs)
626 <        except
627 <          on E: EConvertError do begin
628 <            IBError(ibxeInvalidDataConversion, [nil]);
629 <          end;
630 <        end;
631 <      end;
632 <      SQL_TIMESTAMP: begin
633 <        isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
634 <        try
635 <          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
636 <                              Word(tm_date.tm_mday));
637 <          msecs := (PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time mod 10000) div 10;
638 <          if result >= 0 then
639 <            result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
640 <                                          Word(tm_date.tm_sec), msecs)
641 <          else
642 <            result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
643 <                                          Word(tm_date.tm_sec), msecs)
644 <        except
645 <          on E: EConvertError do begin
646 <            IBError(ibxeInvalidDataConversion, [nil]);
647 <          end;
648 <        end;
649 <      end;
650 <      else
651 <        IBError(ibxeInvalidDataConversion, [nil]);
652 <    end;
653 < end;
654 <
655 < function TIBXSQLVAR.GetAsDouble: Double;
656 < begin
657 <  result := 0;
658 <  if not IsNull then begin
659 <    case FXSQLVAR^.sqltype and (not 1) of
660 <      SQL_TEXT, SQL_VARYING: begin
661 <        try
662 <          result := StrToFloat(AsString);
663 <        except
664 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
665 <        end;
666 <      end;
667 <      SQL_SHORT:
668 <        result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
669 <                              FXSQLVAR^.sqlscale);
670 <      SQL_LONG:
671 <        result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
672 <                              FXSQLVAR^.sqlscale);
673 <      SQL_INT64:
674 <        result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
675 <      SQL_FLOAT:
676 <        result := PFloat(FXSQLVAR^.sqldata)^;
677 <      SQL_DOUBLE, SQL_D_FLOAT:
678 <        result := PDouble(FXSQLVAR^.sqldata)^;
679 <      else
680 <        IBError(ibxeInvalidDataConversion, [nil]);
681 <    end;
682 <    if  FXSQLVAR^.sqlscale <> 0 then
683 <      result :=
684 <        StrToFloat(FloatToStrF(result, fffixed, 15,
685 <                  Abs(FXSQLVAR^.sqlscale) ));
686 <  end;
687 < end;
688 <
689 < function TIBXSQLVAR.GetAsFloat: Float;
690 < begin
691 <  result := 0;
692 <  try
693 <    result := AsDouble;
694 <  except
695 <    on E: EOverflow do
696 <      IBError(ibxeInvalidDataConversion, [nil]);
697 <  end;
698 < end;
699 <
700 < function TIBXSQLVAR.GetAsLong: Long;
701 < begin
702 <  result := 0;
703 <  if not IsNull then
704 <    case FXSQLVAR^.sqltype and (not 1) of
705 <      SQL_TEXT, SQL_VARYING: begin
706 <        try
707 <          result := StrToInt(AsString);
708 <        except
709 <          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
710 <        end;
711 <      end;
712 <      SQL_SHORT:
713 <        result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
714 <                                    FXSQLVAR^.sqlscale));
715 <      SQL_LONG:
716 <        result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
717 <                                    FXSQLVAR^.sqlscale));
718 <      SQL_INT64:
719 <        result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
720 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
721 <        result := Trunc(AsDouble);
722 <      else
723 <        IBError(ibxeInvalidDataConversion, [nil]);
724 <    end;
725 < end;
726 <
727 < function TIBXSQLVAR.GetAsPointer: Pointer;
728 < begin
729 <  if not IsNull then
730 <    result := FXSQLVAR^.sqldata
731 <  else
732 <    result := nil;
733 < end;
734 <
735 < function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
736 < begin
737 <  result.gds_quad_high := 0;
738 <  result.gds_quad_low := 0;
739 <  if not IsNull then
740 <    case FXSQLVAR^.sqltype and (not 1) of
741 <      SQL_BLOB, SQL_ARRAY, SQL_QUAD:
742 <        result := PISC_QUAD(FXSQLVAR^.sqldata)^;
743 <      else
744 <        IBError(ibxeInvalidDataConversion, [nil]);
745 <    end;
746 < end;
747 <
748 < function TIBXSQLVAR.GetAsShort: Short;
749 < begin
750 <  result := 0;
751 <  try
752 <    result := AsLong;
753 <  except
754 <    on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
755 <  end;
756 < end;
757 <
758 <
759 < function TIBXSQLVAR.GetAsString: String;
760 < var
761 <  sz: PChar;
762 <  str_len: Integer;
763 <  ss: TStringStream;
764 < begin
765 <  result := '';
766 <  { Check null, if so return a default string }
767 <  if not IsNull then
768 <    case FXSQLVar^.sqltype and (not 1) of
769 <      SQL_ARRAY:
770 <        result := '(Array)'; {do not localize}
771 <      SQL_BLOB: begin
772 <        ss := TStringStream.Create('');
773 <        try
774 <          SaveToStream(ss);
775 <          result := ss.DataString;
776 <        finally
777 <          ss.Free;
778 <        end;
779 <      end;
780 <      SQL_TEXT, SQL_VARYING: begin
781 <        sz := FXSQLVAR^.sqldata;
782 <        if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
783 <          str_len := FXSQLVar^.sqllen
784 <        else begin
785 <          str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
786 <          Inc(sz, 2);
787 <        end;
788 <        SetString(result, sz, str_len);
789 <        if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
790 <          result := TrimRight(result);
791 <      end;
792 <      SQL_TYPE_DATE:
793 <        case FSQL.Database.SQLDialect of
794 <          1 : result := DateTimeToStr(AsDateTime);
795 <          3 : result := DateToStr(AsDateTime);
796 <        end;
797 <      SQL_TYPE_TIME :
798 <        result := TimeToStr(AsDateTime);
799 <      SQL_TIMESTAMP:
800 <        result := DateTimeToStr(AsDateTime);
801 <      SQL_SHORT, SQL_LONG:
802 <        if FXSQLVAR^.sqlscale = 0 then
803 <          result := IntToStr(AsLong)
804 <        else if FXSQLVAR^.sqlscale >= (-4) then
805 <          result := CurrToStr(AsCurrency)
806 <        else
807 <          result := FloatToStr(AsDouble);
808 <      SQL_INT64:
809 <        if FXSQLVAR^.sqlscale = 0 then
810 <          result := IntToStr(AsInt64)
811 <        else if FXSQLVAR^.sqlscale >= (-4) then
812 <          result := CurrToStr(AsCurrency)
813 <        else
814 <          result := FloatToStr(AsDouble);
815 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
816 <        result := FloatToStr(AsDouble);
817 <      else
818 <        IBError(ibxeInvalidDataConversion, [nil]);
819 <    end;
820 < end;
821 <
822 < function TIBXSQLVAR.GetAsVariant: Variant;
823 < begin
824 <  if IsNull then
825 <    result := NULL
826 <  { Check null, if so return a default string }
827 <  else case FXSQLVar^.sqltype and (not 1) of
828 <      SQL_ARRAY:
829 <        result := '(Array)'; {do not localize}
830 <      SQL_BLOB:
831 <        result := '(Blob)'; {do not localize}
832 <      SQL_TEXT, SQL_VARYING:
833 <        result := AsString;
834 <      SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
835 <        result := AsDateTime;
836 <      SQL_SHORT, SQL_LONG:
837 <        if FXSQLVAR^.sqlscale = 0 then
838 <          result := AsLong
839 <        else if FXSQLVAR^.sqlscale >= (-4) then
840 <          result := AsCurrency
841 <        else
842 <          result := AsDouble;
843 <      SQL_INT64:
844 <        if FXSQLVAR^.sqlscale = 0 then
845 <          IBError(ibxeInvalidDataConversion, [nil])
846 <        else if FXSQLVAR^.sqlscale >= (-4) then
847 <          result := AsCurrency
848 <        else
849 <          result := AsDouble;
850 <      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
851 <        result := AsDouble;
852 <      else
853 <        IBError(ibxeInvalidDataConversion, [nil]);
854 <    end;
855 < end;
856 <
857 < function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
858 < begin
859 <  result := FXSQLVAR;
860 < end;
861 <
862 < function TIBXSQLVAR.GetIsNull: Boolean;
863 < begin
864 <  result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
865 < end;
866 <
867 < function TIBXSQLVAR.GetIsNullable: Boolean;
868 < begin
869 <  result := (FXSQLVAR^.sqltype and 1 = 1);
870 < end;
871 <
872 < procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
873 < var
874 <  fs: TFileStream;
875 < begin
876 <  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
877 <  try
878 <    LoadFromStream(fs);
879 <  finally
880 <    fs.Free;
881 <  end;
882 < end;
883 <
884 < procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
885 < var
886 <  bs: TIBBlobStream;
887 < begin
888 <  bs := TIBBlobStream.Create;
889 <  try
890 <    bs.Mode := bmWrite;
891 <    bs.Database := FSQL.Database;
892 <    bs.Transaction := FSQL.Transaction;
893 <    Stream.Seek(0, soFromBeginning);
894 <    bs.LoadFromStream(Stream);
895 <    bs.Finalize;
896 <    AsQuad := bs.BlobID;
897 <  finally
898 <    bs.Free;
899 <  end;
900 < end;
901 <
902 < procedure TIBXSQLVAR.SaveToFile(const FileName: String);
903 < var
904 <  fs: TFileStream;
905 < begin
906 <  fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
907 <  try
908 <    SaveToStream(fs);
909 <  finally
910 <    fs.Free;
911 <  end;
912 < end;
913 <
914 < procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
915 < var
916 <  bs: TIBBlobStream;
917 < begin
918 <  bs := TIBBlobStream.Create;
919 <  try
920 <    bs.Mode := bmRead;
921 <    bs.Database := FSQL.Database;
922 <    bs.Transaction := FSQL.Transaction;
923 <    bs.BlobID := AsQuad;
924 <    bs.SaveToStream(Stream);
925 <  finally
926 <    bs.Free;
927 <  end;
928 < end;
929 <
930 < function TIBXSQLVAR.GetSize: Integer;
931 < begin
932 <  result := FXSQLVAR^.sqllen;
933 < end;
934 <
935 < function TIBXSQLVAR.GetSQLType: Integer;
936 < begin
937 <  result := FXSQLVAR^.sqltype and (not 1);
938 < end;
939 <
940 < procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
941 < var
942 <  xvar: TIBXSQLVAR;
943 <  i: Integer;
944 < begin
945 <  if FSQL.Database.SQLDialect < 3 then
946 <    AsDouble := Value
947 <  else
948 <  begin
949 <    if IsNullable then
950 <      IsNull := False;
951 <    for i := 0 to FParent.FCount - 1 do
952 <      if FParent.FNames[i] = FName then
953 <      begin
954 <        xvar := FParent[i];
955 <        xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
956 <        xvar.FXSQLVAR^.sqlscale := -4;
957 <        xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
958 <        IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
959 <        PCurrency(xvar.FXSQLVAR^.sqldata)^ := Value;
960 <        xvar.FModified := True;
961 <      end;
962 <  end;
963 < end;
964 <
965 < procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
966 < var
967 <  i: Integer;
968 <  xvar: TIBXSQLVAR;
969 < begin
970 <  if IsNullable then
971 <    IsNull := False;
972 <  for i := 0 to FParent.FCount - 1 do
973 <    if FParent.FNames[i] = FName then
974 <    begin
975 <      xvar := FParent[i];
976 <      xvar.FXSQLVAR^.sqltype := SQL_INT64 or (xvar.FXSQLVAR^.sqltype and 1);
977 <      xvar.FXSQLVAR^.sqlscale := 0;
978 <      xvar.FXSQLVAR^.sqllen := SizeOf(Int64);
979 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
980 <      PInt64(xvar.FXSQLVAR^.sqldata)^ := Value;
981 <      xvar.FModified := True;
982 <    end;
983 < end;
984 <
985 < procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
986 < var
987 <  i: Integer;
988 <  tm_date: TCTimeStructure;
989 <  Yr, Mn, Dy: Word;
990 <  xvar: TIBXSQLVAR;
991 < begin
992 <  if FSQL.Database.SQLDialect < 3 then
993 <  begin
994 <    AsDateTime := Value;
995 <    exit;
996 <  end;
997 <  if IsNullable then
998 <    IsNull := False;
999 <  for i := 0 to FParent.FCount - 1 do
1000 <    if FParent.FNames[i] = FName then
1001 <    begin
1002 <      xvar := FParent[i];
1003 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_DATE or (xvar.FXSQLVAR^.sqltype and 1);
1004 <      DecodeDate(Value, Yr, Mn, Dy);
1005 <      with tm_date do begin
1006 <        tm_sec := 0;
1007 <        tm_min := 0;
1008 <        tm_hour := 0;
1009 <        tm_mday := Dy;
1010 <        tm_mon := Mn - 1;
1011 <        tm_year := Yr - 1900;
1012 <      end;
1013 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
1014 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1015 <      isc_encode_sql_date(@tm_date, PISC_DATE(xvar.FXSQLVAR^.sqldata));
1016 <      xvar.FModified := True;
1017 <    end;
1018 < end;
1019 <
1020 < procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1021 < var
1022 <  i: Integer;
1023 <  tm_date: TCTimeStructure;
1024 <  Hr, Mt, S, Ms: Word;
1025 <  xvar: TIBXSQLVAR;
1026 < begin
1027 <  if FSQL.Database.SQLDialect < 3 then
1028 <  begin
1029 <    AsDateTime := Value;
1030 <    exit;
1031 <  end;
1032 <  if IsNullable then
1033 <    IsNull := False;
1034 <  for i := 0 to FParent.FCount - 1 do
1035 <    if FParent.FNames[i] = FName then
1036 <    begin
1037 <      xvar := FParent[i];
1038 <      xvar.FXSQLVAR^.sqltype := SQL_TYPE_TIME or (xvar.FXSQLVAR^.sqltype and 1);
1039 <      DecodeTime(Value, Hr, Mt, S, Ms);
1040 <      with tm_date do begin
1041 <        tm_sec := S;
1042 <        tm_min := Mt;
1043 <        tm_hour := Hr;
1044 <        tm_mday := 0;
1045 <        tm_mon := 0;
1046 <        tm_year := 0;
1047 <      end;
1048 <      xvar.FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1049 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1050 <      isc_encode_sql_time(@tm_date, PISC_TIME(xvar.FXSQLVAR^.sqldata));
1051 <      if Ms > 0 then
1052 <        Inc(PISC_TIME(xvar.FXSQLVAR^.sqldata)^,Ms*10);
1053 <      xvar.FModified := True;
1054 <    end;
1055 < end;
1056 <
1057 < procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1058 < var
1059 <  i: Integer;
1060 <  tm_date: TCTimeStructure;
1061 <  Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1062 <  xvar: TIBXSQLVAR;
1063 < begin
1064 <  if IsNullable then
1065 <    IsNull := False;
1066 <  for i := 0 to FParent.FCount - 1 do
1067 <    if FParent.FNames[i] = FName then
1068 <    begin
1069 <      xvar := FParent[i];
1070 <      xvar.FXSQLVAR^.sqltype := SQL_TIMESTAMP or (xvar.FXSQLVAR^.sqltype and 1);
1071 <      DecodeDate(Value, Yr, Mn, Dy);
1072 <      DecodeTime(Value, Hr, Mt, S, Ms);
1073 <      with tm_date do begin
1074 <        tm_sec := S;
1075 <        tm_min := Mt;
1076 <        tm_hour := Hr;
1077 <        tm_mday := Dy;
1078 <        tm_mon := Mn - 1;
1079 <        tm_year := Yr - 1900;
1080 <      end;
1081 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1082 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1083 <      isc_encode_date(@tm_date, PISC_QUAD(xvar.FXSQLVAR^.sqldata));
1084 <      if Ms > 0 then
1085 <        Inc(PISC_TIMESTAMP(xvar.FXSQLVAR^.sqldata)^.timestamp_time,Ms*10);
1086 <      xvar.FModified := True;
1087 <    end;
1088 < end;
1089 <
1090 < procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1091 < var
1092 <  i: Integer;
1093 <  xvar: TIBXSQLVAR;
1094 < begin
1095 <  if IsNullable then
1096 <    IsNull := False;
1097 <  for i := 0 to FParent.FCount - 1 do
1098 <    if FParent.FNames[i] = FName then
1099 <    begin
1100 <      xvar := FParent[i];
1101 <      xvar.FXSQLVAR^.sqltype := SQL_DOUBLE or (xvar.FXSQLVAR^.sqltype and 1);
1102 <      xvar.FXSQLVAR^.sqllen := SizeOf(Double);
1103 <      xvar.FXSQLVAR^.sqlscale := 0;
1104 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1105 <      PDouble(xvar.FXSQLVAR^.sqldata)^ := Value;
1106 <      xvar.FModified := True;
1107 <    end;
1108 < end;
1109 <
1110 < procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1111 < var
1112 <  i: Integer;
1113 <  xvar: TIBXSQLVAR;
1114 < begin
1115 <  if IsNullable then
1116 <    IsNull := False;
1117 <  for i := 0 to FParent.FCount - 1 do
1118 <    if FParent.FNames[i] = FName then
1119 <    begin
1120 <      xvar := FParent[i];
1121 <      xvar.FXSQLVAR^.sqltype := SQL_FLOAT or (xvar.FXSQLVAR^.sqltype and 1);
1122 <      xvar.FXSQLVAR^.sqllen := SizeOf(Float);
1123 <      xvar.FXSQLVAR^.sqlscale := 0;
1124 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1125 <      PSingle(xvar.FXSQLVAR^.sqldata)^ := Value;
1126 <      xvar.FModified := True;
1127 <    end;
1128 < end;
1129 <
1130 < procedure TIBXSQLVAR.SetAsLong(Value: Long);
1131 < var
1132 <  i: Integer;
1133 <  xvar: TIBXSQLVAR;
1134 < begin
1135 <  if IsNullable then
1136 <    IsNull := False;
1137 <  for i := 0 to FParent.FCount - 1 do
1138 <    if FParent.FNames[i] = FName then
1139 <    begin
1140 <      xvar := FParent[i];
1141 <      xvar.FXSQLVAR^.sqltype := SQL_LONG or (xvar.FXSQLVAR^.sqltype and 1);
1142 <      xvar.FXSQLVAR^.sqllen := SizeOf(Long);
1143 <      xvar.FXSQLVAR^.sqlscale := 0;
1144 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1145 <      PLong(xvar.FXSQLVAR^.sqldata)^ := Value;
1146 <      xvar.FModified := True;
1147 <    end;
1148 < end;
1149 <
1150 < procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1151 < var
1152 <  i: Integer;
1153 <  xvar: TIBXSQLVAR;
1154 < begin
1155 <  if IsNullable and (Value = nil) then
1156 <    IsNull := True
1157 <  else begin
1158 <    IsNull := False;
1159 <    for i := 0 to FParent.FCount - 1 do
1160 <      if FParent.FNames[i] = FName then
1161 <      begin
1162 <        xvar := FParent[i];
1163 <        xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1164 <        Move(Value^, xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1165 <        xvar.FModified := True;
1166 <      end;
1167 <  end;
1168 < end;
1169 <
1170 < procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1171 < var
1172 <  i: Integer;
1173 <  xvar: TIBXSQLVAR;
1174 < begin
1175 <  if IsNullable then
1176 <    IsNull := False;
1177 <  for i := 0 to FParent.FCount - 1 do
1178 <    if FParent.FNames[i] = FName then
1179 <    begin
1180 <      xvar := FParent[i];
1181 <      if (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1182 <         (xvar.FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1183 <        IBError(ibxeInvalidDataConversion, [nil]);
1184 <      xvar.FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1185 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1186 <      PISC_QUAD(xvar.FXSQLVAR^.sqldata)^ := Value;
1187 <      xvar.FModified := True;
1188 <    end;
1189 < end;
1190 <
1191 < procedure TIBXSQLVAR.SetAsShort(Value: Short);
1192 < var
1193 <  i: Integer;
1194 <  xvar: TIBXSQLVAR;
1195 < begin
1196 <  if IsNullable then
1197 <    IsNull := False;
1198 <  for i := 0 to FParent.FCount - 1 do
1199 <    if FParent.FNames[i] = FName then
1200 <    begin
1201 <      xvar := FParent[i];
1202 <      xvar.FXSQLVAR^.sqltype := SQL_SHORT or (xvar.FXSQLVAR^.sqltype and 1);
1203 <      xvar.FXSQLVAR^.sqllen := SizeOf(Short);
1204 <      xvar.FXSQLVAR^.sqlscale := 0;
1205 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen);
1206 <      PShort(xvar.FXSQLVAR^.sqldata)^ := Value;
1207 <      xvar.FModified := True;
1208 <    end;
1209 < end;
1210 <
1211 < procedure TIBXSQLVAR.SetAsString(Value: String);
1212 < var
1213 <  stype: Integer;
1214 <  ss: TStringStream;
1215 <
1216 <  procedure SetStringValue;
1217 <  var
1218 <    i: Integer;
1219 <    xvar: TIBXSQLVAR;
1220 <  begin
1221 <    for i := 0 to FParent.FCount - 1 do
1222 <      if FParent.FNames[i] = FName then
1223 <      begin
1224 <        xvar := FParent[i];
1225 <        if (xvar.FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1226 <           (xvar.FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1227 <          Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen)
1228 <        else begin
1229 <          xvar.FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1230 <          xvar.FXSQLVAR^.sqllen := Length(Value);
1231 <          IBAlloc(xvar.FXSQLVAR^.sqldata, 0, xvar.FXSQLVAR^.sqllen + 1);
1232 <          if (Length(Value) > 0) then
1233 <            Move(Value[1], xvar.FXSQLVAR^.sqldata^, xvar.FXSQLVAR^.sqllen);
1234 <        end;
1235 <        xvar.FModified := True;
1236 <      end;
1237 <  end;
1238 <
1239 < begin
1240 <  if IsNullable then
1241 <    IsNull := False;
1242 <  stype := FXSQLVAR^.sqltype and (not 1);
1243 <  if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1244 <    SetStringValue
1245 <  else begin
1246 <    if (stype = SQL_BLOB) then
1247 <    begin
1248 <      ss := TStringStream.Create(Value);
1249 <      try
1250 <        LoadFromStream(ss);
1251 <      finally
1252 <        ss.Free;
1253 <      end;
1254 <    end
1255 <    else if Value = '' then
1256 <      IsNull := True
1257 <    else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1258 <      (stype = SQL_TYPE_TIME) then
1259 <      SetAsDateTime(StrToDateTime(Value))
1260 <    else
1261 <      SetStringValue;
1262 <  end;
1263 < end;
1264 <
1265 < procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1266 < begin
1267 <  if VarIsNull(Value) then
1268 <    IsNull := True
1269 <  else case VarType(Value) of
1270 <    varEmpty, varNull:
1271 <      IsNull := True;
1272 <    varSmallint, varInteger, varByte:
1273 <      AsLong := Value;
1274 <    varSingle, varDouble:
1275 <      AsDouble := Value;
1276 <    varCurrency:
1277 <      AsCurrency := Value;
1278 <    varBoolean:
1279 <      if Value then
1280 <        AsLong := ISC_TRUE
1281 <      else
1282 <        AsLong := ISC_FALSE;
1283 <    varDate:
1284 <      AsDateTime := Value;
1285 <    varOleStr, varString:
1286 <      AsString := Value;
1287 <    varArray:
1288 <      IBError(ibxeNotSupported, [nil]);
1289 <    varByRef, varDispatch, varError, varUnknown, varVariant:
1290 <      IBError(ibxeNotPermitted, [nil]);
1291 <  end;
1292 < end;
1293 <
1294 < procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1295 < var
1296 <  i: Integer;
1297 <  xvar: TIBXSQLVAR;
1298 <  sqlind: PShort;
1299 <  sqldata: PChar;
1300 <  local_sqllen: Integer;
1301 < begin
1302 <  for i := 0 to FParent.FCount - 1 do
1303 <    if FParent.FNames[i] = FName then
1304 <    begin
1305 <      xvar := FParent[i];
1306 <      sqlind := xvar.FXSQLVAR^.sqlind;
1307 <      sqldata := xvar.FXSQLVAR^.sqldata;
1308 <      Move(Value^, xvar.FXSQLVAR^, SizeOf(TXSQLVAR));
1309 <      xvar.FXSQLVAR^.sqlind := sqlind;
1310 <      xvar.FXSQLVAR^.sqldata := sqldata;
1311 <      if (Value^.sqltype and 1 = 1) then
1312 <      begin
1313 <        if (xvar.FXSQLVAR^.sqlind = nil) then
1314 <          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1315 <        xvar.FXSQLVAR^.sqlind^ := Value^.sqlind^;
1316 <      end
1317 <      else
1318 <        if (xvar.FXSQLVAR^.sqlind <> nil) then
1319 <          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1320 <      if ((xvar.FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1321 <        local_sqllen := xvar.FXSQLVAR^.sqllen + 2
1322 <      else
1323 <        local_sqllen := xvar.FXSQLVAR^.sqllen;
1324 <      FXSQLVAR^.sqlscale := Value^.sqlscale;
1325 <      IBAlloc(xvar.FXSQLVAR^.sqldata, 0, local_sqllen);
1326 <      Move(Value^.sqldata[0], xvar.FXSQLVAR^.sqldata[0], local_sqllen);
1327 <      xvar.FModified := True;
1328 <    end;
1329 < end;
1330 <
1331 < procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
1332 < var
1333 <  i: Integer;
1334 <  xvar: TIBXSQLVAR;
1335 < begin
1336 <  if Value then
1337 <  begin
1338 <    if not IsNullable then
1339 <      IsNullable := True;
1340 <    for i := 0 to FParent.FCount - 1 do
1341 <      if FParent.FNames[i] = FName then
1342 <      begin
1343 <        xvar := FParent[i];
1344 <        if Assigned(xvar.FXSQLVAR^.sqlind) then
1345 <          xvar.FXSQLVAR^.sqlind^ := -1;
1346 <        xvar.FModified := True;
1347 <      end;
1348 <  end
1349 <  else
1350 <    if ((not Value) and IsNullable) then
1351 <    begin
1352 <      for i := 0 to FParent.FCount - 1 do
1353 <        if FParent.FNames[i] = FName then
1354 <        begin
1355 <          xvar := FParent[i];
1356 <          if Assigned(xvar.FXSQLVAR^.sqlind) then
1357 <            xvar.FXSQLVAR^.sqlind^ := 0;
1358 <          xvar.FModified := True;
1359 <        end;
1360 <    end;
1361 < end;
1362 <
1363 < procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1364 < var
1365 <  i: Integer;
1366 <  xvar: TIBXSQLVAR;
1367 < begin
1368 <  for i := 0 to FParent.FCount - 1 do
1369 <    if FParent.FNames[i] = FName then
1370 <    begin
1371 <      xvar := FParent[i];
1372 <      if (Value <> IsNullable) then
1373 <      begin
1374 <        if Value then
1375 <        begin
1376 <          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype or 1;
1377 <          IBAlloc(xvar.FXSQLVAR^.sqlind, 0, SizeOf(Short));
1378 <        end
1379 <        else
1380 <        begin
1381 <          xvar.FXSQLVAR^.sqltype := xvar.FXSQLVAR^.sqltype and (not 1);
1382 <          ReallocMem(xvar.FXSQLVAR^.sqlind, 0);
1383 <        end;
1384 <      end;
1385 <    end;
1386 < end;
1387 <
1388 < procedure TIBXSQLVAR.Clear;
1389 < begin
1390 <  IsNull := true;
1391 < end;
1392 <
1393 <
1394 < { TIBXSQLDA }
1395 < constructor TIBXSQLDA.Create(Query: TIBSQL);
1396 < begin
1397 <  inherited Create;
1398 <  FSQL := Query;
1399 <  FNames := TStringList.Create;
1400 <  FSize := 0;
1401 <  FUniqueRelationName := '';
1402 < end;
1403 <
1404 < destructor TIBXSQLDA.Destroy;
1405 < var
1406 <  i: Integer;
1407 < begin
1408 <  FNames.Free;
1409 <  if FXSQLDA <> nil then
1410 <  begin
1411 <    for i := 0 to FSize - 1 do
1412 <    begin
1413 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1414 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1415 <      FXSQLVARs[i].Free ;
1416 <    end;
1417 <    FreeMem(FXSQLDA);
1418 <    FXSQLDA := nil;
1419 <    FXSQLVARs := nil;
1420 <  end;
1421 <  inherited Destroy;
1422 < end;
241 >   Variants, IBSQLMonitor, FBMessages, IBCustomDataSet;
242  
243 < procedure TIBXSQLDA.AddName(FieldName: String; Idx: Integer);
1425 < var
1426 <  fn: String;
1427 < begin
1428 <  fn := FormatIdentifierValue(FSQL.Database.SQLDialect, FieldName);
1429 <  while FNames.Count <= Idx do
1430 <    FNames.Add('');
1431 <  FNames[Idx] := fn;
1432 <  FXSQLVARs[Idx].FName := fn;
1433 <  FXSQLVARs[Idx].FIndex := Idx;
1434 < end;
1435 <
1436 < function TIBXSQLDA.GetModified: Boolean;
243 > procedure IBAlloc(var P; OldSize, NewSize: Integer);
244   var
245    i: Integer;
246   begin
247 <  result := False;
248 <  for i := 0 to FCount - 1 do
1442 <    if FXSQLVARs[i].Modified then
1443 <    begin
1444 <      result := True;
1445 <      exit;
1446 <    end;
1447 < end;
1448 <
1449 < function TIBXSQLDA.GetNames: String;
1450 < begin
1451 <  result := FNames.Text;
1452 < end;
1453 <
1454 < function TIBXSQLDA.GetRecordSize: Integer;
1455 < begin
1456 <  result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1457 < end;
1458 <
1459 < function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1460 < begin
1461 <  result := FXSQLDA;
1462 < end;
1463 <
1464 < function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1465 < begin
1466 <  if (Idx < 0) or (Idx >= FCount) then
1467 <    IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1468 <  result := FXSQLVARs[Idx]
1469 < end;
1470 <
1471 < function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1472 < begin
1473 <  result := GetXSQLVARByName(Idx);
1474 <  if result = nil then
1475 <    IBError(ibxeFieldNotFound, [Idx]);
1476 < end;
1477 <
1478 < function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1479 < var
1480 <  s: String;
1481 <  i, Cnt: Integer;
1482 < begin
1483 <  s := FormatIdentifierValue(FSQL.Database.SQLDialect, Idx);
1484 <  i := 0;
1485 <  Cnt := FNames.Count;
1486 <  while (i < Cnt) and (FNames[i] <> s) do Inc(i);
1487 <  if i = Cnt then
1488 <    result := nil
1489 <  else
1490 <    result := GetXSQLVAR(i);
1491 < end;
1492 <
1493 < procedure TIBXSQLDA.Initialize;
1494 < var
1495 <  i, j, j_len: Integer;
1496 <  NamesWereEmpty: Boolean;
1497 <  st: String;
1498 <  bUnique: Boolean;
1499 < begin
1500 <  bUnique := True;
1501 <  NamesWereEmpty := (FNames.Count = 0);
1502 <  if FXSQLDA <> nil then
1503 <  begin
1504 <    for i := 0 to FCount - 1 do
1505 <    begin
1506 <      with FXSQLVARs[i].Data^ do
1507 <      begin
1508 <        if bUnique and (strpas(relname) <> '') then
1509 <        begin
1510 <          if FUniqueRelationName = '' then
1511 <            FUniqueRelationName := strpas(relname)
1512 <          else
1513 <            if strpas(relname) <> FUniqueRelationName then
1514 <            begin
1515 <              FUniqueRelationName := '';
1516 <              bUnique := False;
1517 <            end;
1518 <        end;
1519 <        if NamesWereEmpty then
1520 <        begin
1521 <          st := strpas(aliasname);
1522 <          if st = '' then
1523 <          begin
1524 <            st := 'F_'; {do not localize}
1525 <            aliasname_length := 2;
1526 <            j := 1; j_len := 1;
1527 <            StrPCopy(aliasname, st + IntToStr(j));
1528 <          end
1529 <          else
1530 <          begin
1531 <            StrPCopy(aliasname, st);
1532 <            j := 0; j_len := 0;
1533 <          end;
1534 <          while GetXSQLVARByName(strpas(aliasname)) <> nil do
1535 <          begin
1536 <            Inc(j); j_len := Length(IntToStr(j));
1537 <            if j_len + aliasname_length > 31 then
1538 <              StrPCopy(aliasname,
1539 <                       Copy(st, 1, 31 - j_len) +
1540 <                       IntToStr(j))
1541 <            else
1542 <              StrPCopy(aliasname, st + IntToStr(j));
1543 <          end;
1544 <          Inc(aliasname_length, j_len);
1545 <          AddName(strpas(aliasname), i);
1546 <        end;
1547 <        case sqltype and (not 1) of
1548 <          SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1549 <          SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1550 <          SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1551 <            if (sqllen = 0) then
1552 <              { Make sure you get a valid pointer anyway
1553 <               select '' from foo }
1554 <              IBAlloc(sqldata, 0, 1)
1555 <            else
1556 <              IBAlloc(sqldata, 0, sqllen)
1557 <          end;
1558 <          SQL_VARYING: begin
1559 <            IBAlloc(sqldata, 0, sqllen + 2);
1560 <          end;
1561 <          else
1562 <            IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1563 <        end;
1564 <        if (sqltype and 1 = 1) then
1565 <          IBAlloc(sqlind, 0, SizeOf(Short))
1566 <        else
1567 <          if (sqlind <> nil) then
1568 <            ReallocMem(sqlind, 0);
1569 <      end;
1570 <    end;
1571 <  end;
1572 < end;
1573 <
1574 < procedure TIBXSQLDA.SetCount(Value: Integer);
1575 < var
1576 <  i, OldSize: Integer;
1577 <  p : PXSQLVAR;
1578 < begin
1579 <  FNames.Clear;
1580 <  FCount := Value;
1581 <  if FCount = 0 then
1582 <    FUniqueRelationName := ''
1583 <  else
1584 <  begin
1585 <    if FSize > 0 then
1586 <      OldSize := XSQLDA_LENGTH(FSize)
1587 <    else
1588 <      OldSize := 0;
1589 <    if FCount > FSize then
1590 <    begin
1591 <      IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1592 <      SetLength(FXSQLVARs, FCount);
1593 <      FXSQLDA^.version := SQLDA_VERSION1;
1594 <      p := @FXSQLDA^.sqlvar[0];
1595 <      for i := 0 to FCount - 1 do
1596 <      begin
1597 <        if i >= FSize then
1598 <          FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1599 <        FXSQLVARs[i].FXSQLVAR := p;
1600 <        p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1601 <      end;
1602 <      FSize := FCount;
1603 <    end;
1604 <    if FSize > 0 then
1605 <    begin
1606 <      FXSQLDA^.sqln := Value;
1607 <      FXSQLDA^.sqld := Value;
1608 <    end;
1609 <  end;
247 >  ReallocMem(Pointer(P), NewSize);
248 >  for i := OldSize to NewSize - 1 do PChar(P)[i] := #0;
249   end;
250  
251   { TIBOutputDelimitedFile }
# Line 1652 | Line 291 | begin
291    begin
292      for i := 0 to Columns.Count - 1 do
293        if i = 0 then
294 <        st := strpas(Columns[i].Data^.aliasname)
294 >        st := Columns[i].GetAliasname
295        else
296 <        st := st + FColDelimiter + strpas(Columns[i].Data^.aliasname);
296 >        st := st + FColDelimiter + Columns[i].GetAliasname;
297      st := st + FRowDelimiter;
298      {$IFDEF UNIX}
299      if FHandle <> -1 then
# Line 1845 | Line 484 | begin
484      for i := 0 to Columns.Count - 1 do
485      begin
486        {$IFDEF UNIX}
487 <      BytesWritten := FpWrite(FHandle,Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen);
487 >      BytesWritten := FpWrite(FHandle,Columns[i].GetAsPointer^, Columns[i].GetSize);
488        {$ELSE}
489 <      WriteFile(FHandle, Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen,
489 >      WriteFile(FHandle, Columns[i].GetAsPointer^, Columns[i].GetSize,
490                  BytesWritten, nil);
491        {$ENDIF}
492 <      if BytesWritten <> DWORD(Columns[i].Data^.sqllen) then
492 >      if BytesWritten <> DWORD(Columns[i].GetSize) then
493          exit;
494      end;
495      result := True;
# Line 1885 | Line 524 | begin
524      for i := 0 to Params.Count - 1 do
525      begin
526        {$IFDEF UNIX}
527 <      BytesRead := FpRead(FHandle,Params[i].Data^.sqldata^,Params[i].Data^.sqllen);
527 >      BytesRead := FpRead(FHandle,Params[i].GetAsPointer^,Params[i].GetSize);
528        {$ELSE}
529 <      ReadFile(FHandle, Params[i].Data^.sqldata^, Params[i].Data^.sqllen,
529 >      ReadFile(FHandle, Params[i].GetAsPointer^, Params[i].GetSize,
530                 BytesRead, nil);
531        {$ENDIF}
532 <      if BytesRead <> DWORD(Params[i].Data^.sqllen) then
532 >      if BytesRead <> DWORD(Params[i].GetSize) then
533          exit;
534      end;
535      result := True;
# Line 1919 | Line 558 | end;
558   constructor TIBSQL.Create(AOwner: TComponent);
559   begin
560    inherited Create(AOwner);
1922  FIBLoaded := False;
1923  CheckIBLoaded;
1924  FIBLoaded := True;
561    FGenerateParamNames := False;
562    FGoToFirstRecordOnExecute := True;
563    FBase := TIBBase.Create(Self);
564    FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
565    FBase.BeforeTransactionEnd := BeforeTransactionEnd;
1930  FBOF := False;
1931  FEOF := False;
1932  FPrepared := False;
566    FRecordCount := 0;
567    FSQL := TStringList.Create;
568    TStringList(FSQL).OnChanging := SQLChanging;
569 <  FProcessedSQL := TStringList.Create;
1937 <  FHandle := nil;
1938 <  FSQLParams := TIBXSQLDA.Create(self);
1939 <  FSQLRecord := TIBXSQLDA.Create(self);
1940 <  FSQLType := SQLUnknown;
569 >  TStringList(FSQL).OnChange := SQLChanged;
570    FParamCheck := True;
1942  FCursor := Name + RandomString(8);
571    if AOwner is TIBDatabase then
572      Database := TIBDatabase(AOwner)
573    else
# Line 1949 | Line 577 | end;
577  
578   destructor TIBSQL.Destroy;
579   begin
580 <  if FIBLoaded then
581 <  begin
582 <    if (FOpen) then
1955 <      Close;
1956 <    if (FHandle <> nil) then
1957 <      FreeHandle;
1958 <    FSQL.Free;
1959 <    FProcessedSQL.Free;
1960 <    FBase.Free;
1961 <    FSQLParams.Free;
1962 <    FSQLRecord.Free;
1963 <  end;
580 >  FreeHandle;
581 >  FSQL.Free;
582 >  FBase.Free;
583    inherited Destroy;
584   end;
585  
# Line 1968 | Line 587 | procedure TIBSQL.BatchInput(InputObject:
587   begin
588    if not Prepared then
589      Prepare;
590 <  InputObject.FParams := Self.FSQLParams;
590 >  InputObject.FParams := Self.GetSQLParams;
591    InputObject.ReadyFile;
592 <  if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
592 >  if GetSQLStatementType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
593      while InputObject.ReadParameters do
594        ExecQuery;
595   end;
# Line 1980 | Line 599 | begin
599    CheckClosed;
600    if not Prepared then
601      Prepare;
602 <  if FSQLType = SQLSelect then begin
602 >  if GetSQLStatementType = SQLSelect then begin
603      try
604        ExecQuery;
605 <      OutputObject.FColumns := Self.FSQLRecord;
605 >      OutputObject.FColumns := Self.FResults;
606        OutputObject.ReadyFile;
607        if not FGoToFirstRecordOnExecute then
608          Next;
# Line 1997 | Line 616 | end;
616  
617   procedure TIBSQL.CheckClosed;
618   begin
619 <  if FOpen then IBError(ibxeSQLOpen, [nil]);
619 >  if FResultSet <> nil  then IBError(ibxeSQLOpen, [nil]);
620   end;
621  
622   procedure TIBSQL.CheckOpen;
623   begin
624 <  if not FOpen then IBError(ibxeSQLClosed, [nil]);
624 >  if FResultSet = nil then IBError(ibxeSQLClosed, [nil]);
625   end;
626  
627   procedure TIBSQL.CheckValidStatement;
628   begin
629    FBase.CheckTransaction;
630 <  if (FHandle = nil) then
630 >  if (FStatement = nil) then
631      IBError(ibxeInvalidStatementHandle, [nil]);
632   end;
633  
634   procedure TIBSQL.Close;
2016 var
2017  isc_res: ISC_STATUS;
635   begin
636 <  try
637 <    if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then begin
638 <      isc_res := Call(
639 <                   isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
640 <                   False);
641 <      if (StatusVector^ = 1) and (isc_res > 0) and
642 <        not CheckStatusVector(
2026 <              [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2027 <        IBDatabaseError;
2028 <    end;
2029 <  finally
2030 <    FEOF := False;
2031 <    FBOF := False;
2032 <    FOpen := False;
2033 <    FRecordCount := 0;
2034 <  end;
636 >  if FResults <> nil then
637 >    FResults.SetRetainInterfaces(false);
638 >  FResultSet := nil;
639 >  FResults := nil;
640 >  FBOF := false;
641 >  FEOF := false;
642 >  FRecordCount := 0;
643   end;
644  
645 < function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
645 > function TIBSQL.GetFieldCount: integer;
646   begin
647 <  result := 0;
648 < if Transaction <> nil then
649 <    result := Transaction.Call(ErrCode, RaiseError)
647 >  if FResults <> nil then
648 >    Result := FResults.GetCount
649 >  else
650 >  if FMetaData <> nil then
651 >    Result := FMetaData.GetCount
652    else
653 <  if RaiseError and (ErrCode > 0) then
2044 <    IBDataBaseError;
653 >    Result := 0;
654   end;
655  
656 < function TIBSQL.Current: TIBXSQLDA;
656 > function TIBSQL.GetOpen: Boolean;
657   begin
658 <  result := FSQLRecord;
658 >  Result := FResultSet <> nil;
659   end;
660  
661 < function TIBSQL.GetFieldCount: integer;
661 > function TIBSQL.GetPrepared: Boolean;
662 > begin
663 >  Result := (FStatement <> nil) and FStatement.IsPrepared;
664 > end;
665 >
666 > function TIBSQL.GetSQLStatementType: TIBSQLStatementTypes;
667   begin
668 <  Result := FSQLRecord.Count
668 >  if FStatement = nil then
669 >    Result := SQLUnknown
670 >  else
671 >    Result := FStatement.GetSQLStatementType;
672 > end;
673 >
674 > procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
675 > begin
676 >  if FUniqueParamNames = AValue then Exit;
677 >  FreeHandle;
678 >  FUniqueParamNames := AValue;
679   end;
680  
681   procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
682   begin
683 <  if (FHandle <> nil) then begin
2060 <    Close;
2061 <    FreeHandle;
2062 <  end;
683 >  FreeHandle;
684   end;
685  
686   procedure TIBSQL.ExecQuery;
# Line 2069 | Line 690 | begin
690    CheckClosed;
691    if not Prepared then Prepare;
692    CheckValidStatement;
693 <  case FSQLType of
694 <    SQLSelect: begin
695 <      Call(isc_dsql_execute2(StatusVector,
696 <                            TRHandle,
697 <                            @FHandle,
698 <                            Database.SQLDialect,
699 <                            FSQLParams.AsXSQLDA,
700 <                            nil), True);
701 <      Call(
702 <        isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
703 <        True);
704 <      FOpen := True;
705 <      FBOF := True;
706 <      FEOF := False;
707 <      FRecordCount := 0;
708 <      if FGoToFirstRecordOnExecute then
709 <        Next;
710 <    end;
2090 <    SQLExecProcedure: begin
2091 <      fetch_res := Call(isc_dsql_execute2(StatusVector,
2092 <                            TRHandle,
2093 <                            @FHandle,
2094 <                            Database.SQLDialect,
2095 <                            FSQLParams.AsXSQLDA,
2096 <                            FSQLRecord.AsXSQLDA), False);
2097 <      if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2098 <      begin
2099 <         { Sometimes a prepared stored procedure appears to get
2100 <           off sync on the server ....This code is meant to try
2101 <           to work around the problem simply by "retrying". This
2102 <           need to be reproduced and fixed.
2103 <         }
2104 <        isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2105 <                         PChar(FProcessedSQL.Text), 1, nil);
2106 <        Call(isc_dsql_execute2(StatusVector,
2107 <                            TRHandle,
2108 <                            @FHandle,
2109 <                            Database.SQLDialect,
2110 <                            FSQLParams.AsXSQLDA,
2111 <                            FSQLRecord.AsXSQLDA), True);
2112 <      end;
2113 <    end
2114 <    else
2115 <      Call(isc_dsql_execute(StatusVector,
2116 <                           TRHandle,
2117 <                           @FHandle,
2118 <                           Database.SQLDialect,
2119 <                           FSQLParams.AsXSQLDA), True)
693 >  if SQLStatementType = SQLSelect then
694 >  begin
695 >    FResultSet := FStatement.OpenCursor;
696 >    FResults := FResultSet;
697 >    FResults.SetRetainInterfaces(true);
698 >    FBOF := True;
699 >    FEOF := False;
700 >    FRecordCount := 0;
701 >    if not (csDesigning in ComponentState) then
702 >      MonitorHook.SQLExecute(Self);
703 >    if FGoToFirstRecordOnExecute then
704 >      Next;
705 >  end
706 >  else
707 >  begin
708 >    FResults := FStatement.Execute;
709 >    if not (csDesigning in ComponentState) then
710 >      MonitorHook.SQLExecute(Self);
711    end;
712 <  if not (csDesigning in ComponentState) then
713 <    MonitorHook.SQLExecute(Self);
712 >  FBase.DoAfterExecQuery(self);
713 > end;
714 >
715 > function TIBSQL.HasField(FieldName: String): boolean;
716 > begin
717 >  if FResults = nil then
718 >    IBError(ibxeNoFieldAccess,[nil]);
719 >
720 >  Result := FResults.ByName(FieldName) <> nil;
721   end;
722  
723   function TIBSQL.GetEOF: Boolean;
724   begin
725 <  result := FEOF or not FOpen;
725 >  result := FEOF or (FResultSet = nil);
726   end;
727  
728 < function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
2131 < var
2132 <  i: Integer;
728 > function TIBSQL.FieldByName(FieldName: String): ISQLData;
729   begin
730 <  i := GetFieldIndex(FieldName);
731 <  if (i < 0) then
730 >  if FResults = nil then
731 >    IBError(ibxeNoFieldAccess,[nil]);
732 >
733 >  Result := FResults.ByName(FieldName);
734 >
735 >  if Result = nil then
736      IBError(ibxeFieldNotFound, [FieldName]);
2137  result := GetFields(i);
737   end;
738  
739 < function TIBSQL.ParamByName(ParamName: String): TIBXSQLVAR;
739 > function TIBSQL.ParamByName(ParamName: String): ISQLParam;
740   begin
741    Result := Params.ByName(ParamName);
742   end;
743  
744 < function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
744 > function TIBSQL.GetFields(const Idx: Integer): ISQLData;
745   begin
746 <  if (Idx < 0) or (Idx >= FSQLRecord.Count) then
746 >  if FResults = nil then
747 >    IBError(ibxeNoFieldAccess,[nil]);
748 >
749 >  if (Idx < 0) or (Idx >= FResults.GetCount) then
750      IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
751 <  result := FSQLRecord[Idx];
751 >  result := FResults[Idx];
752   end;
753  
754   function TIBSQL.GetFieldIndex(FieldName: String): Integer;
755 + var Field: IColumnMetaData;
756   begin
757 <  if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
757 >  if FMetaData = nil then
758 >    IBError(ibxeNoFieldAccess,[nil]);
759 >
760 >  Field := FMetaData.ByName(FieldName);
761 >
762 >  if Field = nil then
763      result := -1
764    else
765 <    result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
765 >    result := Field.GetIndex;
766   end;
767  
768 < function TIBSQL.Next: TIBXSQLDA;
2161 < var
2162 <  fetch_res: ISC_STATUS;
768 > function TIBSQL.Next: boolean;
769   begin
770 <  result := nil;
771 <  if not FEOF then begin
770 >  result := false;
771 >  if not FEOF then
772 >  begin
773      CheckOpen;
774 <    { Go to the next record... }
775 <    fetch_res :=
776 <      Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
777 <    if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then begin
778 <      FEOF := True;
779 <    end else if (fetch_res > 0) then begin
780 <      try
781 <        IBDataBaseError;
782 <      except
2176 <        Close;
2177 <        raise;
2178 <      end;
2179 <    end else begin
774 >    try
775 >      Result := FResultSet.FetchNext;
776 >    except
777 >      Close;
778 >      raise;
779 >    end;
780 >
781 >    if Result then
782 >    begin
783        Inc(FRecordCount);
784        FBOF := False;
785 <      result := FSQLRecord;
786 <    end;
785 >    end
786 >    else
787 >      FEOF := true;
788 >
789      if not (csDesigning in ComponentState) then
790        MonitorHook.SQLFetch(Self);
791    end;
792   end;
793  
794   procedure TIBSQL.FreeHandle;
2190 var
2191  isc_res: ISC_STATUS;
795   begin
796 <  try
797 <    { The following two lines merely set the SQLDA count
798 <     variable FCount to 0, but do not deallocate
799 <     That way the allocations can be reused for
800 <     a new query sring in the same SQL instance }
801 <    FSQLRecord.Count := 0;
802 <    FSQLParams.Count := 0;
803 <    if FHandle <> nil then begin
2201 <      isc_res :=
2202 <        Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2203 <      if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2204 <        IBDataBaseError;
2205 <    end;
2206 <  finally
2207 <    FPrepared := False;
2208 <    FHandle := nil;
2209 <  end;
796 >  if FStatement <> nil then
797 >    FStatement.SetRetainInterfaces(false);
798 >  Close;
799 >  FStatement := nil;
800 >  FResults := nil;
801 >  FResultSet := nil;
802 >  FMetaData := nil;
803 >  FSQLParams := nil;
804   end;
805  
806   function TIBSQL.GetDatabase: TIBDatabase;
# Line 2214 | Line 808 | begin
808    result := FBase.Database;
809   end;
810  
2217 function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2218 begin
2219  result := FBase.DBHandle;
2220 end;
2221
811   function TIBSQL.GetPlan: String;
2223 var
2224  result_buffer: array[0..16384] of Char;
2225  result_length, i: Integer;
2226  info_request: Char;
812   begin
813    if (not Prepared) or
814 <     (not (FSQLType in [SQLSelect, SQLSelectForUpdate,
814 >     (not (GetSQLStatementType in [SQLSelect, SQLSelectForUpdate,
815         {TODO: SQLExecProcedure, }
816         SQLUpdate, SQLDelete])) then
817      result := ''
818 <  else begin
819 <    info_request := Char(isc_info_sql_get_plan);
2235 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2236 <                           SizeOf(result_buffer), result_buffer), True);
2237 <    if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2238 <      IBError(ibxeUnknownError, [nil]);
2239 <    result_length := isc_vax_integer(@result_buffer[1], 2);
2240 <    SetString(result, nil, result_length);
2241 <    for i := 1 to result_length do
2242 <      result[i] := result_buffer[i + 2];
2243 <    result := Trim(result);
2244 <  end;
818 >  else
819 >    Result := FStatement.GetPlan;
820   end;
821  
822   function TIBSQL.GetRecordCount: Integer;
823   begin
824 <  result := FRecordCount;
824 >  Result := FRecordCount;
825   end;
826  
827 < function TIBSQL.GetRowsAffected: integer;
827 > function TIBSQL.GetRowsAffected: Integer;
828   var
829 <  result_buffer: array[0..1048] of Char;
2255 <  info_request: Char;
829 >  SelectCount, InsertCount, UpdateCount, DeleteCount: integer;
830   begin
831    if not Prepared then
832 <    result := -1
833 <  else begin
834 <    info_request := Char(isc_info_sql_records);
835 <    if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
836 <                         SizeOf(result_buffer), result_buffer) > 0 then
2263 <      IBDatabaseError;
2264 <    if (result_buffer[0] <> Char(isc_info_sql_records)) then
2265 <      result := -1
2266 <    else
2267 <    case SQLType of
2268 <    SQLUpdate:   Result := isc_vax_integer(@result_buffer[6], 4);
2269 <    SQLDelete:   Result := isc_vax_integer(@result_buffer[13], 4);
2270 <    SQLInsert:   Result := isc_vax_integer(@result_buffer[27], 4);
2271 <    else         Result := -1 ;
2272 <    end ;
832 >    Result := -1
833 >  else
834 >  begin
835 >    FStatement.GetRowsAffected(SelectCount, InsertCount, UpdateCount, DeleteCount);
836 >    Result := InsertCount + UpdateCount + DeleteCount;
837    end;
838   end;
839  
840 < function TIBSQL.GetSQLParams: TIBXSQLDA;
840 > function TIBSQL.GetSQLParams: ISQLParams;
841   begin
842    if not Prepared then
843      Prepare;
844 <  result := FSQLParams;
844 >  result := Statement.SQLParams;
845   end;
846  
847   function TIBSQL.GetTransaction: TIBTransaction;
# Line 2285 | Line 849 | begin
849    result := FBase.Transaction;
850   end;
851  
2288 function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2289 begin
2290  result := FBase.TRHandle;
2291 end;
2292
2293 {
2294 Preprocess SQL
2295 Using FSQL, process the typed SQL and put the process SQL
2296 in FProcessedSQL and parameter names in FSQLParams
2297 }
2298 procedure TIBSQL.PreprocessSQL;
2299 var
2300  cCurChar, cNextChar, cQuoteChar: Char;
2301  sSQL, sProcessedSQL, sParamName: String;
2302  i, iLenSQL, iSQLPos: Integer;
2303  iCurState, iCurParamState: Integer;
2304  iParamSuffix: Integer;
2305  slNames: TStrings;
2306
2307 const
2308  DefaultState = 0;
2309  CommentState = 1;
2310  QuoteState = 2;
2311  ParamState = 3;
2312  ParamDefaultState = 0;
2313  ParamQuoteState = 1;
2314
2315  procedure AddToProcessedSQL(cChar: Char);
2316  begin
2317    sProcessedSQL[iSQLPos] := cChar;
2318    Inc(iSQLPos);
2319  end;
2320
2321 begin
2322  slNames := TStringList.Create;
2323  try
2324    { Do some initializations of variables }
2325    iParamSuffix := 0;
2326    cQuoteChar := '''';
2327    sSQL := FSQL.Text;
2328    iLenSQL := Length(sSQL);
2329    SetString(sProcessedSQL, nil, iLenSQL + 1);
2330    i := 1;
2331    iSQLPos := 1;
2332    iCurState := DefaultState;
2333    iCurParamState := ParamDefaultState;
2334    { Now, traverse through the SQL string, character by character,
2335     picking out the parameters and formatting correctly for InterBase }
2336    while (i <= iLenSQL) do begin
2337      { Get the current token and a look-ahead }
2338      cCurChar := sSQL[i];
2339      if i = iLenSQL then
2340        cNextChar := #0
2341      else
2342        cNextChar := sSQL[i + 1];
2343      { Now act based on the current state }
2344      case iCurState of
2345        DefaultState: begin
2346          case cCurChar of
2347            '''', '"': begin
2348              cQuoteChar := cCurChar;
2349              iCurState := QuoteState;
2350            end;
2351            '?', ':': begin
2352              iCurState := ParamState;
2353              AddToProcessedSQL('?');
2354            end;
2355            '/': if (cNextChar = '*') then begin
2356              AddToProcessedSQL(cCurChar);
2357              Inc(i);
2358              iCurState := CommentState;
2359            end;
2360          end;
2361        end;
2362        CommentState: begin
2363          if (cNextChar = #0) then
2364            IBError(ibxeSQLParseError, [SEOFInComment])
2365          else if (cCurChar = '*') then begin
2366            if (cNextChar = '/') then
2367              iCurState := DefaultState;
2368          end;
2369        end;
2370        QuoteState: begin
2371          if cNextChar = #0 then
2372            IBError(ibxeSQLParseError, [SEOFInString])
2373          else if (cCurChar = cQuoteChar) then begin
2374            if (cNextChar = cQuoteChar) then begin
2375              AddToProcessedSQL(cCurChar);
2376              Inc(i);
2377            end else
2378              iCurState := DefaultState;
2379          end;
2380        end;
2381        ParamState:
2382        begin
2383          { collect the name of the parameter }
2384          if iCurParamState = ParamDefaultState then
2385          begin
2386            if cCurChar = '"' then
2387              iCurParamState := ParamQuoteState
2388            else if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2389                sParamName := sParamName + cCurChar
2390            else if FGenerateParamNames then
2391            begin
2392              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2393              Inc(iParamSuffix);
2394              iCurState := DefaultState;
2395              slNames.Add(sParamName);
2396              sParamName := '';
2397            end
2398            else
2399              IBError(ibxeSQLParseError, [SParamNameExpected]);
2400          end
2401          else begin
2402            { determine if Quoted parameter name is finished }
2403            if cCurChar = '"' then
2404            begin
2405              Inc(i);
2406              slNames.Add(sParamName);
2407              SParamName := '';
2408              iCurParamState := ParamDefaultState;
2409              iCurState := DefaultState;
2410            end
2411            else
2412              sParamName := sParamName + cCurChar
2413          end;
2414          { determine if the unquoted parameter name is finished }
2415          if (iCurParamState <> ParamQuoteState) and
2416            (iCurState <> DefaultState) then
2417          begin
2418            if not (cNextChar in ['A'..'Z', 'a'..'z',
2419                                  '0'..'9', '_', '$']) then begin
2420              Inc(i);
2421              iCurState := DefaultState;
2422              slNames.Add(sParamName);
2423              sParamName := '';
2424            end;
2425          end;
2426        end;
2427      end;
2428      if iCurState <> ParamState then
2429        AddToProcessedSQL(sSQL[i]);
2430      Inc(i);
2431    end;
2432    AddToProcessedSQL(#0);
2433    FSQLParams.Count := slNames.Count;
2434    for i := 0 to slNames.Count - 1 do
2435      FSQLParams.AddName(slNames[i], i);
2436    FProcessedSQL.Text := sProcessedSQL;
2437  finally
2438    slNames.Free;
2439  end;
2440 end;
2441
852   procedure TIBSQL.SetDatabase(Value: TIBDatabase);
853   begin
854    FBase.Database := Value;
855   end;
856  
857   procedure TIBSQL.Prepare;
2448 var
2449  stmt_len: Integer;
2450  res_buffer: array[0..7] of Char;
2451  type_item: Char;
858   begin
859    CheckClosed;
860    FBase.CheckDatabase;
861    FBase.CheckTransaction;
862 <  if FPrepared then
862 >  Close;
863 >  if Prepared then
864      exit;
865    if (FSQL.Text = '') then
866      IBError(ibxeEmptyQuery, [nil]);
867 +
868 +  if FStatement <> nil then
869 +    FStatement.Prepare(Transaction.TransactionIntf)
870 +  else
871    if not ParamCheck then
872 <    FProcessedSQL.Text := FSQL.Text
872 >    FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
873    else
874 <    PreprocessSQL;
875 <  if (FProcessedSQL.Text = '') then
876 <    IBError(ibxeEmptyQuery, [nil]);
877 <  try
878 <    Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
879 <                                    @FHandle), True);
880 <    Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
881 <               PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
2471 <    { After preparing the statement, query the stmt type and possibly
2472 <      create a FSQLRecord "holder" }
2473 <    { Get the type of the statement }
2474 <    type_item := Char(isc_info_sql_stmt_type);
2475 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2476 <                         SizeOf(res_buffer), res_buffer), True);
2477 <    if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2478 <      IBError(ibxeUnknownError, [nil]);
2479 <    stmt_len := isc_vax_integer(@res_buffer[1], 2);
2480 <    FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2481 <    { Done getting the type }
2482 <    case FSQLType of
2483 <      SQLGetSegment,
2484 <      SQLPutSegment,
2485 <      SQLStartTransaction: begin
2486 <        FreeHandle;
2487 <        IBError(ibxeNotPermitted, [nil]);
2488 <      end;
2489 <      SQLCommit,
2490 <      SQLRollback,
2491 <      SQLDDL, SQLSetGenerator,
2492 <      SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2493 <      SQLExecProcedure: begin
2494 <        { We already know how many inputs there are, so... }
2495 <        if (FSQLParams.FXSQLDA <> nil) and
2496 <           (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2497 <                                        FSQLParams.FXSQLDA), False) > 0) then
2498 <          IBDataBaseError;
2499 <        FSQLParams.Initialize;
2500 <        if FSQLType in [SQLSelect, SQLSelectForUpdate,
2501 <                        SQLExecProcedure] then begin
2502 <          { Allocate an initial output descriptor (with one column) }
2503 <          FSQLRecord.Count := 1;
2504 <          { Using isc_dsql_describe, get the right size for the columns... }
2505 <          Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2506 <          if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2507 <            FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2508 <            Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2509 <          end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2510 <            FSQLRecord.Count := 0;
2511 <          FSQLRecord.Initialize;
2512 <        end;
2513 <      end;
2514 <    end;
2515 <    FPrepared := True;
2516 <    if not (csDesigning in ComponentState) then
874 >    FStatement := Database.Attachment.PrepareWithNamedParameters(
875 >                     Transaction.TransactionIntf,
876 >                     SQL.Text,
877 >                     GenerateParamNames);
878 >  FMetaData := FStatement.GetMetaData;
879 >  FSQLParams := FStatement.GetSQLParams;
880 >  FStatement.SetRetainInterfaces(true);
881 >  if not (csDesigning in ComponentState) then
882        MonitorHook.SQLPrepare(Self);
2518  except
2519    on E: Exception do begin
2520      if (FHandle <> nil) then
2521        FreeHandle;
2522      raise;
2523    end;
2524  end;
883   end;
884  
885   function TIBSQL.GetUniqueRelationName: String;
886   begin
887 <  if FPrepared and (FSQLType = SQLSelect) then
888 <    result := FSQLRecord.UniqueRelationName
887 >  if Prepared and (GetSQLStatementType = SQLSelect) then
888 >    result := FMetaData.GetUniqueRelationName
889    else
890      result := '';
891   end;
# Line 2554 | Line 912 | procedure TIBSQL.SQLChanging(Sender: TOb
912   begin
913    if Assigned(OnSQLChanging) then
914      OnSQLChanging(Self);
915 <  if FHandle <> nil then FreeHandle;
915 >
916 >  FreeHandle;
917   end;
918  
919 < procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
919 > procedure TIBSQL.SQLChanged(Sender: TObject);
920   begin
921 <  if (FOpen) then
922 <    Close;
921 >  if assigned(OnSQLChanged) then
922 >    OnSQLChanged(self);
923 > end;
924 >
925 > procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
926 >  Action: TTransactionAction);
927 > begin
928 >  if not (Owner is TIBCustomDataSet) then
929 >    FreeHandle;
930   end;
931  
932   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines