在greenplum/postgresql中,将字符串转换成时间是很方便的,很多种格式的时间,gp都可以自动识别出是否时间字符。如果不是时间或者时间不正确的话,sql会报错。
aligputf8=# select'2011-13-10 10:10:10'::date; ERROR: date/time field value out of range:"2011-13-10 10:10:10" LINE 1: select'2011-13-10 10:10:10'::date; ^ HINT: Perhaps you need a different"datestyle" setting. aligputf8=# select'df'::date; ERROR: invalid input syntax for type date:"df" LINE 1: select'df'::date;
现在有一个需求,就是识别一个字符串是否是有效的时间,查询了很多postgreSQL的文档,都没有发现有这样一个函数。但是当做类型转换的时候,数据库确实是做了这个验证的,只不过没有实现这个函数调用而已。所以看一下postgreSQL的源码,把这个函数抽取出来,重新建一个函数来实现这个功能。
在src/backend/utils/adt/date.c这个代码中,有这个函数:
/* date_in() * Given date text string, convert to internaldate format. */ Datum date_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); DateADT date; fsec_t fsec; struct pg_tm tt, *tm = &tt; int tzp; int dtype; int nf; int dterr; char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[MAXDATELEN + 1]; dterr = ParseDateTime(str, workbuf,sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) dterr = DecodeDateTime(field,ftype, nf, &dtype, tm, &fsec, &tzp); if (dterr != 0) DateTimeParseError(dterr, str,"date");
这个函数调用了ParseDateTime、跟DecodeDateTime这两个函数,就可以识别出这个字符串是否是时间字符,这两个函数是在datetime.h中定义的,所以要将这个头文件给include进来。所以我编写了下面这个C语言的函数,来实现这个功能:
//is_date.c #include"postgres.h" #include"funcapi.h" #include"utils/datetime.h" #include"fmgr.h" #ifdefPG_MODULE_MAGIC PG_MODULE_MAGIC; #endif Datumis_date(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(is_date); Datum is_date(PG_FUNCTION_ARGS) { VarChar *arg1 = PG_GETARG_VARCHAR_P(0); struct pg_tm tt, *tm = &tt; fsec_t fsec; char *str = (char *)palloc(VARSIZE(arg1) - VARHDRSZ +1); memcpy(str, VARDATA(arg1), VARSIZE(arg1)- VARHDRSZ); str[VARSIZE(arg1) - VARHDRSZ]=0; int nf; int dtype; int dterr; int tzp; char workbuf[MAXDATELEN + 1]; char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; elog(INFO, str); //elog(INFO, arg1->vl_len_); dterr = ParseDateTime(str, workbuf,sizeof(workbuf),field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) dterr = DecodeDateTime(field,ftype, nf, &dtype, tm, &fsec, &tzp); if (dterr == 0){ // elog(INFO, "true"); PG_RETURN_BOOL(true); } else { // elog(INFO, "false"); PG_RETURN_BOOL(false); } }
编译:
gcc -m64-I$GPHOME/include -I$GPHOME/include/postgresql -I$GPHOME/include/libpq-I$GPHOME/include/postgresql/server -I$GPHOME/include/postgresql/internal -O2-Wall -Wmissing-prototypes -Wpointer-arith -Winline-Wdeclaration-after-statement -Wendif-labels -fno-strict-aliasing -fwrapv -fPIC-I. -c -o is_date.o is_date.c gcc -m64 -sharedis_date.o -L$GPHOME/lib-L$GPHOME/lib/postgresql -o is_date.so
将is_date.so 这个库文件scp来gp每一台机器的/home/gpadmin1/cxf/is_date目录下:
创建函数:
CREATE or replaceFUNCTION is_date(varchar) RETURNS bool AS'/home/gpadmin1/cxf/is_date' LANGUAGE C IMMUTABLE ;
验证:
aligputf8=# selectis_date('2011-12-10 10:10:10'); is_date --------- t (1 row) aligputf8=# selectis_date('2011-13-10 10:10:10'); is_date --------- f (1 row) aligputf8=# selectis_date('sfd'); is_date --------- f (1 row)
Ps:当字符串是时间格式的话,并且时间正确,则返回true,否则返回false