From 03dd59648560b283e298a8b9d255ae460e1495f7 Mon Sep 17 00:00:00 2001
From: michael <michael@freepascal.org>
Date: Sat, 1 Jun 2013 18:23:41 +0000
Subject: [PATCH] * Refactor TSQLQuery to use TSQLStatement

git-svn-id: trunk@24742 -
---
 packages/fcl-db/src/sqldb/sqldb.pp | 435 ++++++++++++++++++-----------
 1 file changed, 279 insertions(+), 156 deletions(-)

diff --git a/packages/fcl-db/src/sqldb/sqldb.pp b/packages/fcl-db/src/sqldb/sqldb.pp
index 79034b2fda..28bdc3f655 100644
--- a/packages/fcl-db/src/sqldb/sqldb.pp
+++ b/packages/fcl-db/src/sqldb/sqldb.pp
@@ -221,20 +221,26 @@ type
     FDatabase: TSQLConnection;
     FParams: TParams;
     FSQL: TStrings;
-    FSQLBuf : String;
+    FOrigSQL : String;
+    FServerSQL : String;
     FTransaction: TSQLTransaction;
     FDatasource : TDatasource;
     FParseSQL: Boolean;
-    procedure OnChangeSQL(Sender : TObject);
     procedure SetDatabase(AValue: TSQLConnection);
-    procedure SetDataSource(AValue: TDatasource);
     procedure SetParams(AValue: TParams);
     procedure SetSQL(AValue: TStrings);
     procedure SetTransaction(AValue: TSQLTransaction);
     Function GetPrepared : Boolean;
   Protected
+    procedure OnChangeSQL(Sender : TObject); virtual;
+    function GetDataSource: TDatasource; Virtual;
+    procedure SetDataSource(AValue: TDatasource); virtual;
+    procedure AllocateCursor;
     Function GetSchemaType : TSchemaType; virtual;
+    Function GetSchemaObjectName : String; virtual;
+    Function GetSchemaPattern: String; virtual;
     Function IsSelectable : Boolean ; virtual;
+    procedure GetStatementInfo(Var ASQL: String; Full: Boolean; ASchema: TSchemaType; out Info: TSQLStatementInfo); virtual;
     Procedure DoExecute; virtual;
     procedure DoPrepare; virtual;
     procedure DoUnPrepare; virtual;
@@ -247,7 +253,7 @@ type
     Property Transaction : TSQLTransaction Read FTransaction Write SetTransaction;
     Property SQL : TStrings Read FSQL Write SetSQL;
     Property Params : TParams Read FParams Write SetParams;
-    Property Datasource : TDatasource Read FDataSource Write SetDataSource;
+    Property Datasource : TDatasource Read GetDataSource Write SetDataSource;
     Property ParseSQL : Boolean Read FParseSQL Write FParseSQL;
     Property CheckParams : Boolean Read FCheckParams Write FCheckParams default true;
   Public
@@ -276,24 +282,26 @@ type
 
   TCustomSQLQuery = class (TCustomBufDataset)
   private
-    FCheckParams: Boolean;
-    FCursor              : TSQLCursor;
+    // FCheckParams: Boolean;
+    // FCursor              : TSQLCursor;
+    FParams: TParams;
+    FSchemaType: TSchemaType;
+//    FSQL: TStringlist;
     FUpdateable          : boolean;
     FTableName           : string;
-    FSQL                 : TStringList;
+    FStatement           : TCustomSQLStatement;
     FUpdateSQL,
     FInsertSQL,
     FDeleteSQL           : TStringList;
     FIsEOF               : boolean;
     FLoadingFieldDefs    : boolean;
     FUpdateMode          : TUpdateMode;
-    FParams              : TParams;
     FusePrimaryKeyAsKey  : Boolean;
     FSQLBuf              : String;
     FWhereStartPos       : integer;
     FWhereStopPos        : integer;
-    FParseSQL            : boolean;
-    FMasterLink          : TMasterParamsDatalink;
+    // FParseSQL            : boolean;
+//    FMasterLink          : TMasterParamsDatalink;
 //    FSchemaInfo          : TSchemaInfo;
 
     FServerFilterText    : string;
