#
# 2007 May 7
#
# 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 script is the sqlite4_create_collation() and
# collation_needed() APIs. More specifically, the focus is on testing that
# destructor callbacks are invoked correctly.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix collate7
# Implementation of collation sequence callbacks.
#
proc caseless_cmp {zLeft zRight} {
string compare -nocase $zLeft $zRight
}
proc caseless_mkkey {zIn} {
string tolower $zIn
}
#-------------------------------------------------------------------------
# Test the destructor is invoked when a collation sequence is overridden.
#
do_test 1.1 {
set ::caseless_del 0
set del [list incr ::caseless_del]
sqlite4_create_collation db CASELESS caseless_cmp caseless_mkkey $del
set ::caseless_del
} {0}
do_test 1.2 {
set ::caseless_del 0
sqlite4_create_collation db CASELESS {} {} {}
set ::caseless_del
} {1}
do_catchsql_test 1.3 {
CREATE TABLE abc(a COLLATE CASELESS, b, c);
} {1 {no such collation sequence: CASELESS}}
do_test 1.4 {
set ::caseless_del 0
sqlite4_create_collation db CASELESS {} {} {}
set ::caseless_del
} {0}
#-------------------------------------------------------------------------
# Test the destructor is invoked when the database handle is closed.
#
do_test 2.1 {
set ::caseless_del 0
set del [list incr ::caseless_del]
sqlite4_create_collation db CASELESS caseless_cmp caseless_mkkey $del
db close
set ::caseless_del
} {1}
#-------------------------------------------------------------------------
# Test the destructor is invoked if an error occurs within
# sqlite4_create_collation(). The easiest error to provoke here is
# SQLITE4_BUSY - which is returned if an attempt is made to override a
# collation while there are active statements.
#
reset_db
do_test 3.1 {
set ::caseless_del 0
set del [list incr ::caseless_del]
sqlite4_create_collation db CASELESS caseless_cmp caseless_mkkey $del
execsql {
CREATE TABLE t1(a COLLATE caseless);
INSERT INTO t1 VALUES('abc');
INSERT INTO t1 VALUES('def');
}
set ::stmt [sqlite4_prepare db "SELECT a FROM t1" -1 DUMMY]
sqlite4_step $::stmt
} {SQLITE4_ROW}
do_test 3.2 {
set ::caseless_del2 0
set del [list incr ::caseless_del2]
set rc [catch {
sqlite4_create_collation db CASELESS caseless_cmp caseless_mkkey $del
} msg]
list $::caseless_del $caseless_del2 $rc $msg
} {0 1 1 SQLITE4_BUSY}
do_test 3.3 { sqlite4_errcode db } {SQLITE4_BUSY}
do_test 3.4 {
sqlite4_errmsg db
} {unable to delete/modify collation sequence due to active statements}
do_test 3.5 { sqlite4_finalize $::stmt } {SQLITE4_OK}
do_test 3.6 { sqlite4_errcode db } {SQLITE4_OK}
do_test 3.7 { sqlite4_errmsg db } {not an error}
#-------------------------------------------------------------------------
# The following tests verify that the collation needed destructor is
# invoked:
#
# 1. When it is overridden, and
# 2. When the database connection is closed.
#
# It would be good to test that if an error occurs within the
# collation_needed() the destructor is invoked immediately (as the
# documentation says). However there is currently no way to cause
# sqlite4_collation_needed() to fail. sqlite4_collation_needed()
# currently *always* returns SQLITE4_OK.
#
do_test 4.1.1 {
set ::coll_needed_del 0
sqlite4_collation_needed db coll_needed {incr ::coll_needed_del}
set ::coll_needed_del
} {0}
do_test 4.1.2 {
set ::coll_needed_del2 0
sqlite4_collation_needed db coll_needed {incr ::coll_needed_del2}
list $::coll_needed_del $::coll_needed_del2
} {1 0}
do_test 4.2 {
db close
list $::coll_needed_del $::coll_needed_del2
} {1 1}
finish_test