Best stl container for sql parameters - c++

I have to build a dynamic sql query. To proper execute it I have to do it in 3 steps:
Prepare statement
Bind Parameters with functions: bindString(string value, int index); bindInt(int value, int index);
Execute it
Because of the fact, that this query is build dynamically I have to store somewhere proper values for given index.
For example:
SELECT * FROM Table WHERE A = ? AND E = '?';
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.

Related

(C++) Cannot use string::substr to assign substrings to objects

I am designing a cheat for the game WordBrain. Basically this is just a small program that takes a number of letters, permutes the sequence, then splits the sequence into 'words' which have length attribute, then search my text file to file meaningful permutation to print out.
while (next_permutation(letters.begin(), letters.end())) //loop through possible permutations of the combination
{
int position_marker = 0; //serve specific purpose
for (auto x : substring_collection) //trouble is in this loop
{
string temp;
int k = x.take_length();
try { temp = letters.substr(position_marker, k); }
catch (out_of_range) { cout << "OUT OF RANGE"; }
x.set(temp); //member content does not register change
position_marker += x.take_length(); //also member word_length is 0 now, despite their are no invocation of methods capable of changing it
}
if (all_of(substring_collection.begin(), substring_collection.end(), [&](substring & s) {return !(library.find(s.take_content()) == library.end()); }))
{
for (auto x : substring_collection)
{
cout << x.take_content() + " ";
}
}
}
This is the location the trouble stems from. Basically, substring_collection is a vector<substring>, which contains objects of class substring
Here is how the class looks like:
class substring
{
private:
std::string content;
int word_length;
public:
substring() : content(""), word_length(0) {};
substring(std::string & s, int c) : content(s), word_length(c) {};
void set(std::string & s)
{
content = s;
}
void clear()
{
content.clear();
}
void set_length(int c)
{
word_length = c;
}
void showinfo() const
{
std::cout << "Content is " + content << " Length is : " << word_length;
}
int take_length() const
{
return word_length;
}
std::string take_content() const
{
return content;
}
};
I suspect that the reason the code goes wrong is position_marker, whose value depends on the member 'word_length' of the object substring is set to 0.
In the code prior to this loop, I only the setting method for this member to take data from users' input (from std::cin).
Can you please tell me that is there any hidden mechanism that reset the property, or create brand new objects that I did not aware of?
Also, teachings on coding styles are very welcomed. I just started learning to code so any tips are much appreciated.
for (auto x : substring_collection)
Here, x is of type substring. This is a copy of the element in the vector, and then when you modify it, you only modify the copy, not the original.
You'll have to use a reference to modify the original element in the vector
for (auto& x : substring_collection)
For why word_length is 0, I don't know, it isn't in the code you posted. My guess would be that you resized the vector, which called the default constructor of substring, which set word_length to 0.

How to provide the values corresponding to the placeholders in soci?

I'm using soci 3.2.2. I'm looking for a way to provide multiple placeholders and corresponding values.
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]);
Let's say my table has many columns. For example field1, field2, ...
The placeholders :f1 and :f2 is corresponding to filed1 and field2. The number of placeholders changes dynamically. So I create query string that contains placeholders dynamically. It is a simple string manipulation. So far, so good. However, I couldn't find a way to provide multiple values that is corresponding to placeholders. use(vs[0]), use(vs[1]), ... are not string but C++ code. So I can't generate it on run time.
I found a way to solve it but it's not elegant. The way is that giving up to use the function use() and insert the actual value such as "key1" directly using string manipulation. It's not safe. I need to implement to avoid SQL injection. It is achieved by use() function.
I'm looking for a better way.
Updated
Solution1 use Core interface
Thanks to the following comments:
https://github.com/SOCI/soci/issues/354#issuecomment-115658512
https://github.com/SOCI/soci/issues/354#issuecomment-115662758
the problem has been solved using 'Core' interface.
http://soci.sourceforge.net/doc/3.2/interfaces.html
Here is the code using 'Core' interface:
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 define custom mapping
std::vector is reserved by the soci library. I need to define teh different type. MyVectorOfStrings is that. Then define the custom conversion using type_conversion class template specialization.
#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';
}
}
(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 :-)
this is simple example:
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();

C++ Structure with unknown data types

