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