| # 2017-12-16 |
| # |
| # 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 cases for the sqlite_offset() function. |
| # |
| # Some of the tests in this file depend on the exact placement of content |
| # within b-tree pages. Such placement is at the implementations discretion, |
| # and so it is possible for results to change from one release to the next. |
| # |
| set testdir [file dirname $argv0] |
| source $testdir/tester.tcl |
| ifcapable !offset_sql_func { |
| finish_test |
| return |
| } |
| |
| set bNullTrim 0 |
| ifcapable null_trim { |
| set bNullTrim 1 |
| } |
| |
| do_execsql_test func6-100 { |
| PRAGMA page_size=4096; |
| PRAGMA auto_vacuum=NONE; |
| CREATE TABLE t1(a,b,c,d); |
| WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) |
| INSERT INTO t1(a,b,c,d) SELECT printf('abc%03x',x), x, 1000-x, NULL FROM c; |
| CREATE INDEX t1a ON t1(a); |
| CREATE INDEX t1bc ON t1(b,c); |
| CREATE TABLE t2(x TEXT PRIMARY KEY, y) WITHOUT ROWID; |
| INSERT INTO t2(x,y) SELECT a, b FROM t1; |
| } |
| |
| # Load the contents of $file from disk and return it encoded as a hex |
| # string. |
| proc loadhex {file} { |
| set fd [open $file] |
| fconfigure $fd -translation binary -encoding binary |
| set data [read $fd] |
| close $fd |
| binary encode hex $data |
| } |
| |
| # Each argument is either an integer between 0 and 65535, a text value, or |
| # an empty string representing an SQL NULL. This command builds an SQLite |
| # record containing the values passed as arguments and returns it encoded |
| # as a hex string. |
| proc hexrecord {args} { |
| set hdr "" |
| set body "" |
| |
| if {$::bNullTrim} { |
| while {[llength $args] && [lindex $args end]=={}} { |
| set args [lrange $args 0 end-1] |
| } |
| } |
| |
| foreach x $args { |
| if {$x==""} { |
| append hdr 00 |
| } elseif {[string is integer $x]==0} { |
| set n [string length $x] |
| append hdr [format %02x [expr $n*2 + 13]] |
| append body [binary encode hex $x] |
| } elseif {$x == 0} { |
| append hdr 08 |
| } elseif {$x == 1} { |
| append hdr 09 |
| } elseif {$x <= 127} { |
| append hdr 01 |
| append body [format %02x $x] |
| } else { |
| append hdr 02 |
| append body [format %04x $x] |
| } |
| } |
| set res [format %02x [expr 1 + [string length $hdr]/2]] |
| append res $hdr |
| append res $body |
| } |
| |
| # Argument $off is an offset into the database image encoded as a hex string |
| # in argument $hexdb. This command returns 0 if the offset contains the hex |
| # $hexrec, or throws an exception otherwise. |
| # |
| proc offset_contains_record {off hexdb hexrec} { |
| set n [string length $hexrec] |
| set off [expr $off*2] |
| if { [string compare $hexrec [string range $hexdb $off [expr $off+$n-1]]] } { |
| error "record not found!" |
| } |
| return 0 |
| } |
| |
| # This command is the implementation of SQL function "offrec()". The first |
| # argument to this is an offset value. The remaining values are used to |
| # formulate an SQLite record. If database file test.db does not contain |
| # an equivalent record at the specified offset, an exception is thrown. |
| # Otherwise, 0 is returned. |
| # |
| proc offrec {args} { |
| set offset [lindex $args 0] |
| set rec [hexrecord {*}[lrange $args 1 end]] |
| offset_contains_record $offset $::F $rec |
| } |
| set F [loadhex test.db] |
| db func offrec offrec |
| |
| # Test the sanity of the tests. |
| if {$bNullTrim} { |
| set offset 8180 |
| } else { |
| set offset 8179 |
| } |
| do_execsql_test func6-105 { |
| SELECT sqlite_offset(d) FROM t1 ORDER BY rowid LIMIT 1; |
| } $offset |
| do_test func6-106 { |
| set r [hexrecord abc001 1 999 {}] |
| offset_contains_record $offset $F $r |
| } 0 |
| |
| set z100 [string trim [string repeat "0 " 100]] |
| |
| # Test offsets within table b-tree t1. |
| do_execsql_test func6-110 { |
| SELECT offrec(sqlite_offset(d), a, b, c, d) FROM t1 ORDER BY rowid |
| } $z100 |
| |
| do_execsql_test func6-120 { |
| SELECT a, typeof(sqlite_offset(+a)) FROM t1 |
| ORDER BY rowid LIMIT 2; |
| } {abc001 null abc002 null} |
| |
| # Test offsets within index b-tree t1a. |
| do_execsql_test func6-130 { |
| SELECT offrec(sqlite_offset(a), a, rowid) FROM t1 ORDER BY a |
| } $z100 |
| |
| # Test offsets within table b-tree t1 with a temp b-tree ORDER BY. |
| do_execsql_test func6-140 { |
| SELECT offrec(sqlite_offset(d), a, b, c, d) FROM t1 ORDER BY a |
| } $z100 |
| |
| # Test offsets from both index t1a and table t1 in the same query. |
| do_execsql_test func6-150 { |
| SELECT offrec(sqlite_offset(a), a, rowid), |
| offrec(sqlite_offset(d), a, b, c, d) |
| FROM t1 ORDER BY a |
| } [concat $z100 $z100] |
| |
| # Test offsets from both index t1bc and table t1 in the same query. |
| do_execsql_test func6-160 { |
| SELECT offrec(sqlite_offset(b), b, c, rowid), |
| offrec(sqlite_offset(c), b, c, rowid), |
| offrec(sqlite_offset(d), a, b, c, d) |
| FROM t1 |
| ORDER BY b |
| } [concat $z100 $z100 $z100] |
| |
| # Test offsets in WITHOUT ROWID table t2. |
| do_execsql_test func6-200 { |
| SELECT offrec( sqlite_offset(y), x, y ) FROM t2 ORDER BY x |
| } $z100 |
| |
| # 2022-03-14 dbsqlfuzz 474499f3977d95fdf2dbcd99c50be1d0082e4c92 |
| reset_db |
| do_execsql_test func6-300 { |
| CREATE TABLE t2(a INT, b INT PRIMARY KEY) WITHOUT ROWID; |
| CREATE INDEX x3 ON t2(b); |
| CREATE TABLE t1(a INT PRIMARY KEY, b TEXT); |
| SELECT * FROM t1 WHERE a IN (SELECT sqlite_offset(b) FROM t2); |
| } {} |
| |
| finish_test |