I need help with the C++ mysqlpp driver calling a stored procedure and retrieving its output parameter. The queries seem to pass successfully but trying to get the stored value causes segmentation fault. My current pseudo code is:
mysqlpp::Connection* connection; // the connection type I am bound to use, no createStatement, prepareStatement methods
Query query = connection->query();
Query transactionQuery = connection->query();
query << "CALL sp_get_transactions_count(" << inputId << ", #transactionsCount);";
transactionQuery << "SELECT #transactionsCount as combinations;";
ClearQuerySentry cleanUpQuery(transactionQuery);
query.exec();
mysqlpp::StoreQueryResult transactionsResult = transactionQuery.store();
if (!transactionsResult || transactionsResult.num_rows() == 0)
{
logWarning(....);
}
else
{
const mysqlpp::Row& transactionRecord = result[0];
environment.pairTransactionsCount = verboseLexicalCast<int>(transactionRecord, "combinations"); // segfault on trying to cast static_cast<const char*>(row[fieldName.c_str()]))
}
I am not very experienced with mysqlpp and MySQL as a whole so it is possible my perception of the solution to be wrong. Thanks in advance.
Related
I have been picking my brain for a while trying to figure this one out.
The problem I am having is that the function I am using in Oracle returns a BLOB. It's a list of items that are concatenated together using ||.
From the research I have done,
In the QSQLQuery docs it says "Stored procedures that uses the return statement to return values, or return multiple result sets, are not fully supported. For specific details see SQL Database Drivers." - which leads me to believe I may need to switch to a different codebase if Qt cannot handle it yet.
The documentation for the QOCI driver mentions this "Binary Large Objects (BLOBs) can be read and written, but be aware that this process may require a lot of memory. You should use a forward only query to select LOB fields (see QSqlQuery::setForwardOnly())."
I did set
query.setForwardOnly(true);
Before I prepared or executed the statement.
However, I still get this error
QSqlError("6502", "Unable to execute statement", "ORA-06502: PL/SQL: numeric or value error: character string buffer too small\nORA-06512: at line 55\n")
I had to scrub the code a bit, I hope this is still helpful to give context to what i'm trying to accomplish
temp_clob clob;
name varchar2(183) := ?;
start varchar2(16) := ?;
end varchar2(16) := ?;
count integer := ?;
return_val named_redacted_table_object; -- Sorry had to remove this, it's a table with more Date, Varchar, etc
begin
dbms_lob.createtemporary(temp_clob, true);
return_val := package_name.function_name (
set_name => name,
start_time => to_date(start, 'yyyy-mm-dd hh24:mi'),
end_time => to_date(end, 'yyyy-mm-dd hh24:mi'),
max_count => count);
-- In here was a loop that would break apart the removed table object and make it into strings along the following lines
-- '' || return_val(i).name || return_val(i).value || etc
-- and would store these into the CLOB / temp_clob
? := temp_clob;
end;
I could not get something as simple as this to work
begin
? := 'test123';
end;
With the assumption I could at least read this string in Qt.
Here is my code for Qt
QString name = "test";
QSqlQuery query(db);
query.setForwardOnly(true);
query.prepare(sql);
QString test_sql_text = ui->comboBox_test_text->currentText();
qDebug() << name;
query.bindValue(0, name);
query.bindValue(1, "2003-03-14 00:00");
query.bindValue(2, "2005-03-14 23:00");
query.bindValue(3, 2);
query.bindValue(4, QString(""), QSql::Out);
bool query_executed_ok = query.exec();
qDebug() << "did it execute?" << query_executed_ok;
// qDebug() << query.executedQuery();
qDebug() << query.boundValues();
qDebug() << query.lastError();
QSqlRecord rec = query.record();
qDebug() << rec;
int record_count = rec.count();
qDebug() << "Records: " << record_count;
while (query.next()) {
for(int i=0;i<record_count;i++) {
qDebug() << query.isValid() << " - " << rec.fieldName(i) << " " << query.value(i).toString();
}
}
The error posted appears to be from within the Oracle code; ORA.... You have stripped so much it's hard to see what is actually happening, especially the are where the error apparently occurred. But perhaps using Oracle supplied code that is specifically designed to handle CLOBs. Instead of
'' || return_val(i).name ...
Try
dbms_lob.append(temp_clob, to_clob(return_val(i).name))
begin
? := 'test123';
end;
Bind variables are used to assign values to variables. You define your variable in your pl/sql code and assign a value to it at runtime by using a bind variable. In that case pl/sql code will compile correctly.
In your code the bind variable is used to replace the pl/sql variable, not the value, which will fail. Your pl/sql block cannot be compiled because it cannot resolve the "?".
A valid use of bind variables would be
BEGIN
l_xyz := ?;
END;
where you assign the value test123 at runtime.
It took some fiddling, and I realize I gave fairly cryptic code. So thank you to belayer and Koen for taking a shot at my mess.
What I was able to determine and get working for anyone else running into this:
Let me start off by saying I am not sure if this is a bug, or if i'm doing something in a way that was not intended by the designers of QSqlQuery (The class for handling SQL calls).
The call would work in SQL developer and I would see the intended CLOB with all characters. I was unable to get DBMS_Output to work, however, I saw this post saying to reserve space on the string before binding it to the query.
It solves my issue and shows the result in the debug window. However, it presents a new problem. What if the string becomes larger than my hard coded reserve value?
Here's the updated code for that
query.prepare(sql);
QString name= ui->comboBox_name->currentText();
qDebug() << project;
query.bindValue(":name", project);
query.bindValue(":start_date", "2005-03-14 00:00");
query.bindValue(":end_date", "2006-03-14 23:00");
query.bindValue(":max_count", 3);
QString testStr ="*****";
//testStr.truncate(0); //Maybe this works too?
testStr.reserve( 1000000 ); // This did it for sure
qDebug() << testStr.capacity();
query.bindValue(":result_clob", testStr, QSql::Out);
bool query_executed_ok = query.exec();
qDebug() << "did it execute?" << query_executed_ok;
if (query_executed_ok) {
testStr = query.boundValue(":result_clob").toString();
qDebug() << testStr;
} else {
qDebug() << "string is empty";
}
I got the idea to do this, from THIS post.
I have been trying to load an SQL database into a datatable in C++, however; it doesn't seem to want to work. The connection is working though, as DataReader works. Here is my code
void importDatabase() {
SqlConnection con;
SqlDataAdapter^ da;
SqlCommand cmd;
DataTable^ dt;
int count = 1;
try {
con.ConnectionString = "Data Source=MYNAME\\SQLEXPRESS;Initial Catalog=VinylRecords;Integrated Security=True";
cmd.CommandText = "SELECT * FROM Records";
cmd.Connection = %con;
con.Open();
da = gcnew SqlDataAdapter(%cmd);
dt = gcnew DataTable("Records");
Console::Write(da->ToString());
da->Fill(dt);
for (int i = 0; i < dt->Rows->Count - 1; i++) {
String^ value_string;
value_string = dt->Rows[i]->ToString();
Console::WriteLine(dt->Rows[i]->ToString());
count++;
}
cout << "There are " << count << " many records";
}
catch (Exception^ ex) {
Console::WriteLine(ex->ToString());
}
}
Please note, that I slightly altered the source name to post here, but only the first part.
What is wrong with my code?
So, the problem is here:
dt->Rows[i]->ToString()
Rows[i] is a Row object. And the Row class's ToString() method always prints out the fully qualified typename, which is what you are seeing. So this is technically working just fine. What you will need to do to get something useful is: you will need to access a specific column in that row and get it's value, then output that.
Something along the lines of:
foreach (DataRow dr in dt.Rows)
{
Console.Write(dr.Field<int>("ColumnOne"));
Console.Write(" | ");
Console.WriteLine(dr.Field<string>("ColumnTwo"));
}
I am not entirely sure on the syntax for accessing a specific cell inside of a DataTable when using C++\CLI. So I have provided the C# equivalent to explain why it is you were getting output of managed type names (e.g. "System.Data.DataRow") instead of the info inside of the Row's columns.
Also, I noticed you tagged this question with "mysql", but you are using the ADO.NET System.Data.SqlClient namespace. The SqlDataReader and SqlDataAdapter classes only work with TSQL (Microsoft's SQL Server databases) If you are actually connecting to a mysql database you will want to use the System.Data.OdbcDataAdapter class. You can read a little more here: https://msdn.microsoft.com/en-us/library/ms254931.aspx
I created a new TYPE in Oracle in order to have parity between my table and a local c++ object (I am using OCCI interface for C++).
In the code I use
void insertRowInTable ()
{
string sqlStmt = "INSERT INTO MY_TABLE_T VALUES (:x)";
try{
stmt = con->createStatement (sqlStmt);
ObjectDefinition *o = new ObjectDefinition ();
o->setA(0);
o->setB(1);
o->setC(2);
stmt->setObject (1, o);
stmt->executeUpdate ();
cout << "Insert - Success" << endl;
delete (o);
}catch(SQLException ex)
{
//exception code
}
The code compiles, connects to db but throws the following exception
Exception thrown for insertRow Error number: 947 ORA-00947: not enough
values
Do I have a problematic "sqlStmt"? Is something wrong with the syntax or the binding?
Of course I have already setup an environment and connection
env = Environment::createEnvironment (Environment::OBJECT);
occiobjm (env);
con = env->createConnection (user, passwd, db);
How many columns are in the table? The error message indicates that you didn't provide enough values in the insert statement. If you only provide a VALUES clause, all columns in the table must be provided. Otherwise you need to list each of the columns you're providing values for:
string sqlStmt = "INSERT INTO MY_TABLE_T (x_col) VALUES (:x)";
Edit:
The VALUES clause is listing placeholder arguments. I think you need to list one for each value passed, e.g.:
string sqlStmt = "INSERT INTO MY_TABLE_T (GAME_ID, VERSION) VALUES (:x1,:x2)"
Have a look at occidml.cpp in the Oracle OCCI docs for an example.
I am using quite a lot of parameterized queries in my code for performance reasons. In short, some of them work, some don't.
I initialize the query during construction of my database wrapper like this:
QString querystring = QString("SELECT somevalue FROM sometable "
"WHERE one_feature = :one_feature AND other_feature = :other_feature ");
myquery = QSqlQuery(db);
myquery.setForwardOnly(true);
myquery.prepare(querystring);
myquery is a QSqlQuery member variable of my database wrapper. Later on, in the function that wants to use this query, I do something like
int db_wrapper::fetch_some_value (int arg1, int arg2) {
myquery.clear();
myquery.bindValue(":one_feature", arg1);
myquery.bindValue(":other_feature", arg2);
qDebug() << "Bound values: " << myquery.boundValues();
bool OK = myquery.exec();
if (!OK) {
int number = myquery.lastError().number();
qDebug() << "db error " << number;
qDebug() << "db error " << myquery.lastError().text();
#ifdef USE_EXCEPTIONS
throw "Could not fetch some_value!";
#endif
}
// process data...
}
I always get the same error message/output:
Bound values: QMap((":one_feature", QVariant(int, 1) ) ( ":other_feature" , QVariant(int, 1) ) )
db error -1
db error " Parameter count mismatch"
terminate called after throwing an instance of 'char const*'
The exception is not surprising, but the parameter count mismatch is. The call to boundValues actually shows the right values and all, still I get this error message. I have similar queries that work just fine.
I tried substituting positional bind values, renamed the placeholders, used ? and positional bind values, all to no avail. Does anyone have an idea what the problem might be?
I use Qt 4.7.3 and SQLite 3.7.4-2
Usually this error means that the SELECT/UPDATE query itself is incorrect. You did not give the schema of the database so it's not possible to pinpoint which one. So one or more of somevalue, sometable, one_feature, or second_feature is not in the database/table.
I am using quite a lot of parameterized queries in my code for performance reasons. In short, some of them work, some don't.
I initialize the query during construction of my database wrapper like this:
QString querystring = QString("SELECT somevalue FROM sometable "
"WHERE one_feature = :one_feature AND other_feature = :other_feature ");
myquery = QSqlQuery(db);
myquery.setForwardOnly(true);
myquery.prepare(querystring);
myquery is a QSqlQuery member variable of my database wrapper. Later on, in the function that wants to use this query, I do something like
int db_wrapper::fetch_some_value (int arg1, int arg2) {
myquery.clear();
myquery.bindValue(":one_feature", arg1);
myquery.bindValue(":other_feature", arg2);
qDebug() << "Bound values: " << myquery.boundValues();
bool OK = myquery.exec();
if (!OK) {
int number = myquery.lastError().number();
qDebug() << "db error " << number;
qDebug() << "db error " << myquery.lastError().text();
#ifdef USE_EXCEPTIONS
throw "Could not fetch some_value!";
#endif
}
// process data...
}
I always get the same error message/output:
Bound values: QMap((":one_feature", QVariant(int, 1) ) ( ":other_feature" , QVariant(int, 1) ) )
db error -1
db error " Parameter count mismatch"
terminate called after throwing an instance of 'char const*'
The exception is not surprising, but the parameter count mismatch is. The call to boundValues actually shows the right values and all, still I get this error message. I have similar queries that work just fine.
I tried substituting positional bind values, renamed the placeholders, used ? and positional bind values, all to no avail. Does anyone have an idea what the problem might be?
I use Qt 4.7.3 and SQLite 3.7.4-2
Usually this error means that the SELECT/UPDATE query itself is incorrect. You did not give the schema of the database so it's not possible to pinpoint which one. So one or more of somevalue, sometable, one_feature, or second_feature is not in the database/table.