/* ** The next group of routines are convenience wrappers around the ** VFS methods. */ UNQLITE_PRIVATE int unqliteOsOpen( unqlite_vfs *pVfs, SyMemBackend *pAlloc, const char *zPath, unqlite_file **ppOut, unsigned int flags ) { unqlite_file *pFile; int rc; *ppOut = 0; if( zPath == 0 ){ /* May happen if dealing with an in-memory database */ return SXERR_EMPTY; } /* Allocate a new instance */ pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile); if( pFile == 0 ){ return UNQLITE_NOMEM; } /* Zero the structure */ SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile); /* Invoke the xOpen method of the underlying VFS */ rc = pVfs->xOpen(pVfs, zPath, pFile, flags); if( rc != UNQLITE_OK ){ SyMemBackendFree(pAlloc,pFile); pFile = 0; } *ppOut = pFile; return rc; }
/* * Install a freshly created collection in the unqlite VM. */ static int unqliteVmInstallCollection( unqlite_vm *pVm, /* Target VM */ unqlite_col *pCol /* Collection to install */ ) { SyString *pName = &pCol->sName; sxu32 iBucket; /* Hash the collection name */ pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte); /* Install it in the corresponding bucket */ iBucket = pCol->nHash & (pVm->iColSize - 1); pCol->pNextCol = pVm->apCol[iBucket]; if( pVm->apCol[iBucket] ){ pVm->apCol[iBucket]->pPrevCol = pCol; } pVm->apCol[iBucket] = pCol; /* Link to the list of active collections */ MACRO_LD_PUSH(pVm->pCol,pCol); pVm->iCol++; if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){ /* Grow the hashtable */ sxu32 nNewSize = pVm->iColSize << 1; unqlite_col *pEntry; unqlite_col **apNew; sxu32 n; apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *)); if( apNew ){ /* Zero the new table */ SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *)); /* Rehash all entries */ n = 0; pEntry = pVm->pCol; for(;;){ /* Loop one */ if( n >= pVm->iCol ){ break; } pEntry->pNextCol = pEntry->pPrevCol = 0; /* Install in the new bucket */ iBucket = pEntry->nHash & (nNewSize - 1); pEntry->pNextCol = apNew[iBucket]; if( apNew[iBucket] ){ apNew[iBucket]->pPrevCol = pEntry; } apNew[iBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pNext; n++; } /* Release the old table and reflect the change */ SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol); pVm->apCol = apNew; pVm->iColSize = nNewSize; } } return UNQLITE_OK; }
UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId) { int rc = UNQLITE_OK; if( pId ){ rc = pId->pMethods->xClose(pId); SyMemBackendFree(pAlloc,pId); } return rc; }
/* * Drop a collection from the KV storage engine and the underlying * unqlite VM. */ UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol) { unqlite_vm *pVm = pCol->pVm; jx9_int64 nId; int rc; /* Reset the cursor */ unqlite_kv_cursor_reset(pCol->pCursor); /* Seek the cursor to the desired location */ rc = unqlite_kv_cursor_seek(pCol->pCursor, SyStringData(&pCol->sName),SyStringLength(&pCol->sName), UNQLITE_CURSOR_MATCH_EXACT ); if( rc == UNQLITE_OK ){ /* Remove the record from the storage engine */ rc = unqlite_kv_cursor_delete_entry(pCol->pCursor); } if( rc != UNQLITE_OK ){ unqliteGenErrorFormat(pCol->pVm->pDb, "Cannot remove collection '%z' due to a read-only Key/Value storage engine", &pCol->sName ); return rc; } /* Drop collection records */ for( nId = 0 ; nId < pCol->nLastid ; ++nId ){ unqliteCollectionDropRecord(pCol,nId,0,0); } /* Cleanup */ CollectionCacheRelease(pCol); SyBlobRelease(&pCol->sHeader); SyBlobRelease(&pCol->sWorker); SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName)); unqliteReleaseCursor(pVm->pDb,pCol->pCursor); /* Unlink */ if( pCol->pPrevCol ){ pCol->pPrevCol->pNextCol = pCol->pNextCol; }else{ sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1); pVm->apCol[iBucket] = pCol->pNextCol; } if( pCol->pNextCol ){ pCol->pNextCol->pPrevCol = pCol->pPrevCol; } MACRO_LD_REMOVE(pVm->pCol,pCol); pVm->iCol--; SyMemBackendPoolFree(&pVm->sAlloc,pCol); return UNQLITE_OK; }
/* * Discard a collection and its records. */ static int CollectionCacheRelease(unqlite_col *pCol) { unqlite_col_record *pNext,*pRec = pCol->pList; unqlite_vm *pVm = pCol->pVm; sxu32 n; /* Discard all records */ for( n = 0 ; n < pCol->nRec ; ++n ){ pNext = pRec->pNext; jx9MemObjRelease(&pRec->sValue); SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec); /* Point to the next record */ pRec = pNext; } SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord); pCol->nRec = pCol->nRecSize = 0; pCol->pList = 0; return UNQLITE_OK; }
/* * Install a freshly created record in a given collection. */ static int CollectionCacheInstallRecord( unqlite_col *pCol, /* Target collection */ jx9_int64 nId, /* Unique record ID */ jx9_value *pValue /* JSON value */ ) { unqlite_col_record *pRecord; sxu32 iBucket; /* Fetch the record first */ pRecord = CollectionCacheFetchRecord(pCol,nId); if( pRecord ){ /* Record already installed, overwrite its old value */ jx9MemObjStore(pValue,&pRecord->sValue); return UNQLITE_OK; } /* Allocate a new instance */ pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record)); if( pRecord == 0 ){ return UNQLITE_NOMEM; } /* Zero the structure */ SyZero(pRecord,sizeof(unqlite_col_record)); /* Fill in the structure */ jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue); jx9MemObjStore(pValue,&pRecord->sValue); pRecord->nId = nId; pRecord->pCol = pCol; /* Install in the corresponding bucket */ iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1); pRecord->pNextCol = pCol->apRecord[iBucket]; if( pCol->apRecord[iBucket] ){ pCol->apRecord[iBucket]->pPrevCol = pRecord; } pCol->apRecord[iBucket] = pRecord; /* Link */ MACRO_LD_PUSH(pCol->pList,pRecord); pCol->nRec++; if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){ /* Allocate a new larger table */ sxu32 nNewSize = pCol->nRecSize << 1; unqlite_col_record *pEntry; unqlite_col_record **apNew; sxu32 n; apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *)); if( apNew ){ /* Zero the new table */ SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *)); /* Rehash all entries */ n = 0; pEntry = pCol->pList; for(;;){ /* Loop one */ if( n >= pCol->nRec ){ break; } pEntry->pNextCol = pEntry->pPrevCol = 0; /* Install in the new bucket */ iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1); pEntry->pNextCol = apNew[iBucket]; if( apNew[iBucket] ){ apNew[iBucket]->pPrevCol = pEntry; } apNew[iBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pNext; n++; } /* Release the old table and reflect the change */ SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord); pCol->apRecord = apNew; pCol->nRecSize = nNewSize; } } /* All done */ return UNQLITE_OK; }
/* * Load or create a binary collection. */ static int unqliteVmLoadCollection( unqlite_vm *pVm, /* Target VM */ const char *zName, /* Collection name */ sxu32 nByte, /* zName length */ int iFlag, /* Control flag */ unqlite_col **ppOut /* OUT: in-memory collection */ ) { unqlite_kv_methods *pMethods; unqlite_kv_engine *pEngine; unqlite_kv_cursor *pCursor; unqlite *pDb = pVm->pDb; unqlite_col *pCol = 0; /* cc warning */ int rc = SXERR_MEM; char *zDup = 0; /* Point to the underlying KV store */ pEngine = unqlitePagerGetKvEngine(pVm->pDb); pMethods = pEngine->pIo->pMethods; /* Allocate a new cursor */ rc = unqliteInitCursor(pDb,&pCursor); if( rc != UNQLITE_OK ){ return rc; } if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){ /* Seek to the desired location */ rc = pMethods->xSeek(pCursor,(const void *)zName,(unqlite_int64)nByte,UNQLITE_CURSOR_MATCH_EXACT); if( rc != UNQLITE_OK ){ unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName); unqliteReleaseCursor(pDb,pCursor); return rc; } } /* Allocate a new instance */ pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col)); if( pCol == 0 ){ unqliteGenOutofMem(pDb); rc = UNQLITE_NOMEM; goto fail; } SyZero(pCol,sizeof(unqlite_col)); /* Fill in the structure */ SyBlobInit(&pCol->sWorker,&pVm->sAlloc); SyBlobInit(&pCol->sHeader,&pVm->sAlloc); pCol->pVm = pVm; pCol->pCursor = pCursor; /* Duplicate collection name */ zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte); if( zDup == 0 ){ unqliteGenOutofMem(pDb); rc = UNQLITE_NOMEM; goto fail; } pCol->nRecSize = 64; /* Must be a power of two */ pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *)); if( pCol->apRecord == 0 ){ unqliteGenOutofMem(pDb); rc = UNQLITE_NOMEM; goto fail; } /* Zero the table */ SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *)); SyStringInitFromBuf(&pCol->sName,zDup,nByte); jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema); if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){ /* Create a new collection */ if( pMethods->xReplace == 0 ){ /* Read-only KV engine: Generate an error message and return */ unqliteGenErrorFormat(pDb, "Cannot create new collection '%z' due to a read-only Key/Value storage engine", &pCol->sName ); rc = UNQLITE_ABORT; /* Abort VM execution */ goto fail; } /* Write the collection header */ rc = CollectionSetHeader(pEngine,pCol,0,0,0); if( rc != UNQLITE_OK ){ rc = UNQLITE_ABORT; /* Abort VM execution */ goto fail; } }else{ /* Read the collection header */ rc = CollectionLoadHeader(pCol); if( rc != UNQLITE_OK ){ unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName); goto fail; } } /* Finally install the collection */ unqliteVmInstallCollection(pVm,pCol); /* All done */ if( ppOut ){ *ppOut = pCol; } return UNQLITE_OK; fail: unqliteReleaseCursor(pDb,pCursor); if( zDup ){ SyMemBackendFree(&pVm->sAlloc,zDup); } if( pCol ){ if( pCol->apRecord ){ SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord); } SyBlobRelease(&pCol->sHeader); SyBlobRelease(&pCol->sWorker); jx9MemObjRelease(&pCol->sSchema); SyMemBackendPoolFree(&pVm->sAlloc,pCol); } return rc; }