1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Merge all recent trunk changes into the sessions branch.

FossilOrigin-Name: 3879ab1b532828fcc12a50a95b6730faebcb69e9
This commit is contained in:
drh
2013-05-03 18:29:22 +00:00
54 changed files with 4100 additions and 417 deletions

View File

@@ -368,7 +368,6 @@ TESTSRC = \
$(TOP)/src/test_devsym.c \
$(TOP)/src/test_fs.c \
$(TOP)/src/test_func.c \
$(TOP)/src/test_fuzzer.c \
$(TOP)/src/test_hexio.c \
$(TOP)/src/test_init.c \
$(TOP)/src/test_intarray.c \
@@ -380,7 +379,6 @@ TESTSRC = \
$(TOP)/src/test_osinst.c \
$(TOP)/src/test_pcache.c \
$(TOP)/src/test_quota.c \
$(TOP)/src/test_regexp.c \
$(TOP)/src/test_rtree.c \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
@@ -390,12 +388,23 @@ TESTSRC = \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_wholenumber.c \
$(TOP)/src/test_wsd.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c
# Statically linked extensions
#
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/fuzzer.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/nextchar.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/wholenumber.c
# Source code to the library files needed by the test fixture
#
TESTSRC2 = \

View File

@@ -689,7 +689,6 @@ TESTSRC = \
$(TOP)\src\test_devsym.c \
$(TOP)\src\test_fs.c \
$(TOP)\src\test_func.c \
$(TOP)\src\test_fuzzer.c \
$(TOP)\src\test_hexio.c \
$(TOP)\src\test_init.c \
$(TOP)\src\test_intarray.c \
@@ -701,7 +700,6 @@ TESTSRC = \
$(TOP)\src\test_osinst.c \
$(TOP)\src\test_pcache.c \
$(TOP)\src\test_quota.c \
$(TOP)\src\test_regexp.c \
$(TOP)\src\test_rtree.c \
$(TOP)\src\test_schema.c \
$(TOP)\src\test_server.c \
@@ -711,12 +709,24 @@ TESTSRC = \
$(TOP)\src\test_tclvar.c \
$(TOP)\src\test_thread.c \
$(TOP)\src\test_vfs.c \
$(TOP)\src\test_wholenumber.c \
$(TOP)\src\test_wsd.c \
$(TOP)\ext\fts3\fts3_term.c \
$(TOP)\ext\fts3\fts3_test.c \
$(TOP)\ext\session\test_session.c
# Statically linked extensions
#
TESTEXT = \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\closure.c \
$(TOP)\ext\misc\fuzzer.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\nextchar.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\spellfix.c \
$(TOP)\ext\misc\wholenumber.c
# Source code to the library files needed by the test fixture
# (non-amalgamation)
#
@@ -1227,8 +1237,8 @@ sqlite3session.lo: $(TOP)\ext\session\sqlite3sesion.c $(HDR) $(EXTHDR)
TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.lib
TESTFIXTURE_SRC1 = $(TESTSRC3) sqlite3.c
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib
TESTFIXTURE_SRC1 = $(TESTEXT) $(TESTSRC3) sqlite3.c
!IF $(USE_AMALGAMATION)==0
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
!ELSE

View File

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

View File

@@ -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

View File

@@ -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;
static const int MAX_EXPR_DEPTH = 12;
int rc = fts3ExprParseUnbalanced(
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
);
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;
/* 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 ){
static void fts3FreeExprNode(Fts3Expr *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);
}
/*
** 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]);
}
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

File diff suppressed because it is too large Load Diff

942
ext/misc/closure.c Normal file
View 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;
}

View File

@@ -141,13 +141,14 @@
** of the strings in the second or third column of the fuzzer data table
** is 50 bytes. The maximum cost on a rule is 1000.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/* If SQLITE_DEBUG is not defined, disable assert statements. */
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG
#endif
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
@@ -1155,61 +1156,16 @@ static sqlite3_module fuzzerModule = {
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/*
** Register the fuzzer virtual table
*/
int fuzzer_register(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_fuzzer_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
return rc;
}
#ifdef SQLITE_TEST
#include <tcl.h>
/*
** Decode a pointer to an sqlite3 object.
*/
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
/*
** Register the echo virtual table module.
*/
static int register_fuzzer_module(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
getDbPointer(interp, Tcl_GetString(objv[1]), &db);
fuzzer_register(db);
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
int Sqlitetestfuzzer_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
void *clientData;
} aObjCmd[] = {
{ "register_fuzzer_module", register_fuzzer_module, 0 },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
}
return TCL_OK;
}
#endif /* SQLITE_TEST */

131
ext/misc/ieee754.c Normal file
View 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
View 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;
}

View File

@@ -12,7 +12,16 @@
**
** The code in this file implements a compact but reasonably
** efficient regular-expression matcher for posix extended regular
** expressions against UTF8 text. The following syntax is supported:
** 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
@@ -49,7 +58,17 @@
*/
#include <string.h>
#include <stdlib.h>
#include "sqlite3.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 */
@@ -175,7 +194,7 @@ static int re_space_char(int c){
/* Run a compiled regular expression on the zero-terminated input
** string zIn[]. Return true on a match and false if there is no match.
*/
int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2], *pThis, *pNext;
ReStateNumber aSpace[100];
ReStateNumber *pToFree;
@@ -718,53 +737,20 @@ static void re_sql_func(
}
/*
** Invoke this routine in order to install the REGEXP function in an
** Invoke this routine to register the regexp() function with the
** SQLite database connection.
**
** Use:
**
** sqlite3_auto_extension(sqlite3_add_regexp_func);
**
** to cause this extension to be automatically loaded into each new
** database connection.
*/
int sqlite3_add_regexp_func(sqlite3 *db){
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
re_sql_func, 0, 0);
}
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
#include <tcl.h>
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
/* Implementation of the TCL command:
**
** sqlite3_add_regexp_func $DB
*/
static int tclSqlite3AddRegexpFunc(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_regexp_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
sqlite3_add_regexp_func(db);
return TCL_OK;
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;
}
/* Register the sqlite3_add_regexp_func TCL command with the TCL interpreter.
*/
int Sqlitetestregexp_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3_add_regexp_func",
tclSqlite3AddRegexpFunc, 0, 0);
return TCL_OK;
}
#endif /* SQLITE_TEST */
/**************************** End Of Test Code *******************************/

View File

