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:
15
Makefile.in
15
Makefile.in
@@ -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 = \
|
||||
|
20
Makefile.msc
20
Makefile.msc
@@ -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
|
||||
|
@@ -2975,14 +2975,12 @@ static int fts3FilterMethod(
|
||||
pCsr->iLangid = 0;
|
||||
if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
|
||||
|
||||
assert( p->base.zErrMsg==0 );
|
||||
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
|
||||
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
|
||||
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr,
|
||||
&p->base.zErrMsg
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_ERROR ){
|
||||
static const char *zErr = "malformed MATCH expression: [%s]";
|
||||
p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@@ -524,7 +524,7 @@ void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
|
||||
|
||||
/* fts3_expr.c */
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
|
||||
char **, int, int, int, const char *, int, Fts3Expr **
|
||||
char **, int, int, int, const char *, int, Fts3Expr **, char **
|
||||
);
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
#ifdef SQLITE_TEST
|
||||
|
@@ -640,8 +640,10 @@ static int fts3ExprParse(
|
||||
}
|
||||
pNot->eType = FTSQUERY_NOT;
|
||||
pNot->pRight = p;
|
||||
p->pParent = pNot;
|
||||
if( pNotBranch ){
|
||||
pNot->pLeft = pNotBranch;
|
||||
pNotBranch->pParent = pNot;
|
||||
}
|
||||
pNotBranch = pNot;
|
||||
p = pPrev;
|
||||
@@ -729,6 +731,7 @@ static int fts3ExprParse(
|
||||
pIter = pIter->pLeft;
|
||||
}
|
||||
pIter->pLeft = pRet;
|
||||
pRet->pParent = pIter;
|
||||
pRet = pNotBranch;
|
||||
}
|
||||
}
|
||||
@@ -745,6 +748,222 @@ exprparse_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return SQLITE_ERROR if the maximum depth of the expression tree passed
|
||||
** as the only argument is more than nMaxDepth.
|
||||
*/
|
||||
static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){
|
||||
int rc = SQLITE_OK;
|
||||
if( p ){
|
||||
if( nMaxDepth<0 ){
|
||||
rc = SQLITE_TOOBIG;
|
||||
}else{
|
||||
rc = fts3ExprCheckDepth(p->pLeft, nMaxDepth-1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprCheckDepth(p->pRight, nMaxDepth-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function attempts to transform the expression tree at (*pp) to
|
||||
** an equivalent but more balanced form. The tree is modified in place.
|
||||
** If successful, SQLITE_OK is returned and (*pp) set to point to the
|
||||
** new root expression node.
|
||||
**
|
||||
** nMaxDepth is the maximum allowable depth of the balanced sub-tree.
|
||||
**
|
||||
** Otherwise, if an error occurs, an SQLite error code is returned and
|
||||
** expression (*pp) freed.
|
||||
*/
|
||||
static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Fts3Expr *pRoot = *pp; /* Initial root node */
|
||||
Fts3Expr *pFree = 0; /* List of free nodes. Linked by pParent. */
|
||||
int eType = pRoot->eType; /* Type of node in this tree */
|
||||
|
||||
if( nMaxDepth==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
|
||||
Fts3Expr **apLeaf;
|
||||
apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
|
||||
if( 0==apLeaf ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
Fts3Expr *p;
|
||||
|
||||
/* Set $p to point to the left-most leaf in the tree of eType nodes. */
|
||||
for(p=pRoot; p->eType==eType; p=p->pLeft){
|
||||
assert( p->pParent==0 || p->pParent->pLeft==p );
|
||||
assert( p->pLeft && p->pRight );
|
||||
}
|
||||
|
||||
/* This loop runs once for each leaf in the tree of eType nodes. */
|
||||
while( 1 ){
|
||||
int iLvl;
|
||||
Fts3Expr *pParent = p->pParent; /* Current parent of p */
|
||||
|
||||
assert( pParent==0 || pParent->pLeft==p );
|
||||
p->pParent = 0;
|
||||
if( pParent ){
|
||||
pParent->pLeft = 0;
|
||||
}else{
|
||||
pRoot = 0;
|
||||
}
|
||||
rc = fts3ExprBalance(&p, nMaxDepth-1);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
for(iLvl=0; p && iLvl<nMaxDepth; iLvl++){
|
||||
if( apLeaf[iLvl]==0 ){
|
||||
apLeaf[iLvl] = p;
|
||||
p = 0;
|
||||
}else{
|
||||
assert( pFree );
|
||||
pFree->pLeft = apLeaf[iLvl];
|
||||
pFree->pRight = p;
|
||||
pFree->pLeft->pParent = pFree;
|
||||
pFree->pRight->pParent = pFree;
|
||||
|
||||
p = pFree;
|
||||
pFree = pFree->pParent;
|
||||
p->pParent = 0;
|
||||
apLeaf[iLvl] = 0;
|
||||
}
|
||||
}
|
||||
if( p ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_TOOBIG;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If that was the last leaf node, break out of the loop */
|
||||
if( pParent==0 ) break;
|
||||
|
||||
/* Set $p to point to the next leaf in the tree of eType nodes */
|
||||
for(p=pParent->pRight; p->eType==eType; p=p->pLeft);
|
||||
|
||||
/* Remove pParent from the original tree. */
|
||||
assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent );
|
||||
pParent->pRight->pParent = pParent->pParent;
|
||||
if( pParent->pParent ){
|
||||
pParent->pParent->pLeft = pParent->pRight;
|
||||
}else{
|
||||
assert( pParent==pRoot );
|
||||
pRoot = pParent->pRight;
|
||||
}
|
||||
|
||||
/* Link pParent into the free node list. It will be used as an
|
||||
** internal node of the new tree. */
|
||||
pParent->pParent = pFree;
|
||||
pFree = pParent;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
p = 0;
|
||||
for(i=0; i<nMaxDepth; i++){
|
||||
if( apLeaf[i] ){
|
||||
if( p==0 ){
|
||||
p = apLeaf[i];
|
||||
p->pParent = 0;
|
||||
}else{
|
||||
pFree->pRight = p;
|
||||
pFree->pLeft = apLeaf[i];
|
||||
pFree->pLeft->pParent = pFree;
|
||||
pFree->pRight->pParent = pFree;
|
||||
|
||||
p = pFree;
|
||||
pFree = pFree->pParent;
|
||||
p->pParent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
pRoot = p;
|
||||
}else{
|
||||
/* An error occurred. Delete the contents of the apLeaf[] array
|
||||
** and pFree list. Everything else is cleaned up by the call to
|
||||
** sqlite3Fts3ExprFree(pRoot) below. */
|
||||
Fts3Expr *pDel;
|
||||
for(i=0; i<nMaxDepth; i++){
|
||||
sqlite3Fts3ExprFree(apLeaf[i]);
|
||||
}
|
||||
while( (pDel=pFree)!=0 ){
|
||||
pFree = pDel->pParent;
|
||||
sqlite3_free(pDel);
|
||||
}
|
||||
}
|
||||
|
||||
assert( pFree==0 );
|
||||
sqlite3_free( apLeaf );
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Fts3ExprFree(pRoot);
|
||||
pRoot = 0;
|
||||
}
|
||||
*pp = pRoot;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3Fts3ExprParse(), with the following
|
||||
** differences:
|
||||
**
|
||||
** 1. It does not do expression rebalancing.
|
||||
** 2. It does not check that the expression does not exceed the
|
||||
** maximum allowable depth.
|
||||
** 3. Even if it fails, *ppExpr may still be set to point to an
|
||||
** expression tree. It should be deleted using sqlite3Fts3ExprFree()
|
||||
** in this case.
|
||||
*/
|
||||
static int fts3ExprParseUnbalanced(
|
||||
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
|
||||
int iLangid, /* Language id for tokenizer */
|
||||
char **azCol, /* Array of column names for fts3 table */
|
||||
int bFts4, /* True to allow FTS4-only syntax */
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr /* OUT: Parsed query structure */
|
||||
){
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
|
||||
memset(&sParse, 0, sizeof(ParseContext));
|
||||
sParse.pTokenizer = pTokenizer;
|
||||
sParse.iLangid = iLangid;
|
||||
sParse.azCol = (const char **)azCol;
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.bFts4 = bFts4;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( n<0 ){
|
||||
n = (int)strlen(z);
|
||||
}
|
||||
rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
|
||||
assert( rc==SQLITE_OK || *ppExpr==0 );
|
||||
|
||||
/* Check for mismatched parenthesis */
|
||||
if( rc==SQLITE_OK && sParse.nNest ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameters z and n contain a pointer to and length of a buffer containing
|
||||
** an fts3 query expression, respectively. This function attempts to parse the
|
||||
@@ -777,49 +996,74 @@ int sqlite3Fts3ExprParse(
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
Fts3Expr **ppExpr /* OUT: Parsed query structure */
|
||||
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
|
||||
char **pzErr /* OUT: Error message (sqlite3_malloc) */
|
||||
){
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
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
1477
ext/misc/amatch.c
Normal file
File diff suppressed because it is too large
Load Diff
942
ext/misc/closure.c
Normal file
942
ext/misc/closure.c
Normal file
@@ -0,0 +1,942 @@
|
||||
/*
|
||||
** 2013-04-16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code for a virtual table that finds the transitive
|
||||
** closure of a parent/child relationship in a real table. The virtual
|
||||
** table is called "transitive_closure".
|
||||
**
|
||||
** A transitive_closure virtual table is created like this:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE x USING transitive_closure(
|
||||
** tablename=<tablename>, -- T
|
||||
** idcolumn=<columnname>, -- X
|
||||
** parentcolumn=<columnname> -- P
|
||||
** );
|
||||
**
|
||||
** When it is created, the new transitive_closure table may be supplied
|
||||
** with default values for the name of a table T and columns T.X and T.P.
|
||||
** The T.X and T.P columns must contain integers. The ideal case is for
|
||||
** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
|
||||
** the T.X column. The row referenced by T.P is the parent of the current row.
|
||||
**
|
||||
** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
|
||||
** TABLE statement may be overridden in individual queries by including
|
||||
** terms like tablename='newtable', idcolumn='id2', or
|
||||
** parentcolumn='parent3' in the WHERE clause of the query.
|
||||
**
|
||||
** For efficiency, it is essential that there be an index on the P column:
|
||||
**
|
||||
** CREATE Tidx1 ON T(P)
|
||||
**
|
||||
** Suppose a specific instance of the closure table is as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
|
||||
** tablename='group',
|
||||
** idcolumn='groupId',
|
||||
** parentcolumn='parentId'
|
||||
** );
|
||||
**
|
||||
** Such an instance of the transitive_closure virtual table would be
|
||||
** appropriate for walking a tree defined using a table like this, for example:
|
||||
**
|
||||
** CREATE TABLE group(
|
||||
** groupId INTEGER PRIMARY KEY,
|
||||
** parentId INTEGER REFERENCES group
|
||||
** );
|
||||
** CREATE INDEX group_idx1 ON group(parentId);
|
||||
**
|
||||
** The group table above would presumably have other application-specific
|
||||
** fields. The key point here is that rows of the group table form a
|
||||
** tree. The purpose of the ct1 virtual table is to easily extract
|
||||
** branches of that tree.
|
||||
**
|
||||
** Once it has been created, the ct1 virtual table can be queried
|
||||
** as follows:
|
||||
**
|
||||
** SELECT * FROM element
|
||||
** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
|
||||
**
|
||||
** The above query will return all elements that are part of group ?1
|
||||
** or children of group ?1 or grand-children of ?1 and so forth for all
|
||||
** descendents of group ?1. The same query can be formulated as a join:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1;
|
||||
**
|
||||
** The depth of the transitive_closure (the number of generations of
|
||||
** parent/child relations to follow) can be limited by setting "depth"
|
||||
** column in the WHERE clause. So, for example, the following query
|
||||
** finds only children and grandchildren but no further descendents:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1
|
||||
** AND ct1.depth<=2;
|
||||
**
|
||||
** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
|
||||
** order to find only the grandchildren of ?1, not ?1 itself or the
|
||||
** children of ?1.
|
||||
**
|
||||
** The root=?1 term must be supplied in WHERE clause or else the query
|
||||
** of the ct1 virtual table will return an empty set. The tablename,
|
||||
** idcolumn, and parentcolumn attributes can be overridden in the WHERE
|
||||
** clause if desired. So, for example, the ct1 table could be repurposed
|
||||
** to find ancestors rather than descendents by inverting the roles of
|
||||
** the idcolumn and parentcolumn:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1
|
||||
** AND ct1.idcolumn='parentId'
|
||||
** AND ct1.parentcolumn='groupId';
|
||||
**
|
||||
** Multiple calls to ct1 could be combined. For example, the following
|
||||
** query finds all elements that "cousins" of groupId ?1. That is to say
|
||||
** elements where the groupId is a grandchild of the grandparent of ?1.
|
||||
** (This definition of "cousins" also includes siblings and self.)
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupId=ct1.id
|
||||
** AND ct1.depth=2
|
||||
** AND ct1.root IN (SELECT id FROM ct1
|
||||
** WHERE root=?1
|
||||
** AND depth=2
|
||||
** AND idcolumn='parentId'
|
||||
** AND parentcolumn='groupId');
|
||||
**
|
||||
** In our example, the group.groupId column is unique and thus the
|
||||
** subquery will return exactly one row. For that reason, the IN
|
||||
** operator could be replaced by "=" to get the same result. But
|
||||
** in the general case where the idcolumn is not unique, an IN operator
|
||||
** would be required for this kind of query.
|
||||
**
|
||||
** Note that because the tablename, idcolumn, and parentcolumn can
|
||||
** all be specified in the query, it is possible for an application
|
||||
** to define a single transitive_closure virtual table for use on lots
|
||||
** of different hierarchy tables. One might say:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
|
||||
**
|
||||
** As each database connection is being opened. Then the application
|
||||
** would always have a "closure" virtual table handy to use for querying.
|
||||
**
|
||||
** SELECT element.* FROM element, closure
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND closure.root=?1
|
||||
** AND closure.tablename='group'
|
||||
** AND closure.idname='groupId'
|
||||
** AND closure.parentname='parentId';
|
||||
**
|
||||
** See the documentation at http://www.sqlite.org/loadext.html for information
|
||||
** on how to compile and use loadable extensions such as this one.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this implementation
|
||||
*/
|
||||
typedef struct closure_vtab closure_vtab;
|
||||
typedef struct closure_cursor closure_cursor;
|
||||
typedef struct closure_queue closure_queue;
|
||||
typedef struct closure_avl closure_avl;
|
||||
|
||||
/*****************************************************************************
|
||||
** AVL Tree implementation
|
||||
*/
|
||||
/*
|
||||
** Objects that want to be members of the AVL tree should embedded an
|
||||
** instance of this structure.
|
||||
*/
|
||||
struct closure_avl {
|
||||
sqlite3_int64 id; /* Id of this entry in the table */
|
||||
int iGeneration; /* Which generation is this entry part of */
|
||||
closure_avl *pList; /* A linked list of nodes */
|
||||
closure_avl *pBefore; /* Other elements less than id */
|
||||
closure_avl *pAfter; /* Other elements greater than id */
|
||||
closure_avl *pUp; /* Parent element */
|
||||
short int height; /* Height of this node. Leaf==1 */
|
||||
short int imbalance; /* Height difference between pBefore and pAfter */
|
||||
};
|
||||
|
||||
/* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
|
||||
** Assume that the children of p have correct heights.
|
||||
*/
|
||||
static void closureAvlRecomputeHeight(closure_avl *p){
|
||||
short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
||||
short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
||||
p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
||||
p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
||||
}
|
||||
|
||||
/*
|
||||
** P B
|
||||
** / \ / \
|
||||
** B Z ==> X P
|
||||
** / \ / \
|
||||
** X Y Y Z
|
||||
**
|
||||
*/
|
||||
static closure_avl *closureAvlRotateBefore(closure_avl *pP){
|
||||
closure_avl *pB = pP->pBefore;
|
||||
closure_avl *pY = pB->pAfter;
|
||||
pB->pUp = pP->pUp;
|
||||
pB->pAfter = pP;
|
||||
pP->pUp = pB;
|
||||
pP->pBefore = pY;
|
||||
if( pY ) pY->pUp = pP;
|
||||
closureAvlRecomputeHeight(pP);
|
||||
closureAvlRecomputeHeight(pB);
|
||||
return pB;
|
||||
}
|
||||
|
||||
/*
|
||||
** P A
|
||||
** / \ / \
|
||||
** X A ==> P Z
|
||||
** / \ / \
|
||||
** Y Z X Y
|
||||
**
|
||||
*/
|
||||
static closure_avl *closureAvlRotateAfter(closure_avl *pP){
|
||||
closure_avl *pA = pP->pAfter;
|
||||
closure_avl *pY = pA->pBefore;
|
||||
pA->pUp = pP->pUp;
|
||||
pA->pBefore = pP;
|
||||
pP->pUp = pA;
|
||||
pP->pAfter = pY;
|
||||
if( pY ) pY->pUp = pP;
|
||||
closureAvlRecomputeHeight(pP);
|
||||
closureAvlRecomputeHeight(pA);
|
||||
return pA;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the pBefore or pAfter pointer in the parent
|
||||
** of p that points to p. Or if p is the root node, return pp.
|
||||
*/
|
||||
static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
|
||||
closure_avl *pUp = p->pUp;
|
||||
if( pUp==0 ) return pp;
|
||||
if( pUp->pAfter==p ) return &pUp->pAfter;
|
||||
return &pUp->pBefore;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rebalance all nodes starting with p and working up to the root.
|
||||
** Return the new root.
|
||||
*/
|
||||
static closure_avl *closureAvlBalance(closure_avl *p){
|
||||
closure_avl *pTop = p;
|
||||
closure_avl **pp;
|
||||
while( p ){
|
||||
closureAvlRecomputeHeight(p);
|
||||
if( p->imbalance>=2 ){
|
||||
closure_avl *pB = p->pBefore;
|
||||
if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
|
||||
pp = closureAvlFromPtr(p,&p);
|
||||
p = *pp = closureAvlRotateBefore(p);
|
||||
}else if( p->imbalance<=(-2) ){
|
||||
closure_avl *pA = p->pAfter;
|
||||
if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
|
||||
pp = closureAvlFromPtr(p,&p);
|
||||
p = *pp = closureAvlRotateAfter(p);
|
||||
}
|
||||
pTop = p;
|
||||
p = p->pUp;
|
||||
}
|
||||
return pTop;
|
||||
}
|
||||
|
||||
/* Search the tree rooted at p for an entry with id. Return a pointer
|
||||
** to the entry or return NULL.
|
||||
*/
|
||||
static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
|
||||
while( p && id!=p->id ){
|
||||
p = (id<p->id) ? p->pBefore : p->pAfter;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Find the first node (the one with the smallest key).
|
||||
*/
|
||||
static closure_avl *closureAvlFirst(closure_avl *p){
|
||||
if( p ) while( p->pBefore ) p = p->pBefore;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Return the node with the next larger key after p.
|
||||
*/
|
||||
closure_avl *closureAvlNext(closure_avl *p){
|
||||
closure_avl *pPrev = 0;
|
||||
while( p && p->pAfter==pPrev ){
|
||||
pPrev = p;
|
||||
p = p->pUp;
|
||||
}
|
||||
if( p && pPrev==0 ){
|
||||
p = closureAvlFirst(p->pAfter);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Insert a new node pNew. Return NULL on success. If the key is not
|
||||
** unique, then do not perform the insert but instead leave pNew unchanged
|
||||
** and return a pointer to an existing node with the same key.
|
||||
*/
|
||||
static closure_avl *closureAvlInsert(
|
||||
closure_avl **ppHead, /* Head of the tree */
|
||||
closure_avl *pNew /* New node to be inserted */
|
||||
){
|
||||
closure_avl *p = *ppHead;
|
||||
if( p==0 ){
|
||||
p = pNew;
|
||||
pNew->pUp = 0;
|
||||
}else{
|
||||
while( p ){
|
||||
if( pNew->id<p->id ){
|
||||
if( p->pBefore ){
|
||||
p = p->pBefore;
|
||||
}else{
|
||||
p->pBefore = pNew;
|
||||
pNew->pUp = p;
|
||||
break;
|
||||
}
|
||||
}else if( pNew->id>p->id ){
|
||||
if( p->pAfter ){
|
||||
p = p->pAfter;
|
||||
}else{
|
||||
p->pAfter = pNew;
|
||||
pNew->pUp = p;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
pNew->pBefore = 0;
|
||||
pNew->pAfter = 0;
|
||||
pNew->height = 1;
|
||||
pNew->imbalance = 0;
|
||||
*ppHead = closureAvlBalance(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk the tree can call xDestroy on each node
|
||||
*/
|
||||
static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
|
||||
if( p ){
|
||||
closureAvlDestroy(p->pBefore, xDestroy);
|
||||
closureAvlDestroy(p->pAfter, xDestroy);
|
||||
xDestroy(p);
|
||||
}
|
||||
}
|
||||
/*
|
||||
** End of the AVL Tree implementation
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
** A closure virtual-table object
|
||||
*/
|
||||
struct closure_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
char *zDb; /* Name of database. (ex: "main") */
|
||||
char *zSelf; /* Name of this virtual table */
|
||||
char *zTableName; /* Name of table holding parent/child relation */
|
||||
char *zIdColumn; /* Name of ID column of zTableName */
|
||||
char *zParentColumn; /* Name of PARENT column in zTableName */
|
||||
sqlite3 *db; /* The database connection */
|
||||
int nCursor; /* Number of pending cursors */
|
||||
};
|
||||
|
||||
/* A closure cursor object */
|
||||
struct closure_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
closure_vtab *pVtab; /* The virtual table this cursor belongs to */
|
||||
char *zTableName; /* Name of table holding parent/child relation */
|
||||
char *zIdColumn; /* Name of ID column of zTableName */
|
||||
char *zParentColumn; /* Name of PARENT column in zTableName */
|
||||
closure_avl *pCurrent; /* Current element of output */
|
||||
closure_avl *pClosure; /* The complete closure tree */
|
||||
};
|
||||
|
||||
/* A queue of AVL nodes */
|
||||
struct closure_queue {
|
||||
closure_avl *pFirst; /* Oldest node on the queue */
|
||||
closure_avl *pLast; /* Youngest node on the queue */
|
||||
};
|
||||
|
||||
/*
|
||||
** Add a node to the end of the queue
|
||||
*/
|
||||
static void queuePush(closure_queue *pQueue, closure_avl *pNode){
|
||||
pNode->pList = 0;
|
||||
if( pQueue->pLast ){
|
||||
pQueue->pLast->pList = pNode;
|
||||
}else{
|
||||
pQueue->pFirst = pNode;
|
||||
}
|
||||
pQueue->pLast = pNode;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the oldest element (the front element) from the queue.
|
||||
*/
|
||||
static closure_avl *queuePull(closure_queue *pQueue){
|
||||
closure_avl *p = pQueue->pFirst;
|
||||
if( p ){
|
||||
pQueue->pFirst = p->pList;
|
||||
if( pQueue->pFirst==0 ) pQueue->pLast = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function converts an SQL quoted string into an unquoted string
|
||||
** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
||||
** containing the result. The caller should eventually free this buffer
|
||||
** using sqlite3_free.
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** "abc" becomes abc
|
||||
** 'xyz' becomes xyz
|
||||
** [pqr] becomes pqr
|
||||
** `mno` becomes mno
|
||||
*/
|
||||
static char *closureDequote(const char *zIn){
|
||||
int nIn; /* Size of input string, in bytes */
|
||||
char *zOut; /* Output (dequoted) string */
|
||||
|
||||
nIn = (int)strlen(zIn);
|
||||
zOut = sqlite3_malloc(nIn+1);
|
||||
if( zOut ){
|
||||
char q = zIn[0]; /* Quote character (if any ) */
|
||||
|
||||
if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
||||
memcpy(zOut, zIn, nIn+1);
|
||||
}else{
|
||||
int iOut = 0; /* Index of next byte to write to output */
|
||||
int iIn; /* Index of next byte to read from input */
|
||||
|
||||
if( q=='[' ) q = ']';
|
||||
for(iIn=1; iIn<nIn; iIn++){
|
||||
if( zIn[iIn]==q ) iIn++;
|
||||
zOut[iOut++] = zIn[iIn];
|
||||
}
|
||||
}
|
||||
assert( (int)strlen(zOut)<=nIn );
|
||||
}
|
||||
return zOut;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate an closure_vtab object
|
||||
*/
|
||||
static void closureFree(closure_vtab *p){
|
||||
if( p ){
|
||||
sqlite3_free(p->zDb);
|
||||
sqlite3_free(p->zSelf);
|
||||
sqlite3_free(p->zTableName);
|
||||
sqlite3_free(p->zIdColumn);
|
||||
sqlite3_free(p->zParentColumn);
|
||||
memset(p, 0, sizeof(*p));
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xDisconnect/xDestroy method for the closure module.
|
||||
*/
|
||||
static int closureDisconnect(sqlite3_vtab *pVtab){
|
||||
closure_vtab *p = (closure_vtab*)pVtab;
|
||||
assert( p->nCursor==0 );
|
||||
closureFree(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the argument is of the form:
|
||||
**
|
||||
** KEY = VALUE
|
||||
**
|
||||
** If it is, return a pointer to the first character of VALUE.
|
||||
** If not, return NULL. Spaces around the = are ignored.
|
||||
*/
|
||||
static const char *closureValueOfKey(const char *zKey, const char *zStr){
|
||||
int nKey = (int)strlen(zKey);
|
||||
int nStr = (int)strlen(zStr);
|
||||
int i;
|
||||
if( nStr<nKey+1 ) return 0;
|
||||
if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
||||
for(i=nKey; isspace(zStr[i]); i++){}
|
||||
if( zStr[i]!='=' ) return 0;
|
||||
i++;
|
||||
while( isspace(zStr[i]) ){ i++; }
|
||||
return zStr+i;
|
||||
}
|
||||
|
||||
/*
|
||||
** xConnect/xCreate method for the closure module. Arguments are:
|
||||
**
|
||||
** argv[0] -> module name ("approximate_match")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[3...] -> arguments
|
||||
*/
|
||||
static int closureConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
closure_vtab *pNew = 0; /* New virtual table */
|
||||
const char *zDb = argv[1];
|
||||
const char *zVal;
|
||||
int i;
|
||||
|
||||
(void)pAux;
|
||||
*ppVtab = 0;
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->db = db;
|
||||
pNew->zDb = sqlite3_mprintf("%s", zDb);
|
||||
if( pNew->zDb==0 ) goto closureConnectError;
|
||||
pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
||||
if( pNew->zSelf==0 ) goto closureConnectError;
|
||||
for(i=3; i<argc; i++){
|
||||
zVal = closureValueOfKey("tablename", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zTableName);
|
||||
pNew->zTableName = closureDequote(zVal);
|
||||
if( pNew->zTableName==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
zVal = closureValueOfKey("idcolumn", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zIdColumn);
|
||||
pNew->zIdColumn = closureDequote(zVal);
|
||||
if( pNew->zIdColumn==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
zVal = closureValueOfKey("parentcolumn", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zParentColumn);
|
||||
pNew->zParentColumn = closureDequote(zVal);
|
||||
if( pNew->zParentColumn==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
*pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
||||
closureFree(pNew);
|
||||
*ppVtab = 0;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
|
||||
"idcolumn HIDDEN,parentcolumn HIDDEN)"
|
||||
);
|
||||
#define CLOSURE_COL_ID 0
|
||||
#define CLOSURE_COL_DEPTH 1
|
||||
#define CLOSURE_COL_ROOT 2
|
||||
#define CLOSURE_COL_TABLENAME 3
|
||||
#define CLOSURE_COL_IDCOLUMN 4
|
||||
#define CLOSURE_COL_PARENTCOLUMN 5
|
||||
if( rc!=SQLITE_OK ){
|
||||
closureFree(pNew);
|
||||
}
|
||||
*ppVtab = &pNew->base;
|
||||
return rc;
|
||||
|
||||
closureConnectError:
|
||||
closureFree(pNew);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new closure cursor.
|
||||
*/
|
||||
static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
closure_vtab *p = (closure_vtab*)pVTab;
|
||||
closure_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
*ppCursor = &pCur->base;
|
||||
p->nCursor++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
||||
** to indicate that it is at EOF.
|
||||
*/
|
||||
static void closureClearCursor(closure_cursor *pCur){
|
||||
closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
|
||||
sqlite3_free(pCur->zTableName);
|
||||
sqlite3_free(pCur->zIdColumn);
|
||||
sqlite3_free(pCur->zParentColumn);
|
||||
pCur->zTableName = 0;
|
||||
pCur->zIdColumn = 0;
|
||||
pCur->zParentColumn = 0;
|
||||
pCur->pCurrent = 0;
|
||||
pCur->pClosure = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a closure cursor.
|
||||
*/
|
||||
static int closureClose(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor *)cur;
|
||||
closureClearCursor(pCur);
|
||||
pCur->pVtab->nCursor--;
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance a cursor to its next row of output
|
||||
*/
|
||||
static int closureNext(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
pCur->pCurrent = closureAvlNext(pCur->pCurrent);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and insert a node
|
||||
*/
|
||||
static int closureInsertNode(
|
||||
closure_queue *pQueue, /* Add new node to this queue */
|
||||
closure_cursor *pCur, /* The cursor into which to add the node */
|
||||
sqlite3_int64 id, /* The node ID */
|
||||
int iGeneration /* The generation number for this node */
|
||||
){
|
||||
closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->id = id;
|
||||
pNew->iGeneration = iGeneration;
|
||||
closureAvlInsert(&pCur->pClosure, pNew);
|
||||
queuePush(pQueue, pNew);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to "rewind" a cursor back to the beginning so that
|
||||
** it starts its output over again. Always called at least once
|
||||
** prior to any closureColumn, closureRowid, or closureEof call.
|
||||
**
|
||||
** This routine actually computes the closure.
|
||||
**
|
||||
** See the comment at the beginning of closureBestIndex() for a
|
||||
** description of the meaning of idxNum. The idxStr parameter is
|
||||
** not used.
|
||||
*/
|
||||
static int closureFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
closure_cursor *pCur = (closure_cursor *)pVtabCursor;
|
||||
closure_vtab *pVtab = pCur->pVtab;
|
||||
sqlite3_int64 iRoot;
|
||||
int mxGen = 999999999;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt;
|
||||
closure_avl *pAvl;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zTableName = pVtab->zTableName;
|
||||
const char *zIdColumn = pVtab->zIdColumn;
|
||||
const char *zParentColumn = pVtab->zParentColumn;
|
||||
closure_queue sQueue;
|
||||
|
||||
(void)idxStr; /* Unused parameter */
|
||||
(void)argc; /* Unused parameter */
|
||||
closureClearCursor(pCur);
|
||||
memset(&sQueue, 0, sizeof(sQueue));
|
||||
if( (idxNum & 1)==0 ){
|
||||
/* No root=$root in the WHERE clause. Return an empty set */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iRoot = sqlite3_value_int64(argv[0]);
|
||||
if( (idxNum & 0x000f0)!=0 ){
|
||||
mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
|
||||
if( (idxNum & 0x00002)!=0 ) mxGen--;
|
||||
}
|
||||
if( (idxNum & 0x00f00)!=0 ){
|
||||
zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
|
||||
pCur->zTableName = sqlite3_mprintf("%s", zTableName);
|
||||
}
|
||||
if( (idxNum & 0x0f000)!=0 ){
|
||||
zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
|
||||
pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
|
||||
}
|
||||
if( (idxNum & 0x0f0000)!=0 ){
|
||||
zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
|
||||
pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
|
||||
}
|
||||
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
|
||||
zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_free(pVtab->base.zErrMsg);
|
||||
pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
|
||||
}
|
||||
while( (pAvl = queuePull(&sQueue))!=0 ){
|
||||
if( pAvl->iGeneration>=mxGen ) continue;
|
||||
sqlite3_bind_int64(pStmt, 1, pAvl->id);
|
||||
while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
|
||||
sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
|
||||
if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
|
||||
rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->pCurrent = closureAvlFirst(pCur->pClosure);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Only the word and distance columns have values. All other columns
|
||||
** return NULL
|
||||
*/
|
||||
static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
switch( i ){
|
||||
case CLOSURE_COL_ID: {
|
||||
sqlite3_result_int64(ctx, pCur->pCurrent->id);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_DEPTH: {
|
||||
sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_ROOT: {
|
||||
sqlite3_result_null(ctx);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_TABLENAME: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_IDCOLUMN: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_PARENTCOLUMN: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The rowid. For the closure table, this is the same as the "id" column.
|
||||
*/
|
||||
static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
*pRowid = pCur->pCurrent->id;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** EOF indicator
|
||||
*/
|
||||
static int closureEof(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
return pCur->pCurrent==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search for terms of these forms:
|
||||
**
|
||||
** (A) root = $root
|
||||
** (B1) depth < $depth
|
||||
** (B2) depth <= $depth
|
||||
** (B3) depth = $depth
|
||||
** (C) tablename = $tablename
|
||||
** (D) idcolumn = $idcolumn
|
||||
** (E) parentcolumn = $parentcolumn
|
||||
**
|
||||
**
|
||||
**
|
||||
** idxNum meaning
|
||||
** ---------- ------------------------------------------------------
|
||||
** 0x00000001 Term of the form (A) found
|
||||
** 0x00000002 The term of bit-2 is like (B1)
|
||||
** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
|
||||
** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
|
||||
** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
|
||||
** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
|
||||
**
|
||||
** There must be a term of type (A). If there is not, then the index type
|
||||
** is 0 and the query will return an empty set.
|
||||
*/
|
||||
static int closureBestIndex(
|
||||
sqlite3_vtab *pTab, /* The virtual table */
|
||||
sqlite3_index_info *pIdxInfo /* Information about the query */
|
||||
){
|
||||
int iPlan = 0;
|
||||
int i;
|
||||
int idx = 1;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
closure_vtab *pVtab = (closure_vtab*)pTab;
|
||||
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (iPlan & 1)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_ROOT
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
if( (iPlan & 0x0000f0)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_DEPTH
|
||||
&& (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||||
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
|
||||
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
|
||||
){
|
||||
iPlan |= idx<<4;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
|
||||
}
|
||||
if( (iPlan & 0x000f00)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_TABLENAME
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<8;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
if( (iPlan & 0x00f000)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<12;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
if( (iPlan & 0x0f0000)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<16;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
}
|
||||
if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
|
||||
|| (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
|
||||
|| (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
|
||||
){
|
||||
/* All of tablename, idcolumn, and parentcolumn must be specified
|
||||
** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
|
||||
** or else the result is an empty set. */
|
||||
iPlan = 0;
|
||||
}
|
||||
pIdxInfo->idxNum = iPlan;
|
||||
if( pIdxInfo->nOrderBy==1
|
||||
&& pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)10000;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that implements the "approximate_match".
|
||||
*/
|
||||
static sqlite3_module closureModule = {
|
||||
0, /* iVersion */
|
||||
closureConnect, /* xCreate */
|
||||
closureConnect, /* xConnect */
|
||||
closureBestIndex, /* xBestIndex */
|
||||
closureDisconnect, /* xDisconnect */
|
||||
closureDisconnect, /* xDestroy */
|
||||
closureOpen, /* xOpen - open a cursor */
|
||||
closureClose, /* xClose - close a cursor */
|
||||
closureFilter, /* xFilter - configure scan constraints */
|
||||
closureNext, /* xNext - advance a cursor */
|
||||
closureEof, /* xEof - check for end of scan */
|
||||
closureColumn, /* xColumn - read data */
|
||||
closureRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
|
||||
/*
|
||||
** Register the closure virtual table
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_closure_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg;
|
||||
rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
||||
return rc;
|
||||
}
|
@@ -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
131
ext/misc/ieee754.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
** 2013-04-17
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements functions for the exact display
|
||||
** and input of IEEE754 Binary64 floating-point numbers.
|
||||
**
|
||||
** ieee754(X)
|
||||
** ieee754(Y,Z)
|
||||
**
|
||||
** In the first form, the value X should be a floating-point number.
|
||||
** The function will return a string of the form 'ieee754(Y,Z)' where
|
||||
** Y and Z are integers such that X==Y*pow(w.0,Z).
|
||||
**
|
||||
** In the second form, Y and Z are integers which are the mantissa and
|
||||
** base-2 exponent of a new floating point number. The function returns
|
||||
** a floating-point value equal to Y*pow(2.0,Z).
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** ieee754(2.0) -> 'ieee754(2,0)'
|
||||
** ieee754(45.25) -> 'ieee754(181,-2)'
|
||||
** ieee754(2, 0) -> 2.0
|
||||
** ieee754(181, -2) -> 45.25
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Implementation of the ieee754() function
|
||||
*/
|
||||
static void ieee754func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
if( argc==1 ){
|
||||
sqlite3_int64 m, a;
|
||||
double r;
|
||||
int e;
|
||||
int isNeg;
|
||||
char zResult[100];
|
||||
assert( sizeof(m)==sizeof(r) );
|
||||
if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
|
||||
r = sqlite3_value_double(argv[0]);
|
||||
if( r<0.0 ){
|
||||
isNeg = 1;
|
||||
r = -r;
|
||||
}else{
|
||||
isNeg = 0;
|
||||
}
|
||||
memcpy(&a,&r,sizeof(a));
|
||||
if( a==0 ){
|
||||
e = 0;
|
||||
m = 0;
|
||||
}else{
|
||||
e = a>>52;
|
||||
m = a & ((((sqlite3_int64)1)<<52)-1);
|
||||
m |= ((sqlite3_int64)1)<<52;
|
||||
while( e<1075 && m>0 && (m&1)==0 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
if( isNeg ) m = -m;
|
||||
}
|
||||
sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
|
||||
m, e-1075);
|
||||
sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
|
||||
}else if( argc==2 ){
|
||||
sqlite3_int64 m, e, a;
|
||||
double r;
|
||||
int isNeg = 0;
|
||||
m = sqlite3_value_int64(argv[0]);
|
||||
e = sqlite3_value_int64(argv[1]);
|
||||
if( m<0 ){
|
||||
isNeg = 1;
|
||||
m = -m;
|
||||
if( m<0 ) return;
|
||||
}else if( m==0 && e>1000 && e<1000 ){
|
||||
sqlite3_result_double(context, 0.0);
|
||||
return;
|
||||
}
|
||||
while( (m>>32)&0xffe00000 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
while( ((m>>32)&0xfff00000)==0 ){
|
||||
m <<= 1;
|
||||
e--;
|
||||
}
|
||||
e += 1075;
|
||||
if( e<0 ) e = m = 0;
|
||||
if( e>0x7ff ) m = 0;
|
||||
a = m & ((((sqlite3_int64)1)<<52)-1);
|
||||
a |= e<<52;
|
||||
if( isNeg ) a |= ((sqlite3_int64)1)<<63;
|
||||
memcpy(&r, &a, sizeof(r));
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_ieee_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
||||
ieee754func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
||||
ieee754func, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
265
ext/misc/nextchar.c
Normal file
265
ext/misc/nextchar.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
** 2013-02-28
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code to implement the next_char(A,T,F,W) SQL function.
|
||||
**
|
||||
** The next_char(A,T,F,H) function finds all valid "next" characters for
|
||||
** string A given the vocabulary in T.F. The T.F field should be indexed.
|
||||
** If the W value exists and is a non-empty string, then it is an SQL
|
||||
** expression that limits the entries in T.F that will be considered.
|
||||
**
|
||||
** For example, suppose an application has a dictionary like this:
|
||||
**
|
||||
** CREATE TABLE dictionary(word TEXT UNIQUE);
|
||||
**
|
||||
** Further suppose that for user keypad entry, it is desired to disable
|
||||
** (gray out) keys that are not valid as the next character. If the
|
||||
** the user has previously entered (say) 'cha' then to find all allowed
|
||||
** next characters (and thereby determine when keys should not be grayed
|
||||
** out) run the following query:
|
||||
**
|
||||
** SELECT next_char('cha','dictionary','word');
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** A structure to hold context of the next_char() computation across
|
||||
** nested function calls.
|
||||
*/
|
||||
typedef struct nextCharContext nextCharContext;
|
||||
struct nextCharContext {
|
||||
sqlite3 *db; /* Database connection */
|
||||
sqlite3_stmt *pStmt; /* Prepared statement used to query */
|
||||
const unsigned char *zPrefix; /* Prefix to scan */
|
||||
int nPrefix; /* Size of zPrefix in bytes */
|
||||
int nAlloc; /* Space allocated to aResult */
|
||||
int nUsed; /* Space used in aResult */
|
||||
unsigned int *aResult; /* Array of next characters */
|
||||
int mallocFailed; /* True if malloc fails */
|
||||
int otherError; /* True for any other failure */
|
||||
};
|
||||
|
||||
/*
|
||||
** Append a result character if the character is not already in the
|
||||
** result.
|
||||
*/
|
||||
static void nextCharAppend(nextCharContext *p, unsigned c){
|
||||
int i;
|
||||
for(i=0; i<p->nUsed; i++){
|
||||
if( p->aResult[i]==c ) return;
|
||||
}
|
||||
if( p->nUsed+1 > p->nAlloc ){
|
||||
unsigned int *aNew;
|
||||
int n = p->nAlloc*2 + 30;
|
||||
aNew = sqlite3_realloc(p->aResult, n*sizeof(unsigned int));
|
||||
if( aNew==0 ){
|
||||
p->mallocFailed = 1;
|
||||
return;
|
||||
}else{
|
||||
p->aResult = aNew;
|
||||
p->nAlloc = n;
|
||||
}
|
||||
}
|
||||
p->aResult[p->nUsed++] = c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a character into z[] as UTF8. Return the number of bytes needed
|
||||
** to hold the character
|
||||
*/
|
||||
static int writeUtf8(unsigned char *z, unsigned c){
|
||||
if( c<0x00080 ){
|
||||
z[0] = (unsigned char)(c&0xff);
|
||||
return 1;
|
||||
}
|
||||
if( c<0x00800 ){
|
||||
z[0] = 0xC0 + (unsigned char)((c>>6)&0x1F);
|
||||
z[1] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 2;
|
||||
}
|
||||
if( c<0x10000 ){
|
||||
z[0] = 0xE0 + (unsigned char)((c>>12)&0x0F);
|
||||
z[1] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 3;
|
||||
}
|
||||
z[0] = 0xF0 + (unsigned char)((c>>18) & 0x07);
|
||||
z[1] = 0x80 + (unsigned char)((c>>12) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[3] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a UTF8 character out of z[] and write it into *pOut. Return
|
||||
** the number of bytes in z[] that were used to construct the character.
|
||||
*/
|
||||
static int readUtf8(const unsigned char *z, unsigned *pOut){
|
||||
static const unsigned char validBits[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||||
};
|
||||
unsigned c = z[0];
|
||||
if( c<0xc0 ){
|
||||
*pOut = c;
|
||||
return 1;
|
||||
}else{
|
||||
int n = 1;
|
||||
c = validBits[c-0xc0];
|
||||
while( (z[n] & 0xc0)==0x80 ){
|
||||
c = (c<<6) + (0x3f & z[n++]);
|
||||
}
|
||||
if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){
|
||||
c = 0xFFFD;
|
||||
}
|
||||
*pOut = c;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The nextCharContext structure has been set up. Add all "next" characters
|
||||
** to the result set.
|
||||
*/
|
||||
static void findNextChars(nextCharContext *p){
|
||||
unsigned cPrev = 0;
|
||||
unsigned char zPrev[8];
|
||||
int n, rc;
|
||||
|
||||
for(;;){
|
||||
sqlite3_bind_text(p->pStmt, 1, (char*)p->zPrefix, p->nPrefix,
|
||||
SQLITE_STATIC);
|
||||
n = writeUtf8(zPrev, cPrev+1);
|
||||
sqlite3_bind_text(p->pStmt, 2, (char*)zPrev, n, SQLITE_STATIC);
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
if( rc==SQLITE_DONE ){
|
||||
sqlite3_reset(p->pStmt);
|
||||
return;
|
||||
}else if( rc!=SQLITE_ROW ){
|
||||
p->otherError = rc;
|
||||
return;
|
||||
}else{
|
||||
const unsigned char *zOut = sqlite3_column_text(p->pStmt, 0);
|
||||
unsigned cNext;
|
||||
n = readUtf8(zOut+p->nPrefix, &cNext);
|
||||
sqlite3_reset(p->pStmt);
|
||||
nextCharAppend(p, cNext);
|
||||
cPrev = cNext;
|
||||
if( p->mallocFailed ) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** next_character(A,T,F,W)
|
||||
**
|
||||
** Return a string composted of all next possible characters after
|
||||
** A for elements of T.F. If W is supplied, then it is an SQL expression
|
||||
** that limits the elements in T.F that are considered.
|
||||
*/
|
||||
static void nextCharFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
nextCharContext c;
|
||||
const unsigned char *zTable = sqlite3_value_text(argv[1]);
|
||||
const unsigned char *zField = sqlite3_value_text(argv[2]);
|
||||
const unsigned char *zWhere;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.db = sqlite3_context_db_handle(context);
|
||||
c.zPrefix = sqlite3_value_text(argv[0]);
|
||||
c.nPrefix = sqlite3_value_bytes(argv[0]);
|
||||
if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
|
||||
if( argc<4
|
||||
|| (zWhere = sqlite3_value_text(argv[3]))==0
|
||||
|| zWhere[0]==0
|
||||
){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=(?1 || ?2)"
|
||||
" AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
|
||||
" ORDER BY 1 ASC LIMIT 1",
|
||||
zField, zTable, zField, zField);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=(?1 || ?2)"
|
||||
" AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
|
||||
" AND (%s)"
|
||||
" ORDER BY 1 ASC LIMIT 1",
|
||||
zField, zTable, zField, zField, zWhere);
|
||||
}
|
||||
if( zSql==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare_v2(c.db, zSql, -1, &c.pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_result_error(context, sqlite3_errmsg(c.db), -1);
|
||||
return;
|
||||
}
|
||||
findNextChars(&c);
|
||||
if( c.mallocFailed ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
unsigned char *pRes;
|
||||
pRes = sqlite3_malloc( c.nUsed*4 + 1 );
|
||||
if( pRes==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
int i;
|
||||
int n = 0;
|
||||
for(i=0; i<c.nUsed; i++){
|
||||
n += writeUtf8(pRes+n, c.aResult[i]);
|
||||
}
|
||||
pRes[n] = 0;
|
||||
sqlite3_result_text(context, (const char*)pRes, n, sqlite3_free);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(c.pStmt);
|
||||
sqlite3_free(c.aResult);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_nextchar_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
@@ -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 *******************************/
|
@@ -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 */
|
@@ -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
26
magic.txt
Normal 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
16
main.mk
@@ -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
107
manifest
@@ -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
|
||||
|
@@ -1 +1 @@
|
||||
6994826c0784280f2e9728dfa4185848846d03df
|
||||
3879ab1b532828fcc12a50a95b6730faebcb69e9
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
118
src/os_unix.c
118
src/os_unix.c
@@ -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__);
|
||||
|
10
src/pager.c
10
src/pager.c
@@ -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{
|
||||
|
@@ -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{
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
12
src/shell.c
12
src/shell.c
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
64
src/test1.c
64
src/test1.c
@@ -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;
|
||||
|
24
src/test8.c
24
src/test8.c
@@ -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;
|
||||
|
@@ -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) );
|
||||
|
@@ -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
|
||||
|
13
src/walker.c
13
src/walker.c
@@ -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--;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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
224
test/closure01.test
Normal 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
210
test/fts3expr3.test
Normal 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
|
||||
|
||||
|
||||
|
||||
|
@@ -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.
|
||||
#
|
||||
|
@@ -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);
|
||||
|
68
test/io.test
68
test/io.test
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
#
|
||||
|
@@ -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,');
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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]");
|
||||
|
Reference in New Issue
Block a user