AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5#include <stdlib.h>6 7/* Wrapper provided in module under test */8extern void test_groupConcatValue(sqlite3_context *context);9 10static sqlite3 *gDb = NULL;11 12static void execSQL(const char *zSql){13 char *zErr = NULL;14 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr);15 if( rc!=SQLITE_OK ){16 const char *msg = zErr ? zErr : "SQL error";17 TEST_FAIL_MESSAGE(msg);18 }19 if( zErr ) sqlite3_free(zErr);20}21 22/* A scalar SQL function that directly calls the wrapper around groupConcatValue */23static void xCallWrapper(sqlite3_context *ctx, int argc, sqlite3_value **argv){24 (void)argc; (void)argv;25 test_groupConcatValue(ctx);26}27 28void setUp(void) {29 int rc = sqlite3_open(":memory:", &gDb);30 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);31 /* Register the helper wrapper-calling scalar function */32 rc = sqlite3_create_function(gDb, "call_wrapper0", 0, SQLITE_UTF8, NULL,33 xCallWrapper, NULL, NULL);34 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);35}36 37void tearDown(void) {38 if( gDb ){39 sqlite3_close(gDb);40 gDb = NULL;41 }42}43 44/* Directly call the wrapper function in a scalar context (no aggregate context).45 Expect a NULL result. */46void test_groupConcatValue_wrapper_direct_call_returns_NULL(void){47 const char *sql = "SELECT call_wrapper0();";48 sqlite3_stmt *pStmt = NULL;49 int rc = sqlite3_prepare_v2(gDb, sql, -1, &pStmt, 0);50 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);51 52 rc = sqlite3_step(pStmt);53 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);54 int typ = sqlite3_column_type(pStmt, 0);55 TEST_ASSERT_EQUAL_INT(SQLITE_NULL, typ);56 57 rc = sqlite3_step(pStmt);58 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);59 sqlite3_finalize(pStmt);60}61 62/* When the window frame is empty (no rows pass the FILTER), groupConcatValue63 should see no aggregate context and produce NULL. */64void test_groupConcatValue_empty_frame_results_in_NULL(void){65 execSQL("CREATE TABLE t(id INTEGER PRIMARY KEY, x TEXT);");66 execSQL("INSERT INTO t(id,x) VALUES(1,'a'),(2,'b'),(3,'c');");67 68 const char *sql =69 "SELECT group_concat(x) FILTER (WHERE 0) "70 "OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "71 "FROM t ORDER BY id;";72 sqlite3_stmt *pStmt = NULL;73 int rc = sqlite3_prepare_v2(gDb, sql, -1, &pStmt, 0);74 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);75 76 int row = 0;77 while( (rc = sqlite3_step(pStmt)) == SQLITE_ROW ){78 row++;79 int typ = sqlite3_column_type(pStmt, 0);80 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_NULL, typ, "Expected NULL for empty frame");81 }82 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);83 TEST_ASSERT_EQUAL_INT(3, row);84 sqlite3_finalize(pStmt);85}86 87/* If there are inputs (nAccum>0) but the accumulated text length is 088 (e.g., all empty strings with empty separator), groupConcatValue should89 return an empty string (not NULL). */90void test_groupConcatValue_zero_length_inputs_return_empty_string(void){91 execSQL("CREATE TABLE t2(id INTEGER PRIMARY KEY);");92 execSQL("INSERT INTO t2(id) VALUES(1),(2),(3);");93 94 const char *sql =95 "SELECT group_concat('', '') "96 "OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "97 "FROM t2 ORDER BY id;";98 sqlite3_stmt *pStmt = NULL;99 int rc = sqlite3_prepare_v2(gDb, sql, -1, &pStmt, 0);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 102 int row = 0;103 while( (rc = sqlite3_step(pStmt)) == SQLITE_ROW ){104 row++;105 int typ = sqlite3_column_type(pStmt, 0);106 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, typ);107 const unsigned char *z = sqlite3_column_text(pStmt, 0);108 TEST_ASSERT_NOT_NULL(z);109 /* Ensure it is the empty C-string */110 TEST_ASSERT_EQUAL_INT(0, (int)strlen((const char*)z));111 /* Column bytes may be 0 or 1 depending on internal representation */112 int nBytes = sqlite3_column_bytes(pStmt, 0);113 TEST_ASSERT_TRUE(nBytes==0 || nBytes==1);114 }115 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);116 TEST_ASSERT_EQUAL_INT(3, row);117 sqlite3_finalize(pStmt);118}119 120/* Normal case: non-empty inputs and separator producing expected concatenations. */121void test_groupConcatValue_normal_values(void){122 execSQL("CREATE TABLE t3(id INTEGER PRIMARY KEY, x TEXT);");123 execSQL("INSERT INTO t3(id,x) VALUES(1,'a'),(2,'b'),(3,'c');");124 125 const char *sql =126 "SELECT group_concat(x, '|') "127 "OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "128 "FROM t3 ORDER BY id;";129 sqlite3_stmt *pStmt = NULL;130 int rc = sqlite3_prepare_v2(gDb, sql, -1, &pStmt, 0);131 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);132 133 const char *expect[] = {"a", "a|b", "a|b|c"};134 int i = 0;135 while( (rc = sqlite3_step(pStmt)) == SQLITE_ROW ){136 TEST_ASSERT_TRUE(i < 3);137 int typ = sqlite3_column_type(pStmt, 0);138 TEST_ASSERT_EQUAL_INT(SQLITE_TEXT, typ);139 const unsigned char *z = sqlite3_column_text(pStmt, 0);140 TEST_ASSERT_NOT_NULL(z);141 TEST_ASSERT_EQUAL_STRING(expect[i], (const char*)z);142 i++;143 }144 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);145 TEST_ASSERT_EQUAL_INT(3, i);146 sqlite3_finalize(pStmt);147}148 149/* Error path: Force SQLITE_TOOBIG by setting a small SQLITE_LIMIT_LENGTH so that150 the concatenated result exceeds it, and verify that an error is reported. */151void test_groupConcatValue_reports_TOOBIG_error(void){152 execSQL("CREATE TABLE t4(id INTEGER PRIMARY KEY, x TEXT);");153 /* Insert values 'abcde' (length 5) so cumulative lengths are 5,10,15,20,... */154 execSQL("INSERT INTO t4(id,x) VALUES(1,'abcde');");155 execSQL("INSERT INTO t4(id,x) VALUES(2,'abcde');");156 execSQL("INSERT INTO t4(id,x) VALUES(3,'abcde');");157 execSQL("INSERT INTO t4(id,x) VALUES(4,'abcde');");158 execSQL("INSERT INTO t4(id,x) VALUES(5,'abcde');");159 160 /* Save old limit and set new small limit: 16 bytes so the 4th row (20 bytes) will overflow */161 int oldLimit = sqlite3_limit(gDb, SQLITE_LIMIT_LENGTH, 16);162 163 const char *sql =164 "SELECT group_concat(x, '') "165 "OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "166 "FROM t4 ORDER BY id;";167 sqlite3_stmt *pStmt = NULL;168 int rc = sqlite3_prepare_v2(gDb, sql, -1, &pStmt, 0);169 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);170 171 /* Expect rows for first 3 steps, then an error on the 4th step */172 int stepCount = 0;173 while( (rc = sqlite3_step(pStmt)) == SQLITE_ROW ){174 stepCount++;175 if( stepCount > 3 ){176 /* We should not get here with the low length limit */177 break;178 }179 }180 181 /* After 3 rows, the next step should have errored */182 TEST_ASSERT_TRUE(stepCount == 3);183 TEST_ASSERT_TRUE(rc == SQLITE_ERROR || rc == SQLITE_TOOBIG);184 185 int baseErr = sqlite3_errcode(gDb);186 int xErr = sqlite3_extended_errcode(gDb);187 TEST_ASSERT_TRUE_MESSAGE(baseErr==SQLITE_TOOBIG || xErr==SQLITE_TOOBIG,188 "Expected SQLITE_TOOBIG after exceeding length limit");189 190 sqlite3_finalize(pStmt);191 /* Restore the previous limit */192 sqlite3_limit(gDb, SQLITE_LIMIT_LENGTH, oldLimit);193}194 195int main(void) {196 UNITY_BEGIN();197 RUN_TEST(test_groupConcatValue_wrapper_direct_call_returns_NULL);198 RUN_TEST(test_groupConcatValue_empty_frame_results_in_NULL);199 RUN_TEST(test_groupConcatValue_zero_length_inputs_return_empty_string);200 RUN_TEST(test_groupConcatValue_normal_values);201 RUN_TEST(test_groupConcatValue_reports_TOOBIG_error);202 return UNITY_END();203}