My program reads user's input and create easy "table". User on the start specifies data types of columns and the number of rows.
User's input:
create table
add attribute string Name
add attribute int Age
rows 3
I need prepare a structure from user's input now. I have something like this:
CTable
{
unsigned attributesCnt;
string * attributesNames;
void ** attributes;
};
So, from the user's input, the progam does these steps:
CTable myTable;
myTable.attributesCnt = 2; // string "Name", int "Age"
myTable.attributesNames = new string[2];
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
attributes = new void[2]; // 2 attributes
attributes[0] = (void*) new string[3]; // there will be 3 rows
attributes[1] = (void*) new int[3];
I need remember that "attributes[0]" is string and "attributes[1]" is int too.
Is this "right" way?
I would like use only standard libraries.
What you are looking for is a tagged union also called a variant. It allows you to store multiple data types at the same location just like a regular union but includes an additional but separate data member that indicates it's type. The C++ Standard Library does not include variants but they are easy enough to implement.
Once you have a variant you can apply it to your example like below.
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
// I recommend using std::vector here instead of using new/delete yourself
attributes = new Variant*[2]; // 2 attributes
attributes[0] = new Variant("player name");
attributes[1] = new Variant(player_age);
The following example shows how the variant might be implemented.
struct Variant
{
enum Type
{
INT,
STRINGPTR
};
Type type_;
union
{
int int_;
const char* stringptr_;
} data_;
explicit Variant(int data) : type_(INT)
{
data_.int_ = data;
}
explicit Variant(const char *data) : type_(STRINGPTR)
{
data_.stringptr_ = data;
}
Type getType() const { return type_; }
int getIntValue() const
{
if(type_ != INT)
throw std::runtime_error("Variant is not an int");
return data_.int_;
}
const char *getStringPtr() const
{
if(type_ != STRINGPTR)
throw std::runtime_error("Variane is not a string");
return data_.stringptr_;
}
};
int main()
{
Variant intval(1);
Variant stringval("hello");
std::cout << intval.getIntValue() << std::endl;
std::cout << stringval.getStringPtr() << std::endl;
}

How to insert custom class item to map C++

I have a class, how can i add one object of this class to map, and find it by id?
Class code:
class Client {
int FileDescriptor, Id, cryptcode;
unsigned char CustomData[256];
void PrepareClient()
{
// init code
}
public:
AnticheatClient (int fd, int id, int crypt_code)
{
FileDescriptor = fd;
Id = id;
cryptcode = crypt_code;
PrepareCrypt();
}
void OwnCryptEncrypt(unsigned char* data, int len)
{
//...
}
void OwnCryptDecrypt(unsigned char* data, int len)
{
//...
}
};
std::map<int, Client> ClientTable;
int main()
{
int id = 1;
Client c(1, id, 1);
// how can i add this client to map and how can i find it by id?
}
I tried so many example codes but not with custom class so they didn't work.
Thanks!
For adding a Client with key=10:
ClientTable[10] = Client(1, id, 1);
For find an element with key=10:
std::map<int, Client>::iterator it = ClientTable.find(10);
if (it != ClientTable.end()) {
int key = it->first;
Client c = it->second;
}
You can also use:
Client c = ClientTable[10];
but calling operator[] is not const. So, that is most probably not what you want to use if you just want to find an element.
1) "how can i add one object of this class to map?"
ClientTable[id] = c;
Well, technically, it adds a copy of the object to the map.
2) "and find it by id?"
Client lookerUpper = ClientTable[id];

Why is my implementation of C++ map not storing values?

I have a class called ImageMatrix, which implements the C++ map in a recursive fashion; the end result is that I have a 3 dimensional array.
typedef uint32_t VUInt32;
typedef int32_t VInt32;
class ImageMatrix
{
public:
ImageMatrixRow operator[](VInt32 rowIndex)
private:
ImageMatrixRowMap rows;
};
typedef std::map <VUInt32, VInt32> ImageMatrixChannelMap;
class ImageMatrixColumn
{
public:
VInt32 &operator[](VUInt32 channelIndex);
private:
ImageMatrixChannelMap channels;
};
typedef std::map<VUInt32, ImageMatrixColumn> ImageMatrixColumnMap;
class ImageMatrixRow
{
public:
ImageMatrixColumn operator[](VUInt32 columnIndex);
private:
ImageMatrixColumnMap columns;
};
typedef std::map<VUInt32, ImageMatrixRow> ImageMatrixRowMap;
Each operator simply returns a map-wrapper class within, like so:
ImageMatrixRow ImageMatrix::operator[](VInt32 rowIndex)
{
return rows[rowIndex];
}
ImageMatrixColumn ImageMatrixRow::operator[](VUInt32 columnIndex)
{
return columns[columnIndex];
}
VInt32 &ImageMatrixColumn::operator[](VUInt32 channelIndex)
{
return channels[channelIndex];
}
Basically, when I set the value as say 100, and test the value to cout, it shows as 0, and not the number to which I had set it.
for (VUInt32 a = 0; a < GetRowCount(); a++)
{
for (VUInt32 b = 0; b < GetColumnCount(); b++)
{
for (VUInt32 c = 0; c < GetChannelCount(); c++)
{
VInt32 value = 100;
matrix[a][b][c] = value;
VInt32 test = matrix[a][b][c];
// pixel = 100, test = 0 - why?
cout << pixel << "/" << test << endl;
}
}
}
Note: I've altered the original code for this example so that it takes up less space, so some syntax errors may occur (please don't point them out).
The following operators return by value, no writes modify the actual data.
ImageMatrixRow ImageMatrix::operator[](VInt32 rowIndex);
ImageMatrixColumn ImageMatrixRow::operator[](VUInt32 columnIndex);
Use:
ImageMatrixRow& ImageMatrix::operator[](VInt32 rowIndex)
ImageMatrixColumn& ImageMatrixRow::operator[](VUInt32 columnIndex)
All your operator[] functions except one return values - they should all return references.
Your ImageMatrixRow and ImageMatrixColumn operator[]() methods return copies, not referecencs.
"Each returns a reference" - are you sure about that?
They look like they return copies of the stored maps, not references to them.
Try, for example:
ImageMatrixRow & ImageMatrix::operator[](VInt32 rowIndex)
Note the & symbol.