AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <string.h>6#include <stdlib.h>7 8extern int test_hasReadConflicts(Btree *pBtree, Pgno iRoot);9 10static void assert_rc_ok(int rc, const char *msg){11 if( rc!=SQLITE_OK ){12 char buf[256];13 snprintf(buf, sizeof(buf), "%s (rc=%d)", msg, rc);14 TEST_FAIL_MESSAGE(buf);15 }16}17 18/* Helper: open two Btree handles on the same file using shared-cache */19static void open_shared_btrees(20 const char *zFilename,21 sqlite3 **ppDb1, Btree **ppBt1,22 sqlite3 **ppDb2, Btree **ppBt223){24 int rc;25 sqlite3_vfs *pVfs = sqlite3_vfs_find(0);26 27 *ppDb1 = 0;28 *ppDb2 = 0;29 *ppBt1 = 0;30 *ppBt2 = 0;31 32 rc = sqlite3_open(zFilename, ppDb1);33 assert_rc_ok(rc, "sqlite3_open(db1) failed");34 35 rc = sqlite3_open(zFilename, ppDb2);36 assert_rc_ok(rc, "sqlite3_open(db2) failed");37 38 /* Open low-level Btree objects with shared-cache flag */39 rc = sqlite3BtreeOpen(40 pVfs, zFilename, *ppDb1, ppBt1,41 0 /* flags */, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE42 );43 assert_rc_ok(rc, "sqlite3BtreeOpen(bt1) failed");44 45 rc = sqlite3BtreeOpen(46 pVfs, zFilename, *ppDb2, ppBt2,47 0 /* flags */, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE48 );49 assert_rc_ok(rc, "sqlite3BtreeOpen(bt2) failed");50}51 52/* Helper: create a new intkey table and return its root page number */53static Pgno create_table(Btree *pBt){54 int rc;55 Pgno root = 0;56 rc = sqlite3BtreeBeginTrans(pBt, 1, 0);57 assert_rc_ok(rc, "BeginTrans failed");58 rc = sqlite3BtreeCreateTable(pBt, &root, BTREE_INTKEY | BTREE_LEAFDATA);59 assert_rc_ok(rc, "CreateTable failed");60 rc = sqlite3BtreeCommit(pBt);61 assert_rc_ok(rc, "Commit failed");62 return root;63}64 65/* Helper: open a read cursor on a given root page */66static void open_table_cursor(Btree *pBt, Pgno root, BtCursor *pCur){67 int rc;68 memset(pCur, 0, sizeof(*pCur));69 rc = sqlite3BtreeBeginTrans(pBt, 0, 0); /* open a read transaction */70 assert_rc_ok(rc, "BeginTrans(read) failed");71 rc = sqlite3BtreeCursor(pBt, root, 0 /* read-only */, 0 /* pKeyInfo for table */, pCur);72 assert_rc_ok(rc, "BtreeCursor failed");73}74 75static void close_btree_and_db(sqlite3 *db, Btree *pBt){76 if( pBt ){77 int rc = sqlite3BtreeClose(pBt);78 assert_rc_ok(rc, "BtreeClose failed");79 }80 if( db ){81 int rc = sqlite3_close(db);82 assert_rc_ok(rc, "sqlite3_close failed");83 }84}85 86void setUp(void) {87 /* nothing */88}89void tearDown(void) {90 /* nothing */91}92 93/* Test: conflict exists when other connection has a cursor on same root and is not ReadUncommitted */94void test_hasReadConflicts_conflict_basic(void){95 const char *fname = "test_hasReadConflicts_basic.db";96 sqlite3 *db1=0, *db2=0;97 Btree *bt1=0, *bt2=0;98 BtCursor cOther;99 100 open_shared_btrees(fname, &db1, &bt1, &db2, &bt2);101 Pgno root = create_table(bt1);102 103 /* Open a cursor on bt2 (the "other" connection) to create a potential conflict */104 open_table_cursor(bt2, root, &cOther);105 106 /* Expect a conflict when checking from bt1 */107 int res = test_hasReadConflicts(bt1, root);108 TEST_ASSERT_EQUAL_INT_MESSAGE(1, res, "Expected read conflict but did not detect one");109 110 /* Cleanup */111 sqlite3BtreeCloseCursor(&cOther);112 /* End read transactions explicitly (optional, but tidy) */113 sqlite3BtreeCommit(bt2);114 sqlite3BtreeCommit(bt1);115 116 close_btree_and_db(db2, bt2);117 close_btree_and_db(db1, bt1);118 remove(fname);119}120 121/* Test: no conflict if other connection is in read_uncommitted mode */122void test_hasReadConflicts_read_uncommitted(void){123 const char *fname = "test_hasReadConflicts_readuncommitted.db";124 sqlite3 *db1=0, *db2=0;125 Btree *bt1=0, *bt2=0;126 BtCursor cOther;127 128 open_shared_btrees(fname, &db1, &bt1, &db2, &bt2);129 Pgno root = create_table(bt1);130 131 open_table_cursor(bt2, root, &cOther);132 133 /* Enable read_uncommitted on db2 (the other reader) */134 assert_rc_ok(sqlite3_exec(db2, "PRAGMA read_uncommitted=ON;", 0, 0, 0), "PRAGMA failed");135 136 int res = test_hasReadConflicts(bt1, root);137 TEST_ASSERT_EQUAL_INT_MESSAGE(0, res, "Unexpected conflict with read_uncommitted");138 139 sqlite3BtreeCloseCursor(&cOther);140 sqlite3BtreeCommit(bt2);141 sqlite3BtreeCommit(bt1);142 close_btree_and_db(db2, bt2);143 close_btree_and_db(db1, bt1);144 remove(fname);145}146 147/* Test: no conflict if other cursor is on a different root page */148void test_hasReadConflicts_different_root(void){149 const char *fname = "test_hasReadConflicts_diffroot.db";150 sqlite3 *db1=0, *db2=0;151 Btree *bt1=0, *bt2=0;152 BtCursor cOther;153 154 open_shared_btrees(fname, &db1, &bt1, &db2, &bt2);155 Pgno root1 = create_table(bt1);156 Pgno root2 = create_table(bt1); /* Create a second table (root page) */157 158 /* Open other cursor on root2 */159 open_table_cursor(bt2, root2, &cOther);160 161 int res = test_hasReadConflicts(bt1, root1);162 TEST_ASSERT_EQUAL_INT_MESSAGE(0, res, "Unexpected conflict from other root page");163 164 sqlite3BtreeCloseCursor(&cOther);165 sqlite3BtreeCommit(bt2);166 sqlite3BtreeCommit(bt1);167 close_btree_and_db(db2, bt2);168 close_btree_and_db(db1, bt1);169 remove(fname);170}171 172/* Test: no conflict if only cursors belong to the same Btree instance */173void test_hasReadConflicts_same_btree_only(void){174 const char *fname = "test_hasReadConflicts_samebt.db";175 sqlite3 *db1=0, *db2=0;176 Btree *bt1=0, *bt2=0;177 BtCursor cSelf;178 179 open_shared_btrees(fname, &db1, &bt1, &db2, &bt2);180 Pgno root = create_table(bt1);181 182 /* Open a cursor on the same Btree (bt1) only */183 open_table_cursor(bt1, root, &cSelf);184 185 int res = test_hasReadConflicts(bt1, root);186 TEST_ASSERT_EQUAL_INT_MESSAGE(0, res, "Unexpected conflict from same Btree cursor");187 188 sqlite3BtreeCloseCursor(&cSelf);189 sqlite3BtreeCommit(bt1);190 sqlite3BtreeCommit(bt2);191 close_btree_and_db(db2, bt2);192 close_btree_and_db(db1, bt1);193 remove(fname);194}195 196int main(void){197 UNITY_BEGIN();198 RUN_TEST(test_hasReadConflicts_conflict_basic);199 RUN_TEST(test_hasReadConflicts_read_uncommitted);200 RUN_TEST(test_hasReadConflicts_different_root);201 RUN_TEST(test_hasReadConflicts_same_btree_only);202 return UNITY_END();203}