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 typedef struct BoxGeomCtx BoxGeomCtx; 29 struct BoxGeomCtx { 30 Tcl_Interp *interp; 31 Tcl_Obj *pScript; 32 }; 33 34 typedef struct BoxQueryCtx BoxQueryCtx; 35 struct BoxQueryCtx { 36 Tcl_Interp *interp; 37 Tcl_Obj *pScript; 38 }; 39 40 static void testDelUser(void *pCtx){ 41 BoxGeomCtx *p = (BoxGeomCtx*)pCtx; 42 Tcl_EvalObjEx(p->interp, p->pScript, 0); 43 Tcl_DecrRefCount(p->pScript); 44 sqlite3_free(p); 45 } 46 47 static int invokeTclGeomCb( 48 const char *zName, 49 sqlite3_rtree_geometry *p, 50 int nCoord, 51 sqlite3_rtree_dbl *aCoord 52 ){ 53 int rc = SQLITE_OK; 54 if( p->pContext ){ 55 char aPtr[64]; 56 BoxGeomCtx *pCtx = (BoxGeomCtx*)p->pContext; 57 Tcl_Interp *interp = pCtx->interp; 58 Tcl_Obj *pScript = 0; 59 Tcl_Obj *pParam = 0; 60 Tcl_Obj *pCoord = 0; 61 int ii; 62 Tcl_Obj *pRes; 63 64 65 pScript = Tcl_DuplicateObj(pCtx->pScript); 66 Tcl_IncrRefCount(pScript); 67 Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zName,-1)); 68 69 sqlite3_snprintf(sizeof(aPtr)-1, aPtr, "%p", (void*)p->pContext); 70 Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(aPtr,-1)); 71 72 pParam = Tcl_NewObj(); 73 for(ii=0; ii<p->nParam; ii++){ 74 Tcl_ListObjAppendElement( 75 interp, pParam, Tcl_NewDoubleObj(p->aParam[ii]) 76 ); 77 } 78 Tcl_ListObjAppendElement(interp, pScript, pParam); 79 80 pCoord = Tcl_NewObj(); 81 for(ii=0; ii<nCoord; ii++){ 82 Tcl_ListObjAppendElement(interp, pCoord, Tcl_NewDoubleObj(aCoord[ii])); 83 } 84 Tcl_ListObjAppendElement(interp, pScript, pCoord); 85 86 sqlite3_snprintf(sizeof(aPtr)-1, aPtr, "%p", (void*)p); 87 Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(aPtr,-1)); 88 89 rc = Tcl_EvalObjEx(interp, pScript, 0); 90 if( rc!=TCL_OK ){ 91 rc = SQLITE_ERROR; 92 }else{ 93 int nObj = 0; 94 Tcl_Obj **aObj = 0; 95 96 pRes = Tcl_GetObjResult(interp); 97 if( Tcl_ListObjGetElements(interp, pRes, &nObj, &aObj) ) return TCL_ERROR; 98 if( nObj>0 ){ 99 const char *zCmd = Tcl_GetString(aObj[0]); 100 if( 0==sqlite3_stricmp(zCmd, "zero") ){ 101 p->aParam[0] = 0.0; 102 p->nParam = 1; 103 } 104 else if( 0==sqlite3_stricmp(zCmd, "user") ){ 105 if( p->pUser || p->xDelUser ){ 106 rc = SQLITE_ERROR; 107 }else{ 108 BoxGeomCtx *pBGCtx = sqlite3_malloc(sizeof(BoxGeomCtx)); 109 if( pBGCtx==0 ){ 110 rc = SQLITE_NOMEM; 111 }else{ 112 pBGCtx->interp = interp; 113 pBGCtx->pScript = Tcl_DuplicateObj(pRes); 114 Tcl_IncrRefCount(pBGCtx->pScript); 115 Tcl_ListObjReplace(interp, pBGCtx->pScript, 0, 1, 0, 0); 116 p->pUser = (void*)pBGCtx; 117 p->xDelUser = testDelUser; 118 } 119 } 120 } 121 else if( 0==sqlite3_stricmp(zCmd, "user_is_zero") ){ 122 if( p->pUser || p->xDelUser ) rc = SQLITE_ERROR; 123 } 124 } 125 } 126 } 127 return rc; 128 } 129 130 /* 131 # EVIDENCE-OF: R-00693-36727 The legacy xGeom callback is invoked with 132 # four arguments. 133 134 # EVIDENCE-OF: R-50437-53270 The first argument is a pointer to an 135 # sqlite3_rtree_geometry structure which provides information about how 136 # the SQL function was invoked. 137 138 # EVIDENCE-OF: R-00090-24248 The third argument, aCoord[], is an array 139 # of nCoord coordinates that defines a bounding box to be tested. 140 141 # EVIDENCE-OF: R-28207-40885 The last argument is a pointer into which 142 # the callback result should be written. 143 144 */ 145 static int box_geom( 146 sqlite3_rtree_geometry *p, /* R-50437-53270 */ 147 int nCoord, /* R-02424-24769 */ 148 sqlite3_rtree_dbl *aCoord, /* R-00090-24248 */ 149 int *pRes /* R-28207-40885 */ 150 ){ 151 int ii; 152 153 if( p->nParam!=nCoord ){ 154 invokeTclGeomCb("box", p, nCoord, aCoord); 155 return SQLITE_ERROR; 156 } 157 if( invokeTclGeomCb("box", p, nCoord, aCoord) ) return SQLITE_ERROR; 158 159 for(ii=0; ii<nCoord; ii+=2){ 160 if( aCoord[ii]>p->aParam[ii+1] || aCoord[ii+1]<p->aParam[ii] ){ 161 /* R-28207-40885 */ 162 *pRes = 0; 163 return SQLITE_OK; 164 } 165 } 166 167 /* R-28207-40885 */ 168 *pRes = 1; 169 170 return SQLITE_OK; 171 } 172 173 static int SQLITE_TCLAPI register_box_geom( 174 void * clientData, 175 Tcl_Interp *interp, 176 int objc, 177 Tcl_Obj *CONST objv[] 178 ){ 179 extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); 180 extern const char *sqlite3ErrName(int); 181 sqlite3 *db; 182 BoxGeomCtx *pCtx; 183 char aPtr[64]; 184 185 if( objc!=3 ){ 186 Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT"); 187 return TCL_ERROR; 188 } 189 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 190 191 pCtx = (BoxGeomCtx*)ckalloc(sizeof(BoxGeomCtx*)); 192 pCtx->interp = interp; 193 pCtx->pScript = Tcl_DuplicateObj(objv[2]); 194 Tcl_IncrRefCount(pCtx->pScript); 195 196 sqlite3_rtree_geometry_callback(db, "box", box_geom, (void*)pCtx); 197 198 sqlite3_snprintf(64, aPtr, "%p", (void*)pCtx); 199 Tcl_SetObjResult(interp, Tcl_NewStringObj(aPtr, -1)); 200 return TCL_OK; 201 } 202 203 static int box_query(sqlite3_rtree_query_info *pInfo){ 204 const char *azParentWithin[] = {"not", "partly", "fully", 0}; 205 BoxQueryCtx *pCtx = (BoxQueryCtx*)pInfo->pContext; 206 Tcl_Interp *interp = pCtx->interp; 207 Tcl_Obj *pEval; 208 Tcl_Obj *pArg; 209 Tcl_Obj *pTmp = 0; 210 int rc; 211 int ii; 212 213 pEval = Tcl_DuplicateObj(pCtx->pScript); 214 Tcl_IncrRefCount(pEval); 215 pArg = Tcl_NewObj(); 216 Tcl_IncrRefCount(pArg); 217 218 /* aParam[] */ 219 pTmp = Tcl_NewObj(); 220 Tcl_IncrRefCount(pTmp); 221 for(ii=0; ii<pInfo->nParam; ii++){ 222 Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aParam[ii]); 223 Tcl_ListObjAppendElement(interp, pTmp, p); 224 } 225 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aParam", -1)); 226 Tcl_ListObjAppendElement(interp, pArg, pTmp); 227 Tcl_DecrRefCount(pTmp); 228 229 /* aCoord[] */ 230 pTmp = Tcl_NewObj(); 231 Tcl_IncrRefCount(pTmp); 232 for(ii=0; ii<pInfo->nCoord; ii++){ 233 Tcl_Obj *p = Tcl_NewDoubleObj(pInfo->aCoord[ii]); 234 Tcl_ListObjAppendElement(interp, pTmp, p); 235 } 236 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("aCoord", -1)); 237 Tcl_ListObjAppendElement(interp, pArg, pTmp); 238 Tcl_DecrRefCount(pTmp); 239 240 /* anQueue[] */ 241 pTmp = Tcl_NewObj(); 242 Tcl_IncrRefCount(pTmp); 243 for(ii=0; ii<=pInfo->mxLevel; ii++){ 244 Tcl_Obj *p = Tcl_NewIntObj((int)pInfo->anQueue[ii]); 245 Tcl_ListObjAppendElement(interp, pTmp, p); 246 } 247 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("anQueue", -1)); 248 Tcl_ListObjAppendElement(interp, pArg, pTmp); 249 Tcl_DecrRefCount(pTmp); 250 251 /* iLevel */ 252 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iLevel", -1)); 253 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->iLevel)); 254 255 /* mxLevel */ 256 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("mxLevel", -1)); 257 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewIntObj(pInfo->mxLevel)); 258 259 /* iRowid */ 260 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("iRowid", -1)); 261 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewWideIntObj(pInfo->iRowid)); 262 263 /* rParentScore */ 264 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("rParentScore", -1)); 265 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewDoubleObj(pInfo->rParentScore)); 266 267 /* eParentWithin */ 268 assert( pInfo->eParentWithin==0 269 || pInfo->eParentWithin==1 270 || pInfo->eParentWithin==2 271 ); 272 Tcl_ListObjAppendElement(interp, pArg, Tcl_NewStringObj("eParentWithin", -1)); 273 Tcl_ListObjAppendElement(interp, pArg, 274 Tcl_NewStringObj(azParentWithin[pInfo->eParentWithin], -1) 275 ); 276 277 Tcl_ListObjAppendElement(interp, pEval, pArg); 278 rc = Tcl_EvalObjEx(interp, pEval, 0) ? SQLITE_ERROR : SQLITE_OK; 279 280 if( rc==SQLITE_OK ){ 281 double rScore = 0.0; 282 int nObj = 0; 283 int eP = 0; 284 Tcl_Obj **aObj = 0; 285 Tcl_Obj *pRes = Tcl_GetObjResult(interp); 286 287 if( Tcl_ListObjGetElements(interp, pRes, &nObj, &aObj) 288 || nObj!=2 289 || Tcl_GetDoubleFromObj(interp, aObj[1], &rScore) 290 || Tcl_GetIndexFromObj(interp, aObj[0], azParentWithin, "value", 0, &eP) 291 ){ 292 rc = SQLITE_ERROR; 293 }else{ 294 pInfo->rScore = rScore; 295 pInfo->eParentWithin = eP; 296 } 297 } 298 299 Tcl_DecrRefCount(pArg); 300 Tcl_DecrRefCount(pEval); 301 return rc; 302 } 303 304 static void box_query_destroy(void *p){ 305 BoxQueryCtx *pCtx = (BoxQueryCtx*)p; 306 Tcl_DecrRefCount(pCtx->pScript); 307 ckfree((char*)pCtx); 308 } 309 310 static int SQLITE_TCLAPI register_box_query( 311 void * clientData, 312 Tcl_Interp *interp, 313 int objc, 314 Tcl_Obj *CONST objv[] 315 ){ 316 extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); 317 extern const char *sqlite3ErrName(int); 318 sqlite3 *db; 319 BoxQueryCtx *pCtx; 320 321 if( objc!=3 ){ 322 Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT"); 323 return TCL_ERROR; 324 } 325 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 326 327 pCtx = (BoxQueryCtx*)ckalloc(sizeof(BoxQueryCtx)); 328 pCtx->interp = interp; 329 pCtx->pScript = Tcl_DuplicateObj(objv[2]); 330 Tcl_IncrRefCount(pCtx->pScript); 331 332 sqlite3_rtree_query_callback( 333 db, "qbox", box_query, (void*)pCtx, box_query_destroy 334 ); 335 336 Tcl_ResetResult(interp); 337 return TCL_OK; 338 } 339 #endif /* SQLITE_ENABLE_RTREE */ 340 341 342 int Sqlitetestrtreedoc_Init(Tcl_Interp *interp){ 343 #ifdef SQLITE_ENABLE_RTREE 344 Tcl_CreateObjCommand(interp, "register_box_geom", register_box_geom, 0, 0); 345 Tcl_CreateObjCommand(interp, "register_box_query", register_box_query, 0, 0); 346 #endif /* SQLITE_ENABLE_RTREE */ 347 return TCL_OK; 348 } 349