/* * Free an expression tree. */ static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode) { if( pNode->pLeft ){ /* Release the left tree */ ExprFreeTree(&(*pGen), pNode->pLeft); } if( pNode->pRight ){ /* Release the right tree */ ExprFreeTree(&(*pGen), pNode->pRight); } if( pNode->pCond ){ /* Release the conditional tree used by the ternary operator */ ExprFreeTree(&(*pGen), pNode->pCond); } if( SySetUsed(&pNode->aNodeArgs) > 0 ){ jx9_expr_node **apArg; sxu32 n; /* Release node arguments */ apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs); for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){ ExprFreeTree(&(*pGen), apArg[n]); } SySetRelease(&pNode->aNodeArgs); } /* Finally, release this node */ SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); }
/* * 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; }
/* * 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; }
/* * 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; }