/ Check-in [ed06cc32]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Untested incremental check-in. Add the geopoly_xform() function. Complete basic logic for the geopoly virtual table.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rtree-geopoly
Files: files | file ages | folders
SHA3-256: ed06cc32568a3abaa0535b379e0ee3b04ffb7582dcda6405670620d1fbe8996c
User & Date: drh 2018-05-28 13:23:23
Context
2018-05-28
23:59
The geopoly virtual table seems to be working. But only thinly tested so far. check-in: 4288f1ad user: drh tags: rtree-geopoly
13:23
Untested incremental check-in. Add the geopoly_xform() function. Complete basic logic for the geopoly virtual table. check-in: ed06cc32 user: drh tags: rtree-geopoly
2018-05-26
20:04
Merge the ability to plan virtual table queries using overloaded functions. check-in: 2c2a202c user: drh tags: rtree-geopoly
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/rtree/geopoly.c.

   387    387         }
   388    388       }
   389    389       sqlite3_str_appendf(x, "></polyline>");
   390    390       sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
   391    391       sqlite3_free(p);
   392    392     }
   393    393   }
          394  +
          395  +/*
          396  +** SQL Function:      geopoly_xform(poly, A, B, C, D, E, F)
          397  +**
          398  +** Transform and/or translate a polygon as follows:
          399  +**
          400  +**      x1 = A*x0 + B*y0 + E
          401  +**      y1 = C*x0 + D*y0 + F
          402  +**
          403  +** For a translation:
          404  +**
          405  +**      geopoly_xform(poly, 1, 0, 0, 1, x-offset, y-offset)
          406  +**
          407  +** Rotate by R around the point (0,0):
          408  +**
          409  +**      geopoly_xform(poly, cos(R), sin(R), sin(R), cos(R), 0, 0)
          410  +*/
          411  +static void geopolyXformFunc(
          412  +  sqlite3_context *context,
          413  +  int argc,
          414  +  sqlite3_value **argv
          415  +){
          416  +  GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
          417  +  double A = sqlite3_value_double(argv[1]);
          418  +  double B = sqlite3_value_double(argv[2]);
          419  +  double C = sqlite3_value_double(argv[3]);
          420  +  double D = sqlite3_value_double(argv[4]);
          421  +  double E = sqlite3_value_double(argv[5]);
          422  +  double F = sqlite3_value_double(argv[7]);
          423  +  GeoCoord x1, y1, x0, y0;
          424  +  int ii;
          425  +  if( p ){
          426  +    for(ii=0; ii<p->nVertex; ii++){
          427  +      x0 = p->a[ii*2];
          428  +      y0 = p->a[ii*2+1];
          429  +      x1 = A*x0 + B*y0 + E;
          430  +      y1 = C*x0 + D*y0 + F;
          431  +      p->a[ii*2] = x1;
          432  +      p->a[ii*2+1] = y1;
          433  +    }
          434  +    sqlite3_result_blob(context, p->hdr, 
          435  +       4+8*p->nVertex, SQLITE_TRANSIENT);
          436  +    sqlite3_free(p);
          437  +  }
          438  +}
   394    439   
   395    440   /*
   396    441   ** Implementation of the geopoly_area(X) function.
   397    442   **
   398    443   ** If the input is a well-formed Geopoly BLOB then return the area
   399    444   ** enclosed by the polygon.  If the polygon circulates clockwise instead
   400    445   ** of counterclockwise (as it should) then return the negative of the
................................................................................
   937    982     int rc = SQLITE_OK;
   938    983     Rtree *pRtree;
   939    984     int nDb;              /* Length of string argv[1] */
   940    985     int nName;            /* Length of string argv[2] */
   941    986     sqlite3_str *pSql;
   942    987     char *zSql;
   943    988     int ii;
   944         -  char cSep;
   945    989   
   946    990     sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
   947    991   
   948    992     /* Allocate the sqlite3_vtab structure */
   949    993     nDb = (int)strlen(argv[1]);
   950    994     nName = (int)strlen(argv[2]);
   951    995     pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
