Fix list partition constraints for partition keys of array type.
authorRobert Haas
Wed, 31 Jan 2018 20:43:11 +0000 (15:43 -0500)
committerRobert Haas
Wed, 31 Jan 2018 21:08:39 +0000 (16:08 -0500)
The old code generated always generated a constraint of the form
col = ANY(ARRAY[val1, val2, ...]), but that's invalid when col is an
array type.  Instead, generate col = val when there's only one value,
col = val1 OR col = val2 OR ... when there are multiple values and
col is of array type, and the old form when there are multiple values
and col is not of an array type.

As a side benefit, this makes constraint exclusion able to prune
a list partition declared to accept a single Boolean value, which
didn't work before.

Amit Langote, reviewed by Etsuro Fujita

Discussion: http://postgr.es/m/97267195-e235-89d1-a41a-c110198dfce9@lab.ntt.co.jp

src/backend/catalog/partition.c
src/test/regress/expected/create_table.out
src/test/regress/expected/foreign_data.out
src/test/regress/sql/create_table.sql

index c2f304ec338c31873377488dd2732d1f7c9fa5cd..17704f36b9501c4c4b2476775298dc2df6815f63 100644 (file)
@@ -1249,18 +1249,60 @@ make_partition_op_expr(PartitionKey key, int keynum,
    {
        case PARTITION_STRATEGY_LIST:
            {
-               ScalarArrayOpExpr *saopexpr;
-
-               /* Build leftop = ANY (rightop) */
-               saopexpr = makeNode(ScalarArrayOpExpr);
-               saopexpr->opno = operoid;
-               saopexpr->opfuncid = get_opcode(operoid);
-               saopexpr->useOr = true;
-               saopexpr->inputcollid = key->partcollation[keynum];
-               saopexpr->args = list_make2(arg1, arg2);
-               saopexpr->location = -1;
-
-               result = (Expr *) saopexpr;
+               List       *elems = (List *) arg2;
+               int         nelems = list_length(elems);
+
+               Assert(nelems >= 1);
+               Assert(keynum == 0);
+
+               if (nelems > 1 &&
+                   !type_is_array(key->parttypid[keynum]))
+               {
+                   ArrayExpr  *arrexpr;
+                   ScalarArrayOpExpr *saopexpr;
+
+                   /* Construct an ArrayExpr for the right-hand inputs */
+                   arrexpr = makeNode(ArrayExpr);
+                   arrexpr->array_typeid =
+                                   get_array_type(key->parttypid[keynum]);
+                   arrexpr->array_collid = key->parttypcoll[keynum];
+                   arrexpr->element_typeid = key->parttypid[keynum];
+                   arrexpr->elements = elems;
+                   arrexpr->multidims = false;
+                   arrexpr->location = -1;
+
+                   /* Build leftop = ANY (rightop) */
+                   saopexpr = makeNode(ScalarArrayOpExpr);
+                   saopexpr->opno = operoid;
+                   saopexpr->opfuncid = get_opcode(operoid);
+                   saopexpr->useOr = true;
+                   saopexpr->inputcollid = key->partcollation[keynum];
+                   saopexpr->args = list_make2(arg1, arrexpr);
+                   saopexpr->location = -1;
+
+                   result = (Expr *) saopexpr;
+               }
+               else
+               {
+                   List       *elemops = NIL;
+                   ListCell   *lc;
+
+                   foreach (lc, elems)
+                   {
+                       Expr   *elem = lfirst(lc),
+                              *elemop;
+
+                       elemop = make_opclause(operoid,
+                                              BOOLOID,
+                                              false,
+                                              arg1, elem,
+                                              InvalidOid,
+                                              key->partcollation[keynum]);
+                       elemops = lappend(elemops, elemop);
+                   }
+
+                   result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops);
+               }
                break;
            }
 
@@ -1292,11 +1334,10 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 {
    List       *result;
    Expr       *keyCol;
-   ArrayExpr  *arr;
    Expr       *opexpr;
    NullTest   *nulltest;
    ListCell   *cell;
-   List       *arrelems = NIL;
+   List       *elems = NIL;
    bool        list_has_null = false;
 
    /*
@@ -1324,29 +1365,24 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
        if (val->constisnull)
            list_has_null = true;
        else
-           arrelems = lappend(arrelems, copyObject(val));
+           elems = lappend(elems, copyObject(val));
    }
 
-   if (arrelems)
+   if (elems)
    {
-       /* Construct an ArrayExpr for the non-null partition values */
-       arr = makeNode(ArrayExpr);
-       arr->array_typeid = !type_is_array(key->parttypid[0])
-           ? get_array_type(key->parttypid[0])
-           : key->parttypid[0];
-       arr->array_collid = key->parttypcoll[0];
-       arr->element_typeid = key->parttypid[0];
-       arr->elements = arrelems;
-       arr->multidims = false;
-       arr->location = -1;
-
-       /* Generate the main expression, i.e., keyCol = ANY (arr) */
+       /*
+        * Generate the operator expression from the non-null partition
+        * values.
+        */
        opexpr = make_partition_op_expr(key, 0, BTEqualStrategyNumber,
-                                       keyCol, (Expr *) arr);
+                                       keyCol, (Expr *) elems);
    }
    else
    {
-       /* If there are no partition values, we don't need an = ANY expr */
+       /*
+        * If there are no partition values, we don't need an operator
+        * expression.
+        */
        opexpr = NULL;
    }
 
