AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4 5/* Save/restore the global builtin functions hash across tests */6static FuncDefHash savedBuiltinFuncs;7 8void setUp(void) {9 /* Save current global and clear it so each test starts from a clean slate */10 savedBuiltinFuncs = sqlite3BuiltinFunctions;11 memset(&sqlite3BuiltinFunctions, 0, sizeof(sqlite3BuiltinFunctions));12}13void tearDown(void) {14 /* Restore original global */15 sqlite3BuiltinFunctions = savedBuiltinFuncs;16}17 18/* Helper to compute the hash bucket like the target function does */19static int bucket_for(const char *zName){20 int nName = sqlite3Strlen30(zName);21 return SQLITE_FUNC_HASH(zName[0], nName);22}23 24/* Minimal no-op scalar function pointer to mark validity if needed */25static void dummy_xSFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv){26 (void)ctx; (void)argc; (void)argv;27}28 29/* Test: insert into empty bucket -> becomes head, pNext==NULL, u.pHash==old head (NULL) */30void test_sqlite3InsertBuiltinFuncs_insert_into_empty_bucket(void){31 FuncDef def;32 memset(&def, 0, sizeof(def));33 def.zName = "abc";34 def.funcFlags = SQLITE_FUNC_BUILTIN;35 def.xSFunc = dummy_xSFunc;36 37 int h = bucket_for(def.zName);38 TEST_ASSERT_NULL(sqlite3BuiltinFunctions.a[h]);39 40 sqlite3InsertBuiltinFuncs(&def, 1);41 42 TEST_ASSERT_EQUAL_PTR(&def, sqlite3BuiltinFunctions.a[h]);43 TEST_ASSERT_NULL(def.pNext);44 TEST_ASSERT_NULL(def.u.pHash);45}46 47/* Test: insert into bucket with different existing name -> new head, u.pHash points to old head */48void test_sqlite3InsertBuiltinFuncs_bucket_collision_new_name_becomes_head(void){49 /* Existing head in same bucket: "abx" length 3 shares bucket with "aby" */50 FuncDef existing;51 memset(&existing, 0, sizeof(existing));52 existing.zName = "abx";53 existing.funcFlags = SQLITE_FUNC_BUILTIN;54 existing.xSFunc = dummy_xSFunc;55 56 int h = bucket_for(existing.zName);57 sqlite3BuiltinFunctions.a[h] = &existing;58 existing.u.pHash = NULL;59 existing.pNext = NULL;60 61 FuncDef newcomer;62 memset(&newcomer, 0, sizeof(newcomer));63 newcomer.zName = "aby"; /* same first char and length => same bucket, different name */64 newcomer.funcFlags = SQLITE_FUNC_BUILTIN;65 newcomer.xSFunc = dummy_xSFunc;66 67 sqlite3InsertBuiltinFuncs(&newcomer, 1);68 69 /* Newcomer should be new bucket head; its u.pHash should point to previous head */70 TEST_ASSERT_EQUAL_PTR(&newcomer, sqlite3BuiltinFunctions.a[h]);71 TEST_ASSERT_EQUAL_PTR(&existing, newcomer.u.pHash);72 TEST_ASSERT_NULL(newcomer.pNext);73 74 /* Existing head remains otherwise unchanged */75 TEST_ASSERT_NULL(existing.u.pHash);76 TEST_ASSERT_NULL(existing.pNext);77}78 79/* Test: insert new variant for an existing name -> splice into pNext chain after head */80void test_sqlite3InsertBuiltinFuncs_insert_variant_into_pNext_chain(void){81 /* Prepare existing head "foo" with one variant in pNext */82 FuncDef head, variant1;83 memset(&head, 0, sizeof(head));84 memset(&variant1, 0, sizeof(variant1));85 head.zName = "foo";86 head.funcFlags = SQLITE_FUNC_BUILTIN;87 head.xSFunc = dummy_xSFunc;88 variant1.zName = "foo";89 variant1.funcFlags = SQLITE_FUNC_BUILTIN;90 variant1.xSFunc = dummy_xSFunc;91 92 head.pNext = &variant1;93 variant1.pNext = NULL;94 95 int h = bucket_for(head.zName);96 sqlite3BuiltinFunctions.a[h] = &head;97 head.u.pHash = NULL;98 99 /* New variant to insert */100 FuncDef newcomer;101 memset(&newcomer, 0, sizeof(newcomer));102 newcomer.zName = "foo";103 newcomer.funcFlags = SQLITE_FUNC_BUILTIN;104 newcomer.xSFunc = dummy_xSFunc;105 106 sqlite3InsertBuiltinFuncs(&newcomer, 1);107 108 /* Newcomer should be spliced immediately after head, before variant1 */109 TEST_ASSERT_EQUAL_PTR(&newcomer, head.pNext);110 TEST_ASSERT_EQUAL_PTR(&variant1, newcomer.pNext);111 112 /* u.pHash should not be modified in this branch */113 TEST_ASSERT_NULL(head.u.pHash);114 /* We do not assert newcomer.u.pHash value (unspecified by API in this branch) */115}116 117/* Test: inserting multiple entries in one call, including two with same name, preserves loop order118 i.e., second insertion for the same name becomes first in the pNext chain after the head. */119void test_sqlite3InsertBuiltinFuncs_multiple_insertions_ordering(void){120 /* Existing head "bar" */121 FuncDef head;122 memset(&head, 0, sizeof(head));123 head.zName = "bar";124 head.funcFlags = SQLITE_FUNC_BUILTIN;125 head.xSFunc = dummy_xSFunc;126 head.pNext = NULL;127 head.u.pHash = NULL;128 129 int h = bucket_for(head.zName);130 sqlite3BuiltinFunctions.a[h] = &head;131 132 /* Two newcomers for the same name "bar" inserted together */133 FuncDef newcomers[2];134 memset(newcomers, 0, sizeof(newcomers));135 newcomers[0].zName = "bar";136 newcomers[0].funcFlags = SQLITE_FUNC_BUILTIN;137 newcomers[0].xSFunc = dummy_xSFunc;138 139 newcomers[1].zName = "bar";140 newcomers[1].funcFlags = SQLITE_FUNC_BUILTIN;141 newcomers[1].xSFunc = dummy_xSFunc;142 143 sqlite3InsertBuiltinFuncs(newcomers, 2);144 145 /* After first insert: head.pNext = newcomers[0]146 After second insert: head.pNext = newcomers[1], newcomers[1].pNext = newcomers[0]147 */148 TEST_ASSERT_EQUAL_PTR(&newcomers[1], head.pNext);149 TEST_ASSERT_EQUAL_PTR(&newcomers[0], newcomers[1].pNext);150 TEST_ASSERT_NULL(newcomers[0].pNext);151}152 153/* Test: mixed insertion, one new name (new bucket head), one variant for existing name */154void test_sqlite3InsertBuiltinFuncs_mixed_new_and_existing_names(void){155 /* Existing head in bucket for "qux" */156 FuncDef quxHead;157 memset(&quxHead, 0, sizeof(quxHead));158 quxHead.zName = "qux";159 quxHead.funcFlags = SQLITE_FUNC_BUILTIN;160 quxHead.xSFunc = dummy_xSFunc;161 162 int h_qux = bucket_for(quxHead.zName);163 sqlite3BuiltinFunctions.a[h_qux] = &quxHead;164 165 /* Another bucket currently empty: "zed" */166 int h_zed = bucket_for("zed");167 TEST_ASSERT_NULL(sqlite3BuiltinFunctions.a[h_zed]);168 169 FuncDef defs[2];170 memset(defs, 0, sizeof(defs));171 defs[0].zName = "qux"; /* variant for existing name */172 defs[0].funcFlags = SQLITE_FUNC_BUILTIN;173 defs[0].xSFunc = dummy_xSFunc;174 175 defs[1].zName = "zed"; /* new name in empty bucket */176 defs[1].funcFlags = SQLITE_FUNC_BUILTIN;177 defs[1].xSFunc = dummy_xSFunc;178 179 sqlite3InsertBuiltinFuncs(defs, 2);180 181 /* Check "qux" got a new variant inserted after head */182 TEST_ASSERT_EQUAL_PTR(&defs[0], quxHead.pNext);183 TEST_ASSERT_NULL(defs[0].pNext);184 185 /* Check "zed" became new head of its (previously empty) bucket */186 TEST_ASSERT_EQUAL_PTR(&defs[1], sqlite3BuiltinFunctions.a[h_zed]);187 TEST_ASSERT_NULL(defs[1].u.pHash);188 TEST_ASSERT_NULL(defs[1].pNext);189}190 191int main(void){192 UNITY_BEGIN();193 RUN_TEST(test_sqlite3InsertBuiltinFuncs_insert_into_empty_bucket);194 RUN_TEST(test_sqlite3InsertBuiltinFuncs_bucket_collision_new_name_becomes_head);195 RUN_TEST(test_sqlite3InsertBuiltinFuncs_insert_variant_into_pNext_chain);196 RUN_TEST(test_sqlite3InsertBuiltinFuncs_multiple_insertions_ordering);197 RUN_TEST(test_sqlite3InsertBuiltinFuncs_mixed_new_and_existing_names);198 return UNITY_END();199}