AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Wrapper provided for static function under test */8extern void test_sqlite3ErrorIfNotEmpty(9 Parse *pParse,10 const char *zDb,11 const char *zTab,12 const char *zErr13);14 15static sqlite3 *gDb = NULL;16 17/* Helpers */18static void exec_or_fail(const char *zSql){19 char *zErr = NULL;20 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr);21 if( rc!=SQLITE_OK ){22 fprintf(stderr, "SQL failed: %s\n rc=%d err=%s\n", zSql, rc, zErr?zErr:"(null)");23 }24 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "exec_or_fail SQL error");25 if( zErr ) sqlite3_free(zErr);26}27 28static void init_parse(Parse *p){29 memset(p, 0, sizeof(*p));30 p->db = gDb;31}32 33static void cleanup_parse(Parse *p){34 if( p->pVdbe ){35 sqlite3VdbeDelete(p->pVdbe);36 p->pVdbe = NULL;37 }38 if( p->zErrMsg ){39 sqlite3_free(p->zErrMsg);40 p->zErrMsg = NULL;41 }42}43 44/* Unity required hooks */45void setUp(void) {46 int rc = sqlite3_open(":memory:", &gDb);47 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);48}49 50void tearDown(void) {51 if( gDb ){52 int rc = sqlite3_close(gDb);53 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);54 gDb = NULL;55 }56}57 58/* Tests */59 60void test_sqlite3ErrorIfNotEmpty_empty_table_executes_without_error(void){61 /* Setup schema: empty table */62 exec_or_fail("CREATE TABLE t(a)");63 64 /* Call target wrapper to build nested parse (ensures it handles normal names) */65 Parse s = {0};66 init_parse(&s);67 const char *zErr = "table is not empty";68 test_sqlite3ErrorIfNotEmpty(&s, "main", "t", zErr);69 cleanup_parse(&s);70 71 /* Validate functional intent by running the equivalent SQL directly */72 char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",73 zErr, "main", "t");74 TEST_ASSERT_NOT_NULL(zSql);75 76 char *zExecErr = NULL;77 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);78 /* On empty table, this should succeed with SQLITE_OK and no error message */79 if( rc!=SQLITE_OK && zExecErr ){80 fprintf(stderr, "Unexpected error on empty table: %s\n", zExecErr);81 }82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);83 TEST_ASSERT_NULL(zExecErr);84 85 sqlite3_free(zSql);86}87 88void test_sqlite3ErrorIfNotEmpty_nonempty_table_raises_error_with_message(void){89 /* Setup schema: table with one row */90 exec_or_fail("CREATE TABLE t2(a)");91 exec_or_fail("INSERT INTO t2(a) VALUES(123)");92 93 /* Call target wrapper */94 Parse s = {0};95 init_parse(&s);96 const char *zErr = "cannot proceed: table has rows";97 test_sqlite3ErrorIfNotEmpty(&s, "main", "t2", zErr);98 cleanup_parse(&s);99 100 /* Validate behavior by executing equivalent SQL directly */101 char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",102 zErr, "main", "t2");103 TEST_ASSERT_NOT_NULL(zSql);104 105 char *zExecErr = NULL;106 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);107 /* Expect an error. Historically, RAISE(ABORT, ...) maps to a constraint error.108 So just assert that it is not SQLITE_OK and that the message contains our text. */109 TEST_ASSERT_NOT_EQUAL(INT, SQLITE_OK, rc);110 TEST_ASSERT_NOT_NULL(zExecErr);111 TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zExecErr, zErr), "Error message should contain the provided zErr text");112 113 sqlite3_free(zExecErr);114 sqlite3_free(zSql);115}116 117void test_sqlite3ErrorIfNotEmpty_identifier_quoting_with_special_table_name(void){118 /* Create a table name with quotes and a dot to verify proper identifier escaping */119 const char *zTab = "weird\"name.dot";120 char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\"(a)", zTab);121 TEST_ASSERT_NOT_NULL(zCreate);122 exec_or_fail(zCreate);123 sqlite3_free(zCreate);124 125 /* Call target wrapper using an error message with embedded quotes to exercise %Q */126 Parse s = {0};127 init_parse(&s);128 const char *zErr = "msg with \"quotes\" and 'single quotes'";129 test_sqlite3ErrorIfNotEmpty(&s, "main", zTab, zErr);130 cleanup_parse(&s);131 132 /* Empty table: executing the equivalent SQL should succeed */133 char *zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",134 zErr, "main", zTab);135 TEST_ASSERT_NOT_NULL(zSql);136 137 char *zExecErr = NULL;138 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);139 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);140 TEST_ASSERT_NULL(zExecErr);141 142 sqlite3_free(zSql);143 144 /* Now insert a row and confirm it fails with the message */145 char *zInsert = sqlite3_mprintf("INSERT INTO \"%w\"(a) VALUES(1)", zTab);146 TEST_ASSERT_NOT_NULL(zInsert);147 exec_or_fail(zInsert);148 sqlite3_free(zInsert);149 150 zSql = sqlite3_mprintf("SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",151 zErr, "main", zTab);152 TEST_ASSERT_NOT_NULL(zSql);153 rc = sqlite3_exec(gDb, zSql, 0, 0, &zExecErr);154 TEST_ASSERT_NOT_EQUAL(INT, SQLITE_OK, rc);155 TEST_ASSERT_NOT_NULL(zExecErr);156 TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zExecErr, zErr), "Error message should contain zErr");157 sqlite3_free(zExecErr);158 sqlite3_free(zSql);159}160 161void test_sqlite3ErrorIfNotEmpty_nonexistent_table_sets_parse_error(void){162 /* Call target wrapper for a table that does not exist to ensure parse error captured */163 Parse s = {0};164 init_parse(&s);165 test_sqlite3ErrorIfNotEmpty(&s, "main", "no_such_table_xyz", "any error");166 /* We expect an error recorded by the nested parse */167 TEST_ASSERT_TRUE(s.nErr > 0);168 TEST_ASSERT_NOT_NULL(s.zErrMsg);169 /* Avoid asserting exact text; just ensure an error was captured */170 cleanup_parse(&s);171}172 173int main(void) {174 UNITY_BEGIN();175 176 RUN_TEST(test_sqlite3ErrorIfNotEmpty_empty_table_executes_without_error);177 RUN_TEST(test_sqlite3ErrorIfNotEmpty_nonempty_table_raises_error_with_message);178 RUN_TEST(test_sqlite3ErrorIfNotEmpty_identifier_quoting_with_special_table_name);179 RUN_TEST(test_sqlite3ErrorIfNotEmpty_nonexistent_table_sets_parse_error);180 181 return UNITY_END();182}