AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Prototype for the provided wrapper around the static function */7void test_groupConcatStep(sqlite3_context *context, int argc, sqlite3_value **argv);8 9/* Helpers */10 11static void initContext(sqlite3 *db, sqlite3_context *ctx, Mem *acc){12 memset(ctx, 0, sizeof(*ctx));13 sqlite3VdbeMemInit(acc, db, 0);14 sqlite3VdbeMemSetNull(acc);15 ctx->db = db;16 ctx->pMem = acc;17}18 19/* Create a TEXT sqlite3_value from a C string (UTF8). If z==NULL, returns a NULL value. */20static sqlite3_value *valText(sqlite3 *db, const char *z){21 Mem *v = sqlite3ValueNew(db);22 if( z ){23 sqlite3ValueSetStr(v, -1, z, SQLITE_UTF8, SQLITE_TRANSIENT);24 }else{25 sqlite3VdbeMemSetNull(v);26 }27 return (sqlite3_value*)v;28}29 30/* Finalize the StrAccum inside the aggregate context and return a heap-allocated C string. */31static char *finishResultString(sqlite3_context *ctx){32 void *pAgg = sqlite3_aggregate_context(ctx, 0);33 if( pAgg==0 ){34 /* No aggregate context was created => no non-NULL inputs => empty string */35 char *z = sqlite3_mprintf("%s", "");36 return z;37 }38 StrAccum *pStr = (StrAccum*)pAgg; /* GroupConcatCtx starts with StrAccum */39 char *z = sqlite3StrAccumFinish(pStr);40 return z; /* Use sqlite3_free() to free this */41}42 43/* Unity required hooks */44void setUp(void) {45 /* empty */46}47void tearDown(void) {48 /* empty */49}50 51/* Tests */52 53static void test_groupConcatStep_one_arg_single(void){54 sqlite3 *db = 0;55 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));56 57 sqlite3_context ctx;58 Mem acc;59 initContext(db, &ctx, &acc);60 61 sqlite3_value *v = valText(db, "a");62 sqlite3_value *argv1[1] = { v };63 test_groupConcatStep(&ctx, 1, argv1);64 65 char *zRes = finishResultString(&ctx);66 TEST_ASSERT_NOT_NULL(zRes);67 TEST_ASSERT_EQUAL_STRING("a", zRes);68 69 sqlite3_free(zRes);70 sqlite3ValueFree(v);71 sqlite3VdbeMemRelease(&acc);72 sqlite3_close(db);73}74 75static void test_groupConcatStep_one_arg_multiple(void){76 sqlite3 *db = 0;77 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));78 79 sqlite3_context ctx;80 Mem acc;81 initContext(db, &ctx, &acc);82 83 sqlite3_value *v1 = valText(db, "a");84 sqlite3_value *v2 = valText(db, "b");85 sqlite3_value *v3 = valText(db, "c");86 sqlite3_value *argv1[1];87 88 argv1[0] = v1; test_groupConcatStep(&ctx, 1, argv1);89 argv1[0] = v2; test_groupConcatStep(&ctx, 1, argv1);90 argv1[0] = v3; test_groupConcatStep(&ctx, 1, argv1);91 92 char *zRes = finishResultString(&ctx);93 TEST_ASSERT_NOT_NULL(zRes);94 TEST_ASSERT_EQUAL_STRING("a,b,c", zRes);95 96 sqlite3_free(zRes);97 sqlite3ValueFree(v1);98 sqlite3ValueFree(v2);99 sqlite3ValueFree(v3);100 sqlite3VdbeMemRelease(&acc);101 sqlite3_close(db);102}103 104static void test_groupConcatStep_two_arg_fixed_separator(void){105 sqlite3 *db = 0;106 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));107 108 sqlite3_context ctx;109 Mem acc;110 initContext(db, &ctx, &acc);111 112 sqlite3_value *v1 = valText(db, "a");113 sqlite3_value *v2 = valText(db, "b");114 sqlite3_value *sep = valText(db, "|");115 116 sqlite3_value *argv2[2] = { v1, sep };117 test_groupConcatStep(&ctx, 2, argv2);118 119 argv2[0] = v2; /* keep same separator */120 test_groupConcatStep(&ctx, 2, argv2);121 122 char *zRes = finishResultString(&ctx);123 TEST_ASSERT_NOT_NULL(zRes);124 TEST_ASSERT_EQUAL_STRING("a|b", zRes);125 126 sqlite3_free(zRes);127 sqlite3ValueFree(v1);128 sqlite3ValueFree(v2);129 sqlite3ValueFree(sep);130 sqlite3VdbeMemRelease(&acc);131 sqlite3_close(db);132}133 134static void test_groupConcatStep_two_arg_changing_separators(void){135 sqlite3 *db = 0;136 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));137 138 sqlite3_context ctx;139 Mem acc;140 initContext(db, &ctx, &acc);141 142 sqlite3_value *a = valText(db, "a");143 sqlite3_value *b = valText(db, "b");144 sqlite3_value *c = valText(db, "c");145 sqlite3_value *sep1 = valText(db, "|");146 sqlite3_value *sep2 = valText(db, "/");147 sqlite3_value *sep3 = valText(db, "-");148 149 sqlite3_value *argv2[2];150 151 argv2[0] = a; argv2[1] = sep1; test_groupConcatStep(&ctx, 2, argv2);152 argv2[0] = b; argv2[1] = sep2; test_groupConcatStep(&ctx, 2, argv2);153 argv2[0] = c; argv2[1] = sep3; test_groupConcatStep(&ctx, 2, argv2);154 155 char *zRes = finishResultString(&ctx);156 TEST_ASSERT_NOT_NULL(zRes);157 TEST_ASSERT_EQUAL_STRING("a|b/c-d", zRes);158 159 sqlite3_free(zRes);160 sqlite3ValueFree(a);161 sqlite3ValueFree(b);162 sqlite3ValueFree(c);163 sqlite3ValueFree(sep1);164 sqlite3ValueFree(sep2);165 sqlite3ValueFree(sep3);166 sqlite3VdbeMemRelease(&acc);167 sqlite3_close(db);168}169 170static void test_groupConcatStep_ignores_null_values(void){171 sqlite3 *db = 0;172 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));173 174 sqlite3_context ctx;175 Mem acc;176 initContext(db, &ctx, &acc);177 178 sqlite3_value *a = valText(db, "a");179 sqlite3_value *nul = valText(db, NULL);180 sqlite3_value *b = valText(db, "b");181 182 sqlite3_value *argv1[1];183 184 argv1[0] = a; test_groupConcatStep(&ctx, 1, argv1);185 argv1[0] = nul; test_groupConcatStep(&ctx, 1, argv1); /* should be ignored */186 argv1[0] = b; test_groupConcatStep(&ctx, 1, argv1);187 188 char *zRes = finishResultString(&ctx);189 TEST_ASSERT_NOT_NULL(zRes);190 TEST_ASSERT_EQUAL_STRING("a,b", zRes);191 192 sqlite3_free(zRes);193 sqlite3ValueFree(a);194 sqlite3ValueFree(nul);195 sqlite3ValueFree(b);196 sqlite3VdbeMemRelease(&acc);197 sqlite3_close(db);198}199 200static void test_groupConcatStep_null_separator_means_empty(void){201 sqlite3 *db = 0;202 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));203 204 sqlite3_context ctx;205 Mem acc;206 initContext(db, &ctx, &acc);207 208 sqlite3_value *a = valText(db, "a");209 sqlite3_value *b = valText(db, "b");210 sqlite3_value *c = valText(db, "c");211 sqlite3_value *nullSep = valText(db, NULL);212 213 sqlite3_value *argv2[2];214 215 argv2[0] = a; argv2[1] = nullSep; test_groupConcatStep(&ctx, 2, argv2);216 argv2[0] = b; argv2[1] = nullSep; test_groupConcatStep(&ctx, 2, argv2);217 argv2[0] = c; argv2[1] = nullSep; test_groupConcatStep(&ctx, 2, argv2);218 219 char *zRes = finishResultString(&ctx);220 TEST_ASSERT_NOT_NULL(zRes);221 TEST_ASSERT_EQUAL_STRING("abc", zRes);222 223 sqlite3_free(zRes);224 sqlite3ValueFree(a);225 sqlite3ValueFree(b);226 sqlite3ValueFree(c);227 sqlite3ValueFree(nullSep);228 sqlite3VdbeMemRelease(&acc);229 sqlite3_close(db);230}231 232static void test_groupConcatStep_mixed_one_and_two_arg_calls(void){233 sqlite3 *db = 0;234 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));235 236 sqlite3_context ctx;237 Mem acc;238 initContext(db, &ctx, &acc);239 240 sqlite3_value *a = valText(db, "a");241 sqlite3_value *b = valText(db, "b");242 sqlite3_value *c = valText(db, "c");243 sqlite3_value *colon = valText(db, ":");244 245 /* Step 1: one-arg (default ',') */246 sqlite3_value *argv1[1] = { a };247 test_groupConcatStep(&ctx, 1, argv1);248 249 /* Step 2: two-arg with ":" separator */250 sqlite3_value *argv2[2] = { b, colon };251 test_groupConcatStep(&ctx, 2, argv2);252 253 /* Step 3: one-arg again; should use default comma, not ":" */254 argv1[0] = c;255 test_groupConcatStep(&ctx, 1, argv1);256 257 char *zRes = finishResultString(&ctx);258 TEST_ASSERT_NOT_NULL(zRes);259 TEST_ASSERT_EQUAL_STRING("a:b,c", zRes);260 261 sqlite3_free(zRes);262 sqlite3ValueFree(a);263 sqlite3ValueFree(b);264 sqlite3ValueFree(c);265 sqlite3ValueFree(colon);266 sqlite3VdbeMemRelease(&acc);267 sqlite3_close(db);268}269 270static void test_groupConcatStep_empty_values_and_empty_separator(void){271 sqlite3 *db = 0;272 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));273 274 sqlite3_context ctx;275 Mem acc;276 initContext(db, &ctx, &acc);277 278 /* Case 1: empty separator => no characters between values */279 sqlite3_value *a = valText(db, "a");280 sqlite3_value *b = valText(db, "b");281 sqlite3_value *emptySep = valText(db, ""); /* length 0 */282 283 sqlite3_value *argv2[2];284 285 argv2[0] = a; argv2[1] = emptySep; test_groupConcatStep(&ctx, 2, argv2);286 argv2[0] = b; argv2[1] = emptySep; test_groupConcatStep(&ctx, 2, argv2);287 288 char *zRes1 = finishResultString(&ctx);289 TEST_ASSERT_NOT_NULL(zRes1);290 TEST_ASSERT_EQUAL_STRING("ab", zRes1);291 292 sqlite3_free(zRes1);293 sqlite3ValueFree(a);294 sqlite3ValueFree(b);295 sqlite3ValueFree(emptySep);296 297 /* Case 2: first value empty string, then a non-empty with ';' separator */298 /* Reinitialize aggregate context with a new Mem */299 sqlite3VdbeMemRelease(&acc);300 initContext(db, &ctx, &acc);301 302 sqlite3_value *emptyVal = valText(db, "");303 sqlite3_value *x = valText(db, "x");304 sqlite3_value *semi = valText(db, ";");305 306 argv2[0] = emptyVal; argv2[1] = semi; test_groupConcatStep(&ctx, 2, argv2);307 argv2[0] = x; argv2[1] = semi; test_groupConcatStep(&ctx, 2, argv2);308 309 char *zRes2 = finishResultString(&ctx);310 TEST_ASSERT_NOT_NULL(zRes2);311 TEST_ASSERT_EQUAL_STRING(";x", zRes2);312 313 sqlite3_free(zRes2);314 sqlite3ValueFree(emptyVal);315 sqlite3ValueFree(x);316 sqlite3ValueFree(semi);317 318 sqlite3VdbeMemRelease(&acc);319 sqlite3_close(db);320}321 322int main(void) {323 UNITY_BEGIN();324 RUN_TEST(test_groupConcatStep_one_arg_single);325 RUN_TEST(test_groupConcatStep_one_arg_multiple);326 RUN_TEST(test_groupConcatStep_two_arg_fixed_separator);327 RUN_TEST(test_groupConcatStep_two_arg_changing_separators);328 RUN_TEST(test_groupConcatStep_ignores_null_values);329 RUN_TEST(test_groupConcatStep_null_separator_means_empty);330 RUN_TEST(test_groupConcatStep_mixed_one_and_two_arg_calls);331 RUN_TEST(test_groupConcatStep_empty_values_and_empty_separator);332 return UNITY_END();333}