+ Added simulation of parameters for MySQL

+ Fixed problems with EDatabaseError 
 + Fixed a problem with parameters that are used more then once

git-svn-id: trunk@3772 -
This commit is contained in:
joost 2006-06-03 12:13:09 +00:00
parent 06be3aba7d
commit f36010fc31
4 changed files with 107 additions and 23 deletions

View File

@ -1576,7 +1576,7 @@ type
TParamType = (ptUnknown, ptInput, ptOutput, ptInputOutput, ptResult); TParamType = (ptUnknown, ptInput, ptOutput, ptInputOutput, ptResult);
TParamTypes = set of TParamType; TParamTypes = set of TParamType;
TParamStyle = (psInterbase,psPostgreSQL); TParamStyle = (psInterbase,psPostgreSQL,psSimulated);
TParams = class; TParams = class;
@ -1694,6 +1694,7 @@ type
Function ParseSQL(SQL: String; DoCreate: Boolean): String; Function ParseSQL(SQL: String; DoCreate: Boolean): String;
Function ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String; overload; Function ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String; overload;
Function ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String; overload; Function ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String; overload;
Function ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding; var ReplaceString : string): String;
Procedure RemoveParam(Value: TParam); Procedure RemoveParam(Value: TParam);
Procedure CopyParamValuesFromDataset(ADataset : TDataset; CopyBound : Boolean); Procedure CopyParamValuesFromDataset(ADataset : TDataset; CopyBound : Boolean);
Property Dataset : TDataset Read GetDataset; Property Dataset : TDataset Read GetDataset;
@ -1868,7 +1869,10 @@ end;
Procedure DatabaseError (Const Msg : String; Comp : TComponent); Procedure DatabaseError (Const Msg : String; Comp : TComponent);
begin begin
Raise EDatabaseError.CreateFmt('%s : %s',[Comp.Name,Msg]); if assigned(Comp) then
Raise EDatabaseError.CreateFmt('%s : %s',[Comp.Name,Msg])
else
DatabaseError(Msg);
end; end;
Procedure DatabaseErrorFmt (Const Fmt : String; Args : Array Of Const); Procedure DatabaseErrorFmt (Const Fmt : String; Args : Array Of Const);
@ -1880,7 +1884,10 @@ end;
Procedure DatabaseErrorFmt (Const Fmt : String; Args : Array Of const; Procedure DatabaseErrorFmt (Const Fmt : String; Args : Array Of const;
Comp : TComponent); Comp : TComponent);
begin begin
Raise EDatabaseError.CreateFmt(Format('%s : %s',[Comp.Name,Fmt]),Args); if assigned(comp) then
Raise EDatabaseError.CreateFmt(Format('%s : %s',[Comp.Name,Fmt]),Args)
else
DatabaseErrorFmt(Fmt, Args);
end; end;
Function ExtractFieldName(Const Fields: String; var Pos: Integer): String; Function ExtractFieldName(Const Fields: String; var Pos: Integer): String;

View File

