| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #if !defined(SQLITE_CORE) \ |
| || (defined(SQLITE_ENABLE_RTREE) && !defined(SQLITE_OMIT_VIRTUALTABLE)) |
|
|
| #ifndef SQLITE_CORE |
| #include "sqlite3ext.h" |
| SQLITE_EXTENSION_INIT1 |
| #else |
| #include "sqlite3.h" |
| #endif |
| sqlite3_int64 sqlite3GetToken(const unsigned char*,int*); |
|
|
| #include <stddef.h> |
|
|
| |
| |
| |
| |
| #if !defined(SQLITE_AMALGAMATION) |
| #include "sqlite3rtree.h" |
| typedef sqlite3_int64 i64; |
| typedef sqlite3_uint64 u64; |
| typedef unsigned char u8; |
| typedef unsigned short u16; |
| typedef unsigned int u32; |
| #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) |
| # define NDEBUG 1 |
| #endif |
| #if defined(NDEBUG) && defined(SQLITE_DEBUG) |
| # undef NDEBUG |
| #endif |
| #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) |
| # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 |
| #endif |
| #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) |
| # define ALWAYS(X) (1) |
| # define NEVER(X) (0) |
| #elif !defined(NDEBUG) |
| # define ALWAYS(X) ((X)?1:(assert(0),0)) |
| # define NEVER(X) ((X)?(assert(0),1):0) |
| #else |
| # define ALWAYS(X) (X) |
| # define NEVER(X) (X) |
| #endif |
| #ifndef offsetof |
| # define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) |
| #endif |
| #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| # define FLEXARRAY |
| #else |
| # define FLEXARRAY 1 |
| #endif |
| #endif |
|
|
| |
| #ifdef SQLITE_DEBUG |
| # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| #endif |
|
|
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <stdlib.h> |
|
|
| |
| |
| #ifndef UNUSED_PARAMETER |
| # define UNUSED_PARAMETER(x) (void)(x) |
| #endif |
|
|
| typedef struct Rtree Rtree; |
| typedef struct RtreeCursor RtreeCursor; |
| typedef struct RtreeNode RtreeNode; |
| typedef struct RtreeCell RtreeCell; |
| typedef struct RtreeConstraint RtreeConstraint; |
| typedef struct RtreeMatchArg RtreeMatchArg; |
| typedef struct RtreeGeomCallback RtreeGeomCallback; |
| typedef union RtreeCoord RtreeCoord; |
| typedef struct RtreeSearchPoint RtreeSearchPoint; |
|
|
| |
| #define RTREE_MAX_DIMENSIONS 5 |
|
|
| |
| #define RTREE_MAX_AUX_COLUMN 100 |
|
|
| |
| |
| |
| |
| #define HASHSIZE 97 |
|
|
| |
| |
| |
| |
| |
| |
| |
| #define RTREE_DEFAULT_ROWEST 1048576 |
| #define RTREE_MIN_ROWEST 100 |
|
|
| |
| |
| |
| struct Rtree { |
| sqlite3_vtab base; |
| sqlite3 *db; |
| int iNodeSize; |
| u8 nDim; |
| u8 nDim2; |
| u8 eCoordType; |
| u8 nBytesPerCell; |
| u8 inWrTrans; |
| u8 nAux; |
| #ifdef SQLITE_ENABLE_GEOPOLY |
| u8 nAuxNotNull; |
| #endif |
| #ifdef SQLITE_DEBUG |
| u8 bCorrupt; |
| #endif |
| int iDepth; |
| char *zDb; |
| char *zName; |
| char *zNodeName; |
| u32 nBusy; |
| i64 nRowEst; |
| u32 nCursor; |
| u32 nNodeRef; |
| char *zReadAuxSql; |
|
|
| |
| |
| |
| |
| |
| RtreeNode *pDeleted; |
|
|
| |
| sqlite3_blob *pNodeBlob; |
|
|
| |
| sqlite3_stmt *pWriteNode; |
| sqlite3_stmt *pDeleteNode; |
|
|
| |
| sqlite3_stmt *pReadRowid; |
| sqlite3_stmt *pWriteRowid; |
| sqlite3_stmt *pDeleteRowid; |
|
|
| |
| sqlite3_stmt *pReadParent; |
| sqlite3_stmt *pWriteParent; |
| sqlite3_stmt *pDeleteParent; |
|
|
| |
| sqlite3_stmt *pWriteAux; |
|
|
| RtreeNode *aHash[HASHSIZE]; |
| }; |
|
|
| |
| #define RTREE_COORD_REAL32 0 |
| #define RTREE_COORD_INT32 1 |
|
|
| |
| |
| |
| |
| |
| #ifdef SQLITE_RTREE_INT_ONLY |
| typedef sqlite3_int64 RtreeDValue; |
| typedef int RtreeValue; |
| # define RTREE_ZERO 0 |
| #else |
| typedef double RtreeDValue; |
| typedef float RtreeValue; |
| # define RTREE_ZERO 0.0 |
| #endif |
|
|
| |
| |
| |
| #ifdef SQLITE_DEBUG |
| # define RTREE_IS_CORRUPT(X) ((X)->bCorrupt = 1) |
| #else |
| # define RTREE_IS_CORRUPT(X) |
| #endif |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct RtreeSearchPoint { |
| RtreeDValue rScore; |
| sqlite3_int64 id; |
| u8 iLevel; |
| u8 eWithin; |
| u8 iCell; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) |
| #define RTREE_REINSERT(p) RTREE_MINCELLS(p) |
| #define RTREE_MAXCELLS 51 |
|
|
| |
| |
| |
| |
| |
| |
| |
| #define RTREE_MAX_DEPTH 40 |
|
|
|
|
| |
| |
| |
| |
| |
| #define RTREE_CACHE_SZ 5 |
|
|
| |
| |
| |
| struct RtreeCursor { |
| sqlite3_vtab_cursor base; |
| u8 atEOF; |
| u8 bPoint; |
| u8 bAuxValid; |
| int iStrategy; |
| int nConstraint; |
| RtreeConstraint *aConstraint; |
| int nPointAlloc; |
| int nPoint; |
| int mxLevel; |
| RtreeSearchPoint *aPoint; |
| sqlite3_stmt *pReadAux; |
| RtreeSearchPoint sPoint; |
| RtreeNode *aNode[RTREE_CACHE_SZ]; |
| u32 anQueue[RTREE_MAX_DEPTH+1]; |
| }; |
|
|
| |
| #define RTREE_OF_CURSOR(X) ((Rtree*)((X)->base.pVtab)) |
|
|
| |
| |
| |
| |
| union RtreeCoord { |
| RtreeValue f; |
| int i; |
| u32 u; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| #ifdef SQLITE_RTREE_INT_ONLY |
| # define DCOORD(coord) ((RtreeDValue)coord.i) |
| #else |
| # define DCOORD(coord) ( \ |
| (pRtree->eCoordType==RTREE_COORD_REAL32) ? \ |
| ((double)coord.f) : \ |
| ((double)coord.i) \ |
| ) |
| #endif |
|
|
| |
| |
| |
| struct RtreeConstraint { |
| int iCoord; |
| int op; |
| union { |
| RtreeDValue rValue; |
| int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*); |
| int (*xQueryFunc)(sqlite3_rtree_query_info*); |
| } u; |
| sqlite3_rtree_query_info *pInfo; |
| }; |
|
|
| |
| #define RTREE_EQ 0x41 |
| #define RTREE_LE 0x42 |
| #define RTREE_LT 0x43 |
| #define RTREE_GE 0x44 |
| #define RTREE_GT 0x45 |
| #define RTREE_MATCH 0x46 |
| #define RTREE_QUERY 0x47 |
|
|
| |
| |
| |
| |
| #define RTREE_TRUE 0x3f |
| #define RTREE_FALSE 0x40 |
|
|
| |
| |
| |
| struct RtreeNode { |
| RtreeNode *pParent; |
| i64 iNode; |
| int nRef; |
| int isDirty; |
| u8 *zData; |
| RtreeNode *pNext; |
| }; |
|
|
| |
| #define NCELL(pNode) readInt16(&(pNode)->zData[2]) |
|
|
| |
| |
| |
| struct RtreeCell { |
| i64 iRowid; |
| RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; |
| }; |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct RtreeGeomCallback { |
| int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*); |
| int (*xQueryFunc)(sqlite3_rtree_query_info*); |
| void (*xDestructor)(void*); |
| void *pContext; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| struct RtreeMatchArg { |
| u32 iSize; |
| RtreeGeomCallback cb; |
| int nParam; |
| sqlite3_value **apSqlParam; |
| RtreeDValue aParam[FLEXARRAY]; |
| }; |
|
|
| |
| #define SZ_RTREEMATCHARG(N) \ |
| (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) |
|
|
| #ifndef MAX |
| # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| #endif |
| #ifndef MIN |
| # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| #endif |
|
|
| |
| |
| |
| |
| |
| #ifndef GCC_VERSION |
| #if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC) |
| # define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__) |
| #else |
| # define GCC_VERSION 0 |
| #endif |
| #endif |
|
|
| |
| |
| |
| #ifndef SQLITE_AMALGAMATION |
| # if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) |
| unsigned int sqlite3RtreeTestcase = 0; |
| # define testcase(X) if( X ){ sqlite3RtreeTestcase += __LINE__; } |
| # else |
| # define testcase(X) |
| # endif |
| #endif |
|
|
| |
| |
| |
| |
| |
| #if !defined(SQLITE_DISABLE_INTRINSIC) |
| # if defined(_MSC_VER) && _MSC_VER>=1400 |
| # if !defined(_WIN32_WCE) |
| # include <intrin.h> |
| # pragma intrinsic(_byteswap_ulong) |
| # pragma intrinsic(_byteswap_uint64) |
| # else |
| # include <cmnintrin.h> |
| # endif |
| # endif |
| #endif |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef SQLITE_BYTEORDER |
| # if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ |
| # define SQLITE_BYTEORDER 4321 |
| # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ |
| # define SQLITE_BYTEORDER 1234 |
| # elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 |
| # define SQLITE_BYTEORDER 4321 |
| # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ |
| defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ |
| defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ |
| defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) |
| # define SQLITE_BYTEORDER 1234 |
| # elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) |
| # define SQLITE_BYTEORDER 4321 |
| # else |
| # define SQLITE_BYTEORDER 0 |
| # endif |
| #endif |
|
|
|
|
| |
| #ifndef MSVC_VERSION |
| #if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC) |
| # define MSVC_VERSION _MSC_VER |
| #else |
| # define MSVC_VERSION 0 |
| #endif |
| #endif |
|
|
| |
| |
| |
| |
| static int readInt16(u8 *p){ |
| return (p[0]<<8) + p[1]; |
| } |
| static void readCoord(u8 *p, RtreeCoord *pCoord){ |
| assert( FOUR_BYTE_ALIGNED(p) ); |
| #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 |
| pCoord->u = _byteswap_ulong(*(u32*)p); |
| #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 |
| pCoord->u = __builtin_bswap32(*(u32*)p); |
| #elif SQLITE_BYTEORDER==4321 |
| pCoord->u = *(u32*)p; |
| #else |
| pCoord->u = ( |
| (((u32)p[0]) << 24) + |
| (((u32)p[1]) << 16) + |
| (((u32)p[2]) << 8) + |
| (((u32)p[3]) << 0) |
| ); |
| #endif |
| } |
| static i64 readInt64(u8 *p){ |
| #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 |
| u64 x; |
| memcpy(&x, p, 8); |
| return (i64)_byteswap_uint64(x); |
| #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 |
| u64 x; |
| memcpy(&x, p, 8); |
| return (i64)__builtin_bswap64(x); |
| #elif SQLITE_BYTEORDER==4321 |
| i64 x; |
| memcpy(&x, p, 8); |
| return x; |
| #else |
| return (i64)( |
| (((u64)p[0]) << 56) + |
| (((u64)p[1]) << 48) + |
| (((u64)p[2]) << 40) + |
| (((u64)p[3]) << 32) + |
| (((u64)p[4]) << 24) + |
| (((u64)p[5]) << 16) + |
| (((u64)p[6]) << 8) + |
| (((u64)p[7]) << 0) |
| ); |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| static void writeInt16(u8 *p, int i){ |
| p[0] = (i>> 8)&0xFF; |
| p[1] = (i>> 0)&0xFF; |
| } |
| static int writeCoord(u8 *p, RtreeCoord *pCoord){ |
| u32 i; |
| assert( FOUR_BYTE_ALIGNED(p) ); |
| assert( sizeof(RtreeCoord)==4 ); |
| assert( sizeof(u32)==4 ); |
| #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 |
| i = __builtin_bswap32(pCoord->u); |
| memcpy(p, &i, 4); |
| #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 |
| i = _byteswap_ulong(pCoord->u); |
| memcpy(p, &i, 4); |
| #elif SQLITE_BYTEORDER==4321 |
| i = pCoord->u; |
| memcpy(p, &i, 4); |
| #else |
| i = pCoord->u; |
| p[0] = (i>>24)&0xFF; |
| p[1] = (i>>16)&0xFF; |
| p[2] = (i>> 8)&0xFF; |
| p[3] = (i>> 0)&0xFF; |
| #endif |
| return 4; |
| } |
| static int writeInt64(u8 *p, i64 i){ |
| #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 |
| i = (i64)__builtin_bswap64((u64)i); |
| memcpy(p, &i, 8); |
| #elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 |
| i = (i64)_byteswap_uint64((u64)i); |
| memcpy(p, &i, 8); |
| #elif SQLITE_BYTEORDER==4321 |
| memcpy(p, &i, 8); |
| #else |
| p[0] = (i>>56)&0xFF; |
| p[1] = (i>>48)&0xFF; |
| p[2] = (i>>40)&0xFF; |
| p[3] = (i>>32)&0xFF; |
| p[4] = (i>>24)&0xFF; |
| p[5] = (i>>16)&0xFF; |
| p[6] = (i>> 8)&0xFF; |
| p[7] = (i>> 0)&0xFF; |
| #endif |
| return 8; |
| } |
|
|
| |
| |
| |
| static void nodeReference(RtreeNode *p){ |
| if( p ){ |
| assert( p->nRef>0 ); |
| p->nRef++; |
| } |
| } |
|
|
| |
| |
| |
| static void nodeZero(Rtree *pRtree, RtreeNode *p){ |
| memset(&p->zData[2], 0, pRtree->iNodeSize-2); |
| p->isDirty = 1; |
| } |
|
|
| |
| |
| |
| |
| static unsigned int nodeHash(i64 iNode){ |
| return ((unsigned)iNode) % HASHSIZE; |
| } |
|
|
| |
| |
| |
| |
| static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){ |
| RtreeNode *p; |
| for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext); |
| return p; |
| } |
|
|
| |
| |
| |
| static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){ |
| int iHash; |
| assert( pNode->pNext==0 ); |
| iHash = nodeHash(pNode->iNode); |
| pNode->pNext = pRtree->aHash[iHash]; |
| pRtree->aHash[iHash] = pNode; |
| } |
|
|
| |
| |
| |
| static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ |
| RtreeNode **pp; |
| if( pNode->iNode!=0 ){ |
| pp = &pRtree->aHash[nodeHash(pNode->iNode)]; |
| for( ; (*pp)!=pNode; pp = &(*pp)->pNext){ assert(*pp); } |
| *pp = pNode->pNext; |
| pNode->pNext = 0; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ |
| RtreeNode *pNode; |
| pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode) + pRtree->iNodeSize); |
| if( pNode ){ |
| memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); |
| pNode->zData = (u8 *)&pNode[1]; |
| pNode->nRef = 1; |
| pRtree->nNodeRef++; |
| pNode->pParent = pParent; |
| pNode->isDirty = 1; |
| nodeReference(pParent); |
| } |
| return pNode; |
| } |
|
|
| |
| |
| |
| static void nodeBlobReset(Rtree *pRtree){ |
| sqlite3_blob *pBlob = pRtree->pNodeBlob; |
| pRtree->pNodeBlob = 0; |
| sqlite3_blob_close(pBlob); |
| } |
|
|
| |
| |
| |
| static int nodeAcquire( |
| Rtree *pRtree, |
| i64 iNode, |
| RtreeNode *pParent, |
| RtreeNode **ppNode |
| ){ |
| int rc = SQLITE_OK; |
| RtreeNode *pNode = 0; |
|
|
| |
| |
| |
| if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ |
| if( pParent && ALWAYS(pParent!=pNode->pParent) ){ |
| RTREE_IS_CORRUPT(pRtree); |
| return SQLITE_CORRUPT_VTAB; |
| } |
| pNode->nRef++; |
| *ppNode = pNode; |
| return SQLITE_OK; |
| } |
|
|
| if( pRtree->pNodeBlob ){ |
| sqlite3_blob *pBlob = pRtree->pNodeBlob; |
| pRtree->pNodeBlob = 0; |
| rc = sqlite3_blob_reopen(pBlob, iNode); |
| pRtree->pNodeBlob = pBlob; |
| if( rc ){ |
| nodeBlobReset(pRtree); |
| if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; |
| } |
| } |
| if( pRtree->pNodeBlob==0 ){ |
| rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, |
| "data", iNode, 0, |
| &pRtree->pNodeBlob); |
| } |
| if( rc ){ |
| *ppNode = 0; |
| |
| |
| if( rc==SQLITE_ERROR ){ |
| rc = SQLITE_CORRUPT_VTAB; |
| RTREE_IS_CORRUPT(pRtree); |
| } |
| }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){ |
| pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize); |
| 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; |
| rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData, |
| pRtree->iNodeSize, 0); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if( rc==SQLITE_OK && pNode && iNode==1 ){ |
| pRtree->iDepth = readInt16(pNode->zData); |
| if( pRtree->iDepth>RTREE_MAX_DEPTH ){ |
| rc = SQLITE_CORRUPT_VTAB; |
| RTREE_IS_CORRUPT(pRtree); |
| } |
| } |
|
|
| |
| |
| |
| |
| if( pNode && rc==SQLITE_OK ){ |
| if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ |
| rc = SQLITE_CORRUPT_VTAB; |
| RTREE_IS_CORRUPT(pRtree); |
| } |
| } |
|
|
| if( rc==SQLITE_OK ){ |
| if( pNode!=0 ){ |
| nodeReference(pParent); |
| nodeHashInsert(pRtree, pNode); |
| }else{ |
| rc = SQLITE_CORRUPT_VTAB; |
| RTREE_IS_CORRUPT(pRtree); |
| } |
| *ppNode = pNode; |
| }else{ |
| nodeBlobReset(pRtree); |
| if( pNode ){ |
| pRtree->nNodeRef--; |
| sqlite3_free(pNode); |
| } |
| *ppNode = 0; |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| static void nodeOverwriteCell( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| RtreeCell *pCell, |
| int iCell |
| ){ |
| int ii; |
| u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; |
| p += writeInt64(p, pCell->iRowid); |
| for(ii=0; ii<pRtree->nDim2; ii++){ |
| p += writeCoord(p, &pCell->aCoord[ii]); |
| } |
| pNode->isDirty = 1; |
| } |
|
|
| |
| |
| |
| static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){ |
| u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; |
| u8 *pSrc = &pDst[pRtree->nBytesPerCell]; |
| int nByte = (NCELL(pNode) - iCell - 1) * pRtree->nBytesPerCell; |
| memmove(pDst, pSrc, nByte); |
| writeInt16(&pNode->zData[2], NCELL(pNode)-1); |
| pNode->isDirty = 1; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int nodeInsertCell( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| RtreeCell *pCell |
| ){ |
| int nCell; |
| int nMaxCell; |
|
|
| 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); |
| } |
|
|
| |
| |
| |
| static int nodeWrite(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); |
| sqlite3_bind_null(p, 2); |
| if( pNode->iNode==0 && rc==SQLITE_OK ){ |
| pNode->iNode = sqlite3_last_insert_rowid(pRtree->db); |
| nodeHashInsert(pRtree, pNode); |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){ |
| int rc = SQLITE_OK; |
| if( pNode ){ |
| assert( pNode->nRef>0 ); |
| assert( pRtree->nNodeRef>0 ); |
| pNode->nRef--; |
| if( pNode->nRef==0 ){ |
| pRtree->nNodeRef--; |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| static i64 nodeGetRowid( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| int iCell |
| ){ |
| assert( iCell<NCELL(pNode) ); |
| return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]); |
| } |
|
|
| |
| |
| |
| static void nodeGetCoord( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| int iCell, |
| int iCoord, |
| RtreeCoord *pCoord |
| ){ |
| assert( iCell<NCELL(pNode) ); |
| readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); |
| } |
|
|
| |
| |
| |
| |
| static void nodeGetCell( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| int iCell, |
| RtreeCell *pCell |
| ){ |
| u8 *pData; |
| RtreeCoord *pCoord; |
| int ii = 0; |
| pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); |
| pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); |
| pCoord = pCell->aCoord; |
| do{ |
| readCoord(pData, &pCoord[ii]); |
| readCoord(pData+4, &pCoord[ii+1]); |
| pData += 8; |
| ii += 2; |
| }while( ii<pRtree->nDim2 ); |
| } |
|
|
|
|
| |
| |
| |
| static int rtreeInit( |
| sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int |
| ); |
|
|
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| static void rtreeReference(Rtree *pRtree){ |
| pRtree->nBusy++; |
| } |
|
|
| |
| |
| |
| |
| static void rtreeRelease(Rtree *pRtree){ |
| pRtree->nBusy--; |
| if( pRtree->nBusy==0 ){ |
| pRtree->inWrTrans = 0; |
| assert( pRtree->nCursor==0 ); |
| nodeBlobReset(pRtree); |
| assert( pRtree->nNodeRef==0 || pRtree->bCorrupt ); |
| 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_finalize(pRtree->pWriteAux); |
| sqlite3_free(pRtree->zReadAuxSql); |
| sqlite3_free(pRtree); |
| } |
| } |
|
|
| |
| |
| |
| static int rtreeDisconnect(sqlite3_vtab *pVtab){ |
| rtreeRelease((Rtree *)pVtab); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| 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{ |
| nodeBlobReset(pRtree); |
| rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0); |
| sqlite3_free(zCreate); |
| } |
| if( rc==SQLITE_OK ){ |
| rtreeRelease(pRtree); |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ |
| int rc = SQLITE_NOMEM; |
| Rtree *pRtree = (Rtree *)pVTab; |
| RtreeCursor *pCsr; |
|
|
| pCsr = (RtreeCursor *)sqlite3_malloc64(sizeof(RtreeCursor)); |
| if( pCsr ){ |
| memset(pCsr, 0, sizeof(RtreeCursor)); |
| pCsr->base.pVtab = pVTab; |
| rc = SQLITE_OK; |
| pRtree->nCursor++; |
| } |
| *ppCursor = (sqlite3_vtab_cursor *)pCsr; |
|
|
| return rc; |
| } |
|
|
|
|
| |
| |
| |
| static void resetCursor(RtreeCursor *pCsr){ |
| Rtree *pRtree = (Rtree *)(pCsr->base.pVtab); |
| int ii; |
| sqlite3_stmt *pStmt; |
| if( pCsr->aConstraint ){ |
| int i; |
| for(i=0; i<pCsr->nConstraint; i++){ |
| sqlite3_rtree_query_info *pInfo = pCsr->aConstraint[i].pInfo; |
| if( pInfo ){ |
| if( pInfo->xDelUser ) pInfo->xDelUser(pInfo->pUser); |
| sqlite3_free(pInfo); |
| } |
| } |
| sqlite3_free(pCsr->aConstraint); |
| pCsr->aConstraint = 0; |
| } |
| for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]); |
| sqlite3_free(pCsr->aPoint); |
| pStmt = pCsr->pReadAux; |
| memset(pCsr, 0, sizeof(RtreeCursor)); |
| pCsr->base.pVtab = (sqlite3_vtab*)pRtree; |
| pCsr->pReadAux = pStmt; |
|
|
| |
| |
| |
| |
| |
| sqlite3_reset(pStmt); |
| } |
|
|
| |
| |
| |
| static int rtreeClose(sqlite3_vtab_cursor *cur){ |
| Rtree *pRtree = (Rtree *)(cur->pVtab); |
| RtreeCursor *pCsr = (RtreeCursor *)cur; |
| assert( pRtree->nCursor>0 ); |
| resetCursor(pCsr); |
| sqlite3_finalize(pCsr->pReadAux); |
| sqlite3_free(pCsr); |
| pRtree->nCursor--; |
| if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ |
| nodeBlobReset(pRtree); |
| } |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int rtreeEof(sqlite3_vtab_cursor *cur){ |
| RtreeCursor *pCsr = (RtreeCursor *)cur; |
| return pCsr->atEOF; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 |
| #define RTREE_DECODE_COORD(eInt, a, r) { \ |
| RtreeCoord c; \ |
| c.u = _byteswap_ulong(*(u32*)a); \ |
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ |
| } |
| #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 |
| #define RTREE_DECODE_COORD(eInt, a, r) { \ |
| RtreeCoord c; \ |
| c.u = __builtin_bswap32(*(u32*)a); \ |
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ |
| } |
| #elif SQLITE_BYTEORDER==1234 |
| #define RTREE_DECODE_COORD(eInt, a, r) { \ |
| RtreeCoord c; \ |
| memcpy(&c.u,a,4); \ |
| c.u = ((c.u>>24)&0xff)|((c.u>>8)&0xff00)| \ |
| ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \ |
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ |
| } |
| #elif SQLITE_BYTEORDER==4321 |
| #define RTREE_DECODE_COORD(eInt, a, r) { \ |
| RtreeCoord c; \ |
| memcpy(&c.u,a,4); \ |
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ |
| } |
| #else |
| #define RTREE_DECODE_COORD(eInt, a, r) { \ |
| RtreeCoord c; \ |
| c.u = ((u32)a[0]<<24) + ((u32)a[1]<<16) \ |
| +((u32)a[2]<<8) + a[3]; \ |
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \ |
| } |
| #endif |
|
|
| |
| |
| |
| |
| static int rtreeCallbackConstraint( |
| RtreeConstraint *pConstraint, |
| int eInt, |
| u8 *pCellData, |
| RtreeSearchPoint *pSearch, |
| sqlite3_rtree_dbl *prScore, |
| int *peWithin |
| ){ |
| sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; |
| int nCoord = pInfo->nCoord; |
| int rc; |
| RtreeCoord c; |
| sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; |
|
|
| assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY ); |
| assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 ); |
|
|
| if( pConstraint->op==RTREE_QUERY && pSearch->iLevel==1 ){ |
| pInfo->iRowid = readInt64(pCellData); |
| } |
| pCellData += 8; |
| #ifndef SQLITE_RTREE_INT_ONLY |
| if( eInt==0 ){ |
| switch( nCoord ){ |
| case 10: readCoord(pCellData+36, &c); aCoord[9] = c.f; |
| readCoord(pCellData+32, &c); aCoord[8] = c.f; |
| case 8: readCoord(pCellData+28, &c); aCoord[7] = c.f; |
| readCoord(pCellData+24, &c); aCoord[6] = c.f; |
| case 6: readCoord(pCellData+20, &c); aCoord[5] = c.f; |
| readCoord(pCellData+16, &c); aCoord[4] = c.f; |
| case 4: readCoord(pCellData+12, &c); aCoord[3] = c.f; |
| readCoord(pCellData+8, &c); aCoord[2] = c.f; |
| default: readCoord(pCellData+4, &c); aCoord[1] = c.f; |
| readCoord(pCellData, &c); aCoord[0] = c.f; |
| } |
| }else |
| #endif |
| { |
| switch( nCoord ){ |
| case 10: readCoord(pCellData+36, &c); aCoord[9] = c.i; |
| readCoord(pCellData+32, &c); aCoord[8] = c.i; |
| case 8: readCoord(pCellData+28, &c); aCoord[7] = c.i; |
| readCoord(pCellData+24, &c); aCoord[6] = c.i; |
| case 6: readCoord(pCellData+20, &c); aCoord[5] = c.i; |
| readCoord(pCellData+16, &c); aCoord[4] = c.i; |
| case 4: readCoord(pCellData+12, &c); aCoord[3] = c.i; |
| readCoord(pCellData+8, &c); aCoord[2] = c.i; |
| default: readCoord(pCellData+4, &c); aCoord[1] = c.i; |
| readCoord(pCellData, &c); aCoord[0] = c.i; |
| } |
| } |
| if( pConstraint->op==RTREE_MATCH ){ |
| int eWithin = 0; |
| rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo, |
| nCoord, aCoord, &eWithin); |
| if( eWithin==0 ) *peWithin = NOT_WITHIN; |
| *prScore = RTREE_ZERO; |
| }else{ |
| pInfo->aCoord = aCoord; |
| pInfo->iLevel = pSearch->iLevel - 1; |
| pInfo->rScore = pInfo->rParentScore = pSearch->rScore; |
| pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin; |
| rc = pConstraint->u.xQueryFunc(pInfo); |
| if( pInfo->eWithin<*peWithin ) *peWithin = pInfo->eWithin; |
| if( pInfo->rScore<*prScore || *prScore<RTREE_ZERO ){ |
| *prScore = pInfo->rScore; |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| static void rtreeNonleafConstraint( |
| RtreeConstraint *p, |
| int eInt, |
| u8 *pCellData, |
| int *peWithin |
| ){ |
| sqlite3_rtree_dbl val; |
|
|
| |
| |
| |
| pCellData += 8 + 4*(p->iCoord&0xfe); |
|
|
| assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE |
| || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE |
| || p->op==RTREE_FALSE ); |
| assert( FOUR_BYTE_ALIGNED(pCellData) ); |
| switch( p->op ){ |
| case RTREE_TRUE: return; |
| case RTREE_FALSE: break; |
| case RTREE_EQ: |
| RTREE_DECODE_COORD(eInt, pCellData, val); |
| |
| if( p->u.rValue>=val ){ |
| pCellData += 4; |
| RTREE_DECODE_COORD(eInt, pCellData, val); |
| |
| if( p->u.rValue<=val ) return; |
| } |
| break; |
| case RTREE_LE: |
| case RTREE_LT: |
| RTREE_DECODE_COORD(eInt, pCellData, val); |
| |
| if( p->u.rValue>=val ) return; |
| break; |
|
|
| default: |
| pCellData += 4; |
| RTREE_DECODE_COORD(eInt, pCellData, val); |
| |
| if( p->u.rValue<=val ) return; |
| break; |
| } |
| *peWithin = NOT_WITHIN; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreeLeafConstraint( |
| RtreeConstraint *p, |
| int eInt, |
| u8 *pCellData, |
| int *peWithin |
| ){ |
| RtreeDValue xN; |
|
|
| assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE |
| || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE |
| || p->op==RTREE_FALSE ); |
| pCellData += 8 + p->iCoord*4; |
| assert( FOUR_BYTE_ALIGNED(pCellData) ); |
| RTREE_DECODE_COORD(eInt, pCellData, xN); |
| switch( p->op ){ |
| case RTREE_TRUE: return; |
| case RTREE_FALSE: break; |
| case RTREE_LE: if( xN <= p->u.rValue ) return; break; |
| case RTREE_LT: if( xN < p->u.rValue ) return; break; |
| case RTREE_GE: if( xN >= p->u.rValue ) return; break; |
| case RTREE_GT: if( xN > p->u.rValue ) return; break; |
| default: if( xN == p->u.rValue ) return; break; |
| } |
| *peWithin = NOT_WITHIN; |
| } |
|
|
| |
| |
| |
| |
| static int nodeRowidIndex( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| i64 iRowid, |
| int *piIndex |
| ){ |
| int ii; |
| int nCell = NCELL(pNode); |
| assert( nCell<200 ); |
| for(ii=0; ii<nCell; ii++){ |
| if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){ |
| *piIndex = ii; |
| return SQLITE_OK; |
| } |
| } |
| RTREE_IS_CORRUPT(pRtree); |
| return SQLITE_CORRUPT_VTAB; |
| } |
|
|
| |
| |
| |
| |
| static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ |
| RtreeNode *pParent = pNode->pParent; |
| if( ALWAYS(pParent) ){ |
| return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); |
| }else{ |
| *piIndex = -1; |
| return SQLITE_OK; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int rtreeSearchPointCompare( |
| const RtreeSearchPoint *pA, |
| const RtreeSearchPoint *pB |
| ){ |
| if( pA->rScore<pB->rScore ) return -1; |
| if( pA->rScore>pB->rScore ) return +1; |
| if( pA->iLevel<pB->iLevel ) return -1; |
| if( pA->iLevel>pB->iLevel ) return +1; |
| return 0; |
| } |
|
|
| |
| |
| |
| static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){ |
| RtreeSearchPoint t = p->aPoint[i]; |
| assert( i<j ); |
| p->aPoint[i] = p->aPoint[j]; |
| p->aPoint[j] = t; |
| i++; j++; |
| if( i<RTREE_CACHE_SZ ){ |
| if( j>=RTREE_CACHE_SZ ){ |
| nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]); |
| p->aNode[i] = 0; |
| }else{ |
| RtreeNode *pTemp = p->aNode[i]; |
| p->aNode[i] = p->aNode[j]; |
| p->aNode[j] = pTemp; |
| } |
| } |
| } |
|
|
| |
| |
| |
| static RtreeSearchPoint *rtreeSearchPointFirst(RtreeCursor *pCur){ |
| return pCur->bPoint ? &pCur->sPoint : pCur->nPoint ? pCur->aPoint : 0; |
| } |
|
|
| |
| |
| |
| static RtreeNode *rtreeNodeOfFirstSearchPoint(RtreeCursor *pCur, int *pRC){ |
| sqlite3_int64 id; |
| int ii = 1 - pCur->bPoint; |
| assert( ii==0 || ii==1 ); |
| assert( pCur->bPoint || pCur->nPoint ); |
| if( pCur->aNode[ii]==0 ){ |
| assert( pRC!=0 ); |
| id = ii ? pCur->aPoint[0].id : pCur->sPoint.id; |
| *pRC = nodeAcquire(RTREE_OF_CURSOR(pCur), id, 0, &pCur->aNode[ii]); |
| } |
| return pCur->aNode[ii]; |
| } |
|
|
| |
| |
| |
| static RtreeSearchPoint *rtreeEnqueue( |
| RtreeCursor *pCur, |
| RtreeDValue rScore, |
| u8 iLevel |
| ){ |
| int i, j; |
| RtreeSearchPoint *pNew; |
| if( pCur->nPoint>=pCur->nPointAlloc ){ |
| int nNew = pCur->nPointAlloc*2 + 8; |
| pNew = sqlite3_realloc64(pCur->aPoint, nNew*sizeof(pCur->aPoint[0])); |
| if( pNew==0 ) return 0; |
| pCur->aPoint = pNew; |
| pCur->nPointAlloc = nNew; |
| } |
| i = pCur->nPoint++; |
| pNew = pCur->aPoint + i; |
| pNew->rScore = rScore; |
| pNew->iLevel = iLevel; |
| assert( iLevel<=RTREE_MAX_DEPTH ); |
| while( i>0 ){ |
| RtreeSearchPoint *pParent; |
| j = (i-1)/2; |
| pParent = pCur->aPoint + j; |
| if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break; |
| rtreeSearchPointSwap(pCur, j, i); |
| i = j; |
| pNew = pParent; |
| } |
| return pNew; |
| } |
|
|
| |
| |
| |
| |
| static RtreeSearchPoint *rtreeSearchPointNew( |
| RtreeCursor *pCur, |
| RtreeDValue rScore, |
| u8 iLevel |
| ){ |
| RtreeSearchPoint *pNew, *pFirst; |
| pFirst = rtreeSearchPointFirst(pCur); |
| pCur->anQueue[iLevel]++; |
| if( pFirst==0 |
| || pFirst->rScore>rScore |
| || (pFirst->rScore==rScore && pFirst->iLevel>iLevel) |
| ){ |
| if( pCur->bPoint ){ |
| int ii; |
| pNew = rtreeEnqueue(pCur, rScore, iLevel); |
| if( pNew==0 ) return 0; |
| ii = (int)(pNew - pCur->aPoint) + 1; |
| assert( ii==1 ); |
| if( ALWAYS(ii<RTREE_CACHE_SZ) ){ |
| assert( pCur->aNode[ii]==0 ); |
| pCur->aNode[ii] = pCur->aNode[0]; |
| }else{ |
| nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]); |
| } |
| pCur->aNode[0] = 0; |
| *pNew = pCur->sPoint; |
| } |
| pCur->sPoint.rScore = rScore; |
| pCur->sPoint.iLevel = iLevel; |
| pCur->bPoint = 1; |
| return &pCur->sPoint; |
| }else{ |
| return rtreeEnqueue(pCur, rScore, iLevel); |
| } |
| } |
|
|
| #if 0 |
| |
| static void tracePoint(RtreeSearchPoint *p, int idx, RtreeCursor *pCur){ |
| if( idx<0 ){ printf(" s"); }else{ printf("%2d", idx); } |
| printf(" %d.%05lld.%02d %g %d", |
| p->iLevel, p->id, p->iCell, p->rScore, p->eWithin |
| ); |
| idx++; |
| if( idx<RTREE_CACHE_SZ ){ |
| printf(" %p\n", pCur->aNode[idx]); |
| }else{ |
| printf("\n"); |
| } |
| } |
| static void traceQueue(RtreeCursor *pCur, const char *zPrefix){ |
| int ii; |
| printf("=== %9s ", zPrefix); |
| if( pCur->bPoint ){ |
| tracePoint(&pCur->sPoint, -1, pCur); |
| } |
| for(ii=0; ii<pCur->nPoint; ii++){ |
| if( ii>0 || pCur->bPoint ) printf(" "); |
| tracePoint(&pCur->aPoint[ii], ii, pCur); |
| } |
| } |
| # define RTREE_QUEUE_TRACE(A,B) traceQueue(A,B) |
| #else |
| # define RTREE_QUEUE_TRACE(A,B) |
| #endif |
|
|
| |
| |
| static void rtreeSearchPointPop(RtreeCursor *p){ |
| int i, j, k, n; |
| i = 1 - p->bPoint; |
| assert( i==0 || i==1 ); |
| if( p->aNode[i] ){ |
| nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]); |
| p->aNode[i] = 0; |
| } |
| if( p->bPoint ){ |
| p->anQueue[p->sPoint.iLevel]--; |
| p->bPoint = 0; |
| }else if( ALWAYS(p->nPoint) ){ |
| p->anQueue[p->aPoint[0].iLevel]--; |
| n = --p->nPoint; |
| p->aPoint[0] = p->aPoint[n]; |
| if( n<RTREE_CACHE_SZ-1 ){ |
| p->aNode[1] = p->aNode[n+1]; |
| p->aNode[n+1] = 0; |
| } |
| i = 0; |
| while( (j = i*2+1)<n ){ |
| k = j+1; |
| if( k<n && rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[j])<0 ){ |
| if( rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[i])<0 ){ |
| rtreeSearchPointSwap(p, i, k); |
| i = k; |
| }else{ |
| break; |
| } |
| }else{ |
| if( rtreeSearchPointCompare(&p->aPoint[j], &p->aPoint[i])<0 ){ |
| rtreeSearchPointSwap(p, i, j); |
| i = j; |
| }else{ |
| break; |
| } |
| } |
| } |
| } |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| static int rtreeStepToLeaf(RtreeCursor *pCur){ |
| RtreeSearchPoint *p; |
| Rtree *pRtree = RTREE_OF_CURSOR(pCur); |
| RtreeNode *pNode; |
| int eWithin; |
| int rc = SQLITE_OK; |
| int nCell; |
| int nConstraint = pCur->nConstraint; |
| int ii; |
| int eInt; |
| RtreeSearchPoint x; |
|
|
| eInt = pRtree->eCoordType==RTREE_COORD_INT32; |
| while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){ |
| u8 *pCellData; |
| pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc); |
| if( rc ) return rc; |
| nCell = NCELL(pNode); |
| assert( nCell<200 ); |
| pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell); |
| while( p->iCell<nCell ){ |
| sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1; |
| eWithin = FULLY_WITHIN; |
| for(ii=0; ii<nConstraint; ii++){ |
| RtreeConstraint *pConstraint = pCur->aConstraint + ii; |
| if( pConstraint->op>=RTREE_MATCH ){ |
| rc = rtreeCallbackConstraint(pConstraint, eInt, pCellData, p, |
| &rScore, &eWithin); |
| if( rc ) return rc; |
| }else if( p->iLevel==1 ){ |
| rtreeLeafConstraint(pConstraint, eInt, pCellData, &eWithin); |
| }else{ |
| rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin); |
| } |
| if( eWithin==NOT_WITHIN ){ |
| p->iCell++; |
| pCellData += pRtree->nBytesPerCell; |
| break; |
| } |
| } |
| if( eWithin==NOT_WITHIN ) continue; |
| p->iCell++; |
| x.iLevel = p->iLevel - 1; |
| if( x.iLevel ){ |
| x.id = readInt64(pCellData); |
| for(ii=0; ii<pCur->nPoint; ii++){ |
| if( pCur->aPoint[ii].id==x.id ){ |
| RTREE_IS_CORRUPT(pRtree); |
| return SQLITE_CORRUPT_VTAB; |
| } |
| } |
| x.iCell = 0; |
| }else{ |
| x.id = p->id; |
| x.iCell = p->iCell - 1; |
| } |
| if( p->iCell>=nCell ){ |
| RTREE_QUEUE_TRACE(pCur, "POP-S:"); |
| rtreeSearchPointPop(pCur); |
| } |
| if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO; |
| p = rtreeSearchPointNew(pCur, rScore, x.iLevel); |
| if( p==0 ) return SQLITE_NOMEM; |
| p->eWithin = (u8)eWithin; |
| p->id = x.id; |
| p->iCell = x.iCell; |
| RTREE_QUEUE_TRACE(pCur, "PUSH-S:"); |
| break; |
| } |
| if( p->iCell>=nCell ){ |
| RTREE_QUEUE_TRACE(pCur, "POP-Se:"); |
| rtreeSearchPointPop(pCur); |
| } |
| } |
| pCur->atEOF = p==0; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ |
| RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; |
| int rc = SQLITE_OK; |
|
|
| |
| RTREE_QUEUE_TRACE(pCsr, "POP-Nx:"); |
| if( pCsr->bAuxValid ){ |
| pCsr->bAuxValid = 0; |
| sqlite3_reset(pCsr->pReadAux); |
| } |
| rtreeSearchPointPop(pCsr); |
| rc = rtreeStepToLeaf(pCsr); |
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ |
| RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; |
| RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); |
| int rc = SQLITE_OK; |
| RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); |
| if( rc==SQLITE_OK && ALWAYS(p) ){ |
| if( p->iCell>=NCELL(pNode) ){ |
| rc = SQLITE_ABORT; |
| }else{ |
| *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ |
| Rtree *pRtree = (Rtree *)cur->pVtab; |
| RtreeCursor *pCsr = (RtreeCursor *)cur; |
| RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); |
| RtreeCoord c; |
| int rc = SQLITE_OK; |
| RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); |
|
|
| if( rc ) return rc; |
| if( NEVER(p==0) ) return SQLITE_OK; |
| if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static int findLeafNode( |
| Rtree *pRtree, |
| i64 iRowid, |
| RtreeNode **ppLeaf, |
| sqlite3_int64 *piNode |
| ){ |
| int rc; |
| *ppLeaf = 0; |
| sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid); |
| if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){ |
| i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0); |
| if( piNode ) *piNode = iNode; |
| rc = nodeAcquire(pRtree, iNode, 0, ppLeaf); |
| sqlite3_reset(pRtree->pReadRowid); |
| }else{ |
| rc = sqlite3_reset(pRtree->pReadRowid); |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ |
| RtreeMatchArg *pBlob, *pSrc; |
| sqlite3_rtree_query_info *pInfo; |
|
|
| pSrc = sqlite3_value_pointer(pValue, "RtreeMatchArg"); |
| if( pSrc==0 ) return SQLITE_ERROR; |
| pInfo = (sqlite3_rtree_query_info*) |
| sqlite3_malloc64( sizeof(*pInfo)+pSrc->iSize ); |
| if( !pInfo ) return SQLITE_NOMEM; |
| memset(pInfo, 0, sizeof(*pInfo)); |
| pBlob = (RtreeMatchArg*)&pInfo[1]; |
| memcpy(pBlob, pSrc, pSrc->iSize); |
| pInfo->pContext = pBlob->cb.pContext; |
| pInfo->nParam = pBlob->nParam; |
| pInfo->aParam = pBlob->aParam; |
| pInfo->apSqlParam = pBlob->apSqlParam; |
|
|
| if( pBlob->cb.xGeom ){ |
| pCons->u.xGeom = pBlob->cb.xGeom; |
| }else{ |
| pCons->op = RTREE_QUERY; |
| pCons->u.xQueryFunc = pBlob->cb.xQueryFunc; |
| } |
| pCons->pInfo = pInfo; |
| return SQLITE_OK; |
| } |
|
|
| int sqlite3IntFloatCompare(i64,double); |
|
|
| |
| |
| |
| static int rtreeFilter( |
| sqlite3_vtab_cursor *pVtabCursor, |
| int idxNum, const char *idxStr, |
| int argc, sqlite3_value **argv |
| ){ |
| Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; |
| RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; |
| RtreeNode *pRoot = 0; |
| int ii; |
| int rc = SQLITE_OK; |
| int iCell = 0; |
|
|
| rtreeReference(pRtree); |
|
|
| |
| resetCursor(pCsr); |
|
|
| pCsr->iStrategy = idxNum; |
| if( idxNum==1 ){ |
| |
| RtreeNode *pLeaf; |
| RtreeSearchPoint *p; |
| i64 iRowid = sqlite3_value_int64(argv[0]); |
| i64 iNode = 0; |
| int eType = sqlite3_value_numeric_type(argv[0]); |
| if( eType==SQLITE_INTEGER |
| || (eType==SQLITE_FLOAT |
| && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) |
| ){ |
| rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); |
| }else{ |
| rc = SQLITE_OK; |
| pLeaf = 0; |
| } |
| if( rc==SQLITE_OK && pLeaf!=0 ){ |
| p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0); |
| assert( p!=0 ); |
| pCsr->aNode[0] = pLeaf; |
| p->id = iNode; |
| p->eWithin = PARTLY_WITHIN; |
| rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell); |
| p->iCell = (u8)iCell; |
| RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:"); |
| }else{ |
| pCsr->atEOF = 1; |
| } |
| }else{ |
| |
| |
| |
| rc = nodeAcquire(pRtree, 1, 0, &pRoot); |
| if( rc==SQLITE_OK && argc>0 ){ |
| pCsr->aConstraint = sqlite3_malloc64(sizeof(RtreeConstraint)*argc); |
| pCsr->nConstraint = argc; |
| if( !pCsr->aConstraint ){ |
| rc = SQLITE_NOMEM; |
| }else{ |
| memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); |
| memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1)); |
| assert( (idxStr==0 && argc==0) |
| || (idxStr && (int)strlen(idxStr)==argc*2) ); |
| for(ii=0; ii<argc; ii++){ |
| RtreeConstraint *p = &pCsr->aConstraint[ii]; |
| int eType = sqlite3_value_numeric_type(argv[ii]); |
| p->op = idxStr[ii*2]; |
| p->iCoord = idxStr[ii*2+1]-'0'; |
| if( p->op>=RTREE_MATCH ){ |
| |
| |
| |
| |
| rc = deserializeGeometry(argv[ii], p); |
| if( rc!=SQLITE_OK ){ |
| break; |
| } |
| p->pInfo->nCoord = pRtree->nDim2; |
| p->pInfo->anQueue = pCsr->anQueue; |
| p->pInfo->mxLevel = pRtree->iDepth + 1; |
| }else if( eType==SQLITE_INTEGER ){ |
| sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]); |
| #ifdef SQLITE_RTREE_INT_ONLY |
| p->u.rValue = iVal; |
| #else |
| p->u.rValue = (double)iVal; |
| if( iVal>=((sqlite3_int64)1)<<48 |
| || iVal<=-(((sqlite3_int64)1)<<48) |
| ){ |
| if( p->op==RTREE_LT ) p->op = RTREE_LE; |
| if( p->op==RTREE_GT ) p->op = RTREE_GE; |
| } |
| #endif |
| }else if( eType==SQLITE_FLOAT ){ |
| #ifdef SQLITE_RTREE_INT_ONLY |
| p->u.rValue = sqlite3_value_int64(argv[ii]); |
| #else |
| p->u.rValue = sqlite3_value_double(argv[ii]); |
| #endif |
| }else{ |
| p->u.rValue = RTREE_ZERO; |
| if( eType==SQLITE_NULL ){ |
| p->op = RTREE_FALSE; |
| }else if( p->op==RTREE_LT || p->op==RTREE_LE ){ |
| p->op = RTREE_TRUE; |
| }else{ |
| p->op = RTREE_FALSE; |
| } |
| } |
| } |
| } |
| } |
| if( rc==SQLITE_OK ){ |
| RtreeSearchPoint *pNew; |
| assert( pCsr->bPoint==0 ); |
| pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); |
| if( NEVER(pNew==0) ){ |
| return SQLITE_NOMEM; |
| } |
| pNew->id = 1; |
| pNew->iCell = 0; |
| pNew->eWithin = PARTLY_WITHIN; |
| assert( pCsr->bPoint==1 ); |
| pCsr->aNode[0] = pRoot; |
| pRoot = 0; |
| RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:"); |
| rc = rtreeStepToLeaf(pCsr); |
| } |
| } |
|
|
| nodeRelease(pRtree, pRoot); |
| rtreeRelease(pRtree); |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
| Rtree *pRtree = (Rtree*)tab; |
| int rc = SQLITE_OK; |
| int ii; |
| int bMatch = 0; |
| i64 nRow; |
|
|
| int iIdx = 0; |
| char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; |
| memset(zIdxStr, 0, sizeof(zIdxStr)); |
|
|
| |
| |
| |
| |
| for(ii=0; ii<pIdxInfo->nConstraint; ii++){ |
| if( pIdxInfo->aConstraint[ii].op==SQLITE_INDEX_CONSTRAINT_MATCH ){ |
| bMatch = 1; |
| } |
| } |
|
|
| assert( pIdxInfo->idxStr==0 ); |
| for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){ |
| struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; |
|
|
| if( bMatch==0 && p->usable |
| && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ |
| ){ |
| |
| int jj; |
| for(jj=0; jj<ii; jj++){ |
| pIdxInfo->aConstraintUsage[jj].argvIndex = 0; |
| pIdxInfo->aConstraintUsage[jj].omit = 0; |
| } |
| pIdxInfo->idxNum = 1; |
| pIdxInfo->aConstraintUsage[ii].argvIndex = 1; |
| pIdxInfo->aConstraintUsage[jj].omit = 1; |
|
|
| |
| |
| |
| |
| |
| |
| pIdxInfo->estimatedCost = 30.0; |
| pIdxInfo->estimatedRows = 1; |
| pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE; |
| return SQLITE_OK; |
| } |
|
|
| if( p->usable |
| && ((p->iColumn>0 && p->iColumn<=pRtree->nDim2) |
| || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) |
| ){ |
| u8 op; |
| u8 doOmit = 1; |
| switch( p->op ){ |
| case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break; |
| case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break; |
| case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; |
| case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break; |
| case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; |
| case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; |
| default: op = 0; break; |
| } |
| if( op ){ |
| zIdxStr[iIdx++] = op; |
| zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); |
| pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); |
| pIdxInfo->aConstraintUsage[ii].omit = doOmit; |
| } |
| } |
| } |
|
|
| pIdxInfo->idxNum = 2; |
| pIdxInfo->needToFreeIdxStr = 1; |
| if( iIdx>0 ){ |
| pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 ); |
| if( pIdxInfo->idxStr==0 ){ |
| return SQLITE_NOMEM; |
| } |
| memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1); |
| } |
|
|
| nRow = pRtree->nRowEst >> (iIdx/2); |
| pIdxInfo->estimatedCost = (double)6.0 * (double)nRow; |
| pIdxInfo->estimatedRows = nRow; |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| RtreeDValue area = (RtreeDValue)1; |
| assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| #ifndef SQLITE_RTREE_INT_ONLY |
| if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ |
| switch( pRtree->nDim ){ |
| case 5: area = p->aCoord[9].f - p->aCoord[8].f; |
| case 4: area *= p->aCoord[7].f - p->aCoord[6].f; |
| case 3: area *= p->aCoord[5].f - p->aCoord[4].f; |
| case 2: area *= p->aCoord[3].f - p->aCoord[2].f; |
| default: area *= p->aCoord[1].f - p->aCoord[0].f; |
| } |
| }else |
| #endif |
| { |
| switch( pRtree->nDim ){ |
| case 5: area = (i64)p->aCoord[9].i - (i64)p->aCoord[8].i; |
| case 4: area *= (i64)p->aCoord[7].i - (i64)p->aCoord[6].i; |
| case 3: area *= (i64)p->aCoord[5].i - (i64)p->aCoord[4].i; |
| case 2: area *= (i64)p->aCoord[3].i - (i64)p->aCoord[2].i; |
| default: area *= (i64)p->aCoord[1].i - (i64)p->aCoord[0].i; |
| } |
| } |
| return area; |
| } |
|
|
| |
| |
| |
| |
| static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){ |
| RtreeDValue margin = 0; |
| int ii = pRtree->nDim2 - 2; |
| do{ |
| margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); |
| ii -= 2; |
| }while( ii>=0 ); |
| return margin; |
| } |
|
|
| |
| |
| |
| static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ |
| int ii = 0; |
| if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ |
| do{ |
| p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f); |
| p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f); |
| ii += 2; |
| }while( ii<pRtree->nDim2 ); |
| }else{ |
| do{ |
| p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i); |
| p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i); |
| ii += 2; |
| }while( ii<pRtree->nDim2 ); |
| } |
| } |
|
|
| |
| |
| |
| |
| static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ |
| int ii; |
| if( pRtree->eCoordType==RTREE_COORD_INT32 ){ |
| for(ii=0; ii<pRtree->nDim2; ii+=2){ |
| RtreeCoord *a1 = &p1->aCoord[ii]; |
| RtreeCoord *a2 = &p2->aCoord[ii]; |
| if( a2[0].i<a1[0].i || a2[1].i>a1[1].i ) return 0; |
| } |
| }else{ |
| for(ii=0; ii<pRtree->nDim2; ii+=2){ |
| RtreeCoord *a1 = &p1->aCoord[ii]; |
| RtreeCoord *a2 = &p2->aCoord[ii]; |
| if( a2[0].f<a1[0].f || a2[1].f>a1[1].f ) return 0; |
| } |
| } |
| return 1; |
| } |
|
|
| static RtreeDValue cellOverlap( |
| Rtree *pRtree, |
| RtreeCell *p, |
| RtreeCell *aCell, |
| int nCell |
| ){ |
| int ii; |
| RtreeDValue overlap = RTREE_ZERO; |
| for(ii=0; ii<nCell; ii++){ |
| int jj; |
| RtreeDValue o = (RtreeDValue)1; |
| for(jj=0; jj<pRtree->nDim2; jj+=2){ |
| RtreeDValue x1, x2; |
| x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); |
| x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); |
| if( x2<x1 ){ |
| o = (RtreeDValue)0; |
| break; |
| }else{ |
| o = o * (x2-x1); |
| } |
| } |
| overlap += o; |
| } |
| return overlap; |
| } |
|
|
|
|
| |
| |
| |
| |
| static int ChooseLeaf( |
| Rtree *pRtree, |
| RtreeCell *pCell, |
| int iHeight, |
| RtreeNode **ppLeaf |
| ){ |
| int rc; |
| int ii; |
| RtreeNode *pNode = 0; |
| rc = nodeAcquire(pRtree, 1, 0, &pNode); |
|
|
| for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){ |
| int iCell; |
| sqlite3_int64 iBest = 0; |
| int bFound = 0; |
| RtreeDValue fMinGrowth = RTREE_ZERO; |
| RtreeDValue fMinArea = RTREE_ZERO; |
| int nCell = NCELL(pNode); |
| RtreeNode *pChild = 0; |
|
|
| |
| |
| |
| |
| for(iCell=0; iCell<nCell; iCell++){ |
| RtreeCell cell; |
| nodeGetCell(pRtree, pNode, iCell, &cell); |
| if( cellContains(pRtree, &cell, pCell) ){ |
| RtreeDValue area = cellArea(pRtree, &cell); |
| if( bFound==0 || area<fMinArea ){ |
| iBest = cell.iRowid; |
| fMinArea = area; |
| bFound = 1; |
| } |
| } |
| } |
| if( !bFound ){ |
| |
| |
| |
| |
| for(iCell=0; iCell<nCell; iCell++){ |
| RtreeCell cell; |
| RtreeDValue growth; |
| RtreeDValue area; |
| nodeGetCell(pRtree, pNode, iCell, &cell); |
| area = cellArea(pRtree, &cell); |
| cellUnion(pRtree, &cell, pCell); |
| growth = cellArea(pRtree, &cell)-area; |
| if( iCell==0 |
| || growth<fMinGrowth |
| || (growth==fMinGrowth && area<fMinArea) |
| ){ |
| fMinGrowth = growth; |
| fMinArea = area; |
| iBest = cell.iRowid; |
| } |
| } |
| } |
|
|
| rc = nodeAcquire(pRtree, iBest, pNode, &pChild); |
| nodeRelease(pRtree, pNode); |
| pNode = pChild; |
| } |
|
|
| *ppLeaf = pNode; |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| static int AdjustTree( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| RtreeCell *pCell |
| ){ |
| RtreeNode *p = pNode; |
| int cnt = 0; |
| int rc; |
| while( p->pParent ){ |
| RtreeNode *pParent = p->pParent; |
| RtreeCell cell; |
| int iCell; |
|
|
| cnt++; |
| if( NEVER(cnt>100) ){ |
| RTREE_IS_CORRUPT(pRtree); |
| return SQLITE_CORRUPT_VTAB; |
| } |
| rc = nodeParentIndex(pRtree, p, &iCell); |
| if( NEVER(rc!=SQLITE_OK) ){ |
| RTREE_IS_CORRUPT(pRtree); |
| return SQLITE_CORRUPT_VTAB; |
| } |
|
|
| nodeGetCell(pRtree, pParent, iCell, &cell); |
| if( !cellContains(pRtree, &cell, pCell) ){ |
| cellUnion(pRtree, &cell, pCell); |
| nodeOverwriteCell(pRtree, pParent, &cell, iCell); |
| } |
| |
| p = pParent; |
| } |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int rowidWrite(Rtree *pRtree, sqlite3_int64 iRowid, sqlite3_int64 iNode){ |
| sqlite3_bind_int64(pRtree->pWriteRowid, 1, iRowid); |
| sqlite3_bind_int64(pRtree->pWriteRowid, 2, iNode); |
| sqlite3_step(pRtree->pWriteRowid); |
| return sqlite3_reset(pRtree->pWriteRowid); |
| } |
|
|
| |
| |
| |
| static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){ |
| sqlite3_bind_int64(pRtree->pWriteParent, 1, iNode); |
| sqlite3_bind_int64(pRtree->pWriteParent, 2, iPar); |
| sqlite3_step(pRtree->pWriteParent); |
| return sqlite3_reset(pRtree->pWriteParent); |
| } |
|
|
| static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int); |
|
|
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void SortByDimension( |
| Rtree *pRtree, |
| int *aIdx, |
| int nIdx, |
| int iDim, |
| RtreeCell *aCell, |
| int *aSpare |
| ){ |
| if( nIdx>1 ){ |
|
|
| int iLeft = 0; |
| int iRight = 0; |
|
|
| int nLeft = nIdx/2; |
| int nRight = nIdx-nLeft; |
| int *aLeft = aIdx; |
| int *aRight = &aIdx[nLeft]; |
|
|
| SortByDimension(pRtree, aLeft, nLeft, iDim, aCell, aSpare); |
| SortByDimension(pRtree, aRight, nRight, iDim, aCell, aSpare); |
|
|
| memcpy(aSpare, aLeft, sizeof(int)*nLeft); |
| aLeft = aSpare; |
| while( iLeft<nLeft || iRight<nRight ){ |
| RtreeDValue xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]); |
| RtreeDValue xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]); |
| RtreeDValue xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]); |
| RtreeDValue xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]); |
| if( (iLeft!=nLeft) && ((iRight==nRight) |
| || (xleft1<xright1) |
| || (xleft1==xright1 && xleft2<xright2) |
| )){ |
| aIdx[iLeft+iRight] = aLeft[iLeft]; |
| iLeft++; |
| }else{ |
| aIdx[iLeft+iRight] = aRight[iRight]; |
| iRight++; |
| } |
| } |
|
|
| #if 0 |
| |
| { |
| int jj; |
| for(jj=1; jj<nIdx; jj++){ |
| RtreeDValue xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2]; |
| RtreeDValue xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1]; |
| RtreeDValue xright1 = aCell[aIdx[jj]].aCoord[iDim*2]; |
| RtreeDValue xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1]; |
| assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) ); |
| } |
| } |
| #endif |
| } |
| } |
|
|
| |
| |
| |
| static int splitNodeStartree( |
| Rtree *pRtree, |
| RtreeCell *aCell, |
| int nCell, |
| RtreeNode *pLeft, |
| RtreeNode *pRight, |
| RtreeCell *pBboxLeft, |
| RtreeCell *pBboxRight |
| ){ |
| int **aaSorted; |
| int *aSpare; |
| int ii; |
|
|
| int iBestDim = 0; |
| int iBestSplit = 0; |
| RtreeDValue fBestMargin = RTREE_ZERO; |
|
|
| sqlite3_int64 nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); |
|
|
| aaSorted = (int **)sqlite3_malloc64(nByte); |
| if( !aaSorted ){ |
| return SQLITE_NOMEM; |
| } |
|
|
| aSpare = &((int *)&aaSorted[pRtree->nDim])[pRtree->nDim*nCell]; |
| memset(aaSorted, 0, nByte); |
| for(ii=0; ii<pRtree->nDim; ii++){ |
| int jj; |
| aaSorted[ii] = &((int *)&aaSorted[pRtree->nDim])[ii*nCell]; |
| for(jj=0; jj<nCell; jj++){ |
| aaSorted[ii][jj] = jj; |
| } |
| SortByDimension(pRtree, aaSorted[ii], nCell, ii, aCell, aSpare); |
| } |
|
|
| for(ii=0; ii<pRtree->nDim; ii++){ |
| RtreeDValue margin = RTREE_ZERO; |
| RtreeDValue fBestOverlap = RTREE_ZERO; |
| RtreeDValue fBestArea = RTREE_ZERO; |
| int iBestLeft = 0; |
| int nLeft; |
|
|
| for( |
| nLeft=RTREE_MINCELLS(pRtree); |
| nLeft<=(nCell-RTREE_MINCELLS(pRtree)); |
| nLeft++ |
| ){ |
| RtreeCell left; |
| RtreeCell right; |
| int kk; |
| RtreeDValue overlap; |
| RtreeDValue area; |
|
|
| memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell)); |
| memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell)); |
| for(kk=1; kk<(nCell-1); kk++){ |
| if( kk<nLeft ){ |
| cellUnion(pRtree, &left, &aCell[aaSorted[ii][kk]]); |
| }else{ |
| cellUnion(pRtree, &right, &aCell[aaSorted[ii][kk]]); |
| } |
| } |
| margin += cellMargin(pRtree, &left); |
| margin += cellMargin(pRtree, &right); |
| overlap = cellOverlap(pRtree, &left, &right, 1); |
| area = cellArea(pRtree, &left) + cellArea(pRtree, &right); |
| if( (nLeft==RTREE_MINCELLS(pRtree)) |
| || (overlap<fBestOverlap) |
| || (overlap==fBestOverlap && area<fBestArea) |
| ){ |
| iBestLeft = nLeft; |
| fBestOverlap = overlap; |
| fBestArea = area; |
| } |
| } |
|
|
| if( ii==0 || margin<fBestMargin ){ |
| iBestDim = ii; |
| fBestMargin = margin; |
| iBestSplit = iBestLeft; |
| } |
| } |
|
|
| memcpy(pBboxLeft, &aCell[aaSorted[iBestDim][0]], sizeof(RtreeCell)); |
| memcpy(pBboxRight, &aCell[aaSorted[iBestDim][iBestSplit]], sizeof(RtreeCell)); |
| for(ii=0; ii<nCell; ii++){ |
| RtreeNode *pTarget = (ii<iBestSplit)?pLeft:pRight; |
| RtreeCell *pBbox = (ii<iBestSplit)?pBboxLeft:pBboxRight; |
| RtreeCell *pCell = &aCell[aaSorted[iBestDim][ii]]; |
| nodeInsertCell(pRtree, pTarget, pCell); |
| cellUnion(pRtree, pBbox, pCell); |
| } |
|
|
| sqlite3_free(aaSorted); |
| return SQLITE_OK; |
| } |
|
|
|
|
| static int updateMapping( |
| Rtree *pRtree, |
| i64 iRowid, |
| RtreeNode *pNode, |
| int iHeight |
| ){ |
| int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64); |
| xSetMapping = ((iHeight==0)?rowidWrite:parentWrite); |
| if( iHeight>0 ){ |
| RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); |
| RtreeNode *p; |
| for(p=pNode; p; p=p->pParent){ |
| if( p==pChild ) return SQLITE_CORRUPT_VTAB; |
| } |
| if( pChild ){ |
| nodeRelease(pRtree, pChild->pParent); |
| nodeReference(pNode); |
| pChild->pParent = pNode; |
| } |
| } |
| if( NEVER(pNode==0) ) return SQLITE_ERROR; |
| return xSetMapping(pRtree, iRowid, pNode->iNode); |
| } |
|
|
| static int SplitNode( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| RtreeCell *pCell, |
| int iHeight |
| ){ |
| int i; |
| int newCellIsRight = 0; |
|
|
| int rc = SQLITE_OK; |
| int nCell = NCELL(pNode); |
| RtreeCell *aCell; |
| int *aiUsed; |
|
|
| RtreeNode *pLeft = 0; |
| RtreeNode *pRight = 0; |
|
|
| RtreeCell leftbbox; |
| RtreeCell rightbbox; |
|
|
| |
| |
| |
| aCell = sqlite3_malloc64((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); |
| if( !aCell ){ |
| rc = SQLITE_NOMEM; |
| goto splitnode_out; |
| } |
| aiUsed = (int *)&aCell[nCell+1]; |
| memset(aiUsed, 0, sizeof(int)*(nCell+1)); |
| for(i=0; i<nCell; i++){ |
| nodeGetCell(pRtree, pNode, i, &aCell[i]); |
| } |
| nodeZero(pRtree, pNode); |
| memcpy(&aCell[nCell], pCell, sizeof(RtreeCell)); |
| nCell++; |
|
|
| if( pNode->iNode==1 ){ |
| pRight = nodeNew(pRtree, pNode); |
| pLeft = nodeNew(pRtree, pNode); |
| pRtree->iDepth++; |
| pNode->isDirty = 1; |
| writeInt16(pNode->zData, pRtree->iDepth); |
| }else{ |
| pLeft = pNode; |
| pRight = nodeNew(pRtree, pLeft->pParent); |
| pLeft->nRef++; |
| } |
|
|
| if( !pLeft || !pRight ){ |
| rc = SQLITE_NOMEM; |
| goto splitnode_out; |
| } |
|
|
| memset(pLeft->zData, 0, pRtree->iNodeSize); |
| memset(pRight->zData, 0, pRtree->iNodeSize); |
|
|
| rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight, |
| &leftbbox, &rightbbox); |
| if( rc!=SQLITE_OK ){ |
| goto splitnode_out; |
| } |
|
|
| |
| |
| |
| |
| |
| if( SQLITE_OK!=(rc = nodeWrite(pRtree, pRight)) |
| || (0==pLeft->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pLeft))) |
| ){ |
| goto splitnode_out; |
| } |
|
|
| rightbbox.iRowid = pRight->iNode; |
| leftbbox.iRowid = pLeft->iNode; |
|
|
| if( pNode->iNode==1 ){ |
| rc = rtreeInsertCell(pRtree, pLeft->pParent, &leftbbox, iHeight+1); |
| if( rc!=SQLITE_OK ){ |
| goto splitnode_out; |
| } |
| }else{ |
| RtreeNode *pParent = pLeft->pParent; |
| int iCell; |
| rc = nodeParentIndex(pRtree, pLeft, &iCell); |
| if( ALWAYS(rc==SQLITE_OK) ){ |
| nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); |
| rc = AdjustTree(pRtree, pParent, &leftbbox); |
| assert( rc==SQLITE_OK ); |
| } |
| if( NEVER(rc!=SQLITE_OK) ){ |
| goto splitnode_out; |
| } |
| } |
| if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){ |
| goto splitnode_out; |
| } |
|
|
| for(i=0; i<NCELL(pRight); i++){ |
| i64 iRowid = nodeGetRowid(pRtree, pRight, i); |
| rc = updateMapping(pRtree, iRowid, pRight, iHeight); |
| if( iRowid==pCell->iRowid ){ |
| newCellIsRight = 1; |
| } |
| if( rc!=SQLITE_OK ){ |
| goto splitnode_out; |
| } |
| } |
| if( pNode->iNode==1 ){ |
| for(i=0; i<NCELL(pLeft); i++){ |
| i64 iRowid = nodeGetRowid(pRtree, pLeft, i); |
| rc = updateMapping(pRtree, iRowid, pLeft, iHeight); |
| if( rc!=SQLITE_OK ){ |
| goto splitnode_out; |
| } |
| } |
| }else if( newCellIsRight==0 ){ |
| rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight); |
| } |
|
|
| if( rc==SQLITE_OK ){ |
| rc = nodeRelease(pRtree, pRight); |
| pRight = 0; |
| } |
| if( rc==SQLITE_OK ){ |
| rc = nodeRelease(pRtree, pLeft); |
| pLeft = 0; |
| } |
|
|
| splitnode_out: |
| nodeRelease(pRtree, pRight); |
| nodeRelease(pRtree, pLeft); |
| sqlite3_free(aCell); |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ |
| int rc = SQLITE_OK; |
| RtreeNode *pChild = pLeaf; |
| while( rc==SQLITE_OK && pChild->iNode!=1 && pChild->pParent==0 ){ |
| int rc2 = SQLITE_OK; |
| sqlite3_bind_int64(pRtree->pReadParent, 1, pChild->iNode); |
| rc = sqlite3_step(pRtree->pReadParent); |
| if( rc==SQLITE_ROW ){ |
| RtreeNode *pTest; |
| i64 iNode; |
|
|
| |
| |
| |
| |
| |
| iNode = sqlite3_column_int64(pRtree->pReadParent, 0); |
| for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); |
| if( pTest==0 ){ |
| rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); |
| } |
| } |
| rc = sqlite3_reset(pRtree->pReadParent); |
| if( rc==SQLITE_OK ) rc = rc2; |
| if( rc==SQLITE_OK && !pChild->pParent ){ |
| RTREE_IS_CORRUPT(pRtree); |
| rc = SQLITE_CORRUPT_VTAB; |
| } |
| pChild = pChild->pParent; |
| } |
| return rc; |
| } |
|
|
| static int deleteCell(Rtree *, RtreeNode *, int, int); |
|
|
| static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ |
| int rc; |
| int rc2; |
| RtreeNode *pParent = 0; |
| int iCell; |
|
|
| assert( pNode->nRef==1 ); |
|
|
| |
| rc = nodeParentIndex(pRtree, pNode, &iCell); |
| if( rc==SQLITE_OK ){ |
| pParent = pNode->pParent; |
| pNode->pParent = 0; |
| rc = deleteCell(pRtree, pParent, iCell, iHeight+1); |
| testcase( rc!=SQLITE_OK ); |
| } |
| rc2 = nodeRelease(pRtree, pParent); |
| if( rc==SQLITE_OK ){ |
| rc = rc2; |
| } |
| if( rc!=SQLITE_OK ){ |
| return rc; |
| } |
|
|
| |
| sqlite3_bind_int64(pRtree->pDeleteNode, 1, pNode->iNode); |
| sqlite3_step(pRtree->pDeleteNode); |
| if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteNode)) ){ |
| return rc; |
| } |
|
|
| |
| sqlite3_bind_int64(pRtree->pDeleteParent, 1, pNode->iNode); |
| sqlite3_step(pRtree->pDeleteParent); |
| if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteParent)) ){ |
| return rc; |
| } |
| |
| |
| |
| |
| nodeHashDelete(pRtree, pNode); |
| pNode->iNode = iHeight; |
| pNode->pNext = pRtree->pDeleted; |
| pNode->nRef++; |
| pRtree->pDeleted = pNode; |
|
|
| return SQLITE_OK; |
| } |
|
|
| static int fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){ |
| RtreeNode *pParent = pNode->pParent; |
| int rc = SQLITE_OK; |
| if( pParent ){ |
| int ii; |
| int nCell = NCELL(pNode); |
| RtreeCell box; |
| nodeGetCell(pRtree, pNode, 0, &box); |
| for(ii=1; ii<nCell; ii++){ |
| RtreeCell cell; |
| nodeGetCell(pRtree, pNode, ii, &cell); |
| cellUnion(pRtree, &box, &cell); |
| } |
| box.iRowid = pNode->iNode; |
| rc = nodeParentIndex(pRtree, pNode, &ii); |
| if( rc==SQLITE_OK ){ |
| nodeOverwriteCell(pRtree, pParent, &box, ii); |
| rc = fixBoundingBox(pRtree, pParent); |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ |
| RtreeNode *pParent; |
| int rc; |
|
|
| if( SQLITE_OK!=(rc = fixLeafParent(pRtree, pNode)) ){ |
| return rc; |
| } |
|
|
| |
| |
| |
| nodeDeleteCell(pRtree, pNode, iCell); |
|
|
| |
| |
| |
| |
| |
| pParent = pNode->pParent; |
| assert( pParent || pNode->iNode==1 ); |
| if( pParent ){ |
| if( NCELL(pNode)<RTREE_MINCELLS(pRtree) ){ |
| rc = removeNode(pRtree, pNode, iHeight); |
| }else{ |
| rc = fixBoundingBox(pRtree, pNode); |
| } |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| |
| static int rtreeInsertCell( |
| Rtree *pRtree, |
| RtreeNode *pNode, |
| RtreeCell *pCell, |
| int iHeight |
| ){ |
| int rc = SQLITE_OK; |
| if( iHeight>0 ){ |
| RtreeNode *pChild = nodeHashLookup(pRtree, pCell->iRowid); |
| if( pChild ){ |
| nodeRelease(pRtree, pChild->pParent); |
| nodeReference(pNode); |
| pChild->pParent = pNode; |
| } |
| } |
| if( nodeInsertCell(pRtree, pNode, pCell) ){ |
| rc = SplitNode(pRtree, pNode, pCell, iHeight); |
| }else{ |
| rc = AdjustTree(pRtree, pNode, pCell); |
| if( ALWAYS(rc==SQLITE_OK) ){ |
| if( iHeight==0 ){ |
| rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); |
| }else{ |
| rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode); |
| } |
| } |
| } |
| return rc; |
| } |
|
|
| static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){ |
| int ii; |
| int rc = SQLITE_OK; |
| int nCell = NCELL(pNode); |
|
|
| for(ii=0; rc==SQLITE_OK && ii<nCell; ii++){ |
| RtreeNode *pInsert; |
| RtreeCell cell; |
| nodeGetCell(pRtree, pNode, ii, &cell); |
|
|
| |
| |
| |
| rc = ChooseLeaf(pRtree, &cell, (int)pNode->iNode, &pInsert); |
| if( rc==SQLITE_OK ){ |
| int rc2; |
| rc = rtreeInsertCell(pRtree, pInsert, &cell, (int)pNode->iNode); |
| rc2 = nodeRelease(pRtree, pInsert); |
| if( rc==SQLITE_OK ){ |
| rc = rc2; |
| } |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeNewRowid(Rtree *pRtree, i64 *piRowid){ |
| int rc; |
| sqlite3_bind_null(pRtree->pWriteRowid, 1); |
| sqlite3_bind_null(pRtree->pWriteRowid, 2); |
| sqlite3_step(pRtree->pWriteRowid); |
| rc = sqlite3_reset(pRtree->pWriteRowid); |
| *piRowid = sqlite3_last_insert_rowid(pRtree->db); |
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ |
| int rc; |
| RtreeNode *pLeaf = 0; |
| int iCell; |
| RtreeNode *pRoot = 0; |
|
|
|
|
| |
| rc = nodeAcquire(pRtree, 1, 0, &pRoot); |
|
|
| |
| |
| |
| if( rc==SQLITE_OK ){ |
| rc = findLeafNode(pRtree, iDelete, &pLeaf, 0); |
| } |
|
|
| #ifdef CORRUPT_DB |
| assert( pLeaf!=0 || rc!=SQLITE_OK || CORRUPT_DB ); |
| #endif |
|
|
| |
| if( rc==SQLITE_OK && pLeaf ){ |
| int rc2; |
| rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); |
| if( rc==SQLITE_OK ){ |
| rc = deleteCell(pRtree, pLeaf, iCell, 0); |
| } |
| rc2 = nodeRelease(pRtree, pLeaf); |
| if( rc==SQLITE_OK ){ |
| rc = rc2; |
| } |
| } |
|
|
| |
| if( rc==SQLITE_OK ){ |
| sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete); |
| sqlite3_step(pRtree->pDeleteRowid); |
| rc = sqlite3_reset(pRtree->pDeleteRowid); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ |
| int rc2; |
| RtreeNode *pChild = 0; |
| i64 iChild = nodeGetRowid(pRtree, pRoot, 0); |
| rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); |
| if( rc==SQLITE_OK ){ |
| rc = removeNode(pRtree, pChild, pRtree->iDepth-1); |
| } |
| rc2 = nodeRelease(pRtree, pChild); |
| if( rc==SQLITE_OK ) rc = rc2; |
| if( rc==SQLITE_OK ){ |
| pRtree->iDepth--; |
| writeInt16(pRoot->zData, pRtree->iDepth); |
| pRoot->isDirty = 1; |
| } |
| } |
|
|
| |
| for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ |
| if( rc==SQLITE_OK ){ |
| rc = reinsertNodeContent(pRtree, pLeaf); |
| } |
| pRtree->pDeleted = pLeaf->pNext; |
| pRtree->nNodeRef--; |
| sqlite3_free(pLeaf); |
| } |
|
|
| |
| if( rc==SQLITE_OK ){ |
| rc = nodeRelease(pRtree, pRoot); |
| }else{ |
| nodeRelease(pRtree, pRoot); |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| #define RNDTOWARDS (1.0 - 1.0/8388608.0) |
| #define RNDAWAY (1.0 + 1.0/8388608.0) |
|
|
| #if !defined(SQLITE_RTREE_INT_ONLY) |
| |
| |
| |
| |
| static RtreeValue rtreeValueDown(sqlite3_value *v){ |
| double d = sqlite3_value_double(v); |
| float f = (float)d; |
| if( f>d ){ |
| f = (float)(d*(d<0 ? RNDAWAY : RNDTOWARDS)); |
| } |
| return f; |
| } |
| static RtreeValue rtreeValueUp(sqlite3_value *v){ |
| double d = sqlite3_value_double(v); |
| float f = (float)d; |
| if( f<d ){ |
| f = (float)(d*(d<0 ? RNDTOWARDS : RNDAWAY)); |
| } |
| return f; |
| } |
| #endif |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int rtreeConstraintError(Rtree *pRtree, int iCol){ |
| sqlite3_stmt *pStmt = 0; |
| char *zSql; |
| int rc; |
|
|
| assert( iCol==0 || iCol%2 ); |
| zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", pRtree->zDb, pRtree->zName); |
| if( zSql ){ |
| rc = sqlite3_prepare_v2(pRtree->db, zSql, -1, &pStmt, 0); |
| }else{ |
| rc = SQLITE_NOMEM; |
| } |
| sqlite3_free(zSql); |
|
|
| if( rc==SQLITE_OK ){ |
| if( iCol==0 ){ |
| const char *zCol = sqlite3_column_name(pStmt, 0); |
| pRtree->base.zErrMsg = sqlite3_mprintf( |
| "UNIQUE constraint failed: %s.%s", pRtree->zName, zCol |
| ); |
| }else{ |
| const char *zCol1 = sqlite3_column_name(pStmt, iCol); |
| const char *zCol2 = sqlite3_column_name(pStmt, iCol+1); |
| pRtree->base.zErrMsg = sqlite3_mprintf( |
| "rtree constraint failed: %s.(%s<=%s)", pRtree->zName, zCol1, zCol2 |
| ); |
| } |
| } |
|
|
| sqlite3_finalize(pStmt); |
| return (rc==SQLITE_OK ? SQLITE_CONSTRAINT : rc); |
| } |
|
|
|
|
|
|
| |
| |
| |
| static int rtreeUpdate( |
| sqlite3_vtab *pVtab, |
| int nData, |
| sqlite3_value **aData, |
| sqlite_int64 *pRowid |
| ){ |
| Rtree *pRtree = (Rtree *)pVtab; |
| int rc = SQLITE_OK; |
| RtreeCell cell; |
| int bHaveRowid = 0; |
|
|
| if( pRtree->nNodeRef ){ |
| |
| |
| |
| return SQLITE_LOCKED_VTAB; |
| } |
| rtreeReference(pRtree); |
| assert(nData>=1); |
|
|
| memset(&cell, 0, sizeof(cell)); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if( nData>1 ){ |
| int ii; |
| int nn = nData - 4; |
|
|
| if( nn > pRtree->nDim2 ) nn = pRtree->nDim2; |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #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( 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( sqlite3_value_type(aData[0])!=SQLITE_NULL ){ |
| rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0])); |
| } |
|
|
| |
| |
| |
| |
| if( rc==SQLITE_OK && nData>1 ){ |
| |
| RtreeNode *pLeaf = 0; |
|
|
| |
| if( bHaveRowid==0 ){ |
| rc = rtreeNewRowid(pRtree, &cell.iRowid); |
| } |
| *pRowid = cell.iRowid; |
|
|
| if( rc==SQLITE_OK ){ |
| rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf); |
| } |
| if( rc==SQLITE_OK ){ |
| int rc2; |
| rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); |
| rc2 = nodeRelease(pRtree, pLeaf); |
| if( rc==SQLITE_OK ){ |
| rc = rc2; |
| } |
| } |
| if( rc==SQLITE_OK && 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; |
| } |
|
|
| |
| |
| |
| static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ |
| Rtree *pRtree = (Rtree *)pVtab; |
| assert( pRtree->inWrTrans==0 ); |
| pRtree->inWrTrans = 1; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| static int rtreeEndTransaction(sqlite3_vtab *pVtab){ |
| Rtree *pRtree = (Rtree *)pVtab; |
| pRtree->inWrTrans = 0; |
| nodeBlobReset(pRtree); |
| return SQLITE_OK; |
| } |
| static int rtreeRollback(sqlite3_vtab *pVtab){ |
| return rtreeEndTransaction(pVtab); |
| } |
|
|
| |
| |
| |
| static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ |
| Rtree *pRtree = (Rtree *)pVtab; |
| int rc = SQLITE_NOMEM; |
| char *zSql = sqlite3_mprintf( |
| "ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";" |
| "ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";" |
| "ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";" |
| , pRtree->zDb, pRtree->zName, zNewName |
| , pRtree->zDb, pRtree->zName, zNewName |
| , pRtree->zDb, pRtree->zName, zNewName |
| ); |
| if( zSql ){ |
| nodeBlobReset(pRtree); |
| rc = sqlite3_exec(pRtree->db, zSql, 0, 0, 0); |
| sqlite3_free(zSql); |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){ |
| Rtree *pRtree = (Rtree *)pVtab; |
| u8 iwt = pRtree->inWrTrans; |
| UNUSED_PARAMETER(iSavepoint); |
| pRtree->inWrTrans = 0; |
| nodeBlobReset(pRtree); |
| pRtree->inWrTrans = iwt; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ |
| const char *zFmt = "SELECT stat FROM %Q.sqlite_stat1 WHERE tbl = '%q_rowid'"; |
| char *zSql; |
| sqlite3_stmt *p; |
| int rc; |
| i64 nRow = RTREE_MIN_ROWEST; |
|
|
| rc = sqlite3_table_column_metadata( |
| db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 |
| ); |
| if( rc!=SQLITE_OK ){ |
| pRtree->nRowEst = RTREE_DEFAULT_ROWEST; |
| return rc==SQLITE_ERROR ? SQLITE_OK : rc; |
| } |
| zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName); |
| if( zSql==0 ){ |
| rc = SQLITE_NOMEM; |
| }else{ |
| rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0); |
| if( rc==SQLITE_OK ){ |
| if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0); |
| rc = sqlite3_finalize(p); |
| } |
| sqlite3_free(zSql); |
| } |
| pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); |
| return rc; |
| } |
|
|
|
|
| |
| |
| |
| |
| static int rtreeShadowName(const char *zName){ |
| static const char *azName[] = { |
| "node", "parent", "rowid" |
| }; |
| unsigned int i; |
| for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){ |
| if( sqlite3_stricmp(zName, azName[i])==0 ) return 1; |
| } |
| return 0; |
| } |
|
|
| |
| static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**); |
|
|
| static sqlite3_module rtreeModule = { |
| 4, |
| rtreeCreate, |
| rtreeConnect, |
| rtreeBestIndex, |
| rtreeDisconnect, |
| rtreeDestroy, |
| rtreeOpen, |
| rtreeClose, |
| rtreeFilter, |
| rtreeNext, |
| rtreeEof, |
| rtreeColumn, |
| rtreeRowid, |
| rtreeUpdate, |
| rtreeBeginTransaction, |
| rtreeEndTransaction, |
| rtreeEndTransaction, |
| rtreeRollback, |
| 0, |
| rtreeRename, |
| rtreeSavepoint, |
| 0, |
| 0, |
| rtreeShadowName, |
| rtreeIntegrity |
| }; |
|
|
| static int rtreeSqlInit( |
| Rtree *pRtree, |
| sqlite3 *db, |
| const char *zDb, |
| const char *zPrefix, |
| int isCreate |
| ){ |
| int rc = SQLITE_OK; |
|
|
| #define N_STATEMENT 8 |
| static const char *azSql[N_STATEMENT] = { |
| |
| "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)", |
| "DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1", |
|
|
| |
| "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", |
|
|
| |
| "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; |
| const int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; |
|
|
| 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; |
| } |
| } |
|
|
| appStmt[0] = &pRtree->pWriteNode; |
| appStmt[1] = &pRtree->pDeleteNode; |
| appStmt[2] = &pRtree->pReadRowid; |
| appStmt[3] = &pRtree->pWriteRowid; |
| 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 { |
| |
| |
| 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, f, appStmt[i], 0); |
| }else{ |
| rc = SQLITE_NOMEM; |
| } |
| sqlite3_free(zSql); |
| } |
| if( pRtree->nAux && rc!=SQLITE_NOMEM ){ |
| 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); |
| #ifdef SQLITE_ENABLE_GEOPOLY |
| if( ii<pRtree->nAuxNotNull ){ |
| sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); |
| }else |
| #endif |
| { |
| 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, f, &pRtree->pWriteAux, 0); |
| sqlite3_free(zSql); |
| } |
| } |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){ |
| int rc = SQLITE_NOMEM; |
| if( zSql ){ |
| sqlite3_stmt *pStmt = 0; |
| rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| if( rc==SQLITE_OK ){ |
| if( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| *piVal = sqlite3_column_int(pStmt, 0); |
| } |
| rc = sqlite3_finalize(pStmt); |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int getNodeSize( |
| sqlite3 *db, |
| Rtree *pRtree, |
| int isCreate, |
| char **pzErr |
| ){ |
| int rc; |
| char *zSql; |
| if( isCreate ){ |
| int iPageSize = 0; |
| zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb); |
| rc = getIntFromStmt(db, zSql, &iPageSize); |
| if( rc==SQLITE_OK ){ |
| pRtree->iNodeSize = iPageSize-64; |
| if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| } |
| }else{ |
| *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| } |
| }else{ |
| zSql = sqlite3_mprintf( |
| "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| pRtree->zDb, pRtree->zName |
| ); |
| rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| if( rc!=SQLITE_OK ){ |
| *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| }else if( pRtree->iNodeSize<(512-64) ){ |
| rc = SQLITE_CORRUPT_VTAB; |
| RTREE_IS_CORRUPT(pRtree); |
| *pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"", |
| pRtree->zName); |
| } |
| } |
|
|
| sqlite3_free(zSql); |
| return rc; |
| } |
|
|
| |
| |
| |
| static int rtreeTokenLength(const char *z){ |
| int dummy = 0; |
| return sqlite3GetToken((const unsigned char*)z,&dummy); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int rtreeInit( |
| sqlite3 *db, |
| void *pAux, |
| int argc, const char *const*argv, |
| sqlite3_vtab **ppVtab, |
| char **pzErr, |
| int isCreate |
| ){ |
| int rc = SQLITE_OK; |
| Rtree *pRtree; |
| int nDb; |
| int nName; |
| int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); |
| sqlite3_str *pSql; |
| char *zSql; |
| int ii = 4; |
| int iErr; |
|
|
| const char *aErrMsg[] = { |
| 0, |
| "Wrong number of columns for an rtree table", |
| "Too few columns for an rtree table", |
| "Too many columns for an rtree table", |
| "Auxiliary rtree columns must be last" |
| }; |
|
|
| assert( RTREE_MAX_AUX_COLUMN<256 ); |
| if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){ |
| *pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]); |
| return SQLITE_ERROR; |
| } |
|
|
| sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); |
| sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); |
|
|
|
|
| |
| nDb = (int)strlen(argv[1]); |
| nName = (int)strlen(argv[2]); |
| pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); |
| if( !pRtree ){ |
| return SQLITE_NOMEM; |
| } |
| memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); |
| pRtree->nBusy = 1; |
| pRtree->base.pModule = &rtreeModule; |
| pRtree->zDb = (char *)&pRtree[1]; |
| pRtree->zName = &pRtree->zDb[nDb+1]; |
| pRtree->zNodeName = &pRtree->zName[nName+1]; |
| pRtree->eCoordType = (u8)eCoordType; |
| memcpy(pRtree->zDb, argv[1], nDb); |
| memcpy(pRtree->zName, argv[2], nName); |
| memcpy(pRtree->zNodeName, argv[2], nName); |
| memcpy(&pRtree->zNodeName[nName], "_node", 6); |
|
|
|
|
| |
| |
| |
| |
| pSql = sqlite3_str_new(db); |
| sqlite3_str_appendf(pSql, "CREATE TABLE x(%.*s INT", |
| rtreeTokenLength(argv[3]), argv[3]); |
| for(ii=4; ii<argc; ii++){ |
| const char *zArg = argv[ii]; |
| if( zArg[0]=='+' ){ |
| pRtree->nAux++; |
| sqlite3_str_appendf(pSql, ",%.*s", rtreeTokenLength(zArg+1), zArg+1); |
| }else if( pRtree->nAux>0 ){ |
| break; |
| }else{ |
| static const char *azFormat[] = {",%.*s REAL", ",%.*s INT"}; |
| pRtree->nDim2++; |
| sqlite3_str_appendf(pSql, azFormat[eCoordType], |
| rtreeTokenLength(zArg), zArg); |
| } |
| } |
| 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; |
|
|
| |
| 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; |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ |
| RtreeNode node; |
| Rtree tree; |
| int ii; |
| int nData; |
| int errCode; |
| sqlite3_str *pOut; |
|
|
| UNUSED_PARAMETER(nArg); |
| memset(&node, 0, sizeof(RtreeNode)); |
| memset(&tree, 0, sizeof(Rtree)); |
| tree.nDim = (u8)sqlite3_value_int(apArg[0]); |
| if( tree.nDim<1 || tree.nDim>5 ) return; |
| tree.nDim2 = tree.nDim*2; |
| tree.nBytesPerCell = 8 + 8 * tree.nDim; |
| node.zData = (u8 *)sqlite3_value_blob(apArg[1]); |
| if( node.zData==0 ) return; |
| nData = sqlite3_value_bytes(apArg[1]); |
| if( nData<4 ) return; |
| if( nData<NCELL(&node)*tree.nBytesPerCell ) return; |
|
|
| pOut = sqlite3_str_new(0); |
| for(ii=0; ii<NCELL(&node); ii++){ |
| RtreeCell cell; |
| int jj; |
|
|
| nodeGetCell(&tree, &node, ii, &cell); |
| if( ii>0 ) sqlite3_str_append(pOut, " ", 1); |
| sqlite3_str_appendf(pOut, "{%lld", cell.iRowid); |
| for(jj=0; jj<tree.nDim2; jj++){ |
| #ifndef SQLITE_RTREE_INT_ONLY |
| sqlite3_str_appendf(pOut, " %g", (double)cell.aCoord[jj].f); |
| #else |
| sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i); |
| #endif |
| } |
| sqlite3_str_append(pOut, "}", 1); |
| } |
| errCode = sqlite3_str_errcode(pOut); |
| sqlite3_result_error_code(ctx, errCode); |
| sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ |
| UNUSED_PARAMETER(nArg); |
| if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB |
| || sqlite3_value_bytes(apArg[0])<2 |
|
|
| ){ |
| sqlite3_result_error(ctx, "Invalid argument to rtreedepth()", -1); |
| }else{ |
| u8 *zBlob = (u8 *)sqlite3_value_blob(apArg[0]); |
| if( zBlob ){ |
| sqlite3_result_int(ctx, readInt16(zBlob)); |
| }else{ |
| sqlite3_result_error_nomem(ctx); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| typedef struct RtreeCheck RtreeCheck; |
| struct RtreeCheck { |
| sqlite3 *db; |
| const char *zDb; |
| const char *zTab; |
| int bInt; |
| int nDim; |
| sqlite3_stmt *pGetNode; |
| sqlite3_stmt *aCheckMapping[2]; |
| int nLeaf; |
| int nNonLeaf; |
| int rc; |
| char *zReport; |
| int nErr; |
| }; |
|
|
| #define RTREE_CHECK_MAX_ERROR 100 |
|
|
| |
| |
| |
| |
| static void rtreeCheckReset(RtreeCheck *pCheck, sqlite3_stmt *pStmt){ |
| int rc = sqlite3_reset(pStmt); |
| if( pCheck->rc==SQLITE_OK ) pCheck->rc = rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| static sqlite3_stmt *rtreeCheckPrepare( |
| RtreeCheck *pCheck, |
| const char *zFmt, ... |
| ){ |
| va_list ap; |
| char *z; |
| sqlite3_stmt *pRet = 0; |
|
|
| va_start(ap, zFmt); |
| z = sqlite3_vmprintf(zFmt, ap); |
|
|
| if( pCheck->rc==SQLITE_OK ){ |
| if( z==0 ){ |
| pCheck->rc = SQLITE_NOMEM; |
| }else{ |
| pCheck->rc = sqlite3_prepare_v2(pCheck->db, z, -1, &pRet, 0); |
| } |
| } |
|
|
| sqlite3_free(z); |
| va_end(ap); |
| return pRet; |
| } |
|
|
| |
| |
| |
| |
| |
| static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| va_list ap; |
| va_start(ap, zFmt); |
| if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| char *z = sqlite3_vmprintf(zFmt, ap); |
| if( z==0 ){ |
| pCheck->rc = SQLITE_NOMEM; |
| }else{ |
| pCheck->zReport = sqlite3_mprintf("%z%s%z", |
| pCheck->zReport, (pCheck->zReport ? "\n" : ""), z |
| ); |
| if( pCheck->zReport==0 ){ |
| pCheck->rc = SQLITE_NOMEM; |
| } |
| } |
| pCheck->nErr++; |
| } |
| va_end(ap); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){ |
| u8 *pRet = 0; |
|
|
| if( pCheck->rc==SQLITE_OK && pCheck->pGetNode==0 ){ |
| pCheck->pGetNode = rtreeCheckPrepare(pCheck, |
| "SELECT data FROM %Q.'%q_node' WHERE nodeno=?", |
| pCheck->zDb, pCheck->zTab |
| ); |
| } |
|
|
| if( pCheck->rc==SQLITE_OK ){ |
| sqlite3_bind_int64(pCheck->pGetNode, 1, iNode); |
| if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){ |
| int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0); |
| const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0); |
| pRet = sqlite3_malloc64(nNode); |
| if( pRet==0 ){ |
| pCheck->rc = SQLITE_NOMEM; |
| }else{ |
| memcpy(pRet, pNode, nNode); |
| *pnNode = nNode; |
| } |
| } |
| rtreeCheckReset(pCheck, pCheck->pGetNode); |
| if( pCheck->rc==SQLITE_OK && pRet==0 ){ |
| rtreeCheckAppendMsg(pCheck, "Node %lld missing from database", iNode); |
| } |
| } |
|
|
| return pRet; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreeCheckMapping( |
| RtreeCheck *pCheck, |
| int bLeaf, |
| i64 iKey, |
| i64 iVal |
| ){ |
| 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 |
| ); |
| } |
| if( pCheck->rc!=SQLITE_OK ) return; |
|
|
| pStmt = pCheck->aCheckMapping[bLeaf]; |
| sqlite3_bind_int64(pStmt, 1, iKey); |
| rc = sqlite3_step(pStmt); |
| if( rc==SQLITE_DONE ){ |
| rtreeCheckAppendMsg(pCheck, "Mapping (%lld -> %lld) missing from %s table", |
| iKey, iVal, (bLeaf ? "%_rowid" : "%_parent") |
| ); |
| }else if( rc==SQLITE_ROW ){ |
| i64 ii = sqlite3_column_int64(pStmt, 0); |
| if( ii!=iVal ){ |
| rtreeCheckAppendMsg(pCheck, |
| "Found (%lld -> %lld) in %s table, expected (%lld -> %lld)", |
| iKey, ii, (bLeaf ? "%_rowid" : "%_parent"), iKey, iVal |
| ); |
| } |
| } |
| rtreeCheckReset(pCheck, pStmt); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreeCheckCellCoord( |
| RtreeCheck *pCheck, |
| i64 iNode, |
| int iCell, |
| u8 *pCell, |
| u8 *pParent |
| ){ |
| RtreeCoord c1, c2; |
| RtreeCoord p1, p2; |
| int i; |
|
|
| for(i=0; i<pCheck->nDim; i++){ |
| readCoord(&pCell[4*2*i], &c1); |
| readCoord(&pCell[4*(2*i + 1)], &c2); |
|
|
| |
| if( pCheck->bInt ? c1.i>c2.i : c1.f>c2.f ){ |
| rtreeCheckAppendMsg(pCheck, |
| "Dimension %d of cell %d on node %lld is corrupt", i, iCell, iNode |
| ); |
| } |
|
|
| if( pParent ){ |
| readCoord(&pParent[4*2*i], &p1); |
| readCoord(&pParent[4*(2*i + 1)], &p2); |
|
|
| if( (pCheck->bInt ? c1.i<p1.i : c1.f<p1.f) |
| || (pCheck->bInt ? c2.i>p2.i : c2.f>p2.f) |
| ){ |
| rtreeCheckAppendMsg(pCheck, |
| "Dimension %d of cell %d on node %lld is corrupt relative to parent" |
| , i, iCell, iNode |
| ); |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreeCheckNode( |
| RtreeCheck *pCheck, |
| int iDepth, |
| u8 *aParent, |
| i64 iNode |
| ){ |
| u8 *aNode = 0; |
| int nNode = 0; |
|
|
| assert( iNode==1 || aParent!=0 ); |
| assert( pCheck->nDim>0 ); |
|
|
| aNode = rtreeCheckGetNode(pCheck, iNode, &nNode); |
| if( aNode ){ |
| if( nNode<4 ){ |
| rtreeCheckAppendMsg(pCheck, |
| "Node %lld is too small (%d bytes)", iNode, nNode |
| ); |
| }else{ |
| int nCell; |
| int i; |
| if( aParent==0 ){ |
| iDepth = readInt16(aNode); |
| if( iDepth>RTREE_MAX_DEPTH ){ |
| rtreeCheckAppendMsg(pCheck, "Rtree depth out of range (%d)", iDepth); |
| sqlite3_free(aNode); |
| return; |
| } |
| } |
| nCell = readInt16(&aNode[2]); |
| if( (4 + nCell*(8 + pCheck->nDim*2*4))>nNode ){ |
| rtreeCheckAppendMsg(pCheck, |
| "Node %lld is too small for cell count of %d (%d bytes)", |
| iNode, nCell, nNode |
| ); |
| }else{ |
| for(i=0; i<nCell; i++){ |
| u8 *pCell = &aNode[4 + i*(8 + pCheck->nDim*2*4)]; |
| i64 iVal = readInt64(pCell); |
| rtreeCheckCellCoord(pCheck, iNode, i, &pCell[8], aParent); |
|
|
| if( iDepth>0 ){ |
| rtreeCheckMapping(pCheck, 0, iVal, iNode); |
| rtreeCheckNode(pCheck, iDepth-1, &pCell[8], iVal); |
| pCheck->nNonLeaf++; |
| }else{ |
| rtreeCheckMapping(pCheck, 1, iVal, iNode); |
| pCheck->nLeaf++; |
| } |
| } |
| } |
| } |
| sqlite3_free(aNode); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static void rtreeCheckCount(RtreeCheck *pCheck, const char *zTbl, i64 nExpect){ |
| if( pCheck->rc==SQLITE_OK ){ |
| sqlite3_stmt *pCount; |
| pCount = rtreeCheckPrepare(pCheck, "SELECT count(*) FROM %Q.'%q%s'", |
| pCheck->zDb, pCheck->zTab, zTbl |
| ); |
| if( pCount ){ |
| if( sqlite3_step(pCount)==SQLITE_ROW ){ |
| i64 nActual = sqlite3_column_int64(pCount, 0); |
| if( nActual!=nExpect ){ |
| rtreeCheckAppendMsg(pCheck, "Wrong number of entries in %%%s table" |
| " - expected %lld, actual %lld" , zTbl, nExpect, nActual |
| ); |
| } |
| } |
| pCheck->rc = sqlite3_finalize(pCount); |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| static int rtreeCheckTable( |
| sqlite3 *db, |
| const char *zDb, |
| const char *zTab, |
| char **pzReport |
| ){ |
| RtreeCheck check; |
| sqlite3_stmt *pStmt = 0; |
| int nAux = 0; |
|
|
| |
| memset(&check, 0, sizeof(check)); |
| check.db = db; |
| check.zDb = zDb; |
| check.zTab = zTab; |
|
|
| |
| pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab); |
| if( pStmt ){ |
| nAux = sqlite3_column_count(pStmt) - 2; |
| sqlite3_finalize(pStmt); |
| }else |
| if( check.rc!=SQLITE_NOMEM ){ |
| check.rc = SQLITE_OK; |
| } |
|
|
| |
| 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; |
| } |
|
|
| |
| if( check.nDim>=1 ){ |
| if( check.rc==SQLITE_OK ){ |
| rtreeCheckNode(&check, 0, 0, 1); |
| } |
| rtreeCheckCount(&check, "_rowid", check.nLeaf); |
| rtreeCheckCount(&check, "_parent", check.nNonLeaf); |
| } |
|
|
| |
| sqlite3_finalize(check.pGetNode); |
| sqlite3_finalize(check.aCheckMapping[0]); |
| sqlite3_finalize(check.aCheckMapping[1]); |
|
|
| *pzReport = check.zReport; |
| return check.rc; |
| } |
|
|
| |
| |
| |
| static int rtreeIntegrity( |
| sqlite3_vtab *pVtab, |
| const char *zSchema, |
| const char *zName, |
| int isQuick, |
| char **pzErr |
| ){ |
| Rtree *pRtree = (Rtree*)pVtab; |
| int rc; |
| assert( pzErr!=0 && *pzErr==0 ); |
| UNUSED_PARAMETER(zSchema); |
| UNUSED_PARAMETER(zName); |
| UNUSED_PARAMETER(isQuick); |
| rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr); |
| if( rc==SQLITE_OK && *pzErr ){ |
| *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", |
| pRtree->zDb, pRtree->zName, *pzErr); |
| if( (*pzErr)==0 ) rc = SQLITE_NOMEM; |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void rtreecheck( |
| sqlite3_context *ctx, |
| int nArg, |
| sqlite3_value **apArg |
| ){ |
| if( nArg!=1 && nArg!=2 ){ |
| sqlite3_result_error(ctx, |
| "wrong number of arguments to function rtreecheck()", -1 |
| ); |
| }else{ |
| int rc; |
| char *zReport = 0; |
| const char *zDb = (const char*)sqlite3_value_text(apArg[0]); |
| const char *zTab; |
| if( nArg==1 ){ |
| zTab = zDb; |
| zDb = "main"; |
| }else{ |
| zTab = (const char*)sqlite3_value_text(apArg[1]); |
| } |
| rc = rtreeCheckTable(sqlite3_context_db_handle(ctx), zDb, zTab, &zReport); |
| if( rc==SQLITE_OK ){ |
| sqlite3_result_text(ctx, zReport ? zReport : "ok", -1, SQLITE_TRANSIENT); |
| }else{ |
| sqlite3_result_error_code(ctx, rc); |
| } |
| sqlite3_free(zReport); |
| } |
| } |
|
|
| |
| #ifdef SQLITE_ENABLE_GEOPOLY |
| # include "geopoly.c" |
| #endif |
|
|
| |
| |
| |
| |
| |
| int sqlite3RtreeInit(sqlite3 *db){ |
| const int utf8 = SQLITE_UTF8; |
| int rc; |
|
|
| 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); |
| } |
| 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); |
| } |
| #ifdef SQLITE_ENABLE_GEOPOLY |
| if( rc==SQLITE_OK ){ |
| rc = sqlite3_geopoly_init(db); |
| } |
| #endif |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static void rtreeFreeCallback(void *p){ |
| RtreeGeomCallback *pInfo = (RtreeGeomCallback*)p; |
| if( pInfo->xDestructor ) pInfo->xDestructor(pInfo->pContext); |
| sqlite3_free(p); |
| } |
|
|
| |
| |
| |
| static void rtreeMatchArgFree(void *pArg){ |
| int i; |
| RtreeMatchArg *p = (RtreeMatchArg*)pArg; |
| for(i=0; i<p->nParam; i++){ |
| sqlite3_value_free(p->apSqlParam[i]); |
| } |
| sqlite3_free(p); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ |
| RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| RtreeMatchArg *pBlob; |
| sqlite3_int64 nBlob; |
| int memErr = 0; |
|
|
| nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*); |
| pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| if( !pBlob ){ |
| sqlite3_result_error_nomem(ctx); |
| }else{ |
| int i; |
| pBlob->iSize = nBlob; |
| pBlob->cb = pGeomCtx[0]; |
| pBlob->apSqlParam = (sqlite3_value**)&pBlob->aParam[nArg]; |
| pBlob->nParam = nArg; |
| for(i=0; i<nArg; i++){ |
| pBlob->apSqlParam[i] = sqlite3_value_dup(aArg[i]); |
| if( pBlob->apSqlParam[i]==0 ) memErr = 1; |
| #ifdef SQLITE_RTREE_INT_ONLY |
| pBlob->aParam[i] = sqlite3_value_int64(aArg[i]); |
| #else |
| pBlob->aParam[i] = sqlite3_value_double(aArg[i]); |
| #endif |
| } |
| if( memErr ){ |
| sqlite3_result_error_nomem(ctx); |
| rtreeMatchArgFree(pBlob); |
| }else{ |
| sqlite3_result_pointer(ctx, pBlob, "RtreeMatchArg", rtreeMatchArgFree); |
| } |
| } |
| } |
|
|
| |
| |
| |
| int sqlite3_rtree_geometry_callback( |
| sqlite3 *db, |
| const char *zGeom, |
| int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), |
| void *pContext |
| ){ |
| RtreeGeomCallback *pGeomCtx; |
|
|
| |
| pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); |
| if( !pGeomCtx ) return SQLITE_NOMEM; |
| pGeomCtx->xGeom = xGeom; |
| pGeomCtx->xQueryFunc = 0; |
| pGeomCtx->xDestructor = 0; |
| pGeomCtx->pContext = pContext; |
| return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY, |
| (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback |
| ); |
| } |
|
|
| |
| |
| |
| |
| int sqlite3_rtree_query_callback( |
| sqlite3 *db, |
| const char *zQueryFunc, |
| int (*xQueryFunc)(sqlite3_rtree_query_info*), |
| void *pContext, |
| void (*xDestructor)(void*) |
| ){ |
| RtreeGeomCallback *pGeomCtx; |
|
|
| |
| pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); |
| if( !pGeomCtx ){ |
| if( xDestructor ) xDestructor(pContext); |
| return SQLITE_NOMEM; |
| } |
| pGeomCtx->xGeom = 0; |
| pGeomCtx->xQueryFunc = xQueryFunc; |
| pGeomCtx->xDestructor = xDestructor; |
| pGeomCtx->pContext = pContext; |
| return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY, |
| (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback |
| ); |
| } |
|
|
| #ifndef SQLITE_CORE |
| #ifdef _WIN32 |
| __declspec(dllexport) |
| #endif |
| int sqlite3_rtree_init( |
| sqlite3 *db, |
| char **pzErrMsg, |
| const sqlite3_api_routines *pApi |
| ){ |
| SQLITE_EXTENSION_INIT2(pApi) |
| return sqlite3RtreeInit(db); |
| } |
| #endif |
|
|
| #endif |
|
|