................................................................................
   965   1009   
   966   1010   
   967   1011     /* Create/Connect to the underlying relational database schema. If
   968   1012     ** that is successful, call sqlite3_declare_vtab() to configure
   969   1013     ** the r-tree table schema.
   970   1014     */
   971   1015     pSql = sqlite3_str_new(db);
   972         -  sqlite3_str_appendf(pSql, "CREATE TABLE x");
   973         -  cSep = '(';
         1016  +  sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
   974   1017     pRtree->nAux = 1;   /* Add one for _shape */
   975   1018     for(ii=3; ii<argc; ii++){
   976   1019       pRtree->nAux++;
   977         -    sqlite3_str_appendf(pSql, "%c%s", cSep, argv[ii]+1);
   978         -    cSep = ',';
         1020  +    sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
   979   1021     }
   980         -  sqlite3_str_appendf(pSql, "%c _shape, _bbox HIDDEN);", cSep);
         1022  +  sqlite3_str_appendf(pSql, ",_bbox HIDDEN);");
   981   1023     zSql = sqlite3_str_finish(pSql);
   982   1024     if( !zSql ){
   983   1025       rc = SQLITE_NOMEM;
   984   1026     }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
   985   1027       *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
   986   1028     }
   987   1029     sqlite3_free(zSql);
