aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-03-19 18:49:53 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2018-03-19 18:50:05 -0400
commit6497a18e6c1b5874566a77737ec3d381fded3ec2 (patch)
treece065f94d95b5871626cb5a4ba1aeaceeb84a7ff /src/backend/utils/adt/ruleutils.c
parent9ad21a6957ff2d8743e9a59ba062d3c009b24ec4 (diff)
downloadpostgresql-6497a18e6c1b5874566a77737ec3d381fded3ec2.tar.gz
postgresql-6497a18e6c1b5874566a77737ec3d381fded3ec2.zip
Fix some corner-case issues in REFRESH MATERIALIZED VIEW CONCURRENTLY.
refresh_by_match_merge() has some issues in the way it builds a SQL query to construct the "diff" table: 1. It doesn't require the selected unique index(es) to be indimmediate. 2. It doesn't pay attention to the particular equality semantics enforced by a given index, but just assumes that they must be those of the column datatype's default btree opclass. 3. It doesn't check that the indexes are btrees. 4. It's insufficiently careful to ensure that the parser will pick the intended operator when parsing the query. (This would have been a security bug before CVE-2018-1058.) 5. It's not careful about indexes on system columns. The way to fix #4 is to make use of the existing code in ri_triggers.c for generating an arbitrary binary operator clause. I chose to move that to ruleutils.c, since that seems a more reasonable place to be exporting such functionality from than ri_triggers.c. While #1, #3, and #5 are just latent given existing feature restrictions, and #2 doesn't arise in the core system for lack of alternate opclasses with different equality behaviors, #4 seems like an issue worth back-patching. That's the bulk of the change anyway, so just back-patch the whole thing to 9.4 where this code was introduced. Discussion: https://postgr.es/m/13836.1521413227@sss.pgh.pa.us
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b58ee3c3876..2cd54ec33fe 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -462,6 +462,7 @@ static char *generate_function_name(Oid funcid, int nargs,
bool has_variadic, bool *use_variadic_p,
ParseExprKind special_exprkind);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
+static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
@@ -10850,6 +10851,85 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
}
/*
+ * generate_operator_clause --- generate a binary-operator WHERE clause
+ *
+ * This is used for internally-generated-and-executed SQL queries, where
+ * precision is essential and readability is secondary. The basic
+ * requirement is to append "leftop op rightop" to buf, where leftop and
+ * rightop are given as strings and are assumed to yield types leftoptype
+ * and rightoptype; the operator is identified by OID. The complexity
+ * comes from needing to be sure that the parser will select the desired
+ * operator when the query is parsed. We always name the operator using
+ * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
+ * We have to emit casts too, if either input isn't already the input type
+ * of the operator; else we are at the mercy of the parser's heuristics for
+ * ambiguous-operator resolution. The caller must ensure that leftop and
+ * rightop are suitable arguments for a cast operation; it's best to insert
+ * parentheses if they aren't just variables or parameters.
+ */
+void
+generate_operator_clause(StringInfo buf,
+ const char *leftop, Oid leftoptype,
+ Oid opoid,
+ const char *rightop, Oid rightoptype)
+{
+ HeapTuple opertup;
+ Form_pg_operator operform;
+ char *oprname;
+ char *nspname;
+
+ opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
+ if (!HeapTupleIsValid(opertup))
+ elog(ERROR, "cache lookup failed for operator %u", opoid);
+ operform = (Form_pg_operator) GETSTRUCT(opertup);
+ Assert(operform->oprkind == 'b');
+ oprname = NameStr(operform->oprname);
+
+ nspname = get_namespace_name(operform->oprnamespace);
+
+ appendStringInfoString(buf, leftop);
+ if (leftoptype != operform->oprleft)
+ add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ add_cast_to(buf, operform->oprright);
+
+ ReleaseSysCache(opertup);
+}
+
+/*
+ * Add a cast specification to buf. We spell out the type name the hard way,
+ * intentionally not using format_type_be(). This is to avoid corner cases
+ * for CHARACTER, BIT, and perhaps other types, where specifying the type
+ * using SQL-standard syntax results in undesirable data truncation. By
+ * doing it this way we can be certain that the cast will have default (-1)
+ * target typmod.
+ */
+static void
+add_cast_to(StringInfo buf, Oid typid)
+{
+ HeapTuple typetup;
+ Form_pg_type typform;
+ char *typname;
+ char *nspname;
+
+ typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(typetup))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ typform = (Form_pg_type) GETSTRUCT(typetup);
+
+ typname = NameStr(typform->typname);
+ nspname = get_namespace_name(typform->typnamespace);
+
+ appendStringInfo(buf, "::%s.%s",
+ quote_identifier(nspname), quote_identifier(typname));
+
+ ReleaseSysCache(typetup);
+}
+
+/*
* generate_qualified_type_name
* Compute the name to display for a type specified by OID
*