5

我想要做的是在日期超出支持范围的情况下引发超出范围的错误,就像类型转换所做的那样。

我在 CentOS 上使用 PostgreSQL-9.1.6。问题如下...

postgres=# select to_date('20130229','yyyymmdd');
  to_date   
------------
 2013-03-01
(1 row)

但我想看到的输出是:

postgres=# select '20130229'::date;
ERROR:  date/time field value out of range: "20130229"

在网上冲浪时,我发现了一个内容丰富的页面。所以我确实添加IS_VALID_JULIAN到函数体中,将下面to_date标记的四行添加到formatting.c+

Datum
to_date(PG_FUNCTION_ARGS)
{
    text       *date_txt = PG_GETARG_TEXT_P(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    DateADT         result;
    struct pg_tm tm;
    fsec_t          fsec;

    do_to_timestamp(date_txt, fmt, &tm, &fsec);

+       if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
+       ereport(ERROR,
+               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                errmsg("date out of range: \"%s\"",text_to_cstring(date_txt))));

   result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;

   PG_RETURN_DATEADT(result);
}

然后我重建了 PostgreSQL:

pg_ctl -m fast stop                       # 1. stopping pgsql
vi src/backend/utils/adt/formatting.c     # 2. using the version above
rm -rf /usr/local/pgsql/*                 # 3. getting rid of all bin files
./configure --prefix=/usr/local/pgsql 
  --enable-nls --with-perl --with-libxml
  --with-pam --with-openssl
make && make install                      # 4. rebuilding source    
pg_ctl start                              # 5. starting the engine

我的 bin 目录信息如下。

[/home/postgres]echo $PATH
/usr/lib64/qt-3.3/bin:
/usr/local/bin:
/bin:
/usr/bin:
/usr/local/sbin:
/usr/sbin:
/sbin:
/home/postgres/bin:
/usr/bin:
/usr/local/pgsql/bin:
/usr/local/pgpool/bin:
/usr/local/pgtop/bin/pg_top:

[/home/postgres]which pg_ctl
/usr/local/pgsql/bin/pg_ctl

[/home/postgres]which postgres
/usr/local/pgsql/bin/postgres

[/usr/local/bin]which psql
/usr/local/pgsql/bin/psql

但是to_date再次检查,结果还是一样。

postgres=# select to_date('20130229','yyyymmdd');
  to_date   
------------
 2013-03-01
(1 row)

有什么我错过的吗?

4

1 回答 1

2

您可以编写自己的 to_date() 函数,但必须使用其模式限定名称来调用它。(我使用了“公共”模式,但这并没有什么特别之处。)

create or replace function public.to_date(any_date text, format_string text)
returns date as
$$
select to_date((any_date::date)::text, format_string);
$$
language sql

使用裸函数名执行本机 to_date() 函数。

select to_date('20130229', 'yyyymmdd');
2013-03-01

使用模式限定名称执行用户定义的函数。

select public.to_date('20130229', 'yyyymmdd');
ERROR: date/time field value out of range: "20130229"
SQL state: 22008

我知道这不是你要找的。但 。. .

  • 它比从源代码重建 PostgreSQL 更简单。
  • 修复现有 SQL 和 PLPGSQL 源代码是使用流式编辑器进行的简单搜索和替换。我很确定这不会出错,只要您真的希望本机 to_date() 的每次使用都是 public.to_date()。
  • 本机 to_date() 函数仍将按设计工作。扩展和其他代码可能依赖于它有些特殊的行为。在更改本机函数的行为之前,请认真思考。

不过,新的 SQL 和 PLPGSQL 需要重新审核。我不希望开发人员记得每次都写 public.to_date() 。如果您使用版本控制,您可能可以编写一个预提交挂钩以确保仅使用 public.to_date()。

本机 to_date() 函数具有我没有看到记录的行为。不仅可以用 2 月 29 日调用它,还可以用 2 月 345 或 9999 年 2 月调用它。

select to_date('201302345', 'yyyymmdd');
2014-01-11

select to_date('2013029999', 'yyyymmdd');
2040-06-17
于 2013-08-11T01:13:02.253 回答