mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: 3879ab1b532828fcc12a50a95b6730faebcb69e9
This commit is contained in:
@ -2975,14 +2975,12 @@ static int fts3FilterMethod(
|
||||
pCsr->iLangid = 0;
|
||||
if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
|
||||
|
||||
assert( p->base.zErrMsg==0 );
|
||||
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
|
||||
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
|
||||
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr,
|
||||
&p->base.zErrMsg
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_ERROR ){
|
||||
static const char *zErr = "malformed MATCH expression: [%s]";
|
||||
p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -524,7 +524,7 @@ void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
|
||||
|
||||
/* fts3_expr.c */
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
|
||||
char **, int, int, int, const char *, int, Fts3Expr **
|
||||
char **, int, int, int, const char *, int, Fts3Expr **, char **
|
||||
);
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
#ifdef SQLITE_TEST
|
||||
|
@ -640,8 +640,10 @@ static int fts3ExprParse(
|
||||
}
|
||||
pNot->eType = FTSQUERY_NOT;
|
||||
pNot->pRight = p;
|
||||
p->pParent = pNot;
|
||||
if( pNotBranch ){
|
||||
pNot->pLeft = pNotBranch;
|
||||
pNotBranch->pParent = pNot;
|
||||
}
|
||||
pNotBranch = pNot;
|
||||
p = pPrev;
|
||||
@ -729,6 +731,7 @@ static int fts3ExprParse(
|
||||
pIter = pIter->pLeft;
|
||||
}
|
||||
pIter->pLeft = pRet;
|
||||
pRet->pParent = pIter;
|
||||
pRet = pNotBranch;
|
||||
}
|
||||
}
|
||||
@ -745,6 +748,222 @@ exprparse_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return SQLITE_ERROR if the maximum depth of the expression tree passed
|
||||
** as the only argument is more than nMaxDepth.
|
||||
*/
|
||||
static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){
|
||||
int rc = SQLITE_OK;
|
||||
if( p ){
|
||||
if( nMaxDepth<0 ){
|
||||
rc = SQLITE_TOOBIG;
|
||||
}else{
|
||||
rc = fts3ExprCheckDepth(p->pLeft, nMaxDepth-1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprCheckDepth(p->pRight, nMaxDepth-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function attempts to transform the expression tree at (*pp) to
|
||||
** an equivalent but more balanced form. The tree is modified in place.
|
||||
** If successful, SQLITE_OK is returned and (*pp) set to point to the
|
||||
** new root expression node.
|
||||
**
|
||||
** nMaxDepth is the maximum allowable depth of the balanced sub-tree.
|
||||
**
|
||||
** Otherwise, if an error occurs, an SQLite error code is returned and
|
||||
** expression (*pp) freed.
|
||||
*/
|
||||
static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Fts3Expr *pRoot = *pp; /* Initial root node */
|
||||
Fts3Expr *pFree = 0; /* List of free nodes. Linked by pParent. */
|
||||
int eType = pRoot->eType; /* Type of node in this tree */
|
||||
|
||||
if( nMaxDepth==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
|
||||
Fts3Expr **apLeaf;
|
||||
apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
|
||||
if( 0==apLeaf ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
Fts3Expr *p;
|
||||
|
||||
/* Set $p to point to the left-most leaf in the tree of eType nodes. */
|
||||
for(p=pRoot; p->eType==eType; p=p->pLeft){
|
||||
assert( p->pParent==0 || p->pParent->pLeft==p );
|
||||
assert( p->pLeft && p->pRight );
|
||||
}
|
||||
|
||||
/* This loop runs once for each leaf in the tree of eType nodes. */
|
||||
while( 1 ){
|
||||
int iLvl;
|
||||
Fts3Expr *pParent = p->pParent; /* Current parent of p */
|
||||
|
||||
assert( pParent==0 || pParent->pLeft==p );
|
||||
p->pParent = 0;
|
||||
if( pParent ){
|
||||
pParent->pLeft = 0;
|
||||
}else{
|
||||
pRoot = 0;
|
||||
}
|
||||
rc = fts3ExprBalance(&p, nMaxDepth-1);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
for(iLvl=0; p && iLvl<nMaxDepth; iLvl++){
|
||||
if( apLeaf[iLvl]==0 ){
|
||||
apLeaf[iLvl] = p;
|
||||
p = 0;
|
||||
}else{
|
||||
assert( pFree );
|
||||
pFree->pLeft = apLeaf[iLvl];
|
||||
pFree->pRight = p;
|
||||
pFree->pLeft->pParent = pFree;
|
||||
pFree->pRight->pParent = pFree;
|
||||
|
||||
p = pFree;
|
||||
pFree = pFree->pParent;
|
||||
p->pParent = 0;
|
||||
apLeaf[iLvl] = 0;
|
||||
}
|
||||
}
|
||||
if( p ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If that was the last leaf node, break out of the loop */
|
||||
if( pParent==0 ) break;
|
||||
|
||||
/* Set $p to point to the next leaf in the tree of eType nodes */
|
||||
for(p=pParent->pRight; p->eType==eType; p=p->pLeft);
|
||||
|
||||
/* Remove pParent from the original tree. */
|
||||
assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent );
|
||||
pParent->pRight->pParent = pParent->pParent;
|
||||
if( pParent->pParent ){
|
||||
pParent->pParent->pLeft = pParent->pRight;
|
||||
}else{
|
||||
assert( pParent==pRoot );
|
||||
pRoot = pParent->pRight;
|
||||
}
|
||||
|
||||
/* Link pParent into the free node list. It will be used as an
|
||||
** internal node of the new tree. */
|
||||
pParent->pParent = pFree;
|
||||
pFree = pParent;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
p = 0;
|
||||
for(i=0; i<nMaxDepth; i++){
|
||||
if( apLeaf[i] ){
|
||||
if( p==0 ){
|
||||
p = apLeaf[i];
|
||||
p->pParent = 0;
|
||||
}else{
|
||||
pFree->pRight = p;
|
||||
pFree->pLeft = apLeaf[i];
|
||||
pFree->pLeft->pParent = pFree;
|
||||
pFree->pRight->pParent = pFree;
|
||||
|
||||
p = pFree;
|
||||
pFree = pFree->pParent;
|
||||
p->pParent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
pRoot = p;
|
||||
}else{
|
||||
/* An error occurred. Delete the contents of the apLeaf[] array
|
||||
** and pFree list. Everything else is cleaned up by the call to
|
||||
** sqlite3Fts3ExprFree(pRoot) below. */
|
||||
Fts3Expr *pDel;
|
||||
for(i=0; i<nMaxDepth; i++){
|
||||
sqlite3Fts3ExprFree(apLeaf[i]);
|
||||
}
|
||||
while( (pDel=pFree)!=0 ){
|
||||
pFree = pDel->pParent;
|
||||
sqlite3_free(pDel);
|
||||
}
|
||||
}
|
||||
|
||||
assert( pFree==0 );
|
||||
sqlite3_free( apLeaf );
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Fts3ExprFree(pRoot);
|
||||
pRoot = 0;
|
||||
}
|
||||
*pp = pRoot;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3Fts3ExprParse(), with the following
|
||||
** differences:
|
||||
**
|
||||
** 1. It does not do expression rebalancing.
|
||||
** 2. It does not check that the expression does not exceed the
|
||||
** maximum allowable depth.
|
||||
** 3. Even if it fails, *ppExpr may still be set to point to an
|
||||
** expression tree. It should be deleted using sqlite3Fts3ExprFree()
|
||||
** in this case.
|
||||
*/
|
||||
static int fts3ExprParseUnbalanced(
|
||||
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
|
||||
int iLangid, /* Language id for tokenizer */
|
||||
char **azCol, /* Array of column names for fts3 table */
|
||||
int bFts4, /* True to allow FTS4-only syntax */
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr /* OUT: Parsed query structure */
|
||||
){
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
|
||||
memset(&sParse, 0, sizeof(ParseContext));
|
||||
sParse.pTokenizer = pTokenizer;
|
||||
sParse.iLangid = iLangid;
|
||||
sParse.azCol = (const char **)azCol;
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.bFts4 = bFts4;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( n<0 ){
|
||||
n = (int)strlen(z);
|
||||
}
|
||||
rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
|
||||
assert( rc==SQLITE_OK || *ppExpr==0 );
|
||||
|
||||
/* Check for mismatched parenthesis */
|
||||
if( rc==SQLITE_OK && sParse.nNest ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameters z and n contain a pointer to and length of a buffer containing
|
||||
** an fts3 query expression, respectively. This function attempts to parse the
|
||||
@ -777,49 +996,74 @@ int sqlite3Fts3ExprParse(
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr /* OUT: Parsed query structure */
|
||||
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
|
||||
char **pzErr /* OUT: Error message (sqlite3_malloc) */
|
||||
){
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
|
||||
memset(&sParse, 0, sizeof(ParseContext));
|
||||
sParse.pTokenizer = pTokenizer;
|
||||
sParse.iLangid = iLangid;
|
||||
sParse.azCol = (const char **)azCol;
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.bFts4 = bFts4;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
return SQLITE_OK;
|
||||
static const int MAX_EXPR_DEPTH = 12;
|
||||
int rc = fts3ExprParseUnbalanced(
|
||||
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
|
||||
);
|
||||
|
||||
/* Rebalance the expression. And check that its depth does not exceed
|
||||
** MAX_EXPR_DEPTH. */
|
||||
if( rc==SQLITE_OK && *ppExpr ){
|
||||
rc = fts3ExprBalance(ppExpr, MAX_EXPR_DEPTH);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprCheckDepth(*ppExpr, MAX_EXPR_DEPTH);
|
||||
}
|
||||
}
|
||||
if( n<0 ){
|
||||
n = (int)strlen(z);
|
||||
}
|
||||
rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
|
||||
|
||||
/* Check for mismatched parenthesis */
|
||||
if( rc==SQLITE_OK && sParse.nNest ){
|
||||
rc = SQLITE_ERROR;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Fts3ExprFree(*ppExpr);
|
||||
*ppExpr = 0;
|
||||
if( rc==SQLITE_TOOBIG ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"FTS expression tree is too large (maximum depth %d)", MAX_EXPR_DEPTH
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else if( rc==SQLITE_ERROR ){
|
||||
*pzErr = sqlite3_mprintf("malformed MATCH expression: [%s]", z);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
|
||||
** Free a single node of an expression tree.
|
||||
*/
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *p){
|
||||
if( p ){
|
||||
assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
|
||||
sqlite3Fts3ExprFree(p->pLeft);
|
||||
sqlite3Fts3ExprFree(p->pRight);
|
||||
sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
|
||||
sqlite3_free(p->aMI);
|
||||
sqlite3_free(p);
|
||||
static void fts3FreeExprNode(Fts3Expr *p){
|
||||
assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
|
||||
sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
|
||||
sqlite3_free(p->aMI);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
|
||||
**
|
||||
** This function would be simpler if it recursively called itself. But
|
||||
** that would mean passing a sufficiently large expression to ExprParse()
|
||||
** could cause a stack overflow.
|
||||
*/
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *pDel){
|
||||
Fts3Expr *p;
|
||||
assert( pDel==0 || pDel->pParent==0 );
|
||||
for(p=pDel; p && (p->pLeft||p->pRight); p=(p->pLeft ? p->pLeft : p->pRight)){
|
||||
assert( p->pParent==0 || p==p->pParent->pRight || p==p->pParent->pLeft );
|
||||
}
|
||||
while( p ){
|
||||
Fts3Expr *pParent = p->pParent;
|
||||
fts3FreeExprNode(p);
|
||||
if( pParent && p==pParent->pLeft && pParent->pRight ){
|
||||
p = pParent->pRight;
|
||||
while( p && (p->pLeft || p->pRight) ){
|
||||
assert( p==p->pParent->pRight || p==p->pParent->pLeft );
|
||||
p = (p->pLeft ? p->pLeft : p->pRight);
|
||||
}
|
||||
}else{
|
||||
p = pParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -871,6 +1115,9 @@ static int queryTestTokenizer(
|
||||
** the returned expression text and then freed using sqlite3_free().
|
||||
*/
|
||||
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
|
||||
if( pExpr==0 ){
|
||||
return sqlite3_mprintf("");
|
||||
}
|
||||
switch( pExpr->eType ){
|
||||
case FTSQUERY_PHRASE: {
|
||||
Fts3Phrase *pPhrase = pExpr->pPhrase;
|
||||
@ -978,10 +1225,21 @@ static void fts3ExprTest(
|
||||
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ExprParse(
|
||||
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
|
||||
);
|
||||
if( sqlite3_user_data(context) ){
|
||||
char *zDummy = 0;
|
||||
rc = sqlite3Fts3ExprParse(
|
||||
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr, &zDummy
|
||||
);
|
||||
assert( rc==SQLITE_OK || pExpr==0 );
|
||||
sqlite3_free(zDummy);
|
||||
}else{
|
||||
rc = fts3ExprParseUnbalanced(
|
||||
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
|
||||
);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
|
||||
sqlite3Fts3ExprFree(pExpr);
|
||||
sqlite3_result_error(context, "Error parsing expression", -1);
|
||||
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
@ -1004,9 +1262,15 @@ exprtest_out:
|
||||
** with database connection db.
|
||||
*/
|
||||
int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
|
||||
return sqlite3_create_function(
|
||||
int rc = sqlite3_create_function(
|
||||
db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
|
||||
-1, SQLITE_UTF8, (void *)1, fts3ExprTest, 0, 0
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
1477
ext/misc/amatch.c
Normal file
1477
ext/misc/amatch.c
Normal file
File diff suppressed because it is too large
Load Diff
942
ext/misc/closure.c
Normal file
942
ext/misc/closure.c
Normal file
@ -0,0 +1,942 @@
|
||||
/*
|
||||
** 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>
|
||||
|
||||
/*
|
||||
** 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 ("approximate_match")
|
||||
** 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;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)10000;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that implements the "approximate_match".
|
||||
*/
|
||||
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 */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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;
|
||||
rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
||||
return rc;
|
||||
}
|
1171
ext/misc/fuzzer.c
Normal file
1171
ext/misc/fuzzer.c
Normal file
File diff suppressed because it is too large
Load Diff
131
ext/misc/ieee754.c
Normal file
131
ext/misc/ieee754.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
** 2013-04-17
|
||||
**
|
||||
** 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 SQLite extension implements functions for the exact display
|
||||
** and input of IEEE754 Binary64 floating-point numbers.
|
||||
**
|
||||
** ieee754(X)
|
||||
** ieee754(Y,Z)
|
||||
**
|
||||
** In the first form, the value X should be a floating-point number.
|
||||
** The function will return a string of the form 'ieee754(Y,Z)' where
|
||||
** Y and Z are integers such that X==Y*pow(w.0,Z).
|
||||
**
|
||||
** In the second form, Y and Z are integers which are the mantissa and
|
||||
** base-2 exponent of a new floating point number. The function returns
|
||||
** a floating-point value equal to Y*pow(2.0,Z).
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** ieee754(2.0) -> 'ieee754(2,0)'
|
||||
** ieee754(45.25) -> 'ieee754(181,-2)'
|
||||
** ieee754(2, 0) -> 2.0
|
||||
** ieee754(181, -2) -> 45.25
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Implementation of the ieee754() function
|
||||
*/
|
||||
static void ieee754func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
if( argc==1 ){
|
||||
sqlite3_int64 m, a;
|
||||
double r;
|
||||
int e;
|
||||
int isNeg;
|
||||
char zResult[100];
|
||||
assert( sizeof(m)==sizeof(r) );
|
||||
if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
|
||||
r = sqlite3_value_double(argv[0]);
|
||||
if( r<0.0 ){
|
||||
isNeg = 1;
|
||||
r = -r;
|
||||
}else{
|
||||
isNeg = 0;
|
||||
}
|
||||
memcpy(&a,&r,sizeof(a));
|
||||
if( a==0 ){
|
||||
e = 0;
|
||||
m = 0;
|
||||
}else{
|
||||
e = a>>52;
|
||||
m = a & ((((sqlite3_int64)1)<<52)-1);
|
||||
m |= ((sqlite3_int64)1)<<52;
|
||||
while( e<1075 && m>0 && (m&1)==0 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
if( isNeg ) m = -m;
|
||||
}
|
||||
sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
|
||||
m, e-1075);
|
||||
sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
|
||||
}else if( argc==2 ){
|
||||
sqlite3_int64 m, e, a;
|
||||
double r;
|
||||
int isNeg = 0;
|
||||
m = sqlite3_value_int64(argv[0]);
|
||||
e = sqlite3_value_int64(argv[1]);
|
||||
if( m<0 ){
|
||||
isNeg = 1;
|
||||
m = -m;
|
||||
if( m<0 ) return;
|
||||
}else if( m==0 && e>1000 && e<1000 ){
|
||||
sqlite3_result_double(context, 0.0);
|
||||
return;
|
||||
}
|
||||
while( (m>>32)&0xffe00000 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
while( ((m>>32)&0xfff00000)==0 ){
|
||||
m <<= 1;
|
||||
e--;
|
||||
}
|
||||
e += 1075;
|
||||
if( e<0 ) e = m = 0;
|
||||
if( e>0x7ff ) m = 0;
|
||||
a = m & ((((sqlite3_int64)1)<<52)-1);
|
||||
a |= e<<52;
|
||||
if( isNeg ) a |= ((sqlite3_int64)1)<<63;
|
||||
memcpy(&r, &a, sizeof(r));
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_ieee_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
||||
ieee754func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
||||
ieee754func, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
265
ext/misc/nextchar.c
Normal file
265
ext/misc/nextchar.c
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
** 2013-02-28
|
||||
**
|
||||
** 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 to implement the next_char(A,T,F,W) SQL function.
|
||||
**
|
||||
** The next_char(A,T,F,H) function finds all valid "next" characters for
|
||||
** string A given the vocabulary in T.F. The T.F field should be indexed.
|
||||
** If the W value exists and is a non-empty string, then it is an SQL
|
||||
** expression that limits the entries in T.F that will be considered.
|
||||
**
|
||||
** For example, suppose an application has a dictionary like this:
|
||||
**
|
||||
** CREATE TABLE dictionary(word TEXT UNIQUE);
|
||||
**
|
||||
** Further suppose that for user keypad entry, it is desired to disable
|
||||
** (gray out) keys that are not valid as the next character. If the
|
||||
** the user has previously entered (say) 'cha' then to find all allowed
|
||||
** next characters (and thereby determine when keys should not be grayed
|
||||
** out) run the following query:
|
||||
**
|
||||
** SELECT next_char('cha','dictionary','word');
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** A structure to hold context of the next_char() computation across
|
||||
** nested function calls.
|
||||
*/
|
||||
typedef struct nextCharContext nextCharContext;
|
||||
struct nextCharContext {
|
||||
sqlite3 *db; /* Database connection */
|
||||
sqlite3_stmt *pStmt; /* Prepared statement used to query */
|
||||
const unsigned char *zPrefix; /* Prefix to scan */
|
||||
int nPrefix; /* Size of zPrefix in bytes */
|
||||
int nAlloc; /* Space allocated to aResult */
|
||||
int nUsed; /* Space used in aResult */
|
||||
unsigned int *aResult; /* Array of next characters */
|
||||
int mallocFailed; /* True if malloc fails */
|
||||
int otherError; /* True for any other failure */
|
||||
};
|
||||
|
||||
/*
|
||||
** Append a result character if the character is not already in the
|
||||
** result.
|
||||
*/
|
||||
static void nextCharAppend(nextCharContext *p, unsigned c){
|
||||
int i;
|
||||
for(i=0; i<p->nUsed; i++){
|
||||
if( p->aResult[i]==c ) return;
|
||||
}
|
||||
if( p->nUsed+1 > p->nAlloc ){
|
||||
unsigned int *aNew;
|
||||
int n = p->nAlloc*2 + 30;
|
||||
aNew = sqlite3_realloc(p->aResult, n*sizeof(unsigned int));
|
||||
if( aNew==0 ){
|
||||
p->mallocFailed = 1;
|
||||
return;
|
||||
}else{
|
||||
p->aResult = aNew;
|
||||
p->nAlloc = n;
|
||||
}
|
||||
}
|
||||
p->aResult[p->nUsed++] = c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a character into z[] as UTF8. Return the number of bytes needed
|
||||
** to hold the character
|
||||
*/
|
||||
static int writeUtf8(unsigned char *z, unsigned c){
|
||||
if( c<0x00080 ){
|
||||
z[0] = (unsigned char)(c&0xff);
|
||||
return 1;
|
||||
}
|
||||
if( c<0x00800 ){
|
||||
z[0] = 0xC0 + (unsigned char)((c>>6)&0x1F);
|
||||
z[1] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 2;
|
||||
}
|
||||
if( c<0x10000 ){
|
||||
z[0] = 0xE0 + (unsigned char)((c>>12)&0x0F);
|
||||
z[1] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 3;
|
||||
}
|
||||
z[0] = 0xF0 + (unsigned char)((c>>18) & 0x07);
|
||||
z[1] = 0x80 + (unsigned char)((c>>12) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[3] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a UTF8 character out of z[] and write it into *pOut. Return
|
||||
** the number of bytes in z[] that were used to construct the character.
|
||||
*/
|
||||
static int readUtf8(const unsigned char *z, unsigned *pOut){
|
||||
static const unsigned char validBits[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||||
};
|
||||
unsigned c = z[0];
|
||||
if( c<0xc0 ){
|
||||
*pOut = c;
|
||||
return 1;
|
||||
}else{
|
||||
int n = 1;
|
||||
c = validBits[c-0xc0];
|
||||
while( (z[n] & 0xc0)==0x80 ){
|
||||
c = (c<<6) + (0x3f & z[n++]);
|
||||
}
|
||||
if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){
|
||||
c = 0xFFFD;
|
||||
}
|
||||
*pOut = c;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The nextCharContext structure has been set up. Add all "next" characters
|
||||
** to the result set.
|
||||
*/
|
||||
static void findNextChars(nextCharContext *p){
|
||||
unsigned cPrev = 0;
|
||||
unsigned char zPrev[8];
|
||||
int n, rc;
|
||||
|
||||
for(;;){
|
||||
sqlite3_bind_text(p->pStmt, 1, (char*)p->zPrefix, p->nPrefix,
|
||||
SQLITE_STATIC);
|
||||
n = writeUtf8(zPrev, cPrev+1);
|
||||
sqlite3_bind_text(p->pStmt, 2, (char*)zPrev, n, SQLITE_STATIC);
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
if( rc==SQLITE_DONE ){
|
||||
sqlite3_reset(p->pStmt);
|
||||
return;
|
||||
}else if( rc!=SQLITE_ROW ){
|
||||
p->otherError = rc;
|
||||
return;
|
||||
}else{
|
||||
const unsigned char *zOut = sqlite3_column_text(p->pStmt, 0);
|
||||
unsigned cNext;
|
||||
n = readUtf8(zOut+p->nPrefix, &cNext);
|
||||
sqlite3_reset(p->pStmt);
|
||||
nextCharAppend(p, cNext);
|
||||
cPrev = cNext;
|
||||
if( p->mallocFailed ) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** next_character(A,T,F,W)
|
||||
**
|
||||
** Return a string composted of all next possible characters after
|
||||
** A for elements of T.F. If W is supplied, then it is an SQL expression
|
||||
** that limits the elements in T.F that are considered.
|
||||
*/
|
||||
static void nextCharFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
nextCharContext c;
|
||||
const unsigned char *zTable = sqlite3_value_text(argv[1]);
|
||||
const unsigned char *zField = sqlite3_value_text(argv[2]);
|
||||
const unsigned char *zWhere;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.db = sqlite3_context_db_handle(context);
|
||||
c.zPrefix = sqlite3_value_text(argv[0]);
|
||||
c.nPrefix = sqlite3_value_bytes(argv[0]);
|
||||
if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
|
||||
if( argc<4
|
||||
|| (zWhere = sqlite3_value_text(argv[3]))==0
|
||||
|| zWhere[0]==0
|
||||
){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=(?1 || ?2)"
|
||||
" AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
|
||||
" ORDER BY 1 ASC LIMIT 1",
|
||||
zField, zTable, zField, zField);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=(?1 || ?2)"
|
||||
" AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
|
||||
" AND (%s)"
|
||||
" ORDER BY 1 ASC LIMIT 1",
|
||||
zField, zTable, zField, zField, zWhere);
|
||||
}
|
||||
if( zSql==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare_v2(c.db, zSql, -1, &c.pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_result_error(context, sqlite3_errmsg(c.db), -1);
|
||||
return;
|
||||
}
|
||||
findNextChars(&c);
|
||||
if( c.mallocFailed ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
unsigned char *pRes;
|
||||
pRes = sqlite3_malloc( c.nUsed*4 + 1 );
|
||||
if( pRes==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
int i;
|
||||
int n = 0;
|
||||
for(i=0; i<c.nUsed; i++){
|
||||
n += writeUtf8(pRes+n, c.aResult[i]);
|
||||
}
|
||||
pRes[n] = 0;
|
||||
sqlite3_result_text(context, (const char*)pRes, n, sqlite3_free);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(c.pStmt);
|
||||
sqlite3_free(c.aResult);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_nextchar_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
756
ext/misc/regexp.c
Normal file
756
ext/misc/regexp.c
Normal file
@ -0,0 +1,756 @@
|
||||
/*
|
||||
** 2012-11-13
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** The code in this file implements a compact but reasonably
|
||||
** efficient regular-expression matcher for posix extended regular
|
||||
** expressions against UTF8 text.
|
||||
**
|
||||
** This file is an SQLite extension. It registers a single function
|
||||
** named "regexp(A,B)" where A is the regular expression and B is the
|
||||
** string to be matched. By registering this function, SQLite will also
|
||||
** then implement the "B regexp A" operator. Note that with the function
|
||||
** the regular expression comes first, but with the operator it comes
|
||||
** second.
|
||||
**
|
||||
** The following regular expression syntax is supported:
|
||||
**
|
||||
** X* zero or more occurrences of X
|
||||
** X+ one or more occurrences of X
|
||||
** X? zero or one occurrences of X
|
||||
** X{p,q} between p and q occurrences of X
|
||||
** (X) match X
|
||||
** X|Y X or Y
|
||||
** ^X X occurring at the beginning of the string
|
||||
** X$ X occurring at the end of the string
|
||||
** . Match any single character
|
||||
** \c Character c where c is one of \{}()[]|*+?.
|
||||
** \c C-language escapes for c in afnrtv. ex: \t or \n
|
||||
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
||||
** \xXX Where XX is exactly 2 hex digits, unicode value XX
|
||||
** [abc] Any single character from the set abc
|
||||
** [^abc] Any single character not in the set abc
|
||||
** [a-z] Any single character in the range a-z
|
||||
** [^a-z] Any single character not in the range a-z
|
||||
** \b Word boundary
|
||||
** \w Word character. [A-Za-z0-9_]
|
||||
** \W Non-word character
|
||||
** \d Digit
|
||||
** \D Non-digit
|
||||
** \s Whitespace character
|
||||
** \S Non-whitespace character
|
||||
**
|
||||
** A nondeterministic finite automaton (NFA) is used for matching, so the
|
||||
** performance is bounded by O(N*M) where N is the size of the regular
|
||||
** expression and M is the size of the input string. The matcher never
|
||||
** exhibits exponential behavior. Note that the X{p,q} operator expands
|
||||
** to p copies of X following by q-p copies of X? and that the size of the
|
||||
** regular expression in the O(N*M) performance bound is computed after
|
||||
** this expansion.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** The following #defines change the names of some functions implemented in
|
||||
** this file to prevent name collisions with C-library functions of the
|
||||
** same name.
|
||||
*/
|
||||
#define re_match sqlite3re_match
|
||||
#define re_compile sqlite3re_compile
|
||||
#define re_free sqlite3re_free
|
||||
|
||||
/* The end-of-input character */
|
||||
#define RE_EOF 0 /* End of input */
|
||||
|
||||
/* The NFA is implemented as sequence of opcodes taken from the following
|
||||
** set. Each opcode has a single integer argument.
|
||||
*/
|
||||
#define RE_OP_MATCH 1 /* Match the one character in the argument */
|
||||
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
|
||||
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
|
||||
#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
|
||||
#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
|
||||
#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
|
||||
#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
|
||||
#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
|
||||
#define RE_OP_CC_VALUE 9 /* Single value in a character class */
|
||||
#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
|
||||
#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
|
||||
#define RE_OP_NOTWORD 12 /* Not a perl word character */
|
||||
#define RE_OP_DIGIT 13 /* digit: [0-9] */
|
||||
#define RE_OP_NOTDIGIT 14 /* Not a digit */
|
||||
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
|
||||
#define RE_OP_NOTSPACE 16 /* Not a digit */
|
||||
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
|
||||
|
||||
/* Each opcode is a "state" in the NFA */
|
||||
typedef unsigned short ReStateNumber;
|
||||
|
||||
/* Because this is an NFA and not a DFA, multiple states can be active at
|
||||
** once. An instance of the following object records all active states in
|
||||
** the NFA. The implementation is optimized for the common case where the
|
||||
** number of actives states is small.
|
||||
*/
|
||||
typedef struct ReStateSet {
|
||||
unsigned nState; /* Number of current states */
|
||||
ReStateNumber *aState; /* Current states */
|
||||
} ReStateSet;
|
||||
|
||||
/* An input string read one character at a time.
|
||||
*/
|
||||
typedef struct ReInput ReInput;
|
||||
struct ReInput {
|
||||
const unsigned char *z; /* All text */
|
||||
int i; /* Next byte to read */
|
||||
int mx; /* EOF when i>=mx */
|
||||
};
|
||||
|
||||
/* A compiled NFA (or an NFA that is in the process of being compiled) is
|
||||
** an instance of the following object.
|
||||
*/
|
||||
typedef struct ReCompiled ReCompiled;
|
||||
struct ReCompiled {
|
||||
ReInput sIn; /* Regular expression text */
|
||||
const char *zErr; /* Error message to return */
|
||||
char *aOp; /* Operators for the virtual machine */
|
||||
int *aArg; /* Arguments to each operator */
|
||||
unsigned (*xNextChar)(ReInput*); /* Next character function */
|
||||
unsigned char zInit[12]; /* Initial text to match */
|
||||
int nInit; /* Number of characters in zInit */
|
||||
unsigned nState; /* Number of entries in aOp[] and aArg[] */
|
||||
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
|
||||
};
|
||||
|
||||
/* Add a state to the given state set if it is not already there */
|
||||
static void re_add_state(ReStateSet *pSet, int newState){
|
||||
unsigned i;
|
||||
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
|
||||
pSet->aState[pSet->nState++] = newState;
|
||||
}
|
||||
|
||||
/* Extract the next unicode character from *pzIn and return it. Advance
|
||||
** *pzIn to the first byte past the end of the character returned. To
|
||||
** be clear: this routine converts utf8 to unicode. This routine is
|
||||
** optimized for the common case where the next character is a single byte.
|
||||
*/
|
||||
static unsigned re_next_char(ReInput *p){
|
||||
unsigned c;
|
||||
if( p->i>=p->mx ) return 0;
|
||||
c = p->z[p->i++];
|
||||
if( c>=0x80 ){
|
||||
if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
|
||||
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
|
||||
if( c<0x80 ) c = 0xfffd;
|
||||
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 ){
|
||||
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||||
p->i += 2;
|
||||
if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||||
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||||
| (p->z[p->i+2]&0x3f);
|
||||
p->i += 3;
|
||||
if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
|
||||
}else{
|
||||
c = 0xfffd;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
static unsigned re_next_char_nocase(ReInput *p){
|
||||
unsigned c = re_next_char(p);
|
||||
if( c>='A' && c<='Z' ) c += 'a' - 'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
|
||||
static int re_word_char(int c){
|
||||
return (c>='0' && c<='9') || (c>='a' && c<='z')
|
||||
|| (c>='A' && c<='Z') || c=='_';
|
||||
}
|
||||
|
||||
/* Return true if c is a "digit" character: [0-9] */
|
||||
static int re_digit_char(int c){
|
||||
return (c>='0' && c<='9');
|
||||
}
|
||||
|
||||
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
|
||||
static int re_space_char(int c){
|
||||
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
|
||||
}
|
||||
|
||||
/* Run a compiled regular expression on the zero-terminated input
|
||||
** string zIn[]. Return true on a match and false if there is no match.
|
||||
*/
|
||||
static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
|
||||
ReStateSet aStateSet[2], *pThis, *pNext;
|
||||
ReStateNumber aSpace[100];
|
||||
ReStateNumber *pToFree;
|
||||
unsigned int i = 0;
|
||||
unsigned int iSwap = 0;
|
||||
int c = RE_EOF+1;
|
||||
int cPrev = 0;
|
||||
int rc = 0;
|
||||
ReInput in;
|
||||
|
||||
in.z = zIn;
|
||||
in.i = 0;
|
||||
in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
|
||||
|
||||
/* Look for the initial prefix match, if there is one. */
|
||||
if( pRe->nInit ){
|
||||
unsigned char x = pRe->zInit[0];
|
||||
while( in.i+pRe->nInit<=in.mx
|
||||
&& (zIn[in.i]!=x ||
|
||||
strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
|
||||
){
|
||||
in.i++;
|
||||
}
|
||||
if( in.i+pRe->nInit>in.mx ) return 0;
|
||||
}
|
||||
|
||||
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
|
||||
pToFree = 0;
|
||||
aStateSet[0].aState = aSpace;
|
||||
}else{
|
||||
pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState );
|
||||
if( pToFree==0 ) return -1;
|
||||
aStateSet[0].aState = pToFree;
|
||||
}
|
||||
aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
|
||||
pNext = &aStateSet[1];
|
||||
pNext->nState = 0;
|
||||
re_add_state(pNext, 0);
|
||||
while( c!=RE_EOF && pNext->nState>0 ){
|
||||
cPrev = c;
|
||||
c = pRe->xNextChar(&in);
|
||||
pThis = pNext;
|
||||
pNext = &aStateSet[iSwap];
|
||||
iSwap = 1 - iSwap;
|
||||
pNext->nState = 0;
|
||||
for(i=0; i<pThis->nState; i++){
|
||||
int x = pThis->aState[i];
|
||||
switch( pRe->aOp[x] ){
|
||||
case RE_OP_MATCH: {
|
||||
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ANY: {
|
||||
re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_WORD: {
|
||||
if( re_word_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTWORD: {
|
||||
if( !re_word_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_DIGIT: {
|
||||
if( re_digit_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTDIGIT: {
|
||||
if( !re_digit_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_SPACE: {
|
||||
if( re_space_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_NOTSPACE: {
|
||||
if( !re_space_char(c) ) re_add_state(pNext, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_BOUNDARY: {
|
||||
if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ANYSTAR: {
|
||||
re_add_state(pNext, x);
|
||||
re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_FORK: {
|
||||
re_add_state(pThis, x+pRe->aArg[x]);
|
||||
re_add_state(pThis, x+1);
|
||||
break;
|
||||
}
|
||||
case RE_OP_GOTO: {
|
||||
re_add_state(pThis, x+pRe->aArg[x]);
|
||||
break;
|
||||
}
|
||||
case RE_OP_ACCEPT: {
|
||||
rc = 1;
|
||||
goto re_match_end;
|
||||
}
|
||||
case RE_OP_CC_INC:
|
||||
case RE_OP_CC_EXC: {
|
||||
int j = 1;
|
||||
int n = pRe->aArg[x];
|
||||
int hit = 0;
|
||||
for(j=1; j>0 && j<n; j++){
|
||||
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
|
||||
if( pRe->aArg[x+j]==c ){
|
||||
hit = 1;
|
||||
j = -1;
|
||||
}
|
||||
}else{
|
||||
if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
|
||||
hit = 1;
|
||||
j = -1;
|
||||
}else{
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
|
||||
if( hit ) re_add_state(pNext, x+n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<pNext->nState; i++){
|
||||
if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
|
||||
}
|
||||
re_match_end:
|
||||
sqlite3_free(pToFree);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Resize the opcode and argument arrays for an RE under construction.
|
||||
*/
|
||||
static int re_resize(ReCompiled *p, int N){
|
||||
char *aOp;
|
||||
int *aArg;
|
||||
aOp = sqlite3_realloc(p->aOp, N*sizeof(p->aOp[0]));
|
||||
if( aOp==0 ) return 1;
|
||||
p->aOp = aOp;
|
||||
aArg = sqlite3_realloc(p->aArg, N*sizeof(p->aArg[0]));
|
||||
if( aArg==0 ) return 1;
|
||||
p->aArg = aArg;
|
||||
p->nAlloc = N;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert a new opcode and argument into an RE under construction. The
|
||||
** insertion point is just prior to existing opcode iBefore.
|
||||
*/
|
||||
static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
|
||||
int i;
|
||||
if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
|
||||
for(i=p->nState; i>iBefore; i--){
|
||||
p->aOp[i] = p->aOp[i-1];
|
||||
p->aArg[i] = p->aArg[i-1];
|
||||
}
|
||||
p->nState++;
|
||||
p->aOp[iBefore] = op;
|
||||
p->aArg[iBefore] = arg;
|
||||
return iBefore;
|
||||
}
|
||||
|
||||
/* Append a new opcode and argument to the end of the RE under construction.
|
||||
*/
|
||||
static int re_append(ReCompiled *p, int op, int arg){
|
||||
return re_insert(p, p->nState, op, arg);
|
||||
}
|
||||
|
||||
/* Make a copy of N opcodes starting at iStart onto the end of the RE
|
||||
** under construction.
|
||||
*/
|
||||
static void re_copy(ReCompiled *p, int iStart, int N){
|
||||
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
|
||||
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
|
||||
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
|
||||
p->nState += N;
|
||||
}
|
||||
|
||||
/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
|
||||
** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
|
||||
** c is not a hex digit *pV is unchanged.
|
||||
*/
|
||||
static int re_hex(int c, int *pV){
|
||||
if( c>='0' && c<='9' ){
|
||||
c -= '0';
|
||||
}else if( c>='a' && c<='f' ){
|
||||
c -= 'a' - 10;
|
||||
}else if( c>='A' && c<='F' ){
|
||||
c -= 'A' - 10;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
*pV = (*pV)*16 + (c & 0xff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* A backslash character has been seen, read the next character and
|
||||
** return its interpretation.
|
||||
*/
|
||||
static unsigned re_esc_char(ReCompiled *p){
|
||||
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
||||
static const char zTrans[] = "\a\f\n\r\t\v";
|
||||
int i, v = 0;
|
||||
char c;
|
||||
if( p->sIn.i>=p->sIn.mx ) return 0;
|
||||
c = p->sIn.z[p->sIn.i];
|
||||
if( c=='u' && p->sIn.i+4<p->sIn.mx ){
|
||||
const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||||
if( re_hex(zIn[1],&v)
|
||||
&& re_hex(zIn[2],&v)
|
||||
&& re_hex(zIn[3],&v)
|
||||
&& re_hex(zIn[4],&v)
|
||||
){
|
||||
p->sIn.i += 5;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if( c=='x' && p->sIn.i+2<p->sIn.mx ){
|
||||
const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||||
if( re_hex(zIn[1],&v)
|
||||
&& re_hex(zIn[2],&v)
|
||||
){
|
||||
p->sIn.i += 3;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
|
||||
if( zEsc[i] ){
|
||||
if( i<6 ) c = zTrans[i];
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
p->zErr = "unknown \\ escape";
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static const char *re_subcompile_string(ReCompiled*);
|
||||
|
||||
/* Peek at the next byte of input */
|
||||
static unsigned char rePeek(ReCompiled *p){
|
||||
return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
|
||||
}
|
||||
|
||||
/* Compile RE text into a sequence of opcodes. Continue up to the
|
||||
** first unmatched ")" character, then return. If an error is found,
|
||||
** return a pointer to the error message string.
|
||||
*/
|
||||
static const char *re_subcompile_re(ReCompiled *p){
|
||||
const char *zErr;
|
||||
int iStart, iEnd, iGoto;
|
||||
iStart = p->nState;
|
||||
zErr = re_subcompile_string(p);
|
||||
if( zErr ) return zErr;
|
||||
while( rePeek(p)=='|' ){
|
||||
iEnd = p->nState;
|
||||
re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
|
||||
iGoto = re_append(p, RE_OP_GOTO, 0);
|
||||
p->sIn.i++;
|
||||
zErr = re_subcompile_string(p);
|
||||
if( zErr ) return zErr;
|
||||
p->aArg[iGoto] = p->nState - iGoto;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compile an element of regular expression text (anything that can be
|
||||
** an operand to the "|" operator). Return NULL on success or a pointer
|
||||
** to the error message if there is a problem.
|
||||
*/
|
||||
static const char *re_subcompile_string(ReCompiled *p){
|
||||
int iPrev = -1;
|
||||
int iStart;
|
||||
unsigned c;
|
||||
const char *zErr;
|
||||
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||||
iStart = p->nState;
|
||||
switch( c ){
|
||||
case '|':
|
||||
case '$':
|
||||
case ')': {
|
||||
p->sIn.i--;
|
||||
return 0;
|
||||
}
|
||||
case '(': {
|
||||
zErr = re_subcompile_re(p);
|
||||
if( zErr ) return zErr;
|
||||
if( rePeek(p)!=')' ) return "unmatched '('";
|
||||
p->sIn.i++;
|
||||
break;
|
||||
}
|
||||
case '.': {
|
||||
if( rePeek(p)=='*' ){
|
||||
re_append(p, RE_OP_ANYSTAR, 0);
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
re_append(p, RE_OP_ANY, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '*': {
|
||||
if( iPrev<0 ) return "'*' without operand";
|
||||
re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
|
||||
re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
|
||||
break;
|
||||
}
|
||||
case '+': {
|
||||
if( iPrev<0 ) return "'+' without operand";
|
||||
re_append(p, RE_OP_FORK, iPrev - p->nState);
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
if( iPrev<0 ) return "'?' without operand";
|
||||
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
|
||||
break;
|
||||
}
|
||||
case '{': {
|
||||
int m = 0, n = 0;
|
||||
int sz, j;
|
||||
if( iPrev<0 ) return "'{m,n}' without operand";
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
|
||||
n = m;
|
||||
if( c==',' ){
|
||||
p->sIn.i++;
|
||||
n = 0;
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
|
||||
}
|
||||
if( c!='}' ) return "unmatched '{'";
|
||||
if( n>0 && n<m ) return "n less than m in '{m,n}'";
|
||||
p->sIn.i++;
|
||||
sz = p->nState - iPrev;
|
||||
if( m==0 ){
|
||||
if( n==0 ) return "both m and n are zero in '{m,n}'";
|
||||
re_insert(p, iPrev, RE_OP_FORK, sz+1);
|
||||
n--;
|
||||
}else{
|
||||
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
|
||||
}
|
||||
for(j=m; j<n; j++){
|
||||
re_append(p, RE_OP_FORK, sz+1);
|
||||
re_copy(p, iPrev, sz);
|
||||
}
|
||||
if( n==0 && m>0 ){
|
||||
re_append(p, RE_OP_FORK, -sz);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '[': {
|
||||
int iFirst = p->nState;
|
||||
if( rePeek(p)=='^' ){
|
||||
re_append(p, RE_OP_CC_EXC, 0);
|
||||
p->sIn.i++;
|
||||
}else{
|
||||
re_append(p, RE_OP_CC_INC, 0);
|
||||
}
|
||||
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||||
if( c=='[' && rePeek(p)==':' ){
|
||||
return "POSIX character classes not supported";
|
||||
}
|
||||
if( c=='\\' ) c = re_esc_char(p);
|
||||
if( rePeek(p)=='-' ){
|
||||
re_append(p, RE_OP_CC_RANGE, c);
|
||||
p->sIn.i++;
|
||||
c = p->xNextChar(&p->sIn);
|
||||
if( c=='\\' ) c = re_esc_char(p);
|
||||
re_append(p, RE_OP_CC_RANGE, c);
|
||||
}else{
|
||||
re_append(p, RE_OP_CC_VALUE, c);
|
||||
}
|
||||
if( rePeek(p)==']' ){ p->sIn.i++; break; }
|
||||
}
|
||||
if( c==0 ) return "unclosed '['";
|
||||
p->aArg[iFirst] = p->nState - iFirst;
|
||||
break;
|
||||
}
|
||||
case '\\': {
|
||||
int specialOp = 0;
|
||||
switch( rePeek(p) ){
|
||||
case 'b': specialOp = RE_OP_BOUNDARY; break;
|
||||
case 'd': specialOp = RE_OP_DIGIT; break;
|
||||
case 'D': specialOp = RE_OP_NOTDIGIT; break;
|
||||
case 's': specialOp = RE_OP_SPACE; break;
|
||||
case 'S': specialOp = RE_OP_NOTSPACE; break;
|
||||
case 'w': specialOp = RE_OP_WORD; break;
|
||||
case 'W': specialOp = RE_OP_NOTWORD; break;
|
||||
}
|
||||
if( specialOp ){
|
||||
p->sIn.i++;
|
||||
re_append(p, specialOp, 0);
|
||||
}else{
|
||||
c = re_esc_char(p);
|
||||
re_append(p, RE_OP_MATCH, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
re_append(p, RE_OP_MATCH, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
iPrev = iStart;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free and reclaim all the memory used by a previously compiled
|
||||
** regular expression. Applications should invoke this routine once
|
||||
** for every call to re_compile() to avoid memory leaks.
|
||||
*/
|
||||
void re_free(ReCompiled *pRe){
|
||||
if( pRe ){
|
||||
sqlite3_free(pRe->aOp);
|
||||
sqlite3_free(pRe->aArg);
|
||||
sqlite3_free(pRe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compile a textual regular expression in zIn[] into a compiled regular
|
||||
** expression suitable for us by re_match() and return a pointer to the
|
||||
** compiled regular expression in *ppRe. Return NULL on success or an
|
||||
** error message if something goes wrong.
|
||||
*/
|
||||
const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
||||
ReCompiled *pRe;
|
||||
const char *zErr;
|
||||
int i, j;
|
||||
|
||||
*ppRe = 0;
|
||||
pRe = sqlite3_malloc( sizeof(*pRe) );
|
||||
if( pRe==0 ){
|
||||
return "out of memory";
|
||||
}
|
||||
memset(pRe, 0, sizeof(*pRe));
|
||||
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
|
||||
if( re_resize(pRe, 30) ){
|
||||
re_free(pRe);
|
||||
return "out of memory";
|
||||
}
|
||||
if( zIn[0]=='^' ){
|
||||
zIn++;
|
||||
}else{
|
||||
re_append(pRe, RE_OP_ANYSTAR, 0);
|
||||
}
|
||||
pRe->sIn.z = (unsigned char*)zIn;
|
||||
pRe->sIn.i = 0;
|
||||
pRe->sIn.mx = (int)strlen(zIn);
|
||||
zErr = re_subcompile_re(pRe);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
return zErr;
|
||||
}
|
||||
if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
|
||||
re_append(pRe, RE_OP_MATCH, RE_EOF);
|
||||
re_append(pRe, RE_OP_ACCEPT, 0);
|
||||
*ppRe = pRe;
|
||||
}else if( pRe->sIn.i>=pRe->sIn.mx ){
|
||||
re_append(pRe, RE_OP_ACCEPT, 0);
|
||||
*ppRe = pRe;
|
||||
}else{
|
||||
re_free(pRe);
|
||||
return "unrecognized character";
|
||||
}
|
||||
|
||||
/* The following is a performance optimization. If the regex begins with
|
||||
** ".*" (if the input regex lacks an initial "^") and afterwards there are
|
||||
** one or more matching characters, enter those matching characters into
|
||||
** zInit[]. The re_match() routine can then search ahead in the input
|
||||
** string looking for the initial match without having to run the whole
|
||||
** regex engine over the string. Do not worry able trying to match
|
||||
** unicode characters beyond plane 0 - those are very rare and this is
|
||||
** just an optimization. */
|
||||
if( pRe->aOp[0]==RE_OP_ANYSTAR ){
|
||||
for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
|
||||
unsigned x = pRe->aArg[i];
|
||||
if( x<=127 ){
|
||||
pRe->zInit[j++] = x;
|
||||
}else if( x<=0xfff ){
|
||||
pRe->zInit[j++] = 0xc0 | (x>>6);
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else if( x<=0xffff ){
|
||||
pRe->zInit[j++] = 0xd0 | (x>>12);
|
||||
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
||||
pRe->nInit = j;
|
||||
}
|
||||
return pRe->zErr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the regexp() SQL function. This function implements
|
||||
** the build-in REGEXP operator. The first argument to the function is the
|
||||
** pattern and the second argument is the string. So, the SQL statements:
|
||||
**
|
||||
** A REGEXP B
|
||||
**
|
||||
** is implemented as regexp(B,A).
|
||||
*/
|
||||
static void re_sql_func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
ReCompiled *pRe; /* Compiled regular expression */
|
||||
const char *zPattern; /* The regular expression */
|
||||
const unsigned char *zStr;/* String being searched */
|
||||
const char *zErr; /* Compile error message */
|
||||
|
||||
pRe = sqlite3_get_auxdata(context, 0);
|
||||
if( pRe==0 ){
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
zErr = re_compile(&pRe, zPattern, 0);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
return;
|
||||
}
|
||||
if( pRe==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
|
||||
}
|
||||
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
|
||||
if( zStr!=0 ){
|
||||
sqlite3_result_int(context, re_match(pRe, zStr, -1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the regexp() function with the
|
||||
** SQLite database connection.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_regexp_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
||||
re_sql_func, 0, 0);
|
||||
return rc;
|
||||
}
|
2837
ext/misc/spellfix.c
Normal file
2837
ext/misc/spellfix.c
Normal file
File diff suppressed because it is too large
Load Diff
268
ext/misc/wholenumber.c
Normal file
268
ext/misc/wholenumber.c
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
** 2011 April 02
|
||||
**
|
||||
** 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 implements a virtual table that returns the whole numbers
|
||||
** between 1 and 4294967295, inclusive.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
** SELECT value FROM nums WHERE value<10;
|
||||
**
|
||||
** Results in:
|
||||
**
|
||||
** 1 2 3 4 5 6 7 8 9
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
|
||||
/* A wholenumber cursor object */
|
||||
typedef struct wholenumber_cursor wholenumber_cursor;
|
||||
struct wholenumber_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_int64 iValue; /* Current value */
|
||||
sqlite3_int64 mxValue; /* Maximum value */
|
||||
};
|
||||
|
||||
/* Methods for the wholenumber module */
|
||||
static int wholenumberConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* Note that for this virtual table, the xCreate and xConnect
|
||||
** methods are identical. */
|
||||
|
||||
static int wholenumberDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
|
||||
|
||||
/*
|
||||
** Open a new wholenumber cursor.
|
||||
*/
|
||||
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
wholenumber_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a wholenumber cursor.
|
||||
*/
|
||||
static int wholenumberClose(sqlite3_vtab_cursor *cur){
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a cursor to its next row of output
|
||||
*/
|
||||
static int wholenumberNext(sqlite3_vtab_cursor *cur){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
pCur->iValue++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value associated with a wholenumber.
|
||||
*/
|
||||
static int wholenumberColumn(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
sqlite3_result_int64(ctx, pCur->iValue);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The rowid.
|
||||
*/
|
||||
static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
*pRowid = pCur->iValue;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
|
||||
** that the cursor has nothing more to output.
|
||||
*/
|
||||
static int wholenumberEof(sqlite3_vtab_cursor *cur){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
return pCur->iValue>pCur->mxValue || pCur->iValue==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 wholenumberColumn, wholenumberRowid, or wholenumberEof call.
|
||||
**
|
||||
** idxNum Constraints
|
||||
** ------ ---------------------
|
||||
** 0 (none)
|
||||
** 1 value > $argv0
|
||||
** 2 value >= $argv0
|
||||
** 4 value < $argv0
|
||||
** 8 value <= $argv0
|
||||
**
|
||||
** 5 value > $argv0 AND value < $argv1
|
||||
** 6 value >= $argv0 AND value < $argv1
|
||||
** 9 value > $argv0 AND value <= $argv1
|
||||
** 10 value >= $argv0 AND value <= $argv1
|
||||
*/
|
||||
static int wholenumberFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
|
||||
sqlite3_int64 v;
|
||||
int i = 0;
|
||||
pCur->iValue = 1;
|
||||
pCur->mxValue = 0xffffffff; /* 4294967295 */
|
||||
if( idxNum & 3 ){
|
||||
v = sqlite3_value_int64(argv[0]) + (idxNum&1);
|
||||
if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
|
||||
i++;
|
||||
}
|
||||
if( idxNum & 12 ){
|
||||
v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
|
||||
if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search for terms of these forms:
|
||||
**
|
||||
** (1) value > $value
|
||||
** (2) value >= $value
|
||||
** (4) value < $value
|
||||
** (8) value <= $value
|
||||
**
|
||||
** idxNum is an ORed combination of 1 or 2 with 4 or 8.
|
||||
*/
|
||||
static int wholenumberBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i;
|
||||
int idxNum = 0;
|
||||
int argvIdx = 1;
|
||||
int ltIdx = -1;
|
||||
int gtIdx = -1;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
|
||||
idxNum |= 1;
|
||||
ltIdx = i;
|
||||
}
|
||||
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
|
||||
idxNum |= 2;
|
||||
ltIdx = i;
|
||||
}
|
||||
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
||||
idxNum |= 4;
|
||||
gtIdx = i;
|
||||
}
|
||||
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
idxNum |= 8;
|
||||
gtIdx = i;
|
||||
}
|
||||
}
|
||||
pIdxInfo->idxNum = idxNum;
|
||||
if( ltIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
|
||||
pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
|
||||
}
|
||||
if( gtIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
|
||||
pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
|
||||
}
|
||||
if( pIdxInfo->nOrderBy==1
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module wholenumberModule = {
|
||||
0, /* iVersion */
|
||||
wholenumberConnect,
|
||||
wholenumberConnect,
|
||||
wholenumberBestIndex,
|
||||
wholenumberDisconnect,
|
||||
wholenumberDisconnect,
|
||||
wholenumberOpen, /* xOpen - open a cursor */
|
||||
wholenumberClose, /* xClose - close a cursor */
|
||||
wholenumberFilter, /* xFilter - configure scan constraints */
|
||||
wholenumberNext, /* xNext - advance a cursor */
|
||||
wholenumberEof, /* xEof - check for end of scan */
|
||||
wholenumberColumn, /* xColumn - read data */
|
||||
wholenumberRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_wholenumber_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
Reference in New Issue
Block a user