AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdint.h>5 6/* External wrapper for the static function under test */7void test_jsonReturnParse(sqlite3_context *ctx, JsonParse *p);8 9static sqlite3 *gDb = NULL;10 11/* Minimal JSONB encoding helper:12 A single TRUE element: header byte = 0x01 (payload size 0, type TRUE) */13static unsigned char jsonb_true_header_byte(void){14 return (unsigned char)0x01;15}16 17void setUp(void) {18 int rc = sqlite3_open(":memory:", &gDb);19 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory SQLite DB");20 TEST_ASSERT_NOT_NULL(gDb);21}22 23void tearDown(void) {24 if( gDb ){25 sqlite3_close(gDb);26 gDb = NULL;27 }28}29 30/* Callback: Return TEXT result (flags=0) via jsonReturnParse from JSONB TRUE */31static void cb_json_return_text(sqlite3_context *ctx, int argc, sqlite3_value **argv){32 (void)argc; (void)argv;33 JsonParse p;34 memset(&p, 0, sizeof(p));35 unsigned char *a = (unsigned char*)sqlite3_malloc(1);36 TEST_ASSERT_NOT_NULL(a);37 a[0] = jsonb_true_header_byte();38 p.aBlob = a;39 p.nBlob = 1;40 p.nBlobAlloc = 0; /* Not handing ownership to SQLite in text path */41 p.bReadOnly = 0;42 p.oom = 0;43 44 test_jsonReturnParse(ctx, &p);45 46 /* Since text path does not consume/own the blob memory, free it here */47 sqlite3_free(a);48}49 50/* Callback: Return BLOB with SQLITE_DYNAMIC (nBlobAlloc>0 && !bReadOnly)51 Verify that p.nBlobAlloc is cleared to 0 */52static void cb_json_return_blob_dynamic(sqlite3_context *ctx, int argc, sqlite3_value **argv){53 (void)argc; (void)argv;54 JsonParse p;55 memset(&p, 0, sizeof(p));56 unsigned char *a = (unsigned char*)sqlite3_malloc(1);57 TEST_ASSERT_NOT_NULL(a);58 a[0] = jsonb_true_header_byte();59 p.aBlob = a;60 p.nBlob = 1;61 p.nBlobAlloc = 8; /* >0 signals caller-allocated buffer; allows SQLITE_DYNAMIC path */62 p.bReadOnly = 0; /* Allow SQLITE_DYNAMIC and clearing nBlobAlloc */63 p.oom = 0;64 65 test_jsonReturnParse(ctx, &p);66 67 TEST_ASSERT_EQUAL_INT_MESSAGE(0, p.nBlobAlloc, "nBlobAlloc should be cleared to 0 after SQLITE_DYNAMIC result");68 /* Do not free 'a' here; ownership transferred to SQLite via SQLITE_DYNAMIC */69}70 71/* Callback: Return BLOB with SQLITE_TRANSIENT (bReadOnly==1)72 Verify that p.nBlobAlloc is unchanged (>0) and then free memory locally */73static void cb_json_return_blob_readonly(sqlite3_context *ctx, int argc, sqlite3_value **argv){74 (void)argc; (void)argv;75 JsonParse p;76 memset(&p, 0, sizeof(p));77 unsigned char *a = (unsigned char*)sqlite3_malloc(1);78 TEST_ASSERT_NOT_NULL(a);79 a[0] = jsonb_true_header_byte();80 p.aBlob = a;81 p.nBlob = 1;82 p.nBlobAlloc = 16; /* >0 */83 p.bReadOnly = 1; /* Forces SQLITE_TRANSIENT */84 p.oom = 0;85 86 test_jsonReturnParse(ctx, &p);87 88 TEST_ASSERT_TRUE_MESSAGE(p.nBlobAlloc > 0, "nBlobAlloc should remain >0 when using SQLITE_TRANSIENT");89 /* Since SQLITE_TRANSIENT copies the data, we must free our allocation */90 sqlite3_free(a);91}92 93/* Callback: Force OOM path */94static void cb_json_return_oom(sqlite3_context *ctx, int argc, sqlite3_value **argv){95 (void)argc; (void)argv;96 JsonParse p;97 memset(&p, 0, sizeof(p));98 p.oom = 1;99 test_jsonReturnParse(ctx, &p);100}101 102/* Test: Text path yields "true" with JSON subtype */103void test_jsonReturnParse_returns_text_true_with_json_subtype(void){104 int rc;105 sqlite3_stmt *stmt = NULL;106 107 rc = sqlite3_create_function(gDb, "f_text", 0, SQLITE_UTF8,108 (void*)(intptr_t)0 /* flags: no JSON_BLOB */,109 cb_json_return_text, 0, 0);110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);111 112 rc = sqlite3_prepare_v2(gDb, "SELECT f_text()", -1, &stmt, 0);113 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);114 115 rc = sqlite3_step(stmt);116 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);117 118 int ctype = sqlite3_column_type(stmt, 0);119 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, ctype);120 121 int subtype = sqlite3_column_subtype(stmt, 0);122 TEST_ASSERT_EQUAL_INT(JSON_SUBTYPE, subtype);123 124 const unsigned char *txt = sqlite3_column_text(stmt, 0);125 int n = sqlite3_column_bytes(stmt, 0);126 TEST_ASSERT_NOT_NULL(txt);127 TEST_ASSERT_EQUAL_INT(4, n);128 TEST_ASSERT_EQUAL_INT(0, memcmp(txt, "true", 4));129 130 rc = sqlite3_finalize(stmt);131 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);132}133 134/* Test: Blob path with dynamic ownership returns a 1-byte blob {0x01} and clears nBlobAlloc */135void test_jsonReturnParse_returns_blob_and_clears_alloc_on_dynamic(void){136 int rc;137 sqlite3_stmt *stmt = NULL;138 139 rc = sqlite3_create_function(gDb, "f_blob_dyn", 0, SQLITE_UTF8,140 (void*)(intptr_t)JSON_BLOB /* flags include JSON_BLOB */,141 cb_json_return_blob_dynamic, 0, 0);142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);143 144 rc = sqlite3_prepare_v2(gDb, "SELECT f_blob_dyn()", -1, &stmt, 0);145 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);146 147 rc = sqlite3_step(stmt);148 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);149 150 int ctype = sqlite3_column_type(stmt, 0);151 TEST_ASSERT_EQUAL_INT(SQLITE_BLOB, ctype);152 153 const void *blob = sqlite3_column_blob(stmt, 0);154 int n = sqlite3_column_bytes(stmt, 0);155 TEST_ASSERT_NOT_NULL(blob);156 TEST_ASSERT_EQUAL_INT(1, n);157 const unsigned char *b = (const unsigned char*)blob;158 TEST_ASSERT_EQUAL_UINT8(jsonb_true_header_byte(), b[0]);159 160 rc = sqlite3_finalize(stmt);161 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);162}163 164/* Test: Blob path with read-only buffer returns same 1-byte blob and does not clear nBlobAlloc */165void test_jsonReturnParse_returns_blob_and_preserves_alloc_on_readonly(void){166 int rc;167 sqlite3_stmt *stmt = NULL;168 169 rc = sqlite3_create_function(gDb, "f_blob_ro", 0, SQLITE_UTF8,170 (void*)(intptr_t)JSON_BLOB /* flags include JSON_BLOB */,171 cb_json_return_blob_readonly, 0, 0);172 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);173 174 rc = sqlite3_prepare_v2(gDb, "SELECT f_blob_ro()", -1, &stmt, 0);175 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);176 177 rc = sqlite3_step(stmt);178 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);179 180 int ctype = sqlite3_column_type(stmt, 0);181 TEST_ASSERT_EQUAL_INT(SQLITE_BLOB, ctype);182 183 const void *blob = sqlite3_column_blob(stmt, 0);184 int n = sqlite3_column_bytes(stmt, 0);185 TEST_ASSERT_NOT_NULL(blob);186 TEST_ASSERT_EQUAL_INT(1, n);187 const unsigned char *b = (const unsigned char*)blob;188 TEST_ASSERT_EQUAL_UINT8(jsonb_true_header_byte(), b[0]);189 190 rc = sqlite3_finalize(stmt);191 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);192}193 194/* Test: OOM path sets an error (sqlite3_result_error_nomem) */195void test_jsonReturnParse_oom_sets_error_nomem(void){196 int rc;197 sqlite3_stmt *stmt = NULL;198 199 rc = sqlite3_create_function(gDb, "f_oom", 0, SQLITE_UTF8,200 (void*)(intptr_t)0,201 cb_json_return_oom, 0, 0);202 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);203 204 rc = sqlite3_prepare_v2(gDb, "SELECT f_oom()", -1, &stmt, 0);205 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);206 207 rc = sqlite3_step(stmt);208 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);209 210 const char *errmsg = sqlite3_errmsg(gDb);211 TEST_ASSERT_NOT_NULL(errmsg);212 /* Ensure the message contains "out of memory" (sqlite3_result_error_nomem) */213 TEST_ASSERT_NOT_NULL_MESSAGE(strstr(errmsg, "out of memory"), "Error message should contain 'out of memory'");214 215 rc = sqlite3_finalize(stmt);216 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);217}218 219int main(void) {220 UNITY_BEGIN();221 RUN_TEST(test_jsonReturnParse_returns_text_true_with_json_subtype);222 RUN_TEST(test_jsonReturnParse_returns_blob_and_clears_alloc_on_dynamic);223 RUN_TEST(test_jsonReturnParse_returns_blob_and_preserves_alloc_on_readonly);224 RUN_TEST(test_jsonReturnParse_oom_sets_error_nomem);225 return UNITY_END();226}