From bb623e8aa23a430cc5021118a6026e70341c9acf Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Mon, 31 Aug 2020 13:03:41 -0700 Subject: [PATCH v6 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 prefix and postfix operators. In the commit message for 6ca547cf75ef6e922476c51a3fb5e253eef5f1b6, Tom Lane wrote: There is only one built-in postfix operator, ! for factorial. Label it deprecated in the docs and in pg_description, and adjust some examples that formerly relied on it. (The sister prefix operator !! is also deprecated. We don't really have to remove that one, but since we're suggesting that people use factorial() instead, it seems better to remove both operators.) In the spirit of that message, this patch also removes the !! factorial prefix operator, even though it is not strongly connected with the rest of what this commit does. 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 | 1 - doc/src/sgml/catalogs.sgml | 2 +- doc/src/sgml/ref/alter_extension.sgml | 2 +- doc/src/sgml/ref/alter_opfamily.sgml | 2 +- doc/src/sgml/ref/comment.sgml | 2 +- doc/src/sgml/ref/create_opclass.sgml | 2 +- doc/src/sgml/ref/create_operator.sgml | 20 +-- doc/src/sgml/ref/drop_operator.sgml | 7 - doc/src/sgml/syntax.sgml | 3 +- doc/src/sgml/typeconv.sgml | 2 +- doc/src/sgml/xoper.sgml | 12 +- 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 | 30 ++--- 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 | 6 - src/include/catalog/pg_operator.h | 4 +- src/include/catalog/pg_proc.dat | 4 - src/test/regress/expected/create_operator.out | 33 +++-- src/test/regress/expected/numeric.out | 39 +++--- src/test/regress/expected/opr_sanity.out | 17 +-- src/test/regress/sql/create_operator.sql | 15 ++- src/test/regress/sql/numeric.sql | 15 ++- src/test/regress/sql/opr_sanity.sql | 14 +- src/tutorial/complex.source | 2 +- src/tutorial/syscat.source | 14 -- 36 files changed, 264 insertions(+), 339 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..cae90b45e2 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -653,14 +653,6 @@ 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 ----------------------------------------------------------------------------------------------------------- - 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" !))) -(3 rows) - EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 83971665e3..bbad5b6ec2 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -307,7 +307,6 @@ 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 (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 1d1b8ce8fb..39eee9d1e2 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5154,7 +5154,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/alter_opfamily.sgml b/doc/src/sgml/ref/alter_opfamily.sgml index 4ac1cca95a..c86507edac 100644 --- a/doc/src/sgml/ref/alter_opfamily.sgml +++ b/doc/src/sgml/ref/alter_opfamily.sgml @@ -141,7 +141,7 @@ ALTER OPERATOR FAMILY name USING name [ DEFAUL In an OPERATOR clause, the operand data type(s) of the operator, or NONE to - signify a left-unary or right-unary operator. The operand data + signify a left-unary operator. The operand data types can be omitted in the normal case where they are the same as the operator class's data type. diff --git a/doc/src/sgml/ref/create_operator.sgml b/doc/src/sgml/ref/create_operator.sgml index 66c34e0072..082494bd75 100644 --- a/doc/src/sgml/ref/create_operator.sgml +++ b/doc/src/sgml/ref/create_operator.sgml @@ -86,20 +86,9 @@ CREATE OPERATOR name ( - At least one of LEFTARG and RIGHTARG must be defined. For - binary operators, both must be defined. For right unary - operators, only LEFTARG should be defined, while for left - unary operators only RIGHTARG should be defined. - - - - - Right unary, also called postfix, operators are deprecated and will be - removed in PostgreSQL version 14. - - - - + For binary operators, both LEFTARG and + RIGHTARG must be defined. For left unary operators only + RIGHTARG should be defined. The function_name function must have been previously defined using CREATE FUNCTION and must be defined to accept the correct number @@ -169,8 +158,7 @@ CREATE OPERATOR name ( right_type - The data type of the operator's right operand, if any. - This option would be omitted for a right-unary operator. + The data type of the operator's right operand. diff --git a/doc/src/sgml/ref/drop_operator.sgml b/doc/src/sgml/ref/drop_operator.sgml index 2dff050ecf..3c8eecb53a 100644 --- a/doc/src/sgml/ref/drop_operator.sgml +++ b/doc/src/sgml/ref/drop_operator.sgml @@ -120,13 +120,6 @@ DROP OPERATOR ~ (none, bit); - - Remove the right unary factorial operator x! - for type bigint: - -DROP OPERATOR ! (bigint, none); - - Remove multiple operators in one command: diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index b0ae5d2e12..abde93895a 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -1444,11 +1444,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 98662fc91f..ef9e311342 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. diff --git a/doc/src/sgml/xoper.sgml b/doc/src/sgml/xoper.sgml index 56b08491c9..585a8c958f 100644 --- a/doc/src/sgml/xoper.sgml +++ b/doc/src/sgml/xoper.sgml @@ -20,8 +20,8 @@ - PostgreSQL supports left unary, right - unary, and binary operators. Operators can be + PostgreSQL supports left unary + and binary operators. Operators can be overloaded;overloadingoperators that is, the same operator name can be used for different operators that have different numbers and types of operands. When a query is @@ -64,9 +64,9 @@ SELECT (a + b) AS c FROM test_complex; - We've shown how to create a binary operator here. To create unary - operators, just omit one of leftarg (for left unary) or - rightarg (for right unary). The function + We've shown how to create a binary operator here. To create left unary + operators, just omit the leftarg. + The function clause and the argument clauses are the only required items in CREATE OPERATOR. The commutator clause shown in the example is an optional hint to the query @@ -202,7 +202,7 @@ SELECT (a + b) AS c FROM test_complex; Unlike commutators, a pair of unary operators could validly be marked as each other's negators; that would mean (A x) equals NOT (B x) - for all x, or the equivalent for right unary operators. + for all x. 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..d8a607e29a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -741,19 +741,14 @@ 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 - * 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. * * 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 @@ -7993,7 +7988,12 @@ oper_argtypes: | '(' NONE ',' Typename ')' /* left unary */ { $$ = list_make2(NULL, $4); } | '(' Typename ',' NONE ')' /* right unary */ - { $$ = list_make2($2, NULL); } + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator right argument must be specified"), + errhint("Postfix operators are not supported."))); + } ; any_operator: @@ -12989,8 +12989,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); } @@ -13404,8 +13402,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 +14657,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 4f8b9865ef..7cc812adda 100644 --- a/src/include/catalog/pg_operator.dat +++ b/src/include/catalog/pg_operator.dat @@ -218,12 +218,6 @@ oprname => '>=', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool', oprcom => '<=(xid8,xid8)', oprnegate => '<(xid8,xid8)', oprcode => 'xid8ge', oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' }, -{ oid => '388', descr => 'deprecated, use factorial() instead', - oprname => '!', oprkind => 'r', oprleft => 'int8', oprright => '0', - oprresult => 'numeric', oprcode => 'numeric_fac' }, -{ oid => '389', descr => 'deprecated, use factorial() instead', - oprname => '!!', oprkind => 'l', oprleft => '0', oprright => 'int8', - oprresult => 'numeric', oprcode => 'numeric_fac' }, { oid => '385', descr => 'equal', oprname => '=', oprcanhash => 't', oprleft => 'cid', oprright => 'cid', oprresult => 'bool', oprcom => '=(cid,cid)', oprcode => 'cideq', 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/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1dd325e0e6..f5af410434 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -327,10 +327,6 @@ { oid => '110', descr => 'I/O', proname => 'unknownout', prorettype => 'cstring', proargtypes => 'unknown', prosrc => 'unknownout' }, -{ oid => '111', - descr => 'implementation of deprecated ! and !! factorial operators', - proname => 'numeric_fac', prorettype => 'numeric', proargtypes => 'int8', - prosrc => 'numeric_fac' }, { oid => '115', proname => 'box_above_eq', prorettype => 'bool', proargtypes => 'box box', diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out index 54e8b79159..9d97564a4c 100644 --- a/src/test/regress/expected/create_operator.out +++ b/src/test/regress/expected/create_operator.out @@ -15,16 +15,16 @@ CREATE OPERATOR <% ( negator = >=% ); CREATE OPERATOR @#@ ( - rightarg = int8, -- left unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); CREATE OPERATOR #@# ( - leftarg = int8, -- right unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); CREATE OPERATOR #%# ( - leftarg = int8, -- right unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); -- Test operator created above SELECT point '(1,2)' <% widget '(0,0,3)' AS t, @@ -36,7 +36,10 @@ 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: operator right argument must be specified +HINT: 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 +55,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 +175,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..8442fbef7d 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -2960,16 +2960,16 @@ ERROR: value overflows numeric format -- -- Tests for factorial -- -SELECT 4!; - ?column? ----------- - 24 +SELECT factorial(4); + factorial +----------- + 24 (1 row) -SELECT !!3; - ?column? ----------- - 6 +SELECT factorial(3); + factorial +----------- + 6 (1 row) SELECT factorial(15); @@ -2978,19 +2978,28 @@ SELECT factorial(15); 1307674368000 (1 row) -SELECT 100000!; +SELECT factorial(100000); ERROR: value overflows numeric format -SELECT 0!; - ?column? ----------- - 1 +SELECT factorial(0); + factorial +----------- + 1 (1 row) -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..d6c3dc98c1 100644 --- a/src/test/regress/sql/create_operator.sql +++ b/src/test/regress/sql/create_operator.sql @@ -18,18 +18,18 @@ CREATE OPERATOR <% ( ); CREATE OPERATOR @#@ ( - rightarg = int8, -- left unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); CREATE OPERATOR #@# ( - leftarg = int8, -- right unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); CREATE OPERATOR #%# ( - leftarg = int8, -- right unary - procedure = numeric_fac + rightarg = float8, -- left unary + procedure = dsqrt ); -- Test operator created above @@ -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..096cafeb69 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -1298,14 +1298,19 @@ SELECT lcm(9999 * (10::numeric)^131068 + (10::numeric^131068 - 1), 2); -- overfl -- -- Tests for factorial -- -SELECT 4!; -SELECT !!3; +SELECT factorial(4); +SELECT factorial(3); SELECT factorial(15); -SELECT 100000!; -SELECT 0!; -SELECT -4!; +SELECT factorial(100000); +SELECT factorial(0); 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/complex.source b/src/tutorial/complex.source index 0355926701..65db7d5144 100644 --- a/src/tutorial/complex.source +++ b/src/tutorial/complex.source @@ -111,7 +111,7 @@ CREATE FUNCTION complex_add(complex, complex) LANGUAGE C IMMUTABLE STRICT; -- we can now define the operator. We show a binary operator here but you --- can also define unary operators by omitting either of leftarg or rightarg. +-- can also define a left unary operator by omitting the leftarg. CREATE OPERATOR + ( leftarg = complex, rightarg = complex, 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)