Process utf-8 data from MySQL in C++ and give result back - c++

I just simply want to know - how to get MySQL results to C++ in string (or another similar "string" datatype) in a way that would not deform data saved in utf8_unicode_ci.
After C++ process, app should write results back (to another table) into the database where argument is encoded in utf8_unicode_ci as well.
I read somewhere that using wide char is not recommended by Unicode consortium, but my problem is still that a second argument for mysql_query() is string which is not wide enough.
I've already tried some "utf8 string" solutions, but unsuccessfully. I also tried to save data in common string and than write it into the database in the same way (byte after byte), but it doesn't work properly at all... (see my code below)
DATABASE:
save_text: text = ěščřžýáíé
AFTER PROCESS: save_newtext: text = ?š??žýáíé
#include <iostream>
#include <mysql.h>
#include <string>
#define ... // server conection
using namespace std;
MYSQL *connection, mysql;
MYSQL_RES *result;
MYSQL_ROW row;
int query_state;
int main(int argc, char** argv)
{
mysql_init(&mysql);
mysql_real_connect(&mysql,CTH_SERVER,CTH_USER,CTH_PASSWORD,CTH_DB_IN,0,0,0));
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "utf8");
mysql_options(&mysql, MYSQL_INIT_COMMAND, "SET NAMES utf8");
mysql_query(connection, "SELECT text FROM save_text WHERE id = 23");
result = mysql_store_result(connection);
if ((row = mysql_fetch_row(result)) != NULL) {
string sql;
sql = "INSERT INTO save_newtext (text) VALUES ('";
sql += row[0];
sql += "')";
mysql_query(connection, sql.c_str());
}
return 0;
}
Thanks in advance!!

From MySQL Reference
mysql_options() should be called after mysql_init() and before
mysql_connect() or mysql_real_connect().
So your code should be
mysql_init(&mysql);
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "utf8");
mysql_options(&mysql, MYSQL_INIT_COMMAND, "SET NAMES utf8");
mysql_real_connect(&mysql,CTH_SERVER,CTH_USER,CTH_PASSWORD,CTH_DB_IN,0,0,0));

Related

How does prepared statements in Sqlite C++ work

