AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdio.h>4#include <string.h>5#include <stdlib.h>6 7/* Helper: initialize a Parse struct with a given db */8static void initParse(Parse *p, sqlite3 *db){9 memset(p, 0, sizeof(*p));10 p->db = db;11}12 13/* Helper: open a database with shared cache enabled and return whether14** the main btree is sharable. Uses a URI filename with cache=shared.15*/16static int open_shared_db(sqlite3 **ppDb, const char *fname){17 int rc;18 char uri[256];19 sqlite3_enable_shared_cache(1);20 snprintf(uri, sizeof(uri), "file:%s?cache=shared&mode=rwc", fname);21 rc = sqlite3_open_v2(uri, ppDb,22 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, 0);23 if( rc!=SQLITE_OK ){24 *ppDb = NULL;25 return 0;26 }27 /* Confirm sharable btree if possible */28 if( (*ppDb)->aDb[0].pBt==0 ) return 0;29 return sqlite3BtreeSharable((*ppDb)->aDb[0].pBt)!=0;30}31 32/* Helper: open a non-shared-cache in-memory db */33static int open_noshare_memory_db(sqlite3 **ppDb){34 int rc;35 sqlite3_enable_shared_cache(0);36 rc = sqlite3_open(":memory:", ppDb);37 return rc==SQLITE_OK;38}39 40/* Helper: free TableLock array on a Parse/top-level */41static void freeTableLocks(Parse *p){42 if( p && p->db && p->aTableLock ){43 sqlite3DbFree(p->db, p->aTableLock);44 p->aTableLock = 0;45 p->nTableLock = 0;46 }47}48 49void setUp(void) {50 /* No global setup required */51}52 53void tearDown(void) {54 /* No global teardown required */55}56 57/* Test: iDb==1 (TEMP database) is always ignored */58void test_sqlite3TableLock_noop_when_temp_db(void){59 sqlite3 *db = NULL;60 TEST_ASSERT_TRUE_MESSAGE(open_noshare_memory_db(&db), "Failed to open db");61 Parse p;62 initParse(&p, db);63 64 TEST_ASSERT_EQUAL_INT(0, p.nTableLock);65 sqlite3TableLock(&p, 1 /* TEMP */, 5 /* tab pgno */, 1 /* write */, "temp.t");66 TEST_ASSERT_EQUAL_INT(0, p.nTableLock);67 TEST_ASSERT_EQUAL_PTR(NULL, p.aTableLock);68 69 sqlite3_close(db);70}71 72/* Test: When btree is not sharable, no lock is recorded */73void test_sqlite3TableLock_noop_when_not_sharable(void){74 sqlite3 *db = NULL;75 TEST_ASSERT_TRUE_MESSAGE(open_noshare_memory_db(&db), "Failed to open db");76 /* Ensure btree is not sharable */77 if( db->aDb[0].pBt ){78 TEST_ASSERT_EQUAL_INT(0, sqlite3BtreeSharable(db->aDb[0].pBt));79 }80 81 Parse p;82 initParse(&p, db);83 84 sqlite3TableLock(&p, 0 /* main */, 3, 1, "main.t");85 TEST_ASSERT_EQUAL_INT(0, p.nTableLock);86 TEST_ASSERT_EQUAL_PTR(NULL, p.aTableLock);87 88 sqlite3_close(db);89}90 91/* Test: With shared-cache enabled, a lock is recorded */92void test_sqlite3TableLock_records_lock_when_sharable(void){93 sqlite3 *db = NULL;94 int sharable = open_shared_db(&db, "test_sc_1.db");95 if( !sharable ){96 if( db ) sqlite3_close(db);97 TEST_IGNORE_MESSAGE("Shared-cache not available; skipping test.");98 }99 100 /* Precondition: btree sharable */101 TEST_ASSERT_NOT_NULL(db->aDb[0].pBt);102 TEST_ASSERT_NOT_EQUAL(0, sqlite3BtreeSharable(db->aDb[0].pBt));103 104 Parse p;105 initParse(&p, db);106 107 const char *zName = "main.table1";108 sqlite3TableLock(&p, 0, 42, 0, zName);109 110 TEST_ASSERT_EQUAL_INT(1, p.nTableLock);111 TEST_ASSERT_NOT_NULL(p.aTableLock);112 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[0].iDb);113 TEST_ASSERT_EQUAL_INT(42, (int)p.aTableLock[0].iTab);114 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[0].isWriteLock);115 TEST_ASSERT_EQUAL_PTR(zName, p.aTableLock[0].zLockName);116 117 freeTableLocks(&p);118 sqlite3_close(db);119}120 121/* Test: Duplicate (iDb,iTab) entries are merged and write-lock is upgraded */122void test_sqlite3TableLock_merges_and_upgrades_write_lock(void){123 sqlite3 *db = NULL;124 int sharable = open_shared_db(&db, "test_sc_2.db");125 if( !sharable ){126 if( db ) sqlite3_close(db);127 TEST_IGNORE_MESSAGE("Shared-cache not available; skipping test.");128 }129 130 Parse p;131 initParse(&p, db);132 133 const char *zFirst = "main.tA.first";134 const char *zSecond = "main.tA.second";135 sqlite3TableLock(&p, 0, 100, 0 /* read */, zFirst);136 sqlite3TableLock(&p, 0, 100, 1 /* write */, zSecond); /* Should merge */137 138 TEST_ASSERT_EQUAL_INT(1, p.nTableLock);139 TEST_ASSERT_NOT_NULL(p.aTableLock);140 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[0].iDb);141 TEST_ASSERT_EQUAL_INT(100, (int)p.aTableLock[0].iTab);142 TEST_ASSERT_EQUAL_INT(1, p.aTableLock[0].isWriteLock);143 /* Name should remain from first call */144 TEST_ASSERT_EQUAL_PTR(zFirst, p.aTableLock[0].zLockName);145 146 /* Another duplicate that does not downgrade */147 sqlite3TableLock(&p, 0, 100, 0 /* read */, "ignored");148 TEST_ASSERT_EQUAL_INT(1, p.nTableLock);149 TEST_ASSERT_EQUAL_INT(1, p.aTableLock[0].isWriteLock);150 TEST_ASSERT_EQUAL_PTR(zFirst, p.aTableLock[0].zLockName);151 152 freeTableLocks(&p);153 sqlite3_close(db);154}155 156/* Test: Distinct (iDb,iTab) entries both get recorded */157void test_sqlite3TableLock_multiple_distinct_entries(void){158 sqlite3 *db = NULL;159 int sharable = open_shared_db(&db, "test_sc_3.db");160 if( !sharable ){161 if( db ) sqlite3_close(db);162 TEST_IGNORE_MESSAGE("Shared-cache not available; skipping test.");163 }164 165 Parse p;166 initParse(&p, db);167 168 sqlite3TableLock(&p, 0, 7, 0, "t7");169 sqlite3TableLock(&p, 0, 8, 1, "t8");170 171 TEST_ASSERT_EQUAL_INT(2, p.nTableLock);172 TEST_ASSERT_NOT_NULL(p.aTableLock);173 174 /* First entry */175 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[0].iDb);176 TEST_ASSERT_EQUAL_INT(7, (int)p.aTableLock[0].iTab);177 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[0].isWriteLock);178 TEST_ASSERT_EQUAL_PTR("t7", p.aTableLock[0].zLockName);179 180 /* Second entry */181 TEST_ASSERT_EQUAL_INT(0, p.aTableLock[1].iDb);182 TEST_ASSERT_EQUAL_INT(8, (int)p.aTableLock[1].iTab);183 TEST_ASSERT_EQUAL_INT(1, p.aTableLock[1].isWriteLock);184 TEST_ASSERT_EQUAL_PTR("t8", p.aTableLock[1].zLockName);185 186 freeTableLocks(&p);187 sqlite3_close(db);188}189 190/* Test: Locks are recorded on the top-level Parse when called with a child Parse */191void test_sqlite3TableLock_records_on_toplevel(void){192 sqlite3 *db = NULL;193 int sharable = open_shared_db(&db, "test_sc_4.db");194 if( !sharable ){195 if( db ) sqlite3_close(db);196 TEST_IGNORE_MESSAGE("Shared-cache not available; skipping test.");197 }198 199 Parse top, child;200 initParse(&top, db);201 initParse(&child, db);202 child.pToplevel = ⊤203 204 sqlite3TableLock(&child, 0, 55, 1, "child.to.top");205 206 TEST_ASSERT_EQUAL_INT(1, top.nTableLock);207 TEST_ASSERT_NOT_NULL(top.aTableLock);208 TEST_ASSERT_EQUAL_INT(0, top.aTableLock[0].iDb);209 TEST_ASSERT_EQUAL_INT(55, (int)top.aTableLock[0].iTab);210 TEST_ASSERT_EQUAL_INT(1, top.aTableLock[0].isWriteLock);211 TEST_ASSERT_EQUAL_PTR("child.to.top", top.aTableLock[0].zLockName);212 213 /* Child should remain untouched */214 TEST_ASSERT_EQUAL_INT(0, child.nTableLock);215 TEST_ASSERT_NULL(child.aTableLock);216 217 freeTableLocks(&top);218 sqlite3_close(db);219}220 221int main(void){222 UNITY_BEGIN();223 RUN_TEST(test_sqlite3TableLock_noop_when_temp_db);224 RUN_TEST(test_sqlite3TableLock_noop_when_not_sharable);225 RUN_TEST(test_sqlite3TableLock_records_lock_when_sharable);226 RUN_TEST(test_sqlite3TableLock_merges_and_upgrades_write_lock);227 RUN_TEST(test_sqlite3TableLock_multiple_distinct_entries);228 RUN_TEST(test_sqlite3TableLock_records_on_toplevel);229 return UNITY_END();230}