0

我正在使用社会 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() 函数实现的。

我正在寻找更好的方法。

更新

解决方案1 ​​使用Core接口

感谢以下评论: 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;

解决方案2 定义自定义映射

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';
    }
}
4

2 回答 2

0

(正如您在SOCI@GitHub上提出的这个问题,我从那里复制了我的答案)。

AFAIU,您想传递vector<string>给查询,我们称之为垂直或按列扩展。AFAICT 不可能vector<T>再次与水平或逐行扩展一起用作行数据载体。

通常,协议是占位符的数量必须与出现的数量相use匹配。用户定义的数据和 ORM 是一个例外,其中,N 个占位符匹配 1use次出现。

您可以使用生成的占位符尝试 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,我从来没有尝试过运行它,所以不知道它是否会工作:-)

于 2015-06-26T09:34:48.453 回答
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();


于 2020-01-17T07:39:59.327 回答