1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-29 23:43:17 +03:00

Revise collation derivation method and expression-tree representation.

All expression nodes now have an explicit output-collation field, unless
they are known to only return a noncollatable data type (such as boolean
or record).  Also, nodes that can invoke collation-aware functions store
a separate field that is the collation value to pass to the function.
This avoids confusion that arises when a function has collatable inputs
and noncollatable output type, or vice versa.

Also, replace the parser's on-the-fly collation assignment method with
a post-pass over the completed expression tree.  This allows us to use
a more complex (and hopefully more nearly spec-compliant) assignment
rule without paying for it in extra storage in every expression node.

Fix assorted bugs in the planner's handling of collations by making
collation one of the defining properties of an EquivalenceClass and
by converting CollateExprs into discardable RelabelType nodes during
expression preprocessing.
This commit is contained in:
Tom Lane
2011-03-19 20:29:08 -04:00
parent 025f4c72f0
commit b310b6e31c
73 changed files with 2365 additions and 1084 deletions

View File

@@ -4869,6 +4869,16 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
case T_NullIfExpr:
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
appendStringInfo(buf, "NULLIF(");
get_rule_expr((Node *) nullifexpr->args, context, true);
appendStringInfoChar(buf, ')');
}
break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -5529,8 +5539,9 @@ get_rule_expr(Node *node, deparse_context *context,
}
if (xexpr->op == IS_XMLSERIALIZE)
appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type,
xexpr->typmod));
appendStringInfo(buf, " AS %s",
format_type_with_typemod(xexpr->type,
xexpr->typmod));
if (xexpr->op == IS_DOCUMENT)
appendStringInfoString(buf, " IS DOCUMENT");
else
@@ -5538,16 +5549,6 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
case T_NullIfExpr:
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
appendStringInfo(buf, "NULLIF(");
get_rule_expr((Node *) nullifexpr->args, context, true);
appendStringInfoChar(buf, ')');
}
break;
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;

View File

