xref: /sqlite-3.40.0/ext/rtree/test_rtreedoc.c (revision 60f3657b)
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 
testDelUser(void * pCtx)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 
invokeTclGeomCb(const char * zName,sqlite3_rtree_geometry * p,int nCoord,sqlite3_rtree_dbl * aCoord)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 */
box_geom(sqlite3_rtree_geometry * p,int nCoord,sqlite3_rtree_dbl * aCoord,int * pRes)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 
register_box_geom(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
box_query(sqlite3_rtree_query_info * pInfo)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 
box_query_destroy(void * p)304 static void box_query_destroy(void *p){
305   BoxQueryCtx *pCtx = (BoxQueryCtx*)p;
306   Tcl_DecrRefCount(pCtx->pScript);
307   ckfree((char*)pCtx);
308 }
309 
register_box_query(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
Sqlitetestrtreedoc_Init(Tcl_Interp * interp)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