Remove support for postfix (right-unary) operators.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Sep 2020 23:38:05 +0000 (19:38 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Sep 2020 23:38:05 +0000 (19:38 -0400)
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

32 files changed:
contrib/postgres_fdw/deparse.c
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/alter_extension.sgml
doc/src/sgml/ref/alter_operator.sgml
doc/src/sgml/ref/alter_opfamily.sgml
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_opclass.sgml
doc/src/sgml/ref/create_operator.sgml
doc/src/sgml/ref/drop_operator.sgml
doc/src/sgml/syntax.sgml
doc/src/sgml/typeconv.sgml
doc/src/sgml/xoper.sgml
src/backend/catalog/namespace.c
src/backend/catalog/pg_operator.c
src/backend/commands/operatorcmds.c
src/backend/nodes/print.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_oper.c
src/backend/utils/adt/ruleutils.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_upgrade/check.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/pg_operator.h
src/include/parser/parse_oper.h
src/test/regress/expected/create_operator.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/create_operator.sql
src/test/regress/sql/opr_sanity.sql
src/tutorial/complex.source
src/tutorial/syscat.source

index ad37a7422133eb0d4a52cb818e4e23faee0e515a..2d44df19fee09effdc20226cb3a8c287c5e90ba7 100644 (file)
@@ -2706,7 +2706,6 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
        HeapTuple       tuple;
        Form_pg_operator form;
        char            oprkind;
-       ListCell   *arg;
 
        /* Retrieve information about the operator from system catalog. */
        tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
@@ -2716,18 +2715,16 @@ 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')
+       /* Deparse left operand, if any. */
+       if (oprkind == 'b')
        {
-               arg = list_head(node->args);
-               deparseExpr(lfirst(arg), context);
+               deparseExpr(linitial(node->args), context);
                appendStringInfoChar(buf, ' ');
        }
 
@@ -2735,12 +2732,8 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
        deparseOperatorName(buf, form);
 
        /* Deparse right operand. */
-       if (oprkind == 'l' || oprkind == 'b')
-       {
-               arg = list_tail(node->args);
-               appendStringInfoChar(buf, ' ');
-               deparseExpr(lfirst(arg), context);
-       }
+       appendStringInfoChar(buf, ' ');
+       deparseExpr(llast(node->args), context);
 
        appendStringInfoChar(buf, ')');
 
index 508bea3bc6442a083bb345f12d9ef0a2c39c0c09..7e99928d0c1cf96bf3388b3aa488fe1bee1a26e7 100644 (file)
@@ -5159,8 +5159,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        <structfield>oprkind</structfield> <type>char</type>
       </para>
       <para>
-       <literal>b</literal> = infix (<quote>both</quote>), <literal>l</literal> = prefix
-       (<quote>left</quote>), <literal>r</literal> = postfix (<quote>right</quote>)
+       <literal>b</literal> = infix operator (<quote>both</quote>),
+       or <literal>l</literal> = prefix operator (<quote>left</quote>)
       </para></entry>
      </row>
 
@@ -5188,7 +5188,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        (references <link linkend="catalog-pg-type"><structname>pg_type</structname></link>.<structfield>oid</structfield>)
       </para>
       <para>
-       Type of the left operand
+       Type of the left operand (0 if none)
       </para></entry>
      </row>
 
@@ -5266,7 +5266,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
   </table>
 
   <para>
-   Unused column contain zeroes. For example, <structfield>oprleft</structfield>
+   Unused columns contain zeroes. For example, <structfield>oprleft</structfield>
    is zero for a prefix operator.
   </para>
 
index a2d405d6cdfb58d3b65e37ce72a13b23cc8a31fb..c819c7bb4e3c474d4efcb25a9538a1cb5c978175 100644 (file)
@@ -251,7 +251,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
       <para>
        The data type(s) of the operator's arguments (optionally
        schema-qualified).  Write <literal>NONE</literal> for the missing argument
-       of a prefix or postfix operator.
+       of a prefix operator.
       </para>
      </listitem>
     </varlistentry>
index ac35f229a0ca7dc638943306a249bb34bfa55d02..ad90c137f1491fd1625244441f05a89d66ad8aaa 100644 (file)
@@ -21,13 +21,13 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , { <replaceable>right_type</replaceable> | NONE } )
+ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , <replaceable>right_type</replaceable> )
     OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
 
-ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , { <replaceable>right_type</replaceable> | NONE } )
+ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , <replaceable>right_type</replaceable> )
     SET SCHEMA <replaceable>new_schema</replaceable>
 
-ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , { <replaceable>right_type</replaceable> | NONE } )
+ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , <replaceable>right_type</replaceable> )
     SET ( {  RESTRICT = { <replaceable class="parameter">res_proc</replaceable> | NONE }
            | JOIN = { <replaceable class="parameter">join_proc</replaceable> | NONE }
          } [, ... ] )
@@ -79,8 +79,7 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
     <term><replaceable class="parameter">right_type</replaceable></term>
     <listitem>
      <para>
-      The data type of the operator's right operand; write
-      <literal>NONE</literal> if the operator has no right operand.
+      The data type of the operator's right operand.
      </para>
     </listitem>
    </varlistentry>
index 59d5bf107008b20928cbd4622448a0c7b790a6d7..b3b5d61a852e697a03ad07ae034a258b986f8e69 100644 (file)
@@ -141,7 +141,7 @@ ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="
      <para>
       In an <literal>OPERATOR</literal> clause,
       the operand data type(s) of the operator, or <literal>NONE</literal> to
-      signify a left-unary or right-unary operator.  Unlike the comparable
+      signify a prefix operator.  Unlike the comparable
       syntax in <command>CREATE OPERATOR CLASS</command>, the operand data types
       must always be specified.
      </para>
index fd7492a25567e5379909ad6f917042238fb423b4..6e8ced3eaf1191d49901a58f897a625595ab5a85 100644 (file)
@@ -224,7 +224,7 @@ COMMENT ON
      <para>
       The data type(s) of the operator's arguments (optionally
       schema-qualified).  Write <literal>NONE</literal> for the missing argument
-      of a prefix or postfix operator.
+      of a prefix operator.
      </para>
     </listitem>
    </varlistentry>
index f42fb6494c6b3940aa39aba295c9738803b64573..2d75a1c0b0d6ad7e0baf244670db6dafe291c2a9 100644 (file)
@@ -161,7 +161,7 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
      <para>
       In an <literal>OPERATOR</literal> clause,
       the operand data type(s) of the operator, or <literal>NONE</literal> to
-      signify a left-unary or right-unary operator.  The operand data
+      signify a prefix operator.  The operand data
       types can be omitted in the normal case where they are the same
       as the operator class's data type.
      </para>
index 66c34e0072f0df989661c0a107417a7983d56fe7..9462bc1e8caa60f1b3c4a633838da9d4f75cd6d2 100644 (file)
@@ -86,20 +86,9 @@ CREATE OPERATOR <replaceable>name</replaceable> (
   </para>
 
   <para>
-   At least one of <literal>LEFTARG</literal> and <literal>RIGHTARG</literal> must be defined.  For
-   binary operators, both must be defined. For right unary
-   operators, only <literal>LEFTARG</literal> should be defined, while for left
-   unary operators only <literal>RIGHTARG</literal> should be defined.
-  </para>
-
-  <note>
-   <para>
-    Right unary, also called postfix, operators are deprecated and will be
-    removed in <productname>PostgreSQL</productname> version 14.
-   </para>
-  </note>
-
-  <para>
+   For binary operators, both <literal>LEFTARG</literal> and
+   <literal>RIGHTARG</literal> must be defined.  For prefix operators only
+   <literal>RIGHTARG</literal> should be defined.
    The <replaceable class="parameter">function_name</replaceable>
    function must have been previously defined using <command>CREATE
    FUNCTION</command> and must be defined to accept the correct number
@@ -160,7 +149,7 @@ CREATE OPERATOR <replaceable>name</replaceable> (
       <listitem>
        <para>
         The data type of the operator's left operand, if any.
-        This option would be omitted for a left-unary operator.
+        This option would be omitted for a prefix operator.
        </para>
       </listitem>
      </varlistentry>
@@ -169,8 +158,7 @@ CREATE OPERATOR <replaceable>name</replaceable> (
       <term><replaceable class="parameter">right_type</replaceable></term>
       <listitem>
        <para>
-        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.
        </para>
       </listitem>
      </varlistentry>
index 2dff050ecf22258f71acb54ab60304df60815c53..7bcdd082ae70d05f06a9b19c055ac402d506d63a 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-DROP OPERATOR [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ( { <replaceable class="parameter">left_type</replaceable> | NONE } , { <replaceable class="parameter">right_type</replaceable> | NONE } ) [, ...] [ CASCADE | RESTRICT ]
+DROP OPERATOR [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ( { <replaceable class="parameter">left_type</replaceable> | NONE } , <replaceable class="parameter">right_type</replaceable> ) [, ...] [ CASCADE | RESTRICT ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -73,8 +73,7 @@ DROP OPERATOR [ IF EXISTS ] <replaceable class="parameter">name</replaceable> (
     <term><replaceable class="parameter">right_type</replaceable></term>
     <listitem>
      <para>
-      The data type of the operator's right operand; write
-      <literal>NONE</literal> if the operator has no right operand.
+      The data type of the operator's right operand.
      </para>
     </listitem>
    </varlistentry>
@@ -113,24 +112,17 @@ DROP OPERATOR ^ (integer, integer);
   </para>
 
   <para>
-   Remove the left unary bitwise complement operator
+   Remove the bitwise-complement prefix operator
    <literal>~b</literal> for type <type>bit</type>:
 <programlisting>
 DROP OPERATOR ~ (none, bit);
 </programlisting>
   </para>
 
-  <para>
-   Remove the right unary factorial operator <literal>x!</literal>
-   for type <type>bigint</type>:
-<programlisting>
-DROP OPERATOR ! (bigint, none);
-</programlisting></para>
-
   <para>
    Remove multiple operators in one command:
 <programlisting>
-DROP OPERATOR ~ (none, bit), ! (bigint, none);
+DROP OPERATOR ~ (none, bit), ^ (integer, integer);
 </programlisting></para>
  </refsect1>
 
index b0ae5d2e127e13f3cbdc0337df2b7b43f71d1c59..3fdd87823e0082fab97c2fc6a0e84af1d147de7a 100644 (file)
@@ -836,7 +836,7 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> )
    <para>
     When working with non-SQL-standard operator names, you will usually
     need to separate adjacent operators with spaces to avoid ambiguity.
-    For example, if you have defined a left unary operator named <literal>@</literal>,
+    For example, if you have defined a prefix operator named <literal>@</literal>,
     you cannot write <literal>X*@Y</literal>; you must write
     <literal>X* @Y</literal> to ensure that
     <productname>PostgreSQL</productname> reads it as two operator names
@@ -1444,11 +1444,10 @@ $1.somecolumn
    </indexterm>
 
    <para>
-    There are three possible syntaxes for an operator invocation:
+    There are two possible syntaxes for an operator invocation:
     <simplelist>
      <member><replaceable>expression</replaceable> <replaceable>operator</replaceable> <replaceable>expression</replaceable> (binary infix operator)</member>
      <member><replaceable>operator</replaceable> <replaceable>expression</replaceable> (unary prefix operator)</member>
-     <member><replaceable>expression</replaceable> <replaceable>operator</replaceable> (unary postfix operator)</member>
     </simplelist>
     where the <replaceable>operator</replaceable> token follows the syntax
     rules of <xref linkend="sql-syntax-operators"/>, or is one of the
index 98662fc91fb6d8404201e78233f51f9872308e30..cfeb851a507e1307b318cb69a9b90e60cad7257c 100644 (file)
@@ -97,8 +97,8 @@ Operators
 <listitem>
 <para>
 <productname>PostgreSQL</productname> allows expressions with
-prefix and postfix unary (one-argument) operators,
-as well as binary (two-argument) operators.  Like functions, operators can
+prefix (one-argument) operators,
+as well as infix (two-argument) operators.  Like functions, operators can
 be overloaded, so the same problem of selecting the right operator
 exists.
 </para>
@@ -266,7 +266,7 @@ create objects.  In such situations, cast arguments to force an exact match.
 <para>
 If one argument of a binary operator invocation is of the <type>unknown</type> type,
 then assume it is the same type as the other argument for this check.
-Invocations involving two <type>unknown</type> inputs, or a unary operator
+Invocations involving two <type>unknown</type> inputs, or a prefix operator
 with an <type>unknown</type> input, will never find a match at this step.
 </para>
 </step>
index 56b08491c96c928a6feaf47f2befecb865656808..98f4c5c4aa46ad2041b9865d6588addff5d55345 100644 (file)
@@ -20,8 +20,8 @@
   </para>
 
   <para>
-   <productname>PostgreSQL</productname> supports left unary, right
-   unary, and binary operators.  Operators can be
+   <productname>PostgreSQL</productname> supports prefix
+   and infix operators.  Operators can be
    overloaded;<indexterm><primary>overloading</primary><secondary>operators</secondary></indexterm>
    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;
   </para>
 
   <para>
-   We've shown how to create a binary operator here.  To create unary
-   operators, just omit one of <literal>leftarg</literal> (for left unary) or
-   <literal>rightarg</literal> (for right unary).  The <literal>function</literal>
+   We've shown how to create a binary operator here.  To create a prefix
+   operator, just omit the <literal>leftarg</literal>.
+   The <literal>function</literal>
    clause and the argument clauses are the only required items in
    <command>CREATE OPERATOR</command>.  The <literal>commutator</literal>
    clause shown in the example is an optional hint to the query
@@ -202,7 +202,7 @@ SELECT (a + b) AS c FROM test_complex;
    <para>
     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.
    </para>
 
    <para>
index 0152e3869abe2e676dd4be52e5eb514478d7f163..391a9b225db796ffbe9cc1a48d5d45640313eb5d 100644 (file)
@@ -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 --- the first will be
+ * InvalidOid for a prefix oprkind.  nargs is always 2, too.
  */
 FuncCandidateList
 OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
index f7c07c9b5b89174930945da35e225ee8e54f3558..904cb8ef820c1316c0c7e4cc52fe98000d0d2fba 100644 (file)
@@ -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);
index bf23937849c9fbd37bb5289dccfc6b1b84fbeff6..a791e99092d5bd4a5d985b1a7440012aebe22f90 100644 (file)
@@ -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 argument types must be specified")));
+       if (!OidIsValid(typeId2))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("operator right argument type must be specified"),
+                                errdetail("Postfix operators are not supported.")));
 
        if (typeName1)
        {
index 42476724d88f0bb512fd8c0da4b5917c029391c6..970a2d438402a125dd4556b36c07f7270ba7950c 100644 (file)
@@ -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);
                }
index 4558c02f6a616da00c0ae801ba28c52c2e8d853f..b16ffb9bf7fd1f2a8bab75e2ec5ffa256f161eea 100644 (file)
@@ -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
                                {
index f69976cc8c989b0021dd677e0051e87de172322e..d24420c58319fcf2d7088691a6ee5650c837dc2c 100644 (file)
@@ -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",
index 2749974f6384dee686521d9749b882c57d63ddfa..6613a3a8f8798877896ae78430f9fa9a2efe2f74 100644 (file)
@@ -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)
-       {
-               /* 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 */
+               /* prefix operator */
                args = list_make1(rtree);
                actual_arg_types[0] = rtypeId;
                declared_arg_types[0] = opform->oprright;
index 60dd80c23c87d04f33f21718d4e456fecb456057..15877e37a609144baca74355875c8e8242c8112b 100644 (file)
@@ -9198,35 +9198,14 @@ get_oper_expr(OpExpr *expr, deparse_context *context)
        }
        else
        {
-               /* unary operator --- but which side? */
+               /* prefix operator */
                Node       *arg = (Node *) linitial(args);
-               HeapTuple       tp;
-               Form_pg_operator optup;
-
-               tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
-               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);
-               }
-               ReleaseSysCache(tp);
+
+               appendStringInfo(buf, "%s ",
+                                                generate_operator_name(opno,
+                                                                                               InvalidOid,
+                                                                                               exprType(arg)));
+               get_rule_expr_paren(arg, context, true, (Node *) expr);
        }
        if (!PRETTY_PAREN(context))
                appendStringInfoChar(buf, ')');
