Remove support for postfix (right-unary) operators.
authorTom Lane <[email protected]>
Thu, 17 Sep 2020 23:38:05 +0000 (19:38 -0400)
committerTom Lane <[email protected]>
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