AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Forward declaration of the wrapper added in the module */7void test_jsonGroupInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv);8 9static sqlite3 *gDb = NULL;10 11/* Aggregate context structure is JsonString, which is defined in SQLite core.12 We only need to set zBuf and nUsed for jsonGroupInverse to work. */13 14/* Aggregate step: initialize aggregate context with provided JSON text */15static void do_inv_step(sqlite3_context *ctx, int argc, sqlite3_value **argv){16 const unsigned char *zIn;17 int nIn;18 JsonString *pStr;19 20 if( argc<1 || sqlite3_value_type(argv[0])==SQLITE_NULL ){21 /* Create an empty buffer if NULL passed */22 zIn = (const unsigned char *)"[]";23 nIn = 2;24 }else{25 zIn = sqlite3_value_text(argv[0]);26 nIn = sqlite3_value_bytes(argv[0]);27 }28 29 pStr = (JsonString*)sqlite3_aggregate_context(ctx, (int)sizeof(JsonString));30 if( pStr==0 ) return; /* Out of memory or mis-use; let SQLite signal error later */31 memset(pStr, 0, sizeof(*pStr));32 33 /* Allocate buffer and copy input JSON text */34 pStr->zBuf = (char*)sqlite3_malloc64((sqlite3_uint64)nIn + 1);35 if( pStr->zBuf==0 ){36 sqlite3_result_error_nomem(ctx);37 return;38 }39 memcpy(pStr->zBuf, zIn, (size_t)nIn);40 pStr->zBuf[nIn] = 0;41 pStr->nUsed = (u32)nIn;42}43 44/* Aggregate final: call the inverse function, then return the mutated JSON text */45static void do_inv_final(sqlite3_context *ctx){46 JsonString *pStr;47 /* Invoke the target under test */48 test_jsonGroupInverse(ctx, 0, 0);49 50 /* Retrieve the aggregate context and return the resulting buffer */51 pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);52 if( pStr && pStr->zBuf ){53 sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);54 sqlite3_free(pStr->zBuf);55 pStr->zBuf = 0;56 }else{57 sqlite3_result_null(ctx);58 }59}60 61/* Helper to register the aggregate that will drive jsonGroupInverse */62static void register_do_inv(sqlite3 *db){63 int rc = sqlite3_create_function_v2(db, "do_inv", 1, SQLITE_UTF8, 0,64 0, do_inv_step, do_inv_final, 0);65 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to register do_inv()");66}67 68/* Helper to run SELECT do_inv(?) and return heap-allocated result string */69static char *run_do_inv(sqlite3 *db, const char *input){70 sqlite3_stmt *pStmt = 0;71 int rc;72 const unsigned char *zRes;73 int nRes;74 char *zOut = NULL;75 76 rc = sqlite3_prepare_v2(db, "SELECT do_inv(?)", -1, &pStmt, 0);77 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");78 79 rc = sqlite3_bind_text(pStmt, 1, input, -1, SQLITE_TRANSIENT);80 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "bind failed");81 82 rc = sqlite3_step(pStmt);83 TEST_ASSERT_TRUE_MESSAGE(rc==SQLITE_ROW, "step did not return a row");84 85 zRes = sqlite3_column_text(pStmt, 0);86 nRes = sqlite3_column_bytes(pStmt, 0);87 88 if( zRes ){89 zOut = (char*)sqlite3_malloc64((sqlite3_uint64)nRes + 1);90 TEST_ASSERT_NOT_NULL_MESSAGE(zOut, "malloc for result failed");91 memcpy(zOut, zRes, (size_t)nRes);92 zOut[nRes] = 0;93 }94 95 rc = sqlite3_finalize(pStmt);96 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "finalize failed");97 return zOut;98}99 100void setUp(void) {101 int rc = sqlite3_open(":memory:", &gDb);102 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");103 register_do_inv(gDb);104}105 106void tearDown(void) {107 if( gDb ){108 int rc = sqlite3_close(gDb);109 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_close failed");110 gDb = NULL;111 }112}113 114/* Tests */115 116void test_jsonGroupInverse_array_simple(void) {117 const char *input = "[1,2,3]";118 char *out = run_do_inv(gDb, input);119 TEST_ASSERT_NOT_NULL(out);120 TEST_ASSERT_EQUAL_STRING("[2,3]", out);121 sqlite3_free(out);122}123 124void test_jsonGroupInverse_array_string_with_comma(void) {125 const char *input = "[\"a,b\",\"c\"]";126 char *out = run_do_inv(gDb, input);127 TEST_ASSERT_NOT_NULL(out);128 TEST_ASSERT_EQUAL_STRING("[\"c\"]", out);129 sqlite3_free(out);130}131 132void test_jsonGroupInverse_array_nested(void) {133 const char *input = "[{\"x\":1,\"y\":[2,3]},4,5]";134 char *out = run_do_inv(gDb, input);135 TEST_ASSERT_NOT_NULL(out);136 TEST_ASSERT_EQUAL_STRING("[4,5]", out);137 sqlite3_free(out);138}139 140void test_jsonGroupInverse_object_simple(void) {141 const char *input = "{\"a\":1,\"b\":2}";142 char *out = run_do_inv(gDb, input);143 TEST_ASSERT_NOT_NULL(out);144 TEST_ASSERT_EQUAL_STRING("{\"b\":2}", out);145 sqlite3_free(out);146}147 148void test_jsonGroupInverse_single_element_array(void) {149 const char *input = "[1]";150 char *out = run_do_inv(gDb, input);151 TEST_ASSERT_NOT_NULL(out);152 /* With no comma found, nUsed is set to 1, leaving just the opening bracket */153 TEST_ASSERT_EQUAL_STRING("[", out);154 sqlite3_free(out);155}156 157void test_jsonGroupInverse_escapes_and_commas_inside_string(void) {158 /* First element contains an escaped quote and a comma; it should be skipped correctly */159 const char *input = "[\"ab\\\"cd,ef\",\"Z\"]";160 char *out = run_do_inv(gDb, input);161 TEST_ASSERT_NOT_NULL(out);162 TEST_ASSERT_EQUAL_STRING("[\"Z\"]", out);163 sqlite3_free(out);164}165 166int main(void) {167 UNITY_BEGIN();168 RUN_TEST(test_jsonGroupInverse_array_simple);169 RUN_TEST(test_jsonGroupInverse_array_string_with_comma);170 RUN_TEST(test_jsonGroupInverse_array_nested);171 RUN_TEST(test_jsonGroupInverse_object_simple);172 RUN_TEST(test_jsonGroupInverse_single_element_array);173 RUN_TEST(test_jsonGroupInverse_escapes_and_commas_inside_string);174 return UNITY_END();175}