| 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;
|
| -}
|
|
|