Skip to content

Commit c02f7d1

Browse files
committed
added display function
1 parent 6f85fcb commit c02f7d1

File tree

2 files changed

+126
-12
lines changed

2 files changed

+126
-12
lines changed

src/sqlite_bignum.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,29 @@ static void is_u64text_func(sqlite3_context *ctx, int argc, sqlite3_value **argv
144144
sqlite3_result_int(ctx, is_valid_u64text(text, len));
145145
}
146146

147+
/**
148+
* @brief Format u64text for display (removes leading zeros)
149+
*/
150+
static void u64text_display_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
151+
if (argc < 1 || sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
152+
sqlite3_result_null(ctx);
153+
return;
154+
}
155+
156+
const char *text = (const char *) sqlite3_value_text(argv[0]);
157+
int len = sqlite3_value_bytes(argv[0]);
158+
159+
if (!is_valid_u64text(text, len)) {
160+
sqlite3_result_text(ctx, text, len, SQLITE_TRANSIENT);
161+
return;
162+
}
163+
164+
int start = 0;
165+
while (start < U64TEXT_WIDTH - 1 && text[start] == '0') { start++; }
166+
167+
sqlite3_result_text(ctx, text + start, U64TEXT_WIDTH - start, SQLITE_TRANSIENT);
168+
}
169+
147170
#ifdef _WIN32
148171
__declspec(dllexport)
149172
#endif
@@ -157,5 +180,6 @@ int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routi
157180
sqlite3_create_function(db, "text_to_u64", 1, SQLITE_UTF8, 0, text_to_u64_func, 0, 0);
158181
// Function: is_u64text(TEXT) -> bool
159182
sqlite3_create_function(db, "is_u64text", 1, SQLITE_UTF8, 0, is_u64text_func, 0, 0);
183+
sqlite3_create_function(db, "u64text_display", 1, SQLITE_UTF8, 0, u64text_display_func, 0, 0);
160184
return SQLITE_OK;
161185
}

test/test_sqlite_bignum.c

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
// cmokca needs to be explicitly included last
99
#include <cmocka.h>
1010

