Index: third_party/sqlite/src/ext/misc/closure.c |
diff --git a/third_party/sqlite/src/ext/misc/closure.c b/third_party/sqlite/src/ext/misc/closure.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..30c812d2201cd2d08a5d1a5c3464dbdfbed52fb1 |
--- /dev/null |
+++ b/third_party/sqlite/src/ext/misc/closure.c |
@@ -0,0 +1,958 @@ |
+/* |
+** 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; |
+} |