Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_main_sqlite3_create_window_function.c222 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6#include <stdint.h>7 8/* Unity fixtures */9void setUp(void) {10  /* Setup code here, or leave empty */11}12void tearDown(void) {13  /* Cleanup code here, or leave empty */14}15 16/* A simple window/aggregate function: sum of the first argument (ignores NULLs) */17typedef struct SumCtx {18  sqlite3_int64 sum;19} SumCtx;20 21static void winSumStep(sqlite3_context *ctx, int argc, sqlite3_value **argv){22  SumCtx *p = (SumCtx*)sqlite3_aggregate_context(ctx, (int)sizeof(SumCtx));23  if( p==0 ) return;24  if( argc>0 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){25    p->sum += sqlite3_value_int64(argv[0]);26  }27}28static void winSumFinal(sqlite3_context *ctx){29  SumCtx *p = (SumCtx*)sqlite3_aggregate_context(ctx, 0);30  sqlite3_result_int64(ctx, p ? p->sum : 0);31}32static void winSumValue(sqlite3_context *ctx){33  SumCtx *p = (SumCtx*)sqlite3_aggregate_context(ctx, 0);34  sqlite3_result_int64(ctx, p ? p->sum : 0);35}36static void winSumInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv){37  SumCtx *p = (SumCtx*)sqlite3_aggregate_context(ctx, 0);38  if( p==0 ) return;39  if( argc>0 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){40    p->sum -= sqlite3_value_int64(argv[0]);41  }42}43 44/* Destructor that increments an integer counter pointed to by p */45static void xDestroyCounter(void *p){46  if( p ){47    int *pc = (int*)p;48    (*pc)++;49  }50}51 52/* Helper: create :memory: db */53static sqlite3* open_memory_db(void){54  sqlite3 *db = 0;55  int rc = sqlite3_open(":memory:", &db);56  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);57  TEST_ASSERT_NOT_NULL(db);58  return db;59}60 61/* Helper: create a simple table and insert 1,2,3 */62static void create_sample_table(sqlite3 *db){63  char *err = 0;64  int rc = sqlite3_exec(db, "CREATE TABLE t(x INTEGER);"65                            "INSERT INTO t(x) VALUES(1),(2),(3);", 0, 0, &err);66  if( rc!=SQLITE_OK ){67    if( err ) sqlite3_free(err);68  }69  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);70}71 72/* Helper: run SELECT <fname>(x) FROM t and return single integer result */73static sqlite3_int64 run_aggregate_over_t(sqlite3 *db, const char *fname){74  char sql[256];75  sqlite3_stmt *stmt = 0;76  sqlite3_int64 result = 0;77  snprintf(sql, sizeof(sql), "SELECT %s(x) FROM t;", fname);78  int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);79  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");80  rc = sqlite3_step(stmt);81  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "expected one row");82  result = sqlite3_column_int64(stmt, 0);83  rc = sqlite3_step(stmt);84  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_DONE, rc, "expected done");85  sqlite3_finalize(stmt);86  return result;87}88 89/* Test 1: Successful registration and use; destructor called on close */90static void test_sqlite3_create_window_function_success_and_use(void){91  sqlite3 *db = open_memory_db();92  create_sample_table(db);93 94  int *destroyCount = (int*)sqlite3_malloc(sizeof(int));95  TEST_ASSERT_NOT_NULL(destroyCount);96  *destroyCount = 0;97 98  int rc = sqlite3_create_window_function(99    db, "winsum", 1, SQLITE_UTF8, destroyCount,100    winSumStep, winSumFinal, winSumValue, winSumInverse, xDestroyCounter101  );102  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);103 104  /* Use as aggregate to verify registration worked */105  sqlite3_int64 agg = run_aggregate_over_t(db, "winsum");106  TEST_ASSERT_EQUAL_INT64(6, agg);107 108  /* xDestroy should not have been called yet */109  TEST_ASSERT_EQUAL_INT(0, *destroyCount);110 111  rc = sqlite3_close(db);112  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);113 114  /* xDestroy should be called once on db close */115  TEST_ASSERT_EQUAL_INT(1, *destroyCount);116 117  sqlite3_free(destroyCount);118}119 120/* Test 2: Re-registering same function name triggers old destructor immediately */121static void test_sqlite3_create_window_function_replace_triggers_destroy(void){122  sqlite3 *db = open_memory_db();123 124  int *destroyA = (int*)sqlite3_malloc(sizeof(int));125  int *destroyB = (int*)sqlite3_malloc(sizeof(int));126  TEST_ASSERT_NOT_NULL(destroyA);127  TEST_ASSERT_NOT_NULL(destroyB);128  *destroyA = 0;129  *destroyB = 0;130 131  int rc = sqlite3_create_window_function(132    db, "repfn", 1, SQLITE_UTF8, destroyA,133    winSumStep, winSumFinal, winSumValue, winSumInverse, xDestroyCounter134  );135  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);136  TEST_ASSERT_EQUAL_INT(0, *destroyA);137 138  /* Replace with a new registration under the same name */139  rc = sqlite3_create_window_function(140    db, "repfn", 1, SQLITE_UTF8, destroyB,141    winSumStep, winSumFinal, winSumValue, winSumInverse, xDestroyCounter142  );143  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);144 145  /* Old destructor should have been invoked exactly once */146  TEST_ASSERT_EQUAL_INT(1, *destroyA);147  TEST_ASSERT_EQUAL_INT(0, *destroyB);148 149  rc = sqlite3_close(db);150  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);151 152  /* New registration's destructor should be invoked at close */153  TEST_ASSERT_EQUAL_INT(1, *destroyB);154 155  sqlite3_free(destroyA);156  sqlite3_free(destroyB);157}158 159/* Test 3: Error path - missing xStep should fail and call xDestroy immediately */160static void test_sqlite3_create_window_function_error_calls_destroy(void){161  sqlite3 *db = open_memory_db();162 163  int *destroyCount = (int*)sqlite3_malloc(sizeof(int));164  TEST_ASSERT_NOT_NULL(destroyCount);165  *destroyCount = 0;166 167  /* Pass NULL xStep to force an error */168  int rc = sqlite3_create_window_function(169    db, "badwin", 1, SQLITE_UTF8, destroyCount,170    NULL,                     /* xStep = NULL -> invalid */171    winSumFinal,172    winSumValue,173    winSumInverse,174    xDestroyCounter175  );176  TEST_ASSERT(rc != SQLITE_OK);177  /* xDestroy should be called immediately on failure */178  TEST_ASSERT_EQUAL_INT(1, *destroyCount);179 180  rc = sqlite3_close(db);181  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);182 183  sqlite3_free(destroyCount);184}185 186/* Test 4: Varargs (-1) allows calling with extra args (ignored by our impl) */187static void test_sqlite3_create_window_function_varargs(void){188  sqlite3 *db = open_memory_db();189  create_sample_table(db);190 191  int rc = sqlite3_create_window_function(192    db, "winsum_var", -1, SQLITE_UTF8, NULL,193    winSumStep, winSumFinal, winSumValue, winSumInverse, NULL194  );195  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);196 197  /* Use with 2 args; our step ignores extras and sums first arg only */198  sqlite3_stmt *stmt = 0;199  const char *sql = "SELECT winsum_var(x, 99) FROM t;";200  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);201  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);202 203  rc = sqlite3_step(stmt);204  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);205  sqlite3_int64 v = sqlite3_column_int64(stmt, 0);206  TEST_ASSERT_EQUAL_INT64(6, v);207  rc = sqlite3_step(stmt);208  TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);209  sqlite3_finalize(stmt);210 211  rc = sqlite3_close(db);212  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);213}214 215int main(void){216  UNITY_BEGIN();217  RUN_TEST(test_sqlite3_create_window_function_success_and_use);218  RUN_TEST(test_sqlite3_create_window_function_replace_triggers_destroy);219  RUN_TEST(test_sqlite3_create_window_function_error_calls_destroy);220  RUN_TEST(test_sqlite3_create_window_function_varargs);221  return UNITY_END();222}