@@ -285,7 +285,7 @@ var_eq_const(VariableStatData *vardata, Oid operator,
FmgrInfo eqproc;
fmgr_info(get_opcode(operator), &eqproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
for (i = 0; i < nvalues; i++)
{
@@ -515,7 +515,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
fmgr_info(get_opcode(operator), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
/*
* If we have most-common-values info, add up the fractions of the MCV
@@ -1252,7 +1252,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate)
/* Try to use the histogram entries to get selectivity */
fmgr_info(get_opcode(operator), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
selec = histogram_selectivity(&vardata, &opproc, constval, true,
10, 1, &hist_size);
@@ -1701,7 +1701,7 @@ scalararraysel(PlannerInfo *root,
if (!oprsel)
return (Selectivity) 0.5;
fmgr_info(oprsel, &oprselproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &oprselproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &oprselproc);
/* deconstruct the expression */
Assert(list_length(clause->args) == 2);
@@ -1839,6 +1839,7 @@ scalararraysel(PlannerInfo *root,
dummyexpr = makeNode(CaseTestExpr);
dummyexpr->typeId = nominal_element_type;
dummyexpr->typeMod = -1;
dummyexpr->collation = clause->inputcollid;
args = list_make2(leftop, dummyexpr);
if (is_join_clause)
s2 = DatumGetFloat8(FunctionCall5(&oprselproc,
@@ -2118,7 +2119,7 @@ eqjoinsel_inner(Oid operator,
nmatches;
fmgr_info(get_opcode(operator), &eqproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
@@ -2341,7 +2342,7 @@ eqjoinsel_semi(Oid operator,
nmatches;
fmgr_info(get_opcode(operator), &eqproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
@@ -4484,7 +4485,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
FmgrInfo opproc;
fmgr_info(get_opcode(sortop), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
for (i = 0; i < nvalues; i++)
{
@@ -5111,7 +5112,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
if (cmpopr == InvalidOid)
elog(ERROR, "no >= operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
prefixsel = ineq_histogram_selectivity(root, vardata, &opproc, true,
prefixcon->constvalue,
@@ -5133,7 +5134,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
if (cmpopr == InvalidOid)
elog(ERROR, "no < operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
greaterstrcon = make_greater_string(prefixcon, &opproc);
if (greaterstrcon)

View File

@@ -71,6 +71,7 @@ typedef struct
bool fn_strict; /* function is "strict" (NULL in => NULL out) */
bool fn_retset; /* function returns a set (over multiple calls) */
unsigned char fn_stats; /* collect stats if track_functions > this */
Oid fn_collation; /* collation that function should use */
void *fn_extra; /* extra space for use by handler */
MemoryContext fn_mcxt; /* memory context to store fn_extra in */
Node *fn_expr; /* expression parse tree for call, or NULL */
@@ -89,12 +90,16 @@ is the number of arguments expected by the function, fn_strict is its
strictness flag, and fn_retset shows whether it returns a set; all of
these values come from the function's pg_proc entry. fn_stats is also
set up to control whether or not to track runtime statistics for calling
this function. If the function is being called as part of a SQL expression,
this function.
fn_collation supplies the collation to use for collation-sensitive
functions. If the function is being called as part of a SQL expression,
fn_expr will point to the expression parse tree for the function call; this
can be used to extract parse-time knowledge about the actual arguments.
FmgrInfo already exists in the current code, but has fewer fields. This
change should be transparent at the source-code level.
Note that these two fields really are information about the arguments
rather than information about the function, but it's proven to be more
convenient to keep them in FmgrInfo than in FunctionCallInfoData where
they might more logically go.
During a call of a function, the following data structure is created

View File

@@ -192,7 +192,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
* elogs.
*/
finfo->fn_oid = InvalidOid;
finfo->fn_collation = InvalidOid;
finfo->fn_collation = InvalidOid; /* caller may set this later */
finfo->fn_extra = NULL;
finfo->fn_mcxt = mcxt;
finfo->fn_expr = NULL; /* caller may set this later */
@@ -420,25 +420,6 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
ReleaseSysCache(languageTuple);
}
/*
* Initialize the fn_collation field
*/
void
fmgr_info_collation(Oid collationId, FmgrInfo *finfo)
{
finfo->fn_collation = collationId;
}
/*
* Initialize the fn_expr field and set the collation based on it
*/
void
fmgr_info_expr(Node *expr, FmgrInfo *finfo)
{
finfo->fn_expr = expr;
finfo->fn_collation = exprCollation(expr);
}
/*
* Fetch and validate the information record for the given external function.
* The function is specified by a handle for the containing library
@@ -920,6 +901,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
fmgr_info_cxt_security(fcinfo->flinfo->fn_oid, &fcache->flinfo,
fcinfo->flinfo->fn_mcxt, true);
fcache->flinfo.fn_collation = fcinfo->flinfo->fn_collation;
fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr;
tuple = SearchSysCache1(PROCOID,
@@ -1293,6 +1275,11 @@ DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
return result;
}
/*
* These are the same as DirectFunctionCallN except that a nonzero
* collation can be specified. No other fields of FmgrInfo are made valid.
*/
Datum
DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
{
@@ -1300,8 +1287,9 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
FmgrInfo flinfo;
Datum result;
MemSet(&flinfo, 0, sizeof(flinfo));
flinfo.fn_collation = collation;
InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL);
fcinfo.flinfo->fn_collation = collation;
fcinfo.arg[0] = arg1;
fcinfo.argnull[0] = false;
@@ -1316,14 +1304,16 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
}
Datum
DirectFunctionCall2WithCollation(PGFunction func, Oid collation, Datum arg1, Datum arg2)
DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
Datum arg1, Datum arg2)
{
FunctionCallInfoData fcinfo;
FmgrInfo flinfo;
Datum result;
MemSet(&flinfo, 0, sizeof(flinfo));
flinfo.fn_collation = collation;
InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
fcinfo.flinfo->fn_collation = collation;
fcinfo.arg[0] = arg1;
fcinfo.arg[1] = arg2;

View File

@@ -411,6 +411,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anycollation;
int i;
/* See if there are any polymorphic outputs; quick out if not */
@@ -468,6 +469,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
anyelement_type = resolve_generic_type(ANYELEMENTOID,
@@ -486,6 +488,24 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyenum && !type_is_enum(anyelement_type))
return false;
/*
* Identify the collation to use for polymorphic OUT parameters.
* (It'll necessarily be the same for both anyelement and anyarray.)
*/
anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
if (OidIsValid(anycollation))
{
/*
* The types are collatable, so consider whether to use a nondefault
* collation. We do so if we can identify the input collation used
* for the function.
*/
Oid inputcollation = exprInputCollation(call_expr);
if (OidIsValid(inputcollation))
anycollation = inputcollation;
}
/* And finally replace the tuple column types as needed */
for (i = 0; i < natts; i++)
{
@@ -499,6 +519,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
anyelement_type,
-1,
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
case ANYARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -506,13 +527,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
anyarray_type,
-1,
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
default:
break;
}
/* Set collation based on actual argument types */
TupleDescInitEntryCollation(tupdesc, i + 1,
exprCollation(call_expr));
}
return true;

View File

@@ -836,7 +836,7 @@ tuplesort_begin_datum(Oid datumType,
elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator);
fmgr_info(sortFunction, &state->sortOpFn);
fmgr_info_collation(sortCollation, &state->sortOpFn);
fmgr_info_set_collation(sortCollation, &state->sortOpFn);
/* set ordering flags */
state->sortFnFlags = reverse ? SK_BT_DESC : 0;