git.postgresql.org Git - postgresql.git/commitdiff

git projects / postgresql.git / commitdiff
? search:
summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: c60c9ba)
Convert domain_in to report errors softly.
2022年12月11日 17:56:54 +0000 (12:56 -0500)
2022年12月11日 17:56:54 +0000 (12:56 -0500)
This is straightforward as far as it goes. However, it does not
attempt to trap errors occurring during the execution of domain
CHECK constraints. Since those are general user-defined
expressions, the only way to do that would involve starting up a
subtransaction for each check. Of course the entire point of
the soft-errors feature is to not need subtransactions, so that
would be self-defeating. For now, we'll rely on the assumption
that domain checks are written to avoid throwing errors.

Discussion: https://postgr.es/m/1181028.1670635727@sss.pgh.pa.us


diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 82a0b87492932b55ab72d3b82b314cd19c5aa274..73f9f28d6cfae4a50230624f8fe481b8e8026cf7 100644 (file)
--- 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</command>), adjust the function definition, and re-add the
constraint, thereby rechecking it against stored data.
</para>
+
+ <para>
+ It's also good practice to ensure that domain <literal>CHECK</literal>
+ expressions will not throw errors.
+ </para>
</refsect1>
<refsect1>
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index 3de0cb01a2c85a651303144c390900f232bc01ad..99aeaddb5d28081dd236c4ba0ed6545099336b69 100644 (file)
--- 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 73b010f6ed28d38b0ca0c3d5156b0818db643a58..25f6bb9e1f3d62f9e99837a6785c5f81dc2656a8 100644 (file)
--- 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 f2ca1fb675a0d37263f692b0a707a2c3e7ffd6e2..1558bd9a336c254812d291dff23a9811887fe56c 100644 (file)
--- 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];
This is the main PostgreSQL git repository.
RSS Atom

AltStyle によって変換されたページ (->オリジナル) /