Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge trunk changes into this branch. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | mmap-on-demand |
Files: | files | file ages | folders |
SHA1: |
6666862302f019b9253caef4104f9337 |
User & Date: | dan 2013-03-02 09:15:12.724 |
Context
2013-03-02
| ||
16:01 | Fix some problems on this branch. Leaf check-in: a92d659eee user: dan tags: mmap-on-demand | |
09:15 | Merge trunk changes into this branch. check-in: 6666862302 user: dan tags: mmap-on-demand | |
09:09 | Remove a couple of unused variables from sqltest.c. check-in: e423d6c4bc user: dan tags: trunk | |
2013-02-05
| ||
09:51 | Fix bugs on this branch. check-in: 0cbb5cc2cd user: dan tags: mmap-on-demand | |
Changes
Changes to lsm-test/lsmtest_func.c.
1 2 3 4 5 6 7 8 9 | #include "lsmtest.h" int do_work(int nArg, char **azArg){ struct Option { const char *zName; } aOpt [] = { { "-nmerge" }, | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include "lsmtest.h" int do_work(int nArg, char **azArg){ struct Option { const char *zName; } aOpt [] = { { "-nmerge" }, { "-nkb" }, { 0 } }; lsm_db *pDb; int rc; int i; const char *zDb; int nMerge = 1; int nKB = (1<<30); if( nArg==0 ) goto usage; zDb = azArg[nArg-1]; for(i=0; i<(nArg-1); i++){ int iSel; rc = testArgSelect(aOpt, "option", azArg[i], &iSel); if( rc ) return rc; switch( iSel ){ case 0: i++; if( i==(nArg-1) ) goto usage; nMerge = atoi(azArg[i]); break; case 1: i++; if( i==(nArg-1) ) goto usage; nKB = atoi(azArg[i]); break; } } rc = lsm_new(0, &pDb); if( rc!=LSM_OK ){ testPrintError("lsm_open(): rc=%d\n", rc); }else{ rc = lsm_open(pDb, zDb); if( rc!=LSM_OK ){ testPrintError("lsm_open(): rc=%d\n", rc); }else{ int n = -1; lsm_config(pDb, LSM_CONFIG_BLOCK_SIZE, &n); n = n*2; lsm_config(pDb, LSM_CONFIG_AUTOCHECKPOINT, &n); rc = lsm_work(pDb, nMerge, nKB, 0); if( rc!=LSM_OK ){ testPrintError("lsm_work(): rc=%d\n", rc); } } } if( rc==LSM_OK ){ rc = lsm_checkpoint(pDb, 0); |
︙ | ︙ |
Changes to lsm-test/lsmtest_main.c.
︙ | ︙ | |||
563 564 565 566 567 568 569 | "Options are:\n" " -repeat $repeat (default value 10)\n" " -write $write (default value 10000)\n" " -pause $pause (default value 0)\n" " -fetch $fetch (default value 0)\n" " -keysize $keysize (default value 12)\n" " -valsize $valsize (default value 100)\n" | | | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | "Options are:\n" " -repeat $repeat (default value 10)\n" " -write $write (default value 10000)\n" " -pause $pause (default value 0)\n" " -fetch $fetch (default value 0)\n" " -keysize $keysize (default value 12)\n" " -valsize $valsize (default value 100)\n" " -system $system (default value \"lsm\")\n" "\n" ); } int do_speed_test2(int nArg, char **azArg){ struct Option { const char *zOpt; |
︙ | ︙ | |||
642 643 644 645 646 647 648 | } printf("#"); for(i=0; i<ArraySize(aOpt); i++){ if( aOpt[i].zOpt ){ if( aOpt[i].eVal>=0 ){ printf(" %s=%d", &aOpt[i].zOpt[1], aParam[aOpt[i].eVal]); | | | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 | } printf("#"); for(i=0; i<ArraySize(aOpt); i++){ if( aOpt[i].zOpt ){ if( aOpt[i].eVal>=0 ){ printf(" %s=%d", &aOpt[i].zOpt[1], aParam[aOpt[i].eVal]); }else if( aOpt[i].eVal==-1 ){ printf(" %s=\"%s\"", &aOpt[i].zOpt[1], zSystem); } } } printf("\n"); defn.nMinKey = defn.nMaxKey = aParam[ST_KEYSIZE]; |
︙ | ︙ | |||
1329 1330 1331 1332 1333 1334 1335 | if( strcmp(azArg[0], "-")==0 ){ pInput = stdin; }else{ pClose = pInput = fopen(azArg[0], "r"); } zDb = azArg[1]; pEnv = tdb_lsm_env(); | | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 | if( strcmp(azArg[0], "-")==0 ){ pInput = stdin; }else{ pClose = pInput = fopen(azArg[0], "r"); } zDb = azArg[1]; pEnv = tdb_lsm_env(); rc = pEnv->xOpen(pEnv, zDb, 0, &pOut); if( rc!=LSM_OK ) return rc; while( feof(pInput)==0 ){ char zLine[80]; fgets(zLine, sizeof(zLine)-1, pInput); zLine[sizeof(zLine)-1] = '\0'; |
︙ | ︙ | |||
1426 1427 1428 1429 1430 1431 1432 | #ifdef __linux__ #include <sys/time.h> #include <sys/resource.h> static void lsmtest_rusage_report(void){ int res; struct rusage r; | | | 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 | #ifdef __linux__ #include <sys/time.h> #include <sys/resource.h> static void lsmtest_rusage_report(void){ int res; struct rusage r; memset(&r, 0, sizeof(r)); res = getrusage(RUSAGE_SELF, &r); assert( res==0 ); printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock ); |
︙ | ︙ |
Changes to lsm-test/lsmtest_tdb3.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | #include <sys/time.h> typedef struct LsmDb LsmDb; typedef struct LsmWorker LsmWorker; typedef struct LsmFile LsmFile; | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <sys/time.h> typedef struct LsmDb LsmDb; typedef struct LsmWorker LsmWorker; typedef struct LsmFile LsmFile; #define LSMTEST_DFLT_MT_MAX_CKPT (8*1024) #define LSMTEST_DFLT_MT_MIN_CKPT (2*1024) #ifdef LSM_MUTEX_PTHREADS #include <pthread.h> #define LSMTEST_THREAD_CKPT 1 #define LSMTEST_THREAD_WORKER 2 #define LSMTEST_THREAD_WORKER_AC 3 |
︙ | ︙ | |||
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xFullpath(pRealEnv, zFile, zOut, pnOut); } static int testEnvOpen( lsm_env *pEnv, /* Environment for current LsmDb */ const char *zFile, /* Name of file to open */ lsm_file **ppFile /* OUT: New file handle object */ ){ lsm_env *pRealEnv = tdb_lsm_env(); LsmDb *pDb = (LsmDb *)pEnv->pVfsCtx; int rc; /* Return Code */ LsmFile *pRet; /* The new file handle */ int nFile; /* Length of string zFile in bytes */ nFile = strlen(zFile); pRet = (LsmFile *)testMalloc(sizeof(LsmFile)); pRet->pDb = pDb; pRet->bLog = (nFile > 4 && 0==memcmp("-log", &zFile[nFile-4], 4)); | > | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xFullpath(pRealEnv, zFile, zOut, pnOut); } static int testEnvOpen( lsm_env *pEnv, /* Environment for current LsmDb */ const char *zFile, /* Name of file to open */ int flags, lsm_file **ppFile /* OUT: New file handle object */ ){ lsm_env *pRealEnv = tdb_lsm_env(); LsmDb *pDb = (LsmDb *)pEnv->pVfsCtx; int rc; /* Return Code */ LsmFile *pRet; /* The new file handle */ int nFile; /* Length of string zFile in bytes */ nFile = strlen(zFile); pRet = (LsmFile *)testMalloc(sizeof(LsmFile)); pRet->pDb = pDb; pRet->bLog = (nFile > 4 && 0==memcmp("-log", &zFile[nFile-4], 4)); rc = pRealEnv->xOpen(pRealEnv, zFile, flags, &pRet->pReal); if( rc!=LSM_OK ){ testFree(pRet); pRet = 0; } *ppFile = (lsm_file *)pRet; return rc; |
︙ | ︙ | |||
367 368 369 370 371 372 373 374 375 376 377 378 379 380 | lsm_env *pRealEnv = tdb_lsm_env(); if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){ return LSM_BUSY; } return pRealEnv->xLock(p->pReal, iLock, eType); } static int testEnvShmMap(lsm_file *pFile, int iRegion, int sz, void **pp){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xShmMap(p->pReal, iRegion, sz, pp); } | > > > > > > > > > > | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | lsm_env *pRealEnv = tdb_lsm_env(); if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){ return LSM_BUSY; } return pRealEnv->xLock(p->pReal, iLock, eType); } static int testEnvTestLock(lsm_file *pFile, int iLock, int nLock, int eType){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); if( iLock==2 && eType==LSM_LOCK_EXCL && p->pDb->bNoRecovery ){ return LSM_BUSY; } return pRealEnv->xTestLock(p->pReal, iLock, nLock, eType); } static int testEnvShmMap(lsm_file *pFile, int iRegion, int sz, void **pp){ LsmFile *p = (LsmFile *)pFile; lsm_env *pRealEnv = tdb_lsm_env(); return pRealEnv->xShmMap(p->pReal, iRegion, sz, pp); } |
︙ | ︙ | |||
400 401 402 403 404 405 406 | char *zFile = pDb->zName; char *zFree = 0; for(iFile=0; iFile<2; iFile++){ lsm_file *pFile = 0; int i; | | | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | char *zFile = pDb->zName; char *zFree = 0; for(iFile=0; iFile<2; iFile++){ lsm_file *pFile = 0; int i; pEnv->xOpen(pEnv, zFile, 0, &pFile); for(i=0; i<pDb->aFile[iFile].nSector; i++){ u8 *aOld = pDb->aFile[iFile].aSector[i].aOld; if( aOld ){ int iOpt = testPrngValue(iSeed++) % 3; switch( iOpt ){ case 0: break; |
︙ | ︙ | |||
520 521 522 523 524 525 526 | testFree((char *)pDb->pBuf); testFree((char *)pDb); return rc; } static int waitOnCheckpointer(LsmDb *pDb, lsm_db *db){ int nSleep = 0; | | | | | | | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 | testFree((char *)pDb->pBuf); testFree((char *)pDb); return rc; } static int waitOnCheckpointer(LsmDb *pDb, lsm_db *db){ int nSleep = 0; int nKB; int rc; do { nKB = 0; rc = lsm_info(db, LSM_INFO_CHECKPOINT_SIZE, &nKB); if( rc!=LSM_OK || nKB<pDb->nMtMaxCkpt ) break; usleep(5000); nSleep += 5; }while( 1 ); #if 0 if( nSleep ) printf("# waitOnCheckpointer(): nSleep=%d\n", nSleep); #endif return rc; } static int waitOnWorker(LsmDb *pDb){ int rc; int nLimit = -1; int nSleep = 0; rc = lsm_config(pDb->db, LSM_CONFIG_AUTOFLUSH, &nLimit); do { int nOld, nNew, rc; rc = lsm_info(pDb->db, LSM_INFO_TREE_SIZE, &nOld, &nNew); if( rc!=LSM_OK ) return rc; if( nOld==0 || nNew<(nLimit/2) ) break; usleep(5000); nSleep += 5; }while( 1 ); #if 0 if( nSleep ) printf("# waitOnWorker(): nSleep=%d\n", nSleep); #endif |
︙ | ︙ | |||
775 776 777 778 779 780 781 | } aParam[] = { { "autoflush", 0, LSM_CONFIG_AUTOFLUSH }, { "page_size", 0, LSM_CONFIG_PAGE_SIZE }, { "block_size", 0, LSM_CONFIG_BLOCK_SIZE }, { "safety", 0, LSM_CONFIG_SAFETY }, { "autowork", 0, LSM_CONFIG_AUTOWORK }, { "autocheckpoint", 0, LSM_CONFIG_AUTOCHECKPOINT }, | < | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | } aParam[] = { { "autoflush", 0, LSM_CONFIG_AUTOFLUSH }, { "page_size", 0, LSM_CONFIG_PAGE_SIZE }, { "block_size", 0, LSM_CONFIG_BLOCK_SIZE }, { "safety", 0, LSM_CONFIG_SAFETY }, { "autowork", 0, LSM_CONFIG_AUTOWORK }, { "autocheckpoint", 0, LSM_CONFIG_AUTOCHECKPOINT }, { "mmap", 0, LSM_CONFIG_MMAP }, { "use_log", 0, LSM_CONFIG_USE_LOG }, { "automerge", 0, LSM_CONFIG_AUTOMERGE }, { "max_freelist", 0, LSM_CONFIG_MAX_FREELIST }, { "multi_proc", 0, LSM_CONFIG_MULTIPLE_PROCESSES }, { "worker_automerge", 1, LSM_CONFIG_AUTOMERGE }, { "test_no_recovery", 0, TEST_NO_RECOVERY }, |
︙ | ︙ | |||
826 827 828 829 830 831 832 | if( rc!=0 ) return rc; eParam = aParam[i].eParam; z++; zStart = z; while( *z>='0' && *z<='9' ) z++; if( *z=='k' || *z=='K' ){ | | | | 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | if( rc!=0 ) return rc; eParam = aParam[i].eParam; z++; zStart = z; while( *z>='0' && *z<='9' ) z++; if( *z=='k' || *z=='K' ){ iMul = 1; z++; }else if( *z=='M' || *z=='M' ){ iMul = 1024; z++; } nParam = z-zStart; if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error; memcpy(zParam, zStart, nParam); zParam[nParam] = '\0'; iVal = atoi(zParam) * iMul; |
︙ | ︙ | |||
851 852 853 854 855 856 857 | case TEST_NO_RECOVERY: if( pLsm ) pLsm->bNoRecovery = iVal; break; case TEST_MT_MODE: if( pLsm ) nThread = iVal; break; case TEST_MT_MIN_CKPT: | | | | 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | case TEST_NO_RECOVERY: if( pLsm ) pLsm->bNoRecovery = iVal; break; case TEST_MT_MODE: if( pLsm ) nThread = iVal; break; case TEST_MT_MIN_CKPT: if( pLsm && iVal>0 ) pLsm->nMtMinCkpt = iVal*1024; break; case TEST_MT_MAX_CKPT: if( pLsm && iVal>0 ) pLsm->nMtMaxCkpt = iVal*1024; break; #ifdef HAVE_ZLIB case TEST_COMPRESSION: testConfigureCompression(db); break; #endif } |
︙ | ︙ | |||
963 964 965 966 967 968 969 970 971 972 973 974 975 976 | #endif pDb->env.xMap = testEnvMap; pDb->env.xUnmap = testEnvUnmap; pDb->env.xFileid = testEnvFileid; pDb->env.xClose = testEnvClose; pDb->env.xUnlink = testEnvUnlink; pDb->env.xLock = testEnvLock; pDb->env.xShmBarrier = testEnvShmBarrier; pDb->env.xShmMap = testEnvShmMap; pDb->env.xShmUnmap = testEnvShmUnmap; pDb->env.xSleep = testEnvSleep; rc = lsm_new(&pDb->env, &pDb->db); if( rc==LSM_OK ){ | > | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | #endif pDb->env.xMap = testEnvMap; pDb->env.xUnmap = testEnvUnmap; pDb->env.xFileid = testEnvFileid; pDb->env.xClose = testEnvClose; pDb->env.xUnlink = testEnvUnlink; pDb->env.xLock = testEnvLock; pDb->env.xTestLock = testEnvTestLock; pDb->env.xShmBarrier = testEnvShmBarrier; pDb->env.xShmMap = testEnvShmMap; pDb->env.xShmUnmap = testEnvShmUnmap; pDb->env.xSleep = testEnvSleep; rc = lsm_new(&pDb->env, &pDb->db); if( rc==LSM_OK ){ |
︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 | } int test_lsm_small_open( const char *zFile, int bClear, TestDb **ppDb ){ | | | | | | | | 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 | } int test_lsm_small_open( const char *zFile, int bClear, TestDb **ppDb ){ const char *zCfg = "page_size=256 block_size=64"; return testLsmOpen(zCfg, zFile, bClear, ppDb); } int test_lsm_lomem_open( const char *zFilename, int bClear, TestDb **ppDb ){ /* "max_freelist=4 autocheckpoint=32" */ const char *zCfg = "page_size=256 block_size=64 autoflush=16 " "autocheckpoint=32" "mmap=0 " ; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } int test_lsm_zip_open( const char *zFilename, int bClear, TestDb **ppDb ){ const char *zCfg = "page_size=256 block_size=64 autoflush=16 " "autocheckpoint=32 compression=1 mmap=0 " ; return testLsmOpen(zCfg, zFilename, bClear, ppDb); } lsm_db *tdb_lsm(TestDb *pDb){ if( pDb->pMethods->xClose==test_lsm_close ){ return ((LsmDb *)pDb)->db; |
︙ | ︙ | |||
1147 1148 1149 1150 1151 1152 1153 | int rc = LSM_OK; int nCkpt = -1; /* Do some work. If an error occurs, exit. */ pthread_mutex_unlock(&p->worker_mutex); if( p->eType==LSMTEST_THREAD_CKPT ){ | | | | | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 | int rc = LSM_OK; int nCkpt = -1; /* Do some work. If an error occurs, exit. */ pthread_mutex_unlock(&p->worker_mutex); if( p->eType==LSMTEST_THREAD_CKPT ){ int nKB = 0; rc = lsm_info(pWorker, LSM_INFO_CHECKPOINT_SIZE, &nKB); if( rc==LSM_OK && nKB>=p->pDb->nMtMinCkpt ){ rc = lsm_checkpoint(pWorker, 0); } }else{ int nWrite; do { if( p->eType==LSMTEST_THREAD_WORKER ){ |
︙ | ︙ |
Changes to lsm-test/lsmtest_util.c.
1 |
| < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdarg.h> #include <stdio.h> #include <string.h> #include <sys/time.h> /* ** Global variables used within this module. */ static struct TestutilGlobal { char **argv; int argc; } g = {0, 0}; static struct TestutilRnd { unsigned int aRand1[2048]; /* Bits 0..10 */ unsigned int aRand2[2048]; /* Bits 11..21 */ unsigned int aRand3[1024]; /* Bits 22..31 */ } r; /************************************************************************* ** The following block is a copy of the implementation of SQLite function ** sqlite3_randomness. This version has two important differences: ** ** 1. It always uses the same seed. So the sequence of random data output |
︙ | ︙ | |||
68 69 70 71 72 73 74 | 0xC4, 0xEC, 0x80, 0xD0, 0x98, 0xA7, 0x76, 0xCC, 0x9C, 0x2F, 0x7B, 0xFF, 0x8E, 0x0E, 0xBB, 0x90, 0xAE, 0x13, 0x06, 0xF5, 0x1C, 0x4E, 0x52, 0xF7 } }; /* Generate and return single random byte */ | | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 0xC4, 0xEC, 0x80, 0xD0, 0x98, 0xA7, 0x76, 0xCC, 0x9C, 0x2F, 0x7B, 0xFF, 0x8E, 0x0E, 0xBB, 0x90, 0xAE, 0x13, 0x06, 0xF5, 0x1C, 0x4E, 0x52, 0xF7 } }; /* Generate and return single random byte */ static unsigned char randomByte(void){ unsigned char t; sqlite3Prng.i++; t = sqlite3Prng.s[sqlite3Prng.i]; sqlite3Prng.j += t; sqlite3Prng.s[sqlite3Prng.i] = sqlite3Prng.s[sqlite3Prng.j]; sqlite3Prng.s[sqlite3Prng.j] = t; t += sqlite3Prng.s[sqlite3Prng.i]; |
︙ | ︙ | |||
94 95 96 97 98 99 100 | } /* ** End of code copied from SQLite. *************************************************************************/ int testPrngInit(void){ | < | | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | } /* ** End of code copied from SQLite. *************************************************************************/ int testPrngInit(void){ randomBlob(sizeof(r.aRand1), (unsigned char *)r.aRand1); randomBlob(sizeof(r.aRand2), (unsigned char *)r.aRand2); randomBlob(sizeof(r.aRand3), (unsigned char *)r.aRand3); return 0; } unsigned int testPrngValue(unsigned int iVal){ return r.aRand1[iVal & 0x000007FF] ^ r.aRand2[(iVal>>11) & 0x000007FF] ^ r.aRand3[(iVal>>22) & 0x000003FF] ; } void testPrngArray(unsigned int iVal, unsigned int *aOut, int nOut){ int i; for(i=0; i<nOut; i++){ aOut[i] = testPrngValue(iVal+i); } } void testPrngString(unsigned int iVal, char *aOut, int nOut){ int i; for(i=0; i<(nOut-1); i++){ aOut[i] = 'a' + (testPrngValue(iVal+i) % 26); } aOut[i] = '\0'; } |
︙ | ︙ | |||
148 149 150 151 152 153 154 | struct Entry { const char *zName; }; struct Entry *pEntry; const char *zPrev = 0; testPrintError("unrecognized %s \"%s\": must be ", zType, zArg); for(pEntry=(struct Entry *)aData; pEntry->zName; | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | struct Entry { const char *zName; }; struct Entry *pEntry; const char *zPrev = 0; testPrintError("unrecognized %s \"%s\": must be ", zType, zArg); for(pEntry=(struct Entry *)aData; pEntry->zName; pEntry=(struct Entry *)&((unsigned char *)pEntry)[sz] ){ if( zPrev ){ testPrintError("%s, ", zPrev); } zPrev = pEntry->zName; } testPrintError("or %s\n", zPrev); } |
︙ | ︙ | |||
173 174 175 176 177 178 179 | int i = 0; int iOut = -1; int nOut = 0; for(pEntry=(struct Entry *)aData; pEntry->zName; | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | int i = 0; int iOut = -1; int nOut = 0; for(pEntry=(struct Entry *)aData; pEntry->zName; pEntry=(struct Entry *)&((unsigned char *)pEntry)[sz] ){ int nName = strlen(pEntry->zName); if( nArg<=nName && memcmp(pEntry->zName, zArg, nArg)==0 ){ iOut = i; if( nName==nArg ){ nOut = 1; break; |
︙ | ︙ |
Added lsm-test/sqltest.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | /* ** 2013 March 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code for a program that links against SQLite ** versions 3 and 4. It contains a few simple performance test routines ** that can be run against either database system. */ #include "sqlite4.h" #include "sqlite3.h" #include "lsm.h" #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #define SQLITE3_DB_FILE "test.db3" #define SQLITE4_DB_FILE "test.db4" #include "lsmtest_util.c" /* ** Unlink database zDb and its supporting files (wal, shm, journal, and log). ** This function works with both lsm and sqlite3 databases. */ static int unlink_db(const char *zDb){ int i; const char *azExt[] = { "", "-shm", "-wal", "-journal", "-log", 0 }; for(i=0; azExt[i]; i++){ char *zFile = sqlite4_mprintf(0, "%s%s", zDb, azExt[i]); unlink(zFile); sqlite4_free(0, zFile); } return 0; } static char *create_schema_sql(int nIdx){ char *zSchema; int i; zSchema = sqlite4_mprintf(0, "CREATE TABLE t1(k PRIMARY KEY,"); for(i=0; i<nIdx; i++){ zSchema = sqlite4_mprintf(0, "%z c%d BLOB,", zSchema, i); } zSchema = sqlite4_mprintf(0, "%z v BLOB);", zSchema); for(i=0; i<nIdx; i++){ zSchema = sqlite4_mprintf( 0, "%z\nCREATE INDEX i%d ON t1 (c%d);", zSchema, i, i ); } return zSchema; } static char *create_insert_sql(int nIdx){ char *zInsert; int i; zInsert = sqlite4_mprintf(0, "INSERT INTO t1 VALUES(rblob(:1, 8, 20),"); for(i=0; i<nIdx; i++){ zInsert = sqlite4_mprintf(0, "%z rblob((:1<<%d)+:1, 8, 20),", zInsert, i); } zInsert = sqlite4_mprintf(0, "%z rblob((:1<<%d)+:1, 100, 150));", zInsert, i); return zInsert; } static char *create_select_sql(int iIdx){ char *zSql; if( iIdx==0 ){ zSql = sqlite4_mprintf(0, "SELECT * FROM t1 WHERE k = rblob(:1, 8, 20)"); }else{ int iCol = iIdx-1; zSql = sqlite4_mprintf(0, "SELECT * FROM t1 WHERE c%d = rblob((:1<<%d)+:1, 8, 20)", iCol, iCol ); } return zSql; } static int do_explode(const char *zLine, int rc, int iLine){ if( rc ){ fprintf(stderr, "ERROR: \"%s\" at line %d failed. rc=%d\n", zLine, iLine, rc ); exit(-1); } return 0; } #define EXPLODE(rc) do_explode(#rc, rc, __LINE__) /************************************************************************* ** Implementations of the rblob(nMin, nMax) function. One for src4 and ** one for sqlite3. */ /* src4 implementation */ static void rblobFunc4(sqlite4_context *ctx, int nArg, sqlite4_value **apArg){ unsigned char aBlob[1000]; int iSeed = sqlite4_value_int(apArg[0]); int nMin = sqlite4_value_int(apArg[1]); int nMax = sqlite4_value_int(apArg[2]); int nByte; nByte = testPrngValue(iSeed + 1000000) & 0x7FFFFFFF; nByte = (nByte % (nMax+1-nMin)) + nMin; assert( nByte>=nMin && nByte<=nMax ); if( nByte>sizeof(aBlob) ) nByte = sizeof(aBlob); testPrngArray(iSeed, (unsigned int *)aBlob, (nByte+3)/4); sqlite4_result_blob(ctx, aBlob, nByte, SQLITE4_TRANSIENT, 0); } static void install_rblob_function4(sqlite4 *db){ testPrngInit(); sqlite4_create_function(db, "rblob", 3, SQLITE4_UTF8, 0, rblobFunc4, 0, 0); } /* sqlite3 implementation */ static void rblobFunc3(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ unsigned char aBlob[1000]; int iSeed = sqlite3_value_int(apArg[0]); int nMin = sqlite3_value_int(apArg[1]); int nMax = sqlite3_value_int(apArg[2]); int nByte; nByte = testPrngValue(iSeed + 1000000) & 0x7FFFFFFF; nByte = (nByte % (nMax+1-nMin)) + nMin; assert( nByte>=nMin && nByte<=nMax ); if( nByte>sizeof(aBlob) ) nByte = sizeof(aBlob); testPrngArray(iSeed, (unsigned int *)aBlob, (nByte+3)/4); sqlite3_result_blob(ctx, aBlob, nByte, SQLITE_TRANSIENT); } static void install_rblob_function3(sqlite3 *db){ testPrngInit(); sqlite3_create_function(db, "rblob", 3, SQLITE_UTF8, 0, rblobFunc3, 0, 0); } /* ** End of rblob() implementations. *************************************************************************/ /************************************************************************* ** Integer query functions for sqlite3 and src4. */ static int integer_query4(sqlite4 *db, const char *zSql){ int iRet; sqlite4_stmt *pStmt; EXPLODE( sqlite4_prepare(db, zSql, -1, &pStmt, 0) ); EXPLODE( SQLITE_ROW!=sqlite4_step(pStmt) ); iRet = sqlite4_column_int(pStmt, 0); EXPLODE( sqlite4_finalize(pStmt) ); return iRet; } static int integer_query3(sqlite3 *db, const char *zSql){ int iRet; sqlite3_stmt *pStmt; EXPLODE( sqlite3_prepare(db, zSql, -1, &pStmt, 0) ); EXPLODE( SQLITE_ROW!=sqlite3_step(pStmt) ); iRet = sqlite3_column_int(pStmt, 0); EXPLODE( sqlite3_finalize(pStmt) ); return iRet; } /* ** End of integer query implementations. *************************************************************************/ static int do_insert1_test4( int nRow, /* Number of rows to insert in total */ int nRowPerTrans, /* Number of rows per transaction */ int nIdx, /* Number of aux indexes (aside from PK) */ int iSync /* PRAGMA synchronous value (0, 1 or 2) */ ){ char *zCreateTbl; /* Create table statement */ char *zInsert; /* INSERT statement */ sqlite4_stmt *pInsert; /* Compiled INSERT statement */ sqlite4 *db = 0; /* Database handle */ int i; /* Counter to count nRow rows */ int nMs; /* Test time in ms */ lsm_db *pLsm; unlink_db(SQLITE4_DB_FILE); EXPLODE( sqlite4_open(0, SQLITE4_DB_FILE, &db) ); sqlite4_kvstore_control(db, "main", SQLITE4_KVCTRL_LSM_HANDLE, &pLsm); i = iSync; lsm_config(pLsm, LSM_CONFIG_SAFETY, &i); assert( i==iSync ); install_rblob_function4(db); zCreateTbl = create_schema_sql(nIdx); zInsert = create_insert_sql(nIdx); /* Create the db schema and prepare the INSERT statement */ EXPLODE( sqlite4_exec(db, zCreateTbl, 0, 0, 0) ); EXPLODE( sqlite4_prepare(db, zInsert, -1, &pInsert, 0) ); /* Run the test */ testTimeInit(); for(i=0; i<nRow; i++){ if( (i % nRowPerTrans)==0 ){ if( i!=0 ) EXPLODE( sqlite4_exec(db, "COMMIT", 0, 0, 0) ); EXPLODE( sqlite4_exec(db, "BEGIN", 0, 0, 0) ); } sqlite4_bind_int(pInsert, 1, i); sqlite4_step(pInsert); EXPLODE( sqlite4_reset(pInsert) ); } EXPLODE( sqlite4_exec(db, "COMMIT", 0, 0, 0) ); /* Free all the stuff allocated above */ sqlite4_finalize(pInsert); sqlite4_free(0, zCreateTbl); sqlite4_free(0, zInsert); sqlite4_close(db); nMs = testTimeGet(); /* Print out the time taken by the test */ printf("%.3f seconds\n", (double)nMs / 1000.0); return 0; } static int do_insert1_test3( int nRow, /* Number of rows to insert in total */ int nRowPerTrans, /* Number of rows per transaction */ int nIdx, /* Number of aux indexes (aside from PK) */ int iSync /* PRAGMA synchronous value (0, 1 or 2) */ ){ char *zCreateTbl; /* Create table statement */ char *zInsert; /* INSERT statement */ char *zSync; /* "PRAGMA synchronous=" statement */ sqlite3_stmt *pInsert; /* Compiled INSERT statement */ sqlite3 *db = 0; /* Database handle */ int i; /* Counter to count nRow rows */ int nMs; /* Test time in ms */ unlink_db(SQLITE3_DB_FILE); EXPLODE( sqlite3_open(SQLITE3_DB_FILE, &db) ); EXPLODE( sqlite3_exec(db, "PRAGMA journal_mode=WAL", 0, 0, 0) ); zSync = sqlite4_mprintf(0, "PRAGMA synchronous=%d", iSync); EXPLODE( sqlite3_exec(db, zSync, 0, 0, 0) ); sqlite4_free(0, zSync); install_rblob_function3(db); zCreateTbl = create_schema_sql(nIdx); zInsert = create_insert_sql(nIdx); /* Create the db schema and prepare the INSERT statement */ EXPLODE( sqlite3_exec(db, zCreateTbl, 0, 0, 0) ); EXPLODE( sqlite3_prepare(db, zInsert, -1, &pInsert, 0) ); /* Run the test */ testTimeInit(); for(i=0; i<nRow; i++){ if( (i % nRowPerTrans)==0 ){ if( i!=0 ) EXPLODE( sqlite3_exec(db, "COMMIT", 0, 0, 0) ); EXPLODE( sqlite3_exec(db, "BEGIN", 0, 0, 0) ); } sqlite3_bind_int(pInsert, 1, i); sqlite3_step(pInsert); EXPLODE( sqlite3_reset(pInsert) ); } EXPLODE( sqlite3_exec(db, "COMMIT", 0, 0, 0) ); /* Finalize the statement and close the db. */ sqlite3_finalize(pInsert); sqlite3_close(db); nMs = testTimeGet(); /* Free the stuff allocated above */ sqlite4_free(0, zCreateTbl); sqlite4_free(0, zInsert); /* Print out the time taken by the test */ printf("%.3f seconds\n", (double)nMs / 1000.0); return 0; } static int do_insert1(int argc, char **argv){ struct Insert1Arg { const char *zArg; int nMin; int nMax; } aArg[] = { {"-db", 3, 4}, {"-rows", 1, 10000000}, {"-rowspertrans", 1, 10000000}, {"-indexes", 0, 20}, {"-sync", 0, 2}, {0,0,0} }; int i; int iDb = 4; /* SQLite 3 or 4 */ int nRow = 50000; /* Total rows: 50000 */ int nRowPerTrans = 10; /* Total rows each transaction: 50000 */ int nIdx = 3; /* Number of auxilliary indexes */ int iSync = 1; /* PRAGMA synchronous setting */ for(i=0; i<argc; i++){ int iSel; int iVal; int rc; rc = testArgSelectX(aArg, "argument", sizeof(aArg[0]), argv[i], &iSel); if( rc!=0 ) return -1; if( i==argc-1 ){ fprintf(stderr, "option %s requires an argument\n", aArg[iSel].zArg); return -1; } iVal = atoi(argv[++i]); if( iVal<aArg[iSel].nMin || iVal>aArg[iSel].nMax ){ fprintf(stderr, "option %s out of range (%d..%d)\n", aArg[iSel].zArg, aArg[iSel].nMin, aArg[iSel].nMax ); return -1; } switch( iSel ){ case 0: iDb = iVal; break; case 1: nRow = iVal; break; case 2: nRowPerTrans = iVal; break; case 3: nIdx = iVal; break; case 4: iSync = iVal; break; } } printf("insert1: db=%d rows=%d rowspertrans=%d indexes=%d sync=%d ... ", iDb, nRow, nRowPerTrans, nIdx, iSync ); fflush(stdout); if( iDb==3 ){ do_insert1_test3(nRow, nRowPerTrans, nIdx, iSync); }else{ do_insert1_test4(nRow, nRowPerTrans, nIdx, iSync); } return 0; } static int do_select1_test4( int nRow, /* Number of rows to read in total */ int nRowPerTrans, /* Number of rows per transaction */ int iIdx ){ int nMs = 0; sqlite4_stmt *pSelect = 0; char *zSelect; sqlite4 *db; int i; int nTblRow; EXPLODE( sqlite4_open(0, SQLITE4_DB_FILE, &db) ); install_rblob_function4(db); nTblRow = integer_query4(db, "SELECT count(*) FROM t1"); /* Create the db schema and prepare the INSERT statement */ zSelect = create_select_sql(iIdx); EXPLODE( sqlite4_prepare(db, zSelect, -1, &pSelect, 0) ); testTimeInit(); for(i=0; i<nRow; i++){ if( (i % nRowPerTrans)==0 ){ if( i!=0 ) EXPLODE( sqlite4_exec(db, "COMMIT", 0, 0, 0) ); EXPLODE( sqlite4_exec(db, "BEGIN", 0, 0, 0) ); } sqlite4_bind_int(pSelect, 1, (i*211)%nTblRow); EXPLODE( SQLITE_ROW!=sqlite4_step(pSelect) ); EXPLODE( sqlite4_reset(pSelect) ); } EXPLODE( sqlite4_exec(db, "COMMIT", 0, 0, 0) ); nMs = testTimeGet(); sqlite4_finalize(pSelect); sqlite4_close(db); sqlite4_free(0, zSelect); printf("%.3f seconds\n", (double)nMs / 1000.0); return 0; } static int do_select1_test3( int nRow, /* Number of rows to read in total */ int nRowPerTrans, /* Number of rows per transaction */ int iIdx ){ int nMs = 0; sqlite3_stmt *pSelect = 0; char *zSelect; sqlite3 *db; int i; int nTblRow; EXPLODE( sqlite3_open(SQLITE3_DB_FILE, &db) ); install_rblob_function3(db); nTblRow = integer_query3(db, "SELECT count(*) FROM t1"); /* Create the db schema and prepare the INSERT statement */ zSelect = create_select_sql(iIdx); EXPLODE( sqlite3_prepare(db, zSelect, -1, &pSelect, 0) ); testTimeInit(); for(i=0; i<nRow; i++){ if( (i % nRowPerTrans)==0 ){ if( i!=0 ) EXPLODE( sqlite3_exec(db, "COMMIT", 0, 0, 0) ); EXPLODE( sqlite3_exec(db, "BEGIN", 0, 0, 0) ); } sqlite3_bind_int(pSelect, 1, (i*211)%nTblRow); EXPLODE( SQLITE_ROW!=sqlite3_step(pSelect) ); EXPLODE( sqlite3_reset(pSelect) ); } EXPLODE( sqlite3_exec(db, "COMMIT", 0, 0, 0) ); nMs = testTimeGet(); sqlite3_finalize(pSelect); sqlite3_close(db); sqlite4_free(0, zSelect); printf("%.3f seconds\n", (double)nMs / 1000.0); return 0; } static int do_select1(int argc, char **argv){ struct Insert1Arg { const char *zArg; int nMin; int nMax; } aArg[] = { {"-db", 3, 4}, {"-rows", 1, 10000000}, {"-rowspertrans", 1, 10000000}, {"-index", 0, 21}, {0,0,0} }; int i; int iDb = 4; /* SQLite 3 or 4 */ int nRow = 50000; /* Total rows: 50000 */ int nRowPerTrans = 10; /* Total rows each transaction: 50000 */ int iIdx = 0; for(i=0; i<argc; i++){ int iSel; int iVal; int rc; rc = testArgSelectX(aArg, "argument", sizeof(aArg[0]), argv[i], &iSel); if( rc!=0 ) return -1; if( i==argc-1 ){ fprintf(stderr, "option %s requires an argument\n", aArg[iSel].zArg); return -1; } iVal = atoi(argv[++i]); if( iVal<aArg[iSel].nMin || iVal>aArg[iSel].nMax ){ fprintf(stderr, "option %s out of range (%d..%d)\n", aArg[iSel].zArg, aArg[iSel].nMin, aArg[iSel].nMax ); return -1; } switch( iSel ){ case 0: iDb = iVal; break; case 1: nRow = iVal; break; case 2: nRowPerTrans = iVal; break; case 3: iIdx = iVal; break; } } printf("select1: db=%d rows=%d rowspertrans=%d index=%d ... ", iDb, nRow, nRowPerTrans, iIdx ); fflush(stdout); if( iDb==3 ){ do_select1_test3(nRow, nRowPerTrans, iIdx); }else{ do_select1_test4(nRow, nRowPerTrans, iIdx); } return 0; } int main(int argc, char **argv){ struct SqltestArg { const char *zPrg; int (*xPrg)(int, char **); } aArg[] = { {"select", do_select1}, {"insert", do_insert1}, {0, 0} }; int iSel; int rc; if( argc<2 ){ fprintf(stderr, "Usage: %s sub-program...\n", argv[0]); return -1; } rc = testArgSelectX(aArg, "sub-program", sizeof(aArg[0]), argv[1], &iSel); if( rc!=0 ) return -1; aArg[iSel].xPrg(argc-2, argv+2); return 0; } |
Changes to main.mk.
︙ | ︙ | |||
40 41 42 43 44 45 46 | # Once the macros above are defined, the rest of this make script will # build the SQLite library and testing tools. ################################################################################ # FIXME: Required options for now. # OPTS += -DLSM_MUTEX_NONE | | | | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | # Once the macros above are defined, the rest of this make script will # build the SQLite library and testing tools. ################################################################################ # FIXME: Required options for now. # OPTS += -DLSM_MUTEX_NONE #OPTS += -DSQLITE4_DEBUG=1 -DLSM_DEBUG=1 OPTS += -DHAVE_GMTIME_R OPTS += -DHAVE_LOCALTIME_R OPTS += -DHAVE_MALLOC_USABLE_SIZE OPTS += -DHAVE_USLEEP #OPTS += -DSQLITE4_MEMDEBUG=1 #OPTS += -DSQLITE4_NO_SYNC=1 -DLSM_NO_SYNC=1 OPTS += -DSQLITE4_OMIT_ANALYZE OPTS += -DSQLITE4_OMIT_AUTOMATIC_INDEX OPTS += -DSQLITE4_OMIT_BTREECOUNT OPTS += -DSQLITE4_OMIT_VIRTUALTABLE=1 OPTS += -DSQLITE4_OMIT_XFER_OPT OPTS += -DSQLITE4_THREADSAFE=0 # This is how we compile # TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3 TCCX += -I$(TOP)/ext/async TCPPX = g++ -Wall -g -I. -I$(TOP)/src $(OPTS) LIBOBJ+= vdbe.o parse.o \ alter.o analyze.o attach.o auth.o \ build.o \ callback.o complete.o ctime.o date.o delete.o env.o expr.o \ fault.o fkey.o fts5.o fts5func.o \ func.o global.o hash.o \ icu.o insert.o kv.o kvlsm.o kvmem.o legacy.o \ lsm_ckpt.o lsm_file.o lsm_log.o lsm_main.o lsm_mem.o lsm_mutex.o \ lsm_shared.o lsm_str.o lsm_sorted.o lsm_tree.o \ lsm_unix.o lsm_varint.o \ main.o malloc.o math.o mem.o mem0.o mem1.o mem2.o mem3.o mem5.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ opcodes.o os.o \ pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o \ tokenize.o trigger.o \ update.o util.o varint.o \ vdbeapi.o vdbeaux.o vdbecodec.o vdbecursor.o \ |
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 | $(TOP)/src/auth.c \ $(TOP)/src/build.c \ $(TOP)/src/callback.c \ $(TOP)/src/complete.c \ $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/fault.c \ $(TOP)/src/fkey.c \ $(TOP)/src/fts5.c \ $(TOP)/src/fts5func.c \ $(TOP)/src/func.c \ $(TOP)/src/global.c \ | > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | $(TOP)/src/auth.c \ $(TOP)/src/build.c \ $(TOP)/src/callback.c \ $(TOP)/src/complete.c \ $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/env.c \ $(TOP)/src/expr.c \ $(TOP)/src/fault.c \ $(TOP)/src/fkey.c \ $(TOP)/src/fts5.c \ $(TOP)/src/fts5func.c \ $(TOP)/src/func.c \ $(TOP)/src/global.c \ |
︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 | $(TOP)/src/lsm_sorted.c \ $(TOP)/src/lsm_tree.c \ $(TOP)/src/lsm_unix.c \ $(TOP)/src/lsm_varint.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/math.c \ $(TOP)/src/mem0.c \ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ | > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | $(TOP)/src/lsm_sorted.c \ $(TOP)/src/lsm_tree.c \ $(TOP)/src/lsm_unix.c \ $(TOP)/src/lsm_varint.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/math.c \ $(TOP)/src/mem.c \ $(TOP)/src/mem0.c \ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ |
︙ | ︙ | |||
485 486 487 488 489 490 491 | TESTFIXTURE_PREREQ = $(TESTSRC) $(TESTSRC2) TESTFIXTURE_PREREQ += $(TOP)/src/tclsqlite.c TESTFIXTURE_PREREQ += libsqlite4.a testfixture$(EXE): $(TESTFIXTURE_PREREQ) $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ | | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | TESTFIXTURE_PREREQ = $(TESTSRC) $(TESTSRC2) TESTFIXTURE_PREREQ += $(TOP)/src/tclsqlite.c TESTFIXTURE_PREREQ += libsqlite4.a testfixture$(EXE): $(TESTFIXTURE_PREREQ) $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ -o testfixture$(EXE) $(LIBTCL) libsqlite4.a $(THREADLIB) amalgamation-testfixture$(EXE): sqlite4.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite4.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fts3-testfixture$(EXE): sqlite4.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c |
︙ | ︙ | |||
527 528 529 530 531 532 533 534 535 536 537 538 539 540 | # threadtest3$(EXE): sqlite4.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c $(TCCX) -O2 sqlite4.o $(TOP)/test/threadtest3.c \ -o threadtest3$(EXE) $(THREADLIB) threadtest: threadtest3$(EXE) ./threadtest3$(EXE) TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO) $(TEST_EXTENSION): $(TOP)/test/test_loadext.c $(MKSHLIB) $(TOP)/test/test_loadext.c -o $(TEST_EXTENSION) extensiontest: testfixture$(EXE) $(TEST_EXTENSION) ./testfixture$(EXE) $(TOP)/test/loadext.test | > > > > > | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | # threadtest3$(EXE): sqlite4.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c $(TCCX) -O2 sqlite4.o $(TOP)/test/threadtest3.c \ -o threadtest3$(EXE) $(THREADLIB) threadtest: threadtest3$(EXE) ./threadtest3$(EXE) SQLSRC = $(TOP)/lsm-test/sqltest.c $(TOP)/lsm-test/lsmtest_util.c sqltest$(EXE): $(SQLSRC) libsqlite4.a $(TCCX) $(TOP)/lsm-test/sqltest.c \ -o sqltest$(EXE) -lsqlite3 libsqlite4.a $(THREADLIB) TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO) $(TEST_EXTENSION): $(TOP)/test/test_loadext.c $(MKSHLIB) $(TOP)/test/test_loadext.c -o $(TEST_EXTENSION) extensiontest: testfixture$(EXE) $(TEST_EXTENSION) ./testfixture$(EXE) $(TOP)/test/loadext.test |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
75 76 77 78 79 80 81 | len = sqlite4GetToken(zCsr, &token); } while( token==TK_SPACE ); assert( len>0 ); } while( token!=TK_LP && token!=TK_USING ); zRet = sqlite4MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, zTableName, tname.z+tname.n); | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | len = sqlite4GetToken(zCsr, &token); } while( token==TK_SPACE ); assert( len>0 ); } while( token!=TK_LP && token!=TK_USING ); zRet = sqlite4MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, zTableName, tname.z+tname.n); sqlite4_result_text(context, zRet, -1, SQLITE4_TRANSIENT, 0); sqlite4DbFree(db, zRet); } } /* ** This C function implements an SQL user function that is used by SQL code ** generated by the ALTER TABLE ... RENAME command to modify the definition |
︙ | ︙ | |||
138 139 140 141 142 143 144 | zInput = &z[n]; } sqlite4DbFree(db, zParent); } } zResult = sqlite4MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput), | | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | zInput = &z[n]; } sqlite4DbFree(db, zParent); } } zResult = sqlite4MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput), sqlite4_result_text(context, zResult, -1, SQLITE4_TRANSIENT, 0); sqlite4DbFree(db, zOutput); sqlite4DbFree(db, zResult); } #endif #ifndef SQLITE4_OMIT_TRIGGER /* This function is used by SQL generated to implement the |
︙ | ︙ | |||
216 217 218 219 220 221 222 | } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); /* Variable tname now contains the token that is the old table-name ** in the CREATE TRIGGER statement. */ zRet = sqlite4MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, zTableName, tname.z+tname.n); | | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); /* Variable tname now contains the token that is the old table-name ** in the CREATE TRIGGER statement. */ zRet = sqlite4MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, zTableName, tname.z+tname.n); sqlite4_result_text(context, zRet, -1, SQLITE4_TRANSIENT, 0); sqlite4DbFree(db, zRet); } } #endif /* !SQLITE4_OMIT_TRIGGER */ /* ** Register built-in functions used to help implement ALTER TABLE |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 | if( pDb->pKV->iTransLevel ){ sqlite4_snprintf(zErr,sizeof(zErr), "database %s is locked", zName); goto detach_error; } sqlite4KVStoreClose(pDb->pKV); pDb->pKV = 0; pDb->pSchema = 0; sqlite4ResetInternalSchema(db, -1); return; detach_error: sqlite4_result_error(context, zErr, -1); } | > > | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | if( pDb->pKV->iTransLevel ){ sqlite4_snprintf(zErr,sizeof(zErr), "database %s is locked", zName); goto detach_error; } sqlite4KVStoreClose(pDb->pKV); pDb->pKV = 0; sqlite4SchemaClear(db->pEnv, pDb->pSchema); sqlite4DbFree(db, pDb->pSchema); pDb->pSchema = 0; sqlite4ResetInternalSchema(db, -1); return; detach_error: sqlite4_result_error(context, zErr, -1); } |
︙ | ︙ |
Changes to src/callback.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | db->xCollNeeded(db->pCollNeededArg, db, enc, zExternal); sqlite4DbFree(db, zExternal); } #ifndef SQLITE4_OMIT_UTF16 if( db->xCollNeeded16 ){ char const *zExternal; sqlite4_value *pTmp = sqlite4ValueNew(db); | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | db->xCollNeeded(db->pCollNeededArg, db, enc, zExternal); sqlite4DbFree(db, zExternal); } #ifndef SQLITE4_OMIT_UTF16 if( db->xCollNeeded16 ){ char const *zExternal; sqlite4_value *pTmp = sqlite4ValueNew(db); sqlite4ValueSetStr(pTmp, -1, zName, SQLITE4_UTF8, SQLITE4_STATIC, 0); zExternal = sqlite4ValueText(pTmp, SQLITE4_UTF16NATIVE); if( zExternal ){ db->xCollNeeded16(db->pCollNeededArg, db, (int)ENC(db), zExternal); } sqlite4ValueFree(pTmp); } #endif |
︙ | ︙ |
Changes to src/complete.c.
︙ | ︙ | |||
265 266 267 268 269 270 271 | int rc = SQLITE4_NOMEM; #ifndef SQLITE4_OMIT_AUTOINIT rc = sqlite4_initialize(0); if( rc ) return rc; #endif pVal = sqlite4ValueNew(0); | | | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | int rc = SQLITE4_NOMEM; #ifndef SQLITE4_OMIT_AUTOINIT rc = sqlite4_initialize(0); if( rc ) return rc; #endif pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, zSql, SQLITE4_UTF16NATIVE, SQLITE4_STATIC, 0); zSql8 = sqlite4ValueText(pVal, SQLITE4_UTF8); if( zSql8 ){ rc = sqlite4_complete(zSql8); }else{ rc = SQLITE4_NOMEM; } sqlite4ValueFree(pVal); |
︙ | ︙ |
Changes to src/date.c.
︙ | ︙ | |||
810 811 812 813 814 815 816 | ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ char zBuf[100]; computeYMD_HMS(&x); sqlite4_snprintf(zBuf,sizeof(zBuf), "%04d-%02d-%02d %02d:%02d:%02d", x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); | | | | | 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ char zBuf[100]; computeYMD_HMS(&x); sqlite4_snprintf(zBuf,sizeof(zBuf), "%04d-%02d-%02d %02d:%02d:%02d", x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); sqlite4_result_text(context, zBuf, -1, SQLITE4_TRANSIENT, 0); } } /* ** time( TIMESTRING, MOD, MOD, ...) ** ** Return HH:MM:SS */ static void timeFunc( sqlite4_context *context, int argc, sqlite4_value **argv ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ char zBuf[100]; computeHMS(&x); sqlite4_snprintf(zBuf,sizeof(zBuf), "%02d:%02d:%02d", x.h, x.m, (int)x.s); sqlite4_result_text(context, zBuf, -1, SQLITE4_TRANSIENT, 0); } } /* ** date( TIMESTRING, MOD, MOD, ...) ** ** Return YYYY-MM-DD */ static void dateFunc( sqlite4_context *context, int argc, sqlite4_value **argv ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ char zBuf[100]; computeYMD(&x); sqlite4_snprintf(zBuf,sizeof(zBuf), "%04d-%02d-%02d", x.Y, x.M, x.D); sqlite4_result_text(context, zBuf, -1, SQLITE4_TRANSIENT, 0); } } /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** ** Return a string described by FORMAT. Conversions as follows: |
︙ | ︙ | |||
996 997 998 999 1000 1001 1002 | } default: z[j++] = '%'; break; } } } z[j] = 0; sqlite4_result_text(context, z, -1, | | | 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 | } default: z[j++] = '%'; break; } } } z[j] = 0; sqlite4_result_text(context, z, -1, z==zBuf ? SQLITE4_TRANSIENT : SQLITE4_DYNAMIC, 0); } /* ** current_time() ** ** This function returns the same value as time('now'). */ |
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 | sqlite4_mutex_enter(sqlite4MutexAlloc(SQLITE4_MUTEX_STATIC_MASTER)); pTm = gmtime(&t); if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); sqlite4_mutex_leave(sqlite4MutexAlloc(SQLITE4_MUTEX_STATIC_MASTER)); #endif if( pTm ){ strftime(zBuf, 20, zFormat, &sNow); | | | 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 | sqlite4_mutex_enter(sqlite4MutexAlloc(SQLITE4_MUTEX_STATIC_MASTER)); pTm = gmtime(&t); if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); sqlite4_mutex_leave(sqlite4MutexAlloc(SQLITE4_MUTEX_STATIC_MASTER)); #endif if( pTm ){ strftime(zBuf, 20, zFormat, &sNow); sqlite4_result_text(context, zBuf, -1, SQLITE4_TRANSIENT, 0); } } #endif /* ** This function registered all of the above C functions as SQL ** functions. This should be the only routine in this file with |
︙ | ︙ |
Changes to src/delete.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 | } sqlite4SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite4Select(pParse, pDup, &dest); sqlite4SelectDelete(db, pDup); } #endif /* !defined(SQLITE4_OMIT_VIEW) && !defined(SQLITE4_OMIT_TRIGGER) */ | | > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | } sqlite4SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite4Select(pParse, pDup, &dest); sqlite4SelectDelete(db, pDup); } #endif /* !defined(SQLITE4_OMIT_VIEW) && !defined(SQLITE4_OMIT_TRIGGER) */ #if defined(SQLITE4_ENABLE_UPDATE_DELETE_LIMIT) \ && !defined(SQLITE4_OMIT_SUBQUERY) /* ** Generate an expression tree to implement the WHERE, ORDER BY, ** and LIMIT/OFFSET portion of DELETE and UPDATE statements. ** ** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1; ** \__________________________/ ** pLimitWhere (pInClause) |
︙ | ︙ | |||
205 206 207 208 209 210 211 | limit_where_cleanup_2: sqlite4ExprDelete(pParse->db, pWhere); sqlite4ExprListDelete(pParse->db, pOrderBy); sqlite4ExprDelete(pParse->db, pLimit); sqlite4ExprDelete(pParse->db, pOffset); return 0; } | | > | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | limit_where_cleanup_2: sqlite4ExprDelete(pParse->db, pWhere); sqlite4ExprListDelete(pParse->db, pOrderBy); sqlite4ExprDelete(pParse->db, pLimit); sqlite4ExprDelete(pParse->db, pOffset); return 0; } #endif /* defined(SQLITE4_ENABLE_UPDATE_DELETE_LIMIT) */ /* && !defined(SQLITE4_OMIT_SUBQUERY) */ /* ** Generate code for a DELETE FROM statement. ** ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; ** \________/ \________________/ ** pTabList pWhere |
︙ | ︙ | |||
261 262 263 264 265 266 267 | ** delete. */ if( sqlite4IsReadOnly(pParse, pTab, pTrigger!=0) ) goto delete_from_cleanup; assert( !IsView(pTab) || pTrigger ); assert( !IsView(pTab) || pTab->pIndex==0 ); /* Invoke the authorization callback */ rcauth = sqlite4AuthCheck(pParse, SQLITE4_DELETE, pTab->zName, 0, zDb); | | > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | ** delete. */ if( sqlite4IsReadOnly(pParse, pTab, pTrigger!=0) ) goto delete_from_cleanup; assert( !IsView(pTab) || pTrigger ); assert( !IsView(pTab) || pTab->pIndex==0 ); /* Invoke the authorization callback */ rcauth = sqlite4AuthCheck(pParse, SQLITE4_DELETE, pTab->zName, 0, zDb); assert( rcauth==SQLITE4_OK || rcauth==SQLITE4_DENY || rcauth==SQLITE4_IGNORE ); if( rcauth==SQLITE4_DENY ){ goto delete_from_cleanup; } /* Assign a cursor number to the table or view this statement is ** deleting from. If pTab is actually a view, this will be used as the ** ephemeral table cursor. |
︙ | ︙ |
Added src/env.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | /* ** 2013 January 7 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains code used to help implement the sqlite4_env object. */ #include "sqliteInt.h" /* ** Default factory objects */ static KVFactory memFactory = { 0, "temp", sqlite4KVStoreOpenMem, 1 }; KVFactory sqlite4BuiltinFactory = { &memFactory, "main", sqlite4KVStoreOpenLsm, 1 }; /* ** The following singleton contains the global configuration for ** the SQLite library. */ struct sqlite4_env sqlite4DefaultEnv = { sizeof(sqlite4_env), /* nByte */ 1, /* iVersion */ SQLITE4_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE4_THREADSAFE==1, /* bFullMutex */ 0x7ffffffe, /* mxStrlen */ 128, /* szLookaside */ 500, /* nLookaside */ &sqlite4MMSystem, /* pMM */ {0,0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0,0}, /* mutex */ (void*)0, /* pHeap */ 0, /* nHeap */ 0, 0, /* mnHeap, mxHeap */ 0, /* mxParserStack */ &sqlite4BuiltinFactory, /* pFactory */ sqlite4OsRandomness, /* xRandomness */ sqlite4OsCurrentTime, /* xCurrentTime */ /* All the rest should always be initialized to zero */ 0, /* isInit */ 0, /* pFactoryMutex */ 0, /* pPrngMutex */ 0, 0, /* prngX, prngY */ 0, /* xLog */ 0, /* pLogArg */ 0, /* bLocaltimeFault */ 0, /* pMemMutex */ {0,0,0,0}, /* nowValue[] */ {0,0,0,0}, /* mxValue[] */ {0,} /* hashGlobalFunc */ }; /* ** Return the default environment */ sqlite4_env *sqlite4_env_default(void){ return &sqlite4DefaultEnv; } /* ** Initialize SQLite. ** ** This routine must be called to initialize the run-time environment ** As long as you do not compile with SQLITE4_OMIT_AUTOINIT ** this routine will be called automatically by key routines such as ** sqlite4_open(). ** ** This routine is a no-op except on its very first call for a given ** sqlite4_env object, or for the first call after a call to sqlite4_shutdown. ** ** This routine is not threadsafe. It should be called from a single ** thread to initialized the library in a multi-threaded system. Other ** threads should avoid using the sqlite4_env object until after it has ** completely initialized. */ int sqlite4_initialize(sqlite4_env *pEnv){ MUTEX_LOGIC( sqlite4_mutex *pMaster; ) /* The main static mutex */ int rc; /* Result code */ if( pEnv==0 ) pEnv = &sqlite4DefaultEnv; /* If SQLite is already completely initialized, then this call ** to sqlite4_initialize() should be a no-op. But the initialization ** must be complete. So isInit must not be set until the very end ** of this routine. */ if( pEnv->isInit ) return SQLITE4_OK; /* Initialize the mutex subsystem */ rc = sqlite4MutexInit(pEnv); if( rc ){ sqlite4MallocEnd(pEnv); return rc; } /* Initialize the memory allocation subsystem */ rc = sqlite4MallocInit(pEnv); if( rc ) return rc; /* Create required mutexes */ if( pEnv->bCoreMutex ){ pEnv->pMemMutex = sqlite4MutexAlloc(pEnv, SQLITE4_MUTEX_FAST); pEnv->pPrngMutex = sqlite4MutexAlloc(pEnv, SQLITE4_MUTEX_FAST); pEnv->pFactoryMutex = sqlite4MutexAlloc(pEnv, SQLITE4_MUTEX_FAST); if( pEnv->pMemMutex==0 || pEnv->pPrngMutex==0 || pEnv->pFactoryMutex==0 ){ rc = SQLITE4_NOMEM; } }else{ pEnv->pMemMutex = 0; pEnv->pPrngMutex = 0; } pEnv->isInit = 1; sqlite4OsInit(pEnv); /* Register global functions */ if( rc==SQLITE4_OK ){ sqlite4RegisterGlobalFunctions(pEnv); } /* The following is just a sanity check to make sure SQLite has ** been compiled correctly. It is important to run this code, but ** we don't want to run it too often and soak up CPU cycles for no ** reason. So we run it once during initialization. */ #ifndef NDEBUG #ifndef SQLITE4_OMIT_FLOATING_POINT /* This section of code's only "output" is via assert() statements. */ if ( rc==SQLITE4_OK ){ u64 x = (((u64)1)<<63)-1; double y; assert(sizeof(x)==8); assert(sizeof(x)==sizeof(y)); memcpy(&y, &x, 8); assert( sqlite4IsNaN(y) ); } #endif #endif return rc; } /* ** Undo the effects of sqlite4_initialize(). Must not be called while ** there are outstanding database connections or memory allocations or ** while any part of SQLite is otherwise in use in any thread. This ** routine is not threadsafe. But it is safe to invoke this routine ** on when SQLite is already shut down. If SQLite is already shut down ** when this routine is invoked, then this routine is a harmless no-op. */ int sqlite4_shutdown(sqlite4_env *pEnv){ if( pEnv==0 ) pEnv = &sqlite4DefaultEnv; if( pEnv->isInit ){ KVFactory *pMkr; sqlite4_mutex_free(pEnv->pFactoryMutex); sqlite4_mutex_free(pEnv->pPrngMutex); sqlite4_mutex_free(pEnv->pMemMutex); pEnv->pMemMutex = 0; while( (pMkr = pEnv->pFactory)!=0 && pMkr->isPerm==0 ){ KVFactory *pNext = pMkr->pNext; sqlite4_free(pEnv, pMkr); pMkr = pNext; } sqlite4MutexEnd(pEnv); sqlite4MallocEnd(pEnv); pEnv->isInit = 0; } return SQLITE4_OK; } /* ** Return the size of an sqlite4_env object */ int sqlite4_env_size(void){ return sizeof(sqlite4_env); } /* ** This API allows applications to modify the configuration described by ** an sqlite4_env object. */ int sqlite4_env_config(sqlite4_env *pEnv, int op, ...){ va_list ap; int rc = SQLITE4_OK; if( pEnv==0 ) pEnv = sqlite4_env_default(); va_start(ap, op); switch( op ){ /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_INIT, template); ** ** Turn bulk memory into a new sqlite4_env object. The template is ** a prior sqlite4_env that is used as a template in initializing the ** new sqlite4_env object. The size of the bulk memory must be at ** least as many bytes as returned from sqlite4_env_size(). */ case SQLITE4_ENVCONFIG_INIT: { /* Disable all mutexing */ sqlite4_env *pTemplate = va_arg(ap, sqlite4_env*); int n = pTemplate->nByte; if( n>sizeof(sqlite4_env) ) n = sizeof(sqlite4_env); memcpy(pEnv, pTemplate, n); pEnv->pFactory = &sqlite4BuiltinFactory; pEnv->isInit = 0; break; } /* Mutex configuration options are only available in a threadsafe ** compile. */ #if defined(SQLITE4_THREADSAFE) && SQLITE4_THREADSAFE>0 /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_SINGLETHREAD); ** ** Configure this environment for a single-threaded application. */ case SQLITE4_ENVCONFIG_SINGLETHREAD: { /* Disable all mutexing */ if( pEnv->isInit ){ rc = SQLITE4_MISUSE; break; } pEnv->bCoreMutex = 0; pEnv->bFullMutex = 0; break; } /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_MULTITHREAD); ** ** Configure this environment for a multi-threaded application where ** the same database connection is never used by more than a single ** thread at a time. */ case SQLITE4_ENVCONFIG_MULTITHREAD: { /* Disable mutexing of database connections */ /* Enable mutexing of core data structures */ if( pEnv->isInit ){ rc = SQLITE4_MISUSE; break; } pEnv->bCoreMutex = 1; pEnv->bFullMutex = 0; break; } /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_SERIALIZED); ** ** Configure this environment for an unrestricted multi-threaded ** application where any thread can do whatever it wants with any ** database connection at any time. */ case SQLITE4_ENVCONFIG_SERIALIZED: { /* Enable all mutexing */ if( pEnv->isInit ){ rc = SQLITE4_MISUSE; break; } pEnv->bCoreMutex = 1; pEnv->bFullMutex = 1; break; } /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_MUTEXT, sqlite4_mutex_methods*) ** ** Configure this environment to use the mutex routines specified by the ** argument. */ case SQLITE4_ENVCONFIG_MUTEX: { /* Specify an alternative mutex implementation */ if( pEnv->isInit ){ rc = SQLITE4_MISUSE; break; } pEnv->mutex = *va_arg(ap, sqlite4_mutex_methods*); break; } /* ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_GETMUTEX, sqlite4_mutex_methods*) ** ** Copy the mutex routines in use by this environment into the structure ** given in the argument. */ case SQLITE4_ENVCONFIG_GETMUTEX: { /* Retrieve the current mutex implementation */ *va_arg(ap, sqlite4_mutex_methods*) = pEnv->mutex; break; } #endif /* ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_MALLOC, sqlite4_mem_methods*) ** ** Set the memory allocation routines to be used by this environment. */ case SQLITE4_ENVCONFIG_MALLOC: { /* Specify an alternative malloc implementation */ if( pEnv->isInit ) return SQLITE4_MISUSE; pEnv->m = *va_arg(ap, sqlite4_mem_methods*); break; } /* ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_GETMALLOC, sqlite4_mem_methods*) ** ** Copy the memory allocation routines in use by this environment ** into the structure given in the argument. */ case SQLITE4_ENVCONFIG_GETMALLOC: { /* Retrieve the current malloc() implementation */ if( pEnv->m.xMalloc==0 ) sqlite4MemSetDefault(pEnv); *va_arg(ap, sqlite4_mem_methods*) = pEnv->m; break; } /* sqlite4_env_config(p, SQLITE4_ENVCONFIG_MEMSTAT, int onoff); ** ** Enable or disable collection of memory usage statistics according to ** the onoff parameter. */ case SQLITE4_ENVCONFIG_MEMSTATUS: { /* Enable or disable the malloc status collection */ pEnv->bMemstat = va_arg(ap, int); break; } /* ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_LOOKASIDE, size, count); ** ** Set the default lookaside memory settings for all subsequent ** database connections constructed in this environment. The size ** parameter is the size of each lookaside memory buffer and the ** count parameter is the number of lookaside buffers. Set both ** to zero to disable lookaside memory. */ case SQLITE4_ENVCONFIG_LOOKASIDE: { pEnv->szLookaside = va_arg(ap, int); pEnv->nLookaside = va_arg(ap, int); break; } /* ** sqlite4_env_config(p, SQLITE4_ENVCONFIG_LOG, xOutput, pArg); ** ** Set the log function that is called in response to sqlite4_log() ** calls. */ case SQLITE4_ENVCONFIG_LOG: { /* MSVC is picky about pulling func ptrs from va lists. ** http://support.microsoft.com/kb/47961 ** pEnv->xLog = va_arg(ap, void(*)(void*,int,const char*)); */ typedef void(*LOGFUNC_t)(void*,int,const char*); pEnv->xLog = va_arg(ap, LOGFUNC_t); pEnv->pLogArg = va_arg(ap, void*); break; } /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_KVSTORE_PUSH, zName,xFactory); ** ** Push a new KVStore factory onto the factory stack. The new factory ** takes priority over prior factories. */ case SQLITE4_ENVCONFIG_KVSTORE_PUSH: { const char *zName = va_arg(ap, const char*); int nName = sqlite4Strlen30(zName); KVFactory *pMkr = sqlite4_malloc(pEnv, sizeof(*pMkr)+nName+1); char *z; if( pMkr==0 ) return SQLITE4_NOMEM; z = (char*)&pMkr[1]; memcpy(z, zName, nName+1); memset(pMkr, 0, sizeof(*pMkr)); pMkr->zName = z; pMkr->xFactory = va_arg(ap, sqlite4_kvfactory); sqlite4_mutex_enter(pEnv->pFactoryMutex); pMkr->pNext = pEnv->pFactory; pEnv->pFactory = pMkr; sqlite4_mutex_leave(pEnv->pFactoryMutex); break; } /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_KVSTORE_POP, zName, &pxFact); ** ** Remove a KVStore factory from the stack. */ /* ** sqlite4_env_config(pEnv, SQLITE4_ENVCONFIG_KVSTORE_GET, zName, &pxFact); ** ** Get the current factory pointer with the given name but leave the ** factory on the stack. */ case SQLITE4_ENVCONFIG_KVSTORE_POP: case SQLITE4_ENVCONFIG_KVSTORE_GET: { typedef int (**PxFact)(sqlite4_env*,KVStore**,const char*,unsigned); const char *zName = va_arg(ap, const char*); KVFactory *pMkr, **ppPrev; PxFact pxFact; pxFact = va_arg(ap,PxFact); *pxFact = 0; sqlite4_mutex_enter(pEnv->pFactoryMutex); ppPrev = &pEnv->pFactory; pMkr = *ppPrev; while( pMkr && strcmp(zName, pMkr->zName)!=0 ){ ppPrev = &pMkr->pNext; pMkr = *ppPrev; } if( pMkr ){ *pxFact = pMkr->xFactory; if( op==SQLITE4_ENVCONFIG_KVSTORE_POP && pMkr->isPerm==0 ){ *ppPrev = pMkr->pNext; sqlite4_free(pEnv, pMkr); } } sqlite4_mutex_leave(pEnv->pFactoryMutex); break; } default: { rc = SQLITE4_ERROR; break; } } va_end(ap); return rc; } |
Changes to src/expr.c.
︙ | ︙ | |||
362 363 364 365 366 367 368 | ** for this node and for the pToken argument is a single allocation ** obtained from sqlite4DbMalloc(). The calling function ** is responsible for making sure the node eventually gets freed. ** ** If dequote is true, then the token (if it exists) is dequoted. ** If dequote is false, no dequoting is performance. The deQuote ** parameter is ignored if pToken is NULL or if the token does not | | < | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | ** for this node and for the pToken argument is a single allocation ** obtained from sqlite4DbMalloc(). The calling function ** is responsible for making sure the node eventually gets freed. ** ** If dequote is true, then the token (if it exists) is dequoted. ** If dequote is false, no dequoting is performance. The deQuote ** parameter is ignored if pToken is NULL or if the token does not ** appear to be quoted. ** ** Special case: If op==TK_INTEGER and pToken points to a string that ** can be translated into a 32-bit integer, then the token is not ** stored in u.zToken. Instead, the integer values is written ** into u.iValue and the EP_IntValue flag is set. No extra storage ** is allocated to hold the integer text and the dequote flag is ignored. */ |
︙ | ︙ | |||
403 404 405 406 407 408 409 | }else{ int c; pNew->u.zToken = (char*)&pNew[1]; assert( pToken->z!=0 || pToken->n==0 ); if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && nExtra>=3 | | < | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | }else{ int c; pNew->u.zToken = (char*)&pNew[1]; assert( pToken->z!=0 || pToken->n==0 ); if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && nExtra>=3 && ((c = pToken->z[0])=='\'' || c=='"' || c=='[') ){ sqlite4Dequote(pNew->u.zToken); } } } #if SQLITE4_MAX_EXPR_DEPTH>0 pNew->nHeight = 1; #endif } |
︙ | ︙ |
Changes to src/fts5.c.
︙ | ︙ | |||
3392 3393 3394 3395 3396 3397 3398 | if( zErr==0 ){ zErr = sqlite4MPrintf(db, "error parsing expression: %d", rc); } goto fts5_parse_expr_out; } fts5PrintExpr(db, azCol, pExpr, &zRet); | | | 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 | if( zErr==0 ){ zErr = sqlite4MPrintf(db, "error parsing expression: %d", rc); } goto fts5_parse_expr_out; } fts5PrintExpr(db, azCol, pExpr, &zRet); sqlite4_result_text(pCtx, zRet, -1, SQLITE4_TRANSIENT, 0); fts5ExpressionFree(db, pExpr); sqlite4_free(sqlite4_db_env(db), zRet); fts5_parse_expr_out: if( p ) pTok->xDestroy(p); sqlite4DbFree(db, azCol); sqlite4_finalize(pStmt); |
︙ | ︙ | |||
3419 3420 3421 3422 3423 3424 3425 | int rc = sqlite4_create_function( db, "fts5_parse_expr", 3, SQLITE4_UTF8, 0, fts5_parse_expr, 0, 0 ); if( rc!=SQLITE4_OK ) return rc; #endif return sqlite4InitFts5Func(db); } | < | 3419 3420 3421 3422 3423 3424 3425 | int rc = sqlite4_create_function( db, "fts5_parse_expr", 3, SQLITE4_UTF8, 0, fts5_parse_expr, 0, 0 ); if( rc!=SQLITE4_OK ) return rc; #endif return sqlite4InitFts5Func(db); } |
Changes to src/fts5func.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 | struct Fts5RankCtx { sqlite4 *db; double *aAvgdl; /* Average document size of each field */ int nPhrase; /* Number of phrases in query */ double *aIdf; /* IDF weights for each phrase in query */ }; | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | struct Fts5RankCtx { sqlite4 *db; double *aAvgdl; /* Average document size of each field */ int nPhrase; /* Number of phrases in query */ double *aIdf; /* IDF weights for each phrase in query */ }; static void fts5RankFreeCtx(void *pNotUsed, void *pCtx){ if( pCtx ){ Fts5RankCtx *p = (Fts5RankCtx *)pCtx; sqlite4DbFree(p->db, p); } } #define BM25_EXPLAIN 0x01 |
︙ | ︙ | |||
132 133 134 135 136 137 138 | if( p==0 ){ int nPhrase; /* Number of phrases in query expression */ int nByte; /* Number of bytes of data to allocate */ sqlite4_mi_phrase_count(pCtx, &nPhrase); nByte = sizeof(Fts5RankCtx) + (nPhrase+nField) * sizeof(double); p = (Fts5RankCtx *)sqlite4DbMallocZero(db, nByte); | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | if( p==0 ){ int nPhrase; /* Number of phrases in query expression */ int nByte; /* Number of bytes of data to allocate */ sqlite4_mi_phrase_count(pCtx, &nPhrase); nByte = sizeof(Fts5RankCtx) + (nPhrase+nField) * sizeof(double); p = (Fts5RankCtx *)sqlite4DbMallocZero(db, nByte); sqlite4_set_auxdata(pCtx, 0, (void *)p, fts5RankFreeCtx, 0); p = sqlite4_get_auxdata(pCtx, 0); if( !p ){ rc = SQLITE4_NOMEM; }else{ int N; /* Total number of docs in collection */ int ni; /* Number of docs with phrase i */ |
︙ | ︙ | |||
266 267 268 269 270 271 272 | } if( rc==SQLITE4_OK ){ if( bExplain ){ zExplain = sqlite4MAppendf( db, zExplain, "%s</table><b>overall rank=%.2f</b>", zExplain, rank ); | | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | } if( rc==SQLITE4_OK ){ if( bExplain ){ zExplain = sqlite4MAppendf( db, zExplain, "%s</table><b>overall rank=%.2f</b>", zExplain, rank ); sqlite4_result_text(pCtx, zExplain, -1, SQLITE4_TRANSIENT, 0); }else{ sqlite4_result_double(pCtx, rank); } }else{ sqlite4_result_error_code(pCtx, rc); } sqlite4DbFree(db, zExplain); |
︙ | ︙ | |||
542 543 544 545 546 547 548 549 550 551 552 553 554 555 | iOff += nShift; mask = mask >> nShift; pSnip->iOff = iOff; pSnip->hlmask = mask; } static void fts5Snippet(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){ Snippet aSnip[4]; int nSnip; int iCol = -1; int nToken = -15; int rc; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | iOff += nShift; mask = mask >> nShift; pSnip->iOff = iOff; pSnip->hlmask = mask; } /* ** Parameter aSnip points to an array of nSnip Snippet objects, where nSnip ** is less than or equal to 4. This function sorts the array in place in ** ascending order of Snippet.iCol and Snippet.iOff. */ static void fts5SnippetSort(Snippet *aSnip, int nSnip){ Snippet aTmp[4]; int i; assert( nSnip<=4 && nSnip>=1 ); for(i=0; i<nSnip; i++){ int iBest = -1; int iTry; for(iTry=0; iTry<nSnip; iTry++){ Snippet *pTry = &aSnip[iTry]; if( pTry->iCol>=0 && (iBest<0 || pTry->iCol<aSnip[iBest].iCol || (pTry->iCol==aSnip[iBest].iCol && pTry->iOff<aSnip[iBest].iOff) )){ iBest = iTry; } } assert( iBest>=0 ); memcpy(&aTmp[i], &aSnip[iBest], sizeof(Snippet)); aSnip[iBest].iCol = -1; } memcpy(aSnip, aTmp, sizeof(Snippet)*nSnip); } static void fts5Snippet(sqlite4_context *pCtx, int nArg, sqlite4_value **apArg){ Snippet aSnip[4]; int nSnip; int iCol = -1; int nToken = -15; int rc; |
︙ | ︙ | |||
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | memset(aSnip, 0, sizeof(aSnip)); for(i=0; rc==SQLITE4_OK && i<nSnip; i++){ rc = fts5BestSnippet(pCtx, iCol, &mask, nTok, &aSnip[i]); } if( mask==0 || nSnip==4 ){ SnippetText text = {0, 0, 0}; for(i=0; rc==SQLITE4_OK && i<nSnip; i++){ int nSz; rc = sqlite4_mi_size(pCtx, aSnip[i].iCol, -1, &nSz); if( rc==SQLITE4_OK ){ fts5SnippetImprove(pCtx, nTok, nSz, &aSnip[i]); rc = fts5SnippetText( pCtx, &aSnip[i], &text, nTok, zStart, zEnd, zEllipses ); } } | > | | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | memset(aSnip, 0, sizeof(aSnip)); for(i=0; rc==SQLITE4_OK && i<nSnip; i++){ rc = fts5BestSnippet(pCtx, iCol, &mask, nTok, &aSnip[i]); } if( mask==0 || nSnip==4 ){ SnippetText text = {0, 0, 0}; fts5SnippetSort(aSnip, nSnip); for(i=0; rc==SQLITE4_OK && i<nSnip; i++){ int nSz; rc = sqlite4_mi_size(pCtx, aSnip[i].iCol, -1, &nSz); if( rc==SQLITE4_OK ){ fts5SnippetImprove(pCtx, nTok, nSz, &aSnip[i]); rc = fts5SnippetText( pCtx, &aSnip[i], &text, nTok, zStart, zEnd, zEllipses ); } } sqlite4_result_text(pCtx, text.zOut, text.nOut, SQLITE4_TRANSIENT, 0); sqlite4DbFree(sqlite4_context_db_handle(pCtx), text.zOut); break; } } if( rc!=SQLITE4_OK ){ sqlite4_result_error_code(pCtx, rc); |
︙ | ︙ | |||
666 667 668 669 670 671 672 | void *p = SQLITE4_INT_TO_PTR(aRank[i].mask); const char *z = aRank[i].zName; rc = sqlite4_create_mi_function(db, z, -1, SQLITE4_UTF8, p, fts5Rank, 0); } return rc; } | < | 700 701 702 703 704 705 706 | void *p = SQLITE4_INT_TO_PTR(aRank[i].mask); const char *z = aRank[i].zName; rc = sqlite4_create_mi_function(db, z, -1, SQLITE4_UTF8, p, fts5Rank, 0); } return rc; } |
Changes to src/func.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | switch( sqlite4_value_type(argv[0]) ){ case SQLITE4_INTEGER: z = "integer"; break; case SQLITE4_TEXT: z = "text"; break; case SQLITE4_FLOAT: z = "real"; break; case SQLITE4_BLOB: z = "blob"; break; default: z = "null"; break; } | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | switch( sqlite4_value_type(argv[0]) ){ case SQLITE4_INTEGER: z = "integer"; break; case SQLITE4_TEXT: z = "text"; break; case SQLITE4_FLOAT: z = "real"; break; case SQLITE4_BLOB: z = "blob"; break; default: z = "null"; break; } sqlite4_result_text(context, z, -1, SQLITE4_STATIC, 0); } /* ** Implementation of the length() function */ static void lengthFunc( |
︙ | ︙ | |||
240 241 242 243 244 245 246 | while( *z && p1 ){ SQLITE4_SKIP_UTF8(z); p1--; } for(z2=z; *z2 && p2; p2--){ SQLITE4_SKIP_UTF8(z2); } | | | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | while( *z && p1 ){ SQLITE4_SKIP_UTF8(z); p1--; } for(z2=z; *z2 && p2; p2--){ SQLITE4_SKIP_UTF8(z2); } sqlite4_result_text(context, (char*)z, (int)(z2-z), SQLITE4_TRANSIENT, 0); }else{ if( p1+p2>len ){ p2 = len-p1; if( p2<0 ) p2 = 0; } sqlite4_result_blob(context, (char*)&z[p1], (int)p2, SQLITE4_TRANSIENT, 0); } } /* ** Implementation of the round() function */ #ifndef SQLITE4_OMIT_FLOATING_POINT |
︙ | ︙ | |||
332 333 334 335 336 337 338 | assert( z2==(char*)sqlite4_value_text(argv[0]) ); if( z2 ){ z1 = contextMalloc(context, ((i64)n)+1); if( z1 ){ for(i=0; i<n; i++){ z1[i] = (char)sqlite4Toupper(z2[i]); } | | | | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | assert( z2==(char*)sqlite4_value_text(argv[0]) ); if( z2 ){ z1 = contextMalloc(context, ((i64)n)+1); if( z1 ){ for(i=0; i<n; i++){ z1[i] = (char)sqlite4Toupper(z2[i]); } sqlite4_result_text(context, z1, n, SQLITE4_DYNAMIC, 0); } } } static void lowerFunc(sqlite4_context *context, int argc, sqlite4_value **argv){ char *z1; const char *z2; int i, n; UNUSED_PARAMETER(argc); z2 = (char*)sqlite4_value_text(argv[0]); n = sqlite4_value_bytes(argv[0]); /* Verify that the call to _bytes() does not invalidate the _text() pointer */ assert( z2==(char*)sqlite4_value_text(argv[0]) ); if( z2 ){ z1 = contextMalloc(context, ((i64)n)+1); if( z1 ){ for(i=0; i<n; i++){ z1[i] = sqlite4Tolower(z2[i]); } sqlite4_result_text(context, z1, n, SQLITE4_DYNAMIC, 0); } } } #if 0 /* This function is never used. */ /* |
︙ | ︙ | |||
430 431 432 433 434 435 436 | n = sqlite4_value_int(argv[0]); if( n<1 ){ n = 1; } p = contextMalloc(context, n); if( p ){ sqlite4_randomness(sqlite4_context_env(context), n, p); | | < < < < < < < < < < < < < < < < < | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | n = sqlite4_value_int(argv[0]); if( n<1 ){ n = 1; } p = contextMalloc(context, n); if( p ){ sqlite4_randomness(sqlite4_context_env(context), n, p); sqlite4_result_blob(context, (char*)p, n, SQLITE4_DYNAMIC, 0); } } /* ** Implementation of the changes() SQL function. ** ** IMP: R-62073-11209 The changes() SQL function is a wrapper ** around the sqlite4_changes() C/C++ function and hence follows the same |
︙ | ︙ | |||
750 751 752 753 754 755 756 | sqlite4_context *context, int NotUsed, sqlite4_value **NotUsed2 ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); /* IMP: R-48699-48617 This function is an SQL wrapper around the ** sqlite4_libversion() C-interface. */ | | | | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | sqlite4_context *context, int NotUsed, sqlite4_value **NotUsed2 ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); /* IMP: R-48699-48617 This function is an SQL wrapper around the ** sqlite4_libversion() C-interface. */ sqlite4_result_text(context, sqlite4_libversion(), -1, SQLITE4_STATIC, 0); } /* ** Implementation of the sqlite_source_id() function. The result is a string ** that identifies the particular version of the source code used to build ** SQLite. */ static void sourceidFunc( sqlite4_context *context, int NotUsed, sqlite4_value **NotUsed2 ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); /* IMP: R-24470-31136 This function is an SQL wrapper around the ** sqlite4_sourceid() C interface. */ sqlite4_result_text(context, sqlite4_sourceid(), -1, SQLITE4_STATIC, 0); } /* ** Implementation of the sqlite_log() function. This is a wrapper around ** sqlite4_log(). The return value is NULL. The function exists purely for ** its side-effects. */ |
︙ | ︙ | |||
826 827 828 829 830 831 832 | int n; assert( argc==1 ); UNUSED_PARAMETER(argc); /* IMP: R-04922-24076 The sqlite_compileoption_get() SQL function ** is a wrapper around the sqlite4_compileoption_get() C/C++ function. */ n = sqlite4_value_int(argv[0]); | | > | 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | int n; assert( argc==1 ); UNUSED_PARAMETER(argc); /* IMP: R-04922-24076 The sqlite_compileoption_get() SQL function ** is a wrapper around the sqlite4_compileoption_get() C/C++ function. */ n = sqlite4_value_int(argv[0]); sqlite4_result_text(context, sqlite4_compileoption_get(n), -1, SQLITE4_STATIC, 0); } #endif /* SQLITE4_OMIT_COMPILEOPTION_DIAGS */ /* Array for converting from half-bytes (nybbles) into ASCII hex ** digits. */ static const char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', |
︙ | ︙ | |||
873 874 875 876 877 878 879 | zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F]; zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F]; } zText[(nBlob*2)+2] = '\''; zText[(nBlob*2)+3] = '\0'; zText[0] = 'x'; zText[1] = '\''; | | | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F]; zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F]; } zText[(nBlob*2)+2] = '\''; zText[(nBlob*2)+3] = '\0'; zText[0] = 'x'; zText[1] = '\''; sqlite4_result_text(context, zText, -1, SQLITE4_TRANSIENT, 0); sqlite4_free(sqlite4_context_env(context), zText); } break; } case SQLITE4_TEXT: { int i,j; u64 n; |
︙ | ︙ | |||
897 898 899 900 901 902 903 | z[j++] = zArg[i]; if( zArg[i]=='\'' ){ z[j++] = '\''; } } z[j++] = '\''; z[j] = 0; | | | | 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 | z[j++] = zArg[i]; if( zArg[i]=='\'' ){ z[j++] = '\''; } } z[j++] = '\''; z[j] = 0; sqlite4_result_text(context, z, j, SQLITE4_DYNAMIC, 0); } break; } default: { assert( sqlite4_value_type(argv[0])==SQLITE4_NULL ); sqlite4_result_text(context, "NULL", 4, SQLITE4_STATIC, 0); break; } } } /* ** The hex() function. Interpret the argument as a blob. Return |
︙ | ︙ | |||
934 935 936 937 938 939 940 | if( zHex ){ for(i=0; i<n; i++, pBlob++){ unsigned char c = *pBlob; *(z++) = hexdigits[(c>>4)&0xf]; *(z++) = hexdigits[c&0xf]; } *z = 0; | | < < < < < < < < < < < < < < < < < < < < < < | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | if( zHex ){ for(i=0; i<n; i++, pBlob++){ unsigned char c = *pBlob; *(z++) = hexdigits[(c>>4)&0xf]; *(z++) = hexdigits[c&0xf]; } *z = 0; sqlite4_result_text(context, zHex, n*2, SQLITE4_DYNAMIC, 0); } } /* ** The replace() function. Three arguments are all strings: call ** them A, B, and C. The result is also a string which is derived ** from A by replacing every occurance of B with C. The match |
︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 | } } assert( j+nStr-i+1==nOut ); memcpy(&zOut[j], &zStr[i], nStr-i); j += nStr - i; assert( j<=nOut ); zOut[j] = 0; | | | 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | } } assert( j+nStr-i+1==nOut ); memcpy(&zOut[j], &zStr[i], nStr-i); j += nStr - i; assert( j<=nOut ); zOut[j] = 0; sqlite4_result_text(context, (char*)zOut, j, SQLITE4_DYNAMIC, 0); } /* ** Implementation of the TRIM(), LTRIM(), and RTRIM() functions. ** The userdata is 0x1 for left trim, 0x2 for right trim, 0x3 for both. */ static void trimFunc( |
︙ | ︙ | |||
1127 1128 1129 1130 1131 1132 1133 | nIn -= len; } } if( zCharSet ){ sqlite4_free(sqlite4_context_env(context), azChar); } } | | | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | nIn -= len; } } if( zCharSet ){ sqlite4_free(sqlite4_context_env(context), azChar); } } sqlite4_result_text(context, (char*)zIn, nIn, SQLITE4_TRANSIENT, 0); } /* IMP: R-25361-16150 This function is omitted from SQLite by default. It ** is only available if the SQLITE4_SOUNDEX compile-time option is used ** when SQLite is built. */ |
︙ | ︙ | |||
1182 1183 1184 1185 1186 1187 1188 | prevcode = 0; } } while( j<4 ){ zResult[j++] = '0'; } zResult[j] = 0; | | | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 | prevcode = 0; } } while( j<4 ){ zResult[j++] = '0'; } zResult[j] = 0; sqlite4_result_text(context, zResult, 4, SQLITE4_TRANSIENT, 0); }else{ /* IMP: R-64894-50321 The string "?000" is returned if the argument ** is NULL or contains no ASCII alphabetic characters. */ sqlite4_result_text(context, "?000", 4, SQLITE4_STATIC, 0); } } #endif /* SQLITE4_SOUNDEX */ #if 0 /*ndef SQLITE4_OMIT_LOAD_EXTENSION*/ /* ** A function that loads a shared-library extension then returns NULL. |
︙ | ︙ | |||
1303 1304 1305 1306 1307 1308 1309 | */ static void countStep(sqlite4_context *context, int argc, sqlite4_value **argv){ CountCtx *p; p = sqlite4_aggregate_context(context, sizeof(*p)); if( (argc==0 || SQLITE4_NULL!=sqlite4_value_type(argv[0])) && p ){ p->n++; } | < < < < < < < < < | 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 | */ static void countStep(sqlite4_context *context, int argc, sqlite4_value **argv){ CountCtx *p; p = sqlite4_aggregate_context(context, sizeof(*p)); if( (argc==0 || SQLITE4_NULL!=sqlite4_value_type(argv[0])) && p ){ p->n++; } } static void countFinalize(sqlite4_context *context){ CountCtx *p; p = sqlite4_aggregate_context(context, 0); sqlite4_result_int64(context, p ? p->n : 0); } |
︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 | if( pAccum ){ if( pAccum->tooBig ){ sqlite4_result_error_toobig(context); }else if( pAccum->mallocFailed ){ sqlite4_result_error_nomem(context); }else{ sqlite4_result_text(context, sqlite4StrAccumFinish(pAccum), -1, | | | 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 | if( pAccum ){ if( pAccum->tooBig ){ sqlite4_result_error_toobig(context); }else if( pAccum->mallocFailed ){ sqlite4_result_error_nomem(context); }else{ sqlite4_result_text(context, sqlite4StrAccumFinish(pAccum), -1, SQLITE4_DYNAMIC, 0); } } } /* ** This routine does per-connection function registration. Most ** of the built-in functions above are part of the global function set. |
︙ | ︙ | |||
1557 1558 1559 1560 1561 1562 1563 | FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), #ifndef SQLITE4_OMIT_COMPILEOPTION_DIAGS FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE4_OMIT_COMPILEOPTION_DIAGS */ FUNCTION(quote, 1, 0, 0, quoteFunc ), | < < | 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 | FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), #ifndef SQLITE4_OMIT_COMPILEOPTION_DIAGS FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE4_OMIT_COMPILEOPTION_DIAGS */ FUNCTION(quote, 1, 0, 0, quoteFunc ), FUNCTION(changes, 0, 0, 0, changes ), FUNCTION(total_changes, 0, 0, 0, total_changes ), FUNCTION(replace, 3, 0, 0, replaceFunc ), #ifdef SQLITE4_SOUNDEX FUNCTION(soundex, 1, 0, 0, soundexFunc ), #endif #if 0 /*ndef SQLITE4_OMIT_LOAD_EXTENSION*/ FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif |
︙ | ︙ |
Changes to src/global.c.
︙ | ︙ | |||
125 126 127 128 129 130 131 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ }; #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ }; #endif /* ** Constant tokens for values 0 and 1. */ const Token sqlite4IntTokens[] = { { "0", 1 }, { "1", 1 } |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1315 1316 1317 1318 1319 1320 1321 | if( pIdx!=pPk ){ for(i=0; i<pPk->nColumn; i++){ int idx = pPk->aiColumn[i]; sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i+pIdx->nColumn); } } sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iIdx, regTmp, regKey); | < < < | 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 | if( pIdx!=pPk ){ for(i=0; i<pPk->nColumn; i++){ int idx = pPk->aiColumn[i]; sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i+pIdx->nColumn); } } sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iIdx, regTmp, regKey); VdbeComment((v, "key for %s", pIdx->zName)); /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY ** index. In this case there is no need to test the index for uniqueness ** - all that is required is to populate the regKey register. Jump ** to the next iteration of the loop if this is the case. */ onError = pIdx->onError; |
︙ | ︙ | |||
1800 1801 1802 1803 1804 1805 1806 | addr1 = sqlite4VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite4VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } sqlite4VdbeAddOp2(v, OP_RowData, iSrc, regData); sqlite4VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); | | | 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 | addr1 = sqlite4VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite4VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } sqlite4VdbeAddOp2(v, OP_RowData, iSrc, regData); sqlite4VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); sqlite4VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_APPEND); sqlite4VdbeChangeP4(v, -1, pDest->zName, 0); sqlite4VdbeAddOp2(v, OP_Next, iSrc, addr1); for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } assert( pSrcIdx ); |
︙ | ︙ |
Changes to src/kvlsm.c.
︙ | ︙ | |||
459 460 461 462 463 464 465 | memset(pNew, 0, sizeof(KVLsm)); pNew->base.pStoreVfunc = &kvlsmMethods; pNew->base.pEnv = pEnv; rc = lsm_new(0, &pNew->pDb); if( rc==SQLITE4_OK ){ int i; | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | memset(pNew, 0, sizeof(KVLsm)); pNew->base.pStoreVfunc = &kvlsmMethods; pNew->base.pEnv = pEnv; rc = lsm_new(0, &pNew->pDb); if( rc==SQLITE4_OK ){ int i; int bMmap = 1; lsm_config(pNew->pDb, LSM_CONFIG_MMAP, &bMmap); for(i=0; i<ArraySize(aConfig); i++){ const char *zVal = sqlite4_uri_parameter(zName, aConfig[i].zParam); if( zVal ){ int nVal = sqlite4Atoi(zVal); lsm_config(pNew->pDb, aConfig[i].eParam, &nVal); } |
︙ | ︙ |
Changes to src/lsm.h.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | extern "C" { #endif /* ** Opaque handle types. */ typedef struct lsm_compress lsm_compress; /* Compression library functions */ typedef struct lsm_cursor lsm_cursor; /* Database cursor handle */ typedef struct lsm_db lsm_db; /* Database connection handle */ typedef struct lsm_env lsm_env; /* Runtime environment */ typedef struct lsm_file lsm_file; /* OS file handle */ typedef struct lsm_mutex lsm_mutex; /* Mutex handle */ /* 64-bit integer type used for file offsets. */ typedef long long int lsm_i64; /* 64-bit signed integer type */ /* Candidate values for the 3rd argument to lsm_env.xLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 /* ** CAPI: Database Runtime Environment ** ** Run-time environment used by LSM ** ** xMap(pFile, iOff, nByte, ppOut, pnOut): ** Memory map nByte bytes of file pFile starting at file offset iOff. If | > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | extern "C" { #endif /* ** Opaque handle types. */ typedef struct lsm_compress lsm_compress; /* Compression library functions */ typedef struct lsm_compress_factory lsm_compress_factory; typedef struct lsm_cursor lsm_cursor; /* Database cursor handle */ typedef struct lsm_db lsm_db; /* Database connection handle */ typedef struct lsm_env lsm_env; /* Runtime environment */ typedef struct lsm_file lsm_file; /* OS file handle */ typedef struct lsm_mutex lsm_mutex; /* Mutex handle */ /* 64-bit integer type used for file offsets. */ typedef long long int lsm_i64; /* 64-bit signed integer type */ /* Candidate values for the 3rd argument to lsm_env.xLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 /* Flags for lsm_env.xOpen() */ #define LSM_OPEN_READONLY 0x0001 /* ** CAPI: Database Runtime Environment ** ** Run-time environment used by LSM ** ** xMap(pFile, iOff, nByte, ppOut, pnOut): ** Memory map nByte bytes of file pFile starting at file offset iOff. If |
︙ | ︙ | |||
62 63 64 65 66 67 68 | */ struct lsm_env { int nByte; /* Size of this structure in bytes */ int iVersion; /* Version number of this structure (1) */ /****** file i/o ***********************************************/ void *pVfsCtx; int (*xFullpath)(lsm_env*, const char *, char *, int *); | | > | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | */ struct lsm_env { int nByte; /* Size of this structure in bytes */ int iVersion; /* Version number of this structure (1) */ /****** file i/o ***********************************************/ void *pVfsCtx; int (*xFullpath)(lsm_env*, const char *, char *, int *); int (*xOpen)(lsm_env*, const char *, int flags, lsm_file **); int (*xRead)(lsm_file *, lsm_i64, void *, int); int (*xWrite)(lsm_file *, lsm_i64, void *, int); int (*xTruncate)(lsm_file *, lsm_i64); int (*xSync)(lsm_file *); int (*xSectorSize)(lsm_file *); #if 0 int (*xRemap)(lsm_file *, lsm_i64, void **, lsm_i64*); #endif int (*xMap)(lsm_file *, lsm_i64 iOff, lsm_i64 nByte, void **, lsm_i64 *); int (*xUnmap)(lsm_file *, void *, lsm_i64); int (*xFileid)(lsm_file *, void *pBuf, int *pnBuf); int (*xClose)(lsm_file *); int (*xUnlink)(lsm_env*, const char *); int (*xLock)(lsm_file*, int, int); int (*xTestLock)(lsm_file*, int, int, int); int (*xShmMap)(lsm_file*, int, int, void **); void (*xShmBarrier)(void); int (*xShmUnmap)(lsm_file*, int); /****** memory allocation ****************************************/ void *pMemCtx; void *(*xMalloc)(lsm_env*, int); /* malloc(3) function */ void *(*xRealloc)(lsm_env*, void *, int); /* realloc(3) function */ |
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | /* ** CAPI: LSM Error Codes */ #define LSM_OK 0 #define LSM_ERROR 1 #define LSM_BUSY 5 #define LSM_NOMEM 7 #define LSM_IOERR 10 #define LSM_CORRUPT 11 #define LSM_FULL 13 #define LSM_CANTOPEN 14 #define LSM_PROTOCOL 15 #define LSM_MISUSE 21 /* ** CAPI: Creating and Destroying Database Connection Handles ** ** Open and close a database connection handle. */ int lsm_new(lsm_env*, lsm_db **ppDb); int lsm_close(lsm_db *pDb); /* ** CAPI: Connecting to a Database */ int lsm_open(lsm_db *pDb, const char *zFilename); /* | > > > > > > | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | /* ** CAPI: LSM Error Codes */ #define LSM_OK 0 #define LSM_ERROR 1 #define LSM_BUSY 5 #define LSM_NOMEM 7 #define LSM_READONLY 8 #define LSM_IOERR 10 #define LSM_CORRUPT 11 #define LSM_FULL 13 #define LSM_CANTOPEN 14 #define LSM_PROTOCOL 15 #define LSM_MISUSE 21 #define LSM_MISMATCH 50 #define LSM_IOERR_NOENT (LSM_IOERR | (1<<8)) /* ** CAPI: Creating and Destroying Database Connection Handles ** ** Open and close a database connection handle. */ int lsm_new(lsm_env*, lsm_db **ppDb); int lsm_close(lsm_db *pDb); /* ** CAPI: Connecting to a Database */ int lsm_open(lsm_db *pDb, const char *zFilename); /* ** CAPI: Obtaining pointers to database environments ** ** Return a pointer to the environment used by the database connection ** passed as the first argument. Assuming the argument is valid, this ** function always returns a valid environment pointer - it cannot fail. */ lsm_env *lsm_get_env(lsm_db *pDb); |
︙ | ︙ | |||
163 164 165 166 167 168 169 | */ int lsm_config(lsm_db *, int, ...); /* ** The following values may be passed as the second argument to lsm_config(). ** ** LSM_CONFIG_AUTOFLUSH: | | > | > > > > | > > > > > > | > | > > > > > > | | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | */ int lsm_config(lsm_db *, int, ...); /* ** The following values may be passed as the second argument to lsm_config(). ** ** LSM_CONFIG_AUTOFLUSH: ** A read/write integer parameter. ** ** This value determines the amount of data allowed to accumulate in a ** live in-memory tree before it is marked as old. After committing a ** transaction, a connection checks if the size of the live in-memory tree, ** including data structure overhead, is greater than the value of this ** option in KB. If it is, and there is not already an old in-memory tree, ** the live in-memory tree is marked as old. ** ** The maximum allowable value is 1048576 (1GB). There is no minimum ** value. If this parameter is set to zero, then an attempt is made to ** mark the live in-memory tree as old after each transaction is committed. ** ** The default value is 1024 (1MB). ** ** LSM_CONFIG_PAGE_SIZE: ** A read/write integer parameter. This parameter may only be set before ** lsm_open() has been called. ** ** LSM_CONFIG_BLOCK_SIZE: ** A read/write integer parameter. ** ** This parameter may only be set before lsm_open() has been called. It ** must be set to a power of two between 64 and 65536, inclusive (block ** sizes between 64KB and 64MB). ** ** If the connection creates a new database, the block size of the new ** database is set to the value of this option in KB. After lsm_open() ** has been called, querying this parameter returns the actual block ** size of the opened database. ** ** The default value is 1024 (1MB blocks). ** ** LSM_CONFIG_SAFETY: ** A read/write integer parameter. Valid values are 0, 1 (the default) ** and 2. This parameter determines how robust the database is in the ** face of a system crash (e.g. a power failure or operating system ** crash). As follows: ** |
︙ | ︙ | |||
199 200 201 202 203 204 205 206 207 208 209 210 211 212 | ** contains all successfully committed transactions. ** ** LSM_CONFIG_AUTOWORK: ** A read/write integer parameter. ** ** LSM_CONFIG_AUTOCHECKPOINT: ** A read/write integer parameter. ** ** LSM_CONFIG_MMAP: ** A read/write integer parameter. True to use mmap() to access the ** database file. False otherwise. ** ** LSM_CONFIG_USE_LOG: ** A read/write boolean parameter. True (the default) to use the log | > > > > > > > > > > > > > > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | ** contains all successfully committed transactions. ** ** LSM_CONFIG_AUTOWORK: ** A read/write integer parameter. ** ** LSM_CONFIG_AUTOCHECKPOINT: ** A read/write integer parameter. ** ** If this option is set to non-zero value N, then a checkpoint is ** automatically attempted after each N KB of data have been written to ** the database file. ** ** The amount of uncheckpointed data already written to the database file ** is a global parameter. After performing database work (writing to the ** database file), the process checks if the total amount of uncheckpointed ** data exceeds the value of this paramter. If so, a checkpoint is performed. ** This means that this option may cause the connection to perform a ** checkpoint even if the current connection has itself written very little ** data into the database file. ** ** The default value is 2048 (checkpoint every 2MB). ** ** LSM_CONFIG_MMAP: ** A read/write integer parameter. True to use mmap() to access the ** database file. False otherwise. ** ** LSM_CONFIG_USE_LOG: ** A read/write boolean parameter. True (the default) to use the log |
︙ | ︙ | |||
240 241 242 243 244 245 246 247 248 249 250 251 252 | ** ** This option may only be used before lsm_open() is called. Invoking it ** after lsm_open() has been called results in an LSM_MISUSE error. ** ** LSM_CONFIG_GET_COMPRESSION: ** Query the compression methods used to compress and decompress database ** content. */ #define LSM_CONFIG_AUTOFLUSH 1 #define LSM_CONFIG_PAGE_SIZE 2 #define LSM_CONFIG_SAFETY 3 #define LSM_CONFIG_BLOCK_SIZE 4 #define LSM_CONFIG_AUTOWORK 5 | > > > > > > > > < > > > > > > > > > > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | ** ** This option may only be used before lsm_open() is called. Invoking it ** after lsm_open() has been called results in an LSM_MISUSE error. ** ** LSM_CONFIG_GET_COMPRESSION: ** Query the compression methods used to compress and decompress database ** content. ** ** LSM_CONFIG_SET_COMPRESSION_FACTORY: ** Configure a factory method to be invoked in case of an LSM_MISMATCH ** error. ** ** LSM_CONFIG_READONLY: ** A read/write boolean parameter. This parameter may only be set before ** lsm_open() is called. */ #define LSM_CONFIG_AUTOFLUSH 1 #define LSM_CONFIG_PAGE_SIZE 2 #define LSM_CONFIG_SAFETY 3 #define LSM_CONFIG_BLOCK_SIZE 4 #define LSM_CONFIG_AUTOWORK 5 #define LSM_CONFIG_MMAP 7 #define LSM_CONFIG_USE_LOG 8 #define LSM_CONFIG_AUTOMERGE 9 #define LSM_CONFIG_MAX_FREELIST 10 #define LSM_CONFIG_MULTIPLE_PROCESSES 11 #define LSM_CONFIG_AUTOCHECKPOINT 12 #define LSM_CONFIG_SET_COMPRESSION 13 #define LSM_CONFIG_GET_COMPRESSION 14 #define LSM_CONFIG_SET_COMPRESSION_FACTORY 15 #define LSM_CONFIG_READONLY 16 #define LSM_SAFETY_OFF 0 #define LSM_SAFETY_NORMAL 1 #define LSM_SAFETY_FULL 2 #define LSM_MMAP_OFF 0 #define LSM_MMAP_FULL 1 #define LSM_MMAP_LIMITED 2 /* ** CAPI: Compression and/or Encryption Hooks */ struct lsm_compress { void *pCtx; unsigned int iId; int (*xBound)(void *, int nSrc); int (*xCompress)(void *, char *, int *, const char *, int); int (*xUncompress)(void *, char *, int *, const char *, int); void (*xFree)(void *pCtx); }; struct lsm_compress_factory { void *pCtx; int (*xFactory)(void *, lsm_db *, unsigned int); void (*xFree)(void *pCtx); }; #define LSM_COMPRESSION_EMPTY 0 #define LSM_COMPRESSION_NONE 1 /* ** CAPI: Allocating and Freeing Memory ** ** Invoke the memory allocation functions that belong to environment ** pEnv. Or the system defaults if no memory allocation functions have ** been registered. |
︙ | ︙ | |||
386 387 388 389 390 391 392 | ** ** The Tcl structure returned is a list containing one element for each ** free block in the database. The element itself consists of two ** integers - the block number and the id of the snapshot that freed it. ** ** LSM_INFO_CHECKPOINT_SIZE: ** The third argument should be of type (int *). The location pointed to | | | > > > > > < > | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | ** ** The Tcl structure returned is a list containing one element for each ** free block in the database. The element itself consists of two ** integers - the block number and the id of the snapshot that freed it. ** ** LSM_INFO_CHECKPOINT_SIZE: ** The third argument should be of type (int *). The location pointed to ** by this argument is populated with the number of KB written to the ** database file since the most recent checkpoint. ** ** LSM_INFO_TREE_SIZE: ** If this value is passed as the second argument to an lsm_info() call, it ** should be followed by two arguments of type (int *) (for a total of four ** arguments). ** ** At any time, there are either one or two tree structures held in shared ** memory that new database clients will access (there may also be additional ** tree structures being used by older clients - this API does not provide ** information on them). One tree structure - the current tree - is used to ** accumulate new data written to the database. The other tree structure - ** the old tree - is a read-only tree holding older data and may be flushed ** to disk at any time. ** ** Assuming no error occurs, the location pointed to by the first of the two ** (int *) arguments is set to the size of the old in-memory tree in KB. ** The second is set to the size of the current, or live in-memory tree. ** ** LSM_INFO_COMPRESSION_ID: ** This value should be followed by a single argument of type ** (unsigned int *). If successful, the location pointed to is populated ** with the database compression id before returning. */ #define LSM_INFO_NWRITE 1 #define LSM_INFO_NREAD 2 #define LSM_INFO_DB_STRUCTURE 3 #define LSM_INFO_LOG_STRUCTURE 4 #define LSM_INFO_ARRAY_STRUCTURE 5 #define LSM_INFO_PAGE_ASCII_DUMP 6 #define LSM_INFO_PAGE_HEX_DUMP 7 #define LSM_INFO_FREELIST 8 #define LSM_INFO_ARRAY_PAGES 9 #define LSM_INFO_CHECKPOINT_SIZE 10 #define LSM_INFO_TREE_SIZE 11 #define LSM_INFO_FREELIST_SIZE 12 #define LSM_INFO_COMPRESSION_ID 13 /* ** CAPI: Opening and Closing Write Transactions ** ** These functions are used to open and close transactions and nested ** sub-transactions. |
︙ | ︙ | |||
481 482 483 484 485 486 487 | ); /* ** CAPI: Explicit Database Work and Checkpointing ** ** This function is called by a thread to work on the database structure. */ | | | | | | 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | ); /* ** CAPI: Explicit Database Work and Checkpointing ** ** This function is called by a thread to work on the database structure. */ int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite); int lsm_flush(lsm_db *pDb); /* ** Attempt to checkpoint the current database snapshot. Return an LSM ** error code if an error occurs or LSM_OK otherwise. ** ** If the current snapshot has already been checkpointed, calling this ** function is a no-op. In this case if pnKB is not NULL, *pnKB is ** set to 0. Or, if the current snapshot is successfully checkpointed ** by this function and pbKB is not NULL, *pnKB is set to the number ** of bytes written to the database file since the previous checkpoint ** (the same measure as returned by the LSM_INFO_CHECKPOINT_SIZE query). */ int lsm_checkpoint(lsm_db *pDb, int *pnKB); /* ** CAPI: Opening and Closing Database Cursors ** ** Open and close a database cursor. */ int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr); |
︙ | ︙ |
Changes to src/lsmInt.h.
︙ | ︙ | |||
41 42 43 44 45 46 47 | /* ** Default values for various data structure parameters. These may be ** overridden by calls to lsm_config(). */ #define LSM_DFLT_PAGE_SIZE (4 * 1024) #define LSM_DFLT_BLOCK_SIZE (1 * 1024 * 1024) #define LSM_DFLT_AUTOFLUSH (1 * 1024 * 1024) | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | /* ** Default values for various data structure parameters. These may be ** overridden by calls to lsm_config(). */ #define LSM_DFLT_PAGE_SIZE (4 * 1024) #define LSM_DFLT_BLOCK_SIZE (1 * 1024 * 1024) #define LSM_DFLT_AUTOFLUSH (1 * 1024 * 1024) #define LSM_DFLT_AUTOCHECKPOINT (i64)(2 * 1024 * 1024) #define LSM_DFLT_AUTOWORK 1 #define LSM_DFLT_LOG_SIZE (128*1024) #define LSM_DFLT_AUTOMERGE 4 #define LSM_DFLT_SAFETY LSM_SAFETY_NORMAL #define LSM_DFLT_MMAP (LSM_IS_64_BIT ? LSM_MMAP_FULL : 0) #define LSM_DFLT_MULTIPLE_PROCESSES 1 #define LSM_DFLT_USE_LOG 1 |
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | #ifdef LSM_DEBUG int lsmErrorBkpt(int); #else # define lsmErrorBkpt(x) (x) #endif #define LSM_IOERR_BKPT lsmErrorBkpt(LSM_IOERR) #define LSM_NOMEM_BKPT lsmErrorBkpt(LSM_NOMEM) #define LSM_CORRUPT_BKPT lsmErrorBkpt(LSM_CORRUPT) #define LSM_MISUSE_BKPT lsmErrorBkpt(LSM_MISUSE) #define unused_parameter(x) (void)(x) #define array_size(x) (sizeof(x)/sizeof(x[0])) /* The size of each shared-memory chunk */ #define LSM_SHM_CHUNK_SIZE (32*1024) /* The number of bytes reserved at the start of each shm chunk for MM. */ #define LSM_SHM_CHUNK_HDR (sizeof(ShmChunk)) /* The number of available read locks. */ #define LSM_LOCK_NREADER 6 | > > > > | > | | > | | | > | > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | #ifdef LSM_DEBUG int lsmErrorBkpt(int); #else # define lsmErrorBkpt(x) (x) #endif #define LSM_PROTOCOL_BKPT lsmErrorBkpt(LSM_PROTOCOL) #define LSM_IOERR_BKPT lsmErrorBkpt(LSM_IOERR) #define LSM_NOMEM_BKPT lsmErrorBkpt(LSM_NOMEM) #define LSM_CORRUPT_BKPT lsmErrorBkpt(LSM_CORRUPT) #define LSM_MISUSE_BKPT lsmErrorBkpt(LSM_MISUSE) #define unused_parameter(x) (void)(x) #define array_size(x) (sizeof(x)/sizeof(x[0])) /* The size of each shared-memory chunk */ #define LSM_SHM_CHUNK_SIZE (32*1024) /* The number of bytes reserved at the start of each shm chunk for MM. */ #define LSM_SHM_CHUNK_HDR (sizeof(ShmChunk)) /* The number of available read locks. */ #define LSM_LOCK_NREADER 6 /* The number of available read-write client locks. */ #define LSM_LOCK_NRWCLIENT 16 /* Lock definitions. */ #define LSM_LOCK_DMS1 1 /* Serialize connect/disconnect ops */ #define LSM_LOCK_DMS2 2 /* Read-write connections */ #define LSM_LOCK_DMS3 3 /* Read-only connections */ #define LSM_LOCK_WRITER 4 #define LSM_LOCK_WORKER 5 #define LSM_LOCK_CHECKPOINTER 6 #define LSM_LOCK_ROTRANS 7 #define LSM_LOCK_READER(i) ((i) + LSM_LOCK_ROTRANS + 1) #define LSM_LOCK_RWCLIENT(i) ((i) + LSM_LOCK_READER(LSM_LOCK_NREADER)) /* ** Hard limit on the number of free-list entries that may be stored in ** a checkpoint (the remainder are stored as a system record in the LSM). ** See also LSM_CONFIG_MAX_FREELIST. */ #define LSM_MAX_FREELIST_ENTRIES 24 |
︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 | */ #define LSM_START_DELETE 0x01 /* Start of open-ended delete range */ #define LSM_END_DELETE 0x02 /* End of open-ended delete range */ #define LSM_POINT_DELETE 0x04 /* Delete this key */ #define LSM_INSERT 0x08 /* Insert this key and value */ #define LSM_SEPARATOR 0x10 /* True if entry is separator key only */ #define LSM_SYSTEMKEY 0x20 /* True if entry is a system key (FREELIST) */ /* ** A string that can grow by appending. */ struct LsmString { lsm_env *pEnv; /* Run-time environment */ int n; /* Size of string. -1 indicates error */ | > > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | */ #define LSM_START_DELETE 0x01 /* Start of open-ended delete range */ #define LSM_END_DELETE 0x02 /* End of open-ended delete range */ #define LSM_POINT_DELETE 0x04 /* Delete this key */ #define LSM_INSERT 0x08 /* Insert this key and value */ #define LSM_SEPARATOR 0x10 /* True if entry is separator key only */ #define LSM_SYSTEMKEY 0x20 /* True if entry is a system key (FREELIST) */ #define LSM_CONTIGUOUS 0x40 /* Used in lsm_tree.c */ /* ** A string that can grow by appending. */ struct LsmString { lsm_env *pEnv; /* Run-time environment */ int n; /* Size of string. -1 indicates error */ |
︙ | ︙ | |||
280 281 282 283 284 285 286 | /* ** Database handle structure. ** ** mLock: ** A bitmask representing the locks currently held by the connection. ** An LSM database supports N distinct locks, where N is some number less | | | | > > > > > > > > > > > > < | > > > > > > > > > > | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | /* ** Database handle structure. ** ** mLock: ** A bitmask representing the locks currently held by the connection. ** An LSM database supports N distinct locks, where N is some number less ** than or equal to 32. Locks are numbered starting from 1 (see the ** definitions for LSM_LOCK_WRITER and co.). ** ** The least significant 32-bits in mLock represent EXCLUSIVE locks. The ** most significant are SHARED locks. So, if a connection holds a SHARED ** lock on lock region iLock, then the following is true: ** ** (mLock & ((iLock+32-1) << 1)) ** ** Or for an EXCLUSIVE lock: ** ** (mLock & ((iLock-1) << 1)) ** ** pCsr: ** Points to the head of a linked list that contains all currently open ** cursors. Once this list becomes empty, the user has no outstanding ** cursors and the database handle can be successfully closed. ** ** pCsrCache: ** This list contains cursor objects that have been closed using ** lsm_csr_close(). Each time a cursor is closed, it is shifted from ** the pCsr list to this list. When a new cursor is opened, this list ** is inspected to see if there exists a cursor object that can be ** reused. This is an optimization only. */ struct lsm_db { /* Database handle configuration */ lsm_env *pEnv; /* runtime environment */ int (*xCmp)(void *, int, void *, int); /* Compare function */ /* Values configured by calls to lsm_config */ int eSafety; /* LSM_SAFETY_OFF, NORMAL or FULL */ int bAutowork; /* Configured by LSM_CONFIG_AUTOWORK */ int nTreeLimit; /* Configured by LSM_CONFIG_AUTOFLUSH */ int nMerge; /* Configured by LSM_CONFIG_AUTOMERGE */ int bUseLog; /* Configured by LSM_CONFIG_USE_LOG */ int nDfltPgsz; /* Configured by LSM_CONFIG_PAGE_SIZE */ int nDfltBlksz; /* Configured by LSM_CONFIG_BLOCK_SIZE */ int nMaxFreelist; /* Configured by LSM_CONFIG_MAX_FREELIST */ int eMmap; /* Configured by LSM_CONFIG_MMAP */ i64 nAutockpt; /* Configured by LSM_CONFIG_AUTOCHECKPOINT */ int bMultiProc; /* Configured by L_C_MULTIPLE_PROCESSES */ int bReadonly; /* Configured by LSM_CONFIG_READONLY */ lsm_compress compress; /* Compression callbacks */ lsm_compress_factory factory; /* Compression callback factory */ /* Sub-system handles */ FileSystem *pFS; /* On-disk portion of database */ Database *pDatabase; /* Database shared data */ int iRwclient; /* Read-write client lock held (-1 == none) */ /* Client transaction context */ Snapshot *pClient; /* Client snapshot */ int iReader; /* Read lock held (-1 == unlocked) */ int bRoTrans; /* True if a read-only db trans is open */ MultiCursor *pCsr; /* List of all open cursors */ LogWriter *pLogWriter; /* Context for writing to the log file */ int nTransOpen; /* Number of opened write transactions */ int nTransAlloc; /* Allocated size of aTrans[] array */ TransMark *aTrans; /* Array of marks for transaction rollback */ IntArray rollback; /* List of tree-nodes to roll back */ int bDiscardOld; /* True if lsmTreeDiscardOld() was called */ MultiCursor *pCsrCache; /* List of all closed cursors */ /* Worker context */ Snapshot *pWorker; /* Worker snapshot (or NULL) */ Freelist *pFreelist; /* See sortedNewToplevel() */ int bUseFreelist; /* True to use pFreelist */ int bIncrMerge; /* True if currently doing a merge */ int bInFactory; /* True if within factory.xFactory() */ /* Debugging message callback */ void (*xLog)(void *, int, const char *); void *pLogCtx; /* Work done notification callback */ void (*xWork)(lsm_db *, void *); void *pWorkCtx; u64 mLock; /* Mask of current locks. See lsmShmLock(). */ lsm_db *pNext; /* Next connection to same database */ int nShm; /* Size of apShm[] array */ void **apShm; /* Shared memory chunks */ ShmHeader *pShmhdr; /* Live shared-memory header */ TreeHeader treehdr; /* Local copy of tree-header */ u32 aSnapshot[LSM_META_PAGE_SIZE / sizeof(u32)]; |
︙ | ︙ | |||
523 524 525 526 527 528 529 530 531 532 533 534 535 536 | /* ** A snapshot of a database. A snapshot contains all the information required ** to read or write a database file on disk. See the description of struct ** Database below for futher details. */ struct Snapshot { Database *pDatabase; /* Database this snapshot belongs to */ Level *pLevel; /* Pointer to level 0 of snapshot (or NULL) */ i64 iId; /* Snapshot id */ i64 iLogOff; /* Log file offset */ Redirect redirect; /* Block redirection array */ /* Used by worker snapshots only */ int nBlock; /* Number of blocks in database file */ | > | 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | /* ** A snapshot of a database. A snapshot contains all the information required ** to read or write a database file on disk. See the description of struct ** Database below for futher details. */ struct Snapshot { Database *pDatabase; /* Database this snapshot belongs to */ u32 iCmpId; /* Id of compression scheme */ Level *pLevel; /* Pointer to level 0 of snapshot (or NULL) */ i64 iId; /* Snapshot id */ i64 iLogOff; /* Log file offset */ Redirect redirect; /* Block redirection array */ /* Used by worker snapshots only */ int nBlock; /* Number of blocks in database file */ |
︙ | ︙ | |||
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | int lsmCheckpointSaveWorker(lsm_db *pDb, int); int lsmDatabaseFull(lsm_db *pDb); int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite); int lsmCheckpointSize(lsm_db *db, int *pnByte); /* ** Functions from file "lsm_tree.c". */ int lsmTreeNew(lsm_env *, int (*)(void *, int, void *, int), Tree **ppTree); void lsmTreeRelease(lsm_env *, Tree *); int lsmTreeInit(lsm_db *); int lsmTreeRepair(lsm_db *); void lsmTreeMakeOld(lsm_db *pDb); void lsmTreeDiscardOld(lsm_db *pDb); int lsmTreeHasOld(lsm_db *pDb); int lsmTreeSize(lsm_db *); int lsmTreeEndTransaction(lsm_db *pDb, int bCommit); int lsmTreeLoadHeader(lsm_db *pDb, int *); int lsmTreeLoadHeaderOk(lsm_db *, int); int lsmTreeInsert(lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal); void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark); void lsmTreeMark(lsm_db *pDb, TreeMark *pMark); int lsmTreeCursorNew(lsm_db *pDb, int, TreeCursor **); void lsmTreeCursorDestroy(TreeCursor *); int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes); | > > > | 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 | int lsmCheckpointSaveWorker(lsm_db *pDb, int); int lsmDatabaseFull(lsm_db *pDb); int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite); int lsmCheckpointSize(lsm_db *db, int *pnByte); int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId); /* ** Functions from file "lsm_tree.c". */ int lsmTreeNew(lsm_env *, int (*)(void *, int, void *, int), Tree **ppTree); void lsmTreeRelease(lsm_env *, Tree *); int lsmTreeInit(lsm_db *); int lsmTreeRepair(lsm_db *); void lsmTreeMakeOld(lsm_db *pDb); void lsmTreeDiscardOld(lsm_db *pDb); int lsmTreeHasOld(lsm_db *pDb); int lsmTreeSize(lsm_db *); int lsmTreeEndTransaction(lsm_db *pDb, int bCommit); int lsmTreeLoadHeader(lsm_db *pDb, int *); int lsmTreeLoadHeaderOk(lsm_db *, int); int lsmTreeInsert(lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal); int lsmTreeDelete(lsm_db *db, void *pKey1, int nKey1, void *pKey2, int nKey2); void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark); void lsmTreeMark(lsm_db *pDb, TreeMark *pMark); int lsmTreeCursorNew(lsm_db *pDb, int, TreeCursor **); void lsmTreeCursorDestroy(TreeCursor *); int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes); |
︙ | ︙ | |||
638 639 640 641 642 643 644 | int lsmMutexHeld(lsm_env *, lsm_mutex *); int lsmMutexNotHeld(lsm_env *, lsm_mutex *); #endif /************************************************************************** ** Start of functions from "lsm_file.c". */ | | > > > > | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 | int lsmMutexHeld(lsm_env *, lsm_mutex *); int lsmMutexNotHeld(lsm_env *, lsm_mutex *); #endif /************************************************************************** ** Start of functions from "lsm_file.c". */ int lsmFsOpen(lsm_db *, const char *, int); int lsmFsOpenLog(lsm_db *, int *); void lsmFsCloseLog(lsm_db *); void lsmFsClose(FileSystem *); int lsmFsConfigure(lsm_db *db); int lsmFsBlockSize(FileSystem *); void lsmFsSetBlockSize(FileSystem *, int); int lsmFsPageSize(FileSystem *); void lsmFsSetPageSize(FileSystem *, int); |
︙ | ︙ | |||
710 711 712 713 714 715 716 | void lsmFsFlushWaiting(FileSystem *, int *); /* Used by lsm_info(ARRAY_STRUCTURE) and lsm_config(MMAP) */ int lsmInfoArrayStructure(lsm_db *pDb, int bBlock, Pgno iFirst, char **pzOut); int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut); int lsmConfigMmap(lsm_db *pDb, int *piParam); | | > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 | void lsmFsFlushWaiting(FileSystem *, int *); /* Used by lsm_info(ARRAY_STRUCTURE) and lsm_config(MMAP) */ int lsmInfoArrayStructure(lsm_db *pDb, int bBlock, Pgno iFirst, char **pzOut); int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut); int lsmConfigMmap(lsm_db *pDb, int *piParam); int lsmEnvOpen(lsm_env *, const char *, int, lsm_file **); int lsmEnvClose(lsm_env *pEnv, lsm_file *pFile); int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock); int lsmEnvTestLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int nLock, int); int lsmEnvShmMap(lsm_env *, lsm_file *, int, int, void **); void lsmEnvShmBarrier(lsm_env *); void lsmEnvShmUnmap(lsm_env *, lsm_file *, int); void lsmEnvSleep(lsm_env *, int); |
︙ | ︙ | |||
756 757 758 759 760 761 762 | int lsmSortedLoadFreelist(lsm_db *pDb, void **, int *); void *lsmSortedSplitKey(Level *pLevel, int *pnByte); void lsmSortedSaveTreeCursors(lsm_db *); int lsmMCursorNew(lsm_db *, MultiCursor **); | | > | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | int lsmSortedLoadFreelist(lsm_db *pDb, void **, int *); void *lsmSortedSplitKey(Level *pLevel, int *pnByte); void lsmSortedSaveTreeCursors(lsm_db *); int lsmMCursorNew(lsm_db *, MultiCursor **); void lsmMCursorClose(MultiCursor *, int); int lsmMCursorSeek(MultiCursor *, int, void *, int , int); int lsmMCursorFirst(MultiCursor *); int lsmMCursorPrev(MultiCursor *); int lsmMCursorLast(MultiCursor *); int lsmMCursorValid(MultiCursor *); int lsmMCursorNext(MultiCursor *); int lsmMCursorKey(MultiCursor *, void **, int *); int lsmMCursorValue(MultiCursor *, void **, int *); int lsmMCursorType(MultiCursor *, int *); lsm_db *lsmMCursorDb(MultiCursor *); void lsmMCursorFreeCache(lsm_db *); int lsmSaveCursors(lsm_db *pDb); int lsmRestoreCursors(lsm_db *pDb); void lsmSortedDumpStructure(lsm_db *pDb, Snapshot *, int, int, const char *); void lsmFsDumpBlocklists(lsm_db *); |
︙ | ︙ | |||
823 824 825 826 827 828 829 830 831 832 833 834 835 836 | int lsmDbDatabaseConnect(lsm_db*, const char *); void lsmDbDatabaseRelease(lsm_db *); int lsmBeginReadTrans(lsm_db *); int lsmBeginWriteTrans(lsm_db *); int lsmBeginFlush(lsm_db *); int lsmBeginWork(lsm_db *); void lsmFinishWork(lsm_db *, int, int *); int lsmFinishRecovery(lsm_db *); void lsmFinishReadTrans(lsm_db *); int lsmFinishWriteTrans(lsm_db *, int); int lsmFinishFlush(lsm_db *, int); | > > | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 | int lsmDbDatabaseConnect(lsm_db*, const char *); void lsmDbDatabaseRelease(lsm_db *); int lsmBeginReadTrans(lsm_db *); int lsmBeginWriteTrans(lsm_db *); int lsmBeginFlush(lsm_db *); int lsmDetectRoTrans(lsm_db *db, int *); int lsmBeginWork(lsm_db *); void lsmFinishWork(lsm_db *, int, int *); int lsmFinishRecovery(lsm_db *); void lsmFinishReadTrans(lsm_db *); int lsmFinishWriteTrans(lsm_db *, int); int lsmFinishFlush(lsm_db *, int); |
︙ | ︙ | |||
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 | /* Candidate values for the 3rd argument to lsmShmLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 int lsmShmCacheChunks(lsm_db *db, int nChunk); int lsmShmLock(lsm_db *db, int iLock, int eOp, int bBlock); void lsmShmBarrier(lsm_db *db); #ifdef LSM_DEBUG void lsmShmHasLock(lsm_db *db, int iLock, int eOp); #else # define lsmShmHasLock(x,y,z) #endif int lsmReadlock(lsm_db *, i64 iLsm, u32 iShmMin, u32 iShmMax); | > < > > | 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | /* Candidate values for the 3rd argument to lsmShmLock() */ #define LSM_LOCK_UNLOCK 0 #define LSM_LOCK_SHARED 1 #define LSM_LOCK_EXCL 2 int lsmShmCacheChunks(lsm_db *db, int nChunk); int lsmShmLock(lsm_db *db, int iLock, int eOp, int bBlock); int lsmShmTestLock(lsm_db *db, int iLock, int nLock, int eOp); void lsmShmBarrier(lsm_db *db); #ifdef LSM_DEBUG void lsmShmHasLock(lsm_db *db, int iLock, int eOp); #else # define lsmShmHasLock(x,y,z) #endif int lsmReadlock(lsm_db *, i64 iLsm, u32 iShmMin, u32 iShmMax); int lsmLsmInUse(lsm_db *db, i64 iLsmId, int *pbInUse); int lsmTreeInUse(lsm_db *db, u32 iLsmId, int *pbInUse); int lsmFreelistAppend(lsm_env *pEnv, Freelist *p, int iBlk, i64 iId); int lsmDbMultiProc(lsm_db *); void lsmDbDeferredClose(lsm_db *, lsm_file *, LsmFile *); LsmFile *lsmDbRecycleFd(lsm_db *); int lsmWalkFreelist(lsm_db *, int, int (*)(void *, int, i64), void *); int lsmCheckCompressionId(lsm_db *, u32); /************************************************************************** ** functions in lsm_str.c */ void lsmStringInit(LsmString*, lsm_env *pEnv); int lsmStringExtend(LsmString*, int); |
︙ | ︙ |
Changes to src/lsm_ckpt.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | ** ** Checkpoint header (see the CKPT_HDR_XXX #defines): ** ** 1. The checkpoint id MSW. ** 2. The checkpoint id LSW. ** 3. The number of integer values in the entire checkpoint, including ** the two checksum values. | > | | | | | > > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | ** ** Checkpoint header (see the CKPT_HDR_XXX #defines): ** ** 1. The checkpoint id MSW. ** 2. The checkpoint id LSW. ** 3. The number of integer values in the entire checkpoint, including ** the two checksum values. ** 4. The compression scheme id. ** 5. The total number of blocks in the database. ** 6. The block size. ** 7. The number of levels. ** 8. The nominal database page size. ** 9. The number of pages (in total) written to the database file. ** ** Log pointer: ** ** 1. The log offset MSW. ** 2. The log offset LSW. ** 3. Log checksum 0. ** 4. Log checksum 1. ** ** Note that the "log offset" is not the literal byte offset. Instead, ** it is the byte offset multiplied by 2, with least significant bit ** toggled each time the log pointer value is changed. This is to make ** sure that this field changes each time the log pointer is updated, ** even if the log file itself is disabled. See lsmTreeMakeOld(). ** ** See ckptExportLog() and ckptImportLog(). ** ** Append points: ** ** 8 integers (4 * 64-bit page numbers). See ckptExportAppendlist(). ** |
︙ | ︙ | |||
170 171 172 173 174 175 176 | + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ ) static const int one = 1; #define LSM_LITTLE_ENDIAN (*(u8 *)(&one)) /* Sizes, in integers, of various parts of the checkpoint. */ | | > | | | | | | | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ ) static const int one = 1; #define LSM_LITTLE_ENDIAN (*(u8 *)(&one)) /* Sizes, in integers, of various parts of the checkpoint. */ #define CKPT_HDR_SIZE 9 #define CKPT_LOGPTR_SIZE 4 #define CKPT_APPENDLIST_SIZE (LSM_APPLIST_SZ * 2) /* A #define to describe each integer in the checkpoint header. */ #define CKPT_HDR_ID_MSW 0 #define CKPT_HDR_ID_LSW 1 #define CKPT_HDR_NCKPT 2 #define CKPT_HDR_CMPID 3 #define CKPT_HDR_NBLOCK 4 #define CKPT_HDR_BLKSZ 5 #define CKPT_HDR_NLEVEL 6 #define CKPT_HDR_PGSZ 7 #define CKPT_HDR_NWRITE 8 #define CKPT_HDR_LO_MSW 9 #define CKPT_HDR_LO_LSW 10 #define CKPT_HDR_LO_CKSUM1 11 #define CKPT_HDR_LO_CKSUM2 12 typedef struct CkptBuffer CkptBuffer; /* ** Dynamic buffer used to accumulate data for a checkpoint. */ struct CkptBuffer { |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | ckptSetValue(&ckpt, iOut++, (p->iId >> 32) & 0xFFFFFFFF, &rc); ckptSetValue(&ckpt, iOut++, p->iId & 0xFFFFFFFF, &rc); } } /* Write the checkpoint header */ assert( iId>=0 ); ckptSetValue(&ckpt, CKPT_HDR_ID_MSW, (u32)(iId>>32), &rc); ckptSetValue(&ckpt, CKPT_HDR_ID_LSW, (u32)(iId&0xFFFFFFFF), &rc); ckptSetValue(&ckpt, CKPT_HDR_NCKPT, iOut+2, &rc); ckptSetValue(&ckpt, CKPT_HDR_NBLOCK, pSnap->nBlock, &rc); ckptSetValue(&ckpt, CKPT_HDR_BLKSZ, lsmFsBlockSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NLEVEL, nLevel, &rc); ckptSetValue(&ckpt, CKPT_HDR_PGSZ, lsmFsPageSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NWRITE, pSnap->nWrite, &rc); if( bCksum ){ | > > > > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | ckptSetValue(&ckpt, iOut++, (p->iId >> 32) & 0xFFFFFFFF, &rc); ckptSetValue(&ckpt, iOut++, p->iId & 0xFFFFFFFF, &rc); } } /* Write the checkpoint header */ assert( iId>=0 ); assert( pSnap->iCmpId==pDb->compress.iId || pSnap->iCmpId==LSM_COMPRESSION_EMPTY ); ckptSetValue(&ckpt, CKPT_HDR_ID_MSW, (u32)(iId>>32), &rc); ckptSetValue(&ckpt, CKPT_HDR_ID_LSW, (u32)(iId&0xFFFFFFFF), &rc); ckptSetValue(&ckpt, CKPT_HDR_NCKPT, iOut+2, &rc); ckptSetValue(&ckpt, CKPT_HDR_CMPID, pDb->compress.iId, &rc); ckptSetValue(&ckpt, CKPT_HDR_NBLOCK, pSnap->nBlock, &rc); ckptSetValue(&ckpt, CKPT_HDR_BLKSZ, lsmFsBlockSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NLEVEL, nLevel, &rc); ckptSetValue(&ckpt, CKPT_HDR_PGSZ, lsmFsPageSize(pFS), &rc); ckptSetValue(&ckpt, CKPT_HDR_NWRITE, pSnap->nWrite, &rc); if( bCksum ){ |
︙ | ︙ | |||
760 761 762 763 764 765 766 767 768 769 770 771 772 773 | ** is called when no valid snapshot can be found in the database header. */ static void ckptLoadEmpty(lsm_db *pDb){ u32 aCkpt[] = { 0, /* CKPT_HDR_ID_MSW */ 10, /* CKPT_HDR_ID_LSW */ 0, /* CKPT_HDR_NCKPT */ 0, /* CKPT_HDR_NBLOCK */ 0, /* CKPT_HDR_BLKSZ */ 0, /* CKPT_HDR_NLEVEL */ 0, /* CKPT_HDR_PGSZ */ 0, /* CKPT_HDR_OVFL */ 0, /* CKPT_HDR_NWRITE */ 0, 0, 1234, 5678, /* The log pointer and initial checksum */ | > | 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 | ** is called when no valid snapshot can be found in the database header. */ static void ckptLoadEmpty(lsm_db *pDb){ u32 aCkpt[] = { 0, /* CKPT_HDR_ID_MSW */ 10, /* CKPT_HDR_ID_LSW */ 0, /* CKPT_HDR_NCKPT */ LSM_COMPRESSION_EMPTY, /* CKPT_HDR_CMPID */ 0, /* CKPT_HDR_NBLOCK */ 0, /* CKPT_HDR_BLKSZ */ 0, /* CKPT_HDR_NLEVEL */ 0, /* CKPT_HDR_PGSZ */ 0, /* CKPT_HDR_OVFL */ 0, /* CKPT_HDR_NWRITE */ 0, 0, 1234, 5678, /* The log pointer and initial checksum */ |
︙ | ︙ | |||
872 873 874 875 876 877 878 | if( piRead ) *piRead = 2; return LSM_OK; } } lsmShmBarrier(pDb); } | | > > > > > > > > > > > > | 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | if( piRead ) *piRead = 2; return LSM_OK; } } lsmShmBarrier(pDb); } return LSM_PROTOCOL_BKPT; } int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId){ int rc; assert( db->pClient==0 && db->pWorker==0 ); rc = lsmCheckpointLoad(db, 0); if( rc==LSM_OK ){ *piCmpId = db->aSnapshot[CKPT_HDR_CMPID]; } return rc; } int lsmCheckpointLoadOk(lsm_db *pDb, int iSnap){ u32 *aShm; assert( iSnap==1 || iSnap==2 ); aShm = (iSnap==1) ? pDb->pShmhdr->aSnap1 : pDb->pShmhdr->aSnap2; return (lsmCheckpointId(pDb->aSnapshot, 0)==lsmCheckpointId(aShm, 0) ); |
︙ | ︙ | |||
899 900 901 902 903 904 905 | ShmHeader *pShm = pDb->pShmhdr; int nInt1; int nInt2; /* Must be holding the WORKER lock to do this. Or DMS2. */ assert( lsmShmAssertLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_EXCL) | | | > > > > | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | ShmHeader *pShm = pDb->pShmhdr; int nInt1; int nInt2; /* Must be holding the WORKER lock to do this. Or DMS2. */ assert( lsmShmAssertLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_EXCL) || lsmShmAssertLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL) ); /* Check that the two snapshots match. If not, repair them. */ nInt1 = pShm->aSnap1[CKPT_HDR_NCKPT]; nInt2 = pShm->aSnap2[CKPT_HDR_NCKPT]; if( nInt1!=nInt2 || memcmp(pShm->aSnap1, pShm->aSnap2, nInt2*sizeof(u32)) ){ if( ckptChecksumOk(pShm->aSnap1) ){ memcpy(pShm->aSnap2, pShm->aSnap1, sizeof(u32)*nInt1); }else if( ckptChecksumOk(pShm->aSnap2) ){ memcpy(pShm->aSnap1, pShm->aSnap2, sizeof(u32)*nInt2); }else{ return LSM_PROTOCOL_BKPT; } } rc = lsmCheckpointDeserialize(pDb, 1, pShm->aSnap1, &pDb->pWorker); if( pDb->pWorker ) pDb->pWorker->pDatabase = pDb->pDatabase; if( rc==LSM_OK ){ rc = lsmCheckCompressionId(pDb, pDb->pWorker->iCmpId); } #if 0 assert( rc!=LSM_OK || lsmFsIntegrityCheck(pDb) ); #endif return rc; } |
︙ | ︙ | |||
946 947 948 949 950 951 952 953 954 955 956 957 958 959 | int iIn = CKPT_HDR_SIZE + CKPT_APPENDLIST_SIZE + CKPT_LOGPTR_SIZE; pNew->iId = lsmCheckpointId(aCkpt, 0); pNew->nBlock = aCkpt[CKPT_HDR_NBLOCK]; pNew->nWrite = aCkpt[CKPT_HDR_NWRITE]; rc = ckptLoadLevels(pDb, aCkpt, &iIn, nLevel, &pNew->pLevel); pNew->iLogOff = lsmCheckpointLogOffset(aCkpt); /* Make a copy of the append-list */ for(i=0; i<LSM_APPLIST_SZ; i++){ u32 *a = &aCkpt[CKPT_HDR_SIZE + CKPT_LOGPTR_SIZE + i*2]; pNew->aiAppend[i] = ckptRead64(a); } | > | 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 | int iIn = CKPT_HDR_SIZE + CKPT_APPENDLIST_SIZE + CKPT_LOGPTR_SIZE; pNew->iId = lsmCheckpointId(aCkpt, 0); pNew->nBlock = aCkpt[CKPT_HDR_NBLOCK]; pNew->nWrite = aCkpt[CKPT_HDR_NWRITE]; rc = ckptLoadLevels(pDb, aCkpt, &iIn, nLevel, &pNew->pLevel); pNew->iLogOff = lsmCheckpointLogOffset(aCkpt); pNew->iCmpId = aCkpt[CKPT_HDR_CMPID]; /* Make a copy of the append-list */ for(i=0; i<LSM_APPLIST_SZ; i++){ u32 *a = &aCkpt[CKPT_HDR_SIZE + CKPT_LOGPTR_SIZE + i*2]; pNew->aiAppend[i] = ckptRead64(a); } |
︙ | ︙ | |||
1095 1096 1097 1098 1099 1100 1101 | if( nCkpt<(LSM_META_PAGE_SIZE/sizeof(u32)) ){ u32 *aCopy = lsmMallocRc(pDb->pEnv, sizeof(u32) * nCkpt, &rc); if( aCopy ){ memcpy(aCopy, aData, nCkpt*sizeof(u32)); ckptChangeEndianness(aCopy, nCkpt); if( ckptChecksumOk(aCopy) ){ if( piId ) *piId = lsmCheckpointId(aCopy, 0); | | | 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | if( nCkpt<(LSM_META_PAGE_SIZE/sizeof(u32)) ){ u32 *aCopy = lsmMallocRc(pDb->pEnv, sizeof(u32) * nCkpt, &rc); if( aCopy ){ memcpy(aCopy, aData, nCkpt*sizeof(u32)); ckptChangeEndianness(aCopy, nCkpt); if( ckptChecksumOk(aCopy) ){ if( piId ) *piId = lsmCheckpointId(aCopy, 0); if( piLog ) *piLog = (lsmCheckpointLogOffset(aCopy) >> 1); if( pnWrite ) *pnWrite = aCopy[CKPT_HDR_NWRITE]; } lsmFree(pDb->pEnv, aCopy); } } lsmFsMetaPageRelease(pPg); } |
︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 | int lsmCheckpointBlksz(u32 *aCkpt){ return (int)aCkpt[CKPT_HDR_BLKSZ]; } void lsmCheckpointLogoffset( u32 *aCkpt, DbLog *pLog ){ | < < | > | 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 | int lsmCheckpointBlksz(u32 *aCkpt){ return (int)aCkpt[CKPT_HDR_BLKSZ]; } void lsmCheckpointLogoffset( u32 *aCkpt, DbLog *pLog ){ pLog->aRegion[2].iStart = (lsmCheckpointLogOffset(aCkpt) >> 1); pLog->cksum0 = aCkpt[CKPT_HDR_LO_CKSUM1]; pLog->cksum1 = aCkpt[CKPT_HDR_LO_CKSUM2]; pLog->iSnapshotId = lsmCheckpointId(aCkpt, 0); } void lsmCheckpointZeroLogoffset(lsm_db *pDb){ u32 nCkpt; |
︙ | ︙ | |||
1182 1183 1184 1185 1186 1187 1188 | &pDb->aSnapshot[nCkpt-2], &pDb->aSnapshot[nCkpt-1] ); memcpy(pDb->pShmhdr->aSnap1, pDb->aSnapshot, nCkpt*sizeof(u32)); memcpy(pDb->pShmhdr->aSnap2, pDb->aSnapshot, nCkpt*sizeof(u32)); } | > > > > | > > > | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 | &pDb->aSnapshot[nCkpt-2], &pDb->aSnapshot[nCkpt-1] ); memcpy(pDb->pShmhdr->aSnap1, pDb->aSnapshot, nCkpt*sizeof(u32)); memcpy(pDb->pShmhdr->aSnap2, pDb->aSnapshot, nCkpt*sizeof(u32)); } /* ** Set the output variable to the number of KB of data written into the ** database file since the most recent checkpoint. */ int lsmCheckpointSize(lsm_db *db, int *pnKB){ ShmHeader *pShm = db->pShmhdr; int rc = LSM_OK; u32 nSynced; /* Set nSynced to the number of pages that had been written when the ** database was last checkpointed. */ rc = lsmCheckpointSynced(db, 0, 0, &nSynced); if( rc==LSM_OK ){ u32 nPgsz = db->pShmhdr->aSnap1[CKPT_HDR_PGSZ]; u32 nWrite = db->pShmhdr->aSnap1[CKPT_HDR_NWRITE]; *pnKB = (int)(( ((i64)(nWrite - nSynced) * nPgsz) + 1023) / 1024); } return rc; } |
Changes to src/lsm_file.c.
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | ** are carrying pointers into the database file mapping (pMap/nMap). If the ** file has to be unmapped and then remapped (required to grow the mapping ** as the file grows), the Page.aData pointers are updated by iterating ** through the contents of this list. ** ** In non-mmap() mode, this list is an LRU list of cached pages with ** nRef==0. */ struct FileSystem { lsm_db *pDb; /* Database handle that owns this object */ lsm_env *pEnv; /* Environment pointer */ char *zDb; /* Database file name */ char *zLog; /* Database file name */ int nMetasize; /* Size of meta pages in bytes */ | > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | ** are carrying pointers into the database file mapping (pMap/nMap). If the ** file has to be unmapped and then remapped (required to grow the mapping ** as the file grows), the Page.aData pointers are updated by iterating ** through the contents of this list. ** ** In non-mmap() mode, this list is an LRU list of cached pages with ** nRef==0. ** ** apHash, nHash: */ struct FileSystem { lsm_db *pDb; /* Database handle that owns this object */ lsm_env *pEnv; /* Environment pointer */ char *zDb; /* Database file name */ char *zLog; /* Database file name */ int nMetasize; /* Size of meta pages in bytes */ |
︙ | ︙ | |||
349 350 351 352 353 354 355 | ** lsmEnvSync() ** lsmEnvSectorSize() ** lsmEnvClose() ** lsmEnvTruncate() ** lsmEnvUnlink() ** lsmEnvRemap() */ | | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | ** lsmEnvSync() ** lsmEnvSectorSize() ** lsmEnvClose() ** lsmEnvTruncate() ** lsmEnvUnlink() ** lsmEnvRemap() */ int lsmEnvOpen(lsm_env *pEnv, const char *zFile, int flags, lsm_file **ppNew){ return pEnv->xOpen(pEnv, zFile, flags, ppNew); } static int lsmEnvRead( lsm_env *pEnv, lsm_file *pFile, lsm_i64 iOff, void *pRead, int nRead |
︙ | ︙ | |||
419 420 421 422 423 424 425 426 427 428 429 430 431 432 | return pEnv->xMap(pFile, iOff, szMin, ppMap, pszMap); } int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock){ if( pFile==0 ) return LSM_OK; return pEnv->xLock(pFile, iLock, eLock); } int lsmEnvShmMap( lsm_env *pEnv, lsm_file *pFile, int iChunk, int sz, void **ppOut | > > > > > > > > > > | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | return pEnv->xMap(pFile, iOff, szMin, ppMap, pszMap); } int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock){ if( pFile==0 ) return LSM_OK; return pEnv->xLock(pFile, iLock, eLock); } int lsmEnvTestLock( lsm_env *pEnv, lsm_file *pFile, int iLock, int nLock, int eLock ){ return pEnv->xTestLock(pFile, iLock, nLock, eLock); } int lsmEnvShmMap( lsm_env *pEnv, lsm_file *pFile, int iChunk, int sz, void **ppOut |
︙ | ︙ | |||
484 485 486 487 488 489 490 | */ int lsmFsTruncateLog(FileSystem *pFS, i64 nByte){ if( pFS->fdLog==0 ) return LSM_OK; return lsmEnvTruncate(pFS->pEnv, pFS->fdLog, nByte); } /* | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | */ int lsmFsTruncateLog(FileSystem *pFS, i64 nByte){ if( pFS->fdLog==0 ) return LSM_OK; return lsmEnvTruncate(pFS->pEnv, pFS->fdLog, nByte); } /* ** Truncate the db file to nByte bytes in size. */ int lsmFsTruncateDb(FileSystem *pFS, i64 nByte){ if( pFS->fdDb==0 ) return LSM_OK; return lsmEnvTruncate(pFS->pEnv, pFS->fdDb, nByte); } /* |
︙ | ︙ | |||
525 526 527 528 529 530 531 532 533 534 535 536 | /* ** This is a helper function for lsmFsOpen(). It opens a single file on ** disk (either the database or log file). */ static lsm_file *fsOpenFile( FileSystem *pFS, /* File system object */ int bLog, /* True for log, false for db */ int *pRc /* IN/OUT: Error code */ ){ lsm_file *pFile = 0; if( *pRc==LSM_OK ){ | > > > > | | 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | /* ** This is a helper function for lsmFsOpen(). It opens a single file on ** disk (either the database or log file). */ static lsm_file *fsOpenFile( FileSystem *pFS, /* File system object */ int bReadonly, /* True to open this file read-only */ int bLog, /* True for log, false for db */ int *pRc /* IN/OUT: Error code */ ){ lsm_file *pFile = 0; if( *pRc==LSM_OK ){ int flags = (bReadonly ? LSM_OPEN_READONLY : 0); const char *zPath = (bLog ? pFS->zLog : pFS->zDb); *pRc = lsmEnvOpen(pFS->pEnv, zPath, flags, &pFile); } return pFile; } static void fsGrowMapping( FileSystem *pFS, |
︙ | ︙ | |||
789 790 791 792 793 794 795 | ** ** The log file must be opened before any of the following may be called: ** ** lsmFsWriteLog ** lsmFsSyncLog ** lsmFsReadLog */ | | > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | < < | 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 | ** ** The log file must be opened before any of the following may be called: ** ** lsmFsWriteLog ** lsmFsSyncLog ** lsmFsReadLog */ int lsmFsOpenLog(lsm_db *db, int *pbOpen){ int rc = LSM_OK; FileSystem *pFS = db->pFS; if( 0==pFS->fdLog ){ pFS->fdLog = fsOpenFile(pFS, db->bReadonly, 1, &rc); if( rc==LSM_IOERR_NOENT && db->bReadonly ){ rc = LSM_OK; } } if( pbOpen ) *pbOpen = (pFS->fdLog!=0); return rc; } void lsmFsCloseLog(lsm_db *db){ FileSystem *pFS = db->pFS; if( pFS->fdLog ){ lsmEnvClose(pFS->pEnv, pFS->fdLog); pFS->fdLog = 0; } } /* ** Open a connection to a database stored within the file-system (the ** "system of files"). ** ** If parameter bReadonly is true, then open a read-only file-descriptor ** on the database file. It is possible that bReadonly will be false even ** if the user requested that pDb be opened read-only. This is because the ** file-descriptor may later on be recycled by a read-write connection. ** If the db file can be opened for read-write access, it always is. Parameter ** bReadonly is only ever true if it has already been determined that the ** db can only be opened for read-only access. */ int lsmFsOpen( lsm_db *pDb, /* Database connection to open fd for */ const char *zDb, /* Full path to database file */ int bReadonly /* True to open db file read-only */ ){ FileSystem *pFS; int rc = LSM_OK; int nDb = strlen(zDb); int nByte; assert( pDb->pFS==0 ); assert( pDb->pWorker==0 && pDb->pClient==0 ); nByte = sizeof(FileSystem) + nDb+1 + nDb+4+1; pFS = (FileSystem *)lsmMallocZeroRc(pDb->pEnv, nByte, &rc); if( pFS ){ LsmFile *pLsmFile; pFS->zDb = (char *)&pFS[1]; pFS->zLog = &pFS->zDb[nDb+1]; pFS->nPagesize = LSM_DFLT_PAGE_SIZE; pFS->nBlocksize = LSM_DFLT_BLOCK_SIZE; pFS->nMetasize = 4 * 1024; pFS->pDb = pDb; pFS->pEnv = pDb->pEnv; if( !pDb->compress.xCompress ){ pFS->mmapmgr.eUseMmap = pDb->eMmap; pFS->mmapmgr.nMapsz = 1*1024*1024; pFS->mmapmgr.nMapsz = 4*1024; } /* Make a copy of the database and log file names. */ |
︙ | ︙ | |||
848 849 850 851 852 853 854 | if( pLsmFile ){ pFS->pLsmFile = pLsmFile; pFS->fdDb = pLsmFile->pFile; memset(pLsmFile, 0, sizeof(LsmFile)); }else{ pFS->pLsmFile = lsmMallocZeroRc(pDb->pEnv, sizeof(LsmFile), &rc); if( rc==LSM_OK ){ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 | if( pLsmFile ){ pFS->pLsmFile = pLsmFile; pFS->fdDb = pLsmFile->pFile; memset(pLsmFile, 0, sizeof(LsmFile)); }else{ pFS->pLsmFile = lsmMallocZeroRc(pDb->pEnv, sizeof(LsmFile), &rc); if( rc==LSM_OK ){ pFS->fdDb = fsOpenFile(pFS, bReadonly, 0, &rc); } } if( rc!=LSM_OK ){ lsmFsClose(pFS); pFS = 0; }else{ pFS->szSector = lsmEnvSectorSize(pFS->pEnv, pFS->fdDb); } } pDb->pFS = pFS; return rc; } /* ** Configure the file-system object according to the current values of ** the LSM_CONFIG_MMAP and LSM_CONFIG_SET_COMPRESSION options. */ int lsmFsConfigure(lsm_db *db){ FileSystem *pFS = db->pFS; if( pFS ){ lsm_env *pEnv = pFS->pEnv; Page *pPg; assert( pFS->nOut==0 ); assert( pFS->pWaiting==0 ); /* Reset any compression/decompression buffers already allocated */ lsmFree(pEnv, pFS->aIBuffer); lsmFree(pEnv, pFS->aOBuffer); pFS->nBuffer = 0; /* Unmap the file, if it is currently mapped */ if( pFS->pMap ){ lsmEnvRemap(pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap); pFS->bUseMmap = 0; } /* Free all allocate page structures */ pPg = pFS->pLruFirst; while( pPg ){ Page *pNext = pPg->pLruNext; if( pPg->flags & PAGE_FREE ) lsmFree(pEnv, pPg->aData); lsmFree(pEnv, pPg); pPg = pNext; } /* Zero pointers that point to deleted page objects */ pFS->nCacheAlloc = 0; pFS->pLruFirst = 0; pFS->pLruLast = 0; pFS->pFree = 0; /* Configure the FileSystem object */ if( db->compress.xCompress ){ pFS->pCompress = &db->compress; pFS->bUseMmap = 0; }else{ pFS->pCompress = 0; pFS->bUseMmap = db->bMmap; } } return LSM_OK; } /* ** Close and destroy a FileSystem object. */ void lsmFsClose(FileSystem *pFS){ if( pFS ){ Page *pPg; |
︙ | ︙ | |||
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 | p->aData = fsMmapRef(pFS, iOff, pFS->nPagesize, &p->pRef, &rc); if( rc!=LSM_OK ){ p->pHashNext = pFS->pFree; pFS->pFree = p; p = 0; } }else{ /* Search the hash-table for the page */ iHash = fsHashKey(pFS->nHash, iReal); for(p=pFS->apHash[iHash]; p; p=p->pHashNext){ if( p->iPg==iReal) break; } | > > | 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 | p->aData = fsMmapRef(pFS, iOff, pFS->nPagesize, &p->pRef, &rc); if( rc!=LSM_OK ){ p->pHashNext = pFS->pFree; pFS->pFree = p; p = 0; } assert( (p->flags & PAGE_FREE)==0 ); }else{ /* Search the hash-table for the page */ iHash = fsHashKey(pFS->nHash, iReal); for(p=pFS->apHash[iHash]; p; p=p->pHashNext){ if( p->iPg==iReal) break; } |
︙ | ︙ |
Changes to src/lsm_log.c.
︙ | ︙ | |||
201 202 203 204 205 206 207 208 209 210 211 212 213 214 | #define LSM_LOG_WRITE_CKSUM 0x07 #define LSM_LOG_DELETE 0x08 #define LSM_LOG_DELETE_CKSUM 0x09 /* Require a checksum every 32KB. */ #define LSM_CKSUM_MAXDATA (32*1024) /* ** szSector: ** Commit records must be aligned to end on szSector boundaries. If ** the safety-mode is set to NORMAL or OFF, this value is 1. Otherwise, ** if the safety-mode is set to FULL, it is the size of the file-system ** sectors as reported by lsmFsSectorSize(). */ | > > > | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | #define LSM_LOG_WRITE_CKSUM 0x07 #define LSM_LOG_DELETE 0x08 #define LSM_LOG_DELETE_CKSUM 0x09 /* Require a checksum every 32KB. */ #define LSM_CKSUM_MAXDATA (32*1024) /* Do not wrap a log file smaller than this in bytes. */ #define LSM_MIN_LOGWRAP (128*1024) /* ** szSector: ** Commit records must be aligned to end on szSector boundaries. If ** the safety-mode is set to NORMAL or OFF, this value is 1. Otherwise, ** if the safety-mode is set to FULL, it is the size of the file-system ** sectors as reported by lsmFsSectorSize(). */ |
︙ | ︙ | |||
297 298 299 300 301 302 303 | /* ** If possible, reclaim log file space. Log file space is reclaimed after ** a snapshot that points to the same data in the database file is synced ** into the db header. */ static int logReclaimSpace(lsm_db *pDb){ | | > > > > > > | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | /* ** If possible, reclaim log file space. Log file space is reclaimed after ** a snapshot that points to the same data in the database file is synced ** into the db header. */ static int logReclaimSpace(lsm_db *pDb){ int rc; int iMeta; int bRotrans; /* True if there exists some ro-trans */ /* Test if there exists some other connection with a read-only transaction ** open. If there does, then log file space may not be reclaimed. */ rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc!=LSM_OK || bRotrans ) return rc; iMeta = (int)pDb->pShmhdr->iMetaPage; if( iMeta==1 || iMeta==2 ){ DbLog *pLog = &pDb->treehdr.log; i64 iSyncedId; /* Read the snapshot-id of the snapshot stored on meta-page iMeta. Note |
︙ | ︙ | |||
353 354 355 356 357 358 359 | LogWriter *pNew; LogRegion *aReg; if( pDb->bUseLog==0 ) return LSM_OK; /* If the log file has not yet been opened, open it now. Also allocate ** the LogWriter structure, if it has not already been allocated. */ | | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | LogWriter *pNew; LogRegion *aReg; if( pDb->bUseLog==0 ) return LSM_OK; /* If the log file has not yet been opened, open it now. Also allocate ** the LogWriter structure, if it has not already been allocated. */ rc = lsmFsOpenLog(pDb, 0); if( pDb->pLogWriter==0 ){ pNew = lsmMallocZeroRc(pDb->pEnv, sizeof(LogWriter), &rc); if( pNew ){ lsmStringInit(&pNew->buf, pDb->pEnv); rc = lsmStringExtend(&pNew->buf, 2); } }else{ |
︙ | ︙ | |||
399 400 401 402 403 404 405 | }else{ pNew->szSector = 1; } /* There are now three scenarios: ** ** 1) Regions 0 and 1 are both zero bytes in size and region 2 begins | | < | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 | }else{ pNew->szSector = 1; } /* There are now three scenarios: ** ** 1) Regions 0 and 1 are both zero bytes in size and region 2 begins ** at a file offset greater than LSM_MIN_LOGWRAP. In this case, wrap ** around to the start and write data into the start of the log file. ** ** 2) Region 1 is zero bytes in size and region 2 occurs earlier in the ** file than region 0. In this case, append data to region 2, but ** remember to jump over region 1 if required. ** ** 3) Region 2 is the last in the file. Append to it. */ aReg = &pDb->treehdr.log.aRegion[0]; assert( aReg[0].iEnd==0 || aReg[0].iEnd>aReg[0].iStart ); assert( aReg[1].iEnd==0 || aReg[1].iEnd>aReg[1].iStart ); pNew->cksum0 = pDb->treehdr.log.cksum0; pNew->cksum1 = pDb->treehdr.log.cksum1; if( aReg[0].iEnd==0 && aReg[1].iEnd==0 && aReg[2].iStart>=LSM_MIN_LOGWRAP ){ /* Case 1. Wrap around to the start of the file. Write an LSM_LOG_JUMP ** into the log file in this case. Pad it out to 8 bytes using a PAD2 ** record so that the checksums can be updated immediately. */ u8 aJump[] = { LSM_LOG_PAD2, 0x04, 0x00, 0x00, 0x00, 0x00, LSM_LOG_JUMP, 0x00 }; |
︙ | ︙ | |||
952 953 954 955 956 957 958 959 | LsmString buf2; /* Value buffer */ LogReader reader; /* Log reader object */ int rc = LSM_OK; /* Return code */ int nCommit = 0; /* Number of transactions to recover */ int iPass; int nJump = 0; /* Number of LSM_LOG_JUMP records in pass 0 */ DbLog *pLog; | > | > | 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 | LsmString buf2; /* Value buffer */ LogReader reader; /* Log reader object */ int rc = LSM_OK; /* Return code */ int nCommit = 0; /* Number of transactions to recover */ int iPass; int nJump = 0; /* Number of LSM_LOG_JUMP records in pass 0 */ DbLog *pLog; int bOpen; rc = lsmFsOpenLog(pDb, &bOpen); if( rc!=LSM_OK ) return rc; rc = lsmTreeInit(pDb); if( rc!=LSM_OK ) return rc; pLog = &pDb->treehdr.log; lsmCheckpointLogoffset(pDb->pShmhdr->aSnap2, pLog); logReaderInit(pDb, pLog, 1, &reader); lsmStringInit(&buf1, pDb->pEnv); lsmStringInit(&buf2, pDb->pEnv); /* The outer for() loop runs at most twice. The first iteration is to ** count the number of committed transactions in the log. The second ** iterates through those transactions and updates the in-memory tree ** structure with their contents. */ if( bOpen ){ for(iPass=0; iPass<2 && rc==LSM_OK; iPass++){ int bEof = 0; while( rc==LSM_OK && !bEof ){ u8 eType = 0; logReaderByte(&reader, &eType, &rc); |
︙ | ︙ | |||
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 | lsmCheckpointZeroLogoffset(pDb); } } logReaderInit(pDb, pLog, 0, &reader); nCommit = nCommit * -1; } } /* Initialize DbLog object */ if( rc==LSM_OK ){ pLog->aRegion[2].iEnd = reader.iOff - reader.buf.n + reader.iBuf; pLog->cksum0 = reader.cksum0; pLog->cksum1 = reader.cksum1; } if( rc==LSM_OK ){ rc = lsmFinishRecovery(pDb); }else{ lsmFinishRecovery(pDb); } lsmStringClear(&buf1); lsmStringClear(&buf2); lsmStringClear(&reader.buf); return rc; } | > > > > > | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | lsmCheckpointZeroLogoffset(pDb); } } logReaderInit(pDb, pLog, 0, &reader); nCommit = nCommit * -1; } } } /* Initialize DbLog object */ if( rc==LSM_OK ){ pLog->aRegion[2].iEnd = reader.iOff - reader.buf.n + reader.iBuf; pLog->cksum0 = reader.cksum0; pLog->cksum1 = reader.cksum1; } if( rc==LSM_OK ){ rc = lsmFinishRecovery(pDb); }else{ lsmFinishRecovery(pDb); } if( pDb->bRoTrans ){ lsmFsCloseLog(pDb); } lsmStringClear(&buf1); lsmStringClear(&buf2); lsmStringClear(&reader.buf); return rc; } |
︙ | ︙ |
Changes to src/lsm_main.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 | */ static void assert_db_state(lsm_db *pDb){ /* If there is at least one cursor or a write transaction open, the database ** handle must be holding a pointer to a client snapshot. And the reverse ** - if there are no open cursors and no write transactions then there must ** not be a client snapshot. */ | > | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | */ static void assert_db_state(lsm_db *pDb){ /* If there is at least one cursor or a write transaction open, the database ** handle must be holding a pointer to a client snapshot. And the reverse ** - if there are no open cursors and no write transactions then there must ** not be a client snapshot. */ assert( (pDb->pCsr!=0||pDb->nTransOpen>0)==(pDb->iReader>=0||pDb->bRoTrans) ); assert( (pDb->iReader<0 && pDb->bRoTrans==0) || pDb->pClient!=0 ); assert( pDb->nTransOpen>=0 ); } #else # define assert_db_state(x) #endif |
︙ | ︙ | |||
83 84 85 86 87 88 89 | /* Initialize the new object */ pDb->pEnv = pEnv; pDb->nTreeLimit = LSM_DFLT_AUTOFLUSH; pDb->nAutockpt = LSM_DFLT_AUTOCHECKPOINT; pDb->bAutowork = LSM_DFLT_AUTOWORK; pDb->eSafety = LSM_DFLT_SAFETY; pDb->xCmp = xCmp; | < > > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | /* Initialize the new object */ pDb->pEnv = pEnv; pDb->nTreeLimit = LSM_DFLT_AUTOFLUSH; pDb->nAutockpt = LSM_DFLT_AUTOCHECKPOINT; pDb->bAutowork = LSM_DFLT_AUTOWORK; pDb->eSafety = LSM_DFLT_SAFETY; pDb->xCmp = xCmp; pDb->nDfltPgsz = LSM_DFLT_PAGE_SIZE; pDb->nDfltBlksz = LSM_DFLT_BLOCK_SIZE; pDb->nMerge = LSM_DFLT_AUTOMERGE; pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES; pDb->bUseLog = LSM_DFLT_USE_LOG; pDb->iReader = -1; pDb->iRwclient = -1; pDb->bMultiProc = LSM_DFLT_MULTIPLE_PROCESSES; pDb->eMmap = LSM_DFLT_MMAP; pDb->xLog = xLog; pDb->compress.iId = LSM_COMPRESSION_NONE; return LSM_OK; } lsm_env *lsm_get_env(lsm_db *pDb){ assert( pDb->pEnv ); return pDb->pEnv; } |
︙ | ︙ | |||
136 137 138 139 140 141 142 143 144 145 146 147 148 149 | if( rc!=LSM_OK ){ lsmFree(pEnv, zAlloc); zAlloc = 0; } *pzAbs = zAlloc; return rc; } /* ** Open a new connection to database zFilename. */ int lsm_open(lsm_db *pDb, const char *zFilename){ int rc; | > > > > > > > > > > > > > > > > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | if( rc!=LSM_OK ){ lsmFree(pEnv, zAlloc); zAlloc = 0; } *pzAbs = zAlloc; return rc; } /* ** Check that the bits in the db->mLock mask are consistent with the ** value stored in db->iRwclient. An assert shall fail otherwise. */ static void assertRwclientLockValue(lsm_db *db){ #ifndef NDEBUG u64 msk; /* Mask of mLock bits for RWCLIENT locks */ u64 rwclient = 0; /* Bit corresponding to db->iRwclient */ if( db->iRwclient>=0 ){ rwclient = ((u64)1 << (LSM_LOCK_RWCLIENT(db->iRwclient)-1)); } msk = ((u64)1 << (LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT)-1)) - 1; msk -= (((u64)1 << (LSM_LOCK_RWCLIENT(0)-1)) - 1); assert( (db->mLock & msk)==rwclient ); #endif } /* ** Open a new connection to database zFilename. */ int lsm_open(lsm_db *pDb, const char *zFilename){ int rc; |
︙ | ︙ | |||
158 159 160 161 162 163 164 | ** than one purpose - to open both the database and log files, and ** perhaps to unlink the log file during disconnection. An absolute ** path is required to ensure that the correct files are operated ** on even if the application changes the cwd. */ rc = getFullpathname(pDb->pEnv, zFilename, &zFull); assert( rc==LSM_OK || zFull==0 ); | | > | | > | > > > > > < > > > > > > > > > > > > > | > | | > > > | | < < < < < < < < < | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | ** than one purpose - to open both the database and log files, and ** perhaps to unlink the log file during disconnection. An absolute ** path is required to ensure that the correct files are operated ** on even if the application changes the cwd. */ rc = getFullpathname(pDb->pEnv, zFilename, &zFull); assert( rc==LSM_OK || zFull==0 ); /* Connect to the database. */ if( rc==LSM_OK ){ rc = lsmDbDatabaseConnect(pDb, zFull); } if( pDb->bReadonly==0 ){ /* Configure the file-system connection with the page-size and block-size ** of this database. Even if the database file is zero bytes in size ** on disk, these values have been set in shared-memory by now, and so ** are guaranteed not to change during the lifetime of this connection. */ if( rc==LSM_OK && LSM_OK==(rc = lsmCheckpointLoad(pDb, 0)) ){ lsmFsSetPageSize(pDb->pFS, lsmCheckpointPgsz(pDb->aSnapshot)); lsmFsSetBlockSize(pDb->pFS, lsmCheckpointBlksz(pDb->aSnapshot)); } } lsmFree(pDb->pEnv, zFull); assertRwclientLockValue(pDb); } assert( pDb->bReadonly==0 || pDb->bReadonly==1 ); assert( rc!=LSM_OK || (pDb->pShmhdr==0)==(pDb->bReadonly==1) ); return rc; } int lsm_close(lsm_db *pDb){ int rc = LSM_OK; if( pDb ){ assert_db_state(pDb); if( pDb->pCsr || pDb->nTransOpen ){ rc = LSM_MISUSE_BKPT; }else{ lsmMCursorFreeCache(pDb); lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; assertRwclientLockValue(pDb); lsmDbDatabaseRelease(pDb); lsmLogClose(pDb); lsmFsClose(pDb->pFS); assert( pDb->mLock==0 ); /* Invoke any destructors registered for the compression or ** compression factory callbacks. */ if( pDb->factory.xFree ) pDb->factory.xFree(pDb->factory.pCtx); if( pDb->compress.xFree ) pDb->compress.xFree(pDb->compress.pCtx); lsmFree(pDb->pEnv, pDb->rollback.aArray); lsmFree(pDb->pEnv, pDb->aTrans); lsmFree(pDb->pEnv, pDb->apShm); lsmFree(pDb->pEnv, pDb); } } return rc; } int lsm_config(lsm_db *pDb, int eParam, ...){ int rc = LSM_OK; va_list ap; va_start(ap, eParam); switch( eParam ){ case LSM_CONFIG_AUTOFLUSH: { /* This parameter is read and written in KB. But all internal ** processing is done in bytes. */ int *piVal = va_arg(ap, int *); int iVal = *piVal; if( iVal>=0 && iVal<=(1024*1024) ){ pDb->nTreeLimit = iVal*1024; } *piVal = (pDb->nTreeLimit / 1024); break; } case LSM_CONFIG_AUTOWORK: { int *piVal = va_arg(ap, int *); if( *piVal>=0 ){ pDb->bAutowork = *piVal; } *piVal = pDb->bAutowork; break; } case LSM_CONFIG_AUTOCHECKPOINT: { /* This parameter is read and written in KB. But all internal processing ** (including the lsm_db.nAutockpt variable) is done in bytes. */ int *piVal = va_arg(ap, int *); if( *piVal>=0 ){ int iVal = *piVal; pDb->nAutockpt = (i64)iVal * 1024; } *piVal = (int)(pDb->nAutockpt / 1024); break; } case LSM_CONFIG_PAGE_SIZE: { int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. |
︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 | *piVal = pDb->nDfltPgsz; } } break; } case LSM_CONFIG_BLOCK_SIZE: { int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. | > > | | | > | | | | > | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | *piVal = pDb->nDfltPgsz; } } break; } case LSM_CONFIG_BLOCK_SIZE: { /* This parameter is read and written in KB. But all internal ** processing is done in bytes. */ int *piVal = va_arg(ap, int *); if( pDb->pDatabase ){ /* If lsm_open() has been called, this is a read-only parameter. ** Set the output variable to the block-size in KB according to the ** FileSystem object. */ *piVal = lsmFsBlockSize(pDb->pFS) / 1024; }else{ int iVal = *piVal; if( iVal>=64 && iVal<=65536 && ((iVal-1) & iVal)==0 ){ pDb->nDfltBlksz = iVal * 1024; }else{ *piVal = pDb->nDfltBlksz / 1024; } } break; } case LSM_CONFIG_SAFETY: { int *piVal = va_arg(ap, int *); if( *piVal>=0 && *piVal<=2 ){ pDb->eSafety = *piVal; } *piVal = pDb->eSafety; break; } case LSM_CONFIG_MMAP: { int *piVal = va_arg(ap, int *); if( pDb->iReader<0 && *piVal>=0 && *piVal<=1 ){ pDb->bMmap = *piVal; rc = lsmFsConfigure(pDb); } *piVal = pDb->eMmap; break; } case LSM_CONFIG_USE_LOG: { int *piVal = va_arg(ap, int *); |
︙ | ︙ | |||
331 332 333 334 335 336 337 338 339 340 | ** in multi-process mode. */ *piVal = lsmDbMultiProc(pDb); }else{ pDb->bMultiProc = *piVal = (*piVal!=0); } break; } case LSM_CONFIG_SET_COMPRESSION: { lsm_compress *p = va_arg(ap, lsm_compress *); | > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | ** in multi-process mode. */ *piVal = lsmDbMultiProc(pDb); }else{ pDb->bMultiProc = *piVal = (*piVal!=0); } break; } case LSM_CONFIG_READONLY: { int *piVal = va_arg(ap, int *); /* If lsm_open() has been called, this is a read-only parameter. */ if( pDb->pDatabase==0 && *piVal>=0 ){ pDb->bReadonly = *piVal = (*piVal!=0); } *piVal = pDb->bReadonly; break; } case LSM_CONFIG_SET_COMPRESSION: { lsm_compress *p = va_arg(ap, lsm_compress *); if( pDb->iReader>=0 && pDb->bInFactory==0 ){ /* May not change compression schemes with an open transaction */ rc = LSM_MISUSE_BKPT; }else{ if( pDb->compress.xFree ){ /* Invoke any destructor belonging to the current compression. */ pDb->compress.xFree(pDb->compress.pCtx); } if( p->xBound==0 ){ memset(&pDb->compress, 0, sizeof(lsm_compress)); pDb->compress.iId = LSM_COMPRESSION_NONE; }else{ memcpy(&pDb->compress, p, sizeof(lsm_compress)); } rc = lsmFsConfigure(pDb); } break; } case LSM_CONFIG_SET_COMPRESSION_FACTORY: { lsm_compress_factory *p = va_arg(ap, lsm_compress_factory *); if( pDb->factory.xFree ){ /* Invoke any destructor belonging to the current factory. */ pDb->factory.xFree(pDb->factory.pCtx); } memcpy(&pDb->factory, p, sizeof(lsm_compress_factory)); break; } case LSM_CONFIG_GET_COMPRESSION: { lsm_compress *p = va_arg(ap, lsm_compress *); memcpy(p, &pDb->compress, sizeof(lsm_compress)); break; |
︙ | ︙ | |||
429 430 431 432 433 434 435 | return 0; } int lsmInfoFreelist(lsm_db *pDb, char **pzOut){ Snapshot *pWorker; /* Worker snapshot */ int bUnlock = 0; LsmString s; | < < < < | | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | return 0; } int lsmInfoFreelist(lsm_db *pDb, char **pzOut){ Snapshot *pWorker; /* Worker snapshot */ int bUnlock = 0; LsmString s; int rc; /* Obtain the worker snapshot */ rc = infoGetWorker(pDb, &pWorker, &bUnlock); if( rc!=LSM_OK ) return rc; lsmStringInit(&s, pDb->pEnv); rc = lsmWalkFreelist(pDb, 0, infoFreelistCb, &s); if( rc!=LSM_OK ){ lsmFree(pDb->pEnv, s.z); }else{ *pzOut = s.z; } /* Release the snapshot and return */ infoFreeWorker(pDb, bUnlock); return rc; } static int infoTreeSize(lsm_db *db, int *pnOldKB, int *pnNewKB){ ShmHeader *pShm = db->pShmhdr; TreeHeader *p = &pShm->hdr1; /* The following code suffers from two race conditions, as it accesses and ** trusts the contents of shared memory without verifying checksums: ** ** * The two values read - TreeHeader.root.nByte and oldroot.nByte - are |
︙ | ︙ | |||
475 476 477 478 479 480 481 | ** for the size of the "old" tree may reflect the size of an "old" ** tree that was recently flushed to disk. ** ** Given the context in which this function is called (as a result of an ** lsm_info(LSM_INFO_TREE_SIZE) request), neither of these are considered to ** be problems. */ | | | | | | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | ** for the size of the "old" tree may reflect the size of an "old" ** tree that was recently flushed to disk. ** ** Given the context in which this function is called (as a result of an ** lsm_info(LSM_INFO_TREE_SIZE) request), neither of these are considered to ** be problems. */ *pnNewKB = ((int)p->root.nByte + 1023) / 1024; if( p->iOldShmid ){ if( p->iOldLog==lsmCheckpointLogOffset(pShm->aSnap1) ){ *pnOldKB = 0; }else{ *pnOldKB = ((int)p->oldroot.nByte + 1023) / 1024; } }else{ *pnOldKB = 0; } return LSM_OK; } int lsm_info(lsm_db *pDb, int eParam, ...){ int rc = LSM_OK; |
︙ | ︙ | |||
554 555 556 557 558 559 560 | case LSM_INFO_FREELIST: { char **pzVal = va_arg(ap, char **); rc = lsmInfoFreelist(pDb, pzVal); break; } case LSM_INFO_CHECKPOINT_SIZE: { | | | > > > > > > > > > > | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | case LSM_INFO_FREELIST: { char **pzVal = va_arg(ap, char **); rc = lsmInfoFreelist(pDb, pzVal); break; } case LSM_INFO_CHECKPOINT_SIZE: { int *pnKB = va_arg(ap, int *); rc = lsmCheckpointSize(pDb, pnKB); break; } case LSM_INFO_TREE_SIZE: { int *pnOld = va_arg(ap, int *); int *pnNew = va_arg(ap, int *); rc = infoTreeSize(pDb, pnOld, pnNew); break; } case LSM_INFO_COMPRESSION_ID: { unsigned int *piOut = va_arg(ap, unsigned int *); if( pDb->pClient ){ *piOut = pDb->pClient->iCmpId; }else{ rc = lsmInfoCompressionId(pDb, piOut); } break; } default: rc = LSM_MISUSE; break; } va_end(ap); |
︙ | ︙ | |||
677 678 679 680 681 682 683 | /* ** Open a new cursor handle. ** ** If there are currently no other open cursor handles, and no open write ** transaction, open a read transaction here. */ int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr){ | | > > > > > | > > | > | | | 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | /* ** Open a new cursor handle. ** ** If there are currently no other open cursor handles, and no open write ** transaction, open a read transaction here. */ int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr){ int rc = LSM_OK; /* Return code */ MultiCursor *pCsr = 0; /* New cursor object */ /* Open a read transaction if one is not already open. */ assert_db_state(pDb); if( pDb->pShmhdr==0 ){ assert( pDb->bReadonly ); rc = lsmBeginRoTrans(pDb); }else if( pDb->iReader<0 ){ rc = lsmBeginReadTrans(pDb); } /* Allocate the multi-cursor. */ if( rc==LSM_OK ){ rc = lsmMCursorNew(pDb, &pCsr); } /* If an error has occured, set the output to NULL and delete any partially ** allocated cursor. If this means there are no open cursors, release the ** client snapshot. */ if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); dbReleaseClientSnapshot(pDb); } assert_db_state(pDb); *ppCsr = (lsm_cursor *)pCsr; return rc; } /* ** Close a cursor opened using lsm_csr_open(). */ int lsm_csr_close(lsm_cursor *p){ if( p ){ lsm_db *pDb = lsmMCursorDb((MultiCursor *)p); assert_db_state(pDb); lsmMCursorClose((MultiCursor *)p, 1); dbReleaseClientSnapshot(pDb); assert_db_state(pDb); } return LSM_OK; } /* |
︙ | ︙ | |||
785 786 787 788 789 790 791 | va_end(ap2); pDb->xLog(pDb->pLogCtx, rc, s.z); lsmStringClear(&s); } } int lsm_begin(lsm_db *pDb, int iLevel){ | | > < | 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | va_end(ap2); pDb->xLog(pDb->pLogCtx, rc, s.z); lsmStringClear(&s); } } int lsm_begin(lsm_db *pDb, int iLevel){ int rc; assert_db_state( pDb ); rc = (pDb->bReadonly ? LSM_READONLY : LSM_OK); /* A value less than zero means open one more transaction. */ if( iLevel<0 ) iLevel = pDb->nTransOpen + 1; if( iLevel>pDb->nTransOpen ){ int i; /* Extend the pDb->aTrans[] array if required. */ if( rc==LSM_OK && pDb->nTransAlloc<iLevel ){ TransMark *aNew; /* New allocation */ int nByte = sizeof(TransMark) * (iLevel+1); |
︙ | ︙ | |||
836 837 838 839 840 841 842 | assert_db_state( pDb ); /* A value less than zero means close the innermost nested transaction. */ if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1); if( iLevel<pDb->nTransOpen ){ if( iLevel==0 ){ | < < | 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | assert_db_state( pDb ); /* A value less than zero means close the innermost nested transaction. */ if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1); if( iLevel<pDb->nTransOpen ){ if( iLevel==0 ){ /* Commit the transaction to disk. */ if( rc==LSM_OK ) rc = lsmLogCommit(pDb); if( rc==LSM_OK && pDb->eSafety==LSM_SAFETY_FULL ){ rc = lsmFsSyncLog(pDb->pFS); } lsmFinishWriteTrans(pDb, (rc==LSM_OK)); } |
︙ | ︙ |
Changes to src/lsm_shared.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* Protected by the global mutex (enterGlobalMutex/leaveGlobalMutex): */ char *zName; /* Canonical path to database file */ int nName; /* strlen(zName) */ int nDbRef; /* Number of associated lsm_db handles */ Database *pDbNext; /* Next Database structure in global list */ /* Protected by the local mutex (pClientMutex) */ int bMultiProc; /* True if running in multi-process mode */ lsm_file *pFile; /* Used for locks/shm in multi-proc mode */ LsmFile *pLsmFile; /* List of deferred closes */ lsm_mutex *pClientMutex; /* Protects the apShmChunk[] and pConn */ int nShmChunk; /* Number of entries in apShmChunk[] array */ void **apShmChunk; /* Array of "shared" memory regions */ lsm_db *pConn; /* List of connections to this db. */ | > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /* Protected by the global mutex (enterGlobalMutex/leaveGlobalMutex): */ char *zName; /* Canonical path to database file */ int nName; /* strlen(zName) */ int nDbRef; /* Number of associated lsm_db handles */ Database *pDbNext; /* Next Database structure in global list */ /* Protected by the local mutex (pClientMutex) */ int bReadonly; /* True if Database.pFile is read-only */ int bMultiProc; /* True if running in multi-process mode */ lsm_file *pFile; /* Used for locks/shm in multi-proc mode */ LsmFile *pLsmFile; /* List of deferred closes */ lsm_mutex *pClientMutex; /* Protects the apShmChunk[] and pConn */ int nShmChunk; /* Number of entries in apShmChunk[] array */ void **apShmChunk; /* Array of "shared" memory regions */ lsm_db *pConn; /* List of connections to this db. */ |
︙ | ︙ | |||
207 208 209 210 211 212 213 | ** to as small a size as possible without truncating away any blocks that ** contain data. */ static int dbTruncateFile(lsm_db *pDb){ int rc; assert( pDb->pWorker==0 ); | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | ** to as small a size as possible without truncating away any blocks that ** contain data. */ static int dbTruncateFile(lsm_db *pDb){ int rc; assert( pDb->pWorker==0 ); assert( lsmShmAssertLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL) ); rc = lsmCheckpointLoadWorker(pDb); if( rc==LSM_OK ){ DbTruncateCtx ctx; /* Walk the database free-block-list in reverse order. Set ctx.nBlock ** to the block number of the last block in the database that actually |
︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | pDb->pWorker = 0; return rc; } static void doDbDisconnect(lsm_db *pDb){ int rc; /* Block for an exclusive lock on DMS1. This lock serializes all calls ** to doDbConnect() and doDbDisconnect() across all processes. */ rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1); if( rc==LSM_OK ){ /* Try an exclusive lock on DMS2. If successful, this is the last ** connection to the database. In this case flush the contents of the ** in-memory tree to disk and write a checkpoint. */ | > > > | > > > > > | | | > > > > > > > > > > > > | > | > > > > > > > > > > | > > > > > > > > > > | | > | > > > > > | > < > > > > > > > > > > > > > > > > > > > > > > > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | pDb->pWorker = 0; return rc; } static void doDbDisconnect(lsm_db *pDb){ int rc; if( pDb->bReadonly ){ lsmShmLock(pDb, LSM_LOCK_DMS3, LSM_LOCK_UNLOCK, 0); }else{ /* Block for an exclusive lock on DMS1. This lock serializes all calls ** to doDbConnect() and doDbDisconnect() across all processes. */ rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1); if( rc==LSM_OK ){ /* Try an exclusive lock on DMS2. If successful, this is the last ** connection to the database. In this case flush the contents of the ** in-memory tree to disk and write a checkpoint. */ rc = lsmShmTestLock(pDb, LSM_LOCK_DMS2, 1, LSM_LOCK_EXCL); if( rc==LSM_OK ){ rc = lsmShmTestLock(pDb, LSM_LOCK_CHECKPOINTER, 1, LSM_LOCK_EXCL); } if( rc==LSM_OK ){ int bReadonly = 0; /* True if there exist read-only conns. */ /* Flush the in-memory tree, if required. If there is data to flush, ** this will create a new client snapshot in Database.pClient. The ** checkpoint (serialization) of this snapshot may be written to disk ** by the following block. ** ** There is no need to take a WRITER lock here. That there are no ** other locks on DMS2 guarantees that there are no other read-write ** connections at this time (and the lock on DMS1 guarantees that ** no new ones may appear). */ rc = lsmTreeLoadHeader(pDb, 0); if( rc==LSM_OK && (lsmTreeHasOld(pDb) || lsmTreeSize(pDb)>0) ){ rc = lsmFlushTreeToDisk(pDb); } /* Now check if there are any read-only connections. If there are, ** then do not truncate the db file or unlink the shared-memory ** region. */ if( rc==LSM_OK ){ rc = lsmShmTestLock(pDb, LSM_LOCK_DMS3, 1, LSM_LOCK_EXCL); if( rc==LSM_BUSY ){ bReadonly = 1; rc = LSM_OK; } } /* Write a checkpoint to disk. */ if( rc==LSM_OK ){ rc = lsmCheckpointWrite(pDb, (bReadonly==0), 0); } /* If the checkpoint was written successfully, delete the log file ** and, if possible, truncate the database file. */ if( rc==LSM_OK ){ int bRotrans = 0; Database *p = pDb->pDatabase; /* The log file may only be deleted if there are no clients ** read-only clients running rotrans transactions. */ rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc==LSM_OK && bRotrans==0 ){ lsmFsCloseAndDeleteLog(pDb->pFS); } /* The database may only be truncated if there exist no read-only ** clients - either connected or running rotrans transactions. */ if( bReadonly==0 && bRotrans==0 ){ dbTruncateFile(pDb); if( p->pFile && p->bMultiProc ){ lsmEnvShmUnmap(pDb->pEnv, p->pFile, 1); } } } } } if( pDb->iRwclient>=0 ){ lsmShmLock(pDb, LSM_LOCK_RWCLIENT(pDb->iRwclient), LSM_LOCK_UNLOCK, 0); pDb->iRwclient = -1; } lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_UNLOCK, 0); lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); } pDb->pShmhdr = 0; } static int doDbConnect(lsm_db *pDb){ const int nUsMax = 100000; /* Max value for nUs */ int nUs = 1000; /* us to wait between DMS1 attempts */ int rc; /* Obtain a pointer to the shared-memory header */ assert( pDb->pShmhdr==0 ); assert( pDb->bReadonly==0 ); rc = lsmShmCacheChunks(pDb, 1); if( rc!=LSM_OK ) return rc; pDb->pShmhdr = (ShmHeader *)pDb->apShm[0]; /* Block for an exclusive lock on DMS1. This lock serializes all calls ** to doDbConnect() and doDbDisconnect() across all processes. */ while( 1 ){ rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1); if( rc!=LSM_BUSY ) break; lsmEnvSleep(pDb->pEnv, nUs); nUs = nUs * 2; if( nUs>nUsMax ) nUs = nUsMax; } if( rc!=LSM_OK ){ pDb->pShmhdr = 0; return rc; } /* Try an exclusive lock on DMS2/DMS3. If successful, this is the first ** and only connection to the database. In this case initialize the ** shared-memory and run log file recovery. */ assert( LSM_LOCK_DMS3==1+LSM_LOCK_DMS2 ); rc = lsmShmTestLock(pDb, LSM_LOCK_DMS2, 2, LSM_LOCK_EXCL); if( rc==LSM_OK ){ memset(pDb->pShmhdr, 0, sizeof(ShmHeader)); rc = lsmCheckpointRecover(pDb); if( rc==LSM_OK ){ rc = lsmLogRecover(pDb); } if( rc==LSM_OK ){ ShmHeader *pShm = pDb->pShmhdr; pShm->aReader[0].iLsmId = lsmCheckpointId(pShm->aSnap1, 0); pShm->aReader[0].iTreeId = pDb->treehdr.iUsedShmid; } }else if( rc==LSM_BUSY ){ rc = LSM_OK; } /* Take a shared lock on DMS2. In multi-process mode this lock "cannot" ** fail, as connections may only hold an exclusive lock on DMS2 if they ** first hold an exclusive lock on DMS1. And this connection is currently ** holding the exclusive lock on DSM1. ** ** However, if some other connection has the database open in single-process ** mode, this operation will fail. In this case, return the error to the ** caller - the attempt to connect to the db has failed. */ if( rc==LSM_OK ){ rc = lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_SHARED, 0); } /* If anything went wrong, unlock DMS2. Otherwise, try to take an exclusive ** lock on one of the LSM_LOCK_RWCLIENT() locks. Unlock DMS1 in any case. */ if( rc!=LSM_OK ){ pDb->pShmhdr = 0; }else{ int i; for(i=0; i<LSM_LOCK_NRWCLIENT; i++){ int rc2 = lsmShmLock(pDb, LSM_LOCK_RWCLIENT(i), LSM_LOCK_EXCL, 0); if( rc2==LSM_OK ) pDb->iRwclient = i; if( rc2!=LSM_BUSY ){ rc = rc2; break; } } } lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); return rc; } static int dbOpenSharedFd(lsm_env *pEnv, Database *p, int bRoOk){ int rc; rc = lsmEnvOpen(pEnv, p->zName, 0, &p->pFile); if( rc==LSM_IOERR && bRoOk ){ rc = lsmEnvOpen(pEnv, p->zName, LSM_OPEN_READONLY, &p->pFile); p->bReadonly = 1; } return rc; } /* ** Return a reference to the shared Database handle for the database ** identified by canonical path zName. If this is the first connection to ** the named database, a new Database object is allocated. Otherwise, a |
︙ | ︙ | |||
394 395 396 397 398 399 400 | rc = lsmMutexNew(pEnv, &p->pClientMutex); } /* If nothing has gone wrong so far, open the shared fd. And if that ** succeeds and this connection requested single-process mode, ** attempt to take the exclusive lock on DMS2. */ if( rc==LSM_OK ){ | > | > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | rc = lsmMutexNew(pEnv, &p->pClientMutex); } /* If nothing has gone wrong so far, open the shared fd. And if that ** succeeds and this connection requested single-process mode, ** attempt to take the exclusive lock on DMS2. */ if( rc==LSM_OK ){ int bReadonly = (pDb->bReadonly && pDb->bMultiProc); rc = dbOpenSharedFd(pDb->pEnv, p, bReadonly); } if( rc==LSM_OK && p->bMultiProc==0 ){ assert( p->bReadonly==0 ); rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS2, LSM_LOCK_EXCL); } if( rc==LSM_OK ){ p->pDbNext = gShared.pDatabase; gShared.pDatabase = p; }else{ |
︙ | ︙ | |||
425 426 427 428 429 430 431 | lsmMutexLeave(pDb->pEnv, p->pClientMutex); } } pDb->pDatabase = p; if( rc==LSM_OK ){ assert( p ); | | > > > > > > > > > > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | lsmMutexLeave(pDb->pEnv, p->pClientMutex); } } pDb->pDatabase = p; if( rc==LSM_OK ){ assert( p ); rc = lsmFsOpen(pDb, zName, p->bReadonly); } /* If the db handle is read-write, then connect to the system now. Run ** recovery as necessary. Or, if this is a read-only database handle, ** defer attempting to connect to the system until a read-transaction ** is opened. */ if( pDb->bReadonly==0 ){ if( rc==LSM_OK ){ rc = doDbConnect(pDb); } if( rc==LSM_OK ){ rc = lsmFsConfigure(pDb); } } return rc; } static void dbDeferClose(lsm_db *pDb){ if( pDb->pFS ){ |
︙ | ︙ | |||
719 720 721 722 723 724 725 | lsmLogMessage(pDb, 0, "lsmBlockAllocate(): " "snapshot-in-use: %lld (iSynced=%lld) (client-id=%lld)", iInUse, iSynced, (pDb->iReader>=0 ? pDb->pClient->iId : 0) ); } #endif | > > | > > > > > > > > > > > | > > | 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | lsmLogMessage(pDb, 0, "lsmBlockAllocate(): " "snapshot-in-use: %lld (iSynced=%lld) (client-id=%lld)", iInUse, iSynced, (pDb->iReader>=0 ? pDb->pClient->iId : 0) ); } #endif /* Unless there exists a read-only transaction (which prevents us from ** recycling any blocks regardless, query the free block list for a ** suitable block to reuse. ** ** It might seem more natural to check for a read-only transaction at ** the start of this function. However, it is better do wait until after ** the call to lsmCheckpointSynced() to do so. */ if( rc==LSM_OK ){ int bRotrans; rc = lsmDetectRoTrans(pDb, &bRotrans); if( rc==LSM_OK && bRotrans==0 ){ rc = findFreeblock(pDb, iInUse, (iBefore>0), &iRet); } } if( iBefore>0 && (iRet<=0 || iRet>=iBefore) ){ iRet = 0; }else if( rc==LSM_OK ){ /* If a block was found in the free block list, use it and remove it from ** the list. Otherwise, if no suitable block was found, allocate one from |
︙ | ︙ | |||
781 782 783 784 785 786 787 | ** but then not used. This function is used to push the block back onto ** the freelist. Refreeing a block is different from freeing is, as a refreed ** block may be reused immediately. Whereas a freed block can not be reused ** until (at least) after the next checkpoint. */ int lsmBlockRefree(lsm_db *pDb, int iBlk){ int rc = LSM_OK; /* Return code */ | < | 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | ** but then not used. This function is used to push the block back onto ** the freelist. Refreeing a block is different from freeing is, as a refreed ** block may be reused immediately. Whereas a freed block can not be reused ** until (at least) after the next checkpoint. */ int lsmBlockRefree(lsm_db *pDb, int iBlk){ int rc = LSM_OK; /* Return code */ #ifdef LSM_LOG_FREELIST lsmLogMessage(pDb, LSM_OK, "lsmBlockRefree(): Refree block %d", iBlk); #endif rc = freelistAppend(pDb, iBlk, 0); return rc; |
︙ | ︙ | |||
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 | if( p ){ lsmSortedFreeLevel(pEnv, p->pLevel); lsmFree(pEnv, p->freelist.aEntry); lsmFree(pEnv, p->redirect.a); lsmFree(pEnv, p); } } /* ** Argument bFlush is true if the contents of the in-memory tree has just ** been flushed to disk. The significance of this is that once the snapshot ** created to hold the updated state of the database is synced to disk, log ** file space can be recycled. */ void lsmFinishWork(lsm_db *pDb, int bFlush, int *pRc){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 | if( p ){ lsmSortedFreeLevel(pEnv, p->pLevel); lsmFree(pEnv, p->freelist.aEntry); lsmFree(pEnv, p->redirect.a); lsmFree(pEnv, p); } } /* ** Attempt to populate one of the read-lock slots to contain lock values ** iLsm/iShm. Or, if such a slot exists already, this function is a no-op. ** ** It is not an error if no slot can be populated because the write-lock ** cannot be obtained. If any other error occurs, return an LSM error code. ** Otherwise, LSM_OK. ** ** This function is called at various points to try to ensure that there ** always exists at least one read-lock slot that can be used by a read-only ** client. And so that, in the usual case, there is an "exact match" available ** whenever a read transaction is opened by any client. At present this ** function is called when: ** ** * A write transaction that called lsmTreeDiscardOld() is committed, and ** * Whenever the working snapshot is updated (i.e. lsmFinishWork()). */ static int dbSetReadLock(lsm_db *db, i64 iLsm, u32 iShm){ int rc = LSM_OK; ShmHeader *pShm = db->pShmhdr; int i; /* Check if there is already a slot containing the required values. */ for(i=0; i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId==iLsm && p->iTreeId==iShm ) return LSM_OK; } /* Iterate through all read-lock slots, attempting to take a write-lock ** on each of them. If a write-lock succeeds, populate the locked slot ** with the required values and break out of the loop. */ for(i=0; rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_EXCL, 0); if( rc==LSM_BUSY ){ rc = LSM_OK; }else{ ShmReader *p = &pShm->aReader[i]; p->iLsmId = iLsm; p->iTreeId = iShm; lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_UNLOCK, 0); break; } } return rc; } /* ** Release the read-lock currently held by connection db. */ int dbReleaseReadlock(lsm_db *db){ int rc = LSM_OK; if( db->iReader>=0 ){ rc = lsmShmLock(db, LSM_LOCK_READER(db->iReader), LSM_LOCK_UNLOCK, 0); db->iReader = -1; } db->bRoTrans = 0; return rc; } /* ** Argument bFlush is true if the contents of the in-memory tree has just ** been flushed to disk. The significance of this is that once the snapshot ** created to hold the updated state of the database is synced to disk, log ** file space can be recycled. */ void lsmFinishWork(lsm_db *pDb, int bFlush, int *pRc){ int rc = *pRc; assert( rc!=0 || pDb->pWorker ); if( pDb->pWorker ){ /* If no error has occurred, serialize the worker snapshot and write ** it to shared memory. */ if( rc==LSM_OK ){ rc = lsmSaveWorker(pDb, bFlush); } /* Assuming no error has occurred, update a read lock slot with the ** new snapshot id (see comments above function dbSetReadLock()). */ if( rc==LSM_OK ){ if( pDb->iReader<0 ){ rc = lsmTreeLoadHeader(pDb, 0); } if( rc==LSM_OK ){ rc = dbSetReadLock(pDb, pDb->pWorker->iId, pDb->treehdr.iUsedShmid); } } /* Free the snapshot object. */ lsmFreeSnapshot(pDb->pEnv, pDb->pWorker); pDb->pWorker = 0; } lsmShmLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_UNLOCK, 0); *pRc = rc; } /* ** Called when recovery is finished. */ int lsmFinishRecovery(lsm_db *pDb){ lsmTreeEndTransaction(pDb, 1); return LSM_OK; } /* ** Check if the currently configured compression functions ** (LSM_CONFIG_SET_COMPRESSION) are compatible with a database that has its ** compression id set to iReq. Compression routines are compatible if iReq ** is zero (indicating the database is empty), or if it is equal to the ** compression id of the configured compression routines. ** ** If the check shows that the current compression are incompatible and there ** is a compression factory registered, give it a chance to install new ** compression routines. ** ** If, after any registered factory is invoked, the compression functions ** are still incompatible, return LSM_MISMATCH. Otherwise, LSM_OK. */ int lsmCheckCompressionId(lsm_db *pDb, u32 iReq){ if( iReq!=LSM_COMPRESSION_EMPTY && pDb->compress.iId!=iReq ){ if( pDb->factory.xFactory ){ pDb->bInFactory = 1; pDb->factory.xFactory(pDb->factory.pCtx, pDb, iReq); pDb->bInFactory = 0; } if( pDb->compress.iId!=iReq ){ /* Incompatible */ return LSM_MISMATCH; } } /* Compatible */ return LSM_OK; } /* ** Begin a read transaction. This function is a no-op if the connection ** passed as the only argument already has an open read transaction. */ int lsmBeginReadTrans(lsm_db *pDb){ const int MAX_READLOCK_ATTEMPTS = 10; const int nMaxAttempt = (pDb->bRoTrans ? 1 : MAX_READLOCK_ATTEMPTS); int rc = LSM_OK; /* Return code */ int iAttempt = 0; assert( pDb->pWorker==0 ); while( rc==LSM_OK && pDb->iReader<0 && (iAttempt++)<nMaxAttempt ){ int iTreehdr = 0; int iSnap = 0; assert( pDb->pCsr==0 && pDb->nTransOpen==0 ); /* Load the in-memory tree header. */ rc = lsmTreeLoadHeader(pDb, &iTreehdr); /* Load the database snapshot */ if( rc==LSM_OK ){ if( lsmCheckpointClientCacheOk(pDb)==0 ){ lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; lsmMCursorFreeCache(pDb); rc = lsmCheckpointLoad(pDb, &iSnap); }else{ iSnap = 1; } } /* Take a read-lock on the tree and snapshot just loaded. Then check |
︙ | ︙ | |||
971 972 973 974 975 976 977 978 | ** lsm_sorted.c is changed to work directly from the serialized ** version of the snapshot. */ if( pDb->pClient==0 ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot,&pDb->pClient); } assert( (rc==LSM_OK)==(pDb->pClient!=0) ); assert( pDb->iReader>=0 ); }else{ | > > > > > > | > | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 | ** lsm_sorted.c is changed to work directly from the serialized ** version of the snapshot. */ if( pDb->pClient==0 ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot,&pDb->pClient); } assert( (rc==LSM_OK)==(pDb->pClient!=0) ); assert( pDb->iReader>=0 ); /* Check that the client has the right compression hooks loaded. ** If not, set rc to LSM_MISMATCH. */ if( rc==LSM_OK ){ rc = lsmCheckCompressionId(pDb, pDb->pClient->iCmpId); } }else{ rc = dbReleaseReadlock(pDb); } } if( rc==LSM_BUSY ){ rc = LSM_OK; } } #if 0 if( rc==LSM_OK && pDb->pClient ){ fprintf(stderr, |
︙ | ︙ | |||
996 997 998 999 1000 1001 1002 | #endif } if( rc==LSM_OK ){ rc = lsmShmCacheChunks(pDb, pDb->treehdr.nChunk); } if( rc!=LSM_OK ){ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | < | | | | | | < < | | > > > > | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 | #endif } if( rc==LSM_OK ){ rc = lsmShmCacheChunks(pDb, pDb->treehdr.nChunk); } if( rc!=LSM_OK ){ dbReleaseReadlock(pDb); } if( pDb->pClient==0 && rc==LSM_OK ) rc = LSM_BUSY; return rc; } /* ** This function is used by a read-write connection to determine if there ** are currently one or more read-only transactions open on the database ** (in this context a read-only transaction is one opened by a read-only ** connection on a non-live database). ** ** If no error occurs, LSM_OK is returned and *pbExists is set to true if ** some other connection has a read-only transaction open, or false ** otherwise. If an error occurs an LSM error code is returned and the final ** value of *pbExist is undefined. */ int lsmDetectRoTrans(lsm_db *db, int *pbExist){ int rc; /* Only a read-write connection may use this function. */ assert( db->bReadonly==0 ); rc = lsmShmTestLock(db, LSM_LOCK_ROTRANS, 1, LSM_LOCK_EXCL); if( rc==LSM_BUSY ){ *pbExist = 1; rc = LSM_OK; }else{ *pbExist = 0; } return rc; } /* ** db is a read-only database handle in the disconnected state. This function ** attempts to open a read-transaction on the database. This may involve ** connecting to the database system (opening shared memory etc.). */ int lsmBeginRoTrans(lsm_db *db){ int rc = LSM_OK; assert( db->bReadonly && db->pShmhdr==0 ); assert( db->iReader<0 ); if( db->bRoTrans==0 ){ /* Attempt a shared-lock on DMS1. */ rc = lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_SHARED, 0); if( rc!=LSM_OK ) return rc; rc = lsmShmTestLock( db, LSM_LOCK_RWCLIENT(0), LSM_LOCK_NREADER, LSM_LOCK_SHARED ); if( rc==LSM_OK ){ /* System is not live. Take a SHARED lock on the ROTRANS byte and ** release DMS1. Locking ROTRANS tells all read-write clients that they ** may not recycle any disk space from within the database or log files, ** as a read-only client may be using it. */ rc = lsmShmLock(db, LSM_LOCK_ROTRANS, LSM_LOCK_SHARED, 0); lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); if( rc==LSM_OK ){ db->bRoTrans = 1; rc = lsmShmCacheChunks(db, 1); if( rc==LSM_OK ){ db->pShmhdr = (ShmHeader *)db->apShm[0]; memset(db->pShmhdr, 0, sizeof(ShmHeader)); rc = lsmCheckpointRecover(db); if( rc==LSM_OK ){ rc = lsmLogRecover(db); } } } }else if( rc==LSM_BUSY ){ /* System is live! */ rc = lsmShmLock(db, LSM_LOCK_DMS3, LSM_LOCK_SHARED, 0); lsmShmLock(db, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0); if( rc==LSM_OK ){ rc = lsmShmCacheChunks(db, 1); if( rc==LSM_OK ){ db->pShmhdr = (ShmHeader *)db->apShm[0]; } } } if( rc==LSM_OK ){ rc = lsmBeginReadTrans(db); } } return rc; } /* ** Close the currently open read transaction. */ void lsmFinishReadTrans(lsm_db *pDb){ /* Worker connections should not be closing read transactions. And ** read transactions should only be closed after all cursors and write ** transactions have been closed. Finally pClient should be non-NULL ** only iff pDb->iReader>=0. */ assert( pDb->pWorker==0 ); assert( pDb->pCsr==0 && pDb->nTransOpen==0 ); if( pDb->bRoTrans ){ int i; for(i=0; i<pDb->nShm; i++){ lsmFree(pDb->pEnv, pDb->apShm[i]); } lsmFree(pDb->pEnv, pDb->apShm); pDb->apShm = 0; pDb->nShm = 0; pDb->pShmhdr = 0; lsmShmLock(pDb, LSM_LOCK_ROTRANS, LSM_LOCK_UNLOCK, 0); } dbReleaseReadlock(pDb); } /* ** Open a write transaction. */ int lsmBeginWriteTrans(lsm_db *pDb){ int rc = LSM_OK; /* Return code */ ShmHeader *pShm = pDb->pShmhdr; /* Shared memory header */ assert( pDb->nTransOpen==0 ); assert( pDb->bDiscardOld==0 ); assert( pDb->bReadonly==0 ); /* If there is no read-transaction open, open one now. */ if( pDb->iReader<0 ){ rc = lsmBeginReadTrans(pDb); } /* Attempt to take the WRITER lock */ if( rc==LSM_OK ){ rc = lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_EXCL, 0); } /* If the previous writer failed mid-transaction, run emergency rollback. */ |
︙ | ︙ | |||
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 | ** WRITER lock and return an error code. */ if( rc==LSM_OK ){ TreeHeader *p = &pDb->treehdr; pShm->bWriter = 1; p->root.iTransId++; if( lsmTreeHasOld(pDb) && p->iOldLog==pDb->pClient->iLogOff ){ lsmTreeDiscardOld(pDb); } }else{ lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_UNLOCK, 0); if( pDb->pCsr==0 ) lsmFinishReadTrans(pDb); } return rc; } | > | 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 | ** WRITER lock and return an error code. */ if( rc==LSM_OK ){ TreeHeader *p = &pDb->treehdr; pShm->bWriter = 1; p->root.iTransId++; if( lsmTreeHasOld(pDb) && p->iOldLog==pDb->pClient->iLogOff ){ lsmTreeDiscardOld(pDb); pDb->bDiscardOld = 1; } }else{ lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_UNLOCK, 0); if( pDb->pCsr==0 ) lsmFinishReadTrans(pDb); } return rc; } |
︙ | ︙ | |||
1105 1106 1107 1108 1109 1110 1111 | lsmLogEnd(pDb, bCommit); if( rc==LSM_OK && bCommit && lsmTreeSize(pDb)>pDb->nTreeLimit ){ bFlush = 1; lsmTreeMakeOld(pDb); } lsmTreeEndTransaction(pDb, bCommit); | > | > > > > > | 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 | lsmLogEnd(pDb, bCommit); if( rc==LSM_OK && bCommit && lsmTreeSize(pDb)>pDb->nTreeLimit ){ bFlush = 1; lsmTreeMakeOld(pDb); } lsmTreeEndTransaction(pDb, bCommit); if( rc==LSM_OK ){ if( bFlush && pDb->bAutowork ){ rc = lsmSortedAutoWork(pDb, 1); }else if( bCommit && pDb->bDiscardOld ){ rc = dbSetReadLock(pDb, pDb->pClient->iId, pDb->treehdr.iUsedShmid); } } pDb->bDiscardOld = 0; lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_UNLOCK, 0); if( bFlush && pDb->bAutowork==0 && pDb->xWork ){ pDb->xWork(pDb, pDb->pWorkCtx); } return rc; } |
︙ | ︙ | |||
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 | int lsmReadlock(lsm_db *db, i64 iLsm, u32 iShmMin, u32 iShmMax){ int rc = LSM_OK; ShmHeader *pShm = db->pShmhdr; int i; assert( db->iReader<0 ); assert( shm_sequence_ge(iShmMax, iShmMin) ); /* Search for an exact match. */ for(i=0; db->iReader<0 && rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId==iLsm && p->iTreeId==iShmMax ){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_SHARED, 0); if( rc==LSM_OK && p->iLsmId==iLsm && p->iTreeId==iShmMax ){ | > > > > > > | 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 | int lsmReadlock(lsm_db *db, i64 iLsm, u32 iShmMin, u32 iShmMax){ int rc = LSM_OK; ShmHeader *pShm = db->pShmhdr; int i; assert( db->iReader<0 ); assert( shm_sequence_ge(iShmMax, iShmMin) ); /* This is a no-op if the read-only transaction flag is set. */ if( db->bRoTrans ){ db->iReader = 0; return LSM_OK; } /* Search for an exact match. */ for(i=0; db->iReader<0 && rc==LSM_OK && i<LSM_LOCK_NREADER; i++){ ShmReader *p = &pShm->aReader[i]; if( p->iLsmId==iLsm && p->iTreeId==iShmMax ){ rc = lsmShmLock(db, LSM_LOCK_READER(i), LSM_LOCK_SHARED, 0); if( rc==LSM_OK && p->iLsmId==iLsm && p->iTreeId==iShmMax ){ |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 | if( db->pClient && db->pClient->iId<=iLsmId ){ *pbInUse = 1; return LSM_OK; } return isInUse(db, iLsmId, 0, pbInUse); } | < < < < < < < < < < < < | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 | if( db->pClient && db->pClient->iId<=iLsmId ){ *pbInUse = 1; return LSM_OK; } return isInUse(db, iLsmId, 0, pbInUse); } /* ** This function may only be called after a successful call to ** lsmDbDatabaseConnect(). It returns true if the connection is in ** multi-process mode, or false otherwise. */ int lsmDbMultiProc(lsm_db *pDb){ return pDb->pDatabase && pDb->pDatabase->bMultiProc; |
︙ | ︙ | |||
1325 1326 1327 1328 1329 1330 1331 | ** Ensure that database connection db has cached pointers to at least the ** first nChunk chunks of shared memory. */ int lsmShmCacheChunks(lsm_db *db, int nChunk){ int rc = LSM_OK; if( nChunk>db->nShm ){ static const int NINCR = 16; | < > > > > > > > > | 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 | ** Ensure that database connection db has cached pointers to at least the ** first nChunk chunks of shared memory. */ int lsmShmCacheChunks(lsm_db *db, int nChunk){ int rc = LSM_OK; if( nChunk>db->nShm ){ static const int NINCR = 16; Database *p = db->pDatabase; lsm_env *pEnv = db->pEnv; int nAlloc; int i; /* Ensure that the db->apShm[] array is large enough. If an attempt to ** allocate memory fails, return LSM_NOMEM immediately. The apShm[] array ** is always extended in multiples of 16 entries - so the actual allocated ** size can be inferred from nShm. */ nAlloc = ((db->nShm + NINCR - 1) / NINCR) * NINCR; while( nChunk>=nAlloc ){ void **apShm; nAlloc += NINCR; apShm = lsmRealloc(pEnv, db->apShm, sizeof(void*)*nAlloc); if( !apShm ) return LSM_NOMEM_BKPT; db->apShm = apShm; } if( db->bRoTrans ){ for(i=db->nShm; rc==LSM_OK && i<nChunk; i++){ db->apShm[i] = lsmMallocZeroRc(pEnv, LSM_SHM_CHUNK_SIZE, &rc); db->nShm++; } }else{ /* Enter the client mutex */ lsmMutexEnter(pEnv, p->pClientMutex); /* Extend the Database objects apShmChunk[] array if necessary. Using the ** same pattern as for the lsm_db.apShm[] array above. */ nAlloc = ((p->nShmChunk + NINCR - 1) / NINCR) * NINCR; |
︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 | db->nShm++; } } /* Release the client mutex */ lsmMutexLeave(pEnv, p->pClientMutex); } return rc; } static int lockSharedFile(lsm_env *pEnv, Database *p, int iLock, int eOp){ int rc = LSM_OK; if( p->bMultiProc ){ rc = lsmEnvLock(pEnv, p->pFile, iLock, eOp); } return rc; } /* ** Attempt to obtain the lock identified by the iLock and bExcl parameters. ** If successful, return LSM_OK. If the lock cannot be obtained because ** there exists some other conflicting lock, return LSM_BUSY. If some other ** error occurs, return an LSM error code. ** ** Parameter iLock must be one of LSM_LOCK_WRITER, WORKER or CHECKPOINTER, ** or else a value returned by the LSM_LOCK_READER macro. */ int lsmShmLock( lsm_db *db, int iLock, int eOp, /* One of LSM_LOCK_UNLOCK, SHARED or EXCL */ int bBlock /* True for a blocking lock */ ){ lsm_db *pIter; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 | db->nShm++; } } /* Release the client mutex */ lsmMutexLeave(pEnv, p->pClientMutex); } } return rc; } static int lockSharedFile(lsm_env *pEnv, Database *p, int iLock, int eOp){ int rc = LSM_OK; if( p->bMultiProc ){ rc = lsmEnvLock(pEnv, p->pFile, iLock, eOp); } return rc; } /* ** Test if it would be possible for connection db to obtain a lock of type ** eType on the nLock locks starting at iLock. If so, return LSM_OK. If it ** would not be possible to obtain the lock due to a lock held by another ** connection, return LSM_BUSY. If an IO or other error occurs (i.e. in the ** lsm_env.xTestLock function), return some other LSM error code. ** ** Note that this function never actually locks the database - it merely ** queries the system to see if there exists a lock that would prevent ** it from doing so. */ int lsmShmTestLock( lsm_db *db, int iLock, int nLock, int eOp ){ int rc = LSM_OK; lsm_db *pIter; Database *p = db->pDatabase; int i; u64 mask = 0; for(i=iLock; i<(iLock+nLock); i++){ mask |= ((u64)1 << (iLock-1)); if( eOp==LSM_LOCK_EXCL ) mask |= ((u64)1 << (iLock+32-1)); } lsmMutexEnter(db->pEnv, p->pClientMutex); for(pIter=p->pConn; pIter; pIter=pIter->pNext){ if( pIter!=db && (pIter->mLock & mask) ) break; } if( pIter ){ rc = LSM_BUSY; }else if( p->bMultiProc ){ rc = lsmEnvTestLock(db->pEnv, p->pFile, iLock, nLock, eOp); } lsmMutexLeave(db->pEnv, p->pClientMutex); return rc; } /* ** Attempt to obtain the lock identified by the iLock and bExcl parameters. ** If successful, return LSM_OK. If the lock cannot be obtained because ** there exists some other conflicting lock, return LSM_BUSY. If some other ** error occurs, return an LSM error code. ** ** Parameter iLock must be one of LSM_LOCK_WRITER, WORKER or CHECKPOINTER, ** or else a value returned by the LSM_LOCK_READER macro. */ int lsmShmLock( lsm_db *db, int iLock, int eOp, /* One of LSM_LOCK_UNLOCK, SHARED or EXCL */ int bBlock /* True for a blocking lock */ ){ lsm_db *pIter; const u64 me = ((u64)1 << (iLock-1)); const u64 ms = ((u64)1 << (iLock+32-1)); int rc = LSM_OK; Database *p = db->pDatabase; assert( eOp!=LSM_LOCK_EXCL || db->bReadonly==0 ); assert( iLock>=1 && iLock<=LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT-1) ); assert( LSM_LOCK_RWCLIENT(LSM_LOCK_NRWCLIENT-1)<=32 ); assert( eOp==LSM_LOCK_UNLOCK || eOp==LSM_LOCK_SHARED || eOp==LSM_LOCK_EXCL ); /* Check for a no-op. Proceed only if this is not one of those. */ if( (eOp==LSM_LOCK_UNLOCK && (db->mLock & (me|ms))!=0) || (eOp==LSM_LOCK_SHARED && (db->mLock & (me|ms))!=ms) || (eOp==LSM_LOCK_EXCL && (db->mLock & me)==0) ){ |
︙ | ︙ | |||
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 | case LSM_LOCK_SHARED: if( nExcl ){ rc = LSM_BUSY; }else{ if( nShared==0 ){ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_SHARED); } db->mLock |= ms; db->mLock &= ~me; } break; default: assert( eOp==LSM_LOCK_EXCL ); if( nExcl || nShared ){ rc = LSM_BUSY; }else{ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL); db->mLock |= (me|ms); } break; } lsmMutexLeave(db->pEnv, p->pClientMutex); } return rc; } #ifdef LSM_DEBUG int shmLockType(lsm_db *db, int iLock){ | > > > > | | | 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 | case LSM_LOCK_SHARED: if( nExcl ){ rc = LSM_BUSY; }else{ if( nShared==0 ){ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_SHARED); } if( rc==LSM_OK ){ db->mLock |= ms; db->mLock &= ~me; } } break; default: assert( eOp==LSM_LOCK_EXCL ); if( nExcl || nShared ){ rc = LSM_BUSY; }else{ rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL); if( rc==LSM_OK ){ db->mLock |= (me|ms); } } break; } lsmMutexLeave(db->pEnv, p->pClientMutex); } return rc; } #ifdef LSM_DEBUG int shmLockType(lsm_db *db, int iLock){ const u64 me = ((u64)1 << (iLock-1)); const u64 ms = ((u64)1 << (iLock+32-1)); if( db->mLock & me ) return LSM_LOCK_EXCL; if( db->mLock & ms ) return LSM_LOCK_SHARED; return LSM_LOCK_UNLOCK; } /* |
︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 | } #endif void lsmShmBarrier(lsm_db *db){ lsmEnvShmBarrier(db->pEnv); } | | | | | | | | 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 | } #endif void lsmShmBarrier(lsm_db *db){ lsmEnvShmBarrier(db->pEnv); } int lsm_checkpoint(lsm_db *pDb, int *pnKB){ int rc; /* Return code */ u32 nWrite = 0; /* Number of pages checkpointed */ /* Attempt the checkpoint. If successful, nWrite is set to the number of ** pages written between this and the previous checkpoint. */ rc = lsmCheckpointWrite(pDb, 0, &nWrite); /* If required, calculate the output variable (KB of data checkpointed). ** Set it to zero if an error occured. */ if( pnKB ){ int nKB = 0; if( rc==LSM_OK && nWrite ){ nKB = (((i64)nWrite * lsmFsPageSize(pDb->pFS)) + 1023) / 1024; } *pnKB = nKB; } return rc; } |
Changes to src/lsm_sorted.c.
︙ | ︙ | |||
2241 2242 2243 2244 2245 2246 2247 | pCsr->aTree = 0; pCsr->pSystemVal = 0; pCsr->apTreeCsr[0] = 0; pCsr->apTreeCsr[1] = 0; pCsr->pBtCsr = 0; } | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 | pCsr->aTree = 0; pCsr->pSystemVal = 0; pCsr->apTreeCsr[0] = 0; pCsr->apTreeCsr[1] = 0; pCsr->pBtCsr = 0; } void lsmMCursorFreeCache(lsm_db *pDb){ MultiCursor *p; MultiCursor *pNext; for(p=pDb->pCsrCache; p; p=pNext){ pNext = p->pNext; lsmMCursorClose(p, 0); } pDb->pCsrCache = 0; } /* ** Close the cursor passed as the first argument. ** ** If the bCache parameter is true, then shift the cursor to the pCsrCache ** list for possible reuse instead of actually deleting it. */ void lsmMCursorClose(MultiCursor *pCsr, int bCache){ if( pCsr ){ lsm_db *pDb = pCsr->pDb; MultiCursor **pp; /* Iterator variable */ /* The cursor may or may not be currently part of the linked list ** starting at lsm_db.pCsr. If it is, extract it. */ for(pp=&pDb->pCsr; *pp; pp=&((*pp)->pNext)){ if( *pp==pCsr ){ *pp = pCsr->pNext; break; } } if( bCache ){ int i; /* Used to iterate through segment-pointers */ /* Release any page references held by this cursor. */ assert( !pCsr->pBtCsr ); for(i=0; i<pCsr->nPtr; i++){ SegmentPtr *pPtr = &pCsr->aPtr[i]; lsmFsPageRelease(pPtr->pPg); pPtr->pPg = 0; } /* Reset the tree cursors */ lsmTreeCursorReset(pCsr->apTreeCsr[0]); lsmTreeCursorReset(pCsr->apTreeCsr[1]); /* Add the cursor to the pCsrCache list */ pCsr->pNext = pDb->pCsrCache; pDb->pCsrCache = pCsr; }else{ /* Free the allocation used to cache the current key, if any. */ sortedBlobFree(&pCsr->key); sortedBlobFree(&pCsr->val); /* Free the component cursors */ mcursorFreeComponents(pCsr); /* Free the cursor structure itself */ lsmFree(pDb->pEnv, pCsr); } } } #define TREE_NONE 0 #define TREE_OLD 1 #define TREE_BOTH 2 /* ** Parameter eTree is one of TREE_OLD or TREE_BOTH. |
︙ | ︙ | |||
2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 | pCsr->flags |= CURSOR_FLUSH_FREELIST; pCsr->pSystemVal = lsmMallocRc(pCsr->pDb->pEnv, 4 + 8, &rc); return rc; } /* ** Allocate and return a new database cursor. */ int lsmMCursorNew( lsm_db *pDb, /* Database handle */ MultiCursor **ppCsr /* OUT: Allocated cursor */ ){ MultiCursor *pCsr = 0; int rc = LSM_OK; pCsr = multiCursorNew(pDb, &rc); if( rc==LSM_OK ) rc = multiCursorInit(pCsr, pDb->pClient); | > > > > > > > > > > > > > > > > > > > > > > > > | > | | 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 | pCsr->flags |= CURSOR_FLUSH_FREELIST; pCsr->pSystemVal = lsmMallocRc(pCsr->pDb->pEnv, 4 + 8, &rc); return rc; } /* ** Allocate and return a new database cursor. ** ** This method should only be called to allocate user cursors. As it may ** recycle a cursor from lsm_db.pCsrCache. */ int lsmMCursorNew( lsm_db *pDb, /* Database handle */ MultiCursor **ppCsr /* OUT: Allocated cursor */ ){ MultiCursor *pCsr = 0; int rc = LSM_OK; if( pDb->pCsrCache ){ int bOld; /* True if there is an old in-memory tree */ /* Remove a cursor from the pCsrCache list and add it to the open list. */ pCsr = pDb->pCsrCache; pDb->pCsrCache = pCsr->pNext; pCsr->pNext = pDb->pCsr; pDb->pCsr = pCsr; /* The cursor can almost be used as is, except that the old in-memory ** tree cursor may be present and not required, or required and not ** present. Fix this if required. */ bOld = (lsmTreeHasOld(pDb) && pDb->treehdr.iOldLog!=pDb->pClient->iLogOff); if( !bOld && pCsr->apTreeCsr[1] ){ lsmTreeCursorDestroy(pCsr->apTreeCsr[1]); pCsr->apTreeCsr[1] = 0; }else if( bOld && !pCsr->apTreeCsr[1] ){ rc = lsmTreeCursorNew(pDb, 1, &pCsr->apTreeCsr[1]); } }else{ pCsr = multiCursorNew(pDb, &rc); if( rc==LSM_OK ) rc = multiCursorInit(pCsr, pDb->pClient); } if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); pCsr = 0; } assert( (rc==LSM_OK)==(pCsr!=0) ); *ppCsr = pCsr; return rc; } |
︙ | ︙ | |||
2553 2554 2555 2556 2557 2558 2559 | iSnap = (i64)lsmGetU64((u8 *)pVal); if( x(pCtx, iBlk, iSnap) ) break; rc = multiCursorAdvance(pCsr, !bReverse); } } } | | | 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 | iSnap = (i64)lsmGetU64((u8 *)pVal); if( x(pCtx, iBlk, iSnap) ) break; rc = multiCursorAdvance(pCsr, !bReverse); } } } lsmMCursorClose(pCsr, 0); if( pSnap!=pDb->pWorker ){ lsmFreeSnapshot(pDb->pEnv, pSnap); } return rc; } |
︙ | ︙ | |||
2596 2597 2598 2599 2600 2601 2602 | if( *ppVal ){ memcpy(*ppVal, pVal, nVal); *pnVal = nVal; } } } | | | 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 | if( *ppVal ){ memcpy(*ppVal, pVal, nVal); *pnVal = nVal; } } } lsmMCursorClose(pCsr, 0); } return rc; } static int multiCursorAllocTree(MultiCursor *pCsr){ int rc = LSM_OK; |
︙ | ︙ | |||
4031 4032 4033 4034 4035 4036 4037 | }else{ btreeCursorSplitkey(pCsr->pBtCsr, &pMerge->splitkey); } pMerge->iOutputOff = -1; } | | | 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 | }else{ btreeCursorSplitkey(pCsr->pBtCsr, &pMerge->splitkey); } pMerge->iOutputOff = -1; } lsmMCursorClose(pCsr, 0); /* Persist and release the output page. */ if( rc==LSM_OK ) rc = mergeWorkerPersistAndRelease(pMW); if( rc==LSM_OK ) rc = mergeWorkerBtreeIndirect(pMW); if( rc==LSM_OK ) rc = mergeWorkerFinishHierarchy(pMW); if( rc==LSM_OK ) rc = mergeWorkerAddPadding(pMW); lsmFsFlushWaiting(pMW->pDb->pFS, &rc); |
︙ | ︙ | |||
4285 4286 4287 4288 4289 4290 4291 | ** markers present in the in-memory tree. */ if( pNext==0 ){ multiCursorIgnoreDelete(pCsr); } } if( rc!=LSM_OK ){ | | | 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 | ** markers present in the in-memory tree. */ if( pNext==0 ){ multiCursorIgnoreDelete(pCsr); } } if( rc!=LSM_OK ){ lsmMCursorClose(pCsr, 0); }else{ Pgno iLeftPtr = 0; Merge merge; /* Merge object used to create new level */ MergeWorker mergeworker; /* MergeWorker object for the same purpose */ memset(&merge, 0, sizeof(Merge)); memset(&mergeworker, 0, sizeof(MergeWorker)); |
︙ | ︙ | |||
4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 | rc = lsmFsMoveBlock(pDb->pFS, &pLvl->lhs, iTo, iFrom); if( rc==LSM_OK ){ if( p->redirect.a==0 ){ int nByte = sizeof(struct RedirectEntry) * LSM_MAX_BLOCK_REDIRECTS; p->redirect.a = lsmMallocZeroRc(pDb->pEnv, nByte, &rc); } if( rc==LSM_OK ){ memmove(&p->redirect.a[1], &p->redirect.a[0], sizeof(struct RedirectEntry) * p->redirect.n ); p->redirect.a[0].iFrom = iFrom; p->redirect.a[0].iTo = iTo; p->redirect.n++; rc = lsmBlockFree(pDb, iFrom); *pnWrite = lsmFsBlockSize(pDb->pFS) / lsmFsPageSize(pDb->pFS); pLvl->lhs.pRedirect = &p->redirect; } } | > > > > > > > > > > > > > | 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 | rc = lsmFsMoveBlock(pDb->pFS, &pLvl->lhs, iTo, iFrom); if( rc==LSM_OK ){ if( p->redirect.a==0 ){ int nByte = sizeof(struct RedirectEntry) * LSM_MAX_BLOCK_REDIRECTS; p->redirect.a = lsmMallocZeroRc(pDb->pEnv, nByte, &rc); } if( rc==LSM_OK ){ /* Check if the block just moved was already redirected. */ int i; for(i=0; i<p->redirect.n; i++){ if( p->redirect.a[i].iTo==iFrom ) break; } if( i==p->redirect.n ){ /* Block iFrom was not already redirected. Add a new array entry. */ memmove(&p->redirect.a[1], &p->redirect.a[0], sizeof(struct RedirectEntry) * p->redirect.n ); p->redirect.a[0].iFrom = iFrom; p->redirect.a[0].iTo = iTo; p->redirect.n++; }else{ /* Block iFrom was already redirected. Overwrite existing entry. */ p->redirect.a[i].iTo = iTo; } rc = lsmBlockFree(pDb, iFrom); *pnWrite = lsmFsBlockSize(pDb->pFS) / lsmFsPageSize(pDb->pFS); pLvl->lhs.pRedirect = &p->redirect; } } |
︙ | ︙ | |||
5196 5197 5198 5199 5200 5201 5202 | static int doLsmWork(lsm_db *pDb, int nMerge, int nPage, int *pnWrite){ int rc = LSM_OK; /* Return code */ int nWrite = 0; /* Number of pages written */ assert( nMerge>=1 ); | | > > | | | > > > > > > > > > > > > | | > > > > > > | 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 | static int doLsmWork(lsm_db *pDb, int nMerge, int nPage, int *pnWrite){ int rc = LSM_OK; /* Return code */ int nWrite = 0; /* Number of pages written */ assert( nMerge>=1 ); if( nPage!=0 ){ int bCkpt = 0; do { int nThis = 0; int nReq = (nPage>=0) ? (nPage-nWrite) : ((int)0x7FFFFFFF); bCkpt = 0; rc = doLsmSingleWork(pDb, 0, nMerge, nReq, &nThis, &bCkpt); nWrite += nThis; if( rc==LSM_OK && bCkpt ){ rc = lsm_checkpoint(pDb, 0); } }while( rc==LSM_OK && bCkpt && (nWrite<nPage || nPage<0) ); } if( pnWrite ){ if( rc==LSM_OK ){ *pnWrite = nWrite; }else{ *pnWrite = 0; } } return rc; } /* ** Perform work to merge database segments together. */ int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite){ int rc; /* Return code */ int nPgsz; /* Nominal page size in bytes */ int nPage; /* Equivalent of nKB in pages */ int nWrite = 0; /* Number of pages written */ /* This function may not be called if pDb has an open read or write ** transaction. Return LSM_MISUSE if an application attempts this. */ if( pDb->nTransOpen || pDb->pCsr ) return LSM_MISUSE_BKPT; if( nMerge<=0 ) nMerge = pDb->nMerge; /* Convert from KB to pages */ nPgsz = lsmFsPageSize(pDb->pFS); if( nKB>=0 ){ nPage = ((i64)nKB * 1024 + nPgsz - 1) / nPgsz; }else{ nPage = -1; } rc = doLsmWork(pDb, nMerge, nPage, &nWrite); if( pnWrite ){ /* Convert back from pages to KB */ *pnWrite = (int)(((i64)nWrite * 1024 + nPgsz - 1) / nPgsz); } return rc; } int lsm_flush(lsm_db *db){ int rc; if( db->nTransOpen>0 || db->pCsr ){ rc = LSM_MISUSE_BKPT; |
︙ | ︙ | |||
5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 | int nRemaining; /* Units of work to do before returning */ nRemaining = nUnit * nDepth; #ifdef LSM_LOG_WORK lsmLogMessage(pDb, rc, "lsmSortedAutoWork(): %d*%d = %d pages", nUnit, nDepth, nRemaining); #endif rc = doLsmWork(pDb, pDb->nMerge, nRemaining, 0); if( rc==LSM_BUSY ) rc = LSM_OK; if( bRestore && pDb->pCsr ){ lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; rc = lsmCheckpointLoad(pDb, 0); if( rc==LSM_OK ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot, &pDb->pClient); } if( rc==LSM_OK ){ | > > | 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 | int nRemaining; /* Units of work to do before returning */ nRemaining = nUnit * nDepth; #ifdef LSM_LOG_WORK lsmLogMessage(pDb, rc, "lsmSortedAutoWork(): %d*%d = %d pages", nUnit, nDepth, nRemaining); #endif assert( nRemaining>=0 ); rc = doLsmWork(pDb, pDb->nMerge, nRemaining, 0); if( rc==LSM_BUSY ) rc = LSM_OK; if( bRestore && pDb->pCsr ){ lsmMCursorFreeCache(pDb); lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; rc = lsmCheckpointLoad(pDb, 0); if( rc==LSM_OK ){ rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot, &pDb->pClient); } if( rc==LSM_OK ){ |
︙ | ︙ |
Changes to src/lsm_tree.c.
︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | ** discarded. */ static void intArrayTruncate(IntArray *p, int nVal){ p->nArray = nVal; } /* End of IntArray methods. ***********************************************************************/ /* ** The pointer passed as the first argument points to an interior node, ** not a leaf. This function returns the offset of the iCell'th child ** sub-tree of the node. */ static u32 getChildPtr(TreeNode *p, int iVersion, int iCell){ assert( iCell>=0 && iCell<=array_size(p->aiChildPtr) ); if( p->iV2 && p->iV2<=iVersion && iCell==p->iV2Child ) return p->iV2Ptr; return p->aiChildPtr[iCell]; } /* ** Given an offset within the *-shm file, return the associated chunk number. */ static int treeOffsetToChunk(u32 iOff){ assert( LSM_SHM_CHUNK_SIZE==(1<<15) ); return (int)(iOff>>15); } /* ** Return a pointer to the mapped memory location associated with *-shm ** file offset iPtr. */ static void *treeShmptr(lsm_db *pDb, u32 iPtr){ assert( (iPtr>>15)<pDb->nShm ); assert( pDb->apShm[iPtr>>15] ); | > > > > > > > > > > | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | ** discarded. */ static void intArrayTruncate(IntArray *p, int nVal){ p->nArray = nVal; } /* End of IntArray methods. ***********************************************************************/ static int treeKeycmp(void *p1, int n1, void *p2, int n2){ int res; res = memcmp(p1, p2, LSM_MIN(n1, n2)); if( res==0 ) res = (n1-n2); return res; } /* ** The pointer passed as the first argument points to an interior node, ** not a leaf. This function returns the offset of the iCell'th child ** sub-tree of the node. */ static u32 getChildPtr(TreeNode *p, int iVersion, int iCell){ assert( iCell>=0 && iCell<=array_size(p->aiChildPtr) ); if( p->iV2 && p->iV2<=iVersion && iCell==p->iV2Child ) return p->iV2Ptr; return p->aiChildPtr[iCell]; } /* ** Given an offset within the *-shm file, return the associated chunk number. */ static int treeOffsetToChunk(u32 iOff){ assert( LSM_SHM_CHUNK_SIZE==(1<<15) ); return (int)(iOff>>15); } #define treeShmptrUnsafe(pDb, iPtr) \ (&((u8*)((pDb)->apShm[(iPtr)>>15]))[(iPtr) & (LSM_SHM_CHUNK_SIZE-1)]) /* ** Return a pointer to the mapped memory location associated with *-shm ** file offset iPtr. */ static void *treeShmptr(lsm_db *pDb, u32 iPtr){ assert( (iPtr>>15)<pDb->nShm ); assert( pDb->apShm[iPtr>>15] ); return iPtr ? treeShmptrUnsafe(pDb, iPtr) : 0; } static ShmChunk * treeShmChunk(lsm_db *pDb, int iChunk){ return (ShmChunk *)(pDb->apShm[iChunk]); } static ShmChunk * treeShmChunkRc(lsm_db *pDb, int iChunk, int *pRc){ |
︙ | ︙ | |||
561 562 563 564 565 566 567 | } /* ** Return a pointer to the mapping of the TreeKey object that the cursor ** is pointing to. */ static TreeKey *csrGetKey(TreeCursor *pCsr, TreeBlob *pBlob, int *pRc){ | | > | > > > > | > | < | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | } /* ** Return a pointer to the mapping of the TreeKey object that the cursor ** is pointing to. */ static TreeKey *csrGetKey(TreeCursor *pCsr, TreeBlob *pBlob, int *pRc){ TreeKey *pRet; lsm_db *pDb = pCsr->pDb; u32 iPtr = pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]]; assert( iPtr ); pRet = treeShmptrUnsafe(pDb, iPtr); if( !(pRet->flags & LSM_CONTIGUOUS) ){ pRet = treeShmkey(pDb, iPtr, TKV_LOADVAL, pBlob, pRc); } return pRet; } /* ** Save the current position of tree cursor pCsr. */ int lsmTreeCursorSave(TreeCursor *pCsr){ |
︙ | ︙ | |||
717 718 719 720 721 722 723 724 725 726 727 728 729 730 | u32 *piPtr, void *pKey, int nKey, /* Key data */ void *pVal, int nVal, /* Value data (or nVal<0 for delete) */ int *pRc ){ TreeKey *p; u32 iPtr; int nRem; u8 *a; int n; /* Allocate space for the TreeKey structure itself */ *piPtr = iPtr = treeShmalloc(pDb, 1, sizeof(TreeKey), pRc); p = treeShmptr(pDb, iPtr); | > | 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 | u32 *piPtr, void *pKey, int nKey, /* Key data */ void *pVal, int nVal, /* Value data (or nVal<0 for delete) */ int *pRc ){ TreeKey *p; u32 iPtr; u32 iEnd; int nRem; u8 *a; int n; /* Allocate space for the TreeKey structure itself */ *piPtr = iPtr = treeShmalloc(pDb, 1, sizeof(TreeKey), pRc); p = treeShmptr(pDb, iPtr); |
︙ | ︙ | |||
750 751 752 753 754 755 756 757 758 759 760 761 762 763 | memcpy(aAlloc, &a[n-nRem], nAlloc); nRem -= nAlloc; } a = pVal; n = nRem = nVal; pVal = 0; } if( *pRc ) return 0; #if 0 printf("store: %d %s\n", (int)iPtr, (char *)pKey); #endif return p; } | > > > > > > > | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 | memcpy(aAlloc, &a[n-nRem], nAlloc); nRem -= nAlloc; } a = pVal; n = nRem = nVal; pVal = 0; } iEnd = iPtr + sizeof(TreeKey) + nKey + LSM_MAX(0, nVal); if( (iPtr & ~(LSM_SHM_CHUNK_SIZE-1))!=(iEnd & ~(LSM_SHM_CHUNK_SIZE-1)) ){ p->flags = 0; }else{ p->flags = LSM_CONTIGUOUS; } if( *pRc ) return 0; #if 0 printf("store: %d %s\n", (int)iPtr, (char *)pKey); #endif return p; } |
︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 1050 | } } return rc; } void lsmTreeMakeOld(lsm_db *pDb){ if( pDb->treehdr.iOldShmid==0 ){ | > > > > > > > > > | > > | 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | } } return rc; } void lsmTreeMakeOld(lsm_db *pDb){ /* A write transaction must be open. Otherwise the code below that ** assumes (pDb->pClient->iLogOff) is current may malfunction. ** ** Update: currently this assert fails due to lsm_flush(), which does ** not set nTransOpen. */ assert( /* pDb->nTransOpen>0 && */ pDb->iReader>=0 ); if( pDb->treehdr.iOldShmid==0 ){ pDb->treehdr.iOldLog = (pDb->treehdr.log.aRegion[2].iEnd << 1); pDb->treehdr.iOldLog |= (~(pDb->pClient->iLogOff) & (i64)0x0001); pDb->treehdr.oldcksum0 = pDb->treehdr.log.cksum0; pDb->treehdr.oldcksum1 = pDb->treehdr.log.cksum1; pDb->treehdr.iOldShmid = pDb->treehdr.iNextShmid-1; memcpy(&pDb->treehdr.oldroot, &pDb->treehdr.root, sizeof(TreeRoot)); pDb->treehdr.root.iTransId = 1; pDb->treehdr.root.iRoot = 0; |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | ** is initialized here - it will be copied into shared memory if log file ** recovery is successful. */ int lsmTreeInit(lsm_db *pDb){ ShmChunk *pOne; int rc = LSM_OK; pDb->treehdr.root.iTransId = 1; pDb->treehdr.iFirst = 1; pDb->treehdr.nChunk = 2; pDb->treehdr.iWrite = LSM_SHM_CHUNK_SIZE + LSM_SHM_CHUNK_HDR; pDb->treehdr.iNextShmid = 2; pDb->treehdr.iUsedShmid = 1; | > | 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | ** is initialized here - it will be copied into shared memory if log file ** recovery is successful. */ int lsmTreeInit(lsm_db *pDb){ ShmChunk *pOne; int rc = LSM_OK; memset(&pDb->treehdr, 0, sizeof(TreeHeader)); pDb->treehdr.root.iTransId = 1; pDb->treehdr.iFirst = 1; pDb->treehdr.nChunk = 2; pDb->treehdr.iWrite = LSM_SHM_CHUNK_SIZE + LSM_SHM_CHUNK_HDR; pDb->treehdr.iNextShmid = 2; pDb->treehdr.iUsedShmid = 1; |
︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 | int res; /* Result of seek operation on csr */ assert( nVal>=0 || pVal==0 ); assert_tree_looks_ok(LSM_OK, pTree); assert( flags==LSM_INSERT || flags==LSM_POINT_DELETE || flags==LSM_START_DELETE || flags==LSM_END_DELETE ); #if 0 dump_tree_contents(pDb, "before"); #endif if( p->iRoot ){ TreeKey *pRes; /* Key at end of seek operation */ treeCursorInit(pDb, 0, &csr); | > | 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 | int res; /* Result of seek operation on csr */ assert( nVal>=0 || pVal==0 ); assert_tree_looks_ok(LSM_OK, pTree); assert( flags==LSM_INSERT || flags==LSM_POINT_DELETE || flags==LSM_START_DELETE || flags==LSM_END_DELETE ); assert( (flags & LSM_CONTIGUOUS)==0 ); #if 0 dump_tree_contents(pDb, "before"); #endif if( p->iRoot ){ TreeKey *pRes; /* Key at end of seek operation */ treeCursorInit(pDb, 0, &csr); |
︙ | ︙ | |||
1481 1482 1483 1484 1485 1486 1487 | }else{ memset(&csr, 0, sizeof(TreeCursor)); } /* Allocate and populate a new key-value pair structure */ pTreeKey = newTreeKey(pDb, &iTreeKey, pKey, nKey, pVal, nVal, &rc); if( rc!=LSM_OK ) return rc; | > | | 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 | }else{ memset(&csr, 0, sizeof(TreeCursor)); } /* Allocate and populate a new key-value pair structure */ pTreeKey = newTreeKey(pDb, &iTreeKey, pKey, nKey, pVal, nVal, &rc); if( rc!=LSM_OK ) return rc; assert( pTreeKey->flags==0 || pTreeKey->flags==LSM_CONTIGUOUS ); pTreeKey->flags |= flags; if( p->iRoot==0 ){ /* The tree is completely empty. Add a new root node and install ** (pKey/nKey) as the middle entry. Even though it is a leaf at the ** moment, use newTreeNode() to allocate the node (i.e. allocate enough ** space for the fields used by interior nodes). This is because the ** treeInsert() routine may convert this node to an interior node. */ |
︙ | ︙ | |||
1767 1768 1769 1770 1771 1772 1773 | ){ int rc = LSM_OK; int bDone = 0; TreeRoot *p = &db->treehdr.root; TreeBlob blob = {0, 0}; /* The range must be sensible - that (key1 < key2). */ | | | 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 | ){ int rc = LSM_OK; int bDone = 0; TreeRoot *p = &db->treehdr.root; TreeBlob blob = {0, 0}; /* The range must be sensible - that (key1 < key2). */ assert( treeKeycmp(pKey1, nKey1, pKey2, nKey2)<0 ); assert( assert_delete_ranges_match(db) ); #if 0 static int nCall = 0; printf("\n"); nCall++; printf("%d delete %s .. %s\n", nCall, (char *)pKey1, (char *)pKey2); |
︙ | ︙ | |||
1799 1800 1801 1802 1803 1804 1805 | /* If there is no such entry, or if it is greater than pKey2, then the ** tree now contains no keys in the range being deleted. In this case ** break out of the loop. */ bDone = 1; if( lsmTreeCursorValid(&csr) ){ lsmTreeCursorKey(&csr, 0, &pDel, &nDel); | | | 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 | /* If there is no such entry, or if it is greater than pKey2, then the ** tree now contains no keys in the range being deleted. In this case ** break out of the loop. */ bDone = 1; if( lsmTreeCursorValid(&csr) ){ lsmTreeCursorKey(&csr, 0, &pDel, &nDel); if( treeKeycmp(pDel, nDel, pKey2, nKey2)<0 ) bDone = 0; } if( bDone==0 ){ if( csr.iNode==(p->nHeight-1) ){ /* The element to delete already lies on a leaf node */ rc = treeDeleteEntry(db, &csr, 0); }else{ |
︙ | ︙ | |||
1916 1917 1918 1919 1920 1921 1922 | static int treeCsrCompare(TreeCursor *pCsr, void *pKey, int nKey){ TreeKey *p; int cmp = 0; int rc = LSM_OK; assert( pCsr->iNode>=0 ); p = csrGetKey(pCsr, &pCsr->blob, &rc); if( p ){ | | | 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 | static int treeCsrCompare(TreeCursor *pCsr, void *pKey, int nKey){ TreeKey *p; int cmp = 0; int rc = LSM_OK; assert( pCsr->iNode>=0 ); p = csrGetKey(pCsr, &pCsr->blob, &rc); if( p ){ cmp = treeKeycmp(TKV_KEY(p), p->nKey, pKey, nKey); } return cmp; } #endif /* |
︙ | ︙ | |||
1941 1942 1943 1944 1945 1946 1947 | ** ** * If the tree is empty, leave the cursor at EOF and set *pRes to -1. */ int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes){ int rc = LSM_OK; /* Return code */ lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; | < < > | > > > | | > > > > < < < > | > > | 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 | ** ** * If the tree is empty, leave the cursor at EOF and set *pRes to -1. */ int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes){ int rc = LSM_OK; /* Return code */ lsm_db *pDb = pCsr->pDb; TreeRoot *pRoot = pCsr->pRoot; u32 iNodePtr; /* Location of current node in search */ /* Discard any saved position data */ treeCursorRestore(pCsr, 0); iNodePtr = pRoot->iRoot; if( iNodePtr==0 ){ /* Either an error occurred or the tree is completely empty. */ assert( rc!=LSM_OK || pRoot->iRoot==0 ); *pRes = -1; pCsr->iNode = -1; }else{ TreeBlob b = {0, 0}; int res = 0; /* Result of comparison function */ int iNode = -1; while( iNodePtr ){ TreeNode *pNode; /* Node at location iNodePtr */ int iTest; /* Index of second key to test (0 or 2) */ u32 iTreeKey; TreeKey *pTreeKey; /* Key to compare against */ pNode = (TreeNode *)treeShmptrUnsafe(pDb, iNodePtr); iNode++; pCsr->apTreeNode[iNode] = pNode; /* Compare (pKey/nKey) with the key in the middle slot of B-tree node ** pNode. The middle slot is never empty. If the comparison is a match, ** then the search is finished. Break out of the loop. */ pTreeKey = treeShmptrUnsafe(pDb, pNode->aiKeyPtr[1]); if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){ pTreeKey = treeShmkey(pDb, pNode->aiKeyPtr[1], TKV_LOADKEY, &b, &rc); if( rc!=LSM_OK ) break; } res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey); if( res==0 ){ pCsr->aiCell[iNode] = 1; break; } /* Based on the results of the previous comparison, compare (pKey/nKey) ** to either the left or right key of the B-tree node, if such a key ** exists. */ iTest = (res>0 ? 0 : 2); iTreeKey = pNode->aiKeyPtr[iTest]; if( iTreeKey ){ pTreeKey = treeShmptrUnsafe(pDb, iTreeKey); if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){ pTreeKey = treeShmkey(pDb, iTreeKey, TKV_LOADKEY, &b, &rc); if( rc ) break; } res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey); if( res==0 ){ pCsr->aiCell[iNode] = iTest; break; } }else{ iTest = 1; } if( iNode<(pRoot->nHeight-1) ){ iNodePtr = getChildPtr(pNode, pRoot->iTransId, iTest + (res<0)); }else{ iNodePtr = 0; } |
︙ | ︙ | |||
2076 2077 2078 2079 2080 2081 2082 | if( iCell<3 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break; } } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); | | | 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 | if( iCell<3 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break; } } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); assert( rc||treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)>=0 ); } tblobFree(pDb, &key1); #endif return rc; } |
︙ | ︙ | |||
2144 2145 2146 2147 2148 2149 2150 | }while( (--pCsr->iNode)>=0 ); pCsr->aiCell[pCsr->iNode] = iCell; } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); | | | 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 | }while( (--pCsr->iNode)>=0 ); pCsr->aiCell[pCsr->iNode] = iCell; } #ifndef NDEBUG if( pCsr->iNode>=0 ){ TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc); assert( rc || treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)<0 ); } tblobFree(pDb, &key1); #endif return rc; } |
︙ | ︙ | |||
2198 2199 2200 2201 2202 2203 2204 | return rc; } int lsmTreeCursorFlags(TreeCursor *pCsr){ int flags = 0; if( pCsr && pCsr->iNode>=0 ){ int rc = LSM_OK; | | | | 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 | return rc; } int lsmTreeCursorFlags(TreeCursor *pCsr){ int flags = 0; if( pCsr && pCsr->iNode>=0 ){ int rc = LSM_OK; TreeKey *pKey = (TreeKey *)treeShmptrUnsafe(pCsr->pDb, pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]] ); assert( rc==LSM_OK ); flags = (pKey->flags & ~LSM_CONTIGUOUS); } return flags; } int lsmTreeCursorKey(TreeCursor *pCsr, int *pFlags, void **ppKey, int *pnKey){ TreeKey *pTreeKey; int rc = LSM_OK; |
︙ | ︙ | |||
2353 2354 2355 2356 2357 2358 2359 | if( treeHeaderChecksumOk(&pDb->treehdr) ){ if( piRead ) *piRead = 2; return LSM_OK; } lsmShmBarrier(pDb); } | | | 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 | if( treeHeaderChecksumOk(&pDb->treehdr) ){ if( piRead ) *piRead = 2; return LSM_OK; } lsmShmBarrier(pDb); } return LSM_PROTOCOL_BKPT; } int lsmTreeLoadHeaderOk(lsm_db *pDb, int iRead){ TreeHeader *p = (iRead==1) ? &pDb->pShmhdr->hdr1 : &pDb->pShmhdr->hdr2; assert( iRead==1 || iRead==2 ); return (0==memcmp(pDb->treehdr.aCksum, p->aCksum, sizeof(u32)*2)); } |
︙ | ︙ |
Changes to src/lsm_unix.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include <unistd.h> #include <errno.h> #include <sys/mman.h> #include "lsmInt.h" /* ** An open file is an instance of the following object */ typedef struct PosixFile PosixFile; struct PosixFile { lsm_env *pEnv; /* The run-time environment */ const char *zName; /* Full path to file */ int fd; /* The open file descriptor */ int shmfd; /* Shared memory file-descriptor */ void *pMap; /* Pointer to mapping of file fd */ off_t nMap; /* Size of mapping at pMap in bytes */ int nShm; /* Number of entries in array apShm[] */ void **apShm; /* Array of 32K shared memory segments */ }; | > > > > > < < > > > | > | > > > | | > | < > > > > | | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | #include <unistd.h> #include <errno.h> #include <sys/mman.h> #include "lsmInt.h" /* There is no fdatasync() call on Android */ #ifdef __ANDROID__ # define fdatasync(x) fsync(x) #endif /* ** An open file is an instance of the following object */ typedef struct PosixFile PosixFile; struct PosixFile { lsm_env *pEnv; /* The run-time environment */ const char *zName; /* Full path to file */ int fd; /* The open file descriptor */ int shmfd; /* Shared memory file-descriptor */ void *pMap; /* Pointer to mapping of file fd */ off_t nMap; /* Size of mapping at pMap in bytes */ int nShm; /* Number of entries in array apShm[] */ void **apShm; /* Array of 32K shared memory segments */ }; static char *posixShmFile(PosixFile *p){ char *zShm; int nName = strlen(p->zName); zShm = (char *)lsmMalloc(p->pEnv, nName+4+1); if( zShm ){ memcpy(zShm, p->zName, nName); memcpy(&zShm[nName], "-shm", 5); } return zShm; } static int lsmPosixOsOpen( lsm_env *pEnv, const char *zFile, int flags, lsm_file **ppFile ){ int rc = LSM_OK; PosixFile *p; p = lsm_malloc(pEnv, sizeof(PosixFile)); if( p==0 ){ rc = LSM_NOMEM; }else{ int bReadonly = (flags & LSM_OPEN_READONLY); int oflags = (bReadonly ? O_RDONLY : (O_RDWR|O_CREAT)); memset(p, 0, sizeof(PosixFile)); p->zName = zFile; p->pEnv = pEnv; p->fd = open(zFile, oflags, 0644); if( p->fd<0 ){ lsm_free(pEnv, p); p = 0; if( errno==ENOENT ){ rc = lsmErrorBkpt(LSM_IOERR_NOENT); }else{ rc = LSM_IOERR_BKPT; } } } *ppFile = (lsm_file *)p; return rc; } static int lsmPosixOsWrite( lsm_file *pFile, /* File to write to */ lsm_i64 iOff, /* Offset to write to */ void *pData, /* Write data from this buffer */ int nData /* Bytes of data to write */ ){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; off_t offset; offset = lseek(p->fd, (off_t)iOff, SEEK_SET); if( offset!=iOff ){ rc = LSM_IOERR_BKPT; }else{ ssize_t prc = write(p->fd, pData, (size_t)nData); if( prc<0 ) rc = LSM_IOERR_BKPT; } return rc; } static int lsmPosixOsTruncate( lsm_file *pFile, /* File to write to */ lsm_i64 nSize /* Size to truncate file to */ ){ PosixFile *p = (PosixFile *)pFile; int rc = LSM_OK; /* Return code */ int prc; /* Posix Return Code */ struct stat sStat; /* Result of fstat() invocation */ prc = fstat(p->fd, &sStat); if( prc==0 && sStat.st_size>nSize ){ prc = ftruncate(p->fd, (off_t)nSize); } if( prc<0 ) rc = LSM_IOERR_BKPT; return rc; } static int lsmPosixOsRead( lsm_file *pFile, /* File to read from */ lsm_i64 iOff, /* Offset to read from */ void *pData, /* Read data into this buffer */ int nData /* Bytes of data to read */ ){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; off_t offset; offset = lseek(p->fd, (off_t)iOff, SEEK_SET); if( offset!=iOff ){ rc = LSM_IOERR_BKPT; }else{ ssize_t prc = read(p->fd, pData, (size_t)nData); if( prc<0 ){ rc = LSM_IOERR_BKPT; }else if( prc<nData ){ memset(&((u8 *)pData)[prc], 0, nData - prc); } } return rc; } static int lsmPosixOsSync(lsm_file *pFile){ int rc = LSM_OK; #ifndef LSM_NO_SYNC PosixFile *p = (PosixFile *)pFile; int prc = 0; if( p->pMap ){ prc = msync(p->pMap, p->nMap, MS_SYNC); } if( prc==0 ) prc = fdatasync(p->fd); if( prc<0 ) rc = LSM_IOERR_BKPT; #else (void)pFile; #endif return rc; } |
︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | if( p->pMap ){ munmap(p->pMap, p->nMap); *ppOut = p->pMap = 0; *pnOut = p->nMap = 0; } memset(&buf, 0, sizeof(buf)); prc = fstat(p->fd, &buf); if( prc!=0 ) return LSM_IOERR_BKPT; iSz = buf.st_size; if( iSz<iMin ){ iSz = ((iMin + (2<<20) - 1) / (2<<20)) * (2<<20); prc = ftruncate(p->fd, iSz); if( prc!=0 ) return LSM_IOERR_BKPT; } p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0); p->nMap = iSz; *ppOut = p->pMap; *pnOut = p->nMap; return LSM_OK; } static int lsmPosixOsFullpath( | > > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | if( p->pMap ){ munmap(p->pMap, p->nMap); *ppOut = p->pMap = 0; *pnOut = p->nMap = 0; } if( iMin>=0 ){ memset(&buf, 0, sizeof(buf)); prc = fstat(p->fd, &buf); if( prc!=0 ) return LSM_IOERR_BKPT; iSz = buf.st_size; if( iSz<iMin ){ iSz = ((iMin + (2<<20) - 1) / (2<<20)) * (2<<20); prc = ftruncate(p->fd, iSz); if( prc!=0 ) return LSM_IOERR_BKPT; } p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0); p->nMap = iSz; } *ppOut = p->pMap; *pnOut = p->nMap; return LSM_OK; } static int lsmPosixOsFullpath( |
︙ | ︙ | |||
338 339 340 341 342 343 344 | static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK }; struct flock lock; assert( aType[LSM_LOCK_UNLOCK]==F_UNLCK ); assert( aType[LSM_LOCK_SHARED]==F_RDLCK ); assert( aType[LSM_LOCK_EXCL]==F_WRLCK ); assert( eType>=0 && eType<array_size(aType) ); | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK }; struct flock lock; assert( aType[LSM_LOCK_UNLOCK]==F_UNLCK ); assert( aType[LSM_LOCK_SHARED]==F_RDLCK ); assert( aType[LSM_LOCK_EXCL]==F_WRLCK ); assert( eType>=0 && eType<array_size(aType) ); assert( iLock>0 && iLock<=32 ); memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_len = 1; lock.l_type = aType[eType]; lock.l_start = (4096-iLock); if( fcntl(p->fd, F_SETLK, &lock) ){ int e = errno; if( e==EACCES || e==EAGAIN ){ rc = LSM_BUSY; }else{ rc = LSM_IOERR_BKPT; } } return rc; } int lsmPosixOsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){ int rc = LSM_OK; PosixFile *p = (PosixFile *)pFile; static const short aType[3] = { 0, F_RDLCK, F_WRLCK }; struct flock lock; assert( eType==LSM_LOCK_SHARED || eType==LSM_LOCK_EXCL ); assert( aType[LSM_LOCK_SHARED]==F_RDLCK ); assert( aType[LSM_LOCK_EXCL]==F_WRLCK ); assert( eType>=0 && eType<array_size(aType) ); assert( iLock>0 && iLock<=32 ); memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_len = nLock; lock.l_type = aType[eType]; lock.l_start = (4096-iLock); if( fcntl(p->fd, F_GETLK, &lock) ){ rc = LSM_IOERR_BKPT; }else if( lock.l_type!=F_UNLCK ){ rc = LSM_BUSY; } return rc; } int lsmPosixOsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){ PosixFile *p = (PosixFile *)pFile; |
︙ | ︙ | |||
405 406 407 408 409 410 411 | p->nShm = nNew; } if( p->apShm[iChunk]==0 ){ p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE ); | | | 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | p->nShm = nNew; } if( p->apShm[iChunk]==0 ){ p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE ); if( p->apShm[iChunk]==0 ) return LSM_IOERR_BKPT; } *ppShm = p->apShm[iChunk]; return LSM_OK; } void lsmPosixOsShmBarrier(void){ |
︙ | ︙ | |||
448 449 450 451 452 453 454 | close(p->fd); lsm_free(p->pEnv, p->apShm); lsm_free(p->pEnv, p); return LSM_OK; } static int lsmPosixOsSleep(lsm_env *pEnv, int us){ | > | | < > > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | close(p->fd); lsm_free(p->pEnv, p->apShm); lsm_free(p->pEnv, p); return LSM_OK; } static int lsmPosixOsSleep(lsm_env *pEnv, int us){ #if 0 /* Apparently on Android usleep() returns void */ if( usleep(us) ) return LSM_IOERR; #endif usleep(us); return LSM_OK; } /**************************************************************************** ** Memory allocation routines. */ #define BLOCK_HDR_SIZE ROUND8( sizeof(sqlite4_size_t) ) |
︙ | ︙ | |||
711 712 713 714 715 716 717 718 719 720 721 722 723 724 | #endif lsmPosixOsMap, /* xMap */ lsmPosixOsUnmap, /* xUnmap */ lsmPosixOsFileid, /* xFileid */ lsmPosixOsClose, /* xClose */ lsmPosixOsUnlink, /* xUnlink */ lsmPosixOsLock, /* xLock */ lsmPosixOsShmMap, /* xShmMap */ lsmPosixOsShmBarrier, /* xShmBarrier */ lsmPosixOsShmUnmap, /* xShmUnmap */ /***** memory allocation *********/ 0, /* pMemCtx */ lsmPosixOsMalloc, /* xMalloc */ lsmPosixOsRealloc, /* xRealloc */ | > | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | #endif lsmPosixOsMap, /* xMap */ lsmPosixOsUnmap, /* xUnmap */ lsmPosixOsFileid, /* xFileid */ lsmPosixOsClose, /* xClose */ lsmPosixOsUnlink, /* xUnlink */ lsmPosixOsLock, /* xLock */ lsmPosixOsTestLock, /* xTestLock */ lsmPosixOsShmMap, /* xShmMap */ lsmPosixOsShmBarrier, /* xShmBarrier */ lsmPosixOsShmUnmap, /* xShmUnmap */ /***** memory allocation *********/ 0, /* pMemCtx */ lsmPosixOsMalloc, /* xMalloc */ lsmPosixOsRealloc, /* xRealloc */ |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | #ifdef SQLITE4_ENABLE_ICU # include "sqliteicu.h" #endif /* ** Dummy function used as a unique symbol for SQLITE4_DYNAMIC */ | | < < < < < < < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #ifdef SQLITE4_ENABLE_ICU # include "sqliteicu.h" #endif /* ** Dummy function used as a unique symbol for SQLITE4_DYNAMIC */ void sqlite4_dynamic(void *pArg,void *p){ (void)pArg; (void)p; } /* IMPLEMENTATION-OF: R-53536-42575 The sqlite4_libversion() function returns ** a pointer to the to the sqlite4_version[] string constant. */ const char *sqlite4_libversion(void){ return SQLITE4_VERSION; } /* IMPLEMENTATION-OF: R-63124-39300 The sqlite4_sourceid() function returns a |
︙ | ︙ | |||
67 68 69 70 71 72 73 | ** SQLITE4_ENABLE_IOTRACE is enabled, then messages describing ** I/O active are written using this function. These messages ** are intended for debugging activity only. */ void (*sqlite4IoTrace)(const char*, ...) = 0; #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | ** SQLITE4_ENABLE_IOTRACE is enabled, then messages describing ** I/O active are written using this function. These messages ** are intended for debugging activity only. */ void (*sqlite4IoTrace)(const char*, ...) = 0; #endif /* ** Set up the lookaside buffers for a database connection. ** Return SQLITE4_OK on success. ** If lookaside is already active, return SQLITE4_BUSY. ** ** The sz parameter is the number of bytes in each lookaside slot. ** The cnt parameter is the number of slots. If pStart is NULL the |
︙ | ︙ | |||
662 663 664 665 666 667 668 | for(i=0; i<nIn; i++){ aOut[i] = sqlite4_tolower(aIn[i]); } } return nIn; } | < < < < < < < | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | for(i=0; i<nIn; i++){ aOut[i] = sqlite4_tolower(aIn[i]); } } return nIn; } /* ** Return the number of changes in the most recent call to sqlite4_exec(). */ int sqlite4_changes(sqlite4 *db){ return db->nChange; } |
︙ | ︙ | |||
1263 1264 1265 1266 1267 1268 1269 | sqlite4_mutex_enter(db->mutex); if( db->mallocFailed ){ z = (void *)outOfMem; }else{ z = sqlite4_value_text16(db->pErr); if( z==0 ){ sqlite4ValueSetStr(db->pErr, -1, sqlite4ErrStr(db->errCode), | | | 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | sqlite4_mutex_enter(db->mutex); if( db->mallocFailed ){ z = (void *)outOfMem; }else{ z = sqlite4_value_text16(db->pErr); if( z==0 ){ sqlite4ValueSetStr(db->pErr, -1, sqlite4ErrStr(db->errCode), SQLITE4_UTF8, SQLITE4_STATIC, 0); z = sqlite4_value_text16(db->pErr); } /* A malloc() may have failed within the call to sqlite4_value_text16() ** above. If this is the case, then the db->mallocFailed flag needs to ** be cleared before returning. Do this directly, instead of via ** sqlite4ApiExit(), to avoid setting the database handle error message. */ |
︙ | ︙ | |||
1929 1930 1931 1932 1933 1934 1935 | db->xCollNeeded16 = xCollNeeded16; db->pCollNeededArg = pCollNeededArg; sqlite4_mutex_leave(db->mutex); return SQLITE4_OK; } #endif /* SQLITE4_OMIT_UTF16 */ | < < < < < < < < < < < < | 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 | db->xCollNeeded16 = xCollNeeded16; db->pCollNeededArg = pCollNeededArg; sqlite4_mutex_leave(db->mutex); return SQLITE4_OK; } #endif /* SQLITE4_OMIT_UTF16 */ /* ** Test to see whether or not the database connection is in autocommit ** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on ** by default. Autocommit is disabled by a BEGIN statement and reenabled ** by the next COMMIT or ROLLBACK. */ int sqlite4_get_autocommit(sqlite4 *db){ return (db->pSavepoint==0); } /* ** The following routines are subtitutes for constants SQLITE4_CORRUPT, |
︙ | ︙ |
Changes to src/math.c.
︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 221 222 223 224 | r.sign = A.sign ^ B.sign; r.approx = A.approx | B.approx; if( r.approx==0 && A.m%B.m!=0 ) r.approx = 1; r.m = A.m/B.m; r.e = A.e - B.e; return r; } /* ** Compare numbers A and B. Return: ** ** 1 if A<B ** 2 if A==B ** 3 if A>B | > > > > > > > > > > > > > > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | r.sign = A.sign ^ B.sign; r.approx = A.approx | B.approx; if( r.approx==0 && A.m%B.m!=0 ) r.approx = 1; r.m = A.m/B.m; r.e = A.e - B.e; return r; } /* ** Test if A is infinite. */ int sqlite4_num_isinf(sqlite4_num A){ return A.e>SQLITE4_MX_EXP && A.m!=0; } /* ** Test if A is NaN. */ int sqlite4_num_isnan(sqlite4_num A){ return A.e>SQLITE4_MX_EXP && A.m==0; } /* ** Compare numbers A and B. Return: ** ** 1 if A<B ** 2 if A==B ** 3 if A>B |
︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 246 247 248 249 250 | return A.sign ? 1 : 3; } if( B.e>SQLITE4_MX_EXP ){ if( B.m==0 ) return 0; return B.sign ? 3 : 1; } if( A.sign!=B.sign ){ return A.sign ? 1 : 3; } adjustExponent(&A, &B); if( A.sign ){ sqlite4_num t = A; A = B; B = t; | > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | return A.sign ? 1 : 3; } if( B.e>SQLITE4_MX_EXP ){ if( B.m==0 ) return 0; return B.sign ? 3 : 1; } if( A.sign!=B.sign ){ if ( A.m==0 && B.m==0 ) return 2; return A.sign ? 1 : 3; } adjustExponent(&A, &B); if( A.sign ){ sqlite4_num t = A; A = B; B = t; |
︙ | ︙ | |||
319 320 321 322 323 324 325 | i = incr; }else if( zIn[0]=='+' ){ i = incr; }else{ i = 0; } if( nIn<=0 ) goto not_a_valid_number; | | | < < < < | | > | | > > > > > > > > > > > > > > > > > > > > | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | i = incr; }else if( zIn[0]=='+' ){ i = incr; }else{ i = 0; } if( nIn<=0 ) goto not_a_valid_number; if( nIn>=incr*3 && ((c=zIn[i])=='i' || c=='I') && ((c=zIn[i+incr])=='n' || c=='N') && ((c=zIn[i+incr*2])=='f' || c=='F') ){ r.e = SQLITE4_MX_EXP+1; r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0; return r; } while( i<nIn && (c = zIn[i])!=0 ){ i += incr; if( c>='0' && c<='9' ){ if( c=='0' && nDigit==0 ){ if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--; continue; } nDigit++; if( nDigit<=18 ){ r.m = (r.m*10) + c - '0'; if( seenRadix ) r.e--; }else{ if( c!='0' ) r.approx = 1; if( !seenRadix ) r.e++; } }else if( c=='.' ){ seenRadix = 1; }else if( c=='e' || c=='E' ){ int exp = 0; int expsign = 0; int nEDigit = 0; if( zIn[i]=='-' ){ expsign = 1; i += incr; }else if( zIn[i]=='+' ){ i += incr; } if( i>=nIn ) goto not_a_valid_number; while( i<nIn && (c = zIn[i])!=0 ){ i += incr; if( c<'0' || c>'9' ) goto not_a_valid_number; if( c=='0' && nEDigit==0 ) continue; nEDigit++; if( nEDigit>3 ) goto not_a_valid_number; exp = exp*10 + c - '0'; } if( expsign ) exp = -exp; r.e += exp; break; }else{ goto not_a_valid_number; } } return r; not_a_valid_number: r.e = SQLITE4_MX_EXP+1; r.m = 0; return r; } /* ** Convert an sqlite4_int64 to a number and return that number. */ sqlite4_num sqlite4_num_from_int64(sqlite4_int64 n){ sqlite4_num r; r.approx = 0; r.e = 0; r.sign = n < 0; if( n>=0 ){ r.m = n; }else if( n!=SMALLEST_INT64 ){ r.m = -n; }else{ r.m = 1+(u64)LARGEST_INT64; } return r; } /* ** Convert an integer into text in the buffer supplied. The ** text is zero-terminated and right-justified in the buffer. ** A pointer to the first character of text is returned. ** ** The buffer needs to be at least 21 bytes in length. |
︙ | ︙ | |||
465 466 467 468 469 470 471 | zNum += m; n -= m; removeTrailingZeros(zNum, &n); if( n>0 ){ zOut[0] = '.'; memcpy(zOut+1, zNum, n); nOut += n; | < > > > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | zNum += m; n -= m; removeTrailingZeros(zNum, &n); if( n>0 ){ zOut[0] = '.'; memcpy(zOut+1, zNum, n); nOut += n; zOut[n+1] = 0; }else{ zOut[0] = 0; } return nOut; } if( x.e<0 && x.e >= -n-5 ){ /* Values less than 1 and with no more than 5 subsequent zeros prior ** to the first significant digit. Ex: 0.0000012345 */ int j = -(n + x.e); memcpy(zOut, "0.", 2); |
︙ | ︙ |
Changes to src/mem.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains the implementation of the "sqlite4_mm" memory ** allocator object. */ | | < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains the implementation of the "sqlite4_mm" memory ** allocator object. */ #include "sqliteInt.h" /************************************************************************* ** The SQLITE4_MM_SYSTEM memory allocator. This allocator uses the ** malloc/realloc/free from the system library. It also tries to use ** the memory allocation sizer from the system library if such a routine ** exists. If there is no msize in the system library, then each allocation ** is increased in size by 8 bytes and the size of the allocation is stored |
︙ | ︙ | |||
96 97 98 99 100 101 102 | #endif #endif /* __APPLE__ or not __APPLE__ */ /* ** Implementations of core routines */ | | | | | | | > > | > > > | | | > > > > > > > | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | #endif #endif /* __APPLE__ or not __APPLE__ */ /* ** Implementations of core routines */ static void *mmSysMalloc(sqlite4_mm *pMM, sqlite4_size_t iSize){ #ifdef SQLITE4_MALLOCSIZE return SQLITE4_MALLOC(iSize); #else unsigned char *pRes = SQLITE4_MALLOC(iSize+8); if( pRes ){ *(sqlite4_size_t*)pRes = iSize; pRes += 8; } return pRes; #endif } static void *mmSysRealloc(sqlite4_mm *pMM, void *pOld, sqlite4_size_t iSz){ #ifdef SQLITE4_MALLOCSIZE return SQLITE4_REALLOC(pOld, iSz); #else unsigned char *pRes; if( pOld==0 ) return mmSysMalloc(pMM, iSz); pRes = (unsigned char*)pOld; pRes -= 8; pRes = SQLITE4_REALLOC(pRes, iSz+8); if( pRes ){ *(sqlite4_size_t*)pRes = iSz; pRes += 8; } return pRes; #endif } static void mmSysFree(sqlite4_mm *pNotUsed, void *pOld){ #ifdef SQLITE4_MALLOCSIZE SQLITE4_FREE(pOld); #else unsigned char *pRes; if( pOld==0 ) return; pRes = (unsigned char *)pOld; pRes -= 8; SQLITE4_FREE(pRes); #endif } static sqlite4_size_t mmSysMsize(sqlite4_mm *pNotUsed, void *pOld){ #ifdef SQLITE4_MALLOCSIZE return SQLITE4_MALLOCSIZE(pOld); #else unsigned char *pX; if( pOld==0 ) return 0; pX = (unsigned char *)pOld; pX -= 8; return *(sqlite4_size_t*)pX; #endif } static const sqlite4_mm_methods mmSysMethods = { /* iVersion */ 1, /* xMalloc */ mmSysMalloc, /* xRealloc */ mmSysRealloc, /* xFree */ mmSysFree, /* xMsize */ mmSysMsize, /* xMember */ 0, /* xBenign */ 0, /* xStat */ 0, /* xCtrl */ 0, /* xFinal */ 0 }; sqlite4_mm sqlite4MMSystem = { /* pMethods */ &mmSysMethods }; /* The system memory allocator is the default. */ sqlite4_mm *sqlite4_mm_default(void){ return &sqlite4MMSystem; } /************************************************************************* ** The SQLITE4_MM_OVERFLOW memory allocator. ** ** This memory allocator has two child memory allocators, A and B. Always ** try to fulfill the request using A first, then overflow to B if the request ** on A fails. The A allocator must support the xMember method. */ struct mmOvfl { sqlite4_mm base; /* Base class - must be first */ int (*xMemberOfA)(sqlite4_mm*, const void*); sqlite4_mm *pA; /* Primary memory allocator */ sqlite4_mm *pB; /* Backup memory allocator in case pA fails */ }; static void *mmOvflMalloc(sqlite4_mm *pMM, sqlite4_size_t iSz){ const struct mmOvfl *pOvfl = (const struct mmOvfl*)pMM; void *pRes; pRes = pOvfl->pA->pMethods->xMalloc(pOvfl->pA, iSz); if( pRes==0 ){ pRes = pOvfl->pB->pMethods->xMalloc(pOvfl->pB, iSz); } return pRes; } static void *mmOvflRealloc(sqlite4_mm *pMM, void *pOld, sqlite4_size_t iSz){ const struct mmOvfl *pOvfl; void *pRes, *pAlt; if( pOld==0 ) return mmOvflMalloc(pMM, iSz); pOvfl = (const struct mmOvfl*)pMM; if( pOvfl->xMemberOfA(pOvfl->pA, pOld) ){ pRes = pOvfl->pA->pMethods->xRealloc(pOvfl->pA, pOld, iSz); if( pRes==0 && (pAlt = pOvfl->pB->pMethods->xMalloc(pOvfl->pB, iSz))!=0 ){ sqlite4_size_t nOld = pOvfl->pA->pMethods->xMsize(pOvfl->pA, pOld); assert( nOld<iSz ); memcpy(pAlt, pOld, (size_t)nOld); pOvfl->pA->pMethods->xFree(pOvfl->pA, pOld); pRes = pAlt; } }else{ pRes = pOvfl->pB->pMethods->xRealloc(pOvfl->pB, pOld, iSz); } return pRes; } static void mmOvflFree(sqlite4_mm *pMM, void *pOld){ const struct mmOvfl *pOvfl; if( pOld==0 ) return; pOvfl = (const struct mmOvfl*)pMM; if( pOvfl->xMemberOfA(pOvfl->pA, pOld) ){ pOvfl->pA->pMethods->xFree(pOvfl->pA, pOld); }else{ pOvfl->pB->pMethods->xFree(pOvfl->pB, pOld); } } static sqlite4_size_t mmOvflMsize(sqlite4_mm *pMM, void *pOld){ const struct mmOvfl *pOvfl; sqlite4_size_t iSz; if( pOld==0 ) return 0; pOvfl = (const struct mmOvfl*)pMM; if( pOvfl->xMemberOfA(pOvfl->pA, pOld) ){ iSz = sqlite4_mm_msize(pOvfl->pA, pOld); }else{ iSz = sqlite4_mm_msize(pOvfl->pB, pOld); } |
︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 | /* iVersion */ 1, /* xMalloc */ mmOvflMalloc, /* xRealloc */ mmOvflRealloc, /* xFree */ mmOvflFree, /* xMsize */ mmOvflMsize, /* xMember */ mmOvflMember, /* xBenign */ mmOvflBenign, /* xFinal */ mmOvflFinal }; static sqlite4_mm *mmOvflNew(sqlite4_mm *pA, sqlite4_mm *pB){ struct mmOvfl *pOvfl; if( pA->pMethods->xMember==0 ) return 0; pOvfl = sqlite4_mm_malloc(pA, sizeof(*pOvfl)); if( pOvfl==0 ){ | > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | /* iVersion */ 1, /* xMalloc */ mmOvflMalloc, /* xRealloc */ mmOvflRealloc, /* xFree */ mmOvflFree, /* xMsize */ mmOvflMsize, /* xMember */ mmOvflMember, /* xBenign */ mmOvflBenign, /* xStat */ 0, /* xCtrl */ 0, /* xFinal */ mmOvflFinal }; static sqlite4_mm *mmOvflNew(sqlite4_mm *pA, sqlite4_mm *pB){ struct mmOvfl *pOvfl; if( pA->pMethods->xMember==0 ) return 0; pOvfl = sqlite4_mm_malloc(pA, sizeof(*pOvfl)); if( pOvfl==0 ){ |
︙ | ︙ | |||
286 287 288 289 290 291 292 293 294 295 296 297 298 299 | */ struct mmOnesz { sqlite4_mm base; /* Base class. Must be first. */ const void *pSpace; /* Space to allocate */ const void *pLast; /* Last possible allocation */ struct mmOneszBlock *pFree; /* List of free blocks */ int sz; /* Size of each allocation */ }; /* A free block in the buffer */ struct mmOneszBlock { struct mmOneszBlock *pNext; /* Next on the freelist */ }; | > > > > > > | > | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | > > > | | | | | | | | > > > > > > > > > > > > > > > | | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | */ struct mmOnesz { sqlite4_mm base; /* Base class. Must be first. */ const void *pSpace; /* Space to allocate */ const void *pLast; /* Last possible allocation */ struct mmOneszBlock *pFree; /* List of free blocks */ int sz; /* Size of each allocation */ unsigned nFailSize; /* Failures due to size */ unsigned nFailMem; /* Failures due to OOM */ unsigned nSlot; /* Number of available slots */ unsigned nUsed; /* Current number of slots in use */ unsigned nUsedHw; /* Highwater mark for slots in use */ sqlite4_size_t mxSize; /* Maximum request size */ }; /* A free block in the buffer */ struct mmOneszBlock { struct mmOneszBlock *pNext; /* Next on the freelist */ }; static void *mmOneszMalloc(sqlite4_mm *pMM, sqlite4_size_t iSz){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; void *pRes; if( iSz>pOnesz->mxSize ) pOnesz->mxSize = iSz; if( iSz>pOnesz->sz ){ pOnesz->nFailSize++; return 0; } if( pOnesz->pFree==0 ){ pOnesz->nFailMem++; return 0; } pOnesz->nUsed++; if( pOnesz->nUsed>pOnesz->nUsedHw ) pOnesz->nUsedHw = pOnesz->nUsed; pRes = pOnesz->pFree; pOnesz->pFree = pOnesz->pFree->pNext; return pRes; } static void mmOneszFree(sqlite4_mm *pMM, void *pOld){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; if( pOld ){ struct mmOneszBlock *pBlock = (struct mmOneszBlock*)pOld; pBlock->pNext = pOnesz->pFree; pOnesz->pFree = pBlock; pOnesz->nUsed--; } } static void *mmOneszRealloc(sqlite4_mm *pMM, void *pOld, sqlite4_size_t iSz){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; if( pOld==0 ) return mmOneszMalloc(pMM, iSz); if( iSz<=0 ){ mmOneszFree(pMM, pOld); return 0; } if( iSz>pOnesz->sz ) return 0; return pOld; } static sqlite4_size_t mmOneszMsize(sqlite4_mm *pMM, void *pOld){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; return pOld ? pOnesz->sz : 0; } static int mmOneszMember(sqlite4_mm *pMM, const void *pOld){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; return pOld && pOld>=pOnesz->pSpace && pOld<=pOnesz->pLast; } static sqlite4_int64 mmOneszStat(sqlite4_mm *pMM, int eType, unsigned flgs){ struct mmOnesz *pOnesz = (struct mmOnesz*)pMM; sqlite4_int64 x = -1; switch( eType ){ case SQLITE4_MMSTAT_OUT: { x = pOnesz->nUsed*pOnesz->sz; break; } case SQLITE4_MMSTAT_OUT_HW: { x = pOnesz->nUsedHw*pOnesz->sz; if( flgs & SQLITE4_MMSTAT_RESET ) pOnesz->nUsedHw = pOnesz->nUsed; break; } case SQLITE4_MMSTAT_UNITS: { x = pOnesz->nUsed; break; } case SQLITE4_MMSTAT_UNITS_HW: { x = pOnesz->nUsedHw; if( flgs & SQLITE4_MMSTAT_RESET ) pOnesz->nUsedHw = pOnesz->nUsed; break; } case SQLITE4_MMSTAT_SIZE: { x = pOnesz->mxSize; if( flgs & SQLITE4_MMSTAT_RESET ) pOnesz->mxSize = 0; break; } case SQLITE4_MMSTAT_SZFAULT: { x = pOnesz->nFailSize; if( flgs & SQLITE4_MMSTAT_RESET ) pOnesz->nFailSize = 0; break; } case SQLITE4_MMSTAT_MEMFAULT: { x = pOnesz->nFailMem; if( flgs & SQLITE4_MMSTAT_RESET ) pOnesz->nFailMem = 0; break; } case SQLITE4_MMSTAT_FAULT: { x = pOnesz->nFailSize + pOnesz->nFailMem; if( flgs & SQLITE4_MMSTAT_RESET ){ pOnesz->nFailSize = 0; pOnesz->nFailMem = 0; } break; } } return x; } static const sqlite4_mm_methods mmOneszMethods = { /* iVersion */ 1, /* xMalloc */ mmOneszMalloc, /* xRealloc */ mmOneszRealloc, /* xFree */ mmOneszFree, /* xMsize */ mmOneszMsize, /* xMember */ mmOneszMember, /* xBenign */ 0, /* xStat */ mmOneszStat, /* xCtrl */ 0, /* xFinal */ 0 }; static sqlite4_mm *mmOneszNew(void *pSpace, int sz, int cnt){ struct mmOnesz *pOnesz; unsigned char *pMem; int n; if( sz<sizeof(struct mmOneszBlock) ) return 0; pMem = (unsigned char*)pSpace; pOnesz = (struct mmOnesz*)pMem; n = (sizeof(*pOnesz) + sz - 1)/sz; pMem += sz*n; cnt -= n; if( cnt<2 ) return 0; memset(pOnesz, 0, sizeof(*pOnesz)); pOnesz->base.pMethods = &mmOneszMethods; pOnesz->pSpace = (const void*)pMem; pOnesz->sz = sz; pOnesz->pLast = (const void*)(pMem + sz*(cnt-2)); pOnesz->pFree = 0; while( cnt ){ struct mmOneszBlock *pBlock = (struct mmOneszBlock*)pMem; pBlock->pNext = pOnesz->pFree; pOnesz->pFree = pBlock; cnt--; pMem += sz; } return &pOnesz->base; } /************************************************************************* ** Main interfaces. */ void *sqlite4_mm_malloc(sqlite4_mm *pMM, sqlite4_size_t iSize){ if( pMM==0 ) pMM = &sqlite4MMSystem; return pMM->pMethods->xMalloc(pMM,iSize); } void *sqlite4_mm_realloc(sqlite4_mm *pMM, void *pOld, sqlite4_size_t iSize){ if( pMM==0 ) pMM = &sqlite4MMSystem; return pMM->pMethods->xRealloc(pMM,pOld,iSize); } void sqlite4_mm_free(sqlite4_mm *pMM, void *pOld){ if( pMM==0 ) pMM = &sqlite4MMSystem; pMM->pMethods->xFree(pMM,pOld); } sqlite4_size_t sqlite4_mm_msize(sqlite4_mm *pMM, void *pOld){ if( pMM==0 ) pMM = &sqlite4MMSystem; return pMM->pMethods->xMsize(pMM,pOld); } int sqlite4_mm_member(sqlite4_mm *pMM, const void *pOld){ return (pMM && pMM->pMethods->xMember!=0) ? pMM->pMethods->xMember(pMM,pOld) : -1; } void sqlite4_mm_benign_failures(sqlite4_mm *pMM, int bEnable){ if( pMM && pMM->pMethods->xBenign ){ pMM->pMethods->xBenign(pMM, bEnable); } } sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eStatType, unsigned flags){ if( pMM==0 ) return -1; if( pMM->pMethods->xStat==0 ) return -1; return pMM->pMethods->xStat(pMM, eStatType, flags); } int sqlite4_mm_control(sqlite4_mm *pMM, int eCtrlType, ...){ int rc = SQLITE4_NOTFOUND; if( pMM && pMM->pMethods->xCtrl ){ va_list ap; va_start(ap, eCtrlType); rc = pMM->pMethods->xCtrl(pMM, eCtrlType, ap); va_end(ap); } return rc; } void sqlite4_mm_destroy(sqlite4_mm *pMM){ if( pMM && pMM->pMethods->xFinal ) pMM->pMethods->xFinal(pMM); } /* ** Create a new memory allocation object. eType determines the type of ** memory allocator and the arguments. */ sqlite4_mm *sqlite4_mm_new(sqlite4_mm_type eType, ...){ va_list ap; sqlite4_mm *pMM; va_start(ap, eType); switch( eType ){ case SQLITE4_MM_SYSTEM: { pMM = &sqlite4MMSystem; break; } case SQLITE4_MM_OVERFLOW: { sqlite4_mm *pA = va_arg(ap, sqlite4_mm*); sqlite4_mm *pB = va_arg(ap, sqlite4_mm*); pMM = mmOvflNew(pA, pB); break; |
︙ | ︙ |
Changes to src/mem.h.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2013-01-01 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* | < < < > > > > > > > > > > > > > > > > > > > > > | > > | | | | | | > | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /* ** 2013-01-01 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file defines the sqlite4_mm "SQLite4 Memory Manager" object and ** its interfaces. */ /* ** object declarations */ typedef struct sqlite4_mm sqlite4_mm; typedef struct sqlite4_mm_methods sqlite4_mm_methods; /* ** Base class. Each implementation extends this with additional ** fields specific to its own needs. This needs to be public so that ** applications can supply their on customized memory allocators. */ struct sqlite4_mm { const sqlite4_mm_methods *pMethods; }; /* ** Memory statistics reporting */ typedef enum { SQLITE4_MMSTAT_OUT = 1, /* Bytes of memory outstanding */ SQLITE4_MMSTAT_UNITS = 2, /* Separate allocations outstanding */ SQLITE4_MMSTAT_SIZE = 3, /* Size of the allocation */ SQLITE4_MMSTAT_SZFAULT = 4, /* Number of faults due to size */ SQLITE4_MMSTAT_MEMFAULT = 5, /* Number of faults due to out of space */ SQLITE4_MMSTAT_FAULT = 6, /* Total number of faults */ }; /* ** Bit flags for the 3rd parameter of xStat() */ #define SQLITE4_MMSTAT_HIGHWATER 0x01 #define SQLITE4_MMSTAT_RESET 0x02 #define SQLITE4_MMSTAT_HWRESET 0x03 /* ** An instance of the following object defines the methods on ** a BESPOKE memory allocator. */ struct sqlite4_mm_methods { int iVersion; void *(*xMalloc)(sqlite4_mm*, sqlite4_int64); void *(*xRealloc)(sqlite4_mm*, void*, sqlite4_int64); void (*xFree)(sqlite4_mm*, void*); sqlite4_int64 (*xMsize)(sqlite4_mm*, void*); int (*xMember)(sqlite4_mm*, const void*); void (*xBenign)(sqlite4_mm*, int); sqlite4_int64 (*xStat)(sqlite4_mm*, sqlite4_mm_stattype, unsigned flags); void (*xFinal)(sqlite4_mm*); }; /* ** Available memory management types: */ typedef enum { SQLITE4_MM_SYSTEM = 1, /* Use the system malloc() */ SQLITE4_MM_ONESIZE = 2, /* All allocations map to a fixed size */ SQLITE4_MM_OVERFLOW = 3, /* Two allocators. Use A first; failover to B */ SQLITE4_MM_COMPACT = 4, /* Like memsys3 from SQLite3 */ SQLITE4_MM_ROBSON = 5, /* Like memsys5 from SQLite3 */ SQLITE4_MM_LINEAR = 6, /* Allocate from a fixed buffer w/o free */ SQLITE4_MM_BESPOKE = 7, /* Caller-defined implementation */ SQLITE4_MM_DEBUG, /* Debugging memory allocator */ SQLITE4_MM_STATS /* Keep memory statistics */ } sqlite4_mm_type; /* ** Allocate a new memory manager. Return NULL if unable. */ sqlite4_mm *sqlite4_mm_new(sqlite4_mm_type, ...); |
︙ | ︙ | |||
103 104 105 106 107 108 109 | /* ** Enable or disable benign failure mode. Benign failure mode can be ** nested. In benign failure mode, OOM errors do not necessarily propagate ** back out to the application but can be dealt with internally. Memory ** allocations that occur in benign failure mode are considered "optional". */ void sqlite4_mm_benign_failures(sqlite4_mm*, int bEnable); | > > > | 123 124 125 126 127 128 129 130 131 132 | /* ** Enable or disable benign failure mode. Benign failure mode can be ** nested. In benign failure mode, OOM errors do not necessarily propagate ** back out to the application but can be dealt with internally. Memory ** allocations that occur in benign failure mode are considered "optional". */ void sqlite4_mm_benign_failures(sqlite4_mm*, int bEnable); /* ** Rest |
Changes to src/pragma.c.
︙ | ︙ | |||
73 74 75 76 77 78 79 80 81 82 83 84 85 86 | #ifdef SQLITE4_DEBUG { "sql_trace", SQLITE4_SqlTrace }, { "vdbe_listing", SQLITE4_VdbeListing }, { "vdbe_trace", SQLITE4_VdbeTrace }, { "kv_trace", SQLITE4_KvTrace }, { "trace", SQLITE4_SqlTrace | SQLITE4_VdbeListing | SQLITE4_VdbeTrace | SQLITE4_KvTrace }, #endif #ifndef SQLITE4_OMIT_CHECK { "ignore_check_constraints", SQLITE4_IgnoreChecks }, #endif /* The following is VERY experimental */ { "writable_schema", SQLITE4_WriteSchema|SQLITE4_RecoveryMode }, | > | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #ifdef SQLITE4_DEBUG { "sql_trace", SQLITE4_SqlTrace }, { "vdbe_listing", SQLITE4_VdbeListing }, { "vdbe_trace", SQLITE4_VdbeTrace }, { "kv_trace", SQLITE4_KvTrace }, { "trace", SQLITE4_SqlTrace | SQLITE4_VdbeListing | SQLITE4_VdbeTrace | SQLITE4_KvTrace }, { "vdbe_addoptrace", SQLITE4_VdbeAddopTrace }, #endif #ifndef SQLITE4_OMIT_CHECK { "ignore_check_constraints", SQLITE4_IgnoreChecks }, #endif /* The following is VERY experimental */ { "writable_schema", SQLITE4_WriteSchema|SQLITE4_RecoveryMode }, |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
338 339 340 341 342 343 344 | ** we have a match (cnt>0) or when we run out of name contexts. */ if( cnt==0 ){ pNC = pNC->pNext; } } | < < < < < < < < < < < < < < < < | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | ** we have a match (cnt>0) or when we run out of name contexts. */ if( cnt==0 ){ pNC = pNC->pNext; } } /* ** cnt==0 means there was not match. cnt>1 means there were two or ** more matches. Either way, we have an error. */ if( cnt!=1 ){ const char *zErr; zErr = cnt==0 ? "no such column" : "ambiguous column name"; |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
944 945 946 947 948 949 950 | if( p->selFlags & SF_UseSorter ){ int regSortOut = ++pParse->nMem; int ptab2 = pParse->nTab++; sqlite4VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite4VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); codeOffset(v, p, addrContinue); sqlite4VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); | | | | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 | if( p->selFlags & SF_UseSorter ){ int regSortOut = ++pParse->nMem; int ptab2 = pParse->nTab++; sqlite4VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite4VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); codeOffset(v, p, addrContinue); sqlite4VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); sqlite4VdbeAddOp3(v, OP_Column, ptab2, 0, regRow); sqlite4VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ addr = 1 + sqlite4VdbeAddOp2(v, OP_Sort, iTab, addrBreak); codeOffset(v, p, addrContinue); sqlite4VdbeAddOp3(v, OP_Column, iTab, 0, regRow); } switch( eDest ){ case SRT_Table: case SRT_EphemTab: { testcase( eDest==SRT_Table ); testcase( eDest==SRT_EphemTab ); sqlite4VdbeAddOp2(v, OP_NewRowid, iParm, regRowid); |
︙ | ︙ | |||
1278 1279 1280 1281 1282 1283 1284 | int cnt; /* Index added to make the name unique */ Column *aCol, *pCol; /* For looping over result columns */ int nCol; /* Number of columns in the result set */ Expr *p; /* Expression for a single result column */ char *zName; /* Column name */ int nName; /* Size of name in zName[] */ | | | 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | int cnt; /* Index added to make the name unique */ Column *aCol, *pCol; /* For looping over result columns */ int nCol; /* Number of columns in the result set */ Expr *p; /* Expression for a single result column */ char *zName; /* Column name */ int nName; /* Size of name in zName[] */ *pnCol = nCol = pEList ? pEList->nExpr : 0; aCol = *paCol = sqlite4DbMallocZero(db, sizeof(aCol[0])*nCol); if( aCol==0 ) return SQLITE4_NOMEM; for(i=0, pCol=aCol; i<nCol; i++, pCol++){ /* Get an appropriate name for the column */ p = pEList->a[i].pExpr; assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) |
︙ | ︙ | |||
2305 2306 2307 2308 2309 2310 2311 | ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). */ if( op==TK_ALL ){ regPrev = 0; }else{ int nExpr = p->pEList->nExpr; assert( nOrderBy>=nExpr || db->mallocFailed ); | | > | 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 | ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). */ if( op==TK_ALL ){ regPrev = 0; }else{ int nExpr = p->pEList->nExpr; assert( nOrderBy>=nExpr || db->mallocFailed ); regPrev = pParse->nMem + 1; pParse->nMem += nExpr + 1; sqlite4VdbeAddOp2(v, OP_Integer, 0, regPrev); pKeyDup = sqlite4DbMallocZero(db, sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) ); if( pKeyDup ){ pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr]; pKeyDup->nField = (u16)nExpr; pKeyDup->enc = ENC(db); |
︙ | ︙ | |||
2486 2487 2488 2489 2490 2491 2492 | */ sqlite4VdbeResolveLabel(v, labelCmpr); sqlite4VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); sqlite4VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, nOrderBy, (char*)pKeyMerge, P4_KEYINFO_HANDOFF); sqlite4VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); | < < < < < < | 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 | */ sqlite4VdbeResolveLabel(v, labelCmpr); sqlite4VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); sqlite4VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, nOrderBy, (char*)pKeyMerge, P4_KEYINFO_HANDOFF); sqlite4VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); /* Jump to the this point in order to terminate the query. */ sqlite4VdbeResolveLabel(v, labelEnd); /* Set the number of output columns */ if( pDest->eDest==SRT_Output ){ |
︙ | ︙ | |||
2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 | ** operator other than UNION ALL because all the other compound ** operators have an implied DISTINCT which is disallowed by ** restriction (4). ** ** (18) If the sub-query is a compound select, then all terms of the ** ORDER by clause of the parent must be simple references to ** columns of the sub-query. ** ** (19) The subquery does not use LIMIT or the outer query does not ** have a WHERE clause. ** ** (20) If the sub-query is a compound select, then it must not use ** an ORDER BY clause. Ticket #3773. We could relax this constraint ** somewhat by saying that the terms of the ORDER BY clause must | > > > > > > | 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 | ** operator other than UNION ALL because all the other compound ** operators have an implied DISTINCT which is disallowed by ** restriction (4). ** ** (18) If the sub-query is a compound select, then all terms of the ** ORDER by clause of the parent must be simple references to ** columns of the sub-query. ** ** Also, each component of the sub-query must return the same number ** of result columns. This is actually a requirement for any compound ** SELECT statement, but all the code here does is make sure that no ** such (illegal) sub-query is flattened. The caller will detect the ** syntax error and return a detailed message. ** ** (19) The subquery does not use LIMIT or the outer query does not ** have a WHERE clause. ** ** (20) If the sub-query is a compound select, then it must not use ** an ORDER BY clause. Ticket #3773. We could relax this constraint ** somewhat by saying that the terms of the ORDER BY clause must |
︙ | ︙ | |||
2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 | for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); assert( pSub->pSrc!=0 ); if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 || (pSub1->pPrior && pSub1->op!=TK_ALL) || pSub1->pSrc->nSrc<1 ){ return 0; } testcase( pSub1->pSrc->nSrc>1 ); } /* Restriction 18. */ | > | 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); assert( pSub->pSrc!=0 ); if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 || (pSub1->pPrior && pSub1->op!=TK_ALL) || pSub1->pSrc->nSrc<1 || pSub->pEList->nExpr!=pSub1->pEList->nExpr ){ return 0; } testcase( pSub1->pSrc->nSrc>1 ); } /* Restriction 18. */ |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
317 318 319 320 321 322 323 | int argc, sqlite4_value **argv ){ assert( 0==argc ); assert( zShellStatic ); UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | int argc, sqlite4_value **argv ){ assert( 0==argc ); assert( zShellStatic ); UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); sqlite4_result_text(context, zShellStatic, -1, SQLITE4_STATIC, 0); } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() |
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 | int k; for(z=azCol[i], j=1, k=0; z[j]; j++){ if( z[j]=='"' ){ j++; if( z[j]==0 ) break; } z[k++] = z[j]; } z[k] = 0; } | | | 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 | int k; for(z=azCol[i], j=1, k=0; z[j]; j++){ if( z[j]=='"' ){ j++; if( z[j]==0 ) break; } z[k++] = z[j]; } z[k] = 0; } sqlite4_bind_text(pStmt, i+1, azCol[i], -1, SQLITE4_STATIC, 0); } sqlite4_step(pStmt); rc = sqlite4_reset(pStmt); free(zLine); if( rc!=SQLITE4_OK ){ fprintf(stderr,"Error: %s\n", sqlite4_errmsg(db)); zCommit = "ROLLBACK"; |
︙ | ︙ | |||
2168 2169 2170 2171 2172 2173 2174 | zSql = sqlite4_mprintf(0, "%z ORDER BY 1", zSql); rc = sqlite4_prepare(p->db, zSql, -1, &pStmt, 0); sqlite4_free(0, zSql); if( rc ) return rc; nRow = nAlloc = 0; azResult = 0; if( nArg>1 ){ | | | | 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 | zSql = sqlite4_mprintf(0, "%z ORDER BY 1", zSql); rc = sqlite4_prepare(p->db, zSql, -1, &pStmt, 0); sqlite4_free(0, zSql); if( rc ) return rc; nRow = nAlloc = 0; azResult = 0; if( nArg>1 ){ sqlite4_bind_text(pStmt, 1, azArg[1], -1, SQLITE4_TRANSIENT, 0); }else{ sqlite4_bind_text(pStmt, 1, "%", -1, SQLITE4_STATIC, 0); } while( sqlite4_step(pStmt)==SQLITE4_ROW ){ if( nRow>=nAlloc ){ char **azNew; int n = nAlloc*2 + 10; azNew = sqlite4_realloc(0, azResult, sizeof(azResult[0])*n); if( azNew==0 ){ |
︙ | ︙ |
Changes to src/sqlite.h.in.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* | > | < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This header file defines the interface that the SQLite4 library ** presents to client programs. If a C-function, structure, datatype, ** or constant definition does not appear in this file, then it is ** not a published API of SQLite, is subject to change without ** notice, and should not be referenced by programs that use SQLite. ** ** The name of this file under configuration management is "sqlite.h.in". ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite4.h" as ** part of the build process. */ #ifndef _SQLITE4_H_ #define _SQLITE4_H_ |
︙ | ︙ | |||
45 46 47 48 49 50 51 | /* ** Add the ability to override 'extern' */ #ifndef SQLITE4_EXTERN # define SQLITE4_EXTERN extern #endif | < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | /* ** Add the ability to override 'extern' */ #ifndef SQLITE4_EXTERN # define SQLITE4_EXTERN extern #endif /* ** Ensure these symbols were not defined by some previous header file. */ #ifdef SQLITE4_VERSION # undef SQLITE4_VERSION #endif #ifdef SQLITE4_VERSION_NUMBER # undef SQLITE4_VERSION_NUMBER #endif /* ** CAPIREF: 64-Bit Integer Types ** KEYWORDS: sqlite4_int64 sqlite4_uint64 ** ** Because there is no cross-platform way to specify 64-bit integer types ** SQLite includes typedefs for 64-bit signed and unsigned integers. ** ** ^The sqlite4_int64 and sqlite_int64 types can store integer values ** between -9223372036854775808 and +9223372036854775807 inclusive. ^The ** sqlite4_uint64 and sqlite_uint64 types can store integer values ** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE4_INT64_TYPE typedef SQLITE4_INT64_TYPE sqlite4_int64_t; typedef unsigned SQLITE4_INT64_TYPE sqlite4_uint64_t; #elif defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 sqlite4_int64_t; typedef unsigned __int64 sqlite4_uint64_t; #else typedef long long int sqlite4_int64_t; typedef unsigned long long int sqlite4_uint64_t; #endif typedef sqlite4_int64_t sqlite4_int64; typedef sqlite4_uint64_t sqlite4_uint64; /* ** CAPIREF: String length type ** ** A type for measuring the length of the string. Like size_t but ** does not require <stddef.h> */ typedef int sqlite4_size_t; /* ** Available memory allocator object subtypes: */ typedef enum { SQLITE4_MM_SYSTEM = 1, /* Use the system malloc() */ SQLITE4_MM_ONESIZE = 2, /* All allocations map to a fixed size */ SQLITE4_MM_OVERFLOW = 3, /* Two allocators. Use A first; failover to B */ SQLITE4_MM_COMPACT = 4, /* Like memsys3 from SQLite3 */ SQLITE4_MM_ROBSON = 5, /* Like memsys5 from SQLite3 */ SQLITE4_MM_LINEAR = 6, /* Allocate from a fixed buffer w/o free */ SQLITE4_MM_BESPOKE = 7, /* Caller-defined implementation */ SQLITE4_MM_DEBUG, /* Debugging memory allocator */ SQLITE4_MM_STATS /* Keep memory statistics */ } sqlite4_mm_type; /* ** Base class for the memory allocator object. ** ** Implementations may extend this with additional ** fields specific to its own needs. This needs to be public so that ** applications can supply their on customized memory allocators. */ typedef struct sqlite4_mm sqlite4_mm; typedef struct sqlite4_mm_methods sqlite4_mm_methods; struct sqlite4_mm { const struct sqlite4_mm_methods *pMethods; }; struct sqlite4_mm_methods { int iVersion; void *(*xMalloc)(sqlite4_mm*, sqlite4_size_t); void *(*xRealloc)(sqlite4_mm*, void*, sqlite4_size_t); void (*xFree)(sqlite4_mm*, void*); sqlite4_size_t (*xMsize)(sqlite4_mm*, void*); int (*xMember)(sqlite4_mm*, const void*); void (*xBenign)(sqlite4_mm*, int); sqlite4_int64 (*xStat)(sqlite4_mm*, unsigned eType, unsigned bFlags); int (*xCtrl)(sqlite4_mm*, unsigned eType, va_list); void (*xFinal)(sqlite4_mm*); }; /* ** Return a pointer to the default memory allocator, which is basically ** a wrapper around system malloc()/realloc()/free(). */ sqlite4_mm *sqlite4_mm_default(void); /* ** Create a new memory allocator object. */ sqlite4_mm *sqlite4_mm_new(sqlite4_mm_type, ...); /* ** Allocate a new memory manager. Return NULL if unable. */ sqlite4_mm *sqlite4_mm_new(sqlite4_mm_type, ...); /* ** Free the sqlite4_mm object. ** ** All outstanding memory for the allocator must be freed prior to ** invoking this interface, or else the behavior is undefined. */ void sqlite4_mm_destroy(sqlite4_mm*); /* ** Core memory allocation routines: */ void *sqlite4_mm_malloc(sqlite4_mm*, sqlite4_size_t); void *sqlite4_mm_realloc(sqlite4_mm*, void*, sqlite4_size_t); void sqlite4_mm_free(sqlite4_mm*, void*); /* ** Return the size of a memory allocation. ** ** All memory allocators in SQLite4 must be able to report their size. ** When using system malloc() on system that lack the malloc_usable_size() ** routine or its equivalent, then the sqlite4_mm object allocates 8 extra ** bytes for each memory allocation and stores the allocation size in those ** initial 8 bytes. */ sqlite4_size_t sqlite4_mm_msize(sqlite4_mm*, void*); /* ** Check to see if pOld is a memory allocation from pMM. If it is, return ** 1. If not, return 0. If we cannot determine an answer, return -1. ** ** If pOld is not a valid memory allocation or is a memory allocation that ** has previously been freed, then the result of this routine is undefined. */ int sqlite4_mm_member(sqlite4_mm *pMM, const void *pOld); /* ** Allowed values for the second parameter ("eType") to sqlite4_mm_type(). */ #define SQLITE4_MMSTAT_OUT 1 #define SQLITE4_MMSTAT_OUT_HW 2 #define SQLITE4_MMSTAT_UNITS 3 #define SQLITE4_MMSTAT_UNITS_HW 4 #define SQLITE4_MMSTAT_SIZE 5 #define SQLITE4_MMSTAT_SZFAULT 6 #define SQLITE4_MMSTAT_MEMFAULT 7 #define SQLITE4_MMSTAT_FAULT 8 /* ** Bits for the bit vector third parameter ("flags") to sqlite4_mm_type() */ #define SQLITE4_MMSTAT_RESET 0x01 /* ** Return statistics or status information about a memory allocator. ** Not all memory allocators provide all stat values. Some memory ** allocators provides no states at all. If a particular stat for ** a memory allocator is unavailable, then -1 is returned. */ sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eType, unsigned flags); /* ** Send a control message into a memory allocator. */ int sqlit4_mm_control(sqlite4_mm *pMM, int eType, ...); /* ** Enable or disable benign failure mode. Benign failure mode can be ** nested. In benign failure mode, OOM errors do not necessarily propagate ** back out to the application but can be dealt with internally. Memory ** allocations that occur in benign failure mode are considered "optional". */ void sqlite4_mm_benign_failures(sqlite4_mm*, int bEnable); /* ** CAPIREF: Run-time Environment Object ** ** An instance of the following object defines the run-time environment ** for an SQLite4 database connection. This object defines the interface ** to appropriate mutex routines, memory allocation routines, a |
︙ | ︙ | |||
250 251 252 253 254 255 256 | ** is its destructor. There are many other interfaces (such as ** [sqlite4_prepare], [sqlite4_create_function()], and ** [sqlite4_busy_timeout()] to name but three) that are methods on an ** sqlite4 object. */ typedef struct sqlite4 sqlite4; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | ** is its destructor. There are many other interfaces (such as ** [sqlite4_prepare], [sqlite4_create_function()], and ** [sqlite4_busy_timeout()] to name but three) that are methods on an ** sqlite4 object. */ typedef struct sqlite4 sqlite4; /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point. */ #ifdef SQLITE4_OMIT_FLOATING_POINT # define double sqlite4_int64 #endif |
︙ | ︙ | |||
737 738 739 740 741 742 743 | ** </dl> */ #define SQLITE4_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE4_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE4_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 844 845 846 847 848 849 850 851 852 853 854 855 856 857 | ** </dl> */ #define SQLITE4_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE4_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE4_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* ** CAPIREF: Count The Number Of Rows Modified ** ** ^This function returns the number of database rows that were changed ** or inserted or deleted by the most recently completed SQL statement ** on the [database connection] specified by the first parameter. ** ^(Only changes that are directly specified by the [INSERT], [UPDATE], |
︙ | ︙ | |||
1311 1312 1313 1314 1315 1316 1317 | ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite ** might provide greater resolution on the profiler callback. The ** sqlite4_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ void *sqlite4_trace(sqlite4*, void(*xTrace)(void*,const char*), void*); | | | 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 | ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite ** might provide greater resolution on the profiler callback. The ** sqlite4_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ void *sqlite4_trace(sqlite4*, void(*xTrace)(void*,const char*), void*); void *sqlite4_profile(sqlite4*, void(*xProfile)(void*,const char*,sqlite4_uint64), void*); /* ** CAPIREF: Query Progress Callbacks ** ** ^The sqlite4_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to |
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 | ** ^If the fifth argument is ** the special value [SQLITE4_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE4_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite4_bind_*() routine returns. ** | < < < < < < < < | > | > | > < | 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | ** ^If the fifth argument is ** the special value [SQLITE4_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE4_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite4_bind_*() routine returns. ** ** ^If any of the sqlite4_bind_*() routines are called with a NULL pointer ** for the [prepared statement] or with a prepared statement for which ** [sqlite4_step()] has been called more recently than [sqlite4_reset()], ** then the call will return [SQLITE4_MISUSE]. If any sqlite4_bind_() ** routine is passed a [prepared statement] that has been finalized, the ** result is undefined and probably harmful. ** ** ^Bindings are not cleared by the [sqlite4_reset()] routine. ** ^Unbound parameters are interpreted as NULL. ** ** ^The sqlite4_bind_* routines return [SQLITE4_OK] on success or an ** [error code] if anything goes wrong. ** ^[SQLITE4_RANGE] is returned if the parameter ** index is out of range. ^[SQLITE4_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite4_bind_parameter_count()], ** [sqlite4_bind_parameter_name()], and [sqlite4_bind_parameter_index()]. */ int sqlite4_bind_blob(sqlite4_stmt*, int, const void*, int n, void(*)(void*,void*),void*); int sqlite4_bind_double(sqlite4_stmt*, int, double); int sqlite4_bind_int(sqlite4_stmt*, int, int); int sqlite4_bind_int64(sqlite4_stmt*, int, sqlite4_int64); int sqlite4_bind_null(sqlite4_stmt*, int); int sqlite4_bind_text(sqlite4_stmt*, int, const char*, int n, void(*)(void*,void*),void*); int sqlite4_bind_text16(sqlite4_stmt*, int, const void*, int, void(*)(void*,void*),void*); int sqlite4_bind_value(sqlite4_stmt*, int, const sqlite4_value*); /* ** CAPIREF: Number Of SQL Parameters ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as |
︙ | ︙ | |||
2146 2147 2148 2149 2150 2151 2152 | ** If [sqlite4_step()] or [sqlite4_reset()] or [sqlite4_finalize()] ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** ** ^The sqlite4_column_type() routine returns the ** [SQLITE4_INTEGER | datatype code] for the initial data type ** of the result column. ^The returned value is one of [SQLITE4_INTEGER], | | > | 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 | ** If [sqlite4_step()] or [sqlite4_reset()] or [sqlite4_finalize()] ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** ** ^The sqlite4_column_type() routine returns the ** [SQLITE4_INTEGER | datatype code] for the initial data type ** of the result column. ^The returned value is one of [SQLITE4_INTEGER], ** [SQLITE4_FLOAT], [SQLITE4_TEXT], [SQLITE4_BLOB], or [SQLITE4_NULL]. ** The value ** returned by sqlite4_column_type() is only meaningful if no type ** conversions have occurred as described below. After a type conversion, ** the value returned by sqlite4_column_type() is undefined. Future ** versions of SQLite may change the behavior of sqlite4_column_type() ** following a type conversion. ** ** ^If the result is a BLOB or UTF-8 string then the sqlite4_column_bytes() |
︙ | ︙ | |||
2475 2476 2477 2478 2479 2480 2481 | #define SQLITE4_UTF8 1 #define SQLITE4_UTF16LE 2 #define SQLITE4_UTF16BE 3 #define SQLITE4_UTF16 4 /* Use native byte order */ #define SQLITE4_ANY 5 /* sqlite4_create_function only */ #define SQLITE4_UTF16_ALIGNED 8 /* sqlite4_create_collation only */ | < < < < < < < < < < < < < < < < < | 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 | #define SQLITE4_UTF8 1 #define SQLITE4_UTF16LE 2 #define SQLITE4_UTF16BE 3 #define SQLITE4_UTF16 4 /* Use native byte order */ #define SQLITE4_ANY 5 /* sqlite4_create_function only */ #define SQLITE4_UTF16_ALIGNED 8 /* sqlite4_create_collation only */ /* ** CAPIREF: Obtaining SQL Function Parameter Values ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on ** the function or aggregate. ** |
︙ | ︙ | |||
2660 2661 2662 2663 2664 2665 2666 | ** expressions that are constant at compile time. This includes literal ** values and [parameters].)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ void *sqlite4_get_auxdata(sqlite4_context*, int N); | | > | | | 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 | ** expressions that are constant at compile time. This includes literal ** values and [parameters].)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ void *sqlite4_get_auxdata(sqlite4_context*, int N); void sqlite4_set_auxdata(sqlite4_context*, int N, void*, void (*)(void*,void*),void*); /* ** CAPIREF: Constants Defining Special Destructor Behavior ** ** These are special values for the destructor that is passed in as the ** final argument to routines like [sqlite4_result_blob()]. ^If the destructor ** argument is SQLITE4_STATIC, it means that the content pointer is constant ** and will never change. It does not need to be destroyed. ^The ** SQLITE4_TRANSIENT value means that the content will likely change in ** the near future and that SQLite should make its own private copy of ** the content before returning. ** ** The typedef is necessary to work around problems in certain ** C++ compilers. See ticket #2191. */ typedef void (*sqlite4_destructor_type)(void*,void*); void sqlite4_dynamic(void*,void*); #define SQLITE4_STATIC ((sqlite4_destructor_type)0) #define SQLITE4_TRANSIENT ((sqlite4_destructor_type)-1) #define SQLITE4_DYNAMIC (sqlite4_dynamic) /* ** CAPIREF: Setting The Result Of An SQL Function |
︙ | ︙ | |||
2701 2702 2703 2704 2705 2706 2707 | ** Refer to the [SQL parameter] documentation for additional information. ** ** ^The sqlite4_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** | < < < < | 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 | ** Refer to the [SQL parameter] documentation for additional information. ** ** ^The sqlite4_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** ** ^The sqlite4_result_double() interface sets the result from ** an application-defined function to be a floating point value specified ** by its 2nd argument. ** ** ^The sqlite4_result_error() and sqlite4_result_error16() functions ** cause the implemented SQL function to throw an exception. ** ^SQLite uses the string pointed to by the |
︙ | ︙ | |||
2728 2729 2730 2731 2732 2733 2734 | ** bytes (not characters) from the 2nd parameter as the error message. ** ^The sqlite4_result_error() and sqlite4_result_error16() ** routines make a private copy of the error message text before ** they return. Hence, the calling function can deallocate or ** modify the text after they return without harm. ** ^The sqlite4_result_error_code() function changes the error code ** returned by SQLite as a result of an error in a function. ^By default, | | > | 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 | ** bytes (not characters) from the 2nd parameter as the error message. ** ^The sqlite4_result_error() and sqlite4_result_error16() ** routines make a private copy of the error message text before ** they return. Hence, the calling function can deallocate or ** modify the text after they return without harm. ** ^The sqlite4_result_error_code() function changes the error code ** returned by SQLite as a result of an error in a function. ^By default, ** the error code is SQLITE4_ERROR. ** ^A subsequent call to sqlite4_result_error() ** or sqlite4_result_error16() resets the error code to SQLITE4_ERROR. ** ** ^The sqlite4_result_toobig() interface causes SQLite to throw an error ** indicating that a string or BLOB is too long to represent. ** ** ^The sqlite4_result_nomem() interface causes SQLite to throw an error ** indicating that a memory allocation failed. |
︙ | ︙ | |||
2794 2795 2796 2797 2798 2799 2800 | ** [unprotected sqlite4_value] object is required, so either ** kind of [sqlite4_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite4_context] pointer, the results are undefined. */ | | > | > | > | > | > < | 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 | ** [unprotected sqlite4_value] object is required, so either ** kind of [sqlite4_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite4_context] pointer, the results are undefined. */ void sqlite4_result_blob(sqlite4_context*, const void*, int, void(*)(void*,void*),void*); void sqlite4_result_double(sqlite4_context*, double); void sqlite4_result_error(sqlite4_context*, const char*, int); void sqlite4_result_error16(sqlite4_context*, const void*, int); void sqlite4_result_error_toobig(sqlite4_context*); void sqlite4_result_error_nomem(sqlite4_context*); void sqlite4_result_error_code(sqlite4_context*, int); void sqlite4_result_int(sqlite4_context*, int); void sqlite4_result_int64(sqlite4_context*, sqlite4_int64); void sqlite4_result_null(sqlite4_context*); void sqlite4_result_text(sqlite4_context*, const char*, int, void(*)(void*,void*),void*); void sqlite4_result_text16(sqlite4_context*, const void*, int, void(*)(void*,void*),void*); void sqlite4_result_text16le(sqlite4_context*, const void*, int, void(*)(void*,void*),void*); void sqlite4_result_text16be(sqlite4_context*, const void*, int, void(*)(void*,void*),void*); void sqlite4_result_value(sqlite4_context*, sqlite4_value*); /* ** CAPIREF: Define New Collating Sequences ** ** ^This function adds, removes, or modifies a [collation] associated ** with the [database connection] specified as the first argument. ** |
︙ | ︙ | |||
3856 3857 3858 3859 3860 3861 3862 | ** New verbs may be added in future releases of SQLite. Existing verbs ** might be discontinued. Applications should check the return code from ** [sqlite4_db_status()] to make sure that the call worked. ** The [sqlite4_db_status()] interface will return a non-zero error code ** if a discontinued or unsupported verb is invoked. ** ** <dl> | | > | 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 | ** New verbs may be added in future releases of SQLite. Existing verbs ** might be discontinued. Applications should check the return code from ** [sqlite4_db_status()] to make sure that the call worked. ** The [sqlite4_db_status()] interface will return a non-zero error code ** if a discontinued or unsupported verb is invoked. ** ** <dl> ** [[SQLITE4_DBSTATUS_LOOKASIDE_USED]] ** ^(<dt>SQLITE4_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** [[SQLITE4_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE4_DBSTATUS_LOOKASIDE_HIT</dt> ** <dd>This parameter returns the number malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; ** the current value is always zero.)^ |
︙ | ︙ | |||
3961 3962 3963 3964 3965 3966 3967 | ** KEYWORDS: {SQLITE4_STMTSTATUS counter} {SQLITE4_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite4_stmt_status()] interface. ** The meanings of the various counters are as follows: ** ** <dl> | | > | 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 | ** KEYWORDS: {SQLITE4_STMTSTATUS counter} {SQLITE4_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite4_stmt_status()] interface. ** The meanings of the various counters are as follows: ** ** <dl> ** [[SQLITE4_STMTSTATUS_FULLSCAN_STEP]] ** <dt>SQLITE4_STMTSTATUS_FULLSCAN_STEP</dt> ** <dd>^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** ** [[SQLITE4_STMTSTATUS_SORT]] <dt>SQLITE4_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. |
︙ | ︙ | |||
3984 3985 3986 3987 3988 3989 3990 | ** need to be reinitialized each time the statement is run.</dd> ** </dl> */ #define SQLITE4_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE4_STMTSTATUS_SORT 2 #define SQLITE4_STMTSTATUS_AUTOINDEX 3 | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 | ** need to be reinitialized each time the statement is run.</dd> ** </dl> */ #define SQLITE4_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE4_STMTSTATUS_SORT 2 #define SQLITE4_STMTSTATUS_AUTOINDEX 3 /* ** CAPIREF: String Comparison ** ** ^The [sqlite4_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-independent fashion, using the same definition of case independence |
︙ | ︙ | |||
4202 4203 4204 4205 4206 4207 4208 | #define SQLITE4_VTAB_CONSTRAINT_SUPPORT 1 /* ** CAPIREF: Determine The Virtual Table Conflict Policy ** ** This function may only be called from within a call to the [xUpdate] method ** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The | | > | 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 | #define SQLITE4_VTAB_CONSTRAINT_SUPPORT 1 /* ** CAPIREF: Determine The Virtual Table Conflict Policy ** ** This function may only be called from within a call to the [xUpdate] method ** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The ** value returned is one of [SQLITE4_ROLLBACK], [SQLITE4_IGNORE], ** [SQLITE4_FAIL], ** [SQLITE4_ABORT], or [SQLITE4_REPLACE], according to the [ON CONFLICT] mode ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ int sqlite4_vtab_on_conflict(sqlite4 *); /* |
︙ | ︙ | |||
4323 4324 4325 4326 4327 4328 4329 | /* ** CAPIREF: Key-value storage object factory ** ** New key/value storage engines can be added to SQLite4 at run-time. ** In order to create a new KV storage engine, the application must ** supply a "factory" function that creates an instance of the | | | | 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 | /* ** CAPIREF: Key-value storage object factory ** ** New key/value storage engines can be added to SQLite4 at run-time. ** In order to create a new KV storage engine, the application must ** supply a "factory" function that creates an instance of the ** sqlite4_kvstore object. This typedef defines the signature ** of that factory function. */ typedef int (*sqlite4_kvfactory)( sqlite4_env *pEnv, /* The environment to use */ sqlite4_kvstore **ppKVStore, /* OUT: New KV store returned here */ const char *zFilename, /* Name of database file to open */ unsigned flags /* Bit flags */ ); /* ** CAPI4REF: Representation Of Numbers ** ** Every number in SQLite is represented in memory by an instance of ** the following object. */ typedef struct sqlite4_num sqlite4_num; struct sqlite4_num { unsigned char sign; /* Sign of the overall value */ unsigned char approx; /* True if the value is approximate */ short e; /* The exponent. */ sqlite4_uint64 m; /* The significant */ }; /* ** CAPI4REF: Operations On SQLite Number Objects */ sqlite4_num sqlite4_num_add(sqlite4_num, sqlite4_num); |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
164 165 166 167 168 169 170 | ** ** (Historical note: There used to be several other options, but we've ** pared it down to just these three.) ** ** If none of the above are defined, then set SQLITE4_SYSTEM_MALLOC as ** the default. */ | | > | > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | ** ** (Historical note: There used to be several other options, but we've ** pared it down to just these three.) ** ** If none of the above are defined, then set SQLITE4_SYSTEM_MALLOC as ** the default. */ #if defined(SQLITE4_SYSTEM_MALLOC)+defined(SQLITE4_WIN32_MALLOC)\ +defined(SQLITE4_MEMDEBUG)>1 # error "At most one of the following compile-time configuration options\ is allows: SQLITE4_SYSTEM_MALLOC, SQLITE4_WIN32_MALLOC, SQLITE4_MEMDEBUG" #endif #if defined(SQLITE4_SYSTEM_MALLOC)+defined(SQLITE4_WIN32_MALLOC)\ +defined(SQLITE4_MEMDEBUG)==0 # define SQLITE4_SYSTEM_MALLOC 1 #endif /* ** If SQLITE4_MALLOC_SOFT_LIMIT is not zero, then try to keep the ** sizes of memory allocations below this value where possible. */ |
︙ | ︙ | |||
193 194 195 196 197 198 199 | ** Later we learn that _XOPEN_SOURCE is poorly or incorrectly ** implemented on some systems. So we avoid defining it at all ** if it is already defined or if it is unneeded because we are ** not doing a threadsafe build. Ticket #2681. ** ** See also ticket #2741. */ | | > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | ** Later we learn that _XOPEN_SOURCE is poorly or incorrectly ** implemented on some systems. So we avoid defining it at all ** if it is already defined or if it is unneeded because we are ** not doing a threadsafe build. Ticket #2681. ** ** See also ticket #2741. */ #if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__)\ && SQLITE4_THREADSAFE # define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ #endif /* ** The TCL headers are only needed when compiling the TCL bindings. */ #if defined(SQLITE4_TCL) || defined(TCLSH) |
︙ | ︙ | |||
460 461 462 463 464 465 466 | /* ** Constants for the largest and smallest possible 64-bit signed integers. ** These macros are designed to work correctly on both 32-bit and 64-bit ** compilers. */ #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) | | | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | /* ** Constants for the largest and smallest possible 64-bit signed integers. ** These macros are designed to work correctly on both 32-bit and 64-bit ** compilers. */ #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) #define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32)) /* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. */ #define ROUND8(x) (((x)+7)&~7) |
︙ | ︙ | |||
760 761 762 763 764 765 766 | u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ }; /* ** Each database connection is an instance of the following structure. ** | < < < < < < < | | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 | u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ }; /* ** Each database connection is an instance of the following structure. ** ** The sqlite.nChange does not count changes within triggers and keeps no ** context. It is reset at start of sqlite4_exec. ** The sqlite.lsChange represents the number of changes made by the last ** insert, update, or delete statement. It remains constant throughout the ** length of a statement and is then updated by OP_SetCounts. It keeps a ** context stack so that the count of changes ** within a trigger is not seen outside the trigger. Changes to views do not ** affect the value of lsChange. ** The sqlite.csChange keeps track of the number of current changes (since ** the last statement) and is used to update sqlite_lsChange. ** ** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 ** store the most recent error code and, if applicable, string. The |
︙ | ︙ | |||
799 800 801 802 803 804 805 | u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ | < | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 | u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite4_changes() */ int nTotalChange; /* Value returned by sqlite4_total_changes() */ sqlite4_mutex *mutex; /* Connection mutex */ int aLimit[SQLITE4_N_LIMIT]; /* Limits */ Sqlite4InitInfo init; /* Information used during initialization */ int nExtension; /* Number of loaded extensions */ |
︙ | ︙ | |||
889 890 891 892 893 894 895 | */ #define ENC(db) ((db)->aDb[0].pSchema->enc) /* ** Possible values for the sqlite4.flags. */ #define SQLITE4_VdbeTrace 0x00000100 /* True to trace VDBE execution */ | < < < < | | > > > | < | < < | | | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | */ #define ENC(db) ((db)->aDb[0].pSchema->enc) /* ** Possible values for the sqlite4.flags. */ #define SQLITE4_VdbeTrace 0x00000100 /* True to trace VDBE execution */ #define SQLITE4_SqlTrace 0x00000200 /* Debug print SQL as it executes */ #define SQLITE4_VdbeListing 0x00000400 /* Debug listings of VDBE programs */ #define SQLITE4_KvTrace 0x00000800 /* Trace Key/value storage calls */ #define SQLITE4_VdbeAddopTrace 0x00001000 /* Trace sqlite4VdbeAddOp() calls */ #define SQLITE4_InternChanges 0x00010000 /* Uncommitted Hash table changes */ #define SQLITE4_WriteSchema 0x00020000 /* OK to update SQLITE4_MASTER */ #define SQLITE4_IgnoreChecks 0x00040000 /* Dont enforce check constraints */ #define SQLITE4_RecoveryMode 0x00080000 /* Ignore schema errors */ #define SQLITE4_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE4_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE4_ForeignKeys 0x04000000 /* Enable foreign key constraints */ #define SQLITE4_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE4_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ #define SQLITE4_EnableTrigger 0x40000000 /* True to enable triggers */ /* ** Bits of the sqlite4.flags field that are used by the ** sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS,...) interface. |
︙ | ︙ | |||
929 930 931 932 933 934 935 | #define SQLITE4_OptMask 0xff /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. */ | | | | | | | 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 | #define SQLITE4_OptMask 0xff /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. */ #define SQLITE4_MAGIC_OPEN 0x4d06c919 /* Database is open */ #define SQLITE4_MAGIC_CLOSED 0x5f2246b4 /* Database is closed */ #define SQLITE4_MAGIC_SICK 0xcaad9e61 /* Error and awaiting close */ #define SQLITE4_MAGIC_BUSY 0xb07f8c8c /* Database currently in use */ #define SQLITE4_MAGIC_ERROR 0x912e4c46 /* An SQLITE4_MISUSE error occurred */ /* ** This structure encapsulates a user-function destructor callback (as ** configured using create_function_v2()) and a reference counter. When ** create_function_v2() is called to create a function with a destructor, ** a single object of this type is allocated. FuncDestructor.nRef is set to ** the number of FuncDef objects created (either 1 or 3, depending on whether |
︙ | ︙ | |||
964 965 966 967 968 969 970 | */ #define SQLITE4_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE4_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #define SQLITE4_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE4_FUNC_NEEDCOLL 0x08 /* sqlite4GetFuncCollSeq() might be called */ #define SQLITE4_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE4_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ | | | 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 | */ #define SQLITE4_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE4_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #define SQLITE4_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE4_FUNC_NEEDCOLL 0x08 /* sqlite4GetFuncCollSeq() might be called */ #define SQLITE4_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE4_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ #define SQLITE4_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() func */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** ** FUNCTION(zName, nArg, iArg, bNC, xFunc) ** Used to create a scalar function definition of a function zName |
︙ | ︙ | |||
1652 1653 1654 1655 1656 1657 1658 | */ #define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */ #define EP_Agg 0x0002 /* Contains one or more aggregate functions */ #define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */ #define EP_Error 0x0008 /* Expression contains one or more errors */ #define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */ #define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */ | < | 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 | */ #define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */ #define EP_Agg 0x0002 /* Contains one or more aggregate functions */ #define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */ #define EP_Error 0x0008 /* Expression contains one or more errors */ #define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */ #define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */ #define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */ #define EP_FixedDest 0x0200 /* Result needed in a specific register */ #define EP_IntValue 0x0400 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */ #define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ |
︙ | ︙ | |||
2184 2185 2186 2187 2188 2189 2190 | int nOnce; /* Number of OP_Once instructions so far */ int ckBase; /* Base register of data during check constraints */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */ int iNewidxReg; /* First argument to OP_NewIdxid */ u8 nColCache; /* Number of entries in aColCache[] */ u8 iColCache; /* Next entry in aColCache[] to replace */ | | | 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 | int nOnce; /* Number of OP_Once instructions so far */ int ckBase; /* Base register of data during check constraints */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */ int iNewidxReg; /* First argument to OP_NewIdxid */ u8 nColCache; /* Number of entries in aColCache[] */ u8 iColCache; /* Next entry in aColCache[] to replace */ ParseYColCache aColCache[SQLITE4_N_COLCACHE]; /* One per colcache entry */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieValue[SQLITE4_MAX_ATTACHED+2]; /* Values of cookies to verify */ int regRowid; /* Register holding rowid of CREATE TABLE entry */ |
︙ | ︙ | |||
2256 2257 2258 2259 2260 2261 2262 | Parse *pParse; /* The Parse structure */ }; /* ** Bitfield flags for P5 value in OP_Insert and OP_Delete */ #define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ | | | 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 | Parse *pParse; /* The Parse structure */ }; /* ** Bitfield flags for P5 value in OP_Insert and OP_Delete */ #define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ #define OPFLAG_PARTIALKEY 0x02 /* Not all values given to OP_MakeIdxKey */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_SEQCOUNT 0x10 /* Append sequence number to key */ #define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ #define OPFLAG_APPENDBIAS 0x40 /* Bias inserts for appending */ /* |
︙ | ︙ | |||
2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 | int iVersion; /* Version number of this structure */ int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite4_mem_methods m; /* Low-level memory allocation interface */ sqlite4_mutex_methods mutex; /* Low-level mutex interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ int mxParserStack; /* maximum depth of the parser stack */ KVFactory *pFactory; /* List of factories */ | > | 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 | int iVersion; /* Version number of this structure */ int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite4_mm *pMM; /* Memory allocator for this environment */ sqlite4_mem_methods m; /* Low-level memory allocation interface */ sqlite4_mutex_methods mutex; /* Low-level mutex interface */ void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ int mxParserStack; /* maximum depth of the parser stack */ KVFactory *pFactory; /* List of factories */ |
︙ | ︙ | |||
2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 | */ #define SQLITE4_SKIP_UTF8(zIn) { \ if( (*(zIn++))>=0xc0 ){ \ while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ } \ } /* ** The SQLITE4_*_BKPT macros are substitutes for the error codes with ** the same name but without the _BKPT suffix. These macros invoke ** routines that report the line-number on which the error originated ** using sqlite4_log(). The routines also provide a convenient place ** to set a debugger breakpoint. */ | > > > > > | 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 | */ #define SQLITE4_SKIP_UTF8(zIn) { \ if( (*(zIn++))>=0xc0 ){ \ while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ } \ } /* ** Default memory allocator */ extern sqlite4_mm sqlite4MMSystem; /* ** The SQLITE4_*_BKPT macros are substitutes for the error codes with ** the same name but without the _BKPT suffix. These macros invoke ** routines that report the line-number on which the error originated ** using sqlite4_log(). The routines also provide a convenient place ** to set a debugger breakpoint. */ |
︙ | ︙ | |||
2742 2743 2744 2745 2746 2747 2748 | int sqlite4Select(Parse*, Select*, SelectDest*); Select *sqlite4SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,int,Expr*,Expr*); void sqlite4SelectDelete(sqlite4*, Select*); Table *sqlite4SrcListLookup(Parse*, SrcList*); int sqlite4IsReadOnly(Parse*, Table*, int); void sqlite4OpenTable(Parse*, int iCur, int iDb, Table*, int); | | > | 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 | int sqlite4Select(Parse*, Select*, SelectDest*); Select *sqlite4SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,int,Expr*,Expr*); void sqlite4SelectDelete(sqlite4*, Select*); Table *sqlite4SrcListLookup(Parse*, SrcList*); int sqlite4IsReadOnly(Parse*, Table*, int); void sqlite4OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE4_ENABLE_UPDATE_DELETE_LIMIT) \ && !defined(SQLITE4_OMIT_SUBQUERY) Expr *sqlite4LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *); #endif void sqlite4DeleteFrom(Parse*, SrcList*, Expr*); void sqlite4Update(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqlite4WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16); void sqlite4WhereEnd(WhereInfo*); int sqlite4ExprCodeGetColumn(Parse*, Table*, int, int, int); |
︙ | ︙ | |||
2923 2924 2925 2926 2927 2928 2929 | ** x = sqlite4GetVarint32( A, &B ); ** x = sqlite4PutVarint32( A, B ); ** ** x = getVarint32( A, B ); ** x = putVarint32( A, B ); ** */ | | > | > > | 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 | ** x = sqlite4GetVarint32( A, &B ); ** x = sqlite4PutVarint32( A, B ); ** ** x = getVarint32( A, B ); ** x = putVarint32( A, B ); ** */ #define getVarint32(A,B) \ (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite4GetVarint32((A),(u32 *)&(B))) #define putVarint32(A,B) \ (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1\ :sqlite4PutVarint32((A),(B))) #define getVarint sqlite4GetVarint #define putVarint sqlite4PutVarint const char *sqlite4IndexAffinityStr(Vdbe *, Index *); void sqlite4TableAffinityStr(Vdbe *, Table *); char sqlite4CompareAffinity(Expr *pExpr, char aff2); |
︙ | ︙ | |||
2963 2964 2965 2966 2967 2968 2969 | # define sqlite4FileSuffix3(X,Y) #endif u8 sqlite4GetBoolean(const char *z); const void *sqlite4ValueText(sqlite4_value*, u8); int sqlite4ValueBytes(sqlite4_value*, u8); void sqlite4ValueSetStr(sqlite4_value*, int, const void *,u8, | | | 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 | # define sqlite4FileSuffix3(X,Y) #endif u8 sqlite4GetBoolean(const char *z); const void *sqlite4ValueText(sqlite4_value*, u8); int sqlite4ValueBytes(sqlite4_value*, u8); void sqlite4ValueSetStr(sqlite4_value*, int, const void *,u8, void(*)(void*,void*),void*); void sqlite4ValueFree(sqlite4_value*); sqlite4_value *sqlite4ValueNew(sqlite4 *); char *sqlite4Utf16to8(sqlite4 *, const void*, int, u8); #ifdef SQLITE4_ENABLE_STAT3 char *sqlite4Utf8to16(sqlite4 *, u8, char *, int, int *); #endif int sqlite4ValueFromExpr(sqlite4 *, Expr *, u8, u8, sqlite4_value **); |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 | char *zTrace; /* The trace callback routine */ char *zProfile; /* The profile callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ | < | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | char *zTrace; /* The trace callback routine */ char *zProfile; /* The profile callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite4_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ |
︙ | ︙ | |||
312 313 314 315 316 317 318 | Tcl_DStringAppendElement(&str, zTm); Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); Tcl_DStringFree(&str); Tcl_ResetResult(pDb->interp); } #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | Tcl_DStringAppendElement(&str, zTm); Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); Tcl_DStringFree(&str); Tcl_ResetResult(pDb->interp); } #endif static void tclCollateNeeded( void *pCtx, sqlite4 *db, int enc, const char *zName ){ SqliteDb *pDb = (SqliteDb *)pCtx; |
︙ | ︙ | |||
502 503 504 505 506 507 508 | u8 *data; const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); char c = zType[0]; if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ /* Only return a BLOB type if the Tcl variable is a bytearray and ** has no string representation. */ data = Tcl_GetByteArrayFromObj(pVar, &n); | | | | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | u8 *data; const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); char c = zType[0]; if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ /* Only return a BLOB type if the Tcl variable is a bytearray and ** has no string representation. */ data = Tcl_GetByteArrayFromObj(pVar, &n); sqlite4_result_blob(context, data, n, SQLITE4_TRANSIENT, 0); }else if( c=='b' && strcmp(zType,"boolean")==0 ){ Tcl_GetIntFromObj(0, pVar, &n); sqlite4_result_int(context, n); }else if( c=='d' && strcmp(zType,"double")==0 ){ double r; Tcl_GetDoubleFromObj(0, pVar, &r); sqlite4_result_double(context, r); }else if( (c=='w' && strcmp(zType,"wideInt")==0) || (c=='i' && strcmp(zType,"int")==0) ){ Tcl_WideInt v; Tcl_GetWideIntFromObj(0, pVar, &v); sqlite4_result_int64(context, v); }else{ data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); sqlite4_result_text(context, (char *)data, n, SQLITE4_TRANSIENT, 0); } } } #ifndef SQLITE4_OMIT_AUTHORIZATION /* ** This is the authentication function. It appends the authentication |
︙ | ︙ | |||
844 845 846 847 848 849 850 | char c = zType[0]; if( zVar[0]=='@' || (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ /* Load a BLOB type if the Tcl variable is a bytearray and ** it has no string representation or the host ** parameter name begins with "@". */ data = Tcl_GetByteArrayFromObj(pVar, &n); | | | | 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 | char c = zType[0]; if( zVar[0]=='@' || (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ /* Load a BLOB type if the Tcl variable is a bytearray and ** it has no string representation or the host ** parameter name begins with "@". */ data = Tcl_GetByteArrayFromObj(pVar, &n); sqlite4_bind_blob(pStmt, i, data, n, SQLITE4_STATIC, 0); Tcl_IncrRefCount(pVar); pPreStmt->apParm[iParm++] = pVar; }else if( c=='b' && strcmp(zType,"boolean")==0 ){ Tcl_GetIntFromObj(interp, pVar, &n); sqlite4_bind_int(pStmt, i, n); }else if( c=='d' && strcmp(zType,"double")==0 ){ double r; Tcl_GetDoubleFromObj(interp, pVar, &r); sqlite4_bind_double(pStmt, i, r); }else if( (c=='w' && strcmp(zType,"wideInt")==0) || (c=='i' && strcmp(zType,"int")==0) ){ Tcl_WideInt v; Tcl_GetWideIntFromObj(interp, pVar, &v); sqlite4_bind_int64(pStmt, i, v); }else{ data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); sqlite4_bind_text(pStmt, i, (char *)data, n, SQLITE4_STATIC, 0); Tcl_IncrRefCount(pVar); pPreStmt->apParm[iParm++] = pVar; } }else{ sqlite4_bind_null(pStmt, i); } } |
︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 | int choice; int rc = TCL_OK; static const char *DB_strs[] = { "authorizer", "cache", "changes", "close", "collate", "collation_needed", "complete", "copy", "enable_load_extension", "errorcode", "eval", "exists", | | | | | | 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 | int choice; int rc = TCL_OK; static const char *DB_strs[] = { "authorizer", "cache", "changes", "close", "collate", "collation_needed", "complete", "copy", "enable_load_extension", "errorcode", "eval", "exists", "function", "interrupt", "nullvalue", "onecolumn", "profile", "rekey", "status", "total_changes", "trace", "transaction", "version", 0 }; enum DB_enum { DB_AUTHORIZER, DB_CACHE, DB_CHANGES, DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED, DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL, DB_EXISTS, DB_FUNCTION, DB_INTERRUPT, DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE, DB_REKEY, DB_STATUS, DB_TOTAL_CHANGES, DB_TRACE, DB_TRANSACTION, DB_VERSION }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; |
︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 | for(i=0; i<nCol; i++){ /* check for null data, if so, bind as null */ if( (nNull>0 && strcmp(azCol[i], zNull)==0) || strlen30(azCol[i])==0 ){ sqlite4_bind_null(pStmt, i+1); }else{ | | | 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | for(i=0; i<nCol; i++){ /* check for null data, if so, bind as null */ if( (nNull>0 && strcmp(azCol[i], zNull)==0) || strlen30(azCol[i])==0 ){ sqlite4_bind_null(pStmt, i+1); }else{ sqlite4_bind_text(pStmt, i+1, azCol[i], -1, SQLITE4_STATIC, 0); } } sqlite4_step(pStmt); rc = sqlite4_reset(pStmt); free(zLine); if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp,"Error: ", sqlite4_errmsg(pDb->db), 0); |
︙ | ︙ | |||
1899 1900 1901 1902 1903 1904 1905 | pDb->zNull = 0; } } Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); break; } | < < < < < < < < < < < < < < < < < < | 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 | pDb->zNull = 0; } } Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); break; } /* ** The DB_ONECOLUMN method is implemented together with DB_EXISTS. */ /* $db profile ?CALLBACK? ** |
︙ | ︙ | |||
2132 2133 2134 2135 2136 2137 2138 | ** or savepoint. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); Tcl_NREvalObj(interp, pScript, 0); }else{ rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0)); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 | ** or savepoint. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); Tcl_NREvalObj(interp, pScript, 0); }else{ rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0)); } break; } /* $db version ** ** Return the version string for this database. */ |
︙ | ︙ | |||
2799 2800 2801 2802 2803 2804 2805 | static void md5finalize(sqlite4_context *context){ MD5Context *p; unsigned char digest[16]; char zBuf[33]; p = sqlite4_aggregate_context(context, sizeof(*p)); MD5Final(digest,p); MD5DigestToBase16(digest, zBuf); | | | 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 | static void md5finalize(sqlite4_context *context){ MD5Context *p; unsigned char digest[16]; char zBuf[33]; p = sqlite4_aggregate_context(context, sizeof(*p)); MD5Final(digest,p); MD5DigestToBase16(digest, zBuf); sqlite4_result_text(context, zBuf, -1, SQLITE4_TRANSIENT, 0); } int Md5_Register(sqlite4 *db){ int rc = sqlite4_create_function(db, "md5sum", -1, SQLITE4_UTF8, 0, 0, md5step, md5finalize); sqlite4_overload_function(db, "md5sum", -1); /* To exercise this API */ return rc; } |
︙ | ︙ |
Changes to src/tokenize.c.
︙ | ︙ | |||
223 224 225 226 227 228 229 | *tokenType = TK_BITAND; return 1; } case '~': { *tokenType = TK_BITNOT; return 1; } | < | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | *tokenType = TK_BITAND; return 1; } case '~': { *tokenType = TK_BITNOT; return 1; } case '\'': case '"': { int delim = z[0]; testcase( delim=='`' ); testcase( delim=='\'' ); testcase( delim=='"' ); for(i=1; (c=z[i])!=0; i++){ |
︙ | ︙ |
Changes to src/utf.c.
︙ | ︙ | |||
470 471 472 473 474 475 476 | ** ** NULL is returned if there is an allocation error. */ char *sqlite4Utf16to8(sqlite4 *db, const void *z, int nByte, u8 enc){ Mem m; memset(&m, 0, sizeof(m)); m.db = db; | | | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | ** ** NULL is returned if there is an allocation error. */ char *sqlite4Utf16to8(sqlite4 *db, const void *z, int nByte, u8 enc){ Mem m; memset(&m, 0, sizeof(m)); m.db = db; sqlite4VdbeMemSetStr(&m, z, nByte, enc, SQLITE4_STATIC, 0); sqlite4VdbeChangeEncoding(&m, SQLITE4_UTF8); if( db->mallocFailed ){ sqlite4VdbeMemRelease(&m); m.z = 0; } assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); |
︙ | ︙ | |||
498 499 500 501 502 503 504 | ** flag set. */ #ifdef SQLITE4_ENABLE_STAT3 char *sqlite4Utf8to16(sqlite4 *db, u8 enc, char *z, int n, int *pnOut){ Mem m; memset(&m, 0, sizeof(m)); m.db = db; | | | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | ** flag set. */ #ifdef SQLITE4_ENABLE_STAT3 char *sqlite4Utf8to16(sqlite4 *db, u8 enc, char *z, int n, int *pnOut){ Mem m; memset(&m, 0, sizeof(m)); m.db = db; sqlite4VdbeMemSetStr(&m, z, n, SQLITE4_UTF8, SQLITE4_STATIC, 0); if( sqlite4VdbeMemTranslate(&m, enc) ){ assert( db->mallocFailed ); return 0; } assert( m.z==m.zMalloc ); *pnOut = m.n; return m.z; |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
128 129 130 131 132 133 134 | db->errCode = err_code; if( zFormat ){ char *z; va_list ap; va_start(ap, zFormat); z = sqlite4VMPrintf(db, zFormat, ap); va_end(ap); | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | db->errCode = err_code; if( zFormat ){ char *z; va_list ap; va_start(ap, zFormat); z = sqlite4VMPrintf(db, zFormat, ap); va_end(ap); sqlite4ValueSetStr(db->pErr, -1, z, SQLITE4_UTF8, SQLITE4_DYNAMIC, 0); }else{ sqlite4ValueSetStr(db->pErr, 0, 0, SQLITE4_UTF8, SQLITE4_STATIC, 0); } } } /* ** Add an error message to pParse->zErrMsg and increment pParse->nErr. ** The following formatting characters are allowed: |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
349 350 351 352 353 354 355 | for(i=0; i<16 && i<pMem->n; i++){ char z = pMem->z[i]; if( z<32 || z>126 ) *zCsr++ = '.'; else *zCsr++ = z; } zCsr += sqlite4_snprintf(zCsr, 100, "]%s", encnames[pMem->enc]); | < < < | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | for(i=0; i<16 && i<pMem->n; i++){ char z = pMem->z[i]; if( z<32 || z>126 ) *zCsr++ = '.'; else *zCsr++ = z; } zCsr += sqlite4_snprintf(zCsr, 100, "]%s", encnames[pMem->enc]); *zCsr = '\0'; }else if( f & MEM_Str ){ int j, k; zBuf[0] = ' '; if( f & MEM_Dyn ){ zBuf[1] = 'z'; assert( (f & (MEM_Static|MEM_Ephem))==0 ); |
︙ | ︙ | |||
525 526 527 528 529 530 531 | Mem *aMem = p->aMem; /* Copy of p->aMem */ Mem *pIn1 = 0; /* 1st input operand */ Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ int iCompare = 0; /* Result of last OP_Compare operation */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ | < | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | Mem *aMem = p->aMem; /* Copy of p->aMem */ Mem *pIn1 = 0; /* 1st input operand */ Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ int iCompare = 0; /* Result of last OP_Compare operation */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ #endif /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite4_step() verifies this */ |
︙ | ︙ | |||
794 795 796 797 798 799 800 | if( pOp->p1==SQLITE4_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ VdbeFrame *pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite4VdbeSetChanges(db, p->nChange); pc = sqlite4VdbeFrameRestore(pFrame); | < | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | if( pOp->p1==SQLITE4_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ VdbeFrame *pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite4VdbeSetChanges(db, p->nChange); pc = sqlite4VdbeFrameRestore(pFrame); if( pOp->p2==OE_Ignore ){ /* Instruction pc is the OP_Program that invoked the sub-program ** currently being halted. If the p2 instruction of this OP_Halt ** instruction is set to OE_Ignore, then the sub-program is throwing ** an IGNORE exception. In this case jump to the address specified ** as the p2 of the calling OP_Program. */ pc = p->aOp[pc].p2-1; |
︙ | ︙ | |||
880 881 882 883 884 885 886 | case OP_String8: { /* same as TK_STRING, out2-prerelease */ assert( pOp->p4.z!=0 ); pOp->opcode = OP_String; pOp->p1 = sqlite4Strlen30(pOp->p4.z); #ifndef SQLITE4_OMIT_UTF16 if( encoding!=SQLITE4_UTF8 ){ | | > | 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 | case OP_String8: { /* same as TK_STRING, out2-prerelease */ assert( pOp->p4.z!=0 ); pOp->opcode = OP_String; pOp->p1 = sqlite4Strlen30(pOp->p4.z); #ifndef SQLITE4_OMIT_UTF16 if( encoding!=SQLITE4_UTF8 ){ rc = sqlite4VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE4_UTF8, SQLITE4_STATIC, 0); if( rc==SQLITE4_TOOBIG ) goto too_big; if( SQLITE4_OK!=sqlite4VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->zMalloc==pOut->z ); assert( pOut->flags & MEM_Dyn ); pOut->zMalloc = 0; pOut->flags |= MEM_Static; pOut->flags &= ~MEM_Dyn; |
︙ | ︙ | |||
946 947 948 949 950 951 952 | /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ case OP_Blob: { /* out2-prerelease */ assert( pOp->p1 <= SQLITE4_MAX_LENGTH ); | | | 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 | /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ case OP_Blob: { /* out2-prerelease */ assert( pOp->p1 <= SQLITE4_MAX_LENGTH ); sqlite4VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Variable P1 P2 * P4 * ** |
︙ | ︙ | |||
1141 1142 1143 1144 1145 1146 1147 | pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; assert( pIn1!=pOut ); if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite4VdbeMemSetNull(pOut); break; } | < | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 | pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; assert( pIn1!=pOut ); if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite4VdbeMemSetNull(pOut); break; } Stringify(pIn1, encoding); Stringify(pIn2, encoding); nByte = pIn1->n + pIn2->n; if( nByte>db->aLimit[SQLITE4_LIMIT_LENGTH] ){ goto too_big; } MemSetTypeFlag(pOut, MEM_Str); |
︙ | ︙ | |||
1382 1383 1384 1385 1386 1387 1388 | ctx.isError = 0; if( ctx.pFunc->flags & SQLITE4_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } | < < | 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 | ctx.isError = 0; if( ctx.pFunc->flags & SQLITE4_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ /* If any auxiliary data functions have been called by this user function, ** immediately call the destructor for any non-static values. */ if( ctx.pVdbeFunc ){ sqlite4VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1); pOp->p4.pVdbeFunc = ctx.pVdbeFunc; |
︙ | ︙ | |||
1583 1584 1585 1586 1587 1588 1589 | case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE4_AFF_TEXT, encoding); | < | | 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 | case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE4_AFF_TEXT, encoding); assert( pIn1->flags & MEM_Str || db->mallocFailed ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob); UPDATE_MAX_BLOBSIZE(pIn1); break; } /* Opcode: ToBlob P1 * * * * ** ** Force the value in register P1 to be a BLOB. |
︙ | ︙ | |||
2197 2198 2199 2200 2201 2202 2203 | ** the result to register P3. No affinity transformations are applied to ** the input values before they are encoded. ** ** If the OPFLAG_SEQCOUNT bit of P5 is set, then a sequence number ** (unique within the cursor) is appended to the record. The sole purpose ** of this is to ensure that the key blob is unique within the cursors table. ** | | | > | | 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 | ** the result to register P3. No affinity transformations are applied to ** the input values before they are encoded. ** ** If the OPFLAG_SEQCOUNT bit of P5 is set, then a sequence number ** (unique within the cursor) is appended to the record. The sole purpose ** of this is to ensure that the key blob is unique within the cursors table. ** ** If the OPFLAG_PARTIALKEY bit of P5 is set, that means the value supplied ** for N is not the true number of values in the key, only the number that ** need to be encoded for this operation. This effects the encoding of ** final BLOBs. */ case OP_MakeIdxKey: { VdbeCursor *pC; KeyInfo *pKeyInfo; Mem *pData0; /* First in array of input registers */ u8 *aRec; /* The constructed database key */ int nRec; /* Size of aRec[] in bytes */ |
︙ | ︙ | |||
2232 2233 2234 2235 2236 2237 2238 | do { nSeq++; aSeq[sizeof(aSeq)-nSeq] = (u8)(iSeq & 0x007F); iSeq = iSeq >> 7; }while( iSeq ); aSeq[sizeof(aSeq)-nSeq] |= 0x80; } | < < < > | | > | 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 | do { nSeq++; aSeq[sizeof(aSeq)-nSeq] = (u8)(iSeq & 0x007F); iSeq = iSeq >> 7; }while( iSeq ); aSeq[sizeof(aSeq)-nSeq] |= 0x80; } memAboutToChange(p, pOut); nField = pKeyInfo->nField; if( pOp->p4type==P4_INT32 && pOp->p4.i ){ nField = pOp->p4.i; assert( nField<=pKeyInfo->nField ); } rc = sqlite4VdbeEncodeKey( db, pData0, nField, nField+(pOp->p5 & OPFLAG_PARTIALKEY), pC->iRoot, pKeyInfo, &aRec, &nRec, nSeq ); if( rc ){ sqlite4DbFree(db, aRec); }else{ if( nSeq ){ memcpy(&aRec[nRec], &aSeq[sizeof(aSeq)-nSeq], nSeq); } rc = sqlite4VdbeMemSetStr(pOut, (char *)aRec, nRec+nSeq, 0, SQLITE4_DYNAMIC, 0); REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); } break; } |
︙ | ︙ | |||
2329 2330 2331 2332 2333 2334 2335 | ** expand all zero-blobs. */ for(pMem=pData0; pMem<=pLast; pMem++){ assert( memIsValid(pMem) ); if( zAffinity ){ applyAffinity(pMem, *(zAffinity++), encoding); } | < < < | > | > | | 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 | ** expand all zero-blobs. */ for(pMem=pData0; pMem<=pLast; pMem++){ assert( memIsValid(pMem) ); if( zAffinity ){ applyAffinity(pMem, *(zAffinity++), encoding); } } /* Compute the key (if this is a MakeKey opcode) */ if( pC ){ aRec = 0; rc = sqlite4VdbeEncodeKey(db, pData0, pC->pKeyInfo->nField, pC->pKeyInfo->nField, pC->iRoot, pC->pKeyInfo, &aRec, &nRec, 0 ); if( rc ){ sqlite4DbFree(db, aRec); }else{ rc = sqlite4VdbeMemSetStr(pKeyOut, (char *)aRec, nRec, 0, SQLITE4_DYNAMIC, 0); REGISTER_TRACE(keyReg, pKeyOut); UPDATE_MAX_BLOBSIZE(pKeyOut); } } /* If P3 is not 0, compute the data rescord */ if( rc==SQLITE4_OK && pOp->p3 ){ assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); aRec = 0; rc = sqlite4VdbeEncodeData(db, pData0, nField, &aRec, &nRec); if( rc ){ sqlite4DbFree(db, aRec); }else{ rc = sqlite4VdbeMemSetStr(pOut, (char *)aRec, nRec, 0, SQLITE4_DYNAMIC,0); REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); } } break; } |
︙ | ︙ | |||
2578 2579 2580 2581 2582 2583 2584 | p->expired = 0; } break; } /* Opcode: VerifyCookie P1 P2 P3 * * ** | | | 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 | p->expired = 0; } break; } /* Opcode: VerifyCookie P1 P2 P3 * * ** ** Check the value of global database parameter number 0 (the ** schema version) and make sure it is equal to P2 and that the ** generation counter on the local schema parse equals P3. ** ** P1 is the database number which is 0 for the main database file ** and 1 for the file holding temporary tables and some higher number ** for auxiliary databases. ** |
︙ | ︙ | |||
2996 2997 2998 2999 3000 3001 3002 | /* Encode a database key consisting of the contents of the P4 registers ** starting at register P3. Have the vdbecodec module allocate an extra ** free byte at the end of the database key (see below). */ op = pOp->opcode; nField = pOp->p4.i; pIn3 = &aMem[pOp->p3]; rc = sqlite4VdbeEncodeKey( | > | | 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 | /* Encode a database key consisting of the contents of the P4 registers ** starting at register P3. Have the vdbecodec module allocate an extra ** free byte at the end of the database key (see below). */ op = pOp->opcode; nField = pOp->p4.i; pIn3 = &aMem[pOp->p3]; rc = sqlite4VdbeEncodeKey( db, pIn3, nField, nField+(pOp->p5 & OPFLAG_PARTIALKEY), pC->iRoot, pC->pKeyInfo, &aProbe, &nProbe, 1 ); /* Opcode search-dir increment-key ** -------------------------------------- ** SeekLt -1 no ** SeekLe -1 yes ** SeekGe +1 no |
︙ | ︙ | |||
3057 3058 3059 3060 3061 3062 3063 | KVSize nKey; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isTable ); pKVCur = pC->pKVCur; | | | 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 | KVSize nKey; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isTable ); pKVCur = pC->pKVCur; rc = sqlite4VdbeEncodeKey(db, aMem+pOp->p2, 1, 1, pC->iRoot, 0, &aKey, &nKey, 0); if( rc==SQLITE4_OK ){ rc = sqlite4KVCursorSeek(pKVCur, aKey, nKey, 0); if( rc==SQLITE4_NOTFOUND ) rc = SQLITE4_CORRUPT_BKPT; } sqlite4DbFree(db, aKey); break; |
︙ | ︙ | |||
3134 3135 3136 3137 3138 3139 3140 | assert( pOp->p4type==P4_INT32 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pIn3 = &aMem[pOp->p3]; assert( pC->pKVCur!=0 ); assert( pC->isTable==0 || pOp->opcode==OP_NotExists ); if( pOp->p4.i>0 ){ | | > | > | 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 | assert( pOp->p4type==P4_INT32 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pIn3 = &aMem[pOp->p3]; assert( pC->pKVCur!=0 ); assert( pC->isTable==0 || pOp->opcode==OP_NotExists ); if( pOp->p4.i>0 ){ rc = sqlite4VdbeEncodeKey( db, pIn3, pOp->p4.i, pOp->p4.i + (pOp->p5 & OPFLAG_PARTIALKEY), pC->iRoot, pC->pKeyInfo, &pProbe, &nProbe, 0 ); pFree = pProbe; }else{ pProbe = (KVByteArray*)pIn3->z; nProbe = pIn3->n; pFree = 0; } if( rc==SQLITE4_OK ){ |
︙ | ︙ | |||
3434 3435 3436 3437 3438 3439 3440 | iKey = pKey->u.i; }else{ /* assert( pOp->opcode==OP_InsertInt ); */ iKey = pOp->p3; } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; | < | 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 | iKey = pKey->u.i; }else{ /* assert( pOp->opcode==OP_InsertInt ); */ iKey = pOp->p3; } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pData->flags & MEM_Null ){ pData->z = 0; pData->n = 0; }else{ assert( pData->flags & (MEM_Blob|MEM_Str) ); } n = sqlite4PutVarint64(aKey, pC->iRoot); |
︙ | ︙ | |||
3517 3518 3519 3520 3521 3522 3523 | pIn3 = &aMem[pOp->p3]; if( (pIn3->flags & MEM_Blob) && pIn3->n==nKey && 0==memcmp(pIn3->z, aKey, nKey) ){ pc = pOp->p2-1; }else{ | | < < < < < < < < < < > | 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 | pIn3 = &aMem[pOp->p3]; if( (pIn3->flags & MEM_Blob) && pIn3->n==nKey && 0==memcmp(pIn3->z, aKey, nKey) ){ pc = pOp->p2-1; }else{ sqlite4VdbeMemSetStr(pIn3, (const char*)aKey, nKey, 0, SQLITE4_TRANSIENT,0); } break; }; /* Opcode: SorterData P1 P2 * * * ** ** Write into register P2 the current sorter data for sorter cursor P1. */ /* Opcode: RowData P1 P2 * * * ** ** Write into register P2 the complete row data for cursor P1. ** There is no interpretation of the data. ** It is just copied onto the P2 register exactly as ** it is found in the database file. ** ** If the P1 cursor must be pointing to a valid row (not a NULL row) ** of a real table, not a pseudo-table. */ /* Opcode: RowKey P1 P2 * * * ** ** Write into register P2 the complete row key for cursor P1. ** There is no interpretation of the data. ** The key is copied onto the P3 register exactly as ** it is found in the database file. ** ** If the P1 cursor must be pointing to a valid row (not a NULL row) ** of a real table, not a pseudo-table. */ case OP_SorterData: case OP_RowKey: case OP_RowData: { VdbeCursor *pC; KVCursor *pCrsr; const KVByteArray *pData; KVSize nData; |
︙ | ︙ | |||
3584 3585 3586 3587 3588 3589 3590 | rc = sqlite4KVCursorKey(pCrsr, &pData, &nData); }else{ rc = sqlite4KVCursorData(pCrsr, 0, -1, &pData, &nData); } if( rc==SQLITE4_OK && nData>db->aLimit[SQLITE4_LIMIT_LENGTH] ){ goto too_big; } | | | 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 | rc = sqlite4KVCursorKey(pCrsr, &pData, &nData); }else{ rc = sqlite4KVCursorData(pCrsr, 0, -1, &pData, &nData); } if( rc==SQLITE4_OK && nData>db->aLimit[SQLITE4_LIMIT_LENGTH] ){ goto too_big; } sqlite4VdbeMemSetStr(pOut, (const char*)pData, nData, 0, SQLITE4_TRANSIENT,0); pOut->enc = SQLITE4_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Rowid P1 P2 * * * ** |
︙ | ︙ | |||
3646 3647 3648 3649 3650 3651 3652 | case OP_NullRow: { VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pC->nullRow = 1; | < | 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 | case OP_NullRow: { VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pC->nullRow = 1; break; } /* Opcode: Last P1 P2 * * * ** ** The next use of the Rowid or Column or Next instruction for P1 ** will refer to the last entry in the database table or index. |
︙ | ︙ | |||
3787 3788 3789 3790 3791 3792 3793 | #ifdef SQLITE4_TEST sqlite4_search_count++; #endif }else if( rc==SQLITE4_NOTFOUND ){ pC->nullRow = 1; rc = SQLITE4_OK; } | < | 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 | #ifdef SQLITE4_TEST sqlite4_search_count++; #endif }else if( rc==SQLITE4_NOTFOUND ){ pC->nullRow = 1; rc = SQLITE4_OK; } break; } /* Opcode: SorterInsert P1 P2 P3 */ /* Opcode: IdxInsert P1 P2 P3 * P5 |
︙ | ︙ | |||
4105 4106 4107 4108 4109 4110 4111 | CHECK_FOR_INTERRUPT; pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p3]; if( (pIn1->flags & MEM_RowSet) && (aKey = sqlite4RowSetRead(pIn1->u.pRowSet, &nKey)) ){ | | > | 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 | CHECK_FOR_INTERRUPT; pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p3]; if( (pIn1->flags & MEM_RowSet) && (aKey = sqlite4RowSetRead(pIn1->u.pRowSet, &nKey)) ){ rc = sqlite4VdbeMemSetStr(pOut, (char const *)aKey, nKey, 0, SQLITE4_TRANSIENT, 0); sqlite4RowSetNext(pIn1->u.pRowSet); }else{ /* The RowSet is empty */ sqlite4VdbeMemSetNull(pIn1); pc = pOp->p2 - 1; } |
︙ | ︙ | |||
4201 4202 4203 4204 4205 4206 4207 | assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); assert( pProgram->nCsr==pFrame->nChildCsr ); assert( pc==pFrame->pc ); } p->nFrame++; pFrame->pParent = p->pFrame; | < | 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 | assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); assert( pProgram->nCsr==pFrame->nChildCsr ); assert( pc==pFrame->pc ); } p->nFrame++; pFrame->pParent = p->pFrame; pFrame->nChange = p->nChange; p->nChange = 0; p->pFrame = pFrame; p->aMem = aMem = &VdbeFrameMem(pFrame)[-1]; p->nMem = pFrame->nChildMem; p->nCursor = (u16)pFrame->nChildCsr; p->apCsr = (VdbeCursor **)&aMem[p->nMem+1]; |
︙ | ︙ | |||
4826 4827 4828 4829 4830 4831 4832 | apArg[i] = pX; pX++; } db->vtabOnConflict = pOp->p5; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); db->vtabOnConflict = vtabOnConflict; importVtabErrMsg(p, pVtab); | < < < < | 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 | apArg[i] = pX; pX++; } db->vtabOnConflict = pOp->p5; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); db->vtabOnConflict = vtabOnConflict; importVtabErrMsg(p, pVtab); if( rc==SQLITE4_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ if( pOp->p5==OE_Ignore ){ rc = SQLITE4_OK; }else{ p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); } }else{ |
︙ | ︙ | |||
5082 5083 5084 5085 5086 5087 5088 | sqlite4ResetInternalSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: | < | 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 | sqlite4ResetInternalSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: return rc; /* Jump to here if a string or blob larger than SQLITE4_MAX_LENGTH ** is encountered. */ too_big: sqlite4SetString(&p->zErrMsg, db, "string or blob too big"); |
︙ | ︙ |
Changes to src/vdbe.h.
︙ | ︙ | |||
200 201 202 203 204 205 206 | int sqlite4VdbeAssertMayAbort(Vdbe *, int); void sqlite4VdbeTrace(Vdbe*,FILE*); #endif void sqlite4VdbeResetStepResult(Vdbe*); void sqlite4VdbeRewind(Vdbe*); int sqlite4VdbeReset(Vdbe*); void sqlite4VdbeSetNumCols(Vdbe*,int); | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | int sqlite4VdbeAssertMayAbort(Vdbe *, int); void sqlite4VdbeTrace(Vdbe*,FILE*); #endif void sqlite4VdbeResetStepResult(Vdbe*); void sqlite4VdbeRewind(Vdbe*); int sqlite4VdbeReset(Vdbe*); void sqlite4VdbeSetNumCols(Vdbe*,int); int sqlite4VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*,void*)); void sqlite4VdbeCountChanges(Vdbe*); sqlite4 *sqlite4VdbeDb(Vdbe*); void sqlite4VdbeSetSql(Vdbe*, const char *z, int n); void sqlite4VdbeSwap(Vdbe*,Vdbe*); VdbeOp *sqlite4VdbeTakeOpArray(Vdbe*, int*, int*); sqlite4_value *sqlite4VdbeGetValue(Vdbe*, int, u8); void sqlite4VdbeSetVarmask(Vdbe*, int); |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
54 55 56 57 58 59 60 | KVStore *pTmpKV; /* Separate file holding a temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */ int iRoot; /* Root page of the table */ int pseudoTableReg; /* Register holding pseudotable content. */ int nField; /* Number of fields in the header */ Bool zeroed; /* True if zeroed out and ready for reuse */ | < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | KVStore *pTmpKV; /* Separate file holding a temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */ int iRoot; /* Root page of the table */ int pseudoTableReg; /* Register holding pseudotable content. */ int nField; /* Number of fields in the header */ Bool zeroed; /* True if zeroed out and ready for reuse */ Bool atFirst; /* True if pointing to first entry */ Bool nullRow; /* True if pointing to a row with no data */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ sqlite4_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite4_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred move-to */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ Fts5Cursor *pFts; /* Fts5 cursor object (or NULL) */ /* Result of last sqlite4-Moveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ int seekResult; }; |
︙ | ︙ | |||
109 110 111 112 113 114 115 | u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */ int nOnceFlag; /* Number of entries in aOnceFlag */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u16 nCursor; /* Number of entries in apCsr */ void *token; /* Copy of SubProgram.token */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ | < | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */ int nOnceFlag; /* Number of entries in aOnceFlag */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u16 nCursor; /* Number of entries in apCsr */ void *token; /* Copy of SubProgram.token */ int nChildMem; /* Number of memory cells for child frame */ int nChildCsr; /* Number of cursors for child frame */ int nChange; /* Statement changes (Vdbe.nChanges) */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ }; #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) /* |
︙ | ︙ | |||
132 133 134 135 136 137 138 | */ struct Mem { sqlite4 *db; /* The associated database connection */ char *z; /* String or BLOB value */ double r; /* Real value */ union { i64 i; /* Integer value used when MEM_Int is set in flags */ | < | > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | */ struct Mem { sqlite4 *db; /* The associated database connection */ char *z; /* String or BLOB value */ double r; /* Real value */ union { i64 i; /* Integer value used when MEM_Int is set in flags */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE4_NULL, SQLITE4_TEXT, SQLITE4_INTEGER, etc */ u8 enc; /* SQLITE4_UTF8, SQLITE4_UTF16BE, SQLITE4_UTF16LE */ #ifdef SQLITE4_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ #endif void (*xDel)(void*,void*); /* Function to delete Mem.z */ void *pDelArg; /* First argument to xDel() */ char *zMalloc; /* Dynamic buffer allocated by sqlite4_malloc() */ }; /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** ** If the MEM_Null flag is set, then the value is an SQL NULL value. |
︙ | ︙ | |||
182 183 184 185 186 187 188 | ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ | < | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ /* ** Clear any existing type flags from a Mem and replace them with f */ #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask))|f) /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. */ #ifdef SQLITE4_DEBUG #define memIsValid(M) ((M)->flags & MEM_Invalid)==0 |
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** invocations. */ struct VdbeFunc { FuncDef *pFunc; /* The definition of the function */ int nAux; /* Number of entries allocated for apAux[] */ struct AuxData { void *pAux; /* Aux data for the i-th argument */ | | > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | ** invocations. */ struct VdbeFunc { FuncDef *pFunc; /* The definition of the function */ int nAux; /* Number of entries allocated for apAux[] */ struct AuxData { void *pAux; /* Aux data for the i-th argument */ void (*xDelete)(void*,void*); /* Destructor for the aux data */ void *pDeleteArg; /* First argument to xDelete */ } apAux[1]; /* One slot for each function argument */ }; /* ** The "context" argument for a installable function. A pointer to an ** instance of this structure is the first argument to the routines used ** implement the SQL functions. |
︙ | ︙ | |||
375 376 377 378 379 380 381 382 383 384 385 | u8 **pzOut, /* The output data record */ int *pnOut /* Bytes of content in pzOut */ ); int sqlite4VdbeEncodeKey( sqlite4 *db, /* The database connection */ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence information */ u8 **pzOut, /* Write the resulting key here */ int *pnOut, /* Number of bytes in the key */ | > | | > < | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | u8 **pzOut, /* The output data record */ int *pnOut /* Bytes of content in pzOut */ ); int sqlite4VdbeEncodeKey( sqlite4 *db, /* The database connection */ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int nInTotal, /* Number of values in complete key */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence information */ u8 **pzOut, /* Write the resulting key here */ int *pnOut, /* Number of bytes in the key */ int nExtra /* Append extra bytes on end of key */ ); int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v); int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*); int sqlite4VdbeShortKey(const u8 *, int, int); int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*); int sqlite4VdbeExec(Vdbe*); int sqlite4VdbeList(Vdbe*); int sqlite4VdbeHalt(Vdbe*); int sqlite4VdbeChangeEncoding(Mem *, int); int sqlite4VdbeMemTooBig(Mem*); int sqlite4VdbeMemCopy(Mem*, const Mem*); void sqlite4VdbeMemShallowCopy(Mem*, const Mem*, int); void sqlite4VdbeMemMove(Mem*, Mem*); int sqlite4VdbeMemNulTerminate(Mem*); int sqlite4VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*,void*),void*); void sqlite4VdbeMemSetInt64(Mem*, i64); #ifdef SQLITE4_OMIT_FLOATING_POINT # define sqlite4VdbeMemSetDouble sqlite4VdbeMemSetInt64 #else void sqlite4VdbeMemSetDouble(Mem*, double); #endif void sqlite4VdbeMemSetNull(Mem*); int sqlite4VdbeMemMakeWriteable(Mem*); int sqlite4VdbeMemStringify(Mem*, int); i64 sqlite4VdbeIntValue(Mem*); int sqlite4VdbeMemIntegerify(Mem*); double sqlite4VdbeRealValue(Mem*); void sqlite4VdbeIntegerAffinity(Mem*); int sqlite4VdbeMemRealify(Mem*); |
︙ | ︙ | |||
451 452 453 454 455 456 457 | #ifdef SQLITE4_DEBUG void sqlite4VdbePrintSql(Vdbe*); void sqlite4VdbeMemPrettyPrint(Mem *pMem, char *zBuf); #endif int sqlite4VdbeMemHandleBom(Mem *pMem); | < < < | 449 450 451 452 453 454 455 456 | #ifdef SQLITE4_DEBUG void sqlite4VdbePrintSql(Vdbe*); void sqlite4VdbeMemPrettyPrint(Mem *pMem, char *zBuf); #endif int sqlite4VdbeMemHandleBom(Mem *pMem); #endif /* !defined(_VDBEINT_H_) */ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | ** ** This file contains code use to implement APIs that are part of the ** VDBE. */ #include "sqliteInt.h" #include "vdbeInt.h" | < < < < < < < < < < < < < < < | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** ** This file contains code use to implement APIs that are part of the ** VDBE. */ #include "sqliteInt.h" #include "vdbeInt.h" /* ** Check on a Vdbe to make sure it has not been finalized. Log ** an error and return true if it has been finalized (or is otherwise ** invalid). Return false if it is ok. */ static int vdbeSafety(Vdbe *p){ if( p->db==0 ){ |
︙ | ︙ | |||
135 136 137 138 139 140 141 | /**************************** sqlite4_value_ ******************************* ** The following routines extract information from a Mem or sqlite4_value ** structure. */ const void *sqlite4_value_blob(sqlite4_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ | < | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /**************************** sqlite4_value_ ******************************* ** The following routines extract information from a Mem or sqlite4_value ** structure. */ const void *sqlite4_value_blob(sqlite4_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ p->flags &= ~MEM_Str; p->flags |= MEM_Blob; return p->n ? p->z : 0; }else{ return sqlite4_value_text(pVal); } } |
︙ | ︙ | |||
189 190 191 192 193 194 195 | ** then sets the error code to SQLITE4_TOOBIG */ static void setResultStrOrError( sqlite4_context *pCtx, /* Function context */ const char *z, /* String pointer */ int n, /* Bytes in string, or negative */ u8 enc, /* Encoding of z. 0 for BLOBs */ | | > | | > | | | | > | | > | | > | | > | < < < < | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | ** then sets the error code to SQLITE4_TOOBIG */ static void setResultStrOrError( sqlite4_context *pCtx, /* Function context */ const char *z, /* String pointer */ int n, /* Bytes in string, or negative */ u8 enc, /* Encoding of z. 0 for BLOBs */ void (*xDel)(void*,void*), /* Destructor function */ void *pDelArg /* First argument to xDel() */ ){ if( xDel==SQLITE4_DYNAMIC ){ assert( sqlite4MemdebugHasType(z, MEMTYPE_HEAP) ); assert( sqlite4MemdebugNoType(z, ~MEMTYPE_HEAP) ); sqlite4MemdebugSetType((char*)z, MEMTYPE_DB | MEMTYPE_HEAP); } if( sqlite4VdbeMemSetStr(&pCtx->s, z, n, enc, xDel,pDelArg)==SQLITE4_TOOBIG ){ sqlite4_result_error_toobig(pCtx); } } void sqlite4_result_blob( sqlite4_context *pCtx, const void *z, int n, void (*xDel)(void*,void*), void *pDelArg ){ assert( n>=0 ); assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); setResultStrOrError(pCtx, z, n, 0, xDel, pDelArg); } void sqlite4_result_double(sqlite4_context *pCtx, double rVal){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemSetDouble(&pCtx->s, rVal); } void sqlite4_result_error(sqlite4_context *pCtx, const char *z, int n){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE4_ERROR; sqlite4VdbeMemSetStr(&pCtx->s, z, n, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); } #ifndef SQLITE4_OMIT_UTF16 void sqlite4_result_error16(sqlite4_context *pCtx, const void *z, int n){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE4_ERROR; sqlite4VdbeMemSetStr(&pCtx->s, z, n, SQLITE4_UTF16NATIVE,SQLITE4_TRANSIENT,0); } #endif void sqlite4_result_int(sqlite4_context *pCtx, int iVal){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemSetInt64(&pCtx->s, (i64)iVal); } void sqlite4_result_int64(sqlite4_context *pCtx, i64 iVal){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemSetInt64(&pCtx->s, iVal); } void sqlite4_result_null(sqlite4_context *pCtx){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemSetNull(&pCtx->s); } void sqlite4_result_text( sqlite4_context *pCtx, const char *z, int n, void (*xDel)(void*,void*), void *pDelArg ){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE4_UTF8, xDel, pDelArg); } #ifndef SQLITE4_OMIT_UTF16 void sqlite4_result_text16( sqlite4_context *pCtx, const void *z, int n, void (*xDel)(void*,void*), void *pDelArg ){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE4_UTF16NATIVE, xDel, pDelArg); } void sqlite4_result_text16be( sqlite4_context *pCtx, const void *z, int n, void (*xDel)(void*,void*), void *pDelArg ){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE4_UTF16BE, xDel, pDelArg); } void sqlite4_result_text16le( sqlite4_context *pCtx, const void *z, int n, void (*xDel)(void*,void*), void *pDelArg ){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE4_UTF16LE, xDel, pDelArg); } #endif /* SQLITE4_OMIT_UTF16 */ void sqlite4_result_value(sqlite4_context *pCtx, sqlite4_value *pValue){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemCopy(&pCtx->s, pValue); } void sqlite4_result_error_code(sqlite4_context *pCtx, int errCode){ pCtx->isError = errCode; if( pCtx->s.flags & MEM_Null ){ sqlite4VdbeMemSetStr(&pCtx->s, sqlite4ErrStr(errCode), -1, SQLITE4_UTF8, SQLITE4_STATIC, 0); } } /* Force an SQLITE4_TOOBIG error. */ void sqlite4_result_error_toobig(sqlite4_context *pCtx){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE4_TOOBIG; sqlite4VdbeMemSetStr(&pCtx->s, "string or blob too big", -1, SQLITE4_UTF8, SQLITE4_STATIC, 0); } /* An SQLITE4_NOMEM error. */ void sqlite4_result_error_nomem(sqlite4_context *pCtx){ assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); sqlite4VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE4_NOMEM; |
︙ | ︙ | |||
587 588 589 590 591 592 593 | ** argument to the user-function defined by pCtx. Any previous value is ** deleted by calling the delete function specified when it was set. */ void sqlite4_set_auxdata( sqlite4_context *pCtx, int iArg, void *pAux, | | > | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | ** argument to the user-function defined by pCtx. Any previous value is ** deleted by calling the delete function specified when it was set. */ void sqlite4_set_auxdata( sqlite4_context *pCtx, int iArg, void *pAux, void (*xDelete)(void*,void*), void *pDeleteArg ){ struct AuxData *pAuxData; VdbeFunc *pVdbeFunc; if( iArg<0 ) goto failed; assert( sqlite4_mutex_held(pCtx->s.db->mutex) ); pVdbeFunc = pCtx->pVdbeFunc; |
︙ | ︙ | |||
610 611 612 613 614 615 616 | memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux)); pVdbeFunc->nAux = iArg+1; pVdbeFunc->pFunc = pCtx->pFunc; } pAuxData = &pVdbeFunc->apAux[iArg]; if( pAuxData->pAux && pAuxData->xDelete ){ | | > | < < < < < < < < < < < < < < < < | 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux)); pVdbeFunc->nAux = iArg+1; pVdbeFunc->pFunc = pCtx->pFunc; } pAuxData = &pVdbeFunc->apAux[iArg]; if( pAuxData->pAux && pAuxData->xDelete ){ pAuxData->xDelete(pAuxData->pDeleteArg, pAuxData->pAux); } pAuxData->pAux = pAux; pAuxData->xDelete = xDelete; pAuxData->pDeleteArg = pDeleteArg; return; failed: if( xDelete ){ xDelete(pDeleteArg, pAux); } } /* ** Return the number of columns in the result set for the statement pStmt. */ int sqlite4_column_count(sqlite4_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; return pVm ? pVm->nResColumn : 0; |
︙ | ︙ | |||
741 742 743 744 745 746 747 | /**************************** sqlite4_column_ ******************************* ** The following routines are used to access elements of the current row ** in the result set. */ const void *sqlite4_column_blob(sqlite4_stmt *pStmt, int i){ const void *val; val = sqlite4_value_blob( columnMem(pStmt,i) ); | < < < < < | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 | /**************************** sqlite4_column_ ******************************* ** The following routines are used to access elements of the current row ** in the result set. */ const void *sqlite4_column_blob(sqlite4_stmt *pStmt, int i){ const void *val; val = sqlite4_value_blob( columnMem(pStmt,i) ); return val; } int sqlite4_column_bytes(sqlite4_stmt *pStmt, int i){ int val = sqlite4_value_bytes( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 | ** Bind a text or BLOB value. */ static int bindText( sqlite4_stmt *pStmt, /* The statement to bind against */ int i, /* Index of the parameter to bind */ const void *zData, /* Pointer to the data to be bound */ int nData, /* Number of bytes of data to be bound */ | | > | | | > | | 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | ** Bind a text or BLOB value. */ static int bindText( sqlite4_stmt *pStmt, /* The statement to bind against */ int i, /* Index of the parameter to bind */ const void *zData, /* Pointer to the data to be bound */ int nData, /* Number of bytes of data to be bound */ void (*xDel)(void*,void*), /* Destructor for the data */ void *pDelArg, /* First argument to xDel() */ u8 encoding /* Encoding for the data */ ){ Vdbe *p = (Vdbe *)pStmt; Mem *pVar; int rc; rc = vdbeUnbind(p, i); if( rc==SQLITE4_OK ){ if( zData!=0 ){ pVar = &p->aVar[i-1]; rc = sqlite4VdbeMemSetStr(pVar, zData, nData, encoding, xDel, pDelArg); if( rc==SQLITE4_OK && encoding!=0 ){ rc = sqlite4VdbeChangeEncoding(pVar, ENC(p->db)); } sqlite4Error(p->db, rc, 0); rc = sqlite4ApiExit(p->db, rc); } sqlite4_mutex_leave(p->db->mutex); }else if( xDel!=SQLITE4_STATIC && xDel!=SQLITE4_TRANSIENT ){ xDel(pDelArg, (void*)zData); } return rc; } /* ** Bind a blob value to an SQL statement variable. */ int sqlite4_bind_blob( sqlite4_stmt *pStmt, int i, const void *zData, int nData, void (*xDel)(void*,void*), void *pDelArg ){ return bindText(pStmt, i, zData, nData, xDel, pDelArg, 0); } int sqlite4_bind_double(sqlite4_stmt *pStmt, int i, double rValue){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); if( rc==SQLITE4_OK ){ sqlite4VdbeMemSetDouble(&p->aVar[i-1], rValue); |
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 | return rc; } int sqlite4_bind_text( sqlite4_stmt *pStmt, int i, const char *zData, int nData, | | > | | > | < < < | < > | < < < < < < < < < < | 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 | return rc; } int sqlite4_bind_text( sqlite4_stmt *pStmt, int i, const char *zData, int nData, void (*xDel)(void*,void*), void *pDelArg ){ return bindText(pStmt, i, zData, nData, xDel, pDelArg, SQLITE4_UTF8); } #ifndef SQLITE4_OMIT_UTF16 int sqlite4_bind_text16( sqlite4_stmt *pStmt, int i, const void *zData, int nData, void (*xDel)(void*,void*), void *pDelArg ){ return bindText(pStmt, i, zData, nData, xDel, pDelArg, SQLITE4_UTF16NATIVE); } #endif /* SQLITE4_OMIT_UTF16 */ int sqlite4_bind_value(sqlite4_stmt *pStmt, int i, const sqlite4_value *pValue){ int rc; switch( pValue->type ){ case SQLITE4_INTEGER: { rc = sqlite4_bind_int64(pStmt, i, pValue->u.i); break; } case SQLITE4_FLOAT: { rc = sqlite4_bind_double(pStmt, i, pValue->r); break; } case SQLITE4_BLOB: { rc = sqlite4_bind_blob(pStmt, i, pValue->z, pValue->n, SQLITE4_TRANSIENT, 0); break; } case SQLITE4_TEXT: { rc = bindText(pStmt,i, pValue->z, pValue->n, SQLITE4_TRANSIENT, 0, pValue->enc); break; } default: { rc = sqlite4_bind_null(pStmt, i); break; } } return rc; } /* ** Return the number of wildcards that can be potentially bound to. ** This routine is added to support DBD::SQLite. */ |
︙ | ︙ | |||
1204 1205 1206 1207 1208 1209 1210 | for(i=0; i<pFrom->nVar; i++){ sqlite4VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); } sqlite4_mutex_leave(pTo->db->mutex); return SQLITE4_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 | for(i=0; i<pFrom->nVar; i++){ sqlite4VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); } sqlite4_mutex_leave(pTo->db->mutex); return SQLITE4_OK; } /* ** Return the sqlite4* database handle to which the prepared statement given ** in the argument belongs. This is the same database handle that was ** the first argument to the sqlite4_prepare() that was used to create ** the statement in the first place. */ sqlite4 *sqlite4_db_handle(sqlite4_stmt *pStmt){ |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. */ #include "sqliteInt.h" #include "vdbeInt.h" | < < < < < < < < < < < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. */ #include "sqliteInt.h" #include "vdbeInt.h" /* ** Create a new virtual database engine. */ Vdbe *sqlite4VdbeCreate(sqlite4 *db){ Vdbe *p; p = sqlite4DbMallocZero(db, sizeof(Vdbe) ); if( p==0 ) return 0; |
︙ | ︙ | |||
148 149 150 151 152 153 154 | pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; #ifdef SQLITE4_DEBUG pOp->zComment = 0; | > | > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; #ifdef SQLITE4_DEBUG pOp->zComment = 0; if( p->db->flags & SQLITE4_VdbeAddopTrace ){ sqlite4VdbePrintOp(0, i, &p->aOp[i]); } #endif #ifdef VDBE_PROFILE pOp->cycles = 0; pOp->cnt = 0; #endif return i; } |
︙ | ︙ | |||
501 502 503 504 505 506 507 | } pOut->p3 = pIn->p3; pOut->p4type = P4_NOTUSED; pOut->p4.p = 0; pOut->p5 = 0; #ifdef SQLITE4_DEBUG pOut->zComment = 0; | | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 | } pOut->p3 = pIn->p3; pOut->p4type = P4_NOTUSED; pOut->p4.p = 0; pOut->p5 = 0; #ifdef SQLITE4_DEBUG pOut->zComment = 0; if( p->db->flags & SQLITE4_VdbeAddopTrace ){ sqlite4VdbePrintOp(0, i+addr, &p->aOp[i+addr]); } #endif } p->nOp += nOp; } return addr; |
︙ | ︙ | |||
1191 1192 1193 1194 1195 1196 1197 | if( sqlite4VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE4_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); if( z!=pMem->z ){ | | | 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 | if( sqlite4VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE4_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); if( z!=pMem->z ){ sqlite4VdbeMemSetStr(pMem, z, -1, SQLITE4_UTF8, 0, 0); }else{ assert( pMem->z!=0 ); pMem->n = sqlite4Strlen30(pMem->z); pMem->enc = SQLITE4_UTF8; } pMem->type = SQLITE4_TEXT; pMem++; |
︙ | ︙ | |||
1529 1530 1531 1532 1533 1534 1535 | v->nOnceFlag = pFrame->nOnceFlag; v->aOp = pFrame->aOp; v->nOp = pFrame->nOp; v->aMem = pFrame->aMem; v->nMem = pFrame->nMem; v->apCsr = pFrame->apCsr; v->nCursor = pFrame->nCursor; | < | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 | v->nOnceFlag = pFrame->nOnceFlag; v->aOp = pFrame->aOp; v->nOp = pFrame->nOp; v->aMem = pFrame->aMem; v->nMem = pFrame->nMem; v->apCsr = pFrame->apCsr; v->nCursor = pFrame->nCursor; v->nChange = pFrame->nChange; return pFrame->pc; } /* ** Close all cursors. ** |
︙ | ︙ | |||
1634 1635 1636 1637 1638 1639 1640 | ** to by zName will be freed by sqlite4DbFree() when the vdbe is destroyed. */ int sqlite4VdbeSetColName( Vdbe *p, /* Vdbe being configured */ int idx, /* Index of column zName applies to */ int var, /* One of the COLNAME_* constants */ const char *zName, /* Pointer to buffer containing name */ | | > > | | 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | ** to by zName will be freed by sqlite4DbFree() when the vdbe is destroyed. */ int sqlite4VdbeSetColName( Vdbe *p, /* Vdbe being configured */ int idx, /* Index of column zName applies to */ int var, /* One of the COLNAME_* constants */ const char *zName, /* Pointer to buffer containing name */ void (*xDel)(void*,void*) /* Memory management strategy for zName */ ){ int rc; Mem *pColName; assert( idx<p->nResColumn ); assert( var<COLNAME_N ); assert( xDel==SQLITE4_STATIC || xDel==SQLITE4_TRANSIENT || xDel==SQLITE4_DYNAMIC ); if( p->db->mallocFailed ){ assert( !zName || xDel!=SQLITE4_DYNAMIC ); return SQLITE4_NOMEM; } assert( p->aColName!=0 ); pColName = &(p->aColName[idx+var*p->nResColumn]); rc = sqlite4VdbeMemSetStr(pColName, zName, -1, SQLITE4_UTF8, xDel, 0); assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); return rc; } /* ** Free all Savepoint structures that correspond to transaction levels ** larger than iLevel. Passing iLevel==1 deletes all Savepoint structures. |
︙ | ︙ | |||
1972 1973 1974 1975 1976 1977 1978 | */ int sqlite4VdbeTransferError(Vdbe *p){ sqlite4 *db = p->db; int rc = p->rc; if( p->zErrMsg ){ u8 mallocFailed = db->mallocFailed; sqlite4BeginBenignMalloc(db->pEnv); | | > | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 | */ int sqlite4VdbeTransferError(Vdbe *p){ sqlite4 *db = p->db; int rc = p->rc; if( p->zErrMsg ){ u8 mallocFailed = db->mallocFailed; sqlite4BeginBenignMalloc(db->pEnv); sqlite4ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); sqlite4EndBenignMalloc(db->pEnv); db->mallocFailed = mallocFailed; db->errCode = rc; }else{ sqlite4Error(db, rc, 0); } return rc; |
︙ | ︙ | |||
2019 2020 2021 2022 2023 2024 2025 | if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite4_step(). For consistency (since sqlite4_step() was ** called), set the database error in this case as well. */ sqlite4Error(db, p->rc, 0); | | > | 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 | if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call ** to sqlite4_step(). For consistency (since sqlite4_step() was ** called), set the database error in this case as well. */ sqlite4Error(db, p->rc, 0); sqlite4ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); sqlite4DbFree(db, p->zErrMsg); p->zErrMsg = 0; } /* Reclaim all memory used by the VDBE */ Cleanup(p); |
︙ | ︙ | |||
2081 2082 2083 2084 2085 2086 2087 | */ void sqlite4VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ int i; for(i=0; i<pVdbeFunc->nAux; i++){ struct AuxData *pAux = &pVdbeFunc->apAux[i]; if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){ if( pAux->xDelete ){ | | | 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 | */ void sqlite4VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ int i; for(i=0; i<pVdbeFunc->nAux; i++){ struct AuxData *pAux = &pVdbeFunc->apAux[i]; if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){ if( pAux->xDelete ){ pAux->xDelete(pAux->pDeleteArg, pAux->pAux); } pAux->pAux = 0; } } } /* |
︙ | ︙ | |||
2219 2220 2221 2222 2223 2224 2225 | return 6; } if( flags&MEM_Real ){ return 7; } assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); n = pMem->n; | < < < | 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 | return 6; } if( flags&MEM_Real ){ return 7; } assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); n = pMem->n; assert( n>=0 ); return ((n*2) + 12 + ((flags&MEM_Str)!=0)); } /* ** Return the length of the data corresponding to the supplied serial-type. */ |
︙ | ︙ | |||
2335 2336 2337 2338 2339 2340 2341 | v >>= 8; } return len; } /* String or blob */ if( serial_type>=12 ){ | < | < < < < < < < < | 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 | v >>= 8; } return len; } /* String or blob */ if( serial_type>=12 ){ assert( pMem->n == (int)sqlite4VdbeSerialTypeLen(serial_type) ); assert( pMem->n<=nBuf ); len = pMem->n; memcpy(buf, pMem->z, len); return len; } /* NULL or constants 0 or 1 */ return 0; } |
︙ | ︙ |
Changes to src/vdbecodec.c.
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | e = (int)x; n += sqlite4GetVarint64(p->a+ofst+n, p->n-(ofst+n), &x); if( n!=size ) return SQLITE4_CORRUPT; r = (double)x; if( e&1 ) r = -r; if( e&2 ){ e = -(e>>2); while( e<=-10 ){ r /= 1.0e10; e += 10; } while( e<0 ){ r /= 10.0; e++; } }else{ e = e>>2; while( e>=10 ){ r *= 1.0e10; e -= 10; } while( e>0 ){ r *= 10.0; e--; } } sqlite4VdbeMemSetDouble(pOut, r); }else if( cclass==0 ){ if( size==0 ){ | > > > > | | | | > | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | e = (int)x; n += sqlite4GetVarint64(p->a+ofst+n, p->n-(ofst+n), &x); if( n!=size ) return SQLITE4_CORRUPT; r = (double)x; if( e&1 ) r = -r; if( e&2 ){ e = -(e>>2); if( e==0 ){ r *= 1e+300*1e+300; }else{ while( e<=-10 ){ r /= 1.0e10; e += 10; } while( e<0 ){ r /= 10.0; e++; } } }else{ e = e>>2; while( e>=10 ){ r *= 1.0e10; e -= 10; } while( e>0 ){ r *= 10.0; e--; } } sqlite4VdbeMemSetDouble(pOut, r); }else if( cclass==0 ){ if( size==0 ){ sqlite4VdbeMemSetStr(pOut, "", 0, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); }else if( p->a[ofst]>0x02 ){ sqlite4VdbeMemSetStr(pOut, (char*)(p->a+ofst), size, SQLITE4_UTF8, SQLITE4_TRANSIENT, 0); }else{ static const u8 enc[] = {SQLITE4_UTF8,SQLITE4_UTF16LE,SQLITE4_UTF16BE }; sqlite4VdbeMemSetStr(pOut, (char*)(p->a+ofst+1), size-1, enc[p->a[ofst]], SQLITE4_TRANSIENT, 0); } }else{ sqlite4VdbeMemSetStr(pOut, (char*)(p->a+ofst), size, 0, SQLITE4_TRANSIENT, 0); } } testcase( i==iVal ); testcase( i==iVal+1 ); if( i<=iVal ){ if( pDefault ){ sqlite4VdbeMemShallowCopy(pOut, pDefault, MEM_Static); |
︙ | ︙ | |||
501 502 503 504 505 506 507 | } /* ** Encode a single column of the key */ static int encodeOneKeyValue( | | | | > | | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | } /* ** Encode a single column of the key */ static int encodeOneKeyValue( KeyEncoder *p, /* Key encoder context */ Mem *pMem, /* Value to be encoded */ u8 sortOrder, /* Sort order for this value */ u8 isLastValue, /* True if this is the last value in the key */ CollSeq *pColl /* Collating sequence for the value */ ){ int flags = pMem->flags; int i, e; int n; int iStart = p->nOut; if( flags & MEM_Null ){ if( enlargeEncoderAllocation(p, 1) ) return SQLITE4_NOMEM; |
︙ | ︙ | |||
606 607 608 609 610 611 612 | p->nOut += n; } p->aOut[p->nOut++] = 0x00; /* Release any memory allocated to hold the translated text */ if( pEnc==&sMem ) sqlite4VdbeMemRelease(&sMem); | > > > > > > > | < > | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 | p->nOut += n; } p->aOut[p->nOut++] = 0x00; /* Release any memory allocated to hold the translated text */ if( pEnc==&sMem ) sqlite4VdbeMemRelease(&sMem); }else if( isLastValue ){ /* A BLOB value that is the right-most value of a key */ assert( flags & MEM_Blob ); if( enlargeEncoderAllocation(p, pMem->n+1) ) return SQLITE4_NOMEM; p->aOut[p->nOut++] = 0x26; memcpy(p->aOut+p->nOut, pMem->z, pMem->n); p->nOut += pMem->n; }else{ /* A BLOB value that is followed by other values */ const unsigned char *a; unsigned char s, t; assert( flags & MEM_Blob ); n = pMem->n; a = (u8*)pMem->z; s = 1; t = 0; |
︙ | ︙ | |||
676 677 678 679 680 681 682 683 684 685 686 687 688 689 | while( *(p++) ); break; case 0xDB: /* Text (descending index) */ case 0xDA: /* Blob (descending index) */ while( (0xFF!=*(p++)) ); break; case 0x22: case 0xDD: /* Large positive number */ case 0x14: case 0xEB: /* Small negative number */ case 0x16: case 0xE9: /* Small positive number */ case 0x08: case 0xF7: { /* Large negative number */ u8 d; /* Value of byte following "c" */ | > > > > | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 | while( *(p++) ); break; case 0xDB: /* Text (descending index) */ case 0xDA: /* Blob (descending index) */ while( (0xFF!=*(p++)) ); break; case 0x26: /* Blob-final (ascending) */ case 0xD9: /* Blob-final (descending) */ return nKey; case 0x22: case 0xDD: /* Large positive number */ case 0x14: case 0xEB: /* Small negative number */ case 0x16: case 0xE9: /* Small positive number */ case 0x08: case 0xF7: { /* Large negative number */ u8 d; /* Value of byte following "c" */ |
︙ | ︙ | |||
722 723 724 725 726 727 728 729 730 731 732 | ** Space to hold the key is obtained from sqlite4DbMalloc() and should ** be freed by the caller using sqlite4DbFree() to avoid a memory leak. */ int sqlite4VdbeEncodeKey( sqlite4 *db, /* The database connection */ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence and sort-order info */ u8 **paOut, /* Write the resulting key here */ int *pnOut, /* Number of bytes in the key */ | > | | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | ** Space to hold the key is obtained from sqlite4DbMalloc() and should ** be freed by the caller using sqlite4DbFree() to avoid a memory leak. */ int sqlite4VdbeEncodeKey( sqlite4 *db, /* The database connection */ Mem *aIn, /* Values to be encoded */ int nIn, /* Number of entries in aIn[] */ int nInTotal, /* Number of values in a complete key */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence and sort-order info */ u8 **paOut, /* Write the resulting key here */ int *pnOut, /* Number of bytes in the key */ int nExtra /* extra bytes of space appended to the key */ ){ int i; int rc = SQLITE4_OK; KeyEncoder x; u8 *so; CollSeq **aColl; |
︙ | ︙ | |||
749 750 751 752 753 754 755 | *pnOut = 0; if( enlargeEncoderAllocation(&x, (nIn+1)*10) ) return SQLITE4_NOMEM; x.nOut = sqlite4PutVarint64(x.aOut, iTabno); aColl = pKeyInfo->aColl; so = pKeyInfo->aSortOrder; for(i=0; i<nIn && rc==SQLITE4_OK; i++){ | | > | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 | *pnOut = 0; if( enlargeEncoderAllocation(&x, (nIn+1)*10) ) return SQLITE4_NOMEM; x.nOut = sqlite4PutVarint64(x.aOut, iTabno); aColl = pKeyInfo->aColl; so = pKeyInfo->aSortOrder; for(i=0; i<nIn && rc==SQLITE4_OK; i++){ rc = encodeOneKeyValue(&x, aIn+i, so ? so[i] : SQLITE4_SO_ASC, i==nInTotal-1, aColl[i]); } if( rc==SQLITE4_OK && nExtra ){ rc = enlargeEncoderAllocation(&x, nExtra); } if( rc ){ sqlite4DbFree(db, x.aOut); }else{ *paOut = x.aOut; |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
89 90 91 92 93 94 95 | } if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } if( pMem->flags&MEM_Dyn && pMem->xDel ){ assert( pMem->xDel!=SQLITE4_DYNAMIC ); | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | } if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } if( pMem->flags&MEM_Dyn && pMem->xDel ){ assert( pMem->xDel!=SQLITE4_DYNAMIC ); pMem->xDel(pMem->pDelArg, (void *)(pMem->z)); } pMem->z = pMem->zMalloc; if( pMem->z==0 ){ pMem->flags = MEM_Null; }else{ pMem->flags &= ~(MEM_Ephem|MEM_Static); |
︙ | ︙ | |||
114 115 116 117 118 119 120 | ** ** Return SQLITE4_OK on success or SQLITE4_NOMEM if malloc fails. */ int sqlite4VdbeMemMakeWriteable(Mem *pMem){ int f; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags&MEM_RowSet)==0 ); | < | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | ** ** Return SQLITE4_OK on success or SQLITE4_NOMEM if malloc fails. */ int sqlite4VdbeMemMakeWriteable(Mem *pMem){ int f; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags&MEM_RowSet)==0 ); f = pMem->flags; if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite4VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE4_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; |
︙ | ︙ | |||
168 169 170 171 172 173 174 | */ int sqlite4VdbeMemStringify(Mem *pMem, int enc){ int rc = SQLITE4_OK; int fg = pMem->flags; const int nByte = 32; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); | < | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | */ int sqlite4VdbeMemStringify(Mem *pMem, int enc){ int rc = SQLITE4_OK; int fg = pMem->flags; const int nByte = 32; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( !(fg&(MEM_Str|MEM_Blob)) ); assert( fg&(MEM_Int|MEM_Real) ); assert( (pMem->flags&MEM_RowSet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( sqlite4VdbeMemGrow(pMem, nByte, 0) ){ |
︙ | ︙ | |||
240 241 242 243 244 245 246 | if( p->flags&MEM_Agg ){ sqlite4VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite4VdbeMemRelease(p); }else if( p->flags&MEM_Dyn && p->xDel ){ assert( (p->flags&MEM_RowSet)==0 ); assert( p->xDel!=SQLITE4_DYNAMIC ); | | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | if( p->flags&MEM_Agg ){ sqlite4VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite4VdbeMemRelease(p); }else if( p->flags&MEM_Dyn && p->xDel ){ assert( (p->flags&MEM_RowSet)==0 ); assert( p->xDel!=SQLITE4_DYNAMIC ); p->xDel(p->pDelArg, (void *)p->z); p->xDel = 0; }else if( p->flags&MEM_RowSet ){ sqlite4RowSetClear(p->u.pRowSet); }else if( p->flags&MEM_Frame ){ sqlite4VdbeMemSetNull(p); } } |
︙ | ︙ | |||
454 455 456 457 458 459 460 | }else if( pMem->flags & MEM_RowSet ){ sqlite4RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); pMem->type = SQLITE4_NULL; } | < < < < < < < < < < < < < < | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | }else if( pMem->flags & MEM_RowSet ){ sqlite4RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); pMem->type = SQLITE4_NULL; } /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. */ void sqlite4VdbeMemSetInt64(Mem *pMem, i64 val){ sqlite4VdbeMemRelease(pMem); pMem->u.i = val; |
︙ | ︙ | |||
523 524 525 526 527 528 529 | ** Return true if the Mem object contains a TEXT or BLOB that is ** too large - whose size exceeds SQLITE4_MAX_LENGTH. */ int sqlite4VdbeMemTooBig(Mem *p){ assert( p->db!=0 ); if( p->flags & (MEM_Str|MEM_Blob) ){ int n = p->n; | < < < | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | ** Return true if the Mem object contains a TEXT or BLOB that is ** too large - whose size exceeds SQLITE4_MAX_LENGTH. */ int sqlite4VdbeMemTooBig(Mem *p){ assert( p->db!=0 ); if( p->flags & (MEM_Str|MEM_Blob) ){ int n = p->n; return n>p->db->aLimit[SQLITE4_LIMIT_LENGTH]; } return 0; } #ifdef SQLITE4_DEBUG /* |
︙ | ︙ | |||
636 637 638 639 640 641 642 | ** either case, SQLITE4_TOOBIG is returned. */ int sqlite4VdbeMemSetStr( Mem *pMem, /* Memory cell to set to string value */ const char *z, /* String pointer */ int n, /* Bytes in string, or negative */ u8 enc, /* Encoding of z. 0 for BLOBs */ | | > | 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 | ** either case, SQLITE4_TOOBIG is returned. */ int sqlite4VdbeMemSetStr( Mem *pMem, /* Memory cell to set to string value */ const char *z, /* String pointer */ int n, /* Bytes in string, or negative */ u8 enc, /* Encoding of z. 0 for BLOBs */ void (*xDel)(void*,void*),/* Destructor function */ void *pDelArg /* First argument to xDel() */ ){ int nByte = n; /* New value for pMem->n */ int iLimit; /* Maximum allowed string or blob size */ u16 flags = 0; /* New value for pMem->flags */ assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags & MEM_RowSet)==0 ); |
︙ | ︙ | |||
691 692 693 694 695 696 697 698 699 700 701 702 703 704 | sqlite4VdbeMemRelease(pMem); pMem->zMalloc = pMem->z = (char *)z; pMem->xDel = 0; }else{ sqlite4VdbeMemRelease(pMem); pMem->z = (char *)z; pMem->xDel = xDel; flags |= ((xDel==SQLITE4_STATIC)?MEM_Static:MEM_Dyn); } pMem->n = nByte; pMem->flags = flags; pMem->enc = (enc==0 ? SQLITE4_UTF8 : enc); pMem->type = (enc==0 ? SQLITE4_BLOB : SQLITE4_TEXT); | > | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | sqlite4VdbeMemRelease(pMem); pMem->zMalloc = pMem->z = (char *)z; pMem->xDel = 0; }else{ sqlite4VdbeMemRelease(pMem); pMem->z = (char *)z; pMem->xDel = xDel; pMem->pDelArg = pDelArg; flags |= ((xDel==SQLITE4_STATIC)?MEM_Static:MEM_Dyn); } pMem->n = nByte; pMem->flags = flags; pMem->enc = (enc==0 ? SQLITE4_UTF8 : enc); pMem->type = (enc==0 ? SQLITE4_BLOB : SQLITE4_TEXT); |
︙ | ︙ | |||
852 853 854 855 856 857 858 | assert( (pVal->flags & MEM_RowSet)==0 ); if( pVal->flags&MEM_Null ){ return 0; } assert( (MEM_Blob>>3) == MEM_Str ); pVal->flags |= (pVal->flags & MEM_Blob)>>3; | < | 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | assert( (pVal->flags & MEM_RowSet)==0 ); if( pVal->flags&MEM_Null ){ return 0; } assert( (MEM_Blob>>3) == MEM_Str ); pVal->flags |= (pVal->flags & MEM_Blob)>>3; if( pVal->flags&MEM_Str ){ sqlite4VdbeChangeEncoding(pVal, enc & ~SQLITE4_UTF16_ALIGNED); if( (enc & SQLITE4_UTF16_ALIGNED)!=0 && 1==(1&SQLITE4_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite4VdbeMemMakeWriteable(pVal)!=SQLITE4_OK ){ return 0; } |
︙ | ︙ | |||
947 948 949 950 951 952 953 | pVal = sqlite4ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite4VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ zVal = sqlite4MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; | | | 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 | pVal = sqlite4ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite4VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ zVal = sqlite4MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite4ValueSetStr(pVal, -1, zVal, SQLITE4_UTF8, SQLITE4_DYNAMIC, 0); if( op==TK_FLOAT ) pVal->type = SQLITE4_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE4_AFF_NONE ){ sqlite4ValueApplyAffinity(pVal, SQLITE4_AFF_NUMERIC, SQLITE4_UTF8); }else{ sqlite4ValueApplyAffinity(pVal, affinity, SQLITE4_UTF8); } |
︙ | ︙ | |||
988 989 990 991 992 993 994 | assert( pExpr->u.zToken[1]=='\'' ); pVal = sqlite4ValueNew(db); if( !pVal ) goto no_mem; zVal = &pExpr->u.zToken[2]; nVal = sqlite4Strlen30(zVal)-1; assert( zVal[nVal]=='\'' ); sqlite4VdbeMemSetStr(pVal, sqlite4HexToBlob(db, zVal, nVal), nVal/2, | | | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 | assert( pExpr->u.zToken[1]=='\'' ); pVal = sqlite4ValueNew(db); if( !pVal ) goto no_mem; zVal = &pExpr->u.zToken[2]; nVal = sqlite4Strlen30(zVal)-1; assert( zVal[nVal]=='\'' ); sqlite4VdbeMemSetStr(pVal, sqlite4HexToBlob(db, zVal, nVal), nVal/2, 0, SQLITE4_DYNAMIC, 0); } #endif if( pVal ){ sqlite4VdbeMemStoreType(pVal); } *ppVal = pVal; |
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 | ** Change the string value of an sqlite4_value object */ void sqlite4ValueSetStr( sqlite4_value *v, /* Value to be set */ int n, /* Length of string z */ const void *z, /* Text of the new string */ u8 enc, /* Encoding to use */ | | > | < < < < | 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | ** Change the string value of an sqlite4_value object */ void sqlite4ValueSetStr( sqlite4_value *v, /* Value to be set */ int n, /* Length of string z */ const void *z, /* Text of the new string */ u8 enc, /* Encoding to use */ void (*xDel)(void*,void*), /* Destructor for the string */ void *pDelArg /* First argument to xDel() */ ){ if( v ) sqlite4VdbeMemSetStr((Mem *)v, z, n, enc, xDel, pDelArg); } /* ** Free an sqlite4_value object */ void sqlite4ValueFree(sqlite4_value *v){ if( !v ) return; sqlite4VdbeMemRelease((Mem *)v); sqlite4DbFree(((Mem*)v)->db, v); } /* ** Return the number of bytes in the sqlite4_value object assuming ** that it uses the encoding "enc" */ int sqlite4ValueBytes(sqlite4_value *pVal, u8 enc){ Mem *p = (Mem*)pVal; if( (p->flags & MEM_Blob)!=0 || sqlite4ValueText(pVal, enc) ){ return p->n; } return 0; } |
Changes to src/vdbetrace.c.
︙ | ︙ | |||
126 127 128 129 130 131 132 | }else if( pVar->flags & MEM_Str ){ #ifndef SQLITE4_OMIT_UTF16 u8 enc = ENC(db); if( enc!=SQLITE4_UTF8 ){ Mem utf8; memset(&utf8, 0, sizeof(utf8)); utf8.db = db; | | < < | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | }else if( pVar->flags & MEM_Str ){ #ifndef SQLITE4_OMIT_UTF16 u8 enc = ENC(db); if( enc!=SQLITE4_UTF8 ){ Mem utf8; memset(&utf8, 0, sizeof(utf8)); utf8.db = db; sqlite4VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE4_STATIC, 0); sqlite4VdbeChangeEncoding(&utf8, SQLITE4_UTF8); sqlite4XPrintf(&out, "'%.*q'", utf8.n, utf8.z); sqlite4VdbeMemRelease(&utf8); }else #endif { sqlite4XPrintf(&out, "'%.*q'", pVar->n, pVar->z); } }else{ assert( pVar->flags & MEM_Blob ); sqlite4StrAccumAppend(&out, "x'", 2); for(i=0; i<pVar->n; i++){ sqlite4XPrintf(&out, "%02x", pVar->z[i]&0xff); } sqlite4StrAccumAppend(&out, "'", 1); |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 | testcase( op==OP_Rewind ); testcase( op==OP_Last ); testcase( op==OP_SeekGt ); testcase( op==OP_SeekGe ); testcase( op==OP_SeekLe ); testcase( op==OP_SeekLt ); sqlite4VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); /* Set variable op to the instruction required to determine if the ** cursor is passed the end of the range. If the range is unbounded, ** then set op to OP_Noop. Nothing to do in this case. */ assert( (endEq==0 || endEq==1) ); op = aEndOp[(pRangeEnd || nEq) * (1 + (endEq+endEq) + bRev)]; testcase( op==OP_Noop ); | > > > | 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 | testcase( op==OP_Rewind ); testcase( op==OP_Last ); testcase( op==OP_SeekGt ); testcase( op==OP_SeekGe ); testcase( op==OP_SeekLe ); testcase( op==OP_SeekLt ); sqlite4VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); if( (pIdx->nColumn + (pIdx==pPk ? 0 : pPk->nColumn))>nEq ){ sqlite4VdbeChangeP5(v, OPFLAG_PARTIALKEY); } /* Set variable op to the instruction required to determine if the ** cursor is passed the end of the range. If the range is unbounded, ** then set op to OP_Noop. Nothing to do in this case. */ assert( (endEq==0 || endEq==1) ); op = aEndOp[(pRangeEnd || nEq) * (1 + (endEq+endEq) + bRev)]; testcase( op==OP_Noop ); |
︙ | ︙ |
Deleted test/bindxfer.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to test/boundary4.test.
︙ | ︙ | |||
210 211 212 213 214 215 216 | do_test boundary4-3.1 { db eval { UPDATE t1 SET oid=a, a=oid } } {} do_test boundary4-3.2 { db eval { | | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | do_test boundary4-3.1 { db eval { UPDATE t1 SET oid=a, a=oid } } {} do_test boundary4-3.2 { db eval { ALTER TABLE t1 ADD COLUMN z; UPDATE t1 SET z=randomblob(600) } } {} do_test boundary4-3.3 { db eval { SELECT oid, a, x FROM t1 ORDER BY +oid } } {-576460752303423489 33 fffffffff7fffffff7ffffffffffffff -576460752303423488 12 fffffffff8000000f800000000000000 -36028797018963969 14 ffffffffff7fffffff7fffffffffffff -36028797018963968 41 ffffffffff800000ff80000000000000 -140737488355329 42 ffffffffffff7fffffff7fffffffffff -140737488355328 4 ffffffffffff8000ffff800000000000 -549755813889 5 ffffffffffffff7fffffff7fffffffff -549755813888 7 ffffffffffffff80ffffff8000000000 -2147483649 46 ffffffffffffffffffffffff7fffffff -2147483648 35 ffffffffffffffffffffffff80000000 -8388609 29 ffffffffffffffffffffffffff7fffff -8388608 16 ffffffffffffffffffffffffff800000 -32769 13 ffffffffffffffffffffffffffff7fff -32768 45 ffffffffffffffffffffffffffff8000 -129 1 ffffffffffffffffffffffffffffff7f -128 27 ffffffffffffffffffffffffffffff80 127 19 000000000000007f 128 30 0000000000000080 255 48 00000000000000ff 256 2 0000000000000100 32767 8 0000000000007fff 32768 44 0000000000008000 65535 37 000000000000ffff 65536 38 0000000000010000 8388607 24 00000000007fffff 8388608 36 0000000000800000 16777215 20 0000000000ffffff 16777216 34 0000000001000000 2147483647 26 000000007fffffff 2147483648 43 0000000080000000 4294967295 22 00000000ffffffff 4294967296 6 00000001100000000 549755813887 23 0000007f7fffffffff 549755813888 21 000000808000000000 1099511627775 40 000000ffffffffffff 1099511627776 28 0000010010000000000 140737488355327 3 00007fff7fffffffffff 140737488355328 47 00008000800000000000 281474976710655 39 0000ffffffffffffffff 281474976710656 10 000100001000000000000 36028797018963967 18 007fffff7fffffffffffff 36028797018963968 25 0080000080000000000000 72057594037927935 32 00ffffffffffffffffffff 72057594037927936 11 01000000100000000000000 576460752303423487 9 07ffffff7ffffffffffffff 576460752303423488 31 08000000800000000000000 1152921504606846975 15 0ffffffffffffffffffffff 1152921504606846976 17 100000001000000000000000} |
︙ | ︙ |
Changes to test/ckpt1.test.
︙ | ︙ | |||
72 73 74 75 76 77 78 | INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 4K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 8K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 16K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 32K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 64K } do_test 3.2 { | < > | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 4K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 8K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 16K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 32K INSERT INTO t1 SELECT randstr(100,100), randstr(100,100) FROM t1; -- 64K } do_test 3.2 { sqlite4_lsm_work db main -nmerge 1 -npage 1000000 execsql { SELECT count(*) FROM t1 } } {65536} do_test 3.3 { db close sqlite4 db test.db execsql { SELECT count(*) FROM t1 } } {65536} do_test 3.4 { execsql { INSERT INTO t1 VALUES(randstr(100,100), randstr(100,100)) } db close sqlite4 db test.db sqlite4_lsm_work db main -nmerge 1 -npage 1000000 execsql { SELECT count(*) FROM t1 } } {65537} finish_test |
Changes to test/csr1.test.
︙ | ︙ | |||
73 74 75 76 77 78 79 | # tree to be flushed to disk, # populate_db_2 do_execsql_test 3.1 { BEGIN; INSERT INTO t1 VALUES(10, randstr(910, 910)); } | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | # tree to be flushed to disk, # populate_db_2 do_execsql_test 3.1 { BEGIN; INSERT INTO t1 VALUES(10, randstr(910, 910)); } do_test 3.2 { sqlite4_lsm_config db main autoflush } 1024 do_test 3.3 { sqlite4_lsm_config db main autoflush 4 } 4 do_test 3.4 { set res [list] db eval { SELECT a, length(b) AS l FROM t1 } { lappend res $a $l # The following commit will flush the in-memory tree to disk. if {$a == 5} { db eval COMMIT } |
︙ | ︙ |
Changes to test/ctime.test.
︙ | ︙ | |||
68 69 70 71 72 73 74 | SELECT sqlite_compileoption_used('SQLITE4_THREADSAFE'); } } {0 1} do_test ctime-1.4.2 { catchsql { SELECT sqlite_compileoption_used('THREADSAFE'); } | < < < < < | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | SELECT sqlite_compileoption_used('SQLITE4_THREADSAFE'); } } {0 1} do_test ctime-1.4.2 { catchsql { SELECT sqlite_compileoption_used('THREADSAFE'); } } {0 1} do_test ctime-1.5 { set ans1 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=0'); } ] set ans2 [ catchsql { |
︙ | ︙ | |||
118 119 120 121 122 123 124 | } } {1 {wrong number of arguments to function sqlite_compileoption_used()}} do_test ctime-2.1.2 { catchsql { SELECT sqlite_compileoption_used(NULL); } } {0 {{}}} | < < < < < | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | } } {1 {wrong number of arguments to function sqlite_compileoption_used()}} do_test ctime-2.1.2 { catchsql { SELECT sqlite_compileoption_used(NULL); } } {0 {{}}} do_test ctime-2.1.4 { catchsql { SELECT sqlite_compileoption_used(''); } } {0 0} do_test ctime-2.1.5 { catchsql { |
︙ | ︙ |
Changes to test/enc4.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 | do_test enc4-$i.1 { db eval {PRAGMA encoding} } $enc set j 1 foreach init $inits { | < < < < | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | do_test enc4-$i.1 { db eval {PRAGMA encoding} } $enc set j 1 foreach init $inits { set S [sqlite4_prepare db "SELECT $init+?" -1 dummy] set k 1 foreach val $vals { for {set x 1} {$x<16} {incr x} { set part [expr $init + [string range $val 0 [expr $x-1]]] do_realnum_test enc4-$i.$j.$k.3.$x { sqlite4_reset $S |
︙ | ︙ |
Changes to test/in.test.
︙ | ︙ | |||
269 270 271 272 273 274 275 | } } {1 2 3 4 6 8 10} do_test in-8.1 { execsql { SELECT b FROM t1 WHERE a IN ('hello','there') } | < < < < < | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | } } {1 2 3 4 6 8 10} do_test in-8.1 { execsql { SELECT b FROM t1 WHERE a IN ('hello','there') } } {world} # Test constructs of the form: expr IN tablename # do_test in-9.1 { execsql { CREATE TABLE t4 AS SELECT a FROM tb; |
︙ | ︙ |
Changes to test/join.test.
︙ | ︙ | |||
427 428 429 430 431 432 433 | } {1 a xxx 2 b xxx 3 c {}} # A test for ticket #247. # do_test join-7.1 { execsql { CREATE TABLE t7 (x, y); | | | | | | | | | | | | | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | } {1 a xxx 2 b xxx 3 c {}} # A test for ticket #247. # do_test join-7.1 { execsql { CREATE TABLE t7 (x, y); INSERT INTO t7 VALUES ('pa1', 1); INSERT INTO t7 VALUES ('pa2', NULL); INSERT INTO t7 VALUES ('pa3', NULL); INSERT INTO t7 VALUES ('pa4', 2); INSERT INTO t7 VALUES ('pa30', 131); INSERT INTO t7 VALUES ('pa31', 130); INSERT INTO t7 VALUES ('pa28', NULL); CREATE TABLE t8 (a integer primary key, b); INSERT INTO t8 VALUES (1, 'pa1'); INSERT INTO t8 VALUES (2, 'pa4'); INSERT INTO t8 VALUES (3, NULL); INSERT INTO t8 VALUES (4, NULL); INSERT INTO t8 VALUES (130, 'pa31'); INSERT INTO t8 VALUES (131, 'pa30'); SELECT coalesce(t8.a,999) from t7 LEFT JOIN t8 on y=a; } } {1 999 999 2 131 130 999} # Make sure a left join where the right table is really a view that # is itself a join works right. Ticket #306. |
︙ | ︙ |
Deleted test/lastinsert.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to test/log3.test.
︙ | ︙ | |||
32 33 34 35 36 37 38 | do_test 1.7 { sqlite4_lsm_config db main safety } {2} do_test 1.8 { sqlite4_lsm_config db main safety 3 } {2} do_test 1.9 { sqlite4_lsm_config db main safety } {2} #------------------------------------------------------------------------- reset_db do_test 2.0 { sqlite4_lsm_config db main safety 2 } {2} | < | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | do_test 1.7 { sqlite4_lsm_config db main safety } {2} do_test 1.8 { sqlite4_lsm_config db main safety 3 } {2} do_test 1.9 { sqlite4_lsm_config db main safety } {2} #------------------------------------------------------------------------- reset_db do_test 2.0 { sqlite4_lsm_config db main safety 2 } {2} do_execsql_test 2.2 { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); } {} do_filesize_test 2.3 0 1024 do_execsql_test 2.4 { BEGIN; INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); COMMIT; } {} do_filesize_test 2.5 0 2048 do_test 2.6 { optimize_db } {} do_execsql_test 2.7 { INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)) } do_test 2.8 { sqlite4_lsm_checkpoint db main } {} do_test 2.9 { sqlite4_lsm_info db main log-structure } {0 0 0 0 2560 3072} for {set i 1} {$i <= 6} {incr i} { do_execsql_test 2.10.$i.1 { INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); } do_execsql_test 2.10.$i.2 { SELECT count(*) FROM t1 } [expr 8 + $i] do_recover_test 2.10.$i.3 { SELECT count(*) FROM t1 } [expr 8 + $i] } do_test 2.11 { sqlite4_lsm_info db main log-structure } {0 0 0 0 2560 6144} finish_test |
Changes to test/lsm1.test.
︙ | ︙ | |||
86 87 88 89 90 91 92 | db write ccc three db write ddd four db write eee five db write fff six reopen db delete_range a bbb reopen | < | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | db write ccc three db write ddd four db write eee five db write fff six reopen db delete_range a bbb reopen db work 10 } {1} do_contents_test 2.2 { {bbb two} {ccc three} {ddd four} {eee five} {fff six} } #------------------------------------------------------------------------- |
︙ | ︙ | |||
115 116 117 118 119 120 121 | } {1} do_test 3.2 { db write bx seven reopen db delete_range aaa bx reopen | | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | } {1} do_test 3.2 { db write bx seven reopen db delete_range aaa bx reopen db work 2 10 } {1} do_contents_test 3.3 { {aaa one} {bx seven} {ccc three} {ddd four} {eee five} {fff six} } do_test 3.4 { fetch ddd } four |
︙ | ︙ |
Added test/lsm3.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | # 2012 November 02 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file tests that the LSM_CONFIG_MULTIPLE_PROCESSES parameter seems # to work as documented. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set testprefix lsm3 db close do_multiclient_test tn { # The [do_multiclient_test] command automatically opens a connection # in each process (or three connections in this process). We don't want # them in this case. code1 { db close } code2 { db2 close } code3 { db3 close } if { $tn==1 } { set locked {1 {database is locked}} } else { set locked {0 {}} } # Open a single-process connection to the database from an external # process (if $tn==1, otherwise open it from within the current # process). code2 { sqlite4 db2 file:test.db?lsm_multiple_processes=0 } # Try to open some other connections to the database file, both in multi # and single process mode. If ($tn==1), then all such attempts fail. Or, # if ($tn==2), they all succeed. do_test $tn.1 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=0} msg] $msg } $locked do_test $tn.2 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=0} msg] $msg } $locked do_test $tn.3 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=1} msg] $msg } $locked do_test $tn.4 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=1} msg] $msg } $locked # Now open a connection from an external process in multi-proc mode. # Observe that further connections are allowed if they are from within # the same process or if the LSM_CONFIG_MULTIPLE_PROCESSES parameter # is set to true. code2 { db2 close sqlite4 db2 file:test.db } do_test $tn.5 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=0} msg] $msg } $locked do_test $tn.6 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=0} msg] $msg } $locked do_test $tn.7 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=1} msg] $msg } {0 {}} do_test $tn.8 { catch { db close } list [catch {sqlite4 db file:test.db?lsm_multiple_processes=1} msg] $msg } {0 {}} } finish_test |
Added test/lsm4.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | # 2013 February 06 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the LSM library. More specifically, # it focuses on testing the compression, compression-id and # compression-factory functionality. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix lsm4 db close # Compression scheme ids (defined in test_lsm.c): # set compression_id(encrypt) 43 set compression_id(rle) 44 set compression_id(noop) 45 proc db_fetch {db key} { db csr_open csr csr seek $key eq set ret [csr value] csr close set ret } do_test 1.1 { lsm_open db test.db db config {set_compression noop} db write 1 abc db write 2 def db close } {} do_test 1.2 { lsm_open db test.db db config {set_compression noop} list [db_fetch db 1] [db_fetch db 2] } {abc def} do_test 1.3 { db close lsm_open db test.db db config {set_compression rle} list [catch {db_fetch db 1} msg] $msg } {1 {error in lsm_csr_open() - 50}} do_test 1.4 { db close lsm_open db test.db list [catch {db_fetch db 1} msg] $msg } {1 {error in lsm_csr_open() - 50}} do_test 1.5 { db config {set_compression_factory true} list [db_fetch db 1] [db_fetch db 2] } {abc def} do_test 1.6 { db info compression_id } $compression_id(noop) db close #------------------------------------------------------------------------- # forcedelete test.db do_test 2.1 { lsm_open db test.db db info compression_id } {0} do_test 2.2 { db write 1 abc db write 2 abc db info compression_id } {0} do_test 2.3 { lsm_open db2 test.db db2 info compression_id } {0} do_test 2.4 { db close db2 info compression_id } {0} do_test 2.5 { db2 close lsm_open db test.db db info compression_id } {1} db close forcedelete test.db do_test 2.6 { lsm_open db test.db db config {set_compression rle} db write 3 three db write 4 four db close lsm_open db test.db db info compression_id } $compression_id(rle) do_test 2.7 { db config {set_compression rle} list [db_fetch db 3] [db_fetch db 4] } {three four} #------------------------------------------------------------------------- # catch {db close} forcedelete test.db do_test 3.1 { lsm_open db test.db db_fetch db abc } {} finish_test |
Added test/lsm5.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | # 2013 February 08 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the LSM library. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lsm_common.tcl set testprefix lsm5 db close # Create a new database with file name $file. # proc create_abc_db {file} { forcedelete $file lsm_open db $file {block_size 256} db write a alpha db write b bravo db write c charlie db close } proc create_abc_log {file} { forcedelete $file ${file}-2 lsm_open db ${file}-2 db write a alpha db write b bravo db write c charlie file copy ${file}-2 $file file copy ${file}-2-log $file-log db close } #------------------------------------------------------------------------- # When the database system is shut down (i.e. when the last connection # disconnects), an attempt is made to truncate the database file to the # minimum number of blocks required. # # This test case checks that this process does not actually cause the # database to grow. # do_test 1.1 { lsm_open db test.db db config {mmap 0} } {0} do_test 1.2 { db write 1 one db write 2 two db close } {} do_test 1.3 { expr [file size test.db] < (64*1024) } 1 #------------------------------------------------------------------------- # Test that if an attempt is made to open a read-write connection to a # non-live database that the client does not have permission to write to is # attempted an error is reported. In order to open a read-write connection # to a database, the client requires: # # * read-write access to the db file, # * read-write access to the log file, # * for multi-process mode, read-write access to the shm file. # # In the above, "read-write access" includes the ability to create the db, # log or shm file if it does not exist. # # These tests verify that the lsm_open() command returns LSM_IOERR. At some # point in the future this will be improved. Likely when sqlite4 level tests # for opening read-only databases are added. # foreach {tn filename setup} { 1 test.dir/test.db { # Create a directory "test.dir". forcedelete test.dir file mkdir test.dir # Create a database within test.dir create_abc_db test.dir/test.db # Now make the db and its directory read-only. file attr test.dir/test.db -perm r--r--r-- file attr test.dir -perm r-xr-xr-x } 2 test.db { # Create a database test.db and set its permissions to read-only create_abc_db test.db file attr test.db -perm r--r--r-- } 3 test.dir/test.db { # Create a directory "test.dir". forcedelete test.dir file mkdir test.dir # Create a database within test.dir create_abc_db test.dir/test.db # Now make test.dir read-only. file attr test.dir -perm r-xr-xr-x } } { do_test 2.$tn.1 { eval $setup set rc [catch {lsm_open db $filename} msg] list $rc $msg } {1 {error in lsm_open() - 10}} do_test 2.$tn.2 { eval $setup lsm_open db $filename {readonly 1} set res [list [db_fetch db a] [db_fetch db b] [db_fetch db c]] db close set res } {alpha bravo charlie} } #------------------------------------------------------------------------- # Try having a read-only connection connect to a non-live system where the # log file contains content. In this scenario the read-only client must # read the contents from the log file at the start of each read-transaction. # do_test 3.1 { create_abc_log test.db list [file size test.db] [file size test.db-log] } {0 56} do_test 3.2 { lsm_open db $filename {readonly 1} set res [list [db_fetch db a] [db_fetch db b] [db_fetch db c]] db close set res } {alpha bravo charlie} do_test 3.3 { list [file size test.db] [file size test.db-log] } {0 56} # Now make the same db live and check the read-only connection can still # read it. do_test 3.4 { file exists test.db-shm } 0 do_test 3.5 { lsm_open db_rw test.db file exists test.db-shm } 1 do_test 3.6 { lsm_open db test.db {readonly 1} list [db_fetch db a] [db_fetch db b] [db_fetch db c] } {alpha bravo charlie} # Close the read-write connection. This should cause a checkpoint and delete # the log file, even though the system remains live. do_test 3.7 { db_rw close list [file exists test.db-log] [file exists test.db-shm] } {0 1} # Now close the read-only connection. The system is now non-live, but the # *-shm remains in the file-system (the readonly connection cannot unlink it). do_test 3.8 { db close list [file exists test.db-log] [file exists test.db-shm] } {0 1} #------------------------------------------------------------------------- # do_test 4.1 { create_abc_log test.db list [file size test.db] [file size test.db-log] } {0 56} do_test 4.2 { lsm_open db test.db {readonly 1} db csr_open T list [db_fetch db a] [db_fetch db b] [db_fetch db c] } {alpha bravo charlie} do_test 4.3 { lsm_open db_rw test.db {block_size 64} db_rw write b BRAVO db_rw close list [file size test.db] [file size test.db-log] } {65536 74} do_test 4.4 { list [db_fetch db a] [db_fetch db b] [db_fetch db c] } {alpha bravo charlie} do_test 4.5 { T close list [db_fetch db a] [db_fetch db b] [db_fetch db c] } {alpha BRAVO charlie} finish_test |
Added test/lsm6.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # 2013 February 20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the LSM library. Specifically, it # checks that the in-memory tree is flushed to disk when the last connection # is closed. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lsm_common.tcl set testprefix lsm6 db close foreach {tn mp lf} { 1 1 1 2 1 0 3 0 1 4 0 0 } { do_test $tn.1 { forcedelete test.db test.db-log lsm_open db test.db [list multi_proc $mp use_log $lf] for {set i 0} {$i < 1000} {incr i} { db write $i [string repeat "$i." 1000] } expr {[file size test.db-log] > 0} } $lf do_test $tn.2 { db close lsm_open db test.db db_fetch db 999 } [string repeat 999. 1000] db close } finish_test |
Added test/lsm_common.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 2013 Feb 20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains common code used the various lsm tests in this # directory. # proc db_fetch {db key} { db csr_open csr csr seek $key eq set ret [csr value] csr close set ret } |
Added test/num.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | # 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite_*_printf() interface. # # $Id: printf.test,v 1.31 2009/02/01 00:21:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test num-1.1.1 { sqlite4_num_compare 20 20 } {equal} do_test num-1.1.2 { sqlite4_num_compare 20 2e1 } {equal} do_test num-1.1.3 { sqlite4_num_compare -00034 -3.4e1 } {equal} do_test num-1.1.4 { sqlite4_num_compare -inf +inf } {lesser} do_test num-1.1.5 { sqlite4_num_compare -inf 0 } {lesser} do_test num-1.1.6 { sqlite4_num_compare inf 4 } {greater} do_test num-1.1.7 { sqlite4_num_compare nan 7 } {incomparable} # Is +0 > -0? #do_test num-equal-1.1.4 { # sqlite4_num_compare +0 -0 #} {equal} do_test num-2.1.1 { sqlite4_num_to_text [sqlite4_num_from_text 37] } {37} do_test num-2.1.2 { sqlite4_num_to_text [sqlite4_num_from_text 37 2] } {37} do_test num-2.1.4 { sqlite4_num_compare [sqlite4_num_from_text 2.9e2X 5] 290 } {equal} do_test num-2.1.5 { sqlite4_num_isnan [sqlite4_num_from_text inf 2] } {true} do_test num-2.1.6 { sqlite4_num_isinf [sqlite4_num_from_text inf 3] } {true} do_test num-3.1.1 { sqlite4_num_to_text [sqlite4_num_add 5 7] } {12} do_test num-4.1.1 { sqlite4_num_to_text [sqlite4_num_sub 9 3] } {6} do_test num-4.1.2 { sqlite4_num_to_text [sqlite4_num_sub 5 12] } {-7} do_test num-4.2.1 { sqlite4_num_compare [sqlite4_num_sub 1 1] [sqlite4_num_sub -1 -1] } {equal} do_test num-5.1.1 { sqlite4_num_to_text [sqlite4_num_mul 9 8] } {72} do_test num-6.1.1 { sqlite4_num_to_text [sqlite4_num_div 6 5] } {1.2} do_test num-6.1.2 { sqlite4_num_compare 2 [sqlite4_num_div 2 1] } {equal} do_test num-6.1.3 { sqlite4_num_to_text [sqlite4_num_div 2 1] } {2} do_test num-6.1.4 { sqlite4_num_to_text [sqlite4_num_div 22 10] } {2.2} finish_test |
Changes to test/permutations.test.
︙ | ︙ | |||
131 132 133 134 135 136 137 | # lappend ::testsuitelist xxx test_suite "src4" -prefix "" -description { } -files { simple.test simple2.test log3.test | | < | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | # lappend ::testsuitelist xxx test_suite "src4" -prefix "" -description { } -files { simple.test simple2.test log3.test lsm1.test lsm2.test lsm3.test lsm4.test lsm5.test csr1.test ckpt1.test mc1.test fts5expr1.test fts5query1.test fts5rnd1.test fts5create.test fts5snippet.test aggerror.test attach.test autoindex1.test badutf.test between.test bigrow.test bind.test boundary1.test boundary4.test cast.test coalesce.test collate1.test collate2.test collate3.test collate4.test collate5.test collate6.test collate7.test collate8.test collate9.test collateA.test conflict.test |
︙ | ︙ |
Changes to test/quote.test.
︙ | ︙ | |||
30 31 32 33 34 35 36 | } {0 {}} do_test quote-1.2.1 { catchsql {SELECT * FROM '@abc'} } {0 {5 hello}} do_test quote-1.2.2 { catchsql {SELECT * FROM [@abc]} ;# SqlServer compatibility } {0 {5 hello}} | < < < < < < < < | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | } {0 {}} do_test quote-1.2.1 { catchsql {SELECT * FROM '@abc'} } {0 {5 hello}} do_test quote-1.2.2 { catchsql {SELECT * FROM [@abc]} ;# SqlServer compatibility } {0 {5 hello}} do_test quote-1.3 { catchsql { SELECT '@abc'.'!pqr', '@abc'.'#xyz'+5 FROM '@abc' } } {0 {hello 10}} do_test quote-1.3.1 { catchsql { SELECT '!pqr', '#xyz'+5 FROM '@abc' } } {0 {!pqr 5}} do_test quote-1.3.2 { catchsql { SELECT "!pqr", "#xyz"+5 FROM '@abc' } } {0 {hello 10}} do_test quote-1.3.4 { set r [catch { execsql {SELECT '@abc'.'!pqr', '@abc'.'#xyz'+5 FROM '@abc'} } msg ] lappend r $msg } {0 {hello 10}} |
︙ | ︙ |
Changes to test/select6.test.
︙ | ︙ | |||
161 162 163 164 165 166 167 | execsql2 { SELECT * FROM (SELECT * FROM (SELECT * FROM t1 WHERE x=3)); } } {x 3 y 2} do_test select6-3.2 { execsql { SELECT * FROM | | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | execsql2 { SELECT * FROM (SELECT * FROM (SELECT * FROM t1 WHERE x=3)); } } {x 3 y 2} do_test select6-3.2 { execsql { SELECT * FROM (SELECT a.q AS x, a.p, b.r FROM (SELECT count(*) as p , b as q FROM t2 GROUP BY q) AS a, (SELECT max(a) as r, b as s FROM t2 GROUP BY s) as b WHERE a.q=b.s ORDER BY a.q) ORDER BY x } } {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20} do_test select6-3.3 { execsql { SELECT a,b,a+b FROM (SELECT avg(x) as 'a', avg(y) as 'b' FROM t1) } } {10.5 3.7 14.2} |
︙ | ︙ |
Changes to test/simple.test.
︙ | ︙ | |||
83 84 85 86 87 88 89 | do_execsql_test 3.1 { CREATE TABLE t1(k PRIMARY KEY, v UNIQUE) } do_execsql_test 3.2 { SELECT * FROM sqlite_master } { table t1 t1 2 {CREATE TABLE t1(k PRIMARY KEY, v UNIQUE)} | | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | do_execsql_test 3.1 { CREATE TABLE t1(k PRIMARY KEY, v UNIQUE) } do_execsql_test 3.2 { SELECT * FROM sqlite_master } { table t1 t1 2 {CREATE TABLE t1(k PRIMARY KEY, v UNIQUE)} index sqlite_t1_unique2 t1 3 {} } #explain { INSERT INTO t1 VALUES('one', '111') } #execsql { PRAGMA vdbe_trace = 1 } #execsql { PRAGMA kv_trace = 1 } # do_execsql_test 3.3 { INSERT INTO t1 VALUES('one', '111') } {} |
︙ | ︙ | |||
123 124 125 126 127 128 129 | do_execsql_test 5.1 { CREATE TABLE t1(k, v UNIQUE) } do_execsql_test 5.2 { CREATE INDEX i1 ON t1(v) } do_execsql_test 5.3 { SELECT * FROM sqlite_master } { table t1 t1 3 {CREATE TABLE t1(k, v UNIQUE)} | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | do_execsql_test 5.1 { CREATE TABLE t1(k, v UNIQUE) } do_execsql_test 5.2 { CREATE INDEX i1 ON t1(v) } do_execsql_test 5.3 { SELECT * FROM sqlite_master } { table t1 t1 3 {CREATE TABLE t1(k, v UNIQUE)} index sqlite_t1_unique1 t1 2 {} index i1 t1 4 {CREATE INDEX i1 ON t1(v)} } do_execsql_test 5.3 { INSERT INTO t1 VALUES('one', '111') } {} do_execsql_test 5.4 { SELECT * FROM t1 } {one 111} do_execsql_test 5.5 { PRAGMA integrity_check } {ok} |
︙ | ︙ | |||
1457 1458 1459 1460 1461 1462 1463 | do_test 74.2 { for {set i 0} {$i<30} {incr i} { db close sqlite4 db test.db execsql "INSERT INTO t1 VALUES('k$i', randstr(500,500))" } } {} | < > > > > > > > > > > > > > > > > > > | 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 | do_test 74.2 { for {set i 0} {$i<30} {incr i} { db close sqlite4 db test.db execsql "INSERT INTO t1 VALUES('k$i', randstr(500,500))" } } {} db close #------------------------------------------------------------------------- # Index on a blob. # reset_db do_execsql_test 75.1 { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(x'12345678', x'12345678'); } do_execsql_test 75.2 { SELECT count(*) FROM t1 WHERE a = x'12345678' } 1 do_execsql_test 75.3 { SELECT count(*) FROM t1 WHERE b = x'12345678' } 1 finish_test |
Changes to test/subquery.test.
︙ | ︙ | |||
385 386 387 388 389 390 391 | INSERT INTO t4 VALUES('four',4); CREATE TABLE t5(a,b); INSERT INTO t5 VALUES(1,11); INSERT INTO t5 VALUES(2,22); INSERT INTO t5 VALUES(3,33); INSERT INTO t5 VALUES(4,44); SELECT b FROM t5 WHERE a IN | | | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | INSERT INTO t4 VALUES('four',4); CREATE TABLE t5(a,b); INSERT INTO t5 VALUES(1,11); INSERT INTO t5 VALUES(2,22); INSERT INTO t5 VALUES(3,33); INSERT INTO t5 VALUES(4,44); SELECT b FROM t5 WHERE a IN (SELECT callcnt(y)+0 FROM t4 WHERE x='two') } } {22} do_test subquery-5.2 { # This is the key test. The subquery should have only run once. If # The double-quoted identifier "two" were causing the subquery to be # processed as a correlated subquery, then it would have run 4 times. set callcnt |
︙ | ︙ |
Changes to test/test_func.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 | } assert( n<sizeof(zBuf) ); sqlite4_randomness(pEnv, n, zBuf); for(i=0; i<n; i++){ zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)]; } zBuf[n] = 0; | | | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | } assert( n<sizeof(zBuf) ); sqlite4_randomness(pEnv, n, zBuf); for(i=0; i<n; i++){ zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)]; } zBuf[n] = 0; sqlite4_result_text(context, (char*)zBuf, n, SQLITE4_TRANSIENT, 0); } /* ** The following two SQL functions are used to test returning a text ** result with a destructor. Function 'test_destructor' takes one argument ** and returns the same argument interpreted as TEXT. A destructor is ** passed with the sqlite4_result_text() call. ** ** SQL function 'test_destructor_count' returns the number of outstanding ** allocations made by 'test_destructor'; ** ** WARNING: Not threadsafe. */ static int test_destructor_count_var = 0; static void destructor(void *pNotUsed, void *p){ char *zVal = (char *)p; assert(zVal); zVal--; sqlite4_free(0, zVal); test_destructor_count_var--; } static void test_destructor( |
︙ | ︙ | |||
107 108 109 110 111 112 113 | if( !zVal ){ return; } zVal[len+1] = 0; zVal[len+2] = 0; zVal++; memcpy(zVal, sqlite4_value_text(argv[0]), len); | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | if( !zVal ){ return; } zVal[len+1] = 0; zVal[len+2] = 0; zVal++; memcpy(zVal, sqlite4_value_text(argv[0]), len); sqlite4_result_text(pCtx, zVal, -1, destructor, 0); } #ifndef SQLITE4_OMIT_UTF16 static void test_destructor16( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ |
︙ | ︙ | |||
130 131 132 133 134 135 136 | if( !zVal ){ return; } zVal[len+1] = 0; zVal[len+2] = 0; zVal++; memcpy(zVal, sqlite4_value_text16(argv[0]), len); | | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | if( !zVal ){ return; } zVal[len+1] = 0; zVal[len+2] = 0; zVal++; memcpy(zVal, sqlite4_value_text16(argv[0]), len); sqlite4_result_text16(pCtx, zVal, -1, destructor, 0); } #endif static void test_destructor_count( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ |
︙ | ︙ | |||
163 164 165 166 167 168 169 | #ifndef SQLITE4_OMIT_UTF16 const void *z; sqlite4 * db = sqlite4_context_db_handle(ctx); sqlite4_aggregate_context(ctx, 2048); sqlite4BeginBenignMalloc(); z = sqlite4_errmsg16(db); sqlite4EndBenignMalloc(); | | | > > > | > | > | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | #ifndef SQLITE4_OMIT_UTF16 const void *z; sqlite4 * db = sqlite4_context_db_handle(ctx); sqlite4_aggregate_context(ctx, 2048); sqlite4BeginBenignMalloc(); z = sqlite4_errmsg16(db); sqlite4EndBenignMalloc(); sqlite4_result_text16(ctx, z, -1, SQLITE4_TRANSIENT, 0); #endif } /* ** Routines for testing the sqlite4_get_auxdata() and sqlite4_set_auxdata() ** interface. ** ** The test_auxdata() SQL function attempts to register each of its arguments ** as auxiliary data. If there are no prior registrations of aux data for ** that argument (meaning the argument is not a constant or this is its first ** call) then the result for that argument is 0. If there is a prior ** registration, the result for that argument is 1. The overall result ** is the individual argument results separated by spaces. */ static void free_test_auxdata(void *pEnv, void *p){ sqlite4_free((sqlite4_env*)pEnv, p); } static void test_auxdata( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ int i; char *zRet = testContextMalloc(pCtx, nArg*2); sqlite4_env *pEnv; if( !zRet ) return; memset(zRet, 0, nArg*2); for(i=0; i<nArg; i++){ char const *z = (char*)sqlite4_value_text(argv[i]); if( z ){ int n; char *zAux = sqlite4_get_auxdata(pCtx, i); if( zAux ){ zRet[i*2] = '1'; assert( strcmp(zAux,z)==0 ); }else { zRet[i*2] = '0'; } n = strlen(z) + 1; zAux = testContextMalloc(pCtx, n); if( zAux ){ memcpy(zAux, z, n); sqlite4_set_auxdata(pCtx, i, zAux, free_test_auxdata, sqlite4_context_env(pCtx)); } zRet[i*2+1] = ' '; } } sqlite4_result_text(pCtx, zRet, 2*nArg-1, free_test_auxdata, sqlite4_context_env(pCtx)); } /* ** A function to test error reporting from user functions. This function ** returns a copy of its first argument as the error message. If the ** second argument exists, it becomes the error code. */ |
︙ | ︙ | |||
230 231 232 233 234 235 236 | sqlite4_result_error_code(pCtx, sqlite4_value_int(argv[1])); } } /* A counter object with its destructor. Used by counterFunc() below. */ struct counterObject { int cnt; sqlite4_env *pEnv; }; | | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | sqlite4_result_error_code(pCtx, sqlite4_value_int(argv[1])); } } /* A counter object with its destructor. Used by counterFunc() below. */ struct counterObject { int cnt; sqlite4_env *pEnv; }; void counterFree(void *NotUsed, void *x){ struct counterObject *p = (struct counterObject*)x; sqlite4_free(p->pEnv, p); } /* ** Implementation of the counter(X) function. If X is an integer ** constant, then the first invocation will return X. The second X+1. |
︙ | ︙ | |||
256 257 258 259 260 261 262 | pCounter = sqlite4_malloc(sqlite4_context_env(pCtx), sizeof(*pCounter) ); if( pCounter==0 ){ sqlite4_result_error_nomem(pCtx); return; } pCounter->cnt = sqlite4_value_int(argv[0]); pCounter->pEnv = sqlite4_context_env(pCtx); | | | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | pCounter = sqlite4_malloc(sqlite4_context_env(pCtx), sizeof(*pCounter) ); if( pCounter==0 ){ sqlite4_result_error_nomem(pCtx); return; } pCounter->cnt = sqlite4_value_int(argv[0]); pCounter->pEnv = sqlite4_context_env(pCtx); sqlite4_set_auxdata(pCtx, 0, pCounter, counterFree, 0); }else{ pCounter->cnt++; } sqlite4_result_int(pCtx, pCounter->cnt); } |
︙ | ︙ | |||
319 320 321 322 323 324 325 | } if( rc ){ char *zErr; sqlite4_env *pEnv = sqlite4_context_env(pCtx); assert( pStmt==0 ); zErr = sqlite4_mprintf(pEnv, "sqlite4_prepare() error: %s", sqlite4_errmsg(db)); | | | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | } if( rc ){ char *zErr; sqlite4_env *pEnv = sqlite4_context_env(pCtx); assert( pStmt==0 ); zErr = sqlite4_mprintf(pEnv, "sqlite4_prepare() error: %s", sqlite4_errmsg(db)); sqlite4_result_text(pCtx, zErr, -1, SQLITE4_DYNAMIC, 0); sqlite4_result_error_code(pCtx, rc); } } /* ** convert one character from hex to binary |
︙ | ︙ | |||
372 373 374 375 376 377 378 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); | | | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite4_result_text16be(pCtx, zOut, n/2, SQLITE4_DYNAMIC, 0); } } #endif /* ** hex_to_utf8(HEX) ** |
︙ | ︙ | |||
399 400 401 402 403 404 405 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); | | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite4_result_text(pCtx, zOut, n/2, SQLITE4_DYNAMIC, 0); } } /* ** hex_to_utf16le(HEX) ** ** Convert the input string from HEX into binary. Then return the |
︙ | ︙ | |||
426 427 428 429 430 431 432 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); | | | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | n = sqlite4_value_bytes(argv[0]); zIn = (const char*)sqlite4_value_text(argv[0]); zOut = sqlite4_malloc(sqlite4_context_env(pCtx), n/2 ); if( zOut==0 ){ sqlite4_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite4_result_text16le(pCtx, zOut, n/2, SQLITE4_DYNAMIC, 0); } } #endif static int registerTestFunctions(sqlite4 *db){ static const struct { char *zName; |
︙ | ︙ |
Changes to test/test_kv2.c.
︙ | ︙ | |||
292 293 294 295 296 297 298 299 300 301 302 303 304 305 | if( kvwg.xFactory==0 ){ sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET, "main", &kvwg.xFactory); sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH, "main",newFileStorage); } return TCL_OK; } static int kvwrap_seek_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } | > > > > > > > > > > > > > | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | if( kvwg.xFactory==0 ){ sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET, "main", &kvwg.xFactory); sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH, "main",newFileStorage); } return TCL_OK; } static int kvwrap_uninstall_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } if( kvwg.xFactory ){ sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_POP,"main", &kvwg.xFactory); kvwg.xFactory = 0; } return TCL_OK; } static int kvwrap_seek_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } |
︙ | ︙ | |||
344 345 346 347 348 349 350 351 352 353 354 355 356 357 | const char *zCmd; int (*xCmd)(Tcl_Interp *, int, Tcl_Obj **); } aSub[] = { { "install", kvwrap_install_cmd }, { "step", kvwrap_step_cmd }, { "seek", kvwrap_seek_cmd }, { "reset", kvwrap_reset_cmd }, }; int iSub; int rc; rc = Tcl_GetIndexFromObjStruct( interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub ); | > | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | const char *zCmd; int (*xCmd)(Tcl_Interp *, int, Tcl_Obj **); } aSub[] = { { "install", kvwrap_install_cmd }, { "step", kvwrap_step_cmd }, { "seek", kvwrap_seek_cmd }, { "reset", kvwrap_reset_cmd }, { "uninstall", kvwrap_uninstall_cmd }, }; int iSub; int rc; rc = Tcl_GetIndexFromObjStruct( interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub ); |
︙ | ︙ |
Changes to test/test_lsm.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2012 May 21 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < | < | < < < < < < | < < < < | < | > | > | | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | /* ** 2012 May 21 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* */ #include <tcl.h> #include "lsm.h" #include "sqlite4.h" #include <assert.h> #include <string.h> extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite4 **ppDb); extern const char *sqlite4TestErrorName(int); /************************************************************************* */ #define ENCRYPTION_XOR_MASK 0x23b2bbb6 static int testCompressEncBound(void *pCtx, int nSrc){ return nSrc; } static int testCompressEncCompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ int i; unsigned int *aIn = (unsigned int *)pOut; unsigned int *aOut = (unsigned int *)pIn; assert( (nIn%4)==0 ); for(i=0; i<(nIn/4); i++){ aOut[i] = (aIn[i] ^ ENCRYPTION_XOR_MASK); } *pnOut = nIn; return LSM_OK; } static int testCompressEncUncompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ return testCompressEncCompress(pCtx, pOut, pnOut, pIn, nIn); } static void testCompressEncFree(void *pCtx){ /* no-op */ } /* ** End of compression routines "encrypt". *************************************************************************/ /************************************************************************* */ static int testCompressRleBound(void *pCtx, int nSrc){ return nSrc*2; } static int testCompressRleCompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ int iOut = 0; int i; char c; int n; c = pIn[0]; n = 1; for(i=1; i<nIn; i++){ if( pIn[i]==c && n<127 ){ n++; }else{ pOut[iOut++] = c; pOut[iOut++] = (char)n; c = pIn[i]; n = 1; } } pOut[iOut++] = c; pOut[iOut++] = (char)n; *pnOut = iOut; return LSM_OK; } static int testCompressRleUncompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ int i; int iOut = 0; for(i=0; i<nIn; i+=2){ int iRep; char c = pIn[i]; int n = (int)(pIn[i+1]); for(iRep=0; iRep<n; iRep++){ pOut[iOut++] = c; } } *pnOut = iOut; return LSM_OK; } static void testCompressRleFree(void *pCtx){ } /* ** End of compression routines "rle". *************************************************************************/ /************************************************************************* */ static int testCompressNoopBound(void *pCtx, int nSrc){ return nSrc; } static int testCompressNoopCompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ *pnOut = nIn; memcpy(pOut, pIn, nIn); return LSM_OK; } static int testCompressNoopUncompress( void *pCtx, char *pOut, int *pnOut, const char *pIn, int nIn ){ *pnOut = nIn; memcpy(pOut, pIn, nIn); return LSM_OK; } static void testCompressNoopFree(void *pCtx){ } /* ** End of compression routines "noop". *************************************************************************/ static int testConfigureSetCompression( Tcl_Interp *interp, lsm_db *db, Tcl_Obj *pCmp, unsigned int iId ){ struct CompressionScheme { const char *zName; lsm_compress cmp; } aCmp[] = { { "encrypt", { 0, 43, testCompressEncBound, testCompressEncCompress, testCompressEncUncompress, testCompressEncFree } }, { "rle", { 0, 44, testCompressRleBound, testCompressRleCompress, testCompressRleUncompress, testCompressRleFree } }, { "noop", { 0, 45, testCompressNoopBound, testCompressNoopCompress, testCompressNoopUncompress, testCompressNoopFree } }, { 0, {0, 0, 0, 0, 0, 0} } }; int iOpt; int rc; if( interp ){ rc = Tcl_GetIndexFromObjStruct( interp, pCmp, aCmp, sizeof(aCmp[0]), "scheme", 0, &iOpt ); if( rc!=TCL_OK ) return rc; }else{ int nOpt = sizeof(aCmp)/sizeof(aCmp[0]); for(iOpt=0; iOpt<nOpt; iOpt++){ if( iId==aCmp[iOpt].cmp.iId ) break; } if( iOpt==nOpt ) return 0; } rc = lsm_config(db, LSM_CONFIG_SET_COMPRESSION, &aCmp[iOpt].cmp); return rc; } static int testCompressFactory(void *pCtx, lsm_db *db, unsigned int iId){ return testConfigureSetCompression(0, db, 0, iId); } static int testConfigureSetFactory( Tcl_Interp *interp, lsm_db *db, Tcl_Obj *pArg ){ lsm_compress_factory aFactory[2] = { { 0, 0, 0 }, { 0, testCompressFactory, 0 }, }; int bArg = 0; int rc; rc = Tcl_GetBooleanFromObj(interp, pArg, &bArg); if( rc!=TCL_OK ) return rc; assert( bArg==1 || bArg==0 ); rc = lsm_config(db, LSM_CONFIG_SET_COMPRESSION_FACTORY, &aFactory[bArg]); return rc; } /* ** Array apObj[] is an array of nObj Tcl objects intended to be transformed ** into lsm_config() calls on database db. ** ** Each pair of objects in the array is treated as a key/value pair used ** as arguments to a single lsm_config() call. If there are an even number ** of objects in the array, then the interpreter result is set to the output ** value of the final lsm_config() call. Or, if there are an odd number of ** objects in the array, the final object is treated as the key for a ** read-only call to lsm_config(), the return value of which is used as ** the interpreter result. For example, the following: ** ** { safety 1 mmap 0 use_log } ** ** Results in a sequence of calls similar to: ** ** iVal = 1; lsm_config(db, LSM_CONFIG_SAFETY, &iVal); ** iVal = 0; lsm_config(db, LSM_CONFIG_MMAP, &iVal); ** iVal = -1; lsm_config(db, LSM_CONFIG_USE_LOG, &iVal); ** Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); */ static int testConfigureLsm( Tcl_Interp *interp, lsm_db *db, int nObj, Tcl_Obj *const* apObj ){ struct Lsmconfig { const char *zOpt; int eOpt; int bInteger; } aConfig[] = { { "autoflush", LSM_CONFIG_AUTOFLUSH, 1 }, { "page_size", LSM_CONFIG_PAGE_SIZE, 1 }, { "block_size", LSM_CONFIG_BLOCK_SIZE, 1 }, { "safety", LSM_CONFIG_SAFETY, 1 }, { "autowork", LSM_CONFIG_AUTOWORK, 1 }, { "autocheckpoint", LSM_CONFIG_AUTOCHECKPOINT, 1 }, { "mmap", LSM_CONFIG_MMAP, 1 }, { "use_log", LSM_CONFIG_USE_LOG, 1 }, { "automerge", LSM_CONFIG_AUTOMERGE, 1 }, { "max_freelist", LSM_CONFIG_MAX_FREELIST, 1 }, { "multi_proc", LSM_CONFIG_MULTIPLE_PROCESSES, 1 }, { "set_compression", LSM_CONFIG_SET_COMPRESSION, 0 }, { "set_compression_factory", LSM_CONFIG_SET_COMPRESSION_FACTORY, 0 }, { "readonly", LSM_CONFIG_READONLY, 1 }, { 0, 0, 0 } }; int i; int rc = TCL_OK; for(i=0; rc==TCL_OK && i<nObj; i+=2){ int iOpt; rc = Tcl_GetIndexFromObjStruct( interp, apObj[i], aConfig, sizeof(aConfig[0]), "option", 0, &iOpt ); if( rc==TCL_OK ){ if( i==(nObj-1) ){ Tcl_ResetResult(interp); if( aConfig[iOpt].bInteger ){ int iVal = -1; lsm_config(db, aConfig[iOpt].eOpt, &iVal); Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); } }else{ if( aConfig[iOpt].eOpt==LSM_CONFIG_SET_COMPRESSION ){ rc = testConfigureSetCompression(interp, db, apObj[i+1], 0); } else if( aConfig[iOpt].eOpt==LSM_CONFIG_SET_COMPRESSION_FACTORY ){ rc = testConfigureSetFactory(interp, db, apObj[i+1]); } else { int iVal; rc = Tcl_GetIntFromObj(interp, apObj[i+1], &iVal); if( rc==TCL_OK ){ lsm_config(db, aConfig[iOpt].eOpt, &iVal); } Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); } } } } return rc; } /* ** TCLCMD: sqlite4_lsm_config DB DBNAME PARAM ... */ static int test_sqlite4_lsm_config( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zDb; /* objv[1] as a string */ const char *zName; /* objv[2] as a string */ int rc; sqlite4 *db; lsm_db *pLsm; /* Process arguments. Return early if there is a problem. */ if( objc!=4 && objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME PARAM ?VALUE?"); return TCL_ERROR; } zDb = Tcl_GetString(objv[1]); zName = Tcl_GetString(objv[2]); rc = getDbPointer(interp, zDb, &db); if( rc==TCL_OK ){ rc = sqlite4_kvstore_control(db, zName, SQLITE4_KVCTRL_LSM_HANDLE, &pLsm); if( rc!=SQLITE4_OK ){ Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC); rc = TCL_ERROR; } } if( rc==SQLITE4_OK ){ testConfigureLsm(interp, pLsm, objc-3, &objv[3]); } return rc; } /* ** TCLCMD: sqlite4_lsm_info DB DBNAME PARAM */ static int test_sqlite4_lsm_info( void * clientData, |
︙ | ︙ | |||
239 240 241 242 243 244 245 | return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } typedef struct TclLsmCursor TclLsmCursor; typedef struct TclLsm TclLsm; struct TclLsm { lsm_db *db; }; |
︙ | ︙ | |||
371 372 373 374 375 376 377 378 379 380 381 382 383 384 | static void test_lsm_del(void *ctx){ TclLsm *p = (TclLsm *)ctx; if( p ){ lsm_close(p->db); ckfree((char *)p); } } /* ** Usage: CSR sub-command ... */ static int test_lsm_cursor_cmd( void * clientData, Tcl_Interp *interp, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 | static void test_lsm_del(void *ctx){ TclLsm *p = (TclLsm *)ctx; if( p ){ lsm_close(p->db); ckfree((char *)p); } } static int testInfoLsm(Tcl_Interp *interp, lsm_db *db, Tcl_Obj *pObj){ struct Lsminfo { const char *zOpt; int eOpt; } aInfo[] = { { "compression_id", LSM_INFO_COMPRESSION_ID }, { 0, 0 } }; int rc; int iOpt; rc = Tcl_GetIndexFromObjStruct( interp, pObj, aInfo, sizeof(aInfo[0]), "option", 0, &iOpt ); if( rc==LSM_OK ){ switch( aInfo[iOpt].eOpt ){ case LSM_INFO_COMPRESSION_ID: { unsigned int iCmpId = 0; rc = lsm_info(db, LSM_INFO_COMPRESSION_ID, &iCmpId); if( rc==LSM_OK ){ Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)iCmpId)); }else{ test_lsm_error(interp, "lsm_info", rc); } break; } } } return rc; } /* ** Usage: CSR sub-command ... */ static int test_lsm_cursor_cmd( void * clientData, Tcl_Interp *interp, |
︙ | ︙ | |||
517 518 519 520 521 522 523 524 525 526 527 528 529 530 | /* 5 */ {"commit", 1, "LEVEL"}, /* 6 */ {"rollback", 1, "LEVEL"}, /* 7 */ {"csr_open", 1, "CSR"}, /* 8 */ {"work", -1, "?NMERGE? NPAGE"}, /* 9 */ {"flush", 0, ""}, /* 10 */ {"config", 1, "LIST"}, /* 11 */ {"checkpoint", 0, ""}, {0, 0, 0} }; int iCmd; int rc; TclLsm *p = (TclLsm *)clientData; if( objc<2 ){ | > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | /* 5 */ {"commit", 1, "LEVEL"}, /* 6 */ {"rollback", 1, "LEVEL"}, /* 7 */ {"csr_open", 1, "CSR"}, /* 8 */ {"work", -1, "?NMERGE? NPAGE"}, /* 9 */ {"flush", 0, ""}, /* 10 */ {"config", 1, "LIST"}, /* 11 */ {"checkpoint", 0, ""}, /* 12 */ {"info", 1, "OPTION"}, {0, 0, 0} }; int iCmd; int rc; TclLsm *p = (TclLsm *)clientData; if( objc<2 ){ |
︙ | ︙ | |||
619 620 621 622 623 624 625 | return TCL_OK; } case 8: assert( 0==strcmp(aCmd[8].zCmd, "work") ); { int nWork = 0; int nMerge = 1; int nWrite = 0; | < | 816 817 818 819 820 821 822 823 824 825 826 827 828 829 | return TCL_OK; } case 8: assert( 0==strcmp(aCmd[8].zCmd, "work") ); { int nWork = 0; int nMerge = 1; int nWrite = 0; if( objc==3 ){ rc = Tcl_GetIntFromObj(interp, objv[2], &nWork); }else if( objc==4 ){ rc = Tcl_GetIntFromObj(interp, objv[2], &nMerge); if( rc!=TCL_OK ) return rc; rc = Tcl_GetIntFromObj(interp, objv[3], &nWork); |
︙ | ︙ | |||
645 646 647 648 649 650 651 | case 9: assert( 0==strcmp(aCmd[9].zCmd, "flush") ); { rc = lsm_flush(p->db); return test_lsm_error(interp, "lsm_flush", rc); } case 10: assert( 0==strcmp(aCmd[10].zCmd, "config") ); { | > > > | > > > > > > | 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | case 9: assert( 0==strcmp(aCmd[9].zCmd, "flush") ); { rc = lsm_flush(p->db); return test_lsm_error(interp, "lsm_flush", rc); } case 10: assert( 0==strcmp(aCmd[10].zCmd, "config") ); { Tcl_Obj **apObj; int nObj; if( TCL_OK==Tcl_ListObjGetElements(interp, objv[2], &nObj, &apObj) ){ return testConfigureLsm(interp, p->db, nObj, apObj); } return TCL_ERROR; } case 11: assert( 0==strcmp(aCmd[11].zCmd, "checkpoint") ); { rc = lsm_checkpoint(p->db, 0); return test_lsm_error(interp, "lsm_checkpoint", rc); } case 12: assert( 0==strcmp(aCmd[12].zCmd, "info") ); { return testInfoLsm(interp, p->db, objv[2]); } default: assert( 0 ); } Tcl_AppendResult(interp, "internal error", 0); return TCL_ERROR; |
︙ | ︙ | |||
699 700 701 702 703 704 705 | if( rc!=LSM_OK ){ test_lsm_del((void *)p); test_lsm_error(interp, "lsm_new", rc); return TCL_ERROR; } if( objc==4 ){ | > > > > | > | 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 | if( rc!=LSM_OK ){ test_lsm_del((void *)p); test_lsm_error(interp, "lsm_new", rc); return TCL_ERROR; } if( objc==4 ){ Tcl_Obj **apObj; int nObj; rc = Tcl_ListObjGetElements(interp, objv[3], &nObj, &apObj); if( rc==TCL_OK ){ rc = testConfigureLsm(interp, p->db, nObj, apObj); } if( rc!=TCL_OK ){ test_lsm_del((void *)p); return rc; } } lsm_config_log(p->db, xLog, 0); |
︙ | ︙ | |||
727 728 729 730 731 732 733 | int SqlitetestLsm_Init(Tcl_Interp *interp){ struct SyscallCmd { const char *zName; Tcl_ObjCmdProc *xCmd; } aCmd[] = { { "sqlite4_lsm_work", test_sqlite4_lsm_work }, { "sqlite4_lsm_checkpoint", test_sqlite4_lsm_checkpoint }, | < | 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 | int SqlitetestLsm_Init(Tcl_Interp *interp){ struct SyscallCmd { const char *zName; Tcl_ObjCmdProc *xCmd; } aCmd[] = { { "sqlite4_lsm_work", test_sqlite4_lsm_work }, { "sqlite4_lsm_checkpoint", test_sqlite4_lsm_checkpoint }, { "sqlite4_lsm_info", test_sqlite4_lsm_info }, { "sqlite4_lsm_config", test_sqlite4_lsm_config }, { "lsm_open", test_lsm_open }, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0); } return TCL_OK; } |
Changes to test/test_main.c.
︙ | ︙ | |||
563 564 565 566 567 568 569 | sqlite4_snprintf(zStr, sizeof(zStr), "abcdefghijklmnopqrstuvwxyz"); sqlite4_snprintf(zStr, n, zFormat, a1); Tcl_AppendResult(interp, zStr, 0); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | sqlite4_snprintf(zStr, sizeof(zStr), "abcdefghijklmnopqrstuvwxyz"); sqlite4_snprintf(zStr, n, zFormat, a1); Tcl_AppendResult(interp, zStr, 0); return TCL_OK; } /* ** Usage: sqlite4_key DB KEY ** ** Set the codec key. */ static int test_key( void *NotUsed, |
︙ | ︙ | |||
682 683 684 685 686 687 688 | sqlite4_value **argv ){ int i; for(i=0; i<argc; i++){ if( SQLITE4_NULL!=sqlite4_value_type(argv[i]) ){ int n = sqlite4_value_bytes(argv[i]); sqlite4_result_text(context, (char*)sqlite4_value_text(argv[i]), | | | | | 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 | sqlite4_value **argv ){ int i; for(i=0; i<argc; i++){ if( SQLITE4_NULL!=sqlite4_value_type(argv[i]) ){ int n = sqlite4_value_bytes(argv[i]); sqlite4_result_text(context, (char*)sqlite4_value_text(argv[i]), n, SQLITE4_TRANSIENT, 0); break; } } } /* ** These are test functions. hex8() interprets its argument as ** UTF8 and returns a hex encoding. hex16le() interprets its argument ** as UTF16le and returns a hex encoding. */ static void hex8Func(sqlite4_context *p, int argc, sqlite4_value **argv){ const unsigned char *z; int i; char zBuf[200]; z = sqlite4_value_text(argv[0]); for(i=0; i<sizeof(zBuf)/2 - 2 && z[i]; i++){ sprintf(&zBuf[i*2], "%02x", z[i]&0xff); } zBuf[i*2] = 0; sqlite4_result_text(p, (char*)zBuf, -1, SQLITE4_TRANSIENT, 0); } #ifndef SQLITE4_OMIT_UTF16 static void hex16Func(sqlite4_context *p, int argc, sqlite4_value **argv){ const unsigned short int *z; int i; char zBuf[400]; z = sqlite4_value_text16(argv[0]); for(i=0; i<sizeof(zBuf)/4 - 4 && z[i]; i++){ sprintf(&zBuf[i*4], "%04x", z[i]&0xff); } zBuf[i*4] = 0; sqlite4_result_text(p, (char*)zBuf, -1, SQLITE4_TRANSIENT, 0); } #endif /* ** A structure into which to accumulate text. */ struct dstr { |
︙ | ︙ | |||
787 788 789 790 791 792 793 | sqlite4_value **argv ){ struct dstr x; memset(&x, 0, sizeof(x)); (void)sqlite4_exec((sqlite4*)sqlite4_user_data(context), (char*)sqlite4_value_text(argv[0]), execFuncCallback, &x, 0); | | | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | sqlite4_value **argv ){ struct dstr x; memset(&x, 0, sizeof(x)); (void)sqlite4_exec((sqlite4*)sqlite4_user_data(context), (char*)sqlite4_value_text(argv[0]), execFuncCallback, &x, 0); sqlite4_result_text(context, x.z, x.nUsed, SQLITE4_TRANSIENT, 0); sqlite4_free(0, x.z); } /* ** Implementation of tkt2213func(), a scalar function that takes exactly ** one argument. It has two interesting features: ** |
︙ | ︙ | |||
827 828 829 830 831 832 833 | zText3 = sqlite4_value_text(argv[0]); if( zText1!=zText2 || zText2!=zText3 ){ sqlite4_result_error(context, "tkt2213 is not fixed", -1); }else{ char *zCopy = (char *)sqlite4_malloc(sqlite4_context_env(context),nText); memcpy(zCopy, zText1, nText); | | | 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 | zText3 = sqlite4_value_text(argv[0]); if( zText1!=zText2 || zText2!=zText3 ){ sqlite4_result_error(context, "tkt2213 is not fixed", -1); }else{ char *zCopy = (char *)sqlite4_malloc(sqlite4_context_env(context),nText); memcpy(zCopy, zText1, nText); sqlite4_result_text(context, zCopy, nText, SQLITE4_DYNAMIC, 0); } } /* ** The following SQL function takes 4 arguments. The 2nd and ** 4th argument must be one of these strings: 'text', 'text16', ** or 'blob' corresponding to API functions |
︙ | ︙ | |||
965 966 967 968 969 970 971 | /* Use the sqlite4_create_function16() API here. Mainly for fun, but also ** because it is not tested anywhere else. */ if( rc==SQLITE4_OK ){ const void *zUtf16; sqlite4_value *pVal; sqlite4_mutex_enter(db->mutex); pVal = sqlite4ValueNew(db); | | > | 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 | /* Use the sqlite4_create_function16() API here. Mainly for fun, but also ** because it is not tested anywhere else. */ if( rc==SQLITE4_OK ){ const void *zUtf16; sqlite4_value *pVal; sqlite4_mutex_enter(db->mutex); pVal = sqlite4ValueNew(db); sqlite4ValueSetStr(pVal, -1, "x_sqlite_exec", SQLITE4_UTF8, SQLITE4_STATIC, 0); zUtf16 = sqlite4ValueText(pVal, SQLITE4_UTF16NATIVE); if( db->mallocFailed ){ rc = SQLITE4_NOMEM; }else{ rc = sqlite4_create_function16(db, zUtf16, 1, SQLITE4_UTF16, db, sqlite4ExecFunc, 0, 0); } |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 | sqlite4_result_error(context, "x_count totals to 42", -1); }else{ sqlite4_result_int(context, p ? p->n : 0); } } } | < < < < < < < < < < < < < < | 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 | sqlite4_result_error(context, "x_count totals to 42", -1); }else{ sqlite4_result_int(context, p ? p->n : 0); } } } /* ** Usage: sqlite4_create_aggregate DB ** ** Call the sqlite4_create_function API on the given database in order ** to create a function named "x_count". This function is similar ** to the built-in count() function, with a few special quirks ** for testing the sqlite4_result_error() APIs. |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_function(db, "x_count", 0, SQLITE4_UTF8, 0, 0, t1CountStep,t1CountFinalize); if( rc==SQLITE4_OK ){ rc = sqlite4_create_function(db, "x_count", 1, SQLITE4_UTF8, 0, 0, t1CountStep,t1CountFinalize); } | < < < < < < < | 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_function(db, "x_count", 0, SQLITE4_UTF8, 0, 0, t1CountStep,t1CountFinalize); if( rc==SQLITE4_OK ){ rc = sqlite4_create_function(db, "x_count", 1, SQLITE4_UTF8, 0, 0, t1CountStep,t1CountFinalize); } if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } /* |
︙ | ︙ | |||
1710 1711 1712 1713 1714 1715 1716 | if( zArg0 ){ if( 0==sqlite4_stricmp(zArg0, "int") ){ sqlite4_result_int(context, sqlite4_value_int(argv[1])); }else if( sqlite4_stricmp(zArg0,"int64")==0 ){ sqlite4_result_int64(context, sqlite4_value_int64(argv[1])); }else if( sqlite4_stricmp(zArg0,"string")==0 ){ sqlite4_result_text(context, (char*)sqlite4_value_text(argv[1]), -1, | | | 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 | if( zArg0 ){ if( 0==sqlite4_stricmp(zArg0, "int") ){ sqlite4_result_int(context, sqlite4_value_int(argv[1])); }else if( sqlite4_stricmp(zArg0,"int64")==0 ){ sqlite4_result_int64(context, sqlite4_value_int64(argv[1])); }else if( sqlite4_stricmp(zArg0,"string")==0 ){ sqlite4_result_text(context, (char*)sqlite4_value_text(argv[1]), -1, SQLITE4_TRANSIENT, 0); }else if( sqlite4_stricmp(zArg0,"double")==0 ){ sqlite4_result_double(context, sqlite4_value_double(argv[1])); }else if( sqlite4_stricmp(zArg0,"null")==0 ){ sqlite4_result_null(context); }else if( sqlite4_stricmp(zArg0,"value")==0 ){ sqlite4_result_value(context, argv[sqlite4_value_int(argv[1])]); }else{ |
︙ | ︙ | |||
1986 1987 1988 1989 1990 1991 1992 | if( rc ){ return TCL_ERROR; } */ return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 | if( rc ){ return TCL_ERROR; } */ return TCL_OK; } /* ** Usage: sqlite4_changes DB ** ** Return the number of changes made to the database by the last SQL ** execution. */ static int test_changes( |
︙ | ︙ | |||
2096 2097 2098 2099 2100 2101 2102 | return TCL_ERROR; } if( getStmtPointer(interp, argv[1], &pStmt) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &idx) ) return TCL_ERROR; if( strcmp(argv[4],"null")==0 ){ rc = sqlite4_bind_null(pStmt, idx); }else if( strcmp(argv[4],"static")==0 ){ | | | | | | 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 | return TCL_ERROR; } if( getStmtPointer(interp, argv[1], &pStmt) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &idx) ) return TCL_ERROR; if( strcmp(argv[4],"null")==0 ){ rc = sqlite4_bind_null(pStmt, idx); }else if( strcmp(argv[4],"static")==0 ){ rc = sqlite4_bind_text(pStmt, idx, sqlite_static_bind_value, -1, 0, 0); }else if( strcmp(argv[4],"static-nbytes")==0 ){ rc = sqlite4_bind_text(pStmt, idx, sqlite_static_bind_value, sqlite_static_bind_nbyte, 0, 0); }else if( strcmp(argv[4],"normal")==0 ){ rc = sqlite4_bind_text(pStmt, idx, argv[3], -1, SQLITE4_TRANSIENT, 0); }else if( strcmp(argv[4],"blob10")==0 ){ rc = sqlite4_bind_text(pStmt, idx, "abc\000xyz\000pq", 10,SQLITE4_STATIC,0); }else{ Tcl_AppendResult(interp, "4th argument should be " "\"null\" or \"static\" or \"normal\"", 0); return TCL_ERROR; } if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc ){ |
︙ | ︙ | |||
2183 2184 2185 2186 2187 2188 2189 | default: assert(0); } sqlite4BeginBenignMalloc(pEnv); pVal = sqlite4ValueNew(0); if( pVal ){ | | | | 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 | default: assert(0); } sqlite4BeginBenignMalloc(pEnv); pVal = sqlite4ValueNew(0); if( pVal ){ sqlite4ValueSetStr(pVal, nA, zA, encin, SQLITE4_STATIC, 0); n = sqlite4_value_bytes(pVal); Tcl_ListObjAppendElement(i,pX, Tcl_NewStringObj((char*)sqlite4_value_text(pVal),n)); sqlite4ValueSetStr(pVal, nB, zB, encin, SQLITE4_STATIC, 0); n = sqlite4_value_bytes(pVal); Tcl_ListObjAppendElement(i,pX, Tcl_NewStringObj((char*)sqlite4_value_text(pVal),n)); sqlite4ValueFree(pVal); } sqlite4EndBenignMalloc(pEnv); |
︙ | ︙ | |||
2385 2386 2387 2388 2389 2390 2391 | pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-8", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj((char*)sqlite4_value_text(argv[0]), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); | | > | | | | > | | | | | 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 | pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-8", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj((char*)sqlite4_value_text(argv[0]), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); sqlite4_result_text(pCtx, Tcl_GetStringResult(interp), -1, SQLITE4_TRANSIENT, 0); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text16be(pCtx, sqlite4_value_text16be(pVal), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } static void test_function_utf16le( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ Tcl_Interp *interp; Tcl_Obj *pX; sqlite4_value *pVal; interp = (Tcl_Interp *)sqlite4_user_data(pCtx); pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16LE", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj((char*)sqlite4_value_text(argv[0]), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text(pCtx, (char*)sqlite4_value_text(pVal), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } static void test_function_utf16be( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ Tcl_Interp *interp; Tcl_Obj *pX; sqlite4_value *pVal; interp = (Tcl_Interp *)sqlite4_user_data(pCtx); pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16BE", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj((char*)sqlite4_value_text(argv[0]), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text16(pCtx, sqlite4_value_text16le(pVal), -1, SQLITE4_TRANSIENT, 0); sqlite4_result_text16be(pCtx, sqlite4_value_text16le(pVal), -1, SQLITE4_TRANSIENT, 0); sqlite4_result_text16le(pCtx, sqlite4_value_text16le(pVal), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } #endif /* SQLITE4_OMIT_UTF16 */ static int test_function( void * clientData, Tcl_Interp *interp, int objc, |
︙ | ︙ | |||
2530 2531 2532 2533 2534 2535 2536 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return TCL_OK; /* Do nothing */ } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return TCL_OK; /* Do nothing */ } /* ** Usage: sqlite4_bind_int STMT N VALUE ** ** Test the sqlite4_bind_int interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 32-bit integer VALUE to that wildcard. */ |
︙ | ︙ | |||
2784 2785 2786 2787 2788 2789 2790 | } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(objv[3], &bytes); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; | | | 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 | } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(objv[3], &bytes); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; rc = sqlite4_bind_text(pStmt, idx, value, bytes, SQLITE4_TRANSIENT, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp, sqlite4TestErrorName(rc), 0); return TCL_ERROR; } return TCL_OK; |
︙ | ︙ | |||
2832 2833 2834 2835 2836 2837 2838 | } if( getStmtPointer(interp, Tcl_GetString(oStmt), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, oN, &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(oString, 0); if( Tcl_GetIntFromObj(interp, oBytes, &bytes) ) return TCL_ERROR; | | | 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 | } if( getStmtPointer(interp, Tcl_GetString(oStmt), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, oN, &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(oString, 0); if( Tcl_GetIntFromObj(interp, oBytes, &bytes) ) return TCL_ERROR; rc = sqlite4_bind_text16(pStmt, idx, (void *)value, bytes, xDel, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp, sqlite4TestErrorName(rc), 0); return TCL_ERROR; } #endif /* SQLITE4_OMIT_UTF16 */ |
︙ | ︙ | |||
2879 2880 2881 2882 2883 2884 2885 | } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = Tcl_GetString(objv[3]); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; | | | 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 | } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = Tcl_GetString(objv[3]); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; rc = sqlite4_bind_blob(pStmt, idx, value, bytes, xDestructor, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } |
︙ | ︙ | |||
3543 3544 3545 3546 3547 3548 3549 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR; zRet = xFunc(pStmt, col); if( zRet ){ Tcl_SetResult(interp, (char *)zRet, 0); } | < < < < < < < < < < < < < < < < < < | 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR; zRet = xFunc(pStmt, col); if( zRet ){ Tcl_SetResult(interp, (char *)zRet, 0); } return TCL_OK; } /* ** Usage: sqlite4_column_text STMT column ** ** Usage: sqlite4_column_decltype STMT column |
︙ | ︙ | |||
4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 | Tcl_AppendResult(interp, " ", aOpt[i].zOptName); } return TCL_ERROR; } sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite4_search_count; extern int sqlite4_found_count; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 | Tcl_AppendResult(interp, " ", aOpt[i].zOptName); } return TCL_ERROR; } sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask); return TCL_OK; } #define NUM_FORMAT "sign:%d approx:%d e:%d m:%lld" /* Append a return value representing a sqlite4_num. */ static void append_num_result( Tcl_Interp *interp, sqlite4_num A ){ char buf[100]; sprintf( buf, NUM_FORMAT, A.sign, A.approx, A.e, A.m ); Tcl_AppendResult(interp, buf, 0); } /* Convert a string either representing a sqlite4_num (listing its fields as ** returned by append_num_result) or that can be parsed as one. Invalid ** strings become NaN. */ static sqlite4_num test_parse_num( char *arg ){ sqlite4_num A; int sign, approx, e; if( sscanf( arg, NUM_FORMAT, &sign, &approx, &e, &A.m)==4 ){ A.sign = sign; A.approx = approx; A.e = e; return A; } else { return sqlite4_num_from_text(arg, -1, 0); } } /* Convert return values of sqlite4_num to strings that will be readable in ** the tests. */ static char *describe_num_comparison( int code ){ switch( code ){ case 0: return "incomparable"; case 1: return "lesser"; case 2: return "equal"; case 3: return "greater"; default: return "error"; } } /* Compare two numbers A and B. Returns "incomparable", "lesser", "equal", ** "greater", or "error". */ static int test_num_compare( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4_num A, B; int cmp; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " NUM NUM\"", 0); return TCL_ERROR; } A = test_parse_num( argv[1] ); B = test_parse_num( argv[2] ); cmp = sqlite4_num_compare(A, B); Tcl_AppendResult( interp, describe_num_comparison( cmp ), 0); return TCL_OK; } /* Create a sqlite4_num from a string. The optional second argument specifies ** how many bytes may be read. */ static int test_num_from_text( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4_num A; int len; if( argc!=2 && argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " STRING\" or \"", argv[0], " STRING INTEGER\"", 0); return TCL_ERROR; } if( argc==3 ){ if ( Tcl_GetInt(interp, argv[2], &len) ) return TCL_ERROR; }else{ len = -1; } A = sqlite4_num_from_text( argv[1], len, 0 ); append_num_result(interp, A); return TCL_OK; } static int test_num_to_text( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char text[30]; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " NUM\"", 0); return TCL_ERROR; } sqlite4_num_to_text( test_parse_num( argv[1] ), text ); Tcl_AppendResult( interp, text, 0 ); return TCL_OK; } static int test_num_binary_op( Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv, /* Text of each argument */ sqlite4_num (*op) (sqlite4_num, sqlite4_num) ){ sqlite4_num A, B, R; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " NUM NUM\"", 0); return TCL_ERROR; } A = test_parse_num(argv[1]); B = test_parse_num(argv[2]); R = op(A, B); append_num_result(interp, R); return TCL_OK; } static int test_num_add( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_binary_op( interp, argc, argv, sqlite4_num_add ); } static int test_num_sub( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_binary_op( interp, argc, argv, sqlite4_num_sub ); } static int test_num_mul( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_binary_op( interp, argc, argv, sqlite4_num_mul ); } static int test_num_div( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_binary_op( interp, argc, argv, sqlite4_num_div ); } static int test_num_predicate( Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv, /* Text of each argument */ int (*pred) (sqlite4_num) ){ sqlite4_num A; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " NUM\"", 0); return TCL_ERROR; } A = test_parse_num(argv[1]); Tcl_AppendResult(interp, pred(A) ? "true" : "false", 0); return TCL_OK; } static int test_num_isinf( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_predicate( interp, argc, argv, sqlite4_num_isinf ); } static int test_num_isnan( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return test_num_predicate( interp, argc, argv, sqlite4_num_isnan ); } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite4_search_count; extern int sqlite4_found_count; |
︙ | ︙ | |||
4377 4378 4379 4380 4381 4382 4383 | { "sqlite4_mprintf_stronly", (Tcl_CmdProc*)sqlite4_mprintf_stronly}, { "sqlite4_mprintf_double", (Tcl_CmdProc*)sqlite4_mprintf_double }, { "sqlite4_mprintf_scaled", (Tcl_CmdProc*)sqlite4_mprintf_scaled }, { "sqlite4_mprintf_hexdouble", (Tcl_CmdProc*)sqlite4_mprintf_hexdouble}, { "sqlite4_mprintf_z_test", (Tcl_CmdProc*)test_mprintf_z }, { "sqlite4_mprintf_n_test", (Tcl_CmdProc*)test_mprintf_n }, { "sqlite4_snprintf_int", (Tcl_CmdProc*)test_snprintf_int }, | < | 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 | { "sqlite4_mprintf_stronly", (Tcl_CmdProc*)sqlite4_mprintf_stronly}, { "sqlite4_mprintf_double", (Tcl_CmdProc*)sqlite4_mprintf_double }, { "sqlite4_mprintf_scaled", (Tcl_CmdProc*)sqlite4_mprintf_scaled }, { "sqlite4_mprintf_hexdouble", (Tcl_CmdProc*)sqlite4_mprintf_hexdouble}, { "sqlite4_mprintf_z_test", (Tcl_CmdProc*)test_mprintf_z }, { "sqlite4_mprintf_n_test", (Tcl_CmdProc*)test_mprintf_n }, { "sqlite4_snprintf_int", (Tcl_CmdProc*)test_snprintf_int }, { "sqlite4_exec_printf", (Tcl_CmdProc*)test_exec_printf }, { "sqlite4_exec_hex", (Tcl_CmdProc*)test_exec_hex }, { "sqlite4_exec", (Tcl_CmdProc*)test_exec }, { "sqlite4_exec_nr", (Tcl_CmdProc*)test_exec_nr }, { "sqlite4_close", (Tcl_CmdProc*)sqlite_test_close }, { "sqlite4_create_function", (Tcl_CmdProc*)test_create_function }, { "sqlite4_create_aggregate", (Tcl_CmdProc*)test_create_aggregate }, |
︙ | ︙ | |||
4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 | { "sqlite4_interrupt", (Tcl_CmdProc*)test_interrupt }, { "sqlite_delete_function", (Tcl_CmdProc*)delete_function }, { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation }, { "sqlite4_get_autocommit", (Tcl_CmdProc*)get_autocommit }, { "sqlite4_stack_used", (Tcl_CmdProc*)test_stack_used }, { "printf", (Tcl_CmdProc*)test_printf }, { "sqlite4IoTrace", (Tcl_CmdProc*)test_io_trace }, }; static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { { "sqlite4_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite4_bind_int", test_bind_int, 0 }, | > > > > > > > > > < | 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 | { "sqlite4_interrupt", (Tcl_CmdProc*)test_interrupt }, { "sqlite_delete_function", (Tcl_CmdProc*)delete_function }, { "sqlite_delete_collation", (Tcl_CmdProc*)delete_collation }, { "sqlite4_get_autocommit", (Tcl_CmdProc*)get_autocommit }, { "sqlite4_stack_used", (Tcl_CmdProc*)test_stack_used }, { "printf", (Tcl_CmdProc*)test_printf }, { "sqlite4IoTrace", (Tcl_CmdProc*)test_io_trace }, { "sqlite4_num_compare", (Tcl_CmdProc*)test_num_compare }, { "sqlite4_num_from_text", (Tcl_CmdProc*)test_num_from_text }, { "sqlite4_num_to_text", (Tcl_CmdProc*)test_num_to_text }, { "sqlite4_num_add", (Tcl_CmdProc*)test_num_add }, { "sqlite4_num_sub", (Tcl_CmdProc*)test_num_sub }, { "sqlite4_num_mul", (Tcl_CmdProc*)test_num_mul }, { "sqlite4_num_div", (Tcl_CmdProc*)test_num_div }, { "sqlite4_num_isinf", (Tcl_CmdProc*)test_num_isinf }, { "sqlite4_num_isnan", (Tcl_CmdProc*)test_num_isnan }, }; static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { { "sqlite4_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite4_bind_int", test_bind_int, 0 }, { "sqlite4_bind_int64", test_bind_int64, 0 }, { "sqlite4_bind_double", test_bind_double, 0 }, { "sqlite4_bind_null", test_bind_null ,0 }, { "sqlite4_bind_text", test_bind_text ,0 }, { "sqlite4_bind_text16", test_bind_text16 ,0 }, { "sqlite4_bind_blob", test_bind_blob ,0 }, { "sqlite4_bind_parameter_count", test_bind_parameter_count, 0}, |
︙ | ︙ | |||
4430 4431 4432 4433 4434 4435 4436 | { "sqlite4_open_v2", test_open_v2 ,0 }, { "sqlite4_prepare", test_prepare ,0 }, { "sqlite4_prepare_tkt3134", test_prepare_tkt3134, 0}, { "sqlite4_finalize", test_finalize ,0 }, { "sqlite4_stmt_status", test_stmt_status ,0 }, { "sqlite4_reset", test_reset ,0 }, | < < | 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 | { "sqlite4_open_v2", test_open_v2 ,0 }, { "sqlite4_prepare", test_prepare ,0 }, { "sqlite4_prepare_tkt3134", test_prepare_tkt3134, 0}, { "sqlite4_finalize", test_finalize ,0 }, { "sqlite4_stmt_status", test_stmt_status ,0 }, { "sqlite4_reset", test_reset ,0 }, { "sqlite4_changes", test_changes ,0 }, { "sqlite4_step", test_step ,0 }, { "sqlite4_sql", test_sql ,0 }, { "sqlite4_next_stmt", test_next_stmt ,0 }, { "sqlite4_stmt_readonly", test_stmt_readonly ,0 }, { "sqlite4_stmt_busy", test_stmt_busy ,0 }, { "uses_stmt_journal", uses_stmt_journal ,0 }, |
︙ | ︙ | |||
4486 4487 4488 4489 4490 4491 4492 | {"sqlite4_column_database_name16", test_stmt_utf16, sqlite4_column_database_name16}, {"sqlite4_column_table_name16", test_stmt_utf16, (void*)sqlite4_column_table_name16}, {"sqlite4_column_origin_name16", test_stmt_utf16, (void*)sqlite4_column_origin_name16}, #endif #endif { "sqlite4_create_collation", test_create_collation, 0 }, | < | 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 | {"sqlite4_column_database_name16", test_stmt_utf16, sqlite4_column_database_name16}, {"sqlite4_column_table_name16", test_stmt_utf16, (void*)sqlite4_column_table_name16}, {"sqlite4_column_origin_name16", test_stmt_utf16, (void*)sqlite4_column_origin_name16}, #endif #endif { "sqlite4_create_collation", test_create_collation, 0 }, { "working_64bit_int", working_64bit_int, 0 }, { "sqlite4_create_function_v2", test_create_function_v2, 0 }, /* Functions from os.h */ #ifndef SQLITE4_OMIT_UTF16 { "add_test_collate", test_collate, 0 }, { "add_test_collate_needed", test_collate_needed, 0 }, |
︙ | ︙ | |||
4519 4520 4521 4522 4523 4524 4525 | extern int sqlite4_pager_writej_count; #if SQLITE4_OS_WIN extern int sqlite4_os_type; #endif #ifdef SQLITE4_DEBUG extern int sqlite4WhereTrace; extern int sqlite4OSTrace; | < | 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 | extern int sqlite4_pager_writej_count; #if SQLITE4_OS_WIN extern int sqlite4_os_type; #endif #ifdef SQLITE4_DEBUG extern int sqlite4WhereTrace; extern int sqlite4OSTrace; #endif #ifdef SQLITE4_TEST extern char sqlite4_query_plan[]; static char *query_plan = sqlite4_query_plan; #ifdef SQLITE4_ENABLE_FTS3 extern int sqlite4_fts3_enable_parentheses; #endif |
︙ | ︙ | |||
4574 4575 4576 4577 4578 4579 4580 | (char*)&sqlite4_os_type, TCL_LINK_INT); #endif #ifdef SQLITE4_TEST Tcl_LinkVar(interp, "sqlite_query_plan", (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); #endif #ifdef SQLITE4_DEBUG | < < | 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 | (char*)&sqlite4_os_type, TCL_LINK_INT); #endif #ifdef SQLITE4_TEST Tcl_LinkVar(interp, "sqlite_query_plan", (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); #endif #ifdef SQLITE4_DEBUG Tcl_LinkVar(interp, "sqlite_where_trace", (char*)&sqlite4WhereTrace, TCL_LINK_INT); #endif Tcl_LinkVar(interp, "sqlite_static_bind_value", (char*)&sqlite_static_bind_value, TCL_LINK_STRING); Tcl_LinkVar(interp, "sqlite_static_bind_nbyte", (char*)&sqlite_static_bind_nbyte, TCL_LINK_INT); |
︙ | ︙ |
Changes to test/test_utf.c.
︙ | ︙ | |||
112 113 114 115 116 117 118 | } if( pEnc->enc==SQLITE4_UTF16 ){ return SQLITE4_UTF16NATIVE; } return pEnc->enc; } | | > > | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | } if( pEnc->enc==SQLITE4_UTF16 ){ return SQLITE4_UTF16NATIVE; } return pEnc->enc; } static void freeStr(void *pEnv, void *pStr){ sqlite4_free((sqlite4_env*)pEnv, pStr); } /* ** Usage: test_translate <string/blob> <from enc> <to enc> ?<transient>? ** */ static int test_translate( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ u8 enc_from; u8 enc_to; sqlite4_value *pVal; char *z; int len; void (*xDel)(void*,void*) = SQLITE4_STATIC; if( objc!=4 && objc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " <string/blob> <from enc> <to enc>", 0 ); return TCL_ERROR; |
︙ | ︙ | |||
155 156 157 158 159 160 161 | pVal = sqlite4ValueNew(0); if( enc_from==SQLITE4_UTF8 ){ z = Tcl_GetString(objv[1]); if( objc==5 ){ z = sqlite4_mprintf(0, "%s", z); } | | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | pVal = sqlite4ValueNew(0); if( enc_from==SQLITE4_UTF8 ){ z = Tcl_GetString(objv[1]); if( objc==5 ){ z = sqlite4_mprintf(0, "%s", z); } sqlite4ValueSetStr(pVal, -1, z, enc_from, xDel, 0); }else{ z = (char*)Tcl_GetByteArrayFromObj(objv[1], &len); if( objc==5 ){ char *zTmp = z; z = sqlite4_malloc(0, len); memcpy(z, zTmp, len); } sqlite4ValueSetStr(pVal, -1, z, enc_from, xDel, 0); } z = (char *)sqlite4ValueText(pVal, enc_to); len = sqlite4ValueBytes(pVal, enc_to) + (enc_to==SQLITE4_UTF8?1:2); Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((u8*)z, len)); sqlite4ValueFree(pVal); |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
758 759 760 761 762 763 764 765 766 767 768 769 770 771 | puts "all of the test failures above might be a result from this defect" puts "in your TCL build." puts "******************************************************************" } if {$::cmdlinearg(binarylog)} { vfslog finalize binarylog } if {[lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1]>0 || [sqlite4_memory_used]>0} { puts "Unfreed memory: [sqlite4_memory_used] bytes in\ [lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1] allocations" incr nErr ifcapable memdebug||mem5||(mem3&&debug) { puts "Writing unfreed memory log to \"./memleak.txt\"" | > | 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 | puts "all of the test failures above might be a result from this defect" puts "in your TCL build." puts "******************************************************************" } if {$::cmdlinearg(binarylog)} { vfslog finalize binarylog } kvwrap uninstall if {[lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1]>0 || [sqlite4_memory_used]>0} { puts "Unfreed memory: [sqlite4_memory_used] bytes in\ [lindex [sqlite4_env_status SQLITE4_ENVSTATUS_MALLOC_COUNT 0] 1] allocations" incr nErr ifcapable memdebug||mem5||(mem3&&debug) { puts "Writing unfreed memory log to \"./memleak.txt\"" |
︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 | # Flush the in-memory tree to disk and merge all runs together into # a single b-tree structure. Because this annihilates all delete keys, # the next rowid allocated for each table with an IPK will be as expected # by SQLite 3 tests. # proc optimize_db {} { #catch { | > | > > > | 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 | # Flush the in-memory tree to disk and merge all runs together into # a single b-tree structure. Because this annihilates all delete keys, # the next rowid allocated for each table with an IPK will be as expected # by SQLite 3 tests. # proc optimize_db {} { #catch { set af [sqlite4_lsm_config db main autoflush] sqlite4_lsm_config db main autoflush 0 db eval { BEGIN EXCLUSIVE; COMMIT; } sqlite4_lsm_config db main autoflush $af sqlite4_lsm_work db main -nmerge 1 -npage 100000 sqlite4_lsm_checkpoint db main #} return "" } # If the library is compiled with the SQLITE4_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) source $testdir/malloc_common.tcl |
Changes to test/tkt-bd484a090c.test.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | do_test 1.1 { lindex [catchsql { SELECT datetime('now', 'localtime') }] 0 } {0} do_test 1.2 { lindex [catchsql { SELECT datetime('now', 'utc') }] 0 } {0} sqlite4_test_control SQLITE4_TESTCTRL_LOCALTIME_FAULT 1 do_test 2.1 { catchsql { SELECT datetime('now', 'localtime') } } {1 {local time unavailable}} do_test 2.2 { catchsql { SELECT datetime('now', 'utc') } } {1 {local time unavailable}} sqlite4_test_control SQLITE4_TESTCTRL_LOCALTIME_FAULT 0 finish_test | > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | do_test 1.1 { lindex [catchsql { SELECT datetime('now', 'localtime') }] 0 } {0} do_test 1.2 { lindex [catchsql { SELECT datetime('now', 'utc') }] 0 } {0} if 0 { sqlite4_test_control SQLITE4_TESTCTRL_LOCALTIME_FAULT 1 do_test 2.1 { catchsql { SELECT datetime('now', 'localtime') } } {1 {local time unavailable}} do_test 2.2 { catchsql { SELECT datetime('now', 'utc') } } {1 {local time unavailable}} sqlite4_test_control SQLITE4_TESTCTRL_LOCALTIME_FAULT 0 } finish_test |
Changes to test/tkt3442.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 | # These tests perform an EXPLAIN QUERY PLAN on both versions of the # SELECT referenced in ticket #3442 (both '5000' and "5000") # and verify that the query plan is the same. # ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } | < < < | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | # These tests perform an EXPLAIN QUERY PLAN on both versions of the # SELECT referenced in ticket #3442 (both '5000' and "5000") # and verify that the query plan is the same. # ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} } # Some extra tests testing other permutations of 5000. # ifcapable explain { |
︙ | ︙ |
Changes to test/tkt35xx.test.
︙ | ︙ | |||
29 30 31 32 33 34 35 | # Trigger the problem using explicit rollback. # do_test tkt35xx-1.1 { execsql { PRAGMA auto_vacuum = 0; CREATE TABLE t1(a,b,c); CREATE INDEX i1 ON t1(c); | | | | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | # Trigger the problem using explicit rollback. # do_test tkt35xx-1.1 { execsql { PRAGMA auto_vacuum = 0; CREATE TABLE t1(a,b,c); CREATE INDEX i1 ON t1(c); INSERT INTO t1 VALUES(0, 0, randomblob(676)); INSERT INTO t1 VALUES(1, 1, randomblob(676)); DELETE FROM t1; BEGIN; INSERT INTO t1 VALUES(0, 0, randomblob(676)); INSERT INTO t1 VALUES(1, 1, randomblob(676)); ROLLBACK; INSERT INTO t1 VALUES(0, 0, randomblob(676)); } execsql { INSERT INTO t1 VALUES(1, 1, randomblob(676)); } } {} # Trigger the problem using statement rollback. # db close delete_file test.db |
︙ | ︙ |
Changes to test/tkt3761.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # Ticket #3761: Make sure that an incremental vacuum on an in-memory # database can be rolled back. # # $Id: tkt3761.test,v 1.1 2009/03/31 02:54:40 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt3761-1.1 { db close sqlite4 db :memory: db eval { PRAGMA auto_vacuum=INCREMENTAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 SELECT x FROM t1; INSERT INTO t1 SELECT x FROM t1; | > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # Ticket #3761: Make sure that an incremental vacuum on an in-memory # database can be rolled back. # # $Id: tkt3761.test,v 1.1 2009/03/31 02:54:40 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl proc zeroblob {n} { string repeat [binary format c 0] $n } do_test tkt3761-1.1 { db close sqlite4 db :memory: db func zeroblob zeroblob db eval { PRAGMA auto_vacuum=INCREMENTAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 SELECT x FROM t1; INSERT INTO t1 SELECT x FROM t1; |
︙ | ︙ |
Changes to test/tkt3841.test.
︙ | ︙ | |||
23 24 25 26 27 28 29 | } do_test tkt3841.1 { execsql { CREATE TABLE table2 (key TEXT, x TEXT); CREATE TABLE list (key TEXT, value TEXT); | | | | | | | | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | } do_test tkt3841.1 { execsql { CREATE TABLE table2 (key TEXT, x TEXT); CREATE TABLE list (key TEXT, value TEXT); INSERT INTO table2 VALUES ('a', 'alist'); INSERT INTO table2 VALUES ('b', 'blist'); INSERT INTO list VALUES ('a', 1); INSERT INTO list VALUES ('a', 2); INSERT INTO list VALUES ('a', 3); INSERT INTO list VALUES ('b', 4); INSERT INTO list VALUES ('b', 5); INSERT INTO list VALUES ('b', 6); SELECT table2.x, (SELECT group_concat(list.value) FROM list WHERE list.key = table2.key) FROM table2; |
︙ | ︙ |
Changes to test/tkt3918.test.
︙ | ︙ | |||
20 21 22 23 24 25 26 | PRAGMA auto_vacuum = incremental; CREATE TABLE t1(i, x); } } {} do_test tkt3918.2 { execsql { INSERT INTO t1 VALUES(1, randstr(1000,1000)); | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | PRAGMA auto_vacuum = incremental; CREATE TABLE t1(i, x); } } {} do_test tkt3918.2 { execsql { INSERT INTO t1 VALUES(1, randstr(1000,1000)); INSERT INTO t1 VALUES(2, randomblob(248*1020 + 100)); INSERT INTO t1 VALUES(3, randomblob(2*1020 + 100)); } } {} # This set of statements sets up the free list so that the # first free-list trunk page contains only a single leaf. # The leaf page is also the last page in the database. The # second free-list trunk page contains, amongst other things, |
︙ | ︙ |
Changes to test/where8.test.
︙ | ︙ | |||
326 327 328 329 330 331 332 | INSERT INTO t3 VALUES('Alpine', 378678316.5, 'unalike'); INSERT INTO t3 VALUES('Alpine', NULL, 'same'); INSERT INTO t3 VALUES(1339.360726, 2847564.823, 'over'); INSERT INTO t3 VALUES('villages', 'their', 'have'); INSERT INTO t3 VALUES('unalike', 'remarkably', 'in'); INSERT INTO t3 VALUES('and', 8979323846, 'and'); INSERT INTO t3 VALUES(NULL, 1415926535, 'an'); | | | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | INSERT INTO t3 VALUES('Alpine', 378678316.5, 'unalike'); INSERT INTO t3 VALUES('Alpine', NULL, 'same'); INSERT INTO t3 VALUES(1339.360726, 2847564.823, 'over'); INSERT INTO t3 VALUES('villages', 'their', 'have'); INSERT INTO t3 VALUES('unalike', 'remarkably', 'in'); INSERT INTO t3 VALUES('and', 8979323846, 'and'); INSERT INTO t3 VALUES(NULL, 1415926535, 'an'); INSERT INTO t3 VALUES(271.25, 8628034825, 0.4811174502); INSERT INTO t3 VALUES('all', 3421170679, 'the'); INSERT INTO t3 VALUES('Not', 'and', 1415926535); INSERT INTO t3 VALUES('of', 'other', 'light'); INSERT INTO t3 VALUES(NULL, 'towering', 'Not'); INSERT INTO t3 VALUES(346.0348610, NULL, 'other'); INSERT INTO t3 VALUES('Not', 378678316.5, NULL); INSERT INTO t3 VALUES('snapshots', 8628034825, 'of'); |
︙ | ︙ |
Deleted test/zeroblob.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to tool/lsmview.tcl.
︙ | ︙ | |||
397 398 399 400 401 402 403 | set data [string trim [exec_lsmtest_show -c $myCfg $myDb array-pages $iFirst]] $myText delete 0.0 end # Delete the existing tree entries. $myTree delete [$myTree children {}] | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | set data [string trim [exec_lsmtest_show -c $myCfg $myDb array-pages $iFirst]] $myText delete 0.0 end # Delete the existing tree entries. $myTree delete [$myTree children {}] set nBlksz [expr [exec_lsmtest_show -c $myCfg $myDb blocksize] * 1024] set nPgsz [exec_lsmtest_show -c $myCfg $myDb pagesize] if {[regexp {c=1} $myCfg] || [regexp {co=1} $myCfg] || [regexp {com=1} $myCfg] || [regexp {comp=1} $myCfg] || [regexp {compr=1} $myCfg] || [regexp {compres=1} $myCfg] || [regexp {compress=1} $myCfg] || [regexp {compressi=1} $myCfg] || [regexp {compressio=1} $myCfg] || [regexp {compression=1} $myCfg] |
︙ | ︙ |
Changes to tool/mksqlite4c.tcl.
︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | # used subroutines first in order to help the compiler find # inlining opportunities. # foreach file { sqliteInt.h global.c ctime.c status.c date.c os.c fault.c mem0.c mem1.c mem2.c mem3.c mem5.c mutex.c mutex_noop.c | > > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | # used subroutines first in order to help the compiler find # inlining opportunities. # foreach file { sqliteInt.h global.c env.c ctime.c status.c date.c os.c fault.c mem.c mem0.c mem1.c mem2.c mem3.c mem5.c mutex.c mutex_noop.c |
︙ | ︙ |
Changes to www/index.wiki.
1 2 3 4 5 6 7 8 9 10 | <title>SQLite4</title> * [./design.wiki | The Design Of SQLite4] * [./env.wiki | The Run-time Environment Object] * [./varint.wiki | The Varint format] * [./data_encoding.wiki | The Data Encoding] * [./key_encoding.wiki | The Key Encoding] * [./decimal.wiki | Internal representation of numeric values] * [./porting.wiki | Porting an app from SQLite3 to SQLite4] * [./storage.wiki | How to create a new storage engine] | > | 1 2 3 4 5 6 7 8 9 10 11 | <title>SQLite4</title> * [./design.wiki | The Design Of SQLite4] * [./prog-intro.wiki | Introduction to SQLite4 for Programmers] * [./env.wiki | The Run-time Environment Object] * [./varint.wiki | The Varint format] * [./data_encoding.wiki | The Data Encoding] * [./key_encoding.wiki | The Key Encoding] * [./decimal.wiki | Internal representation of numeric values] * [./porting.wiki | Porting an app from SQLite3 to SQLite4] * [./storage.wiki | How to create a new storage engine] |
︙ | ︙ |
Changes to www/key_encoding.wiki.
︙ | ︙ | |||
57 58 59 60 61 62 63 | The text encoding ends in 0x00 in order to ensure that when there are two strings where one is a prefix of the other that the shorter string will sort first. <h2>Binary Encoding</h2> | > > > > | > > > > > > | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | The text encoding ends in 0x00 in order to ensure that when there are two strings where one is a prefix of the other that the shorter string will sort first. <h2>Binary Encoding</h2> The encoding of binaries fields is different depending on whether or not the value to be encoded is the last value (the right-most value) in the key. Each SQL value that is BINARY that is not the last value of the key begins with a single byte of 0x25 and ends with a single byte of 0x00. There are zero or more intervening bytes that encode the binary value. None of the intervening bytes may be zero. Each of the intervening bytes contains 7 bits of blob content with a 1 in the high-order bit (the 0x80 bit). The final byte before the 0x00 contains any left-over bits of the blob content. When the very last value of a key is BINARY, then it is encoded as a single byte of 0x26 and is followed by a byte-for-byte copy of the BINARY value. This alternative encoding is more efficient, but it only works if there are no subsequent values in the key, since there is no termination mark on the BLOB being encoded. The initial byte of a binary value, 0x25 or 0x26, is larger than the initial byte of a text value, 0x24, ensuring that every binary value will sort after every text value. <h2>Numeric Encoding</h2> Numeric SQL values must be coded so as to sort in numeric order. We assume that numeric SQL values can be both integer and floating point values. |
︙ | ︙ | |||
184 185 186 187 188 189 190 191 | <tr><td> zero <td><td> 0x15 <tr><td> positive small <td><td> 0x16, ~-E, M <tr><td> positive medium <td><td> 0x17+E, M <tr><td> positive large <td><td> 0x22, E, M <tr><td> positive infinity <td><td> 0x23 <tr><td> text <td><td> 0x24, T <tr><td> binary <td><td> 0x25, B </table><blockquote> | > | 194 195 196 197 198 199 200 201 202 | <tr><td> zero <td><td> 0x15 <tr><td> positive small <td><td> 0x16, ~-E, M <tr><td> positive medium <td><td> 0x17+E, M <tr><td> positive large <td><td> 0x22, E, M <tr><td> positive infinity <td><td> 0x23 <tr><td> text <td><td> 0x24, T <tr><td> binary <td><td> 0x25, B <tr><td> final binary <td><td> 0x26, X </table><blockquote> |
Changes to www/lsm.wiki.
1 2 3 4 | <title>LSM Design Overview</title> <nowiki> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <title>LSM Design Overview</title> <nowiki> <div id=start_of_toc></div> <a href=#summary style=text-decoration:none>1. Summary </a><br> <a href=#locks style=text-decoration:none>2. Locks </a><br> <a href=#database_connect_and_disconnect_operations style=text-decoration:none>3. Database Connect and Disconnect Operations</a><br> <a href=#read-write_clients style=text-decoration:none>3.1. Read-write clients</a><br> <a href=#read-only_clients style=text-decoration:none>3.2. Read-only clients</a><br> <a href=#data_structures style=text-decoration:none>4. Data Structures </a><br> <a href=#database_file style=text-decoration:none>4.1. Database file</a><br> <a href=#sorted_runs style=text-decoration:none>4.1.1. Sorted Runs</a><br> <a href=#levels style=text-decoration:none>4.1.2. Levels</a><br> <a href=#snapshots style=text-decoration:none>4.1.3. Snapshots</a><br> <a href=#in-memory_tree style=text-decoration:none>4.2. In-Memory Tree</a><br> <a href=#memory_allocation style=text-decoration:none>4.2.1. Memory Allocation</a><br> <a href=#header_fields style=text-decoration:none>4.2.2. Header Fields</a><br> <a href=#other_shared-memory_fields style=text-decoration:none>4.3. Other Shared-Memory Fields</a><br> <a href=#log_file style=text-decoration:none>4.4. Log file</a><br> <a href=#database_operations style=text-decoration:none>5. Database Operations </a><br> <a href=#reading style=text-decoration:none>5.1. Reading</a><br> <a href=#writing style=text-decoration:none>5.2. Writing</a><br> <a href=#working style=text-decoration:none>5.3. Working</a><br> <a href=#free-block_list_management style=text-decoration:none>5.3.1. Free-block list management</a><br> <a href=#checkpoint_operations style=text-decoration:none>5.4. Checkpoint Operations</a><br> <div id=end_of_toc></div> <h1 id=summary>1. Summary </h1> The LSM embedded database software stores data in three distinct data structures: <ul> <li> <p>The <b>shared-memory region</b>. This may actually be allocated in either shared or heap memory, depending on whether LSM is running in |
︙ | ︙ | |||
42 43 44 45 46 47 48 | <p> When an application writes to the database, the new data is written to the in-memory tree. Once the in-memory tree has grown large enough, its contents are written into the database file as a new sorted run. To reduce the number of sorted runs in the database file, chronologically adjacent sorted runs may be merged together into a single run, either automatically or on demand. | | > > > < > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | | > > > | > > | > > > > > > | > | > | > | > > > > > > > > > > > > | > > > > > | > > > > | > > > > > | > > > > > > > > | > | > > > | | > > > > > > > > > | | | > > > > > > > | > > > | > > > > > > > > > > > > > | > > > > > > > > | > > | > > > > > > > > > | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | <p> When an application writes to the database, the new data is written to the in-memory tree. Once the in-memory tree has grown large enough, its contents are written into the database file as a new sorted run. To reduce the number of sorted runs in the database file, chronologically adjacent sorted runs may be merged together into a single run, either automatically or on demand. <h1 id=locks>2. Locks</h1> Read/write (shared/exclusive) file locks are used to control concurrent access. LSM uses the following "locking regions". Each locking region may be locked and unlocked separately. <p> <table style="margin:0 4ex"> <tr><td valign=top style="width:16ex">DMS1 <td><p style=margin-top:0> This locking region is used to serialize all connection and disconnection operations performed by read-write database connections. An EXCLUSIVE lock is taken for the duration of all such operations. <p>Additionally, read-only connections take a SHARED lock on this locking region while attempting to connect to a database. This ensures that a read-only connection does not attempt to connect to the database while a read-write clients connection or disconnection operation is ongoing. <tr><td valign=top>DMS2 <td><p style=margin-top:0> Read-write connections hold a SHARED lock on this locking region for as long as they are connected to the database. <tr><td valign=top>DMS3 <td><p style=margin-top:0> Read-only connections hold a SHARED lock on this locking region for as long as they are connected to the database. <tr><td valign=top>RWCLIENT(n) <td><p style=margin-top:0> There are a total of 16 RWCLIENT locking regions. After a read-write client connects to the database it attempts to find a free RWCLIENT locking slot to take an EXCLUSIVE lock on. If it cannot find one, this is not an error. If it can, then the lock is held for as long as the read-write client is connected to the database for. <p>The sole purpose of these locks is that they allow a read-only client to detect whether or not there exists at least one read-write client connected to the database. Of course if large numbers of read-write clients connect and disconnect from the system in an inconvenient order the system may enter a state where there exists one or more connected read-write clients but none of them hold a RWCLIENT lock. This is not important - if a read-only client fails to detect that the system has read-write clients it may be less efficient, but will not malfunction. <tr><td valign=top>WRITER <td><p style=margin-top:0> A database client holds an EXCLUSIVE lock on this locking region while writing data to the database. Outside of recovery, only clients holding this lock may modify the contents of the in-memory b-tree. Holding this lock is synonymous with having an open write transaction on the database. <tr><td valign=top>WORKER <td><p style=margin-top:0> A database client holds an EXCLUSIVE lock on this locking region while performing database work (writing data into the body of the database file). <tr><td valign=top>CHECKPOINTER <td><p style=margin-top:0> A database client holds an EXCLUSIVE lock on this locking region while performing a checkpoint (syncing the database file and writing to the database header). <tr><td valign=top>ROTRANS <td><p style=margin-top:0> A read-only database client holds a SHARED lock on this locking region while reading from a non-live database system. <tr><td valign=top>READER(n) <td><p style=margin-top:0> There are a total of 6 READER locking regions. Unless it is a read-only client reading from a non-live database, a client holds a SHARED lock on one of these while it has an open read transaction. Each READER lock is associated with a pair of id values identifying the regions of the in-memory tree and database file that may be read by clients holding such SHARED locks. </table> <h1 id=database_connect_and_disconnect_operations>3. Database Connect and Disconnect Operations</h1> <h2 id=read-write_clients>3.1. Read-write clients</h2> <p>When an LSM database connection is opened (i.e. lsm_open() is called): <pre> lock(DMS1, EXCLUSIVE) # Block until successful trylock(DMS2+DMS3, EXCLUSIVE) if( trylock() successful ){ zero shared memory and run recovery } if( no error during recovery ){ lock(DMS2, SHARED) # "cannot" fail lock(RWCLIENT(x), EXCLUSIVE) # see comment below } unlock(DMS1) </pre> <p> Running recovery involves reading the database file header and log file to initialize the contents of shared-memory. Recovery is only run when the first connection connects to the database file. There are no circumstances (including the unexpected failure of a writer process) that may cause recovery to take place after the first client has successfully connected. <p> After the SHARED lock on DMS2 is taken, an effort is made to find a free RWCLIENT locking region and take an EXCLUSIVE lock on it. If no such region can be found, this step is omitted. It is not an error if this happens. <p> Assuming recovery is successful (or not required), the database client is left holding a SHARED lock on DMS2 and, possibly, an EXCLUSIVE lock on one of the RWCLIENT locks. These locks are held for as long as the database connection remains open. <p> When disconnecting from a database (i.e. an lsm_close() call following a successful lsm_open()): <pre> lock(DMS1, EXCLUSIVE) # Block until successful trylock(DMS2, EXCLUSIVE) if( trylock() successful ){ flush in-memory tree checkpoint database trylock(DMS3, EXCLUSIVE) if( trylock() successful ){ delete shared memory } trylock(ROTRANS, EXCLUSIVE) if( trylock() successful ){ unlink log file } } unlock(RWCLIENT(x)) # If holding RWCLIENT lock unlock(DMS2) unlock(DMS1) </pre> <h2 id=read-only_clients>3.2. Read-only clients</h2> <p>It is assumed that read-only clients: <ul> <li>may take SHARED locks only, <li>may not write to shared-memory, the database file or the log file, and <li>may not use the trylock(x, EXCLUSIVE) primitive to detect SHARED locks held by other clients. </ul> <p>A read-only client does not attempt to connect to the database from within the lsm_open() call. Instead, this is defered until the first time the client attempts to read from the database. <pre> lock(DMS1, SHARED) # Block until successful trylock(RWCLIENT(all), SHARED) if( trylock() successful ){ # System is not live. The database must be read directly from disk. lock(ROTRANS, SHARED) }else{ # Client is now connected. The read transaction may now be opened # in the same way as it would be for a read-write client. lock(DMS3, SHARED) } unlock(DMS1) </pre> <p> Assuming no error occurs, the procedure above leads to one of two possible outcomes: <ul> <li> <p>DMS3 is locked and the read-only client is now connected to the database. From this point on, the read-only client uses the same procedure to open a read transaction as any other client. The lock on DMS3 is held until the client disconnects from the database. <li> <p>ROTRANS is locked and the read-only client is still disconnected. Holding the lock on ROTRANS guarantees that no read-write client will overwrite any existing data in the database file or log file. This allows the read-only client to run a disconnected read transaction - it recovers any data in the log file into private memory, then reads data as required from the database file for the duration of the users read transaction. </ul> <p>A disconnected read transaction is closed by dropping the ROTRANS lock. <h1 id=data_structures>4. Data Structures </h1> <p> In the following sections, "the WRITER lock", refers to an exclusive lock on the WRITER locking region. For example "holding the WRITER lock" is equivalent to "holding an exclusive lock on the WRITER locking region". Similar interpretations apply to "the WORKER lock" and "the CHECKPOINTER lock". <h2 id=database_file>4.1. Database file</h2> <p> This section summarizes the contents of the database file informally. A detailed description is found in the header comments for source code files <a href="../src/lsm_file.c">lsm_file.c</a> (blocks, pages etc.), <a href="../src/lsm_sorted.c">lsm_sorted.c</a> (sorted run format) and <a href="../src/lsm_ckpt.c">lsm_ckpt.c</a> (database snapshot format). |
︙ | ︙ | |||
122 123 124 125 126 127 128 | <p> As with an SQLite database file, each page in the database may be addressed by its 32-bit page number. This means the maximum database size is roughly (pgsz * 2^32) bytes. The first and last pages in each block are 4 bytes smaller than the others. This is to make room for a single page-number. | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | <p> As with an SQLite database file, each page in the database may be addressed by its 32-bit page number. This means the maximum database size is roughly (pgsz * 2^32) bytes. The first and last pages in each block are 4 bytes smaller than the others. This is to make room for a single page-number. <h3 id=sorted_runs>4.1.1. Sorted Runs</h3> <p> A single sorted run is spread across one or more database pages (each page is a part of at most one sorted run). Given the page number of a page in a sorted run the following statements are true: <ul> |
︙ | ︙ | |||
164 165 166 167 168 169 170 | In other words, given the page numbers of the first and last pages of a sorted run and the page number of the root page for the embedded b-tree, it is possible to traverse the entire run in either direction or query for arbitrary values. <p><span style="color:red"> TODO: Embedded pointers. </span> | | | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | In other words, given the page numbers of the first and last pages of a sorted run and the page number of the root page for the embedded b-tree, it is possible to traverse the entire run in either direction or query for arbitrary values. <p><span style="color:red"> TODO: Embedded pointers. </span> <h3 id=levels>4.1.2. Levels</h3> <p> Each sorted run is assigned to a "level". Normally, a level consists of a single sorted run. However, a level may also consist of a set of sorted runs being incrementally merged into a single run. <p> |
︙ | ︙ | |||
225 226 227 228 229 230 231 | time for all entries. | | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | time for all entries. <h3 id=snapshots>4.1.3. Snapshots</h3> <p> Each meta page may contain a database <b>snapshot</b>. A snapshot contains all the information required to interpret the remainder of the database file (the sorted runs and free space). Specifically, it contains: <ul> |
︙ | ︙ | |||
250 251 252 253 254 255 256 | Recovery and Shutdown" below). </ul> <p> A more detailed description is available in the header comments in source code file <a href="../src/lsm_ckpt.c">lsm_ckpt.c</a> | | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | Recovery and Shutdown" below). </ul> <p> A more detailed description is available in the header comments in source code file <a href="../src/lsm_ckpt.c">lsm_ckpt.c</a> <h2 id=in-memory_tree>4.2. In-Memory Tree</h2> <p> The in-memory tree is an append-only b-tree of order 4 (each node may have up to 4 children), which is more or less equivalent to a red-black tree. An append-only tree is convenient, as it naturally supports the single-writer/many-readers MVCC concurrency model. <p> The implementation includes some optimizations to reduce the number of interior nodes that are updated when a leaf node is written that are not described here. See header comments in source code file <a href=../src/lsm_tree.c>lsm_tree.c</a> for details. <h3 id=memory_allocation>4.2.1. Memory Allocation</h3> <p> More than one in-memory tree may exist in shared-memory at any time. For example in the following scenario: <ol> |
︙ | ︙ | |||
330 331 332 333 334 335 336 | but the values that connect the linked list together are not. The writer that detects the failure must scan the entire shared-memory region to reconstruct the linked list. Any sequence ids assigned by the failed writer are reverted (perhaps not to their original values, but to values that put them at the start of the linked list - before those chunks that may still be in use by existing readers). | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | but the values that connect the linked list together are not. The writer that detects the failure must scan the entire shared-memory region to reconstruct the linked list. Any sequence ids assigned by the failed writer are reverted (perhaps not to their original values, but to values that put them at the start of the linked list - before those chunks that may still be in use by existing readers). <h3 id=header_fields>4.2.2. Header Fields</h3> <p> As well as the in-memory tree data, the following fixed-size fields stored in well-known locations in shared-memory are part of the in-memory tree. Like the in-memory tree data, outside of recovery these fields are only ever written to by clients holding the WRITER lock. <ul> <li> Two copies of a data structure called a "tree-header". Tree-header-1 and tree-header 2. A tree-header structure contains all the information required to read or write to a particular version of the append only b-tree. It also contains a 64-bit checksum. <li> A boolean flag set to true at the beginning of every write transaction and cleared after that transaction is successfully concluded - the "writer flag". This is used to detect failures that occur mid-transaction. It is only ever read (or written) by clients that hold the WRITER lock. </ul> <h2 id=other_shared-memory_fields>4.3. Other Shared-Memory Fields</h2> <ul> <li> Snapshot 1. <li> Snapshot 2. <li> The meta-page pointer. This value is either 1 or 2. It indicates which of the two meta-pages contains the most recent database snapshot. <li> READER lock values. </ul> <h2 id=log_file>4.4. Log file</h2> <a href=../src/lsm_log.c>lsm_log.c</a>. <h1 id=database_operations>5. Database Operations </h1> <h2 id=reading>5.1. Reading</h2> <p> Opening a read transaction: <ol> <li> <p>Load the current tree-header from shared-memory. <li> <p>Load the current snapshot from shared-memory. <p>Steps 1 and 2 are similar. In both cases, there are two copies of the data structure being read in shared memory. No lock is held to prevent another client updating them while the read is taking place. Updaters use the following pattern: <ol type=i> <li> Update copy 2. <li> Invoke xShmBarrier(). <li> Update copy 1. </ol> |
︙ | ︙ | |||
512 513 514 515 516 517 518 | Once a read transaction is opened, the reader may continue to read the versions of the in-memory tree and database file for as long as the READER lock is held. <p> To close a read transaction all that is required is to drop the SHARED lock held on the READER slot. | | | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | Once a read transaction is opened, the reader may continue to read the versions of the in-memory tree and database file for as long as the READER lock is held. <p> To close a read transaction all that is required is to drop the SHARED lock held on the READER slot. <h2 id=writing>5.2. Writing</h2> <p> To open a write transaction: <ol> <li> <p>Open a read transaction, if one is not already open. <li> <p>Obtain the WRITER lock. |
︙ | ︙ | |||
560 561 562 563 564 565 566 | <li> Sweep the shared-memory area to rebuild the linked list of chunks so that it is consistent with the current tree-header. <li> Clear the writer flag. </ol> | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 | <li> Sweep the shared-memory area to rebuild the linked list of chunks so that it is consistent with the current tree-header. <li> Clear the writer flag. </ol> <h2 id=working>5.3. Working</h2> <p> Working is similar to writing. The difference is that a "writer" modifies the in-memory tree. A "worker" modifies the contents of the database file. <ol> <li> <p>Take the WORKER lock. |
︙ | ︙ | |||
645 646 647 648 649 650 651 | <li> <p>Invoke xShmBarrier(). <li> <p>Update snapshot-1 in shared-memory. <li> <p>Release the WORKER lock. </ol> | | | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | <li> <p>Invoke xShmBarrier(). <li> <p>Update snapshot-1 in shared-memory. <li> <p>Release the WORKER lock. </ol> <h3 id=free-block_list_management>5.3.1. Free-block list management</h3> <p> Worker clients occasionally need to allocate new database blocks or move existing blocks to the free-block list. Along with the block number of each free block, the free-block list contains the snapshot-id of the first snapshot created after the block was moved to the free list. The free-block list is always stored in order of snapshot-id, so that the first block in |
︙ | ︙ | |||
675 676 677 678 679 680 681 | header. This is done by reading (and verifying the checksum) of the snapshot currently stored in the database meta-page indicated by the shared-memory variable. </ul> | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | header. This is done by reading (and verifying the checksum) of the snapshot currently stored in the database meta-page indicated by the shared-memory variable. </ul> <h2 id=checkpoint_operations>5.4. Checkpoint Operations</h2> <ol> <li> Take CHECKPOINTER lock. <li> Load snapshot-1 from shared-memory. If the checksum does not match the content here, release the CHECKPOINTER lock and abandon the attempt to checkpoint the database. |
︙ | ︙ | |||
701 702 703 704 705 706 707 | <li> Update the shared-memory variable to indicate the meta-page written in step 5. <li> Drop the CHECKPOINTER lock. </ol> | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 779 780 781 782 783 784 785 786 787 788 789 790 | <li> Update the shared-memory variable to indicate the meta-page written in step 5. <li> Drop the CHECKPOINTER lock. </ol> |
Changes to www/lsmapi.wiki.
︙ | ︙ | |||
17 18 19 20 21 22 23 | <h1>LSM API Topics</h1> <ol> <li><a href="#database" style=text-decoration:none>Database Runtime Environment</a> <li><a href="#lsm" style=text-decoration:none>LSM Error Codes</a> <li><a href="#creating" style=text-decoration:none>Creating and Destroying Database Connection Handles</a> <li><a href="#connecting" style=text-decoration:none>Connecting to a Database</a> | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <h1>LSM API Topics</h1> <ol> <li><a href="#database" style=text-decoration:none>Database Runtime Environment</a> <li><a href="#lsm" style=text-decoration:none>LSM Error Codes</a> <li><a href="#creating" style=text-decoration:none>Creating and Destroying Database Connection Handles</a> <li><a href="#connecting" style=text-decoration:none>Connecting to a Database</a> <li><a href="#obtaining" style=text-decoration:none>Obtaining pointers to database environments</a> <li><a href="#configuring" style=text-decoration:none>Configuring a database connection.</a> <li><a href="#compression" style=text-decoration:none>Compression and/or Encryption Hooks</a> <li><a href="#allocating" style=text-decoration:none>Allocating and Freeing Memory</a> <li><a href="#querying" style=text-decoration:none>Querying a Connection For Operational Data</a> <li><a href="#opening" style=text-decoration:none>Opening and Closing Write Transactions</a> <li><a href="#writing" style=text-decoration:none>Writing to a Database</a> <li><a href="#explicit" style=text-decoration:none>Explicit Database Work and Checkpointing</a> |
︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | <span style=display:block;float:left;width:35ex><a href=#lsm_new>lsm_new</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_open>lsm_open</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_rollback>lsm_rollback</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_work>lsm_work</a></span> <br style=clear:both> <h1 style=clear:both>All LSM API Types</h1> <span style=display:block;float:left;width:35ex><a href=#lsm_compress>lsm_compress</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_env>lsm_env</a></span> <br style=clear:both> <h1>All LSM API Constants</h1> <span style=display:block;float:left;width:35ex><a href=#LSM_BUSY>LSM_BUSY</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CANTOPEN>LSM_CANTOPEN</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOCHECKPOINT>LSM_CONFIG_AUTOCHECKPOINT</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOFLUSH>LSM_CONFIG_AUTOFLUSH</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOMERGE>LSM_CONFIG_AUTOMERGE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOWORK>LSM_CONFIG_AUTOWORK</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_BLOCK_SIZE>LSM_CONFIG_BLOCK_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_GET_COMPRESSION>LSM_CONFIG_GET_COMPRESSION</a></span> | > < > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | <span style=display:block;float:left;width:35ex><a href=#lsm_new>lsm_new</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_open>lsm_open</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_rollback>lsm_rollback</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_work>lsm_work</a></span> <br style=clear:both> <h1 style=clear:both>All LSM API Types</h1> <span style=display:block;float:left;width:35ex><a href=#lsm_compress>lsm_compress</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_compress>lsm_compress</a></span> <span style=display:block;float:left;width:35ex><a href=#lsm_env>lsm_env</a></span> <br style=clear:both> <h1>All LSM API Constants</h1> <span style=display:block;float:left;width:35ex><a href=#LSM_BUSY>LSM_BUSY</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CANTOPEN>LSM_CANTOPEN</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOCHECKPOINT>LSM_CONFIG_AUTOCHECKPOINT</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOFLUSH>LSM_CONFIG_AUTOFLUSH</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOMERGE>LSM_CONFIG_AUTOMERGE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOWORK>LSM_CONFIG_AUTOWORK</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_BLOCK_SIZE>LSM_CONFIG_BLOCK_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_GET_COMPRESSION>LSM_CONFIG_GET_COMPRESSION</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MAX_FREELIST>LSM_CONFIG_MAX_FREELIST</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MMAP>LSM_CONFIG_MMAP</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MULTIPLE_PROCESSES>LSM_CONFIG_MULTIPLE_PROCESSES</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_PAGE_SIZE>LSM_CONFIG_PAGE_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SAFETY>LSM_CONFIG_SAFETY</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SET_COMPRESSION>LSM_CONFIG_SET_COMPRESSION</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SET_COMPRESSION_FACTORY>LSM_CONFIG_SET_COMPRESSION_FACTORY</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_USE_LOG>LSM_CONFIG_USE_LOG</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_CORRUPT>LSM_CORRUPT</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_ERROR>LSM_ERROR</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_FULL>LSM_FULL</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_ARRAY_PAGES>LSM_INFO_ARRAY_PAGES</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_ARRAY_STRUCTURE>LSM_INFO_ARRAY_STRUCTURE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_CHECKPOINT_SIZE>LSM_INFO_CHECKPOINT_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_DB_STRUCTURE>LSM_INFO_DB_STRUCTURE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_FREELIST>LSM_INFO_FREELIST</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_FREELIST_SIZE>LSM_INFO_FREELIST_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_LOG_STRUCTURE>LSM_INFO_LOG_STRUCTURE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_NREAD>LSM_INFO_NREAD</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_NWRITE>LSM_INFO_NWRITE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_PAGE_ASCII_DUMP>LSM_INFO_PAGE_ASCII_DUMP</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_PAGE_HEX_DUMP>LSM_INFO_PAGE_HEX_DUMP</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_TREE_SIZE>LSM_INFO_TREE_SIZE</a></span> <span style=display:block;float:left;width:35ex><a href=#LSM_IOERR>LSM_IOERR</a></span> |
︙ | ︙ | |||
178 179 180 181 182 183 184 | <verbatim>int lsm_new(lsm_env*, lsm_db **ppDb); int lsm_close(lsm_db *pDb); </verbatim> <p>Open and close a database connection handle. <h2 id=connecting>Connecting to a Database<a id=lsm_open></a></h2> <verbatim>int lsm_open(lsm_db *pDb, const char *zFilename); </verbatim> | | | < > | | | > > > > > > > > | | > > > > | > > > > > > > > > > > > > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | <verbatim>int lsm_new(lsm_env*, lsm_db **ppDb); int lsm_close(lsm_db *pDb); </verbatim> <p>Open and close a database connection handle. <h2 id=connecting>Connecting to a Database<a id=lsm_open></a></h2> <verbatim>int lsm_open(lsm_db *pDb, const char *zFilename); </verbatim> <h2 id=obtaining>Obtaining pointers to database environments</h2> <verbatim>lsm_env *lsm_get_env(lsm_db *pDb); lsm_env *lsm_default_env(void); </verbatim> <p>Return a pointer to the environment used by the database connection passed as the first argument. Assuming the argument is valid, this function always returns a valid environment pointer - it cannot fail. The lsm_default_env() function returns a pointer to the default LSM environment for the current platform. <h2 id=configuring>Configuring a database connection.<a id=lsm_config></a><a id=LSM_CONFIG_AUTOFLUSH></a><a id=LSM_CONFIG_PAGE_SIZE></a><a id=LSM_CONFIG_SAFETY></a><a id=LSM_CONFIG_BLOCK_SIZE></a><a id=LSM_CONFIG_AUTOWORK></a><a id=LSM_CONFIG_MMAP></a><a id=LSM_CONFIG_USE_LOG></a><a id=LSM_CONFIG_AUTOMERGE></a><a id=LSM_CONFIG_MAX_FREELIST></a><a id=LSM_CONFIG_MULTIPLE_PROCESSES></a><a id=LSM_CONFIG_AUTOCHECKPOINT></a><a id=LSM_CONFIG_SET_COMPRESSION></a><a id=LSM_CONFIG_GET_COMPRESSION></a><a id=LSM_CONFIG_SET_COMPRESSION_FACTORY></a><a id=LSM_SAFETY_OFF></a><a id=LSM_SAFETY_NORMAL></a><a id=LSM_SAFETY_FULL></a></h2> <verbatim>int lsm_config(lsm_db *, int, ...); #define LSM_CONFIG_AUTOFLUSH 1 #define LSM_CONFIG_PAGE_SIZE 2 #define LSM_CONFIG_SAFETY 3 #define LSM_CONFIG_BLOCK_SIZE 4 #define LSM_CONFIG_AUTOWORK 5 #define LSM_CONFIG_MMAP 7 #define LSM_CONFIG_USE_LOG 8 #define LSM_CONFIG_AUTOMERGE 9 #define LSM_CONFIG_MAX_FREELIST 10 #define LSM_CONFIG_MULTIPLE_PROCESSES 11 #define LSM_CONFIG_AUTOCHECKPOINT 12 #define LSM_CONFIG_SET_COMPRESSION 13 #define LSM_CONFIG_GET_COMPRESSION 14 #define LSM_CONFIG_SET_COMPRESSION_FACTORY 15 #define LSM_SAFETY_OFF 0 #define LSM_SAFETY_NORMAL 1 #define LSM_SAFETY_FULL 2 </verbatim> <p>The lsm_config() function is used to configure a database connection. The following values may be passed as the second argument to lsm_config(). <p><dl><dt>LSM_CONFIG_AUTOFLUSH<dd>A read/write integer parameter. <p>This value determines the amount of data allowed to accumulate in a live in-memory tree before it is marked as old. After committing a transaction, a connection checks if the size of the live in-memory tree, including data structure overhead, is greater than the value of this option in KB. If it is, and there is not already an old in-memory tree, the live in-memory tree is marked as old. <p>The maximum allowable value is 1048576 (1GB). There is no minimum value. If this parameter is set to zero, then an attempt is made to mark the live in-memory tree as old after each transaction is committed. <p>The default value is 1024 (1MB). <p><dt>LSM_CONFIG_PAGE_SIZE<dd>A read/write integer parameter. This parameter may only be set before lsm_open() has been called. <p><dt>LSM_CONFIG_BLOCK_SIZE<dd>A read/write integer parameter. <p>This parameter may only be set before lsm_open() has been called. It must be set to a power of two between 64 and 65536, inclusive (block sizes between 64KB and 64MB). <p>If the connection creates a new database, the block size of the new database is set to the value of this option in KB. After lsm_open() has been called, querying this parameter returns the actual block size of the opened database. <p>The default value is 1024 (1MB blocks). <p><dt>LSM_CONFIG_SAFETY<dd>A read/write integer parameter. Valid values are 0, 1 (the default) and 2. This parameter determines how robust the database is in the face of a system crash (e.g. a power failure or operating system crash). As follows: <p>0 (off): No robustness. A system crash may corrupt the database. <p>1 (normal): Some robustness. A system crash may not corrupt the database file, but recently committed transactions may be lost following recovery. <p>2 (full): Full robustness. A system crash may not corrupt the database file. Following recovery the database file contains all successfully committed transactions. <p><dt>LSM_CONFIG_AUTOWORK<dd>A read/write integer parameter. <p><dt>LSM_CONFIG_AUTOCHECKPOINT<dd>A read/write integer parameter. <p>If this option is set to non-zero value N, then a checkpoint is automatically attempted after each N KB of data have been written to the database file. <p>The amount of uncheckpointed data already written to the database file is a global parameter. After performing database work (writing to the database file), the process checks if the total amount of uncheckpointed data exceeds the value of this paramter. If so, a checkpoint is performed. This means that this option may cause the connection to perform a checkpoint even if the current connection has itself written very little data into the database file. <p>The default value is 2048 (checkpoint every 2MB). <p><dt>LSM_CONFIG_MMAP<dd>A read/write integer parameter. True to use mmap() to access the database file. False otherwise. <p><dt>LSM_CONFIG_USE_LOG<dd>A read/write boolean parameter. True (the default) to use the log file normally. False otherwise. <p><dt>LSM_CONFIG_AUTOMERGE<dd>A read/write integer parameter. The minimum number of segments to merge together at a time. Default value 4. <p><dt>LSM_CONFIG_MAX_FREELIST<dd>A read/write integer parameter. The maximum number of free-list |
︙ | ︙ | |||
255 256 257 258 259 260 261 | content. The argument to this option should be a pointer to a structure of type lsm_compress. The lsm_config() method takes a copy of the structures contents. <p>This option may only be used before lsm_open() is called. Invoking it after lsm_open() has been called results in an LSM_MISUSE error. <p><dt>LSM_CONFIG_GET_COMPRESSION<dd>Query the compression methods used to compress and decompress database content. | > > | > > > > > > | > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | content. The argument to this option should be a pointer to a structure of type lsm_compress. The lsm_config() method takes a copy of the structures contents. <p>This option may only be used before lsm_open() is called. Invoking it after lsm_open() has been called results in an LSM_MISUSE error. <p><dt>LSM_CONFIG_GET_COMPRESSION<dd>Query the compression methods used to compress and decompress database content. <p><dt>LSM_CONFIG_SET_COMPRESSION_FACTORY<dd>Configure a factory method to be invoked in case of an LSM_MISMATCH error. </dl><h2 id=compression>Compression and/or Encryption Hooks<a id=lsm_compress></a><a id=lsm_compress></a></h2> <verbatim>struct lsm_compress { void *pCtx; unsigned int iId; int (*xBound)(void *, int nSrc); int (*xCompress)(void *, char *, int *, const char *, int); int (*xUncompress)(void *, char *, int *, const char *, int); void (*xFree)(void *pCtx); }; struct lsm_compress_factory { void *pCtx; int (*xFactory)(void *, lsm_db *, u32); void (*xFree)(void *pCtx); }; </verbatim> <h2 id=allocating>Allocating and Freeing Memory<a id=lsm_free></a></h2> <verbatim>void *lsm_malloc(lsm_env*, size_t); void *lsm_realloc(lsm_env*, void *, size_t); void lsm_free(lsm_env*, void *); </verbatim> <p>Invoke the memory allocation functions that belong to environment pEnv. Or the system defaults if no memory allocation functions have been registered. <h2 id=querying>Querying a Connection For Operational Data<a id=lsm_info></a><a id=LSM_INFO_NWRITE></a><a id=LSM_INFO_NREAD></a><a id=LSM_INFO_DB_STRUCTURE></a><a id=LSM_INFO_LOG_STRUCTURE></a><a id=LSM_INFO_ARRAY_STRUCTURE></a><a id=LSM_INFO_PAGE_ASCII_DUMP></a><a id=LSM_INFO_PAGE_HEX_DUMP></a><a id=LSM_INFO_FREELIST></a><a id=LSM_INFO_ARRAY_PAGES></a><a id=LSM_INFO_CHECKPOINT_SIZE></a><a id=LSM_INFO_TREE_SIZE></a><a id=LSM_INFO_FREELIST_SIZE></a></h2> <verbatim>int lsm_info(lsm_db *, int, ...); #define LSM_INFO_NWRITE 1 #define LSM_INFO_NREAD 2 #define LSM_INFO_DB_STRUCTURE 3 #define LSM_INFO_LOG_STRUCTURE 4 #define LSM_INFO_ARRAY_STRUCTURE 5 #define LSM_INFO_PAGE_ASCII_DUMP 6 #define LSM_INFO_PAGE_HEX_DUMP 7 #define LSM_INFO_FREELIST 8 #define LSM_INFO_ARRAY_PAGES 9 #define LSM_INFO_CHECKPOINT_SIZE 10 #define LSM_INFO_TREE_SIZE 11 #define LSM_INFO_FREELIST_SIZE 12 </verbatim> <p>Query a database connection for operational statistics or data. The following values may be passed as the second argument to lsm_info(). <p><dl><dt>LSM_INFO_NWRITE<dd>The third parameter should be of type (int *). The location pointed to by the third parameter is set to the number of 4KB pages written to the database file during the lifetime of this connection. <p><dt>LSM_INFO_NREAD<dd>The third parameter should be of type (int *). The location pointed |
︙ | ︙ | |||
350 351 352 353 354 355 356 | to is populated with a pointer to a nul-terminated string containing the string representation of a Tcl data-structure. The returned string should be eventually freed by the caller using lsm_free(). <p>The Tcl structure returned is a list containing one element for each free block in the database. The element itself consists of two integers - the block number and the id of the snapshot that freed it. <p><dt>LSM_INFO_CHECKPOINT_SIZE<dd>The third argument should be of type (int *). The location pointed to | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | to is populated with a pointer to a nul-terminated string containing the string representation of a Tcl data-structure. The returned string should be eventually freed by the caller using lsm_free(). <p>The Tcl structure returned is a list containing one element for each free block in the database. The element itself consists of two integers - the block number and the id of the snapshot that freed it. <p><dt>LSM_INFO_CHECKPOINT_SIZE<dd>The third argument should be of type (int *). The location pointed to by this argument is populated with the number of KB written to the database file since the most recent checkpoint. <p><dt>LSM_INFO_TREE_SIZE<dd>If this value is passed as the second argument to an lsm_info() call, it should be followed by two arguments of type (int *) (for a total of four arguments). <p>At any time, there are either one or two tree structures held in shared memory that new database clients will access (there may also be additional tree structures being used by older clients - this API does not provide information on them). One tree structure - the current tree - is used to accumulate new data written to the database. The other tree structure - the old tree - is a read-only tree holding older data and may be flushed to disk at any time. <p>Assuming no error occurs, the location pointed to by the first of the two (int *) arguments is set to the size of the old in-memory tree in KB. The second is set to the size of the current, or live in-memory tree. </dl><h2 id=opening>Opening and Closing Write Transactions<a id=lsm_begin></a><a id=lsm_commit></a><a id=lsm_rollback></a></h2> <verbatim>int lsm_begin(lsm_db *pDb, int iLevel); int lsm_commit(lsm_db *pDb, int iLevel); int lsm_rollback(lsm_db *pDb, int iLevel); </verbatim> <p>These functions are used to open and close transactions and nested |
︙ | ︙ | |||
405 406 407 408 409 410 411 | Delete a value from the database. No error is returned if the specified key value does not exist in the database. Delete all database entries with keys that are greater than (pKey1/nKey1) and smaller than (pKey2/nKey2). Note that keys (pKey1/nKey1) and (pKey2/nKey2) themselves, if they exist in the database, are not deleted. <p>Return LSM_OK if successful, or an LSM error code otherwise. <h2 id=explicit>Explicit Database Work and Checkpointing<a id=lsm_work></a><a id=lsm_flush></a><a id=lsm_checkpoint></a></h2> | | | | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | Delete a value from the database. No error is returned if the specified key value does not exist in the database. Delete all database entries with keys that are greater than (pKey1/nKey1) and smaller than (pKey2/nKey2). Note that keys (pKey1/nKey1) and (pKey2/nKey2) themselves, if they exist in the database, are not deleted. <p>Return LSM_OK if successful, or an LSM error code otherwise. <h2 id=explicit>Explicit Database Work and Checkpointing<a id=lsm_work></a><a id=lsm_flush></a><a id=lsm_checkpoint></a></h2> <verbatim>int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite); int lsm_flush(lsm_db *pDb); int lsm_checkpoint(lsm_db *pDb, int *pnKB); </verbatim> <p>This function is called by a thread to work on the database structure. Attempt to checkpoint the current database snapshot. Return an LSM error code if an error occurs or LSM_OK otherwise. <p>If the current snapshot has already been checkpointed, calling this function is a no-op. In this case if pnKB is not NULL, *pnKB is set to 0. Or, if the current snapshot is successfully checkpointed by this function and pbKB is not NULL, *pnKB is set to the number of bytes written to the database file since the previous checkpoint (the same measure as returned by the LSM_INFO_CHECKPOINT_SIZE query). <h2 id=opening>Opening and Closing Database Cursors<a id=lsm_csr_open></a><a id=lsm_csr_close></a></h2> <verbatim>int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr); int lsm_csr_close(lsm_cursor *pCsr); </verbatim> <p>Open and close a database cursor. |
︙ | ︙ |
Changes to www/lsmusr.wiki.
︙ | ︙ | |||
157 158 159 160 161 162 163 | <h1 id=using_lsm_in_applications>2. Using LSM in Applications </h1> <p>LSM is not currently built or distributed independently. Instead, it is part of the SQLite4 library. To use LSM in an application, the application links against libsqlite4 and includes the header file "lsm.h" in any files that access the LSM API. | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | <h1 id=using_lsm_in_applications>2. Using LSM in Applications </h1> <p>LSM is not currently built or distributed independently. Instead, it is part of the SQLite4 library. To use LSM in an application, the application links against libsqlite4 and includes the header file "lsm.h" in any files that access the LSM API. <p><span style=color:red>Pointer to build instructions for sqlite4</span> <h1 id=basic_usage>3. Basic Usage</h1> <h2 id=opening_and_closing_database_connections>3.1. Opening and Closing Database Connections </h2> <p>Opening a connection to a database is a two-step process. The <a href=lsmapi.wiki#lsm_new>lsm_new()</a> function is used to create a new |
︙ | ︙ | |||
370 371 372 373 374 375 376 | the next entry. After lsm_csr_next() is called to advance past the final entry in the database, the cursor is left pointing to no entry at all, lsm_csr_valid() returns 0, and the loop is finished. API function <a href=lsmapi.wiki#lsm_csr_key>lsm_csr_key()</a> is used to retrieve the key associated with each database entry visited. <verbatim> | | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | the next entry. After lsm_csr_next() is called to advance past the final entry in the database, the cursor is left pointing to no entry at all, lsm_csr_valid() returns 0, and the loop is finished. API function <a href=lsmapi.wiki#lsm_csr_key>lsm_csr_key()</a> is used to retrieve the key associated with each database entry visited. <verbatim> for(rc=lsm_csr_first(csr); rc==LSM_OK && lsm_csr_valid(csr); rc=lsm_csr_next(csr)){ const void *pKey; int nKey; const void *pVal; int nVal; rc = lsm_csr_key(csr, &pKey, &nKey); if( rc==LSM_OK ) rc = lsm_csr_value(csr, &pVal, &nVal); if( rc==LSM_OK ) break; /* At this point pKey points to the current key (size nKey bytes) and ** pVal points to the corresponding value (size nVal bytes). */ } </verbatim> <p> The example code above could be modified to iterate backwards through |
︙ | ︙ | |||
723 724 725 726 727 728 729 | int (*xBound)(void *pCtx, int nIn); int (*xCompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn); int (*xUncompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn); void (*xFree)(void *pCtx); }; </verbatim> | | > | | | > > > > > > > > > > | < | | | | | | | | > | | > > > > > > > > > | > > > > > | > > > > > > > | > | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | int (*xBound)(void *pCtx, int nIn); int (*xCompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn); int (*xUncompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn); void (*xFree)(void *pCtx); }; </verbatim> <p><span style=color:red> Explain how the hooks work here (same as zipvfs) </span> <p><span style=color:red> Example code? Using zlib? Or something simple like an RLE implementation?</span> <p>The database file header of any LSM database contains a 32-bit unsigned "compression id" field. If the database is not a compressed database, this field is set to 1. Otherwise, it is set to an application supplied value identifying the compression and/or encryption scheme in use. Application compression scheme ids must be greater than or equal to 10000. Values smaller than 10000 are reserved for internal use. <p>The lsm_info() API may be used to read the compression id from a database connection as follows: <verbatim> unsigned int iCompressionId; rc = lsm_info(db, LSM_INFO_COMPRESSION_ID, &iCompressionId); if( rc==LSM_OK ){ /* Variable iCompressionId now contains the db compression id */ } </verbatim> Because the compression id is stored in the database header, it may be read before any required compression or encryption hooks are configured. <verbatim> #define LSM_COMPRESSION_EMPTY 0 #define LSM_COMPRESSION_NONE 1 </verbatim> <p>When a database is opened for the first time, before it is first written, the compression id field is set to LSM_COMPRESSION_EMPTY (0). After data is written into the database file, the database compression id is set to a copy of the lsm_compress.iId field of the compression hooks for the database handle doing the writing, or to LSM_COMPRESSION_NONE (1) if no compression hooks are configured. <p>Once the compression id is set to something other than LSM_COMPRESSION_EMPTY, when a database handle attempts to read or write the database file, the compression id is compared against the lsm_compress.iId field of the configured compression hooks, or against LSM_COMPRESSION_NONE if no compression hooks are configured. If the compression id does not match, then an LSM_MISMATCH error is returned and the operation fails (no transaction or database cursor is opened). <p>It is also possible to register a compression factory callback with a database handle. If one is registered, the compression factory callback is invoked instead of returning LSM_MISMATCH if the configured compression hooks do not match the compression id of a database. If the callback registers compatible compression hooks with the database handle (using the normal lsm_config() interface), then the database read or write operation resumes after it returns. Otherwise, if the compression factory callback does not register new, compatible, compression hooks with the database handle, LSM_MISMATCH is returned to the user. <p>A compression factory callback is registered with a database handle by calling lsm_config() with the second argument set to LSM_CONFIG_SET_COMPRESSION_FACTORY, and the third argument set to point to an instance of structure lsm_compress_factory. The lsm_config() copies the contents of the structure - it does not retain a pointer to it. <verbatim> typedef struct lsm_compress_factory lsm_compress_factory; struct lsm_compress_factory { void *pCtx; int (*xFactory)(void *pCtx, lsm_db *db, unsigned int iCompressionId); void (*xFree)(void *pCtx); }; </verbatim> <p><span style=color:red> Explain how the xFactory hook works here. </span> <h1 id=performance_tuning>6. Performance Tuning</h1> <p> This section describes the various measures that can be taken in order to fine-tune LSM in order to improve performance in specific circumstances. Sub-section 6.1 contains a high-level overview of the <a href=#overview_of_lsm_architecture>system architecture</a> |
︙ | ︙ | |||
949 950 951 952 953 954 955 | this option is set to the number of segments that the library attempts to merge simultaneously. Increasing this value may reduce the total amount of data written to the database file. Decreasing it increases the amount of data written to the file, but also decreases the average number of segments present in the file, which can improve the performance of database read operations. | | > > > > > | < > | < | 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | this option is set to the number of segments that the library attempts to merge simultaneously. Increasing this value may reduce the total amount of data written to the database file. Decreasing it increases the amount of data written to the file, but also decreases the average number of segments present in the file, which can improve the performance of database read operations. <p>Additionally, whether or not auto-work is enabled, this option is used to determine the maximum number of segments of a given age that are allowed to accumulate in the database file. This is described in the <a href=#compulsary_work_and_checkpoints>compulsary work and checkpoints</a> section below. <p>The default value is 4. This option must be set to a value between 2 and 8, inclusive. <dt> <a href=lsmapi.wiki#LSM_CONFIG_AUTOWORK>LSM_CONFIG_AUTOWORK</a> <dd> <p style=margin-top:0> <p>This option may be set to either 1 (true) or 0 (false). If it is set to true, then work and checkpoint operations are automatically scheduled within calls to lsm_insert(), lsm_delete(), lsm_delete_range() and lsm_commit(). Otherwise, if it is set to false, these operations must be <a href=#explicit_scheduling>explicitly scheduled</a> by the application. <p>The default value is 1. <dt> <a href=lsmapi.wiki#LSM_CONFIG_MMAP>LSM_CONFIG_MMAP</a> <dd> <p style=margin-top:0> If LSM is running on a system with a 64-bit address space, this option may be set to either 1 (true) or 0 (false). On a 32-bit platform, it is always set to 0. <p> If it is set to true, the entire database file is memory mapped. Or, if it is false, data is accessed using ordinary OS file read and write primitives. Memory mapping the database file can significantly improve the performance of read operations, as database pages do not have to be copied from operating system buffers into user space buffers before they can be examined. <p>This option may not be set if there is a read or write transaction open on the database. <p>The default value is 1 (true) on a 64-bit platform, and 0 otherwise. <dt> <a href=lsmapi.wiki#LSM_CONFIG_MULTIPLE_PROCESSES>LSM_CONFIG_MULTIPLE_PROCESSES</a> <dd> <p style=margin-top:0> This option may also be set to either 1 (true) or 0 (false). The default value is 1 (true). If it is set to false, then the library assumes that all database clients are located within the same process (have access to the |
︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 | <p>The example code below might be executed in a background thread or process in order to perform database work and checkpointing. In this case all other clients should set the LSM_CONFIG_AUTOWORK parameter to zero. <verbatim> int rc; lsm_db *db; | | | | 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 | <p>The example code below might be executed in a background thread or process in order to perform database work and checkpointing. In this case all other clients should set the LSM_CONFIG_AUTOWORK parameter to zero. <verbatim> int rc; lsm_db *db; int nCkpt = 4*1024; /* 4096KB == 4MB */ /* Open a database connection to database "test.db". ** ** Configure the connection to automatically checkpoint the database after ** writing each 4MB of data to it (instead of the default 2MB). As well ** as to auto-work, the LSM_CONFIG_AUTOCHECKPOINT parameter applies to data ** written by explicit calls to lsm_work(). */ lsm_new(0, &db); lsm_config(db, LSM_CONFIG_AUTOCHECKPOINT, &nCkpt); lsm_open(db, "test.db"); while( 1 ){ int nWrite; /* Attempt up to 512KB of work. Set nWrite to the number of bytes ** actually written to disk. */ rc = lsm_work(db, 2, 512, &nWrite); if( rc!=LSM_OK && rc!=LSM_BUSY ){ /* Anything other than LSM_OK or LSM_BUSY is a problem. LSM_BUSY ** indicates that some other client has taken the WORKER lock. Any ** other error indicates something has gone quite wrong. */ lsm_close(db); return rc; } |
︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 | <p>Database optimization transforms the contents of database file so that the following are true: <ul> <li> <p>All database content is stored in a single <a href=#architectural_overview>segment</a>. This makes the | | | < < < < | 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 | <p>Database optimization transforms the contents of database file so that the following are true: <ul> <li> <p>All database content is stored in a single <a href=#architectural_overview>segment</a>. This makes the data structure equivalent to an optimally packed b-tree stucture for search operations - minimizing the number of disk sectors that need to be visted when searching the database. <li> <p>The database file contains no (or as little as possible) free space. In other words, it is no larger than required to contain the single segment. </ul> <p>In order to optimize the database, lsm_work() should be called with the nMerge argument set to 1 and the third parameter set to a negative value (interpreted as "keep working until there is no more work to do"). For example: <verbatim> rc = lsm_work(db, 1, -1, 0); </verbatim> <p>When optimizing the database as above, either the LSM_CONFIG_AUTOCHECKPOINT parameter should be set to a non-zero value or lsm_checkpoint() should be called periodically. Otherwise, no checkpoints will be performed, preventing the library from reusing any space occupied by old segments even after their content has been merged into the new segment. The result - a database file that is optimized, except that it is up to twice as large as it otherwise would be. |
Added www/prog-intro.wiki.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | <title>Introduction For Programmers</title> <h2>1.0 Overview</h2> This document provides a broad overview of the Application Programming Interface (API) for SQLite4. If you are new to SQLite and are wondering how to write a program that uses SQLite or how to incorporate SQLite into your existing program, this document is a good starting point. <h2>2.0 Implementation Language</h2> SQLite is written in ANSI-C. The C programming language is used because it is the universal assembly language - it can run on just about any hardware and on just about any operating system. No matter what programming language is used for the application code, it can usually interface easily with a library written in C. SQLite is written in an Object-Oriented (OO) style. This may comes as a surprise to readers who have been trained to think that C++ is necessary for OO programming. While it is true that C++ has lots of syntactic sugar designed to make OO-style programming easier, there is nothing missing from generic ANSI-C that disqualifies it from using OO ideas and practices. In this document, there will be much talk of "objects", which are implemented as ordinary C structures, of course. There is also talk of "methods" which are really just procedures which take a pointer to the object as their first parameter. SQLite also uses object inheritance, though inheritance is only used in fringe areas of the API that do not fall within the scope of this document. <h2>3.0 Principal Objects</h2> Here are four object classes that every programmer who uses SQLite4 needs to be aware of: 1. <b>sqlite4_mm</b> - A memory allocator 2. <b>sqlite4_env</b> - A run-time environment 3. <b>sqlite4</b> - A database connection 4. <b>sqlite4_stmt</b> - A prepared statement The most important of these are the latter two, the database connection and prepared statement objects. SQLite4 provides default instances of a memory allocator and run-time environment which are adequate for most system programming tasks. <h3>3.1 The Memory Allocator Object</h3> A memory allocator object is used to allocate and deallocate bulk memory. SQLite4 supplies a default memory allocator object (obtained by calling the sqlite4_mm_default() function) which is just a wrapper around the system malloc()/realloc()/free() routines. Most applications will get along fine using just this default. But for applications with more advanced needs, other memory allocator objects are available, including: * Memory allocators that use fixed pool of memory (provided at start time) rather than the heap. * Meta-memory allocators that use one memory allocator first but then fail-over to a second one if the first one runs out of memory or is otherwise unable to satisfy an allocation. * Memory allocators operating out of a fixed pool of memory and that provide robust guarantees against memory fragmentation. * Memory allocators that provide lots of information about how much memory is being used. * Memory allocators that can simulate out-of-memory (OOM) faults for testing purposes. * Memory allocators that aggressively check for misuse of the memory allocation system, by (for example) overwriting memory as it is freed, and initializing newly allocated memory to a random bit pattern. * Completely customized and application-specific memory allocators. The various memory allocators mentioned above are implemented by subclassing the top-level memory allocator object. Unless you are implementing your own customized memory allocator, you do not really need to know about this subclassing. At the application programming level, you will only encounter pointers to the sqlite4_mm object base class. There are various methods on the sqlite4_mm object, including: * <b>sqlite4_mm_malloc()</b> - allocate bulk memory * <b>sqlite4_mm_free()</b> - release memory previously allocated * <b>sqlite4_mm_realloc()</b> - change the size of a prior allocation * <b>sqlite4_mm_msize()</b> - report the size of an allocation * <b>sqlite4_mm_stat()</b> - report usage statistics on a memory allocator A memory allocator object is also passed as parameter to certain interfaces in order to be used as a destructor. For example, when binding a string to a parameter in a prepared statement using the sqlite4_bind_text() method on the prepared statement object, there is a parameter which is a pointer to a memory allocator object. If that parameter is not NULL (and is not one of a handful of other values with special meanings) then SQLite4 will invoke the sqlite3_mm_free() method of the memory allocator on the string when it has finished using the string. <h3>3.2 The Run-Time Environment Object</h3> An instance of the run-time environment object defines how SQLite4 interfaces to the rest of the system. Each run-time environment object includes a pointer to a memory allocator object which is the default memory allocator for that environment. The run-time environment also includes information such as: * How to create and use mutexes * Which storage engines are available for use on the backend * A library of SQL functions available for use by all database connections * Methods for obtaining the current time and for obtaining randomness * State information that is shared across multiple database connections * Other "global" configuration settings and start-time settings SQLite4 supplies a default run-time environment object (obtained by calling the sqlite4_env_default() function) that is suitable for use in most applications. In fact, for many routines that require a pointer to a run-time environment object, you can pass in a NULL pointer and the NULL pointer will be automatically converted into a pointer to the default run-time environment. Usually the default run-time environment object is adequate, but some applications, especially those running on custom platforms or on small resource-limited devices, may want to create and use a new run-time environment object with non-standard settings. A reasonable design is to create a single instance of an appropriate run-time environment object, then store a pointer to that object in a global variable where it is readily accessible to all parts of the application. It is possible to use two or more run-time environment objects within the same application. But use caution here. Due to a long-standing bug in the design of Posix Advisor Locks (NB: the bug is in posix, not in SQLite) if you open two or more database connections to the same database using different run-time environment objects, then file locks will be broken in unexpected ways, which can lead to database corruption. Opening connections with different run-time environments to different databases is harmless and safe. Opening multiple connections to the same database using the same run-time environment is also harmless and safe. But opening multiple connections to the same database from different run-time environments can cause problems under posix. Therefore, if you are programming for a posix system (iOS, Android, QNX, Linux, MacOS, etc.) you are advised to stick to using a single run-time environment for your application. Windows does not have the posix advisory locking bug and so you are free to mix and match run-time environment objects, database connections, and databases with wild abandon there, if you are willing to sacrifice portability. <h3>3.3 The Database Connection Object</h3> The most important object is the database connection object: <b>sqlite4</b>. Each database connection object represents an open connection to one or more database files. (The database connection starts out associated with just a single database file, but more databases can be added using the ATTACH statement.) Many applications will have just a single database connection object, since they are only talking to a single database. But there are no arbitrary restrictions on the number of database objects that can be open at once. (The number of open database connections might be constrained by system resource limitations such as the maximum number of open file descriptors.) Multiple database connections can be opened on the same database file, if desired. Every database connection object is associated with a single run-time environment object. The environment of a database connection determines how the database connection interacts with the operating system. The run-time environment for the database connection is specified when the database connection is opened and cannot be changed. In fact, the procedure for creating (for "opening") a database connection object is really a method on the run-time environment object. The life-cycle of a database connection object usually goes something like this: # Create the database connection object (also referred to as "opening" the database connection) using the sqlite4_open() method of the sqlite4_env object. # Run SQL statements against the database connection using either the direct interface (sqlite4_exec()) or by creating prepared statement objects, one for each SQL statement, and evaluating the prepared statement objects. # Destroy the database connection object (also referred to as "closing" the database connection) using the sqlite4_close() method of the sqlite4 object. Much SQLite programming centers around the use of database connection objects and programmers who use the SQLite library will quickly become familar with this object. It is the traditional (at least among the SQLite developers themselves) to use the name "db" for variable that are pointers to a database connection object. <h3>3.4 The Prepared Statement Object</h3> A prepared statement object, <b>sqlite4_stmt</b>, represents a single statement of SQL that is to be run against a particular database connection. Every prepared statement object is associated with a particular database connection object. All prepared statements for a database connection must be destroyed (or "finalized") before the database connection can be destroyed (or "closed"). The use of prepared statements is not strictly necessary in SQLite4. One can do any type of SQL statement desired using the sqlite4_exec() method of the database connection object. However, prepared statements are very handy and every programmer who wants to make serious use of the SQLite4 library needs to be familiar with them. A prepared statement object is created using the sqlite4_prepare() method on the database connection object. The sqlite4_prepare() method takes a string as input which contains the text of the SQL statement, and outputs a pointer to a prepared statement object. Think of the SQL statement text as source code and the prepared statement object as object code and the sqlite4_prepare() method as the compiler. A prepared statement is evaluated or executed using the sqlite4_step() method on the prepared statement object. Each call to sqlite4_step() advances the prepared statement until it either (1) determines the next row of output or (2) completely finishes evaluating the SQL statement or (3) encounters an error. The integer return value from sqlite4_step() lets the application know which of these three outcomes occurred. For an SQL statement like an INSERT, UPDATE, or DELETE that does not return a result, the sqlite4_step() statement is usually called just a single time and reports completion of the SQL statement after the first step. For a SELECT statement that returns multiple rows of output, on the other hand, the sqlite4_step() statement will be called multiple times, once for each row of output, then one last time to complete the evaluation. At any time, though typically after the prepared statement completes, the prepared statement object can be destroyed (or "finalized") by calling the sqlite4_finalize() method. The perpared statement can also be reset back to the beginning of its execution program at any time by using the sqlite4_reset() method. A common paradigm is to prepare a prepared statement once, then run the same statement many times using calls to sqlite4_step() for each execution with calls to sqlite4_reset() to rewind the program back to the beginning whenever it completes, followed by a single call to sqlite4_finalize() to destroy the prepared statement object at the very end. The SQL text used to create a prepared statement might include "parameter" - placeholders for unknown values. For example: <blockquote><verbatim> INSERT INTO tab_xyz(x1,y2,z3) VALUES(:x, :y, :z); </verbatim></blockquote> In the INSERT statement above, the three values to be inserted as a new row into tab_xyz are specified as parameters ":x", ":y", and ":z". Parameters can also be named as just a question mark ("?") or as a question mark followed by a small integer (ex: "?17") or as identifiers that come after "$" or "@" (ex: "$var1" or "@xyz"). When a prepared statement contains parameters, values (strings, blobs, integers, floating point numbers) can be "bound" to those parameters using methods on the prepared statement object. For example, the sqlite4_bind_text() method will bind a UTF8 string to a parameter. The sqlite4_bind_int64() method will bind a signed 64-bit integer to a parameter. The sqlite4_bind_blob() method will bind a BLOB to the parameter. And so forth. A parameter can be bound multiple times. Each binding overrides the previous. The use of parameters has important advantages: 1. Prepared statements can be compiled once and then reused many times, by resetting the prepared statement and rebinding new values into the parameters. This reduces the number of calls to sqlite4_prepare() and thus improves application performance. 2. Values bound to parameters do not need to be escaped or encoded in any way. One binds raw content. This is both more efficient than having to encode the value as SQL text, and it completely eliminates the possibility of an SQL injection attack. It is stated above that the use of prepared statements is not required since all SQL statements can be run using the sqlite4_exec() method on the database connection object. That statement is true, but does not give the whole story. In reality, the use of a prepared statement is the only way to evaluate SQL statements in SQLite4. The sqlite4_exec() method is just a convenience wrapper that does all of the low-level work of preparing, stepping, and finalizing the prepared statements automatically, and out of sight. So even though an application can avoid having to use prepared statements directly by using just sqlite4_exec(), prepared statement objects are still being used behind the scenes. <h2>4.0 Usage Example</h2> <i>TBD..</i> |