Merge recent enhancements from trunk, including table-valued expressions.

FossilOrigin-Name: b9927c876c1d4e146cb6a603d82cd2489594084d
This commit is contained in:
drh 2015-08-20 23:45:59 +00:00
commit c1bd84124b
30 changed files with 989 additions and 178 deletions

View File

@ -420,6 +420,7 @@ TESTSRC += \
$(TOP)/ext/misc/nextchar.c \
$(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c

View File

@ -1086,6 +1086,7 @@ TESTEXT = \
$(TOP)\ext\misc\nextchar.c \
$(TOP)\ext\misc\percentile.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\spellfix.c \
$(TOP)\ext\misc\totype.c \
$(TOP)\ext\misc\wholenumber.c

View File

@ -1866,7 +1866,7 @@ static void fts5ExprFunction(
int iArg = 1;
if( nArg<1 ){
char *zErr = sqlite3_mprintf("wrong number of arguments to function %s",
zErr = sqlite3_mprintf("wrong number of arguments to function %s",
bTcl ? "fts5_expr_tcl" : "fts5_expr"
);
sqlite3_result_error(pCtx, zErr, -1);

403
ext/misc/series.c Normal file
View File

@ -0,0 +1,403 @@
/*
** 2015-08-18
**
** 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 demonstrates how to create a table-valued-function using
** a virtual table. This demo implements the generate_series() function
** which gives similar results to the eponymous function in PostgreSQL.
** Examples:
**
** SELECT * FROM generate_series(0,100,5);
**
** The query above returns integers from 0 through 100 counting by steps
** of 5.
**
** SELECT * FROM generate_series(0,100);
**
** Integers from 0 through 100 with a step size of 1.
**
** SELECT * FROM generate_series(20) LIMIT 10;
**
** Integers 20 through 29.
**
** HOW IT WORKS
**
** The generate_series "function" is really a virtual table with the
** following schema:
**
** CREATE FUNCTION generate_series(
** value,
** start HIDDEN,
** stop HIDDEN,
** step HIDDEN
** );
**
** Function arguments in queries against this virtual table are translated
** into equality constraints against successive hidden columns. In other
** words, the following pairs of queries are equivalent to each other:
**
** SELECT * FROM generate_series(0,100,5);
** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5;
**
** SELECT * FROM generate_series(0,100);
** SELECT * FROM generate_series WHERE start=0 AND stop=100;
**
** SELECT * FROM generate_series(20) LIMIT 10;
** SELECT * FROM generate_series WHERE start=20 LIMIT 10;
**
** The generate_series virtual table implementation leaves the xCreate method
** set to NULL. This means that it is not possible to do a CREATE VIRTUAL
** TABLE command with "generate_series" as the USING argument. Instead, there
** is a single generate_series virtual table that is always available without
** having to be created first.
**
** The xBestIndex method looks for equality constraints against the hidden
** start, stop, and step columns, and if present, it uses those constraints
** to bound the sequence of generated values. If the equality constraints
** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step.
** xBestIndex returns a small cost when both start and stop are available,
** and a very large cost if either start or stop are unavailable. This
** encourages the query planner to order joins such that the bounds of the
** series are well-defined.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int isDesc; /* True to count down rather than up */
sqlite3_int64 iRowid; /* The rowid */
sqlite3_int64 iValue; /* Current value ("value") */
sqlite3_int64 mnValue; /* Mimimum value ("start") */
sqlite3_int64 mxValue; /* Maximum value ("stop") */
sqlite3_int64 iStep; /* Increment ("step") */
};
/*
** The seriesConnect() method is invoked to create a new
** series_vtab that describes the generate_series virtual table.
**
** Think of this routine as the constructor for series_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the series_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against generate_series will look like.
*/
static int seriesConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define SERIES_COLUMN_VALUE 0
#define SERIES_COLUMN_START 1
#define SERIES_COLUMN_STOP 2
#define SERIES_COLUMN_STEP 3
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for series_cursor objects.
*/
static int seriesDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new series_cursor object.
*/
static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
series_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a series_cursor.
*/
static int seriesClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a series_cursor to its next row of output.
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
if( pCur->isDesc ){
pCur->iValue -= pCur->iStep;
}else{
pCur->iValue += pCur->iStep;
}
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the series_cursor
** is currently pointing.
*/
static int seriesColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case SERIES_COLUMN_START: x = pCur->mnValue; break;
case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
case SERIES_COLUMN_STEP: x = pCur->iStep; break;
default: x = pCur->iValue; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
if( pCur->isDesc ){
return pCur->iValue < pCur->mnValue;
}else{
return pCur->iValue > pCur->mxValue;
}
}
/*
** This method is called to "rewind" the series_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to seriesColumn() or seriesRowid() or
** seriesEof().
**
** The query plan selected by seriesBestIndex is passed in the idxNum
** parameter. (idxStr is not used in this implementation.) idxNum
** is a bitmask showing which constraints are available:
**
** 1: start=VALUE
** 2: stop=VALUE
** 4: step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that seriesEof() will return true) if the table is empty.
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int i = 0;
if( idxNum & 1 ){
pCur->mnValue = sqlite3_value_int64(argv[i++]);
}else{
pCur->mnValue = 0;
}
if( idxNum & 2 ){
pCur->mxValue = sqlite3_value_int64(argv[i++]);
}else{
pCur->mxValue = 0xffffffff;
}
if( idxNum & 4 ){
pCur->iStep = sqlite3_value_int64(argv[i++]);
if( pCur->iStep<1 ) pCur->iStep = 1;
}else{
pCur->iStep = 1;
}
if( idxNum & 8 ){
pCur->isDesc = 1;
pCur->iValue = pCur->mxValue;
if( pCur->iStep>0 ){
pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
}
}else{
pCur->isDesc = 0;
pCur->iValue = pCur->mnValue;
}
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** The query plan is represented by bits in idxNum:
**
** (1) start = $value -- constraint exists
** (2) stop = $value -- constraint exists
** (4) step = $value -- constraint exists
** (8) output in descending order
*/
static int seriesBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
int startIdx = -1; /* Index of the start= constraint, or -1 if none */
int stopIdx = -1; /* Index of the stop= constraint, or -1 if none */
int stepIdx = -1; /* Index of the step= constraint, or -1 if none */
int nArg = 0; /* Number of arguments that seriesFilter() expects */
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->iColumn ){
case SERIES_COLUMN_START:
startIdx = i;
idxNum |= 1;
break;
case SERIES_COLUMN_STOP:
stopIdx = i;
idxNum |= 2;
break;
case SERIES_COLUMN_STEP:
stepIdx = i;
idxNum |= 4;
break;
}
}
if( startIdx>=0 ){
pIdxInfo->aConstraintUsage[startIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[startIdx].omit = 1;
}
if( stopIdx>=0 ){
pIdxInfo->aConstraintUsage[stopIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[stopIdx].omit = 1;
}
if( stepIdx>=0 ){
pIdxInfo->aConstraintUsage[stepIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[stepIdx].omit = 1;
}
if( pIdxInfo->nOrderBy==1 ){
if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8;
pIdxInfo->orderByConsumed = 1;
}
if( (idxNum & 3)==3 ){
/* Both start= and stop= boundaries are available. This is the
** the preferred case */
pIdxInfo->estimatedCost = (double)1;
}else{
/* If either boundary is missing, we have to generate a huge span
** of numbers. Make this case very expensive so that the query
** planner will work hard to avoid it. */
pIdxInfo->estimatedCost = (double)2000000000;
}
pIdxInfo->idxNum = idxNum;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** generate_series virtual table.
*/
static sqlite3_module seriesModule = {
0, /* iVersion */
0, /* xCreate */
seriesConnect, /* xConnect */
seriesBestIndex, /* xBestIndex */
seriesDisconnect, /* xDisconnect */
0, /* xDestroy */
seriesOpen, /* xOpen - open a cursor */
seriesClose, /* xClose - close a cursor */
seriesFilter, /* xFilter - configure scan constraints */
seriesNext, /* xNext - advance a cursor */
seriesEof, /* xEof - check for end of scan */
seriesColumn, /* xColumn - read data */
seriesRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_series_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( sqlite3_libversion_number()<3008012 ){
*pzErrMsg = sqlite3_mprintf(
"generate_series() requires SQLite 3.8.12 or later");
return SQLITE_ERROR;
}
rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0);
#endif
return rc;
}

View File

@ -3043,14 +3043,25 @@ sqlite3rbu *sqlite3rbu_open(
if( p->rc==SQLITE_OK ){
if( p->eStage==RBU_STAGE_OAL ){
sqlite3 *db = p->dbMain;
/* Open transactions both databases. The *-oal file is opened or
** created at this point. */
p->rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
}
/* Check if the main database is a zipvfs db. If it is, set the upper
** level pager to use "journal_mode=off". This prevents it from
** generating a large journal using a temp file. */
if( p->rc==SQLITE_OK ){
int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
if( frc==SQLITE_OK ){
p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
}
}
/* Point the object iterator at the first object */
if( p->rc==SQLITE_OK ){
p->rc = rbuObjIterFirst(p, &p->objiter);

View File

@ -300,6 +300,7 @@ TESTSRC += \
$(TOP)/ext/misc/nextchar.c \
$(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c \

View File

@ -1,9 +1,9 @@
C Provide\shints\sfor\sall\sterms\sin\sa\srange\sconstraint\sif\sthere\sare\sany\sequality\nterms\sanywhere\sin\sthe\sconstraint.\s\sRange\sconstraint\sterms\sare\sonly\somitted\nfor\sa\spure\srange\sconstraint\swith\sno\sequality\sprefix.
D 2015-08-18T15:58:05.895
C Merge\srecent\senhancements\sfrom\strunk,\sincluding\stable-valued\sexpressions.
D 2015-08-20T23:45:59.168
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 2fc9ca6bf5949d415801c007ed3004a4bdb7c380
F Makefile.in 4f663b6b4954b9b1eb0e6f08387688a93b57542d
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 5f7861c62c41fe8e3205ef14b90ebed28fa21f1b
F Makefile.msc cf63e11a5395cf887515ac7b78e2057dfe442fcd
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975
@ -110,7 +110,7 @@ F ext/fts5/fts5Int.h 45f2ceb3c030f70e2cc4c199e9f700c2f2367f77
F ext/fts5/fts5_aux.c 044cb176a815f4388308738437f6e130aa384fb0
F ext/fts5/fts5_buffer.c 80f9ba4431848cb857e3d2158f5280093dcd8015
F ext/fts5/fts5_config.c fdfa63ae8e527ecfaa50f94063c610429cc887cf
F ext/fts5/fts5_expr.c 495b24f47f4d71b63339572a5beaf9f6e1b486fe
F ext/fts5/fts5_expr.c d075d36c84975a1cfcf070442d28e28027b61c25
F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246
F ext/fts5/fts5_index.c 076c4995bf06a6d1559a6e31f9a86b90f2105374
F ext/fts5/fts5_main.c fc47ad734dfb55765b7542a345cee981170e7caa
@ -196,6 +196,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
F ext/misc/series.c 6f94daf590d0668187631dee2a4d7e1d8f3095c3
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 86998fb73aefb7b5dc346ba8a58912f312da4996
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
@ -224,7 +225,7 @@ F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
F ext/rbu/sqlite3rbu.c 08fddeae6b89aeb1e960f9330a2228968210170a
F ext/rbu/sqlite3rbu.c 1650e682b3568db0ed97ff2c7ba5d1c8ea060a84
F ext/rbu/sqlite3rbu.h 5357f070cd8c0bcad459b620651ec1656859e4d0
F ext/rbu/test_rbu.c 2a3652241fa45d5eaa141775e4ae68c1d3582c03
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
@ -256,7 +257,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 73167b34b0e67c0be32c1da2d988a376851c9ab1
F main.mk 702135e71d4438ea38c64b22fd6545a0fd87425c
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -280,14 +281,14 @@ F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
F src/btree.c b8083a37c09df6a30af497086d7ba05c1526d359
F src/btree.h 6310645beddbd3eef89fa9b6d1062065976a93c8
F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0
F src/build.c 4acc35c4e0a2d94c906abd164568cd6fc989cfbb
F src/build.c 5eb5d055a1d1cdaaea25e01b12607aa894bc0911
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
F src/date.c 8ec787fed4929d8ccdf6b1bc360fccc3e1d2ca58
F src/dbstat.c f402e77e25089c6003d0c60b3233b9b3947d599a
F src/delete.c 8857a6f27560718f65d43bdbec86c967ae1f8dfa
F src/expr.c e1fc69ce92a27bbb8074db56eece7914393b3ef6
F src/expr.c 673b80a52bbd6baedef15353c22facc436634fe8
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c c9b63a217d86582c22121699a47f22f524608869
F src/func.c 824bea430d3a2b7dbc62806ad54da8fdb8ed9e3f
@ -300,7 +301,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c 92bafa308607dd985ca389a788cd9e0a2b608712
F src/loadext.c dfcee8c7c032cd0fd55af3e0fc1fcfb01e426df2
F src/main.c 0a60b7ca8252c3a6f95438fa4ce8fe5b275c69f2
F src/main.c e17fcffae4306a9b8334faf3bac80d7396850b54
F src/malloc.c 19461e159bccf0e2cf06a50e867963d0a7b124a8
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
@ -324,7 +325,7 @@ F src/os_win.c 40b3af7a47eb1107d0d69e592bec345a3b7b798a
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c aa916ca28606ccf4b6877dfc2b643ccbca86589f
F src/pager.h 6d435f563b3f7fcae4b84433b76a6ac2730036e2
F src/parse.y 6d60dda8f8d418b6dc034f1fbccd816c459983a8
F src/parse.y ad9af8552f6f340bd646577ca63356a6f82b6a7e
F src/pcache.c cde06aa50962595e412d497e22fd2e07878ba1f0
F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9
F src/pcache1.c d08939800abf3031bd0affd5a13fbc4d7ba3fb68
@ -333,19 +334,19 @@ F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
F src/printf.c 2bc439ff20a4aad0e0ad50a37a67b5eae7d20edc
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 2d47554370de8de6dd5be060cef9559eec315005
F src/resolve.c 7a67cd2aebc9a9eeecd1d104eb6a9237388eb452
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 00acffc2e87904d9be0ac8e0fa365fd3326fa6fc
F src/select.c 4f276673f843f80e4db710556fefcede84527b86
F src/shell.c b1f91e60918df3a68efad1e3a11696b9a7e23d23
F src/sqlite.h.in 447ead0a6b3293206f04a0896553955d07cfb4b9
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h a0b948ebc89bac13941254641326a6aa248c2cc4
F src/sqliteInt.h cbd6d166c5f8aa17e4be463ccefef41cd1d40286
F src/sqliteInt.h 58bae1ab01e341359e0674a6e45304eb07c0c460
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
F src/tclsqlite.c d9439b6a910985b7fff43ba6756bcef00de22649
F src/test1.c d339ae9b9baf9221c657c9628c9061d88bd831f6
F src/test1.c c12ed85c22ac95f87f79de2ec9553334d115f71e
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
@ -392,30 +393,30 @@ F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c 57cb3720f53f84d811def2069c2b169b6be539a5
F src/treeview.c c84b1a8ebc7f1d00cd76ce4958eeb3ae1021beed
F src/treeview.c c15df00728034549ff92d78ae851b44952736d3b
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
F src/update.c 487747b328b7216bb7f6af0695d6937d5c9e605f
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c bc9dd64b5db544218b871b66243871c202b2781f
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
F src/vdbe.c 4dbcceb38f5c3e42c0725a56e7c76eb12adedebb
F src/vdbe.c 9d85fa72a73f7f07789d76602f724be63e998ddc
F src/vdbe.h 529bb4a7bedcd28dccba5abb3927e3c5cb70a832
F src/vdbeInt.h 7258d75fc2dad0bccdef14d7d8d2fd50fd1bf2d2
F src/vdbeapi.c adabbd66eb2e3a10f3998485ee0be7e326d06ee4
F src/vdbeaux.c e8dbcc838ca29f0d2767e789f4fe5883541e4f6e
F src/vdbeapi.c bda74ef4b5103d7b4a4be36f936d3cf2b56a7d6f
F src/vdbeaux.c 2262c5a674a30279f666061dd6b86f147df0d4b5
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
F src/vdbemem.c ae38a0d35ae71cf604381a887c170466ba518090
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
F src/vtab.c 082b35a25a26e3d36f365ca8cd73c1922532f05e
F src/vtab.c d31174e4c8f592febab3fa7f69e18320b4fd657a
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 6fb6b68969e4692593c2552c4e7bff5882de2cb8
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c ef95e56b6e7cdfa3ae0b6f72e3578391addfa965
F src/whereInt.h 5f87e3c4b0551747d119730dfebddd3c54f04047
F src/wherecode.c ec90321e51bd13a0396f305403bc8ced2af7407e
F src/whereexpr.c 9ce1c9cfedbf80c93c7d899497025ec8256ce652
F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
F src/where.c 746317b80f377aafbfb6c5e01366dedc38e1255b
F src/whereInt.h 880a8599226ac1c00203490d934f3ed79b292572
F src/wherecode.c 3be7fd91b356fad4379ad1cc98929e22c19c1acb
F src/whereexpr.c f9dbd159127452150c92b558e184827ecb8f9229
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -931,7 +932,7 @@ F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl b46812b9506f22d69c26f66808b90eb1b0318eec
F test/releasetest.tcl cd2de2749aab7f45b2fe91b4a05431fc08e1692a
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
@ -1017,6 +1018,7 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/speedtest1.c 857439869d1cb4db35e1c720ee9c2756eb9ea2a0
F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135
F test/spellfix2.test e5f2bc1dae046dbdd8008f2a84ed7749ff9b325e
F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5
F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68
F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf
@ -1030,6 +1032,7 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
F test/tabfunc01.test d556af2def6af10b0a759b2f8a8f41135c2b634e
F test/table.test 33bf0d1fd07f304582695184b8e6feb017303816
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
@ -1363,7 +1366,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/sqldiff.c a6988cc6e10e08662d73df28538df43b01dc371e
F tool/sqldiff.c b318efc2eaf7a7fac4d281a0ce736193cb2506df
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
@ -1374,7 +1377,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 142b048ac778620dd4e448c2e969982eb8188501
R e232e29543e31c41650c4e43b7879d83
P b5897bc0f003c470eeb2a75e0a2b2bb202681531 64d13339d44d1b7ec6768a33421f2138cb7872d8
R 9b0937bd5d11dff99a3e5d1003be4f97
U drh
Z 6f609828180274045282332efd8f8026
Z 8c2c018336c0b229ac2e1ad26fa5ec97

View File

@ -1 +1 @@
b5897bc0f003c470eeb2a75e0a2b2bb202681531
b9927c876c1d4e146cb6a603d82cd2489594084d

View File

@ -356,6 +356,15 @@ Table *sqlite3LocateTable(
p = sqlite3FindTable(pParse->db, zName, zDbase);
if( p==0 ){
const char *zMsg = isView ? "no such view" : "no such table";
#ifndef SQLITE_OMIT_VIRTUAL_TABLE
/* If zName is the not the name of a table in the schema created using
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
return pMod->pEpoTab;
}
#endif
if( zDbase ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
}else{
@ -560,7 +569,7 @@ void sqlite3CommitInternalChanges(sqlite3 *db){
** Delete memory allocated for the column names of a table or view (the
** Table.aCol[] array).
*/
static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
int i;
Column *pCol;
assert( pTable!=0 );
@ -627,7 +636,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Delete the Table structure itself.
*/
sqliteDeleteColumnNames(db, pTable);
sqlite3DeleteColumnNames(db, pTable);
sqlite3DbFree(db, pTable->zName);
sqlite3DbFree(db, pTable->zColAff);
sqlite3SelectDelete(db, pTable->pSelect);
@ -2218,7 +2227,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
sqliteDeleteColumnNames(db, pTab);
sqlite3DeleteColumnNames(db, pTab);
pTab->aCol = 0;
pTab->nCol = 0;
}
@ -3700,7 +3709,8 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
sqlite3DbFree(db, pItem->zDatabase);
sqlite3DbFree(db, pItem->zName);
sqlite3DbFree(db, pItem->zAlias);
sqlite3DbFree(db, pItem->zIndexedBy);
if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
sqlite3SelectDelete(db, pItem->pSelect);
sqlite3ExprDelete(db, pItem->pOn);
@ -3773,17 +3783,37 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
assert( pIndexedBy!=0 );
if( p && ALWAYS(p->nSrc>0) ){
struct SrcList_item *pItem = &p->a[p->nSrc-1];
assert( pItem->notIndexed==0 && pItem->zIndexedBy==0 );
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
if( pIndexedBy->n==1 && !pIndexedBy->z ){
/* A "NOT INDEXED" clause was supplied. See parse.y
** construct "indexed_opt" for details. */
pItem->notIndexed = 1;
pItem->fg.notIndexed = 1;
}else{
pItem->zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0);
}
}
}
/*
** Add the list of function arguments to the SrcList entry for a
** table-valued-function.
*/
void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
if( p && pList ){
struct SrcList_item *pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
pItem->u1.pFuncArg = pList;
pItem->fg.isTabFunc = 1;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
/*
** When building up a FROM clause in the parser, the join operator
** is initially attached to the left operand. But the code generator
@ -3803,9 +3833,9 @@ void sqlite3SrcListShiftJoinType(SrcList *p){
if( p ){
int i;
for(i=p->nSrc-1; i>0; i--){
p->a[i].jointype = p->a[i-1].jointype;
p->a[i].fg.jointype = p->a[i-1].fg.jointype;
}
p->a[0].jointype = 0;
p->a[0].fg.jointype = 0;
}
}

View File

@ -1034,16 +1034,18 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
pNewItem->jointype = pOldItem->jointype;
pNewItem->fg = pOldItem->fg;
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
pNewItem->isCorrelated = pOldItem->isCorrelated;
pNewItem->viaCoroutine = pOldItem->viaCoroutine;
pNewItem->isRecursive = pOldItem->isRecursive;
pNewItem->zIndexedBy = sqlite3DbStrDup(db, pOldItem->zIndexedBy);
pNewItem->notIndexed = pOldItem->notIndexed;
pNewItem->pIndex = pOldItem->pIndex;
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
pNewItem->pIBIndex = pOldItem->pIBIndex;
if( pNewItem->fg.isTabFunc ){
pNewItem->u1.pFuncArg =
sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
}
pTab = pNewItem->pTab = pOldItem->pTab;
if( pTab ){
pTab->nRef++;

View File

@ -932,17 +932,23 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
static void disconnectAllVtab(sqlite3 *db){
#ifndef SQLITE_OMIT_VIRTUALTABLE
int i;
HashElem *p;
sqlite3BtreeEnterAll(db);
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
if( db->aDb[i].pSchema ){
HashElem *p;
for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
Table *pTab = (Table *)sqliteHashData(p);
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
}
}
}
for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
Module *pMod = (Module *)sqliteHashData(p);
if( pMod->pEpoTab ){
sqlite3VtabDisconnect(db, pMod->pEpoTab);
}
}
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
#else
@ -1120,6 +1126,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
if( pMod->xDestroy ){
pMod->xDestroy(pMod->pAux);
}
sqlite3VtabEponymousTableClear(db, pMod);
sqlite3DbFree(db, pMod);
}
sqlite3HashClear(&db->aModule);

View File

@ -586,7 +586,7 @@ from(A) ::= FROM seltablist(X). {
//
stl_prefix(A) ::= seltablist(X) joinop(Y). {
A = X;
if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].jointype = (u8)Y;
if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].fg.jointype = (u8)Y;
}
stl_prefix(A) ::= . {A = 0;}
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I)
@ -594,6 +594,11 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I)
A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
sqlite3SrcListIndexedBy(pParse, A, &I);
}
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) LP exprlist(E) RP as(Z)
on_opt(N) using_opt(U). {
A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U);
sqlite3SrcListFuncArgs(pParse, A, E);
}
%ifndef SQLITE_OMIT_SUBQUERY
seltablist(A) ::= stl_prefix(X) LP select(S) RP
as(Z) on_opt(N) using_opt(U). {

View File

@ -306,7 +306,7 @@ static int lookupName(
** USING clause, then skip this match.
*/
if( cnt==1 ){
if( pItem->jointype & JT_NATURAL ) continue;
if( pItem->fg.jointype & JT_NATURAL ) continue;
if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
}
cnt++;
@ -321,8 +321,8 @@ static int lookupName(
pExpr->iTable = pMatch->iCursor;
pExpr->pTab = pMatch->pTab;
/* RIGHT JOIN not (yet) supported */
assert( (pMatch->jointype & JT_RIGHT)==0 );
if( (pMatch->jointype & JT_LEFT)!=0 ){
assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
pSchema = pExpr->pTab->pSchema;
@ -1142,7 +1142,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
int isCompound; /* True if p is a compound select */
int nCompound; /* Number of compound terms processed so far */
Parse *pParse; /* Parsing context */
ExprList *pEList; /* Result set expression list */
int i; /* Loop counter */
ExprList *pGroupBy; /* The GROUP BY clause */
Select *pLeftmost; /* Left-most of SELECT of a compound */
@ -1215,7 +1214,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** parent contexts. After resolving references to expressions in
** pItem->pSelect, check if this value has changed. If so, then
** SELECT statement pItem->pSelect must be correlated. Set the
** pItem->isCorrelated flag if this is the case. */
** pItem->fg.isCorrelated flag if this is the case. */
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
@ -1224,8 +1223,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
assert( pItem->isCorrelated==0 && nRef<=0 );
pItem->isCorrelated = (nRef!=0);
assert( pItem->fg.isCorrelated==0 && nRef<=0 );
pItem->fg.isCorrelated = (nRef!=0);
}
}
@ -1237,14 +1236,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
sNC.pNext = pOuterNC;
/* Resolve names in the result set. */
pEList = p->pEList;
assert( pEList!=0 );
for(i=0; i<pEList->nExpr; i++){
Expr *pX = pEList->a[i].pExpr;
if( sqlite3ResolveExprNames(&sNC, pX) ){
return WRC_Abort;
}
}
if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
/* If there are no aggregate functions in the result-set, and no GROUP BY
** expression, do not allow aggregates in any of the other expressions.
@ -1277,6 +1269,16 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
/* Resolve names in table-valued-function arguments */
for(i=0; i<p->pSrc->nSrc; i++){
struct SrcList_item *pItem = &p->pSrc->a[i];
if( pItem->fg.isTabFunc
&& sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
){
return WRC_Abort;
}
}
/* The ORDER BY and GROUP BY clauses may not refer to terms in
** outer queries
*/
@ -1440,6 +1442,22 @@ int sqlite3ResolveExprNames(
return ExprHasProperty(pExpr, EP_Error);
}
/*
** Resolve all names for all expression in an expression list. This is
** just like sqlite3ResolveExprNames() except that it works for an expression
** list rather than a single expression.
*/
int sqlite3ResolveExprListNames(
NameContext *pNC, /* Namespace to resolve expressions in. */
ExprList *pList /* The expression list to be analyzed. */
){
int i;
assert( pList!=0 );
for(i=0; i<pList->nExpr; i++){
if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
}
return WRC_Continue;
}
/*
** Resolve all names in all expressions of a SELECT and in all

View File

@ -406,12 +406,12 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
int isOuter;
if( NEVER(pLeftTab==0 || pRightTab==0) ) continue;
isOuter = (pRight->jointype & JT_OUTER)!=0;
isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
*/
if( pRight->jointype & JT_NATURAL ){
if( pRight->fg.jointype & JT_NATURAL ){
if( pRight->pOn || pRight->pUsing ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
@ -1933,7 +1933,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
**
**
** There is exactly one reference to the recursive-table in the FROM clause
** of recursive-query, marked with the SrcList->a[].isRecursive flag.
** of recursive-query, marked with the SrcList->a[].fg.isRecursive flag.
**
** The setup-query runs once to generate an initial set of rows that go
** into a Queue table. Rows are extracted from the Queue table one by
@ -1998,7 +1998,7 @@ static void generateWithRecursiveQuery(
/* Locate the cursor number of the Current table */
for(i=0; ALWAYS(i<pSrc->nSrc); i++){
if( pSrc->a[i].isRecursive ){
if( pSrc->a[i].fg.isRecursive ){
iCurrent = pSrc->a[i].iCursor;
break;
}
@ -3413,7 +3413,7 @@ static int flattenSubquery(
** is fraught with danger. Best to avoid the whole thing. If the
** subquery is the right term of a LEFT JOIN, then do not flatten.
*/
if( (pSubitem->jointype & JT_OUTER)!=0 ){
if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
return 0;
}
@ -3584,7 +3584,7 @@ static int flattenSubquery(
if( pSrc ){
assert( pParent==p ); /* First time through the loop */
jointype = pSubitem->jointype;
jointype = pSubitem->fg.jointype;
}else{
assert( pParent!=p ); /* 2nd and subsequent times through the loop */
pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
@ -3624,7 +3624,7 @@ static int flattenSubquery(
pSrc->a[i+iFrom] = pSubSrc->a[i];
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].jointype = jointype;
pSrc->a[iFrom].fg.jointype = jointype;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
@ -3875,9 +3875,9 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
** pFrom->pIndex and return SQLITE_OK.
*/
int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
if( pFrom->pTab && pFrom->zIndexedBy ){
if( pFrom->pTab && pFrom->fg.isIndexedBy ){
Table *pTab = pFrom->pTab;
char *zIndexedBy = pFrom->zIndexedBy;
char *zIndexedBy = pFrom->u1.zIndexedBy;
Index *pIdx;
for(pIdx=pTab->pIndex;
pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
@ -3888,7 +3888,7 @@ int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
pParse->checkSchema = 1;
return SQLITE_ERROR;
}
pFrom->pIndex = pIdx;
pFrom->pIBIndex = pIdx;
}
return SQLITE_OK;
}
@ -4083,7 +4083,7 @@ static int withExpand(
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
){
pItem->pTab = pTab;
pItem->isRecursive = 1;
pItem->fg.isRecursive = 1;
pTab->nRef++;
pSel->selFlags |= SF_Recursive;
}
@ -4213,8 +4213,8 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
assert( pFrom->isRecursive==0 || pFrom->pTab );
if( pFrom->isRecursive ) continue;
assert( pFrom->fg.isRecursive==0 || pFrom->pTab );
if( pFrom->fg.isRecursive ) continue;
if( pFrom->pTab!=0 ){
/* This statement has already been prepared. There is no need
** to go further. */
@ -4377,7 +4377,7 @@ static int selectExpander(Walker *pWalker, Select *p){
tableSeen = 1;
if( i>0 && zTName==0 ){
if( (pFrom->jointype & JT_NATURAL)!=0
if( (pFrom->fg.jointype & JT_NATURAL)!=0
&& tableAndColumnIndex(pTabList, i, zName, 0, 0)
){
/* In a NATURAL join, omit the join columns from the
@ -4904,7 +4904,7 @@ int sqlite3Select(
** is sufficient, though the subroutine to manifest the view does need
** to be invoked again. */
if( pItem->addrFillSub ){
if( pItem->viaCoroutine==0 ){
if( pItem->fg.viaCoroutine==0 ){
sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
}
continue;
@ -4922,7 +4922,7 @@ int sqlite3Select(
/* Make copies of constant WHERE-clause terms in the outer query down
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( (pItem->jointype & JT_OUTER)==0
if( (pItem->fg.jointype & JT_OUTER)==0
&& pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
){
#if SELECTTRACE_ENABLED
@ -4951,7 +4951,7 @@ int sqlite3Select(
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
pItem->viaCoroutine = 1;
pItem->fg.viaCoroutine = 1;
pItem->regResult = dest.iSdst;
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
@ -4969,7 +4969,7 @@ int sqlite3Select(
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
if( pItem->isCorrelated==0 ){
if( pItem->fg.isCorrelated==0 ){
/* 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. */

View File

@ -1480,6 +1480,7 @@ struct Module {
const char *zName; /* Name passed to create_module() */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
Table *pEpoTab; /* Eponymous table for this module */
};
/*
@ -1650,7 +1651,7 @@ struct Table {
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
int nModuleArg; /* Number of arguments to the module */
char **azModuleArg; /* Text of all module args. [0] is module name */
char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */
VTable *pVTable; /* List of VTable objects. */
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
@ -2285,11 +2286,15 @@ struct SrcList {
int addrFillSub; /* Address of subroutine to manifest a subquery */
int regReturn; /* Register holding return address of addrFillSub */
int regResult; /* Registers holding results of a co-routine */
u8 jointype; /* Type of join between this able and the previous */
unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
unsigned isCorrelated :1; /* True if sub-query is correlated */
unsigned viaCoroutine :1; /* Implemented as a co-routine */
unsigned isRecursive :1; /* True for recursive reference in WITH */
struct {
u8 jointype; /* Type of join between this able and the previous */
unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
unsigned isTabFunc :1; /* True if table-valued-function syntax */
unsigned isCorrelated :1; /* True if sub-query is correlated */
unsigned viaCoroutine :1; /* Implemented as a co-routine */
unsigned isRecursive :1; /* True for recursive reference in WITH */
} fg;
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
@ -2297,8 +2302,11 @@ struct SrcList {
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
Index *pIndex; /* Index structure corresponding to zIndex, if any */
union {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
} u1;
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
} a[1]; /* One entry for each identifier on the list */
};
@ -3251,6 +3259,7 @@ void sqlite3ResetOneSchema(sqlite3*,int);
void sqlite3CollapseDatabaseArray(sqlite3*);
void sqlite3BeginParse(Parse*,int);
void sqlite3CommitInternalChanges(sqlite3*);
void sqlite3DeleteColumnNames(sqlite3*,Table*);
Table *sqlite3ResultSetOfSelect(Parse*,Select*);
void sqlite3OpenMasterTable(Parse *, int);
Index *sqlite3PrimaryKeyIndex(Table*);
@ -3322,6 +3331,7 @@ SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
@ -3616,6 +3626,7 @@ void sqlite3SelectPrep(Parse*, Select*, NameContext*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
int sqlite3ResolveExprNames(NameContext*, Expr*);
int sqlite3ResolveExprListNames(NameContext*, ExprList*);
void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
@ -3724,6 +3735,8 @@ void sqlite3AutoLoadExtensions(sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
int sqlite3VtabEponymousTableInit(Parse*,Module*);
void sqlite3VtabEponymousTableClear(sqlite3*,Module*);
void sqlite3VtabMakeWritable(Parse*,Table*);
void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
void sqlite3VtabFinishParse(Parse*, Token*);

View File

@ -6380,6 +6380,7 @@ static int tclLoadStaticExtensionCmd(
extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
@ -6400,6 +6401,7 @@ static int tclLoadStaticExtensionCmd(
{ "nextchar", sqlite3_nextchar_init },
{ "percentile", sqlite3_percentile_init },
{ "regexp", sqlite3_regexp_init },
{ "series", sqlite3_series_init },
{ "spellfix", sqlite3_spellfix_init },
{ "totype", sqlite3_totype_init },
{ "wholenumber", sqlite3_wholenumber_init },

View File

@ -120,7 +120,7 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
if( pItem->zAlias ){
sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias);
}
if( pItem->jointype & JT_LEFT ){
if( pItem->fg.jointype & JT_LEFT ){
sqlite3XPrintf(&x, 0, " LEFT-JOIN");
}
sqlite3StrAccumFinish(&x);
@ -128,6 +128,9 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
if( pItem->pSelect ){
sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
}
if( pItem->fg.isTabFunc ){
sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
}
sqlite3TreeViewPop(pView);
}
sqlite3TreeViewPop(pView);

View File

@ -571,7 +571,7 @@ int sqlite3VdbeExec(
** sqlite3_column_text16() failed. */
goto no_mem;
}
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY );
assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
p->iCurrentTime = 0;
@ -3085,9 +3085,11 @@ case OP_Transaction: {
if( pBt ){
rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
if( rc==SQLITE_BUSY ){
testcase( rc==SQLITE_BUSY_SNAPSHOT );
testcase( rc==SQLITE_BUSY_RECOVERY );
if( (rc&0xff)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
p->rc = rc = SQLITE_BUSY;
p->rc = rc;
goto vdbe_return;
}
if( rc!=SQLITE_OK ){

View File

@ -611,7 +611,7 @@ end_of_step:
** were called on statement p.
*/
assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
|| rc==SQLITE_BUSY || rc==SQLITE_MISUSE
|| (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE
);
assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp );
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){

View File

@ -1184,8 +1184,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
zColl = "B";
n = 1;
}
if( i+n>nTemp-6 ){
if( i+n>nTemp-7 ){
memcpy(&zTemp[i],",...",4);
i += 4;
break;
}
zTemp[i++] = ',';

View File

@ -58,6 +58,7 @@ static int createModule(
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
pMod->pEpoTab = 0;
pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
assert( pDel==0 || pDel==pMod );
if( pDel ){
@ -285,23 +286,17 @@ void sqlite3VtabClear(sqlite3 *db, Table *p){
** deleted.
*/
static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
int i = pTable->nModuleArg++;
int nBytes = sizeof(char *)*(1+pTable->nModuleArg);
int nBytes = sizeof(char *)*(2+pTable->nModuleArg);
char **azModuleArg;
azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
if( azModuleArg==0 ){
int j;
for(j=0; j<i; j++){
sqlite3DbFree(db, pTable->azModuleArg[j]);
}
sqlite3DbFree(db, zArg);
sqlite3DbFree(db, pTable->azModuleArg);
pTable->nModuleArg = 0;
}else{
int i = pTable->nModuleArg++;
azModuleArg[i] = zArg;
azModuleArg[i+1] = 0;
pTable->azModuleArg = azModuleArg;
}
pTable->azModuleArg = azModuleArg;
}
/*
@ -704,7 +699,7 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
** invoke it now. If the module has not been registered, return an
** error. Otherwise, do nothing.
*/
if( !pMod ){
if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){
*pzErr = sqlite3MPrintf(db, "no such module: %s", zMod);
rc = SQLITE_ERROR;
}else{
@ -806,6 +801,7 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){
VTable *p;
int (*xDestroy)(sqlite3_vtab *);
for(p=pTab->pVTable; p; p=p->pNext){
assert( p->pVtab );
if( p->pVtab->nRef>0 ){
@ -813,7 +809,9 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
}
}
p = vtabDisconnectAll(db, pTab);
rc = p->pMod->pModule->xDestroy(p->pVtab);
xDestroy = p->pMod->pModule->xDestroy;
assert( xDestroy!=0 ); /* Checked before the virtual table is created */
rc = xDestroy(p->pVtab);
/* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
if( rc==SQLITE_OK ){
assert( pTab->pVTable==p && p->pNext==0 );
@ -1092,6 +1090,67 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
}
}
/*
** Check to see if virtual tale module pMod can be have an eponymous
** virtual table instance. If it can, create one if one does not already
** exist. Return non-zero if the eponymous virtual table instance exists
** when this routine returns, and return zero if it does not exist.
**
** An eponymous virtual table instance is one that is named after its
** module, and more importantly, does not require a CREATE VIRTUAL TABLE
** statement in order to come into existance. Eponymous virtual table
** instances always exist. They cannot be DROP-ed.
**
** Any virtual table module for which xConnect and xCreate are the same
** method can have an eponymous virtual table instance.
*/
int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
const sqlite3_module *pModule = pMod->pModule;
Table *pTab;
char *zErr = 0;
int nName;
int rc;
sqlite3 *db = pParse->db;
if( pMod->pEpoTab ) return 1;
if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0;
nName = sqlite3Strlen30(pMod->zName) + 1;
pTab = sqlite3DbMallocZero(db, sizeof(Table) + nName);
if( pTab==0 ) return 0;
pMod->pEpoTab = pTab;
pTab->zName = (char*)&pTab[1];
memcpy(pTab->zName, pMod->zName, nName);
pTab->nRef = 1;
pTab->pSchema = db->aDb[0].pSchema;
pTab->tabFlags |= TF_Virtual;
pTab->nModuleArg = 0;
pTab->iPKey = -1;
addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
addModuleArgument(db, pTab, 0);
addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
if( rc ){
sqlite3ErrorMsg(pParse, "%s", zErr);
sqlite3DbFree(db, zErr);
sqlite3VtabEponymousTableClear(db, pMod);
return 0;
}
return 1;
}
/*
** Erase the eponymous virtual table instance associated with
** virtual table module pMod, if it exists.
*/
void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
Table *pTab = pMod->pEpoTab;
if( (pTab = pMod->pEpoTab)!=0 ){
sqlite3DeleteColumnNames(db, pTab);
sqlite3VtabClear(db, pTab);
sqlite3DbFree(db, pTab);
pMod->pEpoTab = 0;
}
}
/*
** Return the ON CONFLICT resolution mode in effect for the virtual
** table update operation currently in progress.

View File

@ -105,6 +105,11 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){
return WRC_Abort;
}
if( pItem->fg.isTabFunc
&& sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
){
return WRC_Abort;
}
}
}
return WRC_Continue;

View File

@ -709,7 +709,7 @@ static void constructAutomaticIndex(
/* Fill the automatic index with content */
sqlite3ExprCachePush(pParse);
pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
if( pTabItem->viaCoroutine ){
if( pTabItem->fg.viaCoroutine ){
int regYield = pTabItem->regReturn;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
@ -728,10 +728,10 @@ static void constructAutomaticIndex(
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
if( pTabItem->viaCoroutine ){
if( pTabItem->fg.viaCoroutine ){
translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
pTabItem->viaCoroutine = 0;
pTabItem->fg.viaCoroutine = 0;
}else{
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
}
@ -2128,7 +2128,7 @@ static int whereLoopAddBtreeIndex(
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
}else if( /*pProbe->tnum<=0 ||*/ (pSrc->jointype & JT_LEFT)!=0 ){
}else if( /*pProbe->tnum<=0 ||*/ (pSrc->fg.jointype & JT_LEFT)!=0 ){
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
}else{
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
@ -2502,9 +2502,9 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
if( pSrc->pIndex ){
if( pSrc->pIBIndex ){
/* An INDEXED BY clause specifies a particular index to use */
pProbe = pSrc->pIndex;
pProbe = pSrc->pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@ -2524,7 +2524,7 @@ static int whereLoopAddBtree(
aiRowEstPk[0] = pTab->nRowLogEst;
aiRowEstPk[1] = 0;
pFirst = pSrc->pTab->pIndex;
if( pSrc->notIndexed==0 ){
if( pSrc->fg.notIndexed==0 ){
/* The real indices of the table are only considered if the
** NOT INDEXED qualifier is omitted from the FROM clause */
sPk.pNext = pFirst;
@ -2536,14 +2536,14 @@ static int whereLoopAddBtree(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/* Automatic indexes */
if( !pBuilder->pOrSet /* Not part of an OR optimization */
if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
&& pSrc->pIndex==0 /* Has no INDEXED BY clause */
&& !pSrc->notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->isCorrelated /* Not a correlated subquery */
&& !pSrc->isRecursive /* Not a recursive common table expression. */
&& pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
){
/* Generate auto-index WhereLoops */
WhereTerm *pTerm;
@ -2664,7 +2664,7 @@ static int whereLoopAddBtree(
/* If there was an INDEXED BY clause, then only that one index is
** considered. */
if( pSrc->pIndex ) break;
if( pSrc->pIBIndex ) break;
}
return rc;
}
@ -3010,16 +3010,16 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
Bitmask mUnusable = 0;
pNew->iTab = iTab;
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
if( ((pItem->jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
/* This condition is true when pItem is the FROM clause term on the
** right-hand-side of a LEFT or CROSS JOIN. */
mExtra = mPrior;
}
priorJointype = pItem->jointype;
priorJointype = pItem->fg.jointype;
if( IsVirtual(pItem->pTab) ){
struct SrcList_item *p;
for(p=&pItem[1]; p<pEnd; p++){
if( mUnusable || (p->jointype & (JT_LEFT|JT_CROSS)) ){
if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
}
}
@ -3749,7 +3749,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
pItem = pWInfo->pTabList->a;
pTab = pItem->pTab;
if( IsVirtual(pTab) ) return 0;
if( pItem->zIndexedBy ) return 0;
if( pItem->fg.isIndexedBy ) return 0;
iCur = pItem->iCursor;
pWC = &pWInfo->sWC;
pLoop = pBuilder->pNew;
@ -4030,6 +4030,7 @@ WhereInfo *sqlite3WhereBegin(
*/
for(ii=0; ii<pTabList->nSrc; ii++){
createMask(pMaskSet, pTabList->a[ii].iCursor);
sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
}
#ifndef NDEBUG
{
@ -4136,7 +4137,7 @@ WhereInfo *sqlite3WhereBegin(
while( pWInfo->nLevel>=2 ){
WhereTerm *pTerm, *pEnd;
pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
if( (pWInfo->pTabList->a[pLoop->iTab].jointype & JT_LEFT)==0 ) break;
if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break;
if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
&& (pLoop->wsFlags & WHERE_ONEROW)==0
){
@ -4432,7 +4433,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
** the co-routine into OP_Copy of result contained in a register.
** OP_Rowid becomes OP_Null.
*/
if( pTabItem->viaCoroutine && !db->mallocFailed ){
if( pTabItem->fg.viaCoroutine && !db->mallocFailed ){
translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur,
pTabItem->regResult);
continue;

View File

@ -475,6 +475,7 @@ void sqlite3WhereSplit(WhereClause*,Expr*,u8);
Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);

View File

@ -794,14 +794,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** initialize a memory cell that records if this table matches any
** row of the left table of the join.
*/
if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
pLevel->iLeftJoin = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
VdbeComment((v, "init LEFT JOIN no-match flag"));
}
/* Special case of a FROM clause subquery implemented as a co-routine */
if( pTabItem->viaCoroutine ){
if( pTabItem->fg.viaCoroutine ){
int regYield = pTabItem->regReturn;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
@ -1545,7 +1545,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
static const u8 aStep[] = { OP_Next, OP_Prev };
static const u8 aStart[] = { OP_Rewind, OP_Last };
assert( bRev==0 || bRev==1 );
if( pTabItem->isRecursive ){
if( pTabItem->fg.isRecursive ){
/* Tables marked isRecursive have only a single row that is stored in
** a pseudo-cursor. No need to Rewind or Next such cursors. */
pLevel->op = OP_Noop;

View File

@ -1247,3 +1247,42 @@ void sqlite3WhereExprAnalyze(
exprAnalyze(pTabList, pWC, i);
}
}
/*
** For table-valued-functions, transform the function arguments into
** new WHERE clause terms.
**
** Each function argument translates into an equality constraint against
** a HIDDEN column in the table.
*/
void sqlite3WhereTabFuncArgs(
Parse *pParse, /* Parsing context */
struct SrcList_item *pItem, /* The FROM clause term to process */
WhereClause *pWC /* Xfer function arguments to here */
){
Table *pTab;
int j, k;
ExprList *pArgs;
Expr *pColRef;
Expr *pTerm;
if( pItem->fg.isTabFunc==0 ) return;
pTab = pItem->pTab;
assert( pTab!=0 );
pArgs = pItem->u1.pFuncArg;
assert( pArgs!=0 );
for(j=k=0; j<pArgs->nExpr; j++){
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){ k++; }
if( k>=pTab->nCol ){
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
pTab->zName, j);
return;
}
pColRef = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
if( pColRef==0 ) return;
pColRef->iTable = pItem->iCursor;
pColRef->iColumn = k++;
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
}
}

View File

@ -286,6 +286,22 @@ foreach {key value} [array get ::Platforms] {
}
}
# Output log
#
set LOG [open releasetest-out.txt w]
proc PUTS {args} {
if {[llength $args]==2} {
puts [lindex $args 0] [lindex $args 1]
puts [lindex $args 0] $::LOG [lindex $args 1]
} else {
puts [lindex $args 0]
puts $::LOG [lindex $args 0]
}
}
puts $LOG "$argv0 $argv"
set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1]
puts $LOG "start-time: $tm0 UTC"
# Open the file $logfile and look for a report on the number of errors
# and the number of test cases run. Add these values to the global
# $::NERRCASE and $::NTESTCASE variables.
@ -408,7 +424,7 @@ proc run_test_suite {name testtarget config} {
if {!$::TRACE} {
set n [string length $title]
puts -nonewline "${title}[string repeat . [expr {63-$n}]]"
PUTS -nonewline "${title}[string repeat . [expr {63-$n}]]"
flush stdout
}
@ -433,12 +449,12 @@ proc run_test_suite {name testtarget config} {
set seconds [expr {($tm2-$tm1)%60}]
set tm [format (%02d:%02d:%02d) $hours $minutes $seconds]
if {$rc} {
puts " FAIL $tm"
PUTS " FAIL $tm"
incr ::NERR
} else {
puts " Ok $tm"
PUTS " Ok $tm"
}
if {$errmsg!=""} {puts " $errmsg"}
if {$errmsg!=""} {PUTS " $errmsg"}
}
}
@ -480,7 +496,7 @@ proc makeCommand { targets cflags opts } {
#
proc trace_cmd {args} {
if {$::TRACE} {
puts $args
PUTS $args
}
if {!$::DRYRUN} {
uplevel 1 $args
@ -548,25 +564,25 @@ proc process_options {argv} {
}
-info {
puts "Command-line Options:"
puts " --srcdir $::SRCDIR"
puts " --platform [list $platform]"
puts " --config [list $config]"
PUTS "Command-line Options:"
PUTS " --srcdir $::SRCDIR"
PUTS " --platform [list $platform]"
PUTS " --config [list $config]"
if {$::QUICK} {
if {$::QUICK==1} {puts " --quick"}
if {$::QUICK==2} {puts " --veryquick"}
if {$::QUICK==1} {PUTS " --quick"}
if {$::QUICK==2} {PUTS " --veryquick"}
}
if {$::MSVC} {puts " --msvc"}
if {$::BUILDONLY} {puts " --buildonly"}
if {$::DRYRUN} {puts " --dryrun"}
if {$::TRACE} {puts " --trace"}
puts "\nAvailable --platform options:"
if {$::MSVC} {PUTS " --msvc"}
if {$::BUILDONLY} {PUTS " --buildonly"}
if {$::DRYRUN} {PUTS " --dryrun"}
if {$::TRACE} {PUTS " --trace"}
PUTS "\nAvailable --platform options:"
foreach y [lsort [array names ::Platforms]] {
puts " [list $y]"
PUTS " [list $y]"
}
puts "\nAvailable --config options:"
PUTS "\nAvailable --config options:"
foreach y [lsort [array names ::Configs]] {
puts " [list $y]"
PUTS " [list $y]"
}
exit
}
@ -592,22 +608,22 @@ proc process_options {argv} {
}
default {
puts stderr ""
puts stderr [string trim $::USAGE_MESSAGE]
PUTS stderr ""
PUTS stderr [string trim $::USAGE_MESSAGE]
exit -1
}
}
}
if {0==[info exists ::Platforms($platform)]} {
puts "Unknown platform: $platform"
puts -nonewline "Set the -platform option to "
PUTS "Unknown platform: $platform"
PUTS -nonewline "Set the -platform option to "
set print [list]
foreach p [array names ::Platforms] {
lappend print "\"$p\""
}
lset print end "or [lindex $print end]"
puts "[join $print {, }]."
PUTS "[join $print {, }]."
exit
}
@ -617,17 +633,17 @@ proc process_options {argv} {
} else {
set ::CONFIGLIST $::Platforms($platform)
}
puts "Running the following test configurations for $platform:"
puts " [string trim $::CONFIGLIST]"
puts -nonewline "Flags:"
if {$::DRYRUN} {puts -nonewline " --dryrun"}
if {$::BUILDONLY} {puts -nonewline " --buildonly"}
if {$::MSVC} {puts -nonewline " --msvc"}
PUTS "Running the following test configurations for $platform:"
PUTS " [string trim $::CONFIGLIST]"
PUTS -nonewline "Flags:"
if {$::DRYRUN} {PUTS -nonewline " --dryrun"}
if {$::BUILDONLY} {PUTS -nonewline " --buildonly"}
if {$::MSVC} {PUTS -nonewline " --msvc"}
switch -- $::QUICK {
1 {puts -nonewline " --quick"}
2 {puts -nonewline " --veryquick"}
1 {PUTS -nonewline " --quick"}
2 {PUTS -nonewline " --veryquick"}
}
puts ""
PUTS ""
}
# Main routine.
@ -637,7 +653,7 @@ proc main {argv} {
# Process any command line options.
set ::EXTRACONFIG {}
process_options $argv
puts [string repeat * 79]
PUTS [string repeat * 79]
set ::NERR 0
set ::NTEST 0
@ -648,7 +664,7 @@ proc main {argv} {
foreach {zConfig target} $::CONFIGLIST {
if {$::MSVC && ($zConfig eq "Sanitize" || "checksymbols" in $target
|| "valgrindtest" in $target)} {
puts "Skipping $zConfig / $target for MSVC..."
PUTS "Skipping $zConfig / $target for MSVC..."
continue
}
if {$target ne "checksymbols"} {
@ -693,10 +709,10 @@ proc main {argv} {
set min [expr {($elapsetime/60)%60}]
set sec [expr {$elapsetime%60}]
set etime [format (%02d:%02d:%02d) $hr $min $sec]
puts [string repeat * 79]
puts "$::NERRCASE failures out of $::NTESTCASE tests in $etime"
PUTS [string repeat * 79]
PUTS "$::NERRCASE failures out of $::NTESTCASE tests in $etime"
if {$::SQLITE_VERSION ne ""} {
puts "SQLite $::SQLITE_VERSION"
PUTS "SQLite $::SQLITE_VERSION"
}
}

114
test/spellfix2.test Normal file
View File

@ -0,0 +1,114 @@
# 2012 July 12
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix spellfix2
ifcapable !vtab { finish_test ; return }
load_static_extension db spellfix nextchar
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE demo USING spellfix1;
INSERT INTO demo(word) VALUES ('amsterdam');
INSERT INTO demo(word) VALUES ('amsterdammetje');
INSERT INTO demo(word) VALUES ('amsterdamania');
INSERT INTO demo(word) VALUES ('amsterdamweg');
INSERT INTO demo(word) VALUES ('amsterdamsestraat');
INSERT INTO demo(word) VALUES ('amsterdamlaan');
}
do_execsql_test 1.1 {
SELECT word, distance, matchlen FROM demo
WHERE word MATCH 'amstedam*' AND top=3;
} {
amsterdam 100 9
amsterdammetje 100 9
amsterdamania 100 9
}
do_execsql_test 1.2 {
SELECT word, distance, matchlen FROM demo WHERE
word MATCH 'amstedam*' AND top=3 AND distance <= 100;
} {
amsterdam 100 9
amsterdammetje 100 9
amsterdamania 100 9
}
do_execsql_test 1.3 {
SELECT word, distance, matchlen FROM demo WHERE
word MATCH 'amstedam*' AND distance <= 100;
} {
amsterdam 100 9
amsterdammetje 100 9
amsterdamania 100 9
amsterdamweg 100 9
amsterdamsestraat 100 9
amsterdamlaan 100 9
}
do_test 1.4 {
foreach l {a b c d e f g h i j k l m n o p q r s t u v w x y z} {
execsql { INSERT INTO demo(word) VALUES ('amsterdam' || $l) }
}
} {}
do_execsql_test 1.5 {
SELECT count(*) FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100;
SELECT count(*) FROM demo
WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20;
} {
32 20
}
do_execsql_test 1.6 {
SELECT word, distance, matchlen FROM demo
WHERE word MATCH 'amstedam*' AND distance <= 100;
} {
amsterdam 100 9 amsterdamh 100 9
amsterdamm 100 9 amsterdamn 100 9
amsterdama 100 9 amsterdame 100 9
amsterdami 100 9 amsterdamo 100 9
amsterdamu 100 9 amsterdamy 100 9
amsterdammetje 100 9 amsterdamania 100 9
amsterdamb 100 9 amsterdamf 100 9
amsterdamp 100 9 amsterdamv 100 9
amsterdamw 100 9 amsterdamweg 100 9
amsterdamc 100 9 amsterdamg 100 9
amsterdamj 100 9 amsterdamk 100 9
amsterdamq 100 9 amsterdams 100 9
amsterdamx 100 9 amsterdamz 100 9
amsterdamsestraat 100 9 amsterdamd 100 9
amsterdamt 100 9 amsterdaml 100 9
amsterdamlaan 100 9 amsterdamr 100 9
}
do_execsql_test 1.7 {
SELECT word, distance, matchlen FROM demo
WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20;
} {
amsterdam 100 9 amsterdamh 100 9
amsterdamm 100 9 amsterdamn 100 9
amsterdama 100 9 amsterdame 100 9
amsterdami 100 9 amsterdamo 100 9
amsterdamu 100 9 amsterdamy 100 9
amsterdammetje 100 9 amsterdamania 100 9
amsterdamb 100 9 amsterdamf 100 9
amsterdamp 100 9 amsterdamv 100 9
amsterdamw 100 9 amsterdamweg 100 9
amsterdamc 100 9 amsterdamg 100 9
}
finish_test

69
test/tabfunc01.test Normal file
View File

@ -0,0 +1,69 @@
# 2015-08-19
#
# 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 tests for table-valued-functions implemented using
# eponymous virtual tables.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tabfunc01
ifcapable !vtab {
finish_test
return
}
load_static_extension db series
do_execsql_test tabfunc01-1.1 {
SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2;
} {1 | 3 | 5 | 7 | 9 |}
do_execsql_test tabfunc01-1.2 {
SELECT *, '|' FROM generate_series LIMIT 5;
} {0 | 1 | 2 | 3 | 4 |}
do_catchsql_test tabfunc01-1.3 {
CREATE VIRTUAL TABLE t1 USING generate_series;
} {1 {no such module: generate_series}}
do_execsql_test tabfunc01-1.4 {
SELECT * FROM generate_series(1,9,2);
} {1 3 5 7 9}
do_execsql_test tabfunc01-1.5 {
SELECT * FROM generate_series(1,9);
} {1 2 3 4 5 6 7 8 9}
do_execsql_test tabfunc01-1.6 {
SELECT * FROM generate_series(1,10) WHERE step=3;
} {1 4 7 10}
do_catchsql_test tabfunc01-1.7 {
SELECT * FROM generate_series(1,9,2,11);
} {1 {too many arguments on generate_series() - max 3}}
do_execsql_test tabfunc01-1.8 {
SELECT * FROM generate_series(0,32,5) ORDER BY rowid DESC;
} {30 25 20 15 10 5 0}
do_execsql_test tabfunc01-1.9 {
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY value DESC;
} {1 30 2 25 3 20 4 15 5 10 6 5 7 0}
do_execsql_test tabfunc01-1.10 {
SELECT rowid, * FROM generate_series(0,32,5) ORDER BY +value DESC;
} {7 30 6 25 5 20 4 15 3 10 2 5 1 0}
do_execsql_test tabfunc01-2.1 {
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES(2),(3);
SELECT *, '|' FROM t1, generate_series(1,x) ORDER BY 1, 2
} {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |}
do_execsql_test tabfunc01-2.2 {
SELECT * FROM generate_series() LIMIT 5;
} {0 1 2 3 4}
finish_test

View File

@ -810,9 +810,9 @@ static void hash_init(hash *pHash, const char *z){
*/
static void hash_next(hash *pHash, int c){
u16 old = pHash->z[pHash->i];
pHash->z[pHash->i] = c;
pHash->z[pHash->i] = (char)c;
pHash->i = (pHash->i+1)&(NHASH-1);
pHash->a = pHash->a - old + c;
pHash->a = pHash->a - old + (char)c;
pHash->b = pHash->b - NHASH*old + pHash->a;
}
@ -849,7 +849,7 @@ static void putInt(unsigned int v, char **pz){
*/
static int digit_count(int v){
unsigned int i, x;
for(i=1, x=64; v>=x; i++, x <<= 6){}
for(i=1, x=64; (unsigned int)v>=x; i++, x <<= 6){}
return i;
}
@ -956,7 +956,7 @@ static int rbuDeltaCreate(
unsigned int lenOut, /* Length of the target file */
char *zDelta /* Write the delta into this buffer */
){
int i, base;
unsigned int i, base;
char *zOrigDelta = zDelta;
hash h;
int nHash; /* Number of hash table entries */
@ -1005,7 +1005,7 @@ static int rbuDeltaCreate(
base = 0; /* We have already generated everything before zOut[base] */
while( base+NHASH<lenOut ){
int iSrc, iBlock;
unsigned int bestCnt, bestOfst=0, bestLitsz=0;
int bestCnt, bestOfst=0, bestLitsz=0;
hash_init(&h, &zOut[base]);
i = 0; /* Trying to match a landmark against zOut[base+i] */
bestCnt = 0;
@ -1038,14 +1038,18 @@ static int rbuDeltaCreate(
/* Beginning at iSrc, match forwards as far as we can. j counts
** the number of characters that match */
iSrc = iBlock*NHASH;
for(j=0, x=iSrc, y=base+i; x<lenSrc && y<lenOut; j++, x++, y++){
for(
j=0, x=iSrc, y=base+i;
(unsigned int)x<lenSrc && (unsigned int)y<lenOut;
j++, x++, y++
){
if( zSrc[x]!=zOut[y] ) break;
}
j--;
/* Beginning at iSrc-1, match backwards as far as we can. k counts
** the number of characters that match */
for(k=1; k<iSrc && k<=i; k++){
for(k=1; k<iSrc && (unsigned int)k<=i; k++){
if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
}
k--;