-- -- Test domains. -- -- Test Comment / Drop create domain domaindroptest int4; comment on domain domaindroptest is 'About to drop this..'; create domain dependenttypetest domaindroptest; -- fail because of dependent type drop domain domaindroptest; ERROR: cannot drop type domaindroptest because other objects depend on it DETAIL: type dependenttypetest depends on type domaindroptest HINT: Use DROP ... CASCADE to drop the dependent objects too. drop domain domaindroptest cascade; NOTICE: drop cascades to type dependenttypetest -- this should fail because already gone drop domain domaindroptest cascade; ERROR: type "domaindroptest" does not exist -- Test domain input. -- Note: the point of checking both INSERT and COPY FROM is that INSERT -- exercises CoerceToDomain while COPY exercises domain_in. create domain domainvarchar varchar(5); create domain domainnumeric numeric(8,2); create domain domainint4 int4; create domain domaintext text; -- Test explicit coercions --- these should succeed (and truncate) SELECT cast('123456' as domainvarchar); domainvarchar --------------- 12345 (1 row) SELECT cast('12345' as domainvarchar); domainvarchar --------------- 12345 (1 row) -- Test tables using domains create table basictest ( testint4 domainint4 , testtext domaintext , testvarchar domainvarchar , testnumeric domainnumeric ); INSERT INTO basictest values ('88', 'haha', 'short', '123.12'); -- Good INSERT INTO basictest values ('88', 'haha', 'short text', '123.12'); -- Bad varchar ERROR: value too long for type character varying(5) INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate numeric -- Test copy COPY basictest (testvarchar) FROM stdin; -- fail ERROR: value too long for type character varying(5) CONTEXT: COPY basictest, line 1, column testvarchar: "notsoshorttext" COPY basictest (testvarchar) FROM stdin; select * from basictest; testint4 | testtext | testvarchar | testnumeric ----------+----------+-------------+------------- 88 | haha | short | 123.12 88 | haha | short | 123.12 | | short | (3 rows) -- check that domains inherit operations from base types select testtext || testvarchar as concat, testnumeric + 42 as sum from basictest; concat | sum -----------+-------- hahashort | 165.12 hahashort | 165.12 | (3 rows) -- check that union/case/coalesce type resolution handles domains properly select coalesce(4::domainint4, 7) is of (int4) as t; t --- t (1 row) select coalesce(4::domainint4, 7) is of (domainint4) as f; f --- f (1 row) select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; t --- t (1 row) drop table basictest; drop domain domainvarchar restrict; drop domain domainnumeric restrict; drop domain domainint4 restrict; drop domain domaintext; -- Test domains over array types create domain domainint4arr int4[1]; create domain domainchar4arr varchar(4)[2][3]; create table domarrtest ( testint4arr domainint4arr , testchar4arr domainchar4arr ); INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"}}'); INSERT INTO domarrtest values ('{{2,2},{2,2}}', '{{"a","b"}}'); INSERT INTO domarrtest values ('{2,2}', '{{"a","b"},{"c","d"},{"e","f"}}'); INSERT INTO domarrtest values ('{2,2}', '{{"a"},{"c"}}'); INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}'); ERROR: value too long for type character varying(4) select * from domarrtest; testint4arr | testchar4arr ---------------+--------------------- {2,2} | {{a,b},{c,d}} {{2,2},{2,2}} | {{a,b}} {2,2} | {{a,b},{c,d},{e,f}} {2,2} | {{a},{c}} | {{a,b,c},{d,e,f}} (5 rows) select testint4arr[1], testchar4arr[2:2] from domarrtest; testint4arr | testchar4arr -------------+-------------- 2 | {{c,d}} | {} 2 | {{c,d}} 2 | {{c}} | {{d,e,f}} (5 rows) select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; array_dims | array_dims ------------+------------ [1:2] | [1:2][1:2] [1:2][1:2] | [1:1][1:2] [1:2] | [1:3][1:2] [1:2] | [1:2][1:1] | [1:2][1:3] (5 rows) COPY domarrtest FROM stdin; COPY domarrtest FROM stdin; -- fail ERROR: value too long for type character varying(4) CONTEXT: COPY domarrtest, line 1, column testchar4arr: "{qwerty,w,e}" select * from domarrtest; testint4arr | testchar4arr ---------------+--------------------- {2,2} | {{a,b},{c,d}} {{2,2},{2,2}} | {{a,b}} {2,2} | {{a,b},{c,d},{e,f}} {2,2} | {{a},{c}} | {{a,b,c},{d,e,f}} {3,4} | {q,w,e} | (7 rows) drop table domarrtest; drop domain domainint4arr restrict; drop domain domainchar4arr restrict; create domain dia as int[]; select '{1,2,3}'::dia; dia --------- {1,2,3} (1 row) select array_dims('{1,2,3}'::dia); array_dims ------------ [1:3] (1 row) select pg_typeof('{1,2,3}'::dia); pg_typeof ----------- dia (1 row) select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia pg_typeof ----------- integer[] (1 row) drop domain dia; create domain dnotnull varchar(15) NOT NULL; create domain dnull varchar(15); create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd'); create table nulltest ( col1 dnotnull , col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col3 dnull NOT NULL , col4 dnull , col5 dcheck CHECK (col5 IN ('c', 'd')) ); INSERT INTO nulltest DEFAULT VALUES; ERROR: domain dnotnull does not allow null values INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good insert into nulltest values ('a', 'b', 'c', 'd', NULL); ERROR: domain dcheck does not allow null values insert into nulltest values ('a', 'b', 'c', 'd', 'a'); ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd'); ERROR: domain dnotnull does not allow null values INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c'); ERROR: domain dnotnull does not allow null values INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c'); ERROR: null value in column "col3" violates not-null constraint INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good -- Test copy COPY nulltest FROM stdin; --fail ERROR: null value in column "col3" violates not-null constraint CONTEXT: COPY nulltest, line 1: "a b \N d d" COPY nulltest FROM stdin; --fail ERROR: domain dcheck does not allow null values CONTEXT: COPY nulltest, line 1, column col5: null input -- Last row is bad COPY nulltest FROM stdin; ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" CONTEXT: COPY nulltest, line 3: "a b c \N a" select * from nulltest; col1 | col2 | col3 | col4 | col5 ------+------+------+------+------ a | b | c | d | c a | b | c | | d (2 rows) -- Test out coerced (casted) constraints SELECT cast('1' as dnotnull); dnotnull ---------- 1 (1 row) SELECT cast(NULL as dnotnull); -- fail ERROR: domain dnotnull does not allow null values SELECT cast(cast(NULL as dnull) as dnotnull); -- fail ERROR: domain dnotnull does not allow null values SELECT cast(col4 as dnotnull) from nulltest; -- fail ERROR: domain dnotnull does not allow null values -- cleanup drop table nulltest; drop domain dnotnull restrict; drop domain dnull restrict; drop domain dcheck restrict; create domain ddef1 int4 DEFAULT 3; create domain ddef2 oid DEFAULT '12'; -- Type mixing, function returns int8 create domain ddef3 text DEFAULT 5; create sequence ddef4_seq; create domain ddef4 int4 DEFAULT nextval('ddef4_seq'); create domain ddef5 numeric(8,2) NOT NULL DEFAULT '12.12'; create table defaulttest ( col1 ddef1 , col2 ddef2 , col3 ddef3 , col4 ddef4 PRIMARY KEY , col5 ddef1 NOT NULL DEFAULT NULL , col6 ddef2 DEFAULT '88' , col7 ddef4 DEFAULT 8000 , col8 ddef5 ); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "defaulttest_pkey" for table "defaulttest" insert into defaulttest(col4) values(0); -- fails, col5 defaults to null ERROR: null value in column "col5" violates not-null constraint alter table defaulttest alter column col5 drop default; insert into defaulttest default values; -- succeeds, inserts domain default -- We used to treat SET DEFAULT NULL as equivalent to DROP DEFAULT; wrong alter table defaulttest alter column col5 set default null; insert into defaulttest(col4) values(0); -- fails ERROR: null value in column "col5" violates not-null constraint alter table defaulttest alter column col5 drop default; insert into defaulttest default values; insert into defaulttest default values; -- Test defaults with copy COPY defaulttest(col5) FROM stdin; select * from defaulttest; col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 ------+------+------+------+------+------+------+------- 3 | 12 | 5 | 1 | 3 | 88 | 8000 | 12.12 3 | 12 | 5 | 2 | 3 | 88 | 8000 | 12.12 3 | 12 | 5 | 3 | 3 | 88 | 8000 | 12.12 3 | 12 | 5 | 4 | 42 | 88 | 8000 | 12.12 (4 rows) drop table defaulttest cascade; -- Test ALTER DOMAIN .. NOT NULL create domain dnotnulltest integer; create table domnotnull ( col1 dnotnulltest , col2 dnotnulltest ); insert into domnotnull default values; alter domain dnotnulltest set not null; -- fails ERROR: column "col1" of table "domnotnull" contains null values update domnotnull set col1 = 5; alter domain dnotnulltest set not null; -- fails ERROR: column "col2" of table "domnotnull" contains null values update domnotnull set col2 = 6; alter domain dnotnulltest set not null; update domnotnull set col1 = null; -- fails ERROR: domain dnotnulltest does not allow null values alter domain dnotnulltest drop not null; update domnotnull set col1 = null; drop domain dnotnulltest cascade; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table domnotnull column col1 drop cascades to table domnotnull column col2 -- Test ALTER DOMAIN .. DEFAULT .. create table domdeftest (col1 ddef1); insert into domdeftest default values; select * from domdeftest; col1 ------ 3 (1 row) alter domain ddef1 set default '42'; insert into domdeftest default values; select * from domdeftest; col1 ------ 3 42 (2 rows) alter domain ddef1 drop default; insert into domdeftest default values; select * from domdeftest; col1 ------ 3 42 (3 rows) drop table domdeftest; -- Test ALTER DOMAIN .. CONSTRAINT .. create domain con as integer; create table domcontest (col1 con); insert into domcontest values (1); insert into domcontest values (2); alter domain con add constraint t check (VALUE < 1); -- fails ERROR: column "col1" of table "domcontest" contains values that violate the new constraint alter domain con add constraint t check (VALUE < 34); alter domain con add check (VALUE > 0); insert into domcontest values (-5); -- fails ERROR: value for domain con violates check constraint "con_check" insert into domcontest values (42); -- fails ERROR: value for domain con violates check constraint "t" insert into domcontest values (5); alter domain con drop constraint t; insert into domcontest values (-5); --fails ERROR: value for domain con violates check constraint "con_check" insert into domcontest values (42); -- Test ALTER DOMAIN .. CONSTRAINT .. NOT VALID create domain things AS INT; CREATE TABLE thethings (stuff things); INSERT INTO thethings (stuff) VALUES (55); ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11); ERROR: column "stuff" of table "thethings" contains values that violate the new constraint ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11) NOT VALID; ALTER DOMAIN things VALIDATE CONSTRAINT meow; ERROR: column "stuff" of table "thethings" contains values that violate the new constraint UPDATE thethings SET stuff = 10; ALTER DOMAIN things VALIDATE CONSTRAINT meow; -- Confirm ALTER DOMAIN with RULES. create table domtab (col1 integer); create domain dom as integer; create view domview as select cast(col1 as dom) from domtab; insert into domtab (col1) values (null); insert into domtab (col1) values (5); select * from domview; col1 ------ 5 (2 rows) alter domain dom set not null; select * from domview; -- fail ERROR: domain dom does not allow null values alter domain dom drop not null; select * from domview; col1 ------ 5 (2 rows) alter domain dom add constraint domchkgt6 check(value > 6); select * from domview; --fail ERROR: value for domain dom violates check constraint "domchkgt6" alter domain dom drop constraint domchkgt6 restrict; select * from domview; col1 ------ 5 (2 rows) -- cleanup drop domain ddef1 restrict; drop domain ddef2 restrict; drop domain ddef3 restrict; drop domain ddef4 restrict; drop domain ddef5 restrict; drop sequence ddef4_seq; -- Test domains over domains create domain vchar4 varchar(4); create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x'); create domain dtop dinter check (substring(VALUE, 2, 1) = '1'); select 'x123'::dtop; dtop ------ x123 (1 row) select 'x1234'::dtop; -- explicit coercion should truncate dtop ------ x123 (1 row) select 'y1234'::dtop; -- fail ERROR: value for domain dtop violates check constraint "dinter_check" select 'y123'::dtop; -- fail ERROR: value for domain dtop violates check constraint "dinter_check" select 'yz23'::dtop; -- fail ERROR: value for domain dtop violates check constraint "dinter_check" select 'xz23'::dtop; -- fail ERROR: value for domain dtop violates check constraint "dtop_check" create temp table dtest(f1 dtop); insert into dtest values('x123'); insert into dtest values('x1234'); -- fail, implicit coercion ERROR: value too long for type character varying(4) insert into dtest values('y1234'); -- fail, implicit coercion ERROR: value too long for type character varying(4) insert into dtest values('y123'); -- fail ERROR: value for domain dtop violates check constraint "dinter_check" insert into dtest values('yz23'); -- fail ERROR: value for domain dtop violates check constraint "dinter_check" insert into dtest values('xz23'); -- fail ERROR: value for domain dtop violates check constraint "dtop_check" drop table dtest; drop domain vchar4 cascade; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to type dinter drop cascades to type dtop -- Make sure that constraints of newly-added domain columns are -- enforced correctly, even if there's no default value for the new -- column. Per bug #1433 create domain str_domain as text not null; create table domain_test (a int, b int); insert into domain_test values (1, 2); insert into domain_test values (1, 2); -- should fail alter table domain_test add column c str_domain; ERROR: domain str_domain does not allow null values create domain str_domain2 as text check (value <> 'foo') default 'foo'; -- should fail alter table domain_test add column d str_domain2; ERROR: value for domain str_domain2 violates check constraint "str_domain2_check" -- Check that domain constraints on prepared statement parameters of -- unknown type are enforced correctly. create domain pos_int as int4 check (value > 0) not null; prepare s1 as select $1::pos_int = 10 as "is_ten"; execute s1(10); is_ten -------- t (1 row) execute s1(0); -- should fail ERROR: value for domain pos_int violates check constraint "pos_int_check" execute s1(NULL); -- should fail ERROR: domain pos_int does not allow null values -- Check that domain constraints on plpgsql function parameters, results, -- and local variables are enforced correctly. create function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int; begin return p1; end$$ language plpgsql; select doubledecrement(3); -- fail because of implicit null assignment ERROR: domain pos_int does not allow null values CONTEXT: PL/pgSQL function "doubledecrement" line 3 during statement block local variable initialization create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int := 0; begin return p1; end$$ language plpgsql; select doubledecrement(3); -- fail at initialization assignment ERROR: value for domain pos_int violates check constraint "pos_int_check" CONTEXT: PL/pgSQL function "doubledecrement" line 3 during statement block local variable initialization create or replace function doubledecrement(p1 pos_int) returns pos_int as $$ declare v pos_int := 1; begin v := p1 - 1; return v - 1; end$$ language plpgsql; select doubledecrement(null); -- fail before call ERROR: domain pos_int does not allow null values select doubledecrement(0); -- fail before call ERROR: value for domain pos_int violates check constraint "pos_int_check" select doubledecrement(1); -- fail at assignment to v ERROR: value for domain pos_int violates check constraint "pos_int_check" CONTEXT: PL/pgSQL function "doubledecrement" line 4 at assignment select doubledecrement(2); -- fail at return ERROR: value for domain pos_int violates check constraint "pos_int_check" CONTEXT: PL/pgSQL function "doubledecrement" while casting return value to function's return type select doubledecrement(3); -- good doubledecrement ----------------- 1 (1 row) -- Check that ALTER DOMAIN tests columns of derived types create domain posint as int4; -- Currently, this doesn't work for composite types, but verify it complains create type ddtest1 as (f1 posint); create table ddtest2(f1 ddtest1); insert into ddtest2 values(row(-1)); alter domain posint add constraint c1 check(value >= 0); ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it drop table ddtest2; create table ddtest2(f1 ddtest1[]); insert into ddtest2 values('{(-1)}'); alter domain posint add constraint c1 check(value >= 0); ERROR: cannot alter type "posint" because column "ddtest2.f1" uses it drop table ddtest2; alter domain posint add constraint c1 check(value >= 0); create domain posint2 as posint check (value % 2 = 0); create table ddtest2(f1 posint2); insert into ddtest2 values(11); -- fail ERROR: value for domain posint2 violates check constraint "posint2_check" insert into ddtest2 values(-2); -- fail ERROR: value for domain posint2 violates check constraint "c1" insert into ddtest2 values(2); alter domain posint add constraint c2 check(value >= 10); -- fail ERROR: column "f1" of table "ddtest2" contains values that violate the new constraint alter domain posint add constraint c2 check(value > 0); -- OK drop table ddtest2; drop type ddtest1; drop domain posint cascade; NOTICE: drop cascades to type posint2 -- -- Check enforcement of domain-related typmod in plpgsql (bug #5717) -- create or replace function array_elem_check(numeric) returns numeric as $$ declare x numeric(4,2)[1]; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); ERROR: numeric field overflow DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. CONTEXT: PL/pgSQL function "array_elem_check" line 5 at assignment select array_elem_check(1.23456); array_elem_check ------------------ 1.23 (1 row) create domain mynums as numeric(4,2)[1]; create or replace function array_elem_check(numeric) returns numeric as $$ declare x mynums; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); ERROR: numeric field overflow DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. CONTEXT: PL/pgSQL function "array_elem_check" line 5 at assignment select array_elem_check(1.23456); array_elem_check ------------------ 1.23 (1 row) create domain mynums2 as mynums; create or replace function array_elem_check(numeric) returns numeric as $$ declare x mynums2; begin x[1] := $1; return x[1]; end$$ language plpgsql; select array_elem_check(121.00); ERROR: numeric field overflow DETAIL: A field with precision 4, scale 2 must round to an absolute value less than 10^2. CONTEXT: PL/pgSQL function "array_elem_check" line 5 at assignment select array_elem_check(1.23456); array_elem_check ------------------ 1.23 (1 row) drop function array_elem_check(numeric); -- -- Check enforcement of array-level domain constraints -- create domain orderedpair as int[2] check (value[1] < value[2]); select array[1,2]::orderedpair; array ------- {1,2} (1 row) select array[2,1]::orderedpair; -- fail ERROR: value for domain orderedpair violates check constraint "orderedpair_check" create temp table op (f1 orderedpair); insert into op values (array[1,2]); insert into op values (array[2,1]); -- fail ERROR: value for domain orderedpair violates check constraint "orderedpair_check" update op set f1[2] = 3; update op set f1[2] = 0; -- fail ERROR: value for domain orderedpair violates check constraint "orderedpair_check" select * from op; f1 ------- {1,3} (1 row) create or replace function array_elem_check(int) returns int as $$ declare x orderedpair := '{1,2}'; begin x[2] := $1; return x[2]; end$$ language plpgsql; select array_elem_check(3); array_elem_check ------------------ 3 (1 row) select array_elem_check(-1); ERROR: value for domain orderedpair violates check constraint "orderedpair_check" CONTEXT: PL/pgSQL function "array_elem_check" line 5 at assignment drop function array_elem_check(int);