AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8static int exec_sql(sqlite3 *db, const char *sql){9 return sqlite3_exec(db, sql, 0, 0, 0);10}11 12static Pgno get_rootpage(sqlite3 *db, const char *name){13 Pgno root = 0;14 sqlite3_stmt *stmt = 0;15 const char *zSql = "SELECT rootpage FROM sqlite_master WHERE name=?1";16 int rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, 0);17 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);18 rc = sqlite3_bind_text(stmt, 1, name, -1, SQLITE_TRANSIENT);19 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);20 rc = sqlite3_step(stmt);21 TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);22 root = (Pgno)sqlite3_column_int(stmt, 0);23 sqlite3_finalize(stmt);24 return root;25}26 27static void create_simple_schema(sqlite3 *db, const char *tname){28 char sql[256];29 int rc;30 rc = exec_sql(db, "BEGIN IMMEDIATE;");31 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);32 snprintf(sql, sizeof(sql), "CREATE TABLE %s(x);", tname);33 rc = exec_sql(db, sql);34 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);35 snprintf(sql, sizeof(sql), "INSERT INTO %s VALUES(1);", tname);36 rc = exec_sql(db, sql);37 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);38}39 40void setUp(void) {41 /* No global setup needed */42}43 44void tearDown(void) {45 /* No global teardown needed */46}47 48/* Test 1: Basic move on a disk database with an empty destination page */49void test_sqlite3PagerMovepage_basic_move_disk(void){50 sqlite3 *db = 0;51 int rc;52 DbPage *pSrc = 0;53 DbPage *pCheck = 0;54 55 rc = sqlite3_open("test_pager_move_disk1.sqlite", &db);56 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);57 58 create_simple_schema(db, "t1");59 Pgno srcPgno = get_rootpage(db, "t1");60 TEST_ASSERT_TRUE(srcPgno > 1); /* avoid page 1 */61 62 /* Access internal Pager */63 Btree *pBt = db->aDb[0].pBt;64 TEST_ASSERT_NOT_NULL(pBt);65 Pager *pPager = sqlite3BtreePager(pBt);66 TEST_ASSERT_NOT_NULL(pPager);67 TEST_ASSERT(pPager->tempFile==0); /* disk database for this test */68 69 /* Get the source page and mark it writeable to enter writer state */70 rc = sqlite3PagerGet(pPager, srcPgno, &pSrc, 0);71 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);72 TEST_ASSERT_NOT_NULL(pSrc);73 rc = sqlite3PagerWrite(pSrc);74 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);75 76 Pgno destPgno = srcPgno + 3; /* choose a different page number */77 78 /* Perform the move */79 rc = sqlite3PagerMovepage(pPager, pSrc, destPgno, 0);80 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);81 TEST_ASSERT_EQUAL_UINT(destPgno, pSrc->pgno);82 83 /* Verify the destination page is accessible */84 rc = sqlite3PagerGet(pPager, destPgno, &pCheck, 0);85 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);86 TEST_ASSERT_NOT_NULL(pCheck);87 /* pCheck may be the same PgHdr as pSrc; both refs must be released */88 89 /* Cleanup */90 sqlite3PagerUnrefNotNull(pCheck);91 sqlite3PagerUnrefNotNull(pSrc);92 rc = exec_sql(db, "ROLLBACK;");93 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);94 sqlite3_close(db);95}96 97/* Test 2: Move when destination page is already present in cache (nRef==1) on disk DB */98void test_sqlite3PagerMovepage_overwrite_existing_dest_in_cache_disk(void){99 sqlite3 *db = 0;100 int rc;101 DbPage *pSrc = 0;102 DbPage *pDestHold = 0;103 DbPage *pVerify = 0;104 105 rc = sqlite3_open("test_pager_move_disk2.sqlite", &db);106 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);107 108 create_simple_schema(db, "t2");109 Pgno srcPgno = get_rootpage(db, "t2");110 TEST_ASSERT_TRUE(srcPgno > 1);111 112 Btree *pBt = db->aDb[0].pBt;113 TEST_ASSERT_NOT_NULL(pBt);114 Pager *pPager = sqlite3BtreePager(pBt);115 TEST_ASSERT_NOT_NULL(pPager);116 TEST_ASSERT(pPager->tempFile==0);117 118 /* Prepare source page */119 rc = sqlite3PagerGet(pPager, srcPgno, &pSrc, 0);120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);121 rc = sqlite3PagerWrite(pSrc);122 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);123 124 Pgno destPgno = srcPgno + 5;125 126 /* Ensure destination page is present in cache and referenced once */127 rc = sqlite3PagerGet(pPager, destPgno, &pDestHold, PAGER_GET_NOCONTENT);128 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);129 TEST_ASSERT_NOT_NULL(pDestHold);130 /* Do not unref pDestHold yet to keep nRef==1 during move */131 132 /* Move source to destination - should drop/move away the dest page */133 rc = sqlite3PagerMovepage(pPager, pSrc, destPgno, 0);134 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);135 TEST_ASSERT_EQUAL_UINT(destPgno, pSrc->pgno);136 137 /* Verify we can get the destination page (it should now be pSrc) */138 rc = sqlite3PagerGet(pPager, destPgno, &pVerify, 0);139 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);140 TEST_ASSERT_NOT_NULL(pVerify);141 142 /* Cleanup references. pVerify may equal pSrc. All refs must be released */143 sqlite3PagerUnrefNotNull(pVerify);144 sqlite3PagerUnrefNotNull(pDestHold);145 sqlite3PagerUnrefNotNull(pSrc);146 147 rc = exec_sql(db, "ROLLBACK;");148 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);149 sqlite3_close(db);150}151 152/* Test 3: In-memory (tempFile) database branch preserves original page at old pgno */153void test_sqlite3PagerMovepage_in_memory_preserves_oldpage(void){154 sqlite3 *db = 0;155 int rc;156 DbPage *pSrc = 0;157 DbPage *pDestHold = 0;158 DbPage *pBack = 0;159 160 rc = sqlite3_open(":memory:", &db);161 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);162 163 create_simple_schema(db, "t3");164 Pgno srcPgno = get_rootpage(db, "t3");165 TEST_ASSERT_TRUE(srcPgno > 1);166 167 Btree *pBt = db->aDb[0].pBt;168 TEST_ASSERT_NOT_NULL(pBt);169 Pager *pPager = sqlite3BtreePager(pBt);170 TEST_ASSERT_NOT_NULL(pPager);171 TEST_ASSERT(pPager->tempFile!=0); /* In-memory/temporary pager */172 173 /* Prepare source page */174 rc = sqlite3PagerGet(pPager, srcPgno, &pSrc, 0);175 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);176 /* For tempFile, sqlite3PagerMovepage will call sqlite3PagerWrite itself, */177 /* but calling it here is fine and ensures writer state. */178 rc = sqlite3PagerWrite(pSrc);179 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);180 181 Pgno destPgno = srcPgno + 7;182 183 /* Ensure destination page is present in cache and referenced */184 rc = sqlite3PagerGet(pPager, destPgno, &pDestHold, PAGER_GET_NOCONTENT);185 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);186 TEST_ASSERT_NOT_NULL(pDestHold);187 188 /* Move source to destination */189 rc = sqlite3PagerMovepage(pPager, pSrc, destPgno, 0);190 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);191 TEST_ASSERT_EQUAL_UINT(destPgno, pSrc->pgno);192 193 /* In tempFile case, original page should be preserved at old pgno */194 rc = sqlite3PagerGet(pPager, srcPgno, &pBack, 0);195 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);196 TEST_ASSERT_NOT_NULL(pBack);197 /* The preserved page at old pgno should not be the same PgHdr as pSrc */198 TEST_ASSERT(pBack != pSrc);199 TEST_ASSERT_EQUAL_UINT(srcPgno, pBack->pgno);200 201 /* Cleanup */202 sqlite3PagerUnrefNotNull(pBack);203 sqlite3PagerUnrefNotNull(pDestHold);204 sqlite3PagerUnrefNotNull(pSrc);205 rc = exec_sql(db, "ROLLBACK;");206 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);207 sqlite3_close(db);208}209 210/* Test 4: isCommit=1 path (should still move without extra sync dependencies) */211void test_sqlite3PagerMovepage_isCommit_flag(void){212 sqlite3 *db = 0;213 int rc;214 DbPage *pSrc = 0;215 DbPage *pCheck = 0;216 217 rc = sqlite3_open("test_pager_move_disk3.sqlite", &db);218 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);219 220 create_simple_schema(db, "t4");221 Pgno srcPgno = get_rootpage(db, "t4");222 TEST_ASSERT_TRUE(srcPgno > 1);223 224 Btree *pBt = db->aDb[0].pBt;225 TEST_ASSERT_NOT_NULL(pBt);226 Pager *pPager = sqlite3BtreePager(pBt);227 TEST_ASSERT_NOT_NULL(pPager);228 229 rc = sqlite3PagerGet(pPager, srcPgno, &pSrc, 0);230 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);231 rc = sqlite3PagerWrite(pSrc);232 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);233 234 Pgno destPgno = srcPgno + 9;235 236 rc = sqlite3PagerMovepage(pPager, pSrc, destPgno, 1 /* isCommit */);237 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);238 TEST_ASSERT_EQUAL_UINT(destPgno, pSrc->pgno);239 240 rc = sqlite3PagerGet(pPager, destPgno, &pCheck, 0);241 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);242 TEST_ASSERT_NOT_NULL(pCheck);243 244 sqlite3PagerUnrefNotNull(pCheck);245 sqlite3PagerUnrefNotNull(pSrc);246 rc = exec_sql(db, "ROLLBACK;");247 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);248 sqlite3_close(db);249}250 251int main(void){252 UNITY_BEGIN();253 RUN_TEST(test_sqlite3PagerMovepage_basic_move_disk);254 RUN_TEST(test_sqlite3PagerMovepage_overwrite_existing_dest_in_cache_disk);255 RUN_TEST(test_sqlite3PagerMovepage_in_memory_preserves_oldpage);256 RUN_TEST(test_sqlite3PagerMovepage_isCommit_flag);257 return UNITY_END();258}