From: drh Date: Fri, 25 May 2018 22:39:29 +0000 (+0000) Subject: Untested incremental check-in. Basic infrastructure for geopoly in place, X-Git-Tag: version-3.25.0~60^2~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ecdc742790f07c9b3acc0ff32631fb396524ae59;p=thirdparty%2Fsqlite.git Untested incremental check-in. Basic infrastructure for geopoly in place, except for the MATCH operator. FossilOrigin-Name: b27451910b6880b166d4777e05b14ab2731c3483b5ed0f42c459bbb61c6cd095 --- diff --git a/ext/rtree/geopoly.c b/ext/rtree/geopoly.c index f22f762e7c..427462b560 100644 --- a/ext/rtree/geopoly.c +++ b/ext/rtree/geopoly.c @@ -193,8 +193,9 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){ ** ** If any error occurs, return NULL. */ -static GeoPoly *geopolyParseJson(const unsigned char *z){ +static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){ GeoParse s; + int rc = SQLITE_OK; memset(&s, 0, sizeof(s)); s.z = z; if( geopolySkipSpace(&s)=='[' ){ @@ -208,6 +209,7 @@ static GeoPoly *geopolyParseJson(const unsigned char *z){ s.nAlloc = s.nAlloc*2 + 16; aNew = sqlite3_realloc64(s.a, s.nAlloc*sizeof(GeoCoord)*2 ); if( aNew==0 ){ + rc = SQLITE_NOMEM; s.nErr++; break; } @@ -221,6 +223,7 @@ static GeoPoly *geopolyParseJson(const unsigned char *z){ if( c==',' ) continue; if( c==']' ) break; s.nErr++; + rc = SQLITE_ERROR; goto parse_json_err; } if( geopolySkipSpace(&s)==',' ){ @@ -245,12 +248,15 @@ static GeoPoly *geopolyParseJson(const unsigned char *z){ pOut->hdr[2] = (s.nVertex>>8)&0xff; pOut->hdr[3] = s.nVertex&0xff; sqlite3_free(s.a); + if( pRc ) *pRc = SQLITE_OK; return pOut; }else{ s.nErr++; + rc = SQLITE_ERROR; } } parse_json_err: + if( pRc ) *pRc = rc; sqlite3_free(s.a); return 0; } @@ -261,7 +267,11 @@ parse_json_err: ** return a pointer to that object. Or if the input is not a well-formed ** polygon, put an error message in sqlite3_context and return NULL. */ -static GeoPoly *geopolyFuncParam(sqlite3_context *pCtx, sqlite3_value *pVal){ +static GeoPoly *geopolyFuncParam( + sqlite3_context *pCtx, /* Context for error messages */ + sqlite3_value *pVal, /* The value to decode */ + int *pRc /* Write error here */ +){ GeoPoly *p = 0; int nByte; if( sqlite3_value_type(pVal)==SQLITE_BLOB @@ -274,7 +284,10 @@ static GeoPoly *geopolyFuncParam(sqlite3_context *pCtx, sqlite3_value *pVal){ && (nVertex*2*sizeof(GeoCoord) + 4)==nByte ){ p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) ); - if( p ){ + if( p==0 ){ + if( pRc ) *pRc = SQLITE_NOMEM; + if( pCtx ) sqlite3_result_error_nomem(pCtx); + }else{ int x = 1; p->nVertex = nVertex; memcpy(p->hdr, a, nByte); @@ -287,13 +300,15 @@ static GeoPoly *geopolyFuncParam(sqlite3_context *pCtx, sqlite3_value *pVal){ } } } + if( pRc ) *pRc = SQLITE_OK; + return p; }else if( sqlite3_value_type(pVal)==SQLITE_TEXT ){ - p = geopolyParseJson(sqlite3_value_text(pVal)); - } - if( p==0 ){ - sqlite3_result_error(pCtx, "not a valid polygon", -1); + return geopolyParseJson(sqlite3_value_text(pVal), pRc); + }else{ + *pRc = SQLITE_ERROR; + if( pCtx!=0 ) sqlite3_result_error(pCtx, "not a valid polygon", -1); + return 0; } - return p; } /* @@ -308,7 +323,7 @@ static void geopolyBlobFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0]); + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -327,7 +342,7 @@ static void geopolyJsonFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0]); + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ sqlite3 *db = sqlite3_context_db_handle(context); sqlite3_str *x = sqlite3_str_new(db); @@ -353,7 +368,7 @@ static void geopolySvgFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0]); + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ sqlite3 *db = sqlite3_context_db_handle(context); sqlite3_str *x = sqlite3_str_new(db); @@ -390,7 +405,7 @@ static void geopolyAreaFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0]); + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ double rArea = 0.0; int ii; @@ -415,10 +430,11 @@ static void geopolyAreaFunc( static GeoPoly *geopolyBBox( sqlite3_context *context, /* For recording the error */ sqlite3_value *pPoly, /* The polygon */ - double *aCoord /* Results here */ + RtreeCoord *aCoord, /* Results here */ + int *pRc /* Error code here */ ){ - GeoPoly *p = geopolyFuncParam(context, pPoly); - GeoPoly *pOut; + GeoPoly *p = geopolyFuncParam(context, pPoly, pRc); + GeoPoly *pOut = 0; if( p ){ int ii; float mnX, mxX, mnY, mxY; @@ -432,11 +448,13 @@ static GeoPoly *geopolyBBox( if( rmxY ) mxY = r; } + if( pRc ) *pRc = SQLITE_OK; if( aCoord==0 ){ pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6); if( pOut==0 ){ sqlite3_free(p); - sqlite3_result_error_nomem(context); + if( context ) sqlite3_result_error_nomem(context); + if( pRc ) *pRc = SQLITE_NOMEM; return 0; } pOut->nVertex = 4; @@ -451,17 +469,15 @@ static GeoPoly *geopolyBBox( pOut->a[5] = mxY; pOut->a[6] = mnX; pOut->a[7] = mxY; - return pOut; }else{ sqlite3_free(p); - aCoord[0] = mnX; - aCoord[1] = mxX; - aCoord[2] = mnY; - aCoord[3] = mxY; - return (GeoPoly*)aCoord; + aCoord[0].f = mnX; + aCoord[1].f = mxX; + aCoord[2].f = mnY; + aCoord[3].f = mxY; } } - return 0; + return pOut; } /* @@ -472,7 +488,7 @@ static void geopolyBBoxFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyBBox(context, argv[0], 0); + GeoPoly *p = geopolyBBox(context, argv[0], 0, 0); if( p ){ sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -530,7 +546,7 @@ static void geopolyWithinFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0]); + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); double x0 = sqlite3_value_double(argv[1]); double y0 = sqlite3_value_double(argv[2]); if( p ){ @@ -874,8 +890,8 @@ static void geopolyOverlapFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p1 = geopolyFuncParam(context, argv[0]); - GeoPoly *p2 = geopolyFuncParam(context, argv[1]); + GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); + GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); if( p1 && p2 ){ int x = geopolyOverlap(p1, p2); if( x<0 ){ @@ -955,12 +971,13 @@ static int geopolyInit( pSql = sqlite3_str_new(db); sqlite3_str_appendf(pSql, "CREATE TABLE x"); cSep = '('; + pRtree->nAux = 1; /* Add one for _shape */ for(ii=3; iinAux++; sqlite3_str_appendf(pSql, "%c%s", cSep, argv[ii]+1); cSep = ','; } - sqlite3_str_appendf(pSql, "%c _poly HIDDEN, _bbox HIDDEN);", cSep); + sqlite3_str_appendf(pSql, "%c _shape, _bbox HIDDEN);", cSep); zSql = sqlite3_str_finish(pSql); if( !zSql ){ rc = SQLITE_NOMEM; @@ -1164,9 +1181,13 @@ static int geopolyUpdate( ){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_OK; -// RtreeCell cell; /* New cell to insert if nData>1 */ -// int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ -// int iShapeCol; /* Index of the _shape column */ + RtreeCell cell; /* New cell to insert if nData>1 */ + int iShapeCol; /* Index of the _shape column */ + i64 oldRowid; /* The old rowid */ + int oldRowidValid; /* True if oldRowid is valid */ + i64 newRowid; /* The new rowid */ + int newRowidValid; /* True if newRowid is valid */ + int coordChange = 0; /* Change in coordinates */ if( pRtree->nNodeRef ){ /* Unable to write to the btree while another cursor is reading from it, @@ -1177,72 +1198,43 @@ static int geopolyUpdate( rtreeReference(pRtree); assert(nData>=1); -// cell.iRowid = 0; /* Used only to suppress a compiler warning */ -// iShapeCol = pRtree->nAux; - + iShapeCol = pRtree->nAux; rc = SQLITE_ERROR; - -#if 0 - - /* Constraint handling. A write operation on an r-tree table may return - ** SQLITE_CONSTRAINT for two reasons: - ** - ** 1. A duplicate rowid value, or - ** 2. The supplied data violates the "x2>=x1" constraint. - ** - ** In the first case, if the conflict-handling mode is REPLACE, then - ** the conflicting row can be removed before proceeding. In the second - ** case, SQLITE_CONSTRAINT must be returned regardless of the - ** conflict-handling mode specified by the user. - */ - if( nData>1 - && (!sqlite3_value_nochange(aData[iShapeCol+2]) + oldRowidValid = sqlite3_value_type(aData[0])!=SQLITE_NULL;; + oldRowid = oldRowidValid ? sqlite3_value_int64(aData[0]) : 0; + newRowidValid = nData>1 && sqlite3_value_type(aData[1])!=SQLITE_NULL; + newRowid = newRowidValid ? sqlite3_value_int64(aData[1]) : 0; + cell.iRowid = newRowid; + + if( nData>1 /* not a DELETE */ + && (!oldRowidValid /* INSERT */ + || !sqlite3_value_nochange(aData[iShapeCol+2]) /* UPDATE _shape */ + || oldRowid!=newRowid) /* Rowid change */ ){ - -#ifndef SQLITE_RTREE_INT_ONLY - if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - for(ii=0; iicell.aCoord[ii+1].f ){ - rc = rtreeConstraintError(pRtree, ii+1); - goto constraint; - } - } - }else -#endif - { - for(ii=0; iicell.aCoord[ii+1].i ){ - rc = rtreeConstraintError(pRtree, ii+1); - goto constraint; - } + geopolyBBox(0, aData[iShapeCol+2], cell.aCoord, &rc); + if( rc ){ + if( rc==SQLITE_ERROR ){ + pVtab->zErrMsg = + sqlite3_mprintf("_shape does not contain a valid polygon"); } + return rc; } + coordChange = 1; /* If a rowid value was supplied, check if it is already present in ** the table. If so, the constraint has failed. */ - if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){ - cell.iRowid = sqlite3_value_int64(aData[2]); - if( sqlite3_value_type(aData[0])==SQLITE_NULL - || sqlite3_value_int64(aData[0])!=cell.iRowid - ){ - int steprc; - sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); - steprc = sqlite3_step(pRtree->pReadRowid); - rc = sqlite3_reset(pRtree->pReadRowid); - if( SQLITE_ROW==steprc ){ - if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ - rc = rtreeDeleteRowid(pRtree, cell.iRowid); - }else{ - rc = rtreeConstraintError(pRtree, 0); - goto constraint; - } + if( oldRowidValid && oldRowid!=newRowid ){ + 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); } } - bHaveRowid = 1; } } @@ -1250,24 +1242,18 @@ static int geopolyUpdate( ** record to delete from the r-tree table. The following block does ** just that. */ - if( sqlite3_value_type(aData[0])!=SQLITE_NULL ){ - rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0])); + if( rc==SQLITE_OK && (nData==1 || coordChange) ){ + rc = rtreeDeleteRowid(pRtree, oldRowid); } /* If the aData[] array contains more than one element, elements ** (aData[2]..aData[argc-1]) contain a new record to insert into ** the r-tree structure. */ - if( rc==SQLITE_OK && nData>1 ){ + if( rc==SQLITE_OK && nData>1 && coordChange ){ /* Insert the new record into the r-tree */ RtreeNode *pLeaf = 0; - - /* Figure out the rowid of the new row. */ - if( bHaveRowid==0 ){ - rc = newRowid(pRtree, &cell.iRowid); - } *pRowid = cell.iRowid; - if( rc==SQLITE_OK ){ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf); } @@ -1280,19 +1266,23 @@ static int geopolyUpdate( rc = rc2; } } - if( pRtree->nAux ){ - sqlite3_stmt *pUp = pRtree->pWriteAux; - int jj; - sqlite3_bind_int64(pUp, 1, *pRowid); - for(jj=0; jjnAux; jj++){ - sqlite3_bind_value(pUp, jj+2, aData[pRtree->nDim2+3+jj]); - } + } + + /* Change the data */ + if( rc==SQLITE_OK && pRtree->nAux>0 ){ + sqlite3_stmt *pUp = pRtree->pWriteAux; + int jj; + int nChange = 0; + sqlite3_bind_int64(pUp, 1, newRowid); + for(jj=0; jjnAux; jj++){ + if( !sqlite3_value_nochange(aData[jj+2]) ) nChange++; + sqlite3_bind_value(pUp, jj+2, aData[jj+2]); + } + if( nChange ){ sqlite3_step(pUp); rc = sqlite3_reset(pUp); } } -constraint: -#endif /* 0 */ rtreeRelease(pRtree); return rc; diff --git a/manifest b/manifest index 042bcab12b..44b42dc0f8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Incremental\scheck-in:\sProgress\stoward\simplementing\sthe\sgeopoly\svtab. -D 2018-05-25T20:53:42.012 +C Untested\sincremental\scheck-in.\s\sBasic\sinfrastructure\sfor\sgeopoly\sin\splace,\nexcept\sfor\sthe\sMATCH\soperator. +D 2018-05-25T22:39:29.495 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 51407f0e371dcb9e65d368bd4f1a08fc17ef8361ff11aac9356f0f63693b38dd @@ -355,7 +355,7 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782 F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/geopoly.c bf7a5fc70df145e83b60abd3e9828ad036921a9603087f5f108e054d75f12abc +F ext/rtree/geopoly.c e1b192440686ee041f94a510298f5d1d44e24f5c777674c1d00be616c6ac9633 F ext/rtree/rtree.c 2fd3c149c6fc4d3fdf602dc610b34ad9abdf75cca26d0c362f903aa02ea2ef47 F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412 F ext/rtree/rtree1.test 309afc04d4287542b2cd74f933296832cc681c7b014d9405cb329b62053a5349 @@ -1730,7 +1730,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 0593aac88a8c25ddafba4c29a181ee083dfc3dab44335feb6f12fdea6ce7fb27 -R 36e4ed5dc1a640520059a271545fd8c3 +P 9b7d6f986a19f8fbe26d880dc368870a10df80e06097be34ee3639f78c50be61 +R ac42d4684558c7281aaaaec9453f13e7 U drh -Z b8c6ea17872c9a8b20afb569a3513051 +Z b54e65d1af2e3764e8c52f03dab6b0ff diff --git a/manifest.uuid b/manifest.uuid index 52355d0aed..78ee84ffb1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b7d6f986a19f8fbe26d880dc368870a10df80e06097be34ee3639f78c50be61 \ No newline at end of file +b27451910b6880b166d4777e05b14ab2731c3483b5ed0f42c459bbb61c6cd095 \ No newline at end of file