Commit 926fa801 authored by Tom Lane's avatar Tom Lane

Remove undocumented IS [NOT] OF syntax.

This feature was added a long time ago, in 7c1e67bd and eb121ba2,
but never documented in any user-facing way.  (Documentation added
in 6126d3e7 was commented out almost immediately, in 8272fc3f.)
That's because, while this syntax is defined by SQL:99, our
implementation is only vaguely related to the standard's semantics.
The standard appears to intend a run-time not parse-time test, and
it definitely intends that the test should understand subtype
relationships.

No one has stepped up to fix that in the intervening years, but
people keep coming across the code and asking why it's not documented.
Let's just get rid of it: if anyone ever wants to make it work per
spec, they can easily recover whatever parts of this code are still
of value from our git history.

If there's anyone out there who's actually using this despite its
undocumented status, they can switch to using pg_typeof() instead,
eg. "pg_typeof(something) = 'mytype'::regtype".  That gives
essentially the same semantics as what our IS OF code did.
(We didn't have that function last time this was discussed, or
we would have ripped out IS OF then.)

Discussion: https://postgr.es/m/CAKFQuwZ2pTc-DSkOiTfjauqLYkNREeNZvWmeg12Q-_69D+sYZA@mail.gmail.com
Discussion: https://postgr.es/m/BAY20-F23E9F2B4DAB3E4E88D3623F99B0@phx.gbl
Discussion: https://postgr.es/m/3E7CF81D.1000203@joeconway.com
parent 97390fe8
...@@ -762,25 +762,6 @@ repeat('Pg', 4) <returnvalue>PgPgPgPg</returnvalue> ...@@ -762,25 +762,6 @@ repeat('Pg', 4) <returnvalue>PgPgPgPg</returnvalue>
expression must be of Boolean type. expression must be of Boolean type.
</para> </para>
<!-- IS OF does not conform to the ISO SQL behavior, so it is undocumented here
<para>
<indexterm>
<primary>IS OF</primary>
</indexterm>
<indexterm>
<primary>IS NOT OF</primary>
</indexterm>
It is possible to check the data type of an expression using the
predicates
<synopsis>
<replaceable>expression</replaceable> IS OF (typename, ...)
<replaceable>expression</replaceable> IS NOT OF (typename, ...)
</synopsis>
They return a boolean value based on whether the expression's data
type is one of the listed data types.
</para>
-->
<para> <para>
Some comparison-related functions are also available, as shown in <xref Some comparison-related functions are also available, as shown in <xref
linkend="functions-comparison-func-table"/>. linkend="functions-comparison-func-table"/>.
......
...@@ -373,7 +373,7 @@ S096 Optional array bounds YES ...@@ -373,7 +373,7 @@ S096 Optional array bounds YES
S097 Array element assignment NO S097 Array element assignment NO
S098 ARRAY_AGG YES S098 ARRAY_AGG YES
S111 ONLY in query expressions YES S111 ONLY in query expressions YES
S151 Type predicate NO S151 Type predicate NO see pg_typeof()
S161 Subtype treatment NO S161 Subtype treatment NO
S162 Subtype treatment for references NO S162 Subtype treatment for references NO
S201 SQL-invoked routines on arrays YES S201 SQL-invoked routines on arrays YES
......
...@@ -3213,10 +3213,6 @@ _outAExpr(StringInfo str, const A_Expr *node) ...@@ -3213,10 +3213,6 @@ _outAExpr(StringInfo str, const A_Expr *node)
appendStringInfoString(str, " NULLIF "); appendStringInfoString(str, " NULLIF ");
WRITE_NODE_FIELD(name); WRITE_NODE_FIELD(name);
break; break;
case AEXPR_OF:
appendStringInfoString(str, " OF ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_IN: case AEXPR_IN:
appendStringInfoString(str, " IN "); appendStringInfoString(str, " IN ");
WRITE_NODE_FIELD(name); WRITE_NODE_FIELD(name);
......
...@@ -13238,14 +13238,6 @@ a_expr: c_expr { $$ = $1; } ...@@ -13238,14 +13238,6 @@ a_expr: c_expr { $$ = $1; }
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
} }
| a_expr IS OF '(' type_list ')' %prec IS
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2);
}
| a_expr IS NOT OF '(' type_list ')' %prec IS
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
}
| a_expr BETWEEN opt_asymmetric b_expr AND a_expr %prec BETWEEN | a_expr BETWEEN opt_asymmetric b_expr AND a_expr %prec BETWEEN
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN, $$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN,
...@@ -13464,14 +13456,6 @@ b_expr: c_expr ...@@ -13464,14 +13456,6 @@ b_expr: c_expr
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
} }
| b_expr IS OF '(' type_list ')' %prec IS
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2);
}
| b_expr IS NOT OF '(' type_list ')' %prec IS
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
}
| b_expr IS DOCUMENT_P %prec IS | b_expr IS DOCUMENT_P %prec IS
{ {
$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
......
...@@ -94,7 +94,6 @@ static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a); ...@@ -94,7 +94,6 @@ static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a);
static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a); static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a);
static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a); static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a); static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
static Node *transformAExprIn(ParseState *pstate, A_Expr *a); static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
static Node *transformAExprBetween(ParseState *pstate, A_Expr *a); static Node *transformAExprBetween(ParseState *pstate, A_Expr *a);
static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a); static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a);
...@@ -228,9 +227,6 @@ transformExprRecurse(ParseState *pstate, Node *expr) ...@@ -228,9 +227,6 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case AEXPR_NULLIF: case AEXPR_NULLIF:
result = transformAExprNullIf(pstate, a); result = transformAExprNullIf(pstate, a);
break; break;
case AEXPR_OF:
result = transformAExprOf(pstate, a);
break;
case AEXPR_IN: case AEXPR_IN:
result = transformAExprIn(pstate, a); result = transformAExprIn(pstate, a);
break; break;
...@@ -1168,51 +1164,6 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a) ...@@ -1168,51 +1164,6 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a)
return (Node *) result; return (Node *) result;
} }
/*
* Checking an expression for match to a list of type names. Will result
* in a boolean constant node.
*/
static Node *
transformAExprOf(ParseState *pstate, A_Expr *a)
{
Node *lexpr = a->lexpr;
Const *result;
ListCell *telem;
Oid ltype,
rtype;
bool matched = false;
if (operator_precedence_warning)
emit_precedence_warnings(pstate, PREC_GROUP_POSTFIX_IS, "IS",
lexpr, NULL,
a->location);
lexpr = transformExprRecurse(pstate, lexpr);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
{
rtype = typenameTypeId(pstate, lfirst(telem));
matched = (rtype == ltype);
if (matched)
break;
}
/*
* We have two forms: equals or not equals. Flip the sense of the result
* for not equals.
*/
if (strcmp(strVal(linitial(a->name)), "<>") == 0)
matched = (!matched);
result = (Const *) makeBoolConst(matched, false);
/* Make the result have the original input's parse location */
result->location = exprLocation((Node *) a);
return (Node *) result;
}
static Node * static Node *
transformAExprIn(ParseState *pstate, A_Expr *a) transformAExprIn(ParseState *pstate, A_Expr *a)
{ {
...@@ -3257,11 +3208,6 @@ operator_precedence_group(Node *node, const char **nodename) ...@@ -3257,11 +3208,6 @@ operator_precedence_group(Node *node, const char **nodename)
*nodename = "IS"; *nodename = "IS";
group = PREC_GROUP_INFIX_IS; group = PREC_GROUP_INFIX_IS;
} }
else if (aexpr->kind == AEXPR_OF)
{
*nodename = "IS";
group = PREC_GROUP_POSTFIX_IS;
}
else if (aexpr->kind == AEXPR_IN) else if (aexpr->kind == AEXPR_IN)
{ {
*nodename = "IN"; *nodename = "IN";
......
...@@ -258,7 +258,6 @@ typedef enum A_Expr_Kind ...@@ -258,7 +258,6 @@ typedef enum A_Expr_Kind
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */ AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NOT_DISTINCT, /* IS NOT DISTINCT FROM - name must be "=" */ AEXPR_NOT_DISTINCT, /* IS NOT DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */ AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF, /* IS [NOT] OF - name must be "=" or "<>" */
AEXPR_IN, /* [NOT] IN - name must be "=" or "<>" */ AEXPR_IN, /* [NOT] IN - name must be "=" or "<>" */
AEXPR_LIKE, /* [NOT] LIKE - name must be "~~" or "!~~" */ AEXPR_LIKE, /* [NOT] LIKE - name must be "~~" or "!~~" */
AEXPR_ILIKE, /* [NOT] ILIKE - name must be "~~*" or "!~~*" */ AEXPR_ILIKE, /* [NOT] ILIKE - name must be "~~*" or "!~~*" */
......
...@@ -1149,10 +1149,10 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; ...@@ -1149,10 +1149,10 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}";
{1,2,3} {1,2,3}
(1 row) (1 row)
SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE"; SELECT pg_typeof(ARRAY[1,2,3]::text[]::int[]::float8[]) AS "double precision[]";
TRUE double precision[]
------ --------------------
t double precision[]
(1 row) (1 row)
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}"; SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
...@@ -1161,10 +1161,10 @@ SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk ...@@ -1161,10 +1161,10 @@ SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk
{{a,bc},{def,hijk}} {{a,bc},{def,hijk}}
(1 row) (1 row)
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE"; SELECT pg_typeof(ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[]) AS "character varying[]";
TRUE character varying[]
------ ---------------------
t character varying[]
(1 row) (1 row)
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"; SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
......
...@@ -70,22 +70,16 @@ from basictest; ...@@ -70,22 +70,16 @@ from basictest;
(3 rows) (3 rows)
-- check that union/case/coalesce type resolution handles domains properly -- check that union/case/coalesce type resolution handles domains properly
select coalesce(4::domainint4, 7) is of (int4) as t; select pg_typeof(coalesce(4::domainint4, 7));
t pg_typeof
--- -----------
t integer
(1 row)
select coalesce(4::domainint4, 7) is of (domainint4) as f;
f
---
f
(1 row) (1 row)
select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; select pg_typeof(coalesce(4::domainint4, 7::domainint4));
t pg_typeof
--- ------------
t domainint4
(1 row) (1 row)
drop table basictest; drop table basictest;
......
...@@ -142,10 +142,10 @@ SELECT * FROM t LIMIT 10; ...@@ -142,10 +142,10 @@ SELECT * FROM t LIMIT 10;
-- Test behavior with an unknown-type literal in the WITH -- Test behavior with an unknown-type literal in the WITH
WITH q AS (SELECT 'foo' AS x) WITH q AS (SELECT 'foo' AS x)
SELECT x, x IS OF (text) AS is_text FROM q; SELECT x, pg_typeof(x) FROM q;
x | is_text x | pg_typeof
-----+--------- -----+-----------
foo | t foo | text
(1 row) (1 row)
WITH RECURSIVE t(n) AS ( WITH RECURSIVE t(n) AS (
...@@ -153,15 +153,15 @@ WITH RECURSIVE t(n) AS ( ...@@ -153,15 +153,15 @@ WITH RECURSIVE t(n) AS (
UNION ALL UNION ALL
SELECT n || ' bar' FROM t WHERE length(n) < 20 SELECT n || ' bar' FROM t WHERE length(n) < 20
) )
SELECT n, n IS OF (text) AS is_text FROM t; SELECT n, pg_typeof(n) FROM t;
n | is_text n | pg_typeof
-------------------------+--------- -------------------------+-----------
foo | t foo | text
foo bar | t foo bar | text
foo bar bar | t foo bar bar | text
foo bar bar bar | t foo bar bar bar | text
foo bar bar bar bar | t foo bar bar bar bar | text
foo bar bar bar bar bar | t foo bar bar bar bar bar | text
(6 rows) (6 rows)
-- In a perfect world, this would work and resolve the literal as int ... -- In a perfect world, this would work and resolve the literal as int ...
...@@ -171,7 +171,7 @@ WITH RECURSIVE t(n) AS ( ...@@ -171,7 +171,7 @@ WITH RECURSIVE t(n) AS (
UNION ALL UNION ALL
SELECT n+1 FROM t WHERE n < 10 SELECT n+1 FROM t WHERE n < 10
) )
SELECT n, n IS OF (int) AS is_int FROM t; SELECT n, pg_typeof(n) FROM t;
ERROR: operator does not exist: text + integer ERROR: operator does not exist: text + integer
LINE 4: SELECT n+1 FROM t WHERE n < 10 LINE 4: SELECT n+1 FROM t WHERE n < 10
^ ^
......
...@@ -343,9 +343,9 @@ SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; ...@@ -343,9 +343,9 @@ SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno;
-- array casts -- array casts
SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}";
SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE"; SELECT pg_typeof(ARRAY[1,2,3]::text[]::int[]::float8[]) AS "double precision[]";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}"; SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE"; SELECT pg_typeof(ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[]) AS "character varying[]";
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"; SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
SELECT NULL::text[]::int[] AS "NULL"; SELECT NULL::text[]::int[] AS "NULL";
......
...@@ -59,9 +59,8 @@ select testtext || testvarchar as concat, testnumeric + 42 as sum ...@@ -59,9 +59,8 @@ select testtext || testvarchar as concat, testnumeric + 42 as sum
from basictest; from basictest;
-- check that union/case/coalesce type resolution handles domains properly -- check that union/case/coalesce type resolution handles domains properly
select coalesce(4::domainint4, 7) is of (int4) as t; select pg_typeof(coalesce(4::domainint4, 7));
select coalesce(4::domainint4, 7) is of (domainint4) as f; select pg_typeof(coalesce(4::domainint4, 7::domainint4));
select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t;
drop table basictest; drop table basictest;
drop domain domainvarchar restrict; drop domain domainvarchar restrict;
......
...@@ -77,14 +77,14 @@ SELECT * FROM t LIMIT 10; ...@@ -77,14 +77,14 @@ SELECT * FROM t LIMIT 10;
-- Test behavior with an unknown-type literal in the WITH -- Test behavior with an unknown-type literal in the WITH
WITH q AS (SELECT 'foo' AS x) WITH q AS (SELECT 'foo' AS x)
SELECT x, x IS OF (text) AS is_text FROM q; SELECT x, pg_typeof(x) FROM q;
WITH RECURSIVE t(n) AS ( WITH RECURSIVE t(n) AS (
SELECT 'foo' SELECT 'foo'
UNION ALL UNION ALL
SELECT n || ' bar' FROM t WHERE length(n) < 20 SELECT n || ' bar' FROM t WHERE length(n) < 20
) )
SELECT n, n IS OF (text) AS is_text FROM t; SELECT n, pg_typeof(n) FROM t;
-- In a perfect world, this would work and resolve the literal as int ... -- In a perfect world, this would work and resolve the literal as int ...
-- but for now, we have to be content with resolving to text too soon. -- but for now, we have to be content with resolving to text too soon.
...@@ -93,7 +93,7 @@ WITH RECURSIVE t(n) AS ( ...@@ -93,7 +93,7 @@ WITH RECURSIVE t(n) AS (
UNION ALL UNION ALL
SELECT n+1 FROM t WHERE n < 10 SELECT n+1 FROM t WHERE n < 10
) )
SELECT n, n IS OF (int) AS is_int FROM t; SELECT n, pg_typeof(n) FROM t;
-- --
-- Some examples with a tree -- Some examples with a tree
......
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