From 8636303d7aee147787c86a8f348bba7207d43b70 Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Wed, 26 Aug 2020 07:47:07 -0700 Subject: [PATCH v5 1/3] Removing postfix operator support. Removing postfix operator support from the grammar; disallowing the creation of postfix operators through the CREATE OPERATOR command; and removing the factorial postfix operator. This accomplishes nothing useful in itself, but sets up for grammar changes to allow keywords to be used as bare column aliases without the use of an explicit "AS". --- contrib/postgres_fdw/deparse.c | 5 +- .../postgres_fdw/expected/postgres_fdw.out | 8 +- contrib/postgres_fdw/sql/postgres_fdw.sql | 2 +- doc/src/sgml/catalogs.sgml | 2 +- doc/src/sgml/ref/alter_extension.sgml | 2 +- doc/src/sgml/ref/comment.sgml | 2 +- doc/src/sgml/syntax.sgml | 24 +--- doc/src/sgml/typeconv.sgml | 8 +- src/backend/catalog/namespace.c | 7 +- src/backend/catalog/pg_operator.c | 4 +- src/backend/commands/operatorcmds.c | 14 +- src/backend/nodes/print.c | 1 - src/backend/parser/gram.y | 54 +++----- src/backend/parser/parse_expr.c | 48 ++----- src/backend/parser/parse_oper.c | 121 +++--------------- src/backend/utils/adt/ruleutils.c | 31 ++--- src/bin/pg_dump/pg_dump.c | 6 +- src/bin/pg_dump/pg_dump_sort.c | 2 +- src/bin/pg_upgrade/check.c | 106 +++++++++++++++ src/bin/psql/describe.c | 2 +- src/include/catalog/pg_operator.dat | 5 +- src/include/catalog/pg_operator.h | 4 +- src/test/regress/expected/create_operator.out | 24 ++-- src/test/regress/expected/numeric.out | 19 ++- src/test/regress/expected/opr_sanity.out | 17 +-- src/test/regress/sql/create_operator.sql | 3 +- src/test/regress/sql/numeric.sql | 14 +- src/test/regress/sql/opr_sanity.sql | 14 +- src/tutorial/syscat.source | 14 -- 29 files changed, 251 insertions(+), 312 deletions(-) diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index ad37a74221..32a9b38f10 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -2716,15 +2716,14 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context) oprkind = form->oprkind; /* Sanity check. */ - Assert((oprkind == 'r' && list_length(node->args) == 1) || - (oprkind == 'l' && list_length(node->args) == 1) || + Assert((oprkind == 'l' && list_length(node->args) == 1) || (oprkind == 'b' && list_length(node->args) == 2)); /* Always parenthesize the expression. */ appendStringInfoChar(buf, '('); /* Deparse left operand. */ - if (oprkind == 'r' || oprkind == 'b') + if (oprkind == 'b') { arg = list_head(node->args); deparseExpr(lfirst(arg), context); diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 90db550b92..9728c252ba 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -653,12 +653,12 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- Op Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = (- "C 1"))) (3 rows) -EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r) - QUERY PLAN ----------------------------------------------------------------------------------------------------------- +EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = !!c1; -- OpExpr(r) + QUERY PLAN +----------------------------------------------------------------------------------------------------------- Foreign Scan on public.ft1 t1 Output: c1, c2, c3, c4, c5, c6, c7, c8 - Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((1::numeric = ("C 1" !))) + Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((1::numeric = (!! "C 1"))) (3 rows) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 83971665e3..a8e58a8f24 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -307,7 +307,7 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- Nu EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l) -EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r) +EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = !!c1; -- OpExpr(r) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- SubscriptingRef diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 9fe260ecff..6ed67eedb1 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5150,7 +5150,7 @@ SCRAM-SHA-256$<iteration count>:&l b = infix (both), l = prefix - (left), r = postfix (right) + (left) diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml index a2d405d6cd..c819c7bb4e 100644 --- a/doc/src/sgml/ref/alter_extension.sgml +++ b/doc/src/sgml/ref/alter_extension.sgml @@ -251,7 +251,7 @@ ALTER EXTENSION name DROP The data type(s) of the operator's arguments (optionally schema-qualified). Write NONE for the missing argument - of a prefix or postfix operator. + of a prefix operator. diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index fd7492a255..6e8ced3eaf 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -224,7 +224,7 @@ COMMENT ON The data type(s) of the operator's arguments (optionally schema-qualified). Write NONE for the missing argument - of a prefix or postfix operator. + of a prefix operator. diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 2f993ca2e0..5ad25d96d6 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -979,27 +979,6 @@ CAST ( 'string' AS type ) into the parser. - - You will - sometimes need to add parentheses when using combinations of - binary and unary operators. For instance: - -SELECT 5 ! - 6; - - will be parsed as: - -SELECT 5 ! (- 6); - - because the parser has no idea — until it is too late - — that ! is defined as a postfix operator, - not an infix one. To get the desired behavior in this case, you - must write: - -SELECT (5 !) - 6; - - This is the price one pays for extensibility. - - Operator Precedence (highest to lowest) @@ -1463,11 +1442,10 @@ $1.somecolumn - There are three possible syntaxes for an operator invocation: + There are two possible syntaxes for an operator invocation: expression operator expression (binary infix operator) operator expression (unary prefix operator) - expression operator (unary postfix operator) where the operator token follows the syntax rules of , or is one of the diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index 8900d0eb38..55780d35ec 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -97,7 +97,7 @@ Operators PostgreSQL allows expressions with -prefix and postfix unary (one-argument) operators, +prefix unary (one-argument) operators, as well as binary (two-argument) operators. Like functions, operators can be overloaded, so the same problem of selecting the right operator exists. @@ -357,13 +357,13 @@ Some examples follow. Factorial Operator Type Resolution -There is only one factorial operator (postfix !) +There is only one factorial operator (prefix !!) defined in the standard catalog, and it takes an argument of type bigint. The scanner assigns an initial type of integer to the argument in this query expression: -SELECT 40 ! AS "40 factorial"; +SELECT !! 40 AS "40 factorial"; 40 factorial -------------------------------------------------- @@ -375,7 +375,7 @@ So the parser does a type conversion on the operand and the query is equivalent to: -SELECT CAST(40 AS bigint) ! AS "40 factorial"; +SELECT !! CAST(40 AS bigint) AS "40 factorial"; diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 0152e3869a..0c78ab8af7 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -1473,8 +1473,7 @@ FunctionIsVisible(Oid funcid) * Given a possibly-qualified operator name and exact input datatypes, * look up the operator. Returns InvalidOid if not found. * - * 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. If the name is schema-qualified and the given @@ -1580,8 +1579,8 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright) * namespace case, we arrange for entries in earlier namespaces to mask * identical entries in later namespaces. * - * The returned items always have two args[] entries --- one or the other - * will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too. + * The returned items always have two args[] entries --- one will be + * InvalidOid for a prefix oprkind. nargs is 2, too. */ FuncCandidateList OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok) diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 65a36be5ee..f817627d86 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -245,7 +245,7 @@ OperatorShellMake(const char *operatorName, values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); - values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l'); values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); @@ -494,7 +494,7 @@ OperatorCreate(const char *operatorName, values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); - values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l'); values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index bf23937849..b468c7701b 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -168,10 +168,22 @@ DefineOperator(List *names, List *parameters) if (typeName2) typeId2 = typenameTypeId(NULL, typeName2); + /* + * If only the right argument is missing, the user is likely trying to + * create a postfix operator, so give them a hint about why that does not + * work. But if both arguments are missing, do not mention postfix + * operators, as the user most likely simply neglected to mention the + * arguments. + */ if (!OidIsValid(typeId1) && !OidIsValid(typeId2)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("at least one of leftarg or rightarg must be specified"))); + errmsg("operator arguments must be specified"))); + if (!OidIsValid(typeId2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator right argument must be specified"), + errhint("Postfix operators are not supported."))); if (typeName1) { diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 42476724d8..970a2d4384 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -394,7 +394,6 @@ print_expr(const Node *expr, const List *rtable) } else { - /* we print prefix and postfix ops the same... */ printf("%s ", ((opname != NULL) ? opname : "(invalid operator)")); print_expr(get_leftop((const Expr *) e), rtable); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dbb47d4982..2480fba771 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -731,29 +731,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); /* Precedence: lowest to highest */ -%nonassoc SET /* see relation_expr_opt_alias */ -%left UNION EXCEPT -%left INTERSECT -%left OR -%left AND -%right NOT -%nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */ -%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 - * 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: + * lower than 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: * for PARTITION, RANGE, ROWS, GROUPS to support opt_existing_window_name; - * for RANGE, ROWS, GROUPS so that they can follow a_expr without creating - * postfix-operator problems; - * for GENERATED so that it can follow b_expr; - * and for NULL so that it can follow b_expr in ColQualList without creating - * postfix-operator problems. + * for UNBOUNDED, PRECEDING, FOLLOWING to support frame_bound; * * To support CUBE and ROLLUP in GROUP BY without reserving them, we give them * an explicit priority lower than '(', so that a rule with CUBE '(' will shift @@ -769,8 +755,19 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * keywords anywhere else in the grammar, but it's definitely risky. We can * blame any funny behavior of UNBOUNDED on the SQL standard, though. */ -%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */ -%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP +%nonassoc UNBOUNDED IDENT +%nonassoc PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP + +%nonassoc SET /* see relation_expr_opt_alias */ +%left UNION EXCEPT +%left INTERSECT +%left OR +%left AND +%right NOT +%nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */ +%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 Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -791,8 +788,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * left-associativity among the JOIN rules themselves. */ %left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL -/* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */ -%right PRESERVE STRIP_P %% @@ -12989,9 +12984,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); } | a_expr OR a_expr @@ -13404,8 +13396,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); @@ -14661,11 +14651,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 { diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index f69976cc8c..9e266cf3b0 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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); @@ -2014,15 +2014,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) if (operator_precedence_warning) { - if (sublink->operName == NIL) - emit_precedence_warnings(pstate, PREC_GROUP_IN, "IN", - sublink->testexpr, NULL, - sublink->location); - else - emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP, - strVal(llast(sublink->operName)), - sublink->testexpr, NULL, - sublink->location); + emit_precedence_warnings(pstate, PREC_GROUP_IN, "IN", + sublink->testexpr, NULL, + sublink->location); } /* @@ -3244,28 +3238,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 +3333,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 +3409,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 +3418,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", diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 2749974f63..877ffa03a6 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -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,13 @@ 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_INVALID_PARAMETER_VALUE), + errmsg("postfix operators are not supported"), + parser_errposition(pstate, location))); ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), @@ -507,85 +509,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 +619,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,15 +680,14 @@ 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) - { - /* right operator */ - ltypeId = exprType(ltree); - rtypeId = InvalidOid; - tup = right_oper(pstate, opname, ltypeId, false, location); - } - else if (ltree == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("postfix operators are not supported"))); + + /* Select the operator */ + if (ltree == NULL) { /* left operator */ rtypeId = exprType(rtree); @@ -795,15 +716,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, parser_errposition(pstate, location))); /* Do typecasting and build the expression tree */ - if (rtree == NULL) - { - /* right operator */ - args = list_make1(ltree); - actual_arg_types[0] = ltypeId; - declared_arg_types[0] = opform->oprleft; - nargs = 1; - } - else if (ltree == NULL) + if (ltree == NULL) { /* left operator */ args = list_make1(rtree); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 60dd80c23c..16abfcb406 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9207,25 +9207,16 @@ get_oper_expr(OpExpr *expr, deparse_context *context) if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for operator %u", opno); optup = (Form_pg_operator) GETSTRUCT(tp); - switch (optup->oprkind) - { - case 'l': - appendStringInfo(buf, "%s ", - generate_operator_name(opno, - InvalidOid, - exprType(arg))); - get_rule_expr_paren(arg, context, true, (Node *) expr); - break; - case 'r': - get_rule_expr_paren(arg, context, true, (Node *) expr); - appendStringInfo(buf, " %s", - generate_operator_name(opno, - exprType(arg), - InvalidOid)); - break; - default: - elog(ERROR, "bogus oprkind: %d", optup->oprkind); + if (optup->oprkind == 'l') + { + appendStringInfo(buf, "%s ", + generate_operator_name(opno, + InvalidOid, + exprType(arg))); + get_rule_expr_paren(arg, context, true, (Node *) expr); } + else + elog(ERROR, "bogus oprkind: %d", optup->oprkind); ReleaseSysCache(tp); } if (!PRETTY_PAREN(context)) @@ -11087,10 +11078,6 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2) p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2, true, -1); break; - case 'r': - p_result = right_oper(NULL, list_make1(makeString(oprname)), arg1, - true, -1); - break; default: elog(ERROR, "unrecognized oprkind: %d", operform->oprkind); p_result = NULL; /* keep compiler quiet */ diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 2cb3f9b083..a93ab8eed3 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12643,11 +12643,9 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprinfo->dobj.name); /* - * right unary means there's a left arg and left unary means there's a - * right arg + * left unary means there's only a right arg */ - if (strcmp(oprkind, "r") == 0 || - strcmp(oprkind, "b") == 0) + if (strcmp(oprkind, "b") == 0) { appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft); appendPQExpBufferStr(oprid, oprleft); diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 654e2ec514..c7f06604c6 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -203,7 +203,7 @@ DOTypeNameCompare(const void *p1, const void *p2) OprInfo *oobj1 = *(OprInfo *const *) p1; OprInfo *oobj2 = *(OprInfo *const *) p2; - /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */ + /* oprkind is 'l' or 'b'; this sorts prefix and infix */ cmpval = (oobj2->oprkind - oobj1->oprkind); if (cmpval != 0) return cmpval; diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index 00aef855dc..b86098dc42 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -22,6 +22,7 @@ static void check_is_install_user(ClusterInfo *cluster); static void check_proper_datallowconn(ClusterInfo *cluster); static void check_for_prepared_transactions(ClusterInfo *cluster); static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster); +static void check_for_user_defined_postfix_ops(ClusterInfo *cluster); static void check_for_tables_with_oids(ClusterInfo *cluster); static void check_for_reg_data_type_usage(ClusterInfo *cluster); static void check_for_jsonb_9_4_usage(ClusterInfo *cluster); @@ -100,6 +101,13 @@ check_and_dump_old_cluster(bool live_check) check_for_reg_data_type_usage(&old_cluster); check_for_isn_and_int8_passing_mismatch(&old_cluster); + /* + * Pre-PG 14 allowed user defined postfix operators, which are not + * supported anymore. Verify there are none, iff applicable. + */ + if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300) + check_for_user_defined_postfix_ops(&old_cluster); + /* * Pre-PG 12 allowed tables to be declared WITH OIDS, which is not * supported anymore. Verify there are none, iff applicable. @@ -896,6 +904,104 @@ check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster) check_ok(); } +/* + * Verify that no user defined postfix operators exist. + */ +static void +check_for_user_defined_postfix_ops(ClusterInfo *cluster) +{ + int dbnum; + FILE *script = NULL; + bool found = false; + char output_path[MAXPGPATH]; + + prep_status("Checking for user defined postfix operators"); + + snprintf(output_path, sizeof(output_path), + "postfix_ops.txt"); + + /* Find any user defined postfix operators */ + for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) + { + PGresult *res; + bool db_used = false; + int ntups; + int rowno; + int i_oproid, + i_oprnsp, + i_oprname, + i_typnsp, + i_typname; + DbInfo *active_db = &cluster->dbarr.dbs[dbnum]; + PGconn *conn = connectToServer(cluster, active_db->db_name); + + /* + * The query below hardcodes FirstNormalObjectId as 16384 rather than + * interpolating that C #define into the string because, if that + * #define is ever changed, the cutoff we want to use is the definition + * from pre-verion 14 servers, not from some future version of the + * code. + */ + res = executeQueryOrDie(conn, + "SELECT o.oid AS oproid, " + " n.nspname AS oprnsp, " + " o.oprname, " + " tn.nspname AS typnsp, " + " t.typname " + "FROM pg_catalog.pg_operator o, " + " pg_catalog.pg_namespace n, " + " pg_catalog.pg_type t, " + " pg_catalog.pg_namespace tn " + "WHERE o.oprnamespace = n.oid AND " + " o.oprleft = t.oid AND " + " t.typnamespace = tn.oid AND " + " o.oprright = 0 AND " + " o.oid >= 16384"); + ntups = PQntuples(res); + i_oproid = PQfnumber(res, "oproid"); + i_oprnsp = PQfnumber(res, "oprnsp"); + i_oprname = PQfnumber(res, "oprname"); + i_typnsp = PQfnumber(res, "typnsp"); + i_typname = PQfnumber(res, "typname"); + for (rowno = 0; rowno < ntups; rowno++) + { + found = true; + if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL) + pg_fatal("could not open file \"%s\": %s\n", + output_path, strerror(errno)); + if (!db_used) + { + fprintf(script, "In database: %s\n", active_db->db_name); + db_used = true; + } + fprintf(script, " (oid=%s) %s.%s (%s.%s)\n", + PQgetvalue(res, rowno, i_oproid), + PQgetvalue(res, rowno, i_oprnsp), + PQgetvalue(res, rowno, i_oprname), + PQgetvalue(res, rowno, i_typnsp), + PQgetvalue(res, rowno, i_typname)); + } + + PQclear(res); + + PQfinish(conn); + } + + if (script) + fclose(script); + + if (found) + { + pg_log(PG_REPORT, "fatal\n"); + pg_fatal("Your installation contains user defined postfix operators, which is not\n" + "supported anymore. Consider dropping the postfix operators and replacing\n" + "them with prefix operators or function calls.\n" + "A list of user defined postfix operators is in the file:\n" + " %s\n\n", output_path); + } + else + check_ok(); +} /* * Verify that no tables are declared WITH OIDS. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index d81f1575bf..1aaf5ccb38 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -805,7 +805,7 @@ describeOperators(const char *pattern, bool verbose, bool showSystem) "SELECT n.nspname as \"%s\",\n" " o.oprname AS \"%s\",\n" " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n" - " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n" + " pg_catalog.format_type(o.oprright, NULL) AS \"%s\",\n" " pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n", gettext_noop("Schema"), gettext_noop("Name"), diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat index 5b0e063655..9733f18de7 100644 --- a/src/include/catalog/pg_operator.dat +++ b/src/include/catalog/pg_operator.dat @@ -218,10 +218,7 @@ oprname => '>=', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool', oprcom => '<=(xid8,xid8)', oprnegate => '<(xid8,xid8)', oprcode => 'xid8ge', oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' }, -{ oid => '388', descr => 'factorial', - oprname => '!', oprkind => 'r', oprleft => 'int8', oprright => '0', - oprresult => 'numeric', oprcode => 'numeric_fac' }, -{ oid => '389', descr => 'deprecated, use ! instead', +{ oid => '389', descr => 'factorial', oprname => '!!', oprkind => 'l', oprleft => '0', oprright => 'int8', oprresult => 'numeric', oprcode => 'numeric_fac' }, { oid => '385', descr => 'equal', diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 1daa263852..1491b87582 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -41,7 +41,7 @@ CATALOG(pg_operator,2617,OperatorRelationId) /* operator owner */ Oid oprowner BKI_DEFAULT(PGUID); - /* 'l', 'r', or 'b' */ + /* 'l' or 'b' */ char oprkind BKI_DEFAULT(b); /* can be used in merge join? */ @@ -53,7 +53,7 @@ CATALOG(pg_operator,2617,OperatorRelationId) /* left arg type, or 0 if 'l' oprkind */ Oid oprleft BKI_LOOKUP(pg_type); - /* right arg type, or 0 if 'r' oprkind */ + /* right arg type */ Oid oprright BKI_LOOKUP(pg_type); /* result datatype */ diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out index 54e8b79159..83172cc073 100644 --- a/src/test/regress/expected/create_operator.out +++ b/src/test/regress/expected/create_operator.out @@ -22,10 +22,14 @@ CREATE OPERATOR #@# ( leftarg = int8, -- right unary procedure = numeric_fac ); +ERROR: operator right argument must be specified +HINT: Postfix operators are not supported. CREATE OPERATOR #%# ( leftarg = int8, -- right unary procedure = numeric_fac ); +ERROR: operator right argument must be specified +HINT: Postfix operators are not supported. -- Test operator created above SELECT point '(1,2)' <% widget '(0,0,3)' AS t, point '(1,2)' <% widget '(0,0,1)' AS f; @@ -36,7 +40,9 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t, -- Test comments COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary'; -ERROR: operator does not exist: integer ###### +ERROR: postfix operators are not supported +COMMENT ON OPERATOR ###### (NONE, int4) IS 'bad left unary'; +ERROR: operator does not exist: ###### integer -- => is disallowed now CREATE OPERATOR => ( leftarg = int8, -- right unary @@ -52,12 +58,12 @@ CREATE OPERATOR !=- ( leftarg = int8, -- right unary procedure = numeric_fac ); +ERROR: operator right argument must be specified +HINT: Postfix operators are not supported. SELECT 2 !=-; - ?column? ----------- - 2 -(1 row) - +ERROR: syntax error at or near ";" +LINE 1: SELECT 2 !=-; + ^ -- make sure lexer returns != as <> even in edge cases SELECT 2 !=/**/ 1, 2 !=/**/ 2; ?column? | ?column? @@ -172,11 +178,13 @@ CREATE OPERATOR #@%# ( invalid_att = int8 ); WARNING: operator attribute "invalid_att" not recognized --- Should fail. At least leftarg or rightarg should be mandatorily specified +ERROR: operator right argument must be specified +HINT: Postfix operators are not supported. +-- Should fail. At least rightarg should be mandatorily specified CREATE OPERATOR #@%# ( procedure = numeric_fac ); -ERROR: at least one of leftarg or rightarg must be specified +ERROR: operator arguments must be specified -- Should fail. Procedure should be mandatorily specified CREATE OPERATOR #@%# ( leftarg = int8 diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index 8546ce901f..186cf1d507 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -2960,7 +2960,7 @@ ERROR: value overflows numeric format -- -- Tests for factorial -- -SELECT 4!; +SELECT !!4; ?column? ---------- 24 @@ -2978,19 +2978,30 @@ SELECT factorial(15); 1307674368000 (1 row) -SELECT 100000!; +SELECT !!100000; ERROR: value overflows numeric format -SELECT 0!; +SELECT !!0; ?column? ---------- 1 (1 row) -SELECT -4!; +SELECT !!(-4); ERROR: factorial of a negative number is undefined SELECT factorial(-4); ERROR: factorial of a negative number is undefined -- +-- Postfix operators are not supported. Check that. +-- +SELECT -5!; +ERROR: syntax error at or near ";" +LINE 1: SELECT -5!; + ^ +SELECT 5!; +ERROR: syntax error at or near ";" +LINE 1: SELECT 5!; + ^ +-- -- Tests for pg_lsn() -- SELECT pg_lsn(23783416::numeric); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 1b3c146e4c..7825a765cd 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1066,7 +1066,7 @@ WHERE condefault AND -- Look for illegal values in pg_operator fields. SELECT p1.oid, p1.oprname FROM pg_operator as p1 -WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l' AND p1.oprkind != 'r') OR +WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l') OR p1.oprresult = 0 OR p1.oprcode = 0; oid | oprname -----+--------- @@ -1077,8 +1077,7 @@ SELECT p1.oid, p1.oprname FROM pg_operator as p1 WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR (p1.oprleft != 0 and p1.oprkind = 'l') OR - (p1.oprright = 0 and p1.oprkind != 'r') OR - (p1.oprright != 0 and p1.oprkind = 'r'); + p1.oprright = 0; oid | oprname -----+--------- (0 rows) @@ -1285,18 +1284,6 @@ WHERE p1.oprcode = p2.oid AND -----+---------+-----+--------- (0 rows) -SELECT p1.oid, p1.oprname, p2.oid, p2.proname -FROM pg_operator AS p1, pg_proc AS p2 -WHERE p1.oprcode = p2.oid AND - p1.oprkind = 'r' AND - (p2.pronargs != 1 - OR NOT binary_coercible(p2.prorettype, p1.oprresult) - OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0]) - OR p1.oprright != 0); - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - -- If the operator is mergejoinable or hashjoinable, its underlying function -- should not be volatile. SELECT p1.oid, p1.oprname, p2.oid, p2.proname diff --git a/src/test/regress/sql/create_operator.sql b/src/test/regress/sql/create_operator.sql index 8b6fd0bb43..94961d9899 100644 --- a/src/test/regress/sql/create_operator.sql +++ b/src/test/regress/sql/create_operator.sql @@ -38,6 +38,7 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t, -- Test comments COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary'; +COMMENT ON OPERATOR ###### (NONE, int4) IS 'bad left unary'; -- => is disallowed now CREATE OPERATOR => ( @@ -133,7 +134,7 @@ CREATE OPERATOR #@%# ( invalid_att = int8 ); --- Should fail. At least leftarg or rightarg should be mandatorily specified +-- Should fail. At least rightarg should be mandatorily specified CREATE OPERATOR #@%# ( procedure = numeric_fac ); diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index 416c16722a..45602b00e1 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -1298,14 +1298,20 @@ SELECT lcm(9999 * (10::numeric)^131068 + (10::numeric^131068 - 1), 2); -- overfl -- -- Tests for factorial -- -SELECT 4!; +SELECT !!4; SELECT !!3; SELECT factorial(15); -SELECT 100000!; -SELECT 0!; -SELECT -4!; +SELECT !!100000; +SELECT !!0; +SELECT !!(-4); SELECT factorial(-4); +-- +-- Postfix operators are not supported. Check that. +-- +SELECT -5!; +SELECT 5!; + -- -- Tests for pg_lsn() -- diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 7a9180b081..307aab1deb 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -571,7 +571,7 @@ WHERE condefault AND SELECT p1.oid, p1.oprname FROM pg_operator as p1 -WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l' AND p1.oprkind != 'r') OR +WHERE (p1.oprkind != 'b' AND p1.oprkind != 'l') OR p1.oprresult = 0 OR p1.oprcode = 0; -- Look for missing or unwanted operand types @@ -580,8 +580,7 @@ SELECT p1.oid, p1.oprname FROM pg_operator as p1 WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR (p1.oprleft != 0 and p1.oprkind = 'l') OR - (p1.oprright = 0 and p1.oprkind != 'r') OR - (p1.oprright != 0 and p1.oprkind = 'r'); + p1.oprright = 0; -- Look for conflicting operator definitions (same names and input datatypes). @@ -715,15 +714,6 @@ WHERE p1.oprcode = p2.oid AND OR NOT binary_coercible(p1.oprright, p2.proargtypes[0]) OR p1.oprleft != 0); -SELECT p1.oid, p1.oprname, p2.oid, p2.proname -FROM pg_operator AS p1, pg_proc AS p2 -WHERE p1.oprcode = p2.oid AND - p1.oprkind = 'r' AND - (p2.pronargs != 1 - OR NOT binary_coercible(p2.prorettype, p1.oprresult) - OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0]) - OR p1.oprright != 0); - -- If the operator is mergejoinable or hashjoinable, its underlying function -- should not be volatile. diff --git a/src/tutorial/syscat.source b/src/tutorial/syscat.source index 3a1767f97b..a84ea0d5c9 100644 --- a/src/tutorial/syscat.source +++ b/src/tutorial/syscat.source @@ -110,20 +110,6 @@ SELECT n.nspname, o.oprname AS left_unary, ORDER BY nspname, operand; --- --- lists all right unary operators --- -SELECT n.nspname, o.oprname AS right_unary, - format_type(left_type.oid, null) AS operand, - format_type(result.oid, null) AS return_type - FROM pg_namespace n, pg_operator o, - pg_type left_type, pg_type result - WHERE o.oprnamespace = n.oid - and o.oprkind = 'r' -- right unary - and o.oprleft = left_type.oid - and o.oprresult = result.oid - ORDER BY nspname, operand; - -- -- lists all binary operators -- -- 2.21.1 (Apple Git-122.3)