Merge recent enhancements from trunk.
FossilOrigin-Name: edceaccd66a65d6b36e53ce33d760a7bd9c2261a592d12189f5f55417b5d5d74
This commit is contained in:
commit
844cf56bc6
@ -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 \
|
||||
|
@ -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
83
ext/expert/README.md
Normal 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 <sql-query> test.db
|
||||
</pre>
|
||||
|
||||
Or an entire text file worth of queries with:
|
||||
|
||||
<pre>
|
||||
./sqlite3_expert -file <text-file> 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 <sql-query> test.db
|
||||
|
||||
# Do not generate any statistics at all:
|
||||
./sqlite3_expert -sample 0 -sql <sql-query> test.db
|
||||
</pre>
|
155
ext/expert/expert.c
Normal file
155
ext/expert/expert.c
Normal 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
377
ext/expert/expert1.test
Normal 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
1934
ext/expert/sqlite3expert.c
Normal file
File diff suppressed because it is too large
Load Diff
168
ext/expert/sqlite3expert.h
Normal file
168
ext/expert/sqlite3expert.h
Normal 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
215
ext/expert/test_expert.c
Normal 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
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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
10
main.mk
@ -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
|
||||
|
67
manifest
67
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
ee248b529c2396c5480fb99b0a1dc31032627ec8241eca4a8c0fff257bb4a088
|
||||
edceaccd66a65d6b36e53ce33d760a7bd9c2261a592d12189f5f55417b5d5d74
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
12
src/select.c
12
src/select.c
@ -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 ){
|
||||
|
182
src/shell.c.in
182
src/shell.c.in
@ -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 ){
|
||||
|
@ -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}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
188
src/vdbeaux.c
188
src/vdbeaux.c
@ -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;
|
||||
}
|
||||
|
52
src/where.c
52
src/where.c
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
233
test/swarmvtab3.test
Normal 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
|
||||
|
@ -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 ""
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user