@@ -12,23 +12,22 @@
**
** This module implements the spellfix1 VIRTUAL TABLE that can be used
** to search a large vocabulary for close matches. See separate
** documentation files (spellfix1.wiki and editdist3.wiki) for details.
** documentation (http://www.sqlite.org/spellfix1.html) for details.
*/
#if SQLITE_CORE
# include "sqliteInt.h"
#else
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include "sqlite3ext.h"
# include <assert.h>
# define ALWAYS(X) 1
# define NEVER(X) 0
typedef unsigned char u8;
typedef unsigned short u16;
SQLITE_EXTENSION_INIT1
#endif /* !SQLITE_CORE */
#include <ctype.h>
# include <ctype.h>
#endif
/*
** Character classes for ASCII characters:
@@ -2822,21 +2821,13 @@ static int spellfix1Register(sqlite3 *db){
return rc;
}
#if SQLITE_CORE || defined(SQLITE_TEST)
/*
** Register the spellfix1 virtual table and its associated functions.
*/
int sqlite3_spellfix1_register(sqlite3 *db){
return spellfix1Register(db);
}
#endif
#if !SQLITE_CORE
/*
** Extension load function.
*/
int sqlite3_spellfix1_init(
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_spellfix_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -2844,4 +2835,3 @@ int sqlite3_spellfix1_init(
SQLITE_EXTENSION_INIT2(pApi);
return spellfix1Register(db);
}
#endif /* !SQLITE_CORE */

View File

@@ -22,7 +22,8 @@
**
** 1 2 3 4 5 6 7 8 9
*/
#include "sqlite3.h"
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
@@ -250,62 +251,18 @@ static sqlite3_module wholenumberModule = {
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/*
** Register the wholenumber virtual table
*/
int wholenumber_register(sqlite3 *db){
#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;
}
#ifdef SQLITE_TEST
#include <tcl.h>
/*
** Decode a pointer to an sqlite3 object.
*/
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
/*
** Register the echo virtual table module.
*/
static int register_wholenumber_module(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
wholenumber_register(db);
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
int Sqlitetestwholenumber_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
void *clientData;
} aObjCmd[] = {
{ "register_wholenumber_module", register_wholenumber_module, 0 },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
}
return TCL_OK;
}
#endif /* SQLITE_TEST */

26
magic.txt Normal file
View File

@@ -0,0 +1,26 @@
# This file contains suggested magic(5) text for the unix file(1)
# utility for recognizing SQLite3 databases.
#
# When SQLite is used as an application file format, it is desirable to
# have file(1) recognize the database file as being with the specific
# application. You can set the application_id for a database file
# using:
#
# PRAGMA application_id = INTEGER;
#
# INTEGER can be any signed 32-bit integer. That integer is written as
# a 4-byte big-endian integer into offset 68 of the database header.
#
# The Monotone application used "PRAGMA user_version=1598903374;" to set
# its identifier long before "PRAGMA application_id" became available.
# The user_version is very similar to application_id except that it is
# stored at offset 68 instead of offset 60. The application_id pragma
# is preferred. The rule using offset 60 for Monotone is for historical
# compatibility only.
#
0 string =SQLite\ format\ 3
>68 belong =0x0f055111 Fossil repository -
>68 belong =0x0f055112 Fossil checkout -
>68 belong =0x0f055113 Fossil global configuration -
>60 belong =0x5f4d544e Monotone source repository -
>0 string =SQLite SQLite3 database

16
main.mk
View File

@@ -248,7 +248,6 @@ TESTSRC = \
$(TOP)/src/test_devsym.c \
$(TOP)/src/test_fs.c \
$(TOP)/src/test_func.c \
$(TOP)/src/test_fuzzer.c \
$(TOP)/src/test_hexio.c \
$(TOP)/src/test_init.c \
$(TOP)/src/test_intarray.c \
@@ -260,7 +259,6 @@ TESTSRC = \
$(TOP)/src/test_osinst.c \
$(TOP)/src/test_pcache.c \
$(TOP)/src/test_quota.c \
$(TOP)/src/test_regexp.c \
$(TOP)/src/test_rtree.c \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
@@ -271,9 +269,21 @@ TESTSRC = \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_wholenumber.c \
$(TOP)/src/test_wsd.c
# Extensions to be statically loaded.
#
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/fuzzer.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/nextchar.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/wholenumber.c
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c

107
manifest
View File

@@ -1,9 +1,9 @@
C Merge\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch.
D 2013-04-22T23:59:06.075
C Merge\sall\srecent\strunk\schanges\sinto\sthe\ssessions\sbranch.
D 2013-05-03T18:29:22.159
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 4db477715e5d66fdcbb4f7a0870d10b0adbe007e
F Makefile.in e2acdd75b30e5f2fd8739c923c746d9d2228fe9a
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 95b9e9992abcb32dda9ad7460bb1c4a3e0985909
F Makefile.msc af9891d1f609607a54524f4ccabcbe5c38e4a8e3
F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION 0dee4d2e0c64791ff0085277424fb5c07d79fc9a
@@ -55,11 +55,11 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 784aadfb4c2a217c3eb1feaecac924989f29728f
F ext/fts3/fts3.c 5c3d44d16701cc4bc81ebf0bb9d5bff136d42de0
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h 352c8a83ee4c6a14ced1759a39dd890ab947cbe0
F ext/fts3/fts3Int.h 23ea0a2bb7258d2539376ed60220cce28ba25765
F ext/fts3/fts3_aux.c b02632f6dd0e375ce97870206d914ea6d8df5ccd
F ext/fts3/fts3_expr.c 6cb4410f87676ae633bd7923bbc78526cb839c4d
F ext/fts3/fts3_expr.c 44b4a3c4983ddbf1958c4a40468efb4ff2e0549a
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
@@ -83,6 +83,14 @@ F ext/fts3/unicode/mkunicode.tcl 7a9bc018e2962abb79563c5a39fe581fcbf2f675
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/misc/amatch.c 3369b2b544066e620d986f0085d039c77d1ef17f
F ext/misc/closure.c fec0c8537c69843e0b7631d500a14c0527962cd6
F ext/misc/fuzzer.c fb64a15af978ae73fa9075b9b1dfbe82b8defc6f w src/test_fuzzer.c
F ext/misc/ieee754.c 2565ce373d842977efe0922dc50b8a41b3289556
F ext/misc/nextchar.c 1131e2b36116ffc6fe6b2e3464bfdace27978b1e
F ext/misc/regexp.c c25c65fe775f5d9801fb8573e36ebe73f2c0c2e0 w src/test_regexp.c
F ext/misc/spellfix.c f9d24a2b2617cee143b7841b453e4e1fd8f189cc w src/test_spellfix.c
F ext/misc/wholenumber.c ce362368b9381ea48cbd951ade8df867eeeab014 w src/test_wholenumber.c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 757abea591d4ff67c0ff4e8f9776aeda86b18c14
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
@@ -116,7 +124,8 @@ F ext/session/sqlite3session.h f374c9c4c96e08f67ac418871c29d423245c7673
F ext/session/test_session.c ea4dc9b4a1895c8e6bddcbfe3838d7eb57df2d99
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 88fb64131032933d96fd65039a6dd9140beb2566
F magic.txt 291863ca976425e2e7bf3f775eb98ece4dd120f6
F main.mk 06e980ed70c3fa4c27c35ff38735af6e05a64304
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -141,7 +150,7 @@ F src/backup.c b266767351ae2d847716c56fcb2a1fea7c761c03
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 480a6d255cc4f066029daf23dd54acf152cd0e13
F src/btree.h d9490cd37aaeb530a41b07f06e1262950b1be916
F src/btree.h 6fa8a3ff2483d0bb64a9f0105a8cedeac9e00cca
F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2
F src/build.c 083da8466fd7e481cb8bd5264398f537507f6176
F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
@@ -149,7 +158,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
F src/delete.c 39a770e9729b1acd2de347f8f614584841d0083e
F src/expr.c 48048fca951eedbc74aa32262154410d56c83812
F src/expr.c 437c03d5bb4fe3a53ecab3ad0286d6c5260da7ed
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e16942bd5c8a868ac53287886464a5ed0e72b179
F src/func.c d3fdcff9274bc161152e67ed3f626841c247f4b9
@@ -179,38 +188,38 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be
F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_unix.c 5a9ac4a566fb566a2ff9b9e3a9d723075d9d26a7
F src/os_unix.c 658b180a09a18214d94547f737dbded71667cdab
F src/os_win.c 673b3e3d1fa3040d8d95a7f1f5e0e553aed56cfb
F src/pager.c 6c3a8a5d665498b0344395a2c9f82d5abc4cc771
F src/pager.c 49e23f9898113ddfe90942bdf1c1ef57955d0921
F src/pager.h 5cb78b8e1adfd5451e600be7719f5a99d87ac3b1
F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95
F src/parse.y 9708365594eea519cdc8504dee425c0a41c79502
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9
F src/pragma.c 3eacf001cbf4becbd494f8d82d08fdf1648cf8cb
F src/pragma.c 8779308bc1ea1901c4bc94dfe9a83d436f73f52c
F src/prepare.c 743e484233c51109666d402f470523553b41797c
F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 10a1b332e3eb36e5d561085e18c58a8578cd7d73
F src/resolve.c 83cc2d942ee216bc56956c6e6fadb691c1727fa1
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
F src/select.c 01540bcd3df3c8f1187158e77986028b1c667258
F src/shell.c aca9d94653decd4496846dee0c7ba83eaf96a46d
F src/sqlite.h.in eddda5f1967e84336e11f3a5c6fd3be3337d66c1
F src/select.c 6bfbe11e2fef81c5e18d30513ab6c69f171667eb
F src/shell.c 5d527e5d08f05ec2c43ff194ea44bf62b974f4c9
F src/sqlite.h.in 3b9c6d8e5b3b93e39c266c9534369042c14e9a31
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5
F src/sqliteInt.h a9f727c0d568f64f06ae430e55a074d8dd1ccde4
F src/sqliteInt.h 4b768ec538d5ed20f5ca0196a25c2c029b8513cb
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c a15550a334ca07ac2bc5d32c5f97e3d61be886e8
F src/test1.c 6784fdacb35c33ba564ef749b62c4718fe515484
F src/tclsqlite.c c21f61c56d519b4bcc0dcf453953edba69266854
F src/test1.c 2b0ec224983403312a4d1db8546e1e1c45694251
F src/test2.c 29e7154112f7448d64204e8d31179cf497ecf425
F src/test3.c 96aed72a8e1d542fed127e3e8350ae357712fa82
F src/test4.c cea2c55110241e4674e66d476d29c914627999f5
F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
F src/test6.c a437f76f9874d2563352a7e6cd0d43217663c220
F src/test7.c f4b894b7931f8cf9f5cbf37cfa0727703f526a40
F src/test8.c 58ea1d9698f3947e4662107ef98f429e84ae20e0
F src/test8.c f7e729e3e1973f68e6d98f5aa65046e3e2cb0bad
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
F src/test_autoext.c 5c95b5d435eaa09d6c0e7d90371c5ca8cd567701
@@ -221,7 +230,6 @@ F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c 8f786bfd0ad48030cf2a06fb1f050e9c60a150d7
F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
F src/test_intarray.c 07ddcebe4097d400ffca362770f1d883c112387a
@@ -237,11 +245,9 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
F src/test_quota.c 1ec82e02fd3643899e9a5de9684515e84641c91f
F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
F src/test_regexp.c 06ae8138d41a793330f62351283dd6f6f21104f4
F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
F src/test_spellfix.c 56dfa6d583ac34f61af0834d7b58d674e7e18e13
F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e
F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
@@ -250,14 +256,13 @@ F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4
F src/test_vfs.c 8e6087a8b3dcc260282074b0efba14b76311120c
F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
F src/trigger.c cd95ac64efa60e39faf9b5597443192ff27a22fa
F src/update.c beef58f5fd66153ac9cdf6e9f6551f09ee68976c
F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f
F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9
F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3
F src/vacuum.c ddf21cc9577c4cb459d08bee9863a78ec000c5bb
F src/vdbe.c 349798f630ce49c2e21a6c30863f195c484cfec5
F src/vdbe.h 1223e2548e0970cf96f573ff6b99f804a36ad683
F src/vdbeInt.h a6b7a1fbb2b335fd8c3b4b8a696b1ba28eae2191
@@ -269,10 +274,10 @@ F src/vdbesort.c 4fad64071ae120c25f39dcac572d716b9cadeb7f
F src/vdbetrace.c 3ad1b4e92b60c082a02ac563da4a2735cc7d297c
F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d
F src/wal.h a4d3da523d55a226a0b28e9058ef88d0a8051887
F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b
F src/where.c d54e63087b52c309550aa2defdb20ef27add9f9a
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
F src/where.c 12d4200eb6ae991cad02367c391db076ac1af1b0
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -287,7 +292,7 @@ F test/analyze3.test c3c7f6c3951900c188cf94b2d5ee3246d6b3ff89
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
F test/analyze5.test 713354664c5ff1853ab2cbcb740f0cf5cb7c802e
F test/analyze6.test aa8dae5066bbed35c5f45a507fb87f2d342f2c99
F test/analyze7.test d3587aa5af75c9048d031b94fceca2534fa75d1d
F test/analyze7.test bd09e89264c664d8d8d2450b6866dcdfae080a13
F test/analyze8.test 4ca170de2ba30ccb1af2c0406803db72262f9691
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
@@ -344,6 +349,7 @@ F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
F test/check.test 2eb93611139a7dfaed3be80067c7dc5ceb5fb287
F test/closure01.test 6194a899cdbba561d0439c0d6cc7bcdf4fc413e7
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
F test/collate1.test fd02c4d8afc71879c4bb952586389961a21fb0ce
F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49
@@ -506,6 +512,7 @@ F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3expr3.test 1bfb762b53a794f990f3dffaae8bbea5736422f7
F test/fts3fault.test cb72dccb0a3b9f730f16c5240f3fcb9303eb1660
F test/fts3fault2.test 3198eef2804deea7cac8403e771d9cbcb752d887
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
@@ -539,8 +546,8 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzer1.test a2e93bb1e19513dd6bf9c63d3d7c4673c983ca19
F test/fuzzerfault.test ff2282c81797b6a355f0748d8b54c7287c5d2b25
F test/fuzzer1.test 41bd5aa6ae0cf18d06342a4476e3cad98604ae48
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
F test/hook.test 777b2541f6dd4f4ca5e8d6b66c1df1b3717aeab6
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3
@@ -575,7 +582,7 @@ F test/instr.test a34e1d46a9eefb098a7167ef0e730a4a3d82fba0
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc
F test/intpkey.test 7af30f6ae852d8d1c2b70e4bf1551946742e92d8
F test/io.test a4be25a446a99a0604ceecc039ee7363c56e4185
F test/io.test 0147ed5fdbce3286d6128f5f7e3b76ee8352d652
F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8
F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
@@ -636,7 +643,7 @@ F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
F test/malloc_common.tcl 9a98856549bfb3fab205edbc1317216edc52e70d
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
F test/memdb.test db5260330676de007be8530d6ecc7c9ab2b06ad3
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
@@ -652,7 +659,7 @@ F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test dd82ec9250b89178b96cd28b2aca70639d21e5b3
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
F test/mmap1.test 0b5802cf64acaa509ea889b3c708196cc6eb9d31
F test/mmap1.test 8696aa1b0bd88961c2f16af2a3f7a69d701cea50
F test/mmap2.test a5ba639f90b5fc487400a49e158e14e465943e98
F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
@@ -682,8 +689,8 @@ F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/permutations.test a19a70a80836b5e183e46f4043fb7626446ab5e0
F test/pragma.test 60d29cd3d8098a2c20bf4c072810f99e3bf2757a
F test/permutations.test 1981ef401ecd5292058c19ee10c94aa17c9039fd
F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
@@ -696,7 +703,7 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
F test/regexp1.test 5cbb6e7122ca51260d71079cf9245b63b8f64e1a
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce
@@ -731,7 +738,7 @@ F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
F test/selectD.test 03f7c1ea8d5ab3c637cbc30fcbbbac96b988c162
F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/session.test c1a17c11ef7d01c24fe2b9f7871190d949a8e718
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
@@ -763,7 +770,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/spellfix.test 52ae2680b1247c52b9e2b2116de3fd26a78e6bd2
F test/spellfix.test bea537caf587df30d430c2c6a8fe9f64b8712834
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
F test/stat.test be8d477306006ec696bc86757cfb34bec79447ce
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
@@ -796,7 +803,7 @@ F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
F test/tkt-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6
F test/tkt-2d1a5c67d.test b028a811049eb472cb2d3a43fc8ce4f6894eebda
F test/tkt-2d1a5c67d.test d371279946622698ab393ff88cad9f5f6d82960b
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
F test/tkt-31338dca7e.test 6fb8807851964da0d24e942f2e19c7c705b9fb58
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
@@ -1010,7 +1017,7 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a
F test/where8.test 02619a9bfc6df7b19979a02852bac09c3c99477a
F test/where8.test d9f889e62dccddb9d790b0c84dfc7861e03a162c
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test 0157862ccf0cfdf1a4622cdf697e5e2f09a8de44
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
@@ -1022,9 +1029,9 @@ F test/whereF.test a0e296643cabe5278379bc1a0aa158cf3c54a1c9
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test e7f77fded01dfcdf92ac2c5400f1e35d7a21463c
F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
F tool/build-all-msvc.bat 74fb6e5cca66ebdb6c9bbafb2f8b802f08146d38 x
F tool/build-shell.sh a9c34a606e2e522ba9eeca1e07090f67dce8c912
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
@@ -1047,7 +1054,7 @@ F tool/omittest.tcl 4665982e95a6e5c1bd806cf7bc3dea95be422d77
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/showdb.c acd24ea035a3bd2ffe266f1ef8a161812c29b2f0
F tool/showdb.c 525ecc443578647703051308ad50a93de6ba2c4b
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
@@ -1067,7 +1074,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
P 67b3c0efa7d5e0cb7cc0fc7606ab3f26ea5419fd 1a1cf5aa86734c832d845e07780262a178188d56
R 732bd62d8f3cf92b7ff68a9a14e06f6a
P 6994826c0784280f2e9728dfa4185848846d03df b2efe4f225adc5f4c2e3080bf459cc52fff82e18
R 70853c32af7db548acc44e4c7931cc60
U drh
Z d4e35c064e319291a70a72e245362c9b
Z ccdd14f93f4b60d77de88619ca7032d3

View File

@@ -1 +1 @@
6994826c0784280f2e9728dfa4185848846d03df
3879ab1b532828fcc12a50a95b6730faebcb69e9

View File

@@ -140,6 +140,7 @@ int sqlite3BtreeNewDb(Btree *p);
#define BTREE_TEXT_ENCODING 5
#define BTREE_USER_VERSION 6
#define BTREE_INCR_VACUUM 7
#define BTREE_APPLICATION_ID 8
/*
** Values that may be OR'd together to form the second argument of an

View File

@@ -1214,6 +1214,7 @@ static int selectNodeIsConstant(Walker *pWalker, Select *NotUsed){
}
static int exprIsConst(Expr *p, int initFlag){
Walker w;
memset(&w, 0, sizeof(w));
w.u.i = initFlag;
w.xExprCallback = exprNodeIsConstant;
w.xSelectCallback = selectNodeIsConstant;
@@ -3428,8 +3429,8 @@ void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
Walker w;
if( pParse->cookieGoto ) return;
if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return;
memset(&w, 0, sizeof(w));
w.xExprCallback = evalConstExpr;
w.xSelectCallback = 0;
w.pParse = pParse;
sqlite3WalkExpr(&w, pExpr);
}

View File

@@ -126,7 +126,7 @@
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#ifndef SQLITE_OMIT_WAL
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
#include <sys/mman.h>
#endif
@@ -3193,6 +3193,51 @@ static int unixRead(
}
}
/*
** Attempt to seek the file-descriptor passed as the first argument to
** absolute offset iOff, then attempt to write nBuf bytes of data from
** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
** return the actual number of bytes written (which may be less than
** nBuf).
*/
static int seekAndWriteFd(
int fd, /* File descriptor to write to */
i64 iOff, /* File offset to begin writing at */
const void *pBuf, /* Copy data from this buffer to the file */
int nBuf, /* Size of buffer pBuf in bytes */
int *piErrno /* OUT: Error number if error occurs */
){
int rc = 0; /* Value returned by system call */
assert( nBuf==(nBuf&0x1ffff) );
nBuf &= 0x1ffff;
TIMER_START;
#if defined(USE_PREAD)
do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
#elif defined(USE_PREAD64)
do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
#else
do{
i64 iSeek = lseek(fd, iOff, SEEK_SET);
SimulateIOError( iSeek-- );
if( iSeek!=iOff ){
if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0);
return -1;
}
rc = osWrite(fd, pBuf, nBuf);
}while( rc<0 && errno==EINTR );
#endif
TIMER_END;
OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
if( rc<0 && piErrno ) *piErrno = errno;
return rc;
}
/*
** Seek to the offset in id->offset then read cnt bytes into pBuf.
** Return the number of bytes actually read. Update the offset.
@@ -3201,39 +3246,7 @@ static int unixRead(
** is set before returning.
*/
static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
int got;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
assert( cnt==(cnt&0x1ffff) );
cnt &= 0x1ffff;
TIMER_START;
#if defined(USE_PREAD)
do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
#elif defined(USE_PREAD64)
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
#else
do{
newOffset = lseek(id->h, offset, SEEK_SET);
SimulateIOError( newOffset-- );
if( newOffset!=offset ){
if( newOffset == -1 ){
((unixFile*)id)->lastErrno = errno;
}else{
((unixFile*)id)->lastErrno = 0;
}
return -1;
}
got = osWrite(id->h, pBuf, cnt);
}while( got<0 && errno==EINTR );
#endif
TIMER_END;
if( got<0 ){
((unixFile*)id)->lastErrno = errno;
}
OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
return got;
return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno);
}
@@ -4322,24 +4335,32 @@ static int unixShmMap(
if( sStat.st_size<nByte ){
/* The requested memory region does not exist. If bExtend is set to
** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
**
** Alternatively, if bExtend is true, use ftruncate() to allocate
** the requested memory region.
*/
if( !bExtend ) goto shmpage_out;
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
if( osFallocate(pShmNode->h, sStat.st_size, nByte)!=0 ){
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "fallocate",
pShmNode->zFilename);
if( !bExtend ){
goto shmpage_out;
}
#else
if( robust_ftruncate(pShmNode->h, nByte) ){
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate",
pShmNode->zFilename);
/* Alternatively, if bExtend is true, extend the file. Do this by
** writing a single byte to the end of each (OS) page being
** allocated or extended. Technically, we need only write to the
** last page in order to extend the file. But writing to all new
** pages forces the OS to allocate them immediately, which reduces
** the chances of SIGBUS while accessing the mapped region later on.
*/
else{
static const int pgsz = 4096;
int iPg;
/* Write to the last byte of each newly allocated or extended page */
assert( (nByte % pgsz)==0 );
for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){
const char *zFile = pShmNode->zFilename;
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
goto shmpage_out;
}
#endif
}
}
}
}
@@ -5266,9 +5287,8 @@ static int fillInUnixFile(
if( h>=0 ) robust_close(pNew, h, __LINE__);
h = -1;
osUnlink(zFilename);
isDelete = 0;
pNew->ctrlFlags |= UNIXFILE_DELETE;
}
if( isDelete ) pNew->ctrlFlags |= UNIXFILE_DELETE;
#endif
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);

