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