Commit 1ed6b895 authored by Tom Lane's avatar Tom Lane

Remove support for postfix (right-unary) operators.

This feature has been a thorn in our sides for a long time, causing
many grammatical ambiguity problems.  It doesn't seem worth the
pain to continue to support it, so remove it.

There are some follow-on improvements we can make in the grammar,
but this commit only removes the bare minimum number of productions,
plus assorted backend support code.

Note that pg_dump and psql continue to have full support, since
they may be used against older servers.  However, pg_dump warns
about postfix operators.  There is also a check in pg_upgrade.

Documentation-wise, I (tgl) largely removed the "left unary"
terminology in favor of saying "prefix operator", which is
a more standard and IMO less confusing term.

I included a catversion bump, although no initial catalog data
changes here, to mark the boundary at which oprkind = 'r'
stopped being valid in pg_operator.

Mark Dilger, based on work by myself and Robert Haas;
review by John Naylor

Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
parent 76f412ab
...@@ -2706,7 +2706,6 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context) ...@@ -2706,7 +2706,6 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
HeapTuple tuple; HeapTuple tuple;
Form_pg_operator form; Form_pg_operator form;
char oprkind; char oprkind;
ListCell *arg;
/* Retrieve information about the operator from system catalog. */ /* Retrieve information about the operator from system catalog. */
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno)); tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
...@@ -2716,18 +2715,16 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context) ...@@ -2716,18 +2715,16 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
oprkind = form->oprkind; oprkind = form->oprkind;
/* Sanity check. */ /* Sanity check. */
Assert((oprkind == 'r' && list_length(node->args) == 1) || Assert((oprkind == 'l' && list_length(node->args) == 1) ||
(oprkind == 'l' && list_length(node->args) == 1) ||
(oprkind == 'b' && list_length(node->args) == 2)); (oprkind == 'b' && list_length(node->args) == 2));
/* Always parenthesize the expression. */ /* Always parenthesize the expression. */
appendStringInfoChar(buf, '('); appendStringInfoChar(buf, '(');
/* Deparse left operand. */ /* Deparse left operand, if any. */
if (oprkind == 'r' || oprkind == 'b') if (oprkind == 'b')
{ {
arg = list_head(node->args); deparseExpr(linitial(node->args), context);
deparseExpr(lfirst(arg), context);
appendStringInfoChar(buf, ' '); appendStringInfoChar(buf, ' ');
} }
...@@ -2735,12 +2732,8 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context) ...@@ -2735,12 +2732,8 @@ deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
deparseOperatorName(buf, form); deparseOperatorName(buf, form);
/* Deparse right operand. */ /* Deparse right operand. */
if (oprkind == 'l' || oprkind == 'b')
{
arg = list_tail(node->args);
appendStringInfoChar(buf, ' '); appendStringInfoChar(buf, ' ');
deparseExpr(lfirst(arg), context); deparseExpr(llast(node->args), context);
}
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
......
...@@ -5159,8 +5159,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -5159,8 +5159,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<structfield>oprkind</structfield> <type>char</type> <structfield>oprkind</structfield> <type>char</type>
</para> </para>
<para> <para>
<literal>b</literal> = infix (<quote>both</quote>), <literal>l</literal> = prefix <literal>b</literal> = infix operator (<quote>both</quote>),
(<quote>left</quote>), <literal>r</literal> = postfix (<quote>right</quote>) or <literal>l</literal> = prefix operator (<quote>left</quote>)
</para></entry> </para></entry>
</row> </row>
...@@ -5188,7 +5188,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -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>) (references <link linkend="catalog-pg-type"><structname>pg_type</structname></link>.<structfield>oid</structfield>)
</para> </para>
<para> <para>
Type of the left operand Type of the left operand (0 if none)
</para></entry> </para></entry>
</row> </row>
...@@ -5266,7 +5266,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -5266,7 +5266,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</table> </table>
<para> <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. is zero for a prefix operator.
</para> </para>
......
...@@ -251,7 +251,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea ...@@ -251,7 +251,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
<para> <para>
The data type(s) of the operator's arguments (optionally The data type(s) of the operator's arguments (optionally
schema-qualified). Write <literal>NONE</literal> for the missing argument schema-qualified). Write <literal>NONE</literal> for the missing argument
of a prefix or postfix operator. of a prefix operator.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -21,13 +21,13 @@ PostgreSQL documentation ...@@ -21,13 +21,13 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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 } 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> 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 } SET ( { RESTRICT = { <replaceable class="parameter">res_proc</replaceable> | NONE }
| JOIN = { <replaceable class="parameter">join_proc</replaceable> | NONE } | JOIN = { <replaceable class="parameter">join_proc</replaceable> | NONE }
} [, ... ] ) } [, ... ] )
...@@ -79,8 +79,7 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla ...@@ -79,8 +79,7 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
<term><replaceable class="parameter">right_type</replaceable></term> <term><replaceable class="parameter">right_type</replaceable></term>
<listitem> <listitem>
<para> <para>
The data type of the operator's right operand; write The data type of the operator's right operand.
<literal>NONE</literal> if the operator has no right operand.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -141,7 +141,7 @@ ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class=" ...@@ -141,7 +141,7 @@ ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="
<para> <para>
In an <literal>OPERATOR</literal> clause, In an <literal>OPERATOR</literal> clause,
the operand data type(s) of the operator, or <literal>NONE</literal> to 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 syntax in <command>CREATE OPERATOR CLASS</command>, the operand data types
must always be specified. must always be specified.
</para> </para>
......
...@@ -224,7 +224,7 @@ COMMENT ON ...@@ -224,7 +224,7 @@ COMMENT ON
<para> <para>
The data type(s) of the operator's arguments (optionally The data type(s) of the operator's arguments (optionally
schema-qualified). Write <literal>NONE</literal> for the missing argument schema-qualified). Write <literal>NONE</literal> for the missing argument
of a prefix or postfix operator. of a prefix operator.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -161,7 +161,7 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL ...@@ -161,7 +161,7 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
<para> <para>
In an <literal>OPERATOR</literal> clause, In an <literal>OPERATOR</literal> clause,
the operand data type(s) of the operator, or <literal>NONE</literal> to 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 types can be omitted in the normal case where they are the same
as the operator class's data type. as the operator class's data type.
</para> </para>
......
...@@ -86,20 +86,9 @@ CREATE OPERATOR <replaceable>name</replaceable> ( ...@@ -86,20 +86,9 @@ CREATE OPERATOR <replaceable>name</replaceable> (
</para> </para>
<para> <para>
At least one of <literal>LEFTARG</literal> and <literal>RIGHTARG</literal> must be defined. For For binary operators, both <literal>LEFTARG</literal> and
binary operators, both must be defined. For right unary <literal>RIGHTARG</literal> must be defined. For prefix operators only
operators, only <literal>LEFTARG</literal> should be defined, while for left <literal>RIGHTARG</literal> should be defined.
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>
The <replaceable class="parameter">function_name</replaceable> The <replaceable class="parameter">function_name</replaceable>
function must have been previously defined using <command>CREATE function must have been previously defined using <command>CREATE
FUNCTION</command> and must be defined to accept the correct number FUNCTION</command> and must be defined to accept the correct number
...@@ -160,7 +149,7 @@ CREATE OPERATOR <replaceable>name</replaceable> ( ...@@ -160,7 +149,7 @@ CREATE OPERATOR <replaceable>name</replaceable> (
<listitem> <listitem>
<para> <para>
The data type of the operator's left operand, if any. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -169,8 +158,7 @@ CREATE OPERATOR <replaceable>name</replaceable> ( ...@@ -169,8 +158,7 @@ CREATE OPERATOR <replaceable>name</replaceable> (
<term><replaceable class="parameter">right_type</replaceable></term> <term><replaceable class="parameter">right_type</replaceable></term>
<listitem> <listitem>
<para> <para>
The data type of the operator's right operand, if any. The data type of the operator's right operand.
This option would be omitted for a right-unary operator.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -73,8 +73,7 @@ DROP OPERATOR [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ( ...@@ -73,8 +73,7 @@ DROP OPERATOR [ IF EXISTS ] <replaceable class="parameter">name</replaceable> (
<term><replaceable class="parameter">right_type</replaceable></term> <term><replaceable class="parameter">right_type</replaceable></term>
<listitem> <listitem>
<para> <para>
The data type of the operator's right operand; write The data type of the operator's right operand.
<literal>NONE</literal> if the operator has no right operand.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -113,24 +112,17 @@ DROP OPERATOR ^ (integer, integer); ...@@ -113,24 +112,17 @@ DROP OPERATOR ^ (integer, integer);
</para> </para>
<para> <para>
Remove the left unary bitwise complement operator Remove the bitwise-complement prefix operator
<literal>~b</literal> for type <type>bit</type>: <literal>~b</literal> for type <type>bit</type>:
<programlisting> <programlisting>
DROP OPERATOR ~ (none, bit); DROP OPERATOR ~ (none, bit);
</programlisting> </programlisting>
</para> </para>
<para>
Remove the right unary factorial operator <literal>x!</literal>
for type <type>bigint</type>:
<programlisting>
DROP OPERATOR ! (bigint, none);
</programlisting></para>
<para> <para>
Remove multiple operators in one command: Remove multiple operators in one command:
<programlisting> <programlisting>
DROP OPERATOR ~ (none, bit), ! (bigint, none); DROP OPERATOR ~ (none, bit), ^ (integer, integer);
</programlisting></para> </programlisting></para>
</refsect1> </refsect1>
......
...@@ -836,7 +836,7 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> ) ...@@ -836,7 +836,7 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> )
<para> <para>
When working with non-SQL-standard operator names, you will usually When working with non-SQL-standard operator names, you will usually
need to separate adjacent operators with spaces to avoid ambiguity. 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 you cannot write <literal>X*@Y</literal>; you must write
<literal>X* @Y</literal> to ensure that <literal>X* @Y</literal> to ensure that
<productname>PostgreSQL</productname> reads it as two operator names <productname>PostgreSQL</productname> reads it as two operator names
...@@ -1444,11 +1444,10 @@ $1.somecolumn ...@@ -1444,11 +1444,10 @@ $1.somecolumn
</indexterm> </indexterm>
<para> <para>
There are three possible syntaxes for an operator invocation: There are two possible syntaxes for an operator invocation:
<simplelist> <simplelist>
<member><replaceable>expression</replaceable> <replaceable>operator</replaceable> <replaceable>expression</replaceable> (binary infix operator)</member> <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>operator</replaceable> <replaceable>expression</replaceable> (unary prefix operator)</member>
<member><replaceable>expression</replaceable> <replaceable>operator</replaceable> (unary postfix operator)</member>
</simplelist> </simplelist>
where the <replaceable>operator</replaceable> token follows the syntax where the <replaceable>operator</replaceable> token follows the syntax
rules of <xref linkend="sql-syntax-operators"/>, or is one of the rules of <xref linkend="sql-syntax-operators"/>, or is one of the
......
...@@ -97,8 +97,8 @@ Operators ...@@ -97,8 +97,8 @@ Operators
<listitem> <listitem>
<para> <para>
<productname>PostgreSQL</productname> allows expressions with <productname>PostgreSQL</productname> allows expressions with
prefix and postfix unary (one-argument) operators, prefix (one-argument) operators,
as well as binary (two-argument) operators. Like functions, operators can as well as infix (two-argument) operators. Like functions, operators can
be overloaded, so the same problem of selecting the right operator be overloaded, so the same problem of selecting the right operator
exists. exists.
</para> </para>
...@@ -266,7 +266,7 @@ create objects. In such situations, cast arguments to force an exact match. ...@@ -266,7 +266,7 @@ create objects. In such situations, cast arguments to force an exact match.
<para> <para>
If one argument of a binary operator invocation is of the <type>unknown</type> type, 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. 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. with an <type>unknown</type> input, will never find a match at this step.
</para> </para>
</step> </step>
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
</para> </para>
<para> <para>
<productname>PostgreSQL</productname> supports left unary, right <productname>PostgreSQL</productname> supports prefix
unary, and binary operators. Operators can be and infix operators. Operators can be
overloaded;<indexterm><primary>overloading</primary><secondary>operators</secondary></indexterm> overloaded;<indexterm><primary>overloading</primary><secondary>operators</secondary></indexterm>
that is, the same operator name can be used for different operators that is, the same operator name can be used for different operators
that have different numbers and types of operands. When a query is that have different numbers and types of operands. When a query is
...@@ -64,9 +64,9 @@ SELECT (a + b) AS c FROM test_complex; ...@@ -64,9 +64,9 @@ SELECT (a + b) AS c FROM test_complex;
</para> </para>
<para> <para>
We've shown how to create a binary operator here. To create unary We've shown how to create a binary operator here. To create a prefix
operators, just omit one of <literal>leftarg</literal> (for left unary) or operator, just omit the <literal>leftarg</literal>.
<literal>rightarg</literal> (for right unary). The <literal>function</literal> The <literal>function</literal>
clause and the argument clauses are the only required items in clause and the argument clauses are the only required items in
<command>CREATE OPERATOR</command>. The <literal>commutator</literal> <command>CREATE OPERATOR</command>. The <literal>commutator</literal>
clause shown in the example is an optional hint to the query clause shown in the example is an optional hint to the query
...@@ -202,7 +202,7 @@ SELECT (a + b) AS c FROM test_complex; ...@@ -202,7 +202,7 @@ SELECT (a + b) AS c FROM test_complex;
<para> <para>
Unlike commutators, a pair of unary operators could validly be marked 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) 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>
<para> <para>
......
...@@ -1473,8 +1473,7 @@ FunctionIsVisible(Oid funcid) ...@@ -1473,8 +1473,7 @@ FunctionIsVisible(Oid funcid)
* Given a possibly-qualified operator name and exact input datatypes, * Given a possibly-qualified operator name and exact input datatypes,
* look up the operator. Returns InvalidOid if not found. * look up the operator. Returns InvalidOid if not found.
* *
* Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for * Pass oprleft = InvalidOid for a prefix op.
* a postfix op.
* *
* If the operator name is not schema-qualified, it is sought in the current * 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 * namespace search path. If the name is schema-qualified and the given
...@@ -1580,8 +1579,8 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright) ...@@ -1580,8 +1579,8 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
* namespace case, we arrange for entries in earlier namespaces to mask * namespace case, we arrange for entries in earlier namespaces to mask
* identical entries in later namespaces. * identical entries in later namespaces.
* *
* The returned items always have two args[] entries --- one or the other * The returned items always have two args[] entries --- the first will be
* will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too. * InvalidOid for a prefix oprkind. nargs is always 2, too.
*/ */
FuncCandidateList FuncCandidateList
OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok) OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
......
...@@ -245,7 +245,7 @@ OperatorShellMake(const char *operatorName, ...@@ -245,7 +245,7 @@ OperatorShellMake(const char *operatorName,
values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); 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_oprcanmerge - 1] = BoolGetDatum(false);
values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
...@@ -494,7 +494,7 @@ OperatorCreate(const char *operatorName, ...@@ -494,7 +494,7 @@ OperatorCreate(const char *operatorName,
values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); 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_oprcanmerge - 1] = BoolGetDatum(canMerge);
values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
......
...@@ -168,10 +168,22 @@ DefineOperator(List *names, List *parameters) ...@@ -168,10 +168,22 @@ DefineOperator(List *names, List *parameters)
if (typeName2) if (typeName2)
typeId2 = typenameTypeId(NULL, 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)) if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (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) if (typeName1)
{ {
......
...@@ -394,7 +394,6 @@ print_expr(const Node *expr, const List *rtable) ...@@ -394,7 +394,6 @@ print_expr(const Node *expr, const List *rtable)
} }
else else
{ {
/* we print prefix and postfix ops the same... */
printf("%s ", ((opname != NULL) ? opname : "(invalid operator)")); printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
print_expr(get_leftop((const Expr *) e), rtable); print_expr(get_leftop((const Expr *) e), rtable);
} }
......
...@@ -741,10 +741,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -741,10 +741,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS %nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
%nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA %nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA
%nonassoc ESCAPE /* ESCAPE must be just above LIKE/ILIKE/SIMILAR */ %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 * 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 * various unreserved keywords as needed to resolve ambiguities (this can't
* have any bad effects since obviously the keywords will still behave the * have any bad effects since obviously the keywords will still behave the
* same as if they weren't keywords). We need to do this: * same as if they weren't keywords). We need to do this:
...@@ -12993,8 +12992,6 @@ a_expr: c_expr { $$ = $1; } ...@@ -12993,8 +12992,6 @@ a_expr: c_expr { $$ = $1; }
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
| qual_Op a_expr %prec Op | qual_Op a_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); } { $$ = (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 | a_expr AND a_expr
{ $$ = makeAndExpr($1, $3, @2); } { $$ = makeAndExpr($1, $3, @2); }
...@@ -13408,8 +13405,6 @@ b_expr: c_expr ...@@ -13408,8 +13405,6 @@ b_expr: c_expr
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
| qual_Op b_expr %prec Op | qual_Op b_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); } { $$ = (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 | b_expr IS DISTINCT FROM b_expr %prec IS
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2);
...@@ -14665,11 +14660,7 @@ target_el: a_expr AS ColLabel ...@@ -14665,11 +14660,7 @@ target_el: a_expr AS ColLabel
} }
/* /*
* We support omitting AS only for column labels that aren't * We support omitting AS only for column labels that aren't
* any known keyword. There is an ambiguity against postfix * any known keyword.
* 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.
*/ */
| a_expr IDENT | a_expr IDENT
{ {
......
...@@ -57,7 +57,7 @@ bool Transform_null_equals = false; ...@@ -57,7 +57,7 @@ bool Transform_null_equals = false;
#define PREC_GROUP_NOT_LIKE 9 /* NOT LIKE/ILIKE/SIMILAR */ #define PREC_GROUP_NOT_LIKE 9 /* NOT LIKE/ILIKE/SIMILAR */
#define PREC_GROUP_NOT_BETWEEN 10 /* NOT BETWEEN */ #define PREC_GROUP_NOT_BETWEEN 10 /* NOT BETWEEN */
#define PREC_GROUP_NOT_IN 11 /* NOT IN */ #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_INFIX_OP 13 /* generic infix operators */
#define PREC_GROUP_PREFIX_OP 14 /* generic prefix operators */ #define PREC_GROUP_PREFIX_OP 14 /* generic prefix operators */
...@@ -71,7 +71,7 @@ bool Transform_null_equals = false; ...@@ -71,7 +71,7 @@ bool Transform_null_equals = false;
* 4. LIKE ILIKE SIMILAR * 4. LIKE ILIKE SIMILAR
* 5. BETWEEN * 5. BETWEEN
* 6. IN * 6. IN
* 7. generic postfix Op * 7. ANY ALL
* 8. generic Op, including <= => <> * 8. generic Op, including <= => <>
* 9. generic prefix Op * 9. generic prefix Op
* 10. IS tests (NullTest, BooleanTest, etc) * 10. IS tests (NullTest, BooleanTest, etc)
...@@ -1031,7 +1031,7 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a) ...@@ -1031,7 +1031,7 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a)
Node *rexpr = a->rexpr; Node *rexpr = a->rexpr;
if (operator_precedence_warning) if (operator_precedence_warning)
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP, emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
strVal(llast(a->name)), strVal(llast(a->name)),
lexpr, NULL, lexpr, NULL,
a->location); a->location);
...@@ -1054,7 +1054,7 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a) ...@@ -1054,7 +1054,7 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a)
Node *rexpr = a->rexpr; Node *rexpr = a->rexpr;
if (operator_precedence_warning) if (operator_precedence_warning)
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP, emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
strVal(llast(a->name)), strVal(llast(a->name)),
lexpr, NULL, lexpr, NULL,
a->location); a->location);
...@@ -2019,7 +2019,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) ...@@ -2019,7 +2019,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
sublink->testexpr, NULL, sublink->testexpr, NULL,
sublink->location); sublink->location);
else else
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_OP, emit_precedence_warnings(pstate, PREC_GROUP_ANY_ALL,
strVal(llast(sublink->operName)), strVal(llast(sublink->operName)),
sublink->testexpr, NULL, sublink->testexpr, NULL,
sublink->location); sublink->location);
...@@ -3244,28 +3244,11 @@ operator_precedence_group(Node *node, const char **nodename) ...@@ -3244,28 +3244,11 @@ operator_precedence_group(Node *node, const char **nodename)
group = PREC_GROUP_PREFIX_OP; 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 || else if (aexpr->kind == AEXPR_OP_ANY ||
aexpr->kind == AEXPR_OP_ALL) aexpr->kind == AEXPR_OP_ALL)
{ {
*nodename = strVal(llast(aexpr->name)); *nodename = strVal(llast(aexpr->name));
group = PREC_GROUP_POSTFIX_OP; group = PREC_GROUP_ANY_ALL;
} }
else if (aexpr->kind == AEXPR_DISTINCT || else if (aexpr->kind == AEXPR_DISTINCT ||
aexpr->kind == AEXPR_NOT_DISTINCT) aexpr->kind == AEXPR_NOT_DISTINCT)
...@@ -3356,7 +3339,7 @@ operator_precedence_group(Node *node, const char **nodename) ...@@ -3356,7 +3339,7 @@ operator_precedence_group(Node *node, const char **nodename)
else else
{ {
*nodename = strVal(llast(s->operName)); *nodename = strVal(llast(s->operName));
group = PREC_GROUP_POSTFIX_OP; group = PREC_GROUP_ANY_ALL;
} }
} }
} }
...@@ -3432,9 +3415,8 @@ emit_precedence_warnings(ParseState *pstate, ...@@ -3432,9 +3415,8 @@ emit_precedence_warnings(ParseState *pstate,
* Complain if left child, which should be same or higher precedence * Complain if left child, which should be same or higher precedence
* according to current rules, used to be lower precedence. * according to current rules, used to be lower precedence.
* *
* Exception to precedence rules: if left child is IN or NOT IN or a * Exception to precedence rules: if left child is IN or NOT IN the
* postfix operator, the grouping is syntactically forced regardless of * grouping is syntactically forced regardless of precedence.
* precedence.
*/ */
cgroup = operator_precedence_group(lchild, &copname); cgroup = operator_precedence_group(lchild, &copname);
if (cgroup > 0) if (cgroup > 0)
...@@ -3442,7 +3424,7 @@ emit_precedence_warnings(ParseState *pstate, ...@@ -3442,7 +3424,7 @@ emit_precedence_warnings(ParseState *pstate,
if (oldprecedence_l[cgroup] < oldprecedence_r[opgroup] && if (oldprecedence_l[cgroup] < oldprecedence_r[opgroup] &&
cgroup != PREC_GROUP_IN && cgroup != PREC_GROUP_IN &&
cgroup != PREC_GROUP_NOT_IN && cgroup != PREC_GROUP_NOT_IN &&
cgroup != PREC_GROUP_POSTFIX_OP && cgroup != PREC_GROUP_ANY_ALL &&
cgroup != PREC_GROUP_POSTFIX_IS) cgroup != PREC_GROUP_POSTFIX_IS)
ereport(WARNING, ereport(WARNING,
(errmsg("operator precedence change: %s is now lower precedence than %s", (errmsg("operator precedence change: %s is now lower precedence than %s",
......
...@@ -52,7 +52,7 @@ typedef struct OprCacheKey ...@@ -52,7 +52,7 @@ typedef struct OprCacheKey
{ {
char oprname[NAMEDATALEN]; char oprname[NAMEDATALEN];
Oid left_arg; /* Left input OID, or 0 if prefix op */ 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]; Oid search_path[MAX_CACHED_PATH_LEN];
} OprCacheKey; } OprCacheKey;
...@@ -88,8 +88,7 @@ static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) ...@@ -88,8 +88,7 @@ static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
* Given a possibly-qualified operator name and exact input datatypes, * Given a possibly-qualified operator name and exact input datatypes,
* look up the operator. * look up the operator.
* *
* Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for * Pass oprleft = InvalidOid for a prefix op.
* a postfix op.
* *
* If the operator name is not schema-qualified, it is sought in the current * If the operator name is not schema-qualified, it is sought in the current
* namespace search path. * namespace search path.
...@@ -115,10 +114,16 @@ LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, ...@@ -115,10 +114,16 @@ LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright,
if (!OidIsValid(oprleft)) if (!OidIsValid(oprleft))
oprkind = 'l'; oprkind = 'l';
else if (!OidIsValid(oprright)) else if (OidIsValid(oprright))
oprkind = 'r';
else
oprkind = 'b'; 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, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
...@@ -507,85 +512,6 @@ compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError) ...@@ -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) /* left_oper() -- search for a unary left operator (prefix operator)
* Given operator name and type of arg, return oper struct. * Given operator name and type of arg, return oper struct.
* *
...@@ -696,7 +622,6 @@ op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2) ...@@ -696,7 +622,6 @@ op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2)
appendStringInfoString(&argbuf, NameListToString(op)); 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 */ return argbuf.data; /* return palloc'd string buffer */
...@@ -758,17 +683,16 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, ...@@ -758,17 +683,16 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
Oid rettype; Oid rettype;
OpExpr *result; OpExpr *result;
/* Select the operator */ /* Check it's not a postfix operator */
if (rtree == NULL) if (rtree == NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("postfix operators are not supported")));
/* Select the operator */
if (ltree == NULL)
{ {
/* right operator */ /* prefix operator */
ltypeId = exprType(ltree);
rtypeId = InvalidOid;
tup = right_oper(pstate, opname, ltypeId, false, location);
}
else if (ltree == NULL)
{
/* left operator */
rtypeId = exprType(rtree); rtypeId = exprType(rtree);
ltypeId = InvalidOid; ltypeId = InvalidOid;
tup = left_oper(pstate, opname, rtypeId, false, location); tup = left_oper(pstate, opname, rtypeId, false, location);
...@@ -795,17 +719,9 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, ...@@ -795,17 +719,9 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
parser_errposition(pstate, location))); parser_errposition(pstate, location)));
/* Do typecasting and build the expression tree */ /* Do typecasting and build the expression tree */
if (rtree == NULL) if (ltree == NULL)
{
/* right operator */
args = list_make1(ltree);
actual_arg_types[0] = ltypeId;
declared_arg_types[0] = opform->oprleft;
nargs = 1;
}
else if (ltree == NULL)
{ {
/* left operator */ /* prefix operator */
args = list_make1(rtree); args = list_make1(rtree);
actual_arg_types[0] = rtypeId; actual_arg_types[0] = rtypeId;
declared_arg_types[0] = opform->oprright; declared_arg_types[0] = opform->oprright;
......
...@@ -9198,35 +9198,14 @@ get_oper_expr(OpExpr *expr, deparse_context *context) ...@@ -9198,35 +9198,14 @@ get_oper_expr(OpExpr *expr, deparse_context *context)
} }
else else
{ {
/* unary operator --- but which side? */ /* prefix operator */
Node *arg = (Node *) linitial(args); 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 ", appendStringInfo(buf, "%s ",
generate_operator_name(opno, generate_operator_name(opno,
InvalidOid, InvalidOid,
exprType(arg))); exprType(arg)));
get_rule_expr_paren(arg, context, true, (Node *) expr); 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);
} }
if (!PRETTY_PAREN(context)) if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
...@@ -11087,10 +11066,6 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2) ...@@ -11087,10 +11066,6 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2, p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
true, -1); true, -1);
break; break;
case 'r':
p_result = right_oper(NULL, list_make1(makeString(oprname)), arg1,
true, -1);
break;
default: default:
elog(ERROR, "unrecognized oprkind: %d", operform->oprkind); elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
p_result = NULL; /* keep compiler quiet */ p_result = NULL; /* keep compiler quiet */
......
...@@ -12650,6 +12650,11 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) ...@@ -12650,6 +12650,11 @@ dumpOpr(Archive *fout, OprInfo *oprinfo)
oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge); oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
oprcanhash = PQgetvalue(res, 0, i_oprcanhash); 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); oprregproc = convertRegProcReference(fout, oprcode);
if (oprregproc) if (oprregproc)
{ {
...@@ -12662,7 +12667,8 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) ...@@ -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 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 || if (strcmp(oprkind, "r") == 0 ||
strcmp(oprkind, "b") == 0) strcmp(oprkind, "b") == 0)
......
...@@ -22,6 +22,7 @@ static void check_is_install_user(ClusterInfo *cluster); ...@@ -22,6 +22,7 @@ static void check_is_install_user(ClusterInfo *cluster);
static void check_proper_datallowconn(ClusterInfo *cluster); static void check_proper_datallowconn(ClusterInfo *cluster);
static void check_for_prepared_transactions(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_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_tables_with_oids(ClusterInfo *cluster);
static void check_for_reg_data_type_usage(ClusterInfo *cluster); static void check_for_reg_data_type_usage(ClusterInfo *cluster);
static void check_for_jsonb_9_4_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) ...@@ -100,6 +101,13 @@ check_and_dump_old_cluster(bool live_check)
check_for_reg_data_type_usage(&old_cluster); check_for_reg_data_type_usage(&old_cluster);
check_for_isn_and_int8_passing_mismatch(&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 * Pre-PG 12 allowed tables to be declared WITH OIDS, which is not
* supported anymore. Verify there are none, iff applicable. * supported anymore. Verify there are none, iff applicable.
...@@ -896,6 +904,104 @@ check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster) ...@@ -896,6 +904,104 @@ check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster)
check_ok(); 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. * Verify that no tables are declared WITH OIDS.
......
...@@ -799,6 +799,10 @@ describeOperators(const char *pattern, bool verbose, bool showSystem) ...@@ -799,6 +799,10 @@ describeOperators(const char *pattern, bool verbose, bool showSystem)
* anyway, for now, because (1) third-party modules may still be following * 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 * the old convention, and (2) we'd need to do it anyway when talking to a
* pre-9.1 server. * 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, printfPQExpBuffer(&buf,
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202009171 #define CATALOG_VERSION_NO 202009172
#endif #endif
...@@ -41,7 +41,7 @@ CATALOG(pg_operator,2617,OperatorRelationId) ...@@ -41,7 +41,7 @@ CATALOG(pg_operator,2617,OperatorRelationId)
/* operator owner */ /* operator owner */
Oid oprowner BKI_DEFAULT(PGUID); Oid oprowner BKI_DEFAULT(PGUID);
/* 'l', 'r', or 'b' */ /* 'l' for prefix or 'b' for infix */
char oprkind BKI_DEFAULT(b); char oprkind BKI_DEFAULT(b);
/* can be used in merge join? */ /* can be used in merge join? */
...@@ -50,10 +50,10 @@ CATALOG(pg_operator,2617,OperatorRelationId) ...@@ -50,10 +50,10 @@ CATALOG(pg_operator,2617,OperatorRelationId)
/* can be used in hash join? */ /* can be used in hash join? */
bool oprcanhash BKI_DEFAULT(f); 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); Oid oprleft BKI_LOOKUP(pg_type);
/* right arg type, or 0 if 'r' oprkind */ /* right arg type */
Oid oprright BKI_LOOKUP(pg_type); Oid oprright BKI_LOOKUP(pg_type);
/* result datatype */ /* result datatype */
......
...@@ -31,8 +31,6 @@ extern Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError); ...@@ -31,8 +31,6 @@ extern Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError);
/* NB: the selected operator may require coercion of the input types! */ /* NB: the selected operator may require coercion of the input types! */
extern Operator oper(ParseState *pstate, List *op, Oid arg1, Oid arg2, extern Operator oper(ParseState *pstate, List *op, Oid arg1, Oid arg2,
bool noError, int location); 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, extern Operator left_oper(ParseState *pstate, List *op, Oid arg,
bool noError, int location); bool noError, int location);
......
...@@ -15,17 +15,15 @@ CREATE OPERATOR <% ( ...@@ -15,17 +15,15 @@ CREATE OPERATOR <% (
negator = >=% negator = >=%
); );
CREATE OPERATOR @#@ ( CREATE OPERATOR @#@ (
rightarg = int8, -- left unary rightarg = int8, -- prefix
procedure = factorial
);
CREATE OPERATOR #@# (
leftarg = int8, -- right unary
procedure = factorial procedure = factorial
); );
CREATE OPERATOR #%# ( CREATE OPERATOR #%# (
leftarg = int8, -- right unary leftarg = int8, -- fail, postfix is no longer supported
procedure = factorial procedure = factorial
); );
ERROR: operator right argument type must be specified
DETAIL: Postfix operators are not supported.
-- Test operator created above -- Test operator created above
SELECT point '(1,2)' <% widget '(0,0,3)' AS t, SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
point '(1,2)' <% widget '(0,0,1)' AS f; point '(1,2)' <% widget '(0,0,1)' AS f;
...@@ -35,11 +33,22 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t, ...@@ -35,11 +33,22 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
(1 row) (1 row)
-- Test comments -- Test comments
COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary'; COMMENT ON OPERATOR ###### (NONE, int4) IS 'bad prefix';
ERROR: operator does not exist: integer ###### ERROR: operator does not exist: ###### integer
-- => is disallowed now 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 => ( CREATE OPERATOR => (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
ERROR: syntax error at or near "=>" ERROR: syntax error at or near "=>"
...@@ -49,15 +58,20 @@ LINE 1: CREATE OPERATOR => ( ...@@ -49,15 +58,20 @@ LINE 1: CREATE OPERATOR => (
-- (=> is tested elsewhere) -- (=> is tested elsewhere)
-- this is legal because ! is not allowed in sql ops -- this is legal because ! is not allowed in sql ops
CREATE OPERATOR !=- ( CREATE OPERATOR !=- (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
SELECT 2 !=-; SELECT !=- 10;
?column? ?column?
---------- ----------
2 3628800
(1 row) (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 -- make sure lexer returns != as <> even in edge cases
SELECT 2 !=/**/ 1, 2 !=/**/ 2; SELECT 2 !=/**/ 1, 2 !=/**/ 2;
?column? | ?column? ?column? | ?column?
...@@ -127,7 +141,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC; ...@@ -127,7 +141,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC;
REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1; REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1;
SET ROLE regress_rol_op1; SET ROLE regress_rol_op1;
CREATE OPERATOR schema_op1.#*# ( CREATE OPERATOR schema_op1.#*# (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
ERROR: permission denied for schema schema_op1 ERROR: permission denied for schema schema_op1
...@@ -167,19 +181,19 @@ CREATE OPERATOR === ( ...@@ -167,19 +181,19 @@ CREATE OPERATOR === (
ROLLBACK; ROLLBACK;
-- Should fail. Invalid attribute -- Should fail. Invalid attribute
CREATE OPERATOR #@%# ( CREATE OPERATOR #@%# (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial, procedure = factorial,
invalid_att = int8 invalid_att = int8
); );
WARNING: operator attribute "invalid_att" not recognized 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 #@%# ( CREATE OPERATOR #@%# (
procedure = factorial 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 -- Should fail. Procedure should be mandatorily specified
CREATE OPERATOR #@%# ( CREATE OPERATOR #@%# (
leftarg = int8 rightarg = int8
); );
ERROR: operator function must be specified ERROR: operator function must be specified
-- Should fail. CREATE OPERATOR requires USAGE on TYPE -- Should fail. CREATE OPERATOR requires USAGE on TYPE
......
...@@ -1066,7 +1066,7 @@ WHERE condefault AND ...@@ -1066,7 +1066,7 @@ WHERE condefault AND
-- Look for illegal values in pg_operator fields. -- Look for illegal values in pg_operator fields.
SELECT p1.oid, p1.oprname SELECT p1.oid, p1.oprname
FROM pg_operator as p1 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; p1.oprresult = 0 OR p1.oprcode = 0;
oid | oprname oid | oprname
-----+--------- -----+---------
...@@ -1077,8 +1077,7 @@ SELECT p1.oid, p1.oprname ...@@ -1077,8 +1077,7 @@ SELECT p1.oid, p1.oprname
FROM pg_operator as p1 FROM pg_operator as p1
WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR
(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;
(p1.oprright != 0 and p1.oprkind = 'r');
oid | oprname oid | oprname
-----+--------- -----+---------
(0 rows) (0 rows)
...@@ -1285,18 +1284,6 @@ WHERE p1.oprcode = p2.oid AND ...@@ -1285,18 +1284,6 @@ WHERE p1.oprcode = p2.oid AND
-----+---------+-----+--------- -----+---------+-----+---------
(0 rows) (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 -- If the operator is mergejoinable or hashjoinable, its underlying function
-- should not be volatile. -- should not be volatile.
SELECT p1.oid, p1.oprname, p2.oid, p2.proname SELECT p1.oid, p1.oprname, p2.oid, p2.proname
......
...@@ -18,17 +18,12 @@ CREATE OPERATOR <% ( ...@@ -18,17 +18,12 @@ CREATE OPERATOR <% (
); );
CREATE OPERATOR @#@ ( CREATE OPERATOR @#@ (
rightarg = int8, -- left unary rightarg = int8, -- prefix
procedure = factorial
);
CREATE OPERATOR #@# (
leftarg = int8, -- right unary
procedure = factorial procedure = factorial
); );
CREATE OPERATOR #%# ( CREATE OPERATOR #%# (
leftarg = int8, -- right unary leftarg = int8, -- fail, postfix is no longer supported
procedure = factorial procedure = factorial
); );
...@@ -37,11 +32,18 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t, ...@@ -37,11 +32,18 @@ SELECT point '(1,2)' <% widget '(0,0,3)' AS t,
point '(1,2)' <% widget '(0,0,1)' AS f; point '(1,2)' <% widget '(0,0,1)' AS f;
-- Test comments -- 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 => ( CREATE OPERATOR => (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
...@@ -50,10 +52,12 @@ CREATE OPERATOR => ( ...@@ -50,10 +52,12 @@ CREATE OPERATOR => (
-- this is legal because ! is not allowed in sql ops -- this is legal because ! is not allowed in sql ops
CREATE OPERATOR !=- ( CREATE OPERATOR !=- (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
SELECT 2 !=-; SELECT !=- 10;
-- postfix operators don't work anymore
SELECT 10 !=-;
-- make sure lexer returns != as <> even in edge cases -- make sure lexer returns != as <> even in edge cases
SELECT 2 !=/**/ 1, 2 !=/**/ 2; SELECT 2 !=/**/ 1, 2 !=/**/ 2;
SELECT 2 !=-- comment to be removed by psql SELECT 2 !=-- comment to be removed by psql
...@@ -84,7 +88,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC; ...@@ -84,7 +88,7 @@ GRANT USAGE ON SCHEMA schema_op1 TO PUBLIC;
REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1; REVOKE USAGE ON SCHEMA schema_op1 FROM regress_rol_op1;
SET ROLE regress_rol_op1; SET ROLE regress_rol_op1;
CREATE OPERATOR schema_op1.#*# ( CREATE OPERATOR schema_op1.#*# (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial procedure = factorial
); );
ROLLBACK; ROLLBACK;
...@@ -128,19 +132,19 @@ ROLLBACK; ...@@ -128,19 +132,19 @@ ROLLBACK;
-- Should fail. Invalid attribute -- Should fail. Invalid attribute
CREATE OPERATOR #@%# ( CREATE OPERATOR #@%# (
leftarg = int8, -- right unary rightarg = int8,
procedure = factorial, procedure = factorial,
invalid_att = int8 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 #@%# ( CREATE OPERATOR #@%# (
procedure = factorial procedure = factorial
); );
-- Should fail. Procedure should be mandatorily specified -- Should fail. Procedure should be mandatorily specified
CREATE OPERATOR #@%# ( CREATE OPERATOR #@%# (
leftarg = int8 rightarg = int8
); );
-- Should fail. CREATE OPERATOR requires USAGE on TYPE -- Should fail. CREATE OPERATOR requires USAGE on TYPE
......
...@@ -571,7 +571,7 @@ WHERE condefault AND ...@@ -571,7 +571,7 @@ WHERE condefault AND
SELECT p1.oid, p1.oprname SELECT p1.oid, p1.oprname
FROM pg_operator as p1 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; p1.oprresult = 0 OR p1.oprcode = 0;
-- Look for missing or unwanted operand types -- Look for missing or unwanted operand types
...@@ -580,8 +580,7 @@ SELECT p1.oid, p1.oprname ...@@ -580,8 +580,7 @@ SELECT p1.oid, p1.oprname
FROM pg_operator as p1 FROM pg_operator as p1
WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR WHERE (p1.oprleft = 0 and p1.oprkind != 'l') OR
(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;
(p1.oprright != 0 and p1.oprkind = 'r');
-- Look for conflicting operator definitions (same names and input datatypes). -- Look for conflicting operator definitions (same names and input datatypes).
...@@ -715,15 +714,6 @@ WHERE p1.oprcode = p2.oid AND ...@@ -715,15 +714,6 @@ WHERE p1.oprcode = p2.oid AND
OR NOT binary_coercible(p1.oprright, p2.proargtypes[0]) OR NOT binary_coercible(p1.oprright, p2.proargtypes[0])
OR p1.oprleft != 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 -- If the operator is mergejoinable or hashjoinable, its underlying function
-- should not be volatile. -- should not be volatile.
......
...@@ -111,7 +111,7 @@ CREATE FUNCTION complex_add(complex, complex) ...@@ -111,7 +111,7 @@ CREATE FUNCTION complex_add(complex, complex)
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT;
-- we can now define the operator. We show a binary operator here but you -- 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 + ( CREATE OPERATOR + (
leftarg = complex, leftarg = complex,
rightarg = complex, rightarg = complex,
......
...@@ -96,36 +96,22 @@ SELECT n.nspname, r.rolname, format_type(t.oid, null) as typname ...@@ -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(right_type.oid, null) AS operand,
format_type(result.oid, null) AS return_type format_type(result.oid, null) AS return_type
FROM pg_namespace n, pg_operator o, FROM pg_namespace n, pg_operator o,
pg_type right_type, pg_type result pg_type right_type, pg_type result
WHERE o.oprnamespace = n.oid 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.oprright = right_type.oid
and o.oprresult = result.oid and o.oprresult = result.oid
ORDER BY nspname, operand; ORDER BY nspname, operand;
-- --
-- lists all right unary operators -- lists all infix 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
-- --
SELECT n.nspname, o.oprname AS binary_op, SELECT n.nspname, o.oprname AS binary_op,
format_type(left_type.oid, null) AS left_opr, format_type(left_type.oid, null) AS left_opr,
...@@ -134,7 +120,7 @@ SELECT n.nspname, o.oprname AS binary_op, ...@@ -134,7 +120,7 @@ SELECT n.nspname, o.oprname AS binary_op,
FROM pg_namespace n, pg_operator o, pg_type left_type, FROM pg_namespace n, pg_operator o, pg_type left_type,
pg_type right_type, pg_type result pg_type right_type, pg_type result
WHERE o.oprnamespace = n.oid WHERE o.oprnamespace = n.oid
and o.oprkind = 'b' -- binary and o.oprkind = 'b' -- infix ("binary")
and o.oprleft = left_type.oid and o.oprleft = left_type.oid
and o.oprright = right_type.oid and o.oprright = right_type.oid
and o.oprresult = result.oid and o.oprresult = result.oid
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment