I try to save Data in my esp32. In my table there should be a primary key so that I can´t save something twice with the same id. But I get an error
The Error Code:
SQL error: disk I/O error
That´s the whole Code :
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "esp_timer.h"
#include "sqlite3.h"
static const char *TAG = "sqlite3_spiffs";
const char* data = "Callback function called";
static int callback(void *data, int argc, char **argv, char **azColName) {
int i;
printf("%s: ", (const char*)data);
for (i = 0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int db_open(const char *filename, sqlite3 **db) {
int rc = sqlite3_open(filename, db);
if (rc) {
printf("Can't open database: %s\n", sqlite3_errmsg(*db));
return rc;
} else {
printf("Opened database successfully\n");
}
return rc;
}
char *zErrMsg = 0;
int db_exec(sqlite3 *db, const char *sql) {
printf("%s\n", sql);
int64_t start = esp_timer_get_time();
int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if (rc != SQLITE_OK) {
printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
printf("Operation done successfully\n");
}
printf("Time taken: %lld\n", esp_timer_get_time()-start);
return rc;
}
void setup()
{
sqlite3 *db1;
int rc;
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
//.partition_label = "storage",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
// remove existing file
unlink("/spiffs/test1.db");
unlink("/spiffs/test2.db");
sqlite3_initialize();
if (db_open("/spiffs/test1.db", &db1))
return;
rc = db_exec(db1, "CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "INSERT INTO entries VALUES ('ROOMNAME', 'HH180L23G', 'general');");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "SELECT * FROM entries");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
sqlite3_close(db1);
// All done, unmount partition and disable SPIFFS
esp_vfs_spiffs_unregister(NULL);
ESP_LOGI(TAG, "SPIFFS unmounted");
//while(1);
}
void loop() {
// put your main code here, to run repeatedly:
}
I have ideas like that it has something to do with spiffs or with the mounting ..
I tried both partition.labels .. it´s the same error ..
I can´t figure out why it doesn´t let me take a primary key .. the same thing as I can´t implement unique Text or something I get the same error..
Did someone have experienced it before ?
It would be such a huge support
Log Monitor:
Opened database successfully
CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)
SQL error: disk I/O error
Time taken: 179044
Looks like a known problem: https://github.com/siara-cc/esp32_arduino_sqlite3_lib/issues/18
Somebody has posted a workaround/fix, which doesn't look very promising to me - they explicitly specify the label of the SPIFFS partition when mounting it. Try it, see if it works.
https://github.com/siara-cc/esp32-idf-sqlite3/issues/13
Related
I want to backup the SQLite database from my file system to in memory database using C++. I read this website and try the example 1. It compiled perfectly but I get an error "Segmentation Fault". The code looks like this:
Ps: The last line printf("foo1"); is not printed
#include <stdio.h>
#include <sqlite3.h>
#include <nlohmann/json.hpp>
using namespace std;
using json = nlohmann::json;
static int callback(void *NotUsed, int count, char **data, char **column) {
int i;
int lastEntry = count - 1;
printf("{");
for (i=0; i<count; i++) {
if (i == lastEntry ) {
printf("\"%s\" : \"%s\"", column[i], data[i] ? data[i] : "NULL");
} else {
printf("\"%s\" : \"%s\",", column[i], data[i] ? data[i] : "NULL");
}
}
printf("}\n");
return 0;
}
int loadOrSaveDb(sqlite3 *pFile, const char *zFilename, int isSave){
int rc; /* Function return code */
sqlite3 *pInMemory; /* Database connection opened on zFilename */
sqlite3_backup *pBackup; /* Backup object used to copy data */
sqlite3 *pTo; /* Database to copy to (pFile or pInMemory) */
sqlite3 *pFrom; /* Database to copy from (pFile or pInMemory) */
/* Open the database file identified by zFilename. Exit early if this fails
** for any reason. */
rc = sqlite3_open(zFilename, &pFile);
if( rc==SQLITE_OK ){
pFrom = (isSave ? pInMemory : pFile);
pTo = (isSave ? pFile : pInMemory);
pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if( pBackup ){
(void)sqlite3_backup_step(pBackup, -1);
(void)sqlite3_backup_finish(pBackup);
}
printf("foo");
rc = sqlite3_errcode(pTo);
}
/* Close the database connection opened on database file zFilename
** and return the result of this function. */
(void)sqlite3_close(pFile);
return rc;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
string sql;
/* Open Database */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL Statement */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"name TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Table created successfully\n");
}
/* Insert SQL statement */
sql = "INSERT INTO COMPANY " \
"VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
"INSERT INTO COMPANY " \
"VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); " \
"INSERT INTO COMPANY " \
"VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );" \
"INSERT INTO COMPANY " \
"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Records created successfully\n");
}
/* Select SQL statement */
//sql = "SELECT * from COMPANY WHERE NAME LIKE 'Paul'";
sql = "SELECT * from COMPANY";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Operation done successfully\n");
}
loadOrSaveDb(db, "test.db", 0);
sqlite3_close(db);
printf("foo1");
return 0;
}
The destination for sqlite3_backup_init has to be a database connection handle. In order to use sqlite3 *pInMemory; as the destination, you should initialise it with such a handle:
sqlite3 *pInMemory; /* Database connection opened on zFilename */
sqlite3_open(":memory:", &pInMemory);
I need to execute sql command "select" and return some data from the result of it. I'm trying to do it with sqlite3_exec, but it's only writting in stdout. What I need to do to write the data in array or something like this?
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
void my_exec(char * sql) {
sqlite3 *db;
char *zErrMsg = nullptr;
int rc;
//char * sql;
/* Open database */
rc = sqlite3_open("data_base.db", &db);
if (rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
} else {
fprintf(stdout, "Opened database successfully\n");
}
/* Create SQL statement */
// sql
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, nullptr, &zErrMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Success\n");
}
/* Close database */
sqlite3_close(db);
}
Let's look at the fourth parameter of sqlite3_exec(). This is a pointer that is passed to the callback function. Give sqlite3_exec() a pointer to your data structure and store the results to that pointer in the callback.
You can for example use a vector:
std::vector<std::pair<std::string, std::string>> vec;
rc = sqlite3_exec(db, sql, callback, &vec, &zErrMsg);
The callback:
static int callback(void *dataPtr, int argc, char **argv, char **azColName){
auto vec = static_cast<std::vector<std::pair<std::string, std::string>>*>(dataPtr);
int i;
for(i=0; i<argc; i++){
vec->push_back({azColName[i], argv[i] ? argv[i] : "NULL"});
}
return 0;
}
I'm trying to compare all column data against a const char to see if the name in the column matches the player name that is connected to the game server. The comparison in the spaghetticode below does not work properly and results in new rows being made even if the player name already exists in the sqlite database. What is the correct way to do this comparison? Thanks in advance for your help.
Here's the snippet with the problematic section:
if(enable_sqlite_db) {
sqlite3_stmt *stmt;
sqlite3 *db;
char *zErrMsg = 0;
int rc;
const char *sql;
bool name_match = false;
const char* player_database_names;
char *p_name = ci->name;
char *p_ip = ci->ip;
rc = sqlite3_open("playerinfo.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
if(sql_console_msgs) fprintf(stdout, "Opened database successfully\n");
}
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL Database Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
defformatstring(sqlstrprep)("SELECT NAME FROM PLAYERINFO");
rc = sqlite3_prepare_v2(db, sqlstrprep, -1, &stmt, NULL);
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, SQLITE_ROW);
int columns = sqlite3_column_count(stmt);
if(sql_console_msgs) out(ECHO_CONSOLE, "-- id: %d row: %d columns: %d", id, SQLITE_ROW, columns);
player_database_names = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
}
if(player_database_names == p_name) name_match = true;
else if(player_database_names != p_name) name_match = false;
}
Here is the full code:
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
bool sql_console_msgs = true;
void QServ::savestats(clientinfo *ci)
{
if(enable_sqlite_db) {
sqlite3_stmt *stmt;
sqlite3 *db;
char *zErrMsg = 0;
int rc;
const char *sql;
bool name_match = false;
const char* player_database_names;
char *p_name = ci->name;
char *p_ip = ci->ip;
rc = sqlite3_open("playerinfo.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
if(sql_console_msgs) fprintf(stdout, "Opened database successfully\n");
}
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL Database Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
defformatstring(sqlstrprep)("SELECT NAME FROM PLAYERINFO");
//defformatstring(sqlstrprep)("SELECT group_concat(NAME) FROM PLAYERINFO");
rc = sqlite3_prepare_v2(db, sqlstrprep, -1, &stmt, NULL);
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
//sometimes returns incorrectly - keeps creating undesired new rows firstguy, secondguy, firstguy
int id = sqlite3_column_int(stmt, SQLITE_ROW);
int columns = sqlite3_column_count(stmt);
if(sql_console_msgs) out(ECHO_CONSOLE, "-- id: %d row: %d columns: %d", id, SQLITE_ROW, columns);
player_database_names = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
//if (std::to_string(player_database_names).find(p_name) != std::string::npos) name_match = true;
//else name_match = false;
if(sql_console_msgs) out(ECHO_CONSOLE, "-- player db names: %s", player_database_names);
}
//if(!strcmp(player_database_names, p_name)) name_match = true;
//else if(strcmp(player_database_names, p_name)) name_match = false;
if(player_database_names == p_name) name_match = true;
else if(player_database_names != p_name) name_match = false;
}
sql = "CREATE TABLE IF NOT EXISTS PLAYERINFO(" \
"NAME TEXT NOT NULL," \
"FRAGS INT NOT NULL," \
"DEATHS INT NOT NULL," \
"FLAGS INT NOT NULL," \
"PASSES INT NOT NULL," \
"IP TEXT NOT NULL," \
"ACCURACY DECIMAL(4, 2) NOT NULL," \
"KPD DECIMAL(4, 2) NOT NULL);";
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQLITE3 ERROR # CREATE TABLE IF NOT EXISTS: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
if(sql_console_msgs) {
if(!name_match) fprintf(stdout, "-- No previous record found under that name\n");
else fprintf(stdout, "-- Found name already, updating record instead\n");
}
}
char sqlINSERT[500];
char sqlUPDATE[1000];
int p_frags = ci->state.frags;
int p_deaths = ci->state.deaths;
int p_flags = ci->state.flags;
int p_passes = ci->state.passes;
int p_acc = (ci->state.damage*100)/max(ci->state.shotdamage, 1);
int p_kpd = (ci->state.frags)/max(ci->state.deaths, 1);
//name is different
if(!name_match) {
snprintf(sqlINSERT, 500, "INSERT INTO PLAYERINFO( NAME,FRAGS,DEATHS,FLAGS,PASSES,IP,ACCURACY,KPD ) VALUES (\"%s\", %d, %d, %d, %d, \"%s\", %d, %d)",p_name,p_frags,p_deaths,p_flags,p_passes,p_ip,p_acc,p_kpd);
//sqlEscape(sqlINSERT);
rc = sqlite3_exec(db, sqlINSERT, callback, 0, &zErrMsg);
}
//client name matches db record, update db if new info is > than db info
else if(name_match) {
snprintf(sqlUPDATE, 10000,
"UPDATE PLAYERINFO SET FRAGS = %d+(SELECT FRAGS FROM PLAYERINFO) WHERE NAME = \"%s\";" \
"UPDATE PLAYERINFO SET DEATHS = %d+(SELECT DEATHS FROM PLAYERINFO) WHERE NAME = \"%s\";" \
"UPDATE PLAYERINFO SET FLAGS = %d+(SELECT FLAGS FROM PLAYERINFO) WHERE NAME = \"%s\";" \
"UPDATE PLAYERINFO SET PASSES = %d+(SELECT PASSES FROM PLAYERINFO) WHERE NAME = \"%s\";" \
"UPDATE PLAYERINFO SET ACCURACY = %d+(SELECT ACCURACY FROM PLAYERINFO) WHERE NAME = \"%s\";" \
"UPDATE PLAYERINFO SET KPD = %d+(SELECT KPD FROM PLAYERINFO) WHERE NAME = \"%s\";",
ci->state.frags, ci->name, ci->state.deaths, ci->name, ci->state.flags, ci->name, ci->state.passes, ci->name, p_acc, ci->name, p_kpd, ci->name);
//sqlEscape(sqlUPDATE);
rc = sqlite3_exec(db, sqlUPDATE, callback, 0, &zErrMsg);
}
if( rc != SQLITE_OK ){
fprintf(stderr, "SQLITE3 ERROR # INSERT & UPDATE: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
if(sql_console_msgs) fprintf(stdout, "Playerinfo modified\n");
}
sqlite3_close(db);
}
}
void QServ::getstats(clientinfo *ci)
{
if(enable_sqlite_db) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
rc = sqlite3_open("playerinfo.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL Database Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
sqlite3_stmt *stmt;
defformatstring(sqlstrprep)("SELECT NAME,FRAGS,ACCURACY,KPD FROM PLAYERINFO WHERE NAME == \"%s\";", ci->name);
rc = sqlite3_prepare_v2(db, sqlstrprep, -1, &stmt, NULL);
bool necho = false;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
const char* allfrags = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* avgacc = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
const char* avgkpd = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
if(!necho) {
if(avgacc == NULL) out(ECHO_SERV, "Name: \f0%s\f7, Total Frags: \f3%s\f7, Average KPD: \f6%s", name, allfrags, avgkpd);
else if(avgkpd == NULL) out(ECHO_SERV, "Name: \f0%s\f7, Total Frags: \f3%s\f7, Average Accuracy: \f2%s%%", name, allfrags, avgacc);
else out(ECHO_SERV, "Name: \f0%s\f7, Total Frags: \f3%s\f7, Average Accuracy: \f2%s%%\f7, Average KPD: \f6%s", name,allfrags,avgacc,avgkpd);
necho = true;
}
}
}
sqlite3_close(db);
}
}
void QServ::getnames(clientinfo *ci) {
if(enable_sqlite_db) {
sqlite3_stmt *stmt3;
sqlite3 *db;
int rc;
rc = sqlite3_open("playerinfo.db", &db);
defformatstring(sqlstrprep3)("SELECT group_concat(NAME, \", \") FROM PLAYERINFO WHERE IP == \"%s\";", ci->ip);
rc = sqlite3_prepare_v2(db, sqlstrprep3, -1, &stmt3, NULL);
while ((rc = sqlite3_step(stmt3)) == SQLITE_ROW) {
std::string names(reinterpret_cast<const char*>(sqlite3_column_text(stmt3, 0)));
defformatstring(nmsg)("Names from IP \f2%s\f7: %s", ci->ip, names.c_str());
out(ECHO_SERV, nmsg);
}
sqlite3_close(db);
}
}
void QServ::disconnectclient(clientinfo *ci)
{
if(enable_sqlite_db) savestats(ci);
}
I'm always amused by the fact that nobody wants to provide a minimal working example. In the end, I created one myself to show how to do this thing:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "sqlite3.h"
int main(void)
{
int rc;
sqlite3 *db;
rc = sqlite3_open("playerinfo.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}
else {
fprintf(stdout, "Opened database successfully\n");
}
char *zErrMsg = NULL;
rc = sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS PlayerInfo (id INTEGER PRIMARY KEY, name TEXT);"
"REPLACE INTO PlayerInfo(id, name) VALUES (1,'John'),(2,'Paul'),(3,'George'),(4,'Ringo'),(5,'Pete'),(6,'Stuart');",
NULL, 0, &zErrMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL Database Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
exit(EXIT_FAILURE);
}
const char *search_name = "Ringo";
bool name_match = false;
sqlite3_stmt *stmt;
char *zSql = "SELECT name FROM PlayerInfo";
sqlite3_prepare_v2(db, zSql, -1, &stmt, NULL);
while (sqlite3_step(stmt) == SQLITE_ROW) {
const char* player_database_names = sqlite3_column_text(stmt, 0);
if (strcmp(player_database_names, search_name) == 0) {
name_match = true;
break;
}
}
sqlite3_finalize(stmt);
printf("Search name: %s - Result: %s\n", search_name, name_match ? "true" : "false");
sqlite3_close(db);
return EXIT_SUCCESS;
}
The original code had a lot of very strange things. It clearly looks as if a bunch of stuff was pasted together without reason.
I never used SQLite before, but it's really very well done. So the idea is to
Open the DB (sqlite3_open()) and check if successfull.
Create and populate the DB if needed (sqlite3_exec() without a callback function).
Query the DB rows with sqlite3_prepare_v2(), sqlite3_step(), and sqlite3_finalize().
In the loop you search if the name is found.
Close the DB (sqlite3_close()).
As reported in the comments, this is not a good idea. If you want to see if something exists, query if it exists:
sqlite3_stmt *stmt;
char *zSql = "SELECT EXISTS(SELECT 1 FROM PlayerInfo WHERE name=?)";
sqlite3_prepare_v2(db, zSql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, search_name, -1, SQLITE_STATIC);
sqlite3_step(stmt);
name_match = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
In this specific case, probably the parametrized query is an overkill, but who knows where that search_name comes from...
WARNING: no effort to have decent error handling.
I am using sqlite library for C++ to work with database. I want to get return values from sqlite3_exec() function as used in below code.
For eg., if I execute a "DELETE from COMPANY where id=2;", I want to distinguish between the results of this query(it it deleted an existing row or it didn't or if the table exists or not).
All I am able to do using below code is execute the SQL statement. Is it possible to do this?
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *data, int argc, char **argv, char **azColName){
int i;
fprintf(stderr, "%s: ", (const char*)data);
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
/* Open database */
rc = sqlite3_open("test.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
/* Create merged SQL statement */
sql = "DELETE from COMPANY where ID=2; " \
"SELECT * from COMPANY";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Operation done successfully\n");
}
sqlite3_close(db);
return 0;
}
I have created a MySQL table where one of the columns stores a BLOB type. (The Internet told me BLOB is the correct data type for images.)
I am pretty much a beginner with both C++ and MySQL. What I would like to do is to write a small program with a main() that puts a jpeg into that table. For the sake of this exercise, I do not want to store a reference to a directory that contains an image.
Am I wrong to think that it is as simple as filling out the part in BLOCK 2 below?
#include <iostream>
#include <string>
#include <mysql.h>
using namespace std;
int main(int argc, char **argv)
{
//BLOCK 1: INIT
MYSQL *connection, mysql;
MYSQL_RES *result;
MYSQL_ROW row;
int query_state;
mysql_init(&mysql);
connection = mysql_real_connect(&mysql, "localhost", "root", "secret", "beginner_db",0,0,0);
//BLOCK 2: SEND QUERY
/* do something to insert image to table */
//BLOCK 3: DISPLAY QUERY RESULTS
result = mysql_store_result(connection);
/* do something with result */
//BLOCK 4: FREE
mysql_free_result(result);
mysql_close(connection);
return 0;
}
For this scenario, a good solution would be to use the mysql_stmt_send_long_data() function.
There is an example on the MySQL Manual page that I linked to, but here is a more relevant example of sending file contents:
#ifdef _WIN32
#include <windows.h>
#endif
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/scope_exit.hpp>
#include <mysql.h>
#define ARR_LEN(arr_id) ((sizeof (arr_id))/(sizeof (arr_id)[0]))
int main()
{
using namespace std;
MYSQL *pconn = mysql_init(NULL);
BOOST_SCOPE_EXIT( (pconn) ) {
mysql_close(pconn);
} BOOST_SCOPE_EXIT_END
const char *db_name = "test";
if (!mysql_real_connect(pconn, "localhost", "test", "********", db_name, 0, NULL, CLIENT_COMPRESS)) {
cerr << "Error: mysql_real_connect() failed to connect to `" << db_name << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_STMT *pinsert_into_images_stmt = mysql_stmt_init(pconn);
BOOST_SCOPE_EXIT( (pinsert_into_images_stmt) ) {
mysql_stmt_close(pinsert_into_images_stmt);
} BOOST_SCOPE_EXIT_END
const char sql1[] = "INSERT INTO images(data) VALUES (?)";
if (mysql_stmt_prepare(pinsert_into_images_stmt, sql1, strlen(sql1)) != 0) {
cerr << "Error: mysql_stmt_prepare() failed to prepare `" << sql1 << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_BIND bind_structs[] = {
{ 0 } // One for each ?-placeholder
};
unsigned long length0;
bind_structs[0].length = &length0;
bind_structs[0].buffer_type = MYSQL_TYPE_BLOB;
bind_structs[0].is_null_value = 0;
if (mysql_stmt_bind_param(pinsert_into_images_stmt, bind_structs) != 0) {
cerr << "Error: mysql_stmt_bind_param() failed." << endl;
return EXIT_FAILURE;
}
const char *file_name = "image.jpg";
FILE *fp = fopen(file_name, "rb");
BOOST_SCOPE_EXIT( (fp) ) {
fclose(fp);
} BOOST_SCOPE_EXIT_END
// Use mysql_stmt_send_long_data() to send the file data in chunks.
char buf[10*1024];
while (!ferror(fp) && !feof(fp)) {
size_t res = fread(buf, 1, ARR_LEN(buf), fp);
if (mysql_stmt_send_long_data(pinsert_into_images_stmt, 0, buf, res) != 0) {
cerr << "Error: mysql_stmt_send_long_data() failed." << endl;
return EXIT_FAILURE;
}
}
if (!feof(fp)) {
cerr << "Error: Failed to read `" << file_name << "` in its entirety." << endl;
return EXIT_FAILURE;
}
if (mysql_stmt_execute(pinsert_into_images_stmt) != 0) {
cerr << "Error: mysql_stmt_execute() failed." << endl;
return EXIT_FAILURE;
}
cout << "Inserted record #" << mysql_insert_id(pconn) << endl;
return EXIT_SUCCESS;
}
I am using the following definition of table `images`:
CREATE TABLE images (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data MEDIUMBLOB NOT NULL,
PRIMARY KEY (id)
);
Upon running this program, it successfully sent the 38,339-byte JPEG image.jpg to the server and outputted "Inserted record #1".
You can verify that the correct number of bytes were sent:
mysql> SELECT octet_length(data) FROM images WHERE id=1;
+--------------------+
| octet_length(data) |
+--------------------+
| 38339 |
+--------------------+
1 row in set (0.00 sec)
I found this solution that worked... for images under 10kb.
//http://zetcode.com/tutorials/mysqlcapitutorial/
//g++ -o output source.cpp $(mysql_config --cflags) $(mysql_config --libs)
#include <stdio.h>
#include <iostream>
#include <mysql.h>
int main(int argc, char **argv)
{
MYSQL *conn;
int len, size;
char data[1000*1024];
char chunk[2*1000*1024+1];
char query[1024*5000];
FILE *fp;
conn = mysql_init(NULL);
mysql_real_connect(conn, "localhost", "root", "secret", "beginner_db", 0, NULL, 0);
fp = fopen("filename.png", "rb");
size = fread(data, 1, 1024*1000, fp);
mysql_real_escape_string(conn, chunk, data, size);
char *stat = "INSERT INTO pic_tbl(name, pic) VALUES('cexample', '%s')";
len = snprintf(query, sizeof(stat)+sizeof(chunk) , stat, chunk);
mysql_real_query(conn, query, len);
fclose(fp);
mysql_close(conn);
}
Something like this:
CString SaveFile( CMemoryFile& File )
{
*pFileKey = -1;
SQLRETURN retcode;
SQLHSTMT hstmt;
CLoggEntryList LoggEntryList( this ); // logg entries cannot be made while busy inside the hstmt, use this class to add them later
SQLINTEGER cbDocumentBlock; // For binding the main image
long lDocumentBufferSize = 0;
unsigned char* pDocumentBuffer; // Will be set to point to the buffer that should be written into the document blob field
unsigned char pDummyChar[] = {'0'}; // Dummy buffer to write in the image/thumbnail blob fields when using external storage
lDocumentBufferSize = File.m_lBufferSize;
pDocumentBuffer = File.m_pFileBuffer;
// Allocate statement handle
retcode = SQLAllocHandle(SQL_HANDLE_STMT, m_Database.m_hdbc, &hstmt);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{ // Create a result set
CString szSQL;
szSQL = ( "INSERT INTO ObjectTable (ObjectData) VALUES ( ? )");
retcode = SQLPrepare(hstmt, (SQLCHAR*)szSQL.GetBuffer(), SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Bind the parameters. For parameter 1, pass the parameter number in ParameterValuePtr instead of a buffer address.
SQLINTEGER cbNULL = 0;
SQLINTEGER cbTEXT = SQL_NTS;
int nColumn = 1;
// Bind ObjectData
cbDocumentBlock = SQL_LEN_DATA_AT_EXEC(0); //SQL_LEN_DATA_AT_EXEC(lImageBufferSize);
retcode = SQLBindParameter(hstmt, nColumn++, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
0, 0, (SQLPOINTER) DOCUMENT, 0, &cbDocumentBlock);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Set values so data for parameter 1 will be passed at execution. Note that the length parameter in
// the macro SQL_LEN_DATA_AT_EXEC is 0. This assumes that the driver returns "N" for the
// SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo.
retcode = SQLExecute(hstmt);
const long nMaxChunkSize = 400000;
// For data-at-execution parameters, call SQLParamData to get the parameter number set by SQLBindParameter.
// Call InitUserData. Call GetUserData and SQLPutData repeatedly to get and put all data for the parameter.
// Call SQLParamData to finish processing this parameter.
while (retcode == SQL_NEED_DATA)
{
SQLPOINTER pToken;
retcode = SQLParamData(hstmt, &pToken);
switch( (int)pToken )
{
case DOCUMENT:
{
if (retcode == SQL_NEED_DATA)
{
for( int nPos = 0; nPos < lDocumentBufferSize; nPos += nMaxChunkSize )
{
int nBufferSize = min( lDocumentBufferSize - nPos, nMaxChunkSize );
SQLRETURN retcode2 = SQLPutData(hstmt, pDocumentBuffer+nPos, nBufferSize );
if (retcode2 != SQL_SUCCESS && retcode2 != SQL_SUCCESS_WITH_INFO)
{
SQLCHAR Sqlstate[6];
SQLINTEGER NativeError;
SQLCHAR MessageText[201];
SQLSMALLINT TextLengthPtr;
retcode2 = SQLGetDiagRec( SQL_HANDLE_STMT, hstmt, 1, Sqlstate, &NativeError, MessageText, 200, &TextLengthPtr );
if (retcode2 == SQL_SUCCESS || retcode2 == SQL_SUCCESS_WITH_INFO)
{
MessageText[TextLengthPtr] = 0;
Sqlstate[5] = 0;
CString szSQLState( Sqlstate );
CString szMessageText( MessageText );
CString szMessage;
szMessage.Format("Error in SaveFile(). SQL State %s. Native %ld. Source: %s", szSQLState, NativeError, szMessageText );
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
}
}
}
break;
}
default:
{
CString szMessage;
szMessage.Format("Error in SaveBuffer(). Unknown parameter buffer.");
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
break;
}
}
}
SQLRETURN retcode3;
retcode3 = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
ASSERT(retcode3 == SQL_SUCCESS);
}
}
}
This code is not tested or even compiled, but it should point you in the right direction.