| Index: third_party/sqlite/src/ext/rtree/rtree.c
|
| diff --git a/third_party/sqlite/src/ext/rtree/rtree.c b/third_party/sqlite/src/ext/rtree/rtree.c
|
| index 4e473a22c28a45fd0a2ad9df0099b40c99397197..205939ddc6bf648946e23f6cdc4ef000fff8587a 100644
|
| --- a/third_party/sqlite/src/ext/rtree/rtree.c
|
| +++ b/third_party/sqlite/src/ext/rtree/rtree.c
|
| @@ -68,6 +68,7 @@
|
| #ifndef 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;
|
| @@ -116,13 +117,16 @@ struct Rtree {
|
| sqlite3 *db; /* Host database connection */
|
| int iNodeSize; /* Size in bytes of each node in the node table */
|
| u8 nDim; /* Number of dimensions */
|
| + u8 nDim2; /* Twice the number of dimensions */
|
| u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
|
| u8 nBytesPerCell; /* Bytes consumed per cell */
|
| + u8 inWrTrans; /* True if inside write transaction */
|
| int iDepth; /* Current depth of the r-tree structure */
|
| char *zDb; /* Name of database containing r-tree table */
|
| char *zName; /* Name of r-tree table */
|
| - int nBusy; /* Current number of users of this structure */
|
| + u32 nBusy; /* Current number of users of this structure */
|
| i64 nRowEst; /* Estimated number of rows in this table */
|
| + u32 nCursor; /* Number of open cursors */
|
|
|
| /* List of nodes removed during a CondenseTree operation. List is
|
| ** linked together via the pointer normally used for hash chains -
|
| @@ -132,8 +136,10 @@ struct Rtree {
|
| RtreeNode *pDeleted;
|
| int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
|
|
|
| + /* Blob I/O on xxx_node */
|
| + sqlite3_blob *pNodeBlob;
|
| +
|
| /* Statements to read/write/delete a record from xxx_node */
|
| - sqlite3_stmt *pReadNode;
|
| sqlite3_stmt *pWriteNode;
|
| sqlite3_stmt *pDeleteNode;
|
|
|
| @@ -362,6 +368,64 @@ struct RtreeMatchArg {
|
| # define MIN(x,y) ((x) > (y) ? (y) : (x))
|
| #endif
|
|
|
| +/* What version of GCC is being used. 0 means GCC is not being used */
|
| +#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
|
| +
|
| +/* What version of CLANG is being used. 0 means CLANG is not being used */
|
| +#ifndef CLANG_VERSION
|
| +#if defined(__clang__) && !defined(_WIN32) && !defined(SQLITE_DISABLE_INTRINSIC)
|
| +# define CLANG_VERSION \
|
| + (__clang_major__*1000000+__clang_minor__*1000+__clang_patchlevel__)
|
| +#else
|
| +# define CLANG_VERSION 0
|
| +#endif
|
| +#endif
|
| +
|
| +/* The testcase() macro should already be defined in the amalgamation. If
|
| +** it is not, make it a no-op.
|
| +*/
|
| +#ifndef SQLITE_AMALGAMATION
|
| +# define testcase(X)
|
| +#endif
|
| +
|
| +/*
|
| +** Macros to determine whether the machine is big or little endian,
|
| +** and whether or not that determination is run-time or compile-time.
|
| +**
|
| +** For best performance, an attempt is made to guess at the byte-order
|
| +** using C-preprocessor macros. If that is unsuccessful, or if
|
| +** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
|
| +** at run-time.
|
| +*/
|
| +#ifndef SQLITE_BYTEORDER
|
| +#if 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(__arm__)
|
| +# define SQLITE_BYTEORDER 1234
|
| +#elif defined(sparc) || defined(__ppc__)
|
| +# define SQLITE_BYTEORDER 4321
|
| +#else
|
| +# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
|
| +#endif
|
| +#endif
|
| +
|
| +
|
| +/* What version of MSVC is being used. 0 means MSVC is not being used */
|
| +#ifndef MSVC_VERSION
|
| +#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
|
| +# define MSVC_VERSION _MSC_VER
|
| +#else
|
| +# define MSVC_VERSION 0
|
| +#endif
|
| +#endif
|
| +
|
| /*
|
| ** Functions to deserialize a 16 bit integer, 32 bit real number and
|
| ** 64 bit integer. The deserialized value is returned.
|
| @@ -370,14 +434,36 @@ static int readInt16(u8 *p){
|
| return (p[0]<<8) + p[1];
|
| }
|
| static void readCoord(u8 *p, RtreeCoord *pCoord){
|
| + assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
|
| +#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
| + pCoord->u = _byteswap_ulong(*(u32*)p);
|
| +#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
| + 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 || CLANG_VERSION>=3000000)
|
| + 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)p[0]) << 56) +
|
| (((i64)p[1]) << 48) +
|
| @@ -388,6 +474,7 @@ static i64 readInt64(u8 *p){
|
| (((i64)p[6]) << 8) +
|
| (((i64)p[7]) << 0)
|
| );
|
| +#endif
|
| }
|
|
|
| /*
|
| @@ -395,23 +482,43 @@ static i64 readInt64(u8 *p){
|
| ** 64 bit integer. The value returned is the number of bytes written
|
| ** to the argument buffer (always 2, 4 and 8 respectively).
|
| */
|
| -static int writeInt16(u8 *p, int i){
|
| +static void writeInt16(u8 *p, int i){
|
| p[0] = (i>> 8)&0xFF;
|
| p[1] = (i>> 0)&0xFF;
|
| - return 2;
|
| }
|
| static int writeCoord(u8 *p, RtreeCoord *pCoord){
|
| u32 i;
|
| + assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
|
| assert( sizeof(RtreeCoord)==4 );
|
| assert( sizeof(u32)==4 );
|
| +#if SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
| + 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 || CLANG_VERSION>=3000000)
|
| + 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;
|
| @@ -420,6 +527,7 @@ static int writeInt64(u8 *p, i64 i){
|
| p[5] = (i>>16)&0xFF;
|
| p[6] = (i>> 8)&0xFF;
|
| p[7] = (i>> 0)&0xFF;
|
| +#endif
|
| return 8;
|
| }
|
|
|
| @@ -503,6 +611,17 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
|
| }
|
|
|
| /*
|
| +** Clear the Rtree.pNodeBlob object
|
| +*/
|
| +static void nodeBlobReset(Rtree *pRtree){
|
| + if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
|
| + sqlite3_blob *pBlob = pRtree->pNodeBlob;
|
| + pRtree->pNodeBlob = 0;
|
| + sqlite3_blob_close(pBlob);
|
| + }
|
| +}
|
| +
|
| +/*
|
| ** Obtain a reference to an r-tree node.
|
| */
|
| static int nodeAcquire(
|
| @@ -511,9 +630,8 @@ static int nodeAcquire(
|
| RtreeNode *pParent, /* Either the parent node or NULL */
|
| RtreeNode **ppNode /* OUT: Acquired node */
|
| ){
|
| - int rc;
|
| - int rc2 = SQLITE_OK;
|
| - RtreeNode *pNode;
|
| + int rc = SQLITE_OK;
|
| + RtreeNode *pNode = 0;
|
|
|
| /* Check if the requested node is already in the hash table. If so,
|
| ** increase its reference count and return it.
|
| @@ -529,28 +647,45 @@ static int nodeAcquire(
|
| return SQLITE_OK;
|
| }
|
|
|
| - sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
|
| - rc = sqlite3_step(pRtree->pReadNode);
|
| - if( rc==SQLITE_ROW ){
|
| - const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);
|
| - if( pRtree->iNodeSize==sqlite3_column_bytes(pRtree->pReadNode, 0) ){
|
| - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
|
| - if( !pNode ){
|
| - rc2 = SQLITE_NOMEM;
|
| - }else{
|
| - pNode->pParent = pParent;
|
| - pNode->zData = (u8 *)&pNode[1];
|
| - pNode->nRef = 1;
|
| - pNode->iNode = iNode;
|
| - pNode->isDirty = 0;
|
| - pNode->pNext = 0;
|
| - memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
|
| - nodeReference(pParent);
|
| - }
|
| + 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 ){
|
| + char *zTab = sqlite3_mprintf("%s_node", pRtree->zName);
|
| + if( zTab==0 ) return SQLITE_NOMEM;
|
| + rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0,
|
| + &pRtree->pNodeBlob);
|
| + sqlite3_free(zTab);
|
| + }
|
| + if( rc ){
|
| + nodeBlobReset(pRtree);
|
| + *ppNode = 0;
|
| + /* If unable to open an sqlite3_blob on the desired row, that can only
|
| + ** be because the shadow tables hold erroneous data. */
|
| + if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB;
|
| + }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){
|
| + pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
|
| + if( !pNode ){
|
| + rc = SQLITE_NOMEM;
|
| + }else{
|
| + pNode->pParent = pParent;
|
| + pNode->zData = (u8 *)&pNode[1];
|
| + pNode->nRef = 1;
|
| + pNode->iNode = iNode;
|
| + pNode->isDirty = 0;
|
| + pNode->pNext = 0;
|
| + rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData,
|
| + pRtree->iNodeSize, 0);
|
| + nodeReference(pParent);
|
| }
|
| }
|
| - rc = sqlite3_reset(pRtree->pReadNode);
|
| - if( rc==SQLITE_OK ) rc = rc2;
|
|
|
| /* If the root node was just loaded, set pRtree->iDepth to the height
|
| ** of the r-tree structure. A height of zero means all data is stored on
|
| @@ -602,7 +737,7 @@ static void nodeOverwriteCell(
|
| int ii;
|
| u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
|
| p += writeInt64(p, pCell->iRowid);
|
| - for(ii=0; ii<(pRtree->nDim*2); ii++){
|
| + for(ii=0; ii<pRtree->nDim2; ii++){
|
| p += writeCoord(p, &pCell->aCoord[ii]);
|
| }
|
| pNode->isDirty = 1;
|
| @@ -736,13 +871,16 @@ static void nodeGetCell(
|
| ){
|
| u8 *pData;
|
| RtreeCoord *pCoord;
|
| - int ii;
|
| + int ii = 0;
|
| pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
|
| pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell);
|
| pCoord = pCell->aCoord;
|
| - for(ii=0; ii<pRtree->nDim*2; ii++){
|
| - readCoord(&pData[ii*4], &pCoord[ii]);
|
| - }
|
| + do{
|
| + readCoord(pData, &pCoord[ii]);
|
| + readCoord(pData+4, &pCoord[ii+1]);
|
| + pData += 8;
|
| + ii += 2;
|
| + }while( ii<pRtree->nDim2 );
|
| }
|
|
|
|
|
| @@ -793,7 +931,9 @@ static void rtreeReference(Rtree *pRtree){
|
| static void rtreeRelease(Rtree *pRtree){
|
| pRtree->nBusy--;
|
| if( pRtree->nBusy==0 ){
|
| - sqlite3_finalize(pRtree->pReadNode);
|
| + pRtree->inWrTrans = 0;
|
| + pRtree->nCursor = 0;
|
| + nodeBlobReset(pRtree);
|
| sqlite3_finalize(pRtree->pWriteNode);
|
| sqlite3_finalize(pRtree->pDeleteNode);
|
| sqlite3_finalize(pRtree->pReadRowid);
|
| @@ -831,6 +971,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
|
| if( !zCreate ){
|
| rc = SQLITE_NOMEM;
|
| }else{
|
| + nodeBlobReset(pRtree);
|
| rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0);
|
| sqlite3_free(zCreate);
|
| }
|
| @@ -846,6 +987,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
|
| */
|
| static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
| int rc = SQLITE_NOMEM;
|
| + Rtree *pRtree = (Rtree *)pVTab;
|
| RtreeCursor *pCsr;
|
|
|
| pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor));
|
| @@ -853,6 +995,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
| memset(pCsr, 0, sizeof(RtreeCursor));
|
| pCsr->base.pVtab = pVTab;
|
| rc = SQLITE_OK;
|
| + pRtree->nCursor++;
|
| }
|
| *ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
|
|
| @@ -885,10 +1028,13 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
|
| Rtree *pRtree = (Rtree *)(cur->pVtab);
|
| int ii;
|
| RtreeCursor *pCsr = (RtreeCursor *)cur;
|
| + assert( pRtree->nCursor>0 );
|
| freeCursorConstraints(pCsr);
|
| sqlite3_free(pCsr->aPoint);
|
| for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
| sqlite3_free(pCsr);
|
| + pRtree->nCursor--;
|
| + nodeBlobReset(pRtree);
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -911,15 +1057,22 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
|
| ** false. a[] is the four bytes of the on-disk record to be decoded.
|
| ** Store the results in "r".
|
| **
|
| -** There are three versions of this macro, one each for little-endian and
|
| -** big-endian processors and a third generic implementation. The endian-
|
| -** specific implementations are much faster and are preferred if the
|
| -** processor endianness is known at compile-time. The SQLITE_BYTEORDER
|
| -** macro is part of sqliteInt.h and hence the endian-specific
|
| -** implementation will only be used if this module is compiled as part
|
| -** of the amalgamation.
|
| -*/
|
| -#if defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==1234
|
| +** There are five versions of this macro. The last one is generic. The
|
| +** other four are various architectures-specific optimizations.
|
| +*/
|
| +#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
| +#define RTREE_DECODE_COORD(eInt, a, r) { \
|
| + RtreeCoord c; /* Coordinate decoded */ \
|
| + 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 || CLANG_VERSION>=3000000)
|
| +#define RTREE_DECODE_COORD(eInt, a, r) { \
|
| + RtreeCoord c; /* Coordinate decoded */ \
|
| + 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; /* Coordinate decoded */ \
|
| memcpy(&c.u,a,4); \
|
| @@ -927,7 +1080,7 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
|
| ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \
|
| r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
|
| }
|
| -#elif defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==4321
|
| +#elif SQLITE_BYTEORDER==4321
|
| #define RTREE_DECODE_COORD(eInt, a, r) { \
|
| RtreeCoord c; /* Coordinate decoded */ \
|
| memcpy(&c.u,a,4); \
|
| @@ -954,10 +1107,10 @@ static int rtreeCallbackConstraint(
|
| sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */
|
| int *peWithin /* OUT: visibility of the cell */
|
| ){
|
| - int i; /* Loop counter */
|
| sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
|
| int nCoord = pInfo->nCoord; /* No. of coordinates */
|
| int rc; /* Callback return code */
|
| + RtreeCoord c; /* Translator union */
|
| sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */
|
|
|
| assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY );
|
| @@ -967,13 +1120,41 @@ static int rtreeCallbackConstraint(
|
| pInfo->iRowid = readInt64(pCellData);
|
| }
|
| pCellData += 8;
|
| - for(i=0; i<nCoord; i++, pCellData += 4){
|
| - RTREE_DECODE_COORD(eInt, pCellData, aCoord[i]);
|
| +#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, &i);
|
| - if( i==0 ) *peWithin = NOT_WITHIN;
|
| + nCoord, aCoord, &eWithin);
|
| + if( eWithin==0 ) *peWithin = NOT_WITHIN;
|
| *prScore = RTREE_ZERO;
|
| }else{
|
| pInfo->aCoord = aCoord;
|
| @@ -1009,6 +1190,7 @@ static void rtreeNonleafConstraint(
|
|
|
| assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
| || p->op==RTREE_GT || p->op==RTREE_EQ );
|
| + assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
| switch( p->op ){
|
| case RTREE_LE:
|
| case RTREE_LT:
|
| @@ -1049,6 +1231,7 @@ static void rtreeLeafConstraint(
|
| assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
| || p->op==RTREE_GT || p->op==RTREE_EQ );
|
| pCellData += 8 + p->iCoord*4;
|
| + assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
| RTREE_DECODE_COORD(eInt, pCellData, xN);
|
| switch( p->op ){
|
| case RTREE_LE: if( xN <= p->u.rValue ) return; break;
|
| @@ -1117,7 +1300,7 @@ static int rtreeSearchPointCompare(
|
| }
|
|
|
| /*
|
| -** Interchange to search points in a cursor.
|
| +** Interchange two search points in a cursor.
|
| */
|
| static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){
|
| RtreeSearchPoint t = p->aPoint[i];
|
| @@ -1365,7 +1548,7 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
|
| if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO;
|
| p = rtreeSearchPointNew(pCur, rScore, x.iLevel);
|
| if( p==0 ) return SQLITE_NOMEM;
|
| - p->eWithin = eWithin;
|
| + p->eWithin = (u8)eWithin;
|
| p->id = x.id;
|
| p->iCell = x.iCell;
|
| RTREE_QUEUE_TRACE(pCur, "PUSH-S:");
|
| @@ -1424,7 +1607,6 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
| if( i==0 ){
|
| sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
|
| }else{
|
| - if( rc ) return rc;
|
| nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
|
| #ifndef SQLITE_RTREE_INT_ONLY
|
| if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
| @@ -1542,7 +1724,7 @@ static int rtreeFilter(
|
| if( idxNum==1 ){
|
| /* Special case - lookup by rowid. */
|
| RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
| - RtreeSearchPoint *p; /* Search point for the the leaf */
|
| + RtreeSearchPoint *p; /* Search point for the leaf */
|
| i64 iRowid = sqlite3_value_int64(argv[0]);
|
| i64 iNode = 0;
|
| rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
| @@ -1553,7 +1735,7 @@ static int rtreeFilter(
|
| p->id = iNode;
|
| p->eWithin = PARTLY_WITHIN;
|
| rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
|
| - p->iCell = iCell;
|
| + p->iCell = (u8)iCell;
|
| RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
|
| }else{
|
| pCsr->atEOF = 1;
|
| @@ -1586,7 +1768,7 @@ static int rtreeFilter(
|
| if( rc!=SQLITE_OK ){
|
| break;
|
| }
|
| - p->pInfo->nCoord = pRtree->nDim*2;
|
| + p->pInfo->nCoord = pRtree->nDim2;
|
| p->pInfo->anQueue = pCsr->anQueue;
|
| p->pInfo->mxLevel = pRtree->iDepth + 1;
|
| }else{
|
| @@ -1601,7 +1783,7 @@ static int rtreeFilter(
|
| }
|
| if( rc==SQLITE_OK ){
|
| RtreeSearchPoint *pNew;
|
| - pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1);
|
| + pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
|
| if( pNew==0 ) return SQLITE_NOMEM;
|
| pNew->id = 1;
|
| pNew->iCell = 0;
|
| @@ -1620,19 +1802,6 @@ static int rtreeFilter(
|
| }
|
|
|
| /*
|
| -** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
|
| -** extension is currently being used by a version of SQLite too old to
|
| -** support estimatedRows. In that case this function is a no-op.
|
| -*/
|
| -static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
| -#if SQLITE_VERSION_NUMBER>=3008002
|
| - if( sqlite3_libversion_number()>=3008002 ){
|
| - pIdxInfo->estimatedRows = nRow;
|
| - }
|
| -#endif
|
| -}
|
| -
|
| -/*
|
| ** Rtree virtual table module xBestIndex method. There are three
|
| ** table scan strategies to choose from (in order from most to
|
| ** least desirable):
|
| @@ -1711,7 +1880,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
| ** a single row.
|
| */
|
| pIdxInfo->estimatedCost = 30.0;
|
| - setEstimatedRows(pIdxInfo, 1);
|
| + pIdxInfo->estimatedRows = 1;
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -1729,7 +1898,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
| break;
|
| }
|
| zIdxStr[iIdx++] = op;
|
| - zIdxStr[iIdx++] = p->iColumn - 1 + '0';
|
| + zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0');
|
| pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
|
| pIdxInfo->aConstraintUsage[ii].omit = 1;
|
| }
|
| @@ -1741,9 +1910,9 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
| return SQLITE_NOMEM;
|
| }
|
|
|
| - nRow = pRtree->nRowEst / (iIdx + 1);
|
| + nRow = pRtree->nRowEst >> (iIdx/2);
|
| pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
|
| - setEstimatedRows(pIdxInfo, nRow);
|
| + pIdxInfo->estimatedRows = nRow;
|
|
|
| return rc;
|
| }
|
| @@ -1753,9 +1922,26 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
| */
|
| static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
|
| RtreeDValue area = (RtreeDValue)1;
|
| - int ii;
|
| - for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
| - area = (area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
|
| + 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 = p->aCoord[9].i - p->aCoord[8].i;
|
| + case 4: area *= p->aCoord[7].i - p->aCoord[6].i;
|
| + case 3: area *= p->aCoord[5].i - p->aCoord[4].i;
|
| + case 2: area *= p->aCoord[3].i - p->aCoord[2].i;
|
| + default: area *= p->aCoord[1].i - p->aCoord[0].i;
|
| + }
|
| }
|
| return area;
|
| }
|
| @@ -1765,11 +1951,12 @@ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
|
| ** of the objects size in each dimension.
|
| */
|
| static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
|
| - RtreeDValue margin = (RtreeDValue)0;
|
| - int ii;
|
| - for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
| + 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;
|
| }
|
|
|
| @@ -1777,17 +1964,19 @@ static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
|
| ** Store the union of cells p1 and p2 in p1.
|
| */
|
| static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
| - int ii;
|
| + int ii = 0;
|
| if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
| - for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
| + 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{
|
| - for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
| + 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 );
|
| }
|
| }
|
|
|
| @@ -1798,7 +1987,7 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
| static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
| int ii;
|
| int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
|
| - for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
| + for(ii=0; ii<pRtree->nDim2; ii+=2){
|
| RtreeCoord *a1 = &p1->aCoord[ii];
|
| RtreeCoord *a2 = &p2->aCoord[ii];
|
| if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
|
| @@ -1833,7 +2022,7 @@ static RtreeDValue cellOverlap(
|
| for(ii=0; ii<nCell; ii++){
|
| int jj;
|
| RtreeDValue o = (RtreeDValue)1;
|
| - for(jj=0; jj<(pRtree->nDim*2); jj+=2){
|
| + 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]));
|
| @@ -2800,6 +2989,53 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){
|
| }
|
| #endif /* !defined(SQLITE_RTREE_INT_ONLY) */
|
|
|
| +/*
|
| +** A constraint has failed while inserting a row into an rtree table.
|
| +** Assuming no OOM error occurs, this function sets the error message
|
| +** (at pRtree->base.zErrMsg) to an appropriate value and returns
|
| +** SQLITE_CONSTRAINT.
|
| +**
|
| +** Parameter iCol is the index of the leftmost column involved in the
|
| +** constraint failure. If it is 0, then the constraint that failed is
|
| +** the unique constraint on the id column. Otherwise, it is the rtree
|
| +** (c1<=c2) constraint on columns iCol and iCol+1 that has failed.
|
| +**
|
| +** If an OOM occurs, SQLITE_NOMEM is returned instead of SQLITE_CONSTRAINT.
|
| +*/
|
| +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);
|
| +}
|
| +
|
| +
|
|
|
| /*
|
| ** The xUpdate method for rtree module virtual tables.
|
| @@ -2842,7 +3078,7 @@ static int rtreeUpdate(
|
| ** This problem was discovered after years of use, so we silently ignore
|
| ** these kinds of misdeclared tables to avoid breaking any legacy.
|
| */
|
| - assert( nData<=(pRtree->nDim*2 + 3) );
|
| + assert( nData<=(pRtree->nDim2 + 3) );
|
|
|
| #ifndef SQLITE_RTREE_INT_ONLY
|
| if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
| @@ -2850,7 +3086,7 @@ static int rtreeUpdate(
|
| cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
|
| cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
|
| if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
|
| - rc = SQLITE_CONSTRAINT;
|
| + rc = rtreeConstraintError(pRtree, ii+1);
|
| goto constraint;
|
| }
|
| }
|
| @@ -2861,7 +3097,7 @@ static int rtreeUpdate(
|
| cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
|
| cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
|
| if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
|
| - rc = SQLITE_CONSTRAINT;
|
| + rc = rtreeConstraintError(pRtree, ii+1);
|
| goto constraint;
|
| }
|
| }
|
| @@ -2882,7 +3118,7 @@ static int rtreeUpdate(
|
| if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
|
| rc = rtreeDeleteRowid(pRtree, cell.iRowid);
|
| }else{
|
| - rc = SQLITE_CONSTRAINT;
|
| + rc = rtreeConstraintError(pRtree, 0);
|
| goto constraint;
|
| }
|
| }
|
| @@ -2933,6 +3169,27 @@ constraint:
|
| }
|
|
|
| /*
|
| +** Called when a transaction starts.
|
| +*/
|
| +static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
|
| + Rtree *pRtree = (Rtree *)pVtab;
|
| + assert( pRtree->inWrTrans==0 );
|
| + pRtree->inWrTrans++;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Called when a transaction completes (either by COMMIT or ROLLBACK).
|
| +** The sqlite3_blob object should be released at this point.
|
| +*/
|
| +static int rtreeEndTransaction(sqlite3_vtab *pVtab){
|
| + Rtree *pRtree = (Rtree *)pVtab;
|
| + pRtree->inWrTrans = 0;
|
| + nodeBlobReset(pRtree);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| ** The xRename method for rtree module virtual tables.
|
| */
|
| static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
| @@ -2953,6 +3210,7 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
| return rc;
|
| }
|
|
|
| +
|
| /*
|
| ** This function populates the pRtree->nRowEst variable with an estimate
|
| ** of the number of rows in the virtual table. If possible, this is based
|
| @@ -2965,6 +3223,13 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
|
| int rc;
|
| i64 nRow = 0;
|
|
|
| + 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;
|
| @@ -3005,15 +3270,15 @@ static sqlite3_module rtreeModule = {
|
| rtreeColumn, /* xColumn - read data */
|
| rtreeRowid, /* xRowid - read data */
|
| rtreeUpdate, /* xUpdate - write data */
|
| - 0, /* xBegin - begin transaction */
|
| - 0, /* xSync - sync transaction */
|
| - 0, /* xCommit - commit transaction */
|
| - 0, /* xRollback - rollback transaction */
|
| + rtreeBeginTransaction, /* xBegin - begin transaction */
|
| + rtreeEndTransaction, /* xSync - sync transaction */
|
| + rtreeEndTransaction, /* xCommit - commit transaction */
|
| + rtreeEndTransaction, /* xRollback - rollback transaction */
|
| 0, /* xFindFunction - function overloading */
|
| rtreeRename, /* xRename - rename the table */
|
| 0, /* xSavepoint */
|
| 0, /* xRelease */
|
| - 0 /* xRollbackTo */
|
| + 0, /* xRollbackTo */
|
| };
|
|
|
| static int rtreeSqlInit(
|
| @@ -3025,10 +3290,9 @@ static int rtreeSqlInit(
|
| ){
|
| int rc = SQLITE_OK;
|
|
|
| - #define N_STATEMENT 9
|
| + #define N_STATEMENT 8
|
| static const char *azSql[N_STATEMENT] = {
|
| - /* Read and write the xxx_node table */
|
| - "SELECT data FROM '%q'.'%q_node' WHERE nodeno = :1",
|
| + /* Write the xxx_node table */
|
| "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
|
| "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
|
|
|
| @@ -3066,15 +3330,14 @@ static int rtreeSqlInit(
|
| }
|
| }
|
|
|
| - appStmt[0] = &pRtree->pReadNode;
|
| - appStmt[1] = &pRtree->pWriteNode;
|
| - appStmt[2] = &pRtree->pDeleteNode;
|
| - appStmt[3] = &pRtree->pReadRowid;
|
| - appStmt[4] = &pRtree->pWriteRowid;
|
| - appStmt[5] = &pRtree->pDeleteRowid;
|
| - appStmt[6] = &pRtree->pReadParent;
|
| - appStmt[7] = &pRtree->pWriteParent;
|
| - appStmt[8] = &pRtree->pDeleteParent;
|
| + 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++){
|
| @@ -3212,9 +3475,10 @@ static int rtreeInit(
|
| pRtree->base.pModule = &rtreeModule;
|
| pRtree->zDb = (char *)&pRtree[1];
|
| pRtree->zName = &pRtree->zDb[nDb+1];
|
| - pRtree->nDim = (argc-4)/2;
|
| - pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
|
| - pRtree->eCoordType = eCoordType;
|
| + pRtree->nDim = (u8)((argc-4)/2);
|
| + pRtree->nDim2 = pRtree->nDim*2;
|
| + pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
| + pRtree->eCoordType = (u8)eCoordType;
|
| memcpy(pRtree->zDb, argv[1], nDb);
|
| memcpy(pRtree->zName, argv[2], nName);
|
|
|
| @@ -3287,7 +3551,8 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
| UNUSED_PARAMETER(nArg);
|
| memset(&node, 0, sizeof(RtreeNode));
|
| memset(&tree, 0, sizeof(Rtree));
|
| - tree.nDim = sqlite3_value_int(apArg[0]);
|
| + tree.nDim = (u8)sqlite3_value_int(apArg[0]);
|
| + tree.nDim2 = tree.nDim*2;
|
| tree.nBytesPerCell = 8 + 8 * tree.nDim;
|
| node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
|
|
|
| @@ -3300,7 +3565,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
| nodeGetCell(&tree, &node, ii, &cell);
|
| sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
|
| nCell = (int)strlen(zCell);
|
| - for(jj=0; jj<tree.nDim*2; jj++){
|
| + for(jj=0; jj<tree.nDim2; jj++){
|
| #ifndef SQLITE_RTREE_INT_ONLY
|
| sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
|
| (double)cell.aCoord[jj].f);
|
|
|