使用ADO Query参数(mysql / MyConnector)

时间:2010-09-29 22:03:11

标签: mysql delphi odbc

今天我下载并安装了MyConnector,所以我可以将Mysql与ADO一起使用,一切安装完毕,OK!,我可以与ODBC建立连接并从我的delphi环境进行连接。

当我在runetime构建我的查询时,我收到一条错误说:

  

项目Project1.exe引发异常类EOleException,消息'参数类型错误,超出可接受的范围,或者彼此冲突'。流程停止了。使用步骤或运行继续。

function TForm1.CreateSQL : TADOQuery;
begin
  result := TADOQuery.create(self);
  with Result do
  begin
    Connection     := MainConnection;
    CursorLocation := clUseServer;
    CursorType     := ctStatic;
    CacheSize      := 50;
    AutoCalcFields := true;
    ParamCheck     := true;
    Prepared       := true;
  end;
end;

procedure TForm1.login();
begin
  with CreateSQL do
  try
    with SQL do
    begin
      add('SELECT                       ');
      add('  *                          ');
      add('FROM                         ');
      add('  LisenswebUsers             ');
      add('WHERE                        ');
      add('  UserName     = :MyUsername '); // debugger exception here
      add('AND                          ');
      add('  UserPassword = :MyPassword '); // debugger exception here
      with Parameters do
      begin
        ParamByName('MyUsername').value := txtLogin.text;
        ParamByName('MyPassword').value := strmd5(txtPassword.text);
      end;
      Open;

      if Recordcount <> 1 then
      begin
        lblLoggedinAs.Text := format('Du er logget inn som: %s (%s)',[FieldByName('Username').AsString,FieldByName('UserEmailaddress').AsString]);
        MainPageControl.ActivePageIndex := 1;
      end else
      begin
        txtPassword.Text := '';
        txtPassword.SetFocus;
      end;
    end;
  finally
   free;
  end;
end;

最奇怪的是,如果我在delphi中关闭调试,这是有效的。

4 个答案:

答案 0 :(得分:9)

我会尝试在Adds周围添加SQL.BeginUpdate / SQL.EndUpdate,否则每次调用“Add”时都会解析SQL文本。

这通常是个好主意,因为ADOQuery.SQL是一个TStringList,它有一个设置CommandText的OnChange事件。 SetCommandText文本然后最终调用TADOCommand.AssignCommandText,它执行相当多的工作来解析params,并设置CommandObject.CommandText。有时驱动程序会因部分SQL语句而失败,但这些东西看起来还不错。

多年前我遇到过类似的问题 - 这就是我学习这些东西的原因!

procedure TForm1.login();
var
  Qry : TADOQuery;
begin
  Qry := CreateSQL;
  try
    Qry.SQL.BeginUpdate;

    Qry.SQL.Add('SELECT');
    Qry.SQL.Add('  *');
    Qry.SQL.Add('FROM');
    Qry.SQL.Add('  LisenswebUsers');
    Qry.SQL.Add('WHERE UserName = :MyUsername '); // debugger exception here
    Qry.SQL.Add('  AND UserPassword = :MyPassword '); // debugger exception here

    Qry.SQL.EndUpdate;
    Qry.Parameters.ParamByName('MyUsername').value := txtLogin.text;
    Qry.Parameters.ParamByName('MyPassword').value := strmd5(txtPassword.text);
    Qry.Open;

    if Qry.Recordcount <> 1 then
    begin
      lblLoggedinAs.Text := format('Du er logget inn som: %s (%s)',[FieldByName('Username').AsString,FieldByName('UserEmailaddress').AsString]);
      MainPageControl.ActivePageIndex := 1;
    end
    else
    begin
      txtPassword.Text := '';
      txtPassword.SetFocus;
    end;
  finally
    Qry.Free;
  end;
end;
顺便说一句,嵌套的with真是太丑了(让圣战开始吧)

我有时会使用with,但永远不会嵌套三个级别!如果是的话,至少要缩小SQL的范围,以便它在使用Parameters之前结束。

答案 1 :(得分:2)

尝试设置显式数据类型:

CreateSql.Parameters.ParamByName('MyUserName').DataType := ftString;

答案 2 :(得分:2)

