Merge recent enhancements from trunk.

FossilOrigin-Name: edceaccd66a65d6b36e53ce33d760a7bd9c2261a592d12189f5f55417b5d5d74
This commit is contained in:
drh 2017-12-23 18:40:39 +00:00
commit 844cf56bc6
34 changed files with 4197 additions and 464 deletions

View File

@ -425,6 +425,8 @@ TESTSRC = \
# Statically linked extensions
#
TESTSRC += \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/closure.c \
@ -1197,6 +1199,9 @@ sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext
sqltclsh$(TEXE): sqltclsh.c
$(LTLINK) sqltclsh.c -o $@ $(LIBTCL) $(TLIBS)
sqlite3_expert$(TEXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
$(LTLINK) $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert $(TLIBS)
CHECKER_DEPS =\
$(TOP)/tool/mkccode.tcl \
sqlite3.c \

View File

@ -1400,6 +1400,8 @@ TESTSRC = \
# Statically linked extensions.
#
TESTEXT = \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\test_expert.c \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\carray.c \
$(TOP)\ext\misc\closure.c \
@ -2220,6 +2222,9 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
CHECKER_DEPS =\
$(TOP)/tool/mkccode.tcl \
sqlite3.c \

83
ext/expert/README.md Normal file
View File

@ -0,0 +1,83 @@
## SQLite Expert Extension
This folder contains code for a simple system to propose useful indexes
given a database and a set of SQL queries. It works as follows:
1. The user database schema is copied to a temporary database.
1. All SQL queries are prepared against the temporary database.
Information regarding the WHERE and ORDER BY clauses, and other query
features that affect index selection are recorded.
1. The information gathered in step 2 is used to create candidate
indexes - indexes that the planner might have made use of in the previous
step, had they been available.
1. A subset of the data in the user database is used to generate statistics
for all existing indexes and the candidate indexes generated in step 3
above.
1. The SQL queries are prepared a second time. If the planner uses any
of the indexes created in step 3, they are recommended to the user.
# C API
The SQLite expert C API is defined in sqlite3expert.h. Most uses will proceed
as follows:
1. An sqlite3expert object is created by calling **sqlite3\_expert\_new()**.
A database handle opened by the user is passed as an argument.
1. The sqlite3expert object is configured with one or more SQL statements
by making one or more calls to **sqlite3\_expert\_sql()**. Each call may
specify a single SQL statement, or multiple statements separated by
semi-colons.
1. Optionally, the **sqlite3\_expert\_config()** API may be used to
configure the size of the data subset used to generate index statistics.
Using a smaller subset of the data can speed up the analysis.
1. **sqlite3\_expert\_analyze()** is called to run the analysis.
1. One or more calls are made to **sqlite3\_expert\_report()** to extract
components of the results of the analysis.
1. **sqlite3\_expert\_destroy()** is called to free all resources.
Refer to comments in sqlite3expert.h for further details.
# sqlite3_expert application
The file "expert.c" contains the code for a command line application that
uses the API described above. It can be compiled with (for example):
<pre>
gcc -O2 sqlite3.c expert.c sqlite3expert.c -o sqlite3_expert
</pre>
Assuming the database is named "test.db", it can then be run to analyze a
single query:
<pre>
./sqlite3_expert -sql &lt;sql-query&gt; test.db
</pre>
Or an entire text file worth of queries with:
<pre>
./sqlite3_expert -file &lt;text-file&gt; test.db
</pre>
By default, sqlite3\_expert generates index statistics using all the data in
the user database. For a large database, this may be prohibitively time
consuming. The "-sample" option may be used to configure sqlite3\_expert to
generate statistics based on an integer percentage of the user database as
follows:
<pre>
# Generate statistics based on 25% of the user database rows:
./sqlite3_expert -sample 25 -sql &lt;sql-query&gt; test.db
# Do not generate any statistics at all:
./sqlite3_expert -sample 0 -sql &lt;sql-query&gt; test.db
</pre>

155
ext/expert/expert.c Normal file
View File

@ -0,0 +1,155 @@
/*
** 2017 April 07
**
** 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.
**
*************************************************************************
*/
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3expert.h"
static void option_requires_argument(const char *zOpt){
fprintf(stderr, "Option requires an argument: %s\n", zOpt);
exit(-3);
}
static int option_integer_arg(const char *zVal){
return atoi(zVal);
}
static void usage(char **argv){
fprintf(stderr, "\n");
fprintf(stderr, "Usage %s ?OPTIONS? DATABASE\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "Options are:\n");
fprintf(stderr, " -sql SQL (analyze SQL statements passed as argument)\n");
fprintf(stderr, " -file FILE (read SQL statements from file FILE)\n");
fprintf(stderr, " -verbose LEVEL (integer verbosity level. default 1)\n");
fprintf(stderr, " -sample PERCENT (percent of db to sample. default 100)\n");
exit(-1);
}
static int readSqlFromFile(sqlite3expert *p, const char *zFile, char **pzErr){
FILE *in = fopen(zFile, "rb");
long nIn;
size_t nRead;
char *pBuf;
int rc;
if( in==0 ){
*pzErr = sqlite3_mprintf("failed to open file %s\n", zFile);
return SQLITE_ERROR;
}
fseek(in, 0, SEEK_END);
nIn = ftell(in);
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
nRead = fread(pBuf, nIn, 1, in);
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
*pzErr = sqlite3_mprintf("failed to read file %s\n", zFile);
return SQLITE_ERROR;
}
pBuf[nIn] = 0;
rc = sqlite3_expert_sql(p, pBuf, pzErr);
sqlite3_free(pBuf);
return rc;
}
int main(int argc, char **argv){
const char *zDb;
int rc = 0;
char *zErr = 0;
int i;
int iVerbose = 1; /* -verbose option */
sqlite3 *db = 0;
sqlite3expert *p = 0;
if( argc<2 ) usage(argv);
zDb = argv[argc-1];
if( zDb[0]=='-' ) usage(argv);
rc = sqlite3_open(zDb, &db);
if( rc!=SQLITE_OK ){
fprintf(stderr, "Cannot open db file: %s - %s\n", zDb, sqlite3_errmsg(db));
exit(-2);
}
p = sqlite3_expert_new(db, &zErr);
if( p==0 ){
fprintf(stderr, "Cannot run analysis: %s\n", zErr);
rc = 1;
}else{
for(i=1; i<(argc-1); i++){
char *zArg = argv[i];
if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++;
int nArg = (int)strlen(zArg);
if( nArg>=2 && 0==sqlite3_strnicmp(zArg, "-file", nArg) ){
if( ++i==(argc-1) ) option_requires_argument("-file");
rc = readSqlFromFile(p, argv[i], &zErr);
}
else if( nArg>=3 && 0==sqlite3_strnicmp(zArg, "-sql", nArg) ){
if( ++i==(argc-1) ) option_requires_argument("-sql");
rc = sqlite3_expert_sql(p, argv[i], &zErr);
}
else if( nArg>=3 && 0==sqlite3_strnicmp(zArg, "-sample", nArg) ){
int iSample;
if( ++i==(argc-1) ) option_requires_argument("-sample");
iSample = option_integer_arg(argv[i]);
sqlite3_expert_config(p, EXPERT_CONFIG_SAMPLE, iSample);
}
else if( nArg>=2 && 0==sqlite3_strnicmp(zArg, "-verbose", nArg) ){
if( ++i==(argc-1) ) option_requires_argument("-verbose");
iVerbose = option_integer_arg(argv[i]);
}
else{
usage(argv);
}
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_expert_analyze(p, &zErr);
}
if( rc==SQLITE_OK ){
int nQuery = sqlite3_expert_count(p);
if( iVerbose>0 ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
fprintf(stdout, "-- Candidates -------------------------------\n");
fprintf(stdout, "%s\n", zCand);
}
for(i=0; i<nQuery; i++){
const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES);
const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
if( zIdx==0 ) zIdx = "(no new indexes)\n";
if( iVerbose>0 ){
fprintf(stdout, "-- Query %d ----------------------------------\n",i+1);
fprintf(stdout, "%s\n\n", zSql);
}
fprintf(stdout, "%s\n%s\n", zIdx, zEQP);
}
}else{
fprintf(stderr, "Error: %s\n", zErr ? zErr : "?");
}
sqlite3_expert_destroy(p);
sqlite3_free(zErr);
return rc;
}

377
ext/expert/expert1.test Normal file
View File

@ -0,0 +1,377 @@
# 2009 Nov 11
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the CLI shell tool. Specifically,
# the ".recommend" command.
#
#
# Test plan:
#
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
set testprefix expert1
set CLI [test_binary_name sqlite3]
set CMD [test_binary_name sqlite3_expert]
proc squish {txt} {
regsub -all {[[:space:]]+} $txt { }
}
proc do_setup_rec_test {tn setup sql res} {
reset_db
db eval $setup
uplevel [list do_rec_test $tn $sql $res]
}
foreach {tn setup} {
1 {
if {![file executable $CMD]} { continue }
proc do_rec_test {tn sql res} {
set res [squish [string trim $res]]
set tst [subst -nocommands {
squish [string trim [exec $::CMD -verbose 0 -sql {$sql;} test.db]]
}]
uplevel [list do_test $tn $tst $res]
}
}
2 {
if {[info commands sqlite3_expert_new]==""} { continue }
proc do_rec_test {tn sql res} {
set expert [sqlite3_expert_new db]
$expert sql $sql
$expert analyze
set result [list]
for {set i 0} {$i < [$expert count]} {incr i} {
set idx [string trim [$expert report $i indexes]]
if {$idx==""} {set idx "(no new indexes)"}
lappend result $idx
lappend result [string trim [$expert report $i plan]]
}
$expert destroy
set tst [subst -nocommands {set {} [squish [join {$result}]]}]
uplevel [list do_test $tn $tst [string trim [squish $res]]]
}
}
3 {
if {![file executable $CLI]} { continue }
proc do_rec_test {tn sql res} {
set res [squish [string trim $res]]
set tst [subst -nocommands {
squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
}]
uplevel [list do_test $tn $tst $res]
}
}
} {
eval $setup
do_setup_rec_test $tn.1 { CREATE TABLE t1(a, b, c) } {
SELECT * FROM t1
} {
(no new indexes)
0|0|0|SCAN TABLE t1
}
do_setup_rec_test $tn.2 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 WHERE b>?;
} {
CREATE INDEX t1_idx_00000062 ON t1(b);
0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000062 (b>?)
}
do_setup_rec_test $tn.3 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 WHERE b COLLATE nocase BETWEEN ? AND ?
} {
CREATE INDEX t1_idx_3e094c27 ON t1(b COLLATE NOCASE);
0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_3e094c27 (b>? AND b<?)
}
do_setup_rec_test $tn.4 {
CREATE TABLE t1(a, b, c);
} {
SELECT a FROM t1 ORDER BY b;
} {
CREATE INDEX t1_idx_00000062 ON t1(b);
0|0|0|SCAN TABLE t1 USING INDEX t1_idx_00000062
}
do_setup_rec_test $tn.5 {
CREATE TABLE t1(a, b, c);
} {
SELECT a FROM t1 WHERE a=? ORDER BY b;
} {
CREATE INDEX t1_idx_000123a7 ON t1(a, b);
0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
}
do_setup_rec_test $tn.6 {
CREATE TABLE t1(a, b, c);
} {
SELECT min(a) FROM t1
} {
CREATE INDEX t1_idx_00000061 ON t1(a);
0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
}
do_setup_rec_test $tn.7 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 ORDER BY a, b, c;
} {
CREATE INDEX t1_idx_033e95fe ON t1(a, b, c);
0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_033e95fe
}
#do_setup_rec_test $tn.1.8 {
# CREATE TABLE t1(a, b, c);
#} {
# SELECT * FROM t1 ORDER BY a ASC, b COLLATE nocase DESC, c ASC;
#} {
# CREATE INDEX t1_idx_5be6e222 ON t1(a, b COLLATE NOCASE DESC, c);
# 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5be6e222
#}
do_setup_rec_test $tn.8.1 {
CREATE TABLE t1(a COLLATE NOCase, b, c);
} {
SELECT * FROM t1 WHERE a=?
} {
CREATE INDEX t1_idx_00000061 ON t1(a);
0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000061 (a=?)
}
do_setup_rec_test $tn.8.2 {
CREATE TABLE t1(a, b COLLATE nocase, c);
} {
SELECT * FROM t1 ORDER BY a ASC, b DESC, c ASC;
} {
CREATE INDEX t1_idx_5cb97285 ON t1(a, b DESC, c);
0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5cb97285
}
# Tables with names that require quotes.
#
do_setup_rec_test $tn.9.1 {
CREATE TABLE "t t"(a, b, c);
} {
SELECT * FROM "t t" WHERE a=?
} {
CREATE INDEX 't t_idx_00000061' ON 't t'(a);
0|0|0|SEARCH TABLE t t USING INDEX t t_idx_00000061 (a=?)
}
do_setup_rec_test $tn.9.2 {
CREATE TABLE "t t"(a, b, c);
} {
SELECT * FROM "t t" WHERE b BETWEEN ? AND ?
} {
CREATE INDEX 't t_idx_00000062' ON 't t'(b);
0|0|0|SEARCH TABLE t t USING INDEX t t_idx_00000062 (b>? AND b<?)
}
# Columns with names that require quotes.
#
do_setup_rec_test $tn.10.1 {
CREATE TABLE t3(a, "b b", c);
} {
SELECT * FROM t3 WHERE "b b" = ?
} {
CREATE INDEX t3_idx_00050c52 ON t3('b b');
0|0|0|SEARCH TABLE t3 USING INDEX t3_idx_00050c52 (b b=?)
}
do_setup_rec_test $tn.10.2 {
CREATE TABLE t3(a, "b b", c);
} {
SELECT * FROM t3 ORDER BY "b b"
} {
CREATE INDEX t3_idx_00050c52 ON t3('b b');
0|0|0|SCAN TABLE t3 USING INDEX t3_idx_00050c52
}
# Transitive constraints
#
do_setup_rec_test $tn.11.1 {
CREATE TABLE t5(a, b);
CREATE TABLE t6(c, d);
} {
SELECT * FROM t5, t6 WHERE a=? AND b=c AND c=?
} {
CREATE INDEX t5_idx_000123a7 ON t5(a, b);
CREATE INDEX t6_idx_00000063 ON t6(c);
0|0|1|SEARCH TABLE t6 USING INDEX t6_idx_00000063 (c=?)
0|1|0|SEARCH TABLE t5 USING COVERING INDEX t5_idx_000123a7 (a=? AND b=?)
}
# OR terms.
#
do_setup_rec_test $tn.12.1 {
CREATE TABLE t7(a, b);
} {
SELECT * FROM t7 WHERE a=? OR b=?
} {
CREATE INDEX t7_idx_00000062 ON t7(b);
CREATE INDEX t7_idx_00000061 ON t7(a);
0|0|0|SEARCH TABLE t7 USING INDEX t7_idx_00000061 (a=?)
0|0|0|SEARCH TABLE t7 USING INDEX t7_idx_00000062 (b=?)
}
# rowid terms.
#
do_setup_rec_test $tn.13.1 {
CREATE TABLE t8(a, b);
} {
SELECT * FROM t8 WHERE rowid=?
} {
(no new indexes)
0|0|0|SEARCH TABLE t8 USING INTEGER PRIMARY KEY (rowid=?)
}
do_setup_rec_test $tn.13.2 {
CREATE TABLE t8(a, b);
} {
SELECT * FROM t8 ORDER BY rowid
} {
(no new indexes)
0|0|0|SCAN TABLE t8
}
do_setup_rec_test $tn.13.3 {
CREATE TABLE t8(a, b);
} {
SELECT * FROM t8 WHERE a=? ORDER BY rowid
} {
CREATE INDEX t8_idx_00000061 ON t8(a);
0|0|0|SEARCH TABLE t8 USING INDEX t8_idx_00000061 (a=?)
}
# Triggers
#
do_setup_rec_test $tn.14 {
CREATE TABLE t9(a, b, c);
CREATE TABLE t10(a, b, c);
CREATE TRIGGER t9t AFTER INSERT ON t9 BEGIN
UPDATE t10 SET a=new.a WHERE b = new.b;
END;
} {
INSERT INTO t9 VALUES(?, ?, ?);
} {
CREATE INDEX t10_idx_00000062 ON t10(b);
0|0|0|SEARCH TABLE t10 USING INDEX t10_idx_00000062 (b=?)
}
do_setup_rec_test $tn.15 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT (i-1)/50, (i-1)/20 FROM s;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
} {
SELECT * FROM t2, t1 WHERE b=? AND d=? AND t2.rowid=t1.rowid
} {
CREATE INDEX t2_idx_00000064 ON t2(d);
0|0|0|SEARCH TABLE t2 USING INDEX t2_idx_00000064 (d=?)
0|1|1|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)
}
do_setup_rec_test $tn.16 {
CREATE TABLE t1(a, b);
} {
SELECT * FROM t1 WHERE b IS NOT NULL;
} {
(no new indexes)
0|0|0|SCAN TABLE t1
}
}
proc do_candidates_test {tn sql res} {
set res [squish [string trim $res]]
set expert [sqlite3_expert_new db]
$expert sql $sql
$expert analyze
set candidates [squish [string trim [$expert report 0 candidates]]]
$expert destroy
uplevel [list do_test $tn [list set {} $candidates] $res]
}
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT (i-1)/50, (i-1)/20 FROM s;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
}
do_candidates_test 3.1 {
SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
} {
CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50
CREATE INDEX t2_idx_00000063 ON t2(c); -- stat1: 100 20
CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
}
do_candidates_test 3.2 {
SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=?
} {
CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 17
CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
}
do_execsql_test 3.2 {
CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50
CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 16
CREATE INDEX t2_idx_00000063 ON t2(c); -- stat1: 100 20
CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
ANALYZE;
SELECT * FROM sqlite_stat1 ORDER BY 1, 2;
} {
t1 t1_idx_00000061 {100 50}
t1 t1_idx_00000062 {100 20}
t1 t1_idx_000123a7 {100 50 17}
t2 t2_idx_00000063 {100 20}
t2 t2_idx_00000064 {100 5}
t2 t2_idx_0001295b {100 20 5}
}
finish_test

1934
ext/expert/sqlite3expert.c Normal file

File diff suppressed because it is too large Load Diff

168
ext/expert/sqlite3expert.h Normal file
View File

@ -0,0 +1,168 @@
/*
** 2017 April 07
**
** 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.
**
*************************************************************************
*/
#include "sqlite3.h"
typedef struct sqlite3expert sqlite3expert;
/*
** Create a new sqlite3expert object.
**
** If successful, a pointer to the new object is returned and (*pzErr) set
** to NULL. Or, if an error occurs, NULL is returned and (*pzErr) set to
** an English-language error message. In this case it is the responsibility
** of the caller to eventually free the error message buffer using
** sqlite3_free().
*/
sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErr);
/*
** Configure an sqlite3expert object.
**
** EXPERT_CONFIG_SAMPLE:
** By default, sqlite3_expert_analyze() generates sqlite_stat1 data for
** each candidate index. This involves scanning and sorting the entire
** contents of each user database table once for each candidate index
** associated with the table. For large databases, this can be
** prohibitively slow. This option allows the sqlite3expert object to
** be configured so that sqlite_stat1 data is instead generated based on a
** subset of each table, or so that no sqlite_stat1 data is used at all.
**
** A single integer argument is passed to this option. If the value is less
** than or equal to zero, then no sqlite_stat1 data is generated or used by
** the analysis - indexes are recommended based on the database schema only.
** Or, if the value is 100 or greater, complete sqlite_stat1 data is
** generated for each candidate index (this is the default). Finally, if the
** value falls between 0 and 100, then it represents the percentage of user
** table rows that should be considered when generating sqlite_stat1 data.
**
** Examples:
**
** // Do not generate any sqlite_stat1 data
** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 0);
**
** // Generate sqlite_stat1 data based on 10% of the rows in each table.
** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 10);
*/
int sqlite3_expert_config(sqlite3expert *p, int op, ...);
#define EXPERT_CONFIG_SAMPLE 1 /* int */
/*
** Specify zero or more SQL statements to be included in the analysis.
**
** Buffer zSql must contain zero or more complete SQL statements. This
** function parses all statements contained in the buffer and adds them
** to the internal list of statements to analyze. If successful, SQLITE_OK
** is returned and (*pzErr) set to NULL. Or, if an error occurs - for example
** due to a error in the SQL - an SQLite error code is returned and (*pzErr)
** may be set to point to an English language error message. In this case
** the caller is responsible for eventually freeing the error message buffer
** using sqlite3_free().
**
** If an error does occur while processing one of the statements in the
** buffer passed as the second argument, none of the statements in the
** buffer are added to the analysis.
**
** This function must be called before sqlite3_expert_analyze(). If a call
** to this function is made on an sqlite3expert object that has already
** been passed to sqlite3_expert_analyze() SQLITE_MISUSE is returned
** immediately and no statements are added to the analysis.
*/
int sqlite3_expert_sql(
sqlite3expert *p, /* From a successful sqlite3_expert_new() */
const char *zSql, /* SQL statement(s) to add */
char **pzErr /* OUT: Error message (if any) */
);
/*
** This function is called after the sqlite3expert object has been configured
** with all SQL statements using sqlite3_expert_sql() to actually perform
** the analysis. Once this function has been called, it is not possible to
** add further SQL statements to the analysis.
**
** If successful, SQLITE_OK is returned and (*pzErr) is set to NULL. Or, if
** an error occurs, an SQLite error code is returned and (*pzErr) set to
** point to a buffer containing an English language error message. In this
** case it is the responsibility of the caller to eventually free the buffer
** using sqlite3_free().
**
** If an error does occur within this function, the sqlite3expert object
** is no longer useful for any purpose. At that point it is no longer
** possible to add further SQL statements to the object or to re-attempt
** the analysis. The sqlite3expert object must still be freed using a call
** sqlite3_expert_destroy().
*/
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr);
/*
** Return the total number of statements loaded using sqlite3_expert_sql().
** The total number of SQL statements may be different from the total number
** to calls to sqlite3_expert_sql().
*/
int sqlite3_expert_count(sqlite3expert*);
/*
** Return a component of the report.
**
** This function is called after sqlite3_expert_analyze() to extract the
** results of the analysis. Each call to this function returns either a
** NULL pointer or a pointer to a buffer containing a nul-terminated string.
** The value passed as the third argument must be one of the EXPERT_REPORT_*
** #define constants defined below.
**
** For some EXPERT_REPORT_* parameters, the buffer returned contains
** information relating to a specific SQL statement. In these cases that
** SQL statement is identified by the value passed as the second argument.
** SQL statements are numbered from 0 in the order in which they are parsed.
** If an out-of-range value (less than zero or equal to or greater than the
** value returned by sqlite3_expert_count()) is passed as the second argument
** along with such an EXPERT_REPORT_* parameter, NULL is always returned.
**
** EXPERT_REPORT_SQL:
** Return the text of SQL statement iStmt.
**
** EXPERT_REPORT_INDEXES:
** Return a buffer containing the CREATE INDEX statements for all recommended
** indexes for statement iStmt. If there are no new recommeded indexes, NULL
** is returned.
**
** EXPERT_REPORT_PLAN:
** Return a buffer containing the EXPLAIN QUERY PLAN output for SQL query
** iStmt after the proposed indexes have been added to the database schema.
**
** EXPERT_REPORT_CANDIDATES:
** Return a pointer to a buffer containing the CREATE INDEX statements
** for all indexes that were tested (for all SQL statements). The iStmt
** parameter is ignored for EXPERT_REPORT_CANDIDATES calls.
*/
const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
/*
** Values for the third argument passed to sqlite3_expert_report().
*/
#define EXPERT_REPORT_SQL 1
#define EXPERT_REPORT_INDEXES 2
#define EXPERT_REPORT_PLAN 3
#define EXPERT_REPORT_CANDIDATES 4
/*
** Free an (sqlite3expert*) handle and all associated resources. There
** should be one call to this function for each successful call to
** sqlite3-expert_new().
*/
void sqlite3_expert_destroy(sqlite3expert*);

215
ext/expert/test_expert.c Normal file
View File

@ -0,0 +1,215 @@
/*
** 2017 April 07
**
** 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.
**
*************************************************************************
*/
#if defined(SQLITE_TEST)
#include "sqlite3expert.h"
#include <assert.h>
#include <string.h>
#if defined(INCLUDE_SQLITE_TCL_H)
# include "sqlite_tcl.h"
#else
# include "tcl.h"
# ifndef SQLITE_TCLAPI
# define SQLITE_TCLAPI
# endif
#endif
/*
** Extract an sqlite3* db handle from the object passed as the second
** argument. If successful, set *pDb to point to the db handle and return
** TCL_OK. Otherwise, return TCL_ERROR.
*/
static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
Tcl_CmdInfo info;
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
return TCL_ERROR;
}
*pDb = *(sqlite3 **)info.objClientData;
return TCL_OK;
}
/*
** Tclcmd: $expert sql SQL
** $expert analyze
** $expert count
** $expert report STMT EREPORT
** $expert destroy
*/
static int SQLITE_TCLAPI testExpertCmd(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3expert *pExpert = (sqlite3expert*)clientData;
struct Subcmd {
const char *zSub;
int nArg;
const char *zMsg;
} aSub[] = {
{ "sql", 1, "TABLE", }, /* 0 */
{ "analyze", 0, "", }, /* 1 */
{ "count", 0, "", }, /* 2 */
{ "report", 2, "STMT EREPORT", }, /* 3 */
{ "destroy", 0, "", }, /* 4 */
{ 0 }
};
int iSub;
int rc = TCL_OK;
char *zErr = 0;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
);
if( rc!=TCL_OK ) return rc;
if( objc!=2+aSub[iSub].nArg ){
Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
return TCL_ERROR;
}
switch( iSub ){
case 0: { /* sql */
char *zArg = Tcl_GetString(objv[2]);
rc = sqlite3_expert_sql(pExpert, zArg, &zErr);
break;
}
case 1: { /* analyze */
rc = sqlite3_expert_analyze(pExpert, &zErr);
break;
}
case 2: { /* count */
int n = sqlite3_expert_count(pExpert);
Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
break;
}
case 3: { /* report */
const char *aEnum[] = {
"sql", "indexes", "plan", "candidates", 0
};
int iEnum;
int iStmt;
const char *zReport;
if( Tcl_GetIntFromObj(interp, objv[2], &iStmt)
|| Tcl_GetIndexFromObj(interp, objv[3], aEnum, "report", 0, &iEnum)
){
return TCL_ERROR;
}
assert( EXPERT_REPORT_SQL==1 );
assert( EXPERT_REPORT_INDEXES==2 );
assert( EXPERT_REPORT_PLAN==3 );
assert( EXPERT_REPORT_CANDIDATES==4 );
zReport = sqlite3_expert_report(pExpert, iStmt, 1+iEnum);
Tcl_SetObjResult(interp, Tcl_NewStringObj(zReport, -1));
break;
}
default: /* destroy */
assert( iSub==4 );
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
}
if( rc!=TCL_OK ){
if( zErr ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
}else{
extern const char *sqlite3ErrName(int);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
}
}
sqlite3_free(zErr);
return rc;
}
static void SQLITE_TCLAPI testExpertDel(void *clientData){
sqlite3expert *pExpert = (sqlite3expert*)clientData;
sqlite3_expert_destroy(pExpert);
}
/*
** sqlite3_expert_new DB
*/
static int SQLITE_TCLAPI test_sqlite3_expert_new(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
static int iCmd = 0;
sqlite3 *db;
char *zCmd = 0;
char *zErr = 0;
sqlite3expert *pExpert;
int rc = TCL_OK;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( dbHandleFromObj(interp, objv[1], &db) ){
return TCL_ERROR;
}
zCmd = sqlite3_mprintf("sqlite3expert%d", ++iCmd);
if( zCmd==0 ){
Tcl_AppendResult(interp, "out of memory", (char*)0);
return TCL_ERROR;
}
pExpert = sqlite3_expert_new(db, &zErr);
if( pExpert==0 ){
Tcl_AppendResult(interp, zErr, (char*)0);
rc = TCL_ERROR;
}else{
void *p = (void*)pExpert;
Tcl_CreateObjCommand(interp, zCmd, testExpertCmd, p, testExpertDel);
Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
}
sqlite3_free(zCmd);
sqlite3_free(zErr);
return rc;
}
int TestExpert_Init(Tcl_Interp *interp){
struct Cmd {
const char *zCmd;
Tcl_ObjCmdProc *xProc;
} aCmd[] = {
{ "sqlite3_expert_new", test_sqlite3_expert_new },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
struct Cmd *p = &aCmd[i];
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
}
return TCL_OK;
}
#endif

