AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5 6/* Helper: open an in-memory btree */7static Btree* helper_open_btree(sqlite3 **ppDb){8 int rc;9 sqlite3 *db = 0;10 rc = sqlite3_open(":memory:", &db);11 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);12 sqlite3_vfs *pVfs = sqlite3_vfs_find(0);13 TEST_ASSERT_NOT_NULL(pVfs);14 15 Btree *p = 0;16 rc = sqlite3BtreeOpen(pVfs, 0, db, &p, 0,17 SQLITE_OPEN_MEMORY | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);18 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);19 TEST_ASSERT_NOT_NULL(p);20 *ppDb = db;21 return p;22}23 24/* Helper: close btree and db */25static void helper_close_btree(sqlite3 *db, Btree *p){26 if( p ){27 /* Ensure no cursors left unclosed */28 sqlite3BtreeClose(p);29 }30 if( db ){31 sqlite3_close(db);32 }33}34 35void setUp(void){36 /* nothing */37}38void tearDown(void){39 /* nothing */40}41 42/* Test 1: Basic rollback with tripCode==SQLITE_OK and no cursors. */43void test_sqlite3BtreeRollback_basic_ok(void){44 sqlite3 *db = 0;45 Btree *p = helper_open_btree(&db);46 47 int rc;48 rc = sqlite3BtreeBeginTrans(p, 1, 0);49 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);50 51 /* Verify state before rollback */52 TEST_ASSERT_EQUAL_INT(TRANS_WRITE, p->inTrans);53 TEST_ASSERT_EQUAL_INT(TRANS_WRITE, p->pBt->inTransaction);54 55 rc = sqlite3BtreeRollback(p, SQLITE_OK, 0);56 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);57 58 /* After rollback, both btree and shared should be out of transaction */59 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->inTrans);60 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->pBt->inTransaction);61 62 helper_close_btree(db, p);63}64 65/* Test 2: trip write cursor on non-OK tripCode (writeOnly=1). */66void test_sqlite3BtreeRollback_trips_write_cursor_on_error(void){67 sqlite3 *db = 0;68 Btree *p = helper_open_btree(&db);69 int rc = sqlite3BtreeBeginTrans(p, 1, 0);70 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);71 72 /* Create a table and open a write cursor */73 Pgno root = 0;74 rc = sqlite3BtreeCreateTable(p, &root, BTREE_INTKEY|BTREE_LEAFDATA);75 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);76 TEST_ASSERT_TRUE(root > 0);77 78 BtCursor cur;79 sqlite3BtreeCursorZero(&cur);80 rc = sqlite3BtreeCursor(p, root, BTREE_WRCSR, 0, &cur);81 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);82 83 /* Now rollback with error code -> cursor should be tripped */84 rc = sqlite3BtreeRollback(p, SQLITE_ABORT_ROLLBACK, 1);85 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); /* Function returns pager rc, trip done internally */86 87 TEST_ASSERT_EQUAL_INT(CURSOR_FAULT, cur.eState);88 TEST_ASSERT_EQUAL_INT(SQLITE_ABORT_ROLLBACK, cur.skipNext);89 90 /* Transaction cleared */91 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->inTrans);92 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->pBt->inTransaction);93 94 sqlite3BtreeCloseCursor(&cur);95 helper_close_btree(db, p);96}97 98/* Test 3: tripCode==SQLITE_OK but saveAllCursors fails due to pinned valid cursor.99** Expect function returns SQLITE_CONSTRAINT_PINNED, trips cursor, and clears txn.100*/101void test_sqlite3BtreeRollback_ok_with_pinned_valid_cursor_returns_constraint_and_trips(void){102 sqlite3 *db = 0;103 Btree *p = helper_open_btree(&db);104 int rc = sqlite3BtreeBeginTrans(p, 1, 0);105 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);106 107 /* Create a table and open a write cursor */108 Pgno root = 0;109 rc = sqlite3BtreeCreateTable(p, &root, BTREE_INTKEY|BTREE_LEAFDATA);110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);111 112 BtCursor cur;113 sqlite3BtreeCursorZero(&cur);114 rc = sqlite3BtreeCursor(p, root, BTREE_WRCSR, 0, &cur);115 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);116 117 /* Insert a single row so the cursor can be valid */118 BtreePayload x;119 memset(&x, 0, sizeof(x));120 x.nKey = 1; /* rowid */121 x.pKey = 0; /* intkey table */122 x.pData = 0;123 x.nData = 0;124 x.nZero = 0;125 rc = sqlite3BtreeInsert(&cur, &x, BTREE_APPEND, 0);126 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);127 128 /* Move to first to force CURSOR_VALID */129 int isEmpty = 0;130 rc = sqlite3BtreeFirst(&cur, &isEmpty);131 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);132 TEST_ASSERT_EQUAL_INT(0, isEmpty);133 TEST_ASSERT_EQUAL_INT(CURSOR_VALID, cur.eState);134 135 /* Pin the cursor so saveCursorPosition() fails with SQLITE_CONSTRAINT_PINNED */136 sqlite3BtreeCursorPin(&cur);137 138 rc = sqlite3BtreeRollback(p, SQLITE_OK, 1);139 TEST_ASSERT_EQUAL_INT(SQLITE_CONSTRAINT_PINNED, rc);140 141 /* Should have tripped cursor (writeOnly forced to 0 on failure) */142 TEST_ASSERT_EQUAL_INT(CURSOR_FAULT, cur.eState);143 144 /* Transaction cleared */145 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->inTrans);146 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->pBt->inTransaction);147 148 sqlite3BtreeCloseCursor(&cur);149 helper_close_btree(db, p);150}151 152/* Test 4: tripCode==SQLITE_OK with an unpinned invalid cursor: no trip occurs. */153void test_sqlite3BtreeRollback_ok_does_not_trip_unpinned_invalid_cursor(void){154 sqlite3 *db = 0;155 Btree *p = helper_open_btree(&db);156 int rc = sqlite3BtreeBeginTrans(p, 1, 0);157 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);158 159 /* Create a table and open a write cursor; table is empty so cursor is invalid */160 Pgno root = 0;161 rc = sqlite3BtreeCreateTable(p, &root, BTREE_INTKEY|BTREE_LEAFDATA);162 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);163 164 BtCursor cur;165 sqlite3BtreeCursorZero(&cur);166 rc = sqlite3BtreeCursor(p, root, BTREE_WRCSR, 0, &cur);167 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);168 TEST_ASSERT_TRUE(cur.eState==CURSOR_INVALID);169 170 rc = sqlite3BtreeRollback(p, SQLITE_OK, 1);171 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);172 173 /* Cursor should not be tripped to FAULT */174 TEST_ASSERT_TRUE(cur.eState!=CURSOR_FAULT);175 176 /* Transaction cleared */177 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->inTrans);178 TEST_ASSERT_EQUAL_INT(TRANS_NONE, p->pBt->inTransaction);179 180 sqlite3BtreeCloseCursor(&cur);181 helper_close_btree(db, p);182}183 184int main(void){185 UNITY_BEGIN();186 RUN_TEST(test_sqlite3BtreeRollback_basic_ok);187 RUN_TEST(test_sqlite3BtreeRollback_trips_write_cursor_on_error);188 RUN_TEST(test_sqlite3BtreeRollback_ok_with_pinned_valid_cursor_returns_constraint_and_trips);189 RUN_TEST(test_sqlite3BtreeRollback_ok_does_not_trip_unpinned_invalid_cursor);190 return UNITY_END();191}