AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdint.h>6 7/* Global counters for verification */8static volatile int g_destroy_count = 0;9static volatile int g_scalar_calls = 0;10static volatile int g_step_calls = 0;11static volatile int g_final_calls = 0;12 13/* Destructor used with sqlite3_create_function_v2 */14static void destructor_counter(void *p){15 int *pi = (int*)p;16 g_destroy_count++;17 if( pi ) (*pi)++;18}19 20/* Scalar: double the integer input */21static void udf_double(sqlite3_context *ctx, int argc, sqlite3_value **argv){22 g_scalar_calls++;23 int v = (argc>0) ? sqlite3_value_int(argv[0]) : 0;24 sqlite3_result_int(ctx, v*2);25}26 27/* Scalar variable-arity: return argc */28static void udf_argcount(sqlite3_context *ctx, int argc, sqlite3_value **argv){29 (void)argv;30 sqlite3_result_int(ctx, argc);31}32 33/* Aggregate SUM(x): step */34static void agg_step_sum(sqlite3_context *ctx, int argc, sqlite3_value **argv){35 g_step_calls++;36 if( argc<1 ) return;37 long long *pSum = (long long*)sqlite3_aggregate_context(ctx, sizeof(long long));38 if( pSum ){39 int v = sqlite3_value_int(argv[0]);40 *pSum += v;41 }42}43 44/* Aggregate SUM(x): final */45static void agg_final_sum(sqlite3_context *ctx){46 g_final_calls++;47 long long *pSum = (long long*)sqlite3_aggregate_context(ctx, 0);48 sqlite3_result_int64(ctx, pSum ? *pSum : 0);49}50 51/* Unity hooks */52void setUp(void) {53 g_destroy_count = 0;54 g_scalar_calls = 0;55 g_step_calls = 0;56 g_final_calls = 0;57}58 59void tearDown(void) {60 /* nothing */61}62 63/* Helper: prepare single-row/single-column integer result */64static int eval_int(sqlite3 *db, const char *zSql, int *pOut){65 sqlite3_stmt *stmt = 0;66 int rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, 0);67 if( rc!=SQLITE_OK ) return rc;68 rc = sqlite3_step(stmt);69 if( rc==SQLITE_ROW ){70 *pOut = sqlite3_column_int(stmt, 0);71 rc = sqlite3_step(stmt);72 if( rc!=SQLITE_DONE ){73 sqlite3_finalize(stmt);74 return SQLITE_ERROR;75 }76 rc = SQLITE_OK;77 }else if( rc==SQLITE_DONE ){78 /* No row returned. */79 rc = SQLITE_ERROR;80 }81 sqlite3_finalize(stmt);82 return rc;83}84 85/* Test 1: Register scalar function and call it */86void test_sqlite3_create_function_v2_scalar_basic(void){87 sqlite3 *db = 0;88 int rc = sqlite3_open(":memory:", &db);89 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);90 91 rc = sqlite3_create_function_v2(92 db, "my_double_fn", 1, SQLITE_UTF8, NULL, udf_double, NULL, NULL, NULL93 );94 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);95 96 sqlite3_stmt *stmt = 0;97 rc = sqlite3_prepare_v2(db, "SELECT my_double_fn(7);", -1, &stmt, 0);98 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);99 100 rc = sqlite3_step(stmt);101 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);102 TEST_ASSERT_EQUAL_INT(14, sqlite3_column_int(stmt, 0));103 rc = sqlite3_step(stmt);104 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);105 106 sqlite3_finalize(stmt);107 108 /* Ensure the function body was executed exactly once in this simple query */109 TEST_ASSERT_EQUAL_INT(1, g_scalar_calls);110 111 sqlite3_close(db);112}113 114/* Test 2: Variable-arity function (nArg = -1) returns argument count */115void test_sqlite3_create_function_v2_variable_arity(void){116 sqlite3 *db = 0;117 int rc = sqlite3_open(":memory:", &db);118 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);119 120 rc = sqlite3_create_function_v2(121 db, "argcount", -1, SQLITE_UTF8, NULL, udf_argcount, NULL, NULL, NULL122 );123 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);124 125 int out = -1;126 rc = eval_int(db, "SELECT argcount();", &out);127 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);128 TEST_ASSERT_EQUAL_INT(0, out);129 130 out = -1;131 rc = eval_int(db, "SELECT argcount(1,2,3,4,5);", &out);132 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);133 TEST_ASSERT_EQUAL_INT(5, out);134 135 sqlite3_close(db);136}137 138/* Test 3: xDestroy is invoked on database close */139void test_sqlite3_create_function_v2_destructor_on_close(void){140 sqlite3 *db = 0;141 int rc = sqlite3_open(":memory:", &db);142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);143 144 int user_data_mark = 0;145 146 rc = sqlite3_create_function_v2(147 db, "noop_fn", 1, SQLITE_UTF8, &user_data_mark, udf_double, NULL, NULL, destructor_counter148 );149 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);150 151 /* Close the database: xDestroy should be called exactly once */152 rc = sqlite3_close(db);153 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);154 155 TEST_ASSERT_EQUAL_INT(1, g_destroy_count);156 TEST_ASSERT_EQUAL_INT(1, user_data_mark);157}158 159/* Test 4: xDestroy is invoked when the function is redefined */160void test_sqlite3_create_function_v2_destructor_on_redefine(void){161 sqlite3 *db = 0;162 int rc = sqlite3_open(":memory:", &db);163 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);164 165 int user_a = 0;166 int user_b = 0;167 168 rc = sqlite3_create_function_v2(169 db, "redef_fn", 1, SQLITE_UTF8, &user_a, udf_double, NULL, NULL, destructor_counter170 );171 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173 /* Redefine same function name/signature */174 rc = sqlite3_create_function_v2(175 db, "redef_fn", 1, SQLITE_UTF8, &user_b, udf_double, NULL, NULL, destructor_counter176 );177 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);178 179 /* The first registration should have been destroyed upon redefinition */180 TEST_ASSERT_EQUAL_INT(1, user_a);181 TEST_ASSERT_TRUE(g_destroy_count >= 1);182 183 /* Close the DB should destroy the second registration */184 rc = sqlite3_close(db);185 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);186 187 TEST_ASSERT_EQUAL_INT(1, user_b);188 TEST_ASSERT_EQUAL_INT(2, g_destroy_count);189}190 191/* Test 5: Register and run an aggregate function implemented via xStep/xFinal */192void test_sqlite3_create_function_v2_aggregate_basic(void){193 sqlite3 *db = 0;194 int rc = sqlite3_open(":memory:", &db);195 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);196 197 rc = sqlite3_create_function_v2(198 db, "test_sum", 1, SQLITE_UTF8, NULL, NULL, agg_step_sum, agg_final_sum, NULL199 );200 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);201 202 rc = sqlite3_exec(db, "CREATE TABLE t(x INTEGER);", 0, 0, 0);203 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);204 rc = sqlite3_exec(db, "INSERT INTO t(x) VALUES(1),(2),(3);", 0, 0, 0);205 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);206 207 sqlite3_stmt *stmt = 0;208 rc = sqlite3_prepare_v2(db, "SELECT test_sum(x) FROM t;", -1, &stmt, 0);209 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);210 211 rc = sqlite3_step(stmt);212 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);213 TEST_ASSERT_EQUAL_INT(6, sqlite3_column_int(stmt, 0));214 rc = sqlite3_step(stmt);215 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);216 217 sqlite3_finalize(stmt);218 219 /* Sanity check that step/final were invoked appropriate number of times */220 TEST_ASSERT_EQUAL_INT(3, g_step_calls);221 TEST_ASSERT_EQUAL_INT(1, g_final_calls);222 223 sqlite3_close(db);224}225 226/* Main runner */227int main(void) {228 UNITY_BEGIN();229 RUN_TEST(test_sqlite3_create_function_v2_scalar_basic);230 RUN_TEST(test_sqlite3_create_function_v2_variable_arity);231 RUN_TEST(test_sqlite3_create_function_v2_destructor_on_close);232 RUN_TEST(test_sqlite3_create_function_v2_destructor_on_redefine);233 RUN_TEST(test_sqlite3_create_function_v2_aggregate_basic);234 return UNITY_END();235}