简体   繁体   中英

Best stl container for sql parameters

I have to build a dynamic sql query. To proper execute it I have to do it in 3 steps:

  1. Prepare statement
  2. Bind Parameters with functions: bindString(string value, int index); bindInt(int value, int index);
  3. Execute it

Because of the fact, that this query is build dynamically I have to store somewhere proper values for given index.

For example:

  1. SELECT * FROM Table WHERE A = ? AND E = '?';

  2. SELECT * FROM Table WHERE A = ? AND B = ? AND E = '?';

During building query I have to store somewhere that:

In the first case:

  • index 0 is for int A,
  • index 1 is for string E

In the second case:

  • index 0 is for int A
  • index 1 is for int B
  • index 2 is for string E

My best idea is to create two maps: < int, string >, < int, int > and during creating query set in first place indexes and in second place values and then creating two loops, one for strings, the second one for integers and binding parameters in them and it works fine.

However I wonder if is it possible to do everything in one loop using succeeding indexes and in type safety way.

Thank You.

I would consider creating a class to wrap SQL parameters.

In fact I would create an abstract class like that :

SQLParameterBase
{

   std::string toString() = 0;
   void print()
   {
     std::cout << toString();
   }
}

And then a template class :

template<class ParamType>
SQLParameter : public SQLParameterBase
{
private:
    ParamType value;
public:
    std::string toString()
    {
       // You can use std::ostringstream to convert to string,
       // or create another class (derivated from SQLParameterBase) with very specific values
    }
}

And you could use it like that :

SQLParameterBase * params[10];
maps[0] = new SQLParameter<int>();

Hope that will help

Actually it is modified AMDG solution. Thanks to him!

class SQLParam {
public:
   virtual ~SqlParam(){}
   void bind(DatabaseHandler &db, int index) = 0;
};

class SQLParamInt {
private:
   int value;
public:
   SqlParamInt(int p_value) : value(p_value) {
   }
   ~SqlParamInt() {}
   int bind(DatabaseHandler &db, int index) {
       return db.bindInt(value, index);
   }
};

class SQLParamString {
private:
   string value;
public:
   SqlParamString(std::string p_value) : value(p_value) {
   }
   ~SqlParamString() {}
   int bind(DatabaseHandler &db, int index) {
       return db.bindString(value, index);
   }
};

typedef std::vector<std::unique_ptr<SqlParam>> SqlParamsContainer;
typedef std::unique_ptr<SqlParamInt> SqlParamIntPtr;
typedef std::unique_ptr<SqlParamString> SqlParamStringPtr;

In my function, building query:

int buildQuery(RequestHandler &request) {
    SqlParamsContainer params;
    stringstream query << "SELECT * FROM Table WHERE A = ?";

    params.push_back(SqlParamIntPtr(new SqlParamInt(request.A())));
    if(request.has_B()) {
         params.push_back(SqlParamIntPtr(new SqlParamInt(request.B())));
         query << " AND B = ?";
    }
    if(request.has_C()) {
         params.push_back(SqlParamStringPtr(new SqlParamString(request.C())));
         query << " AND C = ?";
    }
    query << ";";
    db.prepare(query.str());

    for(int i = 0; i < v_container.size(); i++)
        v_container.at(i)->bind(db,i);
}

There is Boost::Any while it is more general than what you ask for and does not prevent the user from storing unsupported types you do not need to worry about creating the according subclasses.

If you want to return results as well from your DB Boost::Any might be the answer as well.

I suggest limiting the types in your bind function rather than in the storage. If you work with a variadic bind function this is necessary anyways.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM