AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Stub xStress callback for PCache */7static int stubStress(void *p, PgHdr *pPg){8 /* Attempt to make page clean if asked to spill */9 if( pPg && (pPg->flags & PGHDR_DIRTY) ){10 sqlite3PcacheMakeClean(pPg);11 }12 return SQLITE_OK;13}14 15static PCache *pcache_new(int szPage, int szExtra, int bPurgeable){16 int rc;17 PCache *p = (PCache*)sqlite3_malloc(sqlite3PcacheSize());18 TEST_ASSERT_NOT_NULL(p);19 rc = sqlite3PcacheOpen(szPage, szExtra, bPurgeable, stubStress, NULL, p);20 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);21 return p;22}23 24static void pcache_free(PCache *p){25 if( p ){26 /* Close underlying cache */27 sqlite3PcacheClose(p);28 sqlite3_free(p);29 }30}31 32static PgHdr* pcache_alloc_page(PCache *pCache, Pgno pgno){33 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, pgno, 3);34 TEST_ASSERT_NOT_NULL_MESSAGE(pLower, "Fetch for create returned NULL");35 PgHdr *pPg = sqlite3PcacheFetchFinish(pCache, pgno, pLower);36 TEST_ASSERT_NOT_NULL(pPg);37 TEST_ASSERT_EQUAL_INT(pgno, (int)pPg->pgno);38 return pPg;39}40 41/* Fetch an existing page (createFlag=0). If found, convert to PgHdr and42** return it with a new reference. Caller must sqlite3PcacheRelease().43*/44static PgHdr* pcache_get_existing_pg(PCache *pCache, Pgno pgno){45 sqlite3_pcache_page *pLower = sqlite3PcacheFetch(pCache, pgno, 0);46 if( pLower==NULL ) return NULL;47 return sqlite3PcacheFetchFinish(pCache, pgno, pLower);48}49 50void setUp(void) {51 int rc = sqlite3_initialize();52 (void)rc; /* ignore return; SQLite may already be initialized */53}54 55void tearDown(void) {56 sqlite3_shutdown();57}58 59/* Test 1: Move a page to an unoccupied page number. */60void test_sqlite3PcacheMove_rekey_to_empty(void){61 PCache *pc = pcache_new(256, 64, 1);62 63 PgHdr *p = pcache_alloc_page(pc, 5);64 /* Baseline: ensure new target 10 does not exist */65 PgHdr *pre = pcache_get_existing_pg(pc, 10);66 if( pre ){ sqlite3PcacheRelease(pre); TEST_FAIL_MESSAGE("Target unexpectedly exists before move"); }67 68 sqlite3_pcache_page *lowerBefore = p->pPage;69 TEST_ASSERT_NOT_NULL(lowerBefore);70 71 /* Move to new pgno */72 sqlite3PcacheMove(p, 10);73 74 /* PgHdr.pgno updated */75 TEST_ASSERT_EQUAL_INT(10, (int)p->pgno);76 77 /* Old key should be gone */78 PgHdr *chkOld = pcache_get_existing_pg(pc, 5);79 TEST_ASSERT_NULL(chkOld);80 81 /* New key should return the same underlying page as p->pPage */82 sqlite3_pcache_page *lowerFetched = sqlite3PcacheFetch(pc, 10, 0);83 TEST_ASSERT_NOT_NULL(lowerFetched);84 /* Convert to PgHdr to balance refs then release */85 PgHdr *pChk = sqlite3PcacheFetchFinish(pc, 10, lowerFetched);86 TEST_ASSERT_NOT_NULL(pChk);87 TEST_ASSERT_EQUAL_PTR(p, pChk);88 sqlite3PcacheRelease(pChk);89 90 /* Release original ref and cleanup */91 sqlite3PcacheRelease(p);92 pcache_free(pc);93}94 95/* Test 2: Move a page to an existing unreferenced destination page.96** The destination must be dropped and replaced by the moved page.97*/98void test_sqlite3PcacheMove_overwrites_existing_unref_destination(void){99 PCache *pc = pcache_new(512, 64, 1);100 101 PgHdr *pA = pcache_alloc_page(pc, 4);102 PgHdr *pB = pcache_alloc_page(pc, 9);103 104 /* Release B to ensure nRef==0 so move can drop it */105 sqlite3PcacheRelease(pB);106 107 int nBefore = sqlite3PcachePagecount(pc);108 TEST_ASSERT_TRUE(nBefore >= 1);109 110 /* Perform the move */111 sqlite3PcacheMove(pA, 9);112 113 /* Old key should be absent */114 PgHdr *chkOld = pcache_get_existing_pg(pc, 4);115 TEST_ASSERT_NULL(chkOld);116 117 /* New key should return pA */118 PgHdr *chkNew = pcache_get_existing_pg(pc, 9);119 TEST_ASSERT_NOT_NULL(chkNew);120 TEST_ASSERT_EQUAL_PTR(pA, chkNew);121 sqlite3PcacheRelease(chkNew);122 123 /* Pagecount should have decreased by 1 due to dropping original dest */124 int nAfter = sqlite3PcachePagecount(pc);125 TEST_ASSERT_EQUAL_INT(nBefore - 1, nAfter);126 127 /* Release and cleanup */128 sqlite3PcacheRelease(pA);129 pcache_free(pc);130}131 132/* Test 3: Move a dirty page with NEED_SYNC set; flags should be preserved. */133void test_sqlite3PcacheMove_preserves_dirty_and_need_sync_flags(void){134 PCache *pc = pcache_new(1024, 64, 1);135 136 PgHdr *p = pcache_alloc_page(pc, 12);137 138 /* Make it dirty and set NEED_SYNC + WRITEABLE */139 sqlite3PcacheMakeDirty(p);140 p->flags |= (PGHDR_NEED_SYNC | PGHDR_WRITEABLE);141 142 /* Sanity before move */143 TEST_ASSERT_TRUE( (p->flags & PGHDR_DIRTY)!=0 );144 TEST_ASSERT_TRUE( (p->flags & PGHDR_NEED_SYNC)!=0 );145 146 /* Move to a new, empty pgno */147 sqlite3PcacheMove(p, 15);148 149 /* Flags preserved */150 TEST_ASSERT_TRUE( (p->flags & PGHDR_DIRTY)!=0 );151 TEST_ASSERT_TRUE( (p->flags & PGHDR_NEED_SYNC)!=0 );152 TEST_ASSERT_EQUAL_INT(15, (int)p->pgno);153 154 /* Cleanup: make clean then release */155 sqlite3PcacheMakeClean(p);156 sqlite3PcacheRelease(p);157 pcache_free(pc);158}159 160int main(void){161 UNITY_BEGIN();162 RUN_TEST(test_sqlite3PcacheMove_rekey_to_empty);163 RUN_TEST(test_sqlite3PcacheMove_overwrites_existing_unref_destination);164 RUN_TEST(test_sqlite3PcacheMove_preserves_dirty_and_need_sync_flags);165 return UNITY_END();166}