/ Check-in [ed06cc32]

Overview
Comment: Untested incremental check-in. Add the geopoly_xform() function. Complete basic logic for the geopoly virtual table. Tarball | ZIP archive | SQL archive family | ancestors | descendants | both | rtree-geopoly files | file ages | folders ed06cc32568a3abaa0535b379e0ee3b04ffb7582dcda6405670620d1fbe8996c 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);
1110  +  memset(pCsr, 0, sizeof(RtreeCursor));
1111  +  pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
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,
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;
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;
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++;
................................................................................
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 */
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   }

```