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

Commit c2c4244

Browse files
jianhe-funCommitfest Bot
authored and
Commitfest Bot
committed
fast default for domain with constraints
This is primarily done by evaluating CoerceToDomain with soft error support. If CoerceToDomain is evaluated as false in ATExecAddColumn, the defval node's value cannot be cast to the domain type. However, in some cases like when the table is empty, we cannot explicitly error out in ATExecAddColumn (Phase 2). For example, imagine add a new domain column to empty x, and the column domain specification is ``CHECK(value > 10) DEFAULT 8``.  In such situations, the ALTER TABLE ADD COLUMN should be success. Thanks to commit aaaf944[1], ExprState.escontext (ErrorSaveContext) was added, and ExecEvalConstraintNotNull, ExecEvalConstraintCheck were changed to use errsave instead of hard error. Now we can evaluate CoerceToDomain in a soft error way. However we do table rewrite for domain with volatile check constraints. discussion: https://postgr.es/m/CACJufxE_+iZBR1i49k_AHigppPwLTJi6km8NOsC7FWvKdEmmXg@mail.gmail.com [1]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274bf
1 parent 4c08a0f commit c2c4244

File tree

3 files changed

+128
-16
lines changed

3 files changed

+128
-16
lines changed

src/backend/commands/tablecmds.c

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7429,15 +7429,6 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
74297429
* NULL if so, so without any modification of the tuple data we will get
74307430
* the effect of NULL values in the new column.
74317431
*
7432-
* An exception occurs when the new column is of a domain type: the domain
7433-
* might have a not-null constraint, or a check constraint that indirectly
7434-
* rejects nulls. If there are any domain constraints then we construct
7435-
* an explicit NULL default value that will be passed through
7436-
* CoerceToDomain processing. (This is a tad inefficient, since it causes
7437-
* rewriting the table which we really wouldn't have to do; but we do it
7438-
* to preserve the historical behavior that such a failure will be raised
7439-
* only if the table currently contains some rows.)
7440-
*
74417432
* Note: we use build_column_default, and not just the cooked default
74427433
* returned by AddRelationNewConstraints, so that the right thing happens
74437434
* when a datatype's default applies.
@@ -7456,6 +7447,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
74567447
{
74577448
bool has_domain_constraints;
74587449
bool has_missing = false;
7450+
bool has_volatile = false;
74597451

74607452
/*
74617453
* For an identity column, we can't use build_column_default(),
@@ -7473,8 +7465,17 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
74737465
else
74747466
defval = (Expr *) build_column_default(rel, attribute->attnum);
74757467

7468+
has_domain_constraints = DomainHaveVolatileConstraints(attribute->atttypid, &has_volatile);
7469+
/*
7470+
* Adding column with volatile domain constraint requires table rewrite
7471+
*/
7472+
if (has_volatile)
7473+
{
7474+
Assert(has_domain_constraints);
7475+
tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7476+
}
7477+
74767478
/* Build CoerceToDomain(NULL) expression if needed */
7477-
has_domain_constraints = DomainHasConstraints(attribute->atttypid);
74787479
if (!defval && has_domain_constraints)
74797480
{
74807481
Oid baseTypeId;
@@ -7516,14 +7517,18 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
75167517
* Attempt to skip a complete table rewrite by storing the
75177518
* specified DEFAULT value outside of the heap. This is only
75187519
* allowed for plain relations and non-generated columns, and the
7519-
* default expression can't be volatile (stable is OK). Note that
7520-
* contain_volatile_functions deems CoerceToDomain immutable, but
7521-
* here we consider that coercion to a domain with constraints is
7522-
* volatile; else it might fail even when the table is empty.
7520+
* default expression can't be volatile (stable is OK), and the
7521+
* domain constraint can't be volatile (stable is OK).
7522+
*
7523+
* Note that contain_volatile_functions deems CoerceToDomain
7524+
* immutable. However we have computed CoerceToDomain is volatile
7525+
* or not via DomainHaveVolatileConstraints. We use soft error
7526+
* evaluation of CoerceToDomain, if evaluation failed, then set
7527+
* table rewrite to true.
75237528
*/
75247529
if (rel->rd_rel->relkind == RELKIND_RELATION &&
75257530
!colDef->generated &&
7526-
!has_domain_constraints &&
7531+
!has_volatile &&
75277532
!contain_volatile_functions((Node *) defval))
75287533
{
75297534
EState *estate;
@@ -7533,10 +7538,18 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
75337538

75347539
/* Evaluate the default expression */
75357540
estate = CreateExecutorState();
7536-
exprState = ExecPrepareExpr(defval, estate);
7541+
exprState = ExecPrepareExprSafe(defval, estate);
7542+
75377543
missingval = ExecEvalExpr(exprState,
75387544
GetPerTupleExprContext(estate),
75397545
&missingIsNull);
7546+
7547+
if (SOFT_ERROR_OCCURRED(exprState->escontext))
7548+
{
7549+
missingIsNull = true;
7550+
tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7551+
}
7552+
75407553
/* If it turns out NULL, nothing to do; else store it */
75417554
if (!missingIsNull)
75427555
{

src/test/regress/expected/fast_default.out

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,66 @@ SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
317317
2 | 3 | t | {This,is,abcd,the,real,world} | t
318318
(2 rows)
319319

320+
---test fast default over domains with constraints
321+
CREATE DOMAIN domain5 AS int check(value > 10) default 8;
322+
CREATE DOMAIN domain6 as int not null;
323+
CREATE DOMAIN domain7 as int check(value > 10) DEFAULT random(min=>10, max=>100);
324+
CREATE DOMAIN domain8 as int check((value + random(min=>11::int, max=>11)) > 12);
325+
--tests with empty table.
326+
CREATE TABLE t3(a int);
327+
ALTER TABLE t3 ADD COLUMN b domain5 default 1; --ok, table rewrite
328+
NOTICE: rewriting table t3 for reason 2
329+
ALTER TABLE t3 ADD COLUMN c domain6 default 11 + NULL; --ok, table rewrite
330+
NOTICE: rewriting table t3 for reason 2
331+
ALTER TABLE t3 ADD COLUMN d domain7 default 2; --ok, table rewrite
332+
NOTICE: rewriting table t3 for reason 2
333+
ALTER TABLE t3 ADD COLUMN e domain8 default 3; --ok, table rewrite
334+
NOTICE: rewriting table t3 for reason 2
335+
DROP TABLE t3;
336+
--tests with non-empty table.
337+
CREATE TABLE t3(a int);
338+
INSERT INTO t3 VALUES(1),(2);
339+
ALTER TABLE t3 ADD COLUMN b domain5; --table rewrite, then fail
340+
NOTICE: rewriting table t3 for reason 2
341+
ERROR: value for domain domain5 violates check constraint "domain5_check"
342+
ALTER TABLE t3 ADD COLUMN b domain6; --table rewrite, then fail
343+
NOTICE: rewriting table t3 for reason 2
344+
ERROR: domain domain6 does not allow null values
345+
ALTER TABLE t3 ADD COLUMN b domain5 default 12; --no table rewrite
346+
ALTER TABLE t3 ADD COLUMN c domain6 default 13; --no table rewrite
347+
--no table rewrite. explicit column default expression override domain default
348+
--expression
349+
ALTER TABLE t3 ADD COLUMN d domain7 default 15;
350+
SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
351+
FROM pg_attribute
352+
WHERE attnum > 0 AND attrelid = 't3'::regclass AND attisdropped is false
353+
AND atthasmissing
354+
ORDER BY attnum;
355+
attnum | attname | atthasmissing | atthasdef | attmissingval
356+
--------+---------+---------------+-----------+---------------
357+
2 | b | t | t | {12}
358+
3 | c | t | t | {13}
359+
4 | d | t | t | {15}
360+
(3 rows)
361+
362+
--table rewrite. we are applying domain volatile default expresion
363+
ALTER TABLE t3 ADD COLUMN e domain7;
364+
NOTICE: rewriting table t3 for reason 2
365+
--table rewrite for volatile domain constraints.
366+
ALTER TABLE t3 ADD COLUMN f domain8 default 14; --table rewrite
367+
NOTICE: rewriting table t3 for reason 2
368+
ALTER TABLE t3 ADD COLUMN f1 domain8; --table rewrite
369+
NOTICE: rewriting table t3 for reason 2
320370
DROP TABLE t2;
371+
DROP TABLE t3;
321372
DROP DOMAIN domain1;
322373
DROP DOMAIN domain2;
323374
DROP DOMAIN domain3;
324375
DROP DOMAIN domain4;
376+
DROP DOMAIN domain5;
377+
DROP DOMAIN domain6;
378+
DROP DOMAIN domain7;
379+
DROP DOMAIN domain8;
325380
DROP FUNCTION foo(INT);
326381
-- Fall back to full rewrite for volatile expressions
327382
CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);

src/test/regress/sql/fast_default.sql

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,55 @@ ORDER BY attnum;
287287

288288
SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
289289

290+
---test fast default over domains with constraints
291+
CREATE DOMAIN domain5 AS int check(value > 10) default 8;
292+
CREATE DOMAIN domain6 as int not null;
293+
CREATE DOMAIN domain7 as int check(value > 10) DEFAULT random(min=>10, max=>100);
294+
CREATE DOMAIN domain8 as int check((value + random(min=>11::int, max=>11)) > 12);
295+
296+
--tests with empty table.
297+
CREATE TABLE t3(a int);
298+
ALTER TABLE t3 ADD COLUMN b domain5 default 1; --ok, table rewrite
299+
ALTER TABLE t3 ADD COLUMN c domain6 default 11 + NULL; --ok, table rewrite
300+
ALTER TABLE t3 ADD COLUMN d domain7 default 2; --ok, table rewrite
301+
ALTER TABLE t3 ADD COLUMN e domain8 default 3; --ok, table rewrite
302+
DROP TABLE t3;
303+
304+
--tests with non-empty table.
305+
CREATE TABLE t3(a int);
306+
INSERT INTO t3 VALUES(1),(2);
307+
308+
ALTER TABLE t3 ADD COLUMN b domain5; --table rewrite, then fail
309+
ALTER TABLE t3 ADD COLUMN b domain6; --table rewrite, then fail
310+
ALTER TABLE t3 ADD COLUMN b domain5 default 12; --no table rewrite
311+
ALTER TABLE t3 ADD COLUMN c domain6 default 13; --no table rewrite
312+
--no table rewrite. explicit column default expression override domain default
313+
--expression
314+
ALTER TABLE t3 ADD COLUMN d domain7 default 15;
315+
316+
SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
317+
FROM pg_attribute
318+
WHERE attnum > 0 AND attrelid = 't3'::regclass AND attisdropped is false
319+
AND atthasmissing
320+
ORDER BY attnum;
321+
322+
--table rewrite. we are applying domain volatile default expresion
323+
ALTER TABLE t3 ADD COLUMN e domain7;
324+
325+
--table rewrite for volatile domain constraints.
326+
ALTER TABLE t3 ADD COLUMN f domain8 default 14; --table rewrite
327+
ALTER TABLE t3 ADD COLUMN f1 domain8; --table rewrite
328+
290329
DROP TABLE t2;
330+
DROP TABLE t3;
291331
DROP DOMAIN domain1;
292332
DROP DOMAIN domain2;
293333
DROP DOMAIN domain3;
294334
DROP DOMAIN domain4;
335+
DROP DOMAIN domain5;
336+
DROP DOMAIN domain6;
337+
DROP DOMAIN domain7;
338+
DROP DOMAIN domain8;
295339
DROP FUNCTION foo(INT);
296340

297341
-- Fall back to full rewrite for volatile expressions

0 commit comments

Comments
 (0)