c++和MySQL -如何在我的查询中包含变量

C++ and MySQL - how to include variable in my query?

本文关键字:查询 我的 包含 变量 MySQL c++      更新时间:2023-10-16

我想我可能在这里错过了一些非常明显的东西,但我已经在这方面挣扎了太长时间,我的c++已经生锈了(10年多)

下面的代码可以正常工作,但是我需要能够将一个变量传递到查询lname。如果我在字符串或字符数组中构建查询,我会得到一个错误,它与SQLWCHAR*

参数类型不兼容

我知道下面的代码很容易受到sql注入的攻击,但这是一个孤立的系统上的一次性攻击,所以我真的在寻找简单性比什么都重要…

SQLHENV env;
SQLHDBC dbc;
SQLHSTMT  sql_hStmt;
SQLRETURN ret;
SQLWCHAR outstr[1024];
SQLSMALLINT outstrlen;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
ret = SQLDriverConnect(dbc, NULL, L"DSN=myDSN", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &sql_hStmt);
SQLWCHAR* SQL = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, SQL, SQL_NTS);
SQLFetch(sql_hStmt);

这里有两个问题,一个是构造包含所需查询的字符串,另一个是将该字符串作为参数传递给函数。

我的建议是尽可能保持"c++",直到你达到这些C的边界。所以我们应该使用std::wstring来处理字符串,直到它需要一个c风格的字符串:

std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(statementText.c_str()), SQL_NTS);

c_str()成员函数返回指向以空结束的数组(即c风格的字符串)的指针,但该指针的类型为const wchar_t*;也就是说,这个c风格字符串的内容不能被修改。

这是一个问题,因为SQLWCHAR*只是wchar_t*;它并没有承诺不去管数据。这就是为什么我包括const_cast,从c_str()值中删除const

这通常不是您想要做的事情。 const_cast可以说是最可怕的类型转换,因为您直接打开了未定义行为的大门,因为修改const对象是不可能的:

const int x = 0;
const int* p = &x; // anyone using this pointer can't modify x
int* bad = const_cast<int*>(p); // but this one is not so good
*bad = 5; // undefined behavior

这里没问题的原因是,SQLExecDirect实际上并没有修改它传递的字符串;它只是一个实现错误,没有使用const,所以把它去掉是可以的。(缺少const的错误在c语言中很常见)

如果你真的需要一个可以修改的缓冲区,那么从当前版本的c++ (c++ 11)开始,你可以安全地做到:

std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, &statementText[0], SQL_NTS);

取第一个元素的地址,它本身在一个以null结尾的数组中;另一个c风格字符串。这一次,我们有一个可修改的数组;类型已经匹配。

(我之所以注意到这在c++ 11中是可以的,是因为在技术上在以前的版本c++ 03中,这种行为没有得到保证。这实际上是有意为之,但标准中的一个措辞错误使其并非如此。从实际角度讲,无论哪种方式都可以。

你想用哪一个取决于你。有些人会认为只要一直使用&str[0],所以我们肯定没有UB,我会认为记录你的意图和信念,即函数不会修改字符串并抛弃const,但最终以const的心态进行操作。如果发生了不好的事情,你很容易就能从const中解脱出来,而不是后悔当初没戴上它。

需要注意的一件重要的事情是,所有这些返回的指针(str.c_str()&str[0])只有在str对象本身是活的并且没有被修改的情况下才有效。

const wchar_t* get_query()
{
    std::wstring result = /* build query */;
    // oops, this pointer stops being meaningful when result stops existing!
    return result.c_str();
}

有了这些,构建这些字符串就很容易了。我们有std::wstringstream:
std::wstringstream ss; 
ss << "this is basically an expanding buffer that accepts anything std::wcout will";
ss << std::endl;
ss << "this includes integers " << 5 << " and other stream-insertable types";

你可能想要这样写:

std::wstring build_query(const std::wstring& name)
{
    // you can provide a starting string
    std::wstringstream result(L"select * from DB.employees where lname=");
    result << "'" << name << "'";
    return result.str(); // this captures the buffer as a C++ string
}
// Remember, this would be bad!
//
// SQLWCHAR* SQL = const_cast<SQLWCHAR*>(build_query(L"Smith").c_str());
//
// Because the C++ string returned by build_query is temporary;
// it stops existing at the end of this full expression,
// so SQL would be a bad pointer. This is right:
std::wstring SQL = build_query(L"Smith");
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS);

希望对你有帮助。


此外,除了宏之外,我会避免使用全大写的标识符,因为阅读c++代码的人绝对希望这些名称是宏。此外,我在示例代码中使用了c++风格的强制转换;你也应该这样做。c风格的强制转换((type)value)太过强大而不安全。

我建议你准备一个参数化的查询字符串。看到

如果您每次都简单地连接字符串来构建一个新的查询,您可能会使您的站点容易受到SQL注入攻击

您可以这样做:

你可以通过

   int var = 10;
   string str = to_string(var);
   string requete="INSERT INTO stat(temps) VALUES ("";
   requete += str;
   requete += "")";
   mysql_query(&mysql,requete.c_str());

只需在mySql中指定字段的类型为int, double, float等。

感谢野兽。如果要记录两个或多个数据,可以按如下方式使用。

int state;    
string mesafe = to_string(getdata1);
string aci = to_string(getdata2);
string query = "INSERT INTO tarama (mesafe,aci) VALUES ("";
            query += mesafe;
            query += "","";
            query += aci;
            query += "")";
qstate = mysql_query(conn, query.c_str());
我好几天都没能解决这个问题。现在问题解决了