Commit cb3a7c2b authored by Alvaro Herrera's avatar Alvaro Herrera

ALTER TABLE: skip FK validation when it's safe to do so

We already skip rewriting the table in these cases, but we still force a
whole table scan to validate the data.  This can be skipped, and thus
we can make the whole ALTER TABLE operation just do some catalog touches
instead of scanning the table, when these two conditions hold:

(a) Old and new pg_constraint.conpfeqop match exactly.  This is actually
stronger than needed; we could loosen things by way of operator
families, but it'd require a lot more effort.

(b) The functions, if any, implementing a cast from the foreign type to
the primary opcintype are the same.  For this purpose, we can consider a
binary coercion equivalent to an exact type match.  When the opcintype
is polymorphic, require that the old and new foreign types match
exactly.  (Since ri_triggers.c does use the executor, the stronger check
for polymorphic types is no mere future-proofing.  However, no core type
exercises its necessity.)

Author: Noah Misch

Committer's note: catalog version bumped due to change of the Constraint
node.  I can't actually find any way to have such a node in a stored
rule, but given that we have "out" support for them, better be safe.
parent 9bf8603c
This diff is collapsed.
...@@ -2364,6 +2364,7 @@ _copyConstraint(const Constraint *from) ...@@ -2364,6 +2364,7 @@ _copyConstraint(const Constraint *from)
COPY_SCALAR_FIELD(fk_matchtype); COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action); COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action); COPY_SCALAR_FIELD(fk_del_action);
COPY_NODE_FIELD(old_conpfeqop);
COPY_SCALAR_FIELD(skip_validation); COPY_SCALAR_FIELD(skip_validation);
COPY_SCALAR_FIELD(initially_valid); COPY_SCALAR_FIELD(initially_valid);
......
...@@ -2199,6 +2199,7 @@ _equalConstraint(const Constraint *a, const Constraint *b) ...@@ -2199,6 +2199,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_SCALAR_FIELD(fk_matchtype); COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action); COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action); COMPARE_SCALAR_FIELD(fk_del_action);
COMPARE_NODE_FIELD(old_conpfeqop);
COMPARE_SCALAR_FIELD(skip_validation); COMPARE_SCALAR_FIELD(skip_validation);
COMPARE_SCALAR_FIELD(initially_valid); COMPARE_SCALAR_FIELD(initially_valid);
......
...@@ -2626,6 +2626,7 @@ _outConstraint(StringInfo str, const Constraint *node) ...@@ -2626,6 +2626,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_CHAR_FIELD(fk_matchtype); WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action); WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action); WRITE_CHAR_FIELD(fk_del_action);
WRITE_NODE_FIELD(old_conpfeqop);
WRITE_BOOL_FIELD(skip_validation); WRITE_BOOL_FIELD(skip_validation);
WRITE_BOOL_FIELD(initially_valid); WRITE_BOOL_FIELD(initially_valid);
break; break;
......
...@@ -3224,6 +3224,7 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo, ...@@ -3224,6 +3224,7 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
elog(ERROR, "null conpfeqop for constraint %u", constraintOid); elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
numkeys = ARR_DIMS(arr)[0]; numkeys = ARR_DIMS(arr)[0];
/* see TryReuseForeignKey if you change the test below */
if (ARR_NDIM(arr) != 1 || if (ARR_NDIM(arr) != 1 ||
numkeys != riinfo->nkeys || numkeys != riinfo->nkeys ||
numkeys > RI_MAX_NUMKEYS || numkeys > RI_MAX_NUMKEYS ||
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201202191 #define CATALOG_VERSION_NO 201202271
#endif #endif
...@@ -1552,6 +1552,7 @@ typedef struct Constraint ...@@ -1552,6 +1552,7 @@ typedef struct Constraint
char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */ char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */
char fk_upd_action; /* ON UPDATE action */ char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */ char fk_del_action; /* ON DELETE action */
List *old_conpfeqop; /* pg_constraint.conpfeqop of my former self */
/* Fields used for constraints that allow a NOT VALID specification */ /* Fields used for constraints that allow a NOT VALID specification */
bool skip_validation; /* skip validation of existing rows? */ bool skip_validation; /* skip validation of existing rows? */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment