📄 rtree.c
字号:
nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); assert(nCell<=nMaxCell); if( nCell<nMaxCell ){ nodeOverwriteCell(pRtree, pNode, pCell, nCell); writeInt16(&pNode->zData[2], nCell+1); pNode->isDirty = 1; } return (nCell==nMaxCell);}/*** If the node is dirty, write it out to the database.*/static intnodeWrite(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode->isDirty ){ sqlite3_stmt *p = pRtree->pWriteNode; if( pNode->iNode ){ sqlite3_bind_int64(p, 1, pNode->iNode); }else{ sqlite3_bind_null(p, 1); } sqlite3_bind_blob(p, 2, pNode->zData, pRtree->iNodeSize, SQLITE_STATIC); sqlite3_step(p); pNode->isDirty = 0; rc = sqlite3_reset(p); if( pNode->iNode==0 && rc==SQLITE_OK ){ pNode->iNode = sqlite3_last_insert_rowid(pRtree->db); nodeHashInsert(pRtree, pNode); } } return rc;}/*** Release a reference to a node. If the node is dirty and the reference** count drops to zero, the node data is written to the database.*/static intnodeRelease(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode ){ assert( pNode->nRef>0 ); pNode->nRef--; if( pNode->nRef==0 ){ if( pNode->iNode==1 ){ pRtree->iDepth = -1; } if( pNode->pParent ){ rc = nodeRelease(pRtree, pNode->pParent); } if( rc==SQLITE_OK ){ rc = nodeWrite(pRtree, pNode); } nodeHashDelete(pRtree, pNode); sqlite3_free(pNode); } } return rc;}/*** Return the 64-bit integer value associated with cell iCell of** node pNode. If pNode is a leaf node, this is a rowid. If it is** an internal node, then the 64-bit integer is a child page number.*/static i64 nodeGetRowid( Rtree *pRtree, RtreeNode *pNode, int iCell){ assert( iCell<NCELL(pNode) ); return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]);}/*** Return coordinate iCoord from cell iCell in node pNode.*/static void nodeGetCoord( Rtree *pRtree, RtreeNode *pNode, int iCell, int iCoord, RtreeCoord *pCoord /* Space to write result to */){ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);}/*** Deserialize cell iCell of node pNode. Populate the structure pointed** to by pCell with the results.*/static void nodeGetCell( Rtree *pRtree, RtreeNode *pNode, int iCell, RtreeCell *pCell){ int ii; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); for(ii=0; ii<pRtree->nDim*2; ii++){ nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]); }}/* Forward declaration for the function that does the work of** the virtual table module xCreate() and xConnect() methods.*/static int rtreeInit( sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int, int);/* ** Rtree virtual table module xCreate method.*/static int rtreeCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr){ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1, (int)pAux);}/* ** Rtree virtual table module xConnect method.*/static int rtreeConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr){ return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 0, (int)pAux);}/*** Increment the r-tree reference count.*/static void rtreeReference(Rtree *pRtree){ pRtree->nBusy++;}/*** Decrement the r-tree reference count. When the reference count reaches** zero the structure is deleted.*/static void rtreeRelease(Rtree *pRtree){ pRtree->nBusy--; if( pRtree->nBusy==0 ){ sqlite3_finalize(pRtree->pReadNode); sqlite3_finalize(pRtree->pWriteNode); sqlite3_finalize(pRtree->pDeleteNode); sqlite3_finalize(pRtree->pReadRowid); sqlite3_finalize(pRtree->pWriteRowid); sqlite3_finalize(pRtree->pDeleteRowid); sqlite3_finalize(pRtree->pReadParent); sqlite3_finalize(pRtree->pWriteParent); sqlite3_finalize(pRtree->pDeleteParent); sqlite3_free(pRtree); }}/* ** Rtree virtual table module xDisconnect method.*/static int rtreeDisconnect(sqlite3_vtab *pVtab){ rtreeRelease((Rtree *)pVtab); return SQLITE_OK;}/* ** Rtree virtual table module xDestroy method.*/static int rtreeDestroy(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; int rc; char *zCreate = sqlite3_mprintf( "DROP TABLE '%q'.'%q_node';" "DROP TABLE '%q'.'%q_rowid';" "DROP TABLE '%q'.'%q_parent';", pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName ); if( !zCreate ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); } if( rc==SQLITE_OK ){ rtreeRelease(pRtree); } return rc;}/* ** Rtree virtual table module xOpen method.*/static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ int rc = SQLITE_NOMEM; RtreeCursor *pCsr; pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); if( pCsr ){ memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = pVTab; rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc;}/* ** Rtree virtual table module xClose method.*/static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int rc; RtreeCursor *pCsr = (RtreeCursor *)cur; sqlite3_free(pCsr->aConstraint); rc = nodeRelease(pRtree, pCsr->pNode); sqlite3_free(pCsr); return rc;}/*** Rtree virtual table module xEof method.**** Return non-zero if the cursor does not currently point to a valid ** record (i.e if the scan has finished), or zero otherwise.*/static int rtreeEof(sqlite3_vtab_cursor *cur){ RtreeCursor *pCsr = (RtreeCursor *)cur; return (pCsr->pNode==0);}/* ** Cursor pCursor currently points to a cell in a non-leaf page.** Return true if the sub-tree headed by the cell is filtered** (excluded) by the constraints in the pCursor->aConstraint[] ** array, or false otherwise.*/static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor){ RtreeCell cell; int ii; int bRes = 0; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]); double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]); assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); switch( p->op ){ case RTREE_LE: case RTREE_LT: bRes = p->rValue<cell_min; break; case RTREE_GE: case RTREE_GT: bRes = p->rValue>cell_max; break; case RTREE_EQ: bRes = (p->rValue>cell_max || p->rValue<cell_min); break; } } return bRes;}/* ** Return true if the cell that cursor pCursor currently points to** would be filtered (excluded) by the constraints in the ** pCursor->aConstraint[] array, or false otherwise.**** This function assumes that the cell is part of a leaf node.*/static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor){ RtreeCell cell; int ii; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double coord = DCOORD(cell.aCoord[p->iCoord]); int res; assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ ); switch( p->op ){ case RTREE_LE: res = (coord<=p->rValue); break; case RTREE_LT: res = (coord<p->rValue); break; case RTREE_GE: res = (coord>=p->rValue); break; case RTREE_GT: res = (coord>p->rValue); break; case RTREE_EQ: res = (coord==p->rValue); break; } if( !res ) return 1; } return 0;}/*** Cursor pCursor currently points at a node that heads a sub-tree of** height iHeight (if iHeight==0, then the node is a leaf). Descend** to point to the left-most cell of the sub-tree that matches the ** configured constraints.*/static int descendToCell( Rtree *pRtree, RtreeCursor *pCursor, int iHeight, int *pEof /* OUT: Set to true if cannot descend */){ int isEof; int rc; int ii; RtreeNode *pChild; sqlite3_int64 iRowid; RtreeNode *pSavedNode = pCursor->pNode; int iSavedCell = pCursor->iCell; assert( iHeight>=0 ); if( iHeight==0 ){ isEof = testRtreeEntry(pRtree, pCursor); }else{ isEof = testRtreeCell(pRtree, pCursor); } if( isEof || iHeight==0 ){ *pEof = isEof; return SQLITE_OK; } iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell); rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild); if( rc!=SQLITE_OK ){ return rc; } nodeRelease(pRtree, pCursor->pNode); pCursor->pNode = pChild; isEof = 1; for(ii=0; isEof && ii<NCELL(pChild); ii++){ pCursor->iCell = ii; rc = descendToCell(pRtree, pCursor, iHeight-1, &isEof); if( rc!=SQLITE_OK ){ return rc; } } if( isEof ){ assert( pCursor->pNode==pChild ); nodeReference(pSavedNode); nodeRelease(pRtree, pChild); pCursor->pNode = pSavedNode; pCursor->iCell = iSavedCell; } *pEof = isEof; return SQLITE_OK;}/*** One of the cells in node pNode is guaranteed to have a 64-bit ** integer value equal to iRowid. Return the index of this cell.*/static int nodeRowidIndex(Rtree *pRtree, RtreeNode *pNode, i64 iRowid){ int ii; for(ii=0; nodeGetRowid(pRtree, pNode, ii)!=iRowid; ii++){ assert( ii<(NCELL(pNode)-1) ); } return ii;}/*** Return the index of the cell containing a pointer to node pNode** in its parent. If pNode is the root node, return -1.*/static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode){ RtreeNode *pParent = pNode->pParent; if( pParent ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode); } return -1;}/* ** Rtree virtual table module xNext method.*/static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab); RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; if( pCsr->iStrategy==1 ){ /* This "scan" is a direct lookup by rowid. There is no next entry. */ nodeRelease(pRtree, pCsr->pNode); pCsr->pNode = 0; } else if( pCsr->pNode ){ /* Move to the next entry that matches the configured constraints. */ int iHeight = 0; while( pCsr->pNode ){ RtreeNode *pNode = pCsr->pNode; int nCell = NCELL(pNode); for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){ int isEof; rc = descendToCell(pRtree, pCsr, iHeight, &isEof); if( rc!=SQLITE_OK || !isEof ){ return rc; } } pCsr->pNode = pNode->pParent; pCsr->iCell = nodeParentIndex(pRtree, pNode); nodeReference(pCsr->pNode); nodeRelease(pRtree, pNode); iHeight++; } } return rc;}/* ** Rtree virtual table module xRowid method.*/static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; assert(pCsr->pNode); *pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell); return SQLITE_OK;}/* ** Rtree virtual table module xColumn method.*/static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ Rtree *pRtree = (Rtree *)cur->pVtab; RtreeCursor *pCsr = (RtreeCursor *)cur; if( i==0 ){ i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell); sqlite3_result_int64(ctx, iRowid); }else{ RtreeCoord c; nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c); if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ sqlite3_result_double(ctx, c.f); }else{ assert( pRtree->eCoordType==RTREE_COORD_INT32 ); sqlite3_result_int(ctx, c.i); } } return SQLITE_OK;}/* ** Use nodeAcquire() to obtain the leaf node containing the record with ** rowid iRowid. If successful, set *ppLeaf to point to the node and** return SQLITE_OK. If there is no such record in the table, set** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf** to zero and return an SQLite error code.*/static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){ int rc; *ppLeaf = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -