--- sql/pgtap.sql 2011-02-01 16:00:45.000000000 -0800 +++ sql/pgtap.sql.orig 2011-02-01 16:00:54.000000000 -0800 @@ -68,6 +68,27 @@ RETURNS text AS 'SELECT current_setting(''server_version'')' LANGUAGE SQL IMMUTABLE; +-- Cast oidvector to regtype[] like 8.1 does. +CREATE OR REPLACE FUNCTION oidvregtype(oidvector) +RETURNS regtype[] AS +'SELECT COALESCE(string_to_array(textin(oidvectorout($1::oidvector)), '' '')::oid[]::regtype[], ''{}''::regtype[]);' +LANGUAGE sql IMMUTABLE STRICT; + +CREATE CAST (oidvector AS regtype[]) WITH FUNCTION oidvregtype(oidvector) AS ASSIGNMENT; + +-- Cast int2vector to int[] like 8.1 does. +CREATE OR REPLACE FUNCTION int2vint(int2vector) +RETURNS int[] AS +'SELECT COALESCE(string_to_array(textin(int2vectorout($1::int2vector)), '' '')::int[], ''{}''::int[]);' +LANGUAGE sql IMMUTABLE STRICT; + +CREATE CAST (int2vector AS int[]) WITH FUNCTION int2vint(int2vector) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION pg_typeof("any") +RETURNS regtype +AS '$libdir/pgtap' +LANGUAGE C STABLE; + CREATE OR REPLACE FUNCTION pg_typeof("any") RETURNS regtype AS '$libdir/pgtap' @@ -149,53 +170,63 @@ CREATE OR REPLACE FUNCTION _get ( text ) RETURNS integer AS $$ DECLARE - ret integer; + rec RECORD; BEGIN - EXECUTE 'SELECT value FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' INTO ret; - RETURN ret; + FOR rec IN EXECUTE 'SELECT value FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' LOOP + RETURN rec.value; + END LOOP; + RETURN NULL; END; $$ LANGUAGE plpgsql strict; CREATE OR REPLACE FUNCTION _get_latest ( text ) RETURNS integer[] AS $$ DECLARE - ret integer[]; + rec RECORD; BEGIN - EXECUTE 'SELECT ARRAY[ id, value] FROM __tcache__ WHERE label = ' || + FOR rec IN EXECUTE 'SELECT ARRAY[ id, value] AS a FROM __tcache__ WHERE label = ' || quote_literal($1) || ' AND id = (SELECT MAX(id) FROM __tcache__ WHERE label = ' || - quote_literal($1) || ') LIMIT 1' INTO ret; - RETURN ret; + quote_literal($1) || ') LIMIT 1' LOOP + RETURN rec.a; + END LOOP; + RETURN NULL; END; $$ LANGUAGE plpgsql strict; CREATE OR REPLACE FUNCTION _get_latest ( text, integer ) RETURNS integer AS $$ DECLARE - ret integer; + rec RECORD; BEGIN - EXECUTE 'SELECT MAX(id) FROM __tcache__ WHERE label = ' || - quote_literal($1) || ' AND value = ' || $2 INTO ret; - RETURN ret; + FOR rec IN EXECUTE 'SELECT MAX(id) AS id FROM __tcache__ WHERE label = ' || + quote_literal($1) || ' AND value = ' || $2 LOOP + RETURN rec.id; + END LOOP; + RETURN NULL; END; $$ LANGUAGE plpgsql strict; CREATE OR REPLACE FUNCTION _get_note ( text ) RETURNS text AS $$ DECLARE - ret text; + rec RECORD; BEGIN - EXECUTE 'SELECT note FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' INTO ret; - RETURN ret; + FOR rec IN EXECUTE 'SELECT note FROM __tcache__ WHERE label = ' || quote_literal($1) || ' LIMIT 1' LOOP + RETURN rec.note; + END LOOP; + RETURN; END; $$ LANGUAGE plpgsql strict; CREATE OR REPLACE FUNCTION _get_note ( integer ) RETURNS text AS $$ DECLARE - ret text; + rec RECORD; BEGIN - EXECUTE 'SELECT note FROM __tcache__ WHERE id = ' || $1 || ' LIMIT 1' INTO ret; - RETURN ret; + FOR rec IN EXECUTE 'SELECT note FROM __tcache__ WHERE id = ' || $1 || ' LIMIT 1' LOOP + RETURN rec.note; + END LOOP; + RETURN; END; $$ LANGUAGE plpgsql strict; @@ -259,10 +290,12 @@ CREATE OR REPLACE FUNCTION num_failed () RETURNS INTEGER AS $$ DECLARE - ret integer; + rec RECORD; BEGIN - EXECUTE 'SELECT COUNT(*)::INTEGER FROM __tresults__ WHERE ok = FALSE' INTO ret; - RETURN ret; + FOR rec IN EXECUTE 'SELECT COUNT(*)::INTEGER AS cnt FROM __tresults__ WHERE ok = FALSE' LOOP + RETURN rec.cnt; + END LOOP; + RETURN; END; $$ LANGUAGE plpgsql strict; @@ -535,13 +568,16 @@ want ALIAS FOR $3; descr ALIAS FOR $4; result BOOLEAN; + rec RECORD; output TEXT; BEGIN - EXECUTE 'SELECT ' || + FOR rec IN EXECUTE 'SELECT ' || COALESCE(quote_literal( have ), 'NULL') || '::' || pg_typeof(have)::text || ' ' || op || ' ' || - COALESCE(quote_literal( want ), 'NULL') || '::' || pg_typeof(want)::text - INTO result; + COALESCE(quote_literal( want ), 'NULL') || '::' || pg_typeof(want)::text || ' AS res' + LOOP + result := rec.res; + END LOOP; output := ok( COALESCE(result, FALSE), descr ); RETURN output || CASE result WHEN TRUE THEN '' ELSE E'\n' || diag( ' ' || COALESCE( quote_literal(have), 'NULL' ) || @@ -1389,19 +1425,21 @@ CREATE OR REPLACE FUNCTION _def_is( TEXT, TEXT, anyelement, TEXT ) RETURNS TEXT AS $$ DECLARE - thing text; + ret RECORD; BEGIN IF $1 ~ '^[^'']+[(]' THEN -- It's a functional default. RETURN is( $1, $3, $4 ); END IF; - EXECUTE 'SELECT is(' + FOR ret IN EXECUTE 'SELECT is(' || COALESCE($1, 'NULL' || '::' || $2) || '::' || $2 || ', ' || COALESCE(quote_literal($3), 'NULL') || '::' || $2 || ', ' || COALESCE(quote_literal($4), 'NULL') - || ')' INTO thing; - RETURN thing; + || ') AS a' LOOP + RETURN ret.a; + END LOOP; + RETURN; END; $$ LANGUAGE plpgsql; @@ -3437,39 +3475,6 @@ SELECT ok( NOT _has_type( $1, ARRAY['e'] ), ('Enum ' || quote_ident($1) || ' should not exist')::text ); $$ LANGUAGE sql; -CREATE OR REPLACE FUNCTION _has_role( NAME ) -RETURNS BOOLEAN AS $$ - SELECT EXISTS( - SELECT true - FROM pg_catalog.pg_roles - WHERE rolname = $1 - ); -$$ LANGUAGE sql STRICT; - --- has_role( role, description ) -CREATE OR REPLACE FUNCTION has_role( NAME, TEXT ) -RETURNS TEXT AS $$ - SELECT ok( _has_role($1), $2 ); -$$ LANGUAGE sql; - --- has_role( role ) -CREATE OR REPLACE FUNCTION has_role( NAME ) -RETURNS TEXT AS $$ - SELECT ok( _has_role($1), 'Role ' || quote_ident($1) || ' should exist' ); -$$ LANGUAGE sql; - --- hasnt_role( role, description ) -CREATE OR REPLACE FUNCTION hasnt_role( NAME, TEXT ) -RETURNS TEXT AS $$ - SELECT ok( NOT _has_role($1), $2 ); -$$ LANGUAGE sql; - --- hasnt_role( role ) -CREATE OR REPLACE FUNCTION hasnt_role( NAME ) -RETURNS TEXT AS $$ - SELECT ok( NOT _has_role($1), 'Role ' || quote_ident($1) || ' should not exist' ); -$$ LANGUAGE sql; - CREATE OR REPLACE FUNCTION _has_user( NAME ) RETURNS BOOLEAN AS $$ SELECT EXISTS( SELECT true FROM pg_catalog.pg_user WHERE usename = $1); @@ -3501,9 +3506,9 @@ CREATE OR REPLACE FUNCTION _is_super( NAME ) RETURNS BOOLEAN AS $$ - SELECT rolsuper - FROM pg_catalog.pg_roles - WHERE rolname = $1 + SELECT usesuper + FROM pg_catalog.pg_user + WHERE usename = $1 $$ LANGUAGE sql STRICT; -- is_superuser( user, description ) @@ -3578,13 +3583,8 @@ $$ LANGUAGE sql; CREATE OR REPLACE FUNCTION _grolist ( NAME ) -RETURNS oid[] AS $$ - SELECT ARRAY( - SELECT member - FROM pg_catalog.pg_auth_members m - JOIN pg_catalog.pg_roles r ON m.roleid = r.oid - WHERE r.rolname = $1 - ); +RETURNS integer[] AS $$ + SELECT grolist FROM pg_catalog.pg_group WHERE groname = $1; $$ LANGUAGE sql; -- is_member_of( group, user[], description ) @@ -3593,9 +3593,9 @@ DECLARE missing text[]; BEGIN - IF NOT _has_role($1) THEN + IF NOT _has_group($1) THEN RETURN fail( $3 ) || E'\n' || diag ( - ' Role ' || quote_ident($1) || ' does not exist' + ' Group ' || quote_ident($1) || ' does not exist' ); END IF; @@ -5542,6 +5542,7 @@ res BOOLEAN; descr TEXT; adiag TEXT; + rec RECORD; have ALIAS FOR $1; eok ALIAS FOR $2; name ALIAS FOR $3; @@ -5553,8 +5554,10 @@ tnumb := currval('__tresults___numb_seq'); -- Fetch the results. - EXECUTE 'SELECT aok, descr FROM __tresults__ WHERE numb = ' || tnumb - INTO aok, adescr; + FOR rec IN EXECUTE 'SELECT aok, descr FROM __tresults__ WHERE numb = ' || tnumb LOOP + aok := rec.aok; + adescr := rec.descr; + END LOOP; -- Now delete those results. EXECUTE 'DELETE FROM __tresults__ WHERE numb = ' || tnumb; @@ -5742,116 +5745,6 @@ SELECT TRUE; $$ LANGUAGE sql; -CREATE OR REPLACE FUNCTION _runner( text[], text[], text[], text[], text[] ) -RETURNS SETOF TEXT AS $$ -DECLARE - startup ALIAS FOR $1; - shutdown ALIAS FOR $2; - setup ALIAS FOR $3; - teardown ALIAS FOR $4; - tests ALIAS FOR $5; - rec record; - verbos boolean := _is_verbose(); -- verbose is a reserved word in 8.5. - num_faild INTEGER := 0; -BEGIN - BEGIN - -- No plan support. - PERFORM * FROM no_plan(); - FOR rec IN SELECT * FROM _runem(startup, false) AS b(a) LOOP RETURN NEXT rec.a; END LOOP; - EXCEPTION - -- Catch all exceptions and simply rethrow custom exceptions. This - -- will roll back everything in the above block. - WHEN raise_exception THEN - RAISE EXCEPTION '%', SQLERRM; - END; - - BEGIN - FOR i IN 1..array_upper(tests, 1) LOOP - BEGIN - -- What test are we running? - IF verbos THEN RETURN NEXT diag(tests[i] || '()'); END IF; - - -- Run the setup functions. - FOR rec IN SELECT * FROM _runem(setup, false) AS b(a) LOOP RETURN NEXT rec.a; END LOOP; - - -- Run the actual test function. - FOR rec IN EXECUTE 'SELECT * FROM ' || tests[i] || '() AS b(a)' LOOP - RETURN NEXT rec.a; - END LOOP; - - -- Run the teardown functions. - FOR rec IN SELECT * FROM _runem(teardown, false) AS b(a) LOOP RETURN NEXT rec.a; END LOOP; - - -- Remember how many failed and then roll back. - num_faild := num_faild + num_failed(); - RAISE EXCEPTION '__TAP_ROLLBACK__'; - - EXCEPTION WHEN raise_exception THEN - IF SQLERRM <> '__TAP_ROLLBACK__' THEN - -- We didn't raise it, so propagate it. - RAISE EXCEPTION '%', SQLERRM; - END IF; - END; - END LOOP; - - -- Run the shutdown functions. - FOR rec IN SELECT * FROM _runem(shutdown, false) AS b(a) LOOP RETURN NEXT rec.a; END LOOP; - - -- Raise an exception to rollback any changes. - RAISE EXCEPTION '__TAP_ROLLBACK__'; - EXCEPTION WHEN raise_exception THEN - IF SQLERRM <> '__TAP_ROLLBACK__' THEN - -- We didn't raise it, so propagate it. - RAISE EXCEPTION '%', SQLERRM; - END IF; - END; - -- Finish up. - FOR rec IN SELECT * FROM _finish( currval('__tresults___numb_seq')::integer, 0, num_faild ) AS b(a) LOOP - RETURN NEXT rec.a; - END LOOP; - - -- Clean up and return. - PERFORM _cleanup(); - RETURN; -END; -$$ LANGUAGE plpgsql; - --- runtests( schema, match ) -CREATE OR REPLACE FUNCTION runtests( NAME, TEXT ) -RETURNS SETOF TEXT AS $$ - SELECT * FROM _runner( - findfuncs( $1, '^startup' ), - findfuncs( $1, '^shutdown' ), - findfuncs( $1, '^setup' ), - findfuncs( $1, '^teardown' ), - findfuncs( $1, $2 ) - ); -$$ LANGUAGE sql; - --- runtests( schema ) -CREATE OR REPLACE FUNCTION runtests( NAME ) -RETURNS SETOF TEXT AS $$ - SELECT * FROM runtests( $1, '^test' ); -$$ LANGUAGE sql; - --- runtests( match ) -CREATE OR REPLACE FUNCTION runtests( TEXT ) -RETURNS SETOF TEXT AS $$ - SELECT * FROM _runner( - findfuncs( '^startup' ), - findfuncs( '^shutdown' ), - findfuncs( '^setup' ), - findfuncs( '^teardown' ), - findfuncs( $1 ) - ); -$$ LANGUAGE sql; - --- runtests( ) -CREATE OR REPLACE FUNCTION runtests( ) -RETURNS SETOF TEXT AS $$ - SELECT * FROM runtests( '^test' ); -$$ LANGUAGE sql; - CREATE OR REPLACE FUNCTION _temptable ( TEXT, TEXT ) RETURNS TEXT AS $$ BEGIN @@ -5901,13 +5794,13 @@ -- Find extra records. FOR rec in EXECUTE 'SELECT * FROM ' || have || ' EXCEPT ' || $4 || 'SELECT * FROM ' || want LOOP - extras := array_append(extras, textin(record_out(rec))); + extras := array_append(extras, textin(record_out(rec, 2249))); END LOOP; -- Find missing records. FOR rec in EXECUTE 'SELECT * FROM ' || want || ' EXCEPT ' || $4 || 'SELECT * FROM ' || have LOOP - missing := array_append(missing, textin(record_out(rec))); + missing := array_append(missing, textin(record_out(rec, 2249))); END LOOP; -- Drop the temporary tables. @@ -6021,16 +5914,20 @@ missing TEXT[] := '{}'; res BOOLEAN := TRUE; msg TEXT := ''; + rec RECORD; BEGIN BEGIN -- Find extra records. - EXECUTE 'SELECT EXISTS ( ' + FOR rec IN EXECUTE 'SELECT EXISTS ( ' || '( SELECT * FROM ' || have || ' EXCEPT ' || $4 || ' SELECT * FROM ' || want || ' ) UNION ( ' || ' SELECT * FROM ' || want || ' EXCEPT ' || $4 || ' SELECT * FROM ' || have - || ' ) LIMIT 1 )' INTO res; + || ' ) LIMIT 1 ) AS a' + LOOP + res := rec.a; + END LOOP; -- Drop the temporary tables. EXECUTE 'DROP TABLE ' || have; @@ -6131,7 +6028,7 @@ -- Find relevant records. FOR rec in EXECUTE 'SELECT * FROM ' || want || ' ' || $4 || ' SELECT * FROM ' || have LOOP - results := array_append(results, textin(record_out(rec))); + results := array_append(results, textin(record_out(rec, 2249))); END LOOP; -- Drop the temporary tables. @@ -6226,11 +6123,11 @@ FETCH want INTO want_rec; want_found := FOUND; WHILE have_found OR want_found LOOP - IF textin(record_out(have_rec)) IS DISTINCT FROM textin(record_out(want_rec)) OR have_found <> want_found THEN + IF textin(record_out(have_rec, 2249)) IS DISTINCT FROM textin(record_out(want_rec, 2249)) OR have_found <> want_found THEN RETURN ok( false, $3 ) || E'\n' || diag( ' Results differ beginning at row ' || rownum || E':\n' || - ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec)) ELSE 'NULL' END || E'\n' || - ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec)) ELSE 'NULL' END + ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec, 2249)) ELSE 'NULL' END || E'\n' || + ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec, 2249)) ELSE 'NULL' END ); END IF; rownum = rownum + 1; @@ -6245,8 +6142,8 @@ WHEN datatype_mismatch THEN RETURN ok( false, $3 ) || E'\n' || diag( E' Columns differ between queries:\n' || - ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec)) ELSE 'NULL' END || E'\n' || - ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec)) ELSE 'NULL' END + ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec, 2249)) ELSE 'NULL' END || E'\n' || + ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec, 2249)) ELSE 'NULL' END ); END; $$ LANGUAGE plpgsql; @@ -6381,7 +6278,7 @@ FETCH want INTO want_rec; want_found := FOUND; WHILE have_found OR want_found LOOP - IF textin(record_out(have_rec)) IS DISTINCT FROM textin(record_out(want_rec)) OR have_found <> want_found THEN + IF textin(record_out(have_rec, 2249)) IS DISTINCT FROM textin(record_out(want_rec, 2249)) OR have_found <> want_found THEN RETURN ok( true, $3 ); ELSE FETCH have INTO have_rec; @@ -6395,8 +6292,8 @@ WHEN datatype_mismatch THEN RETURN ok( false, $3 ) || E'\n' || diag( E' Columns differ between queries:\n' || - ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec)) ELSE 'NULL' END || E'\n' || - ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec)) ELSE 'NULL' END + ' have: ' || CASE WHEN have_found THEN textin(record_out(have_rec, 2249)) ELSE 'NULL' END || E'\n' || + ' want: ' || CASE WHEN want_found THEN textin(record_out(want_rec, 2249)) ELSE 'NULL' END ); END; $$ LANGUAGE plpgsql; @@ -6544,7 +6441,7 @@ BEGIN -- Find extra records. FOR rec in EXECUTE _query($1) LOOP - extras := extras || textin(record_out(rec)); + extras := extras || textin(record_out(rec, 2249)); END LOOP; -- What extra records do we have? @@ -6648,35 +6545,6 @@ SELECT throws_imatching($1, $2, 'Should throw exception matching ' || quote_literal($2) ); $$ LANGUAGE sql; --- roles_are( roles[], description ) -CREATE OR REPLACE FUNCTION roles_are( NAME[], TEXT ) -RETURNS TEXT AS $$ - SELECT _are( - 'roles', - ARRAY( - SELECT rolname - FROM pg_catalog.pg_roles - EXCEPT - SELECT $1[i] - FROM generate_series(1, array_upper($1, 1)) s(i) - ), - ARRAY( - SELECT $1[i] - FROM generate_series(1, array_upper($1, 1)) s(i) - EXCEPT - SELECT rolname - FROM pg_catalog.pg_roles - ), - $2 - ); -$$ LANGUAGE SQL; - --- roles_are( roles[] ) -CREATE OR REPLACE FUNCTION roles_are( NAME[] ) -RETURNS TEXT AS $$ - SELECT roles_are( $1, 'There should be the correct roles' ); -$$ LANGUAGE SQL; - CREATE OR REPLACE FUNCTION _types_are ( NAME, NAME[], TEXT, CHAR[] ) RETURNS TEXT AS $$ SELECT _are( @@ -7039,12 +6907,12 @@ rec RECORD; BEGIN FOR rec in EXECUTE _query($1) LOOP END LOOP; - IF NOT textin(record_out(rec)) IS DISTINCT FROM textin(record_out($2)) + IF NOT textin(record_out(rec, 2249)) IS DISTINCT FROM textin(record_out($2, 2249)) THEN RETURN ok(true, $3); END IF; RETURN ok(false, $3 ) || E'\n' || diag( - ' have: ' || CASE WHEN rec IS NULL THEN 'NULL' ELSE textin(record_out(rec)) END || - E'\n want: ' || CASE WHEN $2 IS NULL THEN 'NULL' ELSE textin(record_out($2)) END + ' have: ' || CASE WHEN rec IS NULL THEN 'NULL' ELSE textin(record_out(rec, 2249)) END || + E'\n want: ' || CASE WHEN $2 IS NULL THEN 'NULL' ELSE textin(record_out($2, 2249)) END ); END; $$ LANGUAGE plpgsql;