View File

@@ -2871,10 +2871,13 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
return SQLITE_OK;
}
#ifndef SQLITE_OMIT_WAL
if( iFrame ){
/* Try to pull the page from the write-ahead log. */
rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
}else{
}else
#endif
{
i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
@@ -5927,6 +5930,11 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
pPager->aStat[PAGER_STAT_WRITE]++;
}
if( rc==SQLITE_OK ){
/* Update the pager's copy of the change-counter. Otherwise, the
** next time a read transaction is opened the cache will be
** flushed (as the change-counter values will not match). */
const void *pCopy = (const void *)&((const char *)zBuf)[24];
memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers));
pPager->changeCountDone = 1;
}
}else{

View File

@@ -520,7 +520,9 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I)
struct SrcList_item *pOld = F->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
sqlite3SrcListDelete(pParse->db, F);
}else{

View File

@@ -1567,6 +1567,11 @@ void sqlite3Pragma(
** PRAGMA [database.]user_version
** PRAGMA [database.]user_version = <integer>
**
** PRAGMA [database.]freelist_count = <integer>
**
** PRAGMA [database.]application_id
** PRAGMA [database.]application_id = <integer>
**
** The pragma's schema_version and user_version are used to set or get
** the value of the schema-version and user-version, respectively. Both
** the schema-version and the user-version are 32-bit signed integers
@@ -1588,10 +1593,14 @@ void sqlite3Pragma(
if( sqlite3StrICmp(zLeft, "schema_version")==0
|| sqlite3StrICmp(zLeft, "user_version")==0
|| sqlite3StrICmp(zLeft, "freelist_count")==0
|| sqlite3StrICmp(zLeft, "application_id")==0
){
int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */
sqlite3VdbeUsesBtree(v, iDb);
switch( zLeft[0] ){
case 'a': case 'A':
iCookie = BTREE_APPLICATION_ID;
break;
case 'f': case 'F':
iCookie = BTREE_FREE_PAGE_COUNT;
break;

View File

@@ -1283,6 +1283,7 @@ int sqlite3ResolveExprNames(
#endif
savedHasAgg = pNC->ncFlags & NC_HasAgg;
pNC->ncFlags &= ~NC_HasAgg;
memset(&w, 0, sizeof(w));
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
w.pParse = pNC->pParse;
@@ -1323,6 +1324,7 @@ void sqlite3ResolveSelectNames(
Walker w;
assert( p!=0 );
memset(&w, 0, sizeof(w));
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
w.pParse = pParse;

View File

@@ -3576,6 +3576,7 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
*/
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
w.xSelectCallback = selectExpander;
w.xExprCallback = exprWalkNoop;
w.pParse = pParse;
@@ -3634,9 +3635,11 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
#ifndef SQLITE_OMIT_SUBQUERY
Walker w;
memset(&w, 0, sizeof(w));
w.xSelectCallback = selectAddSubqueryTypeInfo;
w.xExprCallback = exprWalkNoop;
w.pParse = pParse;
w.bSelectDepthFirst = 1;
sqlite3WalkSelect(&w, pSelect);
#endif
}
@@ -4047,7 +4050,7 @@ int sqlite3Select(
pItem->addrFillSub = topAddr+1;
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
if( pItem->isCorrelated==0 ){
/* If the subquery is no correlated and if we are not inside of
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
onceAddr = sqlite3CodeOnce(pParse);

View File

@@ -1480,18 +1480,6 @@ static void open_db(struct callback_data *p){
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
#ifdef SQLITE_ENABLE_REGEXP
{
extern int sqlite3_add_regexp_func(sqlite3*);
sqlite3_add_regexp_func(db);
}
#endif
#ifdef SQLITE_ENABLE_SPELLFIX
{
extern int sqlite3_spellfix1_register(sqlite3*);
sqlite3_spellfix1_register(db);
}
#endif
}
}

View File

@@ -1587,7 +1587,9 @@ struct sqlite3_mem_methods {
** page cache implementation into that object.)^ </dd>
**
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
** global [error log].
** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
@@ -2522,6 +2524,9 @@ int sqlite3_set_authorizer(
** as each triggered subprogram is entered. The callbacks for triggers
** contain a UTF-8 SQL comment that identifies the trigger.)^
**
** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit
** the length of [bound parameter] expansion in the output of sqlite3_trace().
**
** ^The callback function registered by sqlite3_profile() is invoked
** as each SQL statement finishes. ^The profile callback contains
** the original statement text and an estimate of wall-clock time
@@ -3060,7 +3065,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** <li>
** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
** always used to do, [sqlite3_step()] will automatically recompile the SQL
** statement and try to run it again.
** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY]
** retries will occur before sqlite3_step() gives up and returns an error.
** </li>
**
** <li>
@@ -3264,6 +3270,9 @@ typedef struct sqlite3_context sqlite3_context;
** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
**
** ^The third argument is the value to bind to the parameter.
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
** is ignored and the end result is the same as sqlite3_bind_null().
**
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
@@ -6872,7 +6881,7 @@ int sqlite3_strglob(const char *zGlob, const char *zStr);
/*
** CAPI3REF: Error Logging Interface
**
** ^The [sqlite3_log()] interface writes a message into the error log
** ^The [sqlite3_log()] interface writes a message into the [error log]
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
** ^If logging is enabled, the zFormat string and subsequent arguments are
** used with [sqlite3_snprintf()] to generate the final output string.

View File

@@ -563,7 +563,7 @@ extern const int sqlite3one;
|| defined(_WIN32) \
|| (defined(__APPLE__) && defined(__MACH__)) \
|| defined(__sun)
# define SQLITE_MAX_MMAP_SIZE 2147483648
# define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */
# else
# define SQLITE_MAX_MMAP_SIZE 0
# endif
@@ -2600,6 +2600,7 @@ struct Walker {
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
Parse *pParse; /* Parser context. */
int walkerDepth; /* Number of subqueries */
u8 bSelectDepthFirst; /* Do subqueries first */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
int i; /* Integer value */

View File

@@ -3829,12 +3829,9 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
extern int SqlitetestSyscall_Init(Tcl_Interp*);
extern int Sqlitetestfuzzer_Init(Tcl_Interp*);
extern int Sqlitetestwholenumber_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
extern int TestSession_Init(Tcl_Interp*);
#endif
extern int Sqlitetestregexp_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
@@ -3877,12 +3874,9 @@ static void init_all(Tcl_Interp *interp){
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
SqlitetestSyscall_Init(interp);
Sqlitetestfuzzer_Init(interp);
Sqlitetestwholenumber_Init(interp);
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
TestSession_Init(interp);
#endif
Sqlitetestregexp_Init(interp);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);

View File

@@ -6045,6 +6045,69 @@ static int optimization_control(
return TCL_OK;
}
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** load_static_extension DB NAME ...
**
** Load one or more statically linked extensions.
*/
static int tclLoadStaticExtensionCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
static const struct {
const char *zExtName;
int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*);
} aExtension[] = {
{ "amatch", sqlite3_amatch_init },
{ "closure", sqlite3_closure_init },
{ "fuzzer", sqlite3_fuzzer_init },
{ "ieee754", sqlite3_ieee_init },
{ "nextchar", sqlite3_nextchar_init },
{ "regexp", sqlite3_regexp_init },
{ "spellfix", sqlite3_spellfix_init },
{ "wholenumber", sqlite3_wholenumber_init },
};
sqlite3 *db;
const char *zName;
int i, j, rc;
char *zErrMsg = 0;
if( objc<3 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME ...");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
for(j=2; j<objc; j++){
zName = Tcl_GetString(objv[j]);
for(i=0; i<ArraySize(aExtension); i++){
if( strcmp(zName, aExtension[i].zExtName)==0 ) break;
}
if( i>=ArraySize(aExtension) ){
Tcl_AppendResult(interp, "no such extension: ", zName, (char*)0);
return TCL_ERROR;
}
rc = aExtension[i].pInit(db, &zErrMsg, 0);
if( rc!=SQLITE_OK || zErrMsg ){
Tcl_AppendResult(interp, "initialization of ", zName, " failed: ", zErrMsg,
(char*)0);
sqlite3_free(zErrMsg);
return TCL_ERROR;
}
}
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
*/
@@ -6266,6 +6329,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#if SQLITE_OS_UNIX
{ "getrusage", test_getrusage },
#endif
{ "load_static_extension", tclLoadStaticExtensionCmd },
};
static int bitmask_size = sizeof(Bitmask)*8;
int i;

View File

@@ -1370,29 +1370,6 @@ static int declare_vtab(
return TCL_OK;
}
#include "test_spellfix.c"
/*
** Register the spellfix virtual table module.
*/
static int register_spellfix_module(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
sqlite3_spellfix1_register(db);
return TCL_OK;
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/*
@@ -1406,7 +1383,6 @@ int Sqlitetest8_Init(Tcl_Interp *interp){
void *clientData;
} aObjCmd[] = {
{ "register_echo_module", register_echo_module, 0 },
{ "register_spellfix_module", register_spellfix_module, 0 },
{ "sqlite3_declare_vtab", declare_vtab, 0 },
};
int i;

View File

@@ -289,6 +289,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
BTREE_USER_VERSION, 0, /* Preserve the user version */
BTREE_APPLICATION_ID, 0, /* Preserve the application id */
};
assert( 1==sqlite3BtreeIsInTrans(pTemp) );

View File

@@ -43,6 +43,7 @@
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
# define sqlite3WalFramesize(z) 0
# define sqlite3WalFindFrame(x,y,z) 0
#else
#define WAL_SAVEPOINT_NDATA 4

View File

@@ -113,7 +113,9 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
/*
** Call sqlite3WalkExpr() for every expression in Select statement p.
** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and
** on the compound select chain, p->pPrior.
** on the compound select chain, p->pPrior. Invoke the xSelectCallback()
** either before or after the walk of expressions and FROM clause, depending
** on whether pWalker->bSelectDepthFirst is false or true, respectively.
**
** Return WRC_Continue under normal conditions. Return WRC_Abort if
** there is an abort request.
@@ -127,14 +129,23 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){
rc = WRC_Continue;
pWalker->walkerDepth++;
while( p ){
if( !pWalker->bSelectDepthFirst ){
rc = pWalker->xSelectCallback(pWalker, p);
if( rc ) break;
}
if( sqlite3WalkSelectExpr(pWalker, p)
|| sqlite3WalkSelectFrom(pWalker, p)
){
pWalker->walkerDepth--;
return WRC_Abort;
}
if( pWalker->bSelectDepthFirst ){
rc = pWalker->xSelectCallback(pWalker, p);
/* Depth-first search is currently only used for
** selectAddSubqueryTypeInfo() and that routine always returns
** WRC_Continue (0). So the following branch is never taken. */
if( NEVER(rc) ) break;
}
p = p->pPrior;
}
pWalker->walkerDepth--;

View File

@@ -705,7 +705,7 @@ static WhereTerm *findTerm(
continue;
}
}
if( pTerm->prereqRight==0 ){
if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){
pResult = pTerm;
goto findTerm_success;
}else if( pResult==0 ){
@@ -4883,6 +4883,7 @@ static Bitmask codeOneLoopStart(
assert( (pTerm->prereqRight & newNotReady)!=0 );
pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0);
if( pAlt==0 ) continue;
if( pAlt->wtFlags & (TERM_CODED) ) continue;
VdbeNoopComment((v, "begin transitive constraint"));
sEq = *pAlt->pExpr;
sEq.pLeft = pE->pLeft;

View File

@@ -150,7 +150,7 @@ db close
forcedelete test.db
do_test 8_3_names-5.0 {
sqlite3 db file:./test.db?8_3_names=1
register_wholenumber_module db
load_static_extension db wholenumber
db eval {
PRAGMA journal_mode=WAL;
CREATE TABLE t1(x);
@@ -160,7 +160,7 @@ do_test 8_3_names-5.0 {
UPDATE t1 SET x=x*2;
}
sqlite3 db2 file:./test.db?8_3_names=1
register_wholenumber_module db2
load_static_extension db2 wholenumber
db2 eval {
BEGIN;
SELECT sum(x) FROM t1;

View File

@@ -26,7 +26,7 @@ ifcapable {!analyze||!vtab} {
# Generate some test data
#
do_test analyze7-1.0 {
register_wholenumber_module db
load_static_extension db wholenumber
execsql {
CREATE TABLE t1(a,b,c,d);
CREATE INDEX t1a ON t1(a);

224
test/closure01.test Normal file
View File

@@ -0,0 +1,224 @@
# 2013-04-25
#
# 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.
#
#***********************************************************************
#
# Test cases for transitive_closure virtual table.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix closure01
load_static_extension db closure
do_execsql_test 1.0 {
BEGIN;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y INTEGER);
CREATE INDEX t1y ON t1(y);
INSERT INTO t1(x) VALUES(1),(2);
INSERT INTO t1(x) SELECT x+2 FROM t1;
INSERT INTO t1(x) SELECT x+4 FROM t1;
INSERT INTO t1(x) SELECT x+8 FROM t1;
INSERT INTO t1(x) SELECT x+16 FROM t1;
INSERT INTO t1(x) SELECT x+32 FROM t1;
INSERT INTO t1(x) SELECT x+64 FROM t1;
INSERT INTO t1(x) SELECT x+128 FROM t1;
INSERT INTO t1(x) SELECT x+256 FROM t1;
INSERT INTO t1(x) SELECT x+512 FROM t1;
INSERT INTO t1(x) SELECT x+1024 FROM t1;
INSERT INTO t1(x) SELECT x+2048 FROM t1;
INSERT INTO t1(x) SELECT x+4096 FROM t1;
INSERT INTO t1(x) SELECT x+8192 FROM t1;
INSERT INTO t1(x) SELECT x+16384 FROM t1;
INSERT INTO t1(x) SELECT x+32768 FROM t1;
INSERT INTO t1(x) SELECT x+65536 FROM t1;
UPDATE t1 SET y=x/2 WHERE x>1;
COMMIT;
CREATE VIRTUAL TABLE cx
USING transitive_closure(tablename=t1, idcolumn=x, parentcolumn=y);
} {}
# The entire table
do_execsql_test 1.1 {
SELECT count(*), depth FROM cx WHERE root=1 GROUP BY depth ORDER BY 1;
} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/}
# descendents of 32768
do_execsql_test 1.2 {
SELECT * FROM cx WHERE root=32768 ORDER BY id;
} {32768 0 65536 1 65537 1 131072 2}
# descendents of 16384
do_execsql_test 1.3 {
SELECT * FROM cx WHERE root=16384 AND depth<=2 ORDER BY id;
} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2}
# children of 16384
do_execsql_test 1.4 {
SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx
WHERE root=16384
AND depth=1
ORDER BY id;
} {32768 1 {} t1 x y 32769 1 {} t1 x y}
# great-grandparent of 16384
do_execsql_test 1.5 {
SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx
WHERE root=16384
AND depth=3
AND idcolumn='Y'
AND parentcolumn='X';
} {2048 3 {} t1 Y X}
# depth<5
do_execsql_test 1.6 {
SELECT count(*), depth FROM cx WHERE root=1 AND depth<5
GROUP BY depth ORDER BY 1;
} {1 0 2 1 4 2 8 3 16 4}
# depth<=5
do_execsql_test 1.7 {
SELECT count(*), depth FROM cx WHERE root=1 AND depth<=5
GROUP BY depth ORDER BY 1;
} {1 0 2 1 4 2 8 3 16 4 32 5}
# depth==5
do_execsql_test 1.8 {
SELECT count(*), depth FROM cx WHERE root=1 AND depth=5
GROUP BY depth ORDER BY 1;
} {32 5}
# depth BETWEEN 3 AND 5
do_execsql_test 1.9 {
SELECT count(*), depth FROM cx WHERE root=1 AND depth BETWEEN 3 AND 5
GROUP BY depth ORDER BY 1;
} {8 3 16 4 32 5}
# depth==5 with min() and max()
do_execsql_test 1.10 {
SELECT count(*), min(id), max(id) FROM cx WHERE root=1 AND depth=5;
} {32 32 63}
# Create a much smaller table t2 with only 32 elements
db eval {
CREATE TABLE t2(x INTEGER PRIMARY KEY, y INTEGER);
INSERT INTO t2 SELECT x, y FROM t1 WHERE x<32;
CREATE INDEX t2y ON t2(y);
CREATE VIRTUAL TABLE c2
USING transitive_closure(tablename=t2, idcolumn=x, parentcolumn=y);
}
# t2 full-table
do_execsql_test 2.1 {
SELECT count(*), min(id), max(id) FROM c2 WHERE root=1;
} {31 1 31}
# t2 root=10
do_execsql_test 2.2 {
SELECT id FROM c2 WHERE root=10;
} {10 20 21}
# t2 root=11
do_execsql_test 2.3 {
SELECT id FROM c2 WHERE root=12;
} {12 24 25}
# t2 root IN [10,12]
do_execsql_test 2.4 {
SELECT id FROM c2 WHERE root IN (10,12) ORDER BY id;
} {10 12 20 21 24 25}
# t2 root IN [10,12] (sorted)
do_execsql_test 2.5 {
SELECT id FROM c2 WHERE root IN (10,12) ORDER BY +id;
} {10 12 20 21 24 25}
# t2 c2up from 20
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE c2up USING transitive_closure(
tablename = t2,
idcolumn = y,
parentcolumn = x
);
SELECT id FROM c2up WHERE root=20;
} {1 2 5 10 20}
# cx as c2up
do_execsql_test 3.1 {
SELECT id FROM cx
WHERE root=20
AND tablename='t2'
AND idcolumn='y'
AND parentcolumn='x';
} {1 2 5 10 20}
# t2 first cousins of 20
do_execsql_test 3.2 {
SELECT DISTINCT id FROM c2
WHERE root IN (SELECT id FROM c2up
WHERE root=20 AND depth<=2)
ORDER BY id;
} {5 10 11 20 21 22 23}
# t2 first cousins of 20
do_execsql_test 3.3 {
SELECT id FROM c2
WHERE root=(SELECT id FROM c2up
WHERE root=20 AND depth=2)
AND depth=2
EXCEPT
SELECT id FROM c2
WHERE root=(SELECT id FROM c2up
WHERE root=20 AND depth=1)
AND depth<=1
ORDER BY id;
} {22 23}
# missing tablename.
do_test 4.1 {
catchsql {
SELECT id FROM cx
WHERE root=20
AND tablename='t3'
AND idcolumn='y'
AND parentcolumn='x';
}
} {1 {no such table: t3}}
# missing idcolumn
do_test 4.2 {
catchsql {
SELECT id FROM cx
WHERE root=20
AND tablename='t2'
AND idcolumn='xyz'
AND parentcolumn='x';
}
} {1 {no such column: t2.xyz}}
# missing parentcolumn
do_test 4.3 {
catchsql {
SELECT id FROM cx
WHERE root=20
AND tablename='t2'
AND idcolumn='x'
AND parentcolumn='pqr';
}
} {1 {no such column: t2.pqr}}
# generic closure
do_execsql_test 5.1 {
CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
SELECT id FROM closure
WHERE root=1
AND depth=3
AND tablename='t1'
AND idcolumn='x'
AND parentcolumn='y'
ORDER BY id;
} {8 9 10 11 12 13 14 15}
finish_test

210
test/fts3expr3.test Normal file
View File

@@ -0,0 +1,210 @@
# 2009 January 1
#
# 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 regression tests for SQLite library. The
# focus of this script is testing the part of the FTS3 expression
# parser that rebalances large expressions.
#
# $Id: fts3expr2.test,v 1.2 2009/06/05 17:09:12 drh Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set ::testprefix fts3expr3
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
set sqlite_fts3_enable_parentheses 1
proc strip_phrase_data {L} {
if {[lindex $L 0] eq "PHRASE"} {
return [list P [lrange $L 3 end]]
}
return [list \
[lindex $L 0] \
[strip_phrase_data [lindex $L 1]] \
[strip_phrase_data [lindex $L 2]] \
]
}
proc test_fts3expr2 {expr} {
strip_phrase_data [
db one {SELECT fts3_exprtest_rebalance('simple', $expr, 'a', 'b', 'c')}
]
}
proc balanced_exprtree_structure {nEntry} {
set L [list]
for {set i 1} {$i <= $nEntry} {incr i} {
lappend L xxx
}
while {[llength $L] > 1} {
set N [list]
if {[llength $L] % 2} {
foreach {a b} [lrange $L 0 end-1] { lappend N [list AND $a $b] }
lappend N [lindex $L end]
} else {
foreach {a b} $L { lappend N [list AND $a $b] }
}
set L $N
}
return [lindex $L 0]
}
proc balanced_and_tree {nEntry} {
set query [balanced_exprtree_structure $nEntry]
if {$query == "xxx"} {
return "P 1"
}
for {set i 1} {$i <= $nEntry} {incr i} {
regsub xxx $query "{P $i}" query
}
return $query
}
proc random_tree_structure {nEntry bParen op} {
set query xxx
for {set i 1} {$i < $nEntry} {incr i} {
set x1 [expr int(rand()*4.0)]
set x2 [expr int(rand()*2.0)]
if {$x1==0 && $bParen} {
set query "($query)"
}
if {$x2} {
set query "xxx $op $query"
} else {
set query "$query $op xxx"
}
}
return $query
}
proc random_and_query {nEntry {bParen 0}} {
set query [random_tree_structure $nEntry $bParen AND]
for {set i 1} {$i <= $nEntry} {incr i} {
regsub xxx $query $i query
}
return $query
}
proc random_or_query {nEntry} {
set query [random_tree_structure $nEntry 1 OR]
for {set i 1} {$i <= $nEntry} {incr i} {
regsub xxx $query $i query
}
return $query
}
proc random_andor_query {nEntry} {
set query [random_tree_structure $nEntry 1 AND]
for {set i 1} {$i <= $nEntry} {incr i} {
regsub xxx $query "([random_or_query $nEntry])" query
}
return $query
}
proc balanced_andor_tree {nEntry} {
set tree [balanced_exprtree_structure $nEntry]
set node "{[balanced_and_tree $nEntry]}"
regsub -all AND $node OR node
regsub -all xxx $tree $node tree
return $tree
}
# Test that queries like "1 AND 2 AND 3 AND 4..." are transformed to
# balanced trees by FTS.
#
for {set i 1} {$i < 100} {incr i} {
do_test 1.$i {
test_fts3expr2 [random_and_query $i]
} [balanced_and_tree $i]
}
# Same again, except with parenthesis inserted at arbitrary points.
#
for {set i 1} {$i < 100} {incr i} {
do_test 2.$i {
test_fts3expr2 [random_and_query $i 1]
} [balanced_and_tree $i]
}
# Now attempt to balance two AND trees joined by an OR.
#
for {set i 1} {$i < 100} {incr i} {
do_test 3.$i {
test_fts3expr2 "[random_and_query $i 1] OR [random_and_query $i 1]"
} [list OR [balanced_and_tree $i] [balanced_and_tree $i]]
}
# Try trees of AND nodes with leaves that are themselves trees of OR nodes.
#
for {set i 2} {$i < 64} {incr i 4} {
do_test 3.$i {
test_fts3expr2 [random_andor_query $i]
} [balanced_andor_tree $i]
}
# These exceed the depth limit.
#
for {set i 65} {$i < 70} {incr i} {
do_test 3.$i {
list [catch {test_fts3expr2 [random_andor_query $i]} msg] $msg
} {1 {Error parsing expression}}
}
# This also exceeds the depth limit.
#
do_test 4.1.1 {
set q "1"
for {set i 2} {$i < 5000} {incr i} {
append q " AND $i"
}
list [catch {test_fts3expr2 $q} msg] $msg
} {1 {Error parsing expression}}
do_test 4.1.2 {
set q "1"
for {set i 2} {$i < 4000} {incr i} {
append q " AND $i"
}
catch {test_fts3expr2 $q}
} {0}
proc create_toggle_tree {nDepth} {
if {$nDepth == 0} { return xxx }
set nNew [expr $nDepth-1]
if {$nDepth % 2} {
return "([create_toggle_tree $nNew]) OR ([create_toggle_tree $nNew])"
}
return "([create_toggle_tree $nNew]) AND ([create_toggle_tree $nNew])"
}
do_test 4.2 {
list [catch {test_fts3expr2 [create_toggle_tree 17]} msg] $msg
} {1 {Error parsing expression}}
set query [random_andor_query 12]
set result [balanced_andor_tree 12]
do_faultsim_test fts3expr3-fault-1 -faults oom-* -body {
test_fts3expr2 $::query
} -test {
faultsim_test_result [list 0 $::result]
}
set sqlite_fts3_enable_parentheses 0
finish_test

View File

@@ -24,12 +24,7 @@ ifcapable !vtab {
set ::testprefix fuzzer1
# Test of test code. Only here to make the coverage metric better.
do_test 0.1 {
list [catch { register_fuzzer_module a b c } msg] $msg
} {1 {wrong # args: should be "register_fuzzer_module DB"}}
register_fuzzer_module db
load_static_extension db fuzzer
# Check configuration errors.
#

View File

@@ -17,7 +17,7 @@ source $testdir/tester.tcl
ifcapable !vtab { finish_test ; return }
set ::testprefix fuzzerfault
register_fuzzer_module db
load_static_extension db fuzzer
do_test 1-pre1 {
execsql {
@@ -30,7 +30,7 @@ do_test 1-pre1 {
} {}
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
register_fuzzer_module db
load_static_extension db fuzzer
} -body {
execsql {
CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules);
@@ -43,7 +43,7 @@ do_faultsim_test 1 -prep {
do_test 2-pre1 {
faultsim_delete_and_reopen
register_fuzzer_module db
load_static_extension db fuzzer
execsql {
CREATE TABLE x2_rules(ruleset, cFrom, cTo, cost);
INSERT INTO x2_rules VALUES(0, 'a', 'x', 1);
@@ -56,7 +56,7 @@ do_test 2-pre1 {
do_faultsim_test 2 -prep {
faultsim_restore_and_reopen
register_fuzzer_module db
load_static_extension db fuzzer
} -body {
execsql {
SELECT count(*) FROM x2 WHERE word MATCH 'abc';
@@ -78,7 +78,7 @@ do_test 3-pre1 {
do_faultsim_test 3 -prep {
faultsim_restore_and_reopen
register_fuzzer_module db
load_static_extension db fuzzer
} -body {
execsql {
CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules);

View File

@@ -16,6 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix io
db close
sqlite3_simulate_device
@@ -39,6 +40,10 @@ sqlite3 db test.db -vfs devsym
# io-5.* - Test that the default page size is selected and used
# correctly.
#
# io-6.* - Test that the pager-cache is not being flushed unnecessarily
# after a transaction that uses the special atomic-write path
# is committed.
#
set ::nWrite 0
proc nWrite {db} {
@@ -565,5 +570,68 @@ foreach {char sectorsize pgsize} {
} $pgsize
}
#----------------------------------------------------------------------
#
do_test io-6.1 {
db close
sqlite3_simulate_device -char atomic
forcedelete test.db
sqlite3 db test.db -vfs devsym
execsql {
PRAGMA mmap_size = 0;
PRAGMA page_size = 1024;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
CREATE TABLE t3(x);
CREATE INDEX i3 ON t3(x);
INSERT INTO t3 VALUES(randomblob(100));
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
INSERT INTO t3 SELECT randomblob(100) FROM t3;
}
db_save_and_close
} {}
foreach {tn sql} {
1 { BEGIN;
INSERT INTO t1 VALUES('123');
INSERT INTO t2 VALUES('456');
COMMIT;
}
2 { BEGIN;
INSERT INTO t1 VALUES('123');
COMMIT;
}
} {
db_restore
sqlite3 db test.db -vfs devsym
execsql {
PRAGMA mmap_size = 0;
SELECT x FROM t3 ORDER BY rowid;
SELECT x FROM t3 ORDER BY x;
}
do_execsql_test 6.2.$tn.1 { PRAGMA integrity_check } {ok}
do_execsql_test 6.2.$tn.2 $sql
# Corrupt the database file on disk. This should not matter for the
# purposes of the following "PRAGMA integrity_check", as the entire
# database should be cached in the pager-cache. If corruption is
# reported, it indicates that executing $sql caused the pager cache
# to be flushed. Which is a bug.
hexio_write test.db [expr 1024 * 5] [string repeat 00 2048]
do_execsql_test 6.2.$tn.3 { PRAGMA integrity_check } {ok}
db close
}
sqlite3_simulate_device -char {} -sectorsize 0
finish_test

View File

@@ -365,7 +365,7 @@ do_test memdb-6.15 {
ifcapable subquery&&vtab {
do_test memdb-7.1 {
register_wholenumber_module db
load_static_extension db wholenumber
execsql {
CREATE TABLE t6(x);
CREATE VIRTUAL TABLE nums USING wholenumber;

View File

@@ -40,14 +40,20 @@ proc register_rblob_code {dbname seed} {
}]
}
# For cases 1.1 and 1.4, the number of pages read using xRead() is 4 on
# unix and 9 on windows. The difference is that windows only ever maps
# an integer number of OS pages (i.e. creates mappings that are a multiple
# of 4KB in size). Whereas on unix any sized mapping may be created.
#
foreach {t mmap_size nRead c2init} {
1.1 { PRAGMA mmap_size = 67108864 } 4 {PRAGMA mmap_size = 0}
1.1 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 0}
1.2 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 0}
1.3 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 0}
1.4 { PRAGMA mmap_size = 67108864 } 4 {PRAGMA mmap_size = 67108864 }
1.4 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 67108864 }
1.5 { PRAGMA mmap_size = 53248 } 150 {PRAGMA mmap_size = 67108864 }
1.6 { PRAGMA mmap_size = 0 } 344 {PRAGMA mmap_size = 67108864 }
} {
do_multiclient_test tn {
sql1 {PRAGMA page_size=1024}
sql1 $mmap_size

View File

@@ -189,6 +189,7 @@ test_suite "fts3" -prefix "" -description {
fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test
fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test
fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test
fts3expr3.test
fts3near.test fts3query.test fts3shared.test fts3snippet.test
fts3sort.test
fts3fault.test fts3malloc.test fts3matchinfo.test

View File

@@ -936,6 +936,16 @@ proc check_temp_store {} {
return "unknown"
}
# Application_ID
#
do_test pragma-8.3.1 {
execsql {
PRAGMA application_id;
}
} {0}
do_test pragma-8.3.2 {
execsql {PRAGMA Application_ID(12345); PRAGMA application_id;}
} {12345}
# Test temp_store and temp_store_directory pragmas
#

View File

@@ -16,7 +16,7 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test regexp1-1.1 {
sqlite3_add_regexp_func db
load_static_extension db regexp
db eval {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
INSERT INTO t1 VALUES(1, 'For since by man came death,');

View File

@@ -152,4 +152,23 @@ for {set i 1} {$i<=2} {incr i} {
} {111 x1 111 x2 222 x3 {}}
}
# The following test was added on 2013-04-24 in order to verify that
# the datatypes and affinities of sub-sub-queries are set prior to computing
# the datatypes and affinities of the parent sub-queries because the
# latter computation depends on the former.
#
do_execsql_test selectD-4.1 {
CREATE TABLE t41(a INTEGER PRIMARY KEY, b INTEGER);
CREATE TABLE t42(d INTEGER PRIMARY KEY, e INTEGER);
CREATE TABLE t43(f INTEGER PRIMARY KEY, g INTEGER);
EXPLAIN QUERY PLAN
SELECT *
FROM t41
LEFT JOIN (SELECT count(*) AS cnt, x1.d
FROM (t42 INNER JOIN t43 ON d=g) AS x1
WHERE x1.d>5
GROUP BY x1.d) AS x2
ON t41.b=x2.d;
} {/.*SEARCH SUBQUERY 1 AS x2 USING AUTOMATIC.*/}
finish_test

View File

@@ -16,7 +16,7 @@ set testprefix spellfix
ifcapable !vtab { finish_test ; return }
register_spellfix_module db
load_static_extension db spellfix nextchar
set vocab {
rabbi rabbit rabbits rabble rabid rabies raccoon raccoons race raced racer
@@ -84,6 +84,26 @@ foreach {tn word res} {
} $res
}
# Tests of the next_char function.
#
do_test 1.10 {
db eval {
CREATE TABLE vocab(w TEXT PRIMARY KEY);
INSERT INTO vocab SELECT word FROM t1;
}
} {}
do_execsql_test 1.11 {
SELECT next_char('re','vocab','w');
} {a}
do_execsql_test 1.12 {
SELECT next_char('r','vocab','w');
} {ae}
do_execsql_test 1.13 {
SELECT next_char('','vocab','w');
} {r}
do_test 1.14 {
catchsql {SELECT next_char('','xyzzy','a')}
} {1 {no such table: xyzzy}}
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE t2 USING spellfix1;

View File

@@ -46,7 +46,7 @@ for {set ii 1} {$ii<=10} {incr ii} {
db close
forcedelete test.db test.db-wal
sqlite3 db test.db
register_wholenumber_module db
load_static_extension db wholenumber
db eval {
PRAGMA journal_mode=WAL;
CREATE TABLE t1(a,b);

View File

@@ -296,13 +296,27 @@ do_test where8-3.21 {
SELECT a, d FROM t1, (t2) WHERE (a=d OR b=e) AND a<5 ORDER BY a
}
} {1 1 2 2 3 3 4 2 4 4 0 0}
do_test where8-3.21.1 {
execsql_status {
SELECT a, d FROM t1, ((t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a
}
} {1 1 2 2 3 3 4 2 4 4 0 0}
do_test where8-3.21.2 {
execsql_status {
SELECT a, d FROM t1, ((SELECT * FROM t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a
}
} {1 1 2 2 3 3 4 2 4 4 0 0}
do_test where8-3.22 {
execsql_status {
SELECT a, d FROM ((((((t1))), (((t2))))))
WHERE (a=d OR b=e) AND a<5 ORDER BY a
}
} {1 1 2 2 3 3 4 2 4 4 0 0}
do_test where8-3.23 {
execsql_status {
SELECT * FROM ((SELECT * FROM t2)) AS t3;
}
} {1 {} I 2 four IV 3 {} IX 4 sixteen XVI 5 {} XXV 6 thirtysix XXXVI 7 fortynine XLIX 8 sixtyeight LXIV 9 eightyone LXXXIX 10 {} C 9 0}
#-----------------------------------------------------------------------
# The following tests - where8-4.* - verify that adding or removing

View File

@@ -59,7 +59,7 @@ do_test zerodamage-2.0 {
}
tv filter xDelete
tv script xDeleteCallback
register_wholenumber_module db
load_static_extension db wholenumber
db eval {
PRAGMA page_size=1024;
PRAGMA journal_mode=DELETE;

View File

@@ -15,12 +15,8 @@ gcc -o sqlite3 -g -Os -I. \
-DSQLITE_ENABLE_STAT3 \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_REGEXP \
-DSQLITE_ENABLE_SPELLFIX -DSQLITE_CORE=1 \
-DHAVE_READLINE \
-DHAVE_USLEEP=1 \
../sqlite/src/shell.c \
../sqlite/src/test_regexp.c \
../sqlite/src/test_spellfix.c \
../sqlite/src/test_vfstrace.c \
sqlite3.c -ldl -lreadline -lncurses

View File

@@ -176,7 +176,7 @@ static void print_db_header(void){
print_decode_line(aData, 56, 4, "Text encoding");
print_decode_line(aData, 60, 4, "User version");
print_decode_line(aData, 64, 4, "Incremental-vacuum mode");
print_decode_line(aData, 68, 4, "meta[7]");
print_decode_line(aData, 68, 4, "Application ID");
print_decode_line(aData, 72, 4, "meta[8]");
print_decode_line(aData, 76, 4, "meta[9]");
print_decode_line(aData, 80, 4, "meta[10]");