Hi, On 2019/03/06 17:27, Michael Paquier wrote: > On Wed, Mar 06, 2019 at 04:00:42PM +0900, Amit Langote wrote: >> Thanks for looking at this. Your patch seems better, because it allows us >> to keep the error message consistent with the message one would get with >> list-partitioned syntax. > > Thanks for confirming. I think that it would be nice as well to add > more test coverage for such error patterns with all the strategies. > It would be good to fix that first, so I can take care of that.
I've added some tests to your patch. Also improved the comments a bit. I noticed another issue with the code -- it's using strcmp() to compare specified string against "minvalue" and "maxvalue", which causes the following silly error: create table q2 partition of q for values from ("MINVALUE") to (maxvalue); ERROR: column "MINVALUE" does not exist LINE 1: create table q2 partition of q for values from ("MINVALUE") ... It should be using pg_strncasecmp(). > Now I don't really find the error "missing FROM-clause entry for > table" quite convincing when this is applied to a partition bound when > using a column defined in the relation. Adding more error classes in > the set of CRERR_NO_RTE would be perhaps nice, still I am not sure how > elegant it could be made when looking at expressions for partition > bounds. Note that this is not just a problem for partition bounds. You can see it with default expressions too. create table foo (a int default (aa.a)); ERROR: missing FROM-clause entry for table "aa" LINE 1: create table foo (a int default (aa.a)); create table foo (a int default (a.a.aa.a.a.a.a.aa)); ERROR: improper qualified name (too many dotted names): a.a.aa.a.a.a.a.aa LINE 1: create table foo (a int default (a.a.aa.a.a.a.a.aa)); We could make the error message more meaningful depending on the context, but maybe it'd better be pursue it as a separate project. Thanks, Amit
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index a37d1f18be..67b05b8039 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3746,18 +3746,28 @@ transformPartitionRangeBounds(ParseState *pstate, List *blist, ColumnRef *cref = (ColumnRef *) expr; char *cname = NULL; + /* + * There should be a single field named "minvalue" or "maxvalue". + */ if (list_length(cref->fields) == 1 && IsA(linitial(cref->fields), String)) cname = strVal(linitial(cref->fields)); - Assert(cname != NULL); - if (strcmp("minvalue", cname) == 0) + if (cname == NULL) + { + /* + * ColumnRef is not in the desired single-field-name form; for + * consistency, let transformExpr() report the error rather + * than doing it ourselves. + */ + } + else if (pg_strncasecmp(cname, "minvalue", 6) == 0) { prd = makeNode(PartitionRangeDatum); prd->kind = PARTITION_RANGE_DATUM_MINVALUE; prd->value = NULL; } - else if (strcmp("maxvalue", cname) == 0) + else if (pg_strncasecmp(cname, "maxvalue", 6) == 0) { prd = makeNode(PartitionRangeDatum); prd->kind = PARTITION_RANGE_DATUM_MAXVALUE; diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index d51e547278..503dca5866 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -709,6 +709,66 @@ ERROR: modulus for hash partition must be a positive integer -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); ERROR: remainder for hash partition must be less than modulus +-- more tests for generalized expression syntax for list/range partition bounds +CREATE TABLE list_parted_bound_exprs(a int) PARTITION BY LIST (a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN ("UNKNOWN"); +ERROR: column "UNKNOWN" does not exist +LINE 1: ...RTITION OF list_parted_bound_exprs FOR VALUES IN ("UNKNOWN")... + ^ +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (a); +ERROR: cannot use column references in partition bound expression +LINE 1: ...prs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (a); + ^ +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (foo.a); +ERROR: missing FROM-clause entry for table "foo" +LINE 1: ... PARTITION OF list_parted_bound_exprs FOR VALUES IN (foo.a); + ^ +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs.a); +ERROR: missing FROM-clause entry for table "list_parted_bound_exprs" +LINE 1: ...RTITION OF list_parted_bound_exprs FOR VALUES IN (list_parte... + ^ +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs1.a.a.a.a); +ERROR: improper qualified name (too many dotted names): list_parted_bound_exprs1.a.a.a.a +LINE 1: ...RTITION OF list_parted_bound_exprs FOR VALUES IN (list_parte... + ^ +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs1.a); +ERROR: cannot use column references in partition bound expression +LINE 1: ...RTITION OF list_parted_bound_exprs FOR VALUES IN (list_parte... + ^ +CREATE TABLE range_parted_bound_exprs(a int) PARTITION BY RANGE (a); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO (unknown); +ERROR: column "unknown" does not exist +LINE 1: ...F range_parted_bound_exprs FOR VALUES FROM (1) TO (unknown); + ^ +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("a".unknown); +ERROR: missing FROM-clause entry for table "a" +LINE 1: ... range_parted_bound_exprs FOR VALUES FROM (1) TO ("a".unknow... + ^ +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("a"."a"); +ERROR: missing FROM-clause entry for table "a" +LINE 1: ...F range_parted_bound_exprs FOR VALUES FROM (1) TO ("a"."a"); + ^ +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_parted_bound_exprs1"."a"); +ERROR: cannot use column references in partition bound expression +LINE 1: ... range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_par... + ^ +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_parted_bound_exprs1"."a".a.a); +ERROR: cross-database references are not implemented: range_parted_bound_exprs1.a.a.a +LINE 1: ... range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_par... + ^ +-- the following two should work +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM ("minValue") TO (1); +CREATE TABLE range_parted_bound_exprs2 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO (MAXVALUE); +\d+ range_parted_bound_exprs + Partitioned table "public.range_parted_bound_exprs" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + a | integer | | | | plain | | +Partition key: RANGE (a) +Partitions: range_parted_bound_exprs1 FOR VALUES FROM (MINVALUE) TO (1), + range_parted_bound_exprs2 FOR VALUES FROM (1) TO (MAXVALUE) + +drop table list_parted_bound_exprs, range_parted_bound_exprs; -- check schema propagation from parent CREATE TABLE parted ( a text, diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 4091c19cf0..399a882430 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -627,6 +627,28 @@ CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REM -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); +-- more tests for generalized expression syntax for list/range partition bounds +CREATE TABLE list_parted_bound_exprs(a int) PARTITION BY LIST (a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN ("UNKNOWN"); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (foo.a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs.a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs1.a.a.a.a); +CREATE TABLE list_parted_bound_exprs1 PARTITION OF list_parted_bound_exprs FOR VALUES IN (list_parted_bound_exprs1.a); + +CREATE TABLE range_parted_bound_exprs(a int) PARTITION BY RANGE (a); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO (unknown); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("a".unknown); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("a"."a"); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_parted_bound_exprs1"."a"); +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO ("range_parted_bound_exprs1"."a".a.a); +-- the following two should work +CREATE TABLE range_parted_bound_exprs1 PARTITION OF range_parted_bound_exprs FOR VALUES FROM ("minValue") TO (1); +CREATE TABLE range_parted_bound_exprs2 PARTITION OF range_parted_bound_exprs FOR VALUES FROM (1) TO (MAXVALUE); +\d+ range_parted_bound_exprs + +drop table list_parted_bound_exprs, range_parted_bound_exprs; + -- check schema propagation from parent CREATE TABLE parted (