mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 23:56:58 +03:00
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
This commit is contained in:
parent
76f412ab31
commit
1ed6b89563
@ -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)
|
|||||||
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)
|
|||||||
deparseOperatorName(buf, form);
|
deparseOperatorName(buf, form);
|
||||||
|
|
||||||
/* Deparse right operand. */
|
/* Deparse right operand. */
|
||||||
if (oprkind == 'l' || oprkind == 'b')
|
appendStringInfoChar(buf, ' ');
|
||||||
{
|
deparseExpr(llast(node->args), context);
|
||||||
arg = list_tail(node->args);
|
|
||||||
appendStringInfoChar(buf, ' ');
|
|
||||||
deparseExpr(lfirst(arg), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
|
|
||||||
|
@ -5159,8 +5159,8 @@ SCRAM-SHA-256$<replaceable><iteration count></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><iteration count></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><iteration count></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
|
|||||||
<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
|
|||||||
|
|
||||||
<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
|
|||||||
<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="
|
|||||||
<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
|
|||||||
<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
|
|||||||
<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> (
|
|||||||
</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> (
|
|||||||
<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> (
|
|||||||
<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
|
|||||||
|
|
||||||
<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> (
|
|||||||
<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);
|
|||||||
</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> )
|
|||||||
<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
|
|||||||
</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
|
|||||||
<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.
|
|||||||
<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 @@
|
|||||||
</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;
|
|||||||
</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;
|
|||||||
<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)
|
|||||||
* 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)
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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);
|
|||||||
%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; }
|
|||||||
{ $$ = (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
|
|||||||
{ $$ = (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
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* 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;
|
|||||||
#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;
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
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
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
* 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,
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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,8 +622,7 @@ 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,
|
|||||||
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,
|
|||||||
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 */
|
/* prefix operator */
|
||||||
args = list_make1(ltree);
|
|
||||||
actual_arg_types[0] = ltypeId;
|
|
||||||
declared_arg_types[0] = opform->oprleft;
|
|
||||||
nargs = 1;
|
|
||||||
}
|
|
||||||
else if (ltree == NULL)
|
|
||||||
{
|
|
||||||
/* left 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)
|
|||||||
}
|
}
|
||||||
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));
|
appendStringInfo(buf, "%s ",
|
||||||
if (!HeapTupleIsValid(tp))
|
generate_operator_name(opno,
|
||||||
elog(ERROR, "cache lookup failed for operator %u", opno);
|
InvalidOid,
|
||||||
optup = (Form_pg_operator) GETSTRUCT(tp);
|
exprType(arg)));
|
||||||
switch (optup->oprkind)
|
get_rule_expr_paren(arg, context, true, (Node *) expr);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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);
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202009171
|
#define CATALOG_VERSION_NO 202009172
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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)
|
|||||||
/* 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);
|
|||||||
/* 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 <% (
|
|||||||
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,
|
|||||||
(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 => (
|
|||||||
-- (=> 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;
|
|||||||
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 === (
|
|||||||
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
|
|||||||
-- 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
|
|||||||
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
|
|||||||
-----+---------+-----+---------
|
-----+---------+-----+---------
|
||||||
(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 <% (
|
|||||||
);
|
);
|
||||||
|
|
||||||
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,
|
|||||||
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';
|
||||||
|
|
||||||
-- => is disallowed now
|
-- Check that DROP on a nonexistent op behaves sanely, too
|
||||||
|
DROP OPERATOR ###### (NONE, int4);
|
||||||
|
DROP OPERATOR ###### (int4, NONE);
|
||||||
|
DROP OPERATOR ###### (int4, int8);
|
||||||
|
|
||||||
|
-- => 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 => (
|
|||||||
|
|
||||||
-- 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;
|
|||||||
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;
|
|||||||
|
|
||||||
-- 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
|
|||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
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)
|
|||||||
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
|
|||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- 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,
|
|||||||
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user