AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Prototypes for internal functions used by the tests */8extern Pager *sqlite3BtreePager(Btree*);9extern int test_databaseIsUnmoved(Pager*);10 11/*12** Test VFS infrastructure to intercept SQLITE_FCNTL_HAS_MOVED.13*/14 15static const char *gTestVfsName = "testvfs";16static sqlite3_vfs *gOrigVfs = NULL;17static sqlite3_vfs gTestVfs; /* registered VFS object */18static int gVfsRegistered = 0;19 20/* Mode for HAS_MOVED behavior:21** 0 -> return SQLITE_NOTFOUND22** 1 -> return SQLITE_OK, *(int*)pArg = 0 (not moved)23** 2 -> return SQLITE_OK, *(int*)pArg = 1 (moved)24** 3 -> return SQLITE_IOERR25** -1 -> fall through to underlying xFileControl26*/27static int gHasMovedMode = -1;28 29/* Per-file wrapper */30typedef struct TestFile TestFile;31struct TestFile {32 sqlite3_file base; /* must be first */33 sqlite3_file *pReal; /* underlying real file */34 sqlite3_io_methods *pMeth; /* per-file io_methods we own */35};36 37/* Forward declarations of file method wrappers */38static int tfClose(sqlite3_file*);39static int tfRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);40static int tfWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);41static int tfTruncate(sqlite3_file*, sqlite3_int64 size);42static int tfSync(sqlite3_file*, int flags);43static int tfFileSize(sqlite3_file*, sqlite3_int64 *pSize);44static int tfLock(sqlite3_file*, int);45static int tfUnlock(sqlite3_file*, int);46static int tfCheckReservedLock(sqlite3_file*, int *pResOut);47static int tfFileControl(sqlite3_file*, int op, void *pArg);48static int tfSectorSize(sqlite3_file*);49static int tfDeviceCharacteristics(sqlite3_file*);50/* iVersion >= 2 */51static int tfShmMap(sqlite3_file*, int, int, int, void volatile**);52static int tfShmLock(sqlite3_file*, int, int, int);53static void tfShmBarrier(sqlite3_file*);54static int tfShmUnmap(sqlite3_file*, int);55/* iVersion >= 3 */56static int tfFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);57static int tfUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);58 59static void tfInitIoMethods(TestFile *tf){60 sqlite3_io_methods const *rm = tf->pReal->pMethods;61 sqlite3_io_methods *m = (sqlite3_io_methods*)sqlite3_malloc(sizeof(*m));62 TEST_ASSERT_NOT_NULL(m);63 memset(m, 0, sizeof(*m));64 m->iVersion = rm->iVersion;65 m->xClose = tfClose;66 m->xRead = tfRead;67 m->xWrite = tfWrite;68 m->xTruncate = tfTruncate;69 m->xSync = tfSync;70 m->xFileSize = tfFileSize;71 m->xLock = tfLock;72 m->xUnlock = tfUnlock;73 m->xCheckReservedLock = tfCheckReservedLock;74 m->xFileControl = tfFileControl;75 m->xSectorSize = tfSectorSize;76 m->xDeviceCharacteristics = tfDeviceCharacteristics;77 if( rm->iVersion >= 2 ){78 m->xShmMap = tfShmMap;79 m->xShmLock = tfShmLock;80 m->xShmBarrier = tfShmBarrier;81 m->xShmUnmap = tfShmUnmap;82 }83 if( rm->iVersion >= 3 ){84 m->xFetch = tfFetch;85 m->xUnfetch = tfUnfetch;86 }87 tf->base.pMethods = m;88 tf->pMeth = m;89}90 91/* File method wrappers: mostly pass-through to underlying */92static int tfClose(sqlite3_file *pFile){93 TestFile *tf = (TestFile*)pFile;94 int rc = tf->pReal->pMethods->xClose(tf->pReal);95 sqlite3_free(tf->pReal);96 tf->pReal = NULL;97 if( tf->pMeth ){ sqlite3_free(tf->pMeth); tf->pMeth = NULL; }98 return rc;99}100static int tfRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite3_int64 iOfst){101 TestFile *tf = (TestFile*)pFile;102 return tf->pReal->pMethods->xRead(tf->pReal, zBuf, iAmt, iOfst);103}104static int tfWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite3_int64 iOfst){105 TestFile *tf = (TestFile*)pFile;106 return tf->pReal->pMethods->xWrite(tf->pReal, zBuf, iAmt, iOfst);107}108static int tfTruncate(sqlite3_file *pFile, sqlite3_int64 size){109 TestFile *tf = (TestFile*)pFile;110 return tf->pReal->pMethods->xTruncate(tf->pReal, size);111}112static int tfSync(sqlite3_file *pFile, int flags){113 TestFile *tf = (TestFile*)pFile;114 return tf->pReal->pMethods->xSync(tf->pReal, flags);115}116static int tfFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){117 TestFile *tf = (TestFile*)pFile;118 return tf->pReal->pMethods->xFileSize(tf->pReal, pSize);119}120static int tfLock(sqlite3_file *pFile, int eLock){121 TestFile *tf = (TestFile*)pFile;122 return tf->pReal->pMethods->xLock(tf->pReal, eLock);123}124static int tfUnlock(sqlite3_file *pFile, int eLock){125 TestFile *tf = (TestFile*)pFile;126 return tf->pReal->pMethods->xUnlock(tf->pReal, eLock);127}128static int tfCheckReservedLock(sqlite3_file *pFile, int *pResOut){129 TestFile *tf = (TestFile*)pFile;130 return tf->pReal->pMethods->xCheckReservedLock(tf->pReal, pResOut);131}132static int tfFileControl(sqlite3_file *pFile, int op, void *pArg){133 TestFile *tf = (TestFile*)pFile;134 if( op==SQLITE_FCNTL_HAS_MOVED ){135 if( gHasMovedMode==0 ){136 return SQLITE_NOTFOUND;137 }else if( gHasMovedMode==1 ){138 if( pArg ) *(int*)pArg = 0;139 return SQLITE_OK;140 }else if( gHasMovedMode==2 ){141 if( pArg ) *(int*)pArg = 1;142 return SQLITE_OK;143 }else if( gHasMovedMode==3 ){144 return SQLITE_IOERR;145 }146 }147 return tf->pReal->pMethods->xFileControl(tf->pReal, op, pArg);148}149static int tfSectorSize(sqlite3_file *pFile){150 TestFile *tf = (TestFile*)pFile;151 return tf->pReal->pMethods->xSectorSize(tf->pReal);152}153static int tfDeviceCharacteristics(sqlite3_file *pFile){154 TestFile *tf = (TestFile*)pFile;155 return tf->pReal->pMethods->xDeviceCharacteristics(tf->pReal);156}157static int tfShmMap(sqlite3_file *pFile, int iPg, int pgsz, int isWrite, void volatile **pp){158 TestFile *tf = (TestFile*)pFile;159 sqlite3_io_methods const *rm = tf->pReal->pMethods;160 if( rm->iVersion<2 || rm->xShmMap==0 ) return SQLITE_OK;161 return rm->xShmMap(tf->pReal, iPg, pgsz, isWrite, pp);162}163static int tfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){164 TestFile *tf = (TestFile*)pFile;165 sqlite3_io_methods const *rm = tf->pReal->pMethods;166 if( rm->iVersion<2 || rm->xShmLock==0 ) return SQLITE_OK;167 return rm->xShmLock(tf->pReal, ofst, n, flags);168}169static void tfShmBarrier(sqlite3_file *pFile){170 TestFile *tf = (TestFile*)pFile;171 sqlite3_io_methods const *rm = tf->pReal->pMethods;172 if( rm->iVersion>=2 && rm->xShmBarrier ) rm->xShmBarrier(tf->pReal);173}174static int tfShmUnmap(sqlite3_file *pFile, int delFlag){175 TestFile *tf = (TestFile*)pFile;176 sqlite3_io_methods const *rm = tf->pReal->pMethods;177 if( rm->iVersion<2 || rm->xShmUnmap==0 ) return SQLITE_OK;178 return rm->xShmUnmap(tf->pReal, delFlag);179}180static int tfFetch(sqlite3_file *pFile, sqlite3_int64 iOfst, int iAmt, void **pp){181 TestFile *tf = (TestFile*)pFile;182 sqlite3_io_methods const *rm = tf->pReal->pMethods;183 if( rm->iVersion<3 || rm->xFetch==0 ) { *pp = 0; return SQLITE_OK; }184 return rm->xFetch(tf->pReal, iOfst, iAmt, pp);185}186static int tfUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){187 TestFile *tf = (TestFile*)pFile;188 sqlite3_io_methods const *rm = tf->pReal->pMethods;189 if( rm->iVersion<3 || rm->xUnfetch==0 ) return SQLITE_OK;190 return rm->xUnfetch(tf->pReal, iOfst, p);191}192 193/* VFS wrappers */194static int tvfsOpen(sqlite3_vfs* pVfs, const char *zName, sqlite3_file *pFile,195 int flags, int *pOutFlags){196 TestFile *tf = (TestFile*)pFile;197 memset(tf, 0, sizeof(*tf));198 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;199 tf->pReal = (sqlite3_file*)sqlite3_malloc(orig->szOsFile);200 TEST_ASSERT_NOT_NULL(tf->pReal);201 memset(tf->pReal, 0, orig->szOsFile);202 int rc = orig->xOpen(orig, zName, tf->pReal, flags, pOutFlags);203 if( rc==SQLITE_OK ){204 tfInitIoMethods(tf);205 }else{206 sqlite3_free(tf->pReal);207 tf->pReal = NULL;208 }209 return rc;210}211static int tvfsDelete(sqlite3_vfs* pVfs, const char *zName, int syncDir){212 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;213 return orig->xDelete(orig, zName, syncDir);214}215static int tvfsAccess(sqlite3_vfs* pVfs, const char *zName, int flags, int *pRes){216 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;217 return orig->xAccess(orig, zName, flags, pRes);218}219static int tvfsFullPathname(sqlite3_vfs* pVfs, const char *zName, int nOut, char *zOut){220 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;221 return orig->xFullPathname(orig, zName, nOut, zOut);222}223static void *tvfsDlOpen(sqlite3_vfs* pVfs, const char *zFilename){224 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;225 return orig->xDlOpen(orig, zFilename);226}227static void tvfsDlError(sqlite3_vfs* pVfs, int nByte, char *zErrMsg){228 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;229 orig->xDlError(orig, nByte, zErrMsg);230}231static void (*tvfsDlSym(sqlite3_vfs* pVfs, void *p, const char *zSym))(void){232 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;233 return orig->xDlSym(orig, p, zSym);234}235static void tvfsDlClose(sqlite3_vfs* pVfs, void *pHandle){236 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;237 orig->xDlClose(orig, pHandle);238}239static int tvfsRandomness(sqlite3_vfs* pVfs, int nByte, char *zOut){240 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;241 return orig->xRandomness(orig, nByte, zOut);242}243static int tvfsSleep(sqlite3_vfs* pVfs, int microseconds){244 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;245 return orig->xSleep(orig, microseconds);246}247static int tvfsCurrentTime(sqlite3_vfs* pVfs, double *pTime){248 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;249 return orig->xCurrentTime(orig, pTime);250}251static int tvfsGetLastError(sqlite3_vfs* pVfs, int a, char *b){252 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;253 if( orig->xGetLastError ) return orig->xGetLastError(orig, a, b);254 return 0;255}256static int tvfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64 *pNow){257 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;258 if( orig->iVersion>=2 && orig->xCurrentTimeInt64 ){259 return orig->xCurrentTimeInt64(orig, pNow);260 }261 double r; int rc = orig->xCurrentTime(orig, &r);262 if( rc==SQLITE_OK ) *pNow = (sqlite3_int64)(r*86400000.0);263 return rc;264}265static int tvfsSetSystemCall(sqlite3_vfs* pVfs, const char *zName, sqlite3_syscall_ptr pFunc){266 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;267 if( orig->iVersion>=3 && orig->xSetSystemCall ) return orig->xSetSystemCall(orig, zName, pFunc);268 return SQLITE_OK;269}270static sqlite3_syscall_ptr tvfsGetSystemCall(sqlite3_vfs* pVfs, const char *zName){271 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;272 if( orig->iVersion>=3 && orig->xGetSystemCall ) return orig->xGetSystemCall(orig, zName);273 return 0;274}275static const char *tvfsNextSystemCall(sqlite3_vfs* pVfs, const char *zName){276 sqlite3_vfs *orig = (sqlite3_vfs*)pVfs->pAppData;277 if( orig->iVersion>=3 && orig->xNextSystemCall ) return orig->xNextSystemCall(orig, zName);278 return 0;279}280 281static void registerTestVfs(void){282 if( gVfsRegistered ) return;283 gOrigVfs = sqlite3_vfs_find(NULL);284 TEST_ASSERT_NOT_NULL(gOrigVfs);285 memset(&gTestVfs, 0, sizeof(gTestVfs));286 gTestVfs.iVersion = gOrigVfs->iVersion;287 gTestVfs.szOsFile = sizeof(TestFile);288 gTestVfs.mxPathname = gOrigVfs->mxPathname;289 gTestVfs.zName = gTestVfsName;290 gTestVfs.pAppData = gOrigVfs; /* stash original in pAppData */291 gTestVfs.xOpen = tvfsOpen;292 gTestVfs.xDelete = tvfsDelete;293 gTestVfs.xAccess = tvfsAccess;294 gTestVfs.xFullPathname = tvfsFullPathname;295 gTestVfs.xDlOpen = tvfsDlOpen;296 gTestVfs.xDlError = tvfsDlError;297 gTestVfs.xDlSym = tvfsDlSym;298 gTestVfs.xDlClose = tvfsDlClose;299 gTestVfs.xRandomness = tvfsRandomness;300 gTestVfs.xSleep = tvfsSleep;301 gTestVfs.xCurrentTime = tvfsCurrentTime;302 gTestVfs.xGetLastError = tvfsGetLastError;303 if( gTestVfs.iVersion>=2 ){304 gTestVfs.xCurrentTimeInt64 = tvfsCurrentTimeInt64;305 }306 if( gTestVfs.iVersion>=3 ){307 gTestVfs.xSetSystemCall = tvfsSetSystemCall;308 gTestVfs.xGetSystemCall = tvfsGetSystemCall;309 gTestVfs.xNextSystemCall = tvfsNextSystemCall;310 }311 int rc = sqlite3_vfs_register(&gTestVfs, 0);312 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);313 gVfsRegistered = 1;314}315 316static void unregisterTestVfs(void){317 if( gVfsRegistered ){318 sqlite3_vfs_unregister(&gTestVfs);319 gVfsRegistered = 0;320 }321}322 323/* Utility helpers */324static void exec_sql(sqlite3 *db, const char *sql){325 char *zErr = 0;326 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);327 if( rc!=SQLITE_OK ){328 printf("SQL error: %s\n", zErr ? zErr : "(null)");329 }330 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);331 sqlite3_free(zErr);332}333 334static Pager* getPagerForDb(sqlite3 *db, int iDb){335 if( db->aDb[iDb].pBt==0 ){336 /* Force opening the btree */337 exec_sql(db, "SELECT 1");338 }339 TEST_ASSERT_NOT_NULL(db->aDb[iDb].pBt);340 return sqlite3BtreePager(db->aDb[iDb].pBt);341}342 343static sqlite3* openWithTestVfs(const char *zFile){344 registerTestVfs();345 sqlite3 *db = 0;346 int rc = sqlite3_open_v2(zFile, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, gTestVfsName);347 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);348 TEST_ASSERT_NOT_NULL(db);349 return db;350}351 352/* Unity setup/teardown */353void setUp(void) {354 gHasMovedMode = -1;355}356void tearDown(void) {357 unregisterTestVfs();358}359 360/* Tests */361 362/* Intercept: xFileControl returns SQLITE_NOTFOUND => databaseIsUnmoved() => SQLITE_OK */363void test_databaseIsUnmoved_has_moved_notfound_returns_ok(void){364 sqlite3 *db = openWithTestVfs("unmoved_notfound.db");365 exec_sql(db, "PRAGMA journal_mode=DELETE;");366 exec_sql(db, "CREATE TABLE IF NOT EXISTS t(x);");367 exec_sql(db, "INSERT INTO t VALUES(1);");368 gHasMovedMode = 0; /* NOTFOUND */369 Pager *pPager = getPagerForDb(db, 0);370 int rc = test_databaseIsUnmoved(pPager);371 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);372 sqlite3_close(db);373 remove("unmoved_notfound.db");374 remove("unmoved_notfound.db-journal");375}376 377/* Intercept: xFileControl returns OK with bHasMoved==0 => SQLITE_OK */378void test_databaseIsUnmoved_ok_not_moved_returns_ok(void){379 sqlite3 *db = openWithTestVfs("unmoved_ok.db");380 exec_sql(db, "PRAGMA journal_mode=DELETE;");381 exec_sql(db, "CREATE TABLE IF NOT EXISTS t(x);");382 exec_sql(db, "INSERT INTO t VALUES(2);");383 gHasMovedMode = 1; /* OK, not moved */384 Pager *pPager = getPagerForDb(db, 0);385 int rc = test_databaseIsUnmoved(pPager);386 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);387 sqlite3_close(db);388 remove("unmoved_ok.db");389 remove("unmoved_ok.db-journal");390}391 392/* Intercept: xFileControl returns OK with bHasMoved==1 => SQLITE_READONLY_DBMOVED */393void test_databaseIsUnmoved_ok_moved_returns_dbmoved(void){394 sqlite3 *db = openWithTestVfs("unmoved_moved.db");395 exec_sql(db, "PRAGMA journal_mode=DELETE;");396 exec_sql(db, "CREATE TABLE IF NOT EXISTS t(x);");397 exec_sql(db, "INSERT INTO t VALUES(3);");398 gHasMovedMode = 2; /* OK, moved */399 Pager *pPager = getPagerForDb(db, 0);400 int rc = test_databaseIsUnmoved(pPager);401 TEST_ASSERT_EQUAL_INT(SQLITE_READONLY_DBMOVED, rc);402 sqlite3_close(db);403 remove("unmoved_moved.db");404 remove("unmoved_moved.db-journal");405}406 407/* Intercept: xFileControl returns error (SQLITE_IOERR) => passthrough */408void test_databaseIsUnmoved_error_passthrough(void){409 sqlite3 *db = openWithTestVfs("unmoved_error.db");410 exec_sql(db, "PRAGMA journal_mode=DELETE;");411 exec_sql(db, "CREATE TABLE IF NOT EXISTS t(x);");412 exec_sql(db, "INSERT INTO t VALUES(4);");413 gHasMovedMode = 3; /* IOERR */414 Pager *pPager = getPagerForDb(db, 0);415 int rc = test_databaseIsUnmoved(pPager);416 TEST_ASSERT_EQUAL_INT(SQLITE_IOERR, rc);417 sqlite3_close(db);418 remove("unmoved_error.db");419 remove("unmoved_error.db-journal");420}421 422/* Early return: tempFile => SQLITE_OK. Use a TEMP or :memory: database. */423void test_databaseIsUnmoved_tempfile_returns_ok(void){424 sqlite3 *db = NULL;425 int rc = sqlite3_open(":memory:", &db);426 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);427 exec_sql(db, "CREATE TABLE tmem(x);");428 Pager *pPager = getPagerForDb(db, 0);429 rc = test_databaseIsUnmoved(pPager);430 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);431 sqlite3_close(db);432}433 434/* Early return: dbSize==0 => SQLITE_OK, even if VFS would say moved */435void test_databaseIsUnmoved_dbsize_zero_returns_ok(void){436 sqlite3 *db = openWithTestVfs("unmoved_empty.db");437 /* Do not write anything to keep the database size at 0 pages. */438 /* Force opening the btree without writes */439 exec_sql(db, "SELECT count(*) FROM sqlite_master;");440 gHasMovedMode = 2; /* would claim "moved" if consulted */441 Pager *pPager = getPagerForDb(db, 0);442 int rc = test_databaseIsUnmoved(pPager);443 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);444 sqlite3_close(db);445 remove("unmoved_empty.db");446 remove("unmoved_empty.db-journal");447}448 449int main(void){450 UNITY_BEGIN();451 RUN_TEST(test_databaseIsUnmoved_has_moved_notfound_returns_ok);452 RUN_TEST(test_databaseIsUnmoved_ok_not_moved_returns_ok);453 RUN_TEST(test_databaseIsUnmoved_ok_moved_returns_dbmoved);454 RUN_TEST(test_databaseIsUnmoved_error_passthrough);455 RUN_TEST(test_databaseIsUnmoved_tempfile_returns_ok);456 RUN_TEST(test_databaseIsUnmoved_dbsize_zero_returns_ok);457 return UNITY_END();458}