AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Helper to initialize a Parse object */9static void initParse(Parse *pParse, sqlite3 *db){10 memset(pParse, 0, sizeof(*pParse));11 pParse->db = db;12}13 14/* Choose a value representing IMMEDIATE that is distinct from TK_DEFERRED and TK_EXCLUSIVE */15static int immediateType(void){16#ifdef TK_IMMEDIATE17 return TK_IMMEDIATE;18#else19 return 12345; /* Arbitrary value not equal to TK_DEFERRED or TK_EXCLUSIVE */20#endif21}22 23void setUp(void) {24 /* No global setup */25}26 27void tearDown(void) {28 /* No global cleanup */29}30 31static void assertHasOnlyAutoCommit(Parse *pParse){32 TEST_ASSERT_NOT_NULL(pParse->pVdbe);33 Vdbe *v = pParse->pVdbe;34 TEST_ASSERT_NOT_NULL(v);35 TEST_ASSERT_EQUAL_INT(1, v->nOp);36 TEST_ASSERT_EQUAL_INT(OP_AutoCommit, v->aOp[0].opcode);37}38 39/* Verify that the Vdbe contains OP_Transaction for each db index and p2 matches expected mapping.40 expectedModeNonRO is either 1 (immediate) or 2 (exclusive). Readonly btrees should use 0. */41static void assertTransactionsThenAutoCommit(sqlite3 *db, Parse *pParse, int expectedModeNonRO){42 TEST_ASSERT_NOT_NULL(pParse->pVdbe);43 Vdbe *v = pParse->pVdbe;44 TEST_ASSERT_NOT_NULL(v);45 46 int nDb = db->nDb;47 TEST_ASSERT_GREATER_THAN_INT(0, nDb <= 0 ? 1 : 0); /* sanity: nDb > 0 */48 TEST_ASSERT_EQUAL_INT(nDb + 1, v->nOp);49 50 for(int i=0; i<nDb; i++){51 Op op = v->aOp[i];52 TEST_ASSERT_EQUAL_INT(OP_Transaction, op.opcode);53 TEST_ASSERT_EQUAL_INT(i, op.p1);54 55 Btree *pBt = db->aDb[i].pBt;56 int isRO = pBt ? sqlite3BtreeIsReadonly(pBt) : 0;57 int expectedP2 = isRO ? 0 : expectedModeNonRO;58 TEST_ASSERT_EQUAL_INT(expectedP2, op.p2);59 }60 61 Op last = v->aOp[nDb];62 TEST_ASSERT_EQUAL_INT(OP_AutoCommit, last.opcode);63}64 65/* Test: Deferred transactions add only OP_AutoCommit */66void test_sqlite3BeginTransaction_deferred_only_autocommit(void){67 sqlite3 *db = NULL;68 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));69 TEST_ASSERT_NOT_NULL(db);70 71 Parse p;72 initParse(&p, db);73 74 sqlite3BeginTransaction(&p, TK_DEFERRED);75 76 assertHasOnlyAutoCommit(&p);77 78 /* Cleanup Vdbe and db */79 if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);80 p.pVdbe = NULL;81 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));82}83 84/* Test: Immediate-like transactions generate write txn ops (p2=1) for non-readonly dbs */85void test_sqlite3BeginTransaction_immediate_generates_ops_for_all_dbs(void){86 sqlite3 *db = NULL;87 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));88 TEST_ASSERT_NOT_NULL(db);89 90 Parse p;91 initParse(&p, db);92 93 int tImm = immediateType();94 /* Ensure our chosen immediate type is not TK_DEFERRED or TK_EXCLUSIVE */95 TEST_ASSERT_TRUE(tImm != TK_DEFERRED);96 TEST_ASSERT_TRUE(tImm != TK_EXCLUSIVE);97 98 sqlite3BeginTransaction(&p, tImm);99 assertTransactionsThenAutoCommit(db, &p, /*expectedModeNonRO=*/1);100 101 if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);102 p.pVdbe = NULL;103 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));104}105 106/* Test: Exclusive transactions generate p2=2 for non-readonly dbs */107void test_sqlite3BeginTransaction_exclusive_generates_exclusive_ops(void){108 sqlite3 *db = NULL;109 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));110 TEST_ASSERT_NOT_NULL(db);111 112 Parse p;113 initParse(&p, db);114 115 sqlite3BeginTransaction(&p, TK_EXCLUSIVE);116 assertTransactionsThenAutoCommit(db, &p, /*expectedModeNonRO=*/2);117 118 if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);119 p.pVdbe = NULL;120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));121}122 123/* Test: Read-only main db results in p2==0 for main db (index 0) */124void test_sqlite3BeginTransaction_respects_readonly_btree_main_db(void){125 const char *path = "test_begin_txn.sqlite";126 127 /* Create a database file */128 sqlite3 *dbRW = NULL;129 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(path, &dbRW));130 TEST_ASSERT_NOT_NULL(dbRW);131 /* Ensure file is created; no schema needed */132 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(dbRW));133 134 /* Reopen read-only */135 sqlite3 *dbRO = NULL;136 TEST_ASSERT_EQUAL_INT(SQLITE_OK,137 sqlite3_open_v2(path, &dbRO, SQLITE_OPEN_READONLY, NULL));138 TEST_ASSERT_NOT_NULL(dbRO);139 140 Parse p;141 initParse(&p, dbRO);142 143 int tImm = immediateType();144 sqlite3BeginTransaction(&p, tImm);145 146 /* Verify transactions for all dbs and that main (index 0) has p2==0 */147 assertTransactionsThenAutoCommit(dbRO, &p, /*expectedModeNonRO=*/1);148 149 /* Explicitly check main db is readonly => first op p2==0 */150 TEST_ASSERT_NOT_NULL(p.pVdbe);151 TEST_ASSERT_GREATER_THAN_INT(0, p.pVdbe->nOp <= 0 ? 1 : 0);152 TEST_ASSERT_EQUAL_INT(OP_Transaction, p.pVdbe->aOp[0].opcode);153 TEST_ASSERT_TRUE(sqlite3BtreeIsReadonly(dbRO->aDb[0].pBt) != 0);154 TEST_ASSERT_EQUAL_INT(0, p.pVdbe->aOp[0].p2);155 156 if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);157 p.pVdbe = NULL;158 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(dbRO));159 160 /* Clean up file */161 remove(path);162}163 164#ifndef SQLITE_OMIT_AUTHORIZATION165static int deny_begin_authorizer(void *p, int op, const char *z1,166 const char *z2, const char *z3, const char *z4){167 (void)p; (void)z1; (void)z2; (void)z3; (void)z4;168 if( op==SQLITE_TRANSACTION ){169 return SQLITE_DENY;170 }171 return SQLITE_OK;172}173 174/* Test: Authorization denial prevents Vdbe creation and any ops */175void test_sqlite3BeginTransaction_authorizer_denies_no_vdbe_created(void){176 sqlite3 *db = NULL;177 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));178 TEST_ASSERT_NOT_NULL(db);179 180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_set_authorizer(db, deny_begin_authorizer, NULL));181 182 Parse p;183 initParse(&p, db);184 185 sqlite3BeginTransaction(&p, immediateType());186 187 /* Should return before creating any Vdbe */188 TEST_ASSERT_NULL(p.pVdbe);189 190 /* Cleanup */191 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_set_authorizer(db, NULL, NULL));192 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));193}194#else195void test_sqlite3BeginTransaction_authorizer_denies_no_vdbe_created(void){196 TEST_IGNORE_MESSAGE("Authorization omitted in this build; skipping authorizer test.");197}198#endif199 200int main(void){201 UNITY_BEGIN();202 203 RUN_TEST(test_sqlite3BeginTransaction_deferred_only_autocommit);204 RUN_TEST(test_sqlite3BeginTransaction_immediate_generates_ops_for_all_dbs);205 RUN_TEST(test_sqlite3BeginTransaction_exclusive_generates_exclusive_ops);206 RUN_TEST(test_sqlite3BeginTransaction_respects_readonly_btree_main_db);207 RUN_TEST(test_sqlite3BeginTransaction_authorizer_denies_no_vdbe_created);208 209 return UNITY_END();210}