Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_btree_querySharedCacheTableLock.c248 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* External wrapper provided for the static function under test */7extern int test_querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock);8 9/* Convenience macros for lock types */10#ifndef READ_LOCK11#define READ_LOCK 112#endif13#ifndef WRITE_LOCK14#define WRITE_LOCK 215#endif16 17/* Path for on-disk database to ensure shared-cache */18static const char *kTestDbPath = "test_qsctl_lock.db";19 20typedef struct TestEnv {21  sqlite3 *db1;22  sqlite3 *db2;23  Btree   *bt1;   /* Writer */24  Btree   *bt2;   /* Reader */25  BtShared *pBt;  /* Shared BtShared */26} TestEnv;27 28/* Helper: open two Btree handles on the same file in shared-cache mode */29static int open_shared_btrees(TestEnv *env){30  memset(env, 0, sizeof(*env));31  sqlite3_vfs *pVfs = sqlite3_vfs_find(0);32  int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE;33  int rc;34 35  /* Open two SQLite connections (not strictly used for SQL, but needed for Btree) */36  rc = sqlite3_open(kTestDbPath, &env->db1);37  if( rc!=SQLITE_OK ) return rc;38  rc = sqlite3_open(kTestDbPath, &env->db2);39  if( rc!=SQLITE_OK ) return rc;40 41  /* Open two low-level Btree handles pointing at the same file in shared-cache */42  rc = sqlite3BtreeOpen(pVfs, kTestDbPath, env->db1, &env->bt1, 0, vfsFlags);43  if( rc!=SQLITE_OK ) return rc;44  rc = sqlite3BtreeOpen(pVfs, kTestDbPath, env->db2, &env->bt2, 0, vfsFlags);45  if( rc!=SQLITE_OK ) return rc;46 47  /* Ensure both are sharable and share the same BtShared */48  TEST_ASSERT_NOT_NULL(env->bt1);49  TEST_ASSERT_NOT_NULL(env->bt2);50  TEST_ASSERT_NOT_NULL(env->bt1->pBt);51  TEST_ASSERT_NOT_NULL(env->bt2->pBt);52  env->pBt = env->bt1->pBt;53  TEST_ASSERT_TRUE_MESSAGE(env->bt1->sharable!=0, "bt1 must be sharable");54  TEST_ASSERT_TRUE_MESSAGE(env->bt2->sharable!=0, "bt2 must be sharable");55  TEST_ASSERT_EQUAL_PTR_MESSAGE(env->bt1->pBt, env->bt2->pBt, "BtShared must match");56  return SQLITE_OK;57}58 59/* Helper: cleanup */60static void close_shared_btrees(TestEnv *env){61  if( env->bt1 ){62    sqlite3BtreeClose(env->bt1);63    env->bt1 = NULL;64  }65  if( env->bt2 ){66    sqlite3BtreeClose(env->bt2);67    env->bt2 = NULL;68  }69  if( env->db1 ){70    sqlite3_close(env->db1);71    env->db1 = NULL;72  }73  if( env->db2 ){74    sqlite3_close(env->db2);75    env->db2 = NULL;76  }77  /* Remove the on-disk test database */78  sqlite3_os_init(); /* Ensure VFS initialized for unlink on some platforms */79  remove(kTestDbPath);80}81 82/* Helper: Link a BtLock into pBt->pLock for a given Btree connection */83static BtLock* inject_table_lock(BtShared *pBt, Btree *pOwner, Pgno iTable, u8 eLock){84  BtLock *pNew = (BtLock*)sqlite3MallocZero(sizeof(BtLock));85  TEST_ASSERT_NOT_NULL_MESSAGE(pNew, "Failed to allocate BtLock");86  pNew->pBtree = pOwner;87  pNew->iTable = iTable;88  pNew->eLock  = eLock;89  /* Link into head of lock list */90  pNew->pNext = pBt->pLock;91  pBt->pLock = pNew;92  return pNew;93}94 95/* Helper: Unlink and free an injected BtLock (only for those allocated here) */96static void remove_injected_lock(BtShared *pBt, BtLock *pLock){97  if( pLock==NULL ) return;98  BtLock **pp = &pBt->pLock;99  while( *pp ){100    if( *pp==pLock ){101      *pp = pLock->pNext;102      sqlite3_free(pLock);103      return;104    }105    pp = &((*pp)->pNext);106  }107  /* If not found, ignore */108}109 110/* Unity required */111void setUp(void) { }112void tearDown(void) { }113 114/* Test 1: No conflict for READ_LOCK returns SQLITE_OK */115void test_querySharedCacheTableLock_no_conflict_read_ok(void){116  TestEnv env; int rc;117  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));118 119  /* Query READ lock on schema table (1) with no locks present */120  sqlite3BtreeEnter(env.bt2);121  rc = test_querySharedCacheTableLock(env.bt2, 1, READ_LOCK);122  sqlite3BtreeLeave(env.bt2);123  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);124 125  close_shared_btrees(&env);126}127 128/* Test 2: Exclusive writer prevents other from obtaining READ_LOCK */129void test_querySharedCacheTableLock_exclusive_blocks_read(void){130  TestEnv env; int rc;131  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));132 133  /* Begin an exclusive write transaction on bt1 (writer) */134  rc = sqlite3BtreeBeginTrans(env.bt1, 2, 0); /* wrflag=2 => exclusive */135  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);136 137  sqlite3BtreeEnter(env.bt2);138  rc = test_querySharedCacheTableLock(env.bt2, 1, READ_LOCK);139  sqlite3BtreeLeave(env.bt2);140  TEST_ASSERT_EQUAL_INT(SQLITE_LOCKED_SHAREDCACHE, rc);141 142  /* Rollback writer to release exclusive */143  sqlite3BtreeRollback(env.bt1, SQLITE_OK, 0);144  close_shared_btrees(&env);145}146 147/* Test 3: READ_LOCK held by another connection does not block another READ_LOCK */148void test_querySharedCacheTableLock_readers_do_not_conflict(void){149  TestEnv env; int rc; BtLock *pL = NULL;150  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));151 152  /* Inject a READ_LOCK on table 1 for bt2 (reader) */153  pL = inject_table_lock(env.pBt, env.bt2, 1, READ_LOCK);154 155  /* Now bt1 queries for READ_LOCK on table 1 -> should be OK */156  sqlite3BtreeEnter(env.bt1);157  rc = test_querySharedCacheTableLock(env.bt1, 1, READ_LOCK);158  sqlite3BtreeLeave(env.bt1);159  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);160 161  /* Cleanup */162  remove_injected_lock(env.pBt, pL);163  close_shared_btrees(&env);164}165 166/* Test 4: READ_LOCK by another connection prevents WRITE_LOCK and sets BTS_PENDING */167void test_querySharedCacheTableLock_reader_blocks_writer_sets_pending(void){168  TestEnv env; int rc; BtLock *pL = NULL;169  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));170 171  /* Inject a READ_LOCK for bt2 (other connection) on table 1 */172  pL = inject_table_lock(env.pBt, env.bt2, 1, READ_LOCK);173 174  /* Begin write transaction on bt1 (writer) */175  rc = sqlite3BtreeBeginTrans(env.bt1, 1, 0); /* wrflag=1 */176  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);177 178  /* Query WRITE_LOCK on table 1 as writer */179  sqlite3BtreeEnter(env.bt1);180  rc = test_querySharedCacheTableLock(env.bt1, 1, WRITE_LOCK);181  sqlite3BtreeLeave(env.bt1);182 183  TEST_ASSERT_EQUAL_INT(SQLITE_LOCKED_SHAREDCACHE, rc);184  /* BTS_PENDING bit should be set */185  TEST_ASSERT_TRUE( (env.pBt->btsFlags & BTS_PENDING)!=0 );186 187  /* Cleanup: clear pending, remove lock, rollback writer */188  env.pBt->btsFlags &= ~BTS_PENDING;189  remove_injected_lock(env.pBt, pL);190  sqlite3BtreeRollback(env.bt1, SQLITE_OK, 0);191 192  close_shared_btrees(&env);193}194 195/* Test 5: Existing WRITE_LOCK on table blocks another READ_LOCK */196void test_querySharedCacheTableLock_writer_blocks_reader(void){197  TestEnv env; int rc; BtLock *pL = NULL;198  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));199 200  /* Inject a WRITE_LOCK for bt1 (or bt2) on table 1 to simulate writer holding a write lock */201  pL = inject_table_lock(env.pBt, env.bt1, 1, WRITE_LOCK);202 203  /* Now bt2 queries READ_LOCK on same table - should be locked */204  sqlite3BtreeEnter(env.bt2);205  rc = test_querySharedCacheTableLock(env.bt2, 1, READ_LOCK);206  sqlite3BtreeLeave(env.bt2);207 208  TEST_ASSERT_EQUAL_INT(SQLITE_LOCKED_SHAREDCACHE, rc);209 210  /* Cleanup */211  remove_injected_lock(env.pBt, pL);212  close_shared_btrees(&env);213}214 215/* Test 6: Locks on different tables do not conflict a WRITE request on another table */216void test_querySharedCacheTableLock_different_tables_ok(void){217  TestEnv env; int rc; BtLock *pL = NULL;218  TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_shared_btrees(&env));219 220  /* Inject READ lock on table 5 */221  pL = inject_table_lock(env.pBt, env.bt2, 5, READ_LOCK);222 223  /* Begin write transaction on bt1 (writer) */224  rc = sqlite3BtreeBeginTrans(env.bt1, 1, 0);225  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);226 227  /* Query WRITE on table 1, should be OK since conflicting lock is on table 5 */228  sqlite3BtreeEnter(env.bt1);229  rc = test_querySharedCacheTableLock(env.bt1, 1, WRITE_LOCK);230  sqlite3BtreeLeave(env.bt1);231  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);232 233  /* Cleanup */234  sqlite3BtreeRollback(env.bt1, SQLITE_OK, 0);235  remove_injected_lock(env.pBt, pL);236  close_shared_btrees(&env);237}238 239int main(void){240  UNITY_BEGIN();241  RUN_TEST(test_querySharedCacheTableLock_no_conflict_read_ok);242  RUN_TEST(test_querySharedCacheTableLock_exclusive_blocks_read);243  RUN_TEST(test_querySharedCacheTableLock_readers_do_not_conflict);244  RUN_TEST(test_querySharedCacheTableLock_reader_blocks_writer_sets_pending);245  RUN_TEST(test_querySharedCacheTableLock_writer_blocks_reader);246  RUN_TEST(test_querySharedCacheTableLock_different_tables_ok);247  return UNITY_END();248}