test_rtreedoc.c 9.6 KB

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