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 #include <tcl.h> 18 19 /* Solely for the UNUSED_PARAMETER() macro. */ 20 #include "sqliteInt.h" 21 22 #ifdef SQLITE_ENABLE_RTREE 23 /* 24 ** Type used to cache parameter information for the "circle" r-tree geometry 25 ** callback. 26 */ 27 typedef struct Circle Circle; 28 struct Circle { 29 struct Box { 30 double xmin; 31 double xmax; 32 double ymin; 33 double ymax; 34 } aBox[2]; 35 double centerx; 36 double centery; 37 double radius; 38 double mxArea; 39 }; 40 41 /* 42 ** Destructor function for Circle objects allocated by circle_geom(). 43 */ 44 static void circle_del(void *p){ 45 sqlite3_free(p); 46 } 47 48 /* 49 ** Implementation of "circle" r-tree geometry callback. 50 */ 51 static int circle_geom( 52 sqlite3_rtree_geometry *p, 53 int nCoord, 54 sqlite3_rtree_dbl *aCoord, 55 int *pRes 56 ){ 57 int i; /* Iterator variable */ 58 Circle *pCircle; /* Structure defining circular region */ 59 double xmin, xmax; /* X dimensions of box being tested */ 60 double ymin, ymax; /* X dimensions of box being tested */ 61 62 xmin = aCoord[0]; 63 xmax = aCoord[1]; 64 ymin = aCoord[2]; 65 ymax = aCoord[3]; 66 pCircle = (Circle *)p->pUser; 67 if( pCircle==0 ){ 68 /* If pUser is still 0, then the parameter values have not been tested 69 ** for correctness or stored into a Circle structure yet. Do this now. */ 70 71 /* This geometry callback is for use with a 2-dimensional r-tree table. 72 ** Return an error if the table does not have exactly 2 dimensions. */ 73 if( nCoord!=4 ) return SQLITE_ERROR; 74 75 /* Test that the correct number of parameters (3) have been supplied, 76 ** and that the parameters are in range (that the radius of the circle 77 ** radius is greater than zero). */ 78 if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR; 79 80 /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM 81 ** if the allocation fails. */ 82 pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle))); 83 if( !pCircle ) return SQLITE_NOMEM; 84 p->xDelUser = circle_del; 85 86 /* Record the center and radius of the circular region. One way that 87 ** tested bounding boxes that intersect the circular region are detected 88 ** is by testing if each corner of the bounding box lies within radius 89 ** units of the center of the circle. */ 90 pCircle->centerx = p->aParam[0]; 91 pCircle->centery = p->aParam[1]; 92 pCircle->radius = p->aParam[2]; 93 94 /* Define two bounding box regions. The first, aBox[0], extends to 95 ** infinity in the X dimension. It covers the same range of the Y dimension 96 ** as the circular region. The second, aBox[1], extends to infinity in 97 ** the Y dimension and is constrained to the range of the circle in the 98 ** X dimension. 99 ** 100 ** Then imagine each box is split in half along its short axis by a line 101 ** that intersects the center of the circular region. A bounding box 102 ** being tested can be said to intersect the circular region if it contains 103 ** points from each half of either of the two infinite bounding boxes. 104 */ 105 pCircle->aBox[0].xmin = pCircle->centerx; 106 pCircle->aBox[0].xmax = pCircle->centerx; 107 pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius; 108 pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius; 109 pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius; 110 pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius; 111 pCircle->aBox[1].ymin = pCircle->centery; 112 pCircle->aBox[1].ymax = pCircle->centery; 113 pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0; 114 } 115 116 /* Check if any of the 4 corners of the bounding-box being tested lie 117 ** inside the circular region. If they do, then the bounding-box does 118 ** intersect the region of interest. Set the output variable to true and 119 ** return SQLITE_OK in this case. */ 120 for(i=0; i<4; i++){ 121 double x = (i&0x01) ? xmax : xmin; 122 double y = (i&0x02) ? ymax : ymin; 123 double d2; 124 125 d2 = (x-pCircle->centerx)*(x-pCircle->centerx); 126 d2 += (y-pCircle->centery)*(y-pCircle->centery); 127 if( d2<(pCircle->radius*pCircle->radius) ){ 128 *pRes = 1; 129 return SQLITE_OK; 130 } 131 } 132 133 /* Check if the bounding box covers any other part of the circular region. 134 ** See comments above for a description of how this test works. If it does 135 ** cover part of the circular region, set the output variable to true 136 ** and return SQLITE_OK. */ 137 for(i=0; i<2; i++){ 138 if( xmin<=pCircle->aBox[i].xmin 139 && xmax>=pCircle->aBox[i].xmax 140 && ymin<=pCircle->aBox[i].ymin 141 && ymax>=pCircle->aBox[i].ymax 142 ){ 143 *pRes = 1; 144 return SQLITE_OK; 145 } 146 } 147 148 /* The specified bounding box does not intersect the circular region. Set 149 ** the output variable to zero and return SQLITE_OK. */ 150 *pRes = 0; 151 return SQLITE_OK; 152 } 153 154 /* 155 ** Implementation of "circle" r-tree geometry callback using the 156 ** 2nd-generation interface that allows scoring. 157 */ 158 static int circle_query_func(sqlite3_rtree_query_info *p){ 159 int i; /* Iterator variable */ 160 Circle *pCircle; /* Structure defining circular region */ 161 double xmin, xmax; /* X dimensions of box being tested */ 162 double ymin, ymax; /* X dimensions of box being tested */ 163 int nWithin = 0; /* Number of corners inside the circle */ 164 165 xmin = p->aCoord[0]; 166 xmax = p->aCoord[1]; 167 ymin = p->aCoord[2]; 168 ymax = p->aCoord[3]; 169 pCircle = (Circle *)p->pUser; 170 if( pCircle==0 ){ 171 /* If pUser is still 0, then the parameter values have not been tested 172 ** for correctness or stored into a Circle structure yet. Do this now. */ 173 174 /* This geometry callback is for use with a 2-dimensional r-tree table. 175 ** Return an error if the table does not have exactly 2 dimensions. */ 176 if( p->nCoord!=4 ) return SQLITE_ERROR; 177 178 /* Test that the correct number of parameters (3) have been supplied, 179 ** and that the parameters are in range (that the radius of the circle 180 ** radius is greater than zero). */ 181 if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR; 182 183 /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM 184 ** if the allocation fails. */ 185 pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle))); 186 if( !pCircle ) return SQLITE_NOMEM; 187 p->xDelUser = circle_del; 188 189 /* Record the center and radius of the circular region. One way that 190 ** tested bounding boxes that intersect the circular region are detected 191 ** is by testing if each corner of the bounding box lies within radius 192 ** units of the center of the circle. */ 193 pCircle->centerx = p->aParam[0]; 194 pCircle->centery = p->aParam[1]; 195 pCircle->radius = p->aParam[2]; 196 197 /* Define two bounding box regions. The first, aBox[0], extends to 198 ** infinity in the X dimension. It covers the same range of the Y dimension 199 ** as the circular region. The second, aBox[1], extends to infinity in 200 ** the Y dimension and is constrained to the range of the circle in the 201 ** X dimension. 202 ** 203 ** Then imagine each box is split in half along its short axis by a line 204 ** that intersects the center of the circular region. A bounding box 205 ** being tested can be said to intersect the circular region if it contains 206 ** points from each half of either of the two infinite bounding boxes. 207 */ 208 pCircle->aBox[0].xmin = pCircle->centerx; 209 pCircle->aBox[0].xmax = pCircle->centerx; 210 pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius; 211 pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius; 212 pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius; 213 pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius; 214 pCircle->aBox[1].ymin = pCircle->centery; 215 pCircle->aBox[1].ymax = pCircle->centery; 216 pCircle->mxArea = 200.0*200.0; 217 } 218 219 /* Check if any of the 4 corners of the bounding-box being tested lie 220 ** inside the circular region. If they do, then the bounding-box does 221 ** intersect the region of interest. Set the output variable to true and 222 ** return SQLITE_OK in this case. */ 223 for(i=0; i<4; i++){ 224 double x = (i&0x01) ? xmax : xmin; 225 double y = (i&0x02) ? ymax : ymin; 226 double d2; 227 228 d2 = (x-pCircle->centerx)*(x-pCircle->centerx); 229 d2 += (y-pCircle->centery)*(y-pCircle->centery); 230 if( d2<(pCircle->radius*pCircle->radius) ) nWithin++; 231 } 232 233 /* Check if the bounding box covers any other part of the circular region. 234 ** See comments above for a description of how this test works. If it does 235 ** cover part of the circular region, set the output variable to true 236 ** and return SQLITE_OK. */ 237 if( nWithin==0 ){ 238 for(i=0; i<2; i++){ 239 if( xmin<=pCircle->aBox[i].xmin 240 && xmax>=pCircle->aBox[i].xmax 241 && ymin<=pCircle->aBox[i].ymin 242 && ymax>=pCircle->aBox[i].ymax 243 ){ 244 nWithin = 1; 245 break; 246 } 247 } 248 } 249 250 if( p->iLevel==2 ){ 251 p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea; 252 if( p->rScore<0.01 ) p->rScore = 0.01; 253 }else{ 254 p->rScore = 0.0; 255 } 256 if( nWithin==0 ){ 257 p->eWithin = NOT_WITHIN; 258 }else if( nWithin>=4 ){ 259 p->eWithin = FULLY_WITHIN; 260 }else{ 261 p->eWithin = PARTLY_WITHIN; 262 } 263 return SQLITE_OK; 264 } 265 266 /* END of implementation of "circle" geometry callback. 267 ************************************************************************** 268 *************************************************************************/ 269 270 #include <assert.h> 271 #include "tcl.h" 272 273 typedef struct Cube Cube; 274 struct Cube { 275 double x; 276 double y; 277 double z; 278 double width; 279 double height; 280 double depth; 281 }; 282 283 static void cube_context_free(void *p){ 284 sqlite3_free(p); 285 } 286 287 /* 288 ** The context pointer registered along with the 'cube' callback is 289 ** always ((void *)&gHere). This is just to facilitate testing, it is not 290 ** actually used for anything. 291 */ 292 static int gHere = 42; 293 294 /* 295 ** Implementation of a simple r-tree geom callback to test for intersection 296 ** of r-tree rows with a "cube" shape. Cubes are defined by six scalar 297 ** coordinates as follows: 298 ** 299 ** cube(x, y, z, width, height, depth) 300 ** 301 ** The width, height and depth parameters must all be greater than zero. 302 */ 303 static int cube_geom( 304 sqlite3_rtree_geometry *p, 305 int nCoord, 306 sqlite3_rtree_dbl *aCoord, 307 int *piRes 308 ){ 309 Cube *pCube = (Cube *)p->pUser; 310 311 assert( p->pContext==(void *)&gHere ); 312 313 if( pCube==0 ){ 314 if( p->nParam!=6 || nCoord!=6 315 || p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0 316 ){ 317 return SQLITE_ERROR; 318 } 319 pCube = (Cube *)sqlite3_malloc(sizeof(Cube)); 320 if( !pCube ){ 321 return SQLITE_NOMEM; 322 } 323 pCube->x = p->aParam[0]; 324 pCube->y = p->aParam[1]; 325 pCube->z = p->aParam[2]; 326 pCube->width = p->aParam[3]; 327 pCube->height = p->aParam[4]; 328 pCube->depth = p->aParam[5]; 329 330 p->pUser = (void *)pCube; 331 p->xDelUser = cube_context_free; 332 } 333 334 assert( nCoord==6 ); 335 *piRes = 0; 336 if( aCoord[0]<=(pCube->x+pCube->width) 337 && aCoord[1]>=pCube->x 338 && aCoord[2]<=(pCube->y+pCube->height) 339 && aCoord[3]>=pCube->y 340 && aCoord[4]<=(pCube->z+pCube->depth) 341 && aCoord[5]>=pCube->z 342 ){ 343 *piRes = 1; 344 } 345 346 return SQLITE_OK; 347 } 348 #endif /* SQLITE_ENABLE_RTREE */ 349 350 static int register_cube_geom( 351 void * clientData, 352 Tcl_Interp *interp, 353 int objc, 354 Tcl_Obj *CONST objv[] 355 ){ 356 #ifndef SQLITE_ENABLE_RTREE 357 UNUSED_PARAMETER(clientData); 358 UNUSED_PARAMETER(interp); 359 UNUSED_PARAMETER(objc); 360 UNUSED_PARAMETER(objv); 361 #else 362 extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); 363 extern const char *sqlite3ErrName(int); 364 sqlite3 *db; 365 int rc; 366 367 if( objc!=2 ){ 368 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 369 return TCL_ERROR; 370 } 371 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 372 rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere); 373 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 374 #endif 375 return TCL_OK; 376 } 377 378 static int register_circle_geom( 379 void * clientData, 380 Tcl_Interp *interp, 381 int objc, 382 Tcl_Obj *CONST objv[] 383 ){ 384 #ifndef SQLITE_ENABLE_RTREE 385 UNUSED_PARAMETER(clientData); 386 UNUSED_PARAMETER(interp); 387 UNUSED_PARAMETER(objc); 388 UNUSED_PARAMETER(objv); 389 #else 390 extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); 391 extern const char *sqlite3ErrName(int); 392 sqlite3 *db; 393 int rc; 394 395 if( objc!=2 ){ 396 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 397 return TCL_ERROR; 398 } 399 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 400 rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0); 401 if( rc==SQLITE_OK ){ 402 rc = sqlite3_rtree_query_callback(db, "Qcircle", 403 circle_query_func, 0, 0); 404 } 405 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); 406 #endif 407 return TCL_OK; 408 } 409 410 int Sqlitetestrtree_Init(Tcl_Interp *interp){ 411 Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0); 412 Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0); 413 return TCL_OK; 414 } 415