@ -153,21 +153,32 @@ end;
Function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String; Function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String;
var pb : TParamBinding; var pb : TParamBinding;
rs : string;
begin begin
Result := ParseSQL(SQL,DoCreate,psInterbase, pb); Result := ParseSQL(SQL,DoCreate,psInterbase, pb, rs);
end; end;
Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String; Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String;
var pb : TParamBinding; var pb : TParamBinding;
rs : string;
begin begin
Result := ParseSQL(SQL,DoCreate,ParameterStyle,pb); Result := ParseSQL(SQL,DoCreate,ParameterStyle,pb, rs);
end;
Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String;
var pb : TParamBinding;
rs : string;
begin
Result := ParseSQL(SQL,DoCreate,ParameterStyle,pb, rs);
end; end;
Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String; Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding; var ReplaceString : string): String;
type type
// used for ParamPart // used for ParamPart
@ -189,12 +200,16 @@ var
ParamPart:array of TStringPart; // describe which parts of buf are parameters ParamPart:array of TStringPart; // describe which parts of buf are parameters
NewQueryLength:integer; NewQueryLength:integer;
NewQuery:string; NewQuery:string;
NewQueryIndex,BufIndex,CopyLen,i:integer; // Parambinding will have length ParamCount in the end NewQueryIndex,BufIndex,CopyLen,i:integer; // Parambinding will have length ParamCount in the end
SimStrCount,b:integer; // in psSimulated mode this is the counter with the amount of repeating '$' signs
tmpParam:TParam;
begin begin
if DoCreate then Clear; if DoCreate then Clear;
// Parse the SQL and build ParamBinding // Parse the SQL and build ParamBinding
ParamCount:=0; ParamCount:=0;
SimStrCount := 1;
ReplaceString := '';
NewQueryLength:=Length(SQL); NewQueryLength:=Length(SQL);
SetLength(ParamPart,ParamAllocStepSize); SetLength(ParamPart,ParamAllocStepSize);
SetLength(Parambinding,ParamAllocStepSize); SetLength(Parambinding,ParamAllocStepSize);
@ -286,9 +301,16 @@ begin
SetLength(ParamBinding,NewLength); SetLength(ParamBinding,NewLength);
end; end;
// create Parameter and assign ParameterIndex
if DoCreate then if DoCreate then
ParameterIndex := CreateParam(ftUnknown, ParamName, ptInput).Index begin
// Check if this is the first occurance of the parameter
tmpParam := FindParam(ParamName);
// If so, create the parameter and assign the Parameterindex
if not assigned(tmpParam) then
ParameterIndex := CreateParam(ftUnknown, ParamName, ptInput).Index
else // else only assign the ParameterIndex
ParameterIndex := tmpParam.Index;
end
// else find ParameterIndex // else find ParameterIndex
else else
begin begin
@ -300,6 +322,13 @@ begin
Inc(QuestionMarkParamCount); Inc(QuestionMarkParamCount);
end; end;
end; end;
if ParameterStyle in [psPostgreSQL,psSimulated] then
begin
if ParameterIndex > 8 then
inc(NewQueryLength,2)
else
inc(NewQueryLength,1)
end;
// store ParameterIndex in FParamIndex, ParamPart data // store ParameterIndex in FParamIndex, ParamPart data
ParamBinding[ParamCount-1]:=ParameterIndex; ParamBinding[ParamCount-1]:=ParameterIndex;
@ -310,6 +339,20 @@ begin
Dec(NewQueryLength,p-ParamNameStart); Dec(NewQueryLength,p-ParamNameStart);
end; end;
end; end;
'$':
if ParameterStyle = psSimulated then
begin
b := 1;
while p^='$' do
begin
inc(p);
inc(b);
end;
if b > SimStrCount then SimStrCount := b;
end
else
Inc(p);
#0:Break; #0:Break;
else else
Inc(p); Inc(p);
@ -321,12 +364,18 @@ begin
if ParamCount>0 then if ParamCount>0 then
begin begin
// replace :ParamName by ? (using ParamPart array and NewQueryLength) // replace :ParamName by ? for interbase and by $x for postgresql/psSimulated
if ParameterStyle = psPostgreSQL then // (using ParamPart array and NewQueryLength)
if paramcount < 10 then if (ParameterStyle = psSimulated) then
inc(NewQueryLength,paramcount) begin
if (SimStrCount > 1) then
begin
inc(NewQueryLength,(paramcount)*(SimStrCount-1));
for b := 1 to SimStrCount do ReplaceString := ReplaceString+'$';
end
else else
inc(NewQueryLength,(paramcount-9)*2+9); ReplaceString := '$';
end;
SetLength(NewQuery,NewQueryLength); SetLength(NewQuery,NewQueryLength);
NewQueryIndex:=1; NewQueryIndex:=1;
@ -338,12 +387,16 @@ begin
Inc(NewQueryIndex,CopyLen); Inc(NewQueryIndex,CopyLen);
case ParameterStyle of case ParameterStyle of
psInterbase : NewQuery[NewQueryIndex]:='?'; psInterbase : NewQuery[NewQueryIndex]:='?';
psPostgreSQL: begin psPostgreSQL,
ParamName := IntToStr(i+1); psSimulated : begin
NewQuery[NewQueryIndex]:='$'; ParamName := IntToStr(ParamBinding[i]+1);
Inc(NewQueryIndex); for b := 1 to SimStrCount do
begin
NewQuery[NewQueryIndex]:='$';
Inc(NewQueryIndex);
end;
NewQuery[NewQueryIndex]:= paramname[1]; NewQuery[NewQueryIndex]:= paramname[1];
if i>10 then if length(paramname)>1 then
begin begin
Inc(NewQueryIndex); Inc(NewQueryIndex);
NewQuery[NewQueryIndex]:= paramname[2] NewQuery[NewQueryIndex]:= paramname[2]
@ -358,6 +411,7 @@ begin
end end
else else
NewQuery:=SQL; NewQuery:=SQL;
Result := NewQuery; Result := NewQuery;
end; end;

