如何在社交中提供与占位符对应的值?

时间:2015-06-24 09:45:31

标签: c++ sql 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]);

假设我的表有很多列。例如field1,field2,...... 占位符:f1和:f2对应于filed1和field2。占位符数量动态变化。所以我创建了动态包含占位符的查询字符串。这是一个简单的字符串操作。到现在为止还挺好。但是,我找不到提供与占位符对应的多个值的方法。 use(vs [0]),use(vs [1]),...不是字符串而是C ++代码。所以我无法在运行时生成它。

我找到了一种方法来解决它,但它并不优雅。方法是放弃使用函数use()并使用字符串操作直接插入实际值,如“key1”。这不安全。我需要实现以避免SQL注入。它是通过use()函数实现的。

我正在寻找更好的方法。

更新

Solution1使用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;

Solution2定义自定义映射

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

2 个答案:

答案 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();