Re: unsupportable composite type partition keys - Mailing list pgsql-hackers

From Tom Lane
Subject Re: unsupportable composite type partition keys
Date
Msg-id 26766.1576947735@sss.pgh.pa.us
Whole thread Raw
In response to Re: unsupportable composite type partition keys  (Amit Langote <amitlangote09@gmail.com>)
Responses Re: unsupportable composite type partition keys
Re: unsupportable composite type partition keys
List pgsql-hackers
Amit Langote <amitlangote09@gmail.com> writes:
> On Wed, Dec 18, 2019 at 10:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> My point is basically that CheckAttributeType already covers that
>> issue, as well as a lot of others.  So why isn't the partitioning
>> code using it?

> My reason to not use it was that the error message that are produced
> are not quite helpful in this case;

I can't get terribly excited about that; but in any case, if we think
the errors aren't nice enough, the answer is to improve them, not
re-implement the function badly.

After further thought, it seems to me that we are dealing with two
nearly independent issues:

1. We must not accept partition bounds values that are of underdetermined
types, else (a) we are likely to get failures like "record type has not
been registered" while loading them back from disk, and (b) polymorphic
btree support functions are likely to complain that they can't identify
the type they're supposed to work on.  This is exactly the same issue that
expression indexes face, so we should be applying the same checks, that
is CheckAttributeType().  I do not believe that checking for RECORD is
adequate to close this hole.  At the very least, RECORD[] is equally
dangerous, and in general I think any pseudotype would be risky.

2. If the partitioning expression contains a reference to the partitioned
table's rowtype, we get infinite recursion while trying to load the
relcache entry.  The patch proposes to prevent that by checking whether
the expression's final result type is that type, but that's not nearly
adequate because a reference anywhere inside the expression is just as
bad.  In general, considering possibly-inlined SQL functions, I'm doubtful
that any precheck is going to be able to prevent this scenario.

Now as far as point 1 goes, I think it's not really that awful to use
CheckAttributeType() with a dummy attribute name.  The attached
incomplete patch uses "partition key" which causes it to emit errors
like

regression=# create table fool (a int, b int) partition by list ((row(a, b)));
ERROR:  column "partition key" has pseudo-type record

I don't think that that's unacceptable.  But if we wanted to improve it,
we could imagine adding another flag, say CHKATYPE_IS_PARTITION_KEY,
that doesn't affect CheckAttributeType's semantics, just the wording of
the error messages it throws.

As far as point 2 goes, I think this is another outgrowth of the
fundamental design error that we load a partitioned rel's partitioning
info immediately when the relcache entry is created, rather than later
on-demand.  If we weren't doing that then it wouldn't be problematic
to inspect the rel's rowtype while constructing the partitioning info.
I've bitched about this before, if memory serves, but couldn't light
a fire under anyone about fixing it.  Now I think we have no choice.
It was never a great idea that minimal construction of a relcache
entry could result in running arbitrary user-defined code.

Note that the end result of this would be to allow, not prohibit,
cases like your example.  I wonder whether we couldn't also lift
the restriction against whole-row Vars in partition expressions.
Doesn't seem like there is much difference between such a Var and
a row(...)::table_rowtype expression.

            regards, tom lane

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e8e004e..29efc9c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15102,6 +15102,16 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
             attcollation = exprCollation(expr);

             /*
+             * The expression must be of a storable type (e.g., not RECORD).
+             * The test is the same as for whether a table column is of a safe
+             * type (which is why we needn't check for the non-expression
+             * case).
+             */
+            CheckAttributeType("partition key",
+                               atttype, attcollation,
+                               NIL, 0);
+
+            /*
              * Strip any top-level COLLATE clause.  This ensures that we treat
              * "x COLLATE y" and "(x COLLATE y)" alike.
              */

pgsql-hackers by date:

Previous
From: Tomas Vondra
Date:
Subject: Re: Optimizing TransactionIdIsCurrentTransactionId()
Next
From: Bruce Momjian
Date:
Subject: Re: Misleading comment in pg_upgrade.c