@@ -302,7 +310,6 @@ type
     FServerIndexDefs     : TServerIndexDefs;
 
     // Used by SetSchemaType
-    FSchemaType          : TSchemaType;
     FSchemaObjectName    : string;
     FSchemaPattern       : string;
 
@@ -310,24 +317,31 @@ type
     FDeleteQry,
     FInsertQry           : TCustomSQLQuery;
     procedure FreeFldBuffers;
+    function GetCheckParams: Boolean;
+    function GetParams: TParams;
+    function GetParseSQL: Boolean;
     function GetServerIndexDefs: TServerIndexDefs;
+    function GetSQL: TStringlist;
     function GetStatementType : TStatementType;
+    procedure SetCheckParams(AValue: Boolean);
     procedure SetDeleteSQL(const AValue: TStringlist);
     procedure SetInsertSQL(const AValue: TStringlist);
+    procedure SetParams(AValue: TParams);
     procedure SetParseSQL(AValue : Boolean);
     procedure SetSQL(const AValue: TStringlist);
     procedure SetUpdateSQL(const AValue: TStringlist);
     procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
     procedure SetUpdateMode(AValue : TUpdateMode);
-    procedure OnChangeSQL(Sender : TObject);
+//    procedure OnChangeSQL(Sender : TObject);
     procedure OnChangeModifySQL(Sender : TObject);
     procedure Execute;
-    Function SQLParser(const ASQL : string) : TStatementType;
+//    Function SQLParser(const ASQL : string) : TStatementType;
     procedure ApplyFilter;
     Function AddFilter(SQLstr : string) : string;
   protected
     // abstract & virtual methods of TBufDataset
     function Fetch : boolean; override;
+    Function Cursor : TSQLCursor;
     function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
     // abstract & virtual methods of TDataset
     procedure UpdateServerIndexDefs; virtual;
@@ -395,16 +409,16 @@ type
   // protected
     property SchemaType : TSchemaType read FSchemaType default stNoSchema;
     property Transaction;
-    property SQL : TStringlist read FSQL write SetSQL;
+    property SQL : TStringlist read GetSQL write SetSQL;
     property UpdateSQL : TStringlist read FUpdateSQL write SetUpdateSQL;
     property InsertSQL : TStringlist read FInsertSQL write SetInsertSQL;
     property DeleteSQL : TStringlist read FDeleteSQL write SetDeleteSQL;
-    property Params : TParams read FParams write FParams;
+    property Params : TParams read GetParams Write SetParams;
     property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode default upWhereKeyOnly;
     property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey default true;
     property StatementType : TStatementType read GetStatementType;
-    property ParseSQL : Boolean read FParseSQL write SetParseSQL default true;
-    Property CheckParams : Boolean Read FCheckParams Write FCheckParams default true;
+    property ParseSQL : Boolean read GetParseSQL write SetParseSQL default true;
+    Property CheckParams : Boolean Read GetCheckParams Write SetCheckParams default true;
     Property DataSource : TDatasource Read GetDataSource Write SetDatasource;
     property ServerFilter: string read FServerFilterText write SetServerFilterText;
     property ServerFiltered: Boolean read FServerFiltered write SetServerFiltered default False;
@@ -699,7 +713,7 @@ begin
   If (FParams.Count>0) and Assigned(FDatasource) then
     ; // FMasterLink.CopyParamsFromMaster(False);
   If LogEvent(detExecute) then
-    Log(detExecute,FSQLBuf);
+    Log(detExecute,FServerSQL);
   Database.Execute(FCursor,Transaction, FParams);
 end;
 
@@ -770,29 +784,58 @@ begin
   Result:=stNoSchema
 end;
 
+function TCustomSQLStatement.GetSchemaObjectName: String;
+begin
+  Result:='';
+end;
+
+function TCustomSQLStatement.GetSchemaPattern: String;
+begin
+  Result:='';
+end;
+
 function TCustomSQLStatement.IsSelectable: Boolean;
 begin
   Result:=False;
 end;
 
+
+procedure TCustomSQLStatement.GetStatementInfo(var ASQL: String; Full: Boolean;
+  ASchema: TSchemaType; out Info: TSQLStatementInfo);
+
+begin
+  Info:=Database.GetStatementInfo(ASQL,Full,ASchema);
+end;
+
+procedure TCustomSQLStatement.AllocateCursor;
+
+begin
+  if not assigned(FCursor) then
+    FCursor:=Database.AllocateCursorHandle;
+end;
+
 procedure TCustomSQLStatement.DoPrepare;
 
 var
   StmType: TStatementType;
-
+  I : TSQLStatementInfo;
 begin
-  FSQLBuf := TrimRight(FSQL.Text);
-  if (FSQLBuf='') then
+  if GetSchemaType=stNoSchema then
+    FOrigSQL := TrimRight(FSQL.Text)
+  else
+    FOrigSQL := Database.GetSchemaInfoSQL(GetSchemaType, GetSchemaObjectName, GetSchemaPattern);
+  if (FOrigSQL='') then
     DatabaseError(SErrNoStatement);
-  StmType:=Database.GetStatementInfo(FSQLBuf,ParseSQL,GetSchemaType).StatementType;
-  if not assigned(FCursor) then
-    FCursor:=Database.AllocateCursorHandle;
+  FServerSQL:=FOrigSQL;
+  GetStatementInfo(FServerSQL,ParseSQL,GetSchemaType,I);
+  StmType:=I.StatementType;
+  AllocateCursor;
   FCursor.FSelectable:=False;
   FCursor.FStatementType:=StmType;
   FCursor.FSchemaType:=GetSchemaType;
   If LogEvent(detPrepare) then
-    Log(detPrepare,FSQLBuf);
-  Database.PrepareStatement(FCursor,Transaction,FSQLBuf,FParams);
+    Log(detPrepare,FServerSQL);
+  Database.PrepareStatement(FCursor,Transaction,FServerSQL,FParams);
 end;
 
 procedure TCustomSQLStatement.Prepare;
@@ -834,6 +877,11 @@ begin
       FreeAndNil(FCursor);
 end;
 
+function TCustomSQLStatement.GetDataSource: TDatasource;
+begin
+  Result:=FDatasource;
+end;
+
 procedure TCustomSQLStatement.Unprepare;
 begin
   if Prepared then
@@ -1283,13 +1331,13 @@ begin
 end;
 
 { TCustomSQLQuery }
+(*
 procedure TCustomSQLQuery.OnChangeSQL(Sender : TObject);
 
 var ConnOptions : TConnOptions;
     NewParams: TParams;
 
 begin
-  UnPrepare;
   FSchemaType:=stNoSchema;
   if (FSQL <> nil) and CheckParams then
     begin
@@ -1306,10 +1354,9 @@ begin
     finally
       NewParams.Free;
     end;
-    If Assigned(FMasterLink) then
-      FMasterLink.RefreshParamNames;
     end;
 end;
+*)
 
 function TCustomSQLQuery.ParamByName(const AParamName: String): TParam;
 
@@ -1328,6 +1375,8 @@ procedure TCustomSQLQuery.SetTransaction(Value: TDBTransaction);
 begin
   UnPrepare;
   inherited;
+  If Assigned(FStatement) then
+    FStatement.Transaction:=TSQLTransaction(Value);
   If (Transaction<>Nil) and (Database=Nil) then
     Database:=TSQLTransaction(Transaction).Database;
 end;
@@ -1342,19 +1391,27 @@ begin
     if assigned(value) and not (Value is TSQLConnection) then
       DatabaseErrorFmt(SErrNotASQLConnection,[value.Name],self);
     UnPrepare;
-    if assigned(FCursor) then TSQLConnection(DataBase).DeAllocateCursorHandle(FCursor);
     db := TSQLConnection(Value);
+    If Assigned(FStatement) then
+      FStatement.Database:=DB;
     inherited setdatabase(value);
