草庐IT

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

coder 2023-10-23 原文

我想我可能在这里遗漏了一些非常明显的东西,但我在这方面苦苦挣扎的时间太长了,而且我的 C++ 已经生锈了(10 年以上)

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

我知道下面的代码容易受到 sql 注入(inject)的攻击,但这是对一个孤立系统的一次性攻击,所以我真正寻求的是简单性而不是其他任何东西......

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*;它不 promise 不理会数据。这就是为什么我包含 const_cast,以从 c_str() 值中删除 const

这不是你通常想要做的事情。 const_cast 可以说是最可怕的转换,因为你直接打开了未定义行为的大门,因为它是 UB 来修改一个常量对象:

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) 太强大了,不安全。

关于C++ 和 MySQL - 如何在我的查询中包含变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15328745/

有关C++ 和 MySQL - 如何在我的查询中包含变量?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  10. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

随机推荐