................................................................................
  1031   1073     int argc, const char *const*argv,
  1032   1074     sqlite3_vtab **ppVtab,
  1033   1075     char **pzErr
  1034   1076   ){
  1035   1077     return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
  1036   1078   }
  1037   1079   
         1080  +
         1081  +/* 
         1082  +** GEOPOLY virtual table module xFilter method.
         1083  +**
         1084  +** Query plans:
         1085  +**
         1086  +**      1         rowid lookup
         1087  +**      2         search for objects overlapping the same bounding box
         1088  +**                that contains polygon argv[0]
         1089  +**      3         full table scan
         1090  +*/
         1091  +static int geopolyFilter(
         1092  +  sqlite3_vtab_cursor *pVtabCursor,     /* The cursor to initialize */
         1093  +  int idxNum,                           /* Query plan */
         1094  +  const char *idxStr,                   /* Not Used */
         1095  +  int argc, sqlite3_value **argv        /* Parameters to the query plan */
         1096  +){
         1097  +  Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
         1098  +  RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
         1099  +  RtreeNode *pRoot = 0;
         1100  +  int rc = SQLITE_OK;
         1101  +  int iCell = 0;
         1102  +  sqlite3_stmt *pStmt;
         1103  +
         1104  +  rtreeReference(pRtree);
         1105  +
         1106  +  /* Reset the cursor to the same state as rtreeOpen() leaves it in. */
         1107  +  freeCursorConstraints(pCsr);
         1108  +  sqlite3_free(pCsr->aPoint);
         1109  +  pStmt = pCsr->pReadAux;
         1110  +  memset(pCsr, 0, sizeof(RtreeCursor));
         1111  +  pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
         1112  +  pCsr->pReadAux = pStmt;
         1113  +
         1114  +  pCsr->iStrategy = idxNum;
         1115  +  if( idxNum==1 ){
         1116  +    /* Special case - lookup by rowid. */
         1117  +    RtreeNode *pLeaf;        /* Leaf on which the required cell resides */
         1118  +    RtreeSearchPoint *p;     /* Search point for the leaf */
         1119  +    i64 iRowid = sqlite3_value_int64(argv[0]);
         1120  +    i64 iNode = 0;
         1121  +    rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
         1122  +    if( rc==SQLITE_OK && pLeaf!=0 ){
         1123  +      p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
         1124  +      assert( p!=0 );  /* Always returns pCsr->sPoint */
         1125  +      pCsr->aNode[0] = pLeaf;
         1126  +      p->id = iNode;
         1127  +      p->eWithin = PARTLY_WITHIN;
         1128  +      rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
         1129  +      p->iCell = (u8)iCell;
         1130  +      RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
         1131  +    }else{
         1132  +      pCsr->atEOF = 1;
         1133  +    }
         1134  +  }else{
         1135  +    /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array 
         1136  +    ** with the configured constraints. 
         1137  +    */
         1138  +    rc = nodeAcquire(pRtree, 1, 0, &pRoot);
         1139  +    if( rc==SQLITE_OK && idxNum==2 ){
         1140  +      RtreeCoord bbox[4];
         1141  +      RtreeConstraint *p;
         1142  +      assert( argc==1 );
         1143  +      geopolyBBox(0, argv[0], bbox, &rc);
         1144  +      if( rc ){
         1145  +        return rc;
         1146  +      }
         1147  +      pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
         1148  +      pCsr->nConstraint = 4;
         1149  +      if( p==0 ){
         1150  +        rc = SQLITE_NOMEM;
         1151  +      }else{
         1152  +        memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
         1153  +        memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
         1154  +        p->op = 'B';
         1155  +        p->iCoord = 'a';
         1156  +        p->u.rValue = bbox[0].f;
         1157  +        p++;
         1158  +        p->op = 'D';
         1159  +        p->iCoord = 'b';
         1160  +        p->u.rValue = bbox[1].f;
         1161  +        p++;
         1162  +        p->op = 'B';
         1163  +        p->iCoord = 'c';
         1164  +        p->u.rValue = bbox[2].f;
         1165  +        p++;
         1166  +        p->op = 'D';
         1167  +        p->iCoord = 'd';
         1168  +        p->u.rValue = bbox[3].f;
         1169  +      }
         1170  +    }
         1171  +    if( rc==SQLITE_OK ){
         1172  +      RtreeSearchPoint *pNew;
         1173  +      pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
         1174  +      if( pNew==0 ) return SQLITE_NOMEM;
         1175  +      pNew->id = 1;
         1176  +      pNew->iCell = 0;
         1177  +      pNew->eWithin = PARTLY_WITHIN;
         1178  +      assert( pCsr->bPoint==1 );
         1179  +      pCsr->aNode[0] = pRoot;
         1180  +      pRoot = 0;
         1181  +      RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
         1182  +      rc = rtreeStepToLeaf(pCsr);
         1183  +    }
         1184  +  }
         1185  +
         1186  +  nodeRelease(pRtree, pRoot);
         1187  +  rtreeRelease(pRtree);
         1188  +  return rc;
         1189  +}
  1038   1190   
  1039   1191   /*
  1040         -** GEOPOLY virtual table module xBestIndex method. There are three
         1192  +** Rtree virtual table module xBestIndex method. There are three
  1041   1193   ** table scan strategies to choose from (in order from most to 
  1042   1194   ** least desirable):
  1043   1195   **
  1044   1196   **   idxNum     idxStr        Strategy
  1045   1197   **   ------------------------------------------------
  1046   1198   **     1        Unused        Direct lookup by rowid.
  1047         -**     2        'Fx'           shape query
  1048         -**     2        ''            full-table scan.
         1199  +**     2        Unused        R-tree query
         1200  +**     3        Unused        full-table scan.
  1049   1201   **   ------------------------------------------------
  1050         -**
  1051         -** If strategy 1 is used, then idxStr is not meaningful. If strategy
  1052         -** 2 is used, idxStr is either the two-byte string 'Fx' or an empty
  1053         -** string.
  1054   1202   */
  1055   1203   static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  1056         -  Rtree *pRtree = (Rtree*)tab;
  1057         -  int rc = SQLITE_OK;
  1058   1204     int ii;
  1059         -  int bMatch = 0;                 /* True if there exists a MATCH constraint */
  1060         -  i64 nRow;                       /* Estimated rows returned by this scan */
         1205  +  int iRowidTerm = -1;
         1206  +  int iFuncTerm = -1;
  1061   1207   
  1062         -  int iIdx = 0;
  1063         -  char zIdxStr[3];
  1064         -  memset(zIdxStr, 0, sizeof(zIdxStr));
  1065         -
  1066         -  /* Check if there exists a MATCH constraint - even an unusable one. If there
  1067         -  ** is, do not consider the lookup-by-rowid plan as using such a plan would
  1068         -  ** require the VDBE to evaluate the MATCH constraint, which is not currently
  1069         -  ** possible. */
  1070   1208     for(ii=0; ii<pIdxInfo->nConstraint; ii++){
  1071         -    if( pIdxInfo->aConstraint[ii].op==SQLITE_INDEX_CONSTRAINT_MATCH ){
  1072         -      bMatch = 1;
         1209  +    struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
         1210  +    if( !p->usable ) continue;
         1211  +    if( p->iColumn<0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ  ){
         1212  +      iRowidTerm = ii;
         1213  +      break;
         1214  +    }
         1215  +    if( p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_FUNCTION ){
         1216  +      iFuncTerm = ii;
  1073   1217       }
  1074   1218     }
  1075   1219   
  1076         -  assert( pIdxInfo->idxStr==0 );
  1077         -  for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
  1078         -    struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
  1079         -
  1080         -    if( bMatch==0
  1081         -     && p->usable 
  1082         -     && p->iColumn<0
  1083         -     && p->op==SQLITE_INDEX_CONSTRAINT_EQ 
  1084         -    ){
  1085         -      /* We have an equality constraint on the rowid. Use strategy 1. */
  1086         -      int jj;
  1087         -      for(jj=0; jj<ii; jj++){
  1088         -        pIdxInfo->aConstraintUsage[jj].argvIndex = 0;
  1089         -        pIdxInfo->aConstraintUsage[jj].omit = 0;
  1090         -      }
  1091         -      pIdxInfo->idxNum = 1;
  1092         -      pIdxInfo->aConstraintUsage[ii].argvIndex = 1;
  1093         -      pIdxInfo->aConstraintUsage[jj].omit = 1;
  1094         -
  1095         -      /* This strategy involves a two rowid lookups on an B-Tree structures
  1096         -      ** and then a linear search of an R-Tree node. This should be 
  1097         -      ** considered almost as quick as a direct rowid lookup (for which 
  1098         -      ** sqlite uses an internal cost of 0.0). It is expected to return
  1099         -      ** a single row.
  1100         -      */ 
  1101         -      pIdxInfo->estimatedCost = 30.0;
  1102         -      pIdxInfo->estimatedRows = 1;
  1103         -      pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
  1104         -      return SQLITE_OK;
  1105         -    }
  1106         -
  1107         -    /* A MATCH operator against the _shape column */
  1108         -    if( p->usable
  1109         -     && p->iColumn==pRtree->nAux
  1110         -     && p->op==SQLITE_INDEX_CONSTRAINT_MATCH
  1111         -    ){
  1112         -      zIdxStr[0] = RTREE_QUERY;
  1113         -      zIdxStr[1] = 'x';
  1114         -      zIdxStr[2] = 0;
  1115         -      pIdxInfo->aConstraintUsage[ii].argvIndex = 0;
  1116         -      pIdxInfo->aConstraintUsage[ii].omit = 1;
  1117         -    }
         1220  +  if( iRowidTerm>=0 ){
         1221  +    pIdxInfo->idxNum = 1;
         1222  +    pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
         1223  +    pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
         1224  +    pIdxInfo->estimatedCost = 30.0;
         1225  +    pIdxInfo->estimatedRows = 1;
         1226  +    pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
         1227  +    return SQLITE_OK;
         1228  +  }
         1229  +  if( iFuncTerm>=0 ){
         1230  +    pIdxInfo->idxNum = 2;
         1231  +    pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
         1232  +    pIdxInfo->estimatedCost = 300.0;
         1233  +    pIdxInfo->estimatedRows = 10;
         1234  +    return SQLITE_OK;
  1118   1235     }
  1119         -
  1120         -  pIdxInfo->idxNum = 2;
  1121         -  pIdxInfo->needToFreeIdxStr = 1;
  1122         -  if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
  1123         -    return SQLITE_NOMEM;
  1124         -  }
  1125         -
  1126         -  nRow = pRtree->nRowEst/100 + 5;
  1127         -  pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
  1128         -  pIdxInfo->estimatedRows = nRow;
  1129         -
  1130         -  return rc;
         1236  +  pIdxInfo->idxNum = 3;
         1237  +  pIdxInfo->estimatedCost = 3000000.0;
         1238  +  pIdxInfo->estimatedRows = 100000;
         1239  +  return SQLITE_OK;
  1131   1240   }
  1132   1241   
  1133   1242   
  1134   1243   /* 
  1135   1244   ** GEOPOLY virtual table module xColumn method.
  1136   1245   */
  1137   1246   static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
