Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f61e13f72 | |||
| 5c8b293c75 | |||
| df5e9e977b | |||
| 04de790b09 | |||
| 83dca19e9f | |||
| df24cf81a6 | |||
| 5a7dbc70ea | |||
| dfea45330e | |||
| 449cb9a5af | |||
| 0d3a4085e5 | |||
| d94d4ee7bd | |||
| f7f425d600 | |||
| e18e90ebaf | |||
| 0d51def29e | |||
| 2fc7bc08bd | |||
| 7bd3c89114 | |||
| a09c8855b7 | |||
| e500747cfe | |||
| 84de690b4f | |||
| 2ae56f50ce | |||
| 79211e194d | |||
| a7bf23c6b5 | |||
| c7f6c148d9 | |||
| 012e133772 | |||
| 7a0fd192cc | |||
| fb0d6e56d6 | |||
| e724d3d26a | |||
| 3852413246 | |||
| c5f246eb37 | |||
| e03d76254c | |||
| 5da73e1a09 | |||
| 4243e27f22 | |||
| 8164722c58 | |||
| 75525cbe24 | |||
| 4a6b8a05cd | |||
| 253666e520 | |||
| 224155dd1c | |||
| 264d2b97a9 | |||
| e4618fb30d | |||
| 09e1df6c00 | |||
| 67a03cfd6e | |||
| 7de2a1faf6 | |||
| 42685f211e | |||
| 65e6b0dd12 | |||
| 6b6828625b | |||
| 9881d60d17 | |||
| b51926e67c | |||
| e3bfbb7284 | |||
| 440e6ff3b0 | |||
| 2dd3cdcfab | |||
| 1f8bb4b0a8 | |||
| 453e0261df | |||
| 9940e2aa45 | |||
| 8ad1d8ba38 | |||
| b79c718f1a | |||
| cfc9df76e1 | |||
| 9dfaf621d1 | |||
| a01c7c70fd | |||
| aa9933c115 | |||
| f690c14280 | |||
| 0ff435a782 | |||
| 3de4df27dd | |||
| 9e0ed8d4ec | |||
| 46a06bbe47 | |||
| 958d261bb8 | |||
| e9af1894e3 | |||
| a9f43d735b | |||
| 31a0bfde9b | |||
| db30fc4ff2 | |||
| 7f0a24b7bf | |||
| 22ace89179 | |||
| 1a088a8ef5 | |||
| 9c1586ec7c | |||
| f77ceba594 | |||
| 92a20dde1f | |||
| be3018c18f | |||
| 82a8a9f120 | |||
| 4be4c406fa | |||
| d30ab3d9dd | |||
| bf20a35d5f | |||
| f7da5555da | |||
| 85462ed939 | |||
| 8daefc2af0 | |||
| 6b5d4759ce | |||
| 1aca5caa88 | |||
| 8930c2ab0c | |||
| 6e4cc55e1f | |||
| a634fb1577 | |||
| ac4f0039c0 | |||
| dd95d30f82 | |||
| 8d8f56296b | |||
| fad9f9a8a6 | |||
| 578e1ca8d7 | |||
| 38fdead890 | |||
| b3f56fdb69 | |||
| 853c4a7621 | |||
| e7c84cc7e3 | |||
| ff9fce4d60 | |||
| 73e7d8b2bb | |||
| 2f17001521 | |||
| 7c8e9c78c8 | |||
| 6971952c65 | |||
| face087212 | |||
| bfa463696b | |||
| 664eb14d8e | |||
| e6c7b09df6 | |||
| ab1dcc1a4b | |||
| 78d5843245 | |||
| 3c863634ef | |||
| 5c2b314222 | |||
| dd23c6bfb4 | |||
| fd0a2f9756 | |||
| 3f802ebce2 | |||
| 83553eefaf | |||
| 70f624c3a9 | |||
| d345a66428 | |||
| f45f2326a2 | |||
| f87684189d | |||
| c625ba7f4f | |||
| 6ddecb753a | |||
| daa3ce4b08 | |||
| e22724ee54 | |||
| a806475f86 | |||
| 572df3b1e4 | |||
| da0e47109e | |||
| f51446a38c |
+5
-1
@@ -177,7 +177,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
table.lo threads.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
@@ -261,6 +261,7 @@ SRC = \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/trigger.c \
|
||||
@@ -732,6 +733,9 @@ status.lo: $(TOP)/src/status.c $(HDR)
|
||||
table.lo: $(TOP)/src/table.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/table.c
|
||||
|
||||
threads.lo: $(TOP)/src/threads.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/threads.c
|
||||
|
||||
tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/tokenize.c
|
||||
|
||||
|
||||
+5
-1
@@ -564,7 +564,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
|
||||
pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
table.lo threads.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
@@ -659,6 +659,7 @@ SRC = \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\threads.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
@@ -1139,6 +1140,9 @@ status.lo: $(TOP)\src\status.c $(HDR)
|
||||
table.lo: $(TOP)\src\table.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\table.c
|
||||
|
||||
threads.lo: $(TOP)\src\threads.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\threads.c
|
||||
|
||||
tokenize.lo: $(TOP)\src\tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\tokenize.c
|
||||
|
||||
|
||||
+762
-734
File diff suppressed because it is too large
Load Diff
+11
-8
@@ -120,12 +120,13 @@ proc execsql_intout {sql} {
|
||||
# Test that it is possible to open an existing database that contains
|
||||
# r-tree tables.
|
||||
#
|
||||
do_test rtree-1.4.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 5.0, 10.0);
|
||||
INSERT INTO t1 VALUES(2, 15.0, 20.0);
|
||||
}
|
||||
do_execsql_test rtree-1.4.1a {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 5.0, 10.0);
|
||||
SELECT substr(hex(data),1,40) FROM t1_node;
|
||||
} {00000001000000000000000140A0000041200000}
|
||||
do_execsql_test rtree-1.4.1b {
|
||||
INSERT INTO t1 VALUES(2, 15.0, 20.0);
|
||||
} {}
|
||||
do_test rtree-1.4.2 {
|
||||
db close
|
||||
@@ -435,16 +436,18 @@ do_test rtree-11.2 {
|
||||
# Test on-conflict clause handling.
|
||||
#
|
||||
db_delete_and_reopen
|
||||
do_execsql_test 12.0 {
|
||||
do_execsql_test 12.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
|
||||
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
|
||||
SELECT substr(hex(data),1,56) FROM t1_node;
|
||||
} {00000001000000000000000100000001000000020000000300000004}
|
||||
do_execsql_test 12.0.2 {
|
||||
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
|
||||
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
|
||||
|
||||
CREATE TABLE source(idx, x1, x2, y1, y2);
|
||||
INSERT INTO source VALUES(5, 8, 8, 8, 8);
|
||||
INSERT INTO source VALUES(2, 7, 7, 7, 7);
|
||||
|
||||
}
|
||||
db_save_and_close
|
||||
foreach {tn sql_template testdata} {
|
||||
|
||||
@@ -57,31 +57,31 @@ do_test rtree6-1.1 {
|
||||
|
||||
do_test rtree6-1.2 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
|
||||
} {Ea}
|
||||
} {E0}
|
||||
|
||||
do_test rtree6-1.3 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.4 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.5 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_eqp_test rtree6.2.1 {
|
||||
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.2 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ do_eqp_test rtree6.2.3 {
|
||||
do_eqp_test rtree6.2.4 {
|
||||
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ do_test rtree6.3.2 {
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
do_test rtree6.3.3 {
|
||||
rtree_strategy {
|
||||
SELECT * FROM t3 WHERE
|
||||
@@ -137,7 +137,7 @@ do_test rtree6.3.3 {
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
|
||||
do_execsql_test rtree6-3.4 {
|
||||
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1
|
||||
|
||||
@@ -41,7 +41,7 @@ ifcapable rtree_int_only {
|
||||
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
|
||||
SELECT rtreenode(2, data) FROM t1_node;
|
||||
}
|
||||
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
|
||||
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -29,7 +29,7 @@ do_eqp_test 1.1 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
@@ -37,7 +37,7 @@ do_eqp_test 1.2 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 1.3 {
|
||||
@@ -45,7 +45,7 @@ do_eqp_test 1.3 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 1.5 {
|
||||
@@ -82,7 +82,7 @@ do_eqp_test 2.1 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 2.2 {
|
||||
@@ -90,7 +90,7 @@ do_eqp_test 2.2 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 2.3 {
|
||||
@@ -98,7 +98,7 @@ do_eqp_test 2.3 {
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
|
||||
}
|
||||
|
||||
do_eqp_test 2.5 {
|
||||
@@ -271,4 +271,3 @@ ifcapable rtree {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# 2010 August 28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file contains tests for the r-tree module. Specifically, it tests
|
||||
# that new-style custom r-tree queries (geometry callbacks) work.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
ifcapable rtree_int_only { finish_test; return }
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the example 2d "circle" geometry callback.
|
||||
#
|
||||
register_circle_geom db
|
||||
|
||||
do_execsql_test rtreeE-1.1 {
|
||||
PRAGMA page_size=512;
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1);
|
||||
|
||||
/* A tight pattern of small boxes near 0,0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of small boxes near 100, 0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of larger boxes near 0, 200 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
|
||||
} {}
|
||||
|
||||
# Queries against each of the three clusters */
|
||||
do_execsql_test rtreeE-1.1 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
|
||||
do_execsql_test rtreeE-1.2 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124}
|
||||
do_execsql_test rtreeE-1.3 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) ORDER BY id;
|
||||
} {200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224}
|
||||
|
||||
# The Qcircle geometry function gives a lower score to larger leaf-nodes.
|
||||
# This causes the 200s to sort before the 100s and the 0s to sort before
|
||||
# last.
|
||||
#
|
||||
do_execsql_test rtreeE-1.4 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0
|
||||
} {200 100 0}
|
||||
|
||||
# Exclude odd rowids on a depth-first search
|
||||
do_execsql_test rtreeE-1.5 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,4) ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Exclude odd rowids on a breadth-first search.
|
||||
do_execsql_test rtreeE-1.6 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Construct a large 2-D RTree with thousands of random entries.
|
||||
#
|
||||
do_test rtreeE-2.1 {
|
||||
db eval {
|
||||
CREATE TABLE t2(id,x0,x1,y0,y1);
|
||||
CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
|
||||
BEGIN;
|
||||
}
|
||||
expr srand(0)
|
||||
for {set i 1} {$i<=10000} {incr i} {
|
||||
set dx [expr {int(rand()*40)+1}]
|
||||
set dy [expr {int(rand()*40)+1}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set id [expr {$i+10000}]
|
||||
db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)}
|
||||
}
|
||||
db eval {
|
||||
INSERT INTO rt2 SELECT * FROM t2;
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
|
||||
for {set i 1} {$i<=200} {incr i} {
|
||||
set dx [expr {int(rand()*100)}]
|
||||
set dy [expr {int(rand()*100)}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.2.$i {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id
|
||||
} $ans
|
||||
}
|
||||
|
||||
# Run query that have very deep priority queues
|
||||
#
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.3 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id
|
||||
} $ans
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.4 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id
|
||||
} $ans
|
||||
|
||||
finish_test
|
||||
@@ -21,6 +21,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
|
||||
|
||||
/* The double-precision datatype used by RTree depends on the
|
||||
** SQLITE_RTREE_INT_ONLY compile-time option.
|
||||
*/
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
typedef sqlite3_int64 sqlite3_rtree_dbl;
|
||||
#else
|
||||
typedef double sqlite3_rtree_dbl;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register a geometry callback named zGeom that can be used as part of an
|
||||
@@ -31,11 +41,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
|
||||
#else
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
|
||||
#endif
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
|
||||
void *pContext
|
||||
);
|
||||
|
||||
@@ -47,11 +53,60 @@ int sqlite3_rtree_geometry_callback(
|
||||
struct sqlite3_rtree_geometry {
|
||||
void *pContext; /* Copy of pContext passed to s_r_g_c() */
|
||||
int nParam; /* Size of array aParam[] */
|
||||
double *aParam; /* Parameters passed to SQL geom function */
|
||||
sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
|
||||
void *pUser; /* Callback implementation user data */
|
||||
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
|
||||
};
|
||||
|
||||
/*
|
||||
** Register a 2nd-generation geometry callback named zScore that can be
|
||||
** used as part of an R-Tree geometry query as follows:
|
||||
**
|
||||
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
|
||||
*/
|
||||
int sqlite3_rtree_query_callback(
|
||||
sqlite3 *db,
|
||||
const char *zQueryFunc,
|
||||
int (*xQueryFunc)(sqlite3_rtree_query_info*),
|
||||
void *pContext,
|
||||
void (*xDestructor)(void*)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** A pointer to a structure of the following type is passed as the
|
||||
** argument to scored geometry callback registered using
|
||||
** sqlite3_rtree_query_callback().
|
||||
**
|
||||
** Note that the first 5 fields of this structure are identical to
|
||||
** sqlite3_rtree_geometry. This structure is a subclass of
|
||||
** sqlite3_rtree_geometry.
|
||||
*/
|
||||
struct sqlite3_rtree_query_info {
|
||||
void *pContext; /* pContext from when function registered */
|
||||
int nParam; /* Number of function parameters */
|
||||
sqlite3_rtree_dbl *aParam; /* value of function parameters */
|
||||
void *pUser; /* callback can use this, if desired */
|
||||
void (*xDelUser)(void*); /* function to free pUser */
|
||||
sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
|
||||
unsigned int *anQueue; /* Number of pending entries in the queue */
|
||||
int nCoord; /* Number of coordinates */
|
||||
int iLevel; /* Level of current node or entry */
|
||||
int mxLevel; /* The largest iLevel value in the tree */
|
||||
sqlite3_int64 iRowid; /* Rowid for current entry */
|
||||
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
|
||||
int eParentWithin; /* Visibility of parent node */
|
||||
int eWithin; /* OUT: Visiblity */
|
||||
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
|
||||
*/
|
||||
#define NOT_WITHIN 0 /* Object completely outside of query region */
|
||||
#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
|
||||
#define FULLY_WITHIN 2 /* Object fully contained within query region */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
|
||||
@@ -66,7 +66,7 @@ LIBOBJ+= vdbe.o parse.o \
|
||||
notify.o opcodes.o os.o os_unix.o os_win.o \
|
||||
pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
table.o threads.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
|
||||
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
|
||||
@@ -144,6 +144,7 @@ SRC = \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/trigger.c \
|
||||
$(TOP)/src/utf.c \
|
||||
@@ -311,6 +312,7 @@ TESTSRC2 = \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/utf.c \
|
||||
$(TOP)/src/util.c \
|
||||
@@ -476,7 +478,7 @@ parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
|
||||
mv parse.h parse.h.temp
|
||||
$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
|
||||
tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
C Enhancements\sto\sthe\sdate\sand\stime\sfunctions\sso\sthat\sthey\soptionally\ssupport\na\s5-digit\syear.\s\sNot\ssure\swe\swant\sto\sinclude\sthis\sin\strunk.\s\sSaved\sin\sa\sbranch\nfor\sfuture\sreference.
|
||||
D 2014-04-23T12:00:20.142
|
||||
C Fix\sseveral\sharmless\scompiler\swarnings.
|
||||
D 2014-05-09T00:36:06.204
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||
F Makefile.in ad0921c4b2780d01868cf69b419a4f102308d125
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc fdacba6fb574868c71fde6db6b77789a383a4c19
|
||||
F Makefile.msc 7d6981e8e14d8189a2a2f9b4a485f40b2032cb2e
|
||||
F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315
|
||||
F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
|
||||
F VERSION 9f823c026c6a32fc5f84d212a8aae0a221dba45c
|
||||
@@ -120,30 +120,31 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 2d9f95da404d850474e628c720c5ce15d29b47de
|
||||
F ext/rtree/rtree.c 6f70db93e0e42c369325c5cddcf2024c5a87ca43
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
|
||||
F ext/rtree/rtree1.test e2da4aaa426918d27122d1a1066c6ecf8409a514
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
|
||||
F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
|
||||
F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e
|
||||
F ext/rtree/rtree6.test fe0bd377a21c68ce2826129d14354c884cb1f354
|
||||
F ext/rtree/rtree6.test 756585abc51727fec97c77852476445c10c0ee95
|
||||
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
|
||||
F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
|
||||
F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34
|
||||
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
|
||||
F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2
|
||||
F ext/rtree/rtreeC.test 16d7aa86ecb6a876d2a38cf590a1471a41b3a46d
|
||||
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
|
||||
F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06
|
||||
F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
|
||||
F ext/rtree/rtreeE.test 388c1c8602c3ce55c15f03b509e9cf545fb7c41f
|
||||
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
|
||||
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
|
||||
F ext/rtree/sqlite3rtree.h c34c1e41d1ab80bb8ad09aae402c9c956871a765
|
||||
F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd
|
||||
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
|
||||
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt f439556c5ce01ced70987e5ee86549a45165d9ff
|
||||
F main.mk 3ae543fa446525c1dec55f58de67f41b78651812
|
||||
F main.mk f51f401a87fd07be255bcecae8b03105ca2024e8
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -158,35 +159,35 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c b00900877f766f116f9e16116f1ccacdc21d82f1
|
||||
F src/analyze.c 663e0b291d27eb03c9fd6b421e2d61ba348a2389
|
||||
F src/analyze.c 3596f863bb80126fe56ba217df5932749271efc8
|
||||
F src/attach.c 3801129015ef59d76bf23c95ef9b0069d18a0c52
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 6c9b51abd404ce5b78b173b6f2248e8cb824758c
|
||||
F src/btree.c e14daeed62b5b1499835a6523f9a47d660c297c8
|
||||
F src/btree.h d79306df4ed9181b48916737fe8871a4392c4594
|
||||
F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3
|
||||
F src/build.c 5bfeea8f302ec2926c9eea321a61daea92a29fa4
|
||||
F src/build.c 9c522b6e5af1485c38b12a55f9d946e66fca61b6
|
||||
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
|
||||
F src/date.c 483fd9eeb74e06c2ddc4b33ade4a8a9611230386
|
||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||
F src/delete.c bcf8f72126cea80fc3d5bc5494cf19b3f8935aaf
|
||||
F src/expr.c 4f9e497c66e2f25a4d139357a778c84d5713207c
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 5269ef07b100763134f71b889327c333bd0989cf
|
||||
F src/func.c 2945bb2c4cdc0ac43733046285a4434310be1811
|
||||
F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486
|
||||
F src/global.c b7943ff485c31660ec0b17c68467034804df01b1
|
||||
F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c d8bb30535c8c0785876025a4a07f9074640a15d1
|
||||
F src/insert.c ab34bea5af4fee9f956a0805a32463fb7f674d00
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
|
||||
F src/main.c 0a8cfb6b2899649880e5874ac9bfd1c6f7c6a4a3
|
||||
F src/main.c 2ba4f6eeb84cf8efd8bb632c1abe203dc86adcac
|
||||
F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
|
||||
@@ -194,40 +195,40 @@ F src/mem2.c dce31758da87ec2cfa52ba4c5df1aed6e07d8e8f
|
||||
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
|
||||
F src/mem5.c 74670012946c4adc8a6ad84d03acc80959c3e529
|
||||
F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785
|
||||
F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc
|
||||
F src/mutex.c 84a073c9a23a8d7bdd2ea832522d1730df18812c
|
||||
F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea
|
||||
F src/mutex_noop.c 7682796b7d8d39bf1c138248858efcd10c9e1553
|
||||
F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc
|
||||
F src/mutex_w32.c 6108c88e1cb38d8fbb3534b170793815cbedbf97
|
||||
F src/mutex_noop.c 4222773e5f61e506f232aedc9ad9e16ca00c1399
|
||||
F src/mutex_unix.c 56e22c1bc6aabfa2f9736317a8f56acd5d0c5f7c
|
||||
F src/mutex_w32.c c4726d3bfe80996665a39d27598ebd056e2d6b89
|
||||
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
|
||||
F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace
|
||||
F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_unix.c ae4b5240af4619d711301d7992396e182585269f
|
||||
F src/os_win.c e71678ac927d0a0fb11d993db20a9748eabf808e
|
||||
F src/os_win.c 5024065c68c50739aeef6d490db88e5b682fcc30
|
||||
F src/pager.c ab62a24218d87dda1be641f6c5ad291bff78fd94
|
||||
F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428
|
||||
F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0
|
||||
F src/pcache.c d8eafac28290d4bb80332005435db44991d07fc2
|
||||
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
|
||||
F src/pcache1.c 102e6f5a2fbc646154463eb856d1fd716867b64c
|
||||
F src/pragma.c 21ece94d4f3e76e8e150deecafb9c7abd398ec67
|
||||
F src/pragma.c 810ef31ccfaa233201dcf100637a9777cc24e897
|
||||
F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337
|
||||
F src/printf.c e5a0005f8b3de21f85da6a709d2fbee76775bf4b
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c 273d5f47c4e2c05b2d3d2bffeda939551ab59e66
|
||||
F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
|
||||
F src/select.c bc7feff0fb4c4a1b9d655b717bef166846b48e33
|
||||
F src/shell.c 2afe7a7154e97be0c74c5feacf09626bda8493be
|
||||
F src/sqlite.h.in bde98816e1ba0c9ffef50afe7b32f4e5a8f54fe0
|
||||
F src/select.c a5ed3fdc82ebab5b9b095ea1971515a7f8a303d2
|
||||
F src/shell.c 6946aea9f21af551fa84bc6b2a8de55d93bf0004
|
||||
F src/sqlite.h.in ed6d0cc90da850340c3863c84351e6e164c0ef00
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 03e2f60ccb0745fa2d3a072cb4f75fa29251d2ee
|
||||
F src/sqliteInt.h 851003126071d4a3bac86a0db75c48197fbd0ff0
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c e87c99e28a145943666b51b212dacae35fcea0bd
|
||||
F src/test1.c 2401eee14a4309a7cfe2aeb2f30ad517a1d9c299
|
||||
F src/test1.c a0e59104fec5626bb1d1bd746157f43e85245e4b
|
||||
F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
@@ -240,7 +241,7 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
|
||||
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
|
||||
F src/test_config.c 0336e0bdbe541b4af89d7e3dd0656e8e6b51e585
|
||||
F src/test_config.c bf2e0bf49ebd8fe3dccabb4542157f9571fe48fa
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@@ -251,7 +252,7 @@ F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61
|
||||
F src/test_intarray.h 2ece66438cfd177b78d1bfda7a4180cd3a10844d
|
||||
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
|
||||
F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4
|
||||
F src/test_malloc.c 1ff5b1243d96124c9a180f3b89424820a1f337f3
|
||||
F src/test_malloc.c 27047a841f5bff1cb638123806a2c30714771307
|
||||
F src/test_multiplex.c 9f304bf04170c91c0318238d512df2da039eb1c8
|
||||
F src/test_multiplex.h 110a8c4d356e0aa464ca8730375608a9a0b61ae1
|
||||
F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f
|
||||
@@ -260,7 +261,7 @@ F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
|
||||
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
|
||||
F src/test_quota.c 30c64f0ef84734f2231a686df41ed882b0c59bc0
|
||||
F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
|
||||
F src/test_rtree.c f3d1d12538dccb75fd916e3fa58f250edbdd3b47
|
||||
F src/test_rtree.c fdd8d29ca5165c7857987a2ba263fac5c69e231f
|
||||
F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6
|
||||
F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
|
||||
F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e
|
||||
@@ -270,29 +271,30 @@ F src/test_syscall.c 2e21ca7f7dc54a028f1967b63f1e76155c356f9b
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
|
||||
F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9
|
||||
F src/test_vfstrace.c 3a0ab304682fecbceb689e7d9b904211fde11d78
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c b9daffcb6ae3a9c080da37b905e790d45a8f99db
|
||||
F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7
|
||||
F src/trigger.c 66f3470b03b52b395e839155786966e3e037fddb
|
||||
F src/update.c 5b3e74a03b3811e586b4f2b4cbd7c49f01c93115
|
||||
F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c
|
||||
F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf
|
||||
F src/util.c 2b5fb283a190aacdb286f7835a447c45b345b83c
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c bf284edea1cee0508dc93c79a73498e0f317edb7
|
||||
F src/vdbe.c 89ab2ded5123e823b47293aedd7931a4742fb6bd
|
||||
F src/vdbe.h 394464909ed682334aa3d5831aae0c2fe2abef94
|
||||
F src/vdbeInt.h e6d83e5bfd62fc6685ba1ed6153f7099f82de9f7
|
||||
F src/vdbeInt.h c78ace64dc37495806dd50596eded1f6cd2b5a64
|
||||
F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
|
||||
F src/vdbeaux.c e493f38758c4b8f4ca2007cf6a700bd405d192f3
|
||||
F src/vdbeaux.c 44d4d1f5711f71eaf0d624de5c3e4976fe4e180b
|
||||
F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
|
||||
F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
|
||||
F src/vdbesort.c 4abb7c0f8f19b7d7d82f4558d5da1a30fdf9ea38
|
||||
F src/vdbesort.c b36070436d33372237541e9eaa6068e1a61bc402
|
||||
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
|
||||
F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
|
||||
F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
||||
F src/where.c 3b127bdc24b7aa84ffa69729170be11555cd7733
|
||||
F src/whereInt.h 929c1349b5355fd44f22cee5c14d72b3329c58a6
|
||||
F src/where.c 91dfd382273c3f67fcdc0ac4728f9140f91c6ab3
|
||||
F src/whereInt.h 6804c2e5010378568c2bb1350477537755296a46
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
@@ -305,13 +307,13 @@ F test/alter4.test d6c011fa0d6227abba762498cafbb607c9609e93
|
||||
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
|
||||
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
|
||||
F test/analyze.test 1772936d66471c65221e437b6d1999c3a03166c4
|
||||
F test/analyze3.test 412f690dfe95b337475e3e78a84a85d25f6f125d
|
||||
F test/analyze3.test bf41f0f680dd1e0d44eed5e769531e93a5320275
|
||||
F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
|
||||
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
|
||||
F test/analyze6.test d31defa011a561b938b4608d3538c1b4e0b5e92c
|
||||
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
|
||||
F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
|
||||
F test/analyze9.test e072a5172d55afcba98d6ca6a219ce8878c2f5c9
|
||||
F test/analyze9.test 623e02a99a78fa12fe5def2fd559032d5d887e0f
|
||||
F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944
|
||||
F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
|
||||
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
|
||||
@@ -329,7 +331,7 @@ F test/auth.test 5bdf154eb28c0e4bbc0473f335858c0d96171768
|
||||
F test/auth2.test c3b415b76c033bedb81292118fb7c01f5f10cbcd
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7
|
||||
F test/autoindex1.test d4dfe14001dfcb74cfbd7107f45a79fc1ab6183e
|
||||
F test/autoindex1.test 762ff3f8e25d852aae55c6462ca166a80c0cde61
|
||||
F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
@@ -406,6 +408,7 @@ F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
|
||||
F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
|
||||
F test/corruptH.test 88ed71a086e13591c917aac6de32750e7c7281cb
|
||||
F test/corruptI.test b3e4203d420490fc3d3062711597bc1dea06a789
|
||||
F test/cost.test 04842adec34311d70c3f3c5fd698b22be54138fd
|
||||
F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
|
||||
F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
|
||||
F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
|
||||
@@ -433,12 +436,12 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test ee95d48664503d40f6cc9ef4a7d03216188e2ada
|
||||
F test/e_createtable.test ed82efcedc4b3656b27a5fcd12335cdb7e20eeee
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
|
||||
F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
|
||||
F test/e_expr.test 5c71d183fbf519a4769fd2e2124afdc70b5b1f42
|
||||
F test/e_fkey.test 630597377549af579d34faaf64c6959a5a68ef76
|
||||
F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707
|
||||
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
|
||||
F test/e_insert.test 1e44f84d2abe44d66e4fbf198be4b20e3cc724a0
|
||||
F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589
|
||||
@@ -452,7 +455,7 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
|
||||
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
|
||||
F test/eqp.test 57c6c604c2807fb5531731c5323133453c24afac
|
||||
F test/eqp.test 90b56d03a93a2e7bb90f88be6083a8ea53f11a0e
|
||||
F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
|
||||
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
|
||||
F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75
|
||||
@@ -610,7 +613,7 @@ F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
|
||||
F test/index3.test 55a90cff99834305e8141df7afaef39674b57062
|
||||
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
||||
F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33
|
||||
F test/index6.test a0a2d286ffa6d35813f5003fdb7be124825b4422
|
||||
F test/index6.test fb370966ac3cd0989053dd5385757b5c3e24ab6a
|
||||
F test/index7.test a3baf9a625bda7fd49471e99aeae04095fbfeecf
|
||||
F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
@@ -662,7 +665,7 @@ F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95
|
||||
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
|
||||
F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8
|
||||
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
||||
F test/malloc.test fd368e31fe98d4779ed80442f311ed9f03bcd1f7
|
||||
F test/malloc.test ada8ae15193ffc6415bf431a6db31d4ec507dbfe
|
||||
F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test fafce0aa9157060445cd1a56ad50fc79d82f28c3
|
||||
@@ -670,7 +673,7 @@ F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
|
||||
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
|
||||
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
|
||||
F test/malloc9.test 2307c6ee3703b0a21391f3ea92388b4b73f9105e
|
||||
F test/mallocA.test 1ba0367fb5434e7bc2fa4afcb30b14174d91b160
|
||||
F test/mallocA.test c049224adeb0244b8f6eb770c1fa6ac40f9b3518
|
||||
F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be
|
||||
F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6
|
||||
F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4
|
||||
@@ -722,8 +725,9 @@ F test/orderby1.test 9b524aff9147288da43a6d7ddfdcff47fa2303c6
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
|
||||
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
|
||||
F test/orderby5.test 2490183fef54417209d1df253633a605d46bd350
|
||||
F test/orderby5.test 8f08a54836d21fb7c70245360751aedd1c2286fb
|
||||
F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
|
||||
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
|
||||
@@ -737,7 +741,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test 40add071ba71aefe1c04f5845308cf46f7de8d04
|
||||
F test/permutations.test 33e7e239ba494fdb30e2f4ffc64c508b145ff42f
|
||||
F test/pragma.test adb21a90875bc54a880fa939c4d7c46598905aa0
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
@@ -786,7 +790,7 @@ F test/select6.test e76bd10a56988f15726c097a5d5a7966fe82d3b2
|
||||
F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
|
||||
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
|
||||
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
|
||||
F test/selectA.test 77adaffe9704cb80e301ebaeff4b107b58d435c5
|
||||
F test/selectA.test 64b88a80271c1710966e50e633380696b60a12a4
|
||||
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
|
||||
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
|
||||
F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
|
||||
@@ -810,13 +814,18 @@ F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
|
||||
F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9
|
||||
F test/shell5.test bb755ea9144b8078a752fc56223582627070b5f1
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
|
||||
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
|
||||
F test/skipscan1.test bed8cbe9d554c8c27afb6c88500f704c86a9196f
|
||||
F test/skipscan2.test 5a4db0799c338ddbacb154aaa5589c0254b36a8d
|
||||
F test/skipscan2.test d77f79cdbba25f0f6f35298136cff21a7d7a553a
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
|
||||
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
|
||||
F test/sort.test 688468cef8c9a66fcc1d54235de8e4deac745690
|
||||
F test/sort2.test c5e25eb674689e291d06b5209fe8d337ae0ec010
|
||||
F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2
|
||||
F test/sort4.test 971452fd4e2928e6fc05c3868396ad7d5f9ce2ad
|
||||
F test/sortfault.test 1a12b6e27d475f50658a8164aaa34f0080a86b36
|
||||
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
|
||||
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
|
||||
F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8
|
||||
@@ -825,7 +834,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c 90446861e566a9965a8d005381a3c964ff333646
|
||||
F test/speedtest1.c f452891e50571627f7060c0e1262359127055717
|
||||
F test/spellfix.test 61309f5efbec53603b3f86457d34a504f80abafe
|
||||
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
|
||||
F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de
|
||||
@@ -845,7 +854,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl f31bea1483ea1d39620f982130026e76f872d744
|
||||
F test/tester.tcl bc0889a2f86d9c17307992ca1e70391794780265
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -905,6 +914,7 @@ F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d
|
||||
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
|
||||
F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30
|
||||
F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00
|
||||
F test/tkt-f67b41381a.test a23bc124c981662db712167bacd0ed8ad11abac9
|
||||
F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2
|
||||
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
|
||||
F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead
|
||||
@@ -1022,7 +1032,7 @@ F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
|
||||
F test/unordered.test ef85ac8f2f3c93ed2b9e811b684de73175fc464c
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 1b6c488a8f993d090b7ee9ad0e234faa161b3aeb
|
||||
F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
@@ -1052,7 +1062,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772
|
||||
F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc
|
||||
F test/wal2.test a8e3963abf6b232cf0b852b09b53665ef34007af
|
||||
F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
|
||||
F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test 8f888b50f66b78821e61ed0e233ded5de378224b
|
||||
@@ -1079,7 +1089,7 @@ F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
|
||||
F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
|
||||
F test/where.test 28b64e93428961b07b0d486778d63fd672948f6b
|
||||
F test/where2.test 455a2eb2666e66c1e84e2cb5815173a85e6237db
|
||||
F test/where3.test d28c51f257e60be30f74308fa385ceeddfb54a6e
|
||||
F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e
|
||||
F test/where4.test d8420ceeb8323a41ceff1f1841fc528e824e1ecf
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
@@ -1093,7 +1103,7 @@ F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
|
||||
F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5
|
||||
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
|
||||
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
|
||||
F test/whereG.test 2533b72ed4a31fd1687230a499b557b911525344
|
||||
F test/whereG.test 0ac23e5e8311b69d87245f4a85112de321031658
|
||||
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
@@ -1123,14 +1133,14 @@ F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 07aba6270d5a5016ba8107b09e431eea4ecdc123
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/logest.c 388c318c7ac8b52b7c08ca1e2de0f4ca9a8f7e81
|
||||
F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
|
||||
F tool/mkautoconfamal.sh f8d8dbf7d62f409ebed5134998bf5b51d7266383
|
||||
F tool/mkkeywordhash.c c9e05e4a7bcab8fab9f583d5b321fb72f565ad97
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 78a77b2c554d534c6f2dc903130186ed15715460
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 8bce31074e4cbe631bb7676526a048335f4c9f02
|
||||
F tool/mksqlite3c.tcl ed5b4e9ca8bf209ff401059a16362f37710fd8b8
|
||||
F tool/mksqlite3c-noext.tcl 752f1a9d3287f6c0ef5738b1c4add0b96fbe0854
|
||||
F tool/mksqlite3c.tcl 8da823522f27608494997e1dba19cd426572ba80
|
||||
F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
|
||||
F tool/mksqlite3internalh.tcl 3dca7bb5374cee003379b8cbac73714f610ef795
|
||||
F tool/mkvsix.tcl 6477fb9dab838b7eea1eed50658ff1cda04850b5
|
||||
@@ -1161,10 +1171,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P 65d2544af9adc1e2f1d193e57f8be0422fb0d5eb
|
||||
R 65a7ac109a1878f6dcc92cd296f64cbc
|
||||
T *branch * five-digit-year
|
||||
T *sym-five-digit-year *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 20a358e6aab334ebda712a29750f60b8
|
||||
P dc7f84dfcc8415fad4af041fd30c20b3fd91be45
|
||||
R dab57784c4183d6467c4fe813ebf5ee5
|
||||
U mistachkin
|
||||
Z bd4d9a5acb160a48faa7ad2a8521ce0e
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2029a396dcdb928f79780ec80c808fda6e3285a1
|
||||
360c0fd1cf9db5f1e4f670a4b95971daaeaa5879
|
||||
+18
-7
@@ -1371,6 +1371,7 @@ static void decodeIntArray(
|
||||
char *zIntArray, /* String containing int array to decode */
|
||||
int nOut, /* Number of slots in aOut[] */
|
||||
tRowcnt *aOut, /* Store integers here */
|
||||
LogEst *aLog, /* Or, if aOut==0, here */
|
||||
Index *pIndex /* Handle extra flags for this index, if not NULL */
|
||||
){
|
||||
char *z = zIntArray;
|
||||
@@ -1389,7 +1390,17 @@ static void decodeIntArray(
|
||||
v = v*10 + c - '0';
|
||||
z++;
|
||||
}
|
||||
aOut[i] = v;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( aOut ){
|
||||
aOut[i] = v;
|
||||
}else
|
||||
#else
|
||||
assert( aOut==0 );
|
||||
UNUSED_PARAMETER(aOut);
|
||||
#endif
|
||||
{
|
||||
aLog[i] = sqlite3LogEst(v);
|
||||
}
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
@@ -1445,12 +1456,12 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
z = argv[2];
|
||||
|
||||
if( pIndex ){
|
||||
decodeIntArray((char*)z, pIndex->nKeyCol+1, pIndex->aiRowEst, pIndex);
|
||||
if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0];
|
||||
decodeIntArray((char*)z, pIndex->nKeyCol+1, 0, pIndex->aiRowLogEst, pIndex);
|
||||
if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0];
|
||||
}else{
|
||||
Index fakeIdx;
|
||||
fakeIdx.szIdxRow = pTable->szTabRow;
|
||||
decodeIntArray((char*)z, 1, &pTable->nRowEst, &fakeIdx);
|
||||
decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx);
|
||||
pTable->szTabRow = fakeIdx.szIdxRow;
|
||||
}
|
||||
|
||||
@@ -1642,9 +1653,9 @@ static int loadStatTbl(
|
||||
pPrevIdx = pIdx;
|
||||
}
|
||||
pSample = &pIdx->aSample[pIdx->nSample];
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,1), nCol, pSample->anEq, 0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,2), nCol, pSample->anLt, 0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,3), nCol, pSample->anDLt,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0);
|
||||
|
||||
/* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
|
||||
** This is in case the sample record is corrupted. In that case, the
|
||||
|
||||
+6
-3
@@ -4609,7 +4609,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
|
||||
if( pIdxKey ){
|
||||
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
|
||||
pIdxKey->isCorrupt = 0;
|
||||
pIdxKey->errCode = 0;
|
||||
assert( pIdxKey->default_rc==1
|
||||
|| pIdxKey->default_rc==0
|
||||
|| pIdxKey->default_rc==-1
|
||||
@@ -4733,7 +4733,10 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
c = xRecordCompare(nCell, pCellKey, pIdxKey, 0);
|
||||
sqlite3_free(pCellKey);
|
||||
}
|
||||
assert( pIdxKey->isCorrupt==0 || c==0 );
|
||||
assert(
|
||||
(pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
|
||||
&& (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
|
||||
);
|
||||
if( c<0 ){
|
||||
lwr = idx+1;
|
||||
}else if( c>0 ){
|
||||
@@ -4743,7 +4746,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
*pRes = 0;
|
||||
rc = SQLITE_OK;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
if( pIdxKey->isCorrupt ) rc = SQLITE_CORRUPT;
|
||||
if( pIdxKey->errCode ) rc = SQLITE_CORRUPT;
|
||||
goto moveto_finish;
|
||||
}
|
||||
if( lwr>upr ) break;
|
||||
|
||||
+28
-21
@@ -905,7 +905,7 @@ void sqlite3StartTable(
|
||||
pTable->iPKey = -1;
|
||||
pTable->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
pTable->nRowEst = 1048576;
|
||||
pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
assert( pParse->pNewTable==0 );
|
||||
pParse->pNewTable = pTable;
|
||||
|
||||
@@ -2669,7 +2669,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
|
||||
/* Open the sorter cursor if we are to use one. */
|
||||
iSorter = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nKeyCol, (char*)
|
||||
sqlite3KeyInfoRef(pKey), P4_KEYINFO);
|
||||
|
||||
/* Open the table. Loop through all rows of the table, inserting index
|
||||
@@ -2730,15 +2730,15 @@ Index *sqlite3AllocateIndexObject(
|
||||
|
||||
nByte = ROUND8(sizeof(Index)) + /* Index structure */
|
||||
ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
|
||||
ROUND8(sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
|
||||
sizeof(i16)*nCol + /* Index.aiColumn */
|
||||
sizeof(u8)*nCol); /* Index.aSortOrder */
|
||||
p = sqlite3DbMallocZero(db, nByte + nExtra);
|
||||
if( p ){
|
||||
char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
|
||||
p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
|
||||
p->aiRowEst = (tRowcnt*)pExtra; pExtra += sizeof(tRowcnt)*(nCol+1);
|
||||
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
|
||||
p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
|
||||
p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
|
||||
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
|
||||
p->aSortOrder = (u8*)pExtra;
|
||||
p->nColumn = nCol;
|
||||
p->nKeyCol = nCol - 1;
|
||||
@@ -2968,7 +2968,7 @@ Index *sqlite3CreateIndex(
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
|
||||
pIndex->zName = zExtra;
|
||||
zExtra += nName + 1;
|
||||
@@ -3018,7 +3018,7 @@ Index *sqlite3CreateIndex(
|
||||
pParse->checkSchema = 1;
|
||||
goto exit_create_index;
|
||||
}
|
||||
assert( pTab->nCol<=0x7fff && j<=0x7fff );
|
||||
assert( j<=0x7fff );
|
||||
pIndex->aiColumn[i] = (i16)j;
|
||||
if( pListItem->pExpr ){
|
||||
int nColl;
|
||||
@@ -3249,7 +3249,7 @@ exit_create_index:
|
||||
** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the
|
||||
** number of rows in the table that match any particular value of the
|
||||
** first column of the index. aiRowEst[2] is an estimate of the number
|
||||
** of rows that match any particular combiniation of the first 2 columns
|
||||
** of rows that match any particular combination of the first 2 columns
|
||||
** of the index. And so forth. It must always be the case that
|
||||
*
|
||||
** aiRowEst[N]<=aiRowEst[N-1]
|
||||
@@ -3260,20 +3260,27 @@ exit_create_index:
|
||||
** are based on typical values found in actual indices.
|
||||
*/
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
tRowcnt *a = pIdx->aiRowEst;
|
||||
/* 10, 9, 8, 7, 6 */
|
||||
LogEst aVal[] = { 33, 32, 30, 28, 26 };
|
||||
LogEst *a = pIdx->aiRowLogEst;
|
||||
int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
|
||||
int i;
|
||||
tRowcnt n;
|
||||
assert( a!=0 );
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
n = 10;
|
||||
for(i=1; i<=pIdx->nKeyCol; i++){
|
||||
a[i] = n;
|
||||
if( n>5 ) n--;
|
||||
}
|
||||
if( pIdx->onError!=OE_None ){
|
||||
a[pIdx->nKeyCol] = 1;
|
||||
|
||||
/* Set the first entry (number of rows in the index) to the estimated
|
||||
** number of rows in the table. Or 10, if the estimated number of rows
|
||||
** in the table is less than that. */
|
||||
a[0] = pIdx->pTable->nRowLogEst;
|
||||
if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
|
||||
|
||||
/* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
|
||||
** 6 and each subsequent value (if any) is 5. */
|
||||
memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
|
||||
for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
|
||||
a[i] = 23; assert( 23==sqlite3LogEst(5) );
|
||||
}
|
||||
|
||||
assert( 0==sqlite3LogEst(1) );
|
||||
if( pIdx->onError!=OE_None ) a[pIdx->nKeyCol] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+26
-51
@@ -229,7 +229,7 @@ static void computeJD(DateTime *p){
|
||||
}
|
||||
A = Y/100;
|
||||
B = 2 - A + (A/4);
|
||||
X1 = 365*(Y+4716) + 25*(Y+4716)/100;
|
||||
X1 = 36525*(Y+4716)/100;
|
||||
X2 = 306001*(M+1)/10000;
|
||||
p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000);
|
||||
p->validJD = 1;
|
||||
@@ -258,23 +258,18 @@ static void computeJD(DateTime *p){
|
||||
*/
|
||||
static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
int Y, M, D, neg;
|
||||
char c;
|
||||
|
||||
if( zDate[0]=='-' ){
|
||||
zDate++;
|
||||
neg = 1;
|
||||
}else{
|
||||
neg = 0;
|
||||
if( zDate[0]=='+' ) zDate++;
|
||||
}
|
||||
if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)==3 ){
|
||||
zDate += 10;
|
||||
}else if( getDigits(zDate,5,0,99999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)==3 ){
|
||||
zDate += 11;
|
||||
}else{
|
||||
if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
|
||||
return 1;
|
||||
}
|
||||
while( (c = zDate[0])=='T' || c=='t' || sqlite3Isspace(c) ){ zDate++; }
|
||||
zDate += 10;
|
||||
while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; }
|
||||
if( parseHhMmSs(zDate, p)==0 ){
|
||||
/* We got the time */
|
||||
}else if( *zDate==0 ){
|
||||
@@ -360,7 +355,7 @@ static void computeYMD(DateTime *p){
|
||||
A = Z + 1 + A - (A/4);
|
||||
B = A + 1524;
|
||||
C = (int)((B - 122.1)/365.25);
|
||||
D = 365*C + (25*C)/100;
|
||||
D = (36525*C)/100;
|
||||
E = (int)((B-D)/30.6001);
|
||||
X1 = (int)(30.6001*E);
|
||||
p->D = B - D - X1;
|
||||
@@ -582,8 +577,19 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
}
|
||||
#endif
|
||||
case 'u': {
|
||||
/*
|
||||
** unixepoch
|
||||
**
|
||||
** Treat the current value of p->iJD as the number of
|
||||
** seconds since 1970. Convert to a real julian day number.
|
||||
*/
|
||||
if( strcmp(z, "unixepoch")==0 && p->validJD ){
|
||||
p->iJD = (p->iJD + 43200)/86400 + 21086676*(i64)10000000;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
if( strcmp(z, "utc")==0 ){
|
||||
else if( strcmp(z, "utc")==0 ){
|
||||
sqlite3_int64 c1;
|
||||
computeJD(p);
|
||||
c1 = localtimeOffset(p, pCtx, &rc);
|
||||
@@ -755,7 +761,7 @@ static int isDate(
|
||||
sqlite3_value **argv,
|
||||
DateTime *p
|
||||
){
|
||||
int i = 1;
|
||||
int i;
|
||||
const unsigned char *z;
|
||||
int eType;
|
||||
memset(p, 0, sizeof(*p));
|
||||
@@ -764,14 +770,7 @@ static int isDate(
|
||||
}
|
||||
if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
||||
|| eType==SQLITE_INTEGER ){
|
||||
if( argc>=2 && sqlite3_value_type(argv[1])==SQLITE_TEXT
|
||||
&& strcmp((const char*)sqlite3_value_text(argv[1]),"unixepoch")==0 ){
|
||||
i = 2;
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*1000.0) +
|
||||
21086676*(i64)10000000;
|
||||
}else{
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
|
||||
}
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
}else{
|
||||
z = sqlite3_value_text(argv[0]);
|
||||
@@ -779,8 +778,8 @@ static int isDate(
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
while( i<argc ){
|
||||
z = sqlite3_value_text(argv[i++]);
|
||||
for(i=1; i<argc; i++){
|
||||
z = sqlite3_value_text(argv[i]);
|
||||
if( z==0 || parseModifier(context, (char*)z, p) ) return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -809,27 +808,10 @@ static void juliandayFunc(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Render an ISO-8601 year value into the given buffer. Return the
|
||||
** number of characters.
|
||||
**
|
||||
** Years are rendered differently depending on magnitude:
|
||||
**
|
||||
** 0..9999 YYYY
|
||||
** 10000..99999 +YYYYY
|
||||
** -9999..-1 -YYYY
|
||||
** -99999..-10000 -YYYYY
|
||||
*/
|
||||
static int renderYear(char *zBuf, int Y){
|
||||
sqlite3_snprintf(10, zBuf, Y<0 ? "%05d" : Y>=10000 ? "%+05d" : "%04d", Y);
|
||||
return sqlite3Strlen30(zBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** datetime( TIMESTRING, MOD, MOD, ...)
|
||||
**
|
||||
** Return YYYY-MM-DD HH:MM:SS. The YYYY might be +YYYYY or -YYYY or -YYYYY
|
||||
** depending on the year.
|
||||
** Return YYYY-MM-DD HH:MM:SS
|
||||
*/
|
||||
static void datetimeFunc(
|
||||
sqlite3_context *context,
|
||||
@@ -838,12 +820,10 @@ static void datetimeFunc(
|
||||
){
|
||||
DateTime x;
|
||||
if( isDate(context, argc, argv, &x)==0 ){
|
||||
int i;
|
||||
char zBuf[100];
|
||||
computeYMD_HMS(&x);
|
||||
i = renderYear(zBuf, x.Y);
|
||||
sqlite3_snprintf(sizeof(zBuf)-i, zBuf+i, "-%02d-%02d %02d:%02d:%02d",
|
||||
x.M, x.D, x.h, x.m, (int)(x.s));
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
x.Y, x.M, x.D, x.h, x.m, (int)(x.s));
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
@@ -870,10 +850,7 @@ static void timeFunc(
|
||||
/*
|
||||
** date( TIMESTRING, MOD, MOD, ...)
|
||||
**
|
||||
** Return YYYY-MM-DD
|
||||
** or +YYYYY-MM-DD
|
||||
** or -YYYY-MM-DD
|
||||
** or -YYYYY-MM-DD
|
||||
** Return YYYY-MM-DD
|
||||
*/
|
||||
static void dateFunc(
|
||||
sqlite3_context *context,
|
||||
@@ -882,11 +859,9 @@ static void dateFunc(
|
||||
){
|
||||
DateTime x;
|
||||
if( isDate(context, argc, argv, &x)==0 ){
|
||||
int i;
|
||||
char zBuf[100];
|
||||
computeYMD(&x);
|
||||
i = renderYear(zBuf, x.Y);
|
||||
sqlite3_snprintf(sizeof(zBuf)-i, zBuf+i, "-%02d-%02d", x.M, x.D);
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* nPage */
|
||||
0, /* mxParserStack */
|
||||
0, /* sharedCacheEnabled */
|
||||
SQLITE_DEFAULT_WORKER_THREADS, /* nWorker */
|
||||
/* All the rest should always be initialized to zero */
|
||||
0, /* isInit */
|
||||
0, /* inProgress */
|
||||
|
||||
+12
-3
@@ -1865,15 +1865,24 @@ static int xferOptimization(
|
||||
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
|
||||
}
|
||||
for(i=0; i<pDest->nCol; i++){
|
||||
if( pDest->aCol[i].affinity!=pSrc->aCol[i].affinity ){
|
||||
Column *pDestCol = &pDest->aCol[i];
|
||||
Column *pSrcCol = &pSrc->aCol[i];
|
||||
if( pDestCol->affinity!=pSrcCol->affinity ){
|
||||
return 0; /* Affinity must be the same on all columns */
|
||||
}
|
||||
if( !xferCompatibleCollation(pDest->aCol[i].zColl, pSrc->aCol[i].zColl) ){
|
||||
if( !xferCompatibleCollation(pDestCol->zColl, pSrcCol->zColl) ){
|
||||
return 0; /* Collating sequence must be the same on all columns */
|
||||
}
|
||||
if( pDest->aCol[i].notNull && !pSrc->aCol[i].notNull ){
|
||||
if( pDestCol->notNull && !pSrcCol->notNull ){
|
||||
return 0; /* tab2 must be NOT NULL if tab1 is */
|
||||
}
|
||||
/* Default values for second and subsequent columns need to match. */
|
||||
if( i>0
|
||||
&& ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0)
|
||||
|| (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0))
|
||||
){
|
||||
return 0; /* Default values must be the same for all columns */
|
||||
}
|
||||
}
|
||||
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
|
||||
if( pDestIdx->onError!=OE_None ){
|
||||
|
||||
+15
@@ -515,6 +515,13 @@ int sqlite3_config(int op, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
case SQLITE_CONFIG_WORKER_THREADS: {
|
||||
int n = va_arg(ap, int);
|
||||
if( n>SQLITE_MAX_WORKER_THREADS ) n = SQLITE_MAX_WORKER_THREADS;
|
||||
if( n>=0 ) sqlite3GlobalConfig.nWorker = n;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@@ -2497,6 +2504,7 @@ static int openDatabase(
|
||||
db->nextAutovac = -1;
|
||||
db->szMmap = sqlite3GlobalConfig.szMmap;
|
||||
db->nextPagesize = 0;
|
||||
db->nMaxSorterMmap = 0x7FFFFFFF;
|
||||
db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
|
||||
#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
|
||||
| SQLITE_AutoIndex
|
||||
@@ -3339,6 +3347,13 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, nMax); */
|
||||
case SQLITE_TESTCTRL_SORTER_MMAP: {
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
db->nMaxSorterMmap = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ int sqlite3MutexEnd(void){
|
||||
*/
|
||||
sqlite3_mutex *sqlite3_mutex_alloc(int id){
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
||||
}
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
|
||||
** that means that a mutex could not be allocated.
|
||||
*/
|
||||
static sqlite3_mutex *debugMutexAlloc(int id){
|
||||
static sqlite3_debug_mutex aStatic[6];
|
||||
static sqlite3_debug_mutex aStatic[8];
|
||||
sqlite3_debug_mutex *pNew = 0;
|
||||
switch( id ){
|
||||
case SQLITE_MUTEX_FAST:
|
||||
|
||||
+5
-1
@@ -96,10 +96,12 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
||||
** <li> SQLITE_MUTEX_RECURSIVE
|
||||
** <li> SQLITE_MUTEX_STATIC_MASTER
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_OPEN
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
@@ -133,6 +135,8 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER
|
||||
};
|
||||
sqlite3_mutex *p;
|
||||
|
||||
+6
-2
@@ -93,7 +93,9 @@ static int winMutexNotheld(sqlite3_mutex *p){
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static sqlite3_mutex winMutex_staticMutexes[6] = {
|
||||
static sqlite3_mutex winMutex_staticMutexes[] = {
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
@@ -159,10 +161,12 @@ static int winMutexEnd(void){
|
||||
** <li> SQLITE_MUTEX_RECURSIVE
|
||||
** <li> SQLITE_MUTEX_STATIC_MASTER
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_OPEN
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
|
||||
+108
-5
@@ -267,6 +267,7 @@ struct winFile {
|
||||
sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
|
||||
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
||||
#endif
|
||||
SQLiteThread *preCacheThread; /* Thread used to pre-cache file contents */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -948,11 +949,7 @@ static struct win_syscall {
|
||||
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
|
||||
DWORD))aSyscall[63].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINRT
|
||||
{ "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
|
||||
#else
|
||||
{ "WaitForSingleObjectEx", (SYSCALL)0, 0 },
|
||||
#endif
|
||||
|
||||
#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
|
||||
BOOL))aSyscall[64].pCurrent)
|
||||
@@ -1048,6 +1045,11 @@ static struct win_syscall {
|
||||
#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
|
||||
LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
|
||||
|
||||
{ "DuplicateHandle", (SYSCALL)DuplicateHandle, 0 },
|
||||
|
||||
#define osDuplicateHandle ((BOOL(WINAPI*)(HANDLE, \
|
||||
HANDLE,HANDLE,LPHANDLE,DWORD,BOOL,DWORD))aSyscall[76].pCurrent)
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
/*
|
||||
@@ -1279,6 +1281,13 @@ void sqlite3_win32_sleep(DWORD milliseconds){
|
||||
#endif
|
||||
}
|
||||
|
||||
DWORD sqlite3Win32Wait(HANDLE hObject){
|
||||
DWORD rc;
|
||||
while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
|
||||
TRUE))==WAIT_IO_COMPLETION ){}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
|
||||
** or WinCE. Return false (zero) for Win95, Win98, or WinME.
|
||||
@@ -2351,6 +2360,13 @@ static int winClose(sqlite3_file *id){
|
||||
assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE );
|
||||
OSTRACE(("CLOSE file=%p\n", pFile->h));
|
||||
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
if( pFile->preCacheThread ){
|
||||
void *pOut = 0;
|
||||
sqlite3ThreadJoin(pFile->preCacheThread, &pOut);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
winUnmapfile(pFile);
|
||||
#endif
|
||||
@@ -3195,6 +3211,84 @@ static int winDeviceCharacteristics(sqlite3_file *id){
|
||||
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
/*
|
||||
** Thread routine that attempts to read the entire file. This is used to
|
||||
** provide a hint to the operating system that the entire file should be held
|
||||
** in the cache.
|
||||
*/
|
||||
static void *winPreCacheThread(void *pCtx){
|
||||
winFile *pFile = (winFile*)pCtx;
|
||||
void *pBuf = 0;
|
||||
DWORD lastErrno;
|
||||
HANDLE dupHandle = NULL;
|
||||
DWORD dwSize, dwRet;
|
||||
DWORD dwAmt;
|
||||
DWORD nRead;
|
||||
|
||||
if( !osDuplicateHandle(GetCurrentProcess(), pFile->h, GetCurrentProcess(),
|
||||
&dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) ){
|
||||
pFile->lastErrno = osGetLastError();
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(winLogError(SQLITE_IOERR, pFile->lastErrno,
|
||||
"winPreCacheThread1", pFile->zPath));
|
||||
}
|
||||
dwSize = osSetFilePointer(dupHandle, 0, 0, FILE_END);
|
||||
if( (dwSize==INVALID_SET_FILE_POINTER
|
||||
&& ((lastErrno = osGetLastError())!=NO_ERROR)) ){
|
||||
pFile->lastErrno = lastErrno;
|
||||
osCloseHandle(dupHandle);
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR_SEEK\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
|
||||
"winPreCacheThread2", pFile->zPath));
|
||||
}
|
||||
dwRet = osSetFilePointer(dupHandle, 0, 0, FILE_BEGIN);
|
||||
if( (dwRet==INVALID_SET_FILE_POINTER
|
||||
&& ((lastErrno = osGetLastError())!=NO_ERROR)) ){
|
||||
pFile->lastErrno = lastErrno;
|
||||
osCloseHandle(dupHandle);
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR_SEEK\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
|
||||
"winPreCacheThread3", pFile->zPath));
|
||||
}
|
||||
dwAmt = 4194304; /* TODO: Tuning. */
|
||||
if( dwSize<dwAmt ){
|
||||
dwAmt = dwSize;
|
||||
}
|
||||
pBuf = sqlite3MallocZero( dwAmt );
|
||||
if( pBuf==0 ){
|
||||
osCloseHandle(dupHandle);
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR_NOMEM\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(SQLITE_IOERR_NOMEM);
|
||||
}
|
||||
while( 1 ){
|
||||
if( !osReadFile(dupHandle, pBuf, dwAmt, &nRead, 0) ){
|
||||
pFile->lastErrno = osGetLastError();
|
||||
sqlite3_free(pBuf);
|
||||
osCloseHandle(dupHandle);
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR_READ\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
|
||||
"winPreCacheThread4", pFile->zPath));
|
||||
}
|
||||
if( nRead<dwAmt ){
|
||||
sqlite3_free(pBuf);
|
||||
osCloseHandle(dupHandle);
|
||||
OSTRACE(("PRE-CACHE file=%p, rc=SQLITE_IOERR_SHORT_READ\n", dupHandle));
|
||||
return SQLITE_INT_TO_PTR(winLogError(SQLITE_IOERR_SHORT_READ,
|
||||
pFile->lastErrno,
|
||||
"winPreCacheThread5", pFile->zPath));
|
||||
}
|
||||
dwSize -= dwAmt;
|
||||
if( dwSize==0 ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_free(pBuf);
|
||||
osCloseHandle(dupHandle);
|
||||
return SQLITE_INT_TO_PTR(SQLITE_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Windows will only let you create file view mappings
|
||||
** on allocation size granularity boundaries.
|
||||
@@ -4701,6 +4795,15 @@ static int winOpen(
|
||||
pFile->mmapSizeActual = 0;
|
||||
pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
|
||||
#endif
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
sqlite3ThreadCreate(&pFile->preCacheThread, winPreCacheThread, pFile);
|
||||
|
||||
{
|
||||
void *pOut = 0;
|
||||
sqlite3ThreadJoin(pFile->preCacheThread, &pOut);
|
||||
pFile->preCacheThread = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenCounter(+1);
|
||||
return rc;
|
||||
@@ -5419,7 +5522,7 @@ int sqlite3_os_init(void){
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==76 );
|
||||
assert( ArraySize(aSyscall)==77 );
|
||||
|
||||
/* get memory map allocation granularity */
|
||||
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
|
||||
|
||||
+4
-2
@@ -1488,13 +1488,15 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 2);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pTab->szTabRow), 3);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (int)pTab->nRowEst, 4);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pTab->nRowLogEst), 4);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pIdx->szIdxRow), 3);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (int)pIdx->aiRowEst[0], 4);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]), 4);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
+95
-68
@@ -455,26 +455,43 @@ static KeyInfo *keyInfoFromExprList(
|
||||
);
|
||||
|
||||
/*
|
||||
** Insert code into "v" that will push the record in register regData
|
||||
** into the sorter.
|
||||
** Generate code that will push the record in registers regData
|
||||
** through regData+nData-1 onto the sorter.
|
||||
*/
|
||||
static void pushOntoSorter(
|
||||
Parse *pParse, /* Parser context */
|
||||
SortCtx *pSort, /* Information about the ORDER BY clause */
|
||||
Select *pSelect, /* The whole SELECT statement */
|
||||
int regData /* Register holding data to be sorted */
|
||||
int regData, /* First register holding data to be sorted */
|
||||
int nData, /* Number of elements in the data array */
|
||||
int nPrefixReg /* No. of reg prior to regData available for use */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int nExpr = pSort->pOrderBy->nExpr;
|
||||
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
|
||||
int regRecord = sqlite3GetTempReg(pParse);
|
||||
int nOBSat = pSort->nOBSat;
|
||||
int op;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nExpr+2-nOBSat, regRecord);
|
||||
Vdbe *v = pParse->pVdbe; /* Stmt under construction */
|
||||
int bSeq = ((pSort->sortFlags & SORTFLAG_UseSorter)==0);
|
||||
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
|
||||
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
|
||||
int regBase; /* Regs for sorter record */
|
||||
int regRecord = ++pParse->nMem; /* Assembled sorter record */
|
||||
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
|
||||
int op; /* Opcode to add sorter record to sorter */
|
||||
|
||||
assert( bSeq==0 || bSeq==1 );
|
||||
if( nPrefixReg ){
|
||||
assert( nPrefixReg==nExpr+bSeq );
|
||||
regBase = regData - nExpr - bSeq;
|
||||
}else{
|
||||
regBase = pParse->nMem + 1;
|
||||
pParse->nMem += nBase;
|
||||
}
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
|
||||
if( bSeq ){
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
||||
}
|
||||
if( nPrefixReg==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Move, regData, regBase+nExpr+bSeq, nData);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
|
||||
if( nOBSat>0 ){
|
||||
int regPrevKey; /* The first nOBSat columns of the previous row */
|
||||
int addrFirst; /* Address of the OP_IfNot opcode */
|
||||
@@ -485,12 +502,17 @@ static void pushOntoSorter(
|
||||
|
||||
regPrevKey = pParse->nMem+1;
|
||||
pParse->nMem += pSort->nOBSat;
|
||||
nKey = nExpr - pSort->nOBSat + 1;
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr); VdbeCoverage(v);
|
||||
nKey = nExpr - pSort->nOBSat + bSeq;
|
||||
if( bSeq ){
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
|
||||
}else{
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_SequenceTest, pSort->iECursor);
|
||||
}
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
|
||||
pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
|
||||
if( pParse->db->mallocFailed ) return;
|
||||
pOp->p2 = nKey + 1;
|
||||
pOp->p2 = nKey + nData;
|
||||
pKI = pOp->p4.pKeyInfo;
|
||||
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||
@@ -511,10 +533,6 @@ static void pushOntoSorter(
|
||||
op = OP_IdxInsert;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
||||
if( nOBSat==0 ){
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
|
||||
}
|
||||
if( pSelect->iLimit ){
|
||||
int addr1, addr2;
|
||||
int iLimit;
|
||||
@@ -629,6 +647,7 @@ static void selectInnerLoop(
|
||||
int eDest = pDest->eDest; /* How to dispose of results */
|
||||
int iParm = pDest->iSDParm; /* First argument to disposal method */
|
||||
int nResultCol; /* Number of result columns */
|
||||
int nPrefixReg = 0; /* Number of extra registers before regResult */
|
||||
|
||||
assert( v );
|
||||
assert( pEList!=0 );
|
||||
@@ -644,6 +663,11 @@ static void selectInnerLoop(
|
||||
nResultCol = pEList->nExpr;
|
||||
|
||||
if( pDest->iSdst==0 ){
|
||||
if( pSort ){
|
||||
nPrefixReg = pSort->pOrderBy->nExpr;
|
||||
if( !(pSort->sortFlags & SORTFLAG_UseSorter) ) nPrefixReg++;
|
||||
pParse->nMem += nPrefixReg;
|
||||
}
|
||||
pDest->iSdst = pParse->nMem+1;
|
||||
pParse->nMem += nResultCol;
|
||||
}else if( pDest->iSdst+nResultCol > pParse->nMem ){
|
||||
@@ -760,10 +784,10 @@ static void selectInnerLoop(
|
||||
case SRT_DistFifo:
|
||||
case SRT_Table:
|
||||
case SRT_EphemTab: {
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
|
||||
testcase( eDest==SRT_Table );
|
||||
testcase( eDest==SRT_EphemTab );
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
if( eDest==SRT_DistFifo ){
|
||||
/* If the destination is DistFifo, then cursor (iParm+1) is open
|
||||
@@ -778,7 +802,7 @@ static void selectInnerLoop(
|
||||
}
|
||||
#endif
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg);
|
||||
}else{
|
||||
int r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
|
||||
@@ -786,7 +810,7 @@ static void selectInnerLoop(
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -804,7 +828,7 @@ static void selectInnerLoop(
|
||||
** ORDER BY in this case since the order of entries in the set
|
||||
** does not matter. But there might be a LIMIT clause, in which
|
||||
** case the order does matter */
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
||||
@@ -830,7 +854,7 @@ static void selectInnerLoop(
|
||||
case SRT_Mem: {
|
||||
assert( nResultCol==1 );
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
||||
}else{
|
||||
sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
@@ -844,10 +868,7 @@ static void selectInnerLoop(
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
if( pSort ){
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg);
|
||||
}else if( eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
||||
}else{
|
||||
@@ -1127,46 +1148,62 @@ static void generateSortTail(
|
||||
int addr;
|
||||
int addrOnce = 0;
|
||||
int iTab;
|
||||
int pseudoTab = 0;
|
||||
ExprList *pOrderBy = pSort->pOrderBy;
|
||||
int eDest = pDest->eDest;
|
||||
int iParm = pDest->iSDParm;
|
||||
int regRow;
|
||||
int regRowid;
|
||||
int nKey;
|
||||
int iSortTab; /* Sorter cursor to read from */
|
||||
int nSortData; /* Trailing values to read from sorter */
|
||||
u8 p5; /* p5 parameter for 1st OP_Column */
|
||||
int i;
|
||||
int bSeq; /* True if sorter record includes seq. no. */
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
struct ExprList_item *aOutEx = p->pEList->a;
|
||||
#endif
|
||||
|
||||
if( pSort->labelBkOut ){
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
|
||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
iTab = pSort->iECursor;
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
pseudoTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn);
|
||||
regRowid = 0;
|
||||
regRow = pDest->iSdst;
|
||||
nSortData = nColumn;
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
nSortData = 1;
|
||||
}
|
||||
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
int regSortOut = ++pParse->nMem;
|
||||
int ptab2 = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, nKey+2);
|
||||
iSortTab = pParse->nTab++;
|
||||
if( pSort->labelBkOut ){
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
||||
VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ptab2, nKey+1, regRow);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
p5 = OPFLAG_CLEARCACHE;
|
||||
bSeq = 0;
|
||||
}else{
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, nKey+1, regRow);
|
||||
iSortTab = iTab;
|
||||
p5 = 0;
|
||||
bSeq = 1;
|
||||
}
|
||||
for(i=0; i<nSortData; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
|
||||
if( i==0 ) sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Table:
|
||||
@@ -1195,17 +1232,9 @@ static void generateSortTail(
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
int i;
|
||||
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
for(i=0; i<nColumn; i++){
|
||||
assert( regRow!=pDest->iSdst+i );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iSdst+i);
|
||||
if( i==0 ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
}
|
||||
}
|
||||
if( eDest==SRT_Output ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
|
||||
@@ -1215,9 +1244,10 @@ static void generateSortTail(
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
|
||||
if( regRowid ){
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
}
|
||||
/* The bottom of the loop
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrContinue);
|
||||
@@ -1228,9 +1258,6 @@ static void generateSortTail(
|
||||
}
|
||||
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
|
||||
sqlite3VdbeResolveLabel(v, addrBreak);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1690,7 +1717,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
assert( db->lookaside.bEnabled==0 );
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = 0;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
||||
pTab->iPKey = -1;
|
||||
@@ -3829,7 +3856,7 @@ static int withExpand(
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
||||
if( db->mallocFailed ) return SQLITE_NOMEM;
|
||||
@@ -4005,7 +4032,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
#endif
|
||||
}else{
|
||||
@@ -4655,7 +4682,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
|
||||
@@ -4686,7 +4713,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
@@ -4760,8 +4787,9 @@ int sqlite3Select(
|
||||
sSort.iECursor = pParse->nTab++;
|
||||
sSort.addrSortIndex =
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
sSort.iECursor, sSort.pOrderBy->nExpr+2, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO
|
||||
);
|
||||
}else{
|
||||
sSort.addrSortIndex = -1;
|
||||
}
|
||||
@@ -4892,7 +4920,7 @@ int sqlite3Select(
|
||||
sNC.pSrcList = pTabList;
|
||||
sNC.pAggInfo = &sAggInfo;
|
||||
sAggInfo.mnReg = pParse->nMem+1;
|
||||
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
|
||||
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
|
||||
sAggInfo.pGroupBy = pGroupBy;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pEList);
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
|
||||
@@ -4985,8 +5013,8 @@ int sqlite3Select(
|
||||
|
||||
groupBySort = 1;
|
||||
nGroupBy = pGroupBy->nExpr;
|
||||
nCol = nGroupBy + 1;
|
||||
j = nGroupBy+1;
|
||||
nCol = nGroupBy;
|
||||
j = nGroupBy;
|
||||
for(i=0; i<sAggInfo.nColumn; i++){
|
||||
if( sAggInfo.aCol[i].iSorterColumn>=j ){
|
||||
nCol++;
|
||||
@@ -4996,8 +5024,7 @@ int sqlite3Select(
|
||||
regBase = sqlite3GetTempRange(pParse, nCol);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
|
||||
j = nGroupBy+1;
|
||||
j = nGroupBy;
|
||||
for(i=0; i<sAggInfo.nColumn; i++){
|
||||
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
|
||||
if( pCol->iSorterColumn>=j ){
|
||||
|
||||
+2
-1
@@ -3536,7 +3536,8 @@ static void main_init(struct callback_data *data) {
|
||||
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
|
||||
sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> ");
|
||||
sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> ");
|
||||
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
sqlite3_config(SQLITE_CONFIG_WORKER_THREADS, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+19
-3
@@ -1715,6 +1715,16 @@ struct sqlite3_mem_methods {
|
||||
** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
|
||||
** that specifies the maximum size of the created heap.
|
||||
** </dl>
|
||||
**
|
||||
** [[SQLITE_CONFIG_WORKER_THREADS]]
|
||||
** <dt>SQLITE_CONFIG_WORKER_THREADS
|
||||
** <dd>^SQLITE_CONFIG_WORKER_THREADS takes a single argument of type int.
|
||||
** It is used to set the number of background worker threads that may be
|
||||
** launched when sorting large amounts of data. A value of 0 means launch
|
||||
** no background threads at all. The maximum number of background threads
|
||||
** allowed is configured at build-time by the SQLITE_MAX_WORKER_THREADS
|
||||
** pre-processor option.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
|
||||
@@ -1739,6 +1749,7 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||
#define SQLITE_CONFIG_WORKER_THREADS 24 /* int nWorker */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@@ -5817,10 +5828,12 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
|
||||
** <li> SQLITE_MUTEX_RECURSIVE
|
||||
** <li> SQLITE_MUTEX_STATIC_MASTER
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_OPEN
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU2
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** </ul>)^
|
||||
**
|
||||
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
|
||||
@@ -6024,6 +6037,8 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
|
||||
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
|
||||
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
|
||||
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
|
||||
#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
|
||||
#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Retrieve the mutex for a database connection
|
||||
@@ -6119,7 +6134,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
|
||||
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
|
||||
#define SQLITE_TESTCTRL_BYTEORDER 22
|
||||
#define SQLITE_TESTCTRL_LAST 22
|
||||
#define SQLITE_TESTCTRL_SORTER_MMAP 23
|
||||
#define SQLITE_TESTCTRL_LAST 23
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
|
||||
+43
-5
@@ -422,6 +422,26 @@
|
||||
# define SQLITE_TEMP_STORE_xc 1 /* Exclude from ctime.c */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if
|
||||
** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it
|
||||
** to zero.
|
||||
*/
|
||||
#if SQLITE_TEMP_STORE==3
|
||||
# undef SQLITE_MAX_WORKER_THREADS
|
||||
#endif
|
||||
#ifndef SQLITE_MAX_WORKER_THREADS
|
||||
# define SQLITE_MAX_WORKER_THREADS 0
|
||||
#endif
|
||||
#ifndef SQLITE_DEFAULT_WORKER_THREADS
|
||||
# define SQLITE_DEFAULT_WORKER_THREADS 0
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS
|
||||
# undef SQLITE_MAX_WORKER_THREADS
|
||||
# define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** GCC does not define the offsetof() macro so we'll have to do it
|
||||
** ourselves.
|
||||
@@ -525,10 +545,10 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
|
||||
** gives a possible range of values of approximately 1.0e986 to 1e-986.
|
||||
** But the allowed values are "grainy". Not every value is representable.
|
||||
** For example, quantities 16 and 17 are both represented by a LogEst
|
||||
** of 40. However, since LogEst quantatites are suppose to be estimates,
|
||||
** of 40. However, since LogEst quantaties are suppose to be estimates,
|
||||
** not exact values, this imprecision is not a problem.
|
||||
**
|
||||
** "LogEst" is short for "Logarithimic Estimate".
|
||||
** "LogEst" is short for "Logarithmic Estimate".
|
||||
**
|
||||
** Examples:
|
||||
** 1 -> 0 20 -> 43 10000 -> 132
|
||||
@@ -805,6 +825,7 @@ typedef struct PrintfArguments PrintfArguments;
|
||||
typedef struct RowSet RowSet;
|
||||
typedef struct Savepoint Savepoint;
|
||||
typedef struct Select Select;
|
||||
typedef struct SQLiteThread SQLiteThread;
|
||||
typedef struct SelectDest SelectDest;
|
||||
typedef struct SrcList SrcList;
|
||||
typedef struct StrAccum StrAccum;
|
||||
@@ -984,6 +1005,7 @@ struct sqlite3 {
|
||||
int nChange; /* Value returned by sqlite3_changes() */
|
||||
int nTotalChange; /* Value returned by sqlite3_total_changes() */
|
||||
int aLimit[SQLITE_N_LIMIT]; /* Limits */
|
||||
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
|
||||
struct sqlite3InitInfo { /* Information used during initialization */
|
||||
int newTnum; /* Rootpage of table being initialized */
|
||||
u8 iDb; /* Which db file is being initialized */
|
||||
@@ -1471,7 +1493,7 @@ struct Table {
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
ExprList *pCheck; /* All CHECK constraints */
|
||||
#endif
|
||||
tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
int tnum; /* Root BTree node for this table (see note above) */
|
||||
i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */
|
||||
i16 nCol; /* Number of columns in this table */
|
||||
@@ -1644,7 +1666,7 @@ struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
i8 default_rc; /* Comparison result if keys are equal */
|
||||
u8 isCorrupt; /* Corruption detected by xRecordCompare() */
|
||||
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
|
||||
Mem *aMem; /* Values */
|
||||
int r1; /* Value to return if (lhs > rhs) */
|
||||
int r2; /* Value to return if (rhs < lhs) */
|
||||
@@ -1680,7 +1702,7 @@ struct UnpackedRecord {
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
LogEst *aiRowLogEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
@@ -2702,6 +2724,7 @@ struct Sqlite3Config {
|
||||
int nPage; /* Number of pages in pPage[] */
|
||||
int mxParserStack; /* maximum depth of the parser stack */
|
||||
int sharedCacheEnabled; /* true if shared-cache mode enabled */
|
||||
int nWorker; /* Number of worker threads to use */
|
||||
/* The above might be initialized to non-zero. The following need to always
|
||||
** initially be zero, however. */
|
||||
int isInit; /* True after initialization has finished */
|
||||
@@ -3653,4 +3676,19 @@ SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
|
||||
#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
|
||||
#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */
|
||||
|
||||
/*
|
||||
** Threading interface
|
||||
*/
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
|
||||
int sqlite3ThreadJoin(SQLiteThread*, void**);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Win32 interface
|
||||
*/
|
||||
#if SQLITE_OS_WIN
|
||||
DWORD sqlite3Win32Wait(HANDLE hObject);
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITEINT_H_ */
|
||||
|
||||
+164
@@ -2703,6 +2703,46 @@ bad_args:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: add_test_utf16bin_collate <db ptr>
|
||||
**
|
||||
** Add a utf-16 collation sequence named "utf16bin" to the database
|
||||
** handle. This collation sequence compares arguments in the same way as the
|
||||
** built-in collation "binary".
|
||||
*/
|
||||
static int test_utf16bin_collate_func(
|
||||
void *pCtx,
|
||||
int nA, const void *zA,
|
||||
int nB, const void *zB
|
||||
){
|
||||
int nCmp = (nA>nB ? nB : nA);
|
||||
int res = memcmp(zA, zB, nCmp);
|
||||
if( res==0 ) res = nA - nB;
|
||||
return res;
|
||||
}
|
||||
static int test_utf16bin_collate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ) goto bad_args;
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_create_collation(db, "utf16bin", SQLITE_UTF16, 0,
|
||||
test_utf16bin_collate_func
|
||||
);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
return TCL_OK;
|
||||
|
||||
bad_args:
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** When the collation needed callback is invoked, record the name of
|
||||
** the requested collating function here. The recorded name is linked
|
||||
@@ -5844,6 +5884,7 @@ static int test_test_control(
|
||||
int i;
|
||||
} aVerb[] = {
|
||||
{ "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT },
|
||||
{ "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP },
|
||||
};
|
||||
int iVerb;
|
||||
int iFlag;
|
||||
@@ -5871,6 +5912,19 @@ static int test_test_control(
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, val);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_TESTCTRL_SORTER_MMAP: {
|
||||
int val;
|
||||
sqlite3 *db;
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "DB LIMIT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR;
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
@@ -6282,6 +6336,113 @@ static int tclLoadStaticExtensionCmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sorter_test_fakeheap BOOL
|
||||
**
|
||||
*/
|
||||
static int sorter_test_fakeheap(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int bArg;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[1], &bArg) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( bArg ){
|
||||
if( sqlite3GlobalConfig.pHeap==0 ){
|
||||
sqlite3GlobalConfig.pHeap = SQLITE_INT_TO_PTR(-1);
|
||||
}
|
||||
}else{
|
||||
if( sqlite3GlobalConfig.pHeap==SQLITE_INT_TO_PTR(-1) ){
|
||||
sqlite3GlobalConfig.pHeap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sorter_test_sort4_helper DB SQL1 NSTEP SQL2
|
||||
**
|
||||
** Compile SQL statement $SQL1 and step it $NSTEP times. For each row,
|
||||
** check that the leftmost and rightmost columns returned are both integers,
|
||||
** and that both contain the same value.
|
||||
**
|
||||
** Then execute statement $SQL2. Check that the statement returns the same
|
||||
** set of integers in the same order as in the previous step (using $SQL1).
|
||||
*/
|
||||
static int sorter_test_sort4_helper(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zSql1;
|
||||
const char *zSql2;
|
||||
int nStep;
|
||||
int iStep;
|
||||
int iCksum1 = 0;
|
||||
int iCksum2 = 0;
|
||||
int rc;
|
||||
int iB;
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
if( objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SQL1 NSTEP SQL2");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql1 = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &nStep) ) return TCL_ERROR;
|
||||
zSql2 = Tcl_GetString(objv[4]);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql1, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
iB = sqlite3_column_count(pStmt)-1;
|
||||
for(iStep=0; iStep<nStep && SQLITE_ROW==sqlite3_step(pStmt); iStep++){
|
||||
int a = sqlite3_column_int(pStmt, 0);
|
||||
if( a!=sqlite3_column_int(pStmt, iB) ){
|
||||
Tcl_AppendResult(interp, "data error: (a!=b)", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
iCksum1 += (iCksum1 << 3) + a;
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql2, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
for(iStep=0; SQLITE_ROW==sqlite3_step(pStmt); iStep++){
|
||||
int a = sqlite3_column_int(pStmt, 0);
|
||||
iCksum2 += (iCksum2 << 3) + a;
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
if( iCksum1!=iCksum2 ){
|
||||
Tcl_AppendResult(interp, "checksum mismatch", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
sql_error:
|
||||
Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
@@ -6481,6 +6642,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "add_test_collate", test_collate, 0 },
|
||||
{ "add_test_collate_needed", test_collate_needed, 0 },
|
||||
{ "add_test_function", test_function, 0 },
|
||||
{ "add_test_utf16bin_collate", test_utf16bin_collate, 0 },
|
||||
#endif
|
||||
{ "sqlite3_test_errstr", test_errstr, 0 },
|
||||
{ "tcl_variable_type", tcl_variable_type, 0 },
|
||||
@@ -6514,6 +6676,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "getrusage", test_getrusage },
|
||||
#endif
|
||||
{ "load_static_extension", tclLoadStaticExtensionCmd },
|
||||
{ "sorter_test_fakeheap", sorter_test_fakeheap },
|
||||
{ "sorter_test_sort4_helper", sorter_test_sort4_helper },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
|
||||
@@ -99,6 +99,10 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mmap", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
Tcl_SetVar2(interp, "sqlite_options", "worker_threads",
|
||||
STRINGVALUE(SQLITE_MAX_WORKER_THREADS), TCL_GLOBAL_ONLY
|
||||
);
|
||||
|
||||
#if 1 /* def SQLITE_MEMDEBUG */
|
||||
Tcl_SetVar2(interp, "sqlite_options", "memdebug", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
||||
@@ -1253,6 +1253,32 @@ static int test_config_cis(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_worker_threads N
|
||||
*/
|
||||
static int test_config_worker_threads(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
int nThread;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &nThread) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_config(SQLITE_CONFIG_WORKER_THREADS, nThread);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_dump_memsys3 FILENAME
|
||||
** sqlite3_dump_memsys5 FILENAME
|
||||
@@ -1506,6 +1532,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_config_error", test_config_error ,0 },
|
||||
{ "sqlite3_config_uri", test_config_uri ,0 },
|
||||
{ "sqlite3_config_cis", test_config_cis ,0 },
|
||||
{ "sqlite3_config_worker_threads", test_config_worker_threads ,0 },
|
||||
{ "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 },
|
||||
{ "sqlite3_dump_memsys3", test_dump_memsys3 ,3 },
|
||||
{ "sqlite3_dump_memsys5", test_dump_memsys3 ,5 },
|
||||
|
||||
+183
-17
@@ -35,6 +35,8 @@ struct Circle {
|
||||
double centerx;
|
||||
double centery;
|
||||
double radius;
|
||||
double mxArea;
|
||||
int eScoreType;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -50,11 +52,7 @@ static void circle_del(void *p){
|
||||
static int circle_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
sqlite3_rtree_dbl *aCoord,
|
||||
int *pRes
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
@@ -62,7 +60,12 @@ static int circle_geom(
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
|
||||
if( p->pUser==0 ){
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
pCircle = (Circle *)p->pUser;
|
||||
if( pCircle==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
@@ -108,14 +111,9 @@ static int circle_geom(
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0;
|
||||
}
|
||||
|
||||
pCircle = (Circle *)p->pUser;
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
@@ -154,6 +152,170 @@ static int circle_geom(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of "circle" r-tree geometry callback using the
|
||||
** 2nd-generation interface that allows scoring.
|
||||
*/
|
||||
static int circle_query_func(sqlite3_rtree_query_info *p){
|
||||
int i; /* Iterator variable */
|
||||
Circle *pCircle; /* Structure defining circular region */
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
int nWithin = 0; /* Number of corners inside the circle */
|
||||
|
||||
xmin = p->aCoord[0];
|
||||
xmax = p->aCoord[1];
|
||||
ymin = p->aCoord[2];
|
||||
ymax = p->aCoord[3];
|
||||
pCircle = (Circle *)p->pUser;
|
||||
if( pCircle==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
/* This geometry callback is for use with a 2-dimensional r-tree table.
|
||||
** Return an error if the table does not have exactly 2 dimensions. */
|
||||
if( p->nCoord!=4 ) return SQLITE_ERROR;
|
||||
|
||||
/* Test that the correct number of parameters (4) have been supplied,
|
||||
** and that the parameters are in range (that the radius of the circle
|
||||
** radius is greater than zero). */
|
||||
if( p->nParam!=4 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
|
||||
|
||||
/* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
|
||||
** if the allocation fails. */
|
||||
pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
|
||||
if( !pCircle ) return SQLITE_NOMEM;
|
||||
p->xDelUser = circle_del;
|
||||
|
||||
/* Record the center and radius of the circular region. One way that
|
||||
** tested bounding boxes that intersect the circular region are detected
|
||||
** is by testing if each corner of the bounding box lies within radius
|
||||
** units of the center of the circle. */
|
||||
pCircle->centerx = p->aParam[0];
|
||||
pCircle->centery = p->aParam[1];
|
||||
pCircle->radius = p->aParam[2];
|
||||
pCircle->eScoreType = (int)p->aParam[3];
|
||||
|
||||
/* Define two bounding box regions. The first, aBox[0], extends to
|
||||
** infinity in the X dimension. It covers the same range of the Y dimension
|
||||
** as the circular region. The second, aBox[1], extends to infinity in
|
||||
** the Y dimension and is constrained to the range of the circle in the
|
||||
** X dimension.
|
||||
**
|
||||
** Then imagine each box is split in half along its short axis by a line
|
||||
** that intersects the center of the circular region. A bounding box
|
||||
** being tested can be said to intersect the circular region if it contains
|
||||
** points from each half of either of the two infinite bounding boxes.
|
||||
*/
|
||||
pCircle->aBox[0].xmin = pCircle->centerx;
|
||||
pCircle->aBox[0].xmax = pCircle->centerx;
|
||||
pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
|
||||
pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
|
||||
pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
pCircle->mxArea = 200.0*200.0;
|
||||
}
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
** return SQLITE_OK in this case. */
|
||||
for(i=0; i<4; i++){
|
||||
double x = (i&0x01) ? xmax : xmin;
|
||||
double y = (i&0x02) ? ymax : ymin;
|
||||
double d2;
|
||||
|
||||
d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
|
||||
d2 += (y-pCircle->centery)*(y-pCircle->centery);
|
||||
if( d2<(pCircle->radius*pCircle->radius) ) nWithin++;
|
||||
}
|
||||
|
||||
/* Check if the bounding box covers any other part of the circular region.
|
||||
** See comments above for a description of how this test works. If it does
|
||||
** cover part of the circular region, set the output variable to true
|
||||
** and return SQLITE_OK. */
|
||||
if( nWithin==0 ){
|
||||
for(i=0; i<2; i++){
|
||||
if( xmin<=pCircle->aBox[i].xmin
|
||||
&& xmax>=pCircle->aBox[i].xmax
|
||||
&& ymin<=pCircle->aBox[i].ymin
|
||||
&& ymax>=pCircle->aBox[i].ymax
|
||||
){
|
||||
nWithin = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pCircle->eScoreType==1 ){
|
||||
/* Depth first search */
|
||||
p->rScore = p->iLevel;
|
||||
}else if( pCircle->eScoreType==2 ){
|
||||
/* Breadth first search */
|
||||
p->rScore = 100 - p->iLevel;
|
||||
}else if( pCircle->eScoreType==3 ){
|
||||
/* Depth-first search, except sort the leaf nodes by area with
|
||||
** the largest area first */
|
||||
if( p->iLevel==1 ){
|
||||
p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea;
|
||||
if( p->rScore<0.01 ) p->rScore = 0.01;
|
||||
}else{
|
||||
p->rScore = 0.0;
|
||||
}
|
||||
}else if( pCircle->eScoreType==4 ){
|
||||
/* Depth-first search, except exclude odd rowids */
|
||||
p->rScore = p->iLevel;
|
||||
if( p->iRowid&1 ) nWithin = 0;
|
||||
}else{
|
||||
/* Breadth-first search, except exclude odd rowids */
|
||||
p->rScore = 100 - p->iLevel;
|
||||
if( p->iRowid&1 ) nWithin = 0;
|
||||
}
|
||||
if( nWithin==0 ){
|
||||
p->eWithin = NOT_WITHIN;
|
||||
}else if( nWithin>=4 ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else{
|
||||
p->eWithin = PARTLY_WITHIN;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Implementation of "breadthfirstsearch" r-tree geometry callback using the
|
||||
** 2nd-generation interface that allows scoring.
|
||||
**
|
||||
** ... WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ...
|
||||
**
|
||||
** It returns all entries whose bounding boxes overlap with $x0,$x1,$y0,$y1.
|
||||
*/
|
||||
static int bfs_query_func(sqlite3_rtree_query_info *p){
|
||||
double x0,x1,y0,y1; /* Dimensions of box being tested */
|
||||
double bx0,bx1,by0,by1; /* Boundary of the query function */
|
||||
|
||||
if( p->nParam!=4 ) return SQLITE_ERROR;
|
||||
x0 = p->aCoord[0];
|
||||
x1 = p->aCoord[1];
|
||||
y0 = p->aCoord[2];
|
||||
y1 = p->aCoord[3];
|
||||
bx0 = p->aParam[0];
|
||||
bx1 = p->aParam[1];
|
||||
by0 = p->aParam[2];
|
||||
by1 = p->aParam[3];
|
||||
p->rScore = 100 - p->iLevel;
|
||||
if( p->eParentWithin==FULLY_WITHIN ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else if( x0>=bx0 && x1<=bx1 && y0>=by0 && y1<=by1 ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else if( x1>=bx0 && x0<=bx1 && y1>=by0 && y0<=by1 ){
|
||||
p->eWithin = PARTLY_WITHIN;
|
||||
}else{
|
||||
p->eWithin = NOT_WITHIN;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* END of implementation of "circle" geometry callback.
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
@@ -194,11 +356,7 @@ static int gHere = 42;
|
||||
static int cube_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
sqlite3_rtree_dbl *aCoord,
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
@@ -293,6 +451,14 @@ static int register_circle_geom(
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_rtree_query_callback(db, "Qcircle",
|
||||
circle_query_func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_rtree_query_callback(db, "breadthfirstsearch",
|
||||
bfs_query_func, 0, 0);
|
||||
}
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
|
||||
+1
-1
@@ -678,7 +678,7 @@ static int vfstraceAccess(
|
||||
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
|
||||
sqlite3_vfs *pRoot = pInfo->pRootVfs;
|
||||
int rc;
|
||||
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
|
||||
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
|
||||
pInfo->zVfsName, zPath, flags);
|
||||
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
|
||||
vfstrace_print_errcode(pInfo, " -> %s", rc);
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
** 2012 July 21
|
||||
**
|
||||
** 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 presents a simple cross-platform threading interface for
|
||||
** use internally by SQLite.
|
||||
**
|
||||
** A "thread" can be created using sqlite3ThreadCreate(). This thread
|
||||
** runs independently of its creator until it is joined using
|
||||
** sqlite3ThreadJoin(), at which point it terminates.
|
||||
**
|
||||
** Threads do not have to be real. It could be that the work of the
|
||||
** "thread" is done by the main thread at either the sqlite3ThreadCreate()
|
||||
** or sqlite3ThreadJoin() call. This is, in fact, what happens in
|
||||
** single threaded systems. Nothing in SQLite requires multiple threads.
|
||||
** This interface exists so that applications that want to take advantage
|
||||
** of multiple cores can do so, while also allowing applications to stay
|
||||
** single-threaded if desired.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
|
||||
/********************************* Unix Pthreads ****************************/
|
||||
#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
||||
|
||||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||||
#include <pthread.h>
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
pthread_t tid; /* Thread ID */
|
||||
int done; /* Set to true when thread finishes */
|
||||
void *pOut; /* Result returned by the thread */
|
||||
void *(*xTask)(void*); /* The thread routine */
|
||||
void *pIn; /* Argument to the thread */
|
||||
};
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0
|
||||
|| pthread_create(&p->tid, 0, xTask, pIn)!=0
|
||||
){
|
||||
p->done = 1;
|
||||
p->pOut = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
int rc;
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( p->done ){
|
||||
*ppOut = p->pOut;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = pthread_join(p->tid, ppOut);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
return rc ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
|
||||
/******************************** End Unix Pthreads *************************/
|
||||
|
||||
|
||||
/********************************* Win32 Threads ****************************/
|
||||
#if SQLITE_OS_WIN && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
|
||||
|
||||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||||
#include <process.h>
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
uintptr_t tid; /* The thread handle */
|
||||
void *(*xTask)(void*); /* The routine to run as a thread */
|
||||
void *pIn; /* Argument to xTask */
|
||||
void *pResult; /* Result of xTask */
|
||||
};
|
||||
|
||||
/* Thread procedure Win32 compatibility shim */
|
||||
static void sqlite3ThreadProc(
|
||||
void *pArg /* IN: Pointer to the SQLiteThread structure */
|
||||
){
|
||||
SQLiteThread *p = (SQLiteThread *)pArg;
|
||||
|
||||
assert( p!=0 );
|
||||
assert( p->xTask!=0 );
|
||||
p->pResult = p->xTask(p->pIn);
|
||||
_endthread();
|
||||
}
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0 ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
}else{
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
p->tid = _beginthread(sqlite3ThreadProc, 0, p);
|
||||
if( p->tid==(uintptr_t)-1 ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
}
|
||||
if( p->xTask==0 ){
|
||||
p->pResult = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
DWORD rc;
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( p->xTask==0 ){
|
||||
rc = WAIT_OBJECT_0;
|
||||
}else{
|
||||
rc = sqlite3Win32Wait((HANDLE)p->tid);
|
||||
assert( rc!=WAIT_IO_COMPLETION );
|
||||
}
|
||||
if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
|
||||
sqlite3_free(p);
|
||||
return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINRT */
|
||||
/******************************** End Win32 Threads *************************/
|
||||
|
||||
|
||||
/********************************* Single-Threaded **************************/
|
||||
#ifndef SQLITE_THREADS_IMPLEMENTED
|
||||
/*
|
||||
** This implementation does not actually create a new thread. It does the
|
||||
** work of the thread in the main thread, when either the thread is created
|
||||
** or when it is joined
|
||||
*/
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
void *(*xTask)(void*); /* The routine to run as a thread */
|
||||
void *pIn; /* Argument to xTask */
|
||||
void *pResult; /* Result of xTask */
|
||||
};
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
}else{
|
||||
p->xTask = 0;
|
||||
p->pResult = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( p->xTask ){
|
||||
*ppOut = p->xTask(p->pIn);
|
||||
}else{
|
||||
*ppOut = p->pResult;
|
||||
}
|
||||
sqlite3_free(p);
|
||||
|
||||
#if defined(SQLITE_TEST)
|
||||
{
|
||||
void *pTstAlloc = sqlite3Malloc(10);
|
||||
if (!pTstAlloc) return SQLITE_NOMEM;
|
||||
sqlite3_free(pTstAlloc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */
|
||||
/****************************** End Single-Threaded *************************/
|
||||
#endif /* SQLITE_MAX_WORKER_THREADS>0 */
|
||||
+2
-2
@@ -1246,8 +1246,8 @@ LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an integer into a LogEst. In other words, compute a
|
||||
** good approximatation for 10*log2(x).
|
||||
** Convert an integer into a LogEst. In other words, compute an
|
||||
** approximation for 10*log2(x).
|
||||
*/
|
||||
LogEst sqlite3LogEst(u64 x){
|
||||
static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
|
||||
|
||||
+30
-6
@@ -3390,11 +3390,15 @@ case OP_OpenEphemeral: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SorterOpen P1 P2 * P4 *
|
||||
/* Opcode: SorterOpen P1 P2 P3 P4 *
|
||||
**
|
||||
** This opcode works like OP_OpenEphemeral except that it opens
|
||||
** a transient index that is specifically designed to sort large
|
||||
** tables using an external merge-sort algorithm.
|
||||
**
|
||||
** If argument P3 is non-zero, then it indicates that the sorter may
|
||||
** assume that a stable sort considering the first P3 fields of each
|
||||
** key is sufficient to produce the required results.
|
||||
*/
|
||||
case OP_SorterOpen: {
|
||||
VdbeCursor *pCx;
|
||||
@@ -3406,7 +3410,25 @@ case OP_SorterOpen: {
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pCx->pKeyInfo->db==db );
|
||||
assert( pCx->pKeyInfo->enc==ENC(db) );
|
||||
rc = sqlite3VdbeSorterInit(db, pCx);
|
||||
rc = sqlite3VdbeSorterInit(db, pOp->p3, pCx);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SequenceTest P1 P2 * * *
|
||||
** Synopsis: if( cursor[P1].ctr++ ) pc = P2
|
||||
**
|
||||
** P1 is a sorter cursor. If the sequence counter is currently zero, jump
|
||||
** to P2. Regardless of whether or not the jump is taken, increment the
|
||||
** the sequence value.
|
||||
*/
|
||||
case OP_SequenceTest: {
|
||||
VdbeCursor *pC;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC->pSorter );
|
||||
if( (pC->seqCount++)==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4212,6 +4234,7 @@ case OP_SorterCompare: {
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
nIgnore = pOp->p4.i;
|
||||
res = 0;
|
||||
rc = sqlite3VdbeSorterCompare(pC, pIn3, nIgnore, &res);
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
if( res ){
|
||||
@@ -4232,6 +4255,7 @@ case OP_SorterData: {
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( isSorter(pC) );
|
||||
rc = sqlite3VdbeSorterRowkey(pC, pOut);
|
||||
assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4461,7 +4485,7 @@ case OP_Rewind: { /* jump */
|
||||
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
|
||||
res = 1;
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterRewind(db, pC, &res);
|
||||
rc = sqlite3VdbeSorterRewind(pC, &res);
|
||||
}else{
|
||||
pCrsr = pC->pCursor;
|
||||
assert( pCrsr );
|
||||
@@ -4620,7 +4644,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
rc = ExpandBlob(pIn2);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
|
||||
rc = sqlite3VdbeSorterWrite(pC, pIn2);
|
||||
}else{
|
||||
nKey = pIn2->n;
|
||||
zKey = pIn2->z;
|
||||
@@ -6323,8 +6347,8 @@ default: { /* This is really OP_Noop and OP_Explain */
|
||||
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
u64 elapsed = sqlite3Hwtime() - start;
|
||||
pOp->cycles += elapsed;
|
||||
u64 endTime = sqlite3Hwtime();
|
||||
if( endTime>start ) pOp->cycles += endTime - start;
|
||||
pOp->cnt++;
|
||||
}
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -437,13 +437,13 @@ void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
int sqlite3VdbeTransferError(Vdbe *p);
|
||||
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
|
||||
void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
|
||||
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
|
||||
|
||||
+35
-30
@@ -3137,10 +3137,14 @@ void sqlite3VdbeRecordUnpack(
|
||||
** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
|
||||
** in assert() statements to ensure that the optimized code in
|
||||
** sqlite3VdbeRecordCompare() returns results with these two primitives.
|
||||
**
|
||||
** Return true if the result of comparison is equivalent to desiredResult.
|
||||
** Return false if there is a disagreement.
|
||||
*/
|
||||
static int vdbeRecordCompareDebug(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
const UnpackedRecord *pPKey2 /* Right key */
|
||||
const UnpackedRecord *pPKey2, /* Right key */
|
||||
int desiredResult /* Correct answer */
|
||||
){
|
||||
u32 d1; /* Offset into aKey[] of next data element */
|
||||
u32 idx1; /* Offset into aKey[] of next header element */
|
||||
@@ -3152,6 +3156,7 @@ static int vdbeRecordCompareDebug(
|
||||
Mem mem1;
|
||||
|
||||
pKeyInfo = pPKey2->pKeyInfo;
|
||||
if( pKeyInfo->db==0 ) return 1;
|
||||
mem1.enc = pKeyInfo->enc;
|
||||
mem1.db = pKeyInfo->db;
|
||||
/* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */
|
||||
@@ -3202,7 +3207,7 @@ static int vdbeRecordCompareDebug(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc; /* Invert the result for DESC sort order. */
|
||||
}
|
||||
return rc;
|
||||
goto debugCompareEnd;
|
||||
}
|
||||
i++;
|
||||
}while( idx1<szHdr1 && i<pPKey2->nField );
|
||||
@@ -3216,7 +3221,15 @@ static int vdbeRecordCompareDebug(
|
||||
/* rc==0 here means that one of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
return pPKey2->default_rc;
|
||||
rc = pPKey2->default_rc;
|
||||
|
||||
debugCompareEnd:
|
||||
if( desiredResult==0 && rc==0 ) return 1;
|
||||
if( desiredResult<0 && rc<0 ) return 1;
|
||||
if( desiredResult>0 && rc>0 ) return 1;
|
||||
if( CORRUPT_DB ) return 1;
|
||||
if( pKeyInfo->db->mallocFailed ) return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3229,7 +3242,8 @@ static int vdbeRecordCompareDebug(
|
||||
static int vdbeCompareMemString(
|
||||
const Mem *pMem1,
|
||||
const Mem *pMem2,
|
||||
const CollSeq *pColl
|
||||
const CollSeq *pColl,
|
||||
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
|
||||
){
|
||||
if( pMem1->enc==pColl->enc ){
|
||||
/* The strings are already in the correct encoding. Call the
|
||||
@@ -3252,6 +3266,7 @@ static int vdbeCompareMemString(
|
||||
rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
|
||||
sqlite3VdbeMemRelease(&c1);
|
||||
sqlite3VdbeMemRelease(&c2);
|
||||
if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -3334,7 +3349,7 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
|
||||
assert( !pColl || pColl->xCmp );
|
||||
|
||||
if( pColl ){
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl);
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
|
||||
}
|
||||
/* If a NULL pointer was passed as the collate function, fall through
|
||||
** to the blob case and use memcmp(). */
|
||||
@@ -3406,8 +3421,10 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
|
||||
** fields that appear in both keys are equal, then pPKey2->default_rc is
|
||||
** returned.
|
||||
**
|
||||
** If database corruption is discovered, set pPKey2->isCorrupt to non-zero
|
||||
** and return 0.
|
||||
** If database corruption is discovered, set pPKey2->errCode to
|
||||
** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
|
||||
** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
|
||||
** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
|
||||
*/
|
||||
int sqlite3VdbeRecordCompare(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
@@ -3438,7 +3455,7 @@ int sqlite3VdbeRecordCompare(
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
d1 = szHdr1;
|
||||
if( d1>(unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}
|
||||
i = 0;
|
||||
@@ -3517,14 +3534,16 @@ int sqlite3VdbeRecordCompare(
|
||||
testcase( (d1+mem1.n)==(unsigned)nKey1 );
|
||||
testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
|
||||
if( (d1+mem1.n) > (unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}else if( pKeyInfo->aColl[i] ){
|
||||
mem1.enc = pKeyInfo->enc;
|
||||
mem1.db = pKeyInfo->db;
|
||||
mem1.flags = MEM_Str;
|
||||
mem1.z = (char*)&aKey1[d1];
|
||||
rc = vdbeCompareMemString(&mem1, pRhs, pKeyInfo->aColl[i]);
|
||||
rc = vdbeCompareMemString(
|
||||
&mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode
|
||||
);
|
||||
}else{
|
||||
int nCmp = MIN(mem1.n, pRhs->n);
|
||||
rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
|
||||
@@ -3544,7 +3563,7 @@ int sqlite3VdbeRecordCompare(
|
||||
testcase( (d1+nStr)==(unsigned)nKey1 );
|
||||
testcase( (d1+nStr+1)==(unsigned)nKey1 );
|
||||
if( (d1+nStr) > (unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}else{
|
||||
int nCmp = MIN(nStr, pRhs->n);
|
||||
@@ -3564,11 +3583,7 @@ int sqlite3VdbeRecordCompare(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc;
|
||||
}
|
||||
assert( CORRUPT_DB
|
||||
|| (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
|
||||
assert( mem1.zMalloc==0 ); /* See comment below */
|
||||
return rc;
|
||||
}
|
||||
@@ -3587,9 +3602,7 @@ int sqlite3VdbeRecordCompare(
|
||||
/* rc==0 here means that one or both of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
assert( CORRUPT_DB
|
||||
|| pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) );
|
||||
return pPKey2->default_rc;
|
||||
}
|
||||
|
||||
@@ -3686,11 +3699,7 @@ static int vdbeRecordCompareInt(
|
||||
res = pPKey2->default_rc;
|
||||
}
|
||||
|
||||
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| CORRUPT_DB
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -3724,7 +3733,7 @@ static int vdbeRecordCompareString(
|
||||
|
||||
nStr = (serial_type-12) / 2;
|
||||
if( (szHdr + nStr) > nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}
|
||||
nCmp = MIN( pPKey2->aMem[0].n, nStr );
|
||||
@@ -3750,11 +3759,7 @@ static int vdbeRecordCompareString(
|
||||
}
|
||||
}
|
||||
|
||||
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| CORRUPT_DB
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
+1813
-501
File diff suppressed because it is too large
Load Diff
+257
-153
@@ -227,7 +227,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
|
||||
if( p && ExprHasProperty(p, EP_Unlikely) ){
|
||||
pTerm->truthProb = sqlite3LogEst(p->iTable) - 99;
|
||||
}else{
|
||||
pTerm->truthProb = -1;
|
||||
pTerm->truthProb = 1;
|
||||
}
|
||||
pTerm->pExpr = sqlite3ExprSkipCollate(p);
|
||||
pTerm->wtFlags = wtFlags;
|
||||
@@ -1956,7 +1956,8 @@ static void whereKeyStats(
|
||||
iLower = 0;
|
||||
iUpper = aSample[0].anLt[iCol];
|
||||
}else{
|
||||
iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol];
|
||||
i64 nRow0 = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
|
||||
iUpper = i>=pIdx->nSample ? nRow0 : aSample[i].anLt[iCol];
|
||||
iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol];
|
||||
}
|
||||
aStat[1] = (pIdx->nKeyCol>iCol ? pIdx->aAvgEq[iCol] : 1);
|
||||
@@ -1975,6 +1976,29 @@ static void whereKeyStats(
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
|
||||
/*
|
||||
** If it is not NULL, pTerm is a term that provides an upper or lower
|
||||
** bound on a range scan. Without considering pTerm, it is estimated
|
||||
** that the scan will visit nNew rows. This function returns the number
|
||||
** estimated to be visited after taking pTerm into account.
|
||||
**
|
||||
** If the user explicitly specified a likelihood() value for this term,
|
||||
** then the return value is the likelihood multiplied by the number of
|
||||
** input rows. Otherwise, this function assumes that an "IS NOT NULL" term
|
||||
** has a likelihood of 0.50, and any other term a likelihood of 0.25.
|
||||
*/
|
||||
static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
|
||||
LogEst nRet = nNew;
|
||||
if( pTerm ){
|
||||
if( pTerm->truthProb<=0 ){
|
||||
nRet += pTerm->truthProb;
|
||||
}else if( (pTerm->wtFlags & TERM_VNULL)==0 ){
|
||||
nRet -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
}
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to estimate the number of rows that will be visited
|
||||
** by scanning an index for a range of values. The range may have an upper
|
||||
@@ -2067,7 +2091,7 @@ static int whereRangeScanEst(
|
||||
/* Determine iLower and iUpper using ($P) only. */
|
||||
if( nEq==0 ){
|
||||
iLower = 0;
|
||||
iUpper = p->aiRowEst[0];
|
||||
iUpper = sqlite3LogEstToInt(p->aiRowLogEst[0]);
|
||||
}else{
|
||||
/* Note: this call could be optimized away - since the same values must
|
||||
** have been requested when testing key $P in whereEqualScanEst(). */
|
||||
@@ -2127,17 +2151,18 @@ static int whereRangeScanEst(
|
||||
UNUSED_PARAMETER(pBuilder);
|
||||
#endif
|
||||
assert( pLower || pUpper );
|
||||
/* TUNING: Each inequality constraint reduces the search space 4-fold.
|
||||
** A BETWEEN operator, therefore, reduces the search space 16-fold */
|
||||
nNew = nOut;
|
||||
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ){
|
||||
nNew -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
nOut--;
|
||||
}
|
||||
if( pUpper ){
|
||||
nNew -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
nOut--;
|
||||
}
|
||||
assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 );
|
||||
nNew = whereRangeAdjust(pLower, nOut);
|
||||
nNew = whereRangeAdjust(pUpper, nNew);
|
||||
|
||||
/* TUNING: If there is both an upper and lower limit, assume the range is
|
||||
** reduced by an additional 75%. This means that, by default, an open-ended
|
||||
** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
|
||||
** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to
|
||||
** match 1/64 of the index. */
|
||||
if( pLower && pUpper ) nNew -= 20;
|
||||
|
||||
nOut -= (pLower!=0) + (pUpper!=0);
|
||||
if( nNew<10 ) nNew = 10;
|
||||
if( nNew<nOut ) nOut = nNew;
|
||||
pLoop->nOut = (LogEst)nOut;
|
||||
@@ -2234,6 +2259,7 @@ static int whereInScanEst(
|
||||
tRowcnt *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]);
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
int rc = SQLITE_OK; /* Subfunction return code */
|
||||
tRowcnt nEst; /* Number of rows for a single term */
|
||||
@@ -2242,14 +2268,14 @@ static int whereInScanEst(
|
||||
|
||||
assert( p->aSample!=0 );
|
||||
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
|
||||
nEst = p->aiRowEst[0];
|
||||
nEst = nRow0;
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
|
||||
nRowEst += nEst;
|
||||
pBuilder->nRecValid = nRecValid;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
|
||||
if( nRowEst > nRow0 ) nRowEst = nRow0;
|
||||
*pnRow = nRowEst;
|
||||
WHERETRACE(0x10,("IN row estimate: est=%g\n", nRowEst));
|
||||
}
|
||||
@@ -3757,12 +3783,25 @@ static int whereLoopCheaperProperSubset(
|
||||
** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
|
||||
** WHERE clause terms than Y and that every WHERE clause term used by X is
|
||||
** also used by Y.
|
||||
**
|
||||
** This adjustment is omitted for SKIPSCAN loops. In a SKIPSCAN loop, the
|
||||
** WhereLoop.nLTerm field is not an accurate measure of the number of WHERE
|
||||
** clause terms covered, since some of the first nLTerm entries in aLTerm[]
|
||||
** will be NULL (because they are skipped). That makes it more difficult
|
||||
** to compare the loops. We could add extra code to do the comparison, and
|
||||
** perhaps we will someday. But SKIPSCAN is sufficiently uncommon, and this
|
||||
** adjustment is sufficient minor, that it is very difficult to construct
|
||||
** a test case where the extra code would improve the query plan. Better
|
||||
** to avoid the added complexity and just omit cost adjustments to SKIPSCAN
|
||||
** loops.
|
||||
*/
|
||||
static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
|
||||
if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
|
||||
if( (pTemplate->wsFlags & WHERE_SKIPSCAN)!=0 ) return;
|
||||
for(; p; p=p->pNextLoop){
|
||||
if( p->iTab!=pTemplate->iTab ) continue;
|
||||
if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
|
||||
if( (p->wsFlags & WHERE_SKIPSCAN)!=0 ) continue;
|
||||
if( whereLoopCheaperProperSubset(p, pTemplate) ){
|
||||
/* Adjust pTemplate cost downward so that it is cheaper than its
|
||||
** subset p */
|
||||
@@ -3987,13 +4026,20 @@ static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){
|
||||
if( pX==pTerm ) break;
|
||||
if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
|
||||
}
|
||||
if( j<0 ) pLoop->nOut += pTerm->truthProb;
|
||||
if( j<0 ){
|
||||
pLoop->nOut += (pTerm->truthProb<=0 ? pTerm->truthProb : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex.
|
||||
** Try to match one more.
|
||||
** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
|
||||
** index pIndex. Try to match one more.
|
||||
**
|
||||
** When this function is called, pBuilder->pNew->nOut contains the
|
||||
** number of rows expected to be visited by filtering using the nEq
|
||||
** terms only. If it is modified, this value is restored before this
|
||||
** function returns.
|
||||
**
|
||||
** If pProbe->tnum==0, that means pIndex is a fake index used for the
|
||||
** INTEGER PRIMARY KEY.
|
||||
@@ -4019,7 +4065,6 @@ static int whereLoopAddBtreeIndex(
|
||||
LogEst saved_nOut; /* Original value of pNew->nOut */
|
||||
int iCol; /* Index of the column in the table */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
LogEst nRowEst; /* Estimated index selectivity */
|
||||
LogEst rLogSize; /* Logarithm of table size */
|
||||
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
|
||||
|
||||
@@ -4040,11 +4085,8 @@ static int whereLoopAddBtreeIndex(
|
||||
assert( pNew->u.btree.nEq<=pProbe->nKeyCol );
|
||||
if( pNew->u.btree.nEq < pProbe->nKeyCol ){
|
||||
iCol = pProbe->aiColumn[pNew->u.btree.nEq];
|
||||
nRowEst = sqlite3LogEst(pProbe->aiRowEst[pNew->u.btree.nEq+1]);
|
||||
if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1;
|
||||
}else{
|
||||
iCol = -1;
|
||||
nRowEst = 0;
|
||||
}
|
||||
pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
|
||||
opMask, pProbe);
|
||||
@@ -4055,18 +4097,23 @@ static int whereLoopAddBtreeIndex(
|
||||
saved_prereq = pNew->prereq;
|
||||
saved_nOut = pNew->nOut;
|
||||
pNew->rSetup = 0;
|
||||
rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0]));
|
||||
rLogSize = estLog(pProbe->aiRowLogEst[0]);
|
||||
|
||||
/* Consider using a skip-scan if there are no WHERE clause constraints
|
||||
** available for the left-most terms of the index, and if the average
|
||||
** number of repeats in the left-most terms is at least 18. The magic
|
||||
** number 18 was found by experimentation to be the payoff point where
|
||||
** skip-scan become faster than a full-scan.
|
||||
*/
|
||||
** number of repeats in the left-most terms is at least 18.
|
||||
**
|
||||
** The magic number 18 is selected on the basis that scanning 17 rows
|
||||
** is almost always quicker than an index seek (even though if the index
|
||||
** contains fewer than 2^17 rows we assume otherwise in other parts of
|
||||
** the code). And, even if it is not, it should not be too much slower.
|
||||
** On the other hand, the extra seeks could end up being significantly
|
||||
** more expensive. */
|
||||
assert( 42==sqlite3LogEst(18) );
|
||||
if( pTerm==0
|
||||
&& saved_nEq==saved_nSkip
|
||||
&& saved_nEq+1<pProbe->nKeyCol
|
||||
&& pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */
|
||||
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
|
||||
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
|
||||
){
|
||||
LogEst nIter;
|
||||
@@ -4074,34 +4121,40 @@ static int whereLoopAddBtreeIndex(
|
||||
pNew->u.btree.nSkip++;
|
||||
pNew->aLTerm[pNew->nLTerm++] = 0;
|
||||
pNew->wsFlags |= WHERE_SKIPSCAN;
|
||||
nIter = sqlite3LogEst(pProbe->aiRowEst[0]/pProbe->aiRowEst[saved_nEq+1]);
|
||||
pNew->rRun = rLogSize + nIter;
|
||||
pNew->nOut += nIter;
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter);
|
||||
nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
|
||||
pNew->nOut -= nIter;
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
|
||||
pNew->nOut = saved_nOut;
|
||||
}
|
||||
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
|
||||
u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */
|
||||
LogEst rCostIdx;
|
||||
LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
|
||||
int nIn = 0;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
#endif
|
||||
if( (pTerm->eOperator==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
|
||||
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
|
||||
&& (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
|
||||
){
|
||||
continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
|
||||
}
|
||||
if( pTerm->prereqRight & pNew->maskSelf ) continue;
|
||||
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
|
||||
pNew->wsFlags = saved_wsFlags;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
pNew->nLTerm = saved_nLTerm;
|
||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||
pNew->aLTerm[pNew->nLTerm++] = pTerm;
|
||||
pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
|
||||
pNew->rRun = rLogSize; /* Baseline cost is log2(N). Adjustments below */
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
|
||||
assert( nInMul==0
|
||||
|| (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
|
||||
|| (pNew->wsFlags & WHERE_COLUMN_IN)!=0
|
||||
|| (pNew->wsFlags & WHERE_SKIPSCAN)!=0
|
||||
);
|
||||
|
||||
if( eOp & WO_IN ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
pNew->wsFlags |= WHERE_COLUMN_IN;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
@@ -4113,83 +4166,118 @@ static int whereLoopAddBtreeIndex(
|
||||
}
|
||||
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
|
||||
** changes "x IN (?)" into "x=?". */
|
||||
pNew->rRun += nIn;
|
||||
pNew->u.btree.nEq++;
|
||||
pNew->nOut = nRowEst + nInMul + nIn;
|
||||
}else if( pTerm->eOperator & (WO_EQ) ){
|
||||
assert(
|
||||
(pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN|WHERE_SKIPSCAN))!=0
|
||||
|| nInMul==0
|
||||
);
|
||||
|
||||
}else if( eOp & (WO_EQ) ){
|
||||
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
||||
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1)){
|
||||
assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 );
|
||||
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
|
||||
if( iCol>=0 && pProbe->onError==OE_None ){
|
||||
pNew->wsFlags |= WHERE_UNQ_WANTED;
|
||||
}else{
|
||||
pNew->wsFlags |= WHERE_ONEROW;
|
||||
}
|
||||
}
|
||||
pNew->u.btree.nEq++;
|
||||
pNew->nOut = nRowEst + nInMul;
|
||||
}else if( pTerm->eOperator & (WO_ISNULL) ){
|
||||
}else if( eOp & WO_ISNULL ){
|
||||
pNew->wsFlags |= WHERE_COLUMN_NULL;
|
||||
pNew->u.btree.nEq++;
|
||||
/* TUNING: IS NULL selects 2 rows */
|
||||
nIn = 10; assert( 10==sqlite3LogEst(2) );
|
||||
pNew->nOut = nRowEst + nInMul + nIn;
|
||||
}else if( pTerm->eOperator & (WO_GT|WO_GE) ){
|
||||
testcase( pTerm->eOperator & WO_GT );
|
||||
testcase( pTerm->eOperator & WO_GE );
|
||||
}else if( eOp & (WO_GT|WO_GE) ){
|
||||
testcase( eOp & WO_GT );
|
||||
testcase( eOp & WO_GE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
|
||||
pBtm = pTerm;
|
||||
pTop = 0;
|
||||
}else{
|
||||
assert( pTerm->eOperator & (WO_LT|WO_LE) );
|
||||
testcase( pTerm->eOperator & WO_LT );
|
||||
testcase( pTerm->eOperator & WO_LE );
|
||||
assert( eOp & (WO_LT|WO_LE) );
|
||||
testcase( eOp & WO_LT );
|
||||
testcase( eOp & WO_LE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
|
||||
pTop = pTerm;
|
||||
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
|
||||
pNew->aLTerm[pNew->nLTerm-2] : 0;
|
||||
}
|
||||
|
||||
/* At this point pNew->nOut is set to the number of rows expected to
|
||||
** be visited by the index scan before considering term pTerm, or the
|
||||
** values of nIn and nInMul. In other words, assuming that all
|
||||
** "x IN(...)" terms are replaced with "x = ?". This block updates
|
||||
** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
|
||||
/* Adjust nOut and rRun for STAT3 range values */
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
/* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
|
||||
** data, using some other estimate. */
|
||||
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
|
||||
}
|
||||
}else{
|
||||
int nEq = ++pNew->u.btree.nEq;
|
||||
assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) );
|
||||
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
if( pTerm->truthProb<=0 && iCol>=0 ){
|
||||
assert( (eOp & WO_IN) || nIn==0 );
|
||||
testcase( eOp & WO_IN );
|
||||
pNew->nOut += pTerm->truthProb;
|
||||
pNew->nOut -= nIn;
|
||||
pNew->wsFlags |= WHERE_LIKELIHOOD;
|
||||
}else{
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( nInMul==0
|
||||
&& pProbe->nSample
|
||||
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
||||
&& OptimizationEnabled(db, SQLITE_Stat3)
|
||||
){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
tRowcnt nOut = 0;
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
|
||||
testcase( pTerm->eOperator & WO_EQ );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
|
||||
}else if( (pTerm->eOperator & WO_IN)
|
||||
&& !ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
|
||||
}
|
||||
assert( nOut==0 || rc==SQLITE_OK );
|
||||
if( nOut ){
|
||||
pNew->nOut = sqlite3LogEst(nOut);
|
||||
if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
|
||||
}
|
||||
}
|
||||
tRowcnt nOut = 0;
|
||||
if( nInMul==0
|
||||
&& pProbe->nSample
|
||||
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
||||
&& OptimizationEnabled(db, SQLITE_Stat3)
|
||||
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
|
||||
&& (pNew->wsFlags & WHERE_LIKELIHOOD)==0
|
||||
){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){
|
||||
testcase( eOp & WO_EQ );
|
||||
testcase( eOp & WO_ISNULL );
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
|
||||
}else{
|
||||
rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
|
||||
}
|
||||
assert( rc!=SQLITE_OK || nOut>0 );
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */
|
||||
if( nOut ){
|
||||
pNew->nOut = sqlite3LogEst(nOut);
|
||||
if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
|
||||
pNew->nOut -= nIn;
|
||||
}
|
||||
}
|
||||
if( nOut==0 )
|
||||
#endif
|
||||
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
|
||||
/* Each row involves a step of the index, then a binary search of
|
||||
** the main table */
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun,rLogSize>27 ? rLogSize-17 : 10);
|
||||
{
|
||||
pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]);
|
||||
if( eOp & WO_ISNULL ){
|
||||
/* TUNING: If there is no likelihood() value, assume that a
|
||||
** "col IS NULL" expression matches twice as many rows
|
||||
** as (col=?). */
|
||||
pNew->nOut += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Step cost for each output row */
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut);
|
||||
|
||||
/* Set rCostIdx to the cost of visiting selected rows in index. Add
|
||||
** it to pNew->rRun, which is currently set to the cost of the index
|
||||
** seek only. Then, if this is a non-covering index, add the cost of
|
||||
** visiting the rows in the main table. */
|
||||
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
|
||||
pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
|
||||
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
|
||||
}
|
||||
|
||||
nOutUnadjusted = pNew->nOut;
|
||||
pNew->rRun += nInMul + nIn;
|
||||
pNew->nOut += nInMul + nIn;
|
||||
whereLoopOutputAdjust(pBuilder->pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
|
||||
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
|
||||
pNew->nOut = saved_nOut;
|
||||
}else{
|
||||
pNew->nOut = nOutUnadjusted;
|
||||
}
|
||||
|
||||
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
|
||||
&& pNew->u.btree.nEq<(pProbe->nKeyCol + (pProbe->zName!=0))
|
||||
){
|
||||
@@ -4273,6 +4361,29 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
|
||||
** Add all WhereLoop objects for a single table of the join where the table
|
||||
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
|
||||
** a b-tree table, not a virtual table.
|
||||
**
|
||||
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
|
||||
** are calculated as follows:
|
||||
**
|
||||
** For a full scan, assuming the table (or index) contains nRow rows:
|
||||
**
|
||||
** cost = nRow * 3.0 // full-table scan
|
||||
** cost = nRow * K // scan of covering index
|
||||
** cost = nRow * (K+3.0) // scan of non-covering index
|
||||
**
|
||||
** where K is a value between 1.1 and 3.0 set based on the relative
|
||||
** estimated average size of the index and table records.
|
||||
**
|
||||
** For an index scan, where nVisit is the number of index rows visited
|
||||
** by the scan, and nSeek is the number of seek operations required on
|
||||
** the index b-tree:
|
||||
**
|
||||
** cost = nSeek * (log(nRow) + K * nVisit) // covering index
|
||||
** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index
|
||||
**
|
||||
** Normally, nSeek is 1. nSeek values greater than 1 come about if the
|
||||
** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
|
||||
** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans.
|
||||
*/
|
||||
static int whereLoopAddBtree(
|
||||
WhereLoopBuilder *pBuilder, /* WHERE clause information */
|
||||
@@ -4281,7 +4392,7 @@ static int whereLoopAddBtree(
|
||||
WhereInfo *pWInfo; /* WHERE analysis context */
|
||||
Index *pProbe; /* An index we are evaluating */
|
||||
Index sPk; /* A fake index object for the primary key */
|
||||
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
|
||||
LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
|
||||
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
|
||||
SrcList *pTabList; /* The FROM clause */
|
||||
struct SrcList_item *pSrc; /* The FROM clause btree term to add */
|
||||
@@ -4316,11 +4427,12 @@ static int whereLoopAddBtree(
|
||||
memset(&sPk, 0, sizeof(Index));
|
||||
sPk.nKeyCol = 1;
|
||||
sPk.aiColumn = &aiColumnPk;
|
||||
sPk.aiRowEst = aiRowEstPk;
|
||||
sPk.aiRowLogEst = aiRowEstPk;
|
||||
sPk.onError = OE_Replace;
|
||||
sPk.pTable = pTab;
|
||||
aiRowEstPk[0] = pTab->nRowEst;
|
||||
aiRowEstPk[1] = 1;
|
||||
sPk.szIdxRow = pTab->szTabRow;
|
||||
aiRowEstPk[0] = pTab->nRowLogEst;
|
||||
aiRowEstPk[1] = 0;
|
||||
pFirst = pSrc->pTab->pIndex;
|
||||
if( pSrc->notIndexed==0 ){
|
||||
/* The real indices of the table are only considered if the
|
||||
@@ -4329,7 +4441,7 @@ static int whereLoopAddBtree(
|
||||
}
|
||||
pProbe = &sPk;
|
||||
}
|
||||
rSize = sqlite3LogEst(pTab->nRowEst);
|
||||
rSize = pTab->nRowLogEst;
|
||||
rLogSize = estLog(rSize);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
@@ -4379,6 +4491,7 @@ static int whereLoopAddBtree(
|
||||
&& !whereUsablePartialIndex(pNew->iTab, pWC, pProbe->pPartIdxWhere) ){
|
||||
continue; /* Partial index inappropriate for this query */
|
||||
}
|
||||
rSize = pProbe->aiRowLogEst[0];
|
||||
pNew->u.btree.nEq = 0;
|
||||
pNew->u.btree.nSkip = 0;
|
||||
pNew->nLTerm = 0;
|
||||
@@ -4396,10 +4509,8 @@ static int whereLoopAddBtree(
|
||||
|
||||
/* Full table scan */
|
||||
pNew->iSortIdx = b ? iSortIdx : 0;
|
||||
/* TUNING: Cost of full table scan is 3*(N + log2(N)).
|
||||
** + The extra 3 factor is to encourage the use of indexed lookups
|
||||
** over full scans. FIXME */
|
||||
pNew->rRun = sqlite3LogEstAdd(rSize,rLogSize) + 16;
|
||||
/* TUNING: Cost of full table scan is (N*3.0). */
|
||||
pNew->rRun = rSize + 16;
|
||||
whereLoopOutputAdjust(pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
pNew->nOut = rSize;
|
||||
@@ -4426,35 +4537,16 @@ static int whereLoopAddBtree(
|
||||
)
|
||||
){
|
||||
pNew->iSortIdx = b ? iSortIdx : 0;
|
||||
/* TUNING: The base cost of an index scan is N + log2(N).
|
||||
** The log2(N) is for the initial seek to the beginning and the N
|
||||
** is for the scan itself. */
|
||||
pNew->rRun = sqlite3LogEstAdd(rSize, rLogSize);
|
||||
if( m==0 ){
|
||||
/* TUNING: Cost of a covering index scan is K*(N + log2(N)).
|
||||
** + The extra factor K of between 1.1 and 3.0 that depends
|
||||
** on the relative sizes of the table and the index. K
|
||||
** is smaller for smaller indices, thus favoring them.
|
||||
** The upper bound on K (3.0) matches the penalty factor
|
||||
** on a full table scan that tries to encourage the use of
|
||||
** indexed lookups over full scans.
|
||||
*/
|
||||
pNew->rRun += 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
|
||||
}else{
|
||||
/* TUNING: The cost of scanning a non-covering index is multiplied
|
||||
** by log2(N) to account for the binary search of the main table
|
||||
** that must happen for each row of the index.
|
||||
** TODO: Should there be a multiplier here, analogous to the 3x
|
||||
** multiplier for a fulltable scan or covering index scan, to
|
||||
** further discourage the use of an index scan? Or is the log2(N)
|
||||
** term sufficient discouragement?
|
||||
** TODO: What if some or all of the WHERE clause terms can be
|
||||
** computed without reference to the original table. Then the
|
||||
** penality should reduce to logK where K is the number of output
|
||||
** rows.
|
||||
*/
|
||||
pNew->rRun += rLogSize;
|
||||
|
||||
/* The cost of visiting the index rows is N*K, where K is
|
||||
** between 1.1 and 3.0, depending on the relative sizes of the
|
||||
** index and table rows. If this is a non-covering index scan,
|
||||
** also add the cost of visiting table rows (N*3.0). */
|
||||
pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
|
||||
if( m!=0 ){
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, rSize+16);
|
||||
}
|
||||
|
||||
whereLoopOutputAdjust(pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
pNew->nOut = rSize;
|
||||
@@ -4658,7 +4750,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
int iCur;
|
||||
WhereClause tempWC;
|
||||
WhereLoopBuilder sSubBuild;
|
||||
WhereOrSet sSum, sCur, sPrev;
|
||||
WhereOrSet sSum, sCur;
|
||||
struct SrcList_item *pItem;
|
||||
|
||||
pWC = pBuilder->pWC;
|
||||
@@ -4714,6 +4806,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
whereOrMove(&sSum, &sCur);
|
||||
once = 0;
|
||||
}else{
|
||||
WhereOrSet sPrev;
|
||||
whereOrMove(&sPrev, &sSum);
|
||||
sSum.n = 0;
|
||||
for(i=0; i<sPrev.n; i++){
|
||||
@@ -4732,8 +4825,19 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
pNew->iSortIdx = 0;
|
||||
memset(&pNew->u, 0, sizeof(pNew->u));
|
||||
for(i=0; rc==SQLITE_OK && i<sSum.n; i++){
|
||||
/* TUNING: Multiple by 3.5 for the secondary table lookup */
|
||||
pNew->rRun = sSum.a[i].rRun + 18;
|
||||
/* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs
|
||||
** of all sub-scans required by the OR-scan. However, due to rounding
|
||||
** errors, it may be that the cost of the OR-scan is equal to its
|
||||
** most expensive sub-scan. Add the smallest possible penalty
|
||||
** (equivalent to multiplying the cost by 1.07) to ensure that
|
||||
** this does not happen. Otherwise, for WHERE clauses such as the
|
||||
** following where there is an index on "y":
|
||||
**
|
||||
** WHERE likelihood(x=?, 0.99) OR y=?
|
||||
**
|
||||
** the planner may elect to "OR" together a full-table scan and an
|
||||
** index lookup. And other similarly odd results. */
|
||||
pNew->rRun = sSum.a[i].rRun + 1;
|
||||
pNew->nOut = sSum.a[i].nOut;
|
||||
pNew->prereq = sSum.a[i].prereq;
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
@@ -4857,14 +4961,6 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
*/
|
||||
|
||||
assert( pOrderBy!=0 );
|
||||
|
||||
/* Sortability of virtual tables is determined by the xBestIndex method
|
||||
** of the virtual table itself */
|
||||
if( pLast->wsFlags & WHERE_VIRTUALTABLE ){
|
||||
testcase( nLoop>0 ); /* True when outer loops are one-row and match
|
||||
** no ORDER BY terms */
|
||||
return pLast->u.vtab.isOrdered;
|
||||
}
|
||||
if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
|
||||
|
||||
nOrderBy = pOrderBy->nExpr;
|
||||
@@ -4877,7 +4973,10 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
|
||||
if( iLoop>0 ) ready |= pLoop->maskSelf;
|
||||
pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast;
|
||||
assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
|
||||
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
|
||||
if( pLoop->u.vtab.isOrdered ) obSat = obDone;
|
||||
break;
|
||||
}
|
||||
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
|
||||
|
||||
/* Mark off any ORDER BY term X that is a column in the table of
|
||||
@@ -5184,22 +5283,27 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
|
||||
iLoop, pWLoop, &revMask);
|
||||
if( isOrdered>=0 && isOrdered<nOrderBy ){
|
||||
/* TUNING: Estimated cost of sorting is N*log(N).
|
||||
** If the order-by clause has X terms but only the last Y terms
|
||||
** are out of order, then block-sorting will reduce the sorting
|
||||
** cost to N*log(N)*log(Y/X). The log(Y/X) term is computed
|
||||
** by rScale.
|
||||
** TODO: Should the sorting cost get a small multiplier to help
|
||||
** discourage the use of sorting and encourage the use of index
|
||||
** scans instead?
|
||||
*/
|
||||
/* TUNING: Estimated cost of a full external sort, where N is
|
||||
** the number of rows to sort is:
|
||||
**
|
||||
** cost = (3.0 * N * log(N)).
|
||||
**
|
||||
** Or, if the order-by clause has X terms but only the last Y
|
||||
** terms are out of order, then block-sorting will reduce the
|
||||
** sorting cost to:
|
||||
**
|
||||
** cost = (3.0 * N * log(N)) * (Y/X)
|
||||
**
|
||||
** The (Y/X) term is implemented using stack variable rScale
|
||||
** below. */
|
||||
LogEst rScale, rSortCost;
|
||||
assert( nOrderBy>0 );
|
||||
assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
|
||||
rScale = sqlite3LogEst((nOrderBy-isOrdered)*100/nOrderBy) - 66;
|
||||
rSortCost = nRowEst + estLog(nRowEst) + rScale;
|
||||
rSortCost = nRowEst + estLog(nRowEst) + rScale + 16;
|
||||
|
||||
/* TUNING: The cost of implementing DISTINCT using a B-TREE is
|
||||
** also N*log(N) but it has a larger constant of proportionality.
|
||||
** Multiply by 3.0. */
|
||||
** similar but with a larger constant of proportionality.
|
||||
** Multiply by an additional factor of 3.0. */
|
||||
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
rSortCost += 16;
|
||||
}
|
||||
|
||||
@@ -458,3 +458,4 @@ struct WhereInfo {
|
||||
#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
|
||||
#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
|
||||
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
|
||||
#define WHERE_LIKELIHOOD 0x00020000 /* A likelihood() is affecting nOut */
|
||||
|
||||
+30
-12
@@ -103,12 +103,21 @@ do_test analyze3-1.1.1 {
|
||||
}
|
||||
} {1}
|
||||
|
||||
do_execsql_test analyze3-1.1.x {
|
||||
SELECT count(*) FROM t1 WHERE x>200 AND x<300;
|
||||
SELECT count(*) FROM t1 WHERE x>0 AND x<1100;
|
||||
} {99 1000}
|
||||
|
||||
# The first of the following two SELECT statements visits 99 rows. So
|
||||
# it is better to use the index. But the second visits every row in
|
||||
# the table (1000 in total) so it is better to do a full-table scan.
|
||||
#
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
|
||||
do_eqp_test analyze3-1.1.3 {
|
||||
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
|
||||
do_test analyze3-1.1.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
@@ -125,17 +134,17 @@ do_test analyze3-1.1.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.1.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.1.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
|
||||
|
||||
# The following tests are similar to the block above. The difference is
|
||||
@@ -152,12 +161,17 @@ do_test analyze3-1.2.1 {
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_execsql_test analyze3-2.1.x {
|
||||
SELECT count(*) FROM t2 WHERE x>1 AND x<2;
|
||||
SELECT count(*) FROM t2 WHERE x>0 AND x<99;
|
||||
} {200 990}
|
||||
do_eqp_test analyze3-1.2.2 {
|
||||
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}}
|
||||
do_eqp_test analyze3-1.2.3 {
|
||||
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}}
|
||||
} {0 0 0 {SCAN TABLE t2}}
|
||||
|
||||
do_test analyze3-1.2.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
|
||||
} {161 0 4760}
|
||||
@@ -173,17 +187,17 @@ do_test analyze3-1.2.6 {
|
||||
} {161 0 integer integer 4760}
|
||||
do_test analyze3-1.2.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
|
||||
} {1981 0 490555}
|
||||
} {999 999 490555}
|
||||
do_test analyze3-1.2.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "99" 0 end]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {1981 0 text text 490555}
|
||||
} {999 999 text text 490555}
|
||||
do_test analyze3-1.2.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(99)]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {1981 0 integer integer 490555}
|
||||
} {999 999 integer integer 490555}
|
||||
|
||||
# Same tests a third time. This time, column x has INTEGER affinity and
|
||||
# is not the leftmost column of the table. This triggered a bug causing
|
||||
@@ -199,12 +213,16 @@ do_test analyze3-1.3.1 {
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_execsql_test analyze3-1.3.x {
|
||||
SELECT count(*) FROM t3 WHERE x>200 AND x<300;
|
||||
SELECT count(*) FROM t3 WHERE x>0 AND x<1100
|
||||
} {99 1000}
|
||||
do_eqp_test analyze3-1.3.2 {
|
||||
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}}
|
||||
do_eqp_test analyze3-1.3.3 {
|
||||
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}}
|
||||
} {0 0 0 {SCAN TABLE t3}}
|
||||
|
||||
do_test analyze3-1.3.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
@@ -221,17 +239,17 @@ do_test analyze3-1.3.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.3.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.3.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {2000 0 499500}
|
||||
} {999 999 499500}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the values of bound SQL variables may be used for the LIKE
|
||||
|
||||
+5
-5
@@ -566,7 +566,7 @@ foreach {tn schema} {
|
||||
drop_all_tables
|
||||
do_test 13.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t1(a, b, c, d);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b, c);
|
||||
}
|
||||
@@ -577,16 +577,16 @@ do_test 13.1 {
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_eqp_test 13.2.1 {
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<15 AND b<20
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<15 AND b<12
|
||||
} {/SEARCH TABLE t1 USING INDEX i1/}
|
||||
do_eqp_test 13.2.2 {
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<'15' AND b<20
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<'15' AND b<12
|
||||
} {/SEARCH TABLE t1 USING INDEX i1/}
|
||||
do_eqp_test 13.3.1 {
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<100 AND b<20
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<100 AND b<12
|
||||
} {/SEARCH TABLE t1 USING INDEX i2/}
|
||||
do_eqp_test 13.3.2 {
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<'100' AND b<20
|
||||
SELECT * FROM t1 WHERE a='abc' AND rowid<'100' AND b<12
|
||||
} {/SEARCH TABLE t1 USING INDEX i2/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -97,6 +97,8 @@ do_test autoindex1-210 {
|
||||
PRAGMA automatic_index=ON;
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t1';
|
||||
-- Table t2 actually contains 8 rows.
|
||||
UPDATE sqlite_stat1 SET stat='16' WHERE tbl='t2';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1;
|
||||
}
|
||||
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
# 2014-04-26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix cost
|
||||
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t3(id INTEGER PRIMARY KEY, b NOT NULL);
|
||||
CREATE TABLE t4(c, d, e);
|
||||
CREATE UNIQUE INDEX i3 ON t3(b);
|
||||
CREATE UNIQUE INDEX i4 ON t4(c, d);
|
||||
}
|
||||
do_eqp_test 1.2 {
|
||||
SELECT e FROM t3, t4 WHERE b=c ORDER BY b, d;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t3 USING COVERING INDEX i3}
|
||||
0 1 1 {SEARCH TABLE t4 USING INDEX i4 (c=?)}
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
}
|
||||
|
||||
# It is better to use an index for ORDER BY than sort externally, even
|
||||
# if the index is a non-covering index.
|
||||
do_eqp_test 2.2 {
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 USING INDEX i1}
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t5(a INTEGER PRIMARY KEY,b,c,d,e,f,g);
|
||||
CREATE INDEX t5b ON t5(b);
|
||||
CREATE INDEX t5c ON t5(c);
|
||||
CREATE INDEX t5d ON t5(d);
|
||||
CREATE INDEX t5e ON t5(e);
|
||||
CREATE INDEX t5f ON t5(f);
|
||||
CREATE INDEX t5g ON t5(g);
|
||||
}
|
||||
|
||||
do_eqp_test 3.2 {
|
||||
SELECT a FROM t5
|
||||
WHERE b IS NULL OR c IS NULL OR d IS NULL
|
||||
ORDER BY a;
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t5 USING INDEX t5b (b=?)}
|
||||
0 0 0 {SEARCH TABLE t5 USING INDEX t5c (c=?)}
|
||||
0 0 0 {SEARCH TABLE t5 USING INDEX t5d (d=?)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# If there is no likelihood() or stat3 data, SQLite assumes that a closed
|
||||
# range scan (e.g. one constrained by "col BETWEEN ? AND ?" constraint)
|
||||
# visits 1/64 of the rows in a table.
|
||||
#
|
||||
# Note: 1/63 =~ 0.016
|
||||
# Note: 1/65 =~ 0.015
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
}
|
||||
do_eqp_test 4.2 {
|
||||
SELECT * FROM t1 WHERE likelihood(a=?, 0.014) AND b BETWEEN ? AND ?;
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
|
||||
}
|
||||
do_eqp_test 4.3 {
|
||||
SELECT * FROM t1 WHERE likelihood(a=?, 0.016) AND b BETWEEN ? AND ?;
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b>? AND b<?)}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t2(x, y);
|
||||
CREATE INDEX t2i1 ON t2(x);
|
||||
}
|
||||
|
||||
do_eqp_test 5.2 {
|
||||
SELECT * FROM t2 ORDER BY x, y;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t2 USING INDEX t2i1}
|
||||
0 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
}
|
||||
|
||||
do_eqp_test 5.3 {
|
||||
SELECT * FROM t2 WHERE x BETWEEN ? AND ? ORDER BY rowid;
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t2 USING INDEX t2i1 (x>? AND x<?)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
# where7.test, where8.test:
|
||||
#
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX t3i1 ON t3(b);
|
||||
CREATE INDEX t3i2 ON t3(c);
|
||||
}
|
||||
|
||||
do_eqp_test 6.2 {
|
||||
SELECT a FROM t3 WHERE (b BETWEEN 2 AND 4) OR c=100 ORDER BY a
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3i1 (b>? AND b<?)}
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3i2 (c=?)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d,e,f,g);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
CREATE INDEX t1d ON t1(d);
|
||||
CREATE INDEX t1e ON t1(e);
|
||||
CREATE INDEX t1f ON t1(f);
|
||||
CREATE INDEX t1g ON t1(g);
|
||||
}
|
||||
|
||||
do_eqp_test 7.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE (b>=950 AND b<=1010) OR (b IS NULL AND c NOT NULL)
|
||||
ORDER BY a
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
do_eqp_test 7.3 {
|
||||
SELECT rowid FROM t1
|
||||
WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
OR (b NOT NULL AND c IS NULL AND d NOT NULL)
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
do_eqp_test 7.4 {
|
||||
SELECT rowid FROM t1 WHERE (+b IS NULL AND c NOT NULL) OR c IS NULL
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.1 {
|
||||
CREATE TABLE composer(
|
||||
cid INTEGER PRIMARY KEY,
|
||||
cname TEXT
|
||||
);
|
||||
CREATE TABLE album(
|
||||
aid INTEGER PRIMARY KEY,
|
||||
aname TEXT
|
||||
);
|
||||
CREATE TABLE track(
|
||||
tid INTEGER PRIMARY KEY,
|
||||
cid INTEGER REFERENCES composer,
|
||||
aid INTEGER REFERENCES album,
|
||||
title TEXT
|
||||
);
|
||||
CREATE INDEX track_i1 ON track(cid);
|
||||
CREATE INDEX track_i2 ON track(aid);
|
||||
}
|
||||
|
||||
do_eqp_test 8.2 {
|
||||
SELECT DISTINCT aname
|
||||
FROM album, composer, track
|
||||
WHERE cname LIKE '%bach%'
|
||||
AND unlikely(composer.cid=track.cid)
|
||||
AND unlikely(album.aid=track.aid);
|
||||
} {
|
||||
0 0 2 {SCAN TABLE track}
|
||||
0 1 0 {SEARCH TABLE album USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
0 2 1 {SEARCH TABLE composer USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t1(
|
||||
a,b,c,d,e, f,g,h,i,j,
|
||||
k,l,m,n,o, p,q,r,s,t
|
||||
);
|
||||
CREATE INDEX i1 ON t1(k,l,m,n,o,p,q,r,s,t);
|
||||
}
|
||||
do_test 9.2 {
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO t1 DEFAULT VALUES }
|
||||
}
|
||||
execsql {
|
||||
ANALYZE;
|
||||
CREATE INDEX i2 ON t1(a,b,c,d,e,f,g,h,i,j);
|
||||
}
|
||||
} {}
|
||||
|
||||
set L [list a=? b=? c=? d=? e=? f=? g=? h=? i=? j=?]
|
||||
foreach {tn nTerm nRow} {
|
||||
1 1 10
|
||||
2 2 9
|
||||
3 3 8
|
||||
4 4 7
|
||||
5 5 6
|
||||
6 6 5
|
||||
7 7 5
|
||||
8 8 5
|
||||
9 9 5
|
||||
10 10 5
|
||||
} {
|
||||
set w [join [lrange $L 0 [expr $nTerm-1]] " AND "]
|
||||
set p1 [expr ($nRow-1) / 100.0]
|
||||
set p2 [expr ($nRow+1) / 100.0]
|
||||
|
||||
set sql1 "SELECT * FROM t1 WHERE likelihood(k=?, $p1) AND $w"
|
||||
set sql2 "SELECT * FROM t1 WHERE likelihood(k=?, $p2) AND $w"
|
||||
|
||||
do_eqp_test 9.3.$tn.1 $sql1 {/INDEX i1/}
|
||||
do_eqp_test 9.3.$tn.2 $sql2 {/INDEX i2/}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -884,9 +884,10 @@ do_execsql_test e_createtable-3.3.1 {
|
||||
);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-10288-43169 For the purposes of the DEFAULT clause, an
|
||||
# EVIDENCE-OF: R-36381-62919 For the purposes of the DEFAULT clause, an
|
||||
# expression is considered constant provided that it does not contain
|
||||
# any sub-queries or string constants enclosed in double quotes.
|
||||
# any sub-queries, column or table references, or string literals
|
||||
# enclosed in double-quotes instead of single-quotes.
|
||||
#
|
||||
do_createtable_tests 3.4.1 -error {
|
||||
default value of column [x] is not constant
|
||||
|
||||
+6
-5
@@ -135,9 +135,9 @@ reset_db
|
||||
#
|
||||
# This also tests that foreign key constraints are disabled by default.
|
||||
#
|
||||
# EVIDENCE-OF: R-59578-04990 Foreign key constraints are disabled by
|
||||
# EVIDENCE-OF: R-44261-39702 Foreign key constraints are disabled by
|
||||
# default (for backwards compatibility), so must be enabled separately
|
||||
# for each database connection separately.
|
||||
# for each database connection.
|
||||
#
|
||||
drop_all_tables
|
||||
do_test e_fkey-4.1 {
|
||||
@@ -163,9 +163,10 @@ do_test e_fkey-4.2 {
|
||||
} {world}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-15278-54456 The application can can also use a PRAGMA
|
||||
# EVIDENCE-OF: R-08013-37737 The application can also use a PRAGMA
|
||||
# foreign_keys statement to determine if foreign keys are currently
|
||||
# enabled.
|
||||
|
||||
#
|
||||
# This also tests the example code in section 2 of foreignkeys.in.
|
||||
#
|
||||
@@ -2990,8 +2991,8 @@ if {[clang_sanitize_address]==0} {
|
||||
# The setting of the recursive_triggers pragma does not affect foreign
|
||||
# key actions.
|
||||
#
|
||||
# EVIDENCE-OF: R-51769-32730 The PRAGMA recursive_triggers setting does
|
||||
# not not affect the operation of foreign key actions.
|
||||
# EVIDENCE-OF: R-44355-00270 The PRAGMA recursive_triggers setting does
|
||||
# not affect the operation of foreign key actions.
|
||||
#
|
||||
foreach recursive_triggers_setting [list 0 1 ON OFF] {
|
||||
drop_all_tables
|
||||
|
||||
+6
-6
@@ -312,8 +312,8 @@ do_eqp_test 4.2.3 {
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
|
||||
2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
|
||||
}
|
||||
do_eqp_test 4.2.4 {
|
||||
@@ -321,8 +321,8 @@ do_eqp_test 4.2.4 {
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
|
||||
2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
|
||||
}
|
||||
do_eqp_test 4.2.5 {
|
||||
@@ -330,8 +330,8 @@ do_eqp_test 4.2.5 {
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
|
||||
2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -145,11 +145,11 @@ do_test index6-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a,b);
|
||||
INSERT INTO t2(a,b) SELECT value, value FROM nums WHERE value<1000;
|
||||
UPDATE t2 SET a=NULL WHERE b%5==0;
|
||||
UPDATE t2 SET a=NULL WHERE b%2==0;
|
||||
CREATE INDEX t2a1 ON t2(a) WHERE a IS NOT NULL;
|
||||
SELECT count(*) FROM t2 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {800}
|
||||
} {500}
|
||||
do_test index6-2.2 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
@@ -157,6 +157,7 @@ do_test index6-2.2 {
|
||||
}
|
||||
} {/.* TABLE t2 USING INDEX t2a1 .*/}
|
||||
ifcapable stat4||stat3 {
|
||||
execsql ANALYZE
|
||||
do_test index6-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
|
||||
@@ -880,6 +880,48 @@ do_malloc_test 39 -tclprep {
|
||||
db close
|
||||
}
|
||||
|
||||
reset_db
|
||||
add_test_utf16bin_collate db
|
||||
do_execsql_test 40.1 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES('fghij');
|
||||
INSERT INTO t1 VALUES('pqrst');
|
||||
INSERT INTO t1 VALUES('abcde');
|
||||
INSERT INTO t1 VALUES('uvwxy');
|
||||
INSERT INTO t1 VALUES('klmno');
|
||||
}
|
||||
do_execsql_test 40.2 {
|
||||
SELECT * FROM t1 ORDER BY 1 COLLATE utf16bin;
|
||||
} {abcde fghij klmno pqrst uvwxy}
|
||||
do_faultsim_test 40.3 -faults oom-trans* -body {
|
||||
execsql {
|
||||
SELECT * FROM t1 ORDER BY 1 COLLATE utf16bin;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {abcde fghij klmno pqrst uvwxy}}
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
reset_db
|
||||
add_test_utf16bin_collate db
|
||||
set big [string repeat x 200]
|
||||
do_execsql_test 41.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a COLLATE utf16bin);
|
||||
INSERT INTO t1 VALUES('fghij' || $::big);
|
||||
INSERT INTO t1 VALUES('pqrst' || $::big);
|
||||
INSERT INTO t1 VALUES('abcde' || $::big);
|
||||
INSERT INTO t1 VALUES('uvwxy' || $::big);
|
||||
INSERT INTO t1 VALUES('klmno' || $::big);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
}
|
||||
do_faultsim_test 41.2 -faults oom* -body {
|
||||
execsql { SELECT * FROM t1 WHERE a = ('abcde' || $::big)}
|
||||
} -test {
|
||||
faultsim_test_result [list 0 "abcde$::big"]
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
||||
+22
-1
@@ -25,7 +25,6 @@ if {!$MEMDEBUG} {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
# Construct a test database
|
||||
#
|
||||
forcedelete test.db.bu
|
||||
@@ -116,6 +115,28 @@ ifcapable stat3 {
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
}
|
||||
do_faultsim_test 7 -faults oom-trans* -prep {
|
||||
if {$iFail < 500} { set iFail 2000 }
|
||||
if {$iFail > 1215} { set iFail 2000 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 1000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
}
|
||||
} -test {
|
||||
set res [list 200 100 200 100 200 100 200 100 200 100]
|
||||
faultsim_test_result [list 0 $res]
|
||||
}
|
||||
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
||||
+2
-2
@@ -80,12 +80,12 @@ do_execsql_test 2.1a {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a=0 ORDER BY a, b, c;
|
||||
} {~/B-TREE/}
|
||||
|
||||
do_execsql_test 2.1b {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 WHERE a=0 ORDER BY a, b, c;
|
||||
SELECT * FROM t1 WHERE likelihood(a=0, 0.05) ORDER BY a, b, c;
|
||||
} {/B-TREE/}
|
||||
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 WHERE +a=0 ORDER BY a, b, c;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# 2014-04-25
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing ORDER BY optimizations on joins
|
||||
# that involve virtual tables.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix orderby7
|
||||
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE fts USING fts3(content TEXT);
|
||||
INSERT INTO fts(rowid,content)
|
||||
VALUES(1,'this is a test of the fts3 virtual'),
|
||||
(2,'table used as part of a join together'),
|
||||
(3,'with the DISTINCT keyword. There was'),
|
||||
(4,'a bug at one time (2013-06 through 2014-04)'),
|
||||
(5,'that prevented this from working correctly.'),
|
||||
(11,'a row that occurs twice'),
|
||||
(12,'a row that occurs twice');
|
||||
|
||||
CREATE TABLE t1(x TEXT PRIMARY KEY, y);
|
||||
INSERT OR IGNORE INTO t1 SELECT content, rowid+100 FROM fts;
|
||||
} {}
|
||||
do_execsql_test 1.1 {
|
||||
SELECT DISTINCT fts.rowid, t1.y
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x
|
||||
ORDER BY y;
|
||||
} {11 111 12 111}
|
||||
do_execsql_test 1.2 {
|
||||
SELECT DISTINCT fts.rowid, t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x
|
||||
ORDER BY 1;
|
||||
} {11 {a row that occurs twice} 12 {a row that occurs twice}}
|
||||
do_execsql_test 1.3 {
|
||||
SELECT DISTINCT t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x
|
||||
ORDER BY 1;
|
||||
} {{a row that occurs twice}}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x
|
||||
ORDER BY 1;
|
||||
} {{a row that occurs twice} {a row that occurs twice}}
|
||||
do_execsql_test 1.5 {
|
||||
SELECT DISTINCT t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x;
|
||||
} {{a row that occurs twice}}
|
||||
do_execsql_test 1.6 {
|
||||
SELECT t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts MATCH 'that twice'
|
||||
AND content=x;
|
||||
} {{a row that occurs twice} {a row that occurs twice}}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT DISTINCT t1.x
|
||||
FROM fts, t1
|
||||
WHERE fts.rowid=11
|
||||
AND content=x
|
||||
ORDER BY fts.rowid;
|
||||
} {{a row that occurs twice}}
|
||||
do_execsql_test 2.2 {
|
||||
SELECT DISTINCT t1.*
|
||||
FROM fts, t1
|
||||
WHERE fts.rowid=11
|
||||
AND content=x
|
||||
ORDER BY fts.rowid;
|
||||
} {{a row that occurs twice} 111}
|
||||
do_execsql_test 2.3 {
|
||||
SELECT DISTINCT t1.*
|
||||
FROM fts, t1
|
||||
WHERE fts.rowid=11
|
||||
AND content=x
|
||||
ORDER BY t1.y
|
||||
} {{a row that occurs twice} 111}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -112,6 +112,7 @@ set allquicktests [test_set $alltests -exclude {
|
||||
incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test
|
||||
vtab_err.test walslow.test walcrash.test walcrash3.test
|
||||
walthread.test rtree3.test indexfault.test securedel2.test
|
||||
sort3.test sort4.test
|
||||
}]
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
@@ -331,6 +332,12 @@ test_suite "coverage-analyze" -description {
|
||||
analyze.test analyzeB.test mallocA.test
|
||||
}
|
||||
|
||||
test_suite "coverage-sorter" -description {
|
||||
Coverage tests for file vdbesort.c.
|
||||
} -files {
|
||||
sort.test sortfault.test
|
||||
}
|
||||
|
||||
|
||||
lappend ::testsuitelist xxx
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -462,7 +469,7 @@ test_suite "multithread" -description {
|
||||
} -files {
|
||||
delete.test delete2.test insert.test rollback.test select1.test
|
||||
select2.test trans.test update.test vacuum.test types.test
|
||||
types2.test types3.test
|
||||
types2.test types3.test sort4.test
|
||||
} -shutdown {
|
||||
catch {db close}
|
||||
sqlite3_shutdown
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix selectA
|
||||
|
||||
ifcapable !compound {
|
||||
finish_test
|
||||
@@ -1310,4 +1311,68 @@ do_execsql_test selectA-3.98 {
|
||||
SELECT n FROM xyz ORDER BY +n;
|
||||
} {MAD MAD+ MAD++}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# At one point the following code exposed a temp register reuse problem.
|
||||
#
|
||||
proc f {args} { return 1 }
|
||||
db func f f
|
||||
|
||||
do_execsql_test 4.1.1 {
|
||||
CREATE TABLE t4(a, b);
|
||||
CREATE TABLE t5(c, d);
|
||||
|
||||
INSERT INTO t5 VALUES(1, 'x');
|
||||
INSERT INTO t5 VALUES(2, 'x');
|
||||
INSERT INTO t4 VALUES(3, 'x');
|
||||
INSERT INTO t4 VALUES(4, 'x');
|
||||
|
||||
CREATE INDEX i1 ON t4(a);
|
||||
CREATE INDEX i2 ON t5(c);
|
||||
}
|
||||
|
||||
do_eqp_test 4.1.2 {
|
||||
SELECT c, d FROM t5
|
||||
UNION ALL
|
||||
SELECT a, b FROM t4 WHERE f()==f()
|
||||
ORDER BY 1,2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t5 USING INDEX i2}
|
||||
1 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
2 0 0 {SCAN TABLE t4 USING INDEX i1}
|
||||
2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
|
||||
}
|
||||
|
||||
do_execsql_test 4.1.3 {
|
||||
SELECT c, d FROM t5
|
||||
UNION ALL
|
||||
SELECT a, b FROM t4 WHERE f()==f()
|
||||
ORDER BY 1,2
|
||||
} {
|
||||
1 x 2 x 3 x 4 x
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
CREATE TABLE t6(a, b);
|
||||
CREATE TABLE t7(c, d);
|
||||
|
||||
INSERT INTO t7 VALUES(2, 9);
|
||||
INSERT INTO t6 VALUES(3, 0);
|
||||
INSERT INTO t6 VALUES(4, 1);
|
||||
INSERT INTO t7 VALUES(5, 6);
|
||||
INSERT INTO t6 VALUES(6, 0);
|
||||
INSERT INTO t7 VALUES(7, 6);
|
||||
|
||||
CREATE INDEX i6 ON t6(a);
|
||||
CREATE INDEX i7 ON t7(c);
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.2 {
|
||||
SELECT c, f(d,c,d,c,d) FROM t7
|
||||
UNION ALL
|
||||
SELECT a, b FROM t6
|
||||
ORDER BY 1,2
|
||||
} {/2 . 3 . 4 . 5 . 6 . 7 ./}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# This script displays the field of rectangles used by --testset rtree
|
||||
# of speedtest1. Run this script as follows:
|
||||
#
|
||||
# rm test.db
|
||||
# ./speedtest1 --testset rtree --size 25 test.db
|
||||
# sqlite3 --separator ' ' test.db 'SELECT * FROM rt1' >data.txt
|
||||
# wish show_speedtest1_rtree.tcl
|
||||
#
|
||||
# The filename "data.txt" is hard coded into this script and so that name
|
||||
# must be used on lines 3 and 4 above. Elsewhere, different filenames can
|
||||
# be used. The --size N parameter can be adjusted as desired.
|
||||
#
|
||||
package require Tk
|
||||
set f [open data.txt rb]
|
||||
set data [read $f]
|
||||
close $f
|
||||
canvas .c
|
||||
frame .b
|
||||
button .b.b1 -text X-Y -command refill-xy
|
||||
button .b.b2 -text X-Z -command refill-xz
|
||||
button .b.b3 -text Y-Z -command refill-yz
|
||||
pack .b.b1 .b.b2 .b.b3 -side left
|
||||
pack .c -side top -fill both -expand 1
|
||||
pack .b -side top
|
||||
proc resize_canvas_to_fit {} {
|
||||
foreach {x0 y0 x1 y1} [.c bbox all] break
|
||||
set w [expr {$x1-$x0}]
|
||||
set h [expr {$y1-$y0}]
|
||||
.c config -width $w -height $h
|
||||
}
|
||||
proc refill-xy {} {
|
||||
.c delete all
|
||||
foreach {id x0 x1 y0 y1 z0 z1} $::data {
|
||||
.c create rectangle $x0 $y0 $x1 $y1
|
||||
}
|
||||
.c scale all 0 0 0.05 0.05
|
||||
resize_canvas_to_fit
|
||||
}
|
||||
proc refill-xz {} {
|
||||
.c delete all
|
||||
foreach {id x0 x1 y0 y1 z0 z1} $::data {
|
||||
.c create rectangle $x0 $z0 $x1 $z1
|
||||
}
|
||||
.c scale all 0 0 0.05 0.05
|
||||
resize_canvas_to_fit
|
||||
}
|
||||
proc refill-yz {} {
|
||||
.c delete all
|
||||
foreach {id x0 x1 y0 y1 z0 z1} $::data {
|
||||
.c create rectangle $y0 $z0 $y1 $z1
|
||||
}
|
||||
.c scale all 0 0 0.05 0.05
|
||||
resize_canvas_to_fit
|
||||
}
|
||||
refill-xy
|
||||
@@ -74,6 +74,7 @@ do_execsql_test skipscan2-1.4 {
|
||||
-- of a skip-scan. So make a manual adjustment to the stat1 table
|
||||
-- to make it seem like there are many more.
|
||||
UPDATE sqlite_stat1 SET stat='10000 5000 20' WHERE idx='people_idx1';
|
||||
UPDATE sqlite_stat1 SET stat='10000 1' WHERE idx='sqlite_autoindex_people_1';
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
db cache flush
|
||||
|
||||
+176
-3
@@ -8,10 +8,10 @@
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the CREATE TABLE statement.
|
||||
#
|
||||
# $Id: sort.test,v 1.25 2005/11/14 22:29:06 drh Exp $
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the sorter (code in vdbesort.c).
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -464,4 +464,177 @@ do_test sort-12.1 {
|
||||
}
|
||||
} {1 2 xxx 1 3 yyy 1 1 zzz}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the sorter in vdbesort.c sorts in a stable fashion.
|
||||
#
|
||||
do_execsql_test sort-13.0 {
|
||||
CREATE TABLE t10(a, b);
|
||||
}
|
||||
do_test sort-13.1 {
|
||||
db transaction {
|
||||
for {set i 0} {$i < 100000} {incr i} {
|
||||
execsql { INSERT INTO t10 VALUES( $i/10, $i%10 ) }
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_execsql_test sort-13.2 {
|
||||
SELECT a, b FROM t10 ORDER BY a;
|
||||
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
|
||||
do_execsql_test sort-13.3 {
|
||||
PRAGMA cache_size = 5;
|
||||
SELECT a, b FROM t10 ORDER BY a;
|
||||
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Sort some large ( > 4KiB) records.
|
||||
#
|
||||
proc cksum {x} {
|
||||
set i1 1
|
||||
set i2 2
|
||||
binary scan $x c* L
|
||||
foreach {a b} $L {
|
||||
set i1 [expr (($i2<<3) + $a) & 0x7FFFFFFF]
|
||||
set i2 [expr (($i1<<3) + $b) & 0x7FFFFFFF]
|
||||
}
|
||||
list $i1 $i2
|
||||
}
|
||||
db func cksum cksum
|
||||
|
||||
do_execsql_test sort-14.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
CREATE TABLE t11(a, b);
|
||||
INSERT INTO t11 VALUES(randomblob(5000), NULL);
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --2
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --3
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --4
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --5
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --6
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --7
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --8
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --9
|
||||
UPDATE t11 SET b = cksum(a);
|
||||
}
|
||||
|
||||
foreach {tn mmap_limit} {
|
||||
1 0
|
||||
2 1000000
|
||||
} {
|
||||
do_test sort-14.$tn {
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $mmap_limit
|
||||
set prev ""
|
||||
db eval { SELECT * FROM t11 ORDER BY b } {
|
||||
if {$b != [cksum $a]} {error "checksum failed"}
|
||||
if {[string compare $b $prev] < 0} {error "sort failed"}
|
||||
set prev $b
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn mmap_limit nWorker tmpstore coremutex fakeheap softheaplimit} {
|
||||
1 0 3 file true false 0
|
||||
2 0 3 file true true 0
|
||||
3 0 0 file true false 0
|
||||
4 1000000 3 file true false 0
|
||||
5 0 0 memory false true 0
|
||||
6 0 0 file false true 1000000
|
||||
7 0 0 file false true 10000
|
||||
} {
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads $nWorker
|
||||
if {$coremutex} {
|
||||
sqlite3_config multithread
|
||||
} else {
|
||||
sqlite3_config singlethread
|
||||
}
|
||||
sqlite3_initialize
|
||||
sorter_test_fakeheap $fakeheap
|
||||
sqlite3_soft_heap_limit $softheaplimit
|
||||
|
||||
reset_db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $mmap_limit
|
||||
execsql "PRAGMA temp_store = $tmpstore"
|
||||
|
||||
set ten [string repeat X 10300]
|
||||
set one [string repeat y 200]
|
||||
|
||||
if {$softheaplimit} {
|
||||
execsql { PRAGMA cache_size = 20 };
|
||||
} else {
|
||||
execsql { PRAGMA cache_size = 5 };
|
||||
}
|
||||
|
||||
do_execsql_test 15.$tn.1 {
|
||||
WITH rr AS (
|
||||
SELECT 4, $ten UNION ALL
|
||||
SELECT 2, $one UNION ALL
|
||||
SELECT 1, $ten UNION ALL
|
||||
SELECT 3, $one
|
||||
)
|
||||
SELECT * FROM rr ORDER BY 1;
|
||||
} [list 1 $ten 2 $one 3 $one 4 $ten]
|
||||
|
||||
do_execsql_test 15.$tn.2 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
INSERT INTO t1 VALUES(5);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(6);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_execsql_test 15.$tn.3 {
|
||||
WITH rr AS (
|
||||
SELECT 4, $ten UNION ALL
|
||||
SELECT 2, $one
|
||||
)
|
||||
SELECT * FROM rr ORDER BY 1;
|
||||
} [list 2 $one 4 $ten]
|
||||
|
||||
sorter_test_fakeheap 0
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 0
|
||||
set t(0) singlethread
|
||||
set t(1) multithread
|
||||
set t(2) serialized
|
||||
sqlite3_config $t($sqlite_options(threadsafe))
|
||||
sqlite3_initialize
|
||||
sqlite3_soft_heap_limit 0
|
||||
|
||||
reset_db
|
||||
do_catchsql_test 16.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, NULL, 3);
|
||||
INSERT INTO t1 VALUES(NULL, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, NULL);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a, c);
|
||||
} {0 {}}
|
||||
reset_db
|
||||
do_catchsql_test 16.2 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, NULL, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, NULL);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a, c);
|
||||
} {1 {UNIQUE constraint failed: t1.b, t1.a, t1.c}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
SELECT * FROM sqlite_master ORDER BY sql;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# 2014 March 25.
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# Specifically, the tests in this file attempt to verify that
|
||||
# multi-threaded sorting works.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort2
|
||||
|
||||
foreach {tn script} {
|
||||
1 { }
|
||||
2 {
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 7
|
||||
reset_db
|
||||
}
|
||||
} {
|
||||
|
||||
eval $script
|
||||
|
||||
do_execsql_test $tn.1 {
|
||||
PRAGMA cache_size = 5;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 100000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
} {
|
||||
20000 100 20000 100 20000 100 20000 100 20000 100
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 10000
|
||||
) INSERT INTO t1 SELECT * FROM r;
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.2 {
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a);
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.3 {
|
||||
CREATE UNIQUE INDEX i2 ON t1(a);
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.4 { PRAGMA integrity_check } {ok}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test $tn.3 {
|
||||
PRAGMA cache_size = 5;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 1000000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
} {
|
||||
200000 100 200000 100 200000 100 200000 100 200000 100
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 0
|
||||
sqlite3_initialize
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# 2014 March 25.
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# The tests in this file verify that sorting works when the library is
|
||||
# configured to use mmap(), but the temporary files generated by the
|
||||
# sorter are too large to be completely mapped.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort3
|
||||
|
||||
# Sort roughly 20MB of data. Once with a mmap limit of 5MB and once without.
|
||||
#
|
||||
foreach {itest limit} {
|
||||
1 5000000
|
||||
2 0x7FFFFFFF
|
||||
} {
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $limit
|
||||
do_execsql_test 1.$itest {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(1000)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(1000) FROM r
|
||||
LIMIT 20000
|
||||
)
|
||||
SELECT count(*), sum(length(y)) FROM r GROUP BY (x%5);
|
||||
} {
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
}
|
||||
}
|
||||
|
||||
# Sort more than 2GB of data. At one point this was causing a problem.
|
||||
# This test might take one minute or more to run.
|
||||
#
|
||||
do_execsql_test 2 {
|
||||
PRAGMA cache_size = 20000;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(1000)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(1000) FROM r
|
||||
LIMIT 2200000
|
||||
)
|
||||
SELECT count(*), sum(length(y)) FROM r GROUP BY (x%5);
|
||||
} {
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
# 2014 May 6.
|
||||
#
|
||||
# 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 tests in this file are brute force tests of the multi-threaded
|
||||
# sorter.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort4
|
||||
|
||||
# Configure the sorter to use 3 background threads.
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 3
|
||||
sqlite3_initialize
|
||||
reset_db
|
||||
|
||||
# Minimum number of seconds to run for. If the value is 0, each test
|
||||
# is run exactly once. Otherwise, tests are repeated until the timeout
|
||||
# expires.
|
||||
set SORT4TIMEOUT 0
|
||||
if {[permutation] == "multithread"} { set SORT4TIMEOUT 300 }
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Set up a table "t1" containing $nRow rows. Each row contains also
|
||||
# contains blob fields that collectively contain at least $nPayload
|
||||
# bytes of content. The table schema is as follows:
|
||||
#
|
||||
# CREATE TABLE t1(a INTEGER, <extra-columns>, b INTEGER);
|
||||
#
|
||||
# For each row, the values of columns "a" and "b" are set to the same
|
||||
# pseudo-randomly selected integer. The "extra-columns", of which there
|
||||
# are at most eight, are named c0, c1, c2 etc. Column c0 contains a 4
|
||||
# byte string. Column c1 an 8 byte string. Field c2 16 bytes, and so on.
|
||||
#
|
||||
# This table is intended to be used for testing queries of the form:
|
||||
#
|
||||
# SELECT a, <cols>, b FROM t1 ORDER BY a;
|
||||
#
|
||||
# The test code checks that rows are returned in order, and that the
|
||||
# values of "a" and "b" are the same for each row (the idea being that
|
||||
# if field "b" at the end of the sorter record has not been corrupted,
|
||||
# the rest of the record is probably Ok as well).
|
||||
#
|
||||
proc populate_table {nRow nPayload} {
|
||||
set nCol 0
|
||||
|
||||
set n 0
|
||||
for {set nCol 0} {$n < $nPayload} {incr nCol} {
|
||||
incr n [expr (4 << $nCol)]
|
||||
}
|
||||
|
||||
set cols [lrange [list xxx c0 c1 c2 c3 c4 c5 c6 c7] 1 $nCol]
|
||||
set data [lrange [list xxx \
|
||||
randomblob(4) randomblob(8) randomblob(16) randomblob(32) \
|
||||
randomblob(64) randomblob(128) randomblob(256) randomblob(512) \
|
||||
] 1 $nCol]
|
||||
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
|
||||
db transaction {
|
||||
execsql "CREATE TABLE t1(a, [join $cols ,], b);"
|
||||
set insert "INSERT INTO t1 VALUES(:k, [join $data ,], :k)"
|
||||
for {set i 0} {$i < $nRow} {incr i} {
|
||||
set k [expr int(rand()*1000000000)]
|
||||
execsql $insert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Helper for [do_sorter_test]
|
||||
#
|
||||
proc sorter_test {nRow nRead nPayload} {
|
||||
set res [list]
|
||||
|
||||
set nLoad [expr ($nRow > $nRead) ? $nRead : $nRow]
|
||||
|
||||
set nPayload [expr (($nPayload+3)/4) * 4]
|
||||
set cols [list]
|
||||
foreach {mask col} {
|
||||
0x04 c0 0x08 c1 0x10 c2 0x20 c3
|
||||
0x40 c4 0x80 c5 0x100 c6 0x200 c7
|
||||
} {
|
||||
if {$nPayload & $mask} { lappend cols $col }
|
||||
}
|
||||
|
||||
# Create two SELECT statements. Statement $sql1 uses the sorter to sort
|
||||
# $nRow records of a bit over $nPayload bytes each read from the "t1"
|
||||
# table created by [populate_table] proc above. Rows are sorted in order
|
||||
# of the integer field in each "t1" record.
|
||||
#
|
||||
# The second SQL statement sorts the same set of rows as the first, but
|
||||
# uses a LIMIT clause, causing SQLite to use a temp table instead of the
|
||||
# sorter for sorting.
|
||||
#
|
||||
set sql1 "SELECT a, [join $cols ,], b FROM t1 WHERE rowid<=$nRow ORDER BY a"
|
||||
set sql2 "SELECT a FROM t1 WHERE rowid<=$nRow ORDER BY a LIMIT $nRead"
|
||||
|
||||
# Pass the two SQL statements to a helper command written in C. This
|
||||
# command steps statement $sql1 $nRead times and compares the integer
|
||||
# values in the rows returned with the results of executing $sql2. If
|
||||
# the comparison fails (indicating some bug in the sorter), a Tcl
|
||||
# exception is thrown.
|
||||
#
|
||||
sorter_test_sort4_helper db $sql1 $nRead $sql2
|
||||
set {} {}
|
||||
}
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# do_sorter_test <testname> <args>...
|
||||
#
|
||||
# where <args> are any of the following switches:
|
||||
#
|
||||
# -rows N (number of rows to have sorter sort)
|
||||
# -read N (number of rows to read out of sorter)
|
||||
# -payload N (bytes of payload to read with each row)
|
||||
# -cachesize N (Value for "PRAGMA cache_size = ?")
|
||||
# -repeats N (number of times to repeat test)
|
||||
# -fakeheap BOOL (true to use separate allocations for in-memory records)
|
||||
#
|
||||
proc do_sorter_test {tn args} {
|
||||
set a(-rows) 1000
|
||||
set a(-repeats) 1
|
||||
set a(-read) 100
|
||||
set a(-payload) 100
|
||||
set a(-cachesize) 100
|
||||
set a(-fakeheap) 0
|
||||
|
||||
foreach {s val} $args {
|
||||
if {[info exists a($s)]==0} {
|
||||
unset a(-cachesize)
|
||||
set optlist "[join [array names a] ,] or -cachesize"
|
||||
error "Unknown option $s, expected $optlist"
|
||||
}
|
||||
set a($s) $val
|
||||
}
|
||||
if {[permutation] == "memsys3" || [permutation] == "memsys5"} {
|
||||
set a(-fakeheap) 0
|
||||
}
|
||||
if {$a(-fakeheap)} { sorter_test_fakeheap 1 }
|
||||
|
||||
|
||||
db eval "PRAGMA cache_size = $a(-cachesize)"
|
||||
do_test $tn [subst -nocommands {
|
||||
for {set i 0} {[set i] < $a(-repeats)} {incr i} {
|
||||
sorter_test $a(-rows) $a(-read) $a(-payload)
|
||||
}
|
||||
}] {}
|
||||
|
||||
if {$a(-fakeheap)} { sorter_test_fakeheap 0 }
|
||||
}
|
||||
|
||||
proc clock_seconds {} {
|
||||
db one {SELECT strftime('%s')}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Begin tests here.
|
||||
|
||||
# Create a test database.
|
||||
do_test 1 {
|
||||
execsql "PRAGMA page_size = 4096"
|
||||
populate_table 100000 500
|
||||
} {}
|
||||
|
||||
set iTimeLimit [expr [clock_seconds] + $SORT4TIMEOUT]
|
||||
|
||||
for {set t 2} {1} {incr tn} {
|
||||
do_sorter_test $t.2 -repeats 10 -rows 1000 -read 100
|
||||
do_sorter_test $t.3 -repeats 10 -rows 100000 -read 1000
|
||||
do_sorter_test $t.4 -repeats 10 -rows 100000 -read 1000 -payload 500
|
||||
do_sorter_test $t.5 -repeats 10 -rows 100000 -read 100000 -payload 8
|
||||
do_sorter_test $t.6 -repeats 10 -rows 100000 -read 10 -payload 8
|
||||
do_sorter_test $t.7 -repeats 10 -rows 10000 -read 10000 -payload 8 -fakeheap 1
|
||||
do_sorter_test $t.8 -repeats 10 -rows 100000 -read 10000 -cachesize 250
|
||||
|
||||
set iNow [clock_seconds]
|
||||
if {$iNow>=$iTimeLimit} break
|
||||
do_test "$testprefix-([expr $iTimeLimit-$iNow] seconds remain)" {} {}
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 0
|
||||
sqlite3_initialize
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
# 2014 March 25.
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# Specifically, it tests the effects of fault injection on the sorter
|
||||
# module (code in vdbesort.c).
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sortfault
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
}
|
||||
|
||||
foreach {tn mmap_limit nWorker tmpstore threadsmode fakeheap lookaside} {
|
||||
1 0 0 file multithread false false
|
||||
2 100000 0 file multithread false false
|
||||
3 100000 1 file multithread false false
|
||||
4 2000000 0 file singlethread false true
|
||||
} {
|
||||
if {$sqlite_options(threadsafe)} { set threadsmode singlethread }
|
||||
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads $nWorker
|
||||
sqlite3_config $threadsmode
|
||||
if { $lookaside } {
|
||||
sqlite3_config_lookaside 100 500
|
||||
} else {
|
||||
sqlite3_config_lookaside 0 0
|
||||
}
|
||||
sqlite3_initialize
|
||||
sorter_test_fakeheap $fakeheap
|
||||
|
||||
set str [string repeat a 1000]
|
||||
puts $threadsmode
|
||||
|
||||
do_faultsim_test 1.$tn -prep {
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, $::str
|
||||
UNION ALL
|
||||
SELECT x+1, $::str FROM r
|
||||
LIMIT 200
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {40 1000 40 1000 40 1000 40 1000 40 1000}}
|
||||
}
|
||||
|
||||
do_faultsim_test 2.$tn -faults oom* -prep {
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
add_test_utf16bin_collate db
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 100, $::str
|
||||
UNION ALL
|
||||
SELECT x-1, $::str FROM r
|
||||
LIMIT 100
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY y COLLATE utf16bin, (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {20 1000 20 1000 20 1000 20 1000 20 1000}}
|
||||
}
|
||||
|
||||
if {$mmap_limit > 1000000} {
|
||||
set str2 [string repeat $str 10]
|
||||
|
||||
sqlite3_memdebug_vfs_oom_test 0
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
|
||||
do_faultsim_test 3.$tn -faults oom-trans* -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 300, $::str2
|
||||
UNION ALL
|
||||
SELECT x-1, $::str2 FROM r
|
||||
LIMIT 300
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY y, (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {60 10000 60 10000 60 10000 60 10000 60 10000}}
|
||||
}
|
||||
|
||||
sqlite3_memdebug_vfs_oom_test 1
|
||||
}
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_worker_threads 0
|
||||
set t(0) singlethread
|
||||
set t(1) multithread
|
||||
set t(2) serialized
|
||||
sqlite3_config $t($sqlite_options(threadsafe))
|
||||
sqlite3_config_lookaside 100 500
|
||||
sqlite3_initialize
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
}
|
||||
do_test 4.1 {
|
||||
for {set i 0} {$i < 256} {incr i} {
|
||||
execsql {
|
||||
INSERT INTO t1 SELECT
|
||||
((a<<3) + b) & 2147483647,
|
||||
((b<<3) + c) & 2147483647,
|
||||
((c<<3) + a) & 2147483647
|
||||
FROM t1 ORDER BY rowid DESC LIMIT 1;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 4.2 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE UNIQUE INDEX i1 ON t1(a,b,c) }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
set a [string repeat a 500]
|
||||
set b [string repeat b 500]
|
||||
set c [string repeat c 500]
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES($a, $b, $c);
|
||||
INSERT INTO t1 VALUES($c, $b, $a);
|
||||
}
|
||||
|
||||
do_faultsim_test 5.1 -faults oom* -body {
|
||||
execsql { SELECT * FROM t1 ORDER BY a }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 [list $::a $::b $::c $::c $::b $::a]]
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+195
-1
@@ -27,8 +27,10 @@ static const char zHelp[] =
|
||||
" --stats Show statistics at the end\n"
|
||||
" --testset T Run test-set T\n"
|
||||
" --trace Turn on SQL tracing\n"
|
||||
" --threads N Use up to N threads for sorting\n"
|
||||
" --utf16be Set text encoding to UTF-16BE\n"
|
||||
" --utf16le Set text encoding to UTF-16LE\n"
|
||||
" --verify Run additional verification steps.\n"
|
||||
" --without-rowid Use WITHOUT ROWID where appropriate\n"
|
||||
;
|
||||
|
||||
@@ -51,6 +53,7 @@ static struct Global {
|
||||
int bReprepare; /* True to reprepare the SQL on each rerun */
|
||||
int bSqlOnly; /* True to print the SQL once only */
|
||||
int bExplain; /* Print SQL with EXPLAIN prefix */
|
||||
int bVerify; /* Try to verify that results are correct */
|
||||
int szTest; /* Scale factor for test iterations */
|
||||
const char *zWR; /* Might be WITHOUT ROWID */
|
||||
const char *zNN; /* Might be NOT NULL */
|
||||
@@ -931,6 +934,183 @@ void testset_cte(void){
|
||||
|
||||
}
|
||||
|
||||
/* Generate two numbers between 1 and mx. The first number is less than
|
||||
** the second. Usually the numbers are near each other but can sometimes
|
||||
** be far apart.
|
||||
*/
|
||||
static void twoCoords(
|
||||
int p1, int p2, /* Parameters adjusting sizes */
|
||||
unsigned mx, /* Range of 1..mx */
|
||||
unsigned *pX0, unsigned *pX1 /* OUT: write results here */
|
||||
){
|
||||
unsigned d, x0, x1, span;
|
||||
|
||||
span = mx/100 + 1;
|
||||
if( speedtest1_random()%3==0 ) span *= p1;
|
||||
if( speedtest1_random()%p2==0 ) span = mx/2;
|
||||
d = speedtest1_random()%span + 1;
|
||||
x0 = speedtest1_random()%(mx-d) + 1;
|
||||
x1 = x0 + d;
|
||||
*pX0 = x0;
|
||||
*pX1 = x1;
|
||||
}
|
||||
|
||||
/* The following routine is an R-Tree geometry callback. It returns
|
||||
** true if the object overlaps a slice on the Y coordinate between the
|
||||
** two values given as arguments. In other words
|
||||
**
|
||||
** SELECT count(*) FROM rt1 WHERE id MATCH xslice(10,20);
|
||||
**
|
||||
** Is the same as saying:
|
||||
**
|
||||
** SELECT count(*) FROM rt1 WHERE y1>=10 AND y0<=20;
|
||||
*/
|
||||
static int xsliceGeometryCallback(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
double *aCoord,
|
||||
int *pRes
|
||||
){
|
||||
*pRes = aCoord[3]>=p->aParam[0] && aCoord[2]<=p->aParam[1];
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A testset for the R-Tree virtual table
|
||||
*/
|
||||
void testset_rtree(int p1, int p2){
|
||||
unsigned i, n;
|
||||
unsigned mxCoord;
|
||||
unsigned x0, x1, y0, y1, z0, z1;
|
||||
unsigned iStep;
|
||||
int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*100 );
|
||||
|
||||
mxCoord = 15000;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(100, "%d INSERTs into an r-tree", n);
|
||||
speedtest1_exec("BEGIN");
|
||||
speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)");
|
||||
speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)"
|
||||
"VALUES(?1,?2,?3,?4,?5,?6,?7)");
|
||||
for(i=1; i<=n; i++){
|
||||
twoCoords(p1, p2, mxCoord, &x0, &x1);
|
||||
twoCoords(p1, p2, mxCoord, &y0, &y1);
|
||||
twoCoords(p1, p2, mxCoord, &z0, &z1);
|
||||
sqlite3_bind_int(g.pStmt, 1, i);
|
||||
sqlite3_bind_int(g.pStmt, 2, x0);
|
||||
sqlite3_bind_int(g.pStmt, 3, x1);
|
||||
sqlite3_bind_int(g.pStmt, 4, y0);
|
||||
sqlite3_bind_int(g.pStmt, 5, y1);
|
||||
sqlite3_bind_int(g.pStmt, 6, z0);
|
||||
sqlite3_bind_int(g.pStmt, 7, z1);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_exec("COMMIT");
|
||||
speedtest1_end_test();
|
||||
|
||||
speedtest1_begin_test(101, "Copy from rtree to a regular table");
|
||||
speedtest1_exec("CREATE TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
|
||||
speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*20;
|
||||
speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
aCheck[i] = atoi(g.zResult);
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*20;
|
||||
speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
if( aCheck[i]!=atoi(g.zResult) ){
|
||||
fatal_error("Count disagree step %d: %d..%d. %d vs %d",
|
||||
i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
|
||||
}
|
||||
}
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
n = g.szTest*20;
|
||||
speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
aCheck[i] = atoi(g.zResult);
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*20;
|
||||
speedtest1_begin_test(121, "Verify result from 1-D overlap slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
if( aCheck[i]!=atoi(g.zResult) ){
|
||||
fatal_error("Count disagree step %d: %d..%d. %d vs %d",
|
||||
i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
|
||||
}
|
||||
}
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
|
||||
n = g.szTest*20;
|
||||
speedtest1_begin_test(125, "%d custom geometry callback queries", n);
|
||||
sqlite3_rtree_geometry_callback(g.db, "xslice", xsliceGeometryCallback, 0);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE id MATCH xslice(?1,?2)");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
if( aCheck[i]!=atoi(g.zResult) ){
|
||||
fatal_error("Count disagree step %d: %d..%d. %d vs %d",
|
||||
i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
|
||||
}
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*80;
|
||||
speedtest1_begin_test(130, "%d three-dimensional intersect box queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x1>=?1 AND x0<=?2"
|
||||
" AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
|
||||
speedtest1_run();
|
||||
aCheck[i] = atoi(g.zResult);
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(140, "%d rowid queries", n);
|
||||
speedtest1_prepare("SELECT * FROM rt1 WHERE id=?1");
|
||||
for(i=1; i<=n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
/*
|
||||
** A testset used for debugging speedtest1 itself.
|
||||
*/
|
||||
@@ -962,6 +1142,7 @@ int main(int argc, char **argv){
|
||||
int nPCache = 0, szPCache = 0;/* --pcache configuration */
|
||||
int nScratch = 0, szScratch=0;/* --scratch configuration */
|
||||
int showStats = 0; /* True for --stats */
|
||||
int nThread = 0; /* --threads value */
|
||||
const char *zTSet = "main"; /* Which --testset torun */
|
||||
int doTrace = 0; /* True for --trace */
|
||||
const char *zEncoding = 0; /* --utf16be or --utf16le */
|
||||
@@ -1046,10 +1227,15 @@ int main(int argc, char **argv){
|
||||
zTSet = argv[++i];
|
||||
}else if( strcmp(z,"trace")==0 ){
|
||||
doTrace = 1;
|
||||
}else if( strcmp(z,"threads")==0 ){
|
||||
if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
|
||||
nThread = integerValue(argv[++i]);
|
||||
}else if( strcmp(z,"utf16le")==0 ){
|
||||
zEncoding = "utf16le";
|
||||
}else if( strcmp(z,"utf16be")==0 ){
|
||||
zEncoding = "utf16be";
|
||||
}else if( strcmp(z,"verify")==0 ){
|
||||
g.bVerify = 1;
|
||||
}else if( strcmp(z,"without-rowid")==0 ){
|
||||
g.zWR = "WITHOUT ROWID";
|
||||
g.zPK = "PRIMARY KEY";
|
||||
@@ -1092,6 +1278,11 @@ int main(int argc, char **argv){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, pScratch, szScratch, nScratch);
|
||||
if( rc ) fatal_error("scratch configuration failed: %d\n", rc);
|
||||
}
|
||||
#ifdef SQLITE_CONFIG_WORKER_THREADS
|
||||
if( nThread>0 ){
|
||||
sqlite3_config(SQLITE_CONFIG_WORKER_THREADS, nThread);
|
||||
}
|
||||
#endif
|
||||
if( nLook>0 ){
|
||||
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0);
|
||||
}
|
||||
@@ -1141,8 +1332,11 @@ int main(int argc, char **argv){
|
||||
testset_debug1();
|
||||
}else if( strcmp(zTSet,"cte")==0 ){
|
||||
testset_cte();
|
||||
}else if( strcmp(zTSet,"rtree")==0 ){
|
||||
testset_rtree(6, 147);
|
||||
}else{
|
||||
fatal_error("unknown testset: \"%s\"\n", zTSet);
|
||||
fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n",
|
||||
zTSet);
|
||||
}
|
||||
speedtest1_final();
|
||||
|
||||
|
||||
@@ -1076,6 +1076,7 @@ proc explain_i {sql {db db}} {
|
||||
foreach opcode {
|
||||
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
|
||||
NoConflict Next Prev VNext VPrev VFilter
|
||||
SorterSort SorterNext
|
||||
} {
|
||||
set color($opcode) $B
|
||||
}
|
||||
@@ -1098,6 +1099,7 @@ proc explain_i {sql {db db}} {
|
||||
|
||||
if {$opcode=="Next" || $opcode=="Prev"
|
||||
|| $opcode=="VNext" || $opcode=="VPrev"
|
||||
|| $opcode=="SorterNext"
|
||||
} {
|
||||
for {set i $p2} {$i<$addr} {incr i} {
|
||||
incr x($i) 2
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2014 April 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Test that ticket f67b41381a has been resolved.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix tkt-f67b41381a
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ALTER TABLE t1 ADD COLUMN b DEFAULT 2;
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
} {1 2}
|
||||
|
||||
db cache size 0
|
||||
foreach {tn tbls xfer} {
|
||||
1 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b) } 1
|
||||
2 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b) } 0
|
||||
3 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b DEFAULT 'x') } 1
|
||||
4 { CREATE TABLE t1(a, b DEFAULT NULL); CREATE TABLE t2(a, b) } 0
|
||||
5 { CREATE TABLE t1(a DEFAULT 2, b); CREATE TABLE t2(a DEFAULT 1, b) } 1
|
||||
6 { CREATE TABLE t1(a DEFAULT 1, b); CREATE TABLE t2(a DEFAULT 1, b) } 1
|
||||
7 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1);
|
||||
CREATE TABLE t2(a DEFAULT 3, b DEFAULT 1) } 1
|
||||
8 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1);
|
||||
CREATE TABLE t2(a DEFAULT 3, b DEFAULT 3) } 0
|
||||
|
||||
} {
|
||||
|
||||
execsql { DROP TABLE t1; DROP TABLE t2 }
|
||||
execsql $tbls
|
||||
|
||||
set res 1
|
||||
db eval { EXPLAIN INSERT INTO t1 SELECT * FROM t2 } {
|
||||
if {$opcode == "Column"} { set res 0 }
|
||||
}
|
||||
|
||||
do_test 2.$tn [list set res] $xfer
|
||||
}
|
||||
|
||||
finish_test
|
||||
+1
-1
@@ -42,7 +42,7 @@ foreach idxmode {ordered unordered} {
|
||||
1 "SELECT * FROM t1 ORDER BY a"
|
||||
{0 0 0 {SCAN TABLE t1 USING INDEX i1}}
|
||||
{0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
|
||||
2 "SELECT * FROM t1 WHERE a >?"
|
||||
2 "SELECT * FROM t1 WHERE a > 100"
|
||||
{0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
|
||||
{0 0 0 {SCAN TABLE t1}}
|
||||
3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid"
|
||||
|
||||
+7
-1
@@ -811,7 +811,13 @@ do_test wal2-7.1.1 {
|
||||
do_test wal2-7.1.2 {
|
||||
forcecopy test.db test2.db
|
||||
forcecopy test.db-wal test2.db-wal
|
||||
hexio_write test2.db-wal 48 FF
|
||||
# The first 32 bytes of the WAL file contain the WAL header. Offset 48
|
||||
# is the first byte of the checksum for the first frame in the WAL.
|
||||
# The following three lines replaces the contents of that byte with
|
||||
# a different value.
|
||||
set newval FF
|
||||
if {$newval == [hexio_read test2.db-wal 48 1]} { set newval 00 }
|
||||
hexio_write test2.db-wal 48 $newval
|
||||
} {1}
|
||||
do_test wal2-7.1.3 {
|
||||
sqlite3 db2 test2.db
|
||||
|
||||
+2
-1
@@ -231,6 +231,7 @@ do_execsql_test where3-3.0 {
|
||||
CREATE TABLE t301(a INTEGER PRIMARY KEY,b,c);
|
||||
CREATE INDEX t301c ON t301(c);
|
||||
INSERT INTO t301 VALUES(1,2,3);
|
||||
INSERT INTO t301 VALUES(2,2,3);
|
||||
CREATE TABLE t302(x, y);
|
||||
INSERT INTO t302 VALUES(4,5);
|
||||
ANALYZE;
|
||||
@@ -251,7 +252,7 @@ do_execsql_test where3-3.2 {
|
||||
} {}
|
||||
do_execsql_test where3-3.3 {
|
||||
SELECT * FROM t301 WHERE c=3 AND a IS NOT NULL;
|
||||
} {1 2 3}
|
||||
} {1 2 3 2 2 3}
|
||||
|
||||
if 0 { # Query planner no longer does this
|
||||
# Verify that when there are multiple tables in a join which must be
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix whereG
|
||||
|
||||
do_execsql_test whereG-1.0 {
|
||||
CREATE TABLE composer(
|
||||
@@ -179,5 +180,46 @@ do_execsql_test whereG-4.0 {
|
||||
ORDER BY x;
|
||||
} {right}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that likelihood() specifications on indexed terms are taken into
|
||||
# account by various forms of loops.
|
||||
#
|
||||
# 5.1.*: open ended range scans
|
||||
# 5.2.*: skip-scans
|
||||
#
|
||||
reset_db
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
}
|
||||
do_eqp_test 5.1.2 {
|
||||
SELECT * FROM t1 WHERE a>?
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
|
||||
do_eqp_test 5.1.3 {
|
||||
SELECT * FROM t1 WHERE likelihood(a>?, 0.9)
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
|
||||
do_test 5.2 {
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES('abc', $i, $i); }
|
||||
}
|
||||
execsql { INSERT INTO t1 SELECT 'def', b, c FROM t1; }
|
||||
execsql { ANALYZE }
|
||||
} {}
|
||||
do_eqp_test 5.2.2 {
|
||||
SELECT * FROM t1 WHERE likelihood(b>?, 0.01)
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (ANY(a) AND b>?)}}
|
||||
do_eqp_test 5.2.3 {
|
||||
SELECT * FROM t1 WHERE likelihood(b>?, 0.9)
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
|
||||
do_eqp_test 5.3.1 {
|
||||
SELECT * FROM t1 WHERE a=?
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
|
||||
do_eqp_test 5.3.2 {
|
||||
SELECT * FROM t1 WHERE likelihood(a=?, 0.9)
|
||||
} {0 0 0 {SCAN TABLE t1}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+5
-2
@@ -83,7 +83,8 @@ static LogEst logEstFromDouble(double x){
|
||||
LogEst e;
|
||||
assert( sizeof(x)==8 && sizeof(a)==8 );
|
||||
if( x<=0.0 ) return -32768;
|
||||
if( x<1.0 ) return -logEstFromDouble(1/x);
|
||||
if( x<0.01 ) return -logEstFromDouble(1.0/x);
|
||||
if( x<1.0 ) return logEstFromDouble(100.0*x) - 66;
|
||||
if( x<1024.0 ) return logEstFromInteger((sqlite3_uint64)(1024.0*x)) - 100;
|
||||
if( x<=2000000000.0 ) return logEstFromInteger((sqlite3_uint64)x);
|
||||
memcpy(&a, &x, 8);
|
||||
@@ -156,8 +157,10 @@ int main(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
for(i=n-1; i>=0; i--){
|
||||
if( a[i]<0 ){
|
||||
if( a[i]<-40 ){
|
||||
printf("%5d (%f)\n", a[i], 1.0/(double)logEstToInt(-a[i]));
|
||||
}else if( a[i]<10 ){
|
||||
printf("%5d (%f)\n", a[i], logEstToInt(a[i]+100)/1024.0);
|
||||
}else{
|
||||
sqlite3_uint64 x = logEstToInt(a[i]+100)*100/1024;
|
||||
printf("%5d (%lld.%02lld)\n", a[i], x/100, x%100);
|
||||
|
||||
@@ -237,6 +237,7 @@ foreach file {
|
||||
malloc.c
|
||||
printf.c
|
||||
random.c
|
||||
threads.c
|
||||
utf.c
|
||||
util.c
|
||||
hash.c
|
||||
|
||||
@@ -249,6 +249,7 @@ foreach file {
|
||||
malloc.c
|
||||
printf.c
|
||||
random.c
|
||||
threads.c
|
||||
utf.c
|
||||
util.c
|
||||
hash.c
|
||||
|
||||
Reference in New Issue
Block a user