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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines