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