+(*
+     FStatement.Database:=Db,
+    if assigned(FCursor) then TSQLConnection(DataBase).DeAllocateCursorHandle(FCursor);
+*)
     if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
       transaction := Db.Transaction;
-    OnChangeSQL(Self);
+//    FStatement.OnChangeSQL(Self);
     end;
 end;
 
 function TCustomSQLQuery.IsPrepared: Boolean;
 
 begin
-  Result := Assigned(FCursor) and FCursor.FPrepared;
+  if Assigned(Fstatement) then
+    Result := FStatement.Prepared
+  else
+    Result:=False;
 end;
 
 function TCustomSQLQuery.AddFilter(SQLstr: string): string;
@@ -1381,17 +1438,11 @@ var S : String;
 
 begin
   FreeFldBuffers;
-  TSQLConnection(Database).UnPrepareStatement(FCursor);
+  FStatement.Unprepare;
   FIsEOF := False;
   inherited internalclose;
-
-  s := FSQLBuf;
-
-  if ServerFiltered then s := AddFilter(s);
-
-  TSQLConnection(Database).PrepareStatement(FCursor,(Transaction as TSQLTransaction),S,FParams);
-
-  Execute;
+  FStatement.DoPrepare;
+  FStatement.DoExecute;
   inherited InternalOpen;
   First;
 end;
@@ -1409,11 +1460,13 @@ end;
 procedure TCustomSQLQuery.SetServerFiltered(Value: Boolean);
 
 begin
-  if Value and not FParseSQL then DatabaseErrorFmt(SNoParseSQL,['Filtering ']);
+  if Value and not ParseSQL then
+    DatabaseErrorFmt(SNoParseSQL,['Filtering ']);
   if (ServerFiltered <> Value) then
     begin
     FServerFiltered := Value;
-    if active then ApplyFilter;
+    if active then
+      ApplyFilter;
     end;
 end;
 
@@ -1427,72 +1480,41 @@ begin
 end;
 
 procedure TCustomSQLQuery.Prepare;
-var
-  db     : tsqlconnection;
-  sqltr  : tsqltransaction;
-  StmType: TStatementType;
 
 begin
-  if not IsPrepared then
-    begin
-    db := TSQLConnection(Database);
-    sqltr := (transaction as tsqltransaction);
-    if not assigned(Db) then
-      DatabaseError(SErrDatabasenAssigned);
-    if not assigned(sqltr) then
-      DatabaseError(SErrTransactionnSet);
-
-    if not Db.Connected then db.Open;
-    if not sqltr.Active then sqltr.StartTransaction;
-
-    if FSchemaType=stNoSchema then
-      FSQLBuf := TrimRight(FSQL.Text)
-    else
-      FSQLBuf := db.GetSchemaInfoSQL(FSchemaType, FSchemaObjectName, FSchemaPattern);
-
-    if FSQLBuf = '' then
-      DatabaseError(SErrNoStatement);
-
-    StmType:=SQLParser(FSQLBuf);
-
-    // There may no error occur between the allocation of the cursor and
-    // the preparation of the cursor. Because internalclose (which is called in
-    // case of an exception) assumes that allocated cursors are also prepared,
-    // and thus calls unprepare.
-    // A call to unprepare while the cursor is not prepared at all can lead to
-    // unpredictable results.
-    if not assigned(FCursor) then
-      FCursor := Db.AllocateCursorHandle;
-    FCursor.FSelectable:=True; // let PrepareStatement and/or Execute alter it
-    FCursor.FStatementType:=StmType;
-    FCursor.FSchemaType := FSchemaType;
-    if ServerFiltered then
-      begin
-      If LogEvent(detPrepare) then
-        Log(detPrepare,AddFilter(FSQLBuf));
-      Db.PrepareStatement(FCursor,sqltr,AddFilter(FSQLBuf),FParams)
-      end
-    else
-      begin
-      If LogEvent(detPrepare) then
-        Log(detPrepare,FSQLBuf);
-      Db.PrepareStatement(FCursor,sqltr,FSQLBuf,FParams);
-      end;
-    FCursor.FInitFieldDef := FCursor.FSelectable;
-    end;
+  FStatement.Prepare;
+  If Assigned(Fstatement.FCursor) then
+    With FStatement.FCursor do
+      FInitFieldDef:=FSelectable;
 end;
 
 procedure TCustomSQLQuery.UnPrepare;
 
 begin
   CheckInactive;
-  if IsPrepared then with TSQLConnection(DataBase) do
-    UnPrepareStatement(FCursor);
+  If Assigned(FStatement) then
+    FStatement.Unprepare;
 end;
 
 procedure TCustomSQLQuery.FreeFldBuffers;
 begin
-  if assigned(FCursor) then TSQLConnection(Database).FreeFldBuffers(FCursor);
+  if assigned(Cursor) then
+     TSQLConnection(Database).FreeFldBuffers(Cursor);
+end;
+
+function TCustomSQLQuery.GetCheckParams: Boolean;
+begin
+  Result:=FStatement.CheckParams;
+end;
+
+function TCustomSQLQuery.GetParams: TParams;
+begin
+  Result:=FStatement.Params;
+end;
+
+function TCustomSQLQuery.GetParseSQL: Boolean;
+begin
+  Result:=FStatement.ParseSQL;
 end;
 
 function TCustomSQLQuery.GetServerIndexDefs: TServerIndexDefs;
@@ -1500,36 +1522,40 @@ begin
   Result := FServerIndexDefs;
 end;
 
+function TCustomSQLQuery.GetSQL: TStringlist;
+begin
+  Result:=TStringList(Fstatement.SQL);
+end;
+
 function TCustomSQLQuery.Fetch : boolean;
 begin
-  if not FCursor.FSelectable then
+  if Not Assigned(Cursor) then
     Exit;
-
-  if not FIsEof then FIsEOF := not TSQLConnection(Database).Fetch(FCursor);
+  if not Cursor.FSelectable then
+    Exit;
+  if not FIsEof then FIsEOF := not TSQLConnection(Database).Fetch(Cursor);
   Result := not FIsEOF;
 end;
 
+function TCustomSQLQuery.Cursor: TSQLCursor;
+begin
+  Result:=FStatement.Cursor;
+end;
+
 procedure TCustomSQLQuery.Execute;
 begin
-  If (FParams.Count>0) and Assigned(FMasterLink) then
-    FMasterLink.CopyParamsFromMaster(False);
-  If LogEvent(detExecute) then
-    Log(detExecute,FSQLBuf);
-  TSQLConnection(Database).Execute(FCursor,Transaction as TSQLTransaction, FParams);
+  FStatement.Execute;
 end;
 
 function TCustomSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
 
 begin
-  result := TSQLConnection(Database).LoadField(FCursor,FieldDef,buffer, Createblob)
+  result := TSQLConnection(Database).LoadField(Cursor,FieldDef,buffer, Createblob)
 end;
 
 function TCustomSQLQuery.RowsAffected: TRowsCount;
 begin
-  Result := -1;
-  if not Assigned(Database) then Exit;
-  //assert(Database is TSQLConnection);
-  Result := TSQLConnection(Database).RowsAffected(FCursor);
+  Result:=Fstatement.RowsAffected;
 end;
 
 procedure TCustomSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
@@ -1541,9 +1567,8 @@ procedure TCustomSQLQuery.InternalClose;
 begin
   if not IsReadFromPacket then
     begin
-    if assigned(FCursor) and FCursor.FSelectable then FreeFldBuffers;
-    // Database and FCursor could be nil, for example if the database is not assigned, and .open is called
-    if (not IsPrepared) and (assigned(database)) and (assigned(FCursor)) then TSQLConnection(database).UnPrepareStatement(FCursor);
+    if assigned(Cursor) and Cursor.FSelectable then FreeFldBuffers;
+    FStatement.Unprepare;
     end;
   if DefaultFields then
     DestroyFields;
@@ -1565,28 +1590,30 @@ begin
   try
     FieldDefs.Clear;
     if not Assigned(Database) then DatabaseError(SErrDatabasenAssigned);
