2013-03-10 47 views
3

我想我可能失去了一些东西真的很明显,但我一直在太长时间挣扎的方式就这个问题和我的C++的方式生锈(10yrs +)C++和MySQL - 如何在我的查询中包含变量?

下面的代码工作正常,但我需要能够将一个变量传递给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); 

回答

1

这里有两个问题,一个是构建包含希望查询的字符串,另一个是传递一个字符串参数到功能。

我的建议是尽可能保持“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无疑是最可怕的造型,因为你直接打开门不确定的行为,因为它是UB修改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的心态运行。如果发生了不好的事情,那么很容易就可以放松一下,而不是希望你放下它。需要注意的

一个重要的事情是,所有这些返回指针(某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)太强大了,不能安全。

+0

多德 - 这是完美的。你必须成为所有互联网上最有帮助的人! :-)在你提供的代码中,唯一没有直接显示的东西(仅供其他人参考)是,你必须#include 才能使用wstringstream。非常感谢。 – Gus 2013-03-11 00:52:02

+0

@Gus:知道我忘了一些东西,很乐意帮忙。 :) – GManNickG 2013-03-11 02:04:31

1

我建议你准备参数化查询字符串。见here

如果你简单地连接字符串每次时间来建立一个新的查询,你可能会离开你的网站打开SQL注入攻击

0

你可以简单地做它用:

你可以这样做通过

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型,双,浮法等