postgresql/src/test/regress/sql/create_type.sql

300 lines
8.2 KiB
SQL

--
-- CREATE_TYPE
--
-- directory path and dlsuffix are passed to us in environment variables
\getenv libdir PG_LIBDIR
\getenv dlsuffix PG_DLSUFFIX
\set regresslib :libdir '/regress' :dlsuffix
--
-- Test the "old style" approach of making the I/O functions first,
-- with no explicit shell type creation.
--
CREATE FUNCTION widget_in(cstring)
RETURNS widget
AS :'regresslib'
LANGUAGE C STRICT IMMUTABLE;
CREATE FUNCTION widget_out(widget)
RETURNS cstring
AS :'regresslib'
LANGUAGE C STRICT IMMUTABLE;
CREATE FUNCTION int44in(cstring)
RETURNS city_budget
AS :'regresslib'
LANGUAGE C STRICT IMMUTABLE;
CREATE FUNCTION int44out(city_budget)
RETURNS cstring
AS :'regresslib'
LANGUAGE C STRICT IMMUTABLE;
CREATE TYPE widget (
internallength = 24,
input = widget_in,
output = widget_out,
typmod_in = numerictypmodin,
typmod_out = numerictypmodout,
alignment = double
);
CREATE TYPE city_budget (
internallength = 16,
input = int44in,
output = int44out,
element = int4,
category = 'x', -- just to verify the system will take it
preferred = true -- ditto
);
-- Test creation and destruction of shell types
CREATE TYPE shell;
CREATE TYPE shell; -- fail, type already present
DROP TYPE shell;
DROP TYPE shell; -- fail, type not exist
-- also, let's leave one around for purposes of pg_dump testing
CREATE TYPE myshell;
--
-- Test type-related default values (broken in releases before PG 7.2)
--
-- This part of the test also exercises the "new style" approach of making
-- a shell type and then filling it in.
--
CREATE TYPE int42;
CREATE TYPE text_w_default;
-- Make dummy I/O routines using the existing internal support for int4, text
CREATE FUNCTION int42_in(cstring)
RETURNS int42
AS 'int4in'
LANGUAGE internal STRICT IMMUTABLE;
CREATE FUNCTION int42_out(int42)
RETURNS cstring
AS 'int4out'
LANGUAGE internal STRICT IMMUTABLE;
CREATE FUNCTION text_w_default_in(cstring)
RETURNS text_w_default
AS 'textin'
LANGUAGE internal STRICT IMMUTABLE;
CREATE FUNCTION text_w_default_out(text_w_default)
RETURNS cstring
AS 'textout'
LANGUAGE internal STRICT IMMUTABLE;
CREATE TYPE int42 (
internallength = 4,
input = int42_in,
output = int42_out,
alignment = int4,
default = 42,
passedbyvalue
);
CREATE TYPE text_w_default (
internallength = variable,
input = text_w_default_in,
output = text_w_default_out,
alignment = int4,
default = 'zippo'
);
CREATE TABLE default_test (f1 text_w_default, f2 int42);
INSERT INTO default_test DEFAULT VALUES;
SELECT * FROM default_test;
-- We need a shell type to test some CREATE TYPE failure cases with
CREATE TYPE bogus_type;
-- invalid: non-lowercase quoted identifiers
CREATE TYPE bogus_type (
"Internallength" = 4,
"Input" = int42_in,
"Output" = int42_out,
"Alignment" = int4,
"Default" = 42,
"Passedbyvalue"
);
-- invalid: input/output function incompatibility
CREATE TYPE bogus_type (INPUT = array_in,
OUTPUT = array_out,
ELEMENT = int,
INTERNALLENGTH = 32);
DROP TYPE bogus_type;
-- It no longer is possible to issue CREATE TYPE without making a shell first
CREATE TYPE bogus_type (INPUT = array_in,
OUTPUT = array_out,
ELEMENT = int,
INTERNALLENGTH = 32);
-- Test stand-alone composite type
CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
SELECT * FROM default_test;
' LANGUAGE SQL;
SELECT * FROM get_default_test();
-- Test comments
COMMENT ON TYPE bad IS 'bad comment';
COMMENT ON TYPE default_test_row IS 'good comment';
COMMENT ON TYPE default_test_row IS NULL;
COMMENT ON COLUMN default_test_row.nope IS 'bad comment';
COMMENT ON COLUMN default_test_row.f1 IS 'good comment';
COMMENT ON COLUMN default_test_row.f1 IS NULL;
-- Check shell type create for existing types
CREATE TYPE text_w_default; -- should fail
DROP TYPE default_test_row CASCADE;
DROP TABLE default_test;
-- Check dependencies are established when creating a new type
CREATE TYPE base_type;
CREATE FUNCTION base_fn_in(cstring) RETURNS base_type AS 'boolin'
LANGUAGE internal IMMUTABLE STRICT;
CREATE FUNCTION base_fn_out(base_type) RETURNS cstring AS 'boolout'
LANGUAGE internal IMMUTABLE STRICT;
CREATE TYPE base_type(INPUT = base_fn_in, OUTPUT = base_fn_out);
DROP FUNCTION base_fn_in(cstring); -- error
DROP FUNCTION base_fn_out(base_type); -- error
DROP TYPE base_type; -- error
DROP TYPE base_type CASCADE;
-- Check usage of typmod with a user-defined type
-- (we have borrowed numeric's typmod functions)
CREATE TEMP TABLE mytab (foo widget(42,13,7)); -- should fail
CREATE TEMP TABLE mytab (foo widget(42,13));
SELECT format_type(atttypid,atttypmod) FROM pg_attribute
WHERE attrelid = 'mytab'::regclass AND attnum > 0;
-- might as well exercise the widget type while we're here
INSERT INTO mytab VALUES ('(1,2,3)'), ('(-44,5.5,12)');
TABLE mytab;
-- and test format_type() a bit more, too
select format_type('varchar'::regtype, 42);
select format_type('bpchar'::regtype, null);
-- this behavior difference is intentional
select format_type('bpchar'::regtype, -1);
-- Test non-error-throwing APIs using widget, which still throws errors
SELECT pg_input_is_valid('(1,2,3)', 'widget');
SELECT pg_input_is_valid('(1,2)', 'widget'); -- hard error expected
SELECT pg_input_is_valid('{"(1,2,3)"}', 'widget[]');
SELECT pg_input_is_valid('{"(1,2)"}', 'widget[]'); -- hard error expected
SELECT pg_input_is_valid('("(1,2,3)")', 'mytab');
SELECT pg_input_is_valid('("(1,2)")', 'mytab'); -- hard error expected
-- Test creation of an operator over a user-defined type
CREATE FUNCTION pt_in_widget(point, widget)
RETURNS bool
AS :'regresslib'
LANGUAGE C STRICT;
CREATE OPERATOR <% (
leftarg = point,
rightarg = widget,
procedure = pt_in_widget,
commutator = >% ,
negator = >=%
);
SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
point '(1,2)' <% widget '(0,0,1)' AS f;
-- exercise city_budget type
CREATE TABLE city (
name name,
location box,
budget city_budget
);
INSERT INTO city VALUES
('Podunk', '(1,2),(3,4)', '100,127,1000'),
('Gotham', '(1000,34),(1100,334)', '123456,127,-1000,6789');
TABLE city;
--
-- Test CREATE/ALTER TYPE using a type that's compatible with varchar,
-- so we can re-use those support functions
--
CREATE TYPE myvarchar;
CREATE FUNCTION myvarcharin(cstring, oid, integer) RETURNS myvarchar
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'varcharin';
CREATE FUNCTION myvarcharout(myvarchar) RETURNS cstring
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'varcharout';
CREATE FUNCTION myvarcharsend(myvarchar) RETURNS bytea
LANGUAGE internal STABLE PARALLEL SAFE STRICT AS 'varcharsend';
CREATE FUNCTION myvarcharrecv(internal, oid, integer) RETURNS myvarchar
LANGUAGE internal STABLE PARALLEL SAFE STRICT AS 'varcharrecv';
-- fail, it's still a shell:
ALTER TYPE myvarchar SET (storage = extended);
CREATE TYPE myvarchar (
input = myvarcharin,
output = myvarcharout,
alignment = integer,
storage = main
);
-- want to check updating of a domain over the target type, too
CREATE DOMAIN myvarchardom AS myvarchar;
ALTER TYPE myvarchar SET (storage = plain); -- not allowed
ALTER TYPE myvarchar SET (storage = extended);
ALTER TYPE myvarchar SET (
send = myvarcharsend,
receive = myvarcharrecv,
typmod_in = varchartypmodin,
typmod_out = varchartypmodout,
-- these are bogus, but it's safe as long as we don't use the type:
analyze = ts_typanalyze,
subscript = raw_array_subscript_handler
);
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchar';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchar';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchardom';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchardom';
-- ensure dependencies are straight
DROP FUNCTION myvarcharsend(myvarchar); -- fail
DROP TYPE myvarchar; -- fail
DROP TYPE myvarchar CASCADE;