/* ** 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; }
/* * Initialize a jx9_value to the null type. */ JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj) { /* Zero the structure */ SyZero(pObj, sizeof(jx9_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob, &pVm->sAllocator); /* Set the NULL type */ pObj->iFlags = MEMOBJ_NULL; return SXRET_OK; }
/* * Initialize a jx9_value to the array type. */ JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray) { /* Zero the structure */ SyZero(pObj, sizeof(jx9_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob, &pVm->sAllocator); /* Set the desired type */ pObj->iFlags = MEMOBJ_HASHMAP; pObj->x.pOther = pArray; return SXRET_OK; }
/* * Initialize a jx9_value to the real type. */ JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal) { /* Zero the structure */ SyZero(pObj, sizeof(jx9_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob, &pVm->sAllocator); /* Set the desired type */ pObj->x.rVal = rVal; pObj->iFlags = MEMOBJ_REAL; return SXRET_OK; }
/* * Initialize a jx9_value to the string type. */ JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal) { /* Zero the structure */ SyZero(pObj, sizeof(jx9_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob, &pVm->sAllocator); if( pVal ){ /* Append contents */ SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte); } /* Set the desired type */ pObj->iFlags = MEMOBJ_STRING; return SXRET_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; }
/* * Write and/or alter collection binary header. */ static int CollectionSetHeader( unqlite_kv_engine *pEngine, /* Underlying KV storage engine */ unqlite_col *pCol, /* Target collection */ jx9_int64 iRec, /* Last record ID */ jx9_int64 iTotal, /* Total number of records in this collection */ jx9_value *pSchema /* Collection schema */ ) { SyBlob *pHeader = &pCol->sHeader; unqlite_kv_methods *pMethods; int iWrite = 0; int rc; if( pEngine == 0 ){ /* Default storage engine */ pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb); } pMethods = pEngine->pIo->pMethods; if( SyBlobLength(pHeader) < 1 ){ Sytm *pCreate = &pCol->sCreation; /* Creation time */ unqlite_vfs *pVfs; sxu32 iDos; /* Magic number */ rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC); if( rc != UNQLITE_OK ){ return rc; } /* Initial record ID */ rc = SyBlobAppendBig64(pHeader,0); if( rc != UNQLITE_OK ){ return rc; } /* Total records in the collection */ rc = SyBlobAppendBig64(pHeader,0); if( rc != UNQLITE_OK ){ return rc; } pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs(); /* Creation time of the collection */ if( pVfs->xCurrentTime ){ /* Get the creation time */ pVfs->xCurrentTime(pVfs,pCreate); }else{ /* Zero the structure */ SyZero(pCreate,sizeof(Sytm)); } /* Convert to DOS time */ SyTimeFormatToDos(pCreate,&iDos); rc = SyBlobAppendBig32(pHeader,iDos); if( rc != UNQLITE_OK ){ return rc; } /* Offset to start writing collection schema */ pCol->nSchemaOfft = SyBlobLength(pHeader); iWrite = 1; }else{ unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader); /* Header update */ if( iRec >= 0 ){ /* Update record ID */ SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec); iWrite = 1; } if( iTotal >= 0 ){ /* Total records */ SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal); iWrite = 1; } if( pSchema ){ /* Collection Schema */ SyBlobTruncate(pHeader,pCol->nSchemaOfft); /* Encode the schema to FastJson */ rc = FastJsonEncode(pSchema,pHeader,0); if( rc != UNQLITE_OK ){ return rc; } /* Copy the collection schema */ jx9MemObjStore(pSchema,&pCol->sSchema); iWrite = 1; } } if( iWrite ){ SyString *pId = &pCol->sName; /* Reflect the disk and/or in-memory image */ rc = pMethods->xReplace(pEngine, (const void *)pId->zString,pId->nByte, SyBlobData(pHeader),SyBlobLength(pHeader) ); if( rc != UNQLITE_OK ){ unqliteGenErrorFormat(pCol->pVm->pDb, "Cannot save collection '%z' header in the underlying storage engine", pId ); return rc; } } return UNQLITE_OK; }
/* * Extract a single expression node from the input. * On success store the freshly extractd node in ppNode. * When errors, JX9 take care of generating the appropriate error message. * An expression node can be a variable [i.e: $var], an operator [i.e: ++] * an annonymous function [i.e: function(){ return "Hello"; }, a double/single * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on. */ static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode) { jx9_expr_node *pNode; SyToken *pCur; sxi32 rc; /* Allocate a new node */ pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node)); if( pNode == 0 ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return SXERR_MEM; } /* Zero the structure */ SyZero(pNode, sizeof(jx9_expr_node)); SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **)); /* Point to the head of the token stream */ pCur = pNode->pStart = pGen->pIn; /* Start collecting tokens */ if( pCur->nType & JX9_TK_OP ){ /* Point to the instance that describe this operator */ pNode->pOp = (const jx9_expr_op *)pCur->pUserData; /* Advance the stream cursor */ pCur++; }else if( pCur->nType & JX9_TK_DOLLAR ){ /* Isolate variable */ pCur++; /* Jump the dollar sign */ if( pCur >= pGen->pEnd ){ /* Syntax error */ rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); return rc; } pCur++; /* Jump the variable name */ pNode->xCode = jx9CompileVariable; }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){ /* JSON Object, assemble tokens */ pCur++; jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur); if( pCur < pGen->pEnd ){ pCur++; }else{ /* Syntax error */ rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); return rc; } pNode->xCode = jx9CompileJsonObject; }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){ /* JSON Array, assemble tokens */ pCur++; jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur); if( pCur < pGen->pEnd ){ pCur++; }else{ /* Syntax error */ rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); return rc; } pNode->xCode = jx9CompileJsonArray; }else if( pCur->nType & JX9_TK_KEYWORD ){ int nKeyword = SX_PTR_TO_INT(pCur->pUserData); if( nKeyword == JX9_TKWRD_FUNCTION ){ /* Annonymous function */ if( &pCur[1] >= pGen->pEnd ){ /* Assume a literal */ pCur++; pNode->xCode = jx9CompileLiteral; }else{ /* Assemble annonymous functions body */ rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); return rc; } pNode->xCode = jx9CompileAnnonFunc; } }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){ /* Language constructs [i.e: print,die...] require special handling */ jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur); pNode->xCode = jx9CompileLangConstruct; }else{ /* Assume a literal */ pCur++; pNode->xCode = jx9CompileLiteral; } }else if( pCur->nType & (JX9_TK_ID) ){ /* Constants, function name, namespace path, object name... */ pCur++; pNode->xCode = jx9CompileLiteral; }else{ if( (pCur->nType & (JX9_TK_LPAREN|JX9_TK_RPAREN|JX9_TK_COMMA|JX9_TK_CSB|JX9_TK_OCB|JX9_TK_CCB|JX9_TK_COLON)) == 0 ){ /* Point to the code generator routine */ pNode->xCode = jx9GetNodeHandler(pCur->nType); if( pNode->xCode == 0 ){ rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); return rc; } } /* Advance the stream cursor */ pCur++; } /* Point to the end of the token stream */ pNode->pEnd = pCur; /* Save the node for later processing */ *ppNode = pNode; /* Synchronize cursors */ pGen->pIn = pCur; return SXRET_OK; }