在我的例子中,定义参数并在分配连接之前分配查询字符串纠正了问题。查询在两种情况下都成功执行,但如果在参数化查询之前分配了连接,TADOQuery组件会在内部引发(并随后吞下)OP中记录的EOleException

//LADOQuery.Connection := LADOConnection;  // Exception @ LADOQuery.Text:=...

Param := LADOQuery.Parameters.AddParameter;
Param.Name := 'rid';
Param.DataType := ftFixedChar;
Param := LADOQuery.Parameters.AddParameter;
Param.Name := 'qd';
Param.DataType := ftLongWord;

LADOQuery.SQL.Clear;
LADOQuery.SQL.Text:='SELECT Val FROM table WHERE v1=:rid AND v2=:qd';

LADOQuery.Connection := LADOConnection;  // OK!

我愿意解释为什么会出现这种情况 - 文档中没有任何内容似乎表明需要这种操作顺序。

TADOCommand.AssignCommandText此处

中的ADODB.pas中引发了异常
 try
   // Retrieve additional parameter info from the server if supported 
   Parameters.InternalRefresh; 

如果TADOQuery附加到实时连接,则仅遵循此分支。 InternalRefresh执行:

if OLEDBParameters.GetParameterInfo(ParamCount, 
                                    PDBPARAMINFO(ParamInfo), 
                                    @NamesBuffer) = S_OK then
  for I := 0 to ParamCount - 1 do
    with ParamInfo[I] do
    begin
      // When no default name, fabricate one like ADO does 
      if pwszName = nil then
      Name := 'Param' + IntToStr(I+1) else // Do not localize 
      Name := pwszName;
      // ADO maps DBTYPE_BYTES to adVarBinary 
      if wType = DBTYPE_BYTES then wType := adVarBinary;
      // ADO maps DBTYPE_STR to adVarChar 
      if wType = DBTYPE_STR then wType := adVarChar;
      // ADO maps DBTYPE_WSTR to adVarWChar 
      if wType = DBTYPE_WSTR then wType := adVarWChar;
      Direction := dwFlags and $F;
      // Verify that the Direction is initialized 
      if Direction = adParamUnknown then Direction := adParamInput;
      Parameter := Command.CommandObject.CreateParameter(Name, wType, Direction, ulParamSize, EmptyParam);
      Parameter.Precision := bPrecision;
      Parameter.NumericScale := ParamInfo[I].bScale;
      // EOleException raised here  vvvvvvvvv
      Parameter.Attributes := dwFlags and $FFFFFFF0; //Mask out Input/Output flags 
      AddParameter.FParameter := Parameter;
    end;

问题肯定是在OLE级别,可能是因为MySQL ODBC驱动程序不支持返回此信息(或返回无效信息)。设置

时会在_Parameter界面后面引发异常
 Parameter.Attributes := dwFlags and $FFFFFFF0; 

使用从dwFlags返回的似乎无效的值(DBPARAMFLAGSENUM = 320 - >设置在GetParameterInfo定义长度以上的位)。流控制的异常处理似乎是唯一的选择,因为接口在设置它们(和触发异常)之前不提供任何检查值的机制。


更新:

事实证明,有一个关于此问题的公开质询:http://qc.embarcadero.com/wc/qcmain.aspx?d=107267

答案 3 :(得分:1)

BeginUpdate / EndUpdate对不够用。在分配sql命令之前,使用AddParameter添加参数表达式。像:

var
  Qry : TADOQuery;
begin
  Qry := CreateSQL;
  try
    with Qry.Parameters.AddParameter do
    begin
      Name := 'MyUsername';
      DataType := ftString; 
    end;
    with Qry.Parameters.AddParameter do
    begin
      Name := 'MyPassword';
      DataType := ftString; 
    end;

    Qry.SQL.BeginUpdate;

    Qry.SQL.Add('SELECT');
    Qry.SQL.Add('  *');
    Qry.SQL.Add('FROM');
    Qry.SQL.Add('  LisenswebUsers');
    Qry.SQL.Add('WHERE UserName = :MyUsername '); // debugger exception here
    Qry.SQL.Add('  AND UserPassword = :MyPassword '); // debugger exception here

    Qry.SQL.EndUpdate;
...