index 9ecd45cec761536da09222a59c0518ed9a26b5f1..34305a96d02d4fc154945a633d9781f66a1ec484 100644 (file)
@@ -655,7 +655,7 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
  a      | text    |           |          |         | extended |              | 
  b      | integer |           | not null | 1       | plain    |              | 
 Partition of: parted FOR VALUES IN ('b')
-Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
+Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text))
 Check constraints:
     "check_a" CHECK (length(a) > 0)
     "part_b_b_check" CHECK (b >= 0)
@@ -668,7 +668,7 @@ Check constraints:
  a      | text    |           |          |         | extended |              | 
  b      | integer |           | not null | 0       | plain    |              | 
 Partition of: parted FOR VALUES IN ('c')
-Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])))
+Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text))
 Partition key: RANGE (b)
 Check constraints:
     "check_a" CHECK (length(a) > 0)
@@ -682,7 +682,7 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
  a      | text    |           |          |         | extended |              | 
  b      | integer |           | not null | 0       | plain    |              | 
 Partition of: part_c FOR VALUES FROM (1) TO (10)
-Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
+Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
 Check constraints:
     "check_a" CHECK (length(a) > 0)
 
@@ -770,3 +770,15 @@ SELECT obj_description('parted_col_comment'::regclass);
 Partition key: LIST (a)
 
 DROP TABLE parted_col_comment;
+-- list partitioning on array type column
+CREATE TABLE arrlp (a int[]) PARTITION BY LIST (a);
+CREATE TABLE arrlp12 PARTITION OF arrlp FOR VALUES IN ('{1}', '{2}');
+\d+ arrlp12
+                                   Table "public.arrlp12"
+ Column |   Type    | Collation | Nullable | Default | Storage  | Stats target | Description 
+--------+-----------+-----------+----------+---------+----------+--------------+-------------
+ a      | integer[] |           |          |         | extended |              | 
+Partition of: arrlp FOR VALUES IN ('{1}', '{2}')
+Partition constraint: ((a IS NOT NULL) AND (((a)::anyarray OPERATOR(pg_catalog.=) '{1}'::integer[]) OR ((a)::anyarray OPERATOR(pg_catalog.=) '{2}'::integer[])))
+
+DROP TABLE arrlp;
index c6e558b07f853ecd621f6968956eb5fbac813a55..8b5a56a0d3e3591cf7529351abee7631209e5810 100644 (file)
@@ -1847,7 +1847,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           |          |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
-Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
 Server: s0
 FDW options: (delimiter ',', quote '"', "be quoted" 'value')
 
@@ -1918,7 +1918,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           |          |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
-Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
 Server: s0
 FDW options: (delimiter ',', quote '"', "be quoted" 'value')
 
@@ -1946,7 +1946,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           | not null |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
-Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
 Check constraints:
     "p21chk" CHECK (c2 <> ''::text)
 Server: s0
index 2f097a0303c3fe4782c98777107d4be29d181ebf..c43fc56647f276b3510b6cff9dfa70e523d2db1d 100644 (file)
@@ -642,3 +642,9 @@ COMMENT ON COLUMN parted_col_comment.a IS 'Partition key';
 SELECT obj_description('parted_col_comment'::regclass);
 \d+ parted_col_comment
 DROP TABLE parted_col_comment;
+
+-- list partitioning on array type column
+CREATE TABLE arrlp (a int[]) PARTITION BY LIST (a);
+CREATE TABLE arrlp12 PARTITION OF arrlp FOR VALUES IN ('{1}', '{2}');
+\d+ arrlp12
+DROP TABLE arrlp;