- implemented parameter support for sqldb

- Added TSQLConnection.ConnOptions
- renamed TSQLQuery.FreeStatement to TSQLQuery.CloseStatement
- renamed TSQLQuery.PrepareStatement to TSQLQuery.Prepare
- added TSQLQuery.UnPrepare
This commit is contained in:
joost 2005-04-10 18:29:26 +00:00
parent 70e80b5307
commit 122a00350d

View File

@ -25,6 +25,8 @@ interface
uses SysUtils, Classes, DB; uses SysUtils, Classes, DB;
type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages); type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
TConnOption = (sqSupportParams);
TConnOptions= set of TConnOption;
type type
TSQLConnection = class; TSQLConnection = class;
@ -36,8 +38,12 @@ type
stStartTrans, stCommit, stRollback, stSelectForUpd); stStartTrans, stCommit, stRollback, stSelectForUpd);
TSQLHandle = Class(TObject) TSQLHandle = Class(TObject)
protected end;
StatementType : TStatementType;
TSQLCursor = Class(TSQLHandle)
public
FPrepared : Boolean;
FStatementType : TStatementType;
end; end;
@ -47,6 +53,7 @@ const
'create', 'get', 'put', 'execute', 'create', 'get', 'put', 'execute',
'start','commit','rollback', '?' 'start','commit','rollback', '?'
); );
SQLDelimiterCharacters = [',',' ','(',')',#13,#10,#9];
{ TSQLConnection } { TSQLConnection }
@ -62,22 +69,26 @@ type
procedure SetTransaction(Value : TSQLTransaction); procedure SetTransaction(Value : TSQLTransaction);
protected protected
FConnOptions : TConnOptions;
function StrToStatementType(s : string) : TStatementType; virtual; function StrToStatementType(s : string) : TStatementType; virtual;
procedure DoInternalConnect; override; procedure DoInternalConnect; override;
procedure DoInternalDisconnect; override; procedure DoInternalDisconnect; override;
function GetAsSQLText(Field : TField) : string; virtual; function GetAsSQLText(Field : TField) : string; virtual;
function GetHandle : pointer; virtual; abstract; function GetHandle : pointer; virtual; abstract;
Function AllocateCursorHandle : TSQLHandle; virtual; abstract; Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
Function AllocateTransactionHandle : TSQLHandle; virtual; abstract; Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
procedure FreeStatement(cursor : TSQLHandle); virtual; abstract; procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); virtual; abstract;
procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); virtual; abstract; procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); virtual; abstract;
procedure FreeFldBuffers(cursor : TSQLHandle); virtual; abstract; function Fetch(cursor : TSQLCursor) : boolean; virtual; abstract;
procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); virtual; abstract; procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); virtual; abstract;
procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); virtual; abstract; procedure CloseStatement(cursor : TSQLCursor); virtual; abstract;
function Fetch(cursor : TSQLHandle) : boolean; virtual; abstract; procedure UnPrepareStatement(cursor : TSQLCursor); virtual; abstract;
function LoadField(cursor : TSQLHandle;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
procedure FreeFldBuffers(cursor : TSQLCursor); virtual; abstract;
function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract; function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
function Commit(trans : TSQLHandle) : boolean; virtual; abstract; function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
function RollBack(trans : TSQLHandle) : boolean; virtual; abstract; function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
@ -87,9 +98,11 @@ type
procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual; procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual; function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; virtual;abstract; function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; virtual;abstract;
Procedure ObtainSQLStatementType(Cursor : TSQLCursor; SQLStr : string);
public public
property Handle: Pointer read GetHandle; property Handle: Pointer read GetHandle;
destructor Destroy; override; destructor Destroy; override;
property ConnOptions: TConnOptions read FConnOptions;
published published
property Password : string read FPassword write FPassword; property Password : string read FPassword write FPassword;
property Transaction : TSQLTransaction read FTransaction write SetTransaction; property Transaction : TSQLTransaction read FTransaction write SetTransaction;
@ -136,30 +149,31 @@ type
TSQLQuery = class (Tbufdataset) TSQLQuery = class (Tbufdataset)
private private
FCursor : TSQLHandle; FCursor : TSQLCursor;
FUpdateable : boolean; FUpdateable : boolean;
FTableName : string; FTableName : string;
FSQL : TStrings; FSQL : TStringList;
FIsEOF : boolean; FIsEOF : boolean;
FLoadingFieldDefs : boolean; FLoadingFieldDefs : boolean;
FIndexDefs : TIndexDefs; FIndexDefs : TIndexDefs;
FReadOnly : boolean; FReadOnly : boolean;
FUpdateMode : TUpdateMode; FUpdateMode : TUpdateMode;
FParams : TParams;
FusePrimaryKeyAsKey : Boolean; FusePrimaryKeyAsKey : Boolean;
// FSchemaInfo : TSchemaInfo; // FSchemaInfo : TSchemaInfo;
procedure FreeStatement; procedure CloseStatement;
procedure PrepareStatement;
procedure FreeFldBuffers; procedure FreeFldBuffers;
procedure InitUpdates(SQL : string); procedure InitUpdates(SQL : string);
function GetIndexDefs : TIndexDefs; function GetIndexDefs : TIndexDefs;
function GetStatementType : TStatementType;
procedure SetIndexDefs(AValue : TIndexDefs); procedure SetIndexDefs(AValue : TIndexDefs);
procedure SetReadOnly(AValue : Boolean); procedure SetReadOnly(AValue : Boolean);
procedure SetUsePrimaryKeyAsKey(AValue : Boolean); procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
procedure SetUpdateMode(AValue : TUpdateMode); procedure SetUpdateMode(AValue : TUpdateMode);
procedure OnChangeSQL(Sender : TObject);
procedure Execute; procedure Execute;
protected protected
// abstract & virtual methods of TBufDataset // abstract & virtual methods of TBufDataset
function Fetch : boolean; override; function Fetch : boolean; override;
@ -167,15 +181,18 @@ type
// abstract & virtual methods of TDataset // abstract & virtual methods of TDataset
procedure UpdateIndexDefs; override; procedure UpdateIndexDefs; override;
procedure SetDatabase(Value : TDatabase); override; procedure SetDatabase(Value : TDatabase); override;
Procedure SetTransaction(Value : TDBTransaction); override;
procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override; procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
procedure InternalClose; override; procedure InternalClose; override;
procedure InternalHandleException; override; procedure InternalHandleException; override;
procedure InternalInitFieldDefs; override; procedure InternalInitFieldDefs; override;
procedure InternalOpen; override; procedure InternalOpen; override;
function GetCanModify: Boolean; override; function GetCanModify: Boolean; override;
Function GetSQLStatementType(SQL : String) : TStatementType; virtual;
function ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean; override; function ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean; override;
Function IsPrepared : Boolean; virtual;
public public
procedure Prepare; virtual;
procedure UnPrepare; virtual;
procedure ExecSQL; virtual; procedure ExecSQL; virtual;
constructor Create(AOwner : TComponent); override; constructor Create(AOwner : TComponent); override;
destructor Destroy; override; destructor Destroy; override;
@ -214,16 +231,19 @@ type
property Transaction; property Transaction;
property ReadOnly : Boolean read FReadOnly write SetReadOnly; property ReadOnly : Boolean read FReadOnly write SetReadOnly;
property SQL : TStrings read FSQL write FSQL; property SQL : TStringlist read FSQL write FSQL;
property IndexDefs : TIndexDefs read GetIndexDefs; property IndexDefs : TIndexDefs read GetIndexDefs;
property Params : TParams read FParams write FParams;
property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode; property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey; property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
property Prepared : boolean read IsPrepared;
property StatementType : TStatementType read GetStatementType;
// property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema; // property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema;
end; end;
implementation implementation
uses dbconst; uses dbconst, strutils;
{ TSQLConnection } { TSQLConnection }
@ -291,6 +311,54 @@ begin
end; {case} end; {case}
end; end;
Procedure TSQLConnection.ObtainSQLStatementType(Cursor : TSQLCursor; SQLStr : string);
Var
L : Integer;
cmt : boolean;
P,PE,PP : PChar;
S : string;
begin
L := Length(SQLstr);
if L=0 then
begin
DatabaseError(SErrNoStatement);
exit;
end;
P:=Pchar(SQLstr);
PP:=P;
Cmt:=False;
While ((P-PP)<L) do
begin
if not (P^ in [' ',#13,#10,#9]) then
begin
if not Cmt then
begin
// Check for comment.
Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
if not (cmt) then
Break;
end
else
begin
// Check for end of comment.
Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
If not cmt then
Inc(p);
end;
end;
inc(P);
end;
PE:=P+1;
While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
Inc(PE);
Setlength(S,PE-P);
Move(P^,S[1],(PE-P));
Cursor.FStatementType := StrToStatementType(s);
end;
function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string;
@ -381,6 +449,43 @@ begin
end; end;
{ TSQLQuery } { TSQLQuery }
procedure TSQLQuery.OnChangeSQL(Sender : TObject);
var s : string;
i : integer;
p : pchar;
ParamName : String;
begin
UnPrepare;
if (FSQL <> nil) then
begin
if assigned(FParams) then FParams.Clear;
s := FSQL.Text;
i := posex(':',s);
while i > 0 do
begin
inc(i);
p := @s[i];
repeat
inc(p);
until (p^ in SQLDelimiterCharacters);
if not assigned(FParams) then FParams := TParams.create(self);
ParamName := copy(s,i,p-@s[i]);
if FParams.FindParam(ParamName) = nil then
FParams.CreateParam(ftUnknown, ParamName, ptInput);
i := posex(':',s,i);
end;
end
end;
Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);
begin
UnPrepare;
inherited;
end;
procedure TSQLQuery.SetDatabase(Value : TDatabase); procedure TSQLQuery.SetDatabase(Value : TDatabase);
var db : tsqlconnection; var db : tsqlconnection;
@ -388,6 +493,7 @@ var db : tsqlconnection;
begin begin
if (Database <> Value) then if (Database <> Value) then
begin begin
UnPrepare;
db := value as tsqlconnection; db := value as tsqlconnection;
inherited setdatabase(value); inherited setdatabase(value);
if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
@ -395,49 +501,56 @@ begin
end; end;
end; end;
procedure TSQLQuery.FreeStatement; procedure TSQLQuery.CloseStatement;
begin begin
if assigned(FCursor) then if assigned(FCursor) then
begin (Database as tsqlconnection).CloseStatement(FCursor);
(Database as tsqlconnection).FreeStatement(FCursor);
// FreeAndNil(FCursor);
end;
end; end;
procedure TSQLQuery.PrepareStatement; Function TSQLQuery.IsPrepared : Boolean;
begin
Result := Assigned(FCursor) and FCursor.FPrepared;
end;
procedure TSQLQuery.Prepare;
var var
Buf : string; Buf : string;
x : integer;
db : tsqlconnection; db : tsqlconnection;
sqltr : tsqltransaction; sqltr : tsqltransaction;
begin begin
db := (Database as tsqlconnection); if not IsPrepared then
if not assigned(Db) then
DatabaseError(SErrDatabasenAssigned);
if not Db.Connected then
db.Open;
if not assigned(Transaction) then
DatabaseError(SErrTransactionnSet);
sqltr := (transaction as tsqltransaction);
if not sqltr.Active then sqltr.StartTransaction;
if assigned(fcursor) then FreeAndNil(fcursor);
FCursor := Db.AllocateCursorHandle;
Buf := '';
for x := 0 to FSQL.Count - 1 do
Buf := Buf + FSQL[x] + ' '#10; // multiline SQl. Provides line info in sqlErrors and allows single line comments
if Buf='' then
begin begin
DatabaseError(SErrNoStatement);
exit; db := (Database as tsqlconnection);
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 assigned(fcursor) then FreeAndNil(fcursor);
FCursor := Db.AllocateCursorHandle;
buf := TrimRight(FSQL.Text);
Db.PrepareStatement(Fcursor,sqltr,buf,FParams);
if (FCursor.FStatementType = stSelect) and not ReadOnly then
InitUpdates(Buf);
end; end;
FCursor.StatementType := GetSQLStatementType(buf); end;
if (FCursor.StatementType = stSelect) and not ReadOnly then InitUpdates(Buf);
Db.PrepareStatement(Fcursor,sqltr,buf); procedure TSQLQuery.UnPrepare;
begin
CheckInactive;
if IsPrepared then (Database as tsqlconnection).UnPrepareStatement(FCursor);
FreeAndNil(FCursor);
end; end;
procedure TSQLQuery.FreeFldBuffers; procedure TSQLQuery.FreeFldBuffers;
@ -447,7 +560,7 @@ end;
function TSQLQuery.Fetch : boolean; function TSQLQuery.Fetch : boolean;
begin begin
if not (Fcursor.StatementType in [stSelect]) then if not (Fcursor.FStatementType in [stSelect]) then
Exit; Exit;
if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor); if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
@ -456,7 +569,7 @@ end;
procedure TSQLQuery.Execute; procedure TSQLQuery.Execute;
begin begin
(Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction); (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction, FParams);
end; end;
function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
@ -473,7 +586,7 @@ end;
procedure TSQLQuery.InternalClose; procedure TSQLQuery.InternalClose;
begin begin
FreeFldBuffers; FreeFldBuffers;
FreeStatement; CloseStatement;
if DefaultFields then if DefaultFields then
DestroyFields; DestroyFields;
FIsEOF := False; FIsEOF := False;
@ -523,7 +636,7 @@ Var
if Cmt then if Cmt then
begin begin
end end
else if (p^ in [',',' ','(',')',#13,#10,#9]) then else if (p^ in SQLDelimiterCharacters) then
begin begin
if stm then break; if stm then break;
end end
@ -597,16 +710,18 @@ end;
procedure TSQLQuery.InternalOpen; procedure TSQLQuery.InternalOpen;
var tel : integer; var tel : integer;
f : TField; f : TField;
s : string; s : string;
WasPrepared : boolean;
begin begin
try try
PrepareStatement; WasPrepared := IsPrepared;
if Fcursor.StatementType in [stSelect] then Prepare;
if FCursor.FStatementType in [stSelect] then
begin begin
Execute; Execute;
InternalInitFieldDefs; if not WasPrepared then InternalInitFieldDefs; // if query was prepared before opening, fields are already created
if DefaultFields then if DefaultFields then
begin begin
CreateFields; CreateFields;
@ -642,10 +757,10 @@ end;
procedure TSQLQuery.ExecSQL; procedure TSQLQuery.ExecSQL;
begin begin
try try
PrepareStatement; Prepare;
Execute; Execute;
finally finally
FreeStatement; CloseStatement;
end; end;
end; end;
@ -653,6 +768,7 @@ constructor TSQLQuery.Create(AOwner : TComponent);
begin begin
inherited Create(AOwner); inherited Create(AOwner);
FSQL := TStringList.Create; FSQL := TStringList.Create;
FSQL.OnChange := @OnChangeSQL;
FIndexDefs := TIndexDefs.Create(Self); FIndexDefs := TIndexDefs.Create(Self);
FReadOnly := false; FReadOnly := false;
// Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
@ -665,56 +781,11 @@ end;
destructor TSQLQuery.Destroy; destructor TSQLQuery.Destroy;
begin begin
if Active then Close; if Active then Close;
// if assigned(FCursor) then FCursor.destroy; UnPrepare;
FreeAndNil(FSQL); FreeAndNil(FSQL);
inherited Destroy; inherited Destroy;
end; end;
Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
Var
L : Integer;
cmt : boolean;
P,PE,PP : PChar;
S : string;
begin
Result:=stNone;
L:=Length(SQL);
If (L=0) then
Exit;
P:=Pchar(SQL);
PP:=P;
Cmt:=False;
While ((P-PP)<L) do
begin
if not (P^ in [' ',#13,#10,#9]) then
begin
if not Cmt then
begin
// Check for comment.
Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
if not (cmt) then
Break;
end
else
begin
// Check for end of comment.
Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
If not cmt then
Inc(p);
end;
end;
inc(P);
end;
PE:=P+1;
While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
Inc(PE);
Setlength(S,PE-P);
Move(P^,S[1],(PE-P));
result := (DataBase as TSQLConnection).StrToStatementType(s);
end;
procedure TSQLQuery.SetReadOnly(AValue : Boolean); procedure TSQLQuery.SetReadOnly(AValue : Boolean);
begin begin
@ -854,7 +925,7 @@ end;
Function TSQLQuery.GetCanModify: Boolean; Function TSQLQuery.GetCanModify: Boolean;
begin begin
if FCursor.StatementType = stSelect then if FCursor.FStatementType = stSelect then
Result:= Active and FUpdateable and (not FReadOnly) Result:= Active and FUpdateable and (not FReadOnly)
else else
Result := False; Result := False;
@ -885,17 +956,30 @@ begin
SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern)); SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
end; end;
function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin begin
result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode); result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode);
end; end;
function TSQLQuery.GetStatementType : TStatementType;
begin
if assigned(FCursor) then Result := FCursor.FStatementType
else Result := stNone;
end;
end. end.
{ {
$Log$ $Log$
Revision 1.15 2005-03-23 08:17:51 michael Revision 1.16 2005-04-10 18:29:26 joost
- implemented parameter support for sqldb
- Added TSQLConnection.ConnOptions
- renamed TSQLQuery.FreeStatement to TSQLQuery.CloseStatement
- renamed TSQLQuery.PrepareStatement to TSQLQuery.Prepare
- added TSQLQuery.UnPrepare
Revision 1.15 2005/03/23 08:17:51 michael
+ Several patches from Jose A. Rimon + Several patches from Jose A. Rimon
# Prevents "field not found" error, when use a query without the primary key # Prevents "field not found" error, when use a query without the primary key
Set SQLlen of different data types Set SQLlen of different data types