/* * 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; }
/* * Duplicate the contents of a jx9_value. */ JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest) { jx9_hashmap *pMap = 0; sxi32 rc; if( pSrc->iFlags & MEMOBJ_HASHMAP ){ /* Increment reference count */ ((jx9_hashmap *)pSrc->x.pOther)->iRef++; } if( pDest->iFlags & MEMOBJ_HASHMAP ){ pMap = (jx9_hashmap *)pDest->x.pOther; } SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); rc = SXRET_OK; if( SyBlobLength(&pSrc->sBlob) > 0 ){ SyBlobReset(&pDest->sBlob); rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob); }else{ if( SyBlobLength(&pDest->sBlob) > 0 ){ SyBlobRelease(&pDest->sBlob); } } if( pMap ){ jx9HashmapUnref(pMap); } return rc; }
/* * Convert a jx9_value to type array.Invalidate any prior representations. * According to the JX9 language reference manual. * For any of the types: integer, float, string, boolean converting a value * to an array results in an array with a single element with index zero * and the value of the scalar which was converted. */ JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj) { if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ jx9_hashmap *pMap; /* Allocate a new hashmap instance */ pMap = jx9NewHashmap(pObj->pVm, 0, 0); if( pMap == 0 ){ return SXERR_MEM; } if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ /* * According to the JX9 language reference manual. * For any of the types: integer, float, string, boolean converting a value * to an array results in an array with a single element with index zero * and the value of the scalar which was converted. */ /* Insert a single element */ jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj)); SyBlobRelease(&pObj->sBlob); } /* Invalidate any prior representation */ MemObjSetType(pObj, MEMOBJ_HASHMAP); pObj->x.pOther = pMap; } return SXRET_OK; }
/* * Convert a jx9_value to type boolean.Invalidate any prior representations. */ JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj) { if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){ /* Preform the conversion */ pObj->x.iVal = MemObjBooleanValue(&(*pObj)); /* Invalidate any prior representations */ SyBlobRelease(&pObj->sBlob); MemObjSetType(pObj, MEMOBJ_BOOL); } return SXRET_OK; }
/* * Invalidate any prior representation of a given jx9_value. */ JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj) { if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ if( pObj->iFlags & MEMOBJ_HASHMAP ){ jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther); } /* Release the internal buffer */ SyBlobRelease(&pObj->sBlob); /* Invalidate any prior representation */ pObj->iFlags = MEMOBJ_NULL; } return SXRET_OK; }
/* * Duplicate the contents of a jx9_value but do not copy internal * buffer contents, simply point to it. */ JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest) { SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); if( pSrc->iFlags & MEMOBJ_HASHMAP ){ /* Increment reference count */ ((jx9_hashmap *)pSrc->x.pOther)->iRef++; } if( SyBlobLength(&pDest->sBlob) > 0 ){ SyBlobRelease(&pDest->sBlob); } if( SyBlobLength(&pSrc->sBlob) > 0 ){ SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob)); } return SXRET_OK; }
/* * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT * or both. * Invalidate any prior representations. Every effort is made to force * the conversion, even if the input is a string that does not look * completely like a number.Convert as much of the string as we can * and ignore the rest. */ JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj) { if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){ if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){ if( pObj->iFlags & MEMOBJ_NULL ){ pObj->x.iVal = 0; } MemObjSetType(pObj, MEMOBJ_INT); } /* Already numeric */ return SXRET_OK; } if( pObj->iFlags & MEMOBJ_STRING ){ sxi32 rc = SXERR_INVALID; sxu8 bReal = FALSE; SyString sString; SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); /* Check if the given string looks like a numeric number */ if( sString.nByte > 0 ){ rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0); } if( bReal ){ jx9MemObjToReal(&(*pObj)); }else{ if( rc != SXRET_OK ){ /* The input does not look at all like a number, set the value to 0 */ pObj->x.iVal = 0; }else{ /* Convert as much as we can */ pObj->x.iVal = MemObjStringToInt(&(*pObj)); } MemObjSetType(pObj, MEMOBJ_INT); SyBlobRelease(&pObj->sBlob); } }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){ jx9MemObjToInteger(pObj); }else{ /* Perform a blind cast */ jx9MemObjToReal(&(*pObj)); } return SXRET_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; }
/* * Perform an addition operation of two jx9_values. * The reason this function is implemented here rather than 'vm.c' * is that the '+' operator is overloaded. * That is, the '+' operator is used for arithmetic operation and also * used for operation on arrays [i.e: union]. When used with an array * The + operator returns the right-hand array appended to the left-hand array. * For keys that exist in both arrays, the elements from the left-hand array * will be used, and the matching elements from the right-hand array will * be ignored. * This function take care of handling all the scenarios. */ JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore) { if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){ /* Arithemtic operation */ jx9MemObjToNumeric(pObj1); jx9MemObjToNumeric(pObj2); if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){ /* Floating point arithmetic */ jx9_real a, b; if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ jx9MemObjToReal(pObj1); } if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ jx9MemObjToReal(pObj2); } a = pObj1->x.rVal; b = pObj2->x.rVal; pObj1->x.rVal = a+b; MemObjSetType(pObj1, MEMOBJ_REAL); /* Try to get an integer representation also */ MemObjTryIntger(&(*pObj1)); }else{ /* Integer arithmetic */ sxi64 a, b; a = pObj1->x.iVal; b = pObj2->x.iVal; pObj1->x.iVal = a+b; MemObjSetType(pObj1, MEMOBJ_INT); } }else{ if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){ jx9_hashmap *pMap; sxi32 rc; if( bAddStore ){ /* Do not duplicate the hashmap, use the left one since its an add&store operation. */ if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Force a hashmap cast */ rc = jx9MemObjToHashmap(pObj1); if( rc != SXRET_OK ){ jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); return rc; } } /* Point to the structure that describe the hashmap */ pMap = (jx9_hashmap *)pObj1->x.pOther; }else{ /* Create a new hashmap */ pMap = jx9NewHashmap(pObj1->pVm, 0, 0); if( pMap == 0){ jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); return SXERR_MEM; } } if( !bAddStore ){ if(pObj1->iFlags & MEMOBJ_HASHMAP ){ /* Perform a hashmap duplication */ jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap); }else{ if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){ /* Simple insertion */ jx9HashmapInsert(pMap, 0, pObj1); } } } /* Perform the union */ if(pObj2->iFlags & MEMOBJ_HASHMAP ){ jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther); }else{ if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){ /* Simple insertion */ jx9HashmapInsert(pMap, 0, pObj2); } } /* Reflect the change */ if( pObj1->iFlags & MEMOBJ_STRING ){ SyBlobRelease(&pObj1->sBlob); } pObj1->x.pOther = pMap; MemObjSetType(pObj1, MEMOBJ_HASHMAP); } } return SXRET_OK; }