1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

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.
This commit is contained in:
Tom Lane
2011-03-24 15:29:52 -04:00
parent 472671e133
commit 3bba9ce945
6 changed files with 94 additions and 70 deletions

View File

@ -837,49 +837,62 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
attcollation = attform->attcollation; attcollation = attform->attcollation;
ReleaseSysCache(atttuple); ReleaseSysCache(atttuple);
} }
else if (attribute->expr && IsA(attribute->expr, Var) &&
((Var *) attribute->expr)->varattno != InvalidAttrNumber)
{
/* Tricky tricky, he wrote (column) ... treat as simple attr */
Var *var = (Var *) attribute->expr;
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
atttype = get_atttype(relId, var->varattno);
attcollation = var->varcollid;
}
else else
{ {
/* Index expression */ /* Index expression */
Assert(attribute->expr != NULL); Node *expr = attribute->expr;
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, Assert(expr != NULL);
attribute->expr); atttype = exprType(expr);
atttype = exprType(attribute->expr); attcollation = exprCollation(expr);
attcollation = exprCollation(attribute->expr);
/* /*
* We don't currently support generation of an actual query plan * Strip any top-level COLLATE clause. This ensures that we treat
* for an index expression, only simple scalar expressions; hence * "x COLLATE y" and "(x COLLATE y)" alike.
* these restrictions.
*/ */
if (contain_subplans(attribute->expr)) while (IsA(expr, CollateExpr))
ereport(ERROR, expr = (Node *) ((CollateExpr *) expr)->arg;
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in index expression")));
if (contain_agg_clause(attribute->expr))
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in index expression")));
/* if (IsA(expr, Var) &&
* A expression using mutable functions is probably wrong, since ((Var *) expr)->varattno != InvalidAttrNumber)
* if you aren't going to get the same result for the same data {
* every time, it's not clear what the index entries mean at all. /*
*/ * User wrote "(column)" or "(column COLLATE something)".
if (CheckMutability((Expr *) attribute->expr)) * Treat it like simple attribute anyway.
ereport(ERROR, */
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), indexInfo->ii_KeyAttrNumbers[attn] = ((Var *) expr)->varattno;
errmsg("functions in index expression must be marked IMMUTABLE"))); }
else
{
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
expr);
/*
* We don't currently support generation of an actual query
* plan for an index expression, only simple scalar
* expressions; hence these restrictions.
*/
if (contain_subplans(expr))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in index expression")));
if (contain_agg_clause(expr))
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in index expression")));
/*
* A expression using mutable functions is probably wrong,
* since if you aren't going to get the same result for the
* same data every time, it's not clear what the index entries
* mean at all.
*/
if (CheckMutability((Expr *) expr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index expression must be marked IMMUTABLE")));
}
} }
/* /*

View File

@ -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,
{ {
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,
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,
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
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++;
} }

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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