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

Commit dbcff68

Browse files
author
Commitfest Bot
committed
[CF 5444] v6 - using index to speedup add not null constraints to a table
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5444 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxHNpusqZOhckVZxGAE2zOOWU0YTJvs3nob38XL=8zArpw@mail.gmail.com Author(s): Jian He
2 parents 44ce4e1 + f61d923 commit dbcff68

File tree

8 files changed

+630
-5
lines changed

8 files changed

+630
-5
lines changed

src/backend/commands/tablecmds.c

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,19 @@ typedef struct AlteredTableInfo
209209
List *changedStatisticsDefs; /* string definitions of same */
210210
} AlteredTableInfo;
211211

212-
/* Struct describing one new constraint to check in Phase 3 scan */
213-
/* Note: new not-null constraints are handled elsewhere */
212+
/*
213+
* Struct describing one new constraint to check in Phase 3 scan. Note: new
214+
* not-null constraints are handled here too.
215+
*/
214216
typedef struct NewConstraint
215217
{
216218
char *name; /* Constraint name, or NULL if none */
217-
ConstrType contype; /* CHECK or FOREIGN */
219+
ConstrType contype; /* CHECK or FOREIGN or NOT NULL */
218220
Oid refrelid; /* PK rel, if FOREIGN */
219221
Oid refindid; /* OID of PK's index, if FOREIGN */
220222
bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221223
Oid conid; /* OID of pg_constraint entry, if FOREIGN */
224+
int attnum; /* NOT NULL constraint attribute number */
222225
Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223226
ExprState *qualstate; /* Execution state for CHECK expr */
224227
} NewConstraint;
@@ -6182,6 +6185,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
61826185
needscan = true;
61836186
con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
61846187
break;
6188+
case CONSTR_NOTNULL:
6189+
/* Nothing to do here */
6190+
break;
61856191
case CONSTR_FOREIGN:
61866192
/* Nothing to do here */
61876193
break;
@@ -6235,9 +6241,81 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
62356241
wholeatt->attnum);
62366242
}
62376243
}
6238-
if (notnull_attrs || notnull_virtual_attrs)
6244+
}
6245+
6246+
/*
6247+
* The conditions for using indexscan mechanism fast vertifing not-null
6248+
* constraints are quite strict. All of the following conditions must be
6249+
* met.
6250+
* 1. We must have the need to validate not-null constraints
6251+
* That means AlteredTableInfo->verify_new_notnull must be true.
6252+
* 2. If table scan or table rewrite is expected later, using indexscan is
6253+
* just wastes cycles.
6254+
* 3. Indexes cannot be created on virtual generated columns, so fast
6255+
* checking not-null constraints is not applicable to them.
6256+
* 4. Only apply to regular relations.
6257+
* 5. To avoid concurrency issue, we only do it when table was locked with
6258+
* an AccessExclusiveLock, which is the lock acquired during ALTER TABLE
6259+
* SET NOT NULL.
6260+
*/
6261+
if (!needscan &&
6262+
newrel == NULL &&
6263+
!tab->rewrite &&
6264+
tab->verify_new_notnull &&
6265+
notnull_virtual_attrs == NIL &&
6266+
oldrel->rd_rel->relkind == RELKIND_RELATION &&
6267+
CheckRelationLockedByMe(oldrel, AccessExclusiveLock, false))
6268+
{
6269+
List *notnull_attnums = NIL;
6270+
6271+
foreach(l, tab->constraints)
6272+
{
6273+
NewConstraint *con = lfirst(l);
6274+
6275+
Form_pg_attribute attr = TupleDescAttr(newTupDesc, con->attnum - 1);
6276+
6277+
if (con->contype != CONSTR_NOTNULL)
6278+
continue;
6279+
6280+
Assert(attr->attnotnull);
6281+
Assert(con->attnum > 0);
6282+
Assert(attr->attnum == con->attnum);
6283+
6284+
if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6285+
{
6286+
needscan = true;
6287+
break;
6288+
}
6289+
6290+
notnull_attnums = lappend_int(notnull_attnums, attr->attnum);
6291+
}
6292+
6293+
/*
6294+
* notnull_attnums contains all attributes with NOT NULL constraints
6295+
* that need validation. If needscan is true, later table scan
6296+
* will do the validation, indedxscan would just waste cycle.
6297+
*/
6298+
if (notnull_attnums != NIL && !needscan)
6299+
{
6300+
if (!index_check_notnull(oldrel, notnull_attnums))
6301+
needscan = true;
6302+
else
6303+
ereport(DEBUG1,
6304+
errmsg_internal("all new not-null constraints on relation \"%s\" have been validated by using index scan",
6305+
RelationGetRelationName(oldrel)));
6306+
}
6307+
else
6308+
{
6309+
/*
6310+
* corner case: in Phase2, somehow we didn't add the NOT NULL
6311+
* constraint to AlteredTableInfo->constraints, but already set
6312+
* verify_new_notnull to true then table scan is needed.
6313+
*/
62396314
needscan = true;
6315+
}
62406316
}
6317+
else if (notnull_attrs || notnull_virtual_attrs)
6318+
needscan = true;
62416319

