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 provided test wrapper for the static function */7extern void test_jsonReturnStringAsBlob(JsonString *pStr);8 9static sqlite3 *gDb = NULL;10 11/* UDF: make_blob(text) -> JSONB blob converted by jsonReturnStringAsBlob */12static void xMakeBlob(sqlite3_context *ctx, int argc, sqlite3_value **argv){13 (void)argc;14 JsonString js;15 memset(&js, 0, sizeof(js));16 17 const unsigned char *zArg = sqlite3_value_text(argv[0]);18 if( zArg==NULL ){19 /* Propagate SQL NULL to result NULL for completeness */20 sqlite3_result_null(ctx);21 return;22 }23 24 /* Use a sufficiently large stack buffer to avoid growth during termination */25 char buf[1024];26 size_t n = strlen((const char*)zArg);27 if( n >= sizeof(buf) ) n = sizeof(buf) - 1;28 memcpy(buf, zArg, n);29 30 js.zBuf = buf;31 js.nUsed = (u32)n;32 js.nAlloc = (u32)sizeof(buf);33 js.bStatic = 1;34 js.eErr = 0;35 js.pCtx = ctx;36 37 test_jsonReturnStringAsBlob(&js);38}39 40/* UDF: make_blob_err(text) -> forces eErr to simulate OOM/error precondition */41static void xMakeBlobErr(sqlite3_context *ctx, int argc, sqlite3_value **argv){42 (void)argc;43 (void)argv;44 JsonString js;45 memset(&js, 0, sizeof(js));46 47 char dummy[] = "null";48 js.zBuf = dummy;49 js.nUsed = (u32)strlen(dummy);50 js.nAlloc = (u32)sizeof(dummy);51 js.bStatic = 1;52 js.eErr = 1; /* Non-zero triggers sqlite3_result_error_nomem */53 js.pCtx = ctx;54 55 test_jsonReturnStringAsBlob(&js);56}57 58static void prepare_and_bind_text(sqlite3_stmt **ppStmt, const char *zSql, const char *zParam){59 int rc = sqlite3_prepare_v2(gDb, zSql, -1, ppStmt, NULL);60 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");61 rc = sqlite3_bind_text(*ppStmt, 1, zParam, -1, SQLITE_TRANSIENT);62 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "bind failed");63}64 65void setUp(void) {66 int rc = sqlite3_open(":memory:", &gDb);67 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");68 69 rc = sqlite3_create_function(gDb, "make_blob", 1, SQLITE_UTF8, NULL, xMakeBlob, NULL, NULL);70 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "create_function make_blob failed");71 72 rc = sqlite3_create_function(gDb, "make_blob_err", 1, SQLITE_UTF8, NULL, xMakeBlobErr, NULL, NULL);73 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "create_function make_blob_err failed");74}75 76void tearDown(void) {77 if( gDb ){78 int rc = sqlite3_close(gDb);79 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_close failed");80 gDb = NULL;81 }82}83 84/* Helper to validate typeof, json_valid, and json canonicalization */85static void assert_roundtrip(const char *inJson, const char *expectedCanonical){86 sqlite3_stmt *pStmt = NULL;87 const char *zSql =88 "SELECT typeof(make_blob(?1)), json_valid(make_blob(?1)), json(make_blob(?1))";89 prepare_and_bind_text(&pStmt, zSql, inJson);90 91 int rc = sqlite3_step(pStmt);92 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "step failed");93 94 const unsigned char *zType = sqlite3_column_text(pStmt, 0);95 TEST_ASSERT_NOT_NULL(zType);96 TEST_ASSERT_EQUAL_STRING("blob", (const char*)zType);97 98 int isValid = sqlite3_column_int(pStmt, 1);99 TEST_ASSERT_EQUAL_INT(1, isValid);100 101 const unsigned char *zCanon = sqlite3_column_text(pStmt, 2);102 TEST_ASSERT_NOT_NULL(zCanon);103 TEST_ASSERT_EQUAL_STRING(expectedCanonical, (const char*)zCanon);104 105 rc = sqlite3_finalize(pStmt);106 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "finalize failed");107}108 109void test_jsonReturnStringAsBlob_roundtrip_null(void){110 assert_roundtrip("null", "null");111}112 113void test_jsonReturnStringAsBlob_roundtrip_object_array(void){114 const char *in = "{\"a\":1,\"b\":[2,3]}";115 const char *expected = "{\"a\":1,\"b\":[2,3]}";116 assert_roundtrip(in, expected);117}118 119void test_jsonReturnStringAsBlob_roundtrip_string_escapes(void){120 /* JSON string "a\nb" encoded with escape */121 assert_roundtrip("\"a\\nb\"", "\"a\\nb\"");122}123 124void test_jsonReturnStringAsBlob_accepts_JSON5_single_quotes(void){125 /* JSON5 input with single quotes should canonicalize to double-quoted JSON */126 assert_roundtrip("'abc'", "\"abc\"");127}128 129void test_jsonReturnStringAsBlob_accepts_JSON5_leading_decimal(void){130 /* JSON5 input .5 should canonicalize to 0.5 */131 assert_roundtrip(".5", "0.5");132}133 134void test_jsonReturnStringAsBlob_error_when_eErr_pre_set(void){135 sqlite3_stmt *pStmt = NULL;136 const char *zSql = "SELECT make_blob_err(?1)";137 prepare_and_bind_text(&pStmt, zSql, "{\"a\":1}");138 139 int rc = sqlite3_step(pStmt);140 /* Expect an error due to sqlite3_result_error_nomem inside the target function */141 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);142 143 int eec = sqlite3_extended_errcode(gDb);144 TEST_ASSERT_EQUAL_INT(SQLITE_NOMEM, eec);145 146 /* Optionally verify there is an error message */147 const char *zErr = sqlite3_errmsg(gDb);148 TEST_ASSERT_NOT_NULL(zErr);149 150 rc = sqlite3_finalize(pStmt);151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);152}153 154int main(void) {155 UNITY_BEGIN();156 RUN_TEST(test_jsonReturnStringAsBlob_roundtrip_null);157 RUN_TEST(test_jsonReturnStringAsBlob_roundtrip_object_array);158 RUN_TEST(test_jsonReturnStringAsBlob_roundtrip_string_escapes);159 RUN_TEST(test_jsonReturnStringAsBlob_accepts_JSON5_single_quotes);160 RUN_TEST(test_jsonReturnStringAsBlob_accepts_JSON5_leading_decimal);161 RUN_TEST(test_jsonReturnStringAsBlob_error_when_eErr_pre_set);162 return UNITY_END();163}