@@ -11087,10 +11066,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 */
index 320820165b7d78e734a25f9f204fa3f4452f07e0..dce6af09c9b73ff499a2f2d5bc8641a36c994b65 100644 (file)
@@ -12650,6 +12650,11 @@ dumpOpr(Archive *fout, OprInfo *oprinfo)
        oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
        oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
 
+       /* In PG14 upwards postfix operator support does not exist anymore. */
+       if (strcmp(oprkind, "r") == 0)
+               pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
+                                          oprcode);
+
        oprregproc = convertRegProcReference(fout, oprcode);
        if (oprregproc)
        {
@@ -12662,7 +12667,8 @@ dumpOpr(Archive *fout, OprInfo *oprinfo)
 
        /*
         * right unary means there's a left arg and left unary means there's a
-        * right arg
+        * right arg.  (Although the "r" case is dead code for PG14 and later,
+        * continue to support it in case we're dumping from an old server.)
         */
        if (strcmp(oprkind, "r") == 0 ||
                strcmp(oprkind, "b") == 0)
index 00aef855dc0761baf9454a103dc7d2747c367c27..2f7aa632c52b5a049b15a3b7f5b548beaed231e0 100644 (file)
@@ -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 query because, if that
+                * #define is ever changed, the cutoff we want to use is the value
+                * used by pre-version 14 servers, not that of some future version.
+                */
+               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, NONE)\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 are 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.
index f22d907b1f9f02f4eb059b2b6b173c70df9be3b2..58de433fd302f64ff573b645ad1312c3f29c5196 100644 (file)
@@ -799,6 +799,10 @@ describeOperators(const char *pattern, bool verbose, bool showSystem)
         * anyway, for now, because (1) third-party modules may still be following
         * the old convention, and (2) we'd need to do it anyway when talking to a
         * pre-9.1 server.
