AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Forward declaration of the target under test (already provided by includes) */7/* void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr); */8 9static sqlite3* openTestDb(void){10 sqlite3 *db = 0;11 int rc = sqlite3_open(":memory:", &db);12 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory DB");13 TEST_ASSERT_NOT_NULL(db);14 return db;15}16 17static void closeTestDb(sqlite3 *db){18 if( db ){19 sqlite3_close(db);20 }21}22 23static void initParse(Parse *pParse, sqlite3 *db){24 memset(pParse, 0, sizeof(*pParse));25 pParse->db = db;26}27 28/* Create a minimal non-NULL SrcList to satisfy the assert in sqlite3ExprAnalyzeAggregates */29static SrcList* makeMinimalSrcList(sqlite3 *db){30 SrcList *p = (SrcList*)sqlite3DbMallocZero(db, sizeof(SrcList));31 /* We don't need to populate it; just non-NULL */32 return p;33}34 35/* Helper: build function expression fName(argExprList) */36static Expr* makeFunctionExpr(Parse *pParse, const char *zFunc, ExprList *pArgs){37 Token tFunc;38 tFunc.z = zFunc;39 tFunc.n = (int)strlen(zFunc);40 return sqlite3ExprFunction(pParse, pArgs, &tFunc);41}42 43/* Helper: create integer literal expression with a given string (e.g., "1") */44static Expr* makeIntegerLiteral(sqlite3 *db, const char *zNum){45 Token tNum;46 tNum.z = zNum;47 tNum.n = (int)strlen(zNum);48 return sqlite3ExprAlloc(db, TK_INTEGER, &tNum, 0);49}50 51/* Helper: append expression to an ExprList (creating the list if NULL) */52static ExprList* appendExprToList(Parse *pParse, ExprList *pList, Expr *pExpr){53 return sqlite3ExprListAppend(pParse, pList, pExpr);54}55 56void setUp(void) {57 /* No global setup */58}59void tearDown(void) {60 /* No global cleanup */61}62 63/* Test 1: NULL expression should be a no-op and not crash. */64static void test_sqlite3ExprAnalyzeAggregates_null_expr_noop(void){65 sqlite3 *db = openTestDb();66 Parse parse;67 initParse(&parse, db);68 69 NameContext nc;70 memset(&nc, 0, sizeof(nc));71 nc.pParse = &parse;72 nc.pSrcList = makeMinimalSrcList(db);73 74 AggInfo agg;75 memset(&agg, 0, sizeof(agg));76 nc.pAggInfo = &agg;77 78 /* Call with NULL expression */79 sqlite3ExprAnalyzeAggregates(&nc, NULL);80 81 /* Expect no changes to AggInfo counts */82 TEST_ASSERT_EQUAL_INT(0, agg.nFunc);83 TEST_ASSERT_EQUAL_INT(0, agg.nColumn);84 85 sqlite3DbFree(db, nc.pSrcList);86 closeTestDb(db);87}88 89/* Test 2: count(1) is an aggregate and should be recorded in AggInfo (nFunc increments). */90static void test_sqlite3ExprAnalyzeAggregates_counts_aggfunc_count(void){91 sqlite3 *db = openTestDb();92 Parse parse;93 initParse(&parse, db);94 95 NameContext nc;96 memset(&nc, 0, sizeof(nc));97 nc.pParse = &parse;98 nc.pSrcList = makeMinimalSrcList(db);99 100 AggInfo agg;101 memset(&agg, 0, sizeof(agg));102 nc.pAggInfo = &agg;103 104 /* Build count(1) */105 Expr *lit1 = makeIntegerLiteral(db, "1");106 TEST_ASSERT_NOT_NULL(lit1);107 ExprList *args = appendExprToList(&parse, NULL, lit1);108 TEST_ASSERT_NOT_NULL(args);109 Expr *fn = makeFunctionExpr(&parse, "count", args);110 TEST_ASSERT_NOT_NULL(fn);111 112 /* Resolve names so the function is identified as aggregate */113 int rc = sqlite3ResolveExprNames(&nc, fn);114 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Name resolution failed for count(1)");115 116 /* Analyze aggregates */117 sqlite3ExprAnalyzeAggregates(&nc, fn);118 119 /* Verify that one aggregate function is recorded */120 TEST_ASSERT_EQUAL_INT(1, agg.nFunc);121 122 /* Cleanup */123 sqlite3ExprDelete(db, fn);124 sqlite3DbFree(db, nc.pSrcList);125 closeTestDb(db);126}127 128/* Test 3: Non-aggregate function abs(1) should not be recorded in AggInfo (nFunc remains 0). */129static void test_sqlite3ExprAnalyzeAggregates_nonaggfunc_noop(void){130 sqlite3 *db = openTestDb();131 Parse parse;132 initParse(&parse, db);133 134 NameContext nc;135 memset(&nc, 0, sizeof(nc));136 nc.pParse = &parse;137 nc.pSrcList = makeMinimalSrcList(db);138 139 AggInfo agg;140 memset(&agg, 0, sizeof(agg));141 nc.pAggInfo = &agg;142 143 /* Build abs(1) which is not an aggregate */144 Expr *lit1 = makeIntegerLiteral(db, "1");145 TEST_ASSERT_NOT_NULL(lit1);146 ExprList *args = appendExprToList(&parse, NULL, lit1);147 TEST_ASSERT_NOT_NULL(args);148 Expr *fn = makeFunctionExpr(&parse, "abs", args);149 TEST_ASSERT_NOT_NULL(fn);150 151 int rc = sqlite3ResolveExprNames(&nc, fn);152 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Name resolution failed for abs(1)");153 154 sqlite3ExprAnalyzeAggregates(&nc, fn);155 156 /* No aggregate functions should have been recorded */157 TEST_ASSERT_EQUAL_INT(0, agg.nFunc);158 TEST_ASSERT_EQUAL_INT(0, agg.nColumn);159 160 sqlite3ExprDelete(db, fn);161 sqlite3DbFree(db, nc.pSrcList);162 closeTestDb(db);163}164 165/* Test 4: Nested aggregate inside non-aggregate: abs(count(1)) should still record one aggregate. */166static void test_sqlite3ExprAnalyzeAggregates_nested_agg_inside_nonagg(void){167 sqlite3 *db = openTestDb();168 Parse parse;169 initParse(&parse, db);170 171 NameContext nc;172 memset(&nc, 0, sizeof(nc));173 nc.pParse = &parse;174 nc.pSrcList = makeMinimalSrcList(db);175 176 AggInfo agg;177 memset(&agg, 0, sizeof(agg));178 nc.pAggInfo = &agg;179 180 /* Build inner: count(1) */181 Expr *lit1 = makeIntegerLiteral(db, "1");182 TEST_ASSERT_NOT_NULL(lit1);183 ExprList *countArgs = appendExprToList(&parse, NULL, lit1);184 TEST_ASSERT_NOT_NULL(countArgs);185 Expr *countFn = makeFunctionExpr(&parse, "count", countArgs);186 TEST_ASSERT_NOT_NULL(countFn);187 188 /* Wrap with abs(...) */189 ExprList *outerArgs = appendExprToList(&parse, NULL, countFn);190 TEST_ASSERT_NOT_NULL(outerArgs);191 Expr *absFn = makeFunctionExpr(&parse, "abs", outerArgs);192 TEST_ASSERT_NOT_NULL(absFn);193 194 int rc = sqlite3ResolveExprNames(&nc, absFn);195 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Name resolution failed for abs(count(1))");196 197 sqlite3ExprAnalyzeAggregates(&nc, absFn);198 199 /* Expect exactly one aggregate function recorded */200 TEST_ASSERT_EQUAL_INT(1, agg.nFunc);201 202 sqlite3ExprDelete(db, absFn);203 sqlite3DbFree(db, nc.pSrcList);204 closeTestDb(db);205}206 207int main(void) {208 UNITY_BEGIN();209 RUN_TEST(test_sqlite3ExprAnalyzeAggregates_null_expr_noop);210 RUN_TEST(test_sqlite3ExprAnalyzeAggregates_counts_aggfunc_count);211 RUN_TEST(test_sqlite3ExprAnalyzeAggregates_nonaggfunc_noop);212 RUN_TEST(test_sqlite3ExprAnalyzeAggregates_nested_agg_inside_nonagg);213 return UNITY_END();214}