diff --git a/Changes b/Changes index 0977178..e8a9603 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,15 @@ Revision history for MyTAP ========================== +0.07 2018-06-18 +------------------------- +* col_has_unique_index, col_hasnt_unique_index added to test for + the existence or absence of a unique index. +* tests added for the new functions. +* syntax errors fixed in the tests for the has[nt]_function and + has[nt]_procedure functions. +* runtests.sh script now has a filter option. + 0.06 2017-06-18 ------------------------- * has_table and hasnt_table moved to separate file diff --git a/docs/documentation.md b/docs/documentation.md index 5c05347..f3dcbcb 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -371,6 +371,10 @@ This function tests if the column is part of a key, not a primary key. `col_hasnt_index_key( database, table, column, description )` checks if this column is NOT part of a key. +`col_has_unique_index`( database, table, column, description )` checks if this column has a unique key other than the primary key. + +`col_hasnt_unique_index`( database, table, column, description )` checks if this column doesn't have a unique key other than the primary key. + ### `col_has_named_index( database, table, column, keyname, description )` This function tests if the column is part of a key with a specific name. diff --git a/mytap-column.sql b/mytap-column.sql index b6b3350..61fff39 100644 --- a/mytap-column.sql +++ b/mytap-column.sql @@ -216,6 +216,118 @@ BEGIN END // +/****************************************************************************/ +-- _col_has_unique_index (schema, table, column ) + +DROP FUNCTION IF EXISTS _col_has_unique_index // +CREATE FUNCTION _col_has_unique_index ( dbname TEXT, tname TEXT, cname TEXT ) +RETURNS BOOLEAN +BEGIN + DECLARE ret BOOLEAN; + + SELECT true into ret + FROM information_schema.statistics as db + WHERE db.table_schema = dbname + AND db.table_name = tname + AND db.column_name = cname + AND db.index_name <> 'PRIMARY' + AND db.non_unique = 0 + limit 1; /* only use the first entry */ + RETURN coalesce(ret, false); +END // + +-- col_has_unique_index ( schema, table, column, keyname ) +DROP FUNCTION IF EXISTS col_has_unique_index // +CREATE FUNCTION col_has_unique_index ( dbname TEXT, tname TEXT, cname TEXT, description TEXT ) +RETURNS TEXT +BEGIN + IF NOT _has_column( dbname, tname, cname ) THEN + RETURN fail(concat('Error ', + diag (concat(' Column ', quote_ident(dbname), '.', quote_ident(tname), '.', quote_ident(cname), ' does not exist' )))); + END IF; + + IF description = '' THEN + SET description = concat('Column ', + quote_ident(tname), '.', quote_ident(cname), ' should have unique INDEX' ); + END IF; + + RETURN ok( _col_has_unique_index(dbname, tname, cname), description ); +END // + +-- col_hasnt_unique_index( schema, table, column, keyname ) +DROP FUNCTION IF EXISTS col_hasnt_unique_index // +CREATE FUNCTION col_hasnt_unique_index ( dbname TEXT, tname TEXT, cname TEXT, description TEXT ) +RETURNS TEXT +BEGIN + IF NOT _has_column( dbname, tname, cname ) THEN + RETURN fail(concat('Error ', + diag (concat(' Column ', quote_ident(dbname), '.', quote_ident(tname), '.', quote_ident(cname), ' does not exist' )))); + END IF; + + IF description = '' THEN + SET description = concat('Column ', + quote_ident(tname), '.', quote_ident(cname), ' should not have unique INDEX'); + END IF; + + RETURN ok( NOT _col_has_unique_index(dbname, tname, cname ), description ); +END // + +/****************************************************************************/ +-- _col_has_non_unique_index (schema, table, column ) + +DROP FUNCTION IF EXISTS _col_has_non_unique_index // +CREATE FUNCTION _col_has_non_unique_index ( dbname TEXT, tname TEXT, cname TEXT ) +RETURNS BOOLEAN +BEGIN + DECLARE ret BOOLEAN; + + SELECT true into ret + FROM information_schema.statistics as db + WHERE db.table_schema = dbname + AND db.table_name = tname + AND db.column_name = cname + AND db.index_name <> 'PRIMARY' + AND db.non_unique = 1 + limit 1; /* only use the first entry */ + RETURN coalesce(ret, false); +END // + +-- col_has_non_unique_index ( schema, table, column, keyname ) +DROP FUNCTION IF EXISTS col_has_non_unique_index // +CREATE FUNCTION col_has_non_unique_index ( dbname TEXT, tname TEXT, cname TEXT, description TEXT ) +RETURNS TEXT +BEGIN + IF NOT _has_column( dbname, tname, cname ) THEN + RETURN fail(concat('Error ', + diag (concat(' Column ', quote_ident(dbname), '.', quote_ident(tname), '.', quote_ident(cname), ' does not exist' )))); + END IF; + + IF description = '' THEN + SET description = concat('Column ', + quote_ident(tname), '.', quote_ident(cname), ' should have non unique INDEX' ); + END IF; + + RETURN ok( _col_has_non_unique_index(dbname, tname, cname), description ); +END // + +-- col_hasnt_non_unique_index( schema, table, column, keyname ) +DROP FUNCTION IF EXISTS col_hasnt_non_unique_index // +CREATE FUNCTION col_hasnt_non_unique_index ( dbname TEXT, tname TEXT, cname TEXT, description TEXT ) +RETURNS TEXT +BEGIN + IF NOT _has_column( dbname, tname, cname ) THEN + RETURN fail(concat('Error ', + diag (concat(' Column ', quote_ident(dbname), '.', quote_ident(tname), '.', quote_ident(cname), ' does not exist' )))); + END IF; + + IF description = '' THEN + SET description = concat('Column ', + quote_ident(tname), '.', quote_ident(cname), ' should not have non unique INDEX'); + END IF; + + RETURN ok( NOT _col_has_non_unique_index(dbname, tname, cname), description ); +END // + /****************************************************************************/ -- _col_has_named_index (schema, table, column ) @@ -525,4 +637,4 @@ END // /****************************************************************************/ -DELIMITER ; \ No newline at end of file +DELIMITER ; diff --git a/mytap.sql b/mytap.sql index cc5bdc9..bd37665 100644 --- a/mytap.sql +++ b/mytap.sql @@ -30,7 +30,7 @@ DROP FUNCTION IF EXISTS mytap_version // CREATE FUNCTION mytap_version() RETURNS VARCHAR(10) BEGIN - RETURN '0.06'; + RETURN '0.07'; END // DROP FUNCTION IF EXISTS mysql_version // diff --git a/runsandboxtests.sh b/runsandboxtests.sh new file mode 100755 index 0000000..9385a3a --- /dev/null +++ b/runsandboxtests.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Run tests against the various sandboxed MySQL servers + + +USER=msandbox +PW=msandbox +HOST=127.0.0.1 + +PORT55=3306 +PORT56=5636 +PORT57=5718 +PORT80=8001 + +MYSQLOPTS="-h $HOST -u $USER -p$PW" + +# ==== MySQL 5.5 + +echo "============= updating tap in 5.5 =============" +mysql $MYSQLOPTS --port=$PORT55 --execute 'source ./mytap.sql' + +myprove/bin/my_prove tests/* -h $HOST -P $PORT55 -u $USER -p $PW + +# ==== MySQL 5.6 + +echo "============= updating tap in 5.6 =============" +mysql $MYSQLOPTS --port=$PORT56 --execute 'source ./mytap.sql' + +myprove/bin/my_prove tests/* -h $HOST -P $PORT56 -u $USER -p $PW + +# ==== MySQL 5.7 + +echo "============= updating tap in 5.7 =============" +mysql $MYSQLOPTS --port=$PORT57 --execute 'source ./mytap.sql' + +myprove/bin/my_prove tests/* -h $HOST -P $PORT57 -u $USER -p $PW + +# ==== MySQL 8.0 + +# echo "============= updating tap in 8.0 =============" +# mysql $MYSQLOPTS --port=$PORT80 --execute 'source ./mytap.sql' + +# myprove/bin/my_prove tests/* -h $HOST -P $PORT80 -u $USER -p $PW + diff --git a/runtests.sh b/runtests.sh index 2f7dd99..31b598a 100755 --- a/runtests.sh +++ b/runtests.sh @@ -2,31 +2,88 @@ # shell script to run all tap tests -USER=$1 -PASSW=$2 +if (( $# < 2 )); then + echo "" + echo "$0 username password [--filter hastap|matching|eq|moretap|todotap|utils|viewtap|coltap|routinestap|triggertap]" + echo "" + exit 0 +fi -MYSLOPTS="-u $USER -p$PASSW --disable-pager --batch --raw --skip-column-names --unbuffered" +USER="$1"; shift +PASSW="$1"; shift + +# find out if we want to filter to a specific set +FILTER="$@" + +if [[ ${FILTER:0:8} = "--filter" ]]; then + # strip the --filter prefix + FILTER=${FILTER:8} + + # reset to everything when the filter is empty + if [[ "$FILTER" == "" ]]; then + FILTER=0 + fi +else + # no filtering + FILTER=0 +fi + +MYSLOPTS="-h 127.0.0.1 -u $USER -p$PASSW --disable-pager --batch --raw --skip-column-names --unbuffered" echo "============= updating tap =============" mysql $MYSLOPTS --execute 'source ./mytap.sql' -echo "============= hastap =============" -mysql $MYSLOPTS --database tap --execute 'source tests/hastap.my' -echo "============= matching =============" -mysql $MYSLOPTS --database tap --execute 'source tests/matching.my' +if [[ $FILTER != 0 ]]; then + echo "============= filtering =============" + echo "$FILTER" +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "hastap" ]]; then + echo "============= hastap =============" + mysql $MYSLOPTS --database tap --execute 'source tests/hastap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "matching" ]]; then + echo "============= matching =============" + mysql $MYSLOPTS --database tap --execute 'source tests/matching.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "eq" ]]; then echo "============= eq =============" mysql $MYSLOPTS --database tap --execute 'source tests/eq.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "moretap" ]]; then echo "============= moretap =============" mysql $MYSLOPTS --database tap --execute 'source tests/moretap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "todotap" ]]; then echo "============= todotap =============" mysql $MYSLOPTS --database tap --execute 'source tests/todotap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "utils" ]]; then echo "============= utils =============" mysql $MYSLOPTS --database tap --execute 'source tests/utils.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "viewtap" ]]; then echo "============= viewtap =============" mysql $MYSLOPTS --database tap --execute 'source tests/viewtap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "coltap" ]]; then echo "============= coltap =============" mysql $MYSLOPTS --database tap --execute 'source tests/coltap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "routinestap" ]]; then echo "============= routinestap ========" mysql $MYSLOPTS --database tap --execute 'source tests/routinestap.my' +fi + +if [[ $FILTER == 0 ]] || [[ $FILTER =~ "triggertap" ]]; then echo "============= triggertap ========" mysql $MYSLOPTS --database tap --execute 'source tests/triggertap.my' +fi diff --git a/tests/coltap.my b/tests/coltap.my index 560df3f..823705e 100644 --- a/tests/coltap.my +++ b/tests/coltap.my @@ -2,7 +2,7 @@ BEGIN; -SELECT tap.plan(321); +SELECT tap.plan(357); -- SELECT * from no_plan(); DROP DATABASE IF EXISTS taptest; @@ -17,11 +17,12 @@ CREATE TABLE taptest.sometab( numb FLOAT(10, 2) DEFAULT NULL, myNum INT(8) DEFAULT 24, myat TIMESTAMP DEFAULT NOW() ON UPDATE CURRENT_TIMESTAMP, - mydate DATE DEFAULT '0000-00-00', + mydate DATE DEFAULT '1900-01-01', plain INT, enumCol enum('VAL1', 'VAL2', 'VAL3') NOT NULL, KEY `WeirdIndexName` (`myNum`), - KEY `multiIndex` (`myNum`,`mydate`) + KEY `multiIndex` (`myNum`,`mydate`), + UNIQUE KEY `unique` (`plain`) ); CREATE OR REPLACE VIEW taptest.myView as select myNum as viewNum, plain from taptest.sometab; @@ -406,7 +407,7 @@ SELECT tap.check_test( tap.col_has_index_key( 'taptest', 'sometab', 'myNum', '' ), true, 'col_has_index_key( sch, tab, col has index key)', - 'Column sometab.myNum should have index key', + 'Column sometab.myNum should have INDEX key', '', 0 ); @@ -425,7 +426,7 @@ SELECT tap.check_test( tap.col_has_index_key( database(), 'sometab', 'myNum', '' ), true, 'col_has_index_key( current database, tab, col has index key)', - 'Column sometab.myNum should have index key', + 'Column sometab.myNum should have INDEX key', '', 0 ); @@ -437,7 +438,7 @@ SELECT tap.check_test( tap.col_has_index_key( 'taptest', 'sometab', 'name', '' ), false, 'col_has_index_key( sch, tab, col without any key)', - 'Column sometab.name should have index key', + 'Column sometab.name should have INDEX key', '', 0 ); @@ -530,13 +531,142 @@ SELECT tap.check_test( /****************************************************************************/ +-- Test col_has_unique_index + +SELECT tap.check_test( + tap.col_has_unique_index( 'taptest', 'sometab', 'myNum', '' ), + false, + 'col_has_unique_index( sch, tab, col has not unique index)', + 'Column sometab.myNum should have unique INDEX', + '', + 0 +); + +SELECT tap.check_test( + tap.col_has_unique_index( 'taptest', 'sometab', 'myNum', 'my own description' ), + false, + 'col_has_unique_index( sch, tab, col has not unique index, desc)', + 'my own description', + '', + 0 +); + +use taptest; +SELECT tap.check_test( + tap.col_has_unique_index( database(), 'sometab', 'plain', '' ), + true, + 'col_has_unique_index( current database, tab, col has unique index key)', + 'Column sometab.plain should have unique index', + '', + 0 +); +use tap; + + +-- Make sure failure is correct. +SELECT tap.check_test( + tap.col_has_unique_index( 'taptest', 'sometab', 'name', '' ), + false, + 'col_has_unique_index( sch, tab, col without any key)', + 'Column sometab.name should have unique INDEX', + '', + 0 +); + + +-- Make sure nonexisting column is correctly detected +SELECT tap.check_test( + tap.col_has_unique_index( 'taptest', 'sometab', 'foo', '' ), + false, + 'col_has_unique_index( sch, tab, non existing col )', + 'Error # Column taptest.sometab.foo does not exist', + '', + 0 +); + +-- Make sure primary key is correctly detected as non-index +SELECT tap.check_test( + tap.col_has_unique_index( 'taptest', 'sometab', 'id', '' ), + false, + 'col_has_unique_index( sch, tab, col has primary key)', + 'Column sometab.id should have unique INDEX', + '', + 0 +); + +/****************************************************************************/ + +-- Test col_hasnt_unique_index + +SELECT tap.check_test( + tap.col_hasnt_unique_index( 'taptest', 'sometab', 'plain', '' ), + false, + 'col_hasnt_unique_index( sch, tab, col has unique index)', + 'Column sometab.plain should not have unique INDEX', + '', + 0 +); + +SELECT tap.check_test( + tap.col_hasnt_unique_index( 'taptest', 'sometab', 'plain', 'my own description' ), + false, + 'col_hasnt_unique_index( sch, tab, col has unique index, desc)', + 'my own description', + '', + 0 +); + +use taptest; +SELECT tap.check_test( + tap.col_hasnt_unique_index( database(), 'sometab', 'myNum', '' ), + true, + 'col_hasnt_unique_index( current database, tab, col has non unique key)', + 'Column sometab.myNum should not have unique INDEX', + '', + 0 +); +use tap; + + +-- Make sure failure is correct. +SELECT tap.check_test( + tap.col_hasnt_unique_index( 'taptest', 'sometab', 'name', '' ), + true, + 'col_hasnt_unique_index( sch, tab, col without any key)', + 'Column sometab.name should not have unique INDEX', + '', + 0 +); + +-- Make sure nonexisting column is correctly detected +SELECT tap.check_test( + tap.col_hasnt_unique_index( 'taptest', 'sometab', 'foo', '' ), + false, + 'col_hasnt_unique_index( sch, tab, non existing col )', + 'Error # Column taptest.sometab.foo does not exist', + '', + 0 +); + +-- Make sure primary key is exempt from 'unique keys' +SELECT tap.check_test( + tap.col_hasnt_unique_index( 'taptest', 'sometab', 'id', '' ), + true, + 'col_hasnt_unique_index( sch, tab, col has primary key)', + 'Column sometab.id should not have unique INDEX', + '', + 0 +); + +/****************************************************************************/ + -- Test col_has_named_index SELECT tap.check_test( tap.col_has_named_index( 'taptest', 'sometab', 'myNum', '', '' ), false, 'col_has_named_index( sch, tab, col has different index key name)', - 'Column sometab.myNum should have index key', + 'Column sometab.myNum should have INDEX key', '', 0 ); @@ -567,7 +697,7 @@ SELECT tap.check_test( tap.col_has_named_index( 'taptest', 'sometab', 'name', '', '' ), false, 'col_has_named_index( sch, tab, col without any key)', - 'Column sometab.name should have index key', + 'Column sometab.name should have INDEX key', '', 0 ); @@ -596,7 +726,7 @@ SELECT tap.check_test( tap.col_has_named_index( 'taptest', 'sometab', 'id', '', '' ), false, 'col_has_named_index( sch, tab, col has primary key)', - 'Column sometab.id should have index key', + 'Column sometab.id should have INDEX key', '', 0 ); @@ -1048,10 +1178,10 @@ SELECT tap.check_test( ); SELECT tap.check_test( - col_default_is( 'taptest', 'sometab', 'mydate', '0000-00-00', 'mydate should default to 0000-00-00' ), + col_default_is( 'taptest', 'sometab', 'mydate', '1900-01-01', 'mydate should default to 1900-01-01' ), true, 'col_default_is( sch, tab, col, zero date def, desc )', - 'mydate should default to 0000-00-00', + 'mydate should default to 1900-01-01', '', 0 ); diff --git a/tests/routinestap.my b/tests/routinestap.my index ceb0f5b..8c3dcdb 100644 --- a/tests/routinestap.my +++ b/tests/routinestap.my @@ -17,23 +17,23 @@ CREATE TABLE taptest.sometab( plain INT ); -DELIMITER // +DELIMITER ;; -DROP FUNCTION IF EXISTS taptest.myFunction // -CREATE FUNCTION taptest.myFunction (param varchar(10) ) -RETURNS int(10) +DROP FUNCTION IF EXISTS taptest.myFunction ;; +CREATE FUNCTION taptest.myFunction (funcparam varchar(10) ) +RETURNS int(10) BEGIN DECLARE ret int(10); - + SELECT 12 into ret; RETURN ret; -END // +END ;; -DROP PROCEDURE IF EXISTS taptest.myProc // -CREATE PROCEDURE taptest.myProc ( param TEXT ) +DROP PROCEDURE IF EXISTS taptest.myProc ;; +CREATE PROCEDURE taptest.myProc ( procparam TEXT ) BEGIN SELECT * from taptest.sometab; -END // +END ;; DELIMITER ; @@ -41,126 +41,161 @@ DELIMITER ; /****************************************************************************/ -- Test has_function -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_function throws an error on version 5.7') +else tap.check_test( tap.has_function('taptest', 'myFunction', '' ), true, 'has_function( sch, func )', 'Function taptest.myFunction should exist', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_function throws an error on version 5.7') +else tap.check_test( tap.has_function('taptest', 'myFunction', 'desc' ), true, 'has_function( sch, func, desc )', 'desc', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_function throws an error on version 5.7') +else tap.check_test( tap.has_function('taptest', 'myProc', '' ), false, 'has_function( sch, non func, desc )', 'Function taptest.myProc should exist', '', 0 -); +) +end; /****************************************************************************/ -- Test hasnt_function -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_function throws an error on version 5.7') +else tap.check_test( tap.hasnt_function('taptest', 'myFunction', '' ), false, 'hasnt_function( sch, func )', 'Function taptest.myFunction should not exist', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_function throws an error on version 5.7') +else tap.check_test( tap.hasnt_function('taptest', 'myFunction', 'desc' ), false, 'hasnt_function( sch, func, desc )', 'desc', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_function throws an error on version 5.7') +else tap.check_test( tap.hasnt_function('taptest', 'myProc', '' ), true, 'hasnt_function( sch, non func, desc )', 'Function taptest.myProc should not exist', '', 0 -); +) +end; /****************************************************************************/ -- Test has_procedure -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_procedure throws an error on version 5.7') +else tap.check_test( tap.has_procedure('taptest', 'myProc', '' ), true, 'has_procedure( sch, func )', 'Procedure taptest.myProc should exist', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_procedure throws an error on version 5.7') +else tap.check_test( tap.has_procedure('taptest', 'myProc', 'desc' ), true, 'has_procedure( sch, func, desc )', 'desc', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('has_procedure throws an error on version 5.7') +else tap.check_test( tap.has_procedure('taptest', 'myFunction', '' ), false, 'has_procedure( sch, non func, desc )', 'Procedure taptest.myFunction should exist', '', 0 -); +) +end; /****************************************************************************/ -- Test hasnt_procedure -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_procedure throws an error on version 5.7') +else tap.check_test( tap.hasnt_procedure('taptest', 'myProc', '' ), false, 'hasnt_procedure( sch, func )', 'Procedure taptest.myProc should not exist', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_procedure throws an error on version 5.7') +else tap.check_test( tap.hasnt_procedure('taptest', 'myProc', 'desc' ), false, 'hasnt_procedure( sch, func, desc )', 'desc', '', 0 -); +) +end; -SELECT tap.check_test( +select case when mysql_version() >= 507018 +then skip('hasnt_procedure throws an error on version 5.7') +else tap.check_test( tap.hasnt_procedure('taptest', 'myFunction', '' ), true, 'hasnt_procedure( sch, non func, desc )', 'Procedure taptest.myFunction should not exist', '', 0 -); - +) +end; /****************************************************************************/ -- Finish the tests and clean up. call tap.finish(); DROP DATABASE IF EXISTS taptest; -ROLLBACK; \ No newline at end of file +ROLLBACK;