diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 82a0b87492..73f9f28d6c 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -239,6 +239,11 @@ INSERT INTO tab (domcol) VALUES ((SELECT domcol FROM tab WHERE false));
DOMAIN), adjust the function definition, and re-add the
constraint, thereby rechecking it against stored data.
+
+
+ It's also good practice to ensure that domain CHECK
+ expressions will not throw errors.
+
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index 3de0cb01a2..99aeaddb5d 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -126,9 +126,14 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
* This is roughly similar to the handling of CoerceToDomain nodes in
* execExpr*.c, but we execute each constraint separately, rather than
* compiling them in-line within a larger expression.
+ *
+ * If escontext points to an ErrorStateContext, any failures are reported
+ * there, otherwise they are ereport'ed. Note that we do not attempt to do
+ * soft reporting of errors raised during execution of CHECK constraints.
*/
static void
-domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
+domain_check_input(Datum value, bool isnull, DomainIOData *my_extra,
+ Node *escontext)
{
ExprContext *econtext = my_extra->econtext;
ListCell *l;
@@ -144,11 +149,14 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
{
case DOM_CONSTRAINT_NOTNULL:
if (isnull)
- ereport(ERROR,
+ {
+ errsave(escontext,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("domain %s does not allow null values",
format_type_be(my_extra->domain_type)),
errdatatype(my_extra->domain_type)));
+ goto fail;
+ }
break;
case DOM_CONSTRAINT_CHECK:
{
@@ -179,13 +187,16 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
econtext->domainValue_isNull = isnull;
if (!ExecCheck(con->check_exprstate, econtext))
- ereport(ERROR,
+ {
+ errsave(escontext,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
format_type_be(my_extra->domain_type),
con->name),
errdomainconstraint(my_extra->domain_type,
con->name)));
+ goto fail;
+ }
break;
}
default:
@@ -200,6 +211,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
* per-tuple memory. This avoids leaking non-memory resources, if
* anything in the expression(s) has any.
*/
+fail:
if (econtext)
ReScanExprContext(econtext);
}
@@ -213,6 +225,7 @@ domain_in(PG_FUNCTION_ARGS)
{
char *string;
Oid domainType;
+ Node *escontext = fcinfo->context;
DomainIOData *my_extra;
Datum value;
@@ -245,15 +258,18 @@ domain_in(PG_FUNCTION_ARGS)
/*
* Invoke the base type's typinput procedure to convert the data.
*/
- value = InputFunctionCall(&my_extra->proc,
- string,
- my_extra->typioparam,
- my_extra->typtypmod);
+ if (!InputFunctionCallSafe(&my_extra->proc,
+ string,
+ my_extra->typioparam,
+ my_extra->typtypmod,
+ escontext,
+ &value))
+ PG_RETURN_NULL();
/*
* Do the necessary checks to ensure it's a valid domain value.
*/
- domain_check_input(value, (string == NULL), my_extra);
+ domain_check_input(value, (string == NULL), my_extra, escontext);
if (string == NULL)
PG_RETURN_NULL();
@@ -309,7 +325,7 @@ domain_recv(PG_FUNCTION_ARGS)
/*
* Do the necessary checks to ensure it's a valid domain value.
*/
- domain_check_input(value, (buf == NULL), my_extra);
+ domain_check_input(value, (buf == NULL), my_extra, NULL);
if (buf == NULL)
PG_RETURN_NULL();
@@ -349,7 +365,7 @@ domain_check(Datum value, bool isnull, Oid domainType,
/*
* Do the necessary checks to ensure it's a valid domain value.
*/
- domain_check_input(value, isnull, my_extra);
+ domain_check_input(value, isnull, my_extra, NULL);
}
/*
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 73b010f6ed..25f6bb9e1f 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -87,6 +87,56 @@ drop domain domainvarchar restrict;
drop domain domainnumeric restrict;
drop domain domainint4 restrict;
drop domain domaintext;
+-- Test non-error-throwing input
+create domain positiveint int4 check(value > 0);
+create domain weirdfloat float8 check((1 / value) < 10);
+select pg_input_is_valid('1', 'positiveint');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('junk', 'positiveint');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_is_valid('-1', 'positiveint');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('junk', 'positiveint');
+ pg_input_error_message
+-----------------------------------------------
+ invalid input syntax for type integer: "junk"
+(1 row)
+
+select pg_input_error_message('-1', 'positiveint');
+ pg_input_error_message
+----------------------------------------------------------------------------
+ value for domain positiveint violates check constraint "positiveint_check"
+(1 row)
+
+select pg_input_error_message('junk', 'weirdfloat');
+ pg_input_error_message
+--------------------------------------------------------
+ invalid input syntax for type double precision: "junk"
+(1 row)
+
+select pg_input_error_message('0.01', 'weirdfloat');
+ pg_input_error_message
+--------------------------------------------------------------------------
+ value for domain weirdfloat violates check constraint "weirdfloat_check"
+(1 row)
+
+-- We currently can't trap errors raised in the CHECK expression itself
+select pg_input_error_message('0', 'weirdfloat');
+ERROR: division by zero
+drop domain positiveint;
+drop domain weirdfloat;
-- Test domains over array types
create domain domainint4arr int4[1];
create domain domainchar4arr varchar(4)[2][3];
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index f2ca1fb675..1558bd9a33 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -69,6 +69,25 @@ drop domain domainint4 restrict;
drop domain domaintext;
+-- Test non-error-throwing input
+
+create domain positiveint int4 check(value > 0);
+create domain weirdfloat float8 check((1 / value) < 10);
+
+select pg_input_is_valid('1', 'positiveint');
+select pg_input_is_valid('junk', 'positiveint');
+select pg_input_is_valid('-1', 'positiveint');
+select pg_input_error_message('junk', 'positiveint');
+select pg_input_error_message('-1', 'positiveint');
+select pg_input_error_message('junk', 'weirdfloat');
+select pg_input_error_message('0.01', 'weirdfloat');
+-- We currently can't trap errors raised in the CHECK expression itself
+select pg_input_error_message('0', 'weirdfloat');
+
+drop domain positiveint;
+drop domain weirdfloat;
+
+
-- Test domains over array types
create domain domainint4arr int4[1];