Commit dced55da authored by Tom Lane's avatar Tom Lane

More code review for get_qual_for_list().

Avoid trashing the input PartitionBoundSpec; while that might be safe for
current callers, it's certainly trouble waiting to happen.  In the same
vein, make sure that all of the result data structure is freshly palloc'd,
rather than some of it being pointers into the input data structures
(which we don't know the lifespans of).

Simplify the logic for tacking on IS NULL or IS NOT NULL conditions some
more; commit 85c2b9a1 left a lot on the table there.  And rearrange the
construction of the nodes into (what seems to me) a more logical order.

In passing, make sure that get_qual_for_range() also returns a freshly
palloc'd structure, since there's no value in having that guarantee for
only one kind of partitioning.  And improve some comments there.

Jeevan Ladhe, with further tweaking by me

Discussion: https://postgr.es/m/CAOgcT0MAcYoMs93W80iTUf_dP36=1mZQzeUk+nnwY_-qWDrCfw@mail.gmail.com
parent 917d9128
...@@ -1296,23 +1296,22 @@ make_partition_op_expr(PartitionKey key, int keynum, ...@@ -1296,23 +1296,22 @@ make_partition_op_expr(PartitionKey key, int keynum,
/* /*
* get_qual_for_list * get_qual_for_list
* *
* Returns a list of expressions to use as a list partition's constraint. * Returns an implicit-AND list of expressions to use as a list partition's
* constraint, given the partition key and bound structures.
*/ */
static List * static List *
get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
{ {
List *result; List *result;
Expr *keyCol;
ArrayExpr *arr; ArrayExpr *arr;
Expr *opexpr; Expr *opexpr;
ListCell *cell, NullTest *nulltest;
*prev, ListCell *cell;
*next; List *arrelems = NIL;
Expr *keyCol;
bool list_has_null = false; bool list_has_null = false;
NullTest *nulltest1 = NULL,
*nulltest2 = NULL;
/* Left operand is either a simple Var or arbitrary expression */ /* Construct Var or expression representing the partition column */
if (key->partattrs[0] != 0) if (key->partattrs[0] != 0)
keyCol = (Expr *) makeVar(1, keyCol = (Expr *) makeVar(1,
key->partattrs[0], key->partattrs[0],
...@@ -1323,59 +1322,25 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1323,59 +1322,25 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
else else
keyCol = (Expr *) copyObject(linitial(key->partexprs)); keyCol = (Expr *) copyObject(linitial(key->partexprs));
/* /* Create list of Consts for the allowed values, excluding any nulls */
* We must remove any NULL value in the list; we handle it separately foreach(cell, spec->listdatums)
* below.
*/
prev = NULL;
for (cell = list_head(spec->listdatums); cell; cell = next)
{ {
Const *val = castNode(Const, lfirst(cell)); Const *val = castNode(Const, lfirst(cell));
next = lnext(cell);
if (val->constisnull) if (val->constisnull)
{
list_has_null = true; list_has_null = true;
spec->listdatums = list_delete_cell(spec->listdatums,
cell, prev);
}
else else
prev = cell; arrelems = lappend(arrelems, copyObject(val));
}
if (!list_has_null)
{
/*
* Gin up a col IS NOT NULL test that will be AND'd with other
* expressions
*/
nulltest1 = makeNode(NullTest);
nulltest1->arg = keyCol;
nulltest1->nulltesttype = IS_NOT_NULL;
nulltest1->argisrow = false;
nulltest1->location = -1;
}
else
{
/*
* Gin up a col IS NULL test that will be OR'd with other expressions
*/
nulltest2 = makeNode(NullTest);
nulltest2->arg = keyCol;
nulltest2->nulltesttype = IS_NULL;
nulltest2->argisrow = false;
nulltest2->location = -1;
} }
/* Right operand is an ArrayExpr containing this partition's values */ /* Construct an ArrayExpr for the non-null partition values */
arr = makeNode(ArrayExpr); arr = makeNode(ArrayExpr);
arr->array_typeid = !type_is_array(key->parttypid[0]) arr->array_typeid = !type_is_array(key->parttypid[0])
? get_array_type(key->parttypid[0]) ? get_array_type(key->parttypid[0])
: key->parttypid[0]; : key->parttypid[0];
arr->array_collid = key->parttypcoll[0]; arr->array_collid = key->parttypcoll[0];
arr->element_typeid = key->parttypid[0]; arr->element_typeid = key->parttypid[0];
arr->elements = spec->listdatums; arr->elements = arrelems;
arr->multidims = false; arr->multidims = false;
arr->location = -1; arr->location = -1;
...@@ -1383,14 +1348,36 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1383,14 +1348,36 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber, opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber,
keyCol, (Expr *) arr); keyCol, (Expr *) arr);
if (nulltest1) if (!list_has_null)
result = list_make2(nulltest1, opexpr); {
/*
* Gin up a "col IS NOT NULL" test that will be AND'd with the main
* expression. This might seem redundant, but the partition routing
* machinery needs it.
*/
nulltest = makeNode(NullTest);
nulltest->arg = keyCol;
nulltest->nulltesttype = IS_NOT_NULL;
nulltest->argisrow = false;
nulltest->location = -1;
result = list_make2(nulltest, opexpr);
}
else else
{ {
/*
* Gin up a "col IS NULL" test that will be OR'd with the main
* expression.
*/
Expr *or; Expr *or;
Assert(nulltest2 != NULL); nulltest = makeNode(NullTest);
or = makeBoolExpr(OR_EXPR, list_make2(nulltest2, opexpr), -1); nulltest->arg = keyCol;
nulltest->nulltesttype = IS_NULL;
nulltest->argisrow = false;
nulltest->location = -1;
or = makeBoolExpr(OR_EXPR, list_make2(nulltest, opexpr), -1);
result = list_make1(or); result = list_make1(or);
} }
...@@ -1401,8 +1388,16 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1401,8 +1388,16 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
* get_range_key_properties * get_range_key_properties
* Returns range partition key information for a given column * Returns range partition key information for a given column
* *
* On return, *partexprs_item points to the cell containing the next * This is a subroutine for get_qual_for_range, and its API is pretty
* expression in the key->partexprs list, or NULL. * specialized to that caller.
*
* Constructs an Expr for the key column (returned in *keyCol) and Consts
* for the lower and upper range limits (returned in *lower_val and
* *upper_val). For UNBOUNDED limits, NULL is returned instead of a Const.
* All of these structures are freshly palloc'd.
*
* *partexprs_item points to the cell containing the next expression in
* the key->partexprs list, or NULL. It may be advanced upon return.
*/ */
static void static void
get_range_key_properties(PartitionKey key, int keynum, get_range_key_properties(PartitionKey key, int keynum,
...@@ -1412,7 +1407,7 @@ get_range_key_properties(PartitionKey key, int keynum, ...@@ -1412,7 +1407,7 @@ get_range_key_properties(PartitionKey key, int keynum,
Expr **keyCol, Expr **keyCol,
Const **lower_val, Const **upper_val) Const **lower_val, Const **upper_val)
{ {
/* Partition key expression for this column */ /* Get partition key expression for this column */
if (key->partattrs[keynum] != 0) if (key->partattrs[keynum] != 0)
{ {
*keyCol = (Expr *) makeVar(1, *keyCol = (Expr *) makeVar(1,
...@@ -1424,17 +1419,20 @@ get_range_key_properties(PartitionKey key, int keynum, ...@@ -1424,17 +1419,20 @@ get_range_key_properties(PartitionKey key, int keynum,
} }
else else
{ {
if (*partexprs_item == NULL)
elog(ERROR, "wrong number of partition key expressions");
*keyCol = copyObject(lfirst(*partexprs_item)); *keyCol = copyObject(lfirst(*partexprs_item));
*partexprs_item = lnext(*partexprs_item); *partexprs_item = lnext(*partexprs_item);
} }
/* Get appropriate Const nodes for the bounds */
if (!ldatum->infinite) if (!ldatum->infinite)
*lower_val = castNode(Const, ldatum->value); *lower_val = castNode(Const, copyObject(ldatum->value));
else else
*lower_val = NULL; *lower_val = NULL;
if (!udatum->infinite) if (!udatum->infinite)
*upper_val = castNode(Const, udatum->value); *upper_val = castNode(Const, copyObject(udatum->value));
else else
*upper_val = NULL; *upper_val = NULL;
} }
...@@ -1442,9 +1440,8 @@ get_range_key_properties(PartitionKey key, int keynum, ...@@ -1442,9 +1440,8 @@ get_range_key_properties(PartitionKey key, int keynum,
/* /*
* get_qual_for_range * get_qual_for_range
* *
* Get a list of expressions to use as a range partition's constraint. * Returns an implicit-AND list of expressions to use as a range partition's
* If there are multiple expressions, they are to be considered implicitly * constraint, given the partition key and bound structures.
* ANDed.
* *
* For a multi-column range partition key, say (a, b, c), with (al, bl, cl) * For a multi-column range partition key, say (a, b, c), with (al, bl, cl)
* as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we
...@@ -1480,9 +1477,8 @@ get_range_key_properties(PartitionKey key, int keynum, ...@@ -1480,9 +1477,8 @@ get_range_key_properties(PartitionKey key, int keynum,
* does not really have a constraint, except the IS NOT NULL constraint for * does not really have a constraint, except the IS NOT NULL constraint for
* partition keys. * partition keys.
* *
* If we end up with an empty result list, we append return a single-member * If we end up with an empty result list, we return a single-member list
* list containing a constant-true expression in that case, because callers * containing a constant TRUE, because callers expect a non-empty list.
* expect a non-empty list.
*/ */
static List * static List *
get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
...@@ -1573,7 +1569,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1573,7 +1569,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
/* /*
* Since get_range_key_properties() modifies partexprs_item, and we * Since get_range_key_properties() modifies partexprs_item, and we
* might need to start over from the previous expression in the later * might need to start over from the previous expression in the later
* part of this functiom, save away the current value. * part of this function, save away the current value.
*/ */
partexprs_item_saved = partexprs_item; partexprs_item_saved = partexprs_item;
...@@ -1638,6 +1634,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1638,6 +1634,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
List *lower_or_arm_args = NIL, List *lower_or_arm_args = NIL,
*upper_or_arm_args = NIL; *upper_or_arm_args = NIL;
/* Restart scan of columns from the i'th one */
j = i; j = i;
partexprs_item = partexprs_item_saved; partexprs_item = partexprs_item_saved;
...@@ -1702,7 +1699,6 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1702,7 +1699,6 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
strategy, strategy,
keyCol, keyCol,
(Expr *) upper_val)); (Expr *) upper_val));
} }
/* /*
...@@ -1759,7 +1755,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) ...@@ -1759,7 +1755,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
: linitial(upper_or_arms)); : linitial(upper_or_arms));
/* As noted above, caller expects the list to be non-empty. */ /* As noted above, caller expects the list to be non-empty. */
if (result == NULL) if (result == NIL)
result = list_make1(makeBoolConst(true, false)); result = list_make1(makeBoolConst(true, false));
return result; return result;
......
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