Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_func_groupConcatValue.c203 linesDownload Raw Back to tests
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}