Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch aux-data-in-rtree Excluding Merge-Ins
This is equivalent to a diff from e3b2e0a0 to a350040a
2018-05-18
| ||
17:58 | Add support for auxiliary columns to the rtree extension. (check-in: c6071ac9 user: drh tags: trunk) | |
17:17 | Merge enhancements from trunk, especially the CLI fixes. (Closed-Leaf check-in: a350040a user: drh tags: aux-data-in-rtree) | |
17:11 | In the CLI, detect and report errors on sqlite3_close(). Clear global variables prior to exit to so that valgrind can better detect resource leaks. (check-in: e3b2e0a0 user: drh tags: trunk) | |
16:53 | Avoid unnecessary sqlite3_finalize() operations. (check-in: 16f71032 user: drh tags: aux-data-in-rtree) | |
14:24 | Improvements to integer/float comparisons on architectures that lack a "long double" type. (check-in: 5139ea62 user: drh tags: trunk) | |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 | ** The data structure for a single virtual r-tree table is stored in three ** native SQLite tables declared as follows. In each case, the '%' character ** in the table name is replaced with the user-supplied name of the r-tree ** table. ** ** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB) ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) | | | > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ** The data structure for a single virtual r-tree table is stored in three ** native SQLite tables declared as follows. In each case, the '%' character ** in the table name is replaced with the user-supplied name of the r-tree ** table. ** ** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB) ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) ** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...) ** ** The data for each node of the r-tree structure is stored in the %_node ** table. For each node that is not the root node of the r-tree, there is ** an entry in the %_parent table associating the node with its parent. ** And for each row of data in the table, there is an entry in the %_rowid ** table that maps from the entries rowid to the id of the node that it ** is stored on. If the r-tree contains auxiliary columns, those are stored ** on the end of the %_rowid table. ** ** The root node of an r-tree always exists, even if the r-tree table is ** empty. The nodeno of the root node is always 1. All other nodes in the ** table must be the same size as the root node. The content of each node ** is formatted as follows: ** ** 1. If the node is the root node (node 1), then the first 2 bytes |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 | typedef struct RtreeMatchArg RtreeMatchArg; typedef struct RtreeGeomCallback RtreeGeomCallback; typedef union RtreeCoord RtreeCoord; typedef struct RtreeSearchPoint RtreeSearchPoint; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is ** used. */ #define HASHSIZE 97 | > > > | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | typedef struct RtreeMatchArg RtreeMatchArg; typedef struct RtreeGeomCallback RtreeGeomCallback; typedef union RtreeCoord RtreeCoord; typedef struct RtreeSearchPoint RtreeSearchPoint; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Maximum number of auxiliary columns */ #define RTREE_MAX_AUX_COLUMN 100 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is ** used. */ #define HASHSIZE 97 |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 nDim2; /* Twice the number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ u32 nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ u32 nCursor; /* Number of open cursors */ /* List of nodes removed during a CondenseTree operation. List is ** linked together via the pointer normally used for hash chains - ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree ** headed by the node (leaf nodes have RtreeNode.iNode==0). */ RtreeNode *pDeleted; | > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 nDim2; /* Twice the number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ u32 nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ u32 nCursor; /* Number of open cursors */ char *zReadAuxSql; /* SQL for statement to read aux data */ /* List of nodes removed during a CondenseTree operation. List is ** linked together via the pointer normally used for hash chains - ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree ** headed by the node (leaf nodes have RtreeNode.iNode==0). */ RtreeNode *pDeleted; |
︙ | ︙ | |||
149 150 151 152 153 154 155 156 157 158 159 160 161 162 | sqlite3_stmt *pWriteRowid; sqlite3_stmt *pDeleteRowid; /* Statements to read/write/delete a record from xxx_parent */ sqlite3_stmt *pReadParent; sqlite3_stmt *pWriteParent; sqlite3_stmt *pDeleteParent; RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ }; /* Possible values for Rtree.eCoordType: */ #define RTREE_COORD_REAL32 0 #define RTREE_COORD_INT32 1 | > > > | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | sqlite3_stmt *pWriteRowid; sqlite3_stmt *pDeleteRowid; /* Statements to read/write/delete a record from xxx_parent */ sqlite3_stmt *pReadParent; sqlite3_stmt *pWriteParent; sqlite3_stmt *pDeleteParent; /* Statement for writing to the "aux:" fields, if there are any */ sqlite3_stmt *pWriteAux; RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ }; /* Possible values for Rtree.eCoordType: */ #define RTREE_COORD_REAL32 0 #define RTREE_COORD_INT32 1 |
︙ | ︙ | |||
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ u8 atEOF; /* True if at end of search */ u8 bPoint; /* True if sPoint is valid */ int iStrategy; /* Copy of idxNum search parameter */ int nConstraint; /* Number of entries in aConstraint */ RtreeConstraint *aConstraint; /* Search constraints. */ int nPointAlloc; /* Number of slots allocated for aPoint[] */ int nPoint; /* Number of slots used in aPoint[] */ int mxLevel; /* iLevel value for root of the tree */ RtreeSearchPoint *aPoint; /* Priority queue for search points */ RtreeSearchPoint sPoint; /* Cached next search point */ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */ u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */ }; /* Return the Rtree of a RtreeCursor */ #define RTREE_OF_CURSOR(X) ((Rtree*)((X)->base.pVtab)) | > > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ u8 atEOF; /* True if at end of search */ u8 bPoint; /* True if sPoint is valid */ u8 bAuxValid; /* True if pReadAux is valid */ int iStrategy; /* Copy of idxNum search parameter */ int nConstraint; /* Number of entries in aConstraint */ RtreeConstraint *aConstraint; /* Search constraints. */ int nPointAlloc; /* Number of slots allocated for aPoint[] */ int nPoint; /* Number of slots used in aPoint[] */ int mxLevel; /* iLevel value for root of the tree */ RtreeSearchPoint *aPoint; /* Priority queue for search points */ sqlite3_stmt *pReadAux; /* Statement to read aux-data */ RtreeSearchPoint sPoint; /* Cached next search point */ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */ u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */ }; /* Return the Rtree of a RtreeCursor */ #define RTREE_OF_CURSOR(X) ((Rtree*)((X)->base.pVtab)) |
︙ | ︙ | |||
926 927 928 929 930 931 932 933 934 935 936 937 938 939 | 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. */ | > > | 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | 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_finalize(pRtree->pWriteAux); sqlite3_free(pRtree->zReadAuxSql); sqlite3_free(pRtree); } } /* ** Rtree virtual table module xDisconnect method. */ |
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int ii; RtreeCursor *pCsr = (RtreeCursor *)cur; assert( pRtree->nCursor>0 ); freeCursorConstraints(pCsr); sqlite3_free(pCsr->aPoint); for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]); sqlite3_free(pCsr); pRtree->nCursor--; nodeBlobReset(pRtree); return SQLITE_OK; } | > | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int ii; RtreeCursor *pCsr = (RtreeCursor *)cur; assert( pRtree->nCursor>0 ); freeCursorConstraints(pCsr); sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr->aPoint); for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]); sqlite3_free(pCsr); pRtree->nCursor--; nodeBlobReset(pRtree); return SQLITE_OK; } |
︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | */ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; /* Move to the next entry that matches the configured constraints. */ RTREE_QUEUE_TRACE(pCsr, "POP-Nx:"); rtreeSearchPointPop(pCsr); rc = rtreeStepToLeaf(pCsr); return rc; } /* ** Rtree virtual table module xRowid method. | > > > > | 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 | */ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; /* Move to the next entry that matches the configured constraints. */ RTREE_QUEUE_TRACE(pCsr, "POP-Nx:"); if( pCsr->bAuxValid ){ pCsr->bAuxValid = 0; sqlite3_reset(pCsr->pReadAux); } rtreeSearchPointPop(pCsr); rc = rtreeStepToLeaf(pCsr); return rc; } /* ** Rtree virtual table module xRowid method. |
︙ | ︙ | |||
1590 1591 1592 1593 1594 1595 1596 | int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( p==0 ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); | | > > > > > > | > > > > > > > > > > > > > > | 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 | int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc ) return rc; if( p==0 ) return SQLITE_OK; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c); #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ sqlite3_result_double(ctx, c.f); }else #endif { assert( pRtree->eCoordType==RTREE_COORD_INT32 ); sqlite3_result_int(ctx, c.i); } }else{ if( !pCsr->bAuxValid ){ if( pCsr->pReadAux==0 ){ rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0, &pCsr->pReadAux, 0); if( rc ) return rc; } sqlite3_bind_int64(pCsr->pReadAux, 1, nodeGetRowid(pRtree, pNode, p->iCell)); rc = sqlite3_step(pCsr->pReadAux); if( rc==SQLITE_ROW ){ pCsr->bAuxValid = 1; }else{ sqlite3_reset(pCsr->pReadAux); if( rc==SQLITE_DONE ) rc = SQLITE_OK; return rc; } } sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pReadAux, i - pRtree->nDim2 + 1)); } 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 |
︙ | ︙ | |||
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 | ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; int iCell = 0; rtreeReference(pRtree); /* Reset the cursor to the same state as rtreeOpen() leaves it in. */ freeCursorConstraints(pCsr); sqlite3_free(pCsr->aPoint); memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = (sqlite3_vtab*)pRtree; pCsr->iStrategy = idxNum; if( idxNum==1 ){ /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ RtreeSearchPoint *p; /* Search point for the leaf */ i64 iRowid = sqlite3_value_int64(argv[0]); | > > > | 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 | ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; int iCell = 0; sqlite3_stmt *pStmt; rtreeReference(pRtree); /* Reset the cursor to the same state as rtreeOpen() leaves it in. */ freeCursorConstraints(pCsr); sqlite3_free(pCsr->aPoint); pStmt = pCsr->pReadAux; memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = (sqlite3_vtab*)pRtree; pCsr->pReadAux = pStmt; pCsr->iStrategy = idxNum; if( idxNum==1 ){ /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ RtreeSearchPoint *p; /* Search point for the leaf */ i64 iRowid = sqlite3_value_int64(argv[0]); |
︙ | ︙ | |||
1852 1853 1854 1855 1856 1857 1858 | ** a single row. */ pIdxInfo->estimatedCost = 30.0; pIdxInfo->estimatedRows = 1; return SQLITE_OK; } | > > | > | 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 | ** a single row. */ pIdxInfo->estimatedCost = 30.0; pIdxInfo->estimatedRows = 1; return SQLITE_OK; } if( p->usable && ((p->iColumn>0 && p->iColumn<=pRtree->nDim2) || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ u8 op; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; |
︙ | ︙ | |||
3011 3012 3013 3014 3015 3016 3017 | /* ** The xUpdate method for rtree module virtual tables. */ static int rtreeUpdate( sqlite3_vtab *pVtab, int nData, | | | 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 | /* ** The xUpdate method for rtree module virtual tables. */ static int rtreeUpdate( sqlite3_vtab *pVtab, int nData, sqlite3_value **aData, sqlite_int64 *pRowid ){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_OK; RtreeCell cell; /* New cell to insert if nData>1 */ int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ |
︙ | ︙ | |||
3037 3038 3039 3040 3041 3042 3043 3044 | ** In the first case, if the conflict-handling mode is REPLACE, then ** the conflicting row can be removed before proceeding. In the second ** case, SQLITE_CONSTRAINT must be returned regardless of the ** conflict-handling mode specified by the user. */ if( nData>1 ){ int ii; | > > | < | | | | | | | | | | | | | | | | 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 | ** In the first case, if the conflict-handling mode is REPLACE, then ** the conflicting row can be removed before proceeding. In the second ** case, SQLITE_CONSTRAINT must be returned regardless of the ** conflict-handling mode specified by the user. */ if( nData>1 ){ int ii; int nn = nData - 4; if( nn > pRtree->nDim2 ) nn = pRtree->nDim2; /* Populate the cell.aCoord[] array. The first coordinate is aData[3]. ** ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared ** with "column" that are interpreted as table constraints. ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5)); ** This problem was discovered after years of use, so we silently ignore ** these kinds of misdeclared tables to avoid breaking any legacy. */ #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ for(ii=0; ii<nn; ii+=2){ cell.aCoord[ii].f = rtreeValueDown(aData[ii+3]); cell.aCoord[ii+1].f = rtreeValueUp(aData[ii+4]); if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } }else #endif { for(ii=0; ii<nn; ii+=2){ cell.aCoord[ii].i = sqlite3_value_int(aData[ii+3]); cell.aCoord[ii+1].i = sqlite3_value_int(aData[ii+4]); if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){ rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } } /* If a rowid value was supplied, check if it is already present in ** the table. If so, the constraint has failed. */ if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){ cell.iRowid = sqlite3_value_int64(aData[2]); if( sqlite3_value_type(aData[0])==SQLITE_NULL || sqlite3_value_int64(aData[0])!=cell.iRowid ){ int steprc; sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); steprc = sqlite3_step(pRtree->pReadRowid); rc = sqlite3_reset(pRtree->pReadRowid); if( SQLITE_ROW==steprc ){ if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ rc = rtreeDeleteRowid(pRtree, cell.iRowid); }else{ rc = rtreeConstraintError(pRtree, 0); goto constraint; } } } bHaveRowid = 1; } } /* If aData[0] is not an SQL NULL value, it is the rowid of a ** record to delete from the r-tree table. The following block does ** just that. */ if( sqlite3_value_type(aData[0])!=SQLITE_NULL ){ rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0])); } /* If the aData[] array contains more than one element, elements ** (aData[2]..aData[argc-1]) contain a new record to insert into ** the r-tree structure. */ if( rc==SQLITE_OK && nData>1 ){ /* Insert the new record into the r-tree */ RtreeNode *pLeaf = 0; /* Figure out the rowid of the new row. */ |
︙ | ︙ | |||
3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 | pRtree->iReinsertHeight = -1; rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ rc = rc2; } } } constraint: rtreeRelease(pRtree); return rc; } | > > > > > > > > > > | 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 | pRtree->iReinsertHeight = -1; rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ rc = rc2; } } if( pRtree->nAux ){ sqlite3_stmt *pUp = pRtree->pWriteAux; int jj; sqlite3_bind_int64(pUp, 1, *pRowid); for(jj=0; jj<pRtree->nAux; jj++){ sqlite3_bind_value(pUp, jj+2, aData[pRtree->nDim2+3+jj]); } sqlite3_step(pUp); rc = sqlite3_reset(pUp); } } constraint: rtreeRelease(pRtree); return rc; } |
︙ | ︙ | |||
3285 3286 3287 3288 3289 3290 3291 | int isCreate ){ int rc = SQLITE_OK; #define N_STATEMENT 8 static const char *azSql[N_STATEMENT] = { /* Write the xxx_node table */ | | | | | | | | | | | > > | > > > > > > > > | | > | | | | 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 | int isCreate ){ int rc = SQLITE_OK; #define N_STATEMENT 8 static const char *azSql[N_STATEMENT] = { /* Write the xxx_node table */ "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)", "DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1", /* Read and write the xxx_rowid table */ "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = ?1", "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(?1, ?2)", "DELETE FROM '%q'.'%q_rowid' WHERE rowid = ?1", /* Read and write the xxx_parent table */ "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = ?1", "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(?1, ?2)", "DELETE FROM '%q'.'%q_parent' WHERE nodeno = ?1" }; sqlite3_stmt **appStmt[N_STATEMENT]; int i; pRtree->db = db; if( isCreate ){ char *zCreate; sqlite3_str *p = sqlite3_str_new(db); int ii; sqlite3_str_appendf(p, "CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY,nodeno", zDb, zPrefix); for(ii=0; ii<pRtree->nAux; ii++){ sqlite3_str_appendf(p,",a%d",ii); } sqlite3_str_appendf(p, ");CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY,data);", zDb, zPrefix); sqlite3_str_appendf(p, "CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,parentnode);", zDb, zPrefix); sqlite3_str_appendf(p, "INSERT INTO \"%w\".\"%w_node\"VALUES(1,zeroblob(%d))", zDb, zPrefix, pRtree->iNodeSize); zCreate = sqlite3_str_finish(p); if( !zCreate ){ return SQLITE_NOMEM; } rc = sqlite3_exec(db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc!=SQLITE_OK ){ return rc; |
︙ | ︙ | |||
3333 3334 3335 3336 3337 3338 3339 | appStmt[4] = &pRtree->pDeleteRowid; appStmt[5] = &pRtree->pReadParent; appStmt[6] = &pRtree->pWriteParent; appStmt[7] = &pRtree->pDeleteParent; rc = rtreeQueryStat1(db, pRtree); for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 | appStmt[4] = &pRtree->pDeleteRowid; appStmt[5] = &pRtree->pReadParent; appStmt[6] = &pRtree->pWriteParent; appStmt[7] = &pRtree->pDeleteParent; rc = rtreeQueryStat1(db, pRtree); for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){ char *zSql; const char *zFormat; if( i!=3 || pRtree->nAux==0 ){ zFormat = azSql[i]; }else { /* An UPSERT is very slightly slower than REPLACE, but it is needed ** if there are auxiliary columns */ zFormat = "INSERT INTO\"%w\".\"%w_rowid\"(rowid,nodeno)VALUES(?1,?2)" "ON CONFLICT(rowid)DO UPDATE SET nodeno=excluded.nodeno"; } zSql = sqlite3_mprintf(zFormat, zDb, zPrefix); if( zSql ){ rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } sqlite3_free(zSql); } if( pRtree->nAux ){ pRtree->zReadAuxSql = sqlite3_mprintf( "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1", zDb, zPrefix); if( pRtree->zReadAuxSql==0 ){ rc = SQLITE_NOMEM; }else{ sqlite3_str *p = sqlite3_str_new(db); int ii; char *zSql; sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); for(ii=0; ii<pRtree->nAux; ii++){ if( ii ) sqlite3_str_append(p, ",", 1); sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); } sqlite3_str_appendf(p, " WHERE rowid=?1"); zSql = sqlite3_str_finish(p); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pRtree->pWriteAux, 0); sqlite3_free(zSql); } } } return rc; } /* ** The second argument to this function contains the text of an SQL statement |
︙ | ︙ | |||
3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 | int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); const char *aErrMsg[] = { 0, /* 0 */ "Wrong number of columns for an rtree table", /* 1 */ "Too few columns for an rtree table", /* 2 */ | > > > > | > | | | < < < < < < < < < | < < < < < | | > > | > > > > > | > | | > > > > > > > | | > > > > > | | > > > > > | > | | | > > > > > > > < | > | > > | | | < | 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 | int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); sqlite3_str *pSql; char *zSql; int ii = 4; int iErr; const char *aErrMsg[] = { 0, /* 0 */ "Wrong number of columns for an rtree table", /* 1 */ "Too few columns for an rtree table", /* 2 */ "Too many columns for an rtree table", /* 3 */ "Auxiliary rtree columns must be last" /* 4 */ }; assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */ if( argc>RTREE_MAX_AUX_COLUMN+3 ){ *pzErr = sqlite3_mprintf("%s", aErrMsg[3]); return SQLITE_ERROR; } sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); /* Allocate the sqlite3_vtab structure */ nDb = (int)strlen(argv[1]); nName = (int)strlen(argv[2]); pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); pRtree->nBusy = 1; pRtree->base.pModule = &rtreeModule; pRtree->zDb = (char *)&pRtree[1]; pRtree->zName = &pRtree->zDb[nDb+1]; pRtree->eCoordType = (u8)eCoordType; memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); /* Create/Connect to the underlying relational database schema. If ** that is successful, call sqlite3_declare_vtab() to configure ** the r-tree table schema. */ pSql = sqlite3_str_new(db); sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]); for(ii=4; ii<argc; ii++){ if( argv[ii][0]=='+' ){ pRtree->nAux++; sqlite3_str_appendf(pSql, ",%s", argv[ii]+1); }else if( pRtree->nAux>0 ){ break; }else{ pRtree->nDim2++; sqlite3_str_appendf(pSql, ",%s", argv[ii]); } } sqlite3_str_appendf(pSql, ");"); zSql = sqlite3_str_finish(pSql); if( !zSql ){ rc = SQLITE_NOMEM; }else if( ii<argc ){ *pzErr = sqlite3_mprintf("%s", aErrMsg[4]); rc = SQLITE_ERROR; }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } sqlite3_free(zSql); if( rc ) goto rtreeInit_fail; pRtree->nDim = pRtree->nDim2/2; if( pRtree->nDim<1 ){ iErr = 2; }else if( pRtree->nDim2>RTREE_MAX_DIMENSIONS*2 ){ iErr = 3; }else if( pRtree->nDim2 % 2 ){ iErr = 1; }else{ iErr = 0; } if( iErr ){ *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]); goto rtreeInit_fail; } pRtree->nBytesPerCell = 8 + pRtree->nDim2*4; /* Figure out the node size to use. */ rc = getNodeSize(db, pRtree, isCreate, pzErr); if( rc ) goto rtreeInit_fail; rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate); if( rc ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); goto rtreeInit_fail; } *ppVtab = (sqlite3_vtab *)pRtree; return SQLITE_OK; rtreeInit_fail: if( rc==SQLITE_OK ) rc = SQLITE_ERROR; assert( *ppVtab==0 ); assert( pRtree->nBusy==1 ); rtreeRelease(pRtree); return rc; } /* ** Implementation of a scalar function that decodes r-tree nodes to ** human readable strings. This can be used for debugging and analysis. |
︙ | ︙ | |||
3747 3748 3749 3750 3751 3752 3753 | /* ** This function is used to check that the %_parent (if bLeaf==0) or %_rowid ** (if bLeaf==1) table contains a specified entry. The schemas of the ** two tables are: ** ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) | | | | | 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 | /* ** This function is used to check that the %_parent (if bLeaf==0) or %_rowid ** (if bLeaf==1) table contains a specified entry. The schemas of the ** two tables are: ** ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) ** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...) ** ** In both cases, this function checks that there exists an entry with ** IPK value iKey and the second column set to iVal. ** */ static void rtreeCheckMapping( RtreeCheck *pCheck, /* RtreeCheck object */ int bLeaf, /* True for a leaf cell, false for interior */ i64 iKey, /* Key for mapping */ i64 iVal /* Expected value for mapping */ ){ int rc; sqlite3_stmt *pStmt; const char *azSql[2] = { "SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?1", "SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?1" }; assert( bLeaf==0 || bLeaf==1 ); if( pCheck->aCheckMapping[bLeaf]==0 ){ pCheck->aCheckMapping[bLeaf] = rtreeCheckPrepare(pCheck, azSql[bLeaf], pCheck->zDb, pCheck->zTab ); |
︙ | ︙ | |||
3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 | const char *zDb, /* Name of db ("main", "temp" etc.) */ const char *zTab, /* Name of rtree table to check */ char **pzReport /* OUT: sqlite3_malloc'd report text */ ){ RtreeCheck check; /* Common context for various routines */ sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */ int bEnd = 0; /* True if transaction should be closed */ /* Initialize the context object */ memset(&check, 0, sizeof(check)); check.db = db; check.zDb = zDb; check.zTab = zTab; /* If there is not already an open transaction, open one now. This is ** to ensure that the queries run as part of this integrity-check operate ** on a consistent snapshot. */ if( sqlite3_get_autocommit(db) ){ check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); bEnd = 1; } /* Find number of dimensions in the rtree table. */ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab); if( pStmt ){ int rc; | > > > > > > > > > > > | | 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 | const char *zDb, /* Name of db ("main", "temp" etc.) */ const char *zTab, /* Name of rtree table to check */ char **pzReport /* OUT: sqlite3_malloc'd report text */ ){ RtreeCheck check; /* Common context for various routines */ sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */ int bEnd = 0; /* True if transaction should be closed */ int nAux = 0; /* Number of extra columns. */ /* Initialize the context object */ memset(&check, 0, sizeof(check)); check.db = db; check.zDb = zDb; check.zTab = zTab; /* If there is not already an open transaction, open one now. This is ** to ensure that the queries run as part of this integrity-check operate ** on a consistent snapshot. */ if( sqlite3_get_autocommit(db) ){ check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); bEnd = 1; } /* Find the number of auxiliary columns */ if( check.rc==SQLITE_OK ){ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab); if( pStmt ){ nAux = sqlite3_column_count(pStmt) - 2; sqlite3_finalize(pStmt); } check.rc = SQLITE_OK; } /* Find number of dimensions in the rtree table. */ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab); if( pStmt ){ int rc; check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2; if( check.nDim<1 ){ rtreeCheckAppendMsg(&check, "Schema corrupt or not an rtree"); }else if( SQLITE_ROW==sqlite3_step(pStmt) ){ check.bInt = (sqlite3_column_type(pStmt, 1)==SQLITE_INTEGER); } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_CORRUPT ) check.rc = rc; |
︙ | ︙ |
Changes to ext/rtree/rtree1.test.
︙ | ︙ | |||
605 606 607 608 609 610 611 612 613 | INSERT INTO rt VALUES(1,2,3,4,5); } do_execsql_test 15.2 { DROP TABLE t13; COMMIT; } expand_all_sql db finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 | INSERT INTO rt VALUES(1,2,3,4,5); } do_execsql_test 15.2 { DROP TABLE t13; COMMIT; } # Test cases for the new auxiliary columns feature # do_catchsql_test 16.100 { CREATE VIRTUAL TABLE t16 USING rtree(id,x0,x1,y0,+aux1,x1); } {1 {Auxiliary rtree columns must be last}} do_test 16.110 { set sql { CREATE VIRTUAL TABLE t16 USING rtree( id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41 } for {set i 12} {$i<=100} {incr i} { append sql ", +a$i" } append sql ");" execsql $sql } {} do_test 16.120 { set sql { CREATE VIRTUAL TABLE t16b USING rtree( id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41 } for {set i 12} {$i<=101} {incr i} { append sql ", +a$i" } append sql ");" catchsql $sql } {1 {Too many columns for an rtree table}} do_execsql_test 16.130 { DROP TABLE IF EXISTS rt1; CREATE VIRTUAL TABLE rt1 USING rtree(id, x1, x2, +aux); INSERT INTO rt1 VALUES(1, 1, 2, 'aux1'); INSERT INTO rt1 VALUES(2, 2, 3, 'aux2'); INSERT INTO rt1 VALUES(3, 3, 4, 'aux3'); INSERT INTO rt1 VALUES(4, 4, 5, 'aux4'); SELECT * FROM rt1 WHERE id IN (1, 2, 3, 4); } {1 1.0 2.0 aux1 2 2.0 3.0 aux2 3 3.0 4.0 aux3 4 4.0 5.0 aux4} expand_all_sql db finish_test |
Changes to ext/rtree/rtree3.test.
︙ | ︙ | |||
77 78 79 80 81 82 83 | } -body { execsql { DROP TABLE rt } } do_malloc_test rtree3-3.prep { faultsim_delete_and_reopen execsql { | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | } -body { execsql { DROP TABLE rt } } do_malloc_test rtree3-3.prep { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, +a1, +a2); INSERT INTO rt VALUES(NULL, 3, 5, 7, 9); } faultsim_save_and_close } {} do_faultsim_test rtree3-3a -faults oom* -prep { faultsim_restore_and_reopen |
︙ | ︙ |
Changes to ext/rtree/rtreeC.test.
︙ | ︙ | |||
174 175 176 177 178 179 180 | #-------------------------------------------------------------------- # Test that the sqlite_stat1 data is used correctly. # reset_db do_execsql_test 5.1 { CREATE TABLE t1(x PRIMARY KEY, y); | | | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | #-------------------------------------------------------------------- # Test that the sqlite_stat1 data is used correctly. # reset_db do_execsql_test 5.1 { CREATE TABLE t1(x PRIMARY KEY, y); CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, +d1); INSERT INTO t1(x) VALUES(1); INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2 INSERT INTO t1(x) SELECT x+2 FROM t1; -- 4 INSERT INTO t1(x) SELECT x+4 FROM t1; -- 8 INSERT INTO t1(x) SELECT x+8 FROM t1; -- 16 INSERT INTO t1(x) SELECT x+16 FROM t1; -- 32 INSERT INTO t1(x) SELECT x+32 FROM t1; -- 64 INSERT INTO t1(x) SELECT x+64 FROM t1; -- 128 INSERT INTO t1(x) SELECT x+128 FROM t1; -- 256 INSERT INTO t1(x) SELECT x+256 FROM t1; -- 512 INSERT INTO t1(x) SELECT x+512 FROM t1; --1024 INSERT INTO rt SELECT x, x, x+1, printf('x%04xy',x) FROM t1 WHERE x<=5; } do_rtree_integrity_test 5.1.1 rt # First test a query with no ANALYZE data at all. The outer loop is # real table "t1". # do_eqp_test 5.2 { |
︙ | ︙ |
Added ext/rtree/rtreeH.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | # 2018-05-16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file contains tests for the r-tree module, specifically the # auxiliary column mechanism. if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl ifcapable !rtree { finish_test ; return } do_execsql_test rtreeH-100 { CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1,+label,+other); INSERT INTO t1(x0,x1,y0,y1,label) VALUES (0,10,0,10,'lower-left corner'), (0,10,90,100,'upper-left corner'), (90,100,0,10,'lower-right corner'), (90,100,90,100,'upper-right corner'), (40,60,40,60,'center'), (0,5,0,100,'left edge'), (95,100,0,100,'right edge'), (0,100,0,5,'bottom edge'), (0,100,95,100,'top edge'), (0,100,0,100,'the whole thing'), (0,50,0,100,'left half'), (51,100,0,100,'right half'), (0,100,0,50,'bottom half'), (0,100,51,100,'top half'); } {} do_execsql_test rtreeH-101 { SELECT * FROM t1_rowid ORDER BY rowid } {1 1 {lower-left corner} {} 2 1 {upper-left corner} {} 3 1 {lower-right corner} {} 4 1 {upper-right corner} {} 5 1 center {} 6 1 {left edge} {} 7 1 {right edge} {} 8 1 {bottom edge} {} 9 1 {top edge} {} 10 1 {the whole thing} {} 11 1 {left half} {} 12 1 {right half} {} 13 1 {bottom half} {} 14 1 {top half} {}} do_execsql_test rtreeH-102 { SELECT * FROM t1 WHERE rowid=5; } {5 40.0 60.0 40.0 60.0 center {}} do_execsql_test rtreeH-103 { SELECT * FROM t1 WHERE label='center'; } {5 40.0 60.0 40.0 60.0 center {}} do_rtree_integrity_test rtreeH-110 t1 do_execsql_test rtreeH-120 { SELECT label FROM t1 WHERE x1<=50 ORDER BY id } {{lower-left corner} {upper-left corner} {left edge} {left half}} do_execsql_test rtreeH-121 { SELECT label FROM t1 WHERE x1<=50 AND label NOT LIKE '%corner%' ORDER BY id } {{left edge} {left half}} do_execsql_test rtreeH-200 { WITH RECURSIVE c1(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c1 WHERE x<99), c2(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM c2 WHERE y<99) INSERT INTO t1(id, x0,x1,y0,y1,label) SELECT 1000+x+y*100, x, x+1, y, y+1, printf('box-%d,%d',x,y) FROM c1, c2; } {} do_execsql_test rtreeH-210 { SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50 ORDER BY id; } {box-48,48 box-49,48 box-48,49 box-49,49} do_execsql_test rtreeH-300 { UPDATE t1 SET label='x'||label WHERE x0>=49 AND x1<=50 AND y0>=49 AND y1<=50; SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50 ORDER BY id; } {box-48,48 box-49,48 box-48,49 xbox-49,49} finish_test |