2013-07-08 104 views
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" 

衝浪,我發現an informative page網絡。所以我也加入到IS_VALID_JULIANto_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) 

有什麼我錯過了嗎?

+0

猜測,您運行的服務器與您啓動的二進制文件不同。 –

+0

該方法沒有錯,不是嗎? – KIM

+0

呃...如果你修補代碼並編譯它,你必須運行你編譯的二進制文件。運行其他不相關的二進制文件將不起作用。 –

回答

1

您可以編寫自己的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