................................................................................
  1168   1277     }
  1169   1278     return SQLITE_OK;
  1170   1279   }
  1171   1280   
  1172   1281   
  1173   1282   /*
  1174   1283   ** The xUpdate method for GEOPOLY module virtual tables.
         1284  +**
         1285  +** For DELETE:
         1286  +**
         1287  +**     argv[0] = the rowid to be deleted
         1288  +**
         1289  +** For INSERT:
         1290  +**
         1291  +**     argv[0] = SQL NULL
         1292  +**     argv[1] = rowid to insert, or an SQL NULL to select automatically
         1293  +**     argv[2] = _shape column
         1294  +**     argv[3] = first application-defined column....
         1295  +**
         1296  +** For UPDATE:
         1297  +**
         1298  +**     argv[0] = rowid to modify.  Never NULL
         1299  +**     argv[1] = rowid after the change.  Never NULL
         1300  +**     argv[2] = new value for _shape
         1301  +**     argv[3] = new value for first application-defined column....
  1175   1302   */
  1176   1303   static int geopolyUpdate(
  1177   1304     sqlite3_vtab *pVtab, 
  1178   1305     int nData, 
  1179   1306     sqlite3_value **aData, 
  1180   1307     sqlite_int64 *pRowid
  1181   1308   ){
  1182   1309     Rtree *pRtree = (Rtree *)pVtab;
  1183   1310     int rc = SQLITE_OK;
  1184   1311     RtreeCell cell;                 /* New cell to insert if nData>1 */
  1185         -  int iShapeCol;                  /* Index of the _shape column */
  1186   1312     i64 oldRowid;                   /* The old rowid */
  1187   1313     int oldRowidValid;              /* True if oldRowid is valid */
  1188   1314     i64 newRowid;                   /* The new rowid */
  1189   1315     int newRowidValid;              /* True if newRowid is valid */
  1190   1316     int coordChange = 0;            /* Change in coordinates */
  1191   1317   
  1192   1318     if( pRtree->nNodeRef ){
................................................................................
  1194   1320       ** since the write might do a rebalance which would disrupt the read
  1195   1321       ** cursor. */
  1196   1322       return SQLITE_LOCKED_VTAB;
  1197   1323     }
  1198   1324     rtreeReference(pRtree);
  1199   1325     assert(nData>=1);
  1200   1326   
  1201         -  iShapeCol = pRtree->nAux;
  1202   1327     rc = SQLITE_ERROR;
  1203   1328     oldRowidValid = sqlite3_value_type(aData[0])!=SQLITE_NULL;;
  1204   1329     oldRowid = oldRowidValid ? sqlite3_value_int64(aData[0]) : 0;
  1205   1330     newRowidValid = nData>1 && sqlite3_value_type(aData[1])!=SQLITE_NULL;
  1206   1331     newRowid = newRowidValid ? sqlite3_value_int64(aData[1]) : 0;
  1207   1332     cell.iRowid = newRowid;
  1208   1333   
  1209         -  if( nData>1                                           /* not a DELETE */
  1210         -   && (!oldRowidValid                                   /* INSERT */
  1211         -        || !sqlite3_value_nochange(aData[iShapeCol+2])  /* UPDATE _shape */
  1212         -        || oldRowid!=newRowid)                          /* Rowid change */
         1334  +  if( nData>1                                 /* not a DELETE */
         1335  +   && (!oldRowidValid                         /* INSERT */
         1336  +        || !sqlite3_value_nochange(aData[2])  /* UPDATE _shape */
         1337  +        || oldRowid!=newRowid)                /* Rowid change */
  1213   1338     ){
  1214         -    geopolyBBox(0, aData[iShapeCol+2], cell.aCoord, &rc);
         1339  +    geopolyBBox(0, aData[2], cell.aCoord, &rc);
  1215   1340       if( rc ){
  1216   1341         if( rc==SQLITE_ERROR ){
  1217   1342           pVtab->zErrMsg =
  1218   1343             sqlite3_mprintf("_shape does not contain a valid polygon");
  1219   1344         }
  1220   1345         return rc;
  1221   1346       }
  1222   1347       coordChange = 1;
  1223   1348   
  1224   1349       /* If a rowid value was supplied, check if it is already present in 
  1225   1350       ** the table. If so, the constraint has failed. */
  1226         -    if( oldRowidValid && oldRowid!=newRowid ){
         1351  +    if( newRowidValid ){
  1227   1352         int steprc;
  1228   1353         sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
  1229   1354         steprc = sqlite3_step(pRtree->pReadRowid);
  1230   1355         rc = sqlite3_reset(pRtree->pReadRowid);
  1231   1356         if( SQLITE_ROW==steprc ){
  1232   1357           if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
  1233   1358             rc = rtreeDeleteRowid(pRtree, cell.iRowid);
................................................................................
  1265   1390         if( rc==SQLITE_OK ){
  1266   1391           rc = rc2;
  1267   1392         }
  1268   1393       }
  1269   1394     }
  1270   1395   
  1271   1396     /* Change the data */
  1272         -  if( rc==SQLITE_OK && pRtree->nAux>0 ){
         1397  +  if( rc==SQLITE_OK ){
  1273   1398       sqlite3_stmt *pUp = pRtree->pWriteAux;
  1274   1399       int jj;
  1275   1400       int nChange = 0;
  1276   1401       sqlite3_bind_int64(pUp, 1, newRowid);
  1277   1402       for(jj=0; jj<pRtree->nAux; jj++){
  1278   1403         if( !sqlite3_value_nochange(aData[jj+2]) ) nChange++;
  1279   1404         sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
................................................................................
  1283   1408         rc = sqlite3_reset(pUp);
  1284   1409       }
  1285   1410     }
  1286   1411   
  1287   1412     rtreeRelease(pRtree);
  1288   1413     return rc;
  1289   1414   }
         1415  +
         1416  +/*
         1417  +** Report that geopoly_overlap() is an overloaded function suitable
         1418  +** for use in xBestIndex.
         1419  +*/
         1420  +static int geopolyFindFunction(
         1421  +  sqlite3_vtab *pVtab,
         1422  +  int nArg,
         1423  +  const char *zName,
         1424  +  void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
         1425  +  void **ppArg
         1426  +){
         1427  +  if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
         1428  +    *pxFunc = geopolyOverlapFunc;
         1429  +    *ppArg = 0;
         1430  +    return SQLITE_INDEX_CONSTRAINT_FUNCTION;
         1431  +  }
         1432  +  return 0;
         1433  +}
         1434  +
  1290   1435   
  1291   1436   static sqlite3_module geopolyModule = {
  1292   1437     2,                          /* iVersion */
  1293   1438     geopolyCreate,              /* xCreate - create a table */
  1294   1439     geopolyConnect,             /* xConnect - connect to an existing table */
  1295   1440     geopolyBestIndex,           /* xBestIndex - Determine search strategy */
  1296   1441     rtreeDisconnect,            /* xDisconnect - Disconnect from a table */
  1297   1442     rtreeDestroy,               /* xDestroy - Drop a table */
  1298   1443     rtreeOpen,                  /* xOpen - open a cursor */
  1299   1444     rtreeClose,                 /* xClose - close a cursor */
  1300         -  rtreeFilter,                /* xFilter - configure scan constraints */
         1445  +  geopolyFilter,              /* xFilter - configure scan constraints */
  1301   1446     rtreeNext,                  /* xNext - advance a cursor */
  1302   1447     rtreeEof,                   /* xEof */
  1303   1448     geopolyColumn,              /* xColumn - read data */
  1304   1449     rtreeRowid,                 /* xRowid - read data */
  1305   1450     geopolyUpdate,              /* xUpdate - write data */
  1306   1451     rtreeBeginTransaction,      /* xBegin - begin transaction */
  1307   1452     rtreeEndTransaction,        /* xSync - sync transaction */
  1308   1453     rtreeEndTransaction,        /* xCommit - commit transaction */
  1309   1454     rtreeEndTransaction,        /* xRollback - rollback transaction */
  1310         -  0,                          /* xFindFunction - function overloading */
         1455  +  geopolyFindFunction,        /* xFindFunction - function overloading */
  1311   1456     rtreeRename,                /* xRename - rename the table */
  1312   1457     rtreeSavepoint,             /* xSavepoint */
  1313   1458     0,                          /* xRelease */
  1314   1459     0,                          /* xRollbackTo */
  1315   1460   };
  1316   1461   
  1317   1462   static int sqlite3_geopoly_init(sqlite3 *db){
................................................................................
  1325   1470        { geopolyBlobFunc,          1,    "geopoly_blob"     },
  1326   1471        { geopolyJsonFunc,          1,    "geopoly_json"     },
  1327   1472        { geopolySvgFunc,          -1,    "geopoly_svg"      },
  1328   1473        { geopolyWithinFunc,        3,    "geopoly_within"   },
  1329   1474        { geopolyOverlapFunc,       2,    "geopoly_overlap"  },
  1330   1475        { geopolyDebugFunc,         1,    "geopoly_debug"    },
  1331   1476        { geopolyBBoxFunc,          1,    "geopoly_bbox"     },
         1477  +     { geopolyXformFunc,         7,    "geopoly_xform"    },
  1332   1478     };
  1333   1479     int i;
  1334   1480     for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
  1335   1481       rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
  1336   1482                                    SQLITE_UTF8, 0,
  1337   1483                                    aFunc[i].xFunc, 0, 0);
  1338   1484     }
  1339   1485     if( rc==SQLITE_OK ){
  1340   1486       rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
  1341   1487     }
  1342   1488     return rc;
  1343   1489   }