I want to run SQL script from a C++ program. my code goes like this:
int main()
{
//.....
sql_stmt = "Insert into t1 values ('qwerty');\nInsert into t1 values ('dothar');"
"//and many more INSERT statements";
sql_stmt = "DECLARE\nrollback_check_counter number;\n"
"BEGIN\n"
"rollback_check_counter :=1;\n"
"SAVEPOINT sp_1;\nIF rollback_check_counter = 1 THEN\n"
"BEGIN\n"+sql_stmt+"EXCEPTION\n"
"WHEN PROGRAM_ERROR THEN\n"
"rollback_check_counter :=0;\n"
"ROLLBACK TO sp_1;\n"
"WHEN OTHERS THEN\n"
"rollback_check_counter :=0;\n"
"ROLLBACK TO sp_1;\n"
"END;\n"
"END IF;\n"
"commit;\n"
"END;";
try
{
Connection *conn = env->createConnection(user,passwd); //error prone
Statement *stmt = conn->createStatement();
stmt->setSQL(sql_stmt);
row_count = stmt->execute(); //stmt->execute(sql_stmt);
Connection::conn->terminateStatement(Statement *stmt);
//con->terminateStatement(stmt);
env->terminateConnection(conn);
Environment::terminateEnvironment(env);
}
catch(SQLException& ex)
{}
//.....
return 0;
}
Although when i run these insert statement only they fairly run well but when i forms a SQL Script structure they seems to fail. I want to do so because i want to implement rollback. What am i missing? Could anyone suggest any alternative to implement it.
There are ; missing after both ROLLBACK TO sp_1
Related
In a MySQL Database I have a stored procedure defined by the following create statement:
DROP procedure IF EXISTS `get_next_ticket_number`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_next_ticket_number`()
BEGIN
SELECT IFNULL((SELECT `number`
FROM ticket
ORDER BY `number` DESC
LIMIT 1)+1, 1) as 'next_number';
END$$
DELIMITER ;
In a QT C++ application I'm attempting to call that procedure and use the result later:
std::unique_ptr<sql::PreparedStatement> pstmt;
std::unique_ptr<sql::ResultSet> res;
int next_number = 0;
try
{
pstmt.reset(dbConnection::getInstance()->getDb()->prepareStatement("CALL get_next_ticket_number();"));
res.reset(pstmt->executeQuery());
while(res->next()) {
next_number = res->getInt("next_number");
}
dbConnection::getInstance()->getDb()->commit(); // For good measure...
}
catch (sql::SQLException &e)
{
// Error logging here
}
getInstance()->getDb() returns a copy of the MySQL connector connection, which is setup with setAutoCommit(true) and works fine for all other select, insert, and update queries.
After successfully calling the procedure, all subsequent queries are failing with MySQL error code: 2014, SQLState: "HY000"
Why does this procedure call seem to leave the connection in an occupied state?
Wrapping the call in a do-while fixed the issue:
do {
// execute code
} while (stmt->getMoreResults());
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
Got myself into trouble today trying to create a stored procedure from ax.
Here is a simple example:
static void testProcedureCreation(Args _args)
{
MyParamsTable myParams;
SqlStatementExecutePermission perm;
str sqlStatement;
LogInProperty Lp = new LogInProperty();
OdbcConnection myConnection;
Statement myStatement;
ResultSet myResult;
str temp;
;
select myParams;
LP.setServer(myParams.Server);
LP.setDatabase(myParams.Database);
//Lp.setUsername("sa");
//Lp.setPassword("sa");
sqlStatement = #"create procedure testproc
as begin
print 'a'
end";
//sqlStatement = strFmt(sqlStatement, myStr);
info(sqlStatement);
perm = new SqlStatementExecutePermission(sqlStatement);
perm.assert();
try
{
myConnection = new OdbcConnection(LP);
}
catch
{
info("Check username/password.");
return;
}
myStatement = myConnection.createStatement();
myResult = myStatement.executeQuery(sqlStatement);
while (myResult.next())
{
temp = myResult.getString(1);
info(temp);
if (strScan(temp, 'Error', 1, strLen(temp)) > 0)
throw error(temp);
}
myStatement.close();
CodeAccessPermission::revertAssert();
}
To be honest, in my real example I am using BCP and some string concat with a lot of | ' and "".
Anyway, here is what I got:
For a couple of hours I kept changing and retrying a lot of things and, a good thought came into my mind.
"Let's try with a much easier example and check the results!"
OK, no luck, the results were the same, as you can see in the pic above.
But for no reason, I tried to :
exec testproc
in my ssms instance and to my surprise, it worked. My small procedure was there.
It would be so nice if someone could explain this behavior and maybe what should be the correct approach.
This Q/A should provide an answer.
How to get the results of a direct SQL call to a stored procedure?
executeQuery vs executeUpdate
I have 2 variables, and I want to insert their values into a MySQL database, but I don't know how to do this.
Here is the all of my code so far, please correct/advise:
void RegistrationForm::Register()
{
istifadeciAdi.GetWindowText(i_ad);
par.GetWindowText(i_par);
parTekrar.GetWindowText(i_par_tekrar);
if (istifadeciAdi.GetWindowTextLength() != 0) // if you can please write this line better.
{
if (i_parol == i_parol_tekrar)
{
MySQL_Driver *driver;
Connection *dbConn;
Statement *statement;
//ResultSet *result; // I don't need this line any more
//PreparedStatement *ps;
driver = get_mysql_driver_instance();
dbConn = driver->connect("host", "u", "c");
dbConn->setSchema("mfc_app_database");
statement = dbConn->createStatement();
statement->executeQuery("INSERT INTO users(`username`, `password`) VALUES (/* how to use i_ad and i_par as variable to set column value? */)"); // executes the user "input"
/*ps = dbConn->prepareStatement("INSERT INTO users(`username`, `password`, `name`) VALUES (?)");
ps->setString(1, "cccc");
ps->setString(2, "ffff);*/
//delete result;
//delete[] result;
/*delete ps;
delete[] ps;*/
delete statement;
delete[] statement; // don't use this line in your program as me
delete dbConn;
delete[] dbConn; // don't use this line in your program as me
}
else
MessageBox(L"Şifrə dəqiq təkrar olunmalıdır.", L"Xəbərdarlıq", MB_ICONWARNING);
}
else
AfxMessageBox(L"Boş qoymaq olmaz.");
}
Edit
There's no any error. But when I clicked the (Register) button it says:
Program stopped working
and after clicking the Debug button it takes me to line which insert query I wrote.
p.s Sorry for my poor English.
Use CString to make query.
For example:
CString strQuery;
strQuery.Format(_T("INSERT INTO users(`username`, `password`) VALUES ('%s', '%s')"),i_ad, i_par);
Before using this query string in executeQuery (or in other query commands) you must convert it to std::string. Because, execute, executeQuery and executeUpdate commands only accepts the std::string.So, add this lines:
CT2CA tempString(query);
std::string query(tempString);
And use this string in your execute command
statement->executeQuery(query);
The docs for that MySQL connector say to use statement::execute() for queries that don't return a resultset, and statement::executeQuery() when there is a single row resultset.
So for SQL INSERT INTO maybe your problem is that you should be using execute.
This code looks like using all the system memory. Why does it happend?
sql::Statement *Query;
sql::ResulSet *Result;
while(1){
Query = con->createStatement();
Result = Query->executeQuery("SELECT `Some` FROM `Table` LIMIT 1");
Result->next();
Result->close();
Query->close();
cout << "console message..." << endl;
//delete Query; SEGFAULT
}
If i comment all the lines except the cout the memory doesn't get filled. But using the SQL looks like Query = con->createStatement; is not replacing the old Query value and Result = bla; is not replacing the old value
createStatement() and executeQuery are creating new objects (which are the ones you store in Query and Result. Unless you explicitly delete them, they will remain in memory.
Add
delete Result;
delete Query;
To your loop.
BTW, this is in the connector documentation (together with sample code).
Well it looks like there is no info on google. I found what was the problem.
as SJuan76 said each call to createStatement and to executeQuery is a new object
So i started to do a lot of tryes and i figure out of the following
Use createStatement just once
Before deleting Result check if it's open and close
Delete Respuesta
Only delete con and Query at the end of the program auto_ptr(is your friend sometimes)
So the code to get the "forever running" program to use always the same memory shoud look like this
sql::Statement *Query;
sql::ResulSet *Result;
Query = con->createStatement();
while(1){
if(!Result->isClosed()) Result->close();
delete Result;
Result = Query->executeQuery("SELECT `Some` FROM `Table` LIMIT 1");
Result->next();
cout << "console message..." << endl;
}