-    TSQLConnection(Database).AddFieldDefs(FCursor,FieldDefs);
+    TSQLConnection(Database).AddFieldDefs(Cursor,FieldDefs);
   finally
     FLoadingFieldDefs := False;
-    if Assigned(FCursor) then FCursor.FInitFieldDef := false;
+    if Assigned(Cursor) then Cursor.FInitFieldDef := false;
   end;
 end;
 
 
 
+(*
 function TCustomSQLQuery.SQLParser(const ASQL : string) : TStatementType;
 
 Var
   I : TSQLStatementInfo;
 
 begin
-  I:=(Database as TSQLConnection).GetStatementInfo(ASQL,ParseSQL,FSchemaType);
+  I:=(Database as TSQLConnection).GetStatementInfo(ASQL,ParseSQL,SchemaType);
   FTableName:=I.TableName;
   FUpdateable:=I.Updateable;
   FWhereStartPos:=I.WhereStartPos;
   FWhereStopPos:=I.WhereStopPos;
   Result:=I.StatementType;
 end;
+*)
 
 Function TSQLConnection.GetStatementInfo(const ASQL : string; Full : Boolean; ASchema : TSchemaType) : TSQLStatementInfo;
 
@@ -1772,16 +1799,15 @@ begin
   ReadFromFile:=IsReadFromPacket;
   if ReadFromFile then
     begin
-    if not assigned(FCursor) then
-      FCursor := TSQLConnection(Database).AllocateCursorHandle;
-    FCursor.FSelectable:=True;
-    FCursor.FStatementType:=stSelect;
+    FStatement.AllocateCursor;
+    Cursor.FSelectable:=True;
+    Cursor.FStatementType:=stSelect;
     FUpdateable:=True;
     end
   else
     Prepare;
 
-  if not FCursor.FSelectable then
+  if not Cursor.FSelectable then
     DatabaseError(SErrNoSelectStatement,Self);
 
   if not ReadFromFile then
@@ -1793,12 +1819,12 @@ begin
       UpdateServerIndexDefs;
 
     Execute;
-    if not FCursor.FSelectable then
+    if not Cursor.FSelectable then
       DatabaseError(SErrNoSelectStatement,Self);
 
     // InternalInitFieldDef is only called after a prepare. i.e. not twice if
     // a dataset is opened - closed - opened.
-    if FCursor.FInitFieldDef then InternalInitFieldDefs;
+    if Cursor.FInitFieldDef then InternalInitFieldDefs;
     if DefaultFields then
       begin
       CreateFields;
@@ -1848,18 +1874,120 @@ begin
     Prepare;
     Execute;
   finally
-    // FCursor has to be assigned, or else the prepare went wrong before PrepareStatment was
+    // Cursor has to be assigned, or else the prepare went wrong before PrepareStatment was
     // called, so UnPrepareStatement shoudn't be called either
-    if (not IsPrepared) and (assigned(database)) and (assigned(FCursor)) then TSQLConnection(database).UnPrepareStatement(FCursor);
+    if (not IsPrepared) and (assigned(database)) and (assigned(Cursor)) then TSQLConnection(database).UnPrepareStatement(Cursor);
   end;
 end;
 
