Compare commits

...

3 Commits

Author SHA1 Message Date
dan 003533f724 Dynamically resize the node hash table used by the rtree module.
FossilOrigin-Name: f94f3da5de2b1085ea1967ca8961a923e79f5a1181b9507a2f4c9e29e47cbe29
2023-04-27 19:27:55 +00:00
dan 6fe63748a9 Fix some problems with the rtree module and existing tests on this branch.
FossilOrigin-Name: 50e8a30be1a8d4af42a2c58bdffcf3c4eae00450f0bb9ec39548d5a52d2a68ee
2023-04-26 20:41:00 +00:00
drh dd8bb7d912 Hold the RTREE node cache open and defer writes until the very end of the
INSERT statement, for a 20x performance improvement on the test script
identified at [forum:/forumpost/6b8bca17bb884ef3|forum post 6b8bca17bb884ef3].  However, there are still bugs and tests will crash.

FossilOrigin-Name: b35cb7458fe16390f3b1f0b2acfd3d51fabd6bf9c5f176a44d8b3cc932f74149
2023-04-24 21:08:11 +00:00
5 changed files with 300 additions and 59 deletions
+5 -5
View File
@@ -1772,14 +1772,14 @@ static sqlite3_module geopolyModule = {
rtreeRowid, /* xRowid - read data */
geopolyUpdate, /* xUpdate - write data */
rtreeBeginTransaction, /* xBegin - begin transaction */
rtreeEndTransaction, /* xSync - sync transaction */
rtreeEndTransaction, /* xCommit - commit transaction */
rtreeEndTransaction, /* xRollback - rollback transaction */
rtreeCommit, /* xSync - sync transaction */
rtreeCommit, /* xCommit - commit transaction */
rtreeRollback, /* xRollback - rollback transaction */
geopolyFindFunction, /* xFindFunction - function overloading */
rtreeRename, /* xRename - rename the table */
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
rtreeCacheWrite, /* xRelease */
rtreeCacheAbandon, /* xRollbackTo */
rtreeShadowName /* xShadowName */
};
+278 -43
View File
@@ -116,6 +116,7 @@ typedef struct RtreeMatchArg RtreeMatchArg;
typedef struct RtreeGeomCallback RtreeGeomCallback;
typedef union RtreeCoord RtreeCoord;
typedef struct RtreeSearchPoint RtreeSearchPoint;
typedef struct RtreeGlobal RtreeGlobal;
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
#define RTREE_MAX_DIMENSIONS 5
@@ -123,11 +124,8 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
/* 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
/* Initial size of hash table Rtree.aHash. */
#define INITIAL_HASHSIZE 97
/* The xBestIndex method of this virtual table requires an estimate of
** the number of rows in the virtual table to calculate the costs of
@@ -139,6 +137,16 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
#define RTREE_DEFAULT_ROWEST 1048576
#define RTREE_MIN_ROWEST 100
/*
** There is one instance of this structure for each database handle (sqlite3*)
** that the rtree extension is registered with. It is used to allow the
** rtreecheck() function to locate the Rtree structure it is operating on.
*/
struct RtreeGlobal {
Rtree *pGlobalRtree; /* List of all Rtree structures for this db */
int nGlobalRef; /* Number of pointers to this structure */
};
/*
** An rtree virtual-table object.
*/
@@ -195,7 +203,12 @@ struct Rtree {
/* Statement for writing to the "aux:" fields, if there are any */
sqlite3_stmt *pWriteAux;
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
int nHashSize; /* Number of hash buckets */
int nHashEntry; /* Number of entries in hash table */
RtreeNode **aHash; /* Hash table of in-memory nodes. */
RtreeGlobal *pGlobal; /* Database-wide object */
Rtree *pGlobalNext; /* Next in RtreeGlobal.pGlobalNext */
};
/* Possible values for Rtree.eCoordType: */
@@ -221,7 +234,10 @@ struct Rtree {
** Set the Rtree.bCorrupt flag
*/
#ifdef SQLITE_DEBUG
# define RTREE_IS_CORRUPT(X) ((X)->bCorrupt = 1)
static void rtreeIsCorrupt(Rtree *pRtree){
pRtree->bCorrupt = 1;
}
# define RTREE_IS_CORRUPT(X) rtreeIsCorrupt(X)
#else
# define RTREE_IS_CORRUPT(X)
#endif
@@ -355,12 +371,17 @@ struct RtreeConstraint {
/*
** An rtree structure node.
**
** bDel:
** This node is not in the hash table. It should be freed when its
** ref-count reaches 0.
*/
struct RtreeNode {
RtreeNode *pParent; /* Parent node */
i64 iNode; /* The node number */
int nRef; /* Number of references to this node */
int isDirty; /* True if the node needs to be written to disk */
u8 bDel;
u8 isDirty; /* True if the node needs to be written to disk */
u8 *zData; /* Content of the node, as should be on disk */
RtreeNode *pNext; /* Next node in this hash collision chain */
};
@@ -621,8 +642,8 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){
** Given a node number iNode, return the corresponding key to use
** in the Rtree.aHash table.
*/
static unsigned int nodeHash(i64 iNode){
return ((unsigned)iNode) % HASHSIZE;
static unsigned int nodeHash(Rtree *pRtree, i64 iNode){
return ((unsigned)iNode) % pRtree->nHashSize;
}
/*
@@ -631,7 +652,7 @@ static unsigned int nodeHash(i64 iNode){
*/
static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){
RtreeNode *p;
for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext);
for(p=pRtree->aHash[nodeHash(pRtree, iNode)]; p&&p->iNode!=iNode; p=p->pNext);
return p;
}
@@ -641,9 +662,35 @@ static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){
static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){
int iHash;
assert( pNode->pNext==0 );
iHash = nodeHash(pNode->iNode);
assert( pNode->pParent==0 || pNode->nRef>0 );
assert( pNode->bDel==0 );
if( pRtree->nHashEntry>pRtree->nHashSize ){
int nNew = pRtree->nHashSize * 2;
RtreeNode **aNew = (RtreeNode**)sqlite3_malloc64(sizeof(RtreeNode*)*nNew);
if( aNew ){
int ii;
memset(aNew, 0, sizeof(RtreeNode*)*nNew);
for(ii=0; ii<pRtree->nHashSize; ii++){
RtreeNode *p, *pNext;
for(p=pRtree->aHash[ii]; p; p=pNext){
int iBucket = p->iNode % nNew;
pNext = p->pNext;
p->pNext = aNew[iBucket];
aNew[iBucket] = p;
}
}
sqlite3_free(pRtree->aHash);
pRtree->aHash = aNew;
pRtree->nHashSize = nNew;
}
}
iHash = nodeHash(pRtree, pNode->iNode);
pNode->pNext = pRtree->aHash[iHash];
pRtree->aHash[iHash] = pNode;
pRtree->nHashEntry++;
}
/*
@@ -652,10 +699,11 @@ static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){
static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){
RtreeNode **pp;
if( pNode->iNode!=0 ){
pp = &pRtree->aHash[nodeHash(pNode->iNode)];
pp = &pRtree->aHash[nodeHash(pRtree, pNode->iNode)];
for( ; (*pp)!=pNode; pp = &(*pp)->pNext){ assert(*pp); }
*pp = pNode->pNext;
pNode->pNext = 0;
pRtree->nHashEntry--;
}
}
@@ -675,6 +723,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
pRtree->nNodeRef++;
pNode->pParent = pParent;
pNode->isDirty = 1;
pNode->bDel = 1;
nodeReference(pParent);
}
return pNode;
@@ -707,12 +756,20 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
if( pParent && pParent!=pNode->pParent ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
assert( pNode->pParent==0 || pNode->nRef>0 );
if( pNode->nRef==0 ){
pNode->pParent = pParent;
nodeReference(pParent);
}else{
if( pParent && pParent!=pNode->pParent ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
}
if( pNode->nRef==0 ) pRtree->nNodeRef++;
pNode->nRef++;
*ppNode = pNode;
if( iNode==1 ) pRtree->iDepth = readInt16(pNode->zData);
return SQLITE_OK;
}
@@ -747,13 +804,13 @@ static int nodeAcquire(
if( !pNode ){
rc = SQLITE_NOMEM;
}else{
pNode->pParent = pParent;
pNode->zData = (u8 *)&pNode[1];
pNode->nRef = 1;
pRtree->nNodeRef++;
pNode->iNode = iNode;
pNode->isDirty = 0;
pNode->pNext = 0;
pNode->bDel = 0;
rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData,
pRtree->iNodeSize, 0);
}
@@ -786,6 +843,7 @@ static int nodeAcquire(
if( rc==SQLITE_OK ){
if( pNode!=0 ){
pNode->pParent = pParent;
nodeReference(pParent);
nodeHashInsert(pRtree, pNode);
}else{
@@ -879,6 +937,7 @@ static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){
rc = sqlite3_reset(p);
sqlite3_bind_null(p, 2);
if( pNode->iNode==0 && rc==SQLITE_OK ){
pNode->bDel = 0;
pNode->iNode = sqlite3_last_insert_rowid(pRtree->db);
nodeHashInsert(pRtree, pNode);
}
@@ -886,6 +945,58 @@ static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){
return rc;
}
/*
** Write all dirty nodes out to disk.
*/
static int rtreeFlushCache(Rtree *pRtree){
int i;
int rc = SQLITE_OK;
i64 iLIR = sqlite3_last_insert_rowid(pRtree->db);
for(i=0; i<pRtree->nHashSize && rc==SQLITE_OK; i++){
RtreeNode *pNode;
for(pNode=pRtree->aHash[i]; pNode; pNode=pNode->pNext){
if( pNode->isDirty ){
rc = nodeWrite(pRtree, pNode);
if( rc ) break;
}
}
}
sqlite3_set_last_insert_rowid(pRtree->db, iLIR);
return rc;
}
/*
** Scan the hash table. Verify that every hash table entry has nRef==0
** and remove them. Any dirty pages are written if writeDirty is true.
*/
static int rtreeResetHashTable(Rtree *pRtree, int writeDirty){
int i;
int rc = SQLITE_OK;
for(i=0; i<pRtree->nHashSize; i++){
RtreeNode *pNode = pRtree->aHash[i];
RtreeNode *pNext;
if( pNode==0 ) continue;
while( 1 /*exit-by-break*/ ){
pNext = pNode->pNext;
if( pNode->isDirty && writeDirty ){
rc = nodeWrite(pRtree, pNode);
if( rc ) writeDirty = 0;
}
if( pNode->nRef==0 ){
sqlite3_free(pNode);
}else{
pNode->isDirty = 0;
pNode->bDel = 1;
}
if( pNext==0 ) break;
pNode = pNext;
}
pRtree->aHash[i] = 0;
}
pRtree->nHashEntry = 0;
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.
@@ -903,12 +1014,18 @@ static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
}
if( pNode->pParent ){
rc = nodeRelease(pRtree, pNode->pParent);
pNode->pParent = 0;
}
if( pNode->bDel ){
sqlite3_free(pNode);
}
#if 0
if( rc==SQLITE_OK ){
rc = nodeWrite(pRtree, pNode);
}
nodeHashDelete(pRtree, pNode);
sqlite3_free(pNode);
#endif
}
}
return rc;
@@ -1016,6 +1133,7 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->inWrTrans = 0;
assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
rtreeResetHashTable(pRtree, 0);
assert( pRtree->nNodeRef==0 || pRtree->bCorrupt );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
@@ -1027,6 +1145,15 @@ static void rtreeRelease(Rtree *pRtree){
sqlite3_finalize(pRtree->pDeleteParent);
sqlite3_finalize(pRtree->pWriteAux);
sqlite3_free(pRtree->zReadAuxSql);
sqlite3_free(pRtree->aHash);
/* Remove this object from the global list. */
if( pRtree->pGlobal ){
Rtree **pp;
for(pp=&pRtree->pGlobal->pGlobalRtree;*pp!=pRtree;pp=&(*pp)->pGlobalNext);
*pp = pRtree->pGlobalNext;
}
sqlite3_free(pRtree);
}
}
@@ -1128,6 +1255,11 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
sqlite3_free(pCsr);
pRtree->nCursor--;
nodeBlobReset(pRtree);
if( pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
/* If this was the last open cursor, and there is no write-transaction,
** discard the contents of the node-cache. */
rtreeResetHashTable(pRtree, 0);
}
return SQLITE_OK;
}
@@ -2567,7 +2699,7 @@ static int updateMapping(
for(p=pNode; p; p=p->pParent){
if( p==pChild ) return SQLITE_CORRUPT_VTAB;
}
if( pChild ){
if( pChild && pChild->nRef>0 ){
nodeRelease(pRtree, pChild->pParent);
nodeReference(pNode);
pChild->pParent = pNode;
@@ -2982,7 +3114,7 @@ static int rtreeInsertCell(
int rc = SQLITE_OK;
if( iHeight>0 ){
RtreeNode *pChild = nodeHashLookup(pRtree, pCell->iRowid);
if( pChild ){
if( pChild && pChild->nRef>0 ){
nodeRelease(pRtree, pChild->pParent);
nodeReference(pNode);
pChild->pParent = pNode;
@@ -3226,6 +3358,11 @@ static int rtreeUpdate(
int rc = SQLITE_OK;
RtreeCell cell; /* New cell to insert if nData>1 */
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
i64 iLIR = sqlite3_last_insert_rowid(pRtree->db);
#ifdef SQLITE_DEBUG
int nNodeRefSave = pRtree->nNodeRef;
#endif
if( pRtree->nNodeRef ){
/* Unable to write to the btree while another cursor is reading from it,
@@ -3357,6 +3494,8 @@ static int rtreeUpdate(
}
constraint:
sqlite3_set_last_insert_rowid(pRtree->db, iLIR);
assert( pRtree->nNodeRef==nNodeRefSave );
rtreeRelease(pRtree);
return rc;
}
@@ -3375,10 +3514,35 @@ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
** Called when a transaction completes (either by COMMIT or ROLLBACK).
** The sqlite3_blob object should be released at this point.
*/
static int rtreeEndTransaction(sqlite3_vtab *pVtab){
static int rtreeCommit(sqlite3_vtab *pVtab){
int rc = SQLITE_OK;
Rtree *pRtree = (Rtree *)pVtab;
i64 iLIR = sqlite3_last_insert_rowid(pRtree->db);
pRtree->inWrTrans = 0;
nodeBlobReset(pRtree);
rc = rtreeResetHashTable(pRtree, 1);
sqlite3_set_last_insert_rowid(pRtree->db, iLIR);
return rc;
}
static int rtreeRollback(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
pRtree->inWrTrans = 0;
nodeBlobReset(pRtree);
rtreeResetHashTable(pRtree, 0);
return SQLITE_OK;
}
/*
** Called at the end of a statement to ensure that the cache
** has been cleared and that all changes have been written.
*/
static int rtreeCacheWrite(sqlite3_vtab *pVtab, int n){
UNUSED_PARAMETER(n);
return rtreeFlushCache((Rtree*)pVtab);
}
static int rtreeCacheAbandon(sqlite3_vtab *pVtab, int n){
UNUSED_PARAMETER(n);
rtreeResetHashTable((Rtree*)pVtab, 0);
return SQLITE_OK;
}
@@ -3421,11 +3585,13 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){
Rtree *pRtree = (Rtree *)pVtab;
u8 iwt = pRtree->inWrTrans;
int rc = SQLITE_OK;
UNUSED_PARAMETER(iSavepoint);
rc = rtreeFlushCache(pRtree);
pRtree->inWrTrans = 0;
nodeBlobReset(pRtree);
pRtree->inWrTrans = iwt;
return SQLITE_OK;
return rc;
}
/*
@@ -3494,14 +3660,14 @@ static sqlite3_module rtreeModule = {
rtreeRowid, /* xRowid - read data */
rtreeUpdate, /* xUpdate - write data */
rtreeBeginTransaction, /* xBegin - begin transaction */
rtreeEndTransaction, /* xSync - sync transaction */
rtreeEndTransaction, /* xCommit - commit transaction */
rtreeEndTransaction, /* xRollback - rollback transaction */
rtreeCommit, /* xSync - sync transaction */
rtreeCommit, /* xCommit - commit transaction */
rtreeRollback, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
rtreeRename, /* xRename - rename the table */
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
rtreeCacheWrite, /* xRelease */
rtreeCacheAbandon, /* xRollbackTo */
rtreeShadowName /* xShadowName */
};
@@ -3733,11 +3899,12 @@ static int rtreeInit(
char **pzErr, /* OUT: Error message, if any */
int isCreate /* True for xCreate, false for xConnect */
){
RtreeGlobal *pGlobal = (RtreeGlobal*)pAux;
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);
int eCoordType = 0;
sqlite3_str *pSql;
char *zSql;
int ii = 4;
@@ -3751,6 +3918,13 @@ static int rtreeInit(
"Auxiliary rtree columns must be last" /* 4 */
};
eCoordType = RTREE_COORD_INT32;
#ifndef SQLITE_RTREE_INT_ONLY
if( sqlite3_stricmp("rtree", argv[0])==0 ){
eCoordType = RTREE_COORD_REAL32;
}
#endif
assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){
*pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]);
@@ -3775,6 +3949,15 @@ static int rtreeInit(
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
pRtree->aHash = (RtreeNode**)sqlite3_malloc64(
sizeof(RtreeNode*) * INITIAL_HASHSIZE
);
if( pRtree->aHash==0 ){
rc = SQLITE_NOMEM;
goto rtreeInit_fail;
}
memset(pRtree->aHash, 0, sizeof(RtreeNode*)*INITIAL_HASHSIZE);
pRtree->nHashSize = INITIAL_HASHSIZE;
/* Create/Connect to the underlying relational database schema. If
** that is successful, call sqlite3_declare_vtab() to configure
@@ -3834,6 +4017,11 @@ static int rtreeInit(
goto rtreeInit_fail;
}
/* Link this rtree into the global list */
pRtree->pGlobal = pGlobal;
pRtree->pGlobalNext = pGlobal->pGlobalRtree;
pGlobal->pGlobalRtree = pRtree;
*ppVtab = (sqlite3_vtab *)pRtree;
return SQLITE_OK;
@@ -3940,6 +4128,7 @@ struct RtreeCheck {
sqlite3 *db; /* Database handle */
const char *zDb; /* Database containing rtree table */
const char *zTab; /* Name of rtree table */
Rtree *pRtree; /* Rtree object (may be NULL) */
int bInt; /* True for rtree_i32 table */
int nDim; /* Number of dimensions for this rtree tbl */
sqlite3_stmt *pGetNode; /* Statement used to retrieve nodes */
@@ -4035,6 +4224,21 @@ static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
*/
static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
u8 *pRet = 0; /* Return value */
if( pCheck->pRtree ){
RtreeNode *pNode = nodeHashLookup(pCheck->pRtree, iNode);
if( pNode ){
int nNode = pCheck->pRtree->iNodeSize;
pRet = (u8*)sqlite3_malloc64(nNode);
if( pRet==0 ){
pCheck->rc = SQLITE_NOMEM;
}else{
memcpy(pRet, pNode->zData, nNode);
*pnNode = nNode;
}
return pRet;
}
}
if( pCheck->rc==SQLITE_OK && pCheck->pGetNode==0 ){
pCheck->pGetNode = rtreeCheckPrepare(pCheck,
@@ -4264,6 +4468,7 @@ static void rtreeCheckCount(RtreeCheck *pCheck, const char *zTbl, i64 nExpect){
*/
static int rtreeCheckTable(
sqlite3 *db, /* Database handle to access db through */
RtreeGlobal *pGlobal,
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 */
@@ -4272,12 +4477,19 @@ static int rtreeCheckTable(
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. */
Rtree *pRtree = 0;
/* Initialize the context object */
memset(&check, 0, sizeof(check));
check.db = db;
check.zDb = zDb;
check.zTab = zTab;
for(pRtree=pGlobal->pGlobalRtree; pRtree; pRtree=pRtree->pGlobalNext){
if( sqlite3_stricmp(zDb, pRtree->zDb) ) continue;
if( sqlite3_stricmp(zTab, pRtree->zName) ) continue;
break;
}
check.pRtree = pRtree;
/* 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
@@ -4379,6 +4591,8 @@ static void rtreecheck(
"wrong number of arguments to function rtreecheck()", -1
);
}else{
sqlite3 *db = sqlite3_context_db_handle(ctx);
RtreeGlobal *pGlobal = (RtreeGlobal*)sqlite3_user_data(ctx);
int rc;
char *zReport = 0;
const char *zDb = (const char*)sqlite3_value_text(apArg[0]);
@@ -4389,7 +4603,7 @@ static void rtreecheck(
}else{
zTab = (const char*)sqlite3_value_text(apArg[1]);
}
rc = rtreeCheckTable(sqlite3_context_db_handle(ctx), zDb, zTab, &zReport);
rc = rtreeCheckTable(db, pGlobal, zDb, zTab, &zReport);
if( rc==SQLITE_OK ){
sqlite3_result_text(ctx, zReport ? zReport : "ok", -1, SQLITE_TRANSIENT);
}else{
@@ -4404,33 +4618,47 @@ static void rtreecheck(
# include "geopoly.c"
#endif
/*
** The argument passed to this function is an RtreeGlobal structure.
** Decrement its reference count. Free the structure if it reaches 0.
*/
static void rtreeDestroyGlobal(void *p){
RtreeGlobal *pGlobal = (RtreeGlobal*)p;
assert( pGlobal->nGlobalRef>0 );
if( 0==(--pGlobal->nGlobalRef) ){
sqlite3_free(pGlobal);
}
}
/*
** Register the r-tree module with database handle db. This creates the
** virtual table module "rtree" and the debugging/analysis scalar
** function "rtreenode".
*/
int sqlite3RtreeInit(sqlite3 *db){
RtreeGlobal *pGlobal = 0;
const int utf8 = SQLITE_UTF8;
int rc;
rc = sqlite3_create_function(db, "rtreenode", 2, utf8, 0, rtreenode, 0, 0);
pGlobal = (RtreeGlobal*)sqlite3_malloc(sizeof(*pGlobal));
if( pGlobal==0 ) return SQLITE_NOMEM;
memset(pGlobal, 0, sizeof(*pGlobal));
pGlobal->nGlobalRef++;
rc = sqlite3_create_function_v2(db, "rtreecheck",
-1, utf8, pGlobal, rtreecheck, 0, 0, rtreeDestroyGlobal
);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
pGlobal->nGlobalRef++;
rc = sqlite3_create_module_v2(db, "rtree",
&rtreeModule, pGlobal, rtreeDestroyGlobal
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "rtreecheck", -1, utf8, 0,rtreecheck, 0,0);
}
if( rc==SQLITE_OK ){
#ifdef SQLITE_RTREE_INT_ONLY
void *c = (void *)RTREE_COORD_INT32;
#else
void *c = (void *)RTREE_COORD_REAL32;
#endif
rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
}
if( rc==SQLITE_OK ){
void *c = (void *)RTREE_COORD_INT32;
rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
pGlobal->nGlobalRef++;
rc = sqlite3_create_module_v2(db, "rtree_i32",
&rtreeModule, pGlobal, rtreeDestroyGlobal
);
}
#ifdef SQLITE_ENABLE_GEOPOLY
if( rc==SQLITE_OK ){
@@ -4438,6 +4666,13 @@ int sqlite3RtreeInit(sqlite3 *db){
}
#endif
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "rtreenode", 2, utf8, 0, rtreenode, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
}
return rc;
}
+7
View File
@@ -756,4 +756,11 @@ do_execsql_test 20.4 {
SELECT * FROM t1 JOIN t0 ON true RIGHT JOIN rt0 ON x0>a WHERE x0 = 0;
} {- - 0 0.0 0.0}
reset_db
do_execsql_test 21.0 {
CREATE VIRTUAL TABLE rt0 USING rtree(id, x0, x1);
INSERT INTO rt0 VALUES(5, 10, 10);
SELECT last_insert_rowid();
} {5}
finish_test
+9 -10
View File
@@ -1,5 +1,5 @@
C Update\sthe\scompile-time\sdetection\sof\sarchitecture\sbyte-order\sin\sthe\sRTREE\nextension\sso\sthat\sit\sis\saligned\swith\sthe\slatest\senhancements\sin\sthe\score.
D 2023-04-24T19:23:42.196
C Dynamically\sresize\sthe\snode\shash\stable\sused\sby\sthe\srtree\smodule.
D 2023-04-27T19:27:55.114
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -399,10 +399,10 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c335096108c12c01bddbadcec
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/geopoly.c 971e0b5bd9adaf0811feb8c0842a310811159da10319eb0e74fdb42bf26b99ca
F ext/rtree/rtree.c 925888f7672b326fc2a29b3a212ec3ae4aa2331507ecccfaf7f0ac0335020330
F ext/rtree/geopoly.c 398b966772d81b9e6cd05caaf94f5d9d38b26f4245d4f0908b41c60024298412
F ext/rtree/rtree.c 92def73e587b2daaff1c8b2c50c5ca0c0e543222f4d0e926ce0479454bd2b673
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
F ext/rtree/rtree1.test d47f58832145fcfed9067bc457ca8664962196c4566c17a1ebd679367db55d11
F ext/rtree/rtree1.test 96271886cb39b680ed8c0a8dc0dd341e52a368bbd54d461a433e7aecd0556a2a
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
F ext/rtree/rtree3.test 272594f88c344e973864008bbe4c71fd3a41a264c097d568593ee7886d83d409
F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b
@@ -2059,9 +2059,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P e79c95fc130fc302719690eb6391d96070aff825b2b51ef6c4ad459d9a8918d7
Q +491bd51da5e2069078d7295396d80d2ccdc3a5871714fef948076939174e6acd
R 0ddcb2c385e38b7edc9b293e66b74615
U drh
Z 660ef923c98b6f83ec3ee30adf820b34
P 50e8a30be1a8d4af42a2c58bdffcf3c4eae00450f0bb9ec39548d5a52d2a68ee
R ef7db804b2d3b6c0c526bc82af548b3d
U dan
Z 41e485964a37c8d7a0bf6a892dd19281
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
122431d3a7267ec83768316ab146c0557fb6c0577a4a47ac6ed3d7aa6811ca9a
f94f3da5de2b1085ea1967ca8961a923e79f5a1181b9507a2f4c9e29e47cbe29