aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2020-11-19 17:39:39 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2020-11-19 17:39:39 -0500
commit926fa801ac9eb54c5275472271ec63a059904698 (patch)
tree95a76e3721417c364b28fb4e0a715a3425461b59 /src
parent97390fe8a6e96a153e59b0180f4303acaeb75b84 (diff)
downloadpostgresql-926fa801ac9eb54c5275472271ec63a059904698.tar.gz
postgresql-926fa801ac9eb54c5275472271ec63a059904698.zip
Remove undocumented IS [NOT] OF syntax.
This feature was added a long time ago, in 7c1e67bd5 and eb121ba2c, but never documented in any user-facing way. (Documentation added in 6126d3e70 was commented out almost immediately, in 8272fc3f7.) 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
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/sql_features.txt2
-rw-r--r--src/backend/nodes/outfuncs.c4
-rw-r--r--src/backend/parser/gram.y16
-rw-r--r--src/backend/parser/parse_expr.c54
-rw-r--r--src/include/nodes/parsenodes.h1
-rw-r--r--src/test/regress/expected/arrays.out16
-rw-r--r--src/test/regress/expected/domain.out22
-rw-r--r--src/test/regress/expected/with.out28
-rw-r--r--src/test/regress/sql/arrays.sql4
-rw-r--r--src/test/regress/sql/domain.sql5
-rw-r--r--src/test/regress/sql/with.sql6
11 files changed, 38 insertions, 120 deletions
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index b6e58e84939..caa971c4356 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -373,7 +373,7 @@ S096 Optional array bounds YES
S097 Array element assignment NO
S098 ARRAY_AGG YES
S111 ONLY in query expressions YES
-S151 Type predicate NO
+S151 Type predicate NO see pg_typeof()
S161 Subtype treatment NO
S162 Subtype treatment for references NO
S201 SQL-invoked routines on arrays YES
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 4504b1503b9..f26498cea2d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3213,10 +3213,6 @@ _outAExpr(StringInfo str, const A_Expr *node)
appendStringInfoString(str, " NULLIF ");
WRITE_NODE_FIELD(name);
break;
- case AEXPR_OF:
- appendStringInfoString(str, " OF ");
- WRITE_NODE_FIELD(name);
- break;
case AEXPR_IN:
appendStringInfoString(str, " IN ");
WRITE_NODE_FIELD(name);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2cb377d0342..efc9c997541 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13238,14 +13238,6 @@ a_expr: c_expr { $$ = $1; }
{
$$ = (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
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN,
@@ -13464,14 +13456,6 @@ b_expr: c_expr
{
$$ = (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
{
$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f5165863d77..36002f059d1 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -94,7 +94,6 @@ static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a);
static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a);
static Node *transformAExprDistinct(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 *transformAExprBetween(ParseState *pstate, A_Expr *a);
static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a);
@@ -228,9 +227,6 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case AEXPR_NULLIF:
result = transformAExprNullIf(pstate, a);
break;
- case AEXPR_OF:
- result = transformAExprOf(pstate, a);
- break;
case AEXPR_IN:
result = transformAExprIn(pstate, a);
break;
@@ -1168,51 +1164,6 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a)
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 *
transformAExprIn(ParseState *pstate, A_Expr *a)
{
@@ -3257,11 +3208,6 @@ operator_precedence_group(Node *node, const char **nodename)
*nodename = "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)
{
*nodename = "IN";
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a2dcdef3fad..d1f9ef29ca0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -258,7 +258,6 @@ typedef enum A_Expr_Kind
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NOT_DISTINCT, /* IS NOT DISTINCT FROM - 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_LIKE, /* [NOT] LIKE - name must be "~~" or "!~~" */
AEXPR_ILIKE, /* [NOT] ILIKE - name must be "~~*" or "!~~*" */
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 5abfeb6773b..e0f870e9d7b 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1149,10 +1149,10 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}";
{1,2,3}
(1 row)
-SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE";
- TRUE
-------
- t
+SELECT pg_typeof(ARRAY[1,2,3]::text[]::int[]::float8[]) AS "double precision[]";
+ double precision[]
+--------------------
+ double precision[]
(1 row)
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}}
(1 row)
-SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
- TRUE
-------
- t
+SELECT pg_typeof(ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[]) AS "character varying[]";
+ character varying[]
+---------------------
+ character varying[]
(1 row)
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 2a033a6e11e..411d5c003ef 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -70,22 +70,16 @@ from basictest;
(3 rows)
-- check that union/case/coalesce type resolution handles domains properly
-select coalesce(4::domainint4, 7) is of (int4) as t;
- t
----
- t
-(1 row)
-
-select coalesce(4::domainint4, 7) is of (domainint4) as f;
- f
----
- f
+select pg_typeof(coalesce(4::domainint4, 7));
+ pg_typeof
+-----------
+ integer
(1 row)
-select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t;
- t
----
- t
+select pg_typeof(coalesce(4::domainint4, 7::domainint4));
+ pg_typeof
+------------
+ domainint4
(1 row)
drop table basictest;
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 96835a517ee..9429e9fd28f 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -142,10 +142,10 @@ SELECT * FROM t LIMIT 10;
-- Test behavior with an unknown-type literal in the WITH
WITH q AS (SELECT 'foo' AS x)
-SELECT x, x IS OF (text) AS is_text FROM q;
- x | is_text
------+---------
- foo | t
+SELECT x, pg_typeof(x) FROM q;
+ x | pg_typeof
+-----+-----------
+ foo | text
(1 row)
WITH RECURSIVE t(n) AS (
@@ -153,15 +153,15 @@ WITH RECURSIVE t(n) AS (
UNION ALL
SELECT n || ' bar' FROM t WHERE length(n) < 20
)
-SELECT n, n IS OF (text) AS is_text FROM t;
- n | is_text
--------------------------+---------
- foo | t
- foo bar | t
- foo bar bar | t
- foo bar bar bar | t
- foo bar bar bar bar | t
- foo bar bar bar bar bar | t
+SELECT n, pg_typeof(n) FROM t;
+ n | pg_typeof
+-------------------------+-----------
+ foo | text
+ foo bar | text
+ foo bar bar | text
+ foo bar bar bar | text
+ foo bar bar bar bar | text
+ foo bar bar bar bar bar | text
(6 rows)
-- In a perfect world, this would work and resolve the literal as int ...
@@ -171,7 +171,7 @@ WITH RECURSIVE t(n) AS (
UNION ALL
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
LINE 4: SELECT n+1 FROM t WHERE n < 10
^
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 906fd712b71..199049b2a27 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -343,9 +343,9 @@ SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno;
-- array casts
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[] 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 NULL::text[]::int[] AS "NULL";
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 1291d550d68..549c0b5adfa 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -59,9 +59,8 @@ select testtext || testvarchar as concat, testnumeric + 42 as sum
from basictest;
-- check that union/case/coalesce type resolution handles domains properly
-select coalesce(4::domainint4, 7) is of (int4) as t;
-select coalesce(4::domainint4, 7) is of (domainint4) as f;
-select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t;
+select pg_typeof(coalesce(4::domainint4, 7));
+select pg_typeof(coalesce(4::domainint4, 7::domainint4));
drop table basictest;
drop domain domainvarchar restrict;
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index b1b79eb1722..ad976de5311 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -77,14 +77,14 @@ SELECT * FROM t LIMIT 10;
-- Test behavior with an unknown-type literal in the WITH
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 (
SELECT 'foo'
UNION ALL
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 ...
-- but for now, we have to be content with resolving to text too soon.
@@ -93,7 +93,7 @@ WITH RECURSIVE t(n) AS (
UNION ALL
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