+Type
+
+  { TQuerySQLStatement }
+
+  TQuerySQLStatement = Class(TCustomSQLStatement)
+  protected
+    FMasterLink: TMasterParamsDataLink;
+    FQuery : TCustomSQLQuery;
+    function GetDataSource: TDatasource; override;
+    procedure SetDataSource(AValue: TDatasource); override;
+    Function GetSchemaType : TSchemaType; override;
+    Function GetSchemaObjectName : String; override;
+    Function GetSchemaPattern: String; override;
+    procedure GetStatementInfo(Var ASQL: String; Full: Boolean; ASchema: TSchemaType; out Info: TSQLStatementInfo); override;
+    procedure OnChangeSQL(Sender : TObject); override;
+  Public
+    destructor Destroy; override;
+  end;
+
+{ TQuerySQLStatement }
+
+function TQuerySQLStatement.GetDataSource: TDatasource;
+begin
+  Result:=inherited GetDataSource;
+
+end;
+
+procedure TQuerySQLStatement.SetDataSource(AValue: TDatasource);
+begin
+  inherited SetDataSource(AValue);
+  If Assigned(AValue) then
+    begin
+    AValue.FreeNotification(Self);
+    If (FMasterLink=Nil) then
+      FMasterLink:=TMasterParamsDataLink.Create(FQuery);
+    FMasterLink.Datasource:=AValue;
+    end
+  else
+    FreeAndNil(FMasterLink);
+end;
+
+function TQuerySQLStatement.GetSchemaType: TSchemaType;
+begin
+  if Assigned(FQuery) then
+    Result:=FQuery.FSchemaType
+  else
+    Result:=stNoSchema;
+end;
+
+function TQuerySQLStatement.GetSchemaObjectName: String;
+begin
+  if Assigned(FQuery) then
+    Result:=FQuery.FSchemaObjectname
+  else
+    Result:=inherited GetSchemaObjectName;
+end;
+
+function TQuerySQLStatement.GetSchemaPattern: String;
+begin
+  if Assigned(FQuery) then
+    Result:=FQuery.FSchemaPattern
+  else
+    Result:=inherited GetSchemaPattern;
+end;
+
+procedure TQuerySQLStatement.GetStatementInfo(var ASQL: String; Full: Boolean;
+  ASchema: TSchemaType; out Info: TSQLStatementInfo);
+begin
+  inherited GetStatementInfo(ASQL, Full, ASchema, Info);
+  If Assigned(FQuery) then
+    begin
+    FQuery.FWhereStartPos:=Info.WhereStartPos;
+    FQuery.FWhereStopPos:=Info.WhereStopPos;
+    FQuery.FUpdateable:=info.Updateable;
+    FQuery.FTableName:=Info.TableName;
+    if FQuery.ServerFiltered then
+      ASQL:=FQuery.AddFilter(ASQL);
+    end;
+end;
+
+procedure TQuerySQLStatement.OnChangeSQL(Sender: TObject);
+begin
+  UnPrepare;
+  inherited OnChangeSQL(Sender);
+  If CheckParams and Assigned(FMasterLink) then
+    FMasterLink.RefreshParamNames;
+end;
+
+destructor TQuerySQLStatement.Destroy;
+begin
+  FreeAndNil(FMasterLink);
+  inherited Destroy;
+end;
+
 constructor TCustomSQLQuery.Create(AOwner : TComponent);
+
+Var
+  F : TQuerySQLStatement;
+
 begin
   inherited Create(AOwner);
   FParams := TParams.create(self);
-  FSQL := TStringList.Create;
-  FSQL.OnChange := @OnChangeSQL;
+  F:=TQuerySQLStatement.Create(Self);
+  F.FQuery:=Self;
+  FStatement:=F;
+
+  //FSQL := TStringList.Create;
+  // FSQL.OnChange := @OnChangeSQL;
 
   FUpdateSQL := TStringList.Create;
   FUpdateSQL.OnChange := @OnChangeModifySQL;
@@ -1870,8 +1998,6 @@ begin
 
   FServerIndexDefs := TServerIndexDefs.Create(Self);
 
-  FParseSQL := True;
-  CheckParams:=True;
   FServerFiltered := False;
   FServerFilterText := '';
 
@@ -1889,10 +2015,9 @@ destructor TCustomSQLQuery.Destroy;
 begin
   if Active then Close;
   UnPrepare;
-  if assigned(FCursor) then TSQLConnection(Database).DeAllocateCursorHandle(FCursor);
-  FreeAndNil(FMasterLink);
+  FreeAndNil(Fstatement);
   FreeAndNil(FParams);
-  FreeAndNil(FSQL);
+//  FreeAndNil(FSQL);
   FreeAndNil(FInsertSQL);
   FreeAndNil(FDeleteSQL);
   FreeAndNil(FUpdateSQL);
@@ -1911,18 +2036,14 @@ procedure TCustomSQLQuery.SetParseSQL(AValue : Boolean);
 
 begin
   CheckInactive;
