Inserting multiple rows in an Oracle database with OCCI - c++

This is my first experience so OCCI so I beg your pardon if the answer is obvious.
I have a Statement object, created the usual way:
string sqlStatement = "INSERT INTO data_tab VALUES(:id, :name, :number, :when)";
m_stmt = m_conn->createStatement(sqlStatement);
m_stmt->setMaxIterations(100);
Then I need to loop over a few objects that I should insert into the database:
for(size_t i = 0; i < data.size(); ++i)
{
m_stmt->setInt(1, i);
try {
m_stmt->setString(2, data[i].client());
}
catch(SQLException& e)
{
cerr << "setString(): " << e.what();
exit(1);
}
m_stmt->setDouble(3, data[i].number());
m_stmt->setDate(4, data[i].when());
// ...
// Checks if maxIterations is lesser than data size,
// oteherwise calls executeUpdate and other kind of
// boilerplate code.
// ...
m_stmt->addIteration();
}
But the code borks in the setString method with the following output:
setString(): ORA-32132: maximum iterations cannot be changed
I believe I should get this error is I call setMaxIterations after a setXXX method but I don't do this. What I am doing wrong?

You should invoke setMaxParamSize() prior to setting a variable length parameter:
m_stmt->setMaxParamSize(2, 1000) // or whatever is the max length of your string.

Related

How to use a string in a Sql argument?

