在postgres中从复合类型转换为复合类型

时间:2014-09-11 11:44:56

标签: sql postgresql casting composite

我在postgres中创建了一个复合类型:

CREATE TYPE mytimestamp AS (t timestamp with time zone, o int);

并将其添加到表格

CREATE TABLE t (t0 mytimestamp)

使用自定义转换功能和以下CAST,我能够将带有时区的简单时间戳转换为' mytimestamp'在INSERT语句中:

CREATE CAST (timestamp with time zone AS mytimestamp) 
WITH FUNCTION to_mytimestamp(timestamp with time zone) AS ASSIGNMENT;

INSERT INTO t (t0) VALUES(now()); -- works as intended

但是,我似乎无法创建一个使用以下语句的CAST:

CREATE CAST (varchar AS mytimestamp) 
WITH FUNCTION to_mytimestamp(varchar) AS ASSIGNMENT;

INSERT INTO t (t0) 
VALUES('2014-09-11 13:30:12.564+02'); -- returns 'malformed record literal'

(我需要从varchar,char或text转换 - 但只是实现转换功能并添加相应的CAST似乎不起作用)

我知道我可以通过将查询改为

来实现这一目标
INSERT INTO t (t0) 
VALUES('2014-09-11 13:30:12.564+02'::varchar); -- works
-- the to_mytimestamp(varchar) function is called

但是在我的情况下,我无法更改查询,因为它们已在某些代码中定义,而这些代码不应再被更改。

有没有办法让我确保在执行上面的INSERT语句时调用to_mytimestamp(varchar)函数(不必附加::varchar)?

非常感谢!


编辑: 以下是提到的功能(根据需要将text替换为varcharchar中的to_mytimestamp()

CREATE OR REPLACE FUNCTION to_mytimestamp(t text)
  RETURNS mytimestamp AS
$BODY$
declare
ts mytimestamp;
offs text;
matches text[];
begin

 offs = substr(t, length(t) - 5, 6);
 matches = regexp_matches(offs, '[+-][0-9.:]*', 'g');
 offs = split_part(matches[1], ':', 1)::integer * 3600 + split_part(matches[1], ':', 2)::integer * 60;
 ts.t = t::timestamp with time zone;
 ts.o = offs;
 return ts;

end
$BODY$
  LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION to_mytimestamp(t timestamp with time zone)
  RETURNS mytimestamp AS
' select $1, extract(timezone from $1) '
  LANGUAGE sql;

说明:函数提取utc偏移量并将其存储在单独的值中以便以后重建。 注意:to_mytimestamp(t timestamp with time zone)存储系统偏移量,而不是假设存储在参数t中的那个,因为带时区的时间戳实际上并不包含偏移量数据 - 但这与问题:)

2 个答案:

答案 0 :(得分:2)

我认为你不能这样做。

当你在postgres中写一个字符串文字'foo'时,它实际上有一个"未知"类型,而不是文本。因此,您的强制转换功能不匹配,因为类型不同。这就是你能做到的原因:

SELECT '2014-09-11 13:30:12.564+02'::text::mytimestamp;

它将按预期工作。

即使您编写了一个处理unknown作为参数类型的函数的版本(并首先将其转换为text),并创建相关的强制转换:

CREATE CAST (unknown AS mytimestamp) 
WITH FUNCTION mytimestamp(unknown) 
AS IMPLICIT;

Postgres仍会尝试使用原始的复合类型转换功能。实际上,这打破了转换为文本的第一个" path,因为mytimestamp(text)没有更好的匹配,所以它使用内置函数。

答案 1 :(得分:0)

我无法测试,因为缺少to_mytimestamp()函数。但是,这可能就像这样简单:

CREATE CAST (text AS mytimestamp) 
WITH FUNCTION to_mytimestamp(text) AS ASSIGNMENT;

? -g