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 19 by tony, Mon Jul 7 13:00:15 2014 UTC vs.
Revision 80 by tony, Mon Jan 1 11:31:07 2018 UTC

# Line 35 | Line 35 | unit IBSQL;
35  
36   {$Mode Delphi}
37  
38 < { IBSQL param names in dialect 3 quoted format (e.g. :"MyParam") are by default disabled.
38 > {$codepage UTF8}
39  
40 < Dialect 3 quoted format parameter names represent a significant overhead and are of
41 < limited value - especially for users that use only TIBSQL or TIBCustomDataset
42 < descendents. They were previously used internally by IBX to simplify SQL generation
43 < for TTable components in Master/Slave relationships which are linked by
44 < Dialect 3 names. They were also generated by TStoredProc when the original
45 < parameter names are quoted.
46 <
47 < However, for some users they do cause a big processing overhead. The TTable/TStoredProc
48 < code has been re-written so that they are no required by IBX internally.
49 < The code to support quoted parameter names is now subject  to conditional compilation.
50 < To enable support, ALLOWDIALECT3PARAMNAMES should be defined when IBX is compiled.
51 <
52 < Hint: deleting the space between the brace and the dollar sign below
53 <
54 < }
55 <
56 < { $define ALLOWDIALECT3PARAMNAMES}
57 <
58 < {$ifndef ALLOWDIALECT3PARAMNAMES}
59 <
60 < { Even when dialect 3 quoted format parameter names are not supported, IBX still processes
61 <  parameter names case insensitive. This does result in some additional overhead
62 <  due to a call to "AnsiUpperCase". This can be avoided by undefining
63 <  "UseCaseSensitiveParamName" below.
64 <
65 <  Note: do not define "UseCaseSensitiveParamName" when "ALLOWDIALECT3PARAMNAMES"
66 <  is defined. This will not give a useful result.
67 < }
68 < {$define UseCaseSensitiveParamName}
69 < {$endif}
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  
# Line 76 | Line 55 | uses
55   {$ELSE}
56    baseunix, unix,
57   {$ENDIF}
58 <  SysUtils, Classes, Forms, Controls, IBHeader,
80 <  IBErrorCodes, IBExternals, DB, IB, IBDatabase, IBUtils, IBXConst;
58 >  SysUtils, Classes, IBExternals, IB, IBDatabase, IBUtils;
59  
60   type
83  TIBSQL = class;
84  TIBXSQLDA = class;
85  
86  { TIBXSQLVAR }
87  TIBXSQLVAR = class(TObject)
88  private
89    FParent: TIBXSQLDA;
90    FSQL: TIBSQL;
91    FIndex: Integer;
92    FModified: Boolean;
93    FName: String;
94    FUniqueName: boolean;
95    FXSQLVAR: PXSQLVAR;       { Point to the PXSQLVAR in the owner object }
96
97    function AdjustScale(Value: Int64; Scale: Integer): Double;
98    function AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
99    function AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
100    function GetAsCurrency: Currency;
101    function GetAsInt64: Int64;
102    function GetAsDateTime: TDateTime;
103    function GetAsDouble: Double;
104    function GetAsFloat: Float;
105    function GetAsLong: Long;
106    function GetAsPointer: Pointer;
107    function GetAsQuad: TISC_QUAD;
108    function GetAsShort: Short;
109    function GetAsString: String;
110    function GetAsVariant: Variant;
111    function GetAsXSQLVAR: PXSQLVAR;
112    function GetIsNull: Boolean;
113    function GetIsNullable: Boolean;
114    function GetSize: Integer;
115    function GetSQLType: Integer;
116    procedure SetAsCurrency(Value: Currency);
117    procedure SetAsInt64(Value: Int64);
118    procedure SetAsDate(Value: TDateTime);
119    procedure SetAsTime(Value: TDateTime);
120    procedure SetAsDateTime(Value: TDateTime);
121    procedure SetAsDouble(Value: Double);
122    procedure SetAsFloat(Value: Float);
123    procedure SetAsLong(Value: Long);
124    procedure SetAsPointer(Value: Pointer);
125    procedure SetAsQuad(Value: TISC_QUAD);
126    procedure SetAsShort(Value: Short);
127    procedure SetAsString(Value: String);
128    procedure SetAsVariant(Value: Variant);
129    procedure SetAsXSQLVAR(Value: PXSQLVAR);
130    procedure SetIsNull(Value: Boolean);
131    procedure SetIsNullable(Value: Boolean);
132    procedure xSetAsCurrency(Value: Currency);
133    procedure xSetAsInt64(Value: Int64);
134    procedure xSetAsDate(Value: TDateTime);
135    procedure xSetAsTime(Value: TDateTime);
136    procedure xSetAsDateTime(Value: TDateTime);
137    procedure xSetAsDouble(Value: Double);
138    procedure xSetAsFloat(Value: Float);
139    procedure xSetAsLong(Value: Long);
140    procedure xSetAsPointer(Value: Pointer);
141    procedure xSetAsQuad(Value: TISC_QUAD);
142    procedure xSetAsShort(Value: Short);
143    procedure xSetAsString(Value: String);
144    procedure xSetAsVariant(Value: Variant);
145    procedure xSetAsXSQLVAR(Value: PXSQLVAR);
146    procedure xSetIsNull(Value: Boolean);
147    procedure xSetIsNullable(Value: Boolean);
148  public
149    constructor Create(Parent: TIBXSQLDA; Query: TIBSQL);
150    procedure Assign(Source: TIBXSQLVAR);
151    procedure Clear;
152    procedure LoadFromFile(const FileName: String);
153    procedure LoadFromStream(Stream: TStream);
154    procedure SaveToFile(const FileName: String);
155    procedure SaveToStream(Stream: TStream);
156    property AsDate: TDateTime read GetAsDateTime write SetAsDate;
157    property AsTime: TDateTime read GetAsDateTime write SetAsTime;
158    property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
159    property AsDouble: Double read GetAsDouble write SetAsDouble;
160    property AsFloat: Float read GetAsFloat write SetAsFloat;
161    property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
162    property AsInt64: Int64 read GetAsInt64 write SetAsInt64;
163    property AsInteger: Integer read GetAsLong write SetAsLong;
164    property AsLong: Long read GetAsLong write SetAsLong;
165    property AsPointer: Pointer read GetAsPointer write SetAsPointer;
166    property AsQuad: TISC_QUAD read GetAsQuad write SetAsQuad;
167    property AsShort: Short read GetAsShort write SetAsShort;
168    property AsString: String read GetAsString write SetAsString;
169    property AsVariant: Variant read GetAsVariant write SetAsVariant;
170    property AsXSQLVAR: PXSQLVAR read GetAsXSQLVAR write SetAsXSQLVAR;
171    property Data: PXSQLVAR read FXSQLVAR write FXSQLVAR;
172    property IsNull: Boolean read GetIsNull write SetIsNull;
173    property IsNullable: Boolean read GetIsNullable write SetIsNullable;
174    property Index: Integer read FIndex;
175    property Modified: Boolean read FModified write FModified;
176    property Name: String read FName;
177    property Size: Integer read GetSize;
178    property SQLType: Integer read GetSQLType;
179    property Value: Variant read GetAsVariant write SetAsVariant;
180  end;
181
182  TIBXSQLVARArray = Array of TIBXSQLVAR;
183
184  TIBXSQLDAType = (daInput,daOutput);
185
186  { TIBXSQLDA }
187
188  TIBXSQLDA = class(TObject)
189  protected
190    FSQL: TIBSQL;
191    FCount: Integer;
192    FSize: Integer;
193    FInputSQLDA: boolean;
194    FXSQLDA: PXSQLDA;
195    FXSQLVARs: TIBXSQLVARArray; { array of IBXQLVARs }
196    FUniqueRelationName: String;
197    function GetModified: Boolean;
198    function GetRecordSize: Integer;
199    function GetXSQLDA: PXSQLDA;
200    function GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
201    function GetXSQLVARByName(Idx: String): TIBXSQLVAR;
202    procedure Initialize;
203    procedure SetCount(Value: Integer);
204  public
205    constructor Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
206    destructor Destroy; override;
207     procedure SetParamName(FieldName: String; Idx: Integer; UniqueName: boolean = false);
208    function ByName(Idx: String): TIBXSQLVAR;
209    property AsXSQLDA: PXSQLDA read GetXSQLDA;
210    property Count: Integer read FCount write SetCount;
211    property Modified: Boolean read GetModified;
212    property RecordSize: Integer read GetRecordSize;
213    property Vars[Idx: Integer]: TIBXSQLVAR read GetXSQLVAR; default;
214    property UniqueRelationName: String read FUniqueRelationName;
215  end;
216
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 311 | Line 155 | type
155    end;
156  
157       { TIBSQL }
314  TIBSQLTypes = (SQLUnknown, SQLSelect, SQLInsert,
315                  SQLUpdate, SQLDelete, SQLDDL,
316                  SQLGetSegment, SQLPutSegment,
317                  SQLExecProcedure, SQLStartTransaction,
318                  SQLCommit, SQLRollback,
319                  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? }
330 <    FEOF,                          { At EOF? }
331 <    FGoToFirstRecordOnExecute,     { Automatically position record on first record after executing }
332 <    FOpen,                         { Is a cursor open? }
333 <    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? }
335    FCursor: String;               { Cursor name...}
336    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 }
342 <    FSQLRecord: TIBXSQLDA;         { The current record }
343 <    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;
347    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;
356    function GetTRHandle: PISC_TR_HANDLE;
357    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);
368    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;
373    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;
382    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 FieldCount: integer read GetFieldCount;
222 <    property Open: Boolean read FOpen;
223 <    property Params: TIBXSQLDA read GetSQLParams;
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;
394 <    property TRHandle: PISC_TR_HANDLE read GetTRHandle;
395 <    property Handle: TISC_STMT_HANDLE read FHandle;
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;
# Line 405 | 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 , IBSQLMonitor;
251 >   Variants, IBSQLMonitor, FBMessages, IBCustomDataSet;
252  
253 < { TIBXSQLVAR }
416 < constructor TIBXSQLVAR.Create(Parent: TIBXSQLDA; Query: TIBSQL);
417 < begin
418 <  inherited Create;
419 <  FParent := Parent;
420 <  FSQL := Query;
421 < end;
422 <
423 < procedure TIBXSQLVAR.Assign(Source: TIBXSQLVAR);
253 > procedure IBAlloc(var P; OldSize, NewSize: Integer);
254   var
425  szBuff: PChar;
426  s_bhandle, d_bhandle: TISC_BLOB_HANDLE;
427  bSourceBlob, bDestBlob: Boolean;
428  iSegs: Int64;
429  iMaxSeg: Int64;
430  iSize: Int64;
431  iBlobType: Short;
432 begin
433  szBuff := nil;
434  bSourceBlob := True;
435  bDestBlob := True;
436  s_bhandle := nil;
437  d_bhandle := nil;
438  try
439    if (Source.IsNull) then
440    begin
441      IsNull := True;
442      exit;
443    end
444    else
445      if (FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) or
446         (Source.FXSQLVAR^.sqltype and (not 1) = SQL_ARRAY) then
447        exit; { arrays not supported }
448    if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
449       (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
450    begin
451      AsXSQLVAR := Source.AsXSQLVAR;
452      exit;
453    end
454    else
455      if (Source.FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
456      begin
457        szBuff := nil;
458        IBAlloc(szBuff, 0, Source.FXSQLVAR^.sqllen);
459        Move(Source.FXSQLVAR^.sqldata[0], szBuff[0], Source.FXSQLVAR^.sqllen);
460        bSourceBlob := False;
461        iSize := Source.FXSQLVAR^.sqllen;
462      end
463      else
464        if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) then
465          bDestBlob := False;
466
467    if bSourceBlob then
468    begin
469      { read the blob }
470      Source.FSQL.Call(isc_open_blob2(StatusVector, Source.FSQL.DBHandle,
471        Source.FSQL.TRHandle, @s_bhandle, PISC_QUAD(Source.FXSQLVAR.sqldata),
472        0, nil), True);
473      try
474        IBBlob.GetBlobInfo(@s_bhandle, iSegs, iMaxSeg, iSize,
475          iBlobType);
476        szBuff := nil;
477        IBAlloc(szBuff, 0, iSize);
478        IBBlob.ReadBlob(@s_bhandle, szBuff, iSize);
479      finally
480        Source.FSQL.Call(isc_close_blob(StatusVector, @s_bhandle), True);
481      end;
482    end;
483
484    if bDestBlob then
485    begin
486      { write the blob }
487      FSQL.Call(isc_create_blob2(StatusVector, FSQL.DBHandle,
488        FSQL.TRHandle, @d_bhandle, PISC_QUAD(FXSQLVAR.sqldata),
489        0, nil), True);
490      try
491        IBBlob.WriteBlob(@d_bhandle, szBuff, iSize);
492        isNull := false
493      finally
494        FSQL.Call(isc_close_blob(StatusVector, @d_bhandle), True);
495      end;
496    end
497    else
498    begin
499      { just copy the buffer }
500      FXSQLVAR.sqltype := SQL_TEXT;
501      FXSQLVAR.sqllen := iSize;
502      IBAlloc(FXSQLVAR.sqldata, iSize, iSize);
503      Move(szBuff[0], FXSQLVAR^.sqldata[0], iSize);
504    end;
505  finally
506    FreeMem(szBuff);
507  end;
508 end;
509
510 function TIBXSQLVAR.AdjustScale(Value: Int64; Scale: Integer): Double;
511 var
512  Scaling : Int64;
255    i: Integer;
514  Val: Double;
515 begin
516  Scaling := 1; Val := Value;
517  if Scale > 0 then
518  begin
519    for i := 1 to Scale do
520      Scaling := Scaling * 10;
521    result := Val * Scaling;
522  end
523  else
524    if Scale < 0 then
525    begin
526      for i := -1 downto Scale do
527        Scaling := Scaling * 10;
528      result := Val / Scaling;
529    end
530    else
531      result := Val;
532 end;
533
534 function TIBXSQLVAR.AdjustScaleToInt64(Value: Int64; Scale: Integer): Int64;
535 var
536  Scaling : Int64;
537  i: Integer;
538  Val: Int64;
539 begin
540  Scaling := 1; Val := Value;
541  if Scale > 0 then begin
542    for i := 1 to Scale do Scaling := Scaling * 10;
543    result := Val * Scaling;
544  end else if Scale < 0 then begin
545    for i := -1 downto Scale do Scaling := Scaling * 10;
546    result := Val div Scaling;
547  end else
548    result := Val;
549 end;
550
551 function TIBXSQLVAR.AdjustScaleToCurrency(Value: Int64; Scale: Integer): Currency;
552 var
553  Scaling : Int64;
554  i : Integer;
555  FractionText, PadText, CurrText: string;
556 begin
557  Result := 0;
558  Scaling := 1;
559  if Scale > 0 then
560  begin
561    for i := 1 to Scale do
562      Scaling := Scaling * 10;
563    result := Value * Scaling;
564  end
565  else
566    if Scale < 0 then
567    begin
568      for i := -1 downto Scale do
569        Scaling := Scaling * 10;
570      FractionText := IntToStr(abs(Value mod Scaling));
571      for i := Length(FractionText) to -Scale -1 do
572        PadText := '0' + PadText;
573      if Value < 0 then
574        CurrText := '-' + IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText
575      else
576        CurrText := IntToStr(Abs(Value div Scaling)) + DecimalSeparator + PadText + FractionText;
577      try
578        result := StrToCurr(CurrText);
579      except
580        on E: Exception do
581          IBError(ibxeInvalidDataConversion, [nil]);
582      end;
583    end
584    else
585      result := Value;
586 end;
587
588 function TIBXSQLVAR.GetAsCurrency: Currency;
589 begin
590  result := 0;
591  if FSQL.Database.SQLDialect < 3 then
592    result := GetAsDouble
593  else begin
594    if not IsNull then
595      case FXSQLVAR^.sqltype and (not 1) of
596        SQL_TEXT, SQL_VARYING: begin
597          try
598            result := StrtoCurr(AsString);
599          except
600            on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
601          end;
602        end;
603        SQL_SHORT:
604          result := AdjustScaleToCurrency(Int64(PShort(FXSQLVAR^.sqldata)^),
605                                      FXSQLVAR^.sqlscale);
606        SQL_LONG:
607          result := AdjustScaleToCurrency(Int64(PLong(FXSQLVAR^.sqldata)^),
608                                      FXSQLVAR^.sqlscale);
609        SQL_INT64:
610          result := AdjustScaleToCurrency(PInt64(FXSQLVAR^.sqldata)^,
611                                      FXSQLVAR^.sqlscale);
612        SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
613          result := Trunc(AsDouble);
614        else
615          IBError(ibxeInvalidDataConversion, [nil]);
616      end;
617    end;
618 end;
619
620 function TIBXSQLVAR.GetAsInt64: Int64;
621 begin
622  result := 0;
623  if not IsNull then
624    case FXSQLVAR^.sqltype and (not 1) of
625      SQL_TEXT, SQL_VARYING: begin
626        try
627          result := StrToInt64(AsString);
628        except
629          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
630        end;
631      end;
632      SQL_SHORT:
633        result := AdjustScaleToInt64(Int64(PShort(FXSQLVAR^.sqldata)^),
634                                    FXSQLVAR^.sqlscale);
635      SQL_LONG:
636        result := AdjustScaleToInt64(Int64(PLong(FXSQLVAR^.sqldata)^),
637                                    FXSQLVAR^.sqlscale);
638      SQL_INT64:
639        result := AdjustScaleToInt64(PInt64(FXSQLVAR^.sqldata)^,
640                                    FXSQLVAR^.sqlscale);
641      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
642        result := Trunc(AsDouble);
643      else
644        IBError(ibxeInvalidDataConversion, [nil]);
645    end;
646 end;
647
648 function TIBXSQLVAR.GetAsDateTime: TDateTime;
649 var
650  tm_date: TCTimeStructure;
651  msecs: word;
652 begin
653  result := 0;
654  if not IsNull then
655    case FXSQLVAR^.sqltype and (not 1) of
656      SQL_TEXT, SQL_VARYING: begin
657        try
658          result := StrToDate(AsString);
659        except
660          on E: EConvertError do IBError(ibxeInvalidDataConversion, [nil]);
661        end;
662      end;
663      SQL_TYPE_DATE: begin
664        isc_decode_sql_date(PISC_DATE(FXSQLVAR^.sqldata), @tm_date);
665        try
666          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
667                               Word(tm_date.tm_mday));
668        except
669          on E: EConvertError do begin
670            IBError(ibxeInvalidDataConversion, [nil]);
671          end;
672        end;
673      end;
674      SQL_TYPE_TIME: begin
675        isc_decode_sql_time(PISC_TIME(FXSQLVAR^.sqldata), @tm_date);
676        try
677          msecs :=  (PISC_TIME(FXSQLVAR^.sqldata)^ mod 10000) div 10;
678          result := EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
679                               Word(tm_date.tm_sec), msecs)
680        except
681          on E: EConvertError do begin
682            IBError(ibxeInvalidDataConversion, [nil]);
683          end;
684        end;
685      end;
686      SQL_TIMESTAMP: begin
687        isc_decode_date(PISC_QUAD(FXSQLVAR^.sqldata), @tm_date);
688        try
689          result := EncodeDate(Word(tm_date.tm_year + 1900), Word(tm_date.tm_mon + 1),
690                              Word(tm_date.tm_mday));
691          msecs := (PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time mod 10000) div 10;
692          if result >= 0 then
693            result := result + EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
694                                          Word(tm_date.tm_sec), msecs)
695          else
696            result := result - EncodeTime(Word(tm_date.tm_hour), Word(tm_date.tm_min),
697                                          Word(tm_date.tm_sec), msecs)
698        except
699          on E: EConvertError do begin
700            IBError(ibxeInvalidDataConversion, [nil]);
701          end;
702        end;
703      end;
704      else
705        IBError(ibxeInvalidDataConversion, [nil]);
706    end;
707 end;
708
709 function TIBXSQLVAR.GetAsDouble: Double;
710 begin
711  result := 0;
712  if not IsNull then begin
713    case FXSQLVAR^.sqltype and (not 1) of
714      SQL_TEXT, SQL_VARYING: begin
715        try
716          result := StrToFloat(AsString);
717        except
718          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
719        end;
720      end;
721      SQL_SHORT:
722        result := AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
723                              FXSQLVAR^.sqlscale);
724      SQL_LONG:
725        result := AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
726                              FXSQLVAR^.sqlscale);
727      SQL_INT64:
728        result := AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale);
729      SQL_FLOAT:
730        result := PFloat(FXSQLVAR^.sqldata)^;
731      SQL_DOUBLE, SQL_D_FLOAT:
732        result := PDouble(FXSQLVAR^.sqldata)^;
733      else
734        IBError(ibxeInvalidDataConversion, [nil]);
735    end;
736    if  FXSQLVAR^.sqlscale <> 0 then
737      result :=
738        StrToFloat(FloatToStrF(result, fffixed, 15,
739                  Abs(FXSQLVAR^.sqlscale) ));
740  end;
741 end;
742
743 function TIBXSQLVAR.GetAsFloat: Float;
744 begin
745  result := 0;
746  try
747    result := AsDouble;
748  except
749    on E: EOverflow do
750      IBError(ibxeInvalidDataConversion, [nil]);
751  end;
752 end;
753
754 function TIBXSQLVAR.GetAsLong: Long;
755 begin
756  result := 0;
757  if not IsNull then
758    case FXSQLVAR^.sqltype and (not 1) of
759      SQL_TEXT, SQL_VARYING: begin
760        try
761          result := StrToInt(AsString);
762        except
763          on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
764        end;
765      end;
766      SQL_SHORT:
767        result := Trunc(AdjustScale(Int64(PShort(FXSQLVAR^.sqldata)^),
768                                    FXSQLVAR^.sqlscale));
769      SQL_LONG:
770        result := Trunc(AdjustScale(Int64(PLong(FXSQLVAR^.sqldata)^),
771                                    FXSQLVAR^.sqlscale));
772      SQL_INT64:
773        result := Trunc(AdjustScale(PInt64(FXSQLVAR^.sqldata)^, FXSQLVAR^.sqlscale));
774      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
775        result := Trunc(AsDouble);
776      else
777        IBError(ibxeInvalidDataConversion, [nil]);
778    end;
779 end;
780
781 function TIBXSQLVAR.GetAsPointer: Pointer;
782 begin
783  if not IsNull then
784    result := FXSQLVAR^.sqldata
785  else
786    result := nil;
787 end;
788
789 function TIBXSQLVAR.GetAsQuad: TISC_QUAD;
790 begin
791  result.gds_quad_high := 0;
792  result.gds_quad_low := 0;
793  if not IsNull then
794    case FXSQLVAR^.sqltype and (not 1) of
795      SQL_BLOB, SQL_ARRAY, SQL_QUAD:
796        result := PISC_QUAD(FXSQLVAR^.sqldata)^;
797      else
798        IBError(ibxeInvalidDataConversion, [nil]);
799    end;
800 end;
801
802 function TIBXSQLVAR.GetAsShort: Short;
803 begin
804  result := 0;
805  try
806    result := AsLong;
807  except
808    on E: Exception do IBError(ibxeInvalidDataConversion, [nil]);
809  end;
810 end;
811
812
813 function TIBXSQLVAR.GetAsString: String;
814 var
815  sz: PChar;
816  str_len: Integer;
817  ss: TStringStream;
818 begin
819  result := '';
820  { Check null, if so return a default string }
821  if not IsNull then
822    case FXSQLVar^.sqltype and (not 1) of
823      SQL_ARRAY:
824        result := '(Array)'; {do not localize}
825      SQL_BLOB: begin
826        ss := TStringStream.Create('');
827        try
828          SaveToStream(ss);
829          result := ss.DataString;
830        finally
831          ss.Free;
832        end;
833      end;
834      SQL_TEXT, SQL_VARYING: begin
835        sz := FXSQLVAR^.sqldata;
836        if (FXSQLVar^.sqltype and (not 1) = SQL_TEXT) then
837          str_len := FXSQLVar^.sqllen
838        else begin
839          str_len := isc_vax_integer(FXSQLVar^.sqldata, 2);
840          Inc(sz, 2);
841        end;
842        SetString(result, sz, str_len);
843        if ((FXSQLVar^.sqltype and (not 1)) = SQL_TEXT) then
844          result := TrimRight(result);
845      end;
846      SQL_TYPE_DATE:
847        case FSQL.Database.SQLDialect of
848          1 : result := DateTimeToStr(AsDateTime);
849          3 : result := DateToStr(AsDateTime);
850        end;
851      SQL_TYPE_TIME :
852        result := TimeToStr(AsDateTime);
853      SQL_TIMESTAMP:
854        result := DateTimeToStr(AsDateTime);
855      SQL_SHORT, SQL_LONG:
856        if FXSQLVAR^.sqlscale = 0 then
857          result := IntToStr(AsLong)
858        else if FXSQLVAR^.sqlscale >= (-4) then
859          result := CurrToStr(AsCurrency)
860        else
861          result := FloatToStr(AsDouble);
862      SQL_INT64:
863        if FXSQLVAR^.sqlscale = 0 then
864          result := IntToStr(AsInt64)
865        else if FXSQLVAR^.sqlscale >= (-4) then
866          result := CurrToStr(AsCurrency)
867        else
868          result := FloatToStr(AsDouble);
869      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
870        result := FloatToStr(AsDouble);
871      else
872        IBError(ibxeInvalidDataConversion, [nil]);
873    end;
874 end;
875
876 function TIBXSQLVAR.GetAsVariant: Variant;
877 begin
878  if IsNull then
879    result := NULL
880  { Check null, if so return a default string }
881  else case FXSQLVar^.sqltype and (not 1) of
882      SQL_ARRAY:
883        result := '(Array)'; {do not localize}
884      SQL_BLOB:
885        result := '(Blob)'; {do not localize}
886      SQL_TEXT, SQL_VARYING:
887        result := AsString;
888      SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIME:
889        result := AsDateTime;
890      SQL_SHORT, SQL_LONG:
891        if FXSQLVAR^.sqlscale = 0 then
892          result := AsLong
893        else if FXSQLVAR^.sqlscale >= (-4) then
894          result := AsCurrency
895        else
896          result := AsDouble;
897      SQL_INT64:
898        if FXSQLVAR^.sqlscale = 0 then
899          IBError(ibxeInvalidDataConversion, [nil])
900        else if FXSQLVAR^.sqlscale >= (-4) then
901          result := AsCurrency
902        else
903          result := AsDouble;
904      SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT:
905        result := AsDouble;
906      else
907        IBError(ibxeInvalidDataConversion, [nil]);
908    end;
909 end;
910
911 function TIBXSQLVAR.GetAsXSQLVAR: PXSQLVAR;
912 begin
913  result := FXSQLVAR;
914 end;
915
916 function TIBXSQLVAR.GetIsNull: Boolean;
917 begin
918  result := IsNullable and (FXSQLVAR^.sqlind^ = -1);
919 end;
920
921 function TIBXSQLVAR.GetIsNullable: Boolean;
922 begin
923  result := (FXSQLVAR^.sqltype and 1 = 1);
924 end;
925
926 procedure TIBXSQLVAR.LoadFromFile(const FileName: String);
927 var
928  fs: TFileStream;
929 begin
930  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
931  try
932    LoadFromStream(fs);
933  finally
934    fs.Free;
935  end;
936 end;
937
938 procedure TIBXSQLVAR.LoadFromStream(Stream: TStream);
939 var
940  bs: TIBBlobStream;
941 begin
942  bs := TIBBlobStream.Create;
943  try
944    bs.Mode := bmWrite;
945    bs.Database := FSQL.Database;
946    bs.Transaction := FSQL.Transaction;
947    Stream.Seek(0, soFromBeginning);
948    bs.LoadFromStream(Stream);
949    bs.Finalize;
950    AsQuad := bs.BlobID;
951  finally
952    bs.Free;
953  end;
954 end;
955
956 procedure TIBXSQLVAR.SaveToFile(const FileName: String);
957 var
958  fs: TFileStream;
959 begin
960  fs := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
961  try
962    SaveToStream(fs);
963  finally
964    fs.Free;
965  end;
966 end;
967
968 procedure TIBXSQLVAR.SaveToStream(Stream: TStream);
969 var
970  bs: TIBBlobStream;
971 begin
972  bs := TIBBlobStream.Create;
973  try
974    bs.Mode := bmRead;
975    bs.Database := FSQL.Database;
976    bs.Transaction := FSQL.Transaction;
977    bs.BlobID := AsQuad;
978    bs.SaveToStream(Stream);
979  finally
980    bs.Free;
981  end;
982 end;
983
984 function TIBXSQLVAR.GetSize: Integer;
985 begin
986  result := FXSQLVAR^.sqllen;
987 end;
988
989 function TIBXSQLVAR.GetSQLType: Integer;
990 begin
991  result := FXSQLVAR^.sqltype and (not 1);
992 end;
993
994 procedure TIBXSQLVAR.xSetAsCurrency(Value: Currency);
995 begin
996  if IsNullable then
997    IsNull := False;
998  FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
999  FXSQLVAR^.sqlscale := -4;
1000  FXSQLVAR^.sqllen := SizeOf(Int64);
1001  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1002  PCurrency(FXSQLVAR^.sqldata)^ := Value;
1003  FModified := True;
1004 end;
1005
1006 procedure TIBXSQLVAR.SetAsCurrency(Value: Currency);
1007 var
1008  i: Integer;
1009 begin
1010  if FSQL.Database.SQLDialect < 3 then
1011    AsDouble := Value
1012  else
1013  begin
1014
1015    if FUniqueName then
1016       xSetAsCurrency(Value)
1017    else
1018    for i := 0 to FParent.FCount - 1 do
1019      if FParent[i].FName = FName then
1020           FParent[i].xSetAsCurrency(Value);
1021  end;
1022 end;
1023
1024 procedure TIBXSQLVAR.xSetAsInt64(Value: Int64);
1025 begin
1026  if IsNullable then
1027    IsNull := False;
1028
1029  FXSQLVAR^.sqltype := SQL_INT64 or (FXSQLVAR^.sqltype and 1);
1030  FXSQLVAR^.sqlscale := 0;
1031  FXSQLVAR^.sqllen := SizeOf(Int64);
1032  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1033  PInt64(FXSQLVAR^.sqldata)^ := Value;
1034  FModified := True;
1035 end;
1036
1037 procedure TIBXSQLVAR.SetAsInt64(Value: Int64);
1038 var
1039  i: Integer;
1040 begin
1041  if FUniqueName then
1042     xSetAsInt64(Value)
1043  else
1044  for i := 0 to FParent.FCount - 1 do
1045    if FParent[i].FName = FName then
1046          FParent[i].xSetAsInt64(Value);
1047 end;
1048
1049 procedure TIBXSQLVAR.xSetAsDate(Value: TDateTime);
1050 var
1051   tm_date: TCTimeStructure;
1052   Yr, Mn, Dy: Word;
1053 begin
1054  if IsNullable then
1055    IsNull := False;
1056
1057  FXSQLVAR^.sqltype := SQL_TYPE_DATE or (FXSQLVAR^.sqltype and 1);
1058  DecodeDate(Value, Yr, Mn, Dy);
1059  with tm_date do begin
1060    tm_sec := 0;
1061    tm_min := 0;
1062    tm_hour := 0;
1063    tm_mday := Dy;
1064    tm_mon := Mn - 1;
1065    tm_year := Yr - 1900;
1066  end;
1067  FXSQLVAR^.sqllen := SizeOf(ISC_DATE);
1068  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1069  isc_encode_sql_date(@tm_date, PISC_DATE(FXSQLVAR^.sqldata));
1070  FModified := True;
1071 end;
1072
1073 procedure TIBXSQLVAR.SetAsDate(Value: TDateTime);
1074 var
1075  i: Integer;
1076 begin
1077  if FSQL.Database.SQLDialect < 3 then
1078  begin
1079    AsDateTime := Value;
1080    exit;
1081  end;
1082
1083  if FUniqueName then
1084     xSetAsDate(Value)
1085  else
1086  for i := 0 to FParent.FCount - 1 do
1087    if FParent[i].FName = FName then
1088       FParent[i].xSetAsDate(Value);
1089 end;
1090
1091 procedure TIBXSQLVAR.xSetAsTime(Value: TDateTime);
1092 var
1093  tm_date: TCTimeStructure;
1094  Hr, Mt, S, Ms: Word;
1095 begin
1096  if IsNullable then
1097    IsNull := False;
1098
1099  FXSQLVAR^.sqltype := SQL_TYPE_TIME or (FXSQLVAR^.sqltype and 1);
1100  DecodeTime(Value, Hr, Mt, S, Ms);
1101  with tm_date do begin
1102    tm_sec := S;
1103    tm_min := Mt;
1104    tm_hour := Hr;
1105    tm_mday := 0;
1106    tm_mon := 0;
1107    tm_year := 0;
1108  end;
1109  FXSQLVAR^.sqllen := SizeOf(ISC_TIME);
1110  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1111  isc_encode_sql_time(@tm_date, PISC_TIME(FXSQLVAR^.sqldata));
1112  if Ms > 0 then
1113    Inc(PISC_TIME(FXSQLVAR^.sqldata)^,Ms*10);
1114  FModified := True;
1115 end;
1116
1117 procedure TIBXSQLVAR.SetAsTime(Value: TDateTime);
1118 var
1119  i: Integer;
1120 begin
1121  if FSQL.Database.SQLDialect < 3 then
1122  begin
1123    AsDateTime := Value;
1124    exit;
1125  end;
1126
1127  if FUniqueName then
1128     xSetAsTime(Value)
1129  else
1130  for i := 0 to FParent.FCount - 1 do
1131    if FParent[i].FName = FName then
1132       FParent[i].xSetAsTime(Value);
1133 end;
1134
1135 procedure TIBXSQLVAR.xSetAsDateTime(Value: TDateTime);
1136 var
1137  tm_date: TCTimeStructure;
1138  Yr, Mn, Dy, Hr, Mt, S, Ms: Word;
1139 begin
1140  if IsNullable then
1141    IsNull := False;
1142
1143  FXSQLVAR^.sqltype := SQL_TIMESTAMP or (FXSQLVAR^.sqltype and 1);
1144  DecodeDate(Value, Yr, Mn, Dy);
1145  DecodeTime(Value, Hr, Mt, S, Ms);
1146  with tm_date do begin
1147    tm_sec := S;
1148    tm_min := Mt;
1149    tm_hour := Hr;
1150    tm_mday := Dy;
1151    tm_mon := Mn - 1;
1152    tm_year := Yr - 1900;
1153  end;
1154  FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1155  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1156  isc_encode_date(@tm_date, PISC_QUAD(FXSQLVAR^.sqldata));
1157  if Ms > 0 then
1158    Inc(PISC_TIMESTAMP(FXSQLVAR^.sqldata)^.timestamp_time,Ms*10);
1159  FModified := True;
1160 end;
1161
1162 procedure TIBXSQLVAR.SetAsDateTime(Value: TDateTime);
1163 var
1164  i: Integer;
1165 begin
1166  if FUniqueName then
1167     xSetAsDateTime(value)
1168  else
1169  for i := 0 to FParent.FCount - 1 do
1170    if FParent[i].FName = FName then
1171       FParent[i].xSetAsDateTime(Value);
1172 end;
1173
1174 procedure TIBXSQLVAR.xSetAsDouble(Value: Double);
1175 begin
1176  if IsNullable then
1177    IsNull := False;
1178
1179  FXSQLVAR^.sqltype := SQL_DOUBLE or (FXSQLVAR^.sqltype and 1);
1180  FXSQLVAR^.sqllen := SizeOf(Double);
1181  FXSQLVAR^.sqlscale := 0;
1182  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1183  PDouble(FXSQLVAR^.sqldata)^ := Value;
1184  FModified := True;
1185 end;
1186
1187 procedure TIBXSQLVAR.SetAsDouble(Value: Double);
1188 var
1189  i: Integer;
1190 begin
1191  if FUniqueName then
1192     xSetAsDouble(Value)
1193  else
1194  for i := 0 to FParent.FCount - 1 do
1195    if FParent[i].FName = FName then
1196       FParent[i].xSetAsDouble(Value);
1197 end;
1198
1199 procedure TIBXSQLVAR.xSetAsFloat(Value: Float);
1200 begin
1201  if IsNullable then
1202    IsNull := False;
1203
1204  FXSQLVAR^.sqltype := SQL_FLOAT or (FXSQLVAR^.sqltype and 1);
1205  FXSQLVAR^.sqllen := SizeOf(Float);
1206  FXSQLVAR^.sqlscale := 0;
1207  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1208  PSingle(FXSQLVAR^.sqldata)^ := Value;
1209  FModified := True;
1210 end;
1211
1212 procedure TIBXSQLVAR.SetAsFloat(Value: Float);
1213 var
1214  i: Integer;
1215 begin
1216  if FUniqueName then
1217     xSetAsFloat(Value)
1218  else
1219  for i := 0 to FParent.FCount - 1 do
1220    if FParent[i].FName = FName then
1221       FParent[i].xSetAsFloat(Value);
1222 end;
1223
1224 procedure TIBXSQLVAR.xSetAsLong(Value: Long);
1225 begin
1226  if IsNullable then
1227    IsNull := False;
1228
1229  FXSQLVAR^.sqltype := SQL_LONG or (FXSQLVAR^.sqltype and 1);
1230  FXSQLVAR^.sqllen := SizeOf(Long);
1231  FXSQLVAR^.sqlscale := 0;
1232  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1233  PLong(FXSQLVAR^.sqldata)^ := Value;
1234  FModified := True;
1235 end;
1236
1237 procedure TIBXSQLVAR.SetAsLong(Value: Long);
1238 var
1239  i: Integer;
1240 begin
1241  if FUniqueName then
1242     xSetAsLong(Value)
1243  else
1244  for i := 0 to FParent.FCount - 1 do
1245    if FParent[i].FName = FName then
1246       FParent[i].xSetAsLong(Value);
1247 end;
1248
1249 procedure TIBXSQLVAR.xSetAsPointer(Value: Pointer);
1250 begin
1251  if IsNullable and (Value = nil) then
1252    IsNull := True
1253  else begin
1254    IsNull := False;
1255    FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1256    Move(Value^, FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1257  end;
1258  FModified := True;
1259 end;
1260
1261 procedure TIBXSQLVAR.SetAsPointer(Value: Pointer);
1262 var
1263  i: Integer;
1264 begin
1265    if FUniqueName then
1266       xSetAsPointer(Value)
1267    else
1268    for i := 0 to FParent.FCount - 1 do
1269      if FParent[i].FName = FName then
1270         FParent[i].xSetAsPointer(Value);
1271 end;
1272
1273 procedure TIBXSQLVAR.xSetAsQuad(Value: TISC_QUAD);
256   begin
257 <  if IsNullable then
258 <      IsNull := False;
1277 <  if (FXSQLVAR^.sqltype and (not 1) <> SQL_BLOB) and
1278 <     (FXSQLVAR^.sqltype and (not 1) <> SQL_ARRAY) then
1279 <    IBError(ibxeInvalidDataConversion, [nil]);
1280 <  FXSQLVAR^.sqllen := SizeOf(TISC_QUAD);
1281 <  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1282 <  PISC_QUAD(FXSQLVAR^.sqldata)^ := Value;
1283 <  FModified := True;
1284 < end;
1285 <
1286 < procedure TIBXSQLVAR.SetAsQuad(Value: TISC_QUAD);
1287 < var
1288 <  i: Integer;
1289 < begin
1290 <  if FUniqueName then
1291 <     xSetAsQuad(Value)
1292 <  else
1293 <  for i := 0 to FParent.FCount - 1 do
1294 <    if FParent[i].FName = FName then
1295 <       FParent[i].xSetAsQuad(Value);
1296 < end;
1297 <
1298 < procedure TIBXSQLVAR.xSetAsShort(Value: Short);
1299 < begin
1300 <  if IsNullable then
1301 <    IsNull := False;
1302 <
1303 <  FXSQLVAR^.sqltype := SQL_SHORT or (FXSQLVAR^.sqltype and 1);
1304 <  FXSQLVAR^.sqllen := SizeOf(Short);
1305 <  FXSQLVAR^.sqlscale := 0;
1306 <  IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen);
1307 <  PShort(FXSQLVAR^.sqldata)^ := Value;
1308 <  FModified := True;
1309 < end;
1310 <
1311 < procedure TIBXSQLVAR.SetAsShort(Value: Short);
1312 < var
1313 <  i: Integer;
1314 < begin
1315 <  if FUniqueName then
1316 <     xSetAsShort(Value)
1317 <  else
1318 <  for i := 0 to FParent.FCount - 1 do
1319 <    if FParent[i].FName = FName then
1320 <       FParent[i].xSetAsShort(Value);
1321 < end;
1322 <
1323 < procedure TIBXSQLVAR.xSetAsString(Value: String);
1324 < var
1325 <   stype: Integer;
1326 <   ss: TStringStream;
1327 <
1328 <   procedure SetStringValue;
1329 <   var
1330 <      i: Integer;
1331 <   begin
1332 <      if (FXSQLVAR^.sqlname = 'DB_KEY') or {do not localize}
1333 <         (FXSQLVAR^.sqlname = 'RDB$DB_KEY') then {do not localize}
1334 <        Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen)
1335 <      else begin
1336 <        FXSQLVAR^.sqltype := SQL_TEXT or (FXSQLVAR^.sqltype and 1);
1337 <        FXSQLVAR^.sqllen := Length(Value);
1338 <        IBAlloc(FXSQLVAR^.sqldata, 0, FXSQLVAR^.sqllen + 1);
1339 <        if (Length(Value) > 0) then
1340 <          Move(Value[1], FXSQLVAR^.sqldata^, FXSQLVAR^.sqllen);
1341 <      end;
1342 <      FModified := True;
1343 <   end;
1344 <
1345 < begin
1346 <  if IsNullable then
1347 <    IsNull := False;
1348 <
1349 <  stype := FXSQLVAR^.sqltype and (not 1);
1350 <  if (stype = SQL_TEXT) or (stype = SQL_VARYING) then
1351 <    SetStringValue
1352 <  else begin
1353 <    if (stype = SQL_BLOB) then
1354 <    begin
1355 <      ss := TStringStream.Create(Value);
1356 <      try
1357 <        LoadFromStream(ss);
1358 <      finally
1359 <        ss.Free;
1360 <      end;
1361 <    end
1362 <    else if Value = '' then
1363 <      IsNull := True
1364 <    else if (stype = SQL_TIMESTAMP) or (stype = SQL_TYPE_DATE) or
1365 <      (stype = SQL_TYPE_TIME) then
1366 <      xSetAsDateTime(StrToDateTime(Value))
1367 <    else
1368 <      SetStringValue;
1369 <  end;
1370 < end;
1371 <
1372 < procedure TIBXSQLVAR.SetAsString(Value: String);
1373 < var
1374 <   i: integer;
1375 < begin
1376 <  if FUniqueName then
1377 <     xSetAsString(Value)
1378 <  else
1379 <  for i := 0 to FParent.FCount - 1 do
1380 <    if FParent[i].FName = FName then
1381 <       FParent[i].xSetAsString(Value);
1382 < end;
1383 <
1384 < procedure TIBXSQLVAR.xSetAsVariant(Value: Variant);
1385 < begin
1386 <  if VarIsNull(Value) then
1387 <    IsNull := True
1388 <  else case VarType(Value) of
1389 <    varEmpty, varNull:
1390 <      IsNull := True;
1391 <    varSmallint, varInteger, varByte,
1392 <      varWord, varShortInt:
1393 <      AsLong := Value;
1394 <    varInt64:
1395 <      AsInt64 := Value;
1396 <    varSingle, varDouble:
1397 <      AsDouble := Value;
1398 <    varCurrency:
1399 <      AsCurrency := Value;
1400 <    varBoolean:
1401 <      if Value then
1402 <        AsLong := ISC_TRUE
1403 <      else
1404 <        AsLong := ISC_FALSE;
1405 <    varDate:
1406 <      AsDateTime := Value;
1407 <    varOleStr, varString:
1408 <      AsString := Value;
1409 <    varArray:
1410 <      IBError(ibxeNotSupported, [nil]);
1411 <    varByRef, varDispatch, varError, varUnknown, varVariant:
1412 <      IBError(ibxeNotPermitted, [nil]);
1413 <  end;
1414 < end;
1415 <
1416 < procedure TIBXSQLVAR.SetAsVariant(Value: Variant);
1417 < var
1418 <   i: integer;
1419 < begin
1420 <  if FUniqueName then
1421 <     xSetAsVariant(Value)
1422 <  else
1423 <  for i := 0 to FParent.FCount - 1 do
1424 <    if FParent[i].FName = FName then
1425 <       FParent[i].xSetAsVariant(Value);
1426 < end;
1427 <
1428 < procedure TIBXSQLVAR.xSetAsXSQLVAR(Value: PXSQLVAR);
1429 < var
1430 <  sqlind: PShort;
1431 <  sqldata: PChar;
1432 <  local_sqllen: Integer;
1433 < begin
1434 <  sqlind := FXSQLVAR^.sqlind;
1435 <  sqldata := FXSQLVAR^.sqldata;
1436 <  Move(Value^, FXSQLVAR^, SizeOf(TXSQLVAR));
1437 <  FXSQLVAR^.sqlind := sqlind;
1438 <  FXSQLVAR^.sqldata := sqldata;
1439 <  if (Value^.sqltype and 1 = 1) then
1440 <  begin
1441 <    if (FXSQLVAR^.sqlind = nil) then
1442 <      IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1443 <    FXSQLVAR^.sqlind^ := Value^.sqlind^;
1444 <  end
1445 <  else
1446 <    if (FXSQLVAR^.sqlind <> nil) then
1447 <      ReallocMem(FXSQLVAR^.sqlind, 0);
1448 <  if ((FXSQLVAR^.sqltype and (not 1)) = SQL_VARYING) then
1449 <    local_sqllen := FXSQLVAR^.sqllen + 2
1450 <  else
1451 <    local_sqllen := FXSQLVAR^.sqllen;
1452 <  FXSQLVAR^.sqlscale := Value^.sqlscale;
1453 <  IBAlloc(FXSQLVAR^.sqldata, 0, local_sqllen);
1454 <  Move(Value^.sqldata[0], FXSQLVAR^.sqldata[0], local_sqllen);
1455 <  FModified := True;
1456 < end;
1457 <
1458 < procedure TIBXSQLVAR.SetAsXSQLVAR(Value: PXSQLVAR);
1459 < var
1460 <  i: Integer;
1461 < begin
1462 <  if FUniqueName then
1463 <     xSetAsXSQLVAR(Value)
1464 <  else
1465 <  for i := 0 to FParent.FCount - 1 do
1466 <    if FParent[i].FName = FName then
1467 <       FParent[i].xSetAsXSQLVAR(Value);
1468 < end;
1469 <
1470 < procedure TIBXSQLVAR.xSetIsNull(Value: Boolean);
1471 < begin
1472 <  if Value then
1473 <  begin
1474 <    if not IsNullable then
1475 <      IsNullable := True;
1476 <
1477 <    if Assigned(FXSQLVAR^.sqlind) then
1478 <      FXSQLVAR^.sqlind^ := -1;
1479 <    FModified := True;
1480 <  end
1481 <  else
1482 <    if ((not Value) and IsNullable) then
1483 <    begin
1484 <      if Assigned(FXSQLVAR^.sqlind) then
1485 <        FXSQLVAR^.sqlind^ := 0;
1486 <      FModified := True;
1487 <    end;
1488 < end;
1489 <
1490 < procedure TIBXSQLVAR.SetIsNull(Value: Boolean);
1491 < var
1492 <  i: Integer;
1493 < begin
1494 <  if FUniqueName then
1495 <     xSetIsNull(Value)
1496 <  else
1497 <  for i := 0 to FParent.FCount - 1 do
1498 <    if FParent[i].FName = FName then
1499 <       FParent[i].xSetIsNull(Value);
1500 < end;
1501 <
1502 < procedure TIBXSQLVAR.xSetIsNullable(Value: Boolean);
1503 < begin
1504 <  if (Value <> IsNullable) then
1505 <  begin
1506 <    if Value then
1507 <    begin
1508 <      FXSQLVAR^.sqltype := FXSQLVAR^.sqltype or 1;
1509 <      IBAlloc(FXSQLVAR^.sqlind, 0, SizeOf(Short));
1510 <    end
1511 <    else
1512 <    begin
1513 <      FXSQLVAR^.sqltype := FXSQLVAR^.sqltype and (not 1);
1514 <      ReallocMem(FXSQLVAR^.sqlind, 0);
1515 <    end;
1516 <  end;
1517 < end;
1518 <
1519 < procedure TIBXSQLVAR.SetIsNullable(Value: Boolean);
1520 < var
1521 <  i: Integer;
1522 < begin
1523 <  if FUniqueName then
1524 <     xSetIsNullable(Value)
1525 <  else
1526 <  for i := 0 to FParent.FCount - 1 do
1527 <    if FParent[i].FName = FName then
1528 <       FParent[i].xSetIsNullable(Value);
1529 < end;
1530 <
1531 < procedure TIBXSQLVAR.Clear;
1532 < begin
1533 <  IsNull := true;
1534 < end;
1535 <
1536 <
1537 < { TIBXSQLDA }
1538 < constructor TIBXSQLDA.Create(Query: TIBSQL; sqldaType: TIBXSQLDAType);
1539 < begin
1540 <  inherited Create;
1541 <  FSQL := Query;
1542 <  FSize := 0;
1543 <  FUniqueRelationName := '';
1544 <  FInputSQLDA := sqldaType = daInput;
1545 < end;
1546 <
1547 < destructor TIBXSQLDA.Destroy;
1548 < var
1549 <  i: Integer;
1550 < begin
1551 <  if FXSQLDA <> nil then
1552 <  begin
1553 <    for i := 0 to FSize - 1 do
1554 <    begin
1555 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqldata);
1556 <      FreeMem(FXSQLVARs[i].FXSQLVAR^.sqlind);
1557 <      FXSQLVARs[i].Free ;
1558 <    end;
1559 <    FreeMem(FXSQLDA);
1560 <    FXSQLDA := nil;
1561 <    FXSQLVARs := nil;
1562 <  end;
1563 <  inherited Destroy;
1564 < end;
1565 <
1566 <    procedure TIBXSQLDA.SetParamName(FieldName: String; Idx: Integer;
1567 <    UniqueName: boolean);
1568 < var
1569 <  fn: string;
1570 < begin
1571 <  {$ifdef UseCaseSensitiveParamName}
1572 <  FXSQLVARs[Idx].FName := AnsiUpperCase(FieldName);
1573 <  {$else}
1574 <  FXSQLVARs[Idx].FName := FieldName;
1575 <  {$endif}
1576 <  FXSQLVARs[Idx].FIndex := Idx;
1577 <  FXSQLVARs[Idx].FUniqueName :=  UniqueName
1578 < end;
1579 <
1580 < function TIBXSQLDA.GetModified: Boolean;
1581 < var
1582 <  i: Integer;
1583 < begin
1584 <  result := False;
1585 <  for i := 0 to FCount - 1 do
1586 <    if FXSQLVARs[i].Modified then
1587 <    begin
1588 <      result := True;
1589 <      exit;
1590 <    end;
1591 < end;
1592 <
1593 < function TIBXSQLDA.GetRecordSize: Integer;
1594 < begin
1595 <  result := SizeOf(TIBXSQLDA) + XSQLDA_LENGTH(FSize);
1596 < end;
1597 <
1598 < function TIBXSQLDA.GetXSQLDA: PXSQLDA;
1599 < begin
1600 <  result := FXSQLDA;
1601 < end;
1602 <
1603 < function TIBXSQLDA.GetXSQLVAR(Idx: Integer): TIBXSQLVAR;
1604 < begin
1605 <  if (Idx < 0) or (Idx >= FCount) then
1606 <    IBError(ibxeXSQLDAIndexOutOfRange, [nil]);
1607 <  result := FXSQLVARs[Idx]
1608 < end;
1609 <
1610 < function TIBXSQLDA.ByName(Idx: String): TIBXSQLVAR;
1611 < begin
1612 <  result := GetXSQLVARByName(Idx);
1613 <  if result = nil then
1614 <    IBError(ibxeFieldNotFound, [Idx]);
1615 < end;
1616 <
1617 < function TIBXSQLDA.GetXSQLVARByName(Idx: String): TIBXSQLVAR;
1618 < var
1619 <  s: String;
1620 <  i: Integer;
1621 < begin
1622 <  {$ifdef ALLOWDIALECT3PARAMNAMES}
1623 <  s := FormatIdentifierValueNC(FSQL.Database.SQLDialect, Idx);
1624 <  {$else}
1625 <  {$ifdef UseCaseSensitiveParamName}
1626 <   s := AnsiUpperCase(Idx);
1627 <  {$else}
1628 <   s := Idx;
1629 <  {$endif}
1630 <  {$endif}
1631 <  for i := 0 to FCount - 1 do
1632 <    if Vars[i].FName = s then
1633 <    begin
1634 <         Result := FXSQLVARs[i];
1635 <         Exit;
1636 <    end;
1637 <  Result := nil;
1638 < end;
1639 <
1640 < procedure TIBXSQLDA.Initialize;
1641 <
1642 <    function VarByName(idx: string; limit: integer): TIBXSQLVAR;
1643 <    var
1644 <       k: integer;
1645 <    begin
1646 <         for k := 0 to limit do
1647 <             if FXSQLVARs[k].FName = idx then
1648 <             begin
1649 <                  Result := FXSQLVARs[k];
1650 <                  Exit;
1651 <             end;
1652 <         Result := nil;
1653 <    end;
1654 <
1655 < var
1656 <  i, j, j_len: Integer;
1657 <  st: String;
1658 <  bUnique: Boolean;
1659 <  sBaseName: string;
1660 < begin
1661 <  bUnique := True;
1662 <  if FXSQLDA <> nil then
1663 <  begin
1664 <    for i := 0 to FCount - 1 do
1665 <    begin
1666 <      with FXSQLVARs[i].Data^ do
1667 <      begin
1668 <
1669 <        {First get the unique relation name, if any}
1670 <
1671 <        if bUnique and (strpas(relname) <> '') then
1672 <        begin
1673 <          if FUniqueRelationName = '' then
1674 <            FUniqueRelationName := strpas(relname)
1675 <          else
1676 <            if strpas(relname) <> FUniqueRelationName then
1677 <            begin
1678 <              FUniqueRelationName := '';
1679 <              bUnique := False;
1680 <            end;
1681 <        end;
1682 <
1683 <        {If an output SQLDA then copy the aliasnames to the FName list. Ensure
1684 <         that they are all upper case only and disambiguated.
1685 <        }
1686 <
1687 <        if not FInputSQLDA then
1688 <        begin
1689 <          st := Space2Underscore(AnsiUppercase(strpas(aliasname)));
1690 <          if st = '' then
1691 <          begin
1692 <            sBaseName := 'F_'; {do not localize}
1693 <            aliasname_length := 2;
1694 <            j := 1; j_len := 1;
1695 <            st := sBaseName + IntToStr(j);
1696 <          end
1697 <          else
1698 <          begin
1699 <            j := 0; j_len := 0;
1700 <            sBaseName := st;
1701 <          end;
1702 <
1703 <          {Look for other columns with the same name and make unique}
1704 <
1705 <          while VarByName(st,i-1) <> nil do
1706 <          begin
1707 <               Inc(j);
1708 <               j_len := Length(IntToStr(j));
1709 <               if j_len + Length(sBaseName) > 31 then
1710 <                  st := Copy(sBaseName, 1, 31 - j_len) + IntToStr(j)
1711 <               else
1712 <                  st := sBaseName + IntToStr(j);
1713 <          end;
1714 <
1715 <          FXSQLVARs[i].FName := st;
1716 <        end;
1717 <
1718 <        {Finally initialise the XSQLVAR}
1719 <
1720 <        FXSQLVARs[i].FIndex := i;
1721 <
1722 <        case sqltype and (not 1) of
1723 <          SQL_TEXT, SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP,
1724 <          SQL_BLOB, SQL_ARRAY, SQL_QUAD, SQL_SHORT,
1725 <          SQL_LONG, SQL_INT64, SQL_DOUBLE, SQL_FLOAT, SQL_D_FLOAT: begin
1726 <            if (sqllen = 0) then
1727 <              { Make sure you get a valid pointer anyway
1728 <               select '' from foo }
1729 <              IBAlloc(sqldata, 0, 1)
1730 <            else
1731 <              IBAlloc(sqldata, 0, sqllen)
1732 <          end;
1733 <          SQL_VARYING: begin
1734 <            IBAlloc(sqldata, 0, sqllen + 2);
1735 <          end;
1736 <          else
1737 <            IBError(ibxeUnknownSQLDataType, [sqltype and (not 1)])
1738 <        end;
1739 <        if (sqltype and 1 = 1) then
1740 <          IBAlloc(sqlind, 0, SizeOf(Short))
1741 <        else
1742 <          if (sqlind <> nil) then
1743 <            ReallocMem(sqlind, 0);
1744 <      end;
1745 <    end;
1746 <  end;
1747 < end;
1748 <
1749 < procedure TIBXSQLDA.SetCount(Value: Integer);
1750 < var
1751 <  i, OldSize: Integer;
1752 <  p : PXSQLVAR;
1753 < begin
1754 <  FCount := Value;
1755 <  if FCount = 0 then
1756 <    FUniqueRelationName := ''
1757 <  else
1758 <  begin
1759 <    if FSize > 0 then
1760 <      OldSize := XSQLDA_LENGTH(FSize)
1761 <    else
1762 <      OldSize := 0;
1763 <    if FCount > FSize then
1764 <    begin
1765 <      IBAlloc(FXSQLDA, OldSize, XSQLDA_LENGTH(FCount));
1766 <      SetLength(FXSQLVARs, FCount);
1767 <      FXSQLDA^.version := SQLDA_VERSION1;
1768 <      p := @FXSQLDA^.sqlvar[0];
1769 <      for i := 0 to FCount - 1 do
1770 <      begin
1771 <        if i >= FSize then
1772 <          FXSQLVARs[i] := TIBXSQLVAR.Create(self, FSQL);
1773 <        FXSQLVARs[i].FXSQLVAR := p;
1774 <        p := Pointer(PChar(p) + sizeof(FXSQLDA^.sqlvar));
1775 <      end;
1776 <      FSize := FCount;
1777 <    end;
1778 <    if FSize > 0 then
1779 <    begin
1780 <      FXSQLDA^.sqln := Value;
1781 <      FXSQLDA^.sqld := Value;
1782 <    end;
1783 <  end;
257 >  ReallocMem(Pointer(P), NewSize);
258 >  for i := OldSize to NewSize - 1 do PChar(P)[i] := #0;
259   end;
260  
261   { TIBOutputDelimitedFile }
# Line 1826 | 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 UNIX}
309      if FHandle <> -1 then
# Line 2019 | Line 494 | begin
494      for i := 0 to Columns.Count - 1 do
495      begin
496        {$IFDEF UNIX}
497 <      BytesWritten := FpWrite(FHandle,Columns[i].Data^.sqldata^, Columns[i].Data^.sqllen);
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 2059 | Line 534 | begin
534      for i := 0 to Params.Count - 1 do
535      begin
536        {$IFDEF UNIX}
537 <      BytesRead := FpRead(FHandle,Params[i].Data^.sqldata^,Params[i].Data^.sqllen);
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 2093 | Line 568 | end;
568   constructor TIBSQL.Create(AOwner: TComponent);
569   begin
570    inherited Create(AOwner);
2096  FIBLoaded := False;
2097  CheckIBLoaded;
2098  FIBLoaded := True;
571    FGenerateParamNames := False;
572    FGoToFirstRecordOnExecute := True;
573    FBase := TIBBase.Create(Self);
574    FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
575    FBase.BeforeTransactionEnd := BeforeTransactionEnd;
2104  FBOF := False;
2105  FEOF := False;
2106  FPrepared := False;
576    FRecordCount := 0;
577    FSQL := TStringList.Create;
578    TStringList(FSQL).OnChanging := SQLChanging;
579 <  FProcessedSQL := TStringList.Create;
2111 <  FHandle := nil;
2112 <  FSQLParams := TIBXSQLDA.Create(self,daInput);
2113 <  FSQLRecord := TIBXSQLDA.Create(self,daOutput);
2114 <  FSQLType := SQLUnknown;
579 >  TStringList(FSQL).OnChange := SQLChanged;
580    FParamCheck := True;
2116  FCursor := Name + RandomString(8);
581    if AOwner is TIBDatabase then
582      Database := TIBDatabase(AOwner)
583    else
# Line 2123 | Line 587 | end;
587  
588   destructor TIBSQL.Destroy;
589   begin
590 <  if FIBLoaded then
591 <  begin
592 <    if (FOpen) then
2129 <      Close;
2130 <    if (FHandle <> nil) then
2131 <      FreeHandle;
2132 <    FSQL.Free;
2133 <    FProcessedSQL.Free;
2134 <    FBase.Free;
2135 <    FSQLParams.Free;
2136 <    FSQLRecord.Free;
2137 <  end;
590 >  FreeHandle;
591 >  FSQL.Free;
592 >  FBase.Free;
593    inherited Destroy;
594   end;
595  
# Line 2142 | 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 2154 | 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 2171 | 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;
2190 var
2191  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(
2200 <              [isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
2201 <        IBDatabaseError;
2202 <    end;
2203 <  finally
2204 <    FEOF := False;
2205 <    FBOF := False;
2206 <    FOpen := False;
2207 <    FRecordCount := 0;
2208 <  end;
646 >  if FResults <> nil then
647 >    FResults.SetRetainInterfaces(false);
648 >  FResultSet := nil;
649 >  FResults := nil;
650 >  FBOF := false;
651 >  FEOF := false;
652 >  FRecordCount := 0;
653   end;
654  
655 < function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
655 > function TIBSQL.GetFieldCount: integer;
656   begin
657 <  result := 0;
658 < if Transaction <> nil then
659 <    result := Transaction.Call(ErrCode, RaiseError)
657 >  if FResults <> nil then
658 >    Result := FResults.GetCount
659 >  else
660 >  if FMetaData <> nil then
661 >    Result := FMetaData.GetCount
662    else
663 <  if RaiseError and (ErrCode > 0) then
2218 <    IBDataBaseError;
663 >    Result := 0;
664   end;
665  
666 < function TIBSQL.Current: TIBXSQLDA;
666 > function TIBSQL.GetOpen: Boolean;
667   begin
668 <  result := FSQLRecord;
668 >  Result := FResultSet <> nil;
669   end;
670  
671 < function TIBSQL.GetFieldCount: integer;
671 > function TIBSQL.GetPrepared: Boolean;
672 > begin
673 >  Result := (FStatement <> nil) and FStatement.IsPrepared;
674 > end;
675 >
676 > function TIBSQL.GetSQLStatementType: TIBSQLStatementTypes;
677   begin
678 <  Result := FSQLRecord.Count
678 >  if FStatement = nil then
679 >    Result := SQLUnknown
680 >  else
681 >    Result := FStatement.GetSQLStatementType;
682   end;
683  
684   procedure TIBSQL.SetUniqueParamNames(AValue: Boolean);
# Line 2237 | Line 690 | end;
690  
691   procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
692   begin
693 <  if (FHandle <> nil) then begin
2241 <    Close;
2242 <    FreeHandle;
2243 <  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,
2274 <                            @FHandle,
2275 <                            Database.SQLDialect,
2276 <                            FSQLParams.AsXSQLDA,
2277 <                            FSQLRecord.AsXSQLDA), True);
2278 < (*      if (fetch_res <> 0) and (fetch_res <> isc_deadlock) then
2279 <      begin
2280 <         { Sometimes a prepared stored procedure appears to get
2281 <           off sync on the server ....This code is meant to try
2282 <           to work around the problem simply by "retrying". This
2283 <           need to be reproduced and fixed.
2284 <         }
2285 <        isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
2286 <                         PChar(FProcessedSQL.Text), 1, nil);
2287 <        Call(isc_dsql_execute2(StatusVector,
2288 <                            TRHandle,
2289 <                            @FHandle,
2290 <                            Database.SQLDialect,
2291 <                            FSQLParams.AsXSQLDA,
2292 <                            FSQLRecord.AsXSQLDA), True);
2293 <      end;  *)
2294 <    end
2295 <    else
2296 <      Call(isc_dsql_execute(StatusVector,
2297 <                           TRHandle,
2298 <                           @FHandle,
2299 <                           Database.SQLDialect,
2300 <                           FSQLParams.AsXSQLDA), True)
709 >  {$IFDEF IBXQUERYTIME}
710 >  tmsecs := TimeStampToMSecs(DateTimeToTimeStamp(Now));
711 >  {$ENDIF}
712 >  if SQLStatementType = SQLSelect then
713 >  begin
714 >    FResultSet := FStatement.OpenCursor;
715 >    FResults := FResultSet;
716 >    FResults.SetRetainInterfaces(true);
717 >    FBOF := True;
718 >    FEOF := False;
719 >    FRecordCount := 0;
720 >    if not (csDesigning in ComponentState) then
721 >      MonitorHook.SQLExecute(Self);
722 >    if FGoToFirstRecordOnExecute then
723 >      Next;
724 >  end
725 >  else
726 >  begin
727 >    FResults := FStatement.Execute;
728 >    if not (csDesigning in ComponentState) then
729 >      MonitorHook.SQLExecute(Self);
730    end;
731 <  if not (csDesigning in ComponentState) then
732 <    MonitorHook.SQLExecute(Self);
731 >  {$IFDEF IBXQUERYTIME}
732 >  writeln('Executing ',FStatement.GetSQLText,
733 >    ' Response time= ',Format('%f msecs',[TimeStampToMSecs(DateTimeToTimeStamp(Now)) - tmsecs]));
734 >  {$ENDIF}
735 >  {$IFDEF IBXQUERYSTATS}
736 >  if FStatement.GetPerfStatistics(stats) then
737 >    writeln('Executing ',FStatement.GetSQLText,
738 >    ' Elapsed time= ', FormatFloat('#0.000',stats[psRealTime]/1000),' sec');
739 >  {$ENDIF}
740 >  FBase.DoAfterExecQuery(self);
741 > end;
742 >
743 > function TIBSQL.HasField(FieldName: String): boolean;
744 > begin
745 >  if FResults = nil then
746 >    IBError(ibxeNoFieldAccess,[nil]);
747 >
748 >  Result := FResults.ByName(FieldName) <> nil;
749   end;
750  
751   function TIBSQL.GetEOF: Boolean;
752   begin
753 <  result := FEOF or not FOpen;
753 >  result := FEOF or (FResultSet = nil);
754   end;
755  
756 < function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
2312 < var
2313 <  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]);
2318  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;
2342 < var
2343 <  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
2357 <        Close;
2358 <        raise;
2359 <      end;
2360 <    end else begin
802 >    try
803 >      Result := FResultSet.FetchNext;
804 >    except
805 >      Close;
806 >      raise;
807 >    end;
808 >
809 >    if Result then
810 >    begin
811        Inc(FRecordCount);
812        FBOF := False;
813 <      result := FSQLRecord;
814 <    end;
813 >    end
814 >    else
815 >      FEOF := true;
816 >
817      if not (csDesigning in ComponentState) then
818        MonitorHook.SQLFetch(Self);
819    end;
820   end;
821  
822   procedure TIBSQL.FreeHandle;
2371 var
2372  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
2382 <      isc_res :=
2383 <        Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
2384 <      if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
2385 <        IBDataBaseError;
2386 <    end;
2387 <  finally
2388 <    FPrepared := False;
2389 <    FHandle := nil;
2390 <  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 2395 | Line 836 | begin
836    result := FBase.Database;
837   end;
838  
2398 function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
2399 begin
2400  result := FBase.DBHandle;
2401 end;
2402
839   function TIBSQL.GetPlan: String;
2404 var
2405  result_buffer: array[0..16384] of Char;
2406  result_length, i: Integer;
2407  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);
2416 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
2417 <                           SizeOf(result_buffer), result_buffer), True);
2418 <    if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
2419 <      IBError(ibxeUnknownError, [nil]);
2420 <    result_length := isc_vax_integer(@result_buffer[1], 2);
2421 <    SetString(result, nil, result_length);
2422 <    for i := 1 to result_length do
2423 <      result[i] := result_buffer[i + 2];
2424 <    result := Trim(result);
2425 <  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;
856   var
857 <  result_buffer: array[0..1048] of Char;
2436 <  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
2444 <      IBDatabaseError;
2445 <    if (result_buffer[0] <> Char(isc_info_sql_records)) then
2446 <      result := -1
2447 <    else
2448 <    case SQLType of
2449 <    SQLUpdate:   Result := isc_vax_integer(@result_buffer[6], 4);
2450 <    SQLDelete:   Result := isc_vax_integer(@result_buffer[13], 4);
2451 <    SQLInsert:   Result := isc_vax_integer(@result_buffer[27], 4);
2452 <    else         Result := -1 ;
2453 <    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 2466 | Line 877 | begin
877    result := FBase.Transaction;
878   end;
879  
2469 function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
2470 begin
2471  result := FBase.TRHandle;
2472 end;
2473
2474 {
2475 Preprocess SQL
2476 Using FSQL, process the typed SQL and put the process SQL
2477 in FProcessedSQL and parameter names in FSQLParams
2478 }
2479 procedure TIBSQL.PreprocessSQL;
2480 var
2481  cCurChar, cNextChar, cQuoteChar: Char;
2482  sSQL, sProcessedSQL, sParamName: String;
2483  i, iLenSQL, iSQLPos: Integer;
2484  iCurState {$ifdef ALLOWDIALECT3PARAMNAMES}, iCurParamState {$endif}: Integer;
2485  iParamSuffix: Integer;
2486  slNames: TStrings;
2487
2488 const
2489  DefaultState = 0;
2490  CommentState = 1;
2491  QuoteState = 2;
2492  ParamState = 3;
2493 {$ifdef ALLOWDIALECT3PARAMNAMES}
2494  ParamDefaultState = 0;
2495  ParamQuoteState = 1;
2496  {$endif}
2497
2498  procedure AddToProcessedSQL(cChar: Char);
2499  begin
2500    sProcessedSQL[iSQLPos] := cChar;
2501    Inc(iSQLPos);
2502  end;
2503
2504 begin
2505  slNames := TStringList.Create;
2506  try
2507    { Do some initializations of variables }
2508    iParamSuffix := 0;
2509    cQuoteChar := '''';
2510    sSQL := FSQL.Text;
2511    iLenSQL := Length(sSQL);
2512    SetString(sProcessedSQL, nil, iLenSQL + 1);
2513    i := 1;
2514    iSQLPos := 1;
2515    iCurState := DefaultState;
2516    {$ifdef ALLOWDIALECT3PARAMNAMES}
2517    iCurParamState := ParamDefaultState;
2518    {$endif}
2519    { Now, traverse through the SQL string, character by character,
2520     picking out the parameters and formatting correctly for InterBase }
2521    while (i <= iLenSQL) do begin
2522      { Get the current token and a look-ahead }
2523      cCurChar := sSQL[i];
2524      if i = iLenSQL then
2525        cNextChar := #0
2526      else
2527        cNextChar := sSQL[i + 1];
2528      { Now act based on the current state }
2529      case iCurState of
2530        DefaultState: begin
2531          case cCurChar of
2532            '''', '"': begin
2533              cQuoteChar := cCurChar;
2534              iCurState := QuoteState;
2535            end;
2536            '?', ':': begin
2537              iCurState := ParamState;
2538              AddToProcessedSQL('?');
2539            end;
2540            '/': if (cNextChar = '*') then begin
2541              AddToProcessedSQL(cCurChar);
2542              Inc(i);
2543              iCurState := CommentState;
2544            end;
2545          end;
2546        end;
2547        CommentState: begin
2548          if (cNextChar = #0) then
2549            IBError(ibxeSQLParseError, [SEOFInComment])
2550          else if (cCurChar = '*') then begin
2551            if (cNextChar = '/') then
2552              iCurState := DefaultState;
2553          end;
2554        end;
2555        QuoteState: begin
2556          if cNextChar = #0 then
2557            IBError(ibxeSQLParseError, [SEOFInString])
2558          else if (cCurChar = cQuoteChar) then begin
2559            if (cNextChar = cQuoteChar) then begin
2560              AddToProcessedSQL(cCurChar);
2561              Inc(i);
2562            end else
2563              iCurState := DefaultState;
2564          end;
2565        end;
2566        ParamState:
2567        begin
2568          { collect the name of the parameter }
2569          {$ifdef ALLOWDIALECT3PARAMNAMES}
2570          if iCurParamState = ParamDefaultState then
2571          begin
2572            if cCurChar = '"' then
2573              iCurParamState := ParamQuoteState
2574            else
2575            {$endif}
2576            if (cCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_', '$']) then
2577                sParamName := sParamName + cCurChar
2578            else if FGenerateParamNames then
2579            begin
2580              sParamName := 'IBXParam' + IntToStr(iParamSuffix); {do not localize}
2581              Inc(iParamSuffix);
2582              iCurState := DefaultState;
2583              slNames.AddObject(sParamName,self); //Note local convention
2584                                                  //add pointer to self to mark entry
2585              sParamName := '';
2586            end
2587            else
2588              IBError(ibxeSQLParseError, [SParamNameExpected]);
2589          {$ifdef ALLOWDIALECT3PARAMNAMES}
2590          end
2591          else begin
2592            { determine if Quoted parameter name is finished }
2593            if cCurChar = '"' then
2594            begin
2595              Inc(i);
2596              slNames.Add(sParamName);
2597              SParamName := '';
2598              iCurParamState := ParamDefaultState;
2599              iCurState := DefaultState;
2600            end
2601            else
2602              sParamName := sParamName + cCurChar
2603          end;
2604          {$endif}
2605          { determine if the unquoted parameter name is finished }
2606          if {$ifdef ALLOWDIALECT3PARAMNAMES}(iCurParamState <> ParamQuoteState) and {$endif}
2607            (iCurState <> DefaultState) then
2608          begin
2609            if not (cNextChar in ['A'..'Z', 'a'..'z',
2610                                  '0'..'9', '_', '$']) then begin
2611              Inc(i);
2612              iCurState := DefaultState;
2613              slNames.Add(sParamName);
2614              sParamName := '';
2615            end;
2616          end;
2617        end;
2618      end;
2619      if iCurState <> ParamState then
2620        AddToProcessedSQL(sSQL[i]);
2621      Inc(i);
2622    end;
2623    AddToProcessedSQL(#0);
2624    FSQLParams.Count := slNames.Count;
2625    for i := 0 to slNames.Count - 1 do
2626      FSQLParams.SetParamName(slNames[i], i,FUniqueParamNames or (slNames.Objects[i] <> nil));
2627    FProcessedSQL.Text := sProcessedSQL;
2628  finally
2629    slNames.Free;
2630  end;
2631 end;
2632
880   procedure TIBSQL.SetDatabase(Value: TIBDatabase);
881   begin
882 +  if Value = FBase.Database then Exit;
883    FBase.Database := Value;
884 +  FreeHandle;
885   end;
886  
887   procedure TIBSQL.Prepare;
2639 var
2640  stmt_len: Integer;
2641  res_buffer: array[0..7] of Char;
2642  type_item: Char;
888   begin
889    CheckClosed;
890    FBase.CheckDatabase;
891    FBase.CheckTransaction;
892 <  if FPrepared then
892 >  Close;
893 >  if Prepared then
894      exit;
895    if (FSQL.Text = '') then
896      IBError(ibxeEmptyQuery, [nil]);
897 +
898 +  if FStatement <> nil then
899 +    FStatement.Prepare(Transaction.TransactionIntf)
900 +  else
901    if not ParamCheck then
902 <    FProcessedSQL.Text := FSQL.Text
902 >    FStatement := Database.Attachment.Prepare(Transaction.TransactionIntf,SQL.Text)
903    else
904 <    PreprocessSQL;
905 <  if (FProcessedSQL.Text = '') then
906 <    IBError(ibxeEmptyQuery, [nil]);
907 <  try
908 <    Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
909 <                                    @FHandle), True);
910 <    Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
911 <               PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
912 <    { After preparing the statement, query the stmt type and possibly
913 <      create a FSQLRecord "holder" }
914 <    { Get the type of the statement }
2665 <    type_item := Char(isc_info_sql_stmt_type);
2666 <    Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
2667 <                         SizeOf(res_buffer), res_buffer), True);
2668 <    if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
2669 <      IBError(ibxeUnknownError, [nil]);
2670 <    stmt_len := isc_vax_integer(@res_buffer[1], 2);
2671 <    FSQLType := TIBSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
2672 <    { Done getting the type }
2673 <    case FSQLType of
2674 <      SQLGetSegment,
2675 <      SQLPutSegment,
2676 <      SQLStartTransaction: begin
2677 <        FreeHandle;
2678 <        IBError(ibxeNotPermitted, [nil]);
2679 <      end;
2680 <      SQLCommit,
2681 <      SQLRollback,
2682 <      SQLDDL, SQLSetGenerator,
2683 <      SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
2684 <      SQLExecProcedure: begin
2685 <        { We already know how many inputs there are, so... }
2686 <        if (FSQLParams.FXSQLDA <> nil) and
2687 <           (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
2688 <                                        FSQLParams.FXSQLDA), False) > 0) then
2689 <          IBDataBaseError;
2690 <        FSQLParams.Initialize;
2691 <        if FSQLType in [SQLSelect, SQLSelectForUpdate,
2692 <                        SQLExecProcedure] then begin
2693 <          { Allocate an initial output descriptor (with one column) }
2694 <          FSQLRecord.Count := 1;
2695 <          { Using isc_dsql_describe, get the right size for the columns... }
2696 <          Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2697 <          if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin
2698 <            FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
2699 <            Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
2700 <          end else if FSQLRecord.FXSQLDA^.sqld = 0 then
2701 <            FSQLRecord.Count := 0;
2702 <          FSQLRecord.Initialize;
2703 <        end;
2704 <      end;
2705 <    end;
2706 <    FPrepared := True;
2707 <    if not (csDesigning in ComponentState) then
904 >    FStatement := Database.Attachment.PrepareWithNamedParameters(
905 >                     Transaction.TransactionIntf,
906 >                     SQL.Text,
907 >                     GenerateParamNames);
908 >  {$IFDEF IBXQUERYSTATS}
909 >  FStatement.EnableStatistics(true);
910 >  {$ENDIF}
911 >  FMetaData := FStatement.GetMetaData;
912 >  FSQLParams := FStatement.GetSQLParams;
913 >  FStatement.SetRetainInterfaces(true);
914 >  if not (csDesigning in ComponentState) then
915        MonitorHook.SQLPrepare(Self);
2709  except
2710    on E: Exception do begin
2711      if (FHandle <> nil) then
2712        FreeHandle;
2713      raise;
2714    end;
2715  end;
916   end;
917  
918   function TIBSQL.GetUniqueRelationName: String;
919   begin
920 <  if FPrepared and (FSQLType = SQLSelect) then
921 <    result := FSQLRecord.UniqueRelationName
920 >  if Prepared and (GetSQLStatementType = SQLSelect) then
921 >    result := FMetaData.GetUniqueRelationName
922    else
923      result := '';
924   end;
# Line 2745 | Line 945 | procedure TIBSQL.SQLChanging(Sender: TOb
945   begin
946    if Assigned(OnSQLChanging) then
947      OnSQLChanging(Self);
948 <  if FHandle <> nil then FreeHandle;
948 >
949 >  FreeHandle;
950   end;
951  
952 < procedure TIBSQL.BeforeTransactionEnd(Sender: TObject);
952 > procedure TIBSQL.SQLChanged(Sender: TObject);
953   begin
954 <  if (FOpen) then
955 <    Close;
954 >  if assigned(OnSQLChanged) then
955 >    OnSQLChanged(self);
956 > end;
957 >
958 > procedure TIBSQL.BeforeTransactionEnd(Sender: TObject;
959 >  Action: TTransactionAction);
960 > begin
961 >  if not (Owner is TIBCustomDataSet) then
962 >    FreeHandle;
963   end;
964  
965   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines