我正在使用soci 3.2.2。我正在寻找一种方法来提供多个占位符和相应的值。
std::vector<std::string> vs;
vs.push_back("key1");
vs.push_back("key2");
sql << "select * from mytable as t where t.field1 = :f1 and t.field2 = :f2", use(vs[0]), use(vs[1]);
假设我的表有很多列。例如field1,field2,...... 占位符:f1和:f2对应于filed1和field2。占位符数量动态变化。所以我创建了动态包含占位符的查询字符串。这是一个简单的字符串操作。到现在为止还挺好。但是,我找不到提供与占位符对应的多个值的方法。 use(vs [0]),use(vs [1]),...不是字符串而是C ++代码。所以我无法在运行时生成它。
我找到了一种方法来解决它,但它并不优雅。方法是放弃使用函数use()并使用字符串操作直接插入实际值,如“key1”。这不安全。我需要实现以避免SQL注入。它是通过use()函数实现的。
我正在寻找更好的方法。
感谢以下评论: https://github.com/SOCI/soci/issues/354#issuecomment-115658512 https://github.com/SOCI/soci/issues/354#issuecomment-115662758
使用“核心”界面解决了问题。
http://soci.sourceforge.net/doc/3.2/interfaces.html
以下是使用“核心”界面的代码:
session sql(sqlite3, "./test");
std::vector<std::string> qs { "v1", "v2", "v3" }; // determined on run time
int count;
// Create query string dynamically
std::stringstream ss;
ss << "select count(*) from mytable as t where t.field1 = :f1";
for (std::size_t i = 1; i < qs.size(); ++i) {
ss << " and t.field" << i+1 << " = :f" << i+1;
}
// Give the values corresponding to the placeholders in the query string
statement st(sql);
for (auto const& e : qs) {
st.exchange(use(e));
}
st.exchange(into(count));
st.alloc();
st.prepare(ss.str());
st.define_and_bind();
st.execute(true);
std::cout << count << std::endl;
std :: vector由soci库保留。我需要定义不同的类型。 MyVectorOfStrings就是这样。然后使用type_conversion类模板特化定义自定义转换。
#include <soci.h>
#include <sqlite3/soci-sqlite3.h>
#include <iostream>
using namespace soci;
struct MyVectorOfStrings : public std::vector<std::string> {
using std::vector<std::string>::vector;
};
namespace soci
{
template<>
struct type_conversion<MyVectorOfStrings>
{
typedef values base_type;
static void from_base(values const& v, indicator /* ind */, MyVectorOfStrings &p)
{}
static void to_base(const MyVectorOfStrings& p, values& v, indicator& ind) {
for (auto s : p) v << s;
ind = i_ok;
}
};
}
int main()
{
try {
session sql(sqlite3, "./test");
MyVectorOfStrings qs { "v1", "v2", "v3" }; // determined on run time
int count;
sql << "select count(*) from mytable as t where t.field1 = :f1 and t.field2 = :f2 and t.field3 = :f3", use(qs), into(count);
std::cout << count << std::endl;
}
catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << '\n';
}
}
答案 0 :(得分:0)
(As you have also asked this question on SOCI@GitHub, I copied my answer from there).
AFAIU, you want to pass vector<string>
into query for, let's call it, vertical or column-wise expansion.
AFAICT, it is not possible, vector<T>
can be used with, again, horizontal or row-wise, expansion as a row data carrier.
Typically, the protocol is that number of placeholders must match number of use
occurrences.
User-defined data and ORM is an exception, where, N placeholders match 1 use
occurrence.
You may try ORM with generated placeholders
namespace soci
{
template<>
struct type_conversion<MyVectorOfStrings>
{
typedef values base_type;
static void from_base(values const & v, indicator /* ind */, MyVectorOfStrings & p)
{ ... }
static void to_base(const MyVectorOfStrings & p, values & v, indicator & ind)
{
int i = 0;
for (auto s : p)
{
// generate placeholders from V0...Vn where n is size of p
v.set("V" + std::to_string(i);, s);
i++;
}
ind = i_ok;
}
};
}
Then try something along these lines:
MyVectorOfStrings p = ...;
std::string query = "select * from mytable as t where ";
int i = 0;
for (auto s : p)
{
if (i > 0) query += " and ";
std::string si = std::to_string(i);
query += "t.field" + si + "=:f" + si;
}
sql << query, use(p);
TBH, I have never tried to run it, so no idea if it would even work :-)
答案 1 :(得分:0)
这是简单的示例:
std::vector<std::string> ids;
soci::session s;
auto p = (s.prepare << "select id from mytable as t where false ");
for (auto & i : ids)
{
p << " or id = :v", use(i);
}
soci::statement stmt{p};
stmt.execute();