soci中如何提供占位符对应的值?
How to provide the values corresponding to the placeholders in soci?
我正在使用 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]);
假设我的 table 有很多列。例如 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
问题已使用 'Core' 界面解决。
http://soci.sourceforge.net/doc/3.2/interfaces.html
这是使用 'Core' 接口的代码:
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;
解决方案 2 定义自定义映射
std::vector 由 soci 库保留。我需要定义不同的类型。 MyVectorOfStrings 就是这样。然后使用 type_conversion class 模板专业化定义自定义转换。
#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';
}
}
(因为你也在 SOCI@GitHub 上问过这个问题,我从那里复制了我的答案)。
AFAIU,您想将 vector<string>
传递到查询中,让我们称之为垂直或按列扩展。
AFAICT,这是不可能的,vector<T>
可以再次水平或按行扩展用作行数据载体。
通常,协议是占位符的数量必须匹配 use
出现的次数。
用户自定义数据和ORM是一个例外,其中,N个占位符匹配1 use
次出现。
您可以尝试使用生成的占位符进行 ORM
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;
}
};
}
然后尝试以下方法:
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,我从未尝试过 运行,所以不知道它是否有效:-)
这是一个简单的例子:
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();
我正在使用 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]);
假设我的 table 有很多列。例如 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
问题已使用 'Core' 界面解决。
http://soci.sourceforge.net/doc/3.2/interfaces.html
这是使用 'Core' 接口的代码:
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;
解决方案 2 定义自定义映射
std::vector 由 soci 库保留。我需要定义不同的类型。 MyVectorOfStrings 就是这样。然后使用 type_conversion class 模板专业化定义自定义转换。
#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';
}
}
(因为你也在 SOCI@GitHub 上问过这个问题,我从那里复制了我的答案)。
AFAIU,您想将 vector<string>
传递到查询中,让我们称之为垂直或按列扩展。
AFAICT,这是不可能的,vector<T>
可以再次水平或按行扩展用作行数据载体。
通常,协议是占位符的数量必须匹配 use
出现的次数。
用户自定义数据和ORM是一个例外,其中,N个占位符匹配1 use
次出现。
您可以尝试使用生成的占位符进行 ORM
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;
}
};
}
然后尝试以下方法:
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,我从未尝试过 运行,所以不知道它是否有效:-)
这是一个简单的例子:
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();