View File

@ -78,9 +78,9 @@ struct SHA3Context {
*/
static void KeccakF1600Step(SHA3Context *p){
int i;
u64 B0, B1, B2, B3, B4;
u64 C0, C1, C2, C3, C4;
u64 D0, D1, D2, D3, D4;
u64 b0, b1, b2, b3, b4;
u64 c0, c1, c2, c3, c4;
u64 d0, d1, d2, d3, d4;
static const u64 RC[] = {
0x0000000000000001ULL, 0x0000000000008082ULL,
0x800000000000808aULL, 0x8000000080008000ULL,
@ -95,301 +95,301 @@ static void KeccakF1600Step(SHA3Context *p){
0x8000000080008081ULL, 0x8000000000008080ULL,
0x0000000080000001ULL, 0x8000000080008008ULL
};
# define A00 (p->u.s[0])
# define A01 (p->u.s[1])
# define A02 (p->u.s[2])
# define A03 (p->u.s[3])
# define A04 (p->u.s[4])
# define A10 (p->u.s[5])
# define A11 (p->u.s[6])
# define A12 (p->u.s[7])
# define A13 (p->u.s[8])
# define A14 (p->u.s[9])
# define A20 (p->u.s[10])
# define A21 (p->u.s[11])
# define A22 (p->u.s[12])
# define A23 (p->u.s[13])
# define A24 (p->u.s[14])
# define A30 (p->u.s[15])
# define A31 (p->u.s[16])
# define A32 (p->u.s[17])
# define A33 (p->u.s[18])
# define A34 (p->u.s[19])
# define A40 (p->u.s[20])
# define A41 (p->u.s[21])
# define A42 (p->u.s[22])
# define A43 (p->u.s[23])
# define A44 (p->u.s[24])
# define a00 (p->u.s[0])
# define a01 (p->u.s[1])
# define a02 (p->u.s[2])
# define a03 (p->u.s[3])
# define a04 (p->u.s[4])
# define a10 (p->u.s[5])
# define a11 (p->u.s[6])
# define a12 (p->u.s[7])
# define a13 (p->u.s[8])
# define a14 (p->u.s[9])
# define a20 (p->u.s[10])
# define a21 (p->u.s[11])
# define a22 (p->u.s[12])
# define a23 (p->u.s[13])
# define a24 (p->u.s[14])
# define a30 (p->u.s[15])
# define a31 (p->u.s[16])
# define a32 (p->u.s[17])
# define a33 (p->u.s[18])
# define a34 (p->u.s[19])
# define a40 (p->u.s[20])
# define a41 (p->u.s[21])
# define a42 (p->u.s[22])
# define a43 (p->u.s[23])
# define a44 (p->u.s[24])
# define ROL64(a,x) ((a<<x)|(a>>(64-x)))
for(i=0; i<24; i+=4){
C0 = A00^A10^A20^A30^A40;
C1 = A01^A11^A21^A31^A41;
C2 = A02^A12^A22^A32^A42;
C3 = A03^A13^A23^A33^A43;
C4 = A04^A14^A24^A34^A44;
D0 = C4^ROL64(C1, 1);
D1 = C0^ROL64(C2, 1);
D2 = C1^ROL64(C3, 1);
D3 = C2^ROL64(C4, 1);
D4 = C3^ROL64(C0, 1);
c0 = a00^a10^a20^a30^a40;
c1 = a01^a11^a21^a31^a41;
c2 = a02^a12^a22^a32^a42;
c3 = a03^a13^a23^a33^a43;
c4 = a04^a14^a24^a34^a44;
d0 = c4^ROL64(c1, 1);
d1 = c0^ROL64(c2, 1);
d2 = c1^ROL64(c3, 1);
d3 = c2^ROL64(c4, 1);
d4 = c3^ROL64(c0, 1);
B0 = (A00^D0);
B1 = ROL64((A11^D1), 44);
B2 = ROL64((A22^D2), 43);
B3 = ROL64((A33^D3), 21);
B4 = ROL64((A44^D4), 14);
A00 = B0 ^((~B1)& B2 );
A00 ^= RC[i];
A11 = B1 ^((~B2)& B3 );
A22 = B2 ^((~B3)& B4 );
A33 = B3 ^((~B4)& B0 );
A44 = B4 ^((~B0)& B1 );
b0 = (a00^d0);
b1 = ROL64((a11^d1), 44);
b2 = ROL64((a22^d2), 43);
b3 = ROL64((a33^d3), 21);
b4 = ROL64((a44^d4), 14);
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i];
a11 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
B2 = ROL64((A20^D0), 3);
B3 = ROL64((A31^D1), 45);
B4 = ROL64((A42^D2), 61);
B0 = ROL64((A03^D3), 28);
B1 = ROL64((A14^D4), 20);
A20 = B0 ^((~B1)& B2 );
A31 = B1 ^((~B2)& B3 );
A42 = B2 ^((~B3)& B4 );
A03 = B3 ^((~B4)& B0 );
A14 = B4 ^((~B0)& B1 );
b2 = ROL64((a20^d0), 3);
b3 = ROL64((a31^d1), 45);
b4 = ROL64((a42^d2), 61);
b0 = ROL64((a03^d3), 28);
b1 = ROL64((a14^d4), 20);
a20 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
B4 = ROL64((A40^D0), 18);
B0 = ROL64((A01^D1), 1);
B1 = ROL64((A12^D2), 6);
B2 = ROL64((A23^D3), 25);
B3 = ROL64((A34^D4), 8);
A40 = B0 ^((~B1)& B2 );
A01 = B1 ^((~B2)& B3 );
A12 = B2 ^((~B3)& B4 );
A23 = B3 ^((~B4)& B0 );
A34 = B4 ^((~B0)& B1 );
b4 = ROL64((a40^d0), 18);
b0 = ROL64((a01^d1), 1);
b1 = ROL64((a12^d2), 6);
b2 = ROL64((a23^d3), 25);
b3 = ROL64((a34^d4), 8);
a40 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
B1 = ROL64((A10^D0), 36);
B2 = ROL64((A21^D1), 10);
B3 = ROL64((A32^D2), 15);
B4 = ROL64((A43^D3), 56);
B0 = ROL64((A04^D4), 27);
A10 = B0 ^((~B1)& B2 );
A21 = B1 ^((~B2)& B3 );
A32 = B2 ^((~B3)& B4 );
A43 = B3 ^((~B4)& B0 );
A04 = B4 ^((~B0)& B1 );
b1 = ROL64((a10^d0), 36);
b2 = ROL64((a21^d1), 10);
b3 = ROL64((a32^d2), 15);
b4 = ROL64((a43^d3), 56);
b0 = ROL64((a04^d4), 27);
a10 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
B3 = ROL64((A30^D0), 41);
B4 = ROL64((A41^D1), 2);
B0 = ROL64((A02^D2), 62);
B1 = ROL64((A13^D3), 55);
B2 = ROL64((A24^D4), 39);
A30 = B0 ^((~B1)& B2 );
A41 = B1 ^((~B2)& B3 );
A02 = B2 ^((~B3)& B4 );
A13 = B3 ^((~B4)& B0 );
A24 = B4 ^((~B0)& B1 );
b3 = ROL64((a30^d0), 41);
b4 = ROL64((a41^d1), 2);
b0 = ROL64((a02^d2), 62);
b1 = ROL64((a13^d3), 55);
b2 = ROL64((a24^d4), 39);
a30 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
C0 = A00^A20^A40^A10^A30;
C1 = A11^A31^A01^A21^A41;
C2 = A22^A42^A12^A32^A02;
C3 = A33^A03^A23^A43^A13;
C4 = A44^A14^A34^A04^A24;
D0 = C4^ROL64(C1, 1);
D1 = C0^ROL64(C2, 1);
D2 = C1^ROL64(C3, 1);
D3 = C2^ROL64(C4, 1);
D4 = C3^ROL64(C0, 1);
c0 = a00^a20^a40^a10^a30;
c1 = a11^a31^a01^a21^a41;
c2 = a22^a42^a12^a32^a02;
c3 = a33^a03^a23^a43^a13;
c4 = a44^a14^a34^a04^a24;
d0 = c4^ROL64(c1, 1);
d1 = c0^ROL64(c2, 1);
d2 = c1^ROL64(c3, 1);
d3 = c2^ROL64(c4, 1);
d4 = c3^ROL64(c0, 1);
B0 = (A00^D0);
B1 = ROL64((A31^D1), 44);
B2 = ROL64((A12^D2), 43);
B3 = ROL64((A43^D3), 21);
B4 = ROL64((A24^D4), 14);
A00 = B0 ^((~B1)& B2 );
A00 ^= RC[i+1];
A31 = B1 ^((~B2)& B3 );
A12 = B2 ^((~B3)& B4 );
A43 = B3 ^((~B4)& B0 );
A24 = B4 ^((~B0)& B1 );
b0 = (a00^d0);
b1 = ROL64((a31^d1), 44);
b2 = ROL64((a12^d2), 43);
b3 = ROL64((a43^d3), 21);
b4 = ROL64((a24^d4), 14);
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+1];
a31 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
B2 = ROL64((A40^D0), 3);
B3 = ROL64((A21^D1), 45);
B4 = ROL64((A02^D2), 61);
B0 = ROL64((A33^D3), 28);
B1 = ROL64((A14^D4), 20);
A40 = B0 ^((~B1)& B2 );
A21 = B1 ^((~B2)& B3 );
A02 = B2 ^((~B3)& B4 );
A33 = B3 ^((~B4)& B0 );
A14 = B4 ^((~B0)& B1 );
b2 = ROL64((a40^d0), 3);
b3 = ROL64((a21^d1), 45);
b4 = ROL64((a02^d2), 61);
b0 = ROL64((a33^d3), 28);
b1 = ROL64((a14^d4), 20);
a40 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
B4 = ROL64((A30^D0), 18);
B0 = ROL64((A11^D1), 1);
B1 = ROL64((A42^D2), 6);
B2 = ROL64((A23^D3), 25);
B3 = ROL64((A04^D4), 8);
A30 = B0 ^((~B1)& B2 );
A11 = B1 ^((~B2)& B3 );
A42 = B2 ^((~B3)& B4 );
A23 = B3 ^((~B4)& B0 );
A04 = B4 ^((~B0)& B1 );
b4 = ROL64((a30^d0), 18);
b0 = ROL64((a11^d1), 1);
b1 = ROL64((a42^d2), 6);
b2 = ROL64((a23^d3), 25);
b3 = ROL64((a04^d4), 8);
a30 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
B1 = ROL64((A20^D0), 36);
B2 = ROL64((A01^D1), 10);
B3 = ROL64((A32^D2), 15);
B4 = ROL64((A13^D3), 56);
B0 = ROL64((A44^D4), 27);
A20 = B0 ^((~B1)& B2 );
A01 = B1 ^((~B2)& B3 );
A32 = B2 ^((~B3)& B4 );
A13 = B3 ^((~B4)& B0 );
A44 = B4 ^((~B0)& B1 );
b1 = ROL64((a20^d0), 36);
b2 = ROL64((a01^d1), 10);
b3 = ROL64((a32^d2), 15);
b4 = ROL64((a13^d3), 56);
b0 = ROL64((a44^d4), 27);
a20 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
B3 = ROL64((A10^D0), 41);
B4 = ROL64((A41^D1), 2);
B0 = ROL64((A22^D2), 62);
B1 = ROL64((A03^D3), 55);
B2 = ROL64((A34^D4), 39);
A10 = B0 ^((~B1)& B2 );
A41 = B1 ^((~B2)& B3 );
A22 = B2 ^((~B3)& B4 );
A03 = B3 ^((~B4)& B0 );
A34 = B4 ^((~B0)& B1 );
b3 = ROL64((a10^d0), 41);
b4 = ROL64((a41^d1), 2);
b0 = ROL64((a22^d2), 62);
b1 = ROL64((a03^d3), 55);
b2 = ROL64((a34^d4), 39);
a10 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
C0 = A00^A40^A30^A20^A10;
C1 = A31^A21^A11^A01^A41;
C2 = A12^A02^A42^A32^A22;
C3 = A43^A33^A23^A13^A03;
C4 = A24^A14^A04^A44^A34;
D0 = C4^ROL64(C1, 1);
D1 = C0^ROL64(C2, 1);
D2 = C1^ROL64(C3, 1);
D3 = C2^ROL64(C4, 1);
D4 = C3^ROL64(C0, 1);
c0 = a00^a40^a30^a20^a10;
c1 = a31^a21^a11^a01^a41;
c2 = a12^a02^a42^a32^a22;
c3 = a43^a33^a23^a13^a03;
c4 = a24^a14^a04^a44^a34;
d0 = c4^ROL64(c1, 1);
d1 = c0^ROL64(c2, 1);
d2 = c1^ROL64(c3, 1);
d3 = c2^ROL64(c4, 1);
d4 = c3^ROL64(c0, 1);
B0 = (A00^D0);
B1 = ROL64((A21^D1), 44);
B2 = ROL64((A42^D2), 43);
B3 = ROL64((A13^D3), 21);
B4 = ROL64((A34^D4), 14);
A00 = B0 ^((~B1)& B2 );
A00 ^= RC[i+2];
A21 = B1 ^((~B2)& B3 );
A42 = B2 ^((~B3)& B4 );
A13 = B3 ^((~B4)& B0 );
A34 = B4 ^((~B0)& B1 );
b0 = (a00^d0);
b1 = ROL64((a21^d1), 44);
b2 = ROL64((a42^d2), 43);
b3 = ROL64((a13^d3), 21);
b4 = ROL64((a34^d4), 14);
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+2];
a21 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
B2 = ROL64((A30^D0), 3);
B3 = ROL64((A01^D1), 45);
B4 = ROL64((A22^D2), 61);
B0 = ROL64((A43^D3), 28);
B1 = ROL64((A14^D4), 20);
A30 = B0 ^((~B1)& B2 );
A01 = B1 ^((~B2)& B3 );
A22 = B2 ^((~B3)& B4 );
A43 = B3 ^((~B4)& B0 );
A14 = B4 ^((~B0)& B1 );
b2 = ROL64((a30^d0), 3);
b3 = ROL64((a01^d1), 45);
b4 = ROL64((a22^d2), 61);
b0 = ROL64((a43^d3), 28);
b1 = ROL64((a14^d4), 20);
a30 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
B4 = ROL64((A10^D0), 18);
B0 = ROL64((A31^D1), 1);
B1 = ROL64((A02^D2), 6);
B2 = ROL64((A23^D3), 25);
B3 = ROL64((A44^D4), 8);
A10 = B0 ^((~B1)& B2 );
A31 = B1 ^((~B2)& B3 );
A02 = B2 ^((~B3)& B4 );
A23 = B3 ^((~B4)& B0 );
A44 = B4 ^((~B0)& B1 );
b4 = ROL64((a10^d0), 18);
b0 = ROL64((a31^d1), 1);
b1 = ROL64((a02^d2), 6);
b2 = ROL64((a23^d3), 25);
b3 = ROL64((a44^d4), 8);
a10 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
B1 = ROL64((A40^D0), 36);
B2 = ROL64((A11^D1), 10);
B3 = ROL64((A32^D2), 15);
B4 = ROL64((A03^D3), 56);
B0 = ROL64((A24^D4), 27);
A40 = B0 ^((~B1)& B2 );
A11 = B1 ^((~B2)& B3 );
A32 = B2 ^((~B3)& B4 );
A03 = B3 ^((~B4)& B0 );
A24 = B4 ^((~B0)& B1 );
b1 = ROL64((a40^d0), 36);
b2 = ROL64((a11^d1), 10);
b3 = ROL64((a32^d2), 15);
b4 = ROL64((a03^d3), 56);
b0 = ROL64((a24^d4), 27);
a40 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
B3 = ROL64((A20^D0), 41);
B4 = ROL64((A41^D1), 2);
B0 = ROL64((A12^D2), 62);
B1 = ROL64((A33^D3), 55);
B2 = ROL64((A04^D4), 39);
A20 = B0 ^((~B1)& B2 );
A41 = B1 ^((~B2)& B3 );
A12 = B2 ^((~B3)& B4 );
A33 = B3 ^((~B4)& B0 );
A04 = B4 ^((~B0)& B1 );
b3 = ROL64((a20^d0), 41);
b4 = ROL64((a41^d1), 2);
b0 = ROL64((a12^d2), 62);
b1 = ROL64((a33^d3), 55);
b2 = ROL64((a04^d4), 39);
a20 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
C0 = A00^A30^A10^A40^A20;
C1 = A21^A01^A31^A11^A41;
C2 = A42^A22^A02^A32^A12;
C3 = A13^A43^A23^A03^A33;
C4 = A34^A14^A44^A24^A04;
D0 = C4^ROL64(C1, 1);
D1 = C0^ROL64(C2, 1);
D2 = C1^ROL64(C3, 1);
D3 = C2^ROL64(C4, 1);
D4 = C3^ROL64(C0, 1);
c0 = a00^a30^a10^a40^a20;
c1 = a21^a01^a31^a11^a41;
c2 = a42^a22^a02^a32^a12;
c3 = a13^a43^a23^a03^a33;
c4 = a34^a14^a44^a24^a04;
d0 = c4^ROL64(c1, 1);
d1 = c0^ROL64(c2, 1);
d2 = c1^ROL64(c3, 1);
d3 = c2^ROL64(c4, 1);
d4 = c3^ROL64(c0, 1);
B0 = (A00^D0);
B1 = ROL64((A01^D1), 44);
B2 = ROL64((A02^D2), 43);
B3 = ROL64((A03^D3), 21);
B4 = ROL64((A04^D4), 14);
A00 = B0 ^((~B1)& B2 );
A00 ^= RC[i+3];
A01 = B1 ^((~B2)& B3 );
A02 = B2 ^((~B3)& B4 );
A03 = B3 ^((~B4)& B0 );
A04 = B4 ^((~B0)& B1 );
b0 = (a00^d0);
b1 = ROL64((a01^d1), 44);
b2 = ROL64((a02^d2), 43);
b3 = ROL64((a03^d3), 21);
b4 = ROL64((a04^d4), 14);
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+3];
a01 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
B2 = ROL64((A10^D0), 3);
B3 = ROL64((A11^D1), 45);
B4 = ROL64((A12^D2), 61);
B0 = ROL64((A13^D3), 28);
B1 = ROL64((A14^D4), 20);
A10 = B0 ^((~B1)& B2 );
A11 = B1 ^((~B2)& B3 );
A12 = B2 ^((~B3)& B4 );
A13 = B3 ^((~B4)& B0 );
A14 = B4 ^((~B0)& B1 );
b2 = ROL64((a10^d0), 3);
b3 = ROL64((a11^d1), 45);
b4 = ROL64((a12^d2), 61);
b0 = ROL64((a13^d3), 28);
b1 = ROL64((a14^d4), 20);
a10 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
B4 = ROL64((A20^D0), 18);
B0 = ROL64((A21^D1), 1);
B1 = ROL64((A22^D2), 6);
B2 = ROL64((A23^D3), 25);
B3 = ROL64((A24^D4), 8);
A20 = B0 ^((~B1)& B2 );
A21 = B1 ^((~B2)& B3 );
A22 = B2 ^((~B3)& B4 );
A23 = B3 ^((~B4)& B0 );
A24 = B4 ^((~B0)& B1 );
b4 = ROL64((a20^d0), 18);
b0 = ROL64((a21^d1), 1);
b1 = ROL64((a22^d2), 6);
b2 = ROL64((a23^d3), 25);
b3 = ROL64((a24^d4), 8);
a20 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
B1 = ROL64((A30^D0), 36);
B2 = ROL64((A31^D1), 10);
B3 = ROL64((A32^D2), 15);
B4 = ROL64((A33^D3), 56);
B0 = ROL64((A34^D4), 27);
A30 = B0 ^((~B1)& B2 );
A31 = B1 ^((~B2)& B3 );
A32 = B2 ^((~B3)& B4 );
A33 = B3 ^((~B4)& B0 );
A34 = B4 ^((~B0)& B1 );
b1 = ROL64((a30^d0), 36);
b2 = ROL64((a31^d1), 10);
b3 = ROL64((a32^d2), 15);
b4 = ROL64((a33^d3), 56);
b0 = ROL64((a34^d4), 27);
a30 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
B3 = ROL64((A40^D0), 41);
B4 = ROL64((A41^D1), 2);
B0 = ROL64((A42^D2), 62);
B1 = ROL64((A43^D3), 55);
B2 = ROL64((A44^D4), 39);
A40 = B0 ^((~B1)& B2 );
A41 = B1 ^((~B2)& B3 );
A42 = B2 ^((~B3)& B4 );
A43 = B3 ^((~B4)& B0 );
A44 = B4 ^((~B0)& B1 );
b3 = ROL64((a40^d0), 41);
b4 = ROL64((a41^d1), 2);
b0 = ROL64((a42^d2), 62);
b1 = ROL64((a43^d3), 55);
b2 = ROL64((a44^d4), 39);
a40 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
}
}