How should go about inserting a string into a SQL argument?
Something like this:
string clas = "Computer Science";
sql = "SELECT * from STUDENTS where CLASS='clas'";
There are two ways of doing this:
This is the preferred and more secure way. You can use prepared statements like this
string clas = "Computer Science";
sql = "SELECT * FROM Students WHERE Class=?";
// Prepare the request right here
preparedStatement.setString(1, clas);
// Execute the request down here
A simpler but much less secure option (it's vulnerable to SQL-Injections)
string clas = "Computer Science";
sql = "SELECT * FROM Students WHERE Class='" + clas + "'";
Simple answer:
You can just do as follows:
string clas = "Computer Science";
sql = "SELECT * FROM Students WHERE Class='" + clas + "'";
Good answer:
But, we can do better than that. What if multiple value replacement needed, then what? See the code below, it can replace multiple strings. And also, you can write sql injection check if needed. And the best thing, you just have to call the prepare() function and you're done.
Usage Instructions:
Use ? where you need to put a string. If there are multiple string replacement needed, put all the strings in order(as parameters) when calling prepare function. Also, notice prepare function call prepare(sql, {param_1, param_2, param_3, ..., param_n}).
[Note: it'll work with c++11 and higher versions. It won't work with c++11 pre version. So, while compile it, use -std=c++11 flag with g++]
#include <iostream>
#include <string>
#include <initializer_list>
using namespace std;
// write code for sql injection if you think
// it necessary for your program
// is_safe checks for sql injection
bool is_safe(string str) {
// check if str is sql safe or not
// for sql injection
return true; // or false if not sql injection safe
}
void prepare(string &sql, initializer_list<string> list_buf) {
int idx = 0;
int list_size = (int)list_buf.size();
int i = 0;
for(string it: list_buf) {
// check for sql injection
// if you think it's necessary
if(!is_safe(it)) {
// throw error
// cause, sql injection risk
}
if(i >= list_size) {
// throw error
// cause not enough params are given in list_buf
}
idx = sql.find("?", idx);
if (idx == std::string::npos) {
if(i < list_size - 1) {
// throw error
// cause not all params given in list_buf are used
}
}
sql.replace(idx, 1, it);
idx += 1; // cause "?" is 1 char
i++;
}
}
// now test it
int main() {
string sql = "SELECT * from STUDENTS where CLASS=?";
string clas = "clas";
prepare(sql, {clas});
cout << sql << endl;
string sql2 = "select name from class where marks > ? or attendence > ?";
string marks = "80";
string attendence = "40";
prepare(sql2, {marks, attendence});
cout << sql2 << endl;
return 0;
}
[P.S.]: feel free to ask, if anything is unclear.

MySQL Trouble transitioning from Injection to Paramterization

I have the following code here that executes a query. Originally, I used SQL Injection to return row results. Hearing I should use parametrization, I rearranged my code and read the MySQL docs on how to do so. I'm using MySQL's C library in a C++ application.
However, it's not returning the results.
I know my SQL statement is 100% fine. It has been tested. The only thing I changed was changing %d (injection) to ?, which accepts the player's ID.
This returns -1. It's a SELECT statement though, so maybe it's normal?
// Get the number of affected rows
affected_rows = mysql_stmt_affected_rows(m_stmt);
This returns 2. This is correct. I have two fields being returned.
// Store the field count
m_fieldCount = mysql_field_count(&m_conn);
This returns 0 (success)
if (mysql_stmt_store_result(m_stmt))
Finally, this returns null.
m_result = mysql_store_result(&m_conn);
I need m_result so I can read the rows. "mysql_stmt_store_result" sounds similar, but doesn't return MYSQL_RESULT.
m_result = mysql_store_result(&m_conn);
/// <summary>
/// Executes a query.
/// </summary>
/// <param name="query">The query to execute.</param>
/// <returns>Returns true on success, else false.</returns>
bool SQLConnection::executeQuery_New(const char *query)
{
int param_count = 0;
int affected_rows = 0;
// Validate connection.
if (!m_connected)
return false;
// Initialize the statement
m_stmt = mysql_stmt_init(&m_conn);
if (!m_stmt) {
fprintf(stderr, " mysql_stmt_init(), out of memory\n");
return false;
}
// Prepare the statement
if (mysql_stmt_prepare(m_stmt, query, strlen(query))) {
fprintf(stderr, " mysql_stmt_prepare(), INSERT failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Get the parameter count from the statement
param_count = mysql_stmt_param_count(m_stmt);
if (param_count != m_bind.size()) {
fprintf(stderr, " invalid parameter count returned by MySQL\n");
return false;
}
// Bind buffers
// The parameter binds are stored in std::vector<MYSQL_BIND>
// I need to convert std::vector<MYSQL_BIND> m_bind to MYSQL_BIND *bnd
MYSQL_BIND *bind = new MYSQL_BIND[m_bind.size() + 1];
memset(bind, 0, sizeof(bind) * m_bind.size());
for (int i = 0; i < param_count; i++)
bind[i] = m_bind[i];
if (mysql_stmt_bind_param(m_stmt, &bind[0]))
{
fprintf(stderr, " mysql_stmt_bind_param() failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Execute the query
if (mysql_stmt_execute(m_stmt)) {
fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
return false;
}
// Get the number of affected rows
affected_rows = mysql_stmt_affected_rows(m_stmt);
//if (affected_rows == -1) {
// fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
// fprintf(stderr, " %s\n", mysql_stmt_error(m_stmt));
// return false;
//}
// Store the field count
m_fieldCount = mysql_field_count(&m_conn);
// Store the result
if (mysql_stmt_store_result(m_stmt))
{
fprintf(stderr, " failed retrieving result\n");
fprintf(stderr, " %s\n", mysql_error(&m_conn));
int d = mysql_errno(&m_conn);
return false;
}
// This looks similar to the last above statement, but I need m_result. I used mysql_store_result earlier when using injection and it worked fine, but here in this case it returns null.
m_result = mysql_store_result(&m_conn);
// Close the statement
if (mysql_stmt_close(m_stmt)) {
/* mysql_stmt_close() invalidates stmt, so call */
/* mysql_error(mysql) rather than mysql_stmt_error(stmt) */
fprintf(stderr, " failed while closing the statement\n");
fprintf(stderr, " %s\n", mysql_error(&m_conn));
return false;
}
// Delete bind array
if (bind) {
delete[] bind;
bind = NULL;
}
return true;
}
How I'm adding an int parameter (player's id):
void SQLConnection::addParam(int buffer, enum_field_types type, unsigned long length)
{
MYSQL_BIND bind;
memset(&bind, 0, sizeof(bind));
bind.buffer = (char *)&buffer;
bind.buffer_type = type;
bind.is_null = 0;
bind.length = &length;
m_bind.push_back(bind);
}
My variables and their types:
class SQLConnection
{
private:
MYSQL m_conn;
MYSQL_ROW m_row;
MYSQL_RES *m_result;
char m_errorMessage[ERROR_MSG_MAX];
bool m_connected;
MYSQL_STMT *m_stmt;
std::vector<MYSQL_BIND> m_bind;
int m_fieldCount;
// ...
And finally its calling function at the end of the SQL statement:
...WHERE player_id = ?;");
conn.addParam(m_id, MYSQL_TYPE_LONGLONG, 0);
if (!conn.executeQuery_New(buffer)) {
conn.close();
return "";
}
// Close the connection.
conn.close();
std::string s = conn.getField("max_value_column_name");
The error code I get is 2014:
https://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
Just for the sake of interest, this is a prior function I used. This worked fine for injection. Using the new function above with parameterization is the one causing the issues.
bool SQLConnection::executeQuery(const char *query)
{
// Validate connection.
if (!m_connected)
return false;
// Execute the query
int status = mysql_query(&m_conn, query);
if (status != 0) {
sprintf(m_errorMessage, "Error: %s", mysql_error(&m_conn));
return false;
}
// Store the result
m_result = mysql_store_result(&m_conn);
return true;
}
After I started having language religious wars in my head about using C# over C++, I thought I'd give one last attempt here. Any help is appreciated.
Edit:
This is how I read in column names prior to parameterization (maybe this code needs to be updated after calling mysql_stmt_store_result(m_stmt)?
std::string SQLConnection::getField(const char *fieldName)
{
MYSQL_FIELD *field = NULL;
unsigned int name_field = 0;
mysql_stmt_data_seek(m_stmt, 0);
mysql_stmt_fetch_column(m_stmt, &bind, 0, 0);
//mysql_data_seek(m_result, 0);
//mysql_field_seek(m_result, 0);
const unsigned int num_fields = mysql_stmt_field_count(m_stmt);
// const unsigned int num_fields = mysql_num_fields(m_result);
std::vector<char *> headers(num_fields);
for (unsigned int i = 0; (field = mysql_fetch_field(m_result)); i++)
{
headers[i] = field->name;
if (strcmp(fieldName, headers[i]) == 0)
name_field = i;
}
while ((m_row = mysql_fetch_row(m_result))) {
return std::string(m_row[name_field]);
}
return "";
}
Edit:
What I'm finding is in this last function there are equivalent functions for statements, like mysql_num_fields() is mysql_stmt_field_count(). I'm thinking these need to be updated because it's using m_stmt and not m_result anymore, which gives reason to update the functions so m_stmt is used. It's not very apparent how to update the second half of the function though.
You may need a better understanding of how stmt works.You can't get the final results by mysql_store_result() when you're using stmt.
You shoud bind several buffers for the statement you're using to accept the result set. You can finish this by mysql_stmt_bind_result(), just like mysql_stmt_bind_param().
Then you can use the buffers bound by mysql_stmt_bind_result() to return row data. You can finish this by mysql_stmt_fetch().
Do the fetch method repeatedly, so you can get the whole result set row by row.
The basic sequence of calls:
mysql_stmt_init
mysql_stmt_prepare
mysql_stmt_bind_param
mysql_stmt_execute
mysql_stmt_bind_result
mysql_stmt_store_result
mysql_stmt_fetch (repeatedly, row by row)
mysql_stmt_free_result
It works for me.
It's a long time since I finished this part of my project, you'd better read the manual carefully and find more examples of stmt.
Sorry for my poor English. Good luck!

How to declare an empty rowset properly with SOCI?

Imagine that I have the following function. In case of invalid parameters or exception, the function has to exit with an empty rowset.
rowset<row> SelectAllFromTable(string tableName)
{
session sql(odbc, "...");
// if parameters are not valid -> return empty rowset<row>
if (tableName == "")
{
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
string query = "SELECT * FROM " + tableName;
try
{
rowset<row> rs = sql.prepare << query;
return rs;
}
catch (exception const &e)
{
cerr << "Error: " << e.what() << endl;
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
// query that returns 0 result
rowset<row> res = (sql.prepare << "SELECT ID FROM T1 WHERE ID = -9999");
return res;
}
The solution I wrote above works but my question is : Is there a better way to return an empty rowset with SOCI ?
Since the documentation hasn't much to offer to this I looked into the rowset Header: There is no default constructor for it and no public method to set the iterators, ergo you can't get an empty rowset by yourself.
Despite why don't you use exceptions which are just perfect for that case. Just don't catch the soci_error exception, then the caller SelectAllFromTable could catch it. This would have many advantages:
The caller would know if there is really no data in the table or there is no table
The caller could know why he can't use the table (misspelled or security reasons)
The caller could know if there are other troubles and take action or if not, rethrow it, so his caller might can.

How to send C++ and mysql dynamic mysql queries

Working with Visual Studio, Windows 7 and mysql.h library.
What I want to do is send a MySQL query like this:
mysql_query(conn, "SELECT pass FROM users WHERE name='Leo Tolstoy'");
The only thing I can't get working is sending a query where the name would be not a constant as it's shown above, but a variable taken from a text field or anything else. So how should I work with a variable instead of a constant?
Hope I made my question clear.
Use a prepared statement, which lets you parameterize values, similar to how functions let you parameterize variables in statement blocks. If using MySQL Connector/C++:
// use std::unique_ptr, boost::shared_ptr, or whatever is most appropriate for RAII
// Connector/C++ requires boost, so
std::unique_ptr<sql::Connection> db;
std::unique_ptr<sql::PreparedStatement> getPassword
std::unique_ptr<sql::ResultSet> result;
std::string name = "Nikolai Gogol";
std::string password;
...
getPassword = db->prepareStatement("SELECT pass FROM users WHERE name=? LIMIT 1");
getPassword->setString(1, name);
result = getPassword->execute();
if (result->first()) {
password = result->getString("pass");
} else {
// no result
...
}
// smart pointers will handle deleting the sql::* instances
Create classes to handle database access and wrap that in a method, and the rest of the application doesn't even need to know that a database is being used.
If you really want to use the old C API for some reason:
MYSQL *mysql;
...
const my_bool yes=1, no=0;
const char* getPassStmt = "SELECT password FROM users WHERE username=? LIMIT 1";
MYSQL_STMT *getPassword;
MYSQL_BIND getPassParams;
MYSQL_BIND result;
std::string name = "Nikolai Gogol";
std::string password;
if (! (getPassword = mysql_stmt_init(mysql))) {
// error: couldn't allocate space for statement
...
}
if (mysql_stmt_prepare(getPassword, getPassStmt, strlen(getPassStmt))) {
/* error preparing statement; handle error and
return early or throw an exception. RAII would make
this easier.
*/
...
} else {
unsigned long nameLength = name.size();
memset(&getPassParams, 0, sizeof(getPassParams));
getPassParams.buffer_type = MYSQL_TYPE_STRING;
getPassParams.buffer = (char*) name.c_str();
getPassParams.length = &nameLength;
if (mysql_stmt_bind_param(getPassword, &getPassParams)) {
/* error binding param */
...
} else if (mysql_stmt_execute(getPassword)) {
/* error executing query */
...
} else {
// for mysql_stmt_num_rows()
mysql_stmt_store_result(getPassword);
if (mysql_stmt_num_rows(getPassword)) {
unsigned long passwordLength=0;
memset(&result, 0, sizeof(result));
result.length = &passwordLength;
mysql_stmt_bind_result(getPassword, &result);
mysql_stmt_fetch(getPassword);
if (passwordLength > 0) {
result.buffer = new char[passwordLength+1];
memset(result.buffer, 0, passwordLength+1);
result.buffer_length = passwordLength+1;
if (mysql_stmt_fetch_column(getPassword, &result, 0, 0)) {
...
} else {
password = static_cast<const char*>(result.buffer);
}
}
} else {
// no result
cerr << "No user '" << name << "' found." << endl;
}
}
mysql_stmt_free_result(getPassword);
}
mysql_stmt_close(getPassword);
mysql_close(mysql);
As you see, Connector/C++ is simpler. It's also less error prone; I probably made more mistakes using the C API than Connector/C++.
See also:
Developing Database Applications Using MySQL Connector/C++
Connector C++ in the MySQL Forge wiki
Wouldn't you just build the query-string, using sprint or concatenating strings or whatever, so that by the time it gets to MySQL, MySQL just sees the SQL and has no idea where the constant came from? Or am I missing something?
here is an example:
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
/// ...
string name_value = "Leo Tolstoy";
ostringstream strstr;
strstr << "SELECT pass FROM users WHERE name='" << name_value << "'";
string str = strstr.str();
mysql_query(conn, str.c_str());

MySQL Transactions using C++?

How would I go about wrapping an amount of queries in a transaction in C++? I'm working on Ubuntu 10, using this file:
#include "/usr/include/mysql/mysql.h"
with C++ to interact with a MySQL database.
EDIT: Right now I'm running queries through a small wrapper class, like so:
MYSQL_RES* PDB::query(string query)
{
int s = mysql_query(this->connection, query.c_str());
if( s != 0 )
{
cout << mysql_error(&this->mysql) << endl;
}
return mysql_store_result(this->connection);
}
MYSQL_ROW PDB::getarray(MYSQL_RES *res)
{
return mysql_fetch_row( res );
}
// example one
MYSQL_RES res = db->query( "SELECT * FROM `table` WHERE 1" );
while( MYSQL_ROW row = db->getarray( res ) )
{
cout << row[0] << endl;
}
If you use MySQL++, you get RAII transaction behavior with Transaction objects:
mysqlpp::Connection con( /* login parameters here */ );
auto query = con.query("UPDATE foo SET bar='qux' WHERE ...");
mysqlpp::Transaction trans(con);
if (auto res = query.execute()) {
// query succeeded; optionally use res
trans.commit(); // commit DB changes
}
// else, commit() not called, so changes roll back when
// 'trans' goes out of scope, possibly by stack unwinding
// due to a thrown exception.
You could always just run START TRANSACTION / COMMIT / ... manually.
Another way would be to create a wrapper class which runs START TRANSACTION in the constructor, provides commit/rollback functions and, depending on your use case, does a rollback upon destruction.