Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b8c0ffb

Browse files
committed
Convert domain_in to report errors softly.
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
1 parent c60c9ba commit b8c0ffb

File tree

4 files changed

+100
-10
lines changed

4 files changed

+100
-10
lines changed

doc/src/sgml/ref/create_domain.sgml

+5
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ INSERT INTO tab (domcol) VALUES ((SELECT domcol FROM tab WHERE false));
239239
DOMAIN</command>), adjust the function definition, and re-add the
240240
constraint, thereby rechecking it against stored data.
241241
</para>
242+
243+
<para>
244+
It's also good practice to ensure that domain <literal>CHECK</literal>
245+
expressions will not throw errors.
246+
</para>
242247
</refsect1>
243248

244249
<refsect1>

src/backend/utils/adt/domains.c

+26-10
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,14 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
126126
* This is roughly similar to the handling of CoerceToDomain nodes in
127127
* execExpr*.c, but we execute each constraint separately, rather than
128128
* compiling them in-line within a larger expression.
129+
*
130+
* If escontext points to an ErrorStateContext, any failures are reported
131+
* there, otherwise they are ereport'ed. Note that we do not attempt to do
132+
* soft reporting of errors raised during execution of CHECK constraints.
129133
*/
130134
static void
131-
domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
135+
domain_check_input(Datum value, bool isnull, DomainIOData *my_extra,
136+
Node *escontext)
132137
{
133138
ExprContext *econtext = my_extra->econtext;
134139
ListCell *l;
@@ -144,11 +149,14 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
144149
{
145150
case DOM_CONSTRAINT_NOTNULL:
146151
if (isnull)
147-
ereport(ERROR,
152+
{
153+
errsave(escontext,
148154
(errcode(ERRCODE_NOT_NULL_VIOLATION),
149155
errmsg("domain %s does not allow null values",
150156
format_type_be(my_extra->domain_type)),
151157
errdatatype(my_extra->domain_type)));
158+
goto fail;
159+
}
152160
break;
153161
case DOM_CONSTRAINT_CHECK:
154162
{
@@ -179,13 +187,16 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
179187
econtext->domainValue_isNull = isnull;
180188

181189
if (!ExecCheck(con->check_exprstate, econtext))
182-
ereport(ERROR,
190+
{
191+
errsave(escontext,
183192
(errcode(ERRCODE_CHECK_VIOLATION),
184193
errmsg("value for domain %s violates check constraint \"%s\"",
185194
format_type_be(my_extra->domain_type),
186195
con->name),
187196
errdomainconstraint(my_extra->domain_type,
188197
con->name)));
198+
goto fail;
199+
}
189200
break;
190201
}
191202
default:
@@ -200,6 +211,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
200211
* per-tuple memory. This avoids leaking non-memory resources, if
201212
* anything in the expression(s) has any.
202213
*/
214+
fail:
203215
if (econtext)
204216
ReScanExprContext(econtext);
205217
}
@@ -213,6 +225,7 @@ domain_in(PG_FUNCTION_ARGS)
213225
{
214226
char *string;
215227
Oid domainType;
228+
Node *escontext = fcinfo->context;
216229
DomainIOData *my_extra;
217230
Datum value;
218231

@@ -245,15 +258,18 @@ domain_in(PG_FUNCTION_ARGS)
245258
/*
246259
* Invoke the base type's typinput procedure to convert the data.
247260
*/
248-
value = InputFunctionCall(&my_extra->proc,
249-
string,
250-
my_extra->typioparam,
251-
my_extra->typtypmod);
261+
if (!InputFunctionCallSafe(&my_extra->proc,
262+
string,
263+
my_extra->typioparam,
264+
my_extra->typtypmod,
265+
escontext,
266+
&value))
267+
PG_RETURN_NULL();
252268

253269
/*
254270
* Do the necessary checks to ensure it's a valid domain value.
255271
*/
256-
domain_check_input(value, (string == NULL), my_extra);
272+
domain_check_input(value, (string == NULL), my_extra, escontext);
257273

258274
if (string == NULL)
259275
PG_RETURN_NULL();
@@ -309,7 +325,7 @@ domain_recv(PG_FUNCTION_ARGS)
309325
/*
310326
* Do the necessary checks to ensure it's a valid domain value.
311327
*/
312-
domain_check_input(value, (buf == NULL), my_extra);
328+
domain_check_input(value, (buf == NULL), my_extra, NULL);
313329

314330
if (buf == NULL)
315331
PG_RETURN_NULL();
@@ -349,7 +365,7 @@ domain_check(Datum value, bool isnull, Oid domainType,
349365
/*
350366
* Do the necessary checks to ensure it's a valid domain value.
351367
*/
352-
domain_check_input(value, isnull, my_extra);
368+
domain_check_input(value, isnull, my_extra, NULL);
353369
}
354370

355371
/*

src/test/regress/expected/domain.out

+50
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,56 @@ drop domain domainvarchar restrict;
8787
drop domain domainnumeric restrict;
8888
drop domain domainint4 restrict;
8989
drop domain domaintext;
90+
-- Test non-error-throwing input
91+
create domain positiveint int4 check(value > 0);
92+
create domain weirdfloat float8 check((1 / value) < 10);
93+
select pg_input_is_valid('1', 'positiveint');
94+
pg_input_is_valid
95+
-------------------
96+
t
97+
(1 row)
98+
99+
select pg_input_is_valid('junk', 'positiveint');
100+
pg_input_is_valid
101+
-------------------
102+
f
103+
(1 row)
104+
105+
select pg_input_is_valid('-1', 'positiveint');
106+
pg_input_is_valid
107+
-------------------
108+
f
109+
(1 row)
110+
111+
select pg_input_error_message('junk', 'positiveint');
112+
pg_input_error_message
113+
-----------------------------------------------
114+
invalid input syntax for type integer: "junk"
115+
(1 row)
116+
117+
select pg_input_error_message('-1', 'positiveint');
118+
pg_input_error_message
119+
----------------------------------------------------------------------------
120+
value for domain positiveint violates check constraint "positiveint_check"
121+
(1 row)
122+
123+
select pg_input_error_message('junk', 'weirdfloat');
124+
pg_input_error_message
125+
--------------------------------------------------------
126+
invalid input syntax for type double precision: "junk"
127+
(1 row)
128+
129+
select pg_input_error_message('0.01', 'weirdfloat');
130+
pg_input_error_message
131+
--------------------------------------------------------------------------
132+
value for domain weirdfloat violates check constraint "weirdfloat_check"
133+
(1 row)
134+
135+
-- We currently can't trap errors raised in the CHECK expression itself
136+
select pg_input_error_message('0', 'weirdfloat');
137+
ERROR: division by zero
138+
drop domain positiveint;
139+
drop domain weirdfloat;
90140
-- Test domains over array types
91141
create domain domainint4arr int4[1];
92142
create domain domainchar4arr varchar(4)[2][3];

src/test/regress/sql/domain.sql

+19
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,25 @@ drop domain domainint4 restrict;
6969
drop domain domaintext;
7070

7171

72+
-- Test non-error-throwing input
73+
74+
create domain positiveint int4 check(value > 0);
75+
create domain weirdfloat float8 check((1 / value) < 10);
76+
77+
select pg_input_is_valid('1', 'positiveint');
78+
select pg_input_is_valid('junk', 'positiveint');
79+
select pg_input_is_valid('-1', 'positiveint');
80+
select pg_input_error_message('junk', 'positiveint');
81+
select pg_input_error_message('-1', 'positiveint');
82+
select pg_input_error_message('junk', 'weirdfloat');
83+
select pg_input_error_message('0.01', 'weirdfloat');
84+
-- We currently can't trap errors raised in the CHECK expression itself
85+
select pg_input_error_message('0', 'weirdfloat');
86+
87+
drop domain positiveint;
88+
drop domain weirdfloat;
89+
90+
7291
-- Test domains over array types
7392

7493
create domain domainint4arr int4[1];

0 commit comments

Comments
 (0)