View File

@ -56,6 +56,8 @@
**
** SWARMVTAB
**
** LEGACY SYNTAX:
**
** A "swarmvtab" virtual table is created similarly to a unionvtab table:
**
** CREATE VIRTUAL TABLE <name>
@ -66,13 +68,78 @@
** the database file containing the source table. The <callback> option
** is optional. If included, it is the name of an application-defined
** SQL function that is invoked with the URI of the file, if the file
** does not already exist on disk.
** does not already exist on disk when required by swarmvtab.
**
** NEW SYNTAX:
**
** Using the new syntax, a swarmvtab table is created with:
**
** CREATE VIRTUAL TABLE <name> USING swarmvtab(
** <sql-statement> [, <options>]
** );
**
** where valid <options> are:
**
** missing=<udf-function-name>
** openclose=<udf-function-name>
** maxopen=<integer>
** <sql-parameter>=<text-value>
**
** The <sql-statement> must return the same 4 columns as for a swarmvtab
** table in legacy mode. However, it may also return a 5th column - the
** "context" column. The text value returned in this column is not used
** at all by the swarmvtab implementation, except that it is passed as
** an additional argument to the two UDF functions that may be invoked
** (see below).
**
** The "missing" option, if present, specifies the name of an SQL UDF
** function to be invoked if a database file is not already present on
** disk when required by swarmvtab. If the <sql-statement> did not provide
** a context column, it is invoked as:
**
** SELECT <missing-udf>(<database filename/uri>);
**
** Or, if there was a context column:
**
** SELECT <missing-udf>(<database filename/uri>, <context>);
**
** The "openclose" option may also specify a UDF function. This function
** is invoked right before swarmvtab opens a database, and right after
** it closes one. The first argument - or first two arguments, if
** <sql-statement> supplied the context column - is the same as for
** the "missing" UDF. Following this, the UDF is passed integer value
** 0 before a db is opened, and 1 right after it is closed. If both
** a missing and openclose UDF is supplied, the application should expect
** the following sequence of calls (for a single database):
**
** SELECT <openclose-udf>(<db filename>, <context>, 0);
** if( db not already on disk ){
** SELECT <missing-udf>(<db filename>, <context>);
** }
** ... swarmvtab uses database ...
** SELECT <openclose-udf>(<db filename>, <context>, 1);
**
** The "maxopen" option is used to configure the maximum number of
** database files swarmvtab will hold open simultaneously (default 9).
**
** If an option name begins with a ":" character, then it is assumed
** to be an SQL parameter. In this case, the specified text value is
** bound to the same variable of the <sql-statement> before it is
** executed. It is an error of the named SQL parameter does not exist.
** For example:
**
** CREATE VIRTUAL TABLE swarm USING swarmvtab(
** 'SELECT :path || localfile, tbl, min, max FROM swarmdir',
** :path='/home/user/databases/'
** missing='missing_func'
** );
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
@ -128,6 +195,7 @@ struct UnionSrc {
/* Fields used by swarmvtab only */
char *zFile; /* Database file containing table zTab */
char *zContext; /* Context string, if any */
int nUser; /* Current number of users */
sqlite3 *db; /* Database handle */
UnionSrc *pNextClosable; /* Next in list of closable sources */
@ -145,8 +213,11 @@ struct UnionTab {
UnionSrc *aSrc; /* Array of source tables, sorted by rowid */
/* Used by swarmvtab only */
int bHasContext; /* Has context strings */
char *zSourceStr; /* Expected unionSourceToStr() value */
char *zNotFoundCallback; /* UDF to invoke if file not found on open */
sqlite3_stmt *pNotFound; /* UDF to invoke if file not found on open */
sqlite3_stmt *pOpenClose; /* UDF to invoke on open and close */
UnionSrc *pClosable; /* First in list of closable sources */
int nOpen; /* Current number of open sources */
int nMaxOpen; /* Maximum number of open sources */
@ -351,6 +422,39 @@ static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
}
}
/*
** If an "openclose" UDF was supplied when this virtual table was created,
** invoke it now. The first argument passed is the name of the database
** file for source pSrc. The second is integer value bClose.
**
** If successful, return SQLITE_OK. Otherwise an SQLite error code. In this
** case if argument pzErr is not NULL, also set (*pzErr) to an English
** language error message. The caller is responsible for eventually freeing
** any error message using sqlite3_free().
*/
static int unionInvokeOpenClose(
UnionTab *pTab,
UnionSrc *pSrc,
int bClose,
char **pzErr
){
int rc = SQLITE_OK;
if( pTab->pOpenClose ){
sqlite3_bind_text(pTab->pOpenClose, 1, pSrc->zFile, -1, SQLITE_STATIC);
if( pTab->bHasContext ){
sqlite3_bind_text(pTab->pOpenClose, 2, pSrc->zContext, -1, SQLITE_STATIC);
}
sqlite3_bind_int(pTab->pOpenClose, 2+pTab->bHasContext, bClose);
sqlite3_step(pTab->pOpenClose);
if( SQLITE_OK!=(rc = sqlite3_reset(pTab->pOpenClose)) ){
if( pzErr ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
}
}
return rc;
}
/*
** This function is a no-op for unionvtab. For swarmvtab, it attempts to
** close open database files until at most nMax are open. An SQLite error
@ -358,13 +462,16 @@ static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
*/
static void unionCloseSources(UnionTab *pTab, int nMax){
while( pTab->pClosable && pTab->nOpen>nMax ){
UnionSrc *p;
UnionSrc **pp;
for(pp=&pTab->pClosable; (*pp)->pNextClosable; pp=&(*pp)->pNextClosable);
assert( (*pp)->db );
sqlite3_close((*pp)->db);
(*pp)->db = 0;
p = *pp;
assert( p->db );
sqlite3_close(p->db);
p->db = 0;
*pp = 0;
pTab->nOpen--;
unionInvokeOpenClose(pTab, p, 1, 0);
}
}
@ -377,13 +484,18 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
int i;
for(i=0; i<pTab->nSrc; i++){
UnionSrc *pSrc = &pTab->aSrc[i];
if( pSrc->db ){
unionInvokeOpenClose(pTab, pSrc, 1, 0);
}
sqlite3_free(pSrc->zDb);
sqlite3_free(pSrc->zTab);
sqlite3_free(pSrc->zFile);
sqlite3_free(pSrc->zContext);
sqlite3_close(pSrc->db);
}
sqlite3_finalize(pTab->pNotFound);
sqlite3_finalize(pTab->pOpenClose);
sqlite3_free(pTab->zSourceStr);
sqlite3_free(pTab->zNotFoundCallback);
sqlite3_free(pTab->aSrc);
sqlite3_free(pTab);
}
@ -496,29 +608,31 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){
return rc;
}
/*
** Try to open the swarmvtab database. If initially unable, invoke the
** not-found callback UDF and then try again.
*/
static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){
int rc = SQLITE_OK;
static const int openFlags =
SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
static const int openFlags = SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
int rc;
rc = unionInvokeOpenClose(pTab, pSrc, 0, pzErr);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
if( rc==SQLITE_OK ) return rc;
if( pTab->zNotFoundCallback ){
char *zSql = sqlite3_mprintf("SELECT \"%w\"(%Q);",
pTab->zNotFoundCallback, pSrc->zFile);
if( pTab->pNotFound ){
sqlite3_close(pSrc->db);
pSrc->db = 0;
if( zSql==0 ){
*pzErr = sqlite3_mprintf("out of memory");
return SQLITE_NOMEM;
sqlite3_bind_text(pTab->pNotFound, 1, pSrc->zFile, -1, SQLITE_STATIC);
if( pTab->bHasContext ){
sqlite3_bind_text(pTab->pNotFound, 2, pSrc->zContext, -1, SQLITE_STATIC);
}
sqlite3_step(pTab->pNotFound);
if( SQLITE_OK!=(rc = sqlite3_reset(pTab->pNotFound)) ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
return rc;
}
rc = sqlite3_exec(pTab->db, zSql, 0, 0, pzErr);
sqlite3_free(zSql);
if( rc ) return rc;
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
}
if( rc!=SQLITE_OK ){
@ -572,6 +686,7 @@ static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){
}else{
sqlite3_close(pSrc->db);
pSrc->db = 0;
unionInvokeOpenClose(pTab, pSrc, 1, 0);
}
}
@ -627,6 +742,132 @@ static int unionFinalizeCsrStmt(UnionCsr *pCsr){
return rc;
}
/*
** Return true if the argument is a space, tab, CR or LF character.
*/
static int union_isspace(char c){
return (c==' ' || c=='\n' || c=='\r' || c=='\t');
}
/*
** Return true if the argument is an alphanumeric character in the
** ASCII range.
*/
static int union_isidchar(char c){
return ((c>='a' && c<='z') || (c>='A' && c<'Z') || (c>='0' && c<='9'));
}
/*
** This function is called to handle all arguments following the first
** (the SQL statement) passed to a swarmvtab (not unionvtab) CREATE
** VIRTUAL TABLE statement. It may bind parameters to the SQL statement
** or configure members of the UnionTab object passed as the second
** argument.
**
** Refer to header comments at the top of this file for a description
** of the arguments parsed.
**
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, if an error occurs, *pRc is set to an SQLite error
** code. In this case *pzErr may be set to point to a buffer containing
** an English language error message. It is the responsibility of the
** caller to eventually free the buffer using sqlite3_free().
*/
static void unionConfigureVtab(
int *pRc, /* IN/OUT: Error code */
UnionTab *pTab, /* Table to configure */
sqlite3_stmt *pStmt, /* SQL statement to find sources */
int nArg, /* Number of entries in azArg[] array */
const char * const *azArg, /* Array of arguments to consider */
char **pzErr /* OUT: Error message */
){
int rc = *pRc;
int i;
if( rc==SQLITE_OK ){
pTab->bHasContext = (sqlite3_column_count(pStmt)>4);
}
for(i=0; rc==SQLITE_OK && i<nArg; i++){
char *zArg = unionStrdup(&rc, azArg[i]);
if( zArg ){
int nOpt = 0; /* Size of option name in bytes */
char *zOpt; /* Pointer to option name */
char *zVal; /* Pointer to value */
unionDequote(zArg);
zOpt = zArg;
while( union_isspace(*zOpt) ) zOpt++;
zVal = zOpt;
if( *zVal==':' ) zVal++;
while( union_isidchar(*zVal) ) zVal++;
nOpt = zVal-zOpt;
while( union_isspace(*zVal) ) zVal++;
if( *zVal=='=' ){
zOpt[nOpt] = '\0';
zVal++;
while( union_isspace(*zVal) ) zVal++;
zVal = unionStrdup(&rc, zVal);
if( zVal ){
unionDequote(zVal);
if( zOpt[0]==':' ){
/* A value to bind to the SQL statement */
int iParam = sqlite3_bind_parameter_index(pStmt, zOpt);
if( iParam==0 ){
*pzErr = sqlite3_mprintf(
"swarmvtab: no such SQL parameter: %s", zOpt
);
rc = SQLITE_ERROR;
}else{
rc = sqlite3_bind_text(pStmt, iParam, zVal, -1, SQLITE_TRANSIENT);
}
}else if( nOpt==7 && 0==sqlite3_strnicmp(zOpt, "maxopen", 7) ){
pTab->nMaxOpen = atoi(zVal);
if( pTab->nMaxOpen<=0 ){
*pzErr = sqlite3_mprintf("swarmvtab: illegal maxopen value");
rc = SQLITE_ERROR;
}
}else if( nOpt==7 && 0==sqlite3_strnicmp(zOpt, "missing", 7) ){
if( pTab->pNotFound ){
*pzErr = sqlite3_mprintf(
"swarmvtab: duplicate \"missing\" option");
rc = SQLITE_ERROR;
}else{
pTab->pNotFound = unionPreparePrintf(&rc, pzErr, pTab->db,
"SELECT \"%w\"(?%s)", zVal, pTab->bHasContext ? ",?" : ""
);
}
}else if( nOpt==9 && 0==sqlite3_strnicmp(zOpt, "openclose", 9) ){
if( pTab->pOpenClose ){
*pzErr = sqlite3_mprintf(
"swarmvtab: duplicate \"openclose\" option");
rc = SQLITE_ERROR;
}else{
pTab->pOpenClose = unionPreparePrintf(&rc, pzErr, pTab->db,
"SELECT \"%w\"(?,?%s)", zVal, pTab->bHasContext ? ",?" : ""
);
}
}else{
*pzErr = sqlite3_mprintf("swarmvtab: unrecognized option: %s",zOpt);
rc = SQLITE_ERROR;
}
sqlite3_free(zVal);
}
}else{
if( i==0 && nArg==1 ){
pTab->pNotFound = unionPreparePrintf(&rc, pzErr, pTab->db,
"SELECT \"%w\"(?)", zArg
);
}else{
*pzErr = sqlite3_mprintf( "swarmvtab: parse error: %s", azArg[i]);
rc = SQLITE_ERROR;
}
}
sqlite3_free(zArg);
}
}
*pRc = rc;
}
/*
** xConnect/xCreate method.
**
@ -654,7 +895,7 @@ static int unionConnect(
/* unionvtab tables may only be created in the temp schema */
*pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab);
rc = SQLITE_ERROR;
}else if( argc!=4 && argc!=5 ){
}else if( argc<4 || (argc>4 && bSwarm==0) ){
*pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab);
rc = SQLITE_ERROR;
}else{
@ -673,6 +914,17 @@ static int unionConnect(
/* Allocate the UnionTab structure */
pTab = unionMalloc(&rc, sizeof(UnionTab));
if( pTab ){
assert( rc==SQLITE_OK );
pTab->db = db;
pTab->bSwarm = bSwarm;
pTab->nMaxOpen = SWARMVTAB_MAX_OPEN;
}
/* Parse other CVT arguments, if any */
if( bSwarm ){
unionConfigureVtab(&rc, pTab, pStmt, argc-4, &argv[4], pzErr);
}
/* Iterate through the rows returned by the SQL statement specified
** as an argument to the CREATE VIRTUAL TABLE statement. */
@ -715,17 +967,15 @@ static int unionConnect(
}else{
pSrc->zDb = unionStrdup(&rc, zDb);
}
if( pTab->bHasContext ){
const char *zContext = (const char*)sqlite3_column_text(pStmt, 4);
pSrc->zContext = unionStrdup(&rc, zContext);
}
}
}
unionFinalize(&rc, pStmt, pzErr);
pStmt = 0;
/* Capture the not-found callback UDF name */
if( rc==SQLITE_OK && argc>=5 ){
pTab->zNotFoundCallback = unionStrdup(&rc, argv[4]);
unionDequote(pTab->zNotFoundCallback);
}
/* It is an error if the SELECT statement returned zero rows. If only
** because there is no way to determine the schema of the virtual
** table in this case. */
@ -738,9 +988,6 @@ static int unionConnect(
** compatible schemas. For swarmvtab, attach the first database and
** check that the first table is a rowid table only. */
if( rc==SQLITE_OK ){
pTab->db = db;
pTab->bSwarm = bSwarm;
pTab->nMaxOpen = SWARMVTAB_MAX_OPEN;
if( bSwarm ){
rc = unionOpenDatabase(pTab, 0, pzErr);
}else{

View File

@ -2021,7 +2021,7 @@ static int ChooseLeaf(
){
int rc;
int ii;
RtreeNode *pNode;
RtreeNode *pNode = 0;
rc = nodeAcquire(pRtree, 1, 0, &pNode);
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
@ -2896,7 +2896,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
*/
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
int rc2;
RtreeNode *pChild;
RtreeNode *pChild = 0;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
if( rc==SQLITE_OK ){

10
main.mk
View File

@ -297,6 +297,8 @@ SRC += \
# Source code to the test files.
#
TESTSRC = \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/rbu/test_rbu.c \
@ -691,7 +693,9 @@ SHELL_SRC = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/misc/shathree.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/completion.c
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
tclsh $(TOP)/tool/mkshellc.tcl >shell.c
@ -816,6 +820,9 @@ sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext
sqltclsh$(EXE): sqltclsh.c
$(TCCX) $(TCL_FLAGS) sqltclsh.c -o $@ $(LIBTCL) $(THREADLIB)
sqlite3_expert$(EXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
$(TCCX) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert$(EXE) $(THREADLIB)
CHECKER_DEPS =\
$(TOP)/tool/mkccode.tcl \
sqlite3.c \
@ -1042,6 +1049,7 @@ clean:
rm -f sqlite3rc.h
rm -f shell.c sqlite3ext.h
rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
rm -f sqlite3_expert sqlite3_expert.exe
rm -f sqlite-*-output.vsix
rm -f mptester mptester.exe
rm -f fuzzershell fuzzershell.exe

View File

@ -1,8 +1,8 @@
C Fixes\sto\sthe\sappendvfs.c\sextension.\s\sAdd\sthe\s"sqltclsh"\sapplication\sthat\nuses\sappendvfs.c\sto\sfind\sits\sscripts.
D 2017-12-14T19:24:00.444
F Makefile.in 053284f237e955fba2f386fd9f87020199e6dbcbce9f48bc02cf431458afbe07
C Merge\srecent\senhancements\sfrom\strunk.
D 2017-12-23T18:40:39.094
F Makefile.in f2dc8c140e1d728157834da295eaaa8a0cb29620595c2a9f0efc7258797e6f24
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc a2492b29176edc3c754aa7a2f7daa20cd3fa20a56e3ee64e376092836177c42a
F Makefile.msc 6480671f7c129e61208d69492b3c71ce4310d49fceac83cfb17f1c081e242b69
F README.md eeae1e552f93ef72ef7c5b8f6647b368a001c28820ad1df179d3dae602bef681
F VERSION 0c10cdfed866fdd2d80434f64f042c3330f1daaed12e54287beb104f04b3faaf
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@ -40,6 +40,12 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c 4791c5e064aea81b2b829fa95228b22283380ee370ea88a1e580103b75516ebf
F ext/expert/expert1.test 0c71a3453ce3a0b4dbe952713aec0ae8d416dd846820dd027b08f305f5278b30
F ext/expert/sqlite3expert.c 252f3129f12a0e9df094a14711db98265c9c6d7afa033ec906d94e920f5c7ba7
F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811
F ext/expert/test_expert.c 85f5c743a899063fa48296d21de2f32c26d09a21c8582b2a0bc482e8de183e7a
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@ -283,12 +289,12 @@ F ext/misc/rot13.c 540a169cb0d74f15522a8930b0cccdcb37a4fd071d219a5a083a319fc6e8d
F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb
F ext/misc/series.c f3c0dba5c5c749ce1782b53076108f87cf0b71041eb6023f727a9c50681da564
F ext/misc/sha1.c 0b9e9b855354910d3ca467bf39099d570e73db56
F ext/misc/shathree.c fa185d7aee0ad0aca5e091b4a2db7baff11796170e5793b5de99e511a13af448
F ext/misc/shathree.c 9e960ba50483214c6a7a4b1517f8d8cef799e9db381195178c3fd3ad207e10c0
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 41cf26c6b89fcaa8798ae10ae64d39c1f1d9d6995152e545bd491c13058b8fac
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/unionvtab.c 1e0ebc5078e1a916db191bcd88f87e94ea7ba4aa563ee30ff706261cb4b39461
F ext/misc/unionvtab.c de36c2c45583d68f99e45b392311967066b02e2651d05697da783698b245b387
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
@ -339,7 +345,7 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c cc91b6905bf55512c6ebc7dfdd37ac81c86f1753db8cfa6d62f0ee864464044f
F ext/rtree/rtree.c 2111f685ae07988622c241f819b56fea60782f56e32f97e334473c59f6083481
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 82a353747fcab1083d114b2ac84723dfefdbf86c1a6e1df57bf588c7d4285436
F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2
@ -396,7 +402,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk d0278b8833203dc60ed81eed12412d90559fcd3470e0fd5d509d41ef74a3d64b
F main.mk 992bddc5dc2f37faac929cabc367a89304436566eb385ab89e83d94f4107ad69
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@ -418,7 +424,7 @@ F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
F src/btree.c b83a6b03f160528020bb965f0c3a40af5286cd4923c3870fd218177f03a120a7
F src/btree.h 32ef5d3f25dc70ef1ee9cecf84a023c21378f06a57cd701d2e866e141b150f09
F src/btreeInt.h 55b702efce17e5d1941865464227d3802cfc9c7c832fac81d4c94dced47a71fc
F src/build.c 87b68e3b45559ec404b12f095f0ba5f06f91a6dd2d21bd8443e41d8ac2e67196
F src/build.c ed567f088edbc305dad33a6b14e08f8216a3860f6bad1d180450d5a5414bf346
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
@ -437,8 +443,8 @@ F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c cb67cc56ef2ddd13e6944b2c0dd08a920bcd9503230adef8b9928d338097c722
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c 20865b183bb8a3723d59cf1efffc3c50217eb452c1021d077b908c94da26b0b2
F src/main.c 4f94536a61dc77477e1cee7ecfae2896778c1a3d3de4f978db28f8b9220f6e52
F src/loadext.c 55bcc3c741059a1056859e8adaf133aa179e22be12215c0936b2f354ef71209b
F src/main.c 7ce55fa3c0bf669944de309ebab1655ed06ec67869adb0372c7a1062e461c448
F src/malloc.c a02c9e69bc76bee0f639416b947a946412890b606301454727feadcb313536d6
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@ -466,19 +472,19 @@ F src/parse.y d79001da275bfe344b409006b85e81e486a0f6afc3762fdf0944f000f4aa0111
F src/pcache.c 7ae91a4557a43d77d449accbfdc68846e6516f8e2eda46e8bbe4536fb669b201
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
F src/pragma.c d04725ac25387d9638919e197fb009f378e13af7bf899516979e54b3164e3602
F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
F src/prepare.c 259f4e7960c47082c9653f3d5f0c294abd68bb9c3aab86de7630700cba1c20fb
F src/printf.c 9506b4b96e59c0467047155f09015750cb2878aeda3d39e5610c1192ddc3c41c
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 17e220191860a64a18c084141e1a8b7309e166a6f2d42c02021af27ea080d157
F src/shell.c.in 6ffed0c589f5aff180789a8c8abf5b2d3e2eea7470c86b30e797887cb0c9d0e5
F src/sqlite.h.in 364515dd186285f3c01f5cab42e7db7edc47c70e87b6a25de389a2e6b8c413fd
F src/select.c 8b22abe193e4d8243befa2038e4ae2405802fed1c446e5e502d11f652e09ba74
F src/shell.c.in 339169a3d1307b5566ebe9ce15832d03439206106724c78cc3d9125a7b851795
F src/sqlite.h.in 2126192945019d4cdce335cb236b440a05ec75c93e4cd94c9c6d6e7fcc654cc4
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
F src/sqliteInt.h 55b8e7da85947eb61b13d4d2523ccdda7800a13e987c3fc4ca73d8518bbf02fa
F src/sqliteInt.h 003b78433baae4e5c997f99f2f9cf98d90754f256baeacb32f8189569a48251f
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -526,7 +532,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
F src/test_tclsh.c 74fcfb7f3b0ff1f871e62263dd84ffba46a8e9d477439115e0fb2035e4bf69e1
F src/test_tclsh.c 58052fe48efe8f579834f4648d239569f2efc6285f5019ebdf0040f58d16238d
F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
@ -536,7 +542,7 @@ F src/test_windirent.h 5d67483a55442e31e1bde0f4a230e6e932ad5906
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
F src/treeview.c 08a83195de8fad3f00542e3c8b3c1eb1222c999817c9e301ffb7f332882b96dd
F src/treeview.c eae35972ff44f67064de2eaf35f04afe94e7aea3271a8b3bcebb3f954880fec3
F src/trigger.c 775053eecf6b73062e243404b56f5064446254d5cce17d8704d5cdffd72a546a
F src/update.c 961bd1265d4d1e5cd65c9a54fa5122fb7aefcb003fcf2de0c092fceb7e58972c
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
@ -546,7 +552,7 @@ F src/vdbe.c 3393b508d9ad084ffce232a7c53e375ef5ac99b50b685c5131fcdfce97a9d534
F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
F src/vdbeInt.h 1fe00770144c12c4913128f35262d11527ef3284561baaab59b947a41c08d0d9
F src/vdbeapi.c 9c670ca0dcc1cd86373aa353b747b26fe531ca5cd4331690c611d1f03842e2a1
F src/vdbeaux.c b02a1f842c0e916285643b8475b7189f10b76f9e7edb5e2353a913c7980f90b5
F src/vdbeaux.c 7ae48b180e5dd5d282e6752d155f1ab7929196d8e6577b82742044188152ca85
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
F src/vdbemem.c 8478f7fb1948bf8fdeec7c2cb59ea58155c31258b9cd43c56d485e03ed40bd07
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
@ -556,9 +562,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 5a3f464edd64596f601683ed321d12e6fd93c5fb9afdfb3653d6ffd0fee9c48f
F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
F src/where.c ee9dd4a438a07cd364c8449e834db4c4d6163a2576a69e937d7a4c37685612a2
F src/where.c 5876c9100b622f7b9e5ee7f579b8b6a71ae5ba627724cea4546d9114c32b3cb5
F src/whereInt.h 82c04c5075308abbac59180c8bad5ecb45b07453981f60a53f3c7dee21e1e971
F src/wherecode.c ff2f079097a3bdce6ebabfde1419fba448c9ce5feb7cb964e8bfa2a4e27274ef
F src/wherecode.c af1e79154aaa88cd802d6f2e5b945f67eaca7c958d1525fbf8ee19d5bd7b9020
F src/whereexpr.c 427ea8e96ec24f2a7814c67b8024ad664a9c7656264c4566c34743cb23186e46
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@ -677,7 +683,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test c47639d26cbeba6977457e5ef2c2c55c5b6c889478dd7eb0ed858ba894e7fa93
F test/colname.test a7ecb8f1d6d8b30a6cf8fa84a2cd6f6e91cad8296376fabe485cf93cd5eb6229
F test/conflict.test 029faa2d81a0d1cafb5f88614beb663d972c01db
F test/conflict2.test bb0b94cf7196c64a3cbd815c66d3ee98c2fecd9c
F test/conflict3.test a83db76a6c3503b2fa057c7bfb08c318d8a422202d8bc5b86226e078e5b49ff9
@ -1116,7 +1122,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 490e3333b9b1aefb7ebc6e9ab2ae0e382b7dd8713ccc4a2786b0f75467c2ab6b
F test/permutations.test 8ada8c1dee071e0fc275bc8bc2db7de537d625cad949d2200664b99a0a89eac5
F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@ -1261,8 +1267,9 @@ F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
F test/swarmvtab.test c2279311b44de032f86a8295a9b06818d864856f9428b4c99eee91a0d419cf25
F test/swarmvtab2.test 9a3a68a1e58d00f4ed6c68d12d52f2df971b9e22a80a41f6f8c1409abba8e5b4
F test/swarmvtab.test 9a3fd5ab3e9b3c976ad1b3d7646aab725114f2ac26b59395d0778b33bab6cdaf
F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27cbd0aa5c
F test/swarmvtab3.test c4c8d09e56ae99b90187ac225458f13f373873ea296fc442c7ad7511f25e7314
F test/swarmvtabfault.test 00aec54665909490f5c383f3cae3b5d18bd97c12490b429ff8752a3027acfa42
F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
@ -1281,7 +1288,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 9948bd856ce8a1c127f2f7900365387a42a917ce0dc87185bdd128fa5b11aff2
F test/tester.tcl 3ed81b9e1d9718a8d9603596c8a877793d054294053c4277a3d3897eabab3866
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@ -1603,7 +1610,7 @@ F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
F tool/lemon.c e6056373044d55296d21f81467dba7632bbb81dc49af072b3f0e76338771497e
F tool/lempar.c 105d0d9cbe5a25d24d4769241ffbfc63ac7c09e6ccee0dc43dcc8a4c4ae4e426
F tool/lempar.c 967ebf585cd09b11b89d255d213865109a9c4ff075680d22580a2826de288c89
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
@ -1683,7 +1690,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 7f7b72d83633922e2b5dbf0d3455d0fea72cc6b8925ffcb78dfbad7b3c4b26e9
R 4666f6f48678fbbcc815c221172efa48
P ee248b529c2396c5480fb99b0a1dc31032627ec8241eca4a8c0fff257bb4a088 07c773148d8db185fa54991df09298b64f4fef28879e6c9395759265e8183977
R 38880472a25b77bfdeb87344d5313674
U drh
Z 47302a3d5371e2c5e325f342d15c1218
Z 5274401ef77d35314ed9b1a10cd6e401

View File

@ -1 +1 @@
ee248b529c2396c5480fb99b0a1dc31032627ec8241eca4a8c0fff257bb4a088
edceaccd66a65d6b36e53ce33d760a7bd9c2261a592d12189f5f55417b5d5d74

View File

@ -1965,11 +1965,6 @@ void sqlite3EndTable(
pParse->nTab = 2;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
sqlite3Select(pParse, pSelect, &dest);
sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1);
if( pParse->nErr ) return;
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
if( pSelTab==0 ) return;
assert( p->aCol==0 );
@ -1978,6 +1973,10 @@ void sqlite3EndTable(
pSelTab->nCol = 0;
pSelTab->aCol = 0;
sqlite3DeleteTable(db, pSelTab);
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
sqlite3Select(pParse, pSelect, &dest);
sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1);
addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);

View File

@ -496,8 +496,10 @@ static int sqlite3LoadExtension(
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
int bExists = 0;
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
handle = sqlite3OsDlOpen(pVfs, zAltFile);
sqlite3OsAccess(pVfs, zAltFile, SQLITE_ACCESS_EXISTS, &bExists);
if( bExists ) handle = sqlite3OsDlOpen(pVfs, zAltFile);
sqlite3_free(zAltFile);
}
#endif

View File

@ -817,6 +817,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
{ SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG },
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */

View File

@ -1080,6 +1080,7 @@ void sqlite3Pragma(
** type: Column declaration type.
** notnull: True if 'NOT NULL' is part of column declaration
** dflt_value: The default value for the column, if any.
** pk: Non-zero for PK fields.
*/
case PragTyp_TABLE_INFO: if( zRight ){
Table *pTab;

View File

@ -1381,8 +1381,9 @@ static const char *columnTypeImpl(
assert( pExpr!=0 );
assert( pNC->pSrcList!=0 );
assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
** are processed */
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
/* The expression is a column. Locate the table the column is being
** extracted from in NameContext.pSrcList. This table may be real
@ -1391,8 +1392,6 @@ static const char *columnTypeImpl(
Table *pTab = 0; /* Table structure column is extracted from */
Select *pS = 0; /* Select the column is extracted from */
int iCol = pExpr->iColumn; /* Index of column in pTab */
testcase( pExpr->op==TK_AGG_COLUMN );
testcase( pExpr->op==TK_COLUMN );
while( pNC && !pTab ){
SrcList *pTabList = pNC->pSrcList;
for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++);
@ -1596,6 +1595,7 @@ static void generateColumnNames(
if( pParse->colNamesSet || db->mallocFailed ) return;
/* Column names are determined by the left-most term of a compound select */
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
pTabList = pSelect->pSrc;
pEList = pSelect->pEList;
assert( v!=0 );
@ -1704,12 +1704,12 @@ int sqlite3ColumnsFromExprList(
pColExpr = pColExpr->pRight;
assert( pColExpr!=0 );
}
if( (pColExpr->op==TK_COLUMN || pColExpr->op==TK_AGG_COLUMN)
&& pColExpr->pTab!=0
){
assert( pColExpr->op!=TK_AGG_COLUMN );
if( pColExpr->op==TK_COLUMN ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
Table *pTab = pColExpr->pTab;
assert( pTab!=0 );
if( iCol<0 ) iCol = pTab->iPKey;
zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
}else if( pColExpr->op==TK_ID ){

View File

@ -796,6 +796,8 @@ static void shellAddSchemaName(
INCLUDE ../ext/misc/shathree.c
INCLUDE ../ext/misc/fileio.c
INCLUDE ../ext/misc/completion.c
INCLUDE ../ext/expert/sqlite3expert.h
INCLUDE ../ext/expert/sqlite3expert.c
#if defined(SQLITE_ENABLE_SESSION)
/*
@ -822,6 +824,12 @@ struct SavedModeInfo {
int colWidth[100]; /* Column widths prior to ".explain on" */
};
typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
sqlite3expert *pExpert;
int bVerbose;
};
/*
** State information about the database connection is contained in an
** instance of the following structure.
@ -866,8 +874,16 @@ struct ShellState {
int nSession; /* Number of active sessions */
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
#endif
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
};
/* Allowed values for ShellState.autoEQP
*/
#define AUTOEQP_off 0
#define AUTOEQP_on 1
#define AUTOEQP_trigger 2
#define AUTOEQP_full 3
/*
** These are the allowed shellFlgs values
*/
@ -2249,6 +2265,79 @@ static void exec_prepared_stmt(
}
}
/*
** This function is called to process SQL if the previous shell command
** was ".expert". It passes the SQL in the second argument directly to
** the sqlite3expert object.
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
** code. In this case, (*pzErr) may be set to point to a buffer containing
** an English language error message. It is the responsibility of the
** caller to eventually free this buffer using sqlite3_free().
*/
static int expertHandleSQL(
ShellState *pState,
const char *zSql,
char **pzErr
){
assert( pState->expert.pExpert );
assert( pzErr==0 || *pzErr==0 );
return sqlite3_expert_sql(pState->expert.pExpert, zSql, pzErr);
}
/*
** This function is called either to silently clean up the object
** created by the ".expert" command (if bCancel==1), or to generate a
** report from it and then clean it up (if bCancel==0).
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
** code. In this case, (*pzErr) may be set to point to a buffer containing
** an English language error message. It is the responsibility of the
** caller to eventually free this buffer using sqlite3_free().
*/
static int expertFinish(
ShellState *pState,
int bCancel,
char **pzErr
){
int rc = SQLITE_OK;
sqlite3expert *p = pState->expert.pExpert;
assert( p );
assert( bCancel || pzErr==0 || *pzErr==0 );
if( bCancel==0 ){
FILE *out = pState->out;
int bVerbose = pState->expert.bVerbose;
rc = sqlite3_expert_analyze(p, pzErr);
if( rc==SQLITE_OK ){
int nQuery = sqlite3_expert_count(p);
int i;
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
raw_printf(out, "-- Candidates -----------------------------\n");
raw_printf(out, "%s\n", zCand);
}
for(i=0; i<nQuery; i++){
const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES);
const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
if( zIdx==0 ) zIdx = "(no new indexes)\n";
if( bVerbose ){
raw_printf(out, "-- Query %d --------------------------------\n",i+1);
raw_printf(out, "%s\n\n", zSql);
}
raw_printf(out, "%s\n", zIdx);
raw_printf(out, "%s\n", zEQP);
}
}
}
sqlite3_expert_destroy(p);
pState->expert.pExpert = 0;
return rc;
}
/*
** Execute a statement or set of statements. Print
** any result rows/columns depending on the current mode
@ -2275,6 +2364,11 @@ static int shell_exec(
*pzErrMsg = NULL;
}
if( pArg->expert.pExpert ){
rc = expertHandleSQL(pArg, zSql, pzErrMsg);
return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
}
while( zSql[0] && (SQLITE_OK == rc) ){
static const char *zStmtSql;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
@ -2308,7 +2402,12 @@ static int shell_exec(
if( pArg && pArg->autoEQP && sqlite3_strlike("EXPLAIN%",zStmtSql,0)!=0 ){
sqlite3_stmt *pExplain;
char *zEQP;
int triggerEQP = 0;
disable_debug_trace_modes();
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
if( pArg->autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql);
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc==SQLITE_OK ){
@ -2321,7 +2420,7 @@ static int shell_exec(
}
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
if( pArg->autoEQP>=2 ){
if( pArg->autoEQP>=AUTOEQP_full ){
/* Also do an EXPLAIN for ".eqp full" mode */
zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql);
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
@ -2334,6 +2433,7 @@ static int shell_exec(
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
}
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, triggerEQP, 0);
restore_debug_trace_modes();
}
@ -2692,6 +2792,7 @@ static char zHelp[] =
".echo on|off Turn command echo on or off\n"
".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n"
".exit Exit this program\n"
".expert EXPERIMENTAL. Suggest indexes for specified queries\n"
/* Because explain mode comes on automatically now, the ".explain" mode
** is removed from the help screen. It is still supported for legacy, however */
/*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"*/
@ -4068,6 +4169,64 @@ static int lintDotCommand(
return SQLITE_ERROR;
}
/*
** Implementation of ".expert" dot command.
*/
static int expertDotCommand(
ShellState *pState, /* Current shell tool state */
char **azArg, /* Array of arguments passed to dot command */
int nArg /* Number of entries in azArg[] */
){
int rc = SQLITE_OK;
char *zErr = 0;
int i;
int iSample = 0;
assert( pState->expert.pExpert==0 );
memset(&pState->expert, 0, sizeof(ExpertInfo));
for(i=1; rc==SQLITE_OK && i<nArg; i++){
char *z = azArg[i];
int n;
if( z[0]=='-' && z[1]=='-' ) z++;
n = strlen(z);
if( n>=2 && 0==strncmp(z, "-verbose", n) ){
pState->expert.bVerbose = 1;
}
else if( n>=2 && 0==strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
raw_printf(stderr, "option requires an argument: %s\n", z);
rc = SQLITE_ERROR;
}else{
iSample = (int)integerValue(azArg[++i]);
if( iSample<0 || iSample>100 ){
raw_printf(stderr, "value out of range: %s\n", azArg[i]);
rc = SQLITE_ERROR;
}
}
}
else{
raw_printf(stderr, "unknown option: %s\n", z);
rc = SQLITE_ERROR;
}
}
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr);
rc = SQLITE_ERROR;
}else{
sqlite3_expert_config(
pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample
);
}
}
return rc;
}
/*
** If an input line begins with "." then invoke this routine to
@ -4082,6 +4241,10 @@ static int do_meta_command(char *zLine, ShellState *p){
int rc = 0;
char *azArg[50];
if( p->expert.pExpert ){
expertFinish(p, 1, 0);
}
/* Parse the input line into tokens.
*/
while( zLine[h] && nArg<ArraySize(azArg) ){
@ -4397,12 +4560,14 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
if( strcmp(azArg[1],"full")==0 ){
p->autoEQP = 2;
p->autoEQP = AUTOEQP_full;
}else if( strcmp(azArg[1],"trigger")==0 ){
p->autoEQP = AUTOEQP_trigger;
}else{
p->autoEQP = booleanValue(azArg[1]);
}
}else{
raw_printf(stderr, "Usage: .eqp on|off|full\n");
raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n");
rc = 1;
}
}else
@ -4436,6 +4601,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){
open_db(p, 0);
expertDotCommand(p, azArg, nArg);
}else
if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){
ShellState data;
char *zErrMsg = 0;
@ -5750,7 +5920,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "full", "unk" };
static const char *azBool[] = { "off", "on", "trigger", "full"};
int i;
if( nArg!=1 ){
raw_printf(stderr, "Usage: .show\n");
@ -6902,9 +7072,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( strcmp(z,"-echo")==0 ){
ShellSetFlag(&data, SHFLG_Echo);
}else if( strcmp(z,"-eqp")==0 ){
data.autoEQP = 1;
data.autoEQP = AUTOEQP_on;
}else if( strcmp(z,"-eqpfull")==0 ){
data.autoEQP = 2;
data.autoEQP = AUTOEQP_full;
}else if( strcmp(z,"-stats")==0 ){
data.statsOn = 1;
}else if( strcmp(z,"-scanstats")==0 ){

View File

@ -2060,7 +2060,6 @@ struct sqlite3_mem_methods {
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
@ -2071,7 +2070,16 @@ struct sqlite3_mem_methods {
** the QPSG active, SQLite will always use the same query plan in the field as
** was used during testing in the lab.
** </dd>
**
** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
** behavior. The first parameter passed to this operation is an integer -
** non-zero to enable output for trigger programs, or zero to disable it.
** The second parameter is a pointer to an integer into which is written
** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
** it is not disabled, 1 if it is.
** </dd>
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@ -2082,7 +2090,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_MAX 1008 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@ -7033,7 +7042,7 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ISINIT 23
#define SQLITE_TESTCTRL_SORTER_MMAP 24
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_LAST 25
#define SQLITE_TESTCTRL_LAST 25 /* Largest TESTCTRL */
/*
** CAPI3REF: SQLite Runtime Status
@ -8287,6 +8296,21 @@ int sqlite3_vtab_config(sqlite3*, int op, ...);
*/
int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Determine The Collation For a Virtual Table Constraint
**
** This function may only be called from within a call to the [xBestIndex]
** method of a [virtual table implementation].
**
** The first argument must be the sqlite3_index_info object that is the
** first parameter to the xBestIndex() method. The second argument must be
** an index into the aConstraint[] array belonging to the sqlite3_index_info
** structure passed to xBestIndex. This function returns a pointer to a buffer
** containing the name of the collation sequence for the corresponding
** constraint.
*/
SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
/*
** CAPI3REF: Conflict resolution modes
** KEYWORDS: {conflict resolution mode}

View File

@ -1420,7 +1420,7 @@ struct sqlite3 {
Hash aModule; /* populated by sqlite3_create_module() */
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#endif
Hash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */
@ -1495,7 +1495,9 @@ struct sqlite3 {
#define SQLITE_QueryOnly 0x00100000 /* Disable database changes */
#define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */
#define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */
#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee */
#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee*/
#define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */

View File

@ -104,6 +104,8 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
#ifdef SQLITE_ENABLE_ZIPVFS
extern int Zipvfs_Init(Tcl_Interp*);
#endif
extern int TestExpert_Init(Tcl_Interp*);
Tcl_CmdInfo cmdInfo;
/* Since the primary use case for this binary is testing of SQLite,
@ -166,6 +168,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);
#endif
TestExpert_Init(interp);
Tcl_CreateObjCommand(
interp, "load_testfixture_extensions", load_testfixture_extensions,0,0

View File

@ -507,12 +507,20 @@ void sqlite3TreeViewBareExprList(
sqlite3TreeViewLine(pView, "%s", zLabel);
for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
if( j ){
char *zName = pList->a[i].zName;
if( j || zName ){
sqlite3TreeViewPush(pView, 0);
}
if( zName ){
sqlite3TreeViewLine(pView, "AS %s", zName);
}
if( j ){
sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
}
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
if( j ) sqlite3TreeViewPop(pView);
if( j || zName ){
sqlite3TreeViewPop(pView);
}
}
}
}

View File

@ -1639,6 +1639,8 @@ int sqlite3VdbeList(
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
Op *pOp = 0;
assert( p->explain );
assert( p->magic==VDBE_MAGIC_RUN );
@ -1651,7 +1653,7 @@ int sqlite3VdbeList(
releaseMemArray(pMem, 8);
p->pResultSet = 0;
if( p->rc==SQLITE_NOMEM_BKPT ){
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
sqlite3OomFault(db);
@ -1666,7 +1668,7 @@ int sqlite3VdbeList(
** encountered, but p->pc will eventually catch up to nRow.
*/
nRow = p->nOp;
if( p->explain==1 ){
if( bListSubprogs ){
/* The first 8 memory cells are used for the result set. So we will
** commandeer the 9th cell to use as storage for an array of pointers
** to trigger subprograms. The VDBE is guaranteed to have at least 9
@ -1686,17 +1688,11 @@ int sqlite3VdbeList(
do{
i = p->pc++;
}while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
if( i>=nRow ){
p->rc = SQLITE_OK;
rc = SQLITE_DONE;
}else if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
char *zP4;
Op *pOp;
if( i>=nRow ){
p->rc = SQLITE_OK;
rc = SQLITE_DONE;
break;
}
if( i<p->nOp ){
/* The output line number is small enough that we are still in the
** main program. */
@ -1711,94 +1707,110 @@ int sqlite3VdbeList(
}
pOp = &apSub[j]->aOp[i];
}
if( p->explain==1 ){
pMem->flags = MEM_Int;
pMem->u.i = i; /* Program counter */
pMem++;
pMem->flags = MEM_Static|MEM_Str|MEM_Term;
pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
pMem++;
/* When an OP_Program opcode is encounter (the only opcode that has
** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
** kept in p->aMem[9].z to hold the new program - assuming this subprogram
** has not already been seen.
*/
if( pOp->p4type==P4_SUBPROGRAM ){
int nByte = (nSub+1)*sizeof(SubProgram*);
int j;
for(j=0; j<nSub; j++){
if( apSub[j]==pOp->p4.pProgram ) break;
}
if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){
apSub = (SubProgram **)pSub->z;
apSub[nSub++] = pOp->p4.pProgram;
pSub->flags |= MEM_Blob;
pSub->n = nSub*sizeof(SubProgram*);
/* When an OP_Program opcode is encounter (the only opcode that has
** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
** kept in p->aMem[9].z to hold the new program - assuming this subprogram
** has not already been seen.
*/
if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
int nByte = (nSub+1)*sizeof(SubProgram*);
int j;
for(j=0; j<nSub; j++){
if( apSub[j]==pOp->p4.pProgram ) break;
}
if( j==nSub ){
p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
if( p->rc!=SQLITE_OK ){
rc = SQLITE_ERROR;
break;
}
apSub = (SubProgram **)pSub->z;
apSub[nSub++] = pOp->p4.pProgram;
pSub->flags |= MEM_Blob;
pSub->n = nSub*sizeof(SubProgram*);
nRow += pOp->p4.pProgram->nOp;
}
}
}while( p->explain==2 && pOp->opcode!=OP_Explain );
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p2; /* P2 */
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
pMem++;
if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
if( zP4!=pMem->z ){
pMem->n = 0;
sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
if( rc==SQLITE_OK ){
if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
}
pMem++;
if( p->explain==1 ){
if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
char *zP4;
if( p->explain==1 ){
pMem->flags = MEM_Int;
pMem->u.i = i; /* Program counter */
pMem++;
pMem->flags = MEM_Static|MEM_Str|MEM_Term;
pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
pMem++;
}
pMem->flags = MEM_Str|MEM_Term;
pMem->n = 2;
sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
pMem->enc = SQLITE_UTF8;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
pMem++;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
pMem->flags = MEM_Int;
pMem->u.i = pOp->p2; /* P2 */
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
pMem++;
if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
pMem->n = displayComment(pOp, zP4, pMem->z, 500);
pMem->enc = SQLITE_UTF8;
#else
pMem->flags = MEM_Null; /* Comment */
#endif
}
zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
if( zP4!=pMem->z ){
pMem->n = 0;
sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
}
pMem++;
p->nResColumn = 8 - 4*(p->explain-1);
p->pResultSet = &p->aMem[1];
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
if( p->explain==1 ){
if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
pMem->n = 2;
sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
pMem->enc = SQLITE_UTF8;
pMem++;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
pMem->n = displayComment(pOp, zP4, pMem->z, 500);
pMem->enc = SQLITE_UTF8;
#else
pMem->flags = MEM_Null; /* Comment */
#endif
}
p->nResColumn = 8 - 4*(p->explain-1);
p->pResultSet = &p->aMem[1];
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
}
return rc;
}

View File

@ -19,6 +19,21 @@
#include "sqliteInt.h"
#include "whereInt.h"
/*
** Extra information appended to the end of sqlite3_index_info but not
** visible to the xBestIndex function, at least not directly. The
** sqlite3_vtab_collation() interface knows how to reach it, however.
**
** This object is not an API and can be changed from one release to the
** next. As long as allocateIndexInfo() and sqlite3_vtab_collation()
** agree on the structure, all will be well.
*/
typedef struct HiddenIndexInfo HiddenIndexInfo;
struct HiddenIndexInfo {
WhereClause *pWC; /* The Where clause being analyzed */
Parse *pParse; /* The parsing context */
};
/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);
@ -841,11 +856,11 @@ end_auto_index_create:
** by passing the pointer returned by this function to sqlite3_free().
*/
static sqlite3_index_info *allocateIndexInfo(
Parse *pParse,
WhereClause *pWC,
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause being analyzed */
Bitmask mUnusable, /* Ignore terms with these prereqs */
struct SrcList_item *pSrc,
ExprList *pOrderBy,
struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */
ExprList *pOrderBy, /* The ORDER BY clause */
u16 *pmNoOmit /* Mask of terms not to omit */
){
int i, j;
@ -853,6 +868,7 @@ static sqlite3_index_info *allocateIndexInfo(
struct sqlite3_index_constraint *pIdxCons;
struct sqlite3_index_orderby *pIdxOrderBy;
struct sqlite3_index_constraint_usage *pUsage;
struct HiddenIndexInfo *pHidden;
WhereTerm *pTerm;
int nOrderBy;
sqlite3_index_info *pIdxInfo;
@ -894,7 +910,7 @@ static sqlite3_index_info *allocateIndexInfo(
*/
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
+ sizeof(*pIdxOrderBy)*nOrderBy );
+ sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
return 0;
@ -905,7 +921,8 @@ static sqlite3_index_info *allocateIndexInfo(
** changing them. We have to do some funky casting in order to
** initialize those fields.
*/
pIdxCons = (struct sqlite3_index_constraint*)&pIdxInfo[1];
pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1];
pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];
pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy];
*(int*)&pIdxInfo->nConstraint = nTerm;
@ -915,6 +932,8 @@ static sqlite3_index_info *allocateIndexInfo(
*(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
pUsage;
pHidden->pWC = pWC;
pHidden->pParse = pParse;
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
u16 op;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
@ -3138,6 +3157,27 @@ static int whereLoopAddVirtualOne(
return rc;
}
/*
** If this function is invoked from within an xBestIndex() callback, it
** returns a pointer to a buffer containing the name of the collation
** sequence associated with element iCons of the sqlite3_index_info.aConstraint
** array. Or, if iCons is out of range or there is no active xBestIndex
** call, return NULL.
*/
const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
const char *zRet = 0;
if( iCons>=0 && iCons<pIdxInfo->nConstraint ){
CollSeq *pC = 0;
int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset;
Expr *pX = pHidden->pWC->a[iTerm].pExpr;
if( pX->pLeft ){
pC = sqlite3BinaryCompareCollSeq(pHidden->pParse, pX->pLeft, pX->pRight);
}
zRet = (pC ? pC->zName : "BINARY");
}
return zRet;
}
/*
** Add all WhereLoop objects for a table of the join identified by

View File

@ -128,7 +128,7 @@ int sqlite3WhereExplainOneScan(
){
int ret = 0;
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pParse->explain==2 )
if( sqlite3ParseToplevel(pParse)->explain==2 )
#endif
{
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];

View File

@ -378,6 +378,27 @@ do_test colname-9.210 {
execsql2 {SELECT t1.a, v3.a AS n FROM t1 JOIN v3}
} {a 1 n 3}
# 2017-12-23: Ticket https://www.sqlite.org/src/info/3b4450072511e621
# Inconsistent column names in CREATE TABLE AS
#
# Verify that the names of columns in the created table of a CREATE TABLE AS
# are the same as the names of result columns in the SELECT statement.
#
do_execsql_test colname-9.300 {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(aaa INT);
INSERT INTO t1(aaa) VALUES(123);
}
do_test colname-9.310 {
execsql2 {SELECT BBb FROM (SELECT aaa AS Bbb FROM t1)}
} {Bbb 123}
do_execsql_test colname-9.320 {
CREATE TABLE t2 AS SELECT BBb FROM (SELECT aaa AS Bbb FROM t1);
SELECT name FROM pragma_table_info('t2');
} {Bbb}
# Make sure the quotation marks get removed from the column names
# when constructing a new table from an aggregate SELECT.
# Email from Juergen Palm on 2017-07-11.

View File

@ -86,9 +86,10 @@ proc test_set {args} {
#
set alltests [list]
foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] }
foreach f [glob -nocomplain \
$testdir/../ext/rtree/*.test \
foreach f [glob -nocomplain \
$testdir/../ext/rtree/*.test \
$testdir/../ext/fts5/test/*.test \
$testdir/../ext/expert/*.test \
$testdir/../ext/lsm1/test/*.test \
] {
lappend alltests $f

View File

@ -213,7 +213,7 @@ do_catchsql_test 3.1 {
("test.db2", "t1", 11, 20)
', 'fetch_db_no_such_function'
);
} {1 {no such function: fetch_db_no_such_function}}
} {1 {sql error: no such function: fetch_db_no_such_function}}
do_catchsql_test 3.2 {
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(

View File

@ -14,7 +14,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix swarmvtab
set testprefix swarmvtab2
do_not_use_codec
ifcapable !vtab {

233
test/swarmvtab3.test Normal file
View File

@ -0,0 +1,233 @@
# 2017-07-15
#
# 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 file is the "swarmvtab" extension
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix swarmvtab3
do_not_use_codec
ifcapable !vtab {
finish_test
return
}
load_static_extension db unionvtab
set nFile $sqlite_open_file_count
do_execsql_test 1.0 {
CREATE TEMP TABLE swarm(id, tbl, minval, maxval);
}
# Set up 100 databases with filenames "remote_test.dbN", where N is between
# 0 and 99.
do_test 1.1 {
for {set i 0} {$i < 100} {incr i} {
set file remote_test.db$i
forcedelete $file
forcedelete test.db$i
sqlite3 rrr $file
rrr eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES($i, $i);
}
rrr close
db eval {
INSERT INTO swarm VALUES($i, 't1', $i, $i);
}
set ::dbcache(test.db$i) 0
}
} {}
proc missing_db {filename} {
set remote "remote_$filename"
forcedelete $filename
file copy $remote $filename
}
db func missing_db missing_db
proc openclose_db {filename bClose} {
if {$bClose} {
incr ::dbcache($filename) -1
} else {
incr ::dbcache($filename) 1
}
if {$::dbcache($filename)==0} {
forcedelete $filename
}
}
db func openclose_db openclose_db
proc check_dbcache {} {
set n 0
for {set i 0} {$i<100} {incr i} {
set exists [file exists test.db$i]
if {$exists!=($::dbcache(test.db$i)!=0)} {
error "inconsistent ::dbcache and disk"
}
incr n $exists
}
return $n
}
foreach {tn nMaxOpen cvt} {
1 5 {
CREATE VIRTUAL TABLE temp.s USING swarmvtab(
'SELECT :prefix || id, tbl, minval, minval FROM swarm',
:prefix='test.db',
missing=missing_db,
openclose=openclose_db,
maxopen=5
)
}
2 3 {
CREATE VIRTUAL TABLE temp.s USING swarmvtab(
'SELECT :prefix || id, tbl, minval, minval FROM swarm',
:prefix='test.db',
missing = 'missing_db',
openclose=[openclose_db],
maxopen = 3
)
}
3 1 {
CREATE VIRTUAL TABLE temp.s USING swarmvtab(
'SELECT :prefix||''.''||:suffix||id, tbl, minval, minval FROM swarm',
:prefix=test, :suffix=db,
missing = 'missing_db',
openclose=[openclose_db],
maxopen = 1
)
}
} {
execsql { DROP TABLE IF EXISTS s }
do_execsql_test 1.$tn.1 $cvt
do_execsql_test 1.$tn.2 {
SELECT b FROM s WHERE a<10;
} {0 1 2 3 4 5 6 7 8 9}
do_test 1.$tn.3 { check_dbcache } $nMaxOpen
do_execsql_test 1.$tn.4 {
SELECT b FROM s WHERE (b%10)=0;
} {0 10 20 30 40 50 60 70 80 90}
do_test 1.$tn.5 { check_dbcache } $nMaxOpen
}
execsql { DROP TABLE IF EXISTS s }
for {set i 0} {$i < 100} {incr i} {
forcedelete remote_test.db$i
}
#----------------------------------------------------------------------------
#
do_execsql_test 2.0 {
DROP TABLE IF EXISTS swarm;
CREATE TEMP TABLE swarm(file, tbl, minval, maxval, ctx);
}
catch { array unset ::dbcache }
# Set up 100 databases with filenames "remote_test.dbN", where N is a
# random integer between 0 and 1,000,000
# 0 and 99.
do_test 2.1 {
for {set i 0} {$i < 100} {incr i} {
while 1 {
set ctx [expr abs(int(rand() *1000000))]
if {[info exists ::dbcache($ctx)]==0} break
}
set file test_remote.db$ctx
forcedelete $file
forcedelete test.db$i
sqlite3 rrr $file
rrr eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES($i, $i);
}
rrr close
db eval {
INSERT INTO swarm VALUES('test.db' || $i, 't1', $i, $i, $file)
}
set ::dbcache(test.db$i) 0
}
} {}
proc missing_db {filename ctx} {
file copy $ctx $filename
}
db func missing_db missing_db
proc openclose_db {filename ctx bClose} {
if {$bClose} {
incr ::dbcache($filename) -1
} else {
incr ::dbcache($filename) 1
}
if {$::dbcache($filename)==0} {
forcedelete $filename
}
}
db func openclose_db openclose_db
proc check_dbcache {} {
set n 0
foreach k [array names ::dbcache] {
set exists [file exists $k]
if {$exists!=($::dbcache($k)!=0)} {
error "inconsistent ::dbcache and disk ($k)"
}
incr n $exists
}
return $n
}
foreach {tn nMaxOpen cvt} {
2 5 {
CREATE VIRTUAL TABLE temp.s USING swarmvtab(
'SELECT file, tbl, minval, minval, ctx FROM swarm',
missing=missing_db,
openclose=openclose_db,
maxopen=5
)
}
} {
execsql { DROP TABLE IF EXISTS s }
do_execsql_test 1.$tn.1 $cvt
do_execsql_test 1.$tn.2 {
SELECT b FROM s WHERE a<10;
} {0 1 2 3 4 5 6 7 8 9}
do_test 1.$tn.3 { check_dbcache } $nMaxOpen
do_execsql_test 1.$tn.4 {
SELECT b FROM s WHERE (b%10)=0;
} {0 10 20 30 40 50 60 70 80 90}
do_test 1.$tn.5 { check_dbcache } $nMaxOpen
}
db close
forcedelete {*}[glob test_remote.db*]
finish_test

View File

@ -2271,13 +2271,17 @@ proc test_restore_config_pagecache {} {
sqlite3 db test.db
}
proc test_find_binary {nm} {
proc test_binary_name {nm} {
if {$::tcl_platform(platform)=="windows"} {
set ret "$nm.exe"
} else {
set ret $nm
}
set ret [file normalize [file join $::cmdlinearg(TESTFIXTURE_HOME) $ret]]
file normalize [file join $::cmdlinearg(TESTFIXTURE_HOME) $ret]
}
proc test_find_binary {nm} {
set ret [test_binary_name $nm]
if {![file executable $ret]} {
finish_test
return ""

View File

@ -651,10 +651,18 @@ static void yy_accept(yyParser*); /* Forward Declaration */
/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
**
** The yyLookahead and yyLookaheadToken parameters provide reduce actions
** access to the lookahead token (if any). The yyLookahead will be YYNOCODE
** if the lookahead token has already been consumed. As this procedure is
** only called from one place, optimizing compilers will in-line it, which
** means that the extra parameters have no performance impact.
*/
static void yy_reduce(
yyParser *yypParser, /* The parser */
unsigned int yyruleno /* Number of the rule by which to reduce */
unsigned int yyruleno, /* Number of the rule by which to reduce */
int yyLookahead, /* Lookahead token, or YYNOCODE if none */
ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */
){
int yygoto; /* The next state */
int yyact; /* The next action */
@ -853,7 +861,7 @@ void Parse(
#endif
yymajor = YYNOCODE;
}else if( yyact <= YY_MAX_REDUCE ){
yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,yyminor);
}else{
assert( yyact == YY_ERROR_ACTION );
yyminorunion.yy0 = yyminor;