| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #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 |
| |
|