据我所知,目前在 PostgreSQL v9.5(及更高版本?)中不可能获得自定义范围类型的隐式转换。为了说明,当我定义以下演示设置时:
drop schema if exists tac cascade;
create schema tac;
create domain tac.usascii_small_letters
as text
check ( value ~ '^[a-z]+$' );
create domain tac.ucid as integer
check ( value between x'0'::integer and x'10ffff'::integer );
create function tac.ucid_rng_diff( cid_1 tac.ucid, date_2 tac.ucid )
returns double precision
language sql
immutable
as $$
select cast( cid_1 - date_2 as double precision );
$$;
-- A ----------------------------------------------------------------
create type tac.ucid_rng as range (
subtype = tac.ucid,
subtype_diff = tac.ucid_rng_diff
);
-- ----------------------------------------------------------------
-- -- B ----------------------------------------------------------------
-- set role dba; -- !!!!!!!!!!!!!!!!!!!!
-- create type tac.ucid_rng;
-- create function tac.ucid_rng_canonical( x tac.ucid_rng )
-- returns tac.ucid_rng
-- language plpgsql
-- as $$
-- begin
-- if not lower_inc(x) then
-- x := tac.ucid_rng(lower(x) + 1, upper(x), '[]');
-- end if;
-- if not upper_inc(x) then
-- x := tac.ucid_rng(lower(x), upper(x) - 1, '[]');
-- end if;
-- return x;
-- end;
-- $$;
-- create type tac.ucid_rng as range (
-- subtype = tac.ucid
-- , subtype_diff = tac.ucid_rng_diff
-- , canonical = tac.ucid_rng_canonical
-- );
-- reset role; -- !!!!!!!!!!!!!!!!!!!!
-- -- ----------------------------------------------------------------
create table tac.words (
word tac.usascii_small_letters,
cid tac.ucid,
cid_range tac.ucid_rng
);
insert into tac.words values
( 'foo', 1, '[11,21]' ),
( 'bar', 2, '[12,22]' ),
( 'zip', 3, '[13,23]' ),
( 'dat', 4, '[14,24]' ),
( 'baz', 5, '[15,25]' );
select * from tac.words where word between 'a' and 'c';
select * from tac.words where word between 'a'::tac.usascii_small_letters and 'c';
select * from tac.words where cid between 3 and 5;
select * from tac.words where cid_range @> '[17,23]';
select * from tac.words where cid_range @> '[17,23]'::tac.ucid_rng;
select * from tac.words where cid_range @> 23::tac.ucid;
select * from tac.words where cid_range @> 23;
并像使用块 A 但没有块 B 一样运行它,我得到
ERROR: operator does not exist: integer <@ tac.ucid_rng
LINE 1: select * from tac.words where cid_range @> 23;
HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
测试表明,很多使用自定义域/类型的东西都适用于隐式转换。事实上,我可以对我的自定义字符串数据类型进行范围检查,甚至cid_range @> '[17,23]'
可以工作。只是神秘地失败了。 cid_range @> 23
现在我尝试为我的tac.cid_rng
类型实现一个合适的规范化函数;然而,正如https://stackoverflow.com/a/29939205中所说,那是行不通的:
NOTICE: argument type tac.ucid_rng is only a shell
NOTICE: return type tac.ucid_rng is only a shell
ERROR: PL/pgSQL functions cannot return type tac.ucid_rng
因此,在 PostgreSQL 9.5 中似乎仍然不可能在点包含查询中使用隐式转换的自定义范围类型,尽管类型本身是在一组连续整数上非常透明地定义的,并且类似的查询无需任何额外编码即可正常工作。
我想知道我是否在这里遗漏了什么,以及有什么推荐的方法来处理这种情况。我想我可以接受显式强制转换,或者编写一个函数来为我做这件事。
我不明白的是为什么cid_range @> '[17,23]'
有效但cid_range @> 23
失败了——毕竟,在这两种情况下都必须执行从通用/暗示文字到实际数据类型的转换。从测试中可以看出,从整数文字到tac.ucid
域的隐式转换也可以工作,因此很难看出究竟缺少什么需要一段 C 样板代码,它除了通用检查和整数递增/递减外什么都不做。
我认为这是一个错误,您应该报告它。我认为它可以更容易地证明。
我认为你想要的是完全合理的。我认为这不会成为高优先级项目。我的假设是触发的强制不是查看范围是否超过域,它应该。
随时用我的例子更新你的问题,我认为人们会更容易理解