2013-03-10 60 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型,雙,浮法等