#include #include #include #include #include #include #include void test_sqlite3_changes(); void test_sqlite3_bind_int64(); void test_sqlite3_bind_double(); void test_sqlite3_bind_parameter_name(); void test_sqlite3_bind_parameter_count(); void test_sqlite3_column_name(); void test_sqlite3_last_insert_rowid(); void test_sqlite3_bind_text(); void test_sqlite3_bind_text2(); void test_sqlite3_bind_blob(); void test_sqlite3_column_type(); int allocated = 0; int main(void) { test_sqlite3_changes(); test_sqlite3_bind_int64(); test_sqlite3_bind_double(); test_sqlite3_bind_parameter_name(); test_sqlite3_bind_parameter_count(); //test_sqlite3_column_name(); test_sqlite3_last_insert_rowid(); test_sqlite3_bind_text(); test_sqlite3_bind_text2(); test_sqlite3_bind_blob(); test_sqlite3_column_type(); return 0; } void test_sqlite3_changes() { sqlite3 *db; char *err_msg = NULL; int rc; rc = sqlite3_open("../../testing/testing.db", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS turso_test_changes (id INTEGER PRIMARY KEY, name TEXT);", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } rc = sqlite3_exec(db, "DELETE FROM turso_test_changes;", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } sqlite3_close(db); rc = sqlite3_open("../../testing/testing.db", &db); assert(rc == SQLITE_OK); assert(sqlite3_changes(db) == 0); rc = sqlite3_exec(db, "INSERT INTO turso_test_changes (name) VALUES ('abc');", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } assert(sqlite3_changes(db) == 1); rc = sqlite3_exec(db, "INSERT INTO turso_test_changes (name) VALUES ('def'),('ghi'),('jkl');", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } assert(sqlite3_changes(db) == 3); sqlite3_close(db); } void test_sqlite3_bind_int64() { sqlite3 *db; sqlite3_stmt *stmt; char *err_msg = NULL; int rc; rc = sqlite3_open("../../testing/testing.db", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS turso_test_int64 (id INTEGER PRIMARY KEY, value INTEGER);", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } rc = sqlite3_exec(db, "DELETE FROM turso_test_int64;", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } rc = sqlite3_prepare_v2(db, "INSERT INTO turso_test_int64 (value) VALUES (?);", -1, &stmt, NULL); assert(rc == SQLITE_OK); sqlite_int64 big_value = (sqlite_int64)9223372036854775807LL; rc = sqlite3_bind_int64(stmt, 1, big_value); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); rc = sqlite3_finalize(stmt); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "SELECT value FROM turso_test_int64 LIMIT 1;", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW); sqlite_int64 fetched = sqlite3_column_int64(stmt, 0); assert(fetched == big_value); printf("Inserted value: %lld, Fetched value: %lld\n", (long long)big_value, (long long)fetched); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_bind_double() { sqlite3 *db; sqlite3_stmt *stmt; char *err_msg = NULL; int rc; rc = sqlite3_open("../../testing/testing.db", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS turso_test_double (id INTEGER PRIMARY KEY, value REAL);", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } rc = sqlite3_exec(db, "DELETE FROM turso_test_double;", NULL, NULL, &err_msg); assert(rc == SQLITE_OK); if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; } rc = sqlite3_prepare_v2(db, "INSERT INTO turso_test_double (value) VALUES (?);", -1, &stmt, NULL); assert(rc == SQLITE_OK); double big_value = 1234567890.123456; rc = sqlite3_bind_double(stmt, 1, big_value); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); rc = sqlite3_finalize(stmt); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "SELECT value FROM turso_test_double LIMIT 1;", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW); double fetched = sqlite3_column_double(stmt, 0); assert(fabs(fetched - big_value) < 1e-9); printf("Inserted value: %.15f, Fetched value: %.15f\n", big_value, fetched); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_bind_parameter_name() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); const char *sql = "INSERT INTO test_parameter_name (value) VALUES (:val);"; rc = sqlite3_exec(db, "CREATE TABLE test_parameter_name (id INTEGER PRIMARY KEY, value INTEGER);", NULL, NULL, NULL); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); assert(rc == SQLITE_OK); const char *param_name = sqlite3_bind_parameter_name(stmt, 1); assert(param_name != NULL); printf("Parameter name: %s\n", param_name); assert(strcmp(param_name, ":val") == 0); const char *invalid_name = sqlite3_bind_parameter_name(stmt, 99); assert(invalid_name == NULL); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_bind_parameter_count() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE test_parameter_count (id INTEGER PRIMARY KEY, value TEXT);", NULL, NULL, NULL); assert(rc == SQLITE_OK); const char *sql = "INSERT INTO test_parameter_count (id, value) VALUES (?1, ?2);"; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); assert(rc == SQLITE_OK); int param_count = sqlite3_bind_parameter_count(stmt); printf("Parameter count: %d\n", param_count); assert(param_count == 2); sqlite3_finalize(stmt); rc = sqlite3_prepare_v2(db, "SELECT * FROM test_parameter_count;", -1, &stmt, NULL); assert(rc == SQLITE_OK); param_count = sqlite3_bind_parameter_count(stmt); printf("Parameter count (no params): %d\n", param_count); assert(param_count == 0); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_column_name() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE test_column_name (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);", NULL, NULL, NULL); assert(rc == SQLITE_OK); const char *sql = "SELECT id, name AS full_name, age FROM test_column_name;"; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); assert(rc == SQLITE_OK); int col_count = sqlite3_column_count(stmt); assert(col_count == 3); const char *col0 = sqlite3_column_name(stmt, 0); const char *col1 = sqlite3_column_name(stmt, 1); const char *col2 = sqlite3_column_name(stmt, 2); printf("Column 0 name: %s\n", col0); printf("Column 1 name: %s\n", col1); printf("Column 2 name: %s\n", col2); assert(strcmp(col0, "id") == 0); assert(strcmp(col1, "full_name") == 0); assert(strcmp(col2, "age") == 0); //will cause panic because get_column_name uses expect() const char *invalid_col = sqlite3_column_name(stmt, 99); assert(invalid_col == NULL); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_last_insert_rowid() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE test_last_insert (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);", NULL, NULL, NULL); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "INSERT INTO test_last_insert (name) VALUES ('first');", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_finalize(stmt); sqlite3_int64 rowid1 = sqlite3_last_insert_rowid(db); printf("first: %lld\n", (long long)rowid1); assert(rowid1 == 1); rc = sqlite3_prepare_v2(db, "INSERT INTO test_last_insert (name) VALUES ('second');", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_finalize(stmt); sqlite3_int64 rowid2 = sqlite3_last_insert_rowid(db); printf("second: %lld\n", (long long)rowid2); assert(rowid2 == 2); sqlite3_close(db); } static void custom_destructor(void *ptr) { free(ptr); allocated--; } void test_sqlite3_bind_text() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE bind_text(x TEXT)", 0, 0, 0); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "INSERT INTO bind_text VALUES (?1)", -1, &stmt, 0); assert(rc == SQLITE_OK); char *data = malloc(10); snprintf(data, 10, "leaktest"); allocated++; rc = sqlite3_bind_text(stmt, 1, data, -1, custom_destructor); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); printf("Before final allocated = %d\n", allocated); sqlite3_finalize(stmt); printf("After final allocated = %d\n", allocated); assert(allocated == 0); rc = sqlite3_prepare_v2(db, "SELECT x FROM bind_text", -1, &stmt, 0); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW); const unsigned char *text = sqlite3_column_text(stmt, 0); int len = sqlite3_column_bytes(stmt, 0); assert(text != NULL); assert(strcmp((const char *)text, "leaktest") == 0); printf("Read text: %s (len=%d)\n", text, len); assert(len == 8); sqlite3_finalize(stmt); sqlite3_close(db); printf("Test passed: no leaks detected and column text read correctly!\n"); } void test_sqlite3_bind_text2() { sqlite3 *db; sqlite3_stmt *stmt; sqlite3_stmt *check_stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE bind_text(x TEXT)", 0, 0, 0); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "INSERT INTO bind_text VALUES (?1)", -1, &stmt, 0); assert(rc == SQLITE_OK); rc = sqlite3_bind_text(stmt, 1, "hello", -1, SQLITE_TRANSIENT); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_reset(stmt); const char *long_str = "this_is_a_long_test_string_for_sqlite_bind_text_function"; rc = sqlite3_bind_text(stmt, 1, long_str, -1, SQLITE_TRANSIENT); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_reset(stmt); const char weird_str[] = {'a','b','c','\0','x','y','z'}; //bind text will terminate \0 rc = sqlite3_bind_text(stmt, 1, weird_str, sizeof(weird_str), SQLITE_TRANSIENT); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_finalize(stmt); rc = sqlite3_prepare_v2(db, "SELECT x FROM bind_text", -1, &check_stmt, 0); assert(rc == SQLITE_OK); int row = 0; while ((rc = sqlite3_step(check_stmt)) == SQLITE_ROW) { const unsigned char *val = sqlite3_column_text(check_stmt, 0); int len = sqlite3_column_bytes(check_stmt, 0); printf("Row %d: \"%.*s\" (len=%d)\n", row, len, val, len); row++; } assert(rc == SQLITE_DONE); sqlite3_finalize(check_stmt); sqlite3_close(db); printf("Test passed: bind_text handled multiple cases correctly!\n"); } void test_sqlite3_bind_blob() { sqlite3 *db; sqlite3_stmt *stmt; const char *sql = "INSERT INTO test_blob (data) VALUES (?);"; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE test_blob (data BLOB);", NULL, NULL, NULL); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); assert(rc == SQLITE_OK); unsigned char blob_data[] = {0x61, 0x62, 0x00, 0x63, 0x64}; // "ab\0cd" int blob_size = sizeof(blob_data); rc = sqlite3_bind_blob(stmt, 1, blob_data, blob_size, SQLITE_STATIC); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_DONE); sqlite3_finalize(stmt); rc = sqlite3_prepare_v2(db, "SELECT data FROM test_blob;", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW); const void *retrieved_blob = sqlite3_column_blob(stmt, 0); int retrieved_size = sqlite3_column_bytes(stmt, 0); assert(retrieved_size == blob_size); assert(memcmp(blob_data, retrieved_blob, blob_size) == 0); printf("Test passed: BLOB inserted and retrieved correctly (size=%d)\n", retrieved_size); sqlite3_finalize(stmt); sqlite3_close(db); } void test_sqlite3_column_type() { sqlite3 *db; sqlite3_stmt *stmt; int rc; rc = sqlite3_open(":memory:", &db); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "CREATE TABLE test_column_type (col_int INTEGER, col_float REAL, col_text TEXT, col_blob BLOB, col_null TEXT);", NULL, NULL, NULL); assert(rc == SQLITE_OK); rc = sqlite3_exec(db, "INSERT INTO test_column_type VALUES (42, 3.14, 'hello', x'010203', NULL);", NULL, NULL, NULL); assert(rc == SQLITE_OK); rc = sqlite3_prepare_v2(db, "SELECT col_int, col_float, col_text, col_blob, col_null FROM test_column_type;", -1, &stmt, NULL); assert(rc == SQLITE_OK); rc = sqlite3_step(stmt); assert(rc == SQLITE_ROW); for (int i = 0; i < sqlite3_column_count(stmt); i++) { int type = sqlite3_column_type(stmt, i); switch (i) { case 0: assert(type == SQLITE_INTEGER); break; case 1: assert(type == SQLITE_FLOAT); break; case 2: assert(type == SQLITE_TEXT); break; case 3: assert(type == SQLITE_BLOB); break; case 4: assert(type == SQLITE_NULL); break; } } printf("sqlite3_column_type test completed!\n"); sqlite3_finalize(stmt); sqlite3_close(db); }