mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Per-column collation support
This adds collation support for columns and domains, a COLLATE clause to override it per expression, and B-tree index support. Peter Eisentraut reviewed by Pavel Stehule, Itagaki Takahiro, Robert Haas, Noah Misch
This commit is contained in:
@ -23,6 +23,7 @@
|
||||
#include "catalog/pg_opfamily.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
@ -99,15 +100,15 @@ static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
|
||||
Relids outer_relids, bool isouterjoin);
|
||||
static bool match_boolean_index_clause(Node *clause, int indexcol,
|
||||
IndexOptInfo *index);
|
||||
static bool match_special_index_operator(Expr *clause, Oid opfamily,
|
||||
static bool match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily,
|
||||
bool indexkey_on_left);
|
||||
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
|
||||
IndexOptInfo *index);
|
||||
static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily);
|
||||
static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation);
|
||||
static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
IndexOptInfo *index,
|
||||
int indexcol);
|
||||
static List *prefix_quals(Node *leftop, Oid opfamily,
|
||||
static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
Const *prefix, Pattern_Prefix_Status pstatus);
|
||||
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
|
||||
Datum rightop);
|
||||
@ -1142,7 +1143,9 @@ group_clauses_by_indexkey(IndexOptInfo *index,
|
||||
* and
|
||||
* (2) must contain an operator which is in the same family as the index
|
||||
* operator for this column, or is a "special" operator as recognized
|
||||
* by match_special_index_operator().
|
||||
* by match_special_index_operator();
|
||||
* and
|
||||
* (3) must match the collation of the index.
|
||||
*
|
||||
* Our definition of "const" is pretty liberal: we allow Vars belonging
|
||||
* to the caller-specified outer_relids relations (which had better not
|
||||
@ -1198,6 +1201,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
SaOpControl saop_control)
|
||||
{
|
||||
Expr *clause = rinfo->clause;
|
||||
Oid collation = index->indexcollations[indexcol];
|
||||
Oid opfamily = index->opfamily[indexcol];
|
||||
Node *leftop,
|
||||
*rightop;
|
||||
@ -1280,7 +1284,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
bms_is_subset(right_relids, outer_relids) &&
|
||||
!contain_volatile_functions(rightop))
|
||||
{
|
||||
if (is_indexable_operator(expr_op, opfamily, true))
|
||||
if (is_indexable_operator(expr_op, opfamily, true) &&
|
||||
(!collation || collation == exprCollation((Node *) clause)))
|
||||
return true;
|
||||
|
||||
/*
|
||||
@ -1288,7 +1293,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
* is a "special" indexable operator.
|
||||
*/
|
||||
if (plain_op &&
|
||||
match_special_index_operator(clause, opfamily, true))
|
||||
match_special_index_operator(clause, collation, opfamily, true))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -1298,14 +1303,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
bms_is_subset(left_relids, outer_relids) &&
|
||||
!contain_volatile_functions(leftop))
|
||||
{
|
||||
if (is_indexable_operator(expr_op, opfamily, false))
|
||||
if (is_indexable_operator(expr_op, opfamily, false) &&
|
||||
(!collation || collation == exprCollation((Node *) clause)))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we didn't find a member of the index's opfamily, see whether it
|
||||
* is a "special" indexable operator.
|
||||
*/
|
||||
if (match_special_index_operator(clause, opfamily, false))
|
||||
if (match_special_index_operator(clause, collation, opfamily, false))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -1391,6 +1397,9 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
|
||||
else
|
||||
return false;
|
||||
|
||||
if (index->indexcollations[indexcol] != linitial_oid(clause->collids))
|
||||
return false;
|
||||
|
||||
/* We're good if the operator is the right type of opfamily member */
|
||||
switch (get_op_opfamily_strategy(expr_op, opfamily))
|
||||
{
|
||||
@ -2380,7 +2389,7 @@ match_boolean_index_clause(Node *clause,
|
||||
* Return 'true' if we can do something with it anyway.
|
||||
*/
|
||||
static bool
|
||||
match_special_index_operator(Expr *clause, Oid opfamily,
|
||||
match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily,
|
||||
bool indexkey_on_left)
|
||||
{
|
||||
bool isIndexable = false;
|
||||
@ -2495,7 +2504,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
|
||||
isIndexable =
|
||||
(opfamily == TEXT_PATTERN_BTREE_FAM_OID) ||
|
||||
(opfamily == TEXT_BTREE_FAM_OID &&
|
||||
(pstatus == Pattern_Prefix_Exact || lc_collate_is_c()));
|
||||
(pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation)));
|
||||
break;
|
||||
|
||||
case OID_BPCHAR_LIKE_OP:
|
||||
@ -2505,7 +2514,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
|
||||
isIndexable =
|
||||
(opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) ||
|
||||
(opfamily == BPCHAR_BTREE_FAM_OID &&
|
||||
(pstatus == Pattern_Prefix_Exact || lc_collate_is_c()));
|
||||
(pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation)));
|
||||
break;
|
||||
|
||||
case OID_NAME_LIKE_OP:
|
||||
@ -2526,6 +2535,25 @@ match_special_index_operator(Expr *clause, Oid opfamily,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isIndexable)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* For case-insensitive matching, we also need to check that the
|
||||
* collations match.
|
||||
*/
|
||||
switch (expr_op)
|
||||
{
|
||||
case OID_TEXT_ICLIKE_OP:
|
||||
case OID_TEXT_ICREGEXEQ_OP:
|
||||
case OID_BPCHAR_ICLIKE_OP:
|
||||
case OID_BPCHAR_ICREGEXEQ_OP:
|
||||
case OID_NAME_ICLIKE_OP:
|
||||
case OID_NAME_ICREGEXEQ_OP:
|
||||
isIndexable = (idxcolcollation == exprCollation((Node *) clause));
|
||||
break;
|
||||
}
|
||||
|
||||
return isIndexable;
|
||||
}
|
||||
|
||||
@ -2561,6 +2589,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
||||
{
|
||||
List *clausegroup = (List *) lfirst(lc);
|
||||
Oid curFamily = index->opfamily[indexcol];
|
||||
Oid curCollation = index->indexcollations[indexcol];
|
||||
ListCell *lc2;
|
||||
|
||||
foreach(lc2, clausegroup)
|
||||
@ -2592,7 +2621,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
||||
{
|
||||
resultquals = list_concat(resultquals,
|
||||
expand_indexqual_opclause(rinfo,
|
||||
curFamily));
|
||||
curFamily,
|
||||
curCollation));
|
||||
}
|
||||
else if (IsA(clause, ScalarArrayOpExpr))
|
||||
{
|
||||
@ -2693,7 +2723,7 @@ expand_boolean_index_clause(Node *clause,
|
||||
* expand special cases that were accepted by match_special_index_operator().
|
||||
*/
|
||||
static List *
|
||||
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
|
||||
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation)
|
||||
{
|
||||
Expr *clause = rinfo->clause;
|
||||
|
||||
@ -2724,7 +2754,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
|
||||
{
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
|
||||
&prefix, &rest);
|
||||
return prefix_quals(leftop, opfamily, prefix, pstatus);
|
||||
return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2736,7 +2766,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
|
||||
&prefix, &rest);
|
||||
return prefix_quals(leftop, opfamily, prefix, pstatus);
|
||||
return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2748,7 +2778,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex,
|
||||
&prefix, &rest);
|
||||
return prefix_quals(leftop, opfamily, prefix, pstatus);
|
||||
return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2760,7 +2790,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
|
||||
&prefix, &rest);
|
||||
return prefix_quals(leftop, opfamily, prefix, pstatus);
|
||||
return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2814,6 +2844,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
ListCell *largs_cell;
|
||||
ListCell *rargs_cell;
|
||||
ListCell *opnos_cell;
|
||||
ListCell *collids_cell;
|
||||
|
||||
/* We have to figure out (again) how the first col matches */
|
||||
var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
|
||||
@ -2845,6 +2876,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
largs_cell = lnext(list_head(clause->largs));
|
||||
rargs_cell = lnext(list_head(clause->rargs));
|
||||
opnos_cell = lnext(list_head(clause->opnos));
|
||||
collids_cell = lnext(list_head(clause->collids));
|
||||
|
||||
while (largs_cell != NULL)
|
||||
{
|
||||
@ -2891,6 +2923,10 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
!= op_strategy)
|
||||
break;
|
||||
|
||||
/* Does collation match? */
|
||||
if (lfirst_oid(collids_cell) != index->indexcollations[i])
|
||||
break;
|
||||
|
||||
/* Add opfamily and datatypes to lists */
|
||||
get_op_opfamily_properties(expr_op, index->opfamily[i], false,
|
||||
&op_strategy,
|
||||
@ -2974,6 +3010,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
rc->opnos = new_ops;
|
||||
rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
|
||||
matching_cols);
|
||||
rc->collids = list_truncate(list_copy(clause->collids),
|
||||
matching_cols);
|
||||
rc->largs = list_truncate((List *) copyObject(clause->largs),
|
||||
matching_cols);
|
||||
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
|
||||
@ -2998,7 +3036,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
* operators and operand datatypes.
|
||||
*/
|
||||
static List *
|
||||
prefix_quals(Node *leftop, Oid opfamily,
|
||||
prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
Const *prefix_const, Pattern_Prefix_Status pstatus)
|
||||
{
|
||||
List *result;
|
||||
@ -3100,6 +3138,7 @@ prefix_quals(Node *leftop, Oid opfamily,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "no < operator for opfamily %u", opfamily);
|
||||
fmgr_info(get_opcode(oproid), <proc);
|
||||
fmgr_info_collation(collation, <proc);
|
||||
greaterstr = make_greater_string(prefix_const, <proc);
|
||||
if (greaterstr)
|
||||
{
|
||||
|
Reference in New Issue
Block a user