summaryrefslogtreecommitdiff
path: root/chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c')
-rw-r--r--chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c964
1 files changed, 0 insertions, 964 deletions
diff --git a/chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c b/chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c
deleted file mode 100644
index 74bffc7708a..00000000000
--- a/chromium/third_party/sqlite/sqlite-src-3240000/ext/misc/closure.c
+++ /dev/null
@@ -1,964 +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((unsigned char)zStr[i]); i++){}
- if( zStr[i]!='=' ) return 0;
- i++;
- while( isspace((unsigned char)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;
- 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->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;
- }
- if( (iPlan&1)==0 ){
- /* If there is no usable "root=?" term, then set the index-type to 0.
- ** Also clear any argvIndex variables already set. This is necessary
- ** to prevent the core from throwing an "xBestIndex malfunction error"
- ** error (because the argvIndex values are not contiguously assigned
- ** starting from 1). */
- rCost *= 1e30;
- for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- pIdxInfo->aConstraintUsage[i].argvIndex = 0;
- }
- iPlan = 0;
- }
- pIdxInfo->idxNum = iPlan;
- if( pIdxInfo->nOrderBy==1
- && pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
- && pIdxInfo->aOrderBy[0].desc==0
- ){
- pIdxInfo->orderByConsumed = 1;
- }
- 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;
-}