sl@0: # 2001 September 15 sl@0: # sl@0: # The author disclaims copyright to this source code. In place of sl@0: # a legal notice, here is a blessing: sl@0: # sl@0: # May you do good and not evil. sl@0: # May you find forgiveness for yourself and forgive others. sl@0: # May you share freely, never taking more than you give. sl@0: # sl@0: #*********************************************************************** sl@0: # This file implements regression tests for SQLite library. The sl@0: # focus of this file is testing built-in functions. sl@0: # sl@0: # $Id: func.test,v 1.86 2008/08/04 03:51:24 danielk1977 Exp $ sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: # Create a table to work with. sl@0: # sl@0: do_test func-0.0 { sl@0: execsql {CREATE TABLE tbl1(t1 text)} sl@0: foreach word {this program is free software} { sl@0: execsql "INSERT INTO tbl1 VALUES('$word')" sl@0: } sl@0: execsql {SELECT t1 FROM tbl1 ORDER BY t1} sl@0: } {free is program software this} sl@0: do_test func-0.1 { sl@0: execsql { sl@0: CREATE TABLE t2(a); sl@0: INSERT INTO t2 VALUES(1); sl@0: INSERT INTO t2 VALUES(NULL); sl@0: INSERT INTO t2 VALUES(345); sl@0: INSERT INTO t2 VALUES(NULL); sl@0: INSERT INTO t2 VALUES(67890); sl@0: SELECT * FROM t2; sl@0: } sl@0: } {1 {} 345 {} 67890} sl@0: sl@0: # Check out the length() function sl@0: # sl@0: do_test func-1.0 { sl@0: execsql {SELECT length(t1) FROM tbl1 ORDER BY t1} sl@0: } {4 2 7 8 4} sl@0: do_test func-1.1 { sl@0: set r [catch {execsql {SELECT length(*) FROM tbl1 ORDER BY t1}} msg] sl@0: lappend r $msg sl@0: } {1 {wrong number of arguments to function length()}} sl@0: do_test func-1.2 { sl@0: set r [catch {execsql {SELECT length(t1,5) FROM tbl1 ORDER BY t1}} msg] sl@0: lappend r $msg sl@0: } {1 {wrong number of arguments to function length()}} sl@0: do_test func-1.3 { sl@0: execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1) sl@0: ORDER BY length(t1)} sl@0: } {2 1 4 2 7 1 8 1} sl@0: do_test func-1.4 { sl@0: execsql {SELECT coalesce(length(a),-1) FROM t2} sl@0: } {1 -1 3 -1 5} sl@0: sl@0: # Check out the substr() function sl@0: # sl@0: do_test func-2.0 { sl@0: execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1} sl@0: } {fr is pr so th} sl@0: do_test func-2.1 { sl@0: execsql {SELECT substr(t1,2,1) FROM tbl1 ORDER BY t1} sl@0: } {r s r o h} sl@0: do_test func-2.2 { sl@0: execsql {SELECT substr(t1,3,3) FROM tbl1 ORDER BY t1} sl@0: } {ee {} ogr ftw is} sl@0: do_test func-2.3 { sl@0: execsql {SELECT substr(t1,-1,1) FROM tbl1 ORDER BY t1} sl@0: } {e s m e s} sl@0: do_test func-2.4 { sl@0: execsql {SELECT substr(t1,-1,2) FROM tbl1 ORDER BY t1} sl@0: } {e s m e s} sl@0: do_test func-2.5 { sl@0: execsql {SELECT substr(t1,-2,1) FROM tbl1 ORDER BY t1} sl@0: } {e i a r i} sl@0: do_test func-2.6 { sl@0: execsql {SELECT substr(t1,-2,2) FROM tbl1 ORDER BY t1} sl@0: } {ee is am re is} sl@0: do_test func-2.7 { sl@0: execsql {SELECT substr(t1,-4,2) FROM tbl1 ORDER BY t1} sl@0: } {fr {} gr wa th} sl@0: do_test func-2.8 { sl@0: execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)} sl@0: } {this software free program is} sl@0: do_test func-2.9 { sl@0: execsql {SELECT substr(a,1,1) FROM t2} sl@0: } {1 {} 3 {} 6} sl@0: do_test func-2.10 { sl@0: execsql {SELECT substr(a,2,2) FROM t2} sl@0: } {{} {} 45 {} 78} sl@0: sl@0: # Only do the following tests if TCL has UTF-8 capabilities sl@0: # sl@0: if {"\u1234"!="u1234"} { sl@0: sl@0: # Put some UTF-8 characters in the database sl@0: # sl@0: do_test func-3.0 { sl@0: execsql {DELETE FROM tbl1} sl@0: foreach word "contains UTF-8 characters hi\u1234ho" { sl@0: execsql "INSERT INTO tbl1 VALUES('$word')" sl@0: } sl@0: execsql {SELECT t1 FROM tbl1 ORDER BY t1} sl@0: } "UTF-8 characters contains hi\u1234ho" sl@0: do_test func-3.1 { sl@0: execsql {SELECT length(t1) FROM tbl1 ORDER BY t1} sl@0: } {5 10 8 5} sl@0: do_test func-3.2 { sl@0: execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1} sl@0: } {UT ch co hi} sl@0: do_test func-3.3 { sl@0: execsql {SELECT substr(t1,1,3) FROM tbl1 ORDER BY t1} sl@0: } "UTF cha con hi\u1234" sl@0: do_test func-3.4 { sl@0: execsql {SELECT substr(t1,2,2) FROM tbl1 ORDER BY t1} sl@0: } "TF ha on i\u1234" sl@0: do_test func-3.5 { sl@0: execsql {SELECT substr(t1,2,3) FROM tbl1 ORDER BY t1} sl@0: } "TF- har ont i\u1234h" sl@0: do_test func-3.6 { sl@0: execsql {SELECT substr(t1,3,2) FROM tbl1 ORDER BY t1} sl@0: } "F- ar nt \u1234h" sl@0: do_test func-3.7 { sl@0: execsql {SELECT substr(t1,4,2) FROM tbl1 ORDER BY t1} sl@0: } "-8 ra ta ho" sl@0: do_test func-3.8 { sl@0: execsql {SELECT substr(t1,-1,1) FROM tbl1 ORDER BY t1} sl@0: } "8 s s o" sl@0: do_test func-3.9 { sl@0: execsql {SELECT substr(t1,-3,2) FROM tbl1 ORDER BY t1} sl@0: } "F- er in \u1234h" sl@0: do_test func-3.10 { sl@0: execsql {SELECT substr(t1,-4,3) FROM tbl1 ORDER BY t1} sl@0: } "TF- ter ain i\u1234h" sl@0: do_test func-3.99 { sl@0: execsql {DELETE FROM tbl1} sl@0: foreach word {this program is free software} { sl@0: execsql "INSERT INTO tbl1 VALUES('$word')" sl@0: } sl@0: execsql {SELECT t1 FROM tbl1} sl@0: } {this program is free software} sl@0: sl@0: } ;# End \u1234!=u1234 sl@0: sl@0: # Test the abs() and round() functions. sl@0: # sl@0: do_test func-4.1 { sl@0: execsql { sl@0: CREATE TABLE t1(a,b,c); sl@0: INSERT INTO t1 VALUES(1,2,3); sl@0: INSERT INTO t1 VALUES(2,1.2345678901234,-12345.67890); sl@0: INSERT INTO t1 VALUES(3,-2,-5); sl@0: } sl@0: catchsql {SELECT abs(a,b) FROM t1} sl@0: } {1 {wrong number of arguments to function abs()}} sl@0: do_test func-4.2 { sl@0: catchsql {SELECT abs() FROM t1} sl@0: } {1 {wrong number of arguments to function abs()}} sl@0: do_test func-4.3 { sl@0: catchsql {SELECT abs(b) FROM t1 ORDER BY a} sl@0: } {0 {2 1.2345678901234 2}} sl@0: do_test func-4.4 { sl@0: catchsql {SELECT abs(c) FROM t1 ORDER BY a} sl@0: } {0 {3 12345.6789 5}} sl@0: do_test func-4.4.1 { sl@0: execsql {SELECT abs(a) FROM t2} sl@0: } {1 {} 345 {} 67890} sl@0: do_test func-4.4.2 { sl@0: execsql {SELECT abs(t1) FROM tbl1} sl@0: } {0.0 0.0 0.0 0.0 0.0} sl@0: sl@0: do_test func-4.5 { sl@0: catchsql {SELECT round(a,b,c) FROM t1} sl@0: } {1 {wrong number of arguments to function round()}} sl@0: do_test func-4.6 { sl@0: catchsql {SELECT round(b,2) FROM t1 ORDER BY b} sl@0: } {0 {-2.0 1.23 2.0}} sl@0: do_test func-4.7 { sl@0: catchsql {SELECT round(b,0) FROM t1 ORDER BY a} sl@0: } {0 {2.0 1.0 -2.0}} sl@0: do_test func-4.8 { sl@0: catchsql {SELECT round(c) FROM t1 ORDER BY a} sl@0: } {0 {3.0 -12346.0 -5.0}} sl@0: do_test func-4.9 { sl@0: catchsql {SELECT round(c,a) FROM t1 ORDER BY a} sl@0: } {0 {3.0 -12345.68 -5.0}} sl@0: do_test func-4.10 { sl@0: catchsql {SELECT 'x' || round(c,a) || 'y' FROM t1 ORDER BY a} sl@0: } {0 {x3.0y x-12345.68y x-5.0y}} sl@0: do_test func-4.11 { sl@0: catchsql {SELECT round() FROM t1 ORDER BY a} sl@0: } {1 {wrong number of arguments to function round()}} sl@0: do_test func-4.12 { sl@0: execsql {SELECT coalesce(round(a,2),'nil') FROM t2} sl@0: } {1.0 nil 345.0 nil 67890.0} sl@0: do_test func-4.13 { sl@0: execsql {SELECT round(t1,2) FROM tbl1} sl@0: } {0.0 0.0 0.0 0.0 0.0} sl@0: do_test func-4.14 { sl@0: execsql {SELECT typeof(round(5.1,1));} sl@0: } {real} sl@0: do_test func-4.15 { sl@0: execsql {SELECT typeof(round(5.1));} sl@0: } {real} sl@0: sl@0: sl@0: # Test the upper() and lower() functions sl@0: # sl@0: do_test func-5.1 { sl@0: execsql {SELECT upper(t1) FROM tbl1} sl@0: } {THIS PROGRAM IS FREE SOFTWARE} sl@0: do_test func-5.2 { sl@0: execsql {SELECT lower(upper(t1)) FROM tbl1} sl@0: } {this program is free software} sl@0: do_test func-5.3 { sl@0: execsql {SELECT upper(a), lower(a) FROM t2} sl@0: } {1 1 {} {} 345 345 {} {} 67890 67890} sl@0: ifcapable !icu { sl@0: do_test func-5.4 { sl@0: catchsql {SELECT upper(a,5) FROM t2} sl@0: } {1 {wrong number of arguments to function upper()}} sl@0: } sl@0: do_test func-5.5 { sl@0: catchsql {SELECT upper(*) FROM t2} sl@0: } {1 {wrong number of arguments to function upper()}} sl@0: sl@0: # Test the coalesce() and nullif() functions sl@0: # sl@0: do_test func-6.1 { sl@0: execsql {SELECT coalesce(a,'xyz') FROM t2} sl@0: } {1 xyz 345 xyz 67890} sl@0: do_test func-6.2 { sl@0: execsql {SELECT coalesce(upper(a),'nil') FROM t2} sl@0: } {1 nil 345 nil 67890} sl@0: do_test func-6.3 { sl@0: execsql {SELECT coalesce(nullif(1,1),'nil')} sl@0: } {nil} sl@0: do_test func-6.4 { sl@0: execsql {SELECT coalesce(nullif(1,2),'nil')} sl@0: } {1} sl@0: do_test func-6.5 { sl@0: execsql {SELECT coalesce(nullif(1,NULL),'nil')} sl@0: } {1} sl@0: sl@0: sl@0: # Test the last_insert_rowid() function sl@0: # sl@0: do_test func-7.1 { sl@0: execsql {SELECT last_insert_rowid()} sl@0: } [db last_insert_rowid] sl@0: sl@0: # Tests for aggregate functions and how they handle NULLs. sl@0: # sl@0: do_test func-8.1 { sl@0: ifcapable explain { sl@0: execsql {EXPLAIN SELECT sum(a) FROM t2;} sl@0: } sl@0: execsql { sl@0: SELECT sum(a), count(a), round(avg(a),2), min(a), max(a), count(*) FROM t2; sl@0: } sl@0: } {68236 3 22745.33 1 67890 5} sl@0: do_test func-8.2 { sl@0: execsql { sl@0: SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t2; sl@0: } sl@0: } {z+67890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP} sl@0: sl@0: ifcapable tempdb { sl@0: do_test func-8.3 { sl@0: execsql { sl@0: CREATE TEMP TABLE t3 AS SELECT a FROM t2 ORDER BY a DESC; sl@0: SELECT min('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3; sl@0: } sl@0: } {z+1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP} sl@0: } else { sl@0: do_test func-8.3 { sl@0: execsql { sl@0: CREATE TABLE t3 AS SELECT a FROM t2 ORDER BY a DESC; sl@0: SELECT min('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3; sl@0: } sl@0: } {z+1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP} sl@0: } sl@0: do_test func-8.4 { sl@0: execsql { sl@0: SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3; sl@0: } sl@0: } {z+67890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP} sl@0: ifcapable compound { sl@0: do_test func-8.5 { sl@0: execsql { sl@0: SELECT sum(x) FROM (SELECT '9223372036' || '854775807' AS x sl@0: UNION ALL SELECT -9223372036854775807) sl@0: } sl@0: } {0} sl@0: do_test func-8.6 { sl@0: execsql { sl@0: SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775807' AS x sl@0: UNION ALL SELECT -9223372036854775807) sl@0: } sl@0: } {integer} sl@0: do_test func-8.7 { sl@0: execsql { sl@0: SELECT typeof(sum(x)) FROM (SELECT '9223372036' || '854775808' AS x sl@0: UNION ALL SELECT -9223372036854775807) sl@0: } sl@0: } {real} sl@0: do_test func-8.8 { sl@0: execsql { sl@0: SELECT sum(x)>0.0 FROM (SELECT '9223372036' || '854775808' AS x sl@0: UNION ALL SELECT -9223372036850000000) sl@0: } sl@0: } {1} sl@0: } sl@0: sl@0: # How do you test the random() function in a meaningful, deterministic way? sl@0: # sl@0: do_test func-9.1 { sl@0: execsql { sl@0: SELECT random() is not null; sl@0: } sl@0: } {1} sl@0: do_test func-9.2 { sl@0: execsql { sl@0: SELECT typeof(random()); sl@0: } sl@0: } {integer} sl@0: do_test func-9.3 { sl@0: execsql { sl@0: SELECT randomblob(32) is not null; sl@0: } sl@0: } {1} sl@0: do_test func-9.4 { sl@0: execsql { sl@0: SELECT typeof(randomblob(32)); sl@0: } sl@0: } {blob} sl@0: do_test func-9.5 { sl@0: execsql { sl@0: SELECT length(randomblob(32)), length(randomblob(-5)), sl@0: length(randomblob(2000)) sl@0: } sl@0: } {32 1 2000} sl@0: sl@0: # The "hex()" function was added in order to be able to render blobs sl@0: # generated by randomblob(). So this seems like a good place to test sl@0: # hex(). sl@0: # sl@0: ifcapable bloblit { sl@0: do_test func-9.10 { sl@0: execsql {SELECT hex(x'00112233445566778899aAbBcCdDeEfF')} sl@0: } {00112233445566778899AABBCCDDEEFF} sl@0: } sl@0: set encoding [db one {PRAGMA encoding}] sl@0: if {$encoding=="UTF-16le"} { sl@0: do_test func-9.11-utf16le { sl@0: execsql {SELECT hex(replace('abcdefg','ef','12'))} sl@0: } {6100620063006400310032006700} sl@0: do_test func-9.12-utf16le { sl@0: execsql {SELECT hex(replace('abcdefg','','12'))} sl@0: } {{}} sl@0: do_test func-9.13-utf16le { sl@0: execsql {SELECT hex(replace('aabcdefg','a','aaa'))} sl@0: } {610061006100610061006100620063006400650066006700} sl@0: } elseif {$encoding=="UTF-8"} { sl@0: do_test func-9.11-utf8 { sl@0: execsql {SELECT hex(replace('abcdefg','ef','12'))} sl@0: } {61626364313267} sl@0: do_test func-9.12-utf8 { sl@0: execsql {SELECT hex(replace('abcdefg','','12'))} sl@0: } {{}} sl@0: do_test func-9.13-utf8 { sl@0: execsql {SELECT hex(replace('aabcdefg','a','aaa'))} sl@0: } {616161616161626364656667} sl@0: } sl@0: sl@0: # Use the "sqlite_register_test_function" TCL command which is part of sl@0: # the text fixture in order to verify correct operation of some of sl@0: # the user-defined SQL function APIs that are not used by the built-in sl@0: # functions. sl@0: # sl@0: set ::DB [sqlite3_connection_pointer db] sl@0: sqlite_register_test_function $::DB testfunc sl@0: do_test func-10.1 { sl@0: catchsql { sl@0: SELECT testfunc(NULL,NULL); sl@0: } sl@0: } {1 {first argument should be one of: int int64 string double null value}} sl@0: do_test func-10.2 { sl@0: execsql { sl@0: SELECT testfunc( sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'int', 1234 sl@0: ); sl@0: } sl@0: } {1234} sl@0: do_test func-10.3 { sl@0: execsql { sl@0: SELECT testfunc( sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'string', NULL sl@0: ); sl@0: } sl@0: } {{}} sl@0: do_test func-10.4 { sl@0: execsql { sl@0: SELECT testfunc( sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'double', 1.234 sl@0: ); sl@0: } sl@0: } {1.234} sl@0: do_test func-10.5 { sl@0: execsql { sl@0: SELECT testfunc( sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'int', 1234, sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'string', NULL, sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'double', 1.234, sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'int', 1234, sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'string', NULL, sl@0: 'string', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', sl@0: 'double', 1.234 sl@0: ); sl@0: } sl@0: } {1.234} sl@0: sl@0: # Test the built-in sqlite_version(*) SQL function. sl@0: # sl@0: do_test func-11.1 { sl@0: execsql { sl@0: SELECT sqlite_version(*); sl@0: } sl@0: } [sqlite3 -version] sl@0: sl@0: # Test that destructors passed to sqlite3 by calls to sqlite3_result_text() sl@0: # etc. are called. These tests use two special user-defined functions sl@0: # (implemented in func.c) only available in test builds. sl@0: # sl@0: # Function test_destructor() takes one argument and returns a copy of the sl@0: # text form of that argument. A destructor is associated with the return sl@0: # value. Function test_destructor_count() returns the number of outstanding sl@0: # destructor calls for values returned by test_destructor(). sl@0: # sl@0: if {[db eval {PRAGMA encoding}]=="UTF-8"} { sl@0: do_test func-12.1-utf8 { sl@0: execsql { sl@0: SELECT test_destructor('hello world'), test_destructor_count(); sl@0: } sl@0: } {{hello world} 1} sl@0: } else { sl@0: ifcapable {utf16} { sl@0: do_test func-12.1-utf16 { sl@0: execsql { sl@0: SELECT test_destructor16('hello world'), test_destructor_count(); sl@0: } sl@0: } {{hello world} 1} sl@0: } sl@0: } sl@0: do_test func-12.2 { sl@0: execsql { sl@0: SELECT test_destructor_count(); sl@0: } sl@0: } {0} sl@0: do_test func-12.3 { sl@0: execsql { sl@0: SELECT test_destructor('hello')||' world' sl@0: } sl@0: } {{hello world}} sl@0: do_test func-12.4 { sl@0: execsql { sl@0: SELECT test_destructor_count(); sl@0: } sl@0: } {0} sl@0: do_test func-12.5 { sl@0: execsql { sl@0: CREATE TABLE t4(x); sl@0: INSERT INTO t4 VALUES(test_destructor('hello')); sl@0: INSERT INTO t4 VALUES(test_destructor('world')); sl@0: SELECT min(test_destructor(x)), max(test_destructor(x)) FROM t4; sl@0: } sl@0: } {hello world} sl@0: do_test func-12.6 { sl@0: execsql { sl@0: SELECT test_destructor_count(); sl@0: } sl@0: } {0} sl@0: do_test func-12.7 { sl@0: execsql { sl@0: DROP TABLE t4; sl@0: } sl@0: } {} sl@0: sl@0: sl@0: # Test that the auxdata API for scalar functions works. This test uses sl@0: # a special user-defined function only available in test builds, sl@0: # test_auxdata(). Function test_auxdata() takes any number of arguments. sl@0: do_test func-13.1 { sl@0: execsql { sl@0: SELECT test_auxdata('hello world'); sl@0: } sl@0: } {0} sl@0: sl@0: do_test func-13.2 { sl@0: execsql { sl@0: CREATE TABLE t4(a, b); sl@0: INSERT INTO t4 VALUES('abc', 'def'); sl@0: INSERT INTO t4 VALUES('ghi', 'jkl'); sl@0: } sl@0: } {} sl@0: do_test func-13.3 { sl@0: execsql { sl@0: SELECT test_auxdata('hello world') FROM t4; sl@0: } sl@0: } {0 1} sl@0: do_test func-13.4 { sl@0: execsql { sl@0: SELECT test_auxdata('hello world', 123) FROM t4; sl@0: } sl@0: } {{0 0} {1 1}} sl@0: do_test func-13.5 { sl@0: execsql { sl@0: SELECT test_auxdata('hello world', a) FROM t4; sl@0: } sl@0: } {{0 0} {1 0}} sl@0: do_test func-13.6 { sl@0: execsql { sl@0: SELECT test_auxdata('hello'||'world', a) FROM t4; sl@0: } sl@0: } {{0 0} {1 0}} sl@0: sl@0: # Test that auxilary data is preserved between calls for SQL variables. sl@0: do_test func-13.7 { sl@0: set DB [sqlite3_connection_pointer db] sl@0: set sql "SELECT test_auxdata( ? , a ) FROM t4;" sl@0: set STMT [sqlite3_prepare $DB $sql -1 TAIL] sl@0: sqlite3_bind_text $STMT 1 hello\000 -1 sl@0: set res [list] sl@0: while { "SQLITE_ROW"==[sqlite3_step $STMT] } { sl@0: lappend res [sqlite3_column_text $STMT 0] sl@0: } sl@0: lappend res [sqlite3_finalize $STMT] sl@0: } {{0 0} {1 0} SQLITE_OK} sl@0: sl@0: # Make sure that a function with a very long name is rejected sl@0: do_test func-14.1 { sl@0: catch { sl@0: db function [string repeat X 254] {return "hello"} sl@0: } sl@0: } {0} sl@0: do_test func-14.2 { sl@0: catch { sl@0: db function [string repeat X 256] {return "hello"} sl@0: } sl@0: } {1} sl@0: sl@0: do_test func-15.1 { sl@0: catchsql {select test_error(NULL)} sl@0: } {1 {}} sl@0: do_test func-15.2 { sl@0: catchsql {select test_error('this is the error message')} sl@0: } {1 {this is the error message}} sl@0: do_test func-15.3 { sl@0: catchsql {select test_error('this is the error message',12)} sl@0: } {1 {this is the error message}} sl@0: do_test func-15.4 { sl@0: db errorcode sl@0: } {12} sl@0: sl@0: # Test the quote function for BLOB and NULL values. sl@0: do_test func-16.1 { sl@0: execsql { sl@0: CREATE TABLE tbl2(a, b); sl@0: } sl@0: set STMT [sqlite3_prepare $::DB "INSERT INTO tbl2 VALUES(?, ?)" -1 TAIL] sl@0: sqlite3_bind_blob $::STMT 1 abc 3 sl@0: sqlite3_step $::STMT sl@0: sqlite3_finalize $::STMT sl@0: execsql { sl@0: SELECT quote(a), quote(b) FROM tbl2; sl@0: } sl@0: } {X'616263' NULL} sl@0: sl@0: # Correctly handle function error messages that include %. Ticket #1354 sl@0: # sl@0: do_test func-17.1 { sl@0: proc testfunc1 args {error "Error %d with %s percents %p"} sl@0: db function testfunc1 ::testfunc1 sl@0: catchsql { sl@0: SELECT testfunc1(1,2,3); sl@0: } sl@0: } {1 {Error %d with %s percents %p}} sl@0: sl@0: # The SUM function should return integer results when all inputs are integer. sl@0: # sl@0: do_test func-18.1 { sl@0: execsql { sl@0: CREATE TABLE t5(x); sl@0: INSERT INTO t5 VALUES(1); sl@0: INSERT INTO t5 VALUES(-99); sl@0: INSERT INTO t5 VALUES(10000); sl@0: SELECT sum(x) FROM t5; sl@0: } sl@0: } {9902} sl@0: do_test func-18.2 { sl@0: execsql { sl@0: INSERT INTO t5 VALUES(0.0); sl@0: SELECT sum(x) FROM t5; sl@0: } sl@0: } {9902.0} sl@0: sl@0: # The sum of nothing is NULL. But the sum of all NULLs is NULL. sl@0: # sl@0: # The TOTAL of nothing is 0.0. sl@0: # sl@0: do_test func-18.3 { sl@0: execsql { sl@0: DELETE FROM t5; sl@0: SELECT sum(x), total(x) FROM t5; sl@0: } sl@0: } {{} 0.0} sl@0: do_test func-18.4 { sl@0: execsql { sl@0: INSERT INTO t5 VALUES(NULL); sl@0: SELECT sum(x), total(x) FROM t5 sl@0: } sl@0: } {{} 0.0} sl@0: do_test func-18.5 { sl@0: execsql { sl@0: INSERT INTO t5 VALUES(NULL); sl@0: SELECT sum(x), total(x) FROM t5 sl@0: } sl@0: } {{} 0.0} sl@0: do_test func-18.6 { sl@0: execsql { sl@0: INSERT INTO t5 VALUES(123); sl@0: SELECT sum(x), total(x) FROM t5 sl@0: } sl@0: } {123 123.0} sl@0: sl@0: # Ticket #1664, #1669, #1670, #1674: An integer overflow on SUM causes sl@0: # an error. The non-standard TOTAL() function continues to give a helpful sl@0: # result. sl@0: # sl@0: do_test func-18.10 { sl@0: execsql { sl@0: CREATE TABLE t6(x INTEGER); sl@0: INSERT INTO t6 VALUES(1); sl@0: INSERT INTO t6 VALUES(1<<62); sl@0: SELECT sum(x) - ((1<<62)+1) from t6; sl@0: } sl@0: } 0 sl@0: do_test func-18.11 { sl@0: execsql { sl@0: SELECT typeof(sum(x)) FROM t6 sl@0: } sl@0: } integer sl@0: do_test func-18.12 { sl@0: catchsql { sl@0: INSERT INTO t6 VALUES(1<<62); sl@0: SELECT sum(x) - ((1<<62)*2.0+1) from t6; sl@0: } sl@0: } {1 {integer overflow}} sl@0: do_test func-18.13 { sl@0: execsql { sl@0: SELECT total(x) - ((1<<62)*2.0+1) FROM t6 sl@0: } sl@0: } 0.0 sl@0: do_test func-18.14 { sl@0: execsql { sl@0: SELECT sum(-9223372036854775805); sl@0: } sl@0: } -9223372036854775805 sl@0: sl@0: ifcapable compound&&subquery { sl@0: sl@0: do_test func-18.15 { sl@0: catchsql { sl@0: SELECT sum(x) FROM sl@0: (SELECT 9223372036854775807 AS x UNION ALL sl@0: SELECT 10 AS x); sl@0: } sl@0: } {1 {integer overflow}} sl@0: do_test func-18.16 { sl@0: catchsql { sl@0: SELECT sum(x) FROM sl@0: (SELECT 9223372036854775807 AS x UNION ALL sl@0: SELECT -10 AS x); sl@0: } sl@0: } {0 9223372036854775797} sl@0: do_test func-18.17 { sl@0: catchsql { sl@0: SELECT sum(x) FROM sl@0: (SELECT -9223372036854775807 AS x UNION ALL sl@0: SELECT 10 AS x); sl@0: } sl@0: } {0 -9223372036854775797} sl@0: do_test func-18.18 { sl@0: catchsql { sl@0: SELECT sum(x) FROM sl@0: (SELECT -9223372036854775807 AS x UNION ALL sl@0: SELECT -10 AS x); sl@0: } sl@0: } {1 {integer overflow}} sl@0: do_test func-18.19 { sl@0: catchsql { sl@0: SELECT sum(x) FROM (SELECT 9 AS x UNION ALL SELECT -10 AS x); sl@0: } sl@0: } {0 -1} sl@0: do_test func-18.20 { sl@0: catchsql { sl@0: SELECT sum(x) FROM (SELECT -9 AS x UNION ALL SELECT 10 AS x); sl@0: } sl@0: } {0 1} sl@0: do_test func-18.21 { sl@0: catchsql { sl@0: SELECT sum(x) FROM (SELECT -10 AS x UNION ALL SELECT 9 AS x); sl@0: } sl@0: } {0 -1} sl@0: do_test func-18.22 { sl@0: catchsql { sl@0: SELECT sum(x) FROM (SELECT 10 AS x UNION ALL SELECT -9 AS x); sl@0: } sl@0: } {0 1} sl@0: sl@0: } ;# ifcapable compound&&subquery sl@0: sl@0: # Integer overflow on abs() sl@0: # sl@0: do_test func-18.31 { sl@0: catchsql { sl@0: SELECT abs(-9223372036854775807); sl@0: } sl@0: } {0 9223372036854775807} sl@0: do_test func-18.32 { sl@0: catchsql { sl@0: SELECT abs(-9223372036854775807-1); sl@0: } sl@0: } {1 {integer overflow}} sl@0: sl@0: # The MATCH function exists but is only a stub and always throws an error. sl@0: # sl@0: do_test func-19.1 { sl@0: execsql { sl@0: SELECT match(a,b) FROM t1 WHERE 0; sl@0: } sl@0: } {} sl@0: do_test func-19.2 { sl@0: catchsql { sl@0: SELECT 'abc' MATCH 'xyz'; sl@0: } sl@0: } {1 {unable to use function MATCH in the requested context}} sl@0: do_test func-19.3 { sl@0: catchsql { sl@0: SELECT 'abc' NOT MATCH 'xyz'; sl@0: } sl@0: } {1 {unable to use function MATCH in the requested context}} sl@0: do_test func-19.4 { sl@0: catchsql { sl@0: SELECT match(1,2,3); sl@0: } sl@0: } {1 {wrong number of arguments to function match()}} sl@0: sl@0: # Soundex tests. sl@0: # sl@0: if {![catch {db eval {SELECT soundex('hello')}}]} { sl@0: set i 0 sl@0: foreach {name sdx} { sl@0: euler E460 sl@0: EULER E460 sl@0: Euler E460 sl@0: ellery E460 sl@0: gauss G200 sl@0: ghosh G200 sl@0: hilbert H416 sl@0: Heilbronn H416 sl@0: knuth K530 sl@0: kant K530 sl@0: Lloyd L300 sl@0: LADD L300 sl@0: Lukasiewicz L222 sl@0: Lissajous L222 sl@0: A A000 sl@0: 12345 ?000 sl@0: } { sl@0: incr i sl@0: do_test func-20.$i { sl@0: execsql {SELECT soundex($name)} sl@0: } $sdx sl@0: } sl@0: } sl@0: sl@0: # Tests of the REPLACE function. sl@0: # sl@0: do_test func-21.1 { sl@0: catchsql { sl@0: SELECT replace(1,2); sl@0: } sl@0: } {1 {wrong number of arguments to function replace()}} sl@0: do_test func-21.2 { sl@0: catchsql { sl@0: SELECT replace(1,2,3,4); sl@0: } sl@0: } {1 {wrong number of arguments to function replace()}} sl@0: do_test func-21.3 { sl@0: execsql { sl@0: SELECT typeof(replace("This is the main test string", NULL, "ALT")); sl@0: } sl@0: } {null} sl@0: do_test func-21.4 { sl@0: execsql { sl@0: SELECT typeof(replace(NULL, "main", "ALT")); sl@0: } sl@0: } {null} sl@0: do_test func-21.5 { sl@0: execsql { sl@0: SELECT typeof(replace("This is the main test string", "main", NULL)); sl@0: } sl@0: } {null} sl@0: do_test func-21.6 { sl@0: execsql { sl@0: SELECT replace("This is the main test string", "main", "ALT"); sl@0: } sl@0: } {{This is the ALT test string}} sl@0: do_test func-21.7 { sl@0: execsql { sl@0: SELECT replace("This is the main test string", "main", "larger-main"); sl@0: } sl@0: } {{This is the larger-main test string}} sl@0: do_test func-21.8 { sl@0: execsql { sl@0: SELECT replace("aaaaaaa", "a", "0123456789"); sl@0: } sl@0: } {0123456789012345678901234567890123456789012345678901234567890123456789} sl@0: sl@0: ifcapable tclvar { sl@0: do_test func-21.9 { sl@0: # Attempt to exploit a buffer-overflow that at one time existed sl@0: # in the REPLACE function. sl@0: set ::str "[string repeat A 29998]CC[string repeat A 35537]" sl@0: set ::rep [string repeat B 65536] sl@0: execsql { sl@0: SELECT LENGTH(REPLACE($::str, 'C', $::rep)); sl@0: } sl@0: } [expr 29998 + 2*65536 + 35537] sl@0: } sl@0: sl@0: # Tests for the TRIM, LTRIM and RTRIM functions. sl@0: # sl@0: do_test func-22.1 { sl@0: catchsql {SELECT trim(1,2,3)} sl@0: } {1 {wrong number of arguments to function trim()}} sl@0: do_test func-22.2 { sl@0: catchsql {SELECT ltrim(1,2,3)} sl@0: } {1 {wrong number of arguments to function ltrim()}} sl@0: do_test func-22.3 { sl@0: catchsql {SELECT rtrim(1,2,3)} sl@0: } {1 {wrong number of arguments to function rtrim()}} sl@0: do_test func-22.4 { sl@0: execsql {SELECT trim(' hi ');} sl@0: } {hi} sl@0: do_test func-22.5 { sl@0: execsql {SELECT ltrim(' hi ');} sl@0: } {{hi }} sl@0: do_test func-22.6 { sl@0: execsql {SELECT rtrim(' hi ');} sl@0: } {{ hi}} sl@0: do_test func-22.7 { sl@0: execsql {SELECT trim(' hi ','xyz');} sl@0: } {{ hi }} sl@0: do_test func-22.8 { sl@0: execsql {SELECT ltrim(' hi ','xyz');} sl@0: } {{ hi }} sl@0: do_test func-22.9 { sl@0: execsql {SELECT rtrim(' hi ','xyz');} sl@0: } {{ hi }} sl@0: do_test func-22.10 { sl@0: execsql {SELECT trim('xyxzy hi zzzy','xyz');} sl@0: } {{ hi }} sl@0: do_test func-22.11 { sl@0: execsql {SELECT ltrim('xyxzy hi zzzy','xyz');} sl@0: } {{ hi zzzy}} sl@0: do_test func-22.12 { sl@0: execsql {SELECT rtrim('xyxzy hi zzzy','xyz');} sl@0: } {{xyxzy hi }} sl@0: do_test func-22.13 { sl@0: execsql {SELECT trim(' hi ','');} sl@0: } {{ hi }} sl@0: if {[db one {PRAGMA encoding}]=="UTF-8"} { sl@0: do_test func-22.14 { sl@0: execsql {SELECT hex(trim(x'c280e1bfbff48fbfbf6869',x'6162e1bfbfc280'))} sl@0: } {F48FBFBF6869} sl@0: do_test func-22.15 { sl@0: execsql {SELECT hex(trim(x'6869c280e1bfbff48fbfbf61', sl@0: x'6162e1bfbfc280f48fbfbf'))} sl@0: } {6869} sl@0: do_test func-22.16 { sl@0: execsql {SELECT hex(trim(x'ceb1ceb2ceb3',x'ceb1'));} sl@0: } {CEB2CEB3} sl@0: } sl@0: do_test func-22.20 { sl@0: execsql {SELECT typeof(trim(NULL));} sl@0: } {null} sl@0: do_test func-22.21 { sl@0: execsql {SELECT typeof(trim(NULL,'xyz'));} sl@0: } {null} sl@0: do_test func-22.22 { sl@0: execsql {SELECT typeof(trim('hello',NULL));} sl@0: } {null} sl@0: sl@0: # This is to test the deprecated sqlite3_aggregate_count() API. sl@0: # sl@0: do_test func-23.1 { sl@0: sqlite3_create_aggregate db sl@0: execsql { sl@0: SELECT legacy_count() FROM t6; sl@0: } sl@0: } {3} sl@0: sl@0: # The group_concat() function. sl@0: # sl@0: do_test func-24.1 { sl@0: execsql { sl@0: SELECT group_concat(t1) FROM tbl1 sl@0: } sl@0: } {this,program,is,free,software} sl@0: do_test func-24.2 { sl@0: execsql { sl@0: SELECT group_concat(t1,' ') FROM tbl1 sl@0: } sl@0: } {{this program is free software}} sl@0: do_test func-24.3 { sl@0: execsql { sl@0: SELECT group_concat(t1,' ' || rowid || ' ') FROM tbl1 sl@0: } sl@0: } {{this 2 program 3 is 4 free 5 software}} sl@0: do_test func-24.4 { sl@0: execsql { sl@0: SELECT group_concat(NULL,t1) FROM tbl1 sl@0: } sl@0: } {{}} sl@0: do_test func-24.5 { sl@0: execsql { sl@0: SELECT group_concat(t1,NULL) FROM tbl1 sl@0: } sl@0: } {thisprogramisfreesoftware} sl@0: do_test func-24.6 { sl@0: execsql { sl@0: SELECT 'BEGIN-'||group_concat(t1) FROM tbl1 sl@0: } sl@0: } {BEGIN-this,program,is,free,software} sl@0: unset -nocomplain midargs sl@0: set midargs {} sl@0: unset -nocomplain midres sl@0: set midres {} sl@0: unset -nocomplain result sl@0: for {set i 1} {$i<[sqlite3_limit db SQLITE_LIMIT_FUNCTION_ARG -1]-1} {incr i} { sl@0: append midargs ,'/$i' sl@0: append midres /$i sl@0: set result \ sl@0: "this$midres:program$midres:is$midres:free$midres:software$midres" sl@0: set sql "SELECT group_concat(t1$midargs,':') FROM tbl1" sl@0: do_test func-24.7.$i { sl@0: db eval $::sql sl@0: } $result sl@0: } sl@0: sl@0: # Use the test_isolation function to make sure that type conversions sl@0: # on function arguments do not effect subsequent arguments. sl@0: # sl@0: do_test func-25.1 { sl@0: execsql {SELECT test_isolation(t1,t1) FROM tbl1} sl@0: } {this program is free software} sl@0: sl@0: # Try to misuse the sqlite3_create_function() interface. Verify that sl@0: # errors are returned. sl@0: # sl@0: do_test func-26.1 { sl@0: abuse_create_function db sl@0: } {} sl@0: sl@0: # The previous test (func-26.1) registered a function with a very long sl@0: # function name that takes many arguments and always returns NULL. Verify sl@0: # that this function works correctly. sl@0: # sl@0: do_test func-26.2 { sl@0: set a {} sl@0: for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG} {incr i} { sl@0: lappend a $i sl@0: } sl@0: db eval " sl@0: SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]); sl@0: " sl@0: } {{}} sl@0: do_test func-26.3 { sl@0: set a {} sl@0: for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG+1} {incr i} { sl@0: lappend a $i sl@0: } sl@0: catchsql " sl@0: SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]); sl@0: " sl@0: } {1 {too many arguments on function nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789}} sl@0: do_test func-26.4 { sl@0: set a {} sl@0: for {set i 1} {$i<=$::SQLITE_MAX_FUNCTION_ARG-1} {incr i} { sl@0: lappend a $i sl@0: } sl@0: catchsql " sl@0: SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789([join $a ,]); sl@0: " sl@0: } {1 {wrong number of arguments to function nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789()}} sl@0: do_test func-26.5 { sl@0: catchsql " sl@0: SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678a(0); sl@0: " sl@0: } {1 {no such function: nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678a}} sl@0: do_test func-26.6 { sl@0: catchsql " sl@0: SELECT nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789a(0); sl@0: " sl@0: } {1 {no such function: nullx_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789a}} sl@0: sl@0: finish_test