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