Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_delete_sqlite3DeleteFrom.c211 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <sqlite3.h>4#include <string.h>5 6/* Global database handle for tests */7static sqlite3 *gDb = NULL;8 9/* Unity setUp/tearDown */10void setUp(void) {11  int rc = sqlite3_open(":memory:", &gDb);12  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);13 14  /* Create some tables and indexes used by tests */15  const char *sql =16    "PRAGMA foreign_keys=OFF;"17    "CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);"18    "CREATE INDEX t1_b ON t1(b);"19    "CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT);"20    "CREATE INDEX t2_b ON t2(b);"21    "CREATE TABLE wr(a INT, b INT, PRIMARY KEY(a,b)) WITHOUT ROWID;"22    "CREATE VIEW v1 AS SELECT * FROM t1;";23  char *err = NULL;24  rc = sqlite3_exec(gDb, sql, 0, 0, &err);25  if( rc!=SQLITE_OK ){26    TEST_FAIL_MESSAGE(err ? err : "Failed to initialize schema");27  }28  sqlite3_free(err);29}30 31void tearDown(void) {32  if( gDb ){33    sqlite3_close(gDb);34    gDb = NULL;35  }36}37 38/* Helpers */39 40/* Initialize a Parse object bound to gDb and ready for codegen */41static void initParse(Parse *p){42  memset(p, 0, sizeof(*p));43  p->db = gDb;44  gDb->pParse = p;45}46 47/* Build a SrcList referring to zDb.zTab for sqlite3DeleteFrom.48   Caller must not free the result; sqlite3DeleteFrom frees it. */49static SrcList *makeSrc(Parse *pParse, const char *zDb, const char *zTab){50  SrcList *p = sqlite3SrcListAppend(pParse, 0, 0, 0);51  TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3SrcListAppend failed");52  /* Fill in table and database names */53  p->a[0].zName = sqlite3DbStrDup(pParse->db, zTab);54  if( zDb ){55    p->a[0].u4.zDatabase = sqlite3DbStrDup(pParse->db, zDb);56  }57  return p;58}59 60/* Count occurrences of a specific opcode in the current VDBE for a Parse */61static int countOp(Parse *pParse, int op){62  Vdbe *v = pParse->pVdbe;63  if( v==0 ) return 0;64  int n = sqlite3VdbeCurrentAddr(pParse);65  int cnt = 0;66  for( int i = 0; i < n; i++ ){67    Op *o = sqlite3VdbeGetOp(v, i);68    if( o && o->opcode == op ) cnt++;69  }70  return cnt;71}72 73/* Optional: clean up VDBE after assertions to avoid holding onto memory */74static void freeVdbe(Parse *pParse){75  if( pParse->pVdbe ){76    sqlite3VdbeDelete(pParse->pVdbe);77    pParse->pVdbe = 0;78  }79}80 81/* Authorizer callbacks for tests */82static int authDeny(void *pUser, int action, const char *z1, const char *z2,83                    const char *z3, const char *z4){84  (void)pUser; (void)z1; (void)z2; (void)z3; (void)z4;85  if( action==SQLITE_DELETE ) return SQLITE_DENY;86  return SQLITE_OK;87}88static int authIgnore(void *pUser, int action, const char *z1, const char *z2,89                      const char *z3, const char *z4){90  (void)pUser; (void)z1; (void)z2; (void)z3; (void)z4;91  if( action==SQLITE_DELETE ) return SQLITE_IGNORE;92  return SQLITE_OK;93}94 95/* Tests */96 97/* Truncate optimization: DELETE FROM t1; generates OP_Clear ops and no OP_Delete */98void test_sqlite3DeleteFrom_truncate_optimization_generates_clear_ops(void){99  Parse parse;100  initParse(&parse);101 102  /* Ensure no count-changes flag to avoid extra tail ops */103  gDb->flags &= ~SQLITE_CountRows;104 105  SrcList *pSrc = makeSrc(&parse, "main", "t1");106  sqlite3DeleteFrom(&parse, pSrc, 0, 0, 0);107 108  /* Vdbe should be created */109  TEST_ASSERT_NOT_NULL(parse.pVdbe);110 111  int nClear = countOp(&parse, OP_Clear);112  int nDelete = countOp(&parse, OP_Delete);113 114  /* Expect at least two clears: table and one index */115  TEST_ASSERT_TRUE_MESSAGE(nClear >= 2, "Expected OP_Clear for table and index");116  TEST_ASSERT_EQUAL_INT_MESSAGE(0, nDelete, "Truncate optimization should not emit OP_Delete");117 118  freeVdbe(&parse);119}120 121/* Count changes enabled adds OP_ResultRow at end (DELETE FROM t2;) */122void test_sqlite3DeleteFrom_count_changes_adds_resultrow(void){123  Parse parse;124  initParse(&parse);125 126  /* Enable count-changes */127  unsigned priorFlags = gDb->flags;128  gDb->flags |= SQLITE_CountRows;129 130  SrcList *pSrc = makeSrc(&parse, "main", "t2");131  sqlite3DeleteFrom(&parse, pSrc, 0, 0, 0);132 133  TEST_ASSERT_NOT_NULL(parse.pVdbe);134 135  int nClear = countOp(&parse, OP_Clear);136  int nResultRow = countOp(&parse, OP_ResultRow);137 138  TEST_ASSERT_TRUE_MESSAGE(nClear >= 2, "Expected OP_Clear for table and index");139  TEST_ASSERT_EQUAL_INT_MESSAGE(1, nResultRow, "Expected one OP_ResultRow for count-changes");140 141  /* Restore flags */142  gDb->flags = priorFlags;143 144  freeVdbe(&parse);145}146 147/* Authorizer DENY causes early abort: no VDBE created */148void test_sqlite3DeleteFrom_authorizer_deny_aborts(void){149  Parse parse;150  initParse(&parse);151 152  int rc = sqlite3_set_authorizer(gDb, authDeny, 0);153  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);154 155  SrcList *pSrc = makeSrc(&parse, "main", "t1");156  sqlite3DeleteFrom(&parse, pSrc, 0, 0, 0);157 158  /* Expect no VDBE when denied */159  TEST_ASSERT_NULL(parse.pVdbe);160 161  /* Clear authorizer */162  sqlite3_set_authorizer(gDb, 0, 0);163}164 165/* Authorizer IGNORE disables truncate optimization, generating row-by-row delete (OP_Delete) and no OP_Clear */166void test_sqlite3DeleteFrom_authorizer_ignore_disables_truncate(void){167  Parse parse;168  initParse(&parse);169 170  int rc = sqlite3_set_authorizer(gDb, authIgnore, 0);171  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173  /* No WHERE clause but IGNORE should disable truncate optimization */174  SrcList *pSrc = makeSrc(&parse, "main", "t1");175  sqlite3DeleteFrom(&parse, pSrc, 0, 0, 0);176 177  TEST_ASSERT_NOT_NULL(parse.pVdbe);178 179  int nClear = countOp(&parse, OP_Clear);180  int nDelete = countOp(&parse, OP_Delete);181 182  TEST_ASSERT_EQUAL_INT_MESSAGE(0, nClear, "Truncate optimization should be disabled by SQLITE_IGNORE");183  TEST_ASSERT_TRUE_MESSAGE(nDelete >= 1, "Expected OP_Delete for row-by-row deletion");184 185  sqlite3_set_authorizer(gDb, 0, 0);186 187  freeVdbe(&parse);188}189 190/* Attempt to delete from a view without INSTEAD OF triggers should be rejected (read-only) */191void test_sqlite3DeleteFrom_readonly_view_rejected(void){192  Parse parse;193  initParse(&parse);194 195  SrcList *pSrc = makeSrc(&parse, "main", "v1"); /* v1 is a simple view on t1 */196  sqlite3DeleteFrom(&parse, pSrc, 0, 0, 0);197 198  /* Should abort early with no VDBE generated */199  TEST_ASSERT_NULL(parse.pVdbe);200}201 202/* Main runner */203int main(void){204  UNITY_BEGIN();205  RUN_TEST(test_sqlite3DeleteFrom_truncate_optimization_generates_clear_ops);206  RUN_TEST(test_sqlite3DeleteFrom_count_changes_adds_resultrow);207  RUN_TEST(test_sqlite3DeleteFrom_authorizer_deny_aborts);208  RUN_TEST(test_sqlite3DeleteFrom_authorizer_ignore_disables_truncate);209  RUN_TEST(test_sqlite3DeleteFrom_readonly_view_rejected);210  return UNITY_END();211}