AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* Wrapper declared by the build system for the static target function */7extern int test_hasSharedCacheTableLock(Btree *pBtree, Pgno iRoot, int isIndex, int eLockType);8 9/* Helpers to extract root-page numbers from the loaded schema */10static Pgno findTableRoot(sqlite3 *db, const char *zName){11 Schema *pSchema = db->aDb[0].pSchema;12 if( pSchema==0 ) return 0;13 for(HashElem *p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){14 Table *pTab = (Table*)sqliteHashData(p);15 if( pTab && pTab->zName && 0==sqlite3StrICmp(pTab->zName, zName) ){16 return (Pgno)pTab->tnum;17 }18 }19 return 0;20}21 22static Pgno findIndexRoot(sqlite3 *db, const char *zName){23 Schema *pSchema = db->aDb[0].pSchema;24 if( pSchema==0 ) return 0;25 for(HashElem *p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){26 Index *pIdx = (Index*)sqliteHashData(p);27 if( pIdx && pIdx->zName && 0==sqlite3StrICmp(pIdx->zName, zName) ){28 return (Pgno)pIdx->tnum;29 }30 }31 return 0;32}33 34/* Simple helper to run SQL and assert OK */35static void execSQL(sqlite3 *db, const char *sql){36 char *zErr = 0;37 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);38 if( rc!=SQLITE_OK ){39 fprintf(stderr, "SQL error: %s\n", zErr ? zErr : "(null)");40 }41 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);42 sqlite3_free(zErr);43}44 45void setUp(void) {46 /* no-op */47}48void tearDown(void) {49 /* no-op */50}51 52/* Test 1: READ_LOCK returns true under read-uncommitted regardless of locks */53void test_hasSharedCacheTableLock_read_uncommitted_READ_LOCK_true(void){54 sqlite3 *db = 0;55 int rc;56 sqlite3_enable_shared_cache(1);57 rc = sqlite3_open("file:lock_ru.db?cache=shared", &db);58 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);59 /* Put connection into read-uncommitted mode */60 execSQL(db, "PRAGMA read_uncommitted=ON");61 62 Btree *pBt = db->aDb[0].pBt;63 /* Root page 1 (schema). Expect true because READ_LOCK && ReadUncommitted. */64 int ok = test_hasSharedCacheTableLock(pBt, 1, 0, READ_LOCK);65 TEST_ASSERT_EQUAL_INT(1, ok);66 67 sqlite3_close(db);68}69 70/* Test 2: isIndex==true and schema is not loaded -> returns true */71void test_hasSharedCacheTableLock_index_schema_not_loaded_true(void){72 sqlite3 *db = 0;73 int rc;74 sqlite3_enable_shared_cache(1);75 rc = sqlite3_open("file:lock_schema_not_loaded.db?cache=shared", &db);76 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);77 78 /* Without running any SQL, schema should not be loaded (pSchema == NULL) */79 Btree *pBt = db->aDb[0].pBt;80 /* Arbitrary root page argument; isIndex=1; READ_LOCK */81 int ok = test_hasSharedCacheTableLock(pBt, 2, 1, READ_LOCK);82 TEST_ASSERT_EQUAL_INT(1, ok);83 84 sqlite3_close(db);85}86 87/* Test 3: With write-lock on a table, hasSharedCacheTableLock is true for both88** (a) table write-lock, and (b) index write-lock via associated table.89** Also verify a second connection without locks returns false. */90void test_hasSharedCacheTableLock_write_lock_on_table_and_index(void){91 sqlite3 *db1 = 0;92 sqlite3 *db2 = 0;93 int rc;94 95 sqlite3_enable_shared_cache(1);96 rc = sqlite3_open("file:lock3.db?cache=shared", &db1);97 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);98 rc = sqlite3_open("file:lock3.db?cache=shared", &db2);99 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);100 101 /* Create a table and index */102 execSQL(db1, "PRAGMA journal_mode=memory;");103 execSQL(db1, "BEGIN;");104 execSQL(db1, "CREATE TABLE t(a);");105 execSQL(db1, "CREATE INDEX i1 ON t(a);");106 execSQL(db1, "COMMIT;");107 108 /* Schema now loaded; find root pages */109 Pgno tRoot = findTableRoot(db1, "t");110 Pgno iRoot = findIndexRoot(db1, "i1");111 TEST_ASSERT(tRoot>1);112 TEST_ASSERT(iRoot>1);113 114 Btree *p1 = db1->aDb[0].pBt;115 Btree *p2 = db2->aDb[0].pBt;116 117 /* Begin a write transaction on db1 and obtain a write-lock on table t */118 rc = sqlite3BtreeBeginTrans(p1, 1, 0);119 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);120 rc = sqlite3BtreeLockTable(p1, tRoot, 1);121 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);122 123 /* Positive checks on p1: table write-lock and index write-lock via table */124 int ok_tbl_w = test_hasSharedCacheTableLock(p1, tRoot, 0, WRITE_LOCK);125 TEST_ASSERT_EQUAL_INT(1, ok_tbl_w);126 int ok_idx_w = test_hasSharedCacheTableLock(p1, iRoot, 1, WRITE_LOCK);127 TEST_ASSERT_EQUAL_INT(1, ok_idx_w);128 129 /* Negative checks on p2 (no locks held): should be false */130 int no_tbl_w = test_hasSharedCacheTableLock(p2, tRoot, 0, WRITE_LOCK);131 TEST_ASSERT_EQUAL_INT(0, no_tbl_w);132 int no_idx_r = test_hasSharedCacheTableLock(p2, iRoot, 1, READ_LOCK);133 TEST_ASSERT_EQUAL_INT(0, no_idx_r);134 135 /* Cleanup */136 sqlite3_close(db2);137 sqlite3_close(db1);138}139 140/* Test 4: Schema (root page 1) write-lock is sufficient for any table/index */141void test_hasSharedCacheTableLock_schema_write_lock_satisfies(void){142 sqlite3 *db = 0;143 int rc;144 sqlite3_enable_shared_cache(1);145 rc = sqlite3_open("file:lock_schema.db?cache=shared", &db);146 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);147 148 execSQL(db, "CREATE TABLE t(a); CREATE INDEX i1 ON t(a);");149 Pgno tRoot = findTableRoot(db, "t");150 Pgno iRoot = findIndexRoot(db, "i1");151 TEST_ASSERT(tRoot>1);152 TEST_ASSERT(iRoot>1);153 154 Btree *p = db->aDb[0].pBt;155 rc = sqlite3BtreeBeginTrans(p, 1, 0);156 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);157 158 /* Acquire write lock on schema table (root page 1) */159 rc = sqlite3BtreeLockTable(p, 1, 1);160 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);161 162 /* Now both table and index should be satisfied for WRITE_LOCK */163 int ok_tbl = test_hasSharedCacheTableLock(p, tRoot, 0, WRITE_LOCK);164 int ok_idx = test_hasSharedCacheTableLock(p, iRoot, 1, WRITE_LOCK);165 TEST_ASSERT_EQUAL_INT(1, ok_tbl);166 TEST_ASSERT_EQUAL_INT(1, ok_idx);167 168 sqlite3_close(db);169}170 171int main(void){172 UNITY_BEGIN();173 174 RUN_TEST(test_hasSharedCacheTableLock_read_uncommitted_READ_LOCK_true);175 RUN_TEST(test_hasSharedCacheTableLock_index_schema_not_loaded_true);176 RUN_TEST(test_hasSharedCacheTableLock_write_lock_on_table_and_index);177 RUN_TEST(test_hasSharedCacheTableLock_schema_write_lock_satisfies);178 179 return UNITY_END();180}