aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/common
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-04-06 10:34:37 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-04-06 10:34:39 -0400
commit091e22b2e673e3e8480abd68fbb827c5d6979615 (patch)
tree837fd7f82f0e9d269c6e85f2ca395457f05fa3a1 /src/backend/access/common
parent9de9294b0c4dac77edb80f029648afca79d14653 (diff)
downloadpostgresql-091e22b2e673e3e8480abd68fbb827c5d6979615.tar.gz
postgresql-091e22b2e673e3e8480abd68fbb827c5d6979615.zip
Clean up treatment of missing default and CHECK-constraint records.
Andrew Gierth reported that it's possible to crash the backend if no pg_attrdef record is found to match an attribute that has atthasdef set. AttrDefaultFetch warns about this situation, but then leaves behind a relation tupdesc that has null "adbin" pointer(s), which most places don't guard against. We considered promoting the warning to an error, but throwing errors during relcache load is pretty drastic: it effectively locks one out of using the relation at all. What seems better is to leave the load-time behavior as a warning, but then throw an error in any code path that wants to use a default and can't find it. This confines the error to a subset of INSERT/UPDATE operations on the table, and in particular will at least allow a pg_dump to succeed. Also, we should fix AttrDefaultFetch to not leave any null pointers in the tupdesc, because that just creates an untested bug hazard. While at it, apply the same philosophy of "warn at load, throw error only upon use of the known-missing info" to CHECK constraints. CheckConstraintFetch is very nearly the same logic as AttrDefaultFetch, but for reasons lost in the mists of time, it was throwing ERROR for the same cases that AttrDefaultFetch treats as WARNING. Make the two functions more nearly alike. In passing, get rid of potentially-O(N^2) loops in equalTupleDesc by making AttrDefaultFetch sort the entries after fetching them, so that equalTupleDesc can assume that entries in two equal tupdescs must be in matching order. (CheckConstraintFetch already was sorting CHECK constraints, but equalTupleDesc hadn't been told about it.) There's some argument for back-patching this, but with such a small number of field reports, I'm content to fix it in HEAD. Discussion: https://postgr.es/m/87pmzaq4gx.fsf@news-spur.riddles.org.uk
Diffstat (limited to 'src/backend/access/common')
-rw-r--r--src/backend/access/common/tupdesc.c68
1 files changed, 22 insertions, 46 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index cb76465050b..ffb275afbee 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -174,10 +174,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
for (i = cpy->num_defval - 1; i >= 0; i--)
- {
- if (constr->defval[i].adbin)
- cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
- }
+ cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
}
if (constr->missing)
@@ -203,10 +200,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
for (i = cpy->num_check - 1; i >= 0; i--)
{
- if (constr->check[i].ccname)
- cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
- if (constr->check[i].ccbin)
- cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
+ cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
+ cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
cpy->check[i].ccvalid = constr->check[i].ccvalid;
cpy->check[i].ccnoinherit = constr->check[i].ccnoinherit;
}
@@ -328,10 +323,7 @@ FreeTupleDesc(TupleDesc tupdesc)
AttrDefault *attrdef = tupdesc->constr->defval;
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
- {
- if (attrdef[i].adbin)
- pfree(attrdef[i].adbin);
- }
+ pfree(attrdef[i].adbin);
pfree(attrdef);
}
if (tupdesc->constr->missing)
@@ -352,10 +344,8 @@ FreeTupleDesc(TupleDesc tupdesc)
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
{
- if (check[i].ccname)
- pfree(check[i].ccname);
- if (check[i].ccbin)
- pfree(check[i].ccbin);
+ pfree(check[i].ccname);
+ pfree(check[i].ccbin);
}
pfree(check);
}
@@ -412,7 +402,6 @@ bool
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
{
int i,
- j,
n;
if (tupdesc1->natts != tupdesc2->natts)
@@ -488,22 +477,13 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
n = constr1->num_defval;
if (n != (int) constr2->num_defval)
return false;
+ /* We assume here that both AttrDefault arrays are in adnum order */
for (i = 0; i < n; i++)
{
AttrDefault *defval1 = constr1->defval + i;
- AttrDefault *defval2 = constr2->defval;
-
- /*
- * We can't assume that the items are always read from the system
- * catalogs in the same order; so use the adnum field to identify
- * the matching item to compare.
- */
- for (j = 0; j < n; defval2++, j++)
- {
- if (defval1->adnum == defval2->adnum)
- break;
- }
- if (j >= n)
+ AttrDefault *defval2 = constr2->defval + i;
+
+ if (defval1->adnum != defval2->adnum)
return false;
if (strcmp(defval1->adbin, defval2->adbin) != 0)
return false;
@@ -534,25 +514,21 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
n = constr1->num_check;
if (n != (int) constr2->num_check)
return false;
+
+ /*
+ * Similarly, we rely here on the ConstrCheck entries being sorted by
+ * name. If there are duplicate names, the outcome of the comparison
+ * is uncertain, but that should not happen.
+ */
for (i = 0; i < n; i++)
{
ConstrCheck *check1 = constr1->check + i;
- ConstrCheck *check2 = constr2->check;
-
- /*
- * Similarly, don't assume that the checks are always read in the
- * same order; match them up by name and contents. (The name
- * *should* be unique, but...)
- */
- for (j = 0; j < n; check2++, j++)
- {
- if (strcmp(check1->ccname, check2->ccname) == 0 &&
- strcmp(check1->ccbin, check2->ccbin) == 0 &&
- check1->ccvalid == check2->ccvalid &&
- check1->ccnoinherit == check2->ccnoinherit)
- break;
- }
- if (j >= n)
+ ConstrCheck *check2 = constr2->check + i;
+
+ if (!(strcmp(check1->ccname, check2->ccname) == 0 &&
+ strcmp(check1->ccbin, check2->ccbin) == 0 &&
+ check1->ccvalid == check2->ccvalid &&
+ check1->ccnoinherit == check2->ccnoinherit))
return false;
}
}