mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Remove support for postfix (right-unary) operators.
This feature has been a thorn in our sides for a long time, causing many grammatical ambiguity problems. It doesn't seem worth the pain to continue to support it, so remove it. There are some follow-on improvements we can make in the grammar, but this commit only removes the bare minimum number of productions, plus assorted backend support code. Note that pg_dump and psql continue to have full support, since they may be used against older servers. However, pg_dump warns about postfix operators. There is also a check in pg_upgrade. Documentation-wise, I (tgl) largely removed the "left unary" terminology in favor of saying "prefix operator", which is a more standard and IMO less confusing term. I included a catversion bump, although no initial catalog data changes here, to mark the boundary at which oprkind = 'r' stopped being valid in pg_operator. Mark Dilger, based on work by myself and Robert Haas; review by John Naylor Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
This commit is contained in:
@ -741,10 +741,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
|
||||
%nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA
|
||||
%nonassoc ESCAPE /* ESCAPE must be just above LIKE/ILIKE/SIMILAR */
|
||||
%left POSTFIXOP /* dummy for postfix Op rules */
|
||||
/*
|
||||
* To support target_el without AS, we must give IDENT an explicit priority
|
||||
* between POSTFIXOP and Op. We can safely assign the same priority to
|
||||
* between ESCAPE and Op. We can safely assign the same priority to
|
||||
* various unreserved keywords as needed to resolve ambiguities (this can't
|
||||
* have any bad effects since obviously the keywords will still behave the
|
||||
* same as if they weren't keywords). We need to do this:
|
||||
@ -12993,8 +12992,6 @@ a_expr: c_expr { $$ = $1; }
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
|
||||
| qual_Op a_expr %prec Op
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
|
||||
| a_expr qual_Op %prec POSTFIXOP
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
|
||||
|
||||
| a_expr AND a_expr
|
||||
{ $$ = makeAndExpr($1, $3, @2); }
|
||||
@ -13408,8 +13405,6 @@ b_expr: c_expr
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
|
||||
| qual_Op b_expr %prec Op
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
|
||||
| b_expr qual_Op %prec POSTFIXOP
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
|
||||
| b_expr IS DISTINCT FROM b_expr %prec IS
|
||||
{
|
||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2);
|
||||
@ -14665,11 +14660,7 @@ target_el: a_expr AS ColLabel
|
||||
}
|
||||
/*
|
||||
* We support omitting AS only for column labels that aren't
|
||||
* any known keyword. There is an ambiguity against postfix
|
||||
* operators: is "a ! b" an infix expression, or a postfix
|
||||
* expression and a column label? We prefer to resolve this
|
||||
* as an infix expression, which we accomplish by assigning
|
||||
* IDENT a precedence higher than POSTFIXOP.
|
||||
* any known keyword.
|
||||
*/
|
||||
| a_expr IDENT
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ bool Transform_null_equals = false;
|
||||
#define PREC_GROUP_NOT_LIKE 9 /* NOT LIKE/ILIKE/SIMILAR */
|
||||
#define PREC_GROUP_NOT_BETWEEN 10 /* NOT BETWEEN */
|
||||
#define PREC_GROUP_NOT_IN 11 /* NOT IN */
|
||||
#define PREC_GROUP_POSTFIX_OP 12 /* generic postfix operators */
|
||||
#define PREC_GROUP_ANY_ALL 12 /* ANY/ALL */
|
||||
#define PREC_GROUP_INFIX_OP 13 /* generic infix operators */
|
||||
#define PREC_GROUP_PREFIX_OP 14 /* generic prefix operators */
|
||||
|
||||
@ -71,7 +71,7 @@ bool Transform_null_equals = false;
|
||||
* 4. LIKE ILIKE SIMILAR
|
||||
* 5. BETWEEN
|
||||
* 6. IN
|
||||
* 7. generic postfix Op
|
||||
* 7. ANY ALL
|
||||
* 8. generic Op, including <= => <>
|
||||
* 9. generic prefix Op
|
||||
* 10. IS tests (NullTest, BooleanTest, etc)
|
||||
@ -1031,7 +1031,7 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a)
|
||||
Node *rexpr = a->rexpr;
|
||||
|
||||
if (operator_precedence_warning)
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP,
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
|
||||
strVal(llast(a->name)),
|
||||
lexpr, NULL,
|
||||
a->location);
|
||||
@ -1054,7 +1054,7 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a)
|
||||
Node *rexpr = a->rexpr;
|
||||
|
||||
if (operator_precedence_warning)
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP,
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
|
||||
strVal(llast(a->name)),
|
||||
lexpr, NULL,
|
||||
a->location);
|
||||
@ -2019,7 +2019,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
|
||||
sublink->testexpr, NULL,
|
||||
sublink->location);
|
||||
else
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP,
|
||||
emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
|
||||
strVal(llast(sublink->operName)),
|
||||
sublink->testexpr, NULL,
|
||||
sublink->location);
|
||||
@ -3244,28 +3244,11 @@ operator_precedence_group(Node *node, const char **nodename)
|
||||
group = PREC_GROUP_PREFIX_OP;
|
||||
}
|
||||
}
|
||||
else if (aexpr->kind == AEXPR_OP &&
|
||||
aexpr->lexpr != NULL &&
|
||||
aexpr->rexpr == NULL)
|
||||
{
|
||||
/* postfix operator */
|
||||
if (list_length(aexpr->name) == 1)
|
||||
{
|
||||
*nodename = strVal(linitial(aexpr->name));
|
||||
group = PREC_GROUP_POSTFIX_OP;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* schema-qualified operator syntax */
|
||||
*nodename = "OPERATOR()";
|
||||
group = PREC_GROUP_POSTFIX_OP;
|
||||
}
|
||||
}
|
||||
else if (aexpr->kind == AEXPR_OP_ANY ||
|
||||
aexpr->kind == AEXPR_OP_ALL)
|
||||
{
|
||||
*nodename = strVal(llast(aexpr->name));
|
||||
group = PREC_GROUP_POSTFIX_OP;
|
||||
group = PREC_GROUP_ANY_ALL;
|
||||
}
|
||||
else if (aexpr->kind == AEXPR_DISTINCT ||
|
||||
aexpr->kind == AEXPR_NOT_DISTINCT)
|
||||
@ -3356,7 +3339,7 @@ operator_precedence_group(Node *node, const char **nodename)
|
||||
else
|
||||
{
|
||||
*nodename = strVal(llast(s->operName));
|
||||
group = PREC_GROUP_POSTFIX_OP;
|
||||
group = PREC_GROUP_ANY_ALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3432,9 +3415,8 @@ emit_precedence_warnings(ParseState *pstate,
|
||||
* Complain if left child, which should be same or higher precedence
|
||||
* according to current rules, used to be lower precedence.
|
||||
*
|
||||
* Exception to precedence rules: if left child is IN or NOT IN or a
|
||||
* postfix operator, the grouping is syntactically forced regardless of
|
||||
* precedence.
|
||||
* Exception to precedence rules: if left child is IN or NOT IN the
|
||||
* grouping is syntactically forced regardless of precedence.
|
||||
*/
|
||||
cgroup = operator_precedence_group(lchild, &copname);
|
||||
if (cgroup > 0)
|
||||
@ -3442,7 +3424,7 @@ emit_precedence_warnings(ParseState *pstate,
|
||||
if (oldprecedence_l[cgroup] < oldprecedence_r[opgroup] &&
|
||||
cgroup != PREC_GROUP_IN &&
|
||||
cgroup != PREC_GROUP_NOT_IN &&
|
||||
cgroup != PREC_GROUP_POSTFIX_OP &&
|
||||
cgroup != PREC_GROUP_ANY_ALL &&
|
||||
cgroup != PREC_GROUP_POSTFIX_IS)
|
||||
ereport(WARNING,
|
||||
(errmsg("operator precedence change: %s is now lower precedence than %s",
|
||||
|
@ -52,7 +52,7 @@ typedef struct OprCacheKey
|
||||
{
|
||||
char oprname[NAMEDATALEN];
|
||||
Oid left_arg; /* Left input OID, or 0 if prefix op */
|
||||
Oid right_arg; /* Right input OID, or 0 if postfix op */
|
||||
Oid right_arg; /* Right input OID */
|
||||
Oid search_path[MAX_CACHED_PATH_LEN];
|
||||
} OprCacheKey;
|
||||
|
||||
@ -88,8 +88,7 @@ static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
|
||||
* Given a possibly-qualified operator name and exact input datatypes,
|
||||
* look up the operator.
|
||||
*
|
||||
* Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for
|
||||
* a postfix op.
|
||||
* Pass oprleft = InvalidOid for a prefix op.
|
||||
*
|
||||
* If the operator name is not schema-qualified, it is sought in the current
|
||||
* namespace search path.
|
||||
@ -115,10 +114,16 @@ LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright,
|
||||
|
||||
if (!OidIsValid(oprleft))
|
||||
oprkind = 'l';
|
||||
else if (!OidIsValid(oprright))
|
||||
oprkind = 'r';
|
||||
else
|
||||
else if (OidIsValid(oprright))
|
||||
oprkind = 'b';
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("postfix operators are not supported"),
|
||||
parser_errposition(pstate, location)));
|
||||
oprkind = 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||
@ -507,85 +512,6 @@ compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
|
||||
}
|
||||
|
||||
|
||||
/* right_oper() -- search for a unary right operator (postfix operator)
|
||||
* Given operator name and type of arg, return oper struct.
|
||||
*
|
||||
* IMPORTANT: the returned operator (if any) is only promised to be
|
||||
* coercion-compatible with the input datatype. Do not use this if
|
||||
* you need an exact- or binary-compatible match.
|
||||
*
|
||||
* If no matching operator found, return NULL if noError is true,
|
||||
* raise an error if it is false. pstate and location are used only to report
|
||||
* the error position; pass NULL/-1 if not available.
|
||||
*
|
||||
* NOTE: on success, the returned object is a syscache entry. The caller
|
||||
* must ReleaseSysCache() the entry when done with it.
|
||||
*/
|
||||
Operator
|
||||
right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
|
||||
{
|
||||
Oid operOid;
|
||||
OprCacheKey key;
|
||||
bool key_ok;
|
||||
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
|
||||
HeapTuple tup = NULL;
|
||||
|
||||
/*
|
||||
* Try to find the mapping in the lookaside cache.
|
||||
*/
|
||||
key_ok = make_oper_cache_key(pstate, &key, op, arg, InvalidOid, location);
|
||||
|
||||
if (key_ok)
|
||||
{
|
||||
operOid = find_oper_cache_entry(&key);
|
||||
if (OidIsValid(operOid))
|
||||
{
|
||||
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
||||
if (HeapTupleIsValid(tup))
|
||||
return (Operator) tup;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First try for an "exact" match.
|
||||
*/
|
||||
operOid = OpernameGetOprid(op, arg, InvalidOid);
|
||||
if (!OidIsValid(operOid))
|
||||
{
|
||||
/*
|
||||
* Otherwise, search for the most suitable candidate.
|
||||
*/
|
||||
FuncCandidateList clist;
|
||||
|
||||
/* Get postfix operators of given name */
|
||||
clist = OpernameGetCandidates(op, 'r', false);
|
||||
|
||||
/* No operators found? Then fail... */
|
||||
if (clist != NULL)
|
||||
{
|
||||
/*
|
||||
* We must run oper_select_candidate even if only one candidate,
|
||||
* otherwise we may falsely return a non-type-compatible operator.
|
||||
*/
|
||||
fdresult = oper_select_candidate(1, &arg, clist, &operOid);
|
||||
}
|
||||
}
|
||||
|
||||
if (OidIsValid(operOid))
|
||||
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
||||
|
||||
if (HeapTupleIsValid(tup))
|
||||
{
|
||||
if (key_ok)
|
||||
make_oper_cache_entry(&key, operOid);
|
||||
}
|
||||
else if (!noError)
|
||||
op_error(pstate, op, 'r', arg, InvalidOid, fdresult, location);
|
||||
|
||||
return (Operator) tup;
|
||||
}
|
||||
|
||||
|
||||
/* left_oper() -- search for a unary left operator (prefix operator)
|
||||
* Given operator name and type of arg, return oper struct.
|
||||
*
|
||||
@ -696,8 +622,7 @@ op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2)
|
||||
|
||||
appendStringInfoString(&argbuf, NameListToString(op));
|
||||
|
||||
if (oprkind != 'r')
|
||||
appendStringInfo(&argbuf, " %s", format_type_be(arg2));
|
||||
appendStringInfo(&argbuf, " %s", format_type_be(arg2));
|
||||
|
||||
return argbuf.data; /* return palloc'd string buffer */
|
||||
}
|
||||
@ -758,17 +683,16 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
|
||||
Oid rettype;
|
||||
OpExpr *result;
|
||||
|
||||
/* Select the operator */
|
||||
/* Check it's not a postfix operator */
|
||||
if (rtree == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("postfix operators are not supported")));
|
||||
|
||||
/* Select the operator */
|
||||
if (ltree == NULL)
|
||||
{
|
||||
/* right operator */
|
||||
ltypeId = exprType(ltree);
|
||||
rtypeId = InvalidOid;
|
||||
tup = right_oper(pstate, opname, ltypeId, false, location);
|
||||
}
|
||||
else if (ltree == NULL)
|
||||
{
|
||||
/* left operator */
|
||||
/* prefix operator */
|
||||
rtypeId = exprType(rtree);
|
||||
ltypeId = InvalidOid;
|
||||
tup = left_oper(pstate, opname, rtypeId, false, location);
|
||||
@ -795,17 +719,9 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
|
||||
parser_errposition(pstate, location)));
|
||||
|
||||
/* Do typecasting and build the expression tree */
|
||||
if (rtree == NULL)
|
||||
if (ltree == NULL)
|
||||
{
|
||||
/* right operator */
|
||||
args = list_make1(ltree);
|
||||
actual_arg_types[0] = ltypeId;
|
||||
declared_arg_types[0] = opform->oprleft;
|
||||
nargs = 1;
|
||||
}
|
||||
else if (ltree == NULL)
|
||||
{
|
||||
/* left operator */
|
||||
/* prefix operator */
|
||||
args = list_make1(rtree);
|
||||
actual_arg_types[0] = rtypeId;
|
||||
declared_arg_types[0] = opform->oprright;
|
||||
|
Reference in New Issue
Block a user