modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test_rtree.c (about)

     1  /*
     2  ** 2010 August 28
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  ** Code for testing all sorts of SQLite interfaces. This code
    13  ** is not included in the SQLite library. 
    14  */
    15  
    16  #include "sqlite3.h"
    17  #if defined(INCLUDE_SQLITE_TCL_H)
    18  #  include "sqlite_tcl.h"
    19  #else
    20  #  include "tcl.h"
    21  #endif
    22  
    23  /* Solely for the UNUSED_PARAMETER() macro. */
    24  #include "sqliteInt.h"
    25  
    26  #ifdef SQLITE_ENABLE_RTREE
    27  /* 
    28  ** Type used to cache parameter information for the "circle" r-tree geometry
    29  ** callback.
    30  */
    31  typedef struct Circle Circle;
    32  struct Circle {
    33    struct Box {
    34      double xmin;
    35      double xmax;
    36      double ymin;
    37      double ymax;
    38    } aBox[2];
    39    double centerx;
    40    double centery;
    41    double radius;
    42    double mxArea;
    43    int eScoreType;
    44  };
    45  
    46  /*
    47  ** Destructor function for Circle objects allocated by circle_geom().
    48  */
    49  static void circle_del(void *p){
    50    sqlite3_free(p);
    51  }
    52  
    53  /*
    54  ** Implementation of "circle" r-tree geometry callback.
    55  */
    56  static int circle_geom(
    57    sqlite3_rtree_geometry *p,
    58    int nCoord, 
    59    sqlite3_rtree_dbl *aCoord,
    60    int *pRes
    61  ){
    62    int i;                          /* Iterator variable */
    63    Circle *pCircle;                /* Structure defining circular region */
    64    double xmin, xmax;              /* X dimensions of box being tested */
    65    double ymin, ymax;              /* X dimensions of box being tested */
    66  
    67    xmin = aCoord[0];
    68    xmax = aCoord[1];
    69    ymin = aCoord[2];
    70    ymax = aCoord[3];
    71    pCircle = (Circle *)p->pUser;
    72    if( pCircle==0 ){
    73      /* If pUser is still 0, then the parameter values have not been tested
    74      ** for correctness or stored into a Circle structure yet. Do this now. */
    75  
    76      /* This geometry callback is for use with a 2-dimensional r-tree table.
    77      ** Return an error if the table does not have exactly 2 dimensions. */
    78      if( nCoord!=4 ) return SQLITE_ERROR;
    79  
    80      /* Test that the correct number of parameters (3) have been supplied,
    81      ** and that the parameters are in range (that the radius of the circle 
    82      ** radius is greater than zero). */
    83      if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
    84  
    85      /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
    86      ** if the allocation fails. */
    87      pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
    88      if( !pCircle ) return SQLITE_NOMEM;
    89      p->xDelUser = circle_del;
    90  
    91      /* Record the center and radius of the circular region. One way that
    92      ** tested bounding boxes that intersect the circular region are detected
    93      ** is by testing if each corner of the bounding box lies within radius
    94      ** units of the center of the circle. */
    95      pCircle->centerx = p->aParam[0];
    96      pCircle->centery = p->aParam[1];
    97      pCircle->radius = p->aParam[2];
    98  
    99      /* Define two bounding box regions. The first, aBox[0], extends to
   100      ** infinity in the X dimension. It covers the same range of the Y dimension
   101      ** as the circular region. The second, aBox[1], extends to infinity in
   102      ** the Y dimension and is constrained to the range of the circle in the
   103      ** X dimension.
   104      **
   105      ** Then imagine each box is split in half along its short axis by a line
   106      ** that intersects the center of the circular region. A bounding box
   107      ** being tested can be said to intersect the circular region if it contains
   108      ** points from each half of either of the two infinite bounding boxes.
   109      */
   110      pCircle->aBox[0].xmin = pCircle->centerx;
   111      pCircle->aBox[0].xmax = pCircle->centerx;
   112      pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
   113      pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
   114      pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
   115      pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
   116      pCircle->aBox[1].ymin = pCircle->centery;
   117      pCircle->aBox[1].ymax = pCircle->centery;
   118      pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0;
   119    }
   120  
   121    /* Check if any of the 4 corners of the bounding-box being tested lie 
   122    ** inside the circular region. If they do, then the bounding-box does
   123    ** intersect the region of interest. Set the output variable to true and
   124    ** return SQLITE_OK in this case. */
   125    for(i=0; i<4; i++){
   126      double x = (i&0x01) ? xmax : xmin;
   127      double y = (i&0x02) ? ymax : ymin;
   128      double d2;
   129      
   130      d2  = (x-pCircle->centerx)*(x-pCircle->centerx);
   131      d2 += (y-pCircle->centery)*(y-pCircle->centery);
   132      if( d2<(pCircle->radius*pCircle->radius) ){
   133        *pRes = 1;
   134        return SQLITE_OK;
   135      }
   136    }
   137  
   138    /* Check if the bounding box covers any other part of the circular region.
   139    ** See comments above for a description of how this test works. If it does
   140    ** cover part of the circular region, set the output variable to true
   141    ** and return SQLITE_OK. */
   142    for(i=0; i<2; i++){
   143      if( xmin<=pCircle->aBox[i].xmin 
   144       && xmax>=pCircle->aBox[i].xmax 
   145       && ymin<=pCircle->aBox[i].ymin 
   146       && ymax>=pCircle->aBox[i].ymax 
   147      ){
   148        *pRes = 1;
   149        return SQLITE_OK;
   150      }
   151    }
   152  
   153    /* The specified bounding box does not intersect the circular region. Set
   154    ** the output variable to zero and return SQLITE_OK. */
   155    *pRes = 0;
   156    return SQLITE_OK;
   157  }
   158  
   159  /*
   160  ** Implementation of "circle" r-tree geometry callback using the 
   161  ** 2nd-generation interface that allows scoring.
   162  **
   163  ** Two calling forms:
   164  **
   165  **          Qcircle(X,Y,Radius,eType)        -- All values are doubles
   166  **          Qcircle('x:X y:Y r:R e:ETYPE')   -- Single string parameter
   167  */
   168  static int circle_query_func(sqlite3_rtree_query_info *p){
   169    int i;                          /* Iterator variable */
   170    Circle *pCircle;                /* Structure defining circular region */
   171    double xmin, xmax;              /* X dimensions of box being tested */
   172    double ymin, ymax;              /* X dimensions of box being tested */
   173    int nWithin = 0;                /* Number of corners inside the circle */
   174  
   175    xmin = p->aCoord[0];
   176    xmax = p->aCoord[1];
   177    ymin = p->aCoord[2];
   178    ymax = p->aCoord[3];
   179    pCircle = (Circle *)p->pUser;
   180    if( pCircle==0 ){
   181      /* If pUser is still 0, then the parameter values have not been tested
   182      ** for correctness or stored into a Circle structure yet. Do this now. */
   183  
   184      /* This geometry callback is for use with a 2-dimensional r-tree table.
   185      ** Return an error if the table does not have exactly 2 dimensions. */
   186      if( p->nCoord!=4 ) return SQLITE_ERROR;
   187  
   188      /* Test that the correct number of parameters (1 or 4) have been supplied.
   189      */
   190      if( p->nParam!=4 && p->nParam!=1 ) return SQLITE_ERROR;
   191  
   192      /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
   193      ** if the allocation fails. */
   194      pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
   195      if( !pCircle ) return SQLITE_NOMEM;
   196      p->xDelUser = circle_del;
   197  
   198      /* Record the center and radius of the circular region. One way that
   199      ** tested bounding boxes that intersect the circular region are detected
   200      ** is by testing if each corner of the bounding box lies within radius
   201      ** units of the center of the circle. */
   202      if( p->nParam==4 ){
   203        pCircle->centerx = p->aParam[0];
   204        pCircle->centery = p->aParam[1];
   205        pCircle->radius = p->aParam[2];
   206        pCircle->eScoreType = (int)p->aParam[3];
   207      }else{
   208        const char *z = (const char*)sqlite3_value_text(p->apSqlParam[0]);
   209        pCircle->centerx = 0.0;
   210        pCircle->centery = 0.0;
   211        pCircle->radius = 0.0;
   212        pCircle->eScoreType = 0;
   213        while( z && z[0] ){
   214          if( z[0]=='r' && z[1]==':' ){
   215            pCircle->radius = atof(&z[2]);
   216          }else if( z[0]=='x' && z[1]==':' ){
   217            pCircle->centerx = atof(&z[2]);
   218          }else if( z[0]=='y' && z[1]==':' ){
   219            pCircle->centery = atof(&z[2]);
   220          }else if( z[0]=='e' && z[1]==':' ){
   221            pCircle->eScoreType = (int)atof(&z[2]);
   222          }else if( z[0]==' ' ){
   223            z++;
   224            continue;
   225          }
   226          while( z[0]!=0 && z[0]!=' ' ) z++;
   227          while( z[0]==' ' ) z++;
   228        }
   229      }
   230      if( pCircle->radius<0.0 ){
   231        sqlite3_free(pCircle);
   232        return SQLITE_NOMEM;
   233      }
   234  
   235      /* Define two bounding box regions. The first, aBox[0], extends to
   236      ** infinity in the X dimension. It covers the same range of the Y dimension
   237      ** as the circular region. The second, aBox[1], extends to infinity in
   238      ** the Y dimension and is constrained to the range of the circle in the
   239      ** X dimension.
   240      **
   241      ** Then imagine each box is split in half along its short axis by a line
   242      ** that intersects the center of the circular region. A bounding box
   243      ** being tested can be said to intersect the circular region if it contains
   244      ** points from each half of either of the two infinite bounding boxes.
   245      */
   246      pCircle->aBox[0].xmin = pCircle->centerx;
   247      pCircle->aBox[0].xmax = pCircle->centerx;
   248      pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
   249      pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
   250      pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
   251      pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
   252      pCircle->aBox[1].ymin = pCircle->centery;
   253      pCircle->aBox[1].ymax = pCircle->centery;
   254      pCircle->mxArea = 200.0*200.0;
   255    }
   256  
   257    /* Check if any of the 4 corners of the bounding-box being tested lie 
   258    ** inside the circular region. If they do, then the bounding-box does
   259    ** intersect the region of interest. Set the output variable to true and
   260    ** return SQLITE_OK in this case. */
   261    for(i=0; i<4; i++){
   262      double x = (i&0x01) ? xmax : xmin;
   263      double y = (i&0x02) ? ymax : ymin;
   264      double d2;
   265      
   266      d2  = (x-pCircle->centerx)*(x-pCircle->centerx);
   267      d2 += (y-pCircle->centery)*(y-pCircle->centery);
   268      if( d2<(pCircle->radius*pCircle->radius) ) nWithin++;
   269    }
   270  
   271    /* Check if the bounding box covers any other part of the circular region.
   272    ** See comments above for a description of how this test works. If it does
   273    ** cover part of the circular region, set the output variable to true
   274    ** and return SQLITE_OK. */
   275    if( nWithin==0 ){
   276      for(i=0; i<2; i++){
   277        if( xmin<=pCircle->aBox[i].xmin 
   278         && xmax>=pCircle->aBox[i].xmax 
   279         && ymin<=pCircle->aBox[i].ymin 
   280         && ymax>=pCircle->aBox[i].ymax 
   281        ){
   282          nWithin = 1;
   283          break;
   284        }
   285      }
   286    }
   287  
   288    if( pCircle->eScoreType==1 ){
   289      /* Depth first search */
   290      p->rScore = p->iLevel;
   291    }else if( pCircle->eScoreType==2 ){
   292      /* Breadth first search */
   293      p->rScore = 100 - p->iLevel;
   294    }else if( pCircle->eScoreType==3 ){
   295      /* Depth-first search, except sort the leaf nodes by area with
   296      ** the largest area first */
   297      if( p->iLevel==1 ){
   298        p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea;
   299        if( p->rScore<0.01 ) p->rScore = 0.01;
   300      }else{
   301        p->rScore = 0.0;
   302      }
   303    }else if( pCircle->eScoreType==4 ){
   304      /* Depth-first search, except exclude odd rowids */
   305      p->rScore = p->iLevel;
   306      if( p->iRowid&1 ) nWithin = 0;
   307    }else{
   308      /* Breadth-first search, except exclude odd rowids */
   309      p->rScore = 100 - p->iLevel;
   310      if( p->iRowid&1 ) nWithin = 0;
   311    }
   312    if( nWithin==0 ){
   313      p->eWithin = NOT_WITHIN;
   314    }else if( nWithin>=4 ){
   315      p->eWithin = FULLY_WITHIN;
   316    }else{
   317      p->eWithin = PARTLY_WITHIN;
   318    }
   319    return SQLITE_OK;
   320  }
   321  /*
   322  ** Implementation of "breadthfirstsearch" r-tree geometry callback using the 
   323  ** 2nd-generation interface that allows scoring.
   324  **
   325  **     ... WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ...
   326  **
   327  ** It returns all entries whose bounding boxes overlap with $x0,$x1,$y0,$y1.
   328  */
   329  static int bfs_query_func(sqlite3_rtree_query_info *p){
   330    double x0,x1,y0,y1;        /* Dimensions of box being tested */
   331    double bx0,bx1,by0,by1;    /* Boundary of the query function */
   332  
   333    if( p->nParam!=4 ) return SQLITE_ERROR;
   334    x0 = p->aCoord[0];
   335    x1 = p->aCoord[1];
   336    y0 = p->aCoord[2];
   337    y1 = p->aCoord[3];
   338    bx0 = p->aParam[0];
   339    bx1 = p->aParam[1];
   340    by0 = p->aParam[2];
   341    by1 = p->aParam[3];
   342    p->rScore = 100 - p->iLevel;
   343    if( p->eParentWithin==FULLY_WITHIN ){
   344      p->eWithin = FULLY_WITHIN;
   345    }else if( x0>=bx0 && x1<=bx1 && y0>=by0 && y1<=by1 ){
   346      p->eWithin = FULLY_WITHIN;
   347    }else if( x1>=bx0 && x0<=bx1 && y1>=by0 && y0<=by1 ){
   348      p->eWithin = PARTLY_WITHIN;
   349    }else{
   350      p->eWithin = NOT_WITHIN;
   351    }
   352    return SQLITE_OK;
   353  }
   354  
   355  /* END of implementation of "circle" geometry callback.
   356  **************************************************************************
   357  *************************************************************************/
   358  
   359  #include <assert.h>
   360  #if defined(INCLUDE_SQLITE_TCL_H)
   361  #  include "sqlite_tcl.h"
   362  #else
   363  #  include "tcl.h"
   364  #endif
   365  
   366  typedef struct Cube Cube;
   367  struct Cube {
   368    double x;
   369    double y;
   370    double z;
   371    double width;
   372    double height;
   373    double depth;
   374  };
   375  
   376  static void cube_context_free(void *p){
   377    sqlite3_free(p);
   378  }
   379  
   380  /*
   381  ** The context pointer registered along with the 'cube' callback is
   382  ** always ((void *)&gHere). This is just to facilitate testing, it is not
   383  ** actually used for anything.
   384  */
   385  static int gHere = 42;
   386  
   387  /*
   388  ** Implementation of a simple r-tree geom callback to test for intersection
   389  ** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
   390  ** coordinates as follows:
   391  **
   392  **   cube(x, y, z, width, height, depth)
   393  **
   394  ** The width, height and depth parameters must all be greater than zero.
   395  */
   396  static int cube_geom(
   397    sqlite3_rtree_geometry *p,
   398    int nCoord,
   399    sqlite3_rtree_dbl *aCoord,
   400    int *piRes
   401  ){
   402    Cube *pCube = (Cube *)p->pUser;
   403  
   404    assert( p->pContext==(void *)&gHere );
   405  
   406    if( pCube==0 ){
   407      if( p->nParam!=6 || nCoord!=6
   408       || p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
   409      ){
   410        return SQLITE_ERROR;
   411      }
   412      pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
   413      if( !pCube ){
   414        return SQLITE_NOMEM;
   415      }
   416      pCube->x = p->aParam[0];
   417      pCube->y = p->aParam[1];
   418      pCube->z = p->aParam[2];
   419      pCube->width = p->aParam[3];
   420      pCube->height = p->aParam[4];
   421      pCube->depth = p->aParam[5];
   422  
   423      p->pUser = (void *)pCube;
   424      p->xDelUser = cube_context_free;
   425    }
   426  
   427    assert( nCoord==6 );
   428    *piRes = 0;
   429    if( aCoord[0]<=(pCube->x+pCube->width)
   430     && aCoord[1]>=pCube->x
   431     && aCoord[2]<=(pCube->y+pCube->height)
   432     && aCoord[3]>=pCube->y
   433     && aCoord[4]<=(pCube->z+pCube->depth)
   434     && aCoord[5]>=pCube->z
   435    ){
   436      *piRes = 1;
   437    }
   438  
   439    return SQLITE_OK;
   440  }
   441  #endif /* SQLITE_ENABLE_RTREE */
   442  
   443  static int SQLITE_TCLAPI register_cube_geom(
   444    void * clientData,
   445    Tcl_Interp *interp,
   446    int objc,
   447    Tcl_Obj *CONST objv[]
   448  ){
   449  #ifndef SQLITE_ENABLE_RTREE
   450    UNUSED_PARAMETER(clientData);
   451    UNUSED_PARAMETER(interp);
   452    UNUSED_PARAMETER(objc);
   453    UNUSED_PARAMETER(objv);
   454  #else
   455    extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
   456    extern const char *sqlite3ErrName(int);
   457    sqlite3 *db;
   458    int rc;
   459  
   460    if( objc!=2 ){
   461      Tcl_WrongNumArgs(interp, 1, objv, "DB");
   462      return TCL_ERROR;
   463    }
   464    if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
   465    rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
   466    Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
   467  #endif
   468    return TCL_OK;
   469  }
   470  
   471  static int SQLITE_TCLAPI register_circle_geom(
   472    void * clientData,
   473    Tcl_Interp *interp,
   474    int objc,
   475    Tcl_Obj *CONST objv[]
   476  ){
   477  #ifndef SQLITE_ENABLE_RTREE
   478    UNUSED_PARAMETER(clientData);
   479    UNUSED_PARAMETER(interp);
   480    UNUSED_PARAMETER(objc);
   481    UNUSED_PARAMETER(objv);
   482  #else
   483    extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
   484    extern const char *sqlite3ErrName(int);
   485    sqlite3 *db;
   486    int rc;
   487  
   488    if( objc!=2 ){
   489      Tcl_WrongNumArgs(interp, 1, objv, "DB");
   490      return TCL_ERROR;
   491    }
   492    if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
   493    rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
   494    if( rc==SQLITE_OK ){
   495      rc = sqlite3_rtree_query_callback(db, "Qcircle",
   496                                        circle_query_func, 0, 0);
   497    }
   498    if( rc==SQLITE_OK ){
   499      rc = sqlite3_rtree_query_callback(db, "breadthfirstsearch",
   500                                        bfs_query_func, 0, 0);
   501    }
   502    Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
   503  #endif
   504    return TCL_OK;
   505  }
   506  
   507  int Sqlitetestrtree_Init(Tcl_Interp *interp){
   508    Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
   509    Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0);
   510    return TCL_OK;
   511  }