Commit ce38949b authored by Andres Freund's avatar Andres Freund

Improve expression evaluation test coverage.

Upcoming patches are revamping expression evaluation significantly. It
therefore seems prudent to try to ensure that the coverage of the
existing evaluation code is high.

This commit adds coverage for the cases that can reasonably be
tested. There's still a bunch of unreachable error messages and such,
but otherwise this achieves nearly full regression test coverage (with
the exception of the unused GetAttributeByNum/GetAttributeByName).

Author: Andres Freund
Discussion: https://postgr.es/m/20170310194021.ek4bs4bl2khxkmll@alap3.anarazel.de
parent cd1e23e9
......@@ -392,6 +392,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
}
else
{
/* this is currently unreachable */
econtext->caseValue_datum =
array_get_slice(array_source, i,
upper.indx, lower.indx,
......
......@@ -187,6 +187,44 @@ select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
{{5,6},{8,9}}
(1 row)
--
-- check subscription corner cases
--
-- More subscripts than MAXDIMS(6)
SELECT ('{}'::int[])[1][2][3][4][5][6][7];
ERROR: number of array dimensions (7) exceeds the maximum allowed (6)
-- NULL index yields NULL when selecting
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL][1];
int4
------
(1 row)
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL:1][1];
int4
------
(1 row)
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][1:NULL][1];
int4
------
(1 row)
-- NULL index in assignment is an error
UPDATE arrtest
SET c[NULL] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
ERROR: array subscript in assignment must not be null
UPDATE arrtest
SET c[NULL:1] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
ERROR: array subscript in assignment must not be null
UPDATE arrtest
SET c[1:NULL] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
ERROR: array subscript in assignment must not be null
-- test slices with empty lower and/or upper index
CREATE TEMP TABLE arrtest_s (
a int2[],
......@@ -263,6 +301,36 @@ SELECT f1[:1] FROM POINT_TBL;
ERROR: slices of fixed-length arrays not implemented
SELECT f1[:] FROM POINT_TBL;
ERROR: slices of fixed-length arrays not implemented
-- subscript assignments to fixed-width result in NULL if previous value is NULL
UPDATE point_tbl SET f1[0] = 10 WHERE f1 IS NULL RETURNING *;
f1
----
(1 row)
INSERT INTO point_tbl(f1[0]) VALUES(0) RETURNING *;
f1
----
(1 row)
-- NULL assignments get ignored
UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *;
f1
---------
(10,10)
(1 row)
-- but non-NULL subscript assignments work
UPDATE point_tbl SET f1[0] = -10, f1[1] = -10 WHERE f1::text = '(10,10)'::point::text RETURNING *;
f1
-----------
(-10,-10)
(1 row)
-- but not to expand the range
UPDATE point_tbl SET f1[3] = 10 WHERE f1::text = '(-10,-10)'::point::text RETURNING *;
ERROR: array subscript out of range
--
-- test array extension
--
......@@ -1099,6 +1167,12 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"
{{{{{{a,bb,ccc}}}}}}
(1 row)
SELECT NULL::text[]::int[] AS "NULL";
NULL
------
(1 row)
-- scalar op any/all (array)
select 33 = any ('{1,2,3}');
?column?
......@@ -1214,6 +1288,13 @@ select 33 = all ('{33,null,33}');
(1 row)
-- nulls later in the bitmap
SELECT -1 != ALL(ARRAY(SELECT NULLIF(g.i, 900) FROM generate_series(1,1000) g(i)));
?column?
----------
(1 row)
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
insert into arr_tbl values ('{1,2,3}');
......
......@@ -442,6 +442,29 @@ SELECT '' AS "Not True", f1
| f
(4 rows)
--
-- Tests for BooleanTest
--
CREATE TABLE BOOLTBL3 (d text, b bool, o int);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('true', true, 1);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('false', false, 2);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('null', null, 3);
SELECT
d,
b IS TRUE AS istrue,
b IS NOT TRUE AS isnottrue,
b IS FALSE AS isfalse,
b IS NOT FALSE AS isnotfalse,
b IS UNKNOWN AS isunknown,
b IS NOT UNKNOWN AS isnotunknown
FROM booltbl3 ORDER BY o;
d | istrue | isnottrue | isfalse | isnotfalse | isunknown | isnotunknown
-------+--------+-----------+---------+------------+-----------+--------------
true | t | f | f | t | f | t
false | f | t | t | f | f | t
null | f | t | f | t | t | f
(3 rows)
--
-- Clean up
-- Many tables are retained by the regression test, but these do not seem
......@@ -450,3 +473,4 @@ SELECT '' AS "Not True", f1
--
DROP TABLE BOOLTBL1;
DROP TABLE BOOLTBL2;
DROP TABLE BOOLTBL3;
......@@ -72,6 +72,14 @@ SELECT '6' AS "One",
6 | 6
(1 row)
SELECT '7' AS "None",
CASE WHEN random() < 0 THEN 1
END AS "NULL on no matches";
None | NULL on no matches
------+--------------------
7 |
(1 row)
-- Constant-expression folding shouldn't evaluate unreachable subexpressions
SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END;
case
......
--
-- expression evaluated tests that don't fit into a more specific file
--
--
-- Tests for SQLVAlueFunction
--
-- current_date (always matches because of transactional behaviour)
SELECT date(now())::text = current_date::text;
?column?
----------
t
(1 row)
-- current_time / localtime
SELECT now()::timetz::text = current_time::text;
?column?
----------
t
(1 row)
SELECT now()::time::text = localtime::text;
?column?
----------
t
(1 row)
-- current_timestamp / localtimestamp (always matches because of transactional behaviour)
SELECT current_timestamp = NOW();
?column?
----------
t
(1 row)
-- precision
SELECT length(current_timestamp::text) >= length(current_timestamp(0)::text);
?column?
----------
t
(1 row)
-- localtimestamp
SELECT now()::timestamp::text = localtimestamp::text;
?column?
----------
t
(1 row)
-- current_role/user/user is tested in rolnames.sql
-- current database / catalog
SELECT current_catalog = current_database();
?column?
----------
t
(1 row)
-- current_schema
SELECT current_schema;
current_schema
----------------
public
(1 row)
SET search_path = 'notme';
SELECT current_schema;
current_schema
----------------
(1 row)
SET search_path = 'pg_catalog';
SELECT current_schema;
current_schema
----------------
pg_catalog
(1 row)
RESET search_path;
......@@ -710,6 +710,12 @@ select derived::base from derived;
(0)
(1 row)
select NULL::derived::base;
base
------
(1 row)
drop table derived;
drop table base;
create table p1(ff1 int);
......
......@@ -586,6 +586,43 @@ ERROR: must be owner of function testfunc1
DROP FUNCTION testfunc1(int); -- ok
-- restore to sanity
GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
-- verify privilege checks on coercions
BEGIN;
SELECT NULL::int4[]::int8[];
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[];
int8
------
{1}
(1 row)
REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
SELECT NULL::int4[]::int8[];
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[]; --superuser, suceed
int8
------
{1}
(1 row)
SET SESSION AUTHORIZATION regress_user4;
SELECT NULL::int4[]::int8[]; --other user, no elements to convert
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[]; --other user, fail
ERROR: permission denied for function int8
ROLLBACK;
-- privileges on types
-- switch to superuser
\c -
......
......@@ -711,3 +711,43 @@ select r, r is null as isnull, r is not null as isnotnull from r;
(,) | t | f
(6 rows)
--
-- Tests for component access / FieldSelect
--
CREATE TABLE compositetable(a text, b text) WITH OIDS;
INSERT INTO compositetable(a, b) VALUES('fa', 'fb');
-- composite type columns can't directly be accessed (error)
SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s;
ERROR: missing FROM-clause entry for table "d"
LINE 1: SELECT d.a FROM (SELECT compositetable AS d FROM compositeta...
^
-- but can be accessed with proper parens
SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s;
a | b
----+----
fa | fb
(1 row)
-- oids can't be accessed in composite types (error)
SELECT (d).oid FROM (SELECT compositetable AS d FROM compositetable) s;
ERROR: column "oid" not found in data type compositetable
LINE 1: SELECT (d).oid FROM (SELECT compositetable AS d FROM composi...
^
-- accessing non-existing column in NULL datum errors out
SELECT (NULL::compositetable).nonexistant;
ERROR: column "nonexistant" not found in data type compositetable
LINE 1: SELECT (NULL::compositetable).nonexistant;
^
-- existing column in a NULL composite yield NULL
SELECT (NULL::compositetable).a;
a
---
(1 row)
-- oids can't be accessed in composite types (error)
SELECT (NULL::compositetable).oid;
ERROR: column "oid" not found in data type compositetable
LINE 1: SELECT (NULL::compositetable).oid;
^
DROP TABLE compositetable;
......@@ -30,7 +30,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime
# ----------
test: geometry horology regex oidjoins type_sanity opr_sanity
test: geometry horology regex oidjoins type_sanity opr_sanity expressions
# ----------
# These four each depend on the previous one
......
......@@ -49,6 +49,7 @@ test: regex
test: oidjoins
test: type_sanity
test: opr_sanity
test: expressions
test: insert
test: insert_conflict
test: create_function_1
......
......@@ -103,6 +103,26 @@ select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
select '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[];
select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
--
-- check subscription corner cases
--
-- More subscripts than MAXDIMS(6)
SELECT ('{}'::int[])[1][2][3][4][5][6][7];
-- NULL index yields NULL when selecting
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL][1];
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][NULL:1][1];
SELECT ('{{{1},{2},{3}},{{4},{5},{6}}}'::int[])[1][1:NULL][1];
-- NULL index in assignment is an error
UPDATE arrtest
SET c[NULL] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
UPDATE arrtest
SET c[NULL:1] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
UPDATE arrtest
SET c[1:NULL] = '{"can''t assign"}'
WHERE array_dims(c) is not null;
-- test slices with empty lower and/or upper index
CREATE TEMP TABLE arrtest_s (
a int2[],
......@@ -134,6 +154,16 @@ SELECT f1[0:] FROM POINT_TBL;
SELECT f1[:1] FROM POINT_TBL;
SELECT f1[:] FROM POINT_TBL;
-- subscript assignments to fixed-width result in NULL if previous value is NULL
UPDATE point_tbl SET f1[0] = 10 WHERE f1 IS NULL RETURNING *;
INSERT INTO point_tbl(f1[0]) VALUES(0) RETURNING *;
-- NULL assignments get ignored
UPDATE point_tbl SET f1[0] = NULL WHERE f1::text = '(10,10)'::point::text RETURNING *;
-- but non-NULL subscript assignments work
UPDATE point_tbl SET f1[0] = -10, f1[1] = -10 WHERE f1::text = '(10,10)'::point::text RETURNING *;
-- but not to expand the range
UPDATE point_tbl SET f1[3] = 10 WHERE f1::text = '(-10,-10)'::point::text RETURNING *;
--
-- test array extension
--
......@@ -316,6 +346,7 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
SELECT NULL::text[]::int[] AS "NULL";
-- scalar op any/all (array)
select 33 = any ('{1,2,3}');
......@@ -341,6 +372,8 @@ select 33 = all (null::int[]);
select null::int = all ('{1,2,3}');
select 33 = all ('{1,null,3}');
select 33 = all ('{33,null,33}');
-- nulls later in the bitmap
SELECT -1 != ALL(ARRAY(SELECT NULLIF(g.i, 900) FROM generate_series(1,1000) g(i)));
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
......
......@@ -201,6 +201,24 @@ SELECT '' AS "Not True", f1
FROM BOOLTBL2
WHERE f1 IS NOT TRUE;
--
-- Tests for BooleanTest
--
CREATE TABLE BOOLTBL3 (d text, b bool, o int);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('true', true, 1);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('false', false, 2);
INSERT INTO BOOLTBL3 (d, b, o) VALUES ('null', null, 3);
SELECT
d,
b IS TRUE AS istrue,
b IS NOT TRUE AS isnottrue,
b IS FALSE AS isfalse,
b IS NOT FALSE AS isnotfalse,
b IS UNKNOWN AS isunknown,
b IS NOT UNKNOWN AS isnotunknown
FROM booltbl3 ORDER BY o;
--
-- Clean up
-- Many tables are retained by the regression test, but these do not seem
......@@ -211,3 +229,5 @@ SELECT '' AS "Not True", f1
DROP TABLE BOOLTBL1;
DROP TABLE BOOLTBL2;
DROP TABLE BOOLTBL3;
......@@ -58,6 +58,11 @@ SELECT '6' AS "One",
ELSE 7
END AS "Two WHEN with default";
SELECT '7' AS "None",
CASE WHEN random() < 0 THEN 1
END AS "NULL on no matches";
-- Constant-expression folding shouldn't evaluate unreachable subexpressions
SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END;
SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END;
......
--
-- expression evaluated tests that don't fit into a more specific file
--
--
-- Tests for SQLVAlueFunction
--
-- current_date (always matches because of transactional behaviour)
SELECT date(now())::text = current_date::text;
-- current_time / localtime
SELECT now()::timetz::text = current_time::text;
SELECT now()::time::text = localtime::text;
-- current_timestamp / localtimestamp (always matches because of transactional behaviour)
SELECT current_timestamp = NOW();
-- precision
SELECT length(current_timestamp::text) >= length(current_timestamp(0)::text);
-- localtimestamp
SELECT now()::timestamp::text = localtimestamp::text;
-- current_role/user/user is tested in rolnames.sql
-- current database / catalog
SELECT current_catalog = current_database();
-- current_schema
SELECT current_schema;
SET search_path = 'notme';
SELECT current_schema;
SET search_path = 'pg_catalog';
SELECT current_schema;
RESET search_path;
......@@ -194,6 +194,7 @@ create table base (i integer);
create table derived () inherits (base);
insert into derived (i) values (0);
select derived::base from derived;
select NULL::derived::base;
drop table derived;
drop table base;
......
......@@ -398,6 +398,18 @@ DROP FUNCTION testfunc1(int); -- ok
-- restore to sanity
GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
-- verify privilege checks on coercions
BEGIN;
SELECT NULL::int4[]::int8[];
SELECT '{1}'::int4[]::int8[];
REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
SELECT NULL::int4[]::int8[];
SELECT '{1}'::int4[]::int8[]; --superuser, suceed
SET SESSION AUTHORIZATION regress_user4;
SELECT NULL::int4[]::int8[]; --other user, no elements to convert
SELECT '{1}'::int4[]::int8[]; --other user, fail
ROLLBACK;
-- privileges on types
-- switch to superuser
......
......@@ -310,3 +310,26 @@ with r(a,b) as
(values (1,row(1,2)), (1,row(null,null)), (1,null),
(null,row(1,2)), (null,row(null,null)), (null,null) )
select r, r is null as isnull, r is not null as isnotnull from r;
--
-- Tests for component access / FieldSelect
--
CREATE TABLE compositetable(a text, b text) WITH OIDS;
INSERT INTO compositetable(a, b) VALUES('fa', 'fb');
-- composite type columns can't directly be accessed (error)
SELECT d.a FROM (SELECT compositetable AS d FROM compositetable) s;
-- but can be accessed with proper parens
SELECT (d).a, (d).b FROM (SELECT compositetable AS d FROM compositetable) s;
-- oids can't be accessed in composite types (error)
SELECT (d).oid FROM (SELECT compositetable AS d FROM compositetable) s;
-- accessing non-existing column in NULL datum errors out
SELECT (NULL::compositetable).nonexistant;
-- existing column in a NULL composite yield NULL
SELECT (NULL::compositetable).a;
-- oids can't be accessed in composite types (error)
SELECT (NULL::compositetable).oid;
DROP TABLE compositetable;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment