/* * 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; }
/* * 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; }
/* * 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; }