123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958 |
- /*
- ** 2013-04-16
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- *************************************************************************
- **
- ** This file contains code for a virtual table that finds the transitive
- ** closure of a parent/child relationship in a real table. The virtual
- ** table is called "transitive_closure".
- **
- ** A transitive_closure virtual table is created like this:
- **
- ** CREATE VIRTUAL TABLE x USING transitive_closure(
- ** tablename=<tablename>, -- T
- ** idcolumn=<columnname>, -- X
- ** parentcolumn=<columnname> -- P
- ** );
- **
- ** When it is created, the new transitive_closure table may be supplied
- ** with default values for the name of a table T and columns T.X and T.P.
- ** The T.X and T.P columns must contain integers. The ideal case is for
- ** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
- ** the T.X column. The row referenced by T.P is the parent of the current row.
- **
- ** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
- ** TABLE statement may be overridden in individual queries by including
- ** terms like tablename='newtable', idcolumn='id2', or
- ** parentcolumn='parent3' in the WHERE clause of the query.
- **
- ** For efficiency, it is essential that there be an index on the P column:
- **
- ** CREATE Tidx1 ON T(P)
- **
- ** Suppose a specific instance of the closure table is as follows:
- **
- ** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
- ** tablename='group',
- ** idcolumn='groupId',
- ** parentcolumn='parentId'
- ** );
- **
- ** Such an instance of the transitive_closure virtual table would be
- ** appropriate for walking a tree defined using a table like this, for example:
- **
- ** CREATE TABLE group(
- ** groupId INTEGER PRIMARY KEY,
- ** parentId INTEGER REFERENCES group
- ** );
- ** CREATE INDEX group_idx1 ON group(parentId);
- **
- ** The group table above would presumably have other application-specific
- ** fields. The key point here is that rows of the group table form a
- ** tree. The purpose of the ct1 virtual table is to easily extract
- ** branches of that tree.
- **
- ** Once it has been created, the ct1 virtual table can be queried
- ** as follows:
- **
- ** SELECT * FROM element
- ** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
- **
- ** The above query will return all elements that are part of group ?1
- ** or children of group ?1 or grand-children of ?1 and so forth for all
- ** descendents of group ?1. The same query can be formulated as a join:
- **
- ** SELECT element.* FROM element, ct1
- ** WHERE element.groupid=ct1.id
- ** AND ct1.root=?1;
- **
- ** The depth of the transitive_closure (the number of generations of
- ** parent/child relations to follow) can be limited by setting "depth"
- ** column in the WHERE clause. So, for example, the following query
- ** finds only children and grandchildren but no further descendents:
- **
- ** SELECT element.* FROM element, ct1
- ** WHERE element.groupid=ct1.id
- ** AND ct1.root=?1
- ** AND ct1.depth<=2;
- **
- ** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
- ** order to find only the grandchildren of ?1, not ?1 itself or the
- ** children of ?1.
- **
- ** The root=?1 term must be supplied in WHERE clause or else the query
- ** of the ct1 virtual table will return an empty set. The tablename,
- ** idcolumn, and parentcolumn attributes can be overridden in the WHERE
- ** clause if desired. So, for example, the ct1 table could be repurposed
- ** to find ancestors rather than descendents by inverting the roles of
- ** the idcolumn and parentcolumn:
- **
- ** SELECT element.* FROM element, ct1
- ** WHERE element.groupid=ct1.id
- ** AND ct1.root=?1
- ** AND ct1.idcolumn='parentId'
- ** AND ct1.parentcolumn='groupId';
- **
- ** Multiple calls to ct1 could be combined. For example, the following
- ** query finds all elements that "cousins" of groupId ?1. That is to say
- ** elements where the groupId is a grandchild of the grandparent of ?1.
- ** (This definition of "cousins" also includes siblings and self.)
- **
- ** SELECT element.* FROM element, ct1
- ** WHERE element.groupId=ct1.id
- ** AND ct1.depth=2
- ** AND ct1.root IN (SELECT id FROM ct1
- ** WHERE root=?1
- ** AND depth=2
- ** AND idcolumn='parentId'
- ** AND parentcolumn='groupId');
- **
- ** In our example, the group.groupId column is unique and thus the
- ** subquery will return exactly one row. For that reason, the IN
- ** operator could be replaced by "=" to get the same result. But
- ** in the general case where the idcolumn is not unique, an IN operator
- ** would be required for this kind of query.
- **
- ** Note that because the tablename, idcolumn, and parentcolumn can
- ** all be specified in the query, it is possible for an application
- ** to define a single transitive_closure virtual table for use on lots
- ** of different hierarchy tables. One might say:
- **
- ** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
- **
- ** As each database connection is being opened. Then the application
- ** would always have a "closure" virtual table handy to use for querying.
- **
- ** SELECT element.* FROM element, closure
- ** WHERE element.groupid=ct1.id
- ** AND closure.root=?1
- ** AND closure.tablename='group'
- ** AND closure.idname='groupId'
- ** AND closure.parentname='parentId';
- **
- ** See the documentation at http://www.sqlite.org/loadext.html for information
- ** on how to compile and use loadable extensions such as this one.
- */
- #include "sqlite3ext.h"
- SQLITE_EXTENSION_INIT1
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <stdio.h>
- #include <ctype.h>
- #ifndef SQLITE_OMIT_VIRTUALTABLE
- /*
- ** Forward declaration of objects used by this implementation
- */
- typedef struct closure_vtab closure_vtab;
- typedef struct closure_cursor closure_cursor;
- typedef struct closure_queue closure_queue;
- typedef struct closure_avl closure_avl;
- /*****************************************************************************
- ** AVL Tree implementation
- */
- /*
- ** Objects that want to be members of the AVL tree should embedded an
- ** instance of this structure.
- */
- struct closure_avl {
- sqlite3_int64 id; /* Id of this entry in the table */
- int iGeneration; /* Which generation is this entry part of */
- closure_avl *pList; /* A linked list of nodes */
- closure_avl *pBefore; /* Other elements less than id */
- closure_avl *pAfter; /* Other elements greater than id */
- closure_avl *pUp; /* Parent element */
- short int height; /* Height of this node. Leaf==1 */
- short int imbalance; /* Height difference between pBefore and pAfter */
- };
- /* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
- ** Assume that the children of p have correct heights.
- */
- static void closureAvlRecomputeHeight(closure_avl *p){
- short int hBefore = p->pBefore ? p->pBefore->height : 0;
- short int hAfter = p->pAfter ? p->pAfter->height : 0;
- p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
- p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
- }
- /*
- ** P B
- ** / \ / \
- ** B Z ==> X P
- ** / \ / \
- ** X Y Y Z
- **
- */
- static closure_avl *closureAvlRotateBefore(closure_avl *pP){
- closure_avl *pB = pP->pBefore;
- closure_avl *pY = pB->pAfter;
- pB->pUp = pP->pUp;
- pB->pAfter = pP;
- pP->pUp = pB;
- pP->pBefore = pY;
- if( pY ) pY->pUp = pP;
- closureAvlRecomputeHeight(pP);
- closureAvlRecomputeHeight(pB);
- return pB;
- }
- /*
- ** P A
- ** / \ / \
- ** X A ==> P Z
- ** / \ / \
- ** Y Z X Y
- **
- */
- static closure_avl *closureAvlRotateAfter(closure_avl *pP){
- closure_avl *pA = pP->pAfter;
- closure_avl *pY = pA->pBefore;
- pA->pUp = pP->pUp;
- pA->pBefore = pP;
- pP->pUp = pA;
- pP->pAfter = pY;
- if( pY ) pY->pUp = pP;
- closureAvlRecomputeHeight(pP);
- closureAvlRecomputeHeight(pA);
- return pA;
- }
- /*
- ** Return a pointer to the pBefore or pAfter pointer in the parent
- ** of p that points to p. Or if p is the root node, return pp.
- */
- static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
- closure_avl *pUp = p->pUp;
- if( pUp==0 ) return pp;
- if( pUp->pAfter==p ) return &pUp->pAfter;
- return &pUp->pBefore;
- }
- /*
- ** Rebalance all nodes starting with p and working up to the root.
- ** Return the new root.
- */
- static closure_avl *closureAvlBalance(closure_avl *p){
- closure_avl *pTop = p;
- closure_avl **pp;
- while( p ){
- closureAvlRecomputeHeight(p);
- if( p->imbalance>=2 ){
- closure_avl *pB = p->pBefore;
- if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
- pp = closureAvlFromPtr(p,&p);
- p = *pp = closureAvlRotateBefore(p);
- }else if( p->imbalance<=(-2) ){
- closure_avl *pA = p->pAfter;
- if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
- pp = closureAvlFromPtr(p,&p);
- p = *pp = closureAvlRotateAfter(p);
- }
- pTop = p;
- p = p->pUp;
- }
- return pTop;
- }
- /* Search the tree rooted at p for an entry with id. Return a pointer
- ** to the entry or return NULL.
- */
- static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
- while( p && id!=p->id ){
- p = (id<p->id) ? p->pBefore : p->pAfter;
- }
- return p;
- }
- /* Find the first node (the one with the smallest key).
- */
- static closure_avl *closureAvlFirst(closure_avl *p){
- if( p ) while( p->pBefore ) p = p->pBefore;
- return p;
- }
- /* Return the node with the next larger key after p.
- */
- closure_avl *closureAvlNext(closure_avl *p){
- closure_avl *pPrev = 0;
- while( p && p->pAfter==pPrev ){
- pPrev = p;
- p = p->pUp;
- }
- if( p && pPrev==0 ){
- p = closureAvlFirst(p->pAfter);
- }
- return p;
- }
- /* Insert a new node pNew. Return NULL on success. If the key is not
- ** unique, then do not perform the insert but instead leave pNew unchanged
- ** and return a pointer to an existing node with the same key.
- */
- static closure_avl *closureAvlInsert(
- closure_avl **ppHead, /* Head of the tree */
- closure_avl *pNew /* New node to be inserted */
- ){
- closure_avl *p = *ppHead;
- if( p==0 ){
- p = pNew;
- pNew->pUp = 0;
- }else{
- while( p ){
- if( pNew->id<p->id ){
- if( p->pBefore ){
- p = p->pBefore;
- }else{
- p->pBefore = pNew;
- pNew->pUp = p;
- break;
- }
- }else if( pNew->id>p->id ){
- if( p->pAfter ){
- p = p->pAfter;
- }else{
- p->pAfter = pNew;
- pNew->pUp = p;
- break;
- }
- }else{
- return p;
- }
- }
- }
- pNew->pBefore = 0;
- pNew->pAfter = 0;
- pNew->height = 1;
- pNew->imbalance = 0;
- *ppHead = closureAvlBalance(p);
- return 0;
- }
- /* Walk the tree can call xDestroy on each node
- */
- static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
- if( p ){
- closureAvlDestroy(p->pBefore, xDestroy);
- closureAvlDestroy(p->pAfter, xDestroy);
- xDestroy(p);
- }
- }
- /*
- ** End of the AVL Tree implementation
- ******************************************************************************/
- /*
- ** A closure virtual-table object
- */
- struct closure_vtab {
- sqlite3_vtab base; /* Base class - must be first */
- char *zDb; /* Name of database. (ex: "main") */
- char *zSelf; /* Name of this virtual table */
- char *zTableName; /* Name of table holding parent/child relation */
- char *zIdColumn; /* Name of ID column of zTableName */
- char *zParentColumn; /* Name of PARENT column in zTableName */
- sqlite3 *db; /* The database connection */
- int nCursor; /* Number of pending cursors */
- };
- /* A closure cursor object */
- struct closure_cursor {
- sqlite3_vtab_cursor base; /* Base class - must be first */
- closure_vtab *pVtab; /* The virtual table this cursor belongs to */
- char *zTableName; /* Name of table holding parent/child relation */
- char *zIdColumn; /* Name of ID column of zTableName */
- char *zParentColumn; /* Name of PARENT column in zTableName */
- closure_avl *pCurrent; /* Current element of output */
- closure_avl *pClosure; /* The complete closure tree */
- };
- /* A queue of AVL nodes */
- struct closure_queue {
- closure_avl *pFirst; /* Oldest node on the queue */
- closure_avl *pLast; /* Youngest node on the queue */
- };
- /*
- ** Add a node to the end of the queue
- */
- static void queuePush(closure_queue *pQueue, closure_avl *pNode){
- pNode->pList = 0;
- if( pQueue->pLast ){
- pQueue->pLast->pList = pNode;
- }else{
- pQueue->pFirst = pNode;
- }
- pQueue->pLast = pNode;
- }
- /*
- ** Extract the oldest element (the front element) from the queue.
- */
- static closure_avl *queuePull(closure_queue *pQueue){
- closure_avl *p = pQueue->pFirst;
- if( p ){
- pQueue->pFirst = p->pList;
- if( pQueue->pFirst==0 ) pQueue->pLast = 0;
- }
- return p;
- }
- /*
- ** This function converts an SQL quoted string into an unquoted string
- ** and returns a pointer to a buffer allocated using sqlite3_malloc()
- ** containing the result. The caller should eventually free this buffer
- ** using sqlite3_free.
- **
- ** Examples:
- **
- ** "abc" becomes abc
- ** 'xyz' becomes xyz
- ** [pqr] becomes pqr
- ** `mno` becomes mno
- */
- static char *closureDequote(const char *zIn){
- int nIn; /* Size of input string, in bytes */
- char *zOut; /* Output (dequoted) string */
- nIn = (int)strlen(zIn);
- zOut = sqlite3_malloc(nIn+1);
- if( zOut ){
- char q = zIn[0]; /* Quote character (if any ) */
- if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
- memcpy(zOut, zIn, nIn+1);
- }else{
- int iOut = 0; /* Index of next byte to write to output */
- int iIn; /* Index of next byte to read from input */
- if( q=='[' ) q = ']';
- for(iIn=1; iIn<nIn; iIn++){
- if( zIn[iIn]==q ) iIn++;
- zOut[iOut++] = zIn[iIn];
- }
- }
- assert( (int)strlen(zOut)<=nIn );
- }
- return zOut;
- }
- /*
- ** Deallocate an closure_vtab object
- */
- static void closureFree(closure_vtab *p){
- if( p ){
- sqlite3_free(p->zDb);
- sqlite3_free(p->zSelf);
- sqlite3_free(p->zTableName);
- sqlite3_free(p->zIdColumn);
- sqlite3_free(p->zParentColumn);
- memset(p, 0, sizeof(*p));
- sqlite3_free(p);
- }
- }
- /*
- ** xDisconnect/xDestroy method for the closure module.
- */
- static int closureDisconnect(sqlite3_vtab *pVtab){
- closure_vtab *p = (closure_vtab*)pVtab;
- assert( p->nCursor==0 );
- closureFree(p);
- return SQLITE_OK;
- }
- /*
- ** Check to see if the argument is of the form:
- **
- ** KEY = VALUE
- **
- ** If it is, return a pointer to the first character of VALUE.
- ** If not, return NULL. Spaces around the = are ignored.
- */
- static const char *closureValueOfKey(const char *zKey, const char *zStr){
- int nKey = (int)strlen(zKey);
- int nStr = (int)strlen(zStr);
- int i;
- if( nStr<nKey+1 ) return 0;
- if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
- for(i=nKey; isspace(zStr[i]); i++){}
- if( zStr[i]!='=' ) return 0;
- i++;
- while( isspace(zStr[i]) ){ i++; }
- return zStr+i;
- }
- /*
- ** xConnect/xCreate method for the closure module. Arguments are:
- **
- ** argv[0] -> module name ("transitive_closure")
- ** argv[1] -> database name
- ** argv[2] -> table name
- ** argv[3...] -> arguments
- */
- static int closureConnect(
- sqlite3 *db,
- void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVtab,
- char **pzErr
- ){
- int rc = SQLITE_OK; /* Return code */
- closure_vtab *pNew = 0; /* New virtual table */
- const char *zDb = argv[1];
- const char *zVal;
- int i;
- (void)pAux;
- *ppVtab = 0;
- pNew = sqlite3_malloc( sizeof(*pNew) );
- if( pNew==0 ) return SQLITE_NOMEM;
- rc = SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
- pNew->db = db;
- pNew->zDb = sqlite3_mprintf("%s", zDb);
- if( pNew->zDb==0 ) goto closureConnectError;
- pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
- if( pNew->zSelf==0 ) goto closureConnectError;
- for(i=3; i<argc; i++){
- zVal = closureValueOfKey("tablename", argv[i]);
- if( zVal ){
- sqlite3_free(pNew->zTableName);
- pNew->zTableName = closureDequote(zVal);
- if( pNew->zTableName==0 ) goto closureConnectError;
- continue;
- }
- zVal = closureValueOfKey("idcolumn", argv[i]);
- if( zVal ){
- sqlite3_free(pNew->zIdColumn);
- pNew->zIdColumn = closureDequote(zVal);
- if( pNew->zIdColumn==0 ) goto closureConnectError;
- continue;
- }
- zVal = closureValueOfKey("parentcolumn", argv[i]);
- if( zVal ){
- sqlite3_free(pNew->zParentColumn);
- pNew->zParentColumn = closureDequote(zVal);
- if( pNew->zParentColumn==0 ) goto closureConnectError;
- continue;
- }
- *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
- closureFree(pNew);
- *ppVtab = 0;
- return SQLITE_ERROR;
- }
- rc = sqlite3_declare_vtab(db,
- "CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
- "idcolumn HIDDEN,parentcolumn HIDDEN)"
- );
- #define CLOSURE_COL_ID 0
- #define CLOSURE_COL_DEPTH 1
- #define CLOSURE_COL_ROOT 2
- #define CLOSURE_COL_TABLENAME 3
- #define CLOSURE_COL_IDCOLUMN 4
- #define CLOSURE_COL_PARENTCOLUMN 5
- if( rc!=SQLITE_OK ){
- closureFree(pNew);
- }
- *ppVtab = &pNew->base;
- return rc;
- closureConnectError:
- closureFree(pNew);
- return rc;
- }
- /*
- ** Open a new closure cursor.
- */
- static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- closure_vtab *p = (closure_vtab*)pVTab;
- closure_cursor *pCur;
- pCur = sqlite3_malloc( sizeof(*pCur) );
- if( pCur==0 ) return SQLITE_NOMEM;
- memset(pCur, 0, sizeof(*pCur));
- pCur->pVtab = p;
- *ppCursor = &pCur->base;
- p->nCursor++;
- return SQLITE_OK;
- }
- /*
- ** Free up all the memory allocated by a cursor. Set it rLimit to 0
- ** to indicate that it is at EOF.
- */
- static void closureClearCursor(closure_cursor *pCur){
- closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
- sqlite3_free(pCur->zTableName);
- sqlite3_free(pCur->zIdColumn);
- sqlite3_free(pCur->zParentColumn);
- pCur->zTableName = 0;
- pCur->zIdColumn = 0;
- pCur->zParentColumn = 0;
- pCur->pCurrent = 0;
- pCur->pClosure = 0;
- }
- /*
- ** Close a closure cursor.
- */
- static int closureClose(sqlite3_vtab_cursor *cur){
- closure_cursor *pCur = (closure_cursor *)cur;
- closureClearCursor(pCur);
- pCur->pVtab->nCursor--;
- sqlite3_free(pCur);
- return SQLITE_OK;
- }
- /*
- ** Advance a cursor to its next row of output
- */
- static int closureNext(sqlite3_vtab_cursor *cur){
- closure_cursor *pCur = (closure_cursor*)cur;
- pCur->pCurrent = closureAvlNext(pCur->pCurrent);
- return SQLITE_OK;
- }
- /*
- ** Allocate and insert a node
- */
- static int closureInsertNode(
- closure_queue *pQueue, /* Add new node to this queue */
- closure_cursor *pCur, /* The cursor into which to add the node */
- sqlite3_int64 id, /* The node ID */
- int iGeneration /* The generation number for this node */
- ){
- closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
- if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
- pNew->id = id;
- pNew->iGeneration = iGeneration;
- closureAvlInsert(&pCur->pClosure, pNew);
- queuePush(pQueue, pNew);
- return SQLITE_OK;
- }
- /*
- ** Called to "rewind" a cursor back to the beginning so that
- ** it starts its output over again. Always called at least once
- ** prior to any closureColumn, closureRowid, or closureEof call.
- **
- ** This routine actually computes the closure.
- **
- ** See the comment at the beginning of closureBestIndex() for a
- ** description of the meaning of idxNum. The idxStr parameter is
- ** not used.
- */
- static int closureFilter(
- sqlite3_vtab_cursor *pVtabCursor,
- int idxNum, const char *idxStr,
- int argc, sqlite3_value **argv
- ){
- closure_cursor *pCur = (closure_cursor *)pVtabCursor;
- closure_vtab *pVtab = pCur->pVtab;
- sqlite3_int64 iRoot;
- int mxGen = 999999999;
- char *zSql;
- sqlite3_stmt *pStmt;
- closure_avl *pAvl;
- int rc = SQLITE_OK;
- const char *zTableName = pVtab->zTableName;
- const char *zIdColumn = pVtab->zIdColumn;
- const char *zParentColumn = pVtab->zParentColumn;
- closure_queue sQueue;
- (void)idxStr; /* Unused parameter */
- (void)argc; /* Unused parameter */
- closureClearCursor(pCur);
- memset(&sQueue, 0, sizeof(sQueue));
- if( (idxNum & 1)==0 ){
- /* No root=$root in the WHERE clause. Return an empty set */
- return SQLITE_OK;
- }
- iRoot = sqlite3_value_int64(argv[0]);
- if( (idxNum & 0x000f0)!=0 ){
- mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
- if( (idxNum & 0x00002)!=0 ) mxGen--;
- }
- if( (idxNum & 0x00f00)!=0 ){
- zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
- pCur->zTableName = sqlite3_mprintf("%s", zTableName);
- }
- if( (idxNum & 0x0f000)!=0 ){
- zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
- pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
- }
- if( (idxNum & 0x0f0000)!=0 ){
- zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
- pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
- }
- zSql = sqlite3_mprintf(
- "SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
- zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
- if( zSql==0 ){
- return SQLITE_NOMEM;
- }else{
- rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- if( rc ){
- sqlite3_free(pVtab->base.zErrMsg);
- pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
- return rc;
- }
- }
- if( rc==SQLITE_OK ){
- rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
- }
- while( (pAvl = queuePull(&sQueue))!=0 ){
- if( pAvl->iGeneration>=mxGen ) continue;
- sqlite3_bind_int64(pStmt, 1, pAvl->id);
- while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
- if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
- sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
- if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
- rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
- }
- }
- }
- sqlite3_reset(pStmt);
- }
- sqlite3_finalize(pStmt);
- if( rc==SQLITE_OK ){
- pCur->pCurrent = closureAvlFirst(pCur->pClosure);
- }
- return rc;
- }
- /*
- ** Only the word and distance columns have values. All other columns
- ** return NULL
- */
- static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
- closure_cursor *pCur = (closure_cursor*)cur;
- switch( i ){
- case CLOSURE_COL_ID: {
- sqlite3_result_int64(ctx, pCur->pCurrent->id);
- break;
- }
- case CLOSURE_COL_DEPTH: {
- sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
- break;
- }
- case CLOSURE_COL_ROOT: {
- sqlite3_result_null(ctx);
- break;
- }
- case CLOSURE_COL_TABLENAME: {
- sqlite3_result_text(ctx,
- pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
- -1, SQLITE_TRANSIENT);
- break;
- }
- case CLOSURE_COL_IDCOLUMN: {
- sqlite3_result_text(ctx,
- pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
- -1, SQLITE_TRANSIENT);
- break;
- }
- case CLOSURE_COL_PARENTCOLUMN: {
- sqlite3_result_text(ctx,
- pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
- -1, SQLITE_TRANSIENT);
- break;
- }
- }
- return SQLITE_OK;
- }
- /*
- ** The rowid. For the closure table, this is the same as the "id" column.
- */
- static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
- closure_cursor *pCur = (closure_cursor*)cur;
- *pRowid = pCur->pCurrent->id;
- return SQLITE_OK;
- }
- /*
- ** EOF indicator
- */
- static int closureEof(sqlite3_vtab_cursor *cur){
- closure_cursor *pCur = (closure_cursor*)cur;
- return pCur->pCurrent==0;
- }
- /*
- ** Search for terms of these forms:
- **
- ** (A) root = $root
- ** (B1) depth < $depth
- ** (B2) depth <= $depth
- ** (B3) depth = $depth
- ** (C) tablename = $tablename
- ** (D) idcolumn = $idcolumn
- ** (E) parentcolumn = $parentcolumn
- **
- **
- **
- ** idxNum meaning
- ** ---------- ------------------------------------------------------
- ** 0x00000001 Term of the form (A) found
- ** 0x00000002 The term of bit-2 is like (B1)
- ** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
- ** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
- ** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
- ** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
- **
- ** There must be a term of type (A). If there is not, then the index type
- ** is 0 and the query will return an empty set.
- */
- static int closureBestIndex(
- sqlite3_vtab *pTab, /* The virtual table */
- sqlite3_index_info *pIdxInfo /* Information about the query */
- ){
- int iPlan = 0;
- int i;
- int idx = 1;
- int seenMatch = 0;
- const struct sqlite3_index_constraint *pConstraint;
- closure_vtab *pVtab = (closure_vtab*)pTab;
- double rCost = 10000000.0;
- pConstraint = pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- if( pConstraint->iColumn==CLOSURE_COL_ROOT
- && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
- seenMatch = 1;
- }
- if( pConstraint->usable==0 ) continue;
- if( (iPlan & 1)==0
- && pConstraint->iColumn==CLOSURE_COL_ROOT
- && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
- ){
- iPlan |= 1;
- pIdxInfo->aConstraintUsage[i].argvIndex = 1;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- rCost /= 100.0;
- }
- if( (iPlan & 0x0000f0)==0
- && pConstraint->iColumn==CLOSURE_COL_DEPTH
- && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
- || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
- || pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
- ){
- iPlan |= idx<<4;
- pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
- if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
- rCost /= 5.0;
- }
- if( (iPlan & 0x000f00)==0
- && pConstraint->iColumn==CLOSURE_COL_TABLENAME
- && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
- ){
- iPlan |= idx<<8;
- pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- rCost /= 5.0;
- }
- if( (iPlan & 0x00f000)==0
- && pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
- && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
- ){
- iPlan |= idx<<12;
- pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- }
- if( (iPlan & 0x0f0000)==0
- && pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
- && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
- ){
- iPlan |= idx<<16;
- pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- }
- }
- if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
- || (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
- || (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
- ){
- /* All of tablename, idcolumn, and parentcolumn must be specified
- ** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
- ** or else the result is an empty set. */
- iPlan = 0;
- }
- pIdxInfo->idxNum = iPlan;
- if( pIdxInfo->nOrderBy==1
- && pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
- && pIdxInfo->aOrderBy[0].desc==0
- ){
- pIdxInfo->orderByConsumed = 1;
- }
- if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
- pIdxInfo->estimatedCost = rCost;
-
- return SQLITE_OK;
- }
- /*
- ** A virtual table module that implements the "transitive_closure".
- */
- static sqlite3_module closureModule = {
- 0, /* iVersion */
- closureConnect, /* xCreate */
- closureConnect, /* xConnect */
- closureBestIndex, /* xBestIndex */
- closureDisconnect, /* xDisconnect */
- closureDisconnect, /* xDestroy */
- closureOpen, /* xOpen - open a cursor */
- closureClose, /* xClose - close a cursor */
- closureFilter, /* xFilter - configure scan constraints */
- closureNext, /* xNext - advance a cursor */
- closureEof, /* xEof - check for end of scan */
- closureColumn, /* xColumn - read data */
- closureRowid, /* xRowid - read data */
- 0, /* xUpdate */
- 0, /* xBegin */
- 0, /* xSync */
- 0, /* xCommit */
- 0, /* xRollback */
- 0, /* xFindMethod */
- 0, /* xRename */
- 0, /* xSavepoint */
- 0, /* xRelease */
- 0 /* xRollbackTo */
- };
- #endif /* SQLITE_OMIT_VIRTUALTABLE */
- /*
- ** Register the closure virtual table
- */
- #ifdef _WIN32
- __declspec(dllexport)
- #endif
- int sqlite3_closure_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
- ){
- int rc = SQLITE_OK;
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg;
- #ifndef SQLITE_OMIT_VIRTUALTABLE
- rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
- #endif /* SQLITE_OMIT_VIRTUALTABLE */
- return rc;
- }
|