1c9e8493eSdrh# 2007 October 23 2c9e8493eSdrh# 3c9e8493eSdrh# The author disclaims copyright to this source code. In place of 4c9e8493eSdrh# a legal notice, here is a blessing: 5c9e8493eSdrh# 6c9e8493eSdrh# May you do good and not evil. 7c9e8493eSdrh# May you find forgiveness for yourself and forgive others. 8c9e8493eSdrh# May you share freely, never taking more than you give. 9c9e8493eSdrh# 10c9e8493eSdrh#************************************************************************* 11c9e8493eSdrh# This file implements regression tests for SQLite library. The 12c9e8493eSdrh# focus of this script is measuring executing speed. More specifically, 13c9e8493eSdrh# the focus is on the speed of: 14c9e8493eSdrh# 15c9e8493eSdrh# * joins 16c9e8493eSdrh# * views 17c9e8493eSdrh# * sub-selects 18c9e8493eSdrh# * triggers 19c9e8493eSdrh# 20*67ade0a2Sdrh# $Id: speed4p.test,v 1.4 2008/04/10 13:32:37 drh Exp $ 21c9e8493eSdrh# 22c9e8493eSdrh 23c9e8493eSdrhset testdir [file dirname $argv0] 24c9e8493eSdrhsource $testdir/tester.tcl 25c9e8493eSdrhspeed_trial_init speed1 26c9e8493eSdrh 27c9e8493eSdrh# Set a uniform random seed 28c9e8493eSdrhexpr srand(0) 29c9e8493eSdrh 30c9e8493eSdrhset sqlout [open speed1.txt w] 31c9e8493eSdrhproc tracesql {sql} { 32c9e8493eSdrh puts $::sqlout $sql\; 33c9e8493eSdrh} 34c9e8493eSdrh#db trace tracesql 35c9e8493eSdrh 36c9e8493eSdrh# The number_name procedure below converts its argment (an integer) 37c9e8493eSdrh# into a string which is the English-language name for that number. 38c9e8493eSdrh# 39c9e8493eSdrh# Example: 40c9e8493eSdrh# 41c9e8493eSdrh# puts [number_name 123] -> "one hundred twenty three" 42c9e8493eSdrh# 43c9e8493eSdrhset ones {zero one two three four five six seven eight nine 44c9e8493eSdrh ten eleven twelve thirteen fourteen fifteen sixteen seventeen 45c9e8493eSdrh eighteen nineteen} 46c9e8493eSdrhset tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety} 47c9e8493eSdrhproc number_name {n} { 48c9e8493eSdrh if {$n>=1000} { 49c9e8493eSdrh set txt "[number_name [expr {$n/1000}]] thousand" 50c9e8493eSdrh set n [expr {$n%1000}] 51c9e8493eSdrh } else { 52c9e8493eSdrh set txt {} 53c9e8493eSdrh } 54c9e8493eSdrh if {$n>=100} { 55c9e8493eSdrh append txt " [lindex $::ones [expr {$n/100}]] hundred" 56c9e8493eSdrh set n [expr {$n%100}] 57c9e8493eSdrh } 58c9e8493eSdrh if {$n>=20} { 59c9e8493eSdrh append txt " [lindex $::tens [expr {$n/10}]]" 60c9e8493eSdrh set n [expr {$n%10}] 61c9e8493eSdrh } 62c9e8493eSdrh if {$n>0} { 63c9e8493eSdrh append txt " [lindex $::ones $n]" 64c9e8493eSdrh } 65c9e8493eSdrh set txt [string trim $txt] 66c9e8493eSdrh if {$txt==""} {set txt zero} 67c9e8493eSdrh return $txt 68c9e8493eSdrh} 69c9e8493eSdrh 70c9e8493eSdrh# Summary of tests: 71c9e8493eSdrh# 72c9e8493eSdrh# speed4p-join1: Join three tables using IPK index. 73c9e8493eSdrh# speed4p-join2: Join three tables using an index. 74c9e8493eSdrh# speed4p-join3: Join two tables without an index. 75c9e8493eSdrh# 76c9e8493eSdrh# speed4p-view1: Querying a view. 77c9e8493eSdrh# speed4p-table1: Same queries as in speed4p-view1, but run directly against 78c9e8493eSdrh# the tables for comparison purposes. 79c9e8493eSdrh# 80c9e8493eSdrh# speed4p-subselect1: A SELECT statement that uses many sub-queries.. 81c9e8493eSdrh# 82c9e8493eSdrh# speed4p-trigger1: An INSERT statement that fires a trigger. 83c9e8493eSdrh# speed4p-trigger2: An UPDATE statement that fires a trigger. 84c9e8493eSdrh# speed4p-trigger3: A DELETE statement that fires a trigger. 85c9e8493eSdrh# speed4p-notrigger1: Same operation as trigger1, but without the trigger. 86c9e8493eSdrh# speed4p-notrigger2: " trigger2 " 87c9e8493eSdrh# speed4p-notrigger3: " trigger3 " 88c9e8493eSdrh# 89c9e8493eSdrh 90c9e8493eSdrh# Set up the schema. Each of the tables t1, t2 and t3 contain 50,000 rows. 91c9e8493eSdrh# This creates a database of around 16MB. 92c9e8493eSdrhexecsql { 93f704c1d6Sdrh PRAGMA page_size=1024; 94f704c1d6Sdrh PRAGMA cache_size=8192; 95f704c1d6Sdrh PRAGMA locking_mode=EXCLUSIVE; 96c9e8493eSdrh BEGIN; 97c9e8493eSdrh CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT); 98c9e8493eSdrh CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT); 99c9e8493eSdrh CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT); 100c9e8493eSdrh 101c9e8493eSdrh CREATE VIEW v1 AS SELECT rowid, i, t FROM t1; 102c9e8493eSdrh CREATE VIEW v2 AS SELECT rowid, i, t FROM t2; 103c9e8493eSdrh CREATE VIEW v3 AS SELECT rowid, i, t FROM t3; 104c9e8493eSdrh} 105c9e8493eSdrhfor {set jj 1} {$jj <= 3} {incr jj} { 106c9e8493eSdrh set stmt [string map "%T% t$jj" {INSERT INTO %T% VALUES(NULL, $i, $t)}] 107c9e8493eSdrh for {set ii 0} {$ii < 50000} {incr ii} { 108c9e8493eSdrh set i [expr {int(rand()*50000)}] 109c9e8493eSdrh set t [number_name $i] 110c9e8493eSdrh execsql $stmt 111c9e8493eSdrh } 112c9e8493eSdrh} 113c9e8493eSdrhexecsql { 114c9e8493eSdrh CREATE INDEX i1 ON t1(t); 115c9e8493eSdrh CREATE INDEX i2 ON t2(t); 116c9e8493eSdrh CREATE INDEX i3 ON t3(t); 117c9e8493eSdrh COMMIT; 118c9e8493eSdrh} 119c9e8493eSdrh 120c9e8493eSdrh# Join t1, t2, t3 on IPK. 121c9e8493eSdrhset sql "SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid" 122c9e8493eSdrhspeed_trial speed4p-join1 50000 row $sql 123c9e8493eSdrh 124c9e8493eSdrh# Join t1, t2, t3 on the non-IPK index. 125c9e8493eSdrhset sql "SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t" 126c9e8493eSdrhspeed_trial speed4p-join2 50000 row $sql 127c9e8493eSdrh 128c9e8493eSdrh# Run 10000 simple queries against the views. 129c9e8493eSdrhset script { 130c9e8493eSdrh for {set ii 1} {$ii < 10000} {incr ii} { 131c9e8493eSdrh set v [expr {$ii*3}] 132c9e8493eSdrh set t [expr {$ii%3+1}] 133c9e8493eSdrh db eval "SELECT * FROM v$t WHERE rowid = \$v" 134c9e8493eSdrh } 135c9e8493eSdrh} 136c9e8493eSdrhspeed_trial_tcl speed4p-view1 10000 stmt $script 137c9e8493eSdrh 138c9e8493eSdrh# Run the same 10000 simple queries as in the previous test case against 139c9e8493eSdrh# the underlying tables. The compiled vdbe programs should be identical, so 140c9e8493eSdrh# the only difference in running time is the extra time taken to compile 141c9e8493eSdrh# the view definitions. 142c9e8493eSdrh# 143c9e8493eSdrhset script { 144c9e8493eSdrh for {set ii 1} {$ii < 10000} {incr ii} { 145c9e8493eSdrh set v [expr {$ii*3}] 146c9e8493eSdrh set t [expr {$ii%3+1}] 147c9e8493eSdrh db eval "SELECT t FROM t$t WHERE rowid = \$v" 148c9e8493eSdrh } 149c9e8493eSdrh} 150c9e8493eSdrhspeed_trial_tcl speed4p-table1 10000 stmt $script 151c9e8493eSdrh 152c9e8493eSdrh# Run a SELECT that uses sub-queries 10000 times. A total of 30000 sub-selects. 153c9e8493eSdrh# 154c9e8493eSdrhset script { 155c9e8493eSdrh for {set ii 1} {$ii < 10000} {incr ii} { 156c9e8493eSdrh set v [expr {$ii*3}] 157c9e8493eSdrh db eval { 158c9e8493eSdrh SELECT (SELECT t FROM t1 WHERE rowid = $v), 159c9e8493eSdrh (SELECT t FROM t2 WHERE rowid = $v), 160c9e8493eSdrh (SELECT t FROM t3 WHERE rowid = $v) 161c9e8493eSdrh } 162c9e8493eSdrh } 163c9e8493eSdrh} 164c9e8493eSdrhspeed_trial_tcl speed4p-subselect1 10000 stmt $script 165c9e8493eSdrh 166*67ade0a2Sdrh# Single-row updates performance. 167*67ade0a2Sdrh# 168*67ade0a2Sdrhset script { 169*67ade0a2Sdrh db eval BEGIN 170*67ade0a2Sdrh for {set ii 1} {$ii < 10000} {incr ii} { 171*67ade0a2Sdrh db eval {UPDATE t1 SET i=i+1 WHERE rowid=$ii} 172*67ade0a2Sdrh } 173*67ade0a2Sdrh db eval COMMIT 174*67ade0a2Sdrh} 175*67ade0a2Sdrhspeed_trial_tcl speed4p-rowid-update 10000 stmt $script 176*67ade0a2Sdrh 177*67ade0a2Sdrh 178*67ade0a2Sdrhdb eval { 179*67ade0a2Sdrh CREATE TABLE t5(t TEXT PRIMARY KEY, i INTEGER); 180*67ade0a2Sdrh} 181*67ade0a2Sdrhspeed_trial speed4p-insert-ignore 50000 row { 182*67ade0a2Sdrh INSERT OR IGNORE INTO t5 SELECT t, i FROM t1; 183*67ade0a2Sdrh} 184*67ade0a2Sdrh 185*67ade0a2Sdrhset list [db eval {SELECT t FROM t5}] 186*67ade0a2Sdrhset script { 187*67ade0a2Sdrh db eval BEGIN 188*67ade0a2Sdrh foreach t $::list { 189*67ade0a2Sdrh db eval {UPDATE t5 SET i=i+1 WHERE t=$t} 190*67ade0a2Sdrh } 191*67ade0a2Sdrh db eval COMMIT 192*67ade0a2Sdrh} 193*67ade0a2Sdrhspeed_trial_tcl speed4p-unique-update [llength $list] stmt $script 194*67ade0a2Sdrh 195c9e8493eSdrh# The following block tests the speed of some DML statements that cause 196c9e8493eSdrh# triggers to fire. 197c9e8493eSdrh# 198c9e8493eSdrhexecsql { 199c9e8493eSdrh CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT); 200c9e8493eSdrh CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT); 201c9e8493eSdrh CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN 202c9e8493eSdrh INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t); 203c9e8493eSdrh END; 204c9e8493eSdrh CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN 205c9e8493eSdrh INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t); 206c9e8493eSdrh END; 207c9e8493eSdrh CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN 208c9e8493eSdrh INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t); 209c9e8493eSdrh END; 210c9e8493eSdrh BEGIN; 211c9e8493eSdrh} 212c9e8493eSdrhset list {} 213c9e8493eSdrhfor {set ii 1} {$ii < 10000} {incr ii} { 214c9e8493eSdrh lappend list $ii [number_name $ii] 215c9e8493eSdrh} 216c9e8493eSdrhset script { 217c9e8493eSdrh foreach {ii name} $::list { 218c9e8493eSdrh db eval {INSERT INTO t4 VALUES(NULL, $ii, $name)} 219c9e8493eSdrh } 220c9e8493eSdrh} 221c9e8493eSdrhspeed_trial_tcl speed4p-trigger1 10000 stmt $script 222c9e8493eSdrh 223c9e8493eSdrhset list {} 224c9e8493eSdrhfor {set ii 1} {$ii < 20000} {incr ii 2} { 225c9e8493eSdrh set ii2 [expr {$ii*2}] 226c9e8493eSdrh lappend list $ii $ii2 [number_name $ii2] 227c9e8493eSdrh} 228c9e8493eSdrhset script { 229c9e8493eSdrh foreach {ii ii2 name} $::list { 230c9e8493eSdrh db eval { 231c9e8493eSdrh UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii; 232c9e8493eSdrh } 233c9e8493eSdrh } 234c9e8493eSdrh} 235c9e8493eSdrhspeed_trial_tcl speed4p-trigger2 10000 stmt $script 236c9e8493eSdrh 237c9e8493eSdrhset script { 238c9e8493eSdrh for {set ii 1} {$ii < 20000} {incr ii 2} { 239c9e8493eSdrh db eval {DELETE FROM t4 WHERE rowid = $ii} 240c9e8493eSdrh } 241c9e8493eSdrh} 242c9e8493eSdrhspeed_trial_tcl speed4p-trigger3 10000 stmt $script 243c9e8493eSdrhexecsql {COMMIT} 244c9e8493eSdrh 245c9e8493eSdrh# The following block contains the same tests as the above block that 246c9e8493eSdrh# tests triggers, with one crucial difference: no triggers are defined. 247c9e8493eSdrh# So the difference in speed between these tests and the preceding ones 248c9e8493eSdrh# is the amount of time taken to compile and execute the trigger programs. 249c9e8493eSdrh# 250c9e8493eSdrhexecsql { 251c9e8493eSdrh DROP TABLE t4; 252c9e8493eSdrh DROP TABLE log; 253c9e8493eSdrh VACUUM; 254c9e8493eSdrh CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT); 255c9e8493eSdrh BEGIN; 256c9e8493eSdrh} 257c9e8493eSdrhset list {} 258c9e8493eSdrhfor {set ii 1} {$ii < 10000} {incr ii} { 259c9e8493eSdrh lappend list $ii [number_name $ii] 260c9e8493eSdrh} 261c9e8493eSdrhset script { 262c9e8493eSdrh foreach {ii name} $::list { 263c9e8493eSdrh db eval {INSERT INTO t4 VALUES(NULL, $ii, $name);} 264c9e8493eSdrh } 265c9e8493eSdrh} 266c9e8493eSdrhspeed_trial_tcl speed4p-notrigger1 10000 stmt $script 267c9e8493eSdrh 268c9e8493eSdrhset list {} 269c9e8493eSdrhfor {set ii 1} {$ii < 20000} {incr ii 2} { 270c9e8493eSdrh set ii2 [expr {$ii*2}] 271c9e8493eSdrh lappend list $ii $ii2 [number_name $ii2] 272c9e8493eSdrh} 273c9e8493eSdrhset script { 274c9e8493eSdrh foreach {ii ii2 name} $::list { 275c9e8493eSdrh db eval { 276c9e8493eSdrh UPDATE t4 SET i = $ii2, t = $name WHERE rowid = $ii; 277c9e8493eSdrh } 278c9e8493eSdrh } 279c9e8493eSdrh} 280c9e8493eSdrhspeed_trial_tcl speed4p-notrigger2 10000 stmt $script 281c9e8493eSdrh 282c9e8493eSdrhset script { 283c9e8493eSdrh for {set ii 1} {$ii < 20000} {incr ii 2} { 284c9e8493eSdrh db eval {DELETE FROM t4 WHERE rowid = $ii} 285c9e8493eSdrh } 286c9e8493eSdrh} 287c9e8493eSdrhspeed_trial_tcl speed4p-notrigger3 10000 stmt $script 288c9e8493eSdrhexecsql {COMMIT} 289c9e8493eSdrh 290c9e8493eSdrhspeed_trial_summary speed4 291c9e8493eSdrhfinish_test 292