I do not know how to implement prepared statements in my Sqlite3 code
#include <iostream>
#include <sqlite3.h>
#include <stdio.h>
static int callback (void* NotUsed, int argc, char** argv, char** azColName) {
int i;
for (i = 0; i < argc; i++) {
std::cout << ("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
std::cout << ("\n");
return 0;
}
int main (int argc, char* argv[]) {
sqlite3* db;
char* zErrMsg = 0;
int rc;
char* sql;
/* Open database */
rc = sqlite3_open ("test.db", &db);
if (rc) {
std::cerr << "Can't open database: \n" << sqlite3_errmsg (db);
return (0);
}
else {
std::cout << "Opened database successfully\n";
}
std::string newName;
std::cin >> newName;
/* Create SQL statement */
sql = "UPDATE company SET name = newName WHERE id = 1";
/* Execute SQL statement */
rc = sqlite3_exec (db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_OK) {
std::cout << "SQL error: \n" << zErrMsg;
sqlite3_free (zErrMsg);
}
else {
std::cout << "Records created successfully\n";
}
sqlite3_close (db);
return 0;
}
The user has to input newName and this variable should be used to Update a field in the Database. This way it does not work, because the Sql script is searching for a column. In the internet I found, that I had to use a prepared statement, but I do not know how to implement it.
You start with an sql statement that has placeholders for the parameters that you wish to bind later. Here, I use a single question mark for the placeholder, but there are other options described in the documentation.
std::string sql = "UPDATE company SET name = ? WHERE id = 1";
Then you construct a prepared statement (or "compile", as they say it in sqlite documentation). You'll normally use sqlite_prepare_v2 function, but there are others (for when your statement is encoded in something else than utf-8, for example).
sqlite3_stmt* stmt; // will point to prepared stamement object
sqlite3_prepare_v2(
db, // the handle to your (opened and ready) database
sql.c_str(), // the sql statement, utf-8 encoded
sql.length(), // max length of sql statement
&stmt, // this is an "out" parameter, the compiled statement goes here
nullptr); // pointer to the tail end of sql statement (when there are
// multiple statements inside the string; can be null)
Then you bind the parameter(s). There's a whole bunch of avaliable functions. Which one exactly you use depends on the type
of data that you're binding to the parameter. Here, we bind text, so we use sqlite3_bind_text:
std::string newName = /* get name from user */;
sqlite3_bind_text(
stmt, // previously compiled prepared statement object
1, // parameter index, 1-based
newName.c_str(), // the data
newName.length(), // length of data
SQLITE_STATIC); // this parameter is a little tricky - it's a pointer to the callback
// function that frees the data after the call to this function.
// It can be null if the data doesn't need to be freed, or like in this case,
// special value SQLITE_STATIC (the data is managed by the std::string
// object and will be freed automatically).
So, the prepared statement is ready to go. Now you execute it by passing it to sqlite3_step:
sqlite3_step(stmt); // you'll want to check the return value, read on...
Now, when you step through a statement that's supposed to return rows of a result table, this function will keep returning SQLITE_ROW as long as there are result rows to process, and SQLITE_DONE when there are none left. You can use sqlite3_column_* family of functions to get the single columns from a result row. I'll let you figure this out on your own.
For a simple update statements that you have, sqlite3_step will return SQLITE_DONE on the first call. More info and possible error codes are here.
When it's all done, you finish by destructing the prepared statement.
sqlite3_finalize(stmt);
I hope this should get you started.

C++: handle sql-String in UTF32

I'm writing a program, that reads a UTF32 encoded string from a MySQL-Database and should write it to a file/cout. The Problem is, that as long as I just use letters and so on, it works, but as soon as I take a emojicon it just writes a "?". I tried:
1. Change the locale (with std::setlocale(LC_ALL, "en_EN.UTF-32");)
2. Use wstringsbut gives an eror like that: Can't convert sql::String to wstring...
Any suggestions how to do this?
I use the following (shortened) code:
int main(int argc, char **argv){
sql::Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
sql::ResultSet *res;
driver = get_driver_instance();
con = driver->connect(IP, USER, Password);
con->setSchema(DBNAME);
stmt = con->createStatement();
res= stmt->executeQuery("Select * from messages");
std::string tmp;
while (res->next()) {
tmp = res->getString(7);
std::cout << tmp;
}
}
The MySQL-Field is created with the following (shortened) command:
std::string msgs = "CREATE TABLE if not Exists `"+dbnam+e"`.`messages` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`message` LONGTEXT CHARACTER SET utf32 COLLATE utf32_bin NOT NULL ,
UNIQUE (`id`)) ENGINE = InnoDB;;
stmt->execute(msgs);"
Thanks for your help in advance.

What is segmentation fault in C++ mysql?

I am using C++ to send MySQL commands to a server. Why does it say "Segmentation fault" and what does it mean?
The output of the program when I run it is:
connection Succeeded
Segmentation fault
Edited the code to remove the i+1 line but still having same problem
the code:
#include <stdlib.h>
#include <iostream>
#include <mysql.h>
#include <stdio.h>
#define SERVER "localhost"
#define USER "root"
#define PASSWORD "coursework"
int main()
{
MYSQL *connect;
connect=mysql_init(NULL);
if (!connect)
{
std::cout<<"MySQL Initialization failed";
return 1;
}
connect=mysql_real_connect(connect, SERVER, USER, PASSWORD, NULL ,0,NULL,0);
if (connect)
{
std::cout<<"connection Succeeded\n";
}
else
{
std::cout<<"connection failed\n";
exit(1);
}
mysql_query (connect, "drop database if exists TTU;");
mysql_query (connect,"create database TTU;");
mysql_query (connect, "use TTU;");
mysql_query (connect, "create table students(tnumber char(8) PRIMARY KEY,");
mysql_query (connect, "firstname varchar(20) NOT NULL,");
mysql_query (connect, "lastname varchar(20) NOT NULL,");
mysql_query (connect, "dateofbirth date,");
mysql_query (connect, "INDEX 'lastname_i' ('lastname'),");
mysql_query (connect, "ENGINE=INNODB);");
mysql_query (connect, "insert into students(tnumber, firstname, lastname, dateofbirth)");
mysql_query (connect, "values (00001234, Joe, Smith, 1950-08-12);");
MYSQL_RES *res_set;
MYSQL_ROW row;
mysql_query (connect,"select * from students;");
unsigned int i =0;
res_set = mysql_store_result(connect);
unsigned int numrows = mysql_num_rows(res_set);
while ((row= mysql_fetch_row(res_set)) != NULL )
{
std::cout << row[i] << std::endl;
}
mysql_close (connect);
std::cout << "TEST" << std::endl;
return 0;
}
There are multiple bugs in the shown code. There are two major bugs: Incorrect usage of mysql_query(), and repeated failures to check the return value from various MySQL library functions, resulting in a failure to detect various errors.
mysql_query (connect, "create table students(tnumber char(8) PRIMARY KEY,");
This is not valid SQL. mysql_query() takes a complete SQL statement and executes it. It doesn't take individual fragments of an SQL statement, broken down across multiple consecutive invocations, and only executes it once a complete SQL statement gets parsed.
Because the shown code fails to check the return value from mysql_query(), it fails to detect that this whole sequence of mysql_query() calls have failed, and the table did not get created.
Subsequent call also fails to check the return value from other MySQL library functions.
The shown code is likely getting a NULL back from mysql_store_result(), fails to check for it, passes a NULL pointer to mysql_num_rows(), and crashes.

how to pass file path in run time in sql statement using C programming

I am trying to merge two SQL databases with same schema using C programming. As I am new to SQL, I tried the below code to merge using C programming.
But I could not merge it as I could not pass the file path in the sql statement in runtime. How to pass the file path in the below sql statement so that I can merge.
/Create SQL statement to merge two DBs/
rc = sqlite3_open(argv[1], &db); /*-----> argv[1] is old_student.db*/
if( rc )
{
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else
{
printf("Opened database successfully\n");
}
/*Create SQL statement to merge two DBs*/
sql = "attach '<file_path_name>' as toMerge;\ /*--------> how to pass file path here in runtime */
BEGIN;\
insert or ignore into student_table (Name, AGE, Address)\
select Name, AGE, Address from toMerge.student_table;\
COMMIT;";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK )
{
printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
printf("%s merged New_student.db successfully with old_student.db\n");
}
sqlite3_close(db);
Please let me know how should i pass the parameter in SQL statement in runtime.
Your help is highly appreciated.
Thanks in advance.
This can be accomplished by declaring the sql array as a VLA, and then using sprintf to construct the query as shown below.
static const char *prefix = "attach '";
static const char *suffix =
"' as toMerge;"
" BEGIN;"
" insert or ignore into student_table (Name, AGE, Address)"
" select Name, AGE, Address from toMerge.student_table;"
" COMMIT;";
int length = strlen(prefix) + strlen(argv[1]) + strlen(suffix) + 1;
char sql[length];
sprintf( sql, "%s%s%s", prefix, argv[1], suffix );

Simple C++ connection to mysql database

I just started with c++, after reading some tutorials I'm trying to create a simple myqsl connection with a database. Keep in mind that I'm mainly a web developer hence this is a new thing for me. My current code (taken from a tutorial) is this:
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>
//#include <string>
using namespace std;
struct connection_details
{
char *server;
char *user;
char *password;
char *database;
};
MYSQL* mysql_connection_setup(struct connection_details mysql_details) {
MYSQL *connection = mysql_init(NULL);
if (!mysql_real_connect(connection,mysql_details.server, mysql_details.user, mysql_details.password, mysql_details.database, 0, NULL, 0)) {
printf("Conection error : %s\n", mysql_error(connection));
exit(1);
}
return connection;
}
MYSQL_RES* mysql_perform_query(MYSQL *connection, char *sql_query) {
if (mysql_query(connection, sql_query)) {
printf("MySQL query error : %s\n", mysql_error(connection));
exit(1);
}
return mysql_use_result(connection);
}
int main() {
MYSQL *conn; // the connection
MYSQL_RES *res; // the results
MYSQL_ROW row; // the results row (line by line)
struct connection_details mysqlD;
mysqlD.server = "localhost"; // line 46
mysqlD.user = "root"; // line 47
mysqlD.password = "root"; // line 48
mysqlD.database = "backseat"; // line 49
conn = mysql_connection_setup(mysqlD);
res = mysql_perform_query(conn, "show tables"); // line 53
printf("MySQL Tables in mysql database:\n");
while ((row = mysql_fetch_row(res)) !=NULL)
printf("%s\n", row[0]);
mysql_free_result(res);
mysql_close(conn);
return 0;
}
however g++ gives me the followign warning:
main.cpp:46:19: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
main.cpp:47:17: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
main.cpp:48:21: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
main.cpp:49:21: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
main.cpp:53:48: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Which is beyond my understanding at the time being, although the program works.
What I did in order to remove the warning was the following:
char s[] = "localhost";
char u[] = "root";
char p[] = "root";
char d[] = "backseat";
and then
mysqlD.server = s;
mysqlD.user = u;
mysqlD.password = p;
mysqlD.database = d;
That took care of the warning but I assume there must be a better way & explanation on how I should take care of this.
Moreover I tried to use:
struct connection_details
{
string *server;
string *user;
string *password;
string *database;
};
But that didn't take care the warning on line 53 plus all hell broke loose after doing this.
My question is how should I be approaching simple mysql connection, why did those warnings came up and was it a bad idea to use strings in the struct?
I'm sorry for the long post, but I'm on a learning curve here and there a lot of things to take in.
PS: Any good tutorials / guides will be appreciated. Thank you.
You're missing some basics. I don't know about good tutorials to link to, but I'll explain what you're doing wrong:
First of all, when you're writing C++, you should use string rather than char*. You should read up the details of char*; it's a pointer and handling it is therefore significantly different from handling a string.
What happens in your code is that you assign the address of a string literal like "localhost" to a char* (because it's a pointer). The problem is, gcc converts string literals to constants, which are read-only (as the warning tells you). The type char* however implies that the contents of the string can be changed. Change the type to const char* to get rid of the warning.
Here's some code to illustrate the issue:
char* a;
const char* b;
a = "foo";
b = "bar";
a[0] = 'a'; // you're changing the constant string!
b[0] = 'b'; // compiler error; b is const
a = "foz"; // both allowed because you're not changing
b = "baz"; // the string, but only the pointer
The easier option is to use string:
struct connection_details
{
string server;
string user;
string password;
string database;
}
You shouldn't use pointers here. string manages the low-level memory stuff internally. Now the problem is that you're using the C-level MySQL binding, which expects char* rather than string. To call it when you're using strings in your code, use c_str() for conversion:
connection.server.c_str()
and so on.
Alternatively, you might want to use a higher-level C++ API to connect to MySQL. If you're learning C++, you'll be better off avoiding C APIs if possible. There is, for example, MySQL++, but I don't know it and therefore cannot recommend it. A higher-level API is provided by SOCI, which works with multiple database backends (including MySQL).