From b81c7b4098f52e64df89efe1461ba00a54649a10 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Sun, 7 Jun 2015 20:46:00 -0400
Subject: [PATCH] Desupport jsonb subscript deletion on objects

Supporting deletion of JSON pairs within jsonb objects using an
array-style integer subscript allowed for surprising outcomes.  This was
mostly due to the implementation-defined ordering of pairs within
objects for jsonb.

It also seems desirable to make jsonb integer subscript deletion
consistent with the 9.4 era general purpose integer subscripting
operator for jsonb (although that operator returns NULL when an object
is encountered, while we prefer here to throw an error).

Peter Geoghegan, following discussion on -hackers.
---
 doc/src/sgml/func.sgml                |  5 ++-
 src/backend/utils/adt/jsonfuncs.c     |  5 +++
 src/test/regress/expected/jsonb.out   | 56 +--------------------------
 src/test/regress/expected/jsonb_1.out | 56 +--------------------------
 src/test/regress/sql/jsonb.sql        | 11 +-----
 5 files changed, 13 insertions(+), 120 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 1fb9ae217f7..2d42b1e1743 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10309,8 +10309,9 @@ table2-mapping
        <row>
         <entry><literal>-</literal></entry>
         <entry><type>integer</type></entry>
-        <entry>Delete the field or element with specified index (Negative
-        integers count from the end)</entry>
+        <entry>Delete the array element with specified index (Negative
+        integers count from the end).  Throws an error if top level
+        container is not an array.</entry>
         <entry><literal>'["a", "b"]'::jsonb - 1 </literal></entry>
        </row>
        <row>
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index f87ba77e3eb..c14d3f73fca 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3400,6 +3400,11 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("cannot delete from scalar")));
 
+	if (JB_ROOT_IS_OBJECT(in))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot delete from object using integer subscript")));
+
 	if (JB_ROOT_COUNT(in) == 0)
 		PG_RETURN_JSONB(in);
 
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 412bf97b83f..e6654d47158 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3031,54 +3031,6 @@ select '["a","b","c"]'::jsonb - -4;
  ["a", "b", "c"]
 (1 row)
 
-select '{"a":1, "b":2, "c":3}'::jsonb - 3;
-         ?column?         
---------------------------
- {"a": 1, "b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 2;
-     ?column?     
-------------------
- {"a": 1, "b": 2}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 1;
-     ?column?     
-------------------
- {"a": 1, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 0;
-     ?column?     
-------------------
- {"b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -1;
-     ?column?     
-------------------
- {"a": 1, "b": 2}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -2;
-     ?column?     
-------------------
- {"a": 1, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -3;
-     ?column?     
-------------------
- {"b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -4;
-         ?column?         
---------------------------
- {"a": 1, "b": 2, "c": 3}
-(1 row)
-
 select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
                                 jsonb_set                                 
 --------------------------------------------------------------------------
@@ -3192,12 +3144,8 @@ select '[]'::jsonb - 'a';
 
 select '"a"'::jsonb - 1; -- error
 ERROR:  cannot delete from scalar
-select '{}'::jsonb -  1 ;
- ?column? 
-----------
- {}
-(1 row)
-
+select '{}'::jsonb -  1; -- error
+ERROR:  cannot delete from object using integer subscript
 select '[]'::jsonb - 1;
  ?column? 
 ----------
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
index 4ead74b5726..0a1ec934569 100644
--- a/src/test/regress/expected/jsonb_1.out
+++ b/src/test/regress/expected/jsonb_1.out
@@ -3031,54 +3031,6 @@ select '["a","b","c"]'::jsonb - -4;
  ["a", "b", "c"]
 (1 row)
 
-select '{"a":1, "b":2, "c":3}'::jsonb - 3;
-         ?column?         
---------------------------
- {"a": 1, "b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 2;
-     ?column?     
-------------------
- {"a": 1, "b": 2}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 1;
-     ?column?     
-------------------
- {"a": 1, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - 0;
-     ?column?     
-------------------
- {"b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -1;
-     ?column?     
-------------------
- {"a": 1, "b": 2}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -2;
-     ?column?     
-------------------
- {"a": 1, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -3;
-     ?column?     
-------------------
- {"b": 2, "c": 3}
-(1 row)
-
-select '{"a":1, "b":2, "c":3}'::jsonb - -4;
-         ?column?         
---------------------------
- {"a": 1, "b": 2, "c": 3}
-(1 row)
-
 select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
                                 jsonb_set                                 
 --------------------------------------------------------------------------
@@ -3192,12 +3144,8 @@ select '[]'::jsonb - 'a';
 
 select '"a"'::jsonb - 1; -- error
 ERROR:  cannot delete from scalar
-select '{}'::jsonb -  1 ;
- ?column? 
-----------
- {}
-(1 row)
-
+select '{}'::jsonb -  1; -- error
+ERROR:  cannot delete from object using integer subscript
 select '[]'::jsonb - 1;
  ?column? 
 ----------
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 2abec221b43..29c82a2a62c 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -738,15 +738,6 @@ select '["a","b","c"]'::jsonb - -2;
 select '["a","b","c"]'::jsonb - -3;
 select '["a","b","c"]'::jsonb - -4;
 
-select '{"a":1, "b":2, "c":3}'::jsonb - 3;
-select '{"a":1, "b":2, "c":3}'::jsonb - 2;
-select '{"a":1, "b":2, "c":3}'::jsonb - 1;
-select '{"a":1, "b":2, "c":3}'::jsonb - 0;
-select '{"a":1, "b":2, "c":3}'::jsonb - -1;
-select '{"a":1, "b":2, "c":3}'::jsonb - -2;
-select '{"a":1, "b":2, "c":3}'::jsonb - -3;
-select '{"a":1, "b":2, "c":3}'::jsonb - -4;
-
 select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
 select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
 select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
@@ -775,7 +766,7 @@ select '"a"'::jsonb - 'a'; -- error
 select '{}'::jsonb - 'a';
 select '[]'::jsonb - 'a';
 select '"a"'::jsonb - 1; -- error
-select '{}'::jsonb -  1 ;
+select '{}'::jsonb -  1; -- error
 select '[]'::jsonb - 1;
 select '"a"'::jsonb - '{a}'::text[]; -- error
 select '{}'::jsonb - '{a}'::text[];
-- 
GitLab