View File

@ -46,6 +46,8 @@ Type
Row : MYSQL_ROW; Row : MYSQL_ROW;
RowsAffected : QWord; RowsAffected : QWord;
LastInsertID : QWord; LastInsertID : QWord;
ParamBinding : TParamBinding;
ParamReplaceString : String;
end; end;
TConnectionName = class (TSQLConnection) TConnectionName = class (TSQLConnection)
@ -258,11 +260,13 @@ end;
procedure TConnectionName.PrepareStatement(cursor: TSQLCursor; procedure TConnectionName.PrepareStatement(cursor: TSQLCursor;
ATransaction: TSQLTransaction; buf: string;AParams : TParams); ATransaction: TSQLTransaction; buf: string;AParams : TParams);
begin begin
if assigned(AParams) and (AParams.count > 0) then // if assigned(AParams) and (AParams.count > 0) then
DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self); // DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
With Cursor as TCursorName do With Cursor as TCursorName do
begin begin
FStatement:=Buf; FStatement:=Buf;
if assigned(AParams) and (AParams.count > 0) then
FStatement := AParams.ParseSQL(FStatement,false,psSimulated,paramBinding,ParamReplaceString);
if FStatementType=stSelect then if FStatementType=stSelect then
FNeedData:=True; FNeedData:=True;
ConnectMySQL(FQMySQL,FMySQL^.host,FMySQL^.user,FMySQL^.passwd); ConnectMySQL(FQMySQL,FMySQL^.host,FMySQL^.user,FMySQL^.passwd);
@ -306,11 +310,15 @@ procedure TConnectionName.Execute(cursor: TSQLCursor;
Var Var
C : TCursorName; C : TCursorName;
i : integer;
begin begin
C:=Cursor as TCursorName; C:=Cursor as TCursorName;
If (C.FRes=Nil) then If (C.FRes=Nil) then
begin begin
if Assigned(AParams) and (AParams.count > 0) then
for i := 0 to AParams.count -1 do
C.FStatement := stringreplace(C.FStatement,C.ParamReplaceString+inttostr(AParams[i].Index+1),GetAsSQLText(AParams[i]),[rfReplaceAll,rfIgnoreCase]);
if mysql_query(c.FQMySQL,Pchar(C.FStatement))<>0 then if mysql_query(c.FQMySQL,Pchar(C.FStatement))<>0 then
MySQLError(c.FQMYSQL,Format(SErrExecuting,[StrPas(mysql_error(c.FQMySQL))]),Self) MySQLError(c.FQMYSQL,Format(SErrExecuting,[StrPas(mysql_error(c.FQMySQL))]),Self)
else else

View File

@ -76,7 +76,8 @@ type
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; overload; virtual;
function GetAsSQLText(Param : TParam) : string; overload; virtual;
function GetHandle : pointer; virtual; virtual; function GetHandle : pointer; virtual; virtual;
Function AllocateCursorHandle : TSQLCursor; virtual; abstract; Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
@ -425,7 +426,7 @@ end;
function TSQLConnection.GetAsSQLText(Field : TField) : string; function TSQLConnection.GetAsSQLText(Field : TField) : string;
begin begin
if not assigned(field) then Result := 'Null' if (not assigned(field)) or field.IsNull then Result := 'Null'
else case field.DataType of else case field.DataType of
ftString : Result := '''' + field.asstring + ''''; ftString : Result := '''' + field.asstring + '''';
ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Field.AsDateTime) + ''''; ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Field.AsDateTime) + '''';
@ -435,6 +436,20 @@ begin
end; {case} end; {case}
end; end;
function TSQLConnection.GetAsSQLText(Param: TParam) : string;
begin
if (not assigned(param)) or param.IsNull then Result := 'Null'
else case param.DataType of
ftString : Result := '''' + param.asstring + '''';
ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Param.AsDateTime) + '''';
ftDateTime : Result := '''' + FormatDateTime('yyyy-mm-dd hh:mm:ss',Param.AsDateTime) + ''''
else
Result := Param.asstring;
end; {case}
end;
function TSQLConnection.GetHandle: pointer; function TSQLConnection.GetHandle: pointer;
begin begin
Result := nil; Result := nil;