The query SET #t=NOW(); INSERT INTO tests(posted) VALUES(#t); from C++ code (libmysqlclient) results in the following message:
You have an error in your SQL syntax; check the manual that
corresponds to your MariaDB server version for the right syntax to use
near 'INSERT INTO tests(posted) VALUES(#t)' at line 1
But the query works fine from console or HeidiSQL.
Table "tests":
'id' int(10) unsigned NOT NULL AUTO_INCREMENT,
'posted' datetime NOT NULL,
PRIMARY KEY ('id')
main.cpp
#include <cstdio>
#include "sqldb.h"
int main(int argc, char** argv) {
MySQLClient DB;
if (!DB.Connect("192.168.1.254", "test", "testpass")) {
printf("MySQL: %s\n", DB.Error());
return 1;
}
if (!DB.UseDB("test")) {
printf("MySQL: %s\n", DB.Error());
return 2;
}
if (!DB.Query("SET #t=NOW(); INSERT INTO tests(posted) VALUES(#t);")) {
printf("MySQL: %s\n", DB.Error());
return 3;
}
return 0;
}
Function "Query"
bool MySQLClient::Query(const char * statement) {
if (!ctx || !statement) return false;
unsigned long length = 0;
while(statement[length]) ++length;
return !mysql_real_query(static_cast<MYSQL*>(ctx), statement, length);
}
Why `libmysqlclient can't process this query?
CLIENT_MULTI_STATEMENTS enables mysql_query() and mysql_real_query() to execute statement strings containing multiple statements separated by semicolons.
mysql_real_connect(mysql, server, username, password, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
multiple queries with mysql_query in a c++ project
Related
Im currently trying C++ and building a databaseconnection with mysql c api from oracle. In first place it works fine.
If i query for a col like " select username [...] where id=1" I'll get my result: testuser.
But if i try "select * from..." I'll get:
1 //correct ID
t // only first letter.
Iam looking arround quite a bit, even here but cant figuer out ho to get the complete result:
1
testuser
As always: Thanks for your time and experience.
Nasten
my Code:
#include "mysql.h"
[...]
sendToLog(_T("Abfrage Starten."));
MYSQL* m_pConn = ConnectToDatabase();
ASSERT(m_pConn != nullptr);
MYSQL_RES *m_pResultSet;
MYSQL_ROW m_mysqlRow;
//Query:
mysql_query(m_pConn, "SELECT * FROM fplaner.user WHERE ll_UserID=1");
//Result speichern
m_pResultSet = mysql_store_result(m_pConn);
//Resultset durchgehen
ASSERT(m_pResultSet != nullptr);
int m_llResCount = mysql_num_fields(m_pResultSet);
if (m_llResCount == 0)
{
sendToLog(_T("ResultSet ist NICHT null aber Leer."));
}
else
{
while ((m_mysqlRow = mysql_fetch_row(m_pResultSet)))
{
for (int i = 0; i < m_llResCount; i++)
{
if (m_mysqlRow == NULL)
{
sendToLog(_T("Ungültiges ResultSet erhalten on COunt: "+i));
}
else
{
sendToLog(_T("Gültiges ResultSet erhalten."));
CString strTest(*m_mysqlRow[i]);
//CString strTest(*m_mysqlRow);--> gives correct name when query with select name
from...
m_mysqlRow
sendToLog((strTest));
}
}
}
}
Probleme solved:
The resultset is an Char pointer on an pointer. By dereferencing it with * it will only provide the first char if the strong. So without it works fine:
Change "CString strTest(*m_mysqlRow[i]);" to "CString strTest(m_mysqlRow[i]);" . and it will work as intended.
bool MySql_Register(const char* id, const char* pw) {
MYSQL* connect_ptr;
connect_ptr = mysql_init(NULL);
if(!mysql_real_connect(connect_ptr, HOST, USER, PW, NAME, 3306, NULL, 0)) {
fprintf(stderr, "%s ",mysql_error(connect_ptr));
exit(1);
}
char sql[256] = {0};
sprintf(sql, "select * from user where id='%s'", id);
mysql_query(connect_ptr, sql);
int len = mysql_affected_rows(connect_ptr);
if(len == 1)
return PK_ID_OVERLAP;
sprintf(sql, "insert into user (id, pw) values ('%s', '%s')", id, pw);
mysql_query(connect_ptr, sql);
len = mysql_affected_rows(connect_ptr);
if(len == 1)
return true;
else
return false;
}
mysql_connect and mysql_query running is very well. but mysql_affected_rows returned value -1.
I don't know where is the problem....
From the documentation:
-1 indicates that the query returned an error or that, for a SELECT query, mysql_affected_rows() was called prior to calling
mysql_store_result()
Moreover, I suggest you to use prepared statements instead of sprintf to avoid SQL injections.
If I have the 2 following functions:
int AccessDb::InsertColValue(string tableName, string col, string val)
{
try
{
sql::Statement *stmt;
bool ret;
if ((nomTable != "") && (col != "") && (val != ""))
{
string query = "INSERT INTO " + tableName + "(" + col + ") values (";
query += val + ");";
stmt = con->createStatement();
ret = stmt->execute(query);
}
delete stmt;
return 0;
}
catch (sql::SQLException &e)
{
return -1;
}
}
and
long AccessDb::LastInsertId()
{
try
{
sql::Statement *stmt;
sql::ResultSet *res;
string query = "SELECT LAST_INSERT_ID() AS LAST_ID";
stmt = con->createStatement();
res = stmt->executeQuery(query);
delete stmt;
long lastId;
while (res->next())
{
lastId = res->getInt("LAST_ID");
}
return lastId;
}
catch (sql::SQLException &e)
{
return -1;
}
}
Can I be sure that the return of LastInsertId() will always give me the correct id if I write the following lines and if the id is auto generated by the database?
AccessDb adb; // initialize the connexion with the db
int ret = adb.InsertColValue("people", "name", "John");
if (ret == 0)
long lastId = adb.LastInsertId();
If the previous code is called somewhere else at the same time, can I have a wrong value in my lastId variable ? If yes, do I have to use locks and unlocks on my table to avoid that or another solution ?
Here's what the docs says:
The ID that was generated is maintained in the server on a
per-connection basis. This means that the value returned by the
function to a given client is the first AUTO_INCREMENT value generated
for most recent statement affecting an AUTO_INCREMENT column by that
client. This value cannot be affected by other clients, even if they
generate AUTO_INCREMENT values of their own. This behavior ensures
that each client can retrieve its own ID without concern for the
activity of other clients, and without the need for locks or
transactions.
So, unless your own code on the client is sharing a connection between several threads (Which it looks like you're not, since there are no mutexes or locks in your code) you can be sure SELECT LAST_INSERT_ID() isn't mixed up with any other connection or client.
I can't find the docs for the C++ mysql library but verify what the return value of ret = stmt->execute(query); in your InsertColValue() function means, such that you're sure the only possible way that you fail to insert anything is when an exception is thrown.
Currently my application only supports SQLite databases, but I would like to support both SQLite and MySQL databases, so I'm testing out the SOCI library to see if it does what I need. However, despite the examples and documentation, I can't figure out how SOCI handles prepared statements.
When using the SQLite C API, you prepare statement:
sqlite3_stmt* statement;
sqlite3_prepare_v2( database_handle_pointer,
"SELECT * FROM table WHERE user_id=:id;",
-1,
&statement,
NULL );
And later you bind a value to the :id place holder, execute the statement and step through the results:
const sqlite3_int64 user_id = some_function_that_returns_a_user_id();
const int index = sqlite3_bind_parameter_index( statement, ":id" );
sqlite3_bind_int64( statement, index, user_id );
while ( sqlite3_step( statement ) == SQLITE_ROW )
{
// Do something with the row
}
How do I do this with SOCI? It looks like the prepare and bind concepts are not separated like with the native SQLite API. Does the bind have to happen during the prepare using soci::use()?
Update 1: In case I'm not explaining the question well enough: Here's a small, working, C++ example using the SQLite C API. If I could see this re-implemented using SOCI, it would answer the question.
#include <sqlite3.h>
#include <iostream>
// Tables and data
const char* table = "CREATE TABLE test ( user_id INTEGER, name CHAR );";
const char* hank = "INSERT INTO test (user_id,name) VALUES(1,'Hank');";
const char* bill = "INSERT INTO test (user_id,name) VALUES(2,'Bill');";
const char* fred = "INSERT INTO test (user_id,name) VALUES(3,'Fred');";
// Create a SQLite prepared statement to select a user from the test table.
sqlite3_stmt* make_statement( sqlite3* database )
{
sqlite3_stmt* statement;
sqlite3_prepare_v2( database,
"SELECT name FROM test WHERE user_id=:id;",
-1, &statement, NULL );
return statement;
}
// Bind the requested user_id to the prepared statement.
void bind_statement( sqlite3_stmt* statement, const sqlite3_int64 user_id )
{
const int index = sqlite3_bind_parameter_index( statement, ":id" );
sqlite3_bind_int64( statement, index, user_id );
}
// Execute the statement and print the name of the selected user.
void execute_statement( sqlite3_stmt* statement )
{
while ( sqlite3_step( statement ) == SQLITE_ROW )
{
std::cout << sqlite3_column_text( statement, 0 ) << "\n";
}
}
int main()
{
// Create an in-memory database.
sqlite3* database;
if ( sqlite3_open( ":memory:", &database ) != SQLITE_OK )
{
std::cerr << "Error creating database" << std::endl;
return -1;
}
// Create a table and some rows.
sqlite3_exec( database, table, NULL, NULL, NULL );
sqlite3_exec( database, hank, NULL, NULL, NULL );
sqlite3_exec( database, bill, NULL, NULL, NULL );
sqlite3_exec( database, fred, NULL, NULL, NULL );
sqlite3_stmt* statement = make_statement( database );
bind_statement( statement, 2 );
execute_statement( statement );
// Cleanup
sqlite3_finalize( statement );
sqlite3_close( database );
return 1;
}
The same program partially implemented using SOCI (Note the two stub functions marked as HELPME)
#include <soci/soci.h>
#include <iostream>
const char* table = "CREATE TABLE test ( user_id INTEGER, name CHAR );";
const char* hank = "INSERT INTO test (user_id,name) VALUES(1,'Hank');";
const char* bill = "INSERT INTO test (user_id,name) VALUES(2,'Bill');";
const char* fred = "INSERT INTO test (user_id,name) VALUES(3,'Fred');";
soci::statement make_statement( soci::session& database )
{
soci::statement statement =
database.prepare << "SELECT name FROM test WHERE user_id=:id";
return statement;
}
void bind_statement( soci::statement& statement, const int user_id )
{
// HELPME: What goes here?
}
void execute_statement( soci::statement& statement )
{
// HELPME: What goes here?
}
int main()
{
soci::session database( "sqlite3", ":memory:" );
database << table;
database << hank;
database << bill;
database << fred;
soci::statement statement = make_statement( database );
bind_statement( statement, 2 );
execute_statement( statement );
}
Update 2: I ended up ditching SOCI when I found the cppdb library. Unlike SOCI, it is just a very thin wrapper around the native C APIs, which suits my needs at this time.
The documentation explains how to use prepared statements with parameters:
int user_id;
string name;
statement st = (database.prepare << "SELECT name FROM test WHERE user_id = :id",
use(user_id),
into(name));
user_id = 1;
st.execute(true);
Please note that the lifetime of the user_id and name variables must be at least as long as that of st.
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());