18416fc7fSdrh /*
28416fc7fSdrh ** 2013-04-16
38416fc7fSdrh **
48416fc7fSdrh ** The author disclaims copyright to this source code. In place of
58416fc7fSdrh ** a legal notice, here is a blessing:
68416fc7fSdrh **
78416fc7fSdrh ** May you do good and not evil.
88416fc7fSdrh ** May you find forgiveness for yourself and forgive others.
98416fc7fSdrh ** May you share freely, never taking more than you give.
108416fc7fSdrh **
118416fc7fSdrh *************************************************************************
128416fc7fSdrh **
138416fc7fSdrh ** This file contains code for a virtual table that finds the transitive
148416fc7fSdrh ** closure of a parent/child relationship in a real table. The virtual
158416fc7fSdrh ** table is called "transitive_closure".
168416fc7fSdrh **
178416fc7fSdrh ** A transitive_closure virtual table is created like this:
188416fc7fSdrh **
198416fc7fSdrh ** CREATE VIRTUAL TABLE x USING transitive_closure(
208416fc7fSdrh ** tablename=<tablename>, -- T
218416fc7fSdrh ** idcolumn=<columnname>, -- X
228416fc7fSdrh ** parentcolumn=<columnname> -- P
238416fc7fSdrh ** );
248416fc7fSdrh **
258416fc7fSdrh ** When it is created, the new transitive_closure table may be supplied
268416fc7fSdrh ** with default values for the name of a table T and columns T.X and T.P.
278416fc7fSdrh ** The T.X and T.P columns must contain integers. The ideal case is for
288416fc7fSdrh ** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
298416fc7fSdrh ** the T.X column. The row referenced by T.P is the parent of the current row.
308416fc7fSdrh **
318416fc7fSdrh ** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
328416fc7fSdrh ** TABLE statement may be overridden in individual queries by including
338416fc7fSdrh ** terms like tablename='newtable', idcolumn='id2', or
348416fc7fSdrh ** parentcolumn='parent3' in the WHERE clause of the query.
358416fc7fSdrh **
368416fc7fSdrh ** For efficiency, it is essential that there be an index on the P column:
378416fc7fSdrh **
388416fc7fSdrh ** CREATE Tidx1 ON T(P)
398416fc7fSdrh **
408416fc7fSdrh ** Suppose a specific instance of the closure table is as follows:
418416fc7fSdrh **
428416fc7fSdrh ** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
438416fc7fSdrh ** tablename='group',
448416fc7fSdrh ** idcolumn='groupId',
458416fc7fSdrh ** parentcolumn='parentId'
468416fc7fSdrh ** );
478416fc7fSdrh **
488416fc7fSdrh ** Such an instance of the transitive_closure virtual table would be
498416fc7fSdrh ** appropriate for walking a tree defined using a table like this, for example:
508416fc7fSdrh **
518416fc7fSdrh ** CREATE TABLE group(
528416fc7fSdrh ** groupId INTEGER PRIMARY KEY,
538416fc7fSdrh ** parentId INTEGER REFERENCES group
548416fc7fSdrh ** );
558416fc7fSdrh ** CREATE INDEX group_idx1 ON group(parentId);
568416fc7fSdrh **
578416fc7fSdrh ** The group table above would presumably have other application-specific
588416fc7fSdrh ** fields. The key point here is that rows of the group table form a
598416fc7fSdrh ** tree. The purpose of the ct1 virtual table is to easily extract
608416fc7fSdrh ** branches of that tree.
618416fc7fSdrh **
628416fc7fSdrh ** Once it has been created, the ct1 virtual table can be queried
638416fc7fSdrh ** as follows:
648416fc7fSdrh **
658416fc7fSdrh ** SELECT * FROM element
668416fc7fSdrh ** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
678416fc7fSdrh **
688416fc7fSdrh ** The above query will return all elements that are part of group ?1
698416fc7fSdrh ** or children of group ?1 or grand-children of ?1 and so forth for all
708416fc7fSdrh ** descendents of group ?1. The same query can be formulated as a join:
718416fc7fSdrh **
728416fc7fSdrh ** SELECT element.* FROM element, ct1
738416fc7fSdrh ** WHERE element.groupid=ct1.id
748416fc7fSdrh ** AND ct1.root=?1;
758416fc7fSdrh **
768416fc7fSdrh ** The depth of the transitive_closure (the number of generations of
778416fc7fSdrh ** parent/child relations to follow) can be limited by setting "depth"
788416fc7fSdrh ** column in the WHERE clause. So, for example, the following query
798416fc7fSdrh ** finds only children and grandchildren but no further descendents:
808416fc7fSdrh **
818416fc7fSdrh ** SELECT element.* FROM element, ct1
828416fc7fSdrh ** WHERE element.groupid=ct1.id
838416fc7fSdrh ** AND ct1.root=?1
848416fc7fSdrh ** AND ct1.depth<=2;
858416fc7fSdrh **
868416fc7fSdrh ** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
878416fc7fSdrh ** order to find only the grandchildren of ?1, not ?1 itself or the
888416fc7fSdrh ** children of ?1.
898416fc7fSdrh **
908416fc7fSdrh ** The root=?1 term must be supplied in WHERE clause or else the query
918416fc7fSdrh ** of the ct1 virtual table will return an empty set. The tablename,
928416fc7fSdrh ** idcolumn, and parentcolumn attributes can be overridden in the WHERE
938416fc7fSdrh ** clause if desired. So, for example, the ct1 table could be repurposed
948416fc7fSdrh ** to find ancestors rather than descendents by inverting the roles of
958416fc7fSdrh ** the idcolumn and parentcolumn:
968416fc7fSdrh **
978416fc7fSdrh ** SELECT element.* FROM element, ct1
988416fc7fSdrh ** WHERE element.groupid=ct1.id
998416fc7fSdrh ** AND ct1.root=?1
1008416fc7fSdrh ** AND ct1.idcolumn='parentId'
1018416fc7fSdrh ** AND ct1.parentcolumn='groupId';
1028416fc7fSdrh **
1038416fc7fSdrh ** Multiple calls to ct1 could be combined. For example, the following
1048416fc7fSdrh ** query finds all elements that "cousins" of groupId ?1. That is to say
1058416fc7fSdrh ** elements where the groupId is a grandchild of the grandparent of ?1.
1068416fc7fSdrh ** (This definition of "cousins" also includes siblings and self.)
1078416fc7fSdrh **
1088416fc7fSdrh ** SELECT element.* FROM element, ct1
1098416fc7fSdrh ** WHERE element.groupId=ct1.id
1108416fc7fSdrh ** AND ct1.depth=2
1118416fc7fSdrh ** AND ct1.root IN (SELECT id FROM ct1
1128416fc7fSdrh ** WHERE root=?1
1138416fc7fSdrh ** AND depth=2
1148416fc7fSdrh ** AND idcolumn='parentId'
1158416fc7fSdrh ** AND parentcolumn='groupId');
1168416fc7fSdrh **
1178416fc7fSdrh ** In our example, the group.groupId column is unique and thus the
1188416fc7fSdrh ** subquery will return exactly one row. For that reason, the IN
1198416fc7fSdrh ** operator could be replaced by "=" to get the same result. But
1208416fc7fSdrh ** in the general case where the idcolumn is not unique, an IN operator
1218416fc7fSdrh ** would be required for this kind of query.
1228416fc7fSdrh **
1238416fc7fSdrh ** Note that because the tablename, idcolumn, and parentcolumn can
1248416fc7fSdrh ** all be specified in the query, it is possible for an application
1258416fc7fSdrh ** to define a single transitive_closure virtual table for use on lots
1268416fc7fSdrh ** of different hierarchy tables. One might say:
1278416fc7fSdrh **
1288416fc7fSdrh ** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
1298416fc7fSdrh **
1308416fc7fSdrh ** As each database connection is being opened. Then the application
1318416fc7fSdrh ** would always have a "closure" virtual table handy to use for querying.
1328416fc7fSdrh **
1338416fc7fSdrh ** SELECT element.* FROM element, closure
1348416fc7fSdrh ** WHERE element.groupid=ct1.id
1358416fc7fSdrh ** AND closure.root=?1
1368416fc7fSdrh ** AND closure.tablename='group'
1378416fc7fSdrh ** AND closure.idname='groupId'
1388416fc7fSdrh ** AND closure.parentname='parentId';
1398416fc7fSdrh **
1408416fc7fSdrh ** See the documentation at http://www.sqlite.org/loadext.html for information
1418416fc7fSdrh ** on how to compile and use loadable extensions such as this one.
1428416fc7fSdrh */
1438416fc7fSdrh #include "sqlite3ext.h"
1448416fc7fSdrh SQLITE_EXTENSION_INIT1
1458416fc7fSdrh #include <stdlib.h>
1468416fc7fSdrh #include <string.h>
1478416fc7fSdrh #include <assert.h>
1488416fc7fSdrh #include <stdio.h>
1498416fc7fSdrh #include <ctype.h>
1508416fc7fSdrh
15111f71d6aSdan #ifndef SQLITE_OMIT_VIRTUALTABLE
15211f71d6aSdan
1538416fc7fSdrh /*
1548416fc7fSdrh ** Forward declaration of objects used by this implementation
1558416fc7fSdrh */
1568416fc7fSdrh typedef struct closure_vtab closure_vtab;
1578416fc7fSdrh typedef struct closure_cursor closure_cursor;
1588416fc7fSdrh typedef struct closure_queue closure_queue;
1598416fc7fSdrh typedef struct closure_avl closure_avl;
1608416fc7fSdrh
1618416fc7fSdrh /*****************************************************************************
1628416fc7fSdrh ** AVL Tree implementation
1638416fc7fSdrh */
1648416fc7fSdrh /*
1658416fc7fSdrh ** Objects that want to be members of the AVL tree should embedded an
1668416fc7fSdrh ** instance of this structure.
1678416fc7fSdrh */
1688416fc7fSdrh struct closure_avl {
1698416fc7fSdrh sqlite3_int64 id; /* Id of this entry in the table */
1708416fc7fSdrh int iGeneration; /* Which generation is this entry part of */
1718416fc7fSdrh closure_avl *pList; /* A linked list of nodes */
1728416fc7fSdrh closure_avl *pBefore; /* Other elements less than id */
1738416fc7fSdrh closure_avl *pAfter; /* Other elements greater than id */
1748416fc7fSdrh closure_avl *pUp; /* Parent element */
1758416fc7fSdrh short int height; /* Height of this node. Leaf==1 */
1768416fc7fSdrh short int imbalance; /* Height difference between pBefore and pAfter */
1778416fc7fSdrh };
1788416fc7fSdrh
1798416fc7fSdrh /* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
1808416fc7fSdrh ** Assume that the children of p have correct heights.
1818416fc7fSdrh */
closureAvlRecomputeHeight(closure_avl * p)1828416fc7fSdrh static void closureAvlRecomputeHeight(closure_avl *p){
1838416fc7fSdrh short int hBefore = p->pBefore ? p->pBefore->height : 0;
1848416fc7fSdrh short int hAfter = p->pAfter ? p->pAfter->height : 0;
1858416fc7fSdrh p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
1868416fc7fSdrh p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
1878416fc7fSdrh }
1888416fc7fSdrh
1898416fc7fSdrh /*
1908416fc7fSdrh ** P B
1918416fc7fSdrh ** / \ / \
1928416fc7fSdrh ** B Z ==> X P
1938416fc7fSdrh ** / \ / \
1948416fc7fSdrh ** X Y Y Z
1958416fc7fSdrh **
1968416fc7fSdrh */
closureAvlRotateBefore(closure_avl * pP)1978416fc7fSdrh static closure_avl *closureAvlRotateBefore(closure_avl *pP){
1988416fc7fSdrh closure_avl *pB = pP->pBefore;
1998416fc7fSdrh closure_avl *pY = pB->pAfter;
2008416fc7fSdrh pB->pUp = pP->pUp;
2018416fc7fSdrh pB->pAfter = pP;
2028416fc7fSdrh pP->pUp = pB;
2038416fc7fSdrh pP->pBefore = pY;
2048416fc7fSdrh if( pY ) pY->pUp = pP;
2058416fc7fSdrh closureAvlRecomputeHeight(pP);
2068416fc7fSdrh closureAvlRecomputeHeight(pB);
2078416fc7fSdrh return pB;
2088416fc7fSdrh }
2098416fc7fSdrh
2108416fc7fSdrh /*
2118416fc7fSdrh ** P A
2128416fc7fSdrh ** / \ / \
2138416fc7fSdrh ** X A ==> P Z
2148416fc7fSdrh ** / \ / \
2158416fc7fSdrh ** Y Z X Y
2168416fc7fSdrh **
2178416fc7fSdrh */
closureAvlRotateAfter(closure_avl * pP)2188416fc7fSdrh static closure_avl *closureAvlRotateAfter(closure_avl *pP){
2198416fc7fSdrh closure_avl *pA = pP->pAfter;
2208416fc7fSdrh closure_avl *pY = pA->pBefore;
2218416fc7fSdrh pA->pUp = pP->pUp;
2228416fc7fSdrh pA->pBefore = pP;
2238416fc7fSdrh pP->pUp = pA;
2248416fc7fSdrh pP->pAfter = pY;
2258416fc7fSdrh if( pY ) pY->pUp = pP;
2268416fc7fSdrh closureAvlRecomputeHeight(pP);
2278416fc7fSdrh closureAvlRecomputeHeight(pA);
2288416fc7fSdrh return pA;
2298416fc7fSdrh }
2308416fc7fSdrh
2318416fc7fSdrh /*
2328416fc7fSdrh ** Return a pointer to the pBefore or pAfter pointer in the parent
2338416fc7fSdrh ** of p that points to p. Or if p is the root node, return pp.
2348416fc7fSdrh */
closureAvlFromPtr(closure_avl * p,closure_avl ** pp)2358416fc7fSdrh static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
2368416fc7fSdrh closure_avl *pUp = p->pUp;
2378416fc7fSdrh if( pUp==0 ) return pp;
2388416fc7fSdrh if( pUp->pAfter==p ) return &pUp->pAfter;
2398416fc7fSdrh return &pUp->pBefore;
2408416fc7fSdrh }
2418416fc7fSdrh
2428416fc7fSdrh /*
2438416fc7fSdrh ** Rebalance all nodes starting with p and working up to the root.
2448416fc7fSdrh ** Return the new root.
2458416fc7fSdrh */
closureAvlBalance(closure_avl * p)2468416fc7fSdrh static closure_avl *closureAvlBalance(closure_avl *p){
2478416fc7fSdrh closure_avl *pTop = p;
2488416fc7fSdrh closure_avl **pp;
2498416fc7fSdrh while( p ){
2508416fc7fSdrh closureAvlRecomputeHeight(p);
2518416fc7fSdrh if( p->imbalance>=2 ){
2528416fc7fSdrh closure_avl *pB = p->pBefore;
2538416fc7fSdrh if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
2548416fc7fSdrh pp = closureAvlFromPtr(p,&p);
2558416fc7fSdrh p = *pp = closureAvlRotateBefore(p);
2568416fc7fSdrh }else if( p->imbalance<=(-2) ){
2578416fc7fSdrh closure_avl *pA = p->pAfter;
2588416fc7fSdrh if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
2598416fc7fSdrh pp = closureAvlFromPtr(p,&p);
2608416fc7fSdrh p = *pp = closureAvlRotateAfter(p);
2618416fc7fSdrh }
2628416fc7fSdrh pTop = p;
2638416fc7fSdrh p = p->pUp;
2648416fc7fSdrh }
2658416fc7fSdrh return pTop;
2668416fc7fSdrh }
2678416fc7fSdrh
2688416fc7fSdrh /* Search the tree rooted at p for an entry with id. Return a pointer
2698416fc7fSdrh ** to the entry or return NULL.
2708416fc7fSdrh */
closureAvlSearch(closure_avl * p,sqlite3_int64 id)2718416fc7fSdrh static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
2728416fc7fSdrh while( p && id!=p->id ){
2738416fc7fSdrh p = (id<p->id) ? p->pBefore : p->pAfter;
2748416fc7fSdrh }
2758416fc7fSdrh return p;
2768416fc7fSdrh }
2778416fc7fSdrh
2788416fc7fSdrh /* Find the first node (the one with the smallest key).
2798416fc7fSdrh */
closureAvlFirst(closure_avl * p)2808416fc7fSdrh static closure_avl *closureAvlFirst(closure_avl *p){
2818416fc7fSdrh if( p ) while( p->pBefore ) p = p->pBefore;
2828416fc7fSdrh return p;
2838416fc7fSdrh }
2848416fc7fSdrh
2858416fc7fSdrh /* Return the node with the next larger key after p.
2868416fc7fSdrh */
closureAvlNext(closure_avl * p)2878416fc7fSdrh closure_avl *closureAvlNext(closure_avl *p){
2888416fc7fSdrh closure_avl *pPrev = 0;
2898416fc7fSdrh while( p && p->pAfter==pPrev ){
2908416fc7fSdrh pPrev = p;
2918416fc7fSdrh p = p->pUp;
2928416fc7fSdrh }
2938416fc7fSdrh if( p && pPrev==0 ){
2948416fc7fSdrh p = closureAvlFirst(p->pAfter);
2958416fc7fSdrh }
2968416fc7fSdrh return p;
2978416fc7fSdrh }
2988416fc7fSdrh
2998416fc7fSdrh /* Insert a new node pNew. Return NULL on success. If the key is not
3008416fc7fSdrh ** unique, then do not perform the insert but instead leave pNew unchanged
3018416fc7fSdrh ** and return a pointer to an existing node with the same key.
3028416fc7fSdrh */
closureAvlInsert(closure_avl ** ppHead,closure_avl * pNew)3038416fc7fSdrh static closure_avl *closureAvlInsert(
3048416fc7fSdrh closure_avl **ppHead, /* Head of the tree */
3058416fc7fSdrh closure_avl *pNew /* New node to be inserted */
3068416fc7fSdrh ){
3078416fc7fSdrh closure_avl *p = *ppHead;
3088416fc7fSdrh if( p==0 ){
3098416fc7fSdrh p = pNew;
3108416fc7fSdrh pNew->pUp = 0;
3118416fc7fSdrh }else{
3128416fc7fSdrh while( p ){
3138416fc7fSdrh if( pNew->id<p->id ){
3148416fc7fSdrh if( p->pBefore ){
3158416fc7fSdrh p = p->pBefore;
3168416fc7fSdrh }else{
3178416fc7fSdrh p->pBefore = pNew;
3188416fc7fSdrh pNew->pUp = p;
3198416fc7fSdrh break;
3208416fc7fSdrh }
3218416fc7fSdrh }else if( pNew->id>p->id ){
3228416fc7fSdrh if( p->pAfter ){
3238416fc7fSdrh p = p->pAfter;
3248416fc7fSdrh }else{
3258416fc7fSdrh p->pAfter = pNew;
3268416fc7fSdrh pNew->pUp = p;
3278416fc7fSdrh break;
3288416fc7fSdrh }
3298416fc7fSdrh }else{
3308416fc7fSdrh return p;
3318416fc7fSdrh }
3328416fc7fSdrh }
3338416fc7fSdrh }
3348416fc7fSdrh pNew->pBefore = 0;
3358416fc7fSdrh pNew->pAfter = 0;
3368416fc7fSdrh pNew->height = 1;
3378416fc7fSdrh pNew->imbalance = 0;
3388416fc7fSdrh *ppHead = closureAvlBalance(p);
3398416fc7fSdrh return 0;
3408416fc7fSdrh }
3418416fc7fSdrh
3428416fc7fSdrh /* Walk the tree can call xDestroy on each node
3438416fc7fSdrh */
closureAvlDestroy(closure_avl * p,void (* xDestroy)(closure_avl *))3448416fc7fSdrh static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
3458416fc7fSdrh if( p ){
3468416fc7fSdrh closureAvlDestroy(p->pBefore, xDestroy);
3478416fc7fSdrh closureAvlDestroy(p->pAfter, xDestroy);
3488416fc7fSdrh xDestroy(p);
3498416fc7fSdrh }
3508416fc7fSdrh }
3518416fc7fSdrh /*
3528416fc7fSdrh ** End of the AVL Tree implementation
3538416fc7fSdrh ******************************************************************************/
3548416fc7fSdrh
3558416fc7fSdrh /*
3568416fc7fSdrh ** A closure virtual-table object
3578416fc7fSdrh */
3588416fc7fSdrh struct closure_vtab {
3598416fc7fSdrh sqlite3_vtab base; /* Base class - must be first */
3608416fc7fSdrh char *zDb; /* Name of database. (ex: "main") */
3618416fc7fSdrh char *zSelf; /* Name of this virtual table */
3628416fc7fSdrh char *zTableName; /* Name of table holding parent/child relation */
3638416fc7fSdrh char *zIdColumn; /* Name of ID column of zTableName */
3648416fc7fSdrh char *zParentColumn; /* Name of PARENT column in zTableName */
3658416fc7fSdrh sqlite3 *db; /* The database connection */
3668416fc7fSdrh int nCursor; /* Number of pending cursors */
3678416fc7fSdrh };
3688416fc7fSdrh
3698416fc7fSdrh /* A closure cursor object */
3708416fc7fSdrh struct closure_cursor {
3718416fc7fSdrh sqlite3_vtab_cursor base; /* Base class - must be first */
3728416fc7fSdrh closure_vtab *pVtab; /* The virtual table this cursor belongs to */
3738416fc7fSdrh char *zTableName; /* Name of table holding parent/child relation */
3748416fc7fSdrh char *zIdColumn; /* Name of ID column of zTableName */
3758416fc7fSdrh char *zParentColumn; /* Name of PARENT column in zTableName */
3768416fc7fSdrh closure_avl *pCurrent; /* Current element of output */
3778416fc7fSdrh closure_avl *pClosure; /* The complete closure tree */
3788416fc7fSdrh };
3798416fc7fSdrh
3808416fc7fSdrh /* A queue of AVL nodes */
3818416fc7fSdrh struct closure_queue {
3828416fc7fSdrh closure_avl *pFirst; /* Oldest node on the queue */
3838416fc7fSdrh closure_avl *pLast; /* Youngest node on the queue */
3848416fc7fSdrh };
3858416fc7fSdrh
3868416fc7fSdrh /*
3878416fc7fSdrh ** Add a node to the end of the queue
3888416fc7fSdrh */
queuePush(closure_queue * pQueue,closure_avl * pNode)3898416fc7fSdrh static void queuePush(closure_queue *pQueue, closure_avl *pNode){
3908416fc7fSdrh pNode->pList = 0;
3918416fc7fSdrh if( pQueue->pLast ){
3928416fc7fSdrh pQueue->pLast->pList = pNode;
3938416fc7fSdrh }else{
3948416fc7fSdrh pQueue->pFirst = pNode;
3958416fc7fSdrh }
3968416fc7fSdrh pQueue->pLast = pNode;
3978416fc7fSdrh }
3988416fc7fSdrh
3998416fc7fSdrh /*
4008416fc7fSdrh ** Extract the oldest element (the front element) from the queue.
4018416fc7fSdrh */
queuePull(closure_queue * pQueue)4028416fc7fSdrh static closure_avl *queuePull(closure_queue *pQueue){
4038416fc7fSdrh closure_avl *p = pQueue->pFirst;
4048416fc7fSdrh if( p ){
4058416fc7fSdrh pQueue->pFirst = p->pList;
4068416fc7fSdrh if( pQueue->pFirst==0 ) pQueue->pLast = 0;
4078416fc7fSdrh }
4088416fc7fSdrh return p;
4098416fc7fSdrh }
4108416fc7fSdrh
4118416fc7fSdrh /*
4128416fc7fSdrh ** This function converts an SQL quoted string into an unquoted string
4138416fc7fSdrh ** and returns a pointer to a buffer allocated using sqlite3_malloc()
4148416fc7fSdrh ** containing the result. The caller should eventually free this buffer
4158416fc7fSdrh ** using sqlite3_free.
4168416fc7fSdrh **
4178416fc7fSdrh ** Examples:
4188416fc7fSdrh **
4198416fc7fSdrh ** "abc" becomes abc
4208416fc7fSdrh ** 'xyz' becomes xyz
4218416fc7fSdrh ** [pqr] becomes pqr
4228416fc7fSdrh ** `mno` becomes mno
4238416fc7fSdrh */
closureDequote(const char * zIn)4248416fc7fSdrh static char *closureDequote(const char *zIn){
4252d77d80aSdrh sqlite3_int64 nIn; /* Size of input string, in bytes */
4268416fc7fSdrh char *zOut; /* Output (dequoted) string */
4278416fc7fSdrh
4282d77d80aSdrh nIn = strlen(zIn);
4292d77d80aSdrh zOut = sqlite3_malloc64(nIn+1);
4308416fc7fSdrh if( zOut ){
4318416fc7fSdrh char q = zIn[0]; /* Quote character (if any ) */
4328416fc7fSdrh
4338416fc7fSdrh if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
434*065f3bf4Smistachkin memcpy(zOut, zIn, (size_t)(nIn+1));
4358416fc7fSdrh }else{
4368416fc7fSdrh int iOut = 0; /* Index of next byte to write to output */
4378416fc7fSdrh int iIn; /* Index of next byte to read from input */
4388416fc7fSdrh
4398416fc7fSdrh if( q=='[' ) q = ']';
4408416fc7fSdrh for(iIn=1; iIn<nIn; iIn++){
4418416fc7fSdrh if( zIn[iIn]==q ) iIn++;
4428416fc7fSdrh zOut[iOut++] = zIn[iIn];
4438416fc7fSdrh }
4448416fc7fSdrh }
4458416fc7fSdrh assert( (int)strlen(zOut)<=nIn );
4468416fc7fSdrh }
4478416fc7fSdrh return zOut;
4488416fc7fSdrh }
4498416fc7fSdrh
4508416fc7fSdrh /*
4518416fc7fSdrh ** Deallocate an closure_vtab object
4528416fc7fSdrh */
closureFree(closure_vtab * p)4538416fc7fSdrh static void closureFree(closure_vtab *p){
4548416fc7fSdrh if( p ){
4558416fc7fSdrh sqlite3_free(p->zDb);
4568416fc7fSdrh sqlite3_free(p->zSelf);
4578416fc7fSdrh sqlite3_free(p->zTableName);
4588416fc7fSdrh sqlite3_free(p->zIdColumn);
4598416fc7fSdrh sqlite3_free(p->zParentColumn);
4608416fc7fSdrh memset(p, 0, sizeof(*p));
4618416fc7fSdrh sqlite3_free(p);
4628416fc7fSdrh }
4638416fc7fSdrh }
4648416fc7fSdrh
4658416fc7fSdrh /*
4668416fc7fSdrh ** xDisconnect/xDestroy method for the closure module.
4678416fc7fSdrh */
closureDisconnect(sqlite3_vtab * pVtab)4688416fc7fSdrh static int closureDisconnect(sqlite3_vtab *pVtab){
4698416fc7fSdrh closure_vtab *p = (closure_vtab*)pVtab;
4708416fc7fSdrh assert( p->nCursor==0 );
4718416fc7fSdrh closureFree(p);
4728416fc7fSdrh return SQLITE_OK;
4738416fc7fSdrh }
4748416fc7fSdrh
4758416fc7fSdrh /*
4768416fc7fSdrh ** Check to see if the argument is of the form:
4778416fc7fSdrh **
4788416fc7fSdrh ** KEY = VALUE
4798416fc7fSdrh **
4808416fc7fSdrh ** If it is, return a pointer to the first character of VALUE.
4818416fc7fSdrh ** If not, return NULL. Spaces around the = are ignored.
4828416fc7fSdrh */
closureValueOfKey(const char * zKey,const char * zStr)4838416fc7fSdrh static const char *closureValueOfKey(const char *zKey, const char *zStr){
4848416fc7fSdrh int nKey = (int)strlen(zKey);
4858416fc7fSdrh int nStr = (int)strlen(zStr);
4868416fc7fSdrh int i;
4878416fc7fSdrh if( nStr<nKey+1 ) return 0;
4888416fc7fSdrh if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
489c56fac74Sdrh for(i=nKey; isspace((unsigned char)zStr[i]); i++){}
4908416fc7fSdrh if( zStr[i]!='=' ) return 0;
4918416fc7fSdrh i++;
492c56fac74Sdrh while( isspace((unsigned char)zStr[i]) ){ i++; }
4938416fc7fSdrh return zStr+i;
4948416fc7fSdrh }
4958416fc7fSdrh
4968416fc7fSdrh /*
4978416fc7fSdrh ** xConnect/xCreate method for the closure module. Arguments are:
4988416fc7fSdrh **
49947af6e76Sdrh ** argv[0] -> module name ("transitive_closure")
5008416fc7fSdrh ** argv[1] -> database name
5018416fc7fSdrh ** argv[2] -> table name
5028416fc7fSdrh ** argv[3...] -> arguments
5038416fc7fSdrh */
closureConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)5048416fc7fSdrh static int closureConnect(
5058416fc7fSdrh sqlite3 *db,
5068416fc7fSdrh void *pAux,
5078416fc7fSdrh int argc, const char *const*argv,
5088416fc7fSdrh sqlite3_vtab **ppVtab,
5098416fc7fSdrh char **pzErr
5108416fc7fSdrh ){
5118416fc7fSdrh int rc = SQLITE_OK; /* Return code */
5128416fc7fSdrh closure_vtab *pNew = 0; /* New virtual table */
5138416fc7fSdrh const char *zDb = argv[1];
5148416fc7fSdrh const char *zVal;
5158416fc7fSdrh int i;
5168416fc7fSdrh
5178416fc7fSdrh (void)pAux;
5188416fc7fSdrh *ppVtab = 0;
5198416fc7fSdrh pNew = sqlite3_malloc( sizeof(*pNew) );
5208416fc7fSdrh if( pNew==0 ) return SQLITE_NOMEM;
5218416fc7fSdrh rc = SQLITE_NOMEM;
5228416fc7fSdrh memset(pNew, 0, sizeof(*pNew));
5238416fc7fSdrh pNew->db = db;
5248416fc7fSdrh pNew->zDb = sqlite3_mprintf("%s", zDb);
5258416fc7fSdrh if( pNew->zDb==0 ) goto closureConnectError;
5268416fc7fSdrh pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
5278416fc7fSdrh if( pNew->zSelf==0 ) goto closureConnectError;
5288416fc7fSdrh for(i=3; i<argc; i++){
5298416fc7fSdrh zVal = closureValueOfKey("tablename", argv[i]);
5308416fc7fSdrh if( zVal ){
5318416fc7fSdrh sqlite3_free(pNew->zTableName);
5328416fc7fSdrh pNew->zTableName = closureDequote(zVal);
5338416fc7fSdrh if( pNew->zTableName==0 ) goto closureConnectError;
5348416fc7fSdrh continue;
5358416fc7fSdrh }
5368416fc7fSdrh zVal = closureValueOfKey("idcolumn", argv[i]);
5378416fc7fSdrh if( zVal ){
5388416fc7fSdrh sqlite3_free(pNew->zIdColumn);
5398416fc7fSdrh pNew->zIdColumn = closureDequote(zVal);
5408416fc7fSdrh if( pNew->zIdColumn==0 ) goto closureConnectError;
5418416fc7fSdrh continue;
5428416fc7fSdrh }
5438416fc7fSdrh zVal = closureValueOfKey("parentcolumn", argv[i]);
5448416fc7fSdrh if( zVal ){
5458416fc7fSdrh sqlite3_free(pNew->zParentColumn);
5468416fc7fSdrh pNew->zParentColumn = closureDequote(zVal);
5478416fc7fSdrh if( pNew->zParentColumn==0 ) goto closureConnectError;
5488416fc7fSdrh continue;
5498416fc7fSdrh }
5508416fc7fSdrh *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
5518416fc7fSdrh closureFree(pNew);
5528416fc7fSdrh *ppVtab = 0;
5538416fc7fSdrh return SQLITE_ERROR;
5548416fc7fSdrh }
5558416fc7fSdrh rc = sqlite3_declare_vtab(db,
5568416fc7fSdrh "CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
5578416fc7fSdrh "idcolumn HIDDEN,parentcolumn HIDDEN)"
5588416fc7fSdrh );
5598416fc7fSdrh #define CLOSURE_COL_ID 0
5608416fc7fSdrh #define CLOSURE_COL_DEPTH 1
5618416fc7fSdrh #define CLOSURE_COL_ROOT 2
5628416fc7fSdrh #define CLOSURE_COL_TABLENAME 3
5638416fc7fSdrh #define CLOSURE_COL_IDCOLUMN 4
5648416fc7fSdrh #define CLOSURE_COL_PARENTCOLUMN 5
5658416fc7fSdrh if( rc!=SQLITE_OK ){
5668416fc7fSdrh closureFree(pNew);
5678416fc7fSdrh }
5688416fc7fSdrh *ppVtab = &pNew->base;
5698416fc7fSdrh return rc;
5708416fc7fSdrh
5718416fc7fSdrh closureConnectError:
5728416fc7fSdrh closureFree(pNew);
5738416fc7fSdrh return rc;
5748416fc7fSdrh }
5758416fc7fSdrh
5768416fc7fSdrh /*
5778416fc7fSdrh ** Open a new closure cursor.
5788416fc7fSdrh */
closureOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)5798416fc7fSdrh static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
5808416fc7fSdrh closure_vtab *p = (closure_vtab*)pVTab;
5818416fc7fSdrh closure_cursor *pCur;
5828416fc7fSdrh pCur = sqlite3_malloc( sizeof(*pCur) );
5838416fc7fSdrh if( pCur==0 ) return SQLITE_NOMEM;
5848416fc7fSdrh memset(pCur, 0, sizeof(*pCur));
5858416fc7fSdrh pCur->pVtab = p;
5868416fc7fSdrh *ppCursor = &pCur->base;
5878416fc7fSdrh p->nCursor++;
5888416fc7fSdrh return SQLITE_OK;
5898416fc7fSdrh }
5908416fc7fSdrh
5918416fc7fSdrh /*
5928416fc7fSdrh ** Free up all the memory allocated by a cursor. Set it rLimit to 0
5938416fc7fSdrh ** to indicate that it is at EOF.
5948416fc7fSdrh */
closureClearCursor(closure_cursor * pCur)5958416fc7fSdrh static void closureClearCursor(closure_cursor *pCur){
5968416fc7fSdrh closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
5978416fc7fSdrh sqlite3_free(pCur->zTableName);
5988416fc7fSdrh sqlite3_free(pCur->zIdColumn);
5998416fc7fSdrh sqlite3_free(pCur->zParentColumn);
6008416fc7fSdrh pCur->zTableName = 0;
6018416fc7fSdrh pCur->zIdColumn = 0;
6028416fc7fSdrh pCur->zParentColumn = 0;
6038416fc7fSdrh pCur->pCurrent = 0;
6048416fc7fSdrh pCur->pClosure = 0;
6058416fc7fSdrh }
6068416fc7fSdrh
6078416fc7fSdrh /*
6088416fc7fSdrh ** Close a closure cursor.
6098416fc7fSdrh */
closureClose(sqlite3_vtab_cursor * cur)6108416fc7fSdrh static int closureClose(sqlite3_vtab_cursor *cur){
6118416fc7fSdrh closure_cursor *pCur = (closure_cursor *)cur;
6128416fc7fSdrh closureClearCursor(pCur);
6138416fc7fSdrh pCur->pVtab->nCursor--;
6148416fc7fSdrh sqlite3_free(pCur);
6158416fc7fSdrh return SQLITE_OK;
6168416fc7fSdrh }
6178416fc7fSdrh
6188416fc7fSdrh /*
6198416fc7fSdrh ** Advance a cursor to its next row of output
6208416fc7fSdrh */
closureNext(sqlite3_vtab_cursor * cur)6218416fc7fSdrh static int closureNext(sqlite3_vtab_cursor *cur){
6228416fc7fSdrh closure_cursor *pCur = (closure_cursor*)cur;
6238416fc7fSdrh pCur->pCurrent = closureAvlNext(pCur->pCurrent);
6248416fc7fSdrh return SQLITE_OK;
6258416fc7fSdrh }
6268416fc7fSdrh
6278416fc7fSdrh /*
6288416fc7fSdrh ** Allocate and insert a node
6298416fc7fSdrh */
closureInsertNode(closure_queue * pQueue,closure_cursor * pCur,sqlite3_int64 id,int iGeneration)6308416fc7fSdrh static int closureInsertNode(
6318416fc7fSdrh closure_queue *pQueue, /* Add new node to this queue */
6328416fc7fSdrh closure_cursor *pCur, /* The cursor into which to add the node */
6338416fc7fSdrh sqlite3_int64 id, /* The node ID */
6348416fc7fSdrh int iGeneration /* The generation number for this node */
6358416fc7fSdrh ){
6368416fc7fSdrh closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
6378416fc7fSdrh if( pNew==0 ) return SQLITE_NOMEM;
6388416fc7fSdrh memset(pNew, 0, sizeof(*pNew));
6398416fc7fSdrh pNew->id = id;
6408416fc7fSdrh pNew->iGeneration = iGeneration;
6418416fc7fSdrh closureAvlInsert(&pCur->pClosure, pNew);
6428416fc7fSdrh queuePush(pQueue, pNew);
6438416fc7fSdrh return SQLITE_OK;
6448416fc7fSdrh }
6458416fc7fSdrh
6468416fc7fSdrh /*
6478416fc7fSdrh ** Called to "rewind" a cursor back to the beginning so that
6488416fc7fSdrh ** it starts its output over again. Always called at least once
6498416fc7fSdrh ** prior to any closureColumn, closureRowid, or closureEof call.
6508416fc7fSdrh **
6518416fc7fSdrh ** This routine actually computes the closure.
6528416fc7fSdrh **
6538416fc7fSdrh ** See the comment at the beginning of closureBestIndex() for a
6548416fc7fSdrh ** description of the meaning of idxNum. The idxStr parameter is
6558416fc7fSdrh ** not used.
6568416fc7fSdrh */
closureFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)6578416fc7fSdrh static int closureFilter(
6588416fc7fSdrh sqlite3_vtab_cursor *pVtabCursor,
6598416fc7fSdrh int idxNum, const char *idxStr,
6608416fc7fSdrh int argc, sqlite3_value **argv
6618416fc7fSdrh ){
6628416fc7fSdrh closure_cursor *pCur = (closure_cursor *)pVtabCursor;
6638416fc7fSdrh closure_vtab *pVtab = pCur->pVtab;
6648416fc7fSdrh sqlite3_int64 iRoot;
6658416fc7fSdrh int mxGen = 999999999;
6668416fc7fSdrh char *zSql;
6678416fc7fSdrh sqlite3_stmt *pStmt;
6688416fc7fSdrh closure_avl *pAvl;
6698416fc7fSdrh int rc = SQLITE_OK;
6708416fc7fSdrh const char *zTableName = pVtab->zTableName;
6718416fc7fSdrh const char *zIdColumn = pVtab->zIdColumn;
6728416fc7fSdrh const char *zParentColumn = pVtab->zParentColumn;
6738416fc7fSdrh closure_queue sQueue;
6748416fc7fSdrh
6758416fc7fSdrh (void)idxStr; /* Unused parameter */
6768416fc7fSdrh (void)argc; /* Unused parameter */
6778416fc7fSdrh closureClearCursor(pCur);
6788416fc7fSdrh memset(&sQueue, 0, sizeof(sQueue));
6798416fc7fSdrh if( (idxNum & 1)==0 ){
6808416fc7fSdrh /* No root=$root in the WHERE clause. Return an empty set */
6818416fc7fSdrh return SQLITE_OK;
6828416fc7fSdrh }
6838416fc7fSdrh iRoot = sqlite3_value_int64(argv[0]);
6848416fc7fSdrh if( (idxNum & 0x000f0)!=0 ){
6858416fc7fSdrh mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
6868416fc7fSdrh if( (idxNum & 0x00002)!=0 ) mxGen--;
6878416fc7fSdrh }
6888416fc7fSdrh if( (idxNum & 0x00f00)!=0 ){
6898416fc7fSdrh zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
6908416fc7fSdrh pCur->zTableName = sqlite3_mprintf("%s", zTableName);
6918416fc7fSdrh }
6928416fc7fSdrh if( (idxNum & 0x0f000)!=0 ){
6938416fc7fSdrh zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
6948416fc7fSdrh pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
6958416fc7fSdrh }
6968416fc7fSdrh if( (idxNum & 0x0f0000)!=0 ){
6978416fc7fSdrh zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
6988416fc7fSdrh pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
6998416fc7fSdrh }
7008416fc7fSdrh
7018416fc7fSdrh zSql = sqlite3_mprintf(
7028416fc7fSdrh "SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
7038416fc7fSdrh zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
7048416fc7fSdrh if( zSql==0 ){
7058416fc7fSdrh return SQLITE_NOMEM;
7068416fc7fSdrh }else{
7078416fc7fSdrh rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
7088416fc7fSdrh sqlite3_free(zSql);
7098416fc7fSdrh if( rc ){
7108416fc7fSdrh sqlite3_free(pVtab->base.zErrMsg);
7118416fc7fSdrh pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
7128416fc7fSdrh return rc;
7138416fc7fSdrh }
7148416fc7fSdrh }
7158416fc7fSdrh if( rc==SQLITE_OK ){
7168416fc7fSdrh rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
7178416fc7fSdrh }
7188416fc7fSdrh while( (pAvl = queuePull(&sQueue))!=0 ){
7198416fc7fSdrh if( pAvl->iGeneration>=mxGen ) continue;
7208416fc7fSdrh sqlite3_bind_int64(pStmt, 1, pAvl->id);
7218416fc7fSdrh while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
7228416fc7fSdrh if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
7238416fc7fSdrh sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
7248416fc7fSdrh if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
7258416fc7fSdrh rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
7268416fc7fSdrh }
7278416fc7fSdrh }
7288416fc7fSdrh }
7298416fc7fSdrh sqlite3_reset(pStmt);
7308416fc7fSdrh }
7318416fc7fSdrh sqlite3_finalize(pStmt);
7328416fc7fSdrh if( rc==SQLITE_OK ){
7338416fc7fSdrh pCur->pCurrent = closureAvlFirst(pCur->pClosure);
7348416fc7fSdrh }
7358416fc7fSdrh
7368416fc7fSdrh return rc;
7378416fc7fSdrh }
7388416fc7fSdrh
7398416fc7fSdrh /*
7408416fc7fSdrh ** Only the word and distance columns have values. All other columns
7418416fc7fSdrh ** return NULL
7428416fc7fSdrh */
closureColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)7438416fc7fSdrh static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
7448416fc7fSdrh closure_cursor *pCur = (closure_cursor*)cur;
7458416fc7fSdrh switch( i ){
7468416fc7fSdrh case CLOSURE_COL_ID: {
7478416fc7fSdrh sqlite3_result_int64(ctx, pCur->pCurrent->id);
7488416fc7fSdrh break;
7498416fc7fSdrh }
7508416fc7fSdrh case CLOSURE_COL_DEPTH: {
7518416fc7fSdrh sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
7528416fc7fSdrh break;
7538416fc7fSdrh }
7548416fc7fSdrh case CLOSURE_COL_ROOT: {
7558416fc7fSdrh sqlite3_result_null(ctx);
7568416fc7fSdrh break;
7578416fc7fSdrh }
7588416fc7fSdrh case CLOSURE_COL_TABLENAME: {
7598416fc7fSdrh sqlite3_result_text(ctx,
7608416fc7fSdrh pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
7618416fc7fSdrh -1, SQLITE_TRANSIENT);
7628416fc7fSdrh break;
7638416fc7fSdrh }
7648416fc7fSdrh case CLOSURE_COL_IDCOLUMN: {
7658416fc7fSdrh sqlite3_result_text(ctx,
7668416fc7fSdrh pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
7678416fc7fSdrh -1, SQLITE_TRANSIENT);
7688416fc7fSdrh break;
7698416fc7fSdrh }
7708416fc7fSdrh case CLOSURE_COL_PARENTCOLUMN: {
7718416fc7fSdrh sqlite3_result_text(ctx,
7728416fc7fSdrh pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
7738416fc7fSdrh -1, SQLITE_TRANSIENT);
7748416fc7fSdrh break;
7758416fc7fSdrh }
7768416fc7fSdrh }
7778416fc7fSdrh return SQLITE_OK;
7788416fc7fSdrh }
7798416fc7fSdrh
7808416fc7fSdrh /*
7818416fc7fSdrh ** The rowid. For the closure table, this is the same as the "id" column.
7828416fc7fSdrh */
closureRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)7838416fc7fSdrh static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
7848416fc7fSdrh closure_cursor *pCur = (closure_cursor*)cur;
7858416fc7fSdrh *pRowid = pCur->pCurrent->id;
7868416fc7fSdrh return SQLITE_OK;
7878416fc7fSdrh }
7888416fc7fSdrh
7898416fc7fSdrh /*
7908416fc7fSdrh ** EOF indicator
7918416fc7fSdrh */
closureEof(sqlite3_vtab_cursor * cur)7928416fc7fSdrh static int closureEof(sqlite3_vtab_cursor *cur){
7938416fc7fSdrh closure_cursor *pCur = (closure_cursor*)cur;
7948416fc7fSdrh return pCur->pCurrent==0;
7958416fc7fSdrh }
7968416fc7fSdrh
7978416fc7fSdrh /*
7988416fc7fSdrh ** Search for terms of these forms:
7998416fc7fSdrh **
8008416fc7fSdrh ** (A) root = $root
8018416fc7fSdrh ** (B1) depth < $depth
8028416fc7fSdrh ** (B2) depth <= $depth
8038416fc7fSdrh ** (B3) depth = $depth
8048416fc7fSdrh ** (C) tablename = $tablename
8058416fc7fSdrh ** (D) idcolumn = $idcolumn
8068416fc7fSdrh ** (E) parentcolumn = $parentcolumn
8078416fc7fSdrh **
8088416fc7fSdrh **
8098416fc7fSdrh **
8108416fc7fSdrh ** idxNum meaning
8118416fc7fSdrh ** ---------- ------------------------------------------------------
8128416fc7fSdrh ** 0x00000001 Term of the form (A) found
8138416fc7fSdrh ** 0x00000002 The term of bit-2 is like (B1)
8148416fc7fSdrh ** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
8158416fc7fSdrh ** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
8168416fc7fSdrh ** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
8178416fc7fSdrh ** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
8188416fc7fSdrh **
8198416fc7fSdrh ** There must be a term of type (A). If there is not, then the index type
8208416fc7fSdrh ** is 0 and the query will return an empty set.
8218416fc7fSdrh */
closureBestIndex(sqlite3_vtab * pTab,sqlite3_index_info * pIdxInfo)8228416fc7fSdrh static int closureBestIndex(
8238416fc7fSdrh sqlite3_vtab *pTab, /* The virtual table */
8248416fc7fSdrh sqlite3_index_info *pIdxInfo /* Information about the query */
8258416fc7fSdrh ){
8268416fc7fSdrh int iPlan = 0;
8278416fc7fSdrh int i;
8288416fc7fSdrh int idx = 1;
8298416fc7fSdrh const struct sqlite3_index_constraint *pConstraint;
8308416fc7fSdrh closure_vtab *pVtab = (closure_vtab*)pTab;
831d2b113bcSdrh double rCost = 10000000.0;
8328416fc7fSdrh
8338416fc7fSdrh pConstraint = pIdxInfo->aConstraint;
8348416fc7fSdrh for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
8358416fc7fSdrh if( pConstraint->usable==0 ) continue;
8368416fc7fSdrh if( (iPlan & 1)==0
8378416fc7fSdrh && pConstraint->iColumn==CLOSURE_COL_ROOT
8388416fc7fSdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
8398416fc7fSdrh ){
8408416fc7fSdrh iPlan |= 1;
8418416fc7fSdrh pIdxInfo->aConstraintUsage[i].argvIndex = 1;
8428416fc7fSdrh pIdxInfo->aConstraintUsage[i].omit = 1;
843d2b113bcSdrh rCost /= 100.0;
8448416fc7fSdrh }
8458416fc7fSdrh if( (iPlan & 0x0000f0)==0
8468416fc7fSdrh && pConstraint->iColumn==CLOSURE_COL_DEPTH
8478416fc7fSdrh && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
8488416fc7fSdrh || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
8498416fc7fSdrh || pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
8508416fc7fSdrh ){
8518416fc7fSdrh iPlan |= idx<<4;
8528416fc7fSdrh pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
8538416fc7fSdrh if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
854d2b113bcSdrh rCost /= 5.0;
8558416fc7fSdrh }
8568416fc7fSdrh if( (iPlan & 0x000f00)==0
8578416fc7fSdrh && pConstraint->iColumn==CLOSURE_COL_TABLENAME
8588416fc7fSdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
8598416fc7fSdrh ){
8608416fc7fSdrh iPlan |= idx<<8;
8618416fc7fSdrh pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
8628416fc7fSdrh pIdxInfo->aConstraintUsage[i].omit = 1;
863d2b113bcSdrh rCost /= 5.0;
8648416fc7fSdrh }
8658416fc7fSdrh if( (iPlan & 0x00f000)==0
8668416fc7fSdrh && pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
8678416fc7fSdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
8688416fc7fSdrh ){
8698416fc7fSdrh iPlan |= idx<<12;
8708416fc7fSdrh pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
8718416fc7fSdrh pIdxInfo->aConstraintUsage[i].omit = 1;
8728416fc7fSdrh }
8738416fc7fSdrh if( (iPlan & 0x0f0000)==0
8748416fc7fSdrh && pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
8758416fc7fSdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
8768416fc7fSdrh ){
8778416fc7fSdrh iPlan |= idx<<16;
8788416fc7fSdrh pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
8798416fc7fSdrh pIdxInfo->aConstraintUsage[i].omit = 1;
8808416fc7fSdrh }
8818416fc7fSdrh }
8828416fc7fSdrh if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
8838416fc7fSdrh || (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
8848416fc7fSdrh || (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
8858416fc7fSdrh ){
8868416fc7fSdrh /* All of tablename, idcolumn, and parentcolumn must be specified
8878416fc7fSdrh ** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
8888416fc7fSdrh ** or else the result is an empty set. */
8898416fc7fSdrh iPlan = 0;
8908416fc7fSdrh }
891fa5c69f5Sdan if( (iPlan&1)==0 ){
892fa5c69f5Sdan /* If there is no usable "root=?" term, then set the index-type to 0.
893fa5c69f5Sdan ** Also clear any argvIndex variables already set. This is necessary
894fa5c69f5Sdan ** to prevent the core from throwing an "xBestIndex malfunction error"
895fa5c69f5Sdan ** error (because the argvIndex values are not contiguously assigned
896fa5c69f5Sdan ** starting from 1). */
897fa5c69f5Sdan rCost *= 1e30;
898fa5c69f5Sdan for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
899fa5c69f5Sdan pIdxInfo->aConstraintUsage[i].argvIndex = 0;
900fa5c69f5Sdan }
901fa5c69f5Sdan iPlan = 0;
902fa5c69f5Sdan }
9038416fc7fSdrh pIdxInfo->idxNum = iPlan;
9048416fc7fSdrh if( pIdxInfo->nOrderBy==1
9058416fc7fSdrh && pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
9068416fc7fSdrh && pIdxInfo->aOrderBy[0].desc==0
9078416fc7fSdrh ){
9088416fc7fSdrh pIdxInfo->orderByConsumed = 1;
9098416fc7fSdrh }
910d2b113bcSdrh pIdxInfo->estimatedCost = rCost;
9118416fc7fSdrh
9128416fc7fSdrh return SQLITE_OK;
9138416fc7fSdrh }
9148416fc7fSdrh
9158416fc7fSdrh /*
91647af6e76Sdrh ** A virtual table module that implements the "transitive_closure".
9178416fc7fSdrh */
9188416fc7fSdrh static sqlite3_module closureModule = {
9198416fc7fSdrh 0, /* iVersion */
9208416fc7fSdrh closureConnect, /* xCreate */
9218416fc7fSdrh closureConnect, /* xConnect */
9228416fc7fSdrh closureBestIndex, /* xBestIndex */
9238416fc7fSdrh closureDisconnect, /* xDisconnect */
9248416fc7fSdrh closureDisconnect, /* xDestroy */
9258416fc7fSdrh closureOpen, /* xOpen - open a cursor */
9268416fc7fSdrh closureClose, /* xClose - close a cursor */
9278416fc7fSdrh closureFilter, /* xFilter - configure scan constraints */
9288416fc7fSdrh closureNext, /* xNext - advance a cursor */
9298416fc7fSdrh closureEof, /* xEof - check for end of scan */
9308416fc7fSdrh closureColumn, /* xColumn - read data */
9318416fc7fSdrh closureRowid, /* xRowid - read data */
9328416fc7fSdrh 0, /* xUpdate */
9338416fc7fSdrh 0, /* xBegin */
9348416fc7fSdrh 0, /* xSync */
9358416fc7fSdrh 0, /* xCommit */
9368416fc7fSdrh 0, /* xRollback */
9378416fc7fSdrh 0, /* xFindMethod */
9388416fc7fSdrh 0, /* xRename */
9398416fc7fSdrh 0, /* xSavepoint */
9408416fc7fSdrh 0, /* xRelease */
94184c501baSdrh 0, /* xRollbackTo */
94284c501baSdrh 0 /* xShadowName */
9438416fc7fSdrh };
9448416fc7fSdrh
94511f71d6aSdan #endif /* SQLITE_OMIT_VIRTUALTABLE */
94611f71d6aSdan
9478416fc7fSdrh /*
9488416fc7fSdrh ** Register the closure virtual table
9498416fc7fSdrh */
9508416fc7fSdrh #ifdef _WIN32
9518416fc7fSdrh __declspec(dllexport)
9528416fc7fSdrh #endif
sqlite3_closure_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)9538416fc7fSdrh int sqlite3_closure_init(
9548416fc7fSdrh sqlite3 *db,
9558416fc7fSdrh char **pzErrMsg,
9568416fc7fSdrh const sqlite3_api_routines *pApi
9578416fc7fSdrh ){
9588416fc7fSdrh int rc = SQLITE_OK;
9598416fc7fSdrh SQLITE_EXTENSION_INIT2(pApi);
9608416fc7fSdrh (void)pzErrMsg;
96111f71d6aSdan #ifndef SQLITE_OMIT_VIRTUALTABLE
9628416fc7fSdrh rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
96311f71d6aSdan #endif /* SQLITE_OMIT_VIRTUALTABLE */
9648416fc7fSdrh return rc;
9658416fc7fSdrh }
966