+  FStatement.ParseSQL:=AValue;
   if not AValue then
-    begin
     FServerFiltered := False;
-    FParseSQL := False;
-    end
-  else
-    FParseSQL := True;
 end;
 
 procedure TCustomSQLQuery.SetSQL(const AValue: TStringlist);
 begin
-  FSQL.Assign(AValue);
+  FStatement.SQL.Assign(AValue);
 end;
 
 procedure TCustomSQLQuery.SetUpdateSQL(const AValue: TStringlist);
@@ -2092,8 +2213,8 @@ end;
 function TCustomSQLQuery.GetCanModify: Boolean;
 
 begin
-  // the test for assigned(FCursor) is needed for the case that the dataset isn't opened
-  if assigned(FCursor) and (FCursor.FStatementType = stSelect) then
+  // the test for assigned(Cursor) is needed for the case that the dataset isn't opened
+  if assigned(Cursor) and (Cursor.FStatementType = stSelect) then
     Result:= FUpdateable and (not ReadOnly) and (not IsUniDirectional)
   else
     Result := False;
@@ -2116,7 +2237,7 @@ end;
 procedure TCustomSQLQuery.LoadBlobIntoBuffer(FieldDef: TFieldDef;
   ABlobBuf: PBufBlobField);
 begin
-  TSQLConnection(DataBase).LoadBlobIntoBuffer(FieldDef, ABlobBuf, FCursor,(Transaction as TSQLTransaction));
+  TSQLConnection(DataBase).LoadBlobIntoBuffer(FieldDef, ABlobBuf, Cursor,(Transaction as TSQLTransaction));
 end;
 
 procedure TCustomSQLQuery.BeforeRefreshOpenCursor;
@@ -2126,7 +2247,7 @@ begin
   // problems because in SetActive(false) queries are always
   // unprepared. (which is also wrong, but has to be fixed later)
   if IsPrepared then with TSQLConnection(DataBase) do
-    UnPrepareStatement(FCursor);
+    UnPrepareStatement(Cursor);
 end;
 
 function TCustomSQLQuery.LogEvent(EventType: TDBEventType): Boolean;
@@ -2152,10 +2273,15 @@ end;
 function TCustomSQLQuery.GetStatementType : TStatementType;
 
 begin
-  if assigned(FCursor) then
-    Result := FCursor.FStatementType
+  if Assigned(Cursor) then
+    Result:=Cursor.FStatementType
   else
-    Result := stUnknown;
+    Result:=stUnknown;
+end;
+
+procedure TCustomSQLQuery.SetCheckParams(AValue: Boolean);
+begin
+  FStatement.CheckParams:=Avalue;
 end;
 
 procedure TCustomSQLQuery.SetDeleteSQL(const AValue: TStringlist);
@@ -2168,6 +2294,11 @@ begin
   FInsertSQL.Assign(AValue);
 end;
 
+procedure TCustomSQLQuery.SetParams(AValue: TParams);
+begin
+  FStatement.Params.Assign(AValue);
+end;
+
 procedure TCustomSQLQuery.SetDataSource(AValue: TDatasource);
 
 Var
@@ -2181,23 +2312,15 @@ begin
       DatabaseError(SErrCircularDataSourceReferenceNotAllowed,Self);
     If Assigned(DS) then
       DS.RemoveFreeNotification(Self);
-    If Assigned(AValue) then
-      begin
-      AValue.FreeNotification(Self);
-      If (FMasterLink=Nil) then
-        FMasterLink:=TMasterParamsDataLink.Create(Self);
-      FMasterLink.Datasource:=AValue;
-      end
-    else
-      FreeAndNil(FMasterLink);
+    FStatement.Datasource:=AValue;
     end;
 end;
 
 function TCustomSQLQuery.GetDataSource: TDatasource;
 
 begin
-  If Assigned(FMasterLink) then
-    Result:=FMasterLink.DataSource
+  If Assigned(FStatement) then
+    Result:=FStatement.Datasource
   else
     Result:=Nil;
 end;