diff --git a/TODO.txt b/TODO.txt index 50924e3..d0ae5cb 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,7 +6,6 @@ Update Doxygen Documentation, but remove it from the master branch Publish the Doxygen Documentation in the Github Pages (gh-pages branch) Missing test/example in v0.5.0: -- BLOB : make an example with images stored in a row - :memory: table Missing features in v0.5.0: diff --git a/example1.vcxproj b/example1.vcxproj index b559728..7c35f78 100644 --- a/example1.vcxproj +++ b/example1.vcxproj @@ -48,7 +48,7 @@ Disabled src/sqlite3;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -69,7 +69,7 @@ MaxSpeed true src/sqlite3;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDLL true diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..ba2d895 Binary files /dev/null and b/logo.png differ diff --git a/src/SQLiteC++/Column.h b/src/SQLiteC++/Column.h index 0377820..0963b3e 100644 --- a/src/SQLiteC++/Column.h +++ b/src/SQLiteC++/Column.h @@ -88,9 +88,9 @@ public: { return (SQLITE_NULL == getType()); } - + /** - * @brief Return the number of bytes used by the text value of the column + * @brief Return the number of bytes used by the text (or blob) value of the column * * Return either : * - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator @@ -100,6 +100,12 @@ public: */ int getBytes(void) const throw(); + /// Alias returning the number of bytes used by the text (or blob) value of the column + inline int size(void) const throw() + { + return getBytes (); + } + /// Inline cast operator to int inline operator int() const { diff --git a/src/SQLiteC++/Statement.cpp b/src/SQLiteC++/Statement.cpp index 6039fb3..730e922 100644 --- a/src/SQLiteC++/Statement.cpp +++ b/src/SQLiteC++/Statement.cpp @@ -79,7 +79,7 @@ void Statement::bind(const int aIndex, const char* apValue) // throw(SQLite::Exc // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const void* apValue, const int aSize) // throw(SQLite::Exception) { - int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT); + int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT); check(ret); } diff --git a/src/SQLiteC++/Statement.h b/src/SQLiteC++/Statement.h index 3879dde..4d52fa0 100644 --- a/src/SQLiteC++/Statement.h +++ b/src/SQLiteC++/Statement.h @@ -163,9 +163,14 @@ public: /** * @brief Execute a one-step query with no expected result. * - * This exec() method is for use with precompiled statement that does not fetch results (INSERT, UPDATE, DELETE...). - * It is intended for similar usage as Database::exec(), but it add the ability to bind() arguments to it. - * Combined with reusing the underlying precompiled SQLite statement, it allows better performances. + * This method is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : + * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" + * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" + * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" + * + * It is similar to Database::exec(), but using a precompiled statement, it adds : + * - the ability to bind() arguments to it (best way to insert data), + * - reusing it allows for better performances (efficent for multiple insersion). * * @see executeStep() execute a step of the prepared query to fetch one row of results * @see Database::exec() is a shortcut to execute one or multiple statements without results diff --git a/src/example1/main.cpp b/src/example1/main.cpp index 5dd18a9..260b032 100644 --- a/src/example1/main.cpp +++ b/src/example1/main.cpp @@ -60,7 +60,7 @@ private: int main (void) { - // Basic example (1/5) : + // Basic example (1/6) : try { // Open a database file in readonly mode @@ -115,7 +115,7 @@ int main (void) } //////////////////////////////////////////////////////////////////////////// - // Object Oriented Basic example (2/5) : + // Object Oriented Basic example (2/6) : try { // Open the database and compile the query @@ -132,7 +132,7 @@ int main (void) abort(); // unexpected error : abort the example program } - // The execAndGet wrapper example (3/5) : + // The execAndGet wrapper example (3/6) : try { // Open a database file in readonly mode @@ -152,7 +152,7 @@ int main (void) } //////////////////////////////////////////////////////////////////////////// - // Simple batch queries example (4/5) : + // Simple batch queries example (4/6) : try { // Open a database file in create/write mode @@ -193,7 +193,7 @@ int main (void) remove("test.db3"); //////////////////////////////////////////////////////////////////////////// - // RAII transaction example (5/5) : + // RAII transaction example (5/6) : try { // Open a database file in create/write mode @@ -260,7 +260,76 @@ int main (void) } remove("transaction.db3"); - std::cout << "everything ok, quitting" << std::endl; + //////////////////////////////////////////////////////////////////////////// + // Binary blob example (6/6) : + try + { + // Open a database file in create/write mode + SQLite::Database db("blob.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n"; + + db.exec("DROP TABLE IF EXISTS test"); + db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value BLOB)"); + + FILE* fp = fopen("logo.png", "rb"); + if (NULL != fp) + { + char buffer[16*1024]; + void* blob = &buffer; + size_t size = fread(blob, 1, 16*1024, fp); + buffer[size] = '\0'; + fclose (fp); + std::cout << "blob size=" << size << " :\n"; + + // Insert query + SQLite::Statement query(db, "INSERT INTO test VALUES (NULL, ?)"); + // Bind the blob value to the first parameter of the SQL query + query.bind(1, blob, size); + std::cout << "blob binded successfully\n"; + + // Execute the one-step query to insert the blob + int nb = query.exec (); + std::cout << "INSERT INTO test VALUES (NULL, ?)\", returned " << nb << std::endl; + } + else + { + std::cout << "file logo.png not found !\n"; + abort(); // unexpected error : abort the example program + } + + fp = fopen("out.png", "wb"); + if (NULL != fp) + { + const void* blob = NULL; + size_t size; + + SQLite::Statement query(db, "SELECT * FROM test"); + std::cout << "SELECT * FROM test :\n"; + if (query.executeStep()) + { + SQLite::Column colBlob = query.getColumn(1); + blob = colBlob.getBlob (); + size = colBlob.getBytes (); + std::cout << "row : (" << query.getColumn(0) << ", size=" << size << ")\n"; + size_t sizew = fwrite(blob, 1, size, fp); + fclose (fp); + } + } + else + { + std::cout << "file out.png not created !\n"; + abort(); // unexpected error : abort the example program + } + } + catch (std::exception& e) + { + std::cout << "SQLite exception: " << e.what() << std::endl; + abort(); // unexpected error : abort the example program + } + remove("blob.db3"); + remove("out.png"); + + std::cout << "everything ok, quitting\n"; return 0; }