Browse Source

+ 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 -

joost 19 years ago
parent
commit
f36010fc31
4 changed files with 107 additions and 23 deletions
  1. 10 3
      fcl/db/db.pp
  2. 70 16
      fcl/db/dsparams.inc
  3. 10 2
      fcl/db/sqldb/mysql/mysqlconn.inc
  4. 17 2
      fcl/db/sqldb/sqldb.pp

+ 10 - 3
fcl/db/db.pp

@@ -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;

+ 70 - 16
fcl/db/dsparams.inc

@@ -153,22 +153,33 @@ 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;
 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): String;
 
 
+var pb : TParamBinding;
+    rs : string;
+
+begin
+  Result := ParseSQL(SQL,DoCreate,ParameterStyle,pb, rs);
+end;
+
+
+Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding; var ReplaceString : string): String;
+
 type
 type
   // used for ParamPart
   // used for ParamPart
   TStringPart = record
   TStringPart = record
@@ -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)
-    if ParameterStyle = psPostgreSQL then
-      if paramcount < 10 then
-        inc(NewQueryLength,paramcount)
+    // replace :ParamName by ? for interbase and by $x for postgresql/psSimulated
+    // (using ParamPart array and NewQueryLength)
+    if (ParameterStyle = psSimulated) then
+      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
-                        ParamName := IntToStr(i+1);
-                        NewQuery[NewQueryIndex]:='$';
-                        Inc(NewQueryIndex);
+        psPostgreSQL,
+        psSimulated : begin
+                        ParamName := IntToStr(ParamBinding[i]+1);
+                        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;
 
 

+ 10 - 2
fcl/db/sqldb/mysql/mysqlconn.inc

@@ -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
-    DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
+//  if assigned(AParams) and (AParams.count > 0) then
+//    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

+ 17 - 2
fcl/db/sqldb/sqldb.pp

@@ -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;