diff --git a/include/SQLiteCpp/Database.h b/include/SQLiteCpp/Database.h index 1584a6e5..3a32bedb 100644 --- a/include/SQLiteCpp/Database.h +++ b/include/SQLiteCpp/Database.h @@ -336,9 +336,9 @@ class Database bool abDeterministic, void* apApp, void (*apFunc)(sqlite3_context *, int, sqlite3_value **), - void (*apStep)(sqlite3_context *, int, sqlite3_value **), - void (*apFinal)(sqlite3_context *), // NOLINT(readability/casting) - void (*apDestroy)(void *)); + void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, + void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting) + void (*apDestroy)(void *) = nullptr); /** * @brief Create or redefine a SQL function or aggregate in the sqlite database. @@ -364,12 +364,12 @@ class Database bool abDeterministic, void* apApp, void (*apFunc)(sqlite3_context *, int, sqlite3_value **), - void (*apStep)(sqlite3_context *, int, sqlite3_value **), - void (*apFinal)(sqlite3_context *), // NOLINT(readability/casting) - void (*apDestroy)(void *)) + void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, + void (*apFinal)(sqlite3_context *) = nullptr, + void (*apDestroy)(void *) = nullptr) { - return createFunction(aFuncName.c_str(), aNbArg, abDeterministic, - apApp, apFunc, apStep, apFinal, apDestroy); + createFunction(aFuncName.c_str(), aNbArg, abDeterministic, + apApp, apFunc, apStep, apFinal, apDestroy); } /** diff --git a/src/Database.cpp b/src/Database.cpp index 88a35b1e..fa848362 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -192,17 +192,17 @@ void Database::createFunction(const char* apFuncName, bool abDeterministic, void* apApp, void (*apFunc)(sqlite3_context *, int, sqlite3_value **), - void (*apStep)(sqlite3_context *, int, sqlite3_value **), - void (*apFinal)(sqlite3_context *), // NOLINT(readability/casting) - void (*apDestroy)(void *)) + void (*apStep)(sqlite3_context *, int, sqlite3_value **) /* = nullptr */, + void (*apFinal)(sqlite3_context *) /* = nullptr */, // NOLINT(readability/casting) + void (*apDestroy)(void *) /* = nullptr */) { - int TextRep = SQLITE_UTF8; + int textRep = SQLITE_UTF8; // optimization if deterministic function (e.g. of nondeterministic function random()) if (abDeterministic) { - TextRep = TextRep|SQLITE_DETERMINISTIC; + textRep = textRep | SQLITE_DETERMINISTIC; } - const int ret = sqlite3_create_function_v2(mpSQLite, apFuncName, aNbArg, TextRep, + const int ret = sqlite3_create_function_v2(mpSQLite, apFuncName, aNbArg, textRep, apApp, apFunc, apStep, apFinal, apDestroy); check(ret); } diff --git a/tests/Database_test.cpp b/tests/Database_test.cpp index b9973e3f..cade782f 100644 --- a/tests/Database_test.cpp +++ b/tests/Database_test.cpp @@ -311,7 +311,40 @@ TEST(Database, execException) EXPECT_STREQ("table test has 3 columns but 4 values were supplied", db.getErrorMsg()); } -// TODO: test Database::createFunction() +// From https://stackoverflow.com/a/8283265/1163698 How can I create a user-defined function in SQLite? +static void firstchar(sqlite3_context *context, int argc, sqlite3_value **argv) +{ + if (argc == 1) + { + const unsigned char *text = sqlite3_value_text(argv[0]); + if (text && text[0]) + { + char result[2]; + result[0] = text[0]; result[1] = '\0'; + sqlite3_result_text(context, result, -1, SQLITE_TRANSIENT); + return; + } + } + sqlite3_result_null(context); +} + +TEST(Database, createFunction) +{ + // Create a new database + SQLite::Database db(":memory:", SQLite::OPEN_READWRITE); + db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"); + + EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\")")); + EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"second\")")); + + // exception with SQL error: "no such function: firstchar" + EXPECT_THROW(db.exec("SELECT firstchar(value) FROM test WHERE id=1"), SQLite::Exception); + + db.createFunction("firstchar", 1, true, nullptr, &firstchar, nullptr, nullptr, nullptr); + + EXPECT_EQ(1, db.exec("SELECT firstchar(value) FROM test WHERE id=1")); +} + // TODO: test Database::loadExtension() #ifdef SQLITE_HAS_CODEC