11-
static int open_db(void **state) {
12-
sqlite3 *db = NULL;
13-
int rc = sqlite3_open(":memory:", &db);
14-
assert_int_equal(rc, SQLITE_OK);
15-
*state = db;
16-
return 0;
17-
}
18-
1911
static int close_db(void **state) {
2012
sqlite3 *db = *(sqlite3 **) state;
2113
sqlite3_close(db);
@@ -31,6 +23,17 @@ static void exec_ok(sqlite3 *db, const char *sql) {
3123
sqlite3_free(errmsg);
3224
}
3325

26+
static int open_db(void **state) {
27+
sqlite3 *db = NULL;
28+
int rc = sqlite3_open(":memory:", &db);
29+
assert_int_equal(rc, SQLITE_OK);
30+
*state = db;
31+
rc = sqlite3_enable_load_extension(db, 1);
32+
assert_int_equal(rc, SQLITE_OK);
33+
exec_ok(db, "SELECT load_extension('sqlite_bignum.dll')");
34+
return 0;
35+
}
36+
3437
static sqlite3_stmt *prepare(sqlite3 *db, const char *sql) {
3538
sqlite3_stmt *stmt = NULL;
3639
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
@@ -155,9 +158,6 @@ static void test_text_ordering_fails_without_collation(void **state) {
155158

156159
static void test_u64_edgecases(void **state) {
157160
sqlite3 *db = *(sqlite3 **) state;
158-
int rc = sqlite3_enable_load_extension(db, 1);
159-
assert_int_equal(rc, SQLITE_OK);
160-
exec_ok(db, "SELECT load_extension('sqlite_bignum.dll')");
161161
exec_ok(db, "CREATE TABLE t(n TEXT COLLATE U64TEXT);");
162162

163163
exec_ok(db,
@@ -190,9 +190,99 @@ static void test_u64_edgecases(void **state) {
190190
sqlite3_finalize(stmt);
191191
}
192192

193+
static void test_u64text_display_function(void **state) {
194+
sqlite3 *db = *(sqlite3 **) state;
195+
196+
struct {
197+
const char *input;
198+
const char *expected;
199+
const char *description;
200+
} test_cases[] = {
201+
// Basic zero removal
202+
{"00000000000000000042", "42", "Simple number with leading zeros"},
203+
{"00000000000000000001", "1", "Single digit with leading zeros"},
204+
{"00000000000000000000", "0", "All zeros should display as single zero"},
205+
206+
// Edge cases
207+
{"18446744073709551615", "18446744073709551615", "UINT64_MAX (no leading zeros)"},
208+
{"09223372036854775807", "9223372036854775807", "INT64_MAX with one leading zero"},
209+
{"00009007199254740992", "9007199254740992", "2^53 with leading zeros"},
210+
211+
// Maximum leading zeros
212+
{"00000000000000000123", "123", "Many leading zeros"},
213+
{"01234567890123456789", "1234567890123456789", "19 digits with one leading zero"},
214+
215+
// All digits from 1-19 length results
216+
{"00000000000000000001", "1", "1 digit result"},
217+
{"00000000000000000012", "12", "2 digit result"},
218+
{"00000000000000000123", "123", "3 digit result"},
219+
{"00000000000001234567", "1234567", "7 digit result"},
220+
{"00000012345678901234", "12345678901234", "14 digit result"},
221+
{"12345678901234567890", "12345678901234567890", "20 digit result (no leading zeros)"},
222+
};
223+
224+
for (size_t i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
225+
char sql[256];
226+
snprintf(sql, sizeof(sql), "SELECT u64text_display('%s')", test_cases[i].input);
227+
228+
sqlite3_stmt *stmt = prepare(db, sql);
229+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
230+
231+
const char *result = (const char *) sqlite3_column_text(stmt, 0);
232+
printf("Test %zu: %s\n", i + 1, test_cases[i].description);
233+
printf(" Input: '%s'\n", test_cases[i].input);
234+
printf(" Expected: '%s'\n", test_cases[i].expected);
235+
printf(" Got: '%s'\n", result);
236+
237+
assert_string_equal(result, test_cases[i].expected);
238+
sqlite3_finalize(stmt);
239+
}
240+
241+
printf("\nTesting error/edge cases:\n");
242+
243+
// NULL input
244+
sqlite3_stmt *stmt = prepare(db, "SELECT u64text_display(NULL)");
245+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
246+
assert_int_equal(sqlite3_column_type(stmt, 0), SQLITE_NULL);
247+
sqlite3_finalize(stmt);
248+
printf(" NULL input -> NULL result: ✓\n");
249+
250+
// non-TEXT input (should return NULL)
251+
stmt = prepare(db, "SELECT u64text_display(42)");
252+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
253+
assert_int_equal(sqlite3_column_type(stmt, 0), SQLITE_NULL);
254+
sqlite3_finalize(stmt);
255+
printf(" INTEGER input -> NULL result: ✓\n");
256+
257+
// invalid u64text format (should return original string)
258+
stmt = prepare(db, "SELECT u64text_display('invalid_format')");
259+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
260+
const char *result = (const char *) sqlite3_column_text(stmt, 0);
261+
assert_string_equal(result, "invalid_format");
262+
sqlite3_finalize(stmt);
263+
printf(" Invalid format -> original string: ✓\n");
264+
265+
// wrong length (should return original string)
266+
stmt = prepare(db, "SELECT u64text_display('123456789')");
267+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
268+
result = (const char *) sqlite3_column_text(stmt, 0);
269+
assert_string_equal(result, "123456789");
270+
sqlite3_finalize(stmt);
271+
printf(" Wrong length -> original string: ✓\n");
272+
273+
// non-numeric characters (should return original string)
274+
stmt = prepare(db, "SELECT u64text_display('1234567890abcdef1234')");
275+
assert_int_equal(sqlite3_step(stmt), SQLITE_ROW);
276+
result = (const char *) sqlite3_column_text(stmt, 0);
277+
assert_string_equal(result, "1234567890abcdef1234");
278+
sqlite3_finalize(stmt);
279+
printf(" Non-numeric characters -> original string: ✓\n");
280+
}
281+
193282
int main(void) {
194283
const struct CMUnitTest tests[] = {cmocka_unit_test_setup_teardown(test_wrong_ordering_on_u64, open_db, close_db),
195284
cmocka_unit_test_setup_teardown(test_text_ordering_fails_without_collation, open_db, close_db),
196-
cmocka_unit_test_setup_teardown(test_u64_edgecases, open_db, close_db)};
285+
cmocka_unit_test_setup_teardown(test_u64_edgecases, open_db, close_db),
286+
cmocka_unit_test_setup_teardown(test_u64text_display_function, open_db, close_db)};
197287
return cmocka_run_group_tests(tests, NULL, NULL);
198288
}

0 commit comments

Comments
 (0)