On Mon, 2007-28-05 at 15:38 -0400, Tom Lane wrote: > More generally, I'm really hoping to get rid of bespoke text<->whatever > cast functions in favor of using datatypes' I/O functions. To what > extent can we make the boolean I/O functions serve for this? It seems > relatively painless on the input side --- just allow whitespace --- but > I suppose we can't change boolout's historical result of "t"/"f" without > causing problems.
Attached is a revised version of this patch that modifies boolin() to ignore leading and trailing whitespace. This makes text => boolean trivial, but boolean => text is still distinct from boolout(). Barring any objections, I'll apply this later today or tomorrow. -Neil
Index: src/backend/utils/adt/bool.c =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/adt/bool.c,v retrieving revision 1.38 diff -p -c -r1.38 bool.c *** src/backend/utils/adt/bool.c 5 Jan 2007 22:19:40 -0000 1.38 --- src/backend/utils/adt/bool.c 30 May 2007 18:55:26 -0000 *************** *** 15,20 **** --- 15,22 ---- #include "postgres.h" + #include <ctype.h> + #include "libpq/pqformat.h" #include "utils/builtins.h" *************** *** 33,73 **** Datum boolin(PG_FUNCTION_ARGS) { ! char *b = PG_GETARG_CSTRING(0); ! switch (*b) { case 't': case 'T': ! if (pg_strncasecmp(b, "true", strlen(b)) == 0) PG_RETURN_BOOL(true); break; case 'f': case 'F': ! if (pg_strncasecmp(b, "false", strlen(b)) == 0) PG_RETURN_BOOL(false); break; case 'y': case 'Y': ! if (pg_strncasecmp(b, "yes", strlen(b)) == 0) PG_RETURN_BOOL(true); break; case '1': ! if (pg_strncasecmp(b, "1", strlen(b)) == 0) PG_RETURN_BOOL(true); break; case 'n': case 'N': ! if (pg_strncasecmp(b, "no", strlen(b)) == 0) PG_RETURN_BOOL(false); break; case '0': ! if (pg_strncasecmp(b, "0", strlen(b)) == 0) PG_RETURN_BOOL(false); break; --- 35,88 ---- Datum boolin(PG_FUNCTION_ARGS) { ! const char *in_str = PG_GETARG_CSTRING(0); ! const char *str; ! size_t len; ! ! /* ! * Skip leading and trailing whitespace ! */ ! str = in_str; ! while (isspace((unsigned char) *str)) ! str++; ! ! len = strlen(str); ! while (len > 0 && isspace((unsigned char) str[len - 1])) ! len--; ! switch (*str) { case 't': case 'T': ! if (pg_strncasecmp(str, "true", len) == 0) PG_RETURN_BOOL(true); break; case 'f': case 'F': ! if (pg_strncasecmp(str, "false", len) == 0) PG_RETURN_BOOL(false); break; case 'y': case 'Y': ! if (pg_strncasecmp(str, "yes", len) == 0) PG_RETURN_BOOL(true); break; case '1': ! if (pg_strncasecmp(str, "1", len) == 0) PG_RETURN_BOOL(true); break; case 'n': case 'N': ! if (pg_strncasecmp(str, "no", len) == 0) PG_RETURN_BOOL(false); break; case '0': ! if (pg_strncasecmp(str, "0", len) == 0) PG_RETURN_BOOL(false); break; *************** boolin(PG_FUNCTION_ARGS) *** 77,83 **** ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), ! errmsg("invalid input syntax for type boolean: \"%s\"", b))); /* not reached */ PG_RETURN_BOOL(false); --- 92,98 ---- ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), ! errmsg("invalid input syntax for type boolean: \"%s\"", in_str))); /* not reached */ PG_RETURN_BOOL(false); *************** boolsend(PG_FUNCTION_ARGS) *** 127,132 **** --- 142,178 ---- PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } + /* + * textbool - cast function for text => bool + */ + Datum + textbool(PG_FUNCTION_ARGS) + { + Datum in_text = PG_GETARG_DATUM(0); + char *str; + + str = DatumGetCString(DirectFunctionCall1(textout, in_text)); + + PG_RETURN_DATUM(DirectFunctionCall1(boolin, CStringGetDatum(str))); + } + + /* + * booltext - cast function for bool => text + */ + Datum + booltext(PG_FUNCTION_ARGS) + { + bool arg1 = PG_GETARG_BOOL(0); + char *str; + + if (arg1) + str = "true"; + else + str = "false"; + + PG_RETURN_DATUM(DirectFunctionCall1(textin, CStringGetDatum(str))); + } + /***************************************************************************** * PUBLIC ROUTINES * Index: src/include/catalog/pg_cast.h =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/catalog/pg_cast.h,v retrieving revision 1.32 diff -p -c -r1.32 pg_cast.h *** src/include/catalog/pg_cast.h 2 Apr 2007 03:49:40 -0000 1.32 --- src/include/catalog/pg_cast.h 30 May 2007 17:59:20 -0000 *************** DATA(insert ( 1700 25 1688 i )); *** 302,307 **** --- 302,309 ---- DATA(insert ( 25 1700 1686 e )); DATA(insert ( 142 25 2922 e )); DATA(insert ( 25 142 2896 e )); + DATA(insert ( 16 25 2971 e )); + DATA(insert ( 25 16 2970 e )); /* * Cross-category casts to and from VARCHAR *************** DATA(insert ( 1700 1043 1688 a )); *** 342,347 **** --- 344,351 ---- DATA(insert ( 1043 1700 1686 e )); DATA(insert ( 142 1043 2922 e )); DATA(insert ( 1043 142 2896 e )); + DATA(insert ( 16 1043 2971 e )); + DATA(insert ( 1043 16 2970 e )); /* * Cross-category casts to and from BPCHAR Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.456 diff -p -c -r1.456 pg_proc.h *** src/include/catalog/pg_proc.h 21 May 2007 17:10:29 -0000 1.456 --- src/include/catalog/pg_proc.h 30 May 2007 17:59:20 -0000 *************** DESCR("List all files in a directory"); *** 3221,3226 **** --- 3221,3230 ---- DATA(insert OID = 2626 ( pg_sleep PGNSP PGUID 12 1 0 f f t f v 1 2278 "701" _null_ _null_ _null_ pg_sleep - _null_ )); DESCR("Sleep for the specified time in seconds"); + DATA(insert OID = 2970 ( boolean PGNSP PGUID 12 1 0 f f t f i 1 16 "25" _null_ _null_ _null_ textbool - _null_ )); + DESCR("text to boolean"); + DATA(insert OID = 2971 ( text PGNSP PGUID 12 1 0 f f t f i 1 25 "16" _null_ _null_ _null_ booltext - _null_ )); + DESCR("boolean to text"); /* Aggregates (moved here from pg_aggregate for 7.3) */ Index: src/include/utils/builtins.h =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/utils/builtins.h,v retrieving revision 1.293 diff -p -c -r1.293 builtins.h *** src/include/utils/builtins.h 17 May 2007 23:31:49 -0000 1.293 --- src/include/utils/builtins.h 30 May 2007 17:59:20 -0000 *************** extern Datum boolin(PG_FUNCTION_ARGS); *** 70,75 **** --- 70,77 ---- extern Datum boolout(PG_FUNCTION_ARGS); extern Datum boolrecv(PG_FUNCTION_ARGS); extern Datum boolsend(PG_FUNCTION_ARGS); + extern Datum booltext(PG_FUNCTION_ARGS); + extern Datum textbool(PG_FUNCTION_ARGS); extern Datum booleq(PG_FUNCTION_ARGS); extern Datum boolne(PG_FUNCTION_ARGS); extern Datum boollt(PG_FUNCTION_ARGS); Index: src/test/regress/expected/boolean.out =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/test/regress/expected/boolean.out,v retrieving revision 1.12 diff -p -c -r1.12 boolean.out *** src/test/regress/expected/boolean.out 7 Apr 2005 01:51:40 -0000 1.12 --- src/test/regress/expected/boolean.out 30 May 2007 18:57:52 -0000 *************** SELECT bool 't' AS true; *** 18,24 **** t (1 row) ! SELECT bool 'f' AS false; false ------- f --- 18,24 ---- t (1 row) ! SELECT bool ' f ' AS false; false ------- f *************** SELECT bool 't' <> bool 'f' AS true; *** 54,59 **** --- 54,83 ---- t (1 row) + -- explicit casts to/from text + SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; + true | false + ------+------- + t | f + (1 row) + + SELECT ' true '::text::boolean AS true, + ' FALSE'::text::boolean AS false; + true | false + ------+------- + t | f + (1 row) + + SELECT true::boolean::text AS true, false::boolean::text AS false; + true | false + ------+------- + true | false + (1 row) + + SELECT ' tru e '::text::boolean AS invalid; -- error + ERROR: invalid input syntax for type boolean: " tru e " + SELECT ''::text::boolean AS invalid; -- error + ERROR: invalid input syntax for type boolean: "" CREATE TABLE BOOLTBL1 (f1 bool); INSERT INTO BOOLTBL1 (f1) VALUES (bool 't'); INSERT INTO BOOLTBL1 (f1) VALUES (bool 'True'); Index: src/test/regress/sql/boolean.sql =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/test/regress/sql/boolean.sql,v retrieving revision 1.7 diff -p -c -r1.7 boolean.sql *** src/test/regress/sql/boolean.sql 7 Apr 2005 01:51:41 -0000 1.7 --- src/test/regress/sql/boolean.sql 30 May 2007 18:56:49 -0000 *************** SELECT 1 AS one; *** 14,20 **** SELECT bool 't' AS true; ! SELECT bool 'f' AS false; SELECT bool 't' or bool 'f' AS true; --- 14,20 ---- SELECT bool 't' AS true; ! SELECT bool ' f ' AS false; SELECT bool 't' or bool 'f' AS true; *************** SELECT bool 't' = bool 'f' AS false; *** 26,31 **** --- 26,39 ---- SELECT bool 't' <> bool 'f' AS true; + -- explicit casts to/from text + SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; + SELECT ' true '::text::boolean AS true, + ' FALSE'::text::boolean AS false; + SELECT true::boolean::text AS true, false::boolean::text AS false; + + SELECT ' tru e '::text::boolean AS invalid; -- error + SELECT ''::text::boolean AS invalid; -- error CREATE TABLE BOOLTBL1 (f1 bool);
---------------------------(end of broadcast)--------------------------- TIP 6: explain analyze is your friend