postgresql/src/test/regress/expected/enum.out

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

722 lines
16 KiB
Plaintext
Raw Normal View History

--
-- Enum tests
--
CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
--
-- Did it create the right number of rows?
--
SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype;
count
-------
6
(1 row)
--
-- I/O functions
--
SELECT 'red'::rainbow;
rainbow
---------
red
(1 row)
SELECT 'mauve'::rainbow;
ERROR: invalid input value for enum rainbow: "mauve"
LINE 1: SELECT 'mauve'::rainbow;
^
-- Also try it with non-error-throwing API
SELECT pg_input_is_valid('red', 'rainbow');
pg_input_is_valid
-------------------
t
(1 row)
SELECT pg_input_is_valid('mauve', 'rainbow');
pg_input_is_valid
-------------------
f
(1 row)
SELECT * FROM pg_input_error_info('mauve', 'rainbow');
message | detail | hint | sql_error_code
-----------------------------------------------+--------+------+----------------
invalid input value for enum rainbow: "mauve" | | | 22P02
(1 row)
\x
SELECT * FROM pg_input_error_info(repeat('too_long', 32), 'rainbow');
-[ RECORD 1 ]--+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
message | invalid input value for enum rainbow: "too_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_longtoo_long"
detail |
hint |
sql_error_code | 22P02
\x
--
-- adding new values
--
CREATE TYPE planets AS ENUM ( 'venus', 'earth', 'mars' );
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'planets'::regtype
ORDER BY 2;
enumlabel | enumsortorder
-----------+---------------
venus | 1
earth | 2
mars | 3
(3 rows)
ALTER TYPE planets ADD VALUE 'uranus';
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'planets'::regtype
ORDER BY 2;
enumlabel | enumsortorder
-----------+---------------
venus | 1
earth | 2
mars | 3
uranus | 4
(4 rows)
ALTER TYPE planets ADD VALUE 'mercury' BEFORE 'venus';
ALTER TYPE planets ADD VALUE 'saturn' BEFORE 'uranus';
ALTER TYPE planets ADD VALUE 'jupiter' AFTER 'mars';
ALTER TYPE planets ADD VALUE 'neptune' AFTER 'uranus';
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'planets'::regtype
ORDER BY 2;
enumlabel | enumsortorder
-----------+---------------
mercury | 0
venus | 1
earth | 2
mars | 3
jupiter | 3.25
saturn | 3.5
uranus | 4
neptune | 5
(8 rows)
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'planets'::regtype
ORDER BY enumlabel::planets;
enumlabel | enumsortorder
-----------+---------------
mercury | 0
venus | 1
earth | 2
mars | 3
jupiter | 3.25
saturn | 3.5
uranus | 4
neptune | 5
(8 rows)
-- errors for adding labels
ALTER TYPE planets ADD VALUE
'plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto';
ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutoplutopluto"
DETAIL: Labels must be 63 bytes or less.
ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus';
ERROR: "zeus" is not an existing enum label
-- if not exists tests
-- existing value gives error
ALTER TYPE planets ADD VALUE 'mercury';
ERROR: enum label "mercury" already exists
-- unless IF NOT EXISTS is specified
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'mercury';
NOTICE: enum label "mercury" already exists, skipping
-- should be neptune, not mercury
SELECT enum_last(NULL::planets);
enum_last
-----------
neptune
(1 row)
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'pluto';
-- should be pluto, i.e. the new value
SELECT enum_last(NULL::planets);
enum_last
-----------
pluto
(1 row)
--
-- Test inserting so many values that we have to renumber
--
create type insenum as enum ('L1', 'L2');
alter type insenum add value 'i1' before 'L2';
alter type insenum add value 'i2' before 'L2';
alter type insenum add value 'i3' before 'L2';
alter type insenum add value 'i4' before 'L2';
alter type insenum add value 'i5' before 'L2';
alter type insenum add value 'i6' before 'L2';
alter type insenum add value 'i7' before 'L2';
alter type insenum add value 'i8' before 'L2';
alter type insenum add value 'i9' before 'L2';
alter type insenum add value 'i10' before 'L2';
alter type insenum add value 'i11' before 'L2';
alter type insenum add value 'i12' before 'L2';
alter type insenum add value 'i13' before 'L2';
alter type insenum add value 'i14' before 'L2';
alter type insenum add value 'i15' before 'L2';
alter type insenum add value 'i16' before 'L2';
alter type insenum add value 'i17' before 'L2';
alter type insenum add value 'i18' before 'L2';
alter type insenum add value 'i19' before 'L2';
alter type insenum add value 'i20' before 'L2';
alter type insenum add value 'i21' before 'L2';
alter type insenum add value 'i22' before 'L2';
alter type insenum add value 'i23' before 'L2';
alter type insenum add value 'i24' before 'L2';
alter type insenum add value 'i25' before 'L2';
alter type insenum add value 'i26' before 'L2';
alter type insenum add value 'i27' before 'L2';
alter type insenum add value 'i28' before 'L2';
alter type insenum add value 'i29' before 'L2';
alter type insenum add value 'i30' before 'L2';
-- The exact values of enumsortorder will now depend on the local properties
-- of float4, but in any reasonable implementation we should get at least
-- 20 splits before having to renumber; so only hide values > 20.
SELECT enumlabel,
case when enumsortorder > 20 then null else enumsortorder end as so
FROM pg_enum
WHERE enumtypid = 'insenum'::regtype
ORDER BY enumsortorder;
enumlabel | so
-----------+----
L1 | 1
i1 | 2
i2 | 3
i3 | 4
i4 | 5
i5 | 6
i6 | 7
i7 | 8
i8 | 9
i9 | 10
i10 | 11
i11 | 12
i12 | 13
i13 | 14
i14 | 15
i15 | 16
i16 | 17
i17 | 18
i18 | 19
i19 | 20
i20 |
i21 |
i22 |
i23 |
i24 |
i25 |
i26 |
i27 |
i28 |
i29 |
i30 |
L2 |
(32 rows)
--
-- Basic table creation, row selection
--
CREATE TABLE enumtest (col rainbow);
INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green');
COPY enumtest FROM stdin;
SELECT * FROM enumtest;
col
--------
red
orange
yellow
green
blue
purple
(6 rows)
--
-- Operators, no index
--
SELECT * FROM enumtest WHERE col = 'orange';
col
--------
orange
(1 row)
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
col
--------
red
yellow
green
blue
purple
(5 rows)
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
col
--------
green
blue
purple
(3 rows)
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
col
--------
yellow
green
blue
purple
(4 rows)
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
col
--------
red
orange
yellow
(3 rows)
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
col
--------
red
orange
yellow
green
(4 rows)
--
-- Cast to/from text
--
SELECT 'red'::rainbow::text || 'hithere';
?column?
------------
redhithere
(1 row)
SELECT 'red'::text::rainbow = 'red'::rainbow;
?column?
----------
t
(1 row)
--
-- Aggregates
--
SELECT min(col) FROM enumtest;
min
-----
red
(1 row)
SELECT max(col) FROM enumtest;
max
--------
purple
(1 row)
SELECT max(col) FROM enumtest WHERE col < 'green';
max
--------
yellow
(1 row)
--
-- Index tests, force use of index
--
SET enable_seqscan = off;
SET enable_bitmapscan = off;
--
-- Btree index / opclass with the various operators
--
CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col);
SELECT * FROM enumtest WHERE col = 'orange';
col
--------
orange
(1 row)
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
col
--------
red
yellow
green
blue
purple
(5 rows)
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
col
--------
green
blue
purple
(3 rows)
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
col
--------
yellow
green
blue
purple
(4 rows)
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
col
--------
red
orange
yellow
(3 rows)
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
col
--------
red
orange
yellow
green
(4 rows)
SELECT min(col) FROM enumtest;
min
-----
red
(1 row)
SELECT max(col) FROM enumtest;
max
--------
purple
(1 row)
SELECT max(col) FROM enumtest WHERE col < 'green';
max
--------
yellow
(1 row)
DROP INDEX enumtest_btree;
--
-- Hash index / opclass with the = operator
--
CREATE INDEX enumtest_hash ON enumtest USING hash (col);
SELECT * FROM enumtest WHERE col = 'orange';
col
--------
orange
(1 row)
DROP INDEX enumtest_hash;
--
-- End index tests
--
RESET enable_seqscan;
RESET enable_bitmapscan;
--
-- Domains over enums
--
CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
SELECT 'red'::rgb;
rgb
-----
red
(1 row)
SELECT 'purple'::rgb;
ERROR: value for domain rgb violates check constraint "rgb_check"
SELECT 'purple'::rainbow::rgb;
ERROR: value for domain rgb violates check constraint "rgb_check"
DROP DOMAIN rgb;
--
-- Arrays
--
SELECT '{red,green,blue}'::rainbow[];
rainbow
------------------
{red,green,blue}
(1 row)
SELECT ('{red,green,blue}'::rainbow[])[2];
rainbow
---------
green
(1 row)
SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]);
?column?
----------
t
(1 row)
SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]);
?column?
----------
f
(1 row)
SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]);
?column?
----------
f
(1 row)
SELECT 'red' = ALL ('{red,red}'::rainbow[]);
?column?
----------
t
(1 row)
--
-- Support functions
--
SELECT enum_first(NULL::rainbow);
enum_first
------------
red
(1 row)
SELECT enum_last('green'::rainbow);
enum_last
-----------
purple
(1 row)
SELECT enum_range(NULL::rainbow);
enum_range
---------------------------------------
{red,orange,yellow,green,blue,purple}
(1 row)
SELECT enum_range('orange'::rainbow, 'green'::rainbow);
enum_range
-----------------------
{orange,yellow,green}
(1 row)
SELECT enum_range(NULL, 'green'::rainbow);
enum_range
---------------------------
{red,orange,yellow,green}
(1 row)
SELECT enum_range('orange'::rainbow, NULL);
enum_range
-----------------------------------
{orange,yellow,green,blue,purple}
(1 row)
SELECT enum_range(NULL::rainbow, NULL);
enum_range
---------------------------------------
{red,orange,yellow,green,blue,purple}
(1 row)
--
-- User functions, can't test perl/python etc here since may not be compiled.
--
CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$
BEGIN
RETURN $1::text || 'omg';
END
$$ LANGUAGE plpgsql;
SELECT echo_me('red'::rainbow);
echo_me
---------
redomg
(1 row)
--
-- Concrete function should override generic one
--
CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$
BEGIN
RETURN $1::text || 'wtf';
END
$$ LANGUAGE plpgsql;
SELECT echo_me('red'::rainbow);
echo_me
---------
redwtf
(1 row)
--
-- If we drop the original generic one, we don't have to qualify the type
-- anymore, since there's only one match
--
DROP FUNCTION echo_me(anyenum);
SELECT echo_me('red');
echo_me
---------
redwtf
(1 row)
DROP FUNCTION echo_me(rainbow);
--
-- RI triggers on enum types
--
CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY);
CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent);
INSERT INTO enumtest_parent VALUES ('red');
INSERT INTO enumtest_child VALUES ('red');
INSERT INTO enumtest_child VALUES ('blue'); -- fail
ERROR: insert or update on table "enumtest_child" violates foreign key constraint "enumtest_child_parent_fkey"
DETAIL: Key (parent)=(blue) is not present in table "enumtest_parent".
DELETE FROM enumtest_parent; -- fail
ERROR: update or delete on table "enumtest_parent" violates foreign key constraint "enumtest_child_parent_fkey" on table "enumtest_child"
DETAIL: Key (id)=(red) is still referenced from table "enumtest_child".
--
-- cross-type RI should fail
--
CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly');
CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent);
ERROR: foreign key constraint "enumtest_bogus_child_parent_fkey" cannot be implemented
DETAIL: Key columns "parent" and "id" are of incompatible types: bogus and rainbow.
DROP TYPE bogus;
-- check renaming a value
ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson';
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'rainbow'::regtype
ORDER BY 2;
enumlabel | enumsortorder
-----------+---------------
crimson | 1
orange | 2
yellow | 3
green | 4
blue | 5
purple | 6
(6 rows)
-- check that renaming a non-existent value fails
ALTER TYPE rainbow RENAME VALUE 'red' TO 'crimson';
ERROR: "red" is not an existing enum label
-- check that renaming to an existent value fails
ALTER TYPE rainbow RENAME VALUE 'blue' TO 'green';
ERROR: enum label "green" already exists
--
-- check transactional behaviour of ALTER TYPE ... ADD VALUE
--
CREATE TYPE bogus AS ENUM('good');
-- check that we can add new values to existing enums in a transaction
-- but we can't use them
BEGIN;
ALTER TYPE bogus ADD VALUE 'new';
SAVEPOINT x;
SELECT 'new'::bogus; -- unsafe
ERROR: unsafe use of new value "new" of enum type bogus
LINE 1: SELECT 'new'::bogus;
^
HINT: New enum values must be committed before they can be used.
ROLLBACK TO x;
SELECT enum_first(null::bogus); -- safe
enum_first
------------
good
(1 row)
SELECT enum_last(null::bogus); -- unsafe
ERROR: unsafe use of new value "new" of enum type bogus
HINT: New enum values must be committed before they can be used.
ROLLBACK TO x;
SELECT enum_range(null::bogus); -- unsafe
ERROR: unsafe use of new value "new" of enum type bogus
HINT: New enum values must be committed before they can be used.
ROLLBACK TO x;
COMMIT;
SELECT 'new'::bogus; -- now safe
bogus
-------
new
(1 row)
SELECT enumlabel, enumsortorder
FROM pg_enum
WHERE enumtypid = 'bogus'::regtype
ORDER BY 2;
enumlabel | enumsortorder
-----------+---------------
good | 1
new | 2
(2 rows)
-- check that we recognize the case where the enum already existed but was
-- modified in the current txn; this should not be considered safe
BEGIN;
ALTER TYPE bogus RENAME TO bogon;
ALTER TYPE bogon ADD VALUE 'bad';
SELECT 'bad'::bogon;
ERROR: unsafe use of new value "bad" of enum type bogon
LINE 1: SELECT 'bad'::bogon;
^
HINT: New enum values must be committed before they can be used.
ROLLBACK;
-- but a renamed value is safe to use later in same transaction
BEGIN;
ALTER TYPE bogus RENAME VALUE 'good' to 'bad';
SELECT 'bad'::bogus;
bogus
-------
bad
(1 row)
ROLLBACK;
DROP TYPE bogus;
-- check that values created during CREATE TYPE can be used in any case
BEGIN;
CREATE TYPE bogus AS ENUM('good','bad','ugly');
ALTER TYPE bogus RENAME TO bogon;
select enum_range(null::bogon);
enum_range
-----------------
{good,bad,ugly}
(1 row)
ROLLBACK;
Allow more cases to pass the unsafe-use-of-new-enum-value restriction. Up to now we've rejected cases like BEGIN; CREATE TYPE rainbow AS ENUM (); ALTER TYPE rainbow ADD VALUE 'red'; -- use the value 'red', perhaps in a constraint or index COMMIT; The concern is that the uncommitted enum value 'red' might get into an index and then break the index if we roll back the ALTER ADD. If the ALTER is in the same transaction as the CREATE then it's really perfectly safe, but we weren't taking the trouble to identify that. pg_dump in binary-upgrade mode will emit enum definitions that look like the above, which up to now didn't fall foul of the unsafe-usage check because we processed each restore command as a separate transaction. However an upcoming patch proposes to bundle the restore commands into large transactions to reduce XID consumption during pg_upgrade, and that makes this behavior a problem. To fix, remember the OIDs of enum types created in the current transaction, and allow use of enum values that are added to one later in the same transaction. To do this fully correctly in the presence of subtransactions, we'd have to track subtransaction nesting level of the CREATE and do maintenance work at every subsequent subtransaction exit. That seems expensive, and we don't need it to satisfy pg_dump's usage. Hence, apply the additional optimization only when the CREATE and ALTER are at outermost transaction level. Patch by me, reviewed by Andrew Dunstan Discussion: https://postgr.es/m/1548468.1711220438@sss.pgh.pa.us
2024-03-24 19:30:29 +01:00
-- we must allow this usage to support pg_dump in binary upgrade mode
BEGIN;
CREATE TYPE bogus AS ENUM('good');
ALTER TYPE bogus RENAME TO bogon;
ALTER TYPE bogon ADD VALUE 'bad';
ALTER TYPE bogon ADD VALUE 'ugly';
Allow more cases to pass the unsafe-use-of-new-enum-value restriction. Up to now we've rejected cases like BEGIN; CREATE TYPE rainbow AS ENUM (); ALTER TYPE rainbow ADD VALUE 'red'; -- use the value 'red', perhaps in a constraint or index COMMIT; The concern is that the uncommitted enum value 'red' might get into an index and then break the index if we roll back the ALTER ADD. If the ALTER is in the same transaction as the CREATE then it's really perfectly safe, but we weren't taking the trouble to identify that. pg_dump in binary-upgrade mode will emit enum definitions that look like the above, which up to now didn't fall foul of the unsafe-usage check because we processed each restore command as a separate transaction. However an upcoming patch proposes to bundle the restore commands into large transactions to reduce XID consumption during pg_upgrade, and that makes this behavior a problem. To fix, remember the OIDs of enum types created in the current transaction, and allow use of enum values that are added to one later in the same transaction. To do this fully correctly in the presence of subtransactions, we'd have to track subtransaction nesting level of the CREATE and do maintenance work at every subsequent subtransaction exit. That seems expensive, and we don't need it to satisfy pg_dump's usage. Hence, apply the additional optimization only when the CREATE and ALTER are at outermost transaction level. Patch by me, reviewed by Andrew Dunstan Discussion: https://postgr.es/m/1548468.1711220438@sss.pgh.pa.us
2024-03-24 19:30:29 +01:00
select enum_range(null::bogon);
enum_range
-----------------
{good,bad,ugly}
(1 row)
ROLLBACK;
--
-- Cleanup
--
DROP TABLE enumtest_child;
DROP TABLE enumtest_parent;
DROP TABLE enumtest;
DROP TYPE rainbow;
--
-- Verify properly cleaned up
--
SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
count
-------
0
(1 row)
SELECT * FROM pg_enum WHERE NOT EXISTS
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
Remove WITH OIDS support, change oid catalog column visibility. Previously tables declared WITH OIDS, including a significant fraction of the catalog tables, stored the oid column not as a normal column, but as part of the tuple header. This special column was not shown by default, which was somewhat odd, as it's often (consider e.g. pg_class.oid) one of the more important parts of a row. Neither pg_dump nor COPY included the contents of the oid column by default. The fact that the oid column was not an ordinary column necessitated a significant amount of special case code to support oid columns. That already was painful for the existing, but upcoming work aiming to make table storage pluggable, would have required expanding and duplicating that "specialness" significantly. WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0). Remove it. Removing includes: - CREATE TABLE and ALTER TABLE syntax for declaring the table to be WITH OIDS has been removed (WITH (oids[ = true]) will error out) - pg_dump does not support dumping tables declared WITH OIDS and will issue a warning when dumping one (and ignore the oid column). - restoring an pg_dump archive with pg_restore will warn when restoring a table with oid contents (and ignore the oid column) - COPY will refuse to load binary dump that includes oids. - pg_upgrade will error out when encountering tables declared WITH OIDS, they have to be altered to remove the oid column first. - Functionality to access the oid of the last inserted row (like plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed. The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false) for CREATE TABLE) is still supported. While that requires a bit of support code, it seems unnecessary to break applications / dumps that do not use oids, and are explicit about not using them. The biggest user of WITH OID columns was postgres' catalog. This commit changes all 'magic' oid columns to be columns that are normally declared and stored. To reduce unnecessary query breakage all the newly added columns are still named 'oid', even if a table's column naming scheme would indicate 'reloid' or such. This obviously requires adapting a lot code, mostly replacing oid access via HeapTupleGetOid() with access to the underlying Form_pg_*->oid column. The bootstrap process now assigns oids for all oid columns in genbki.pl that do not have an explicit value (starting at the largest oid previously used), only oids assigned later by oids will be above FirstBootstrapObjectId. As the oid column now is a normal column the special bootstrap syntax for oids has been removed. Oids are not automatically assigned during insertion anymore, all backend code explicitly assigns oids with GetNewOidWithIndex(). For the rare case that insertions into the catalog via SQL are called for the new pg_nextoid() function can be used (which only works on catalog tables). The fact that oid columns on system tables are now normal columns means that they will be included in the set of columns expanded by * (i.e. SELECT * FROM pg_class will now include the table's oid, previously it did not). It'd not technically be hard to hide oid column by default, but that'd mean confusing behavior would either have to be carried forward forever, or it'd cause breakage down the line. While it's not unlikely that further adjustments are needed, the scope/invasiveness of the patch makes it worthwhile to get merge this now. It's painful to maintain externally, too complicated to commit after the code code freeze, and a dependency of a number of other patches. Catversion bump, for obvious reasons. Author: Andres Freund, with contributions by John Naylor Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
oid | enumtypid | enumsortorder | enumlabel
-----+-----------+---------------+-----------
(0 rows)