Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLite3: Insert BLOB with NULL characters in C++

I'm working on the development of a C++ API which uses custom-designed plugins to interface with different database engines using their APIs and specific SQL syntax.

Currently, I'm attempting to find a way of inserting BLOBs, but since NULL is the terminating character in C/C++, the BLOB becomes truncated when constructing the INSERT INTO query string. So far, I've worked with

//...
char* sql;
void* blob;
int len;
//...
blob = some_blob_already_in_memory;
len = length_of_blob_already_known;
sql = sqlite3_malloc(2*len+1);
sql = sqlite3_mprintf("INSERT INTO table VALUES (%Q)", (char*)blob);
//...

I expect that, if it is at all possible to do it in the SQLite3 interactive console, it should be possible to construct the query string with properly escaped NULL characters. Maybe there's a way to do this with standard SQL which is also supported by SQLite SQL syntax?

Surely someone must have faced the same situation before. I've googled and found some answers but were in other programming languages (Python).

Thank you in advance for your feedback.

like image 788
jbatista Avatar asked Jan 01 '09 11:01

jbatista


3 Answers

Thank you all again for your feedback. This time I'm reporting how I solved the problem with the help of the indications provided here. Hopefully this will help others in the future.

As suggested by the first three posters, I did use prepared statements — additionally because I was also interested in getting the columns' data types, and a simple sqlite3_get_table() wouldn't do.

After preparing the SQL statement in the form of the following constant string:

INSERT INTO table VALUES(?,?,?,?);

it remains the binding of the corresponding values. This is done by issuing as many sqlite3_bind_blob() calls as the columns. (I also resorted to sqlite3_bind_text() for other "simple" data types because the API I'm working on can translate integers/doubles/etc into a string). So:

#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
/* ... */
void* blobvalue[4] = { NULL, NULL, NULL, NULL };
int blobsize[4] = { 0, 0, 0, 0 };
const char* tail = NULL;
const char* sql = "INSERT INTO tabl VALUES(?,?,?,?)";
sqlite3_stmt* stmt = NULL;
sqlite3* db = NULL;
/* ... */
sqlite3_open("sqlite.db", &db);
sqlite3_prepare_v2(db,
                   sql, strlen(sql) + 1,
                   &stmt, &tail);
for(unsigned int i = 0; i < 4; i++) {
    sqlite3_bind_blob(stmt, 
                      i + 1, blobvalue[i], blobsize[i], 
                      SQLITE_TRANSIENT);
}
if(sqlite3_step(stmt) != SQLITE_DONE) {
    printf("Error message: %s\n", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
sqlite3_close(db);

Note also that some functions (sqlite3_open_v2(), sqlite3_prepare_v2()) appear on the later SQLite versions (I suppose 3.5.x and later).

The SQLite table tabl in file sqlite.db can be created with (for example)

CREATE TABLE tabl(a TEXT PRIMARY KEY, b TEXT, c TEXT, d TEXT);
like image 54
jbatista Avatar answered Sep 21 '22 08:09

jbatista


You'll want to use this function with a prepared statement.

int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));

In C/C++, the standard way of dealing with NULLs in strings is to either store the beginning of the string and a length, or store a pointer to the beginning of a string and one to the end of the string.

like image 31
Eclipse Avatar answered Sep 20 '22 08:09

Eclipse


You want to precompile the statement sqlite_prepare_v2(), and then bind the blob in using sqlite3_bind_blob(). Note that the statement you bind in will be INSERT INTO table VALUES (?).

like image 30
Louis Gerbarg Avatar answered Sep 21 '22 08:09

Louis Gerbarg