Fix crash when using COLLATE in partition bound expressions
authorMichael Paquier
Wed, 8 Apr 2020 06:04:57 +0000 (15:04 +0900)
committerMichael Paquier
Wed, 8 Apr 2020 06:04:57 +0000 (15:04 +0900)
Attempting to use a COLLATE clause with a type that it not collatable in
a partition bound expression could crash the server.  This commit fixes
the code by adding more checks similar to what is done when computing
index or partition attributes by making sure that there is a collation
iff the type is collatable.

Backpatch down to 12, as 7c079d7 introduced this problem.

Reported-by: Alexander Lakhin
Author: Dmitry Dolgov
Discussion: https://postgr.es/m/16325-809194cf742313ab@postgresql.org
Backpatch-through: 12

src/backend/parser/parse_utilcmd.c
src/test/regress/expected/create_table.out
src/test/regress/sql/create_table.sql

index 484ca40ddf4b8ecb3ac4376cad5ea2bff347d6fe..a8c2af4c5d34fe3b6028688825157c137029afc9 100644 (file)
@@ -4037,6 +4037,30 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
    {
        Oid         exprCollOid = exprCollation(value);
 
+       /*
+        * Check we have a collation iff it is a collatable type.  The only
+        * expected failures here are (1) COLLATE applied to a noncollatable
+        * type, or (2) partition bound expression had an unresolved
+        * collation.  But we might as well code this to be a complete
+        * consistency check.
+        */
+       if (type_is_collatable(colType))
+       {
+           if (!OidIsValid(exprCollOid))
+               ereport(ERROR,
+                       (errcode(ERRCODE_INDETERMINATE_COLLATION),
+                        errmsg("could not determine which collation to use for partition bound expression"),
+                        errhint("Use the COLLATE clause to set the collation explicitly.")));
+       }
+       else
+       {
+           if (OidIsValid(exprCollOid))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("collations are not supported by type %s",
+                               format_type_be(colType))));
+       }
+
        if (OidIsValid(exprCollOid) &&
            exprCollOid != DEFAULT_COLLATION_OID &&
            exprCollOid != partCollation)
index 50de4b380a5836c8238e9cd2aebd2449b0cc615e..2753778197a22084761446f63973bc57ae503796 100644 (file)
@@ -603,6 +603,12 @@ CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (genera
 ERROR:  set-returning functions are not allowed in partition bound
 LINE 1: ...expr_fail PARTITION OF list_parted FOR VALUES IN (generate_s...
                                                              ^
+CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ('1' collate "POSIX");
+ERROR:  collations are not supported by type integer
+CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ((1+1) collate "POSIX");
+ERROR:  collations are not supported by type integer
+LINE 1: ...ail PARTITION OF list_parted FOR VALUES IN ((1+1) collate "P...
+                                                             ^
 -- syntax does not allow empty list of values for list partitions
 CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN ();
 ERROR:  syntax error at or near ")"
index 9a40d7b8cdffb791b4b9f1581c5ec5a5fc4bab9d..97074ff1c11ae4516d3e14edb02f1d5d81497bd8 100644 (file)
@@ -516,6 +516,8 @@ CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(so
 CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (sum(1));
 CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ((select 1));
 CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN (generate_series(4, 6));
+CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ('1' collate "POSIX");
+CREATE TABLE part_bogus_expr_fail PARTITION OF list_parted FOR VALUES IN ((1+1) collate "POSIX");
 
 -- syntax does not allow empty list of values for list partitions
 CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN ();