Commit 3bba9ce9 authored by Tom Lane's avatar Tom Lane

Clean up handling of COLLATE clauses in index column definitions.

Ensure that COLLATE at the top level of an index expression is treated the
same as a grammatically separate COLLATE.  Fix bogus reverse-parsing logic
in pg_get_indexdef.
parent 472671e1
...@@ -837,50 +837,63 @@ ComputeIndexAttrs(IndexInfo *indexInfo, ...@@ -837,50 +837,63 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
attcollation = attform->attcollation; attcollation = attform->attcollation;
ReleaseSysCache(atttuple); ReleaseSysCache(atttuple);
} }
else if (attribute->expr && IsA(attribute->expr, Var) && else
((Var *) attribute->expr)->varattno != InvalidAttrNumber)
{ {
/* Tricky tricky, he wrote (column) ... treat as simple attr */ /* Index expression */
Var *var = (Var *) attribute->expr; Node *expr = attribute->expr;
Assert(expr != NULL);
atttype = exprType(expr);
attcollation = exprCollation(expr);
/*
* Strip any top-level COLLATE clause. This ensures that we treat
* "x COLLATE y" and "(x COLLATE y)" alike.
*/
while (IsA(expr, CollateExpr))
expr = (Node *) ((CollateExpr *) expr)->arg;
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno; if (IsA(expr, Var) &&
atttype = get_atttype(relId, var->varattno); ((Var *) expr)->varattno != InvalidAttrNumber)
attcollation = var->varcollid; {
/*
* User wrote "(column)" or "(column COLLATE something)".
* Treat it like simple attribute anyway.
*/
indexInfo->ii_KeyAttrNumbers[attn] = ((Var *) expr)->varattno;
} }
else else
{ {
/* Index expression */
Assert(attribute->expr != NULL);
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */ indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
attribute->expr); expr);
atttype = exprType(attribute->expr);
attcollation = exprCollation(attribute->expr);
/* /*
* We don't currently support generation of an actual query plan * We don't currently support generation of an actual query
* for an index expression, only simple scalar expressions; hence * plan for an index expression, only simple scalar
* these restrictions. * expressions; hence these restrictions.
*/ */
if (contain_subplans(attribute->expr)) if (contain_subplans(expr))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in index expression"))); errmsg("cannot use subquery in index expression")));
if (contain_agg_clause(attribute->expr)) if (contain_agg_clause(expr))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in index expression"))); errmsg("cannot use aggregate function in index expression")));
/* /*
* A expression using mutable functions is probably wrong, since * A expression using mutable functions is probably wrong,
* if you aren't going to get the same result for the same data * since if you aren't going to get the same result for the
* every time, it's not clear what the index entries mean at all. * same data every time, it's not clear what the index entries
* mean at all.
*/ */
if (CheckMutability((Expr *) attribute->expr)) if (CheckMutability((Expr *) expr))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index expression must be marked IMMUTABLE"))); errmsg("functions in index expression must be marked IMMUTABLE")));
} }
}
/* /*
* Apply collation override if any * Apply collation override if any
......
...@@ -794,7 +794,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, ...@@ -794,7 +794,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
List *context; List *context;
Oid indrelid; Oid indrelid;
int keyno; int keyno;
Oid keycoltype;
Datum indcollDatum; Datum indcollDatum;
Datum indclassDatum; Datum indclassDatum;
Datum indoptionDatum; Datum indoptionDatum;
...@@ -902,6 +901,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, ...@@ -902,6 +901,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
{ {
AttrNumber attnum = idxrec->indkey.values[keyno]; AttrNumber attnum = idxrec->indkey.values[keyno];
int16 opt = indoption->values[keyno]; int16 opt = indoption->values[keyno];
Oid keycoltype;
Oid keycolcollation;
if (!colno) if (!colno)
appendStringInfoString(&buf, sep); appendStringInfoString(&buf, sep);
...@@ -916,6 +917,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, ...@@ -916,6 +917,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!colno || colno == keyno + 1) if (!colno || colno == keyno + 1)
appendStringInfoString(&buf, quote_identifier(attname)); appendStringInfoString(&buf, quote_identifier(attname));
keycoltype = get_atttype(indrelid, attnum); keycoltype = get_atttype(indrelid, attnum);
keycolcollation = get_attcollation(indrelid, attnum);
} }
else else
{ {
...@@ -939,16 +941,18 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, ...@@ -939,16 +941,18 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
appendStringInfo(&buf, "(%s)", str); appendStringInfo(&buf, "(%s)", str);
} }
keycoltype = exprType(indexkey); keycoltype = exprType(indexkey);
keycolcollation = exprCollation(indexkey);
} }
if (!attrsOnly && (!colno || colno == keyno + 1)) if (!attrsOnly && (!colno || colno == keyno + 1))
{ {
Oid coll; Oid indcoll;
/* Add collation, if not default */ /* Add collation, if not default for column */
coll = indcollation->values[keyno]; indcoll = indcollation->values[keyno];
if (coll && coll != DEFAULT_COLLATION_OID && coll != get_attcollation(indrelid, attnum)) if (OidIsValid(indcoll) && indcoll != keycolcollation)
appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcollation->values[keyno]))); appendStringInfo(&buf, " COLLATE %s",
generate_collation_name((indcoll)));
/* Add the operator class name, if not default */ /* Add the operator class name, if not default */
get_opclass_name(indclass->values[keyno], keycoltype, &buf); get_opclass_name(indclass->values[keyno], keycoltype, &buf);
...@@ -6646,7 +6650,8 @@ get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collat ...@@ -6646,7 +6650,8 @@ get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collat
quote_identifier(attname), quote_identifier(attname),
format_type_with_typemod(atttypid, atttypmod)); format_type_with_typemod(atttypid, atttypmod));
if (attcollation && attcollation != DEFAULT_COLLATION_OID) if (attcollation && attcollation != DEFAULT_COLLATION_OID)
appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation)); appendStringInfo(buf, " COLLATE %s",
generate_collation_name(attcollation));
i++; i++;
} }
......
...@@ -747,19 +747,21 @@ SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2; ...@@ -747,19 +747,21 @@ SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2;
CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C");
CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically
CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX"));
CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail
ERROR: collations are not supported by type integer ERROR: collations are not supported by type integer
CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail
ERROR: collations are not supported by type integer ERROR: collations are not supported by type integer
LINE 1: ...ATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C... LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C...
^ ^
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
relname | pg_get_indexdef relname | pg_get_indexdef
--------------------+---------------------------------------------------------------------------------------------- --------------------+-----------------------------------------------------------------------------------------------------
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b) collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b)
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "C") collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "C")
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (((b COLLATE "C")) COLLATE "C") collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (b COLLATE "C")
(3 rows) collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
(4 rows)
-- schema manipulation commands -- schema manipulation commands
CREATE ROLE regress_test_role; CREATE ROLE regress_test_role;
......
...@@ -524,21 +524,23 @@ SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2; ...@@ -524,21 +524,23 @@ SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2;
-- indexes -- indexes
CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "POSIX");
CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "POSIX")); -- this is different grammatically
CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX"));
CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "POSIX"); -- fail
ERROR: collations are not supported by type integer ERROR: collations are not supported by type integer
CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "POSIX")); -- fail
ERROR: collations are not supported by type integer ERROR: collations are not supported by type integer
LINE 1: ...ATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C... LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "P...
^ ^
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
relname | pg_get_indexdef relname | pg_get_indexdef
--------------------+---------------------------------------------------------------------------------------------- --------------------+-----------------------------------------------------------------------------------------------------
collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b) collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b)
collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b) collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "POSIX")
collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (((b COLLATE "C")) COLLATE "C") collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (b COLLATE "POSIX")
(3 rows) collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX")
(4 rows)
-- --
-- Clean up. Many of these table names will be re-used if the user is -- Clean up. Many of these table names will be re-used if the user is
......
...@@ -231,11 +231,12 @@ SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2; ...@@ -231,11 +231,12 @@ SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2;
CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C");
CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically
CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX"));
CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail
CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
-- schema manipulation commands -- schema manipulation commands
......
...@@ -178,13 +178,14 @@ SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2; ...@@ -178,13 +178,14 @@ SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2;
-- indexes -- indexes
CREATE INDEX collate_test1_idx1 ON collate_test1 (b); CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "POSIX");
CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "POSIX")); -- this is different grammatically
CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX"));
CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "POSIX"); -- fail
CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "POSIX")); -- fail
SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1;
-- --
-- Clean up. Many of these table names will be re-used if the user is -- Clean up. Many of these table names will be re-used if the user is
......
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