+        *
+        * The support for postfix operators in this query is dead code as of
+        * Postgres 14, but we need to keep it for as long as we support talking
+        * to pre-v14 servers.
         */
 
        printfPQExpBuffer(&buf,
index 358935997087b32173c1455394ecd71dd052903b..365552635b84128000c2e13d38503c2d240143a7 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202009171
+#define CATALOG_VERSION_NO     202009172
 
 #endif
index 1daa2638520f685bad06ca474fb72f07ff58a060..62a7dbf23f69ba42d9c5f17ffac877f3d61b1c6c 100644 (file)
@@ -41,7 +41,7 @@ CATALOG(pg_operator,2617,OperatorRelationId)
        /* operator owner */
        Oid                     oprowner BKI_DEFAULT(PGUID);
 
-       /* 'l', 'r', or 'b' */
+       /* 'l' for prefix or 'b' for infix */
        char            oprkind BKI_DEFAULT(b);
 
        /* can be used in merge join? */
@@ -50,10 +50,10 @@ CATALOG(pg_operator,2617,OperatorRelationId)
        /* can be used in hash join? */
        bool            oprcanhash BKI_DEFAULT(f);
 
-       /* left arg type, or 0 if 'l' oprkind */
+       /* left arg type, or 0 if prefix operator */
        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 */
index bcd861e43ac34cfd79c98fb9a6c7329c9ad7628b..09695a2765cff62400492ac5ee1a47696e178bd6 100644 (file)
@@ -31,8 +31,6 @@ extern Oid    LookupOperWithArgs(ObjectWithArgs *oper, bool noError);
 /* NB: the selected operator may require coercion of the input types! */
 extern Operator oper(ParseState *pstate, List *op, Oid arg1, Oid arg2,
                                         bool noError, int location);
-extern Operator right_oper(ParseState *pstate, List *op, Oid arg,
-                                                  bool noError, int location);
 extern Operator left_oper(ParseState *pstate, List *op, Oid arg,
                                                  bool noError, int location);
 
index 9e4d4e93fb79e464ecae4df90f43cfdaf2fd40ea..530327759140c56d68a28c959d0ae6ba04b4c08e 100644 (file)
@@ -15,17 +15,15 @@ CREATE OPERATOR <% (
    negator = >=%
 );
 CREATE OPERATOR @#@ (
-   rightarg = int8,            -- left unary
-   procedure = factorial
-);
-CREATE OPERATOR #@# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,            -- prefix
    procedure = factorial
 );
 CREATE OPERATOR #%# (