62426320
if (newrel || needscan)
62436321
{
@@ -7910,6 +7988,7 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
79107988
CookedConstraint *ccon;
79117989
List *cooked;
79127990
bool is_no_inherit = false;
7991+
AlteredTableInfo *tab = NULL;
79137992

79147993
/* Guard against stack overflow due to overly deep inheritance tree. */
79157994
check_stack_depth();
@@ -8034,10 +8113,31 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
80348113
constraint->is_no_inherit = is_no_inherit;
80358114
constraint->conname = conName;
80368115

8116+
tab = ATGetQueueEntry(wqueue, rel);
8117+
80378118
/* and do it */
80388119
cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
80398120
false, !recursing, false, NULL);
80408121
ccon = linitial(cooked);
8122+
8123+
Assert(ccon->contype == CONSTR_NOTNULL);
8124+
8125+
/*
8126+
* we may use indexscan to validate not-null constraint in Phase3. Add the
8127+
* to-be-validated not-null constraint to Phase 3's queue.
8128+
*/
8129+
if (!ccon->skip_validation)
8130+
{
8131+
NewConstraint *newcon;
8132+
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
8133+
8134+
newcon->name = ccon->name;
8135+
newcon->contype = ccon->contype;
8136+
newcon->attnum = ccon->attnum;
8137+
8138+
tab->constraints = lappend(tab->constraints, newcon);
8139+
}
8140+
80418141
ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
80428142

80438143
InvokeObjectPostAlterHook(RelationRelationId,
@@ -9918,13 +10018,15 @@ ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
991810018
{
991910019
CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
992010020

9921-
if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
10021+
if (!ccon->skip_validation)
992210022
{
992310023
NewConstraint *newcon;
992410024

992510025
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
992610026
newcon->name = ccon->name;
992710027
newcon->contype = ccon->contype;
10028+
if (ccon->contype == CONSTR_NOTNULL)
10029+
newcon->attnum = ccon->attnum;
992810030
newcon->qual = ccon->expr;
992910031

993010032
tab->constraints = lappend(tab->constraints, newcon);
@@ -13166,6 +13268,7 @@ QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
1316613268
List *children = NIL;
1316713269
AttrNumber attnum;
1316813270
char *colname;
13271+
NewConstraint *newcon;
1316913272

1317013273
con = (Form_pg_constraint) GETSTRUCT(contuple);
1317113274
Assert(con->contype == CONSTRAINT_NOTNULL);
@@ -13229,7 +13332,19 @@ QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
1322913332
/* Set attnotnull appropriately without queueing another validation */
1323013333
set_attnotnull(NULL, rel, attnum, true, false);
1323113334

13335+
/*
13336+
* Queue validation for phase 3. ALTER TABLE SET NOT NULL will add NOT NULL
13337+
* constraint to AlteredTableInfo->constraints, for consistency, do the same
13338+
* here.
13339+
*/
13340+
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13341+
newcon->name = colname;
13342+
newcon->contype = CONSTR_NOTNULL;
13343+
newcon->attnum = attnum;
13344+
13345+
/* Find or create work queue entry for this table */
1323213346
tab = ATGetQueueEntry(wqueue, rel);
13347+
tab->constraints = lappend(tab->constraints, newcon);
1323313348
tab->verify_new_notnull = true;
1323413349

1323513350
/*

0 commit comments

Comments
 (0)