-   leftarg = int8,             -- right unary
+   leftarg = int8,             -- fail, postfix is no longer supported
    procedure = factorial
 );
+ERROR:  operator right argument type must be specified
+DETAIL:  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;
@@ -35,11 +33,22 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
 (1 row)
 
 -- Test comments
-COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary';
-ERROR:  operator does not exist: integer ######
--- => is disallowed now
+COMMENT ON OPERATOR ###### (NONE, int4) IS 'bad prefix';
+ERROR:  operator does not exist: ###### integer
+COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad postfix';
+ERROR:  postfix operators are not supported
+COMMENT ON OPERATOR ###### (int4, int8) IS 'bad infix';
+ERROR:  operator does not exist: integer ###### bigint
+-- Check that DROP on a nonexistent op behaves sanely, too
+DROP OPERATOR ###### (NONE, int4);
+ERROR:  operator does not exist: ###### integer
+DROP OPERATOR ###### (int4, NONE);
+ERROR:  postfix operators are not supported
+DROP OPERATOR ###### (int4, int8);
+ERROR:  operator does not exist: integer ###### bigint
+-- => is disallowed as an operator name now
 CREATE OPERATOR => (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
 ERROR:  syntax error at or near "=>"
@@ -49,15 +58,20 @@ LINE 1: CREATE OPERATOR => (
 -- (=> is tested elsewhere)
 -- this is legal because ! is not allowed in sql ops
 CREATE OPERATOR !=- (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
-SELECT 2 !=-;
+SELECT !=- 10;
  ?column? 
 ----------
-        2
+  3628800
 (1 row)
 
+-- postfix operators don't work anymore
+SELECT 10 !=-;
+ERROR:  syntax error at or near ";"
+LINE 1: SELECT 10 !=-;
+                     ^
 -- make sure lexer returns != as <> even in edge cases
 SELECT 2 !=/**/ 1, 2 !=/**/ 2;
  ?column? | ?column? 
@@ -127,7 +141,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC;
 REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1;
 SET ROLE regress_rol_op1;
 CREATE OPERATOR schema_op1.#*# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
 ERROR:  permission denied for schema schema_op1
@@ -167,19 +181,19 @@ CREATE OPERATOR === (
 ROLLBACK;
 -- Should fail. Invalid attribute
 CREATE OPERATOR #@%# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial,
    invalid_att = int8
 );
 WARNING:  operator attribute "invalid_att" not recognized
--- Should fail. At least leftarg or rightarg should be mandatorily specified
+-- Should fail. At least rightarg should be mandatorily specified
 CREATE OPERATOR #@%# (
    procedure = factorial
 );
-ERROR:  at least one of leftarg or rightarg must be specified
+ERROR:  operator argument types must be specified
 -- Should fail. Procedure should be mandatorily specified
 CREATE OPERATOR #@%# (
-   leftarg = int8
+   rightarg = int8
 );
 ERROR:  operator function must be specified
 -- Should fail. CREATE OPERATOR requires USAGE on TYPE
index 1b3c146e4cc9556b713247a2d1ab02daaf2fbec8..7825a765cd7bf7461b91d3b422983a01ff0f1ff3 100644 (file)
@@ -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
index c32da8c066a77d8cc2212aeafcfca1c7712b0d6a..4ff2c0ff21678867126157afa6e73c8124862e1c 100644 (file)
@@ -18,17 +18,12 @@ CREATE OPERATOR <% (
 );
 
 CREATE OPERATOR @#@ (
-   rightarg = int8,            -- left unary
-   procedure = factorial
-);
-
-CREATE OPERATOR #@# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,            -- prefix
    procedure = factorial
 );
 
 CREATE OPERATOR #%# (
-   leftarg = int8,             -- right unary
+   leftarg = int8,             -- fail, postfix is no longer supported
    procedure = factorial
 );
 
@@ -37,11 +32,18 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
        point '(1,2)' <% widget '(0,0,1)' AS f;
 
 -- Test comments
-COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary';
+COMMENT ON OPERATOR ###### (NONE, int4) IS 'bad prefix';
+COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad postfix';
+COMMENT ON OPERATOR ###### (int4, int8) IS 'bad infix';
+
+-- Check that DROP on a nonexistent op behaves sanely, too
+DROP OPERATOR ###### (NONE, int4);
+DROP OPERATOR ###### (int4, NONE);
+DROP OPERATOR ###### (int4, int8);
 
--- => is disallowed now
+-- => is disallowed as an operator name now
 CREATE OPERATOR => (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
 
@@ -50,10 +52,12 @@ CREATE OPERATOR => (
 
 -- this is legal because ! is not allowed in sql ops
 CREATE OPERATOR !=- (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
-SELECT 2 !=-;
+SELECT !=- 10;
+-- postfix operators don't work anymore
+SELECT 10 !=-;
 -- make sure lexer returns != as <> even in edge cases
 SELECT 2 !=/**/ 1, 2 !=/**/ 2;
 SELECT 2 !=-- comment to be removed by psql
@@ -84,7 +88,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC;
 REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1;
 SET ROLE regress_rol_op1;
 CREATE OPERATOR schema_op1.#*# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial
 );
 ROLLBACK;
@@ -128,19 +132,19 @@ ROLLBACK;
 
 -- Should fail. Invalid attribute
 CREATE OPERATOR #@%# (
-   leftarg = int8,             -- right unary
+   rightarg = int8,
    procedure = factorial,
    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 = factorial
 );
 
 -- Should fail. Procedure should be mandatorily specified
 CREATE OPERATOR #@%# (
-   leftarg = int8
+   rightarg = int8
 );
 
 -- Should fail. CREATE OPERATOR requires USAGE on TYPE
index 7a9180b081524da30b4598eb0fd87fb59e1f1b62..307aab1deb766fb16a9e6ec38e5b476e55e770be 100644 (file)
@@ -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.
 
index 0355926701623ab64ce933fd2dab29fc25428b99..d849ec0d4b707f093f1c11bcabdcb8b199ad7b26 100644 (file)
@@ -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 prefix operator by omitting the leftarg.
 CREATE OPERATOR + (
    leftarg = complex,
    rightarg = complex,
index 3a1767f97be74d919ce0e771b3b126165faa01cf..8a04d6a961f2880b553fe75870994a9edbb3744b 100644 (file)
@@ -96,36 +96,22 @@ SELECT n.nspname, r.rolname, format_type(t.oid, null) as typname
 
 
 --
--- lists all left unary operators
+-- lists all prefix operators
 --
-SELECT n.nspname, o.oprname AS left_unary,
+SELECT n.nspname, o.oprname AS prefix_op,
        format_type(right_type.oid, null) AS operand,
        format_type(result.oid, null) AS return_type
   FROM pg_namespace n, pg_operator o,
        pg_type right_type, pg_type result
   WHERE o.oprnamespace = n.oid
-    and o.oprkind = 'l'           -- left unary
+    and o.oprkind = 'l'           -- prefix ("left unary")
     and o.oprright = right_type.oid
     and o.oprresult = result.oid
   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
+-- lists all infix operators
 --
 SELECT n.nspname, o.oprname AS binary_op,
        format_type(left_type.oid, null) AS left_opr,
@@ -134,7 +120,7 @@ SELECT n.nspname, o.oprname AS binary_op,
   FROM pg_namespace n, pg_operator o, pg_type left_type,
        pg_type right_type, pg_type result
   WHERE o.oprnamespace = n.oid
-    and o.oprkind = 'b'         -- binary
+    and o.oprkind = 'b'         -- infix ("binary")
     and o.oprleft = left_type.oid
     and o.oprright = right_type.oid
     and o.oprresult = result.oid