Skip to content

Commit 9bac660

Browse files
committed
Fix core dump in jsonb #> operator, and add regression test cases.
jsonb's #> operator segfaulted (dereferencing a null pointer) if the RHS was a zero-length array, as reported in bug #11207 from Justin Van Winkle. json's #> operator returns NULL in such cases, so for the moment let's make jsonb act likewise. Also add a bunch of regression test queries memorializing the -> and #> operators' behavior for this and other corner cases. There is a good argument for changing some of these behaviors, as they are not very consistent with each other, and throwing an error isn't necessarily a desirable behavior for operators that are likely to be used in indexes. However, everybody can agree that a core dump is the Wrong Thing, and we need test cases even if we decide to change their expected output later.
1 parent 7567d94 commit 9bac660

File tree

7 files changed

+711
-119
lines changed

7 files changed

+711
-119
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,13 @@ get_path_all(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
726726
deconstruct_array(path, TEXTOID, -1, false, 'i',
727727
&pathtext, &pathnulls, &npath);
728728

729+
/*
730+
* If the array is empty, return NULL; this is dubious but it's what 9.3
731+
* did.
732+
*/
733+
if (npath <= 0)
734+
PG_RETURN_NULL();
735+
729736
tpath = palloc(npath * sizeof(char *));
730737
ipath = palloc(npath * sizeof(int));
731738

@@ -1100,11 +1107,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
11001107
{
11011108
Jsonb *jb = PG_GETARG_JSONB(0);
11021109
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
1110+
Jsonb *res;
11031111
Datum *pathtext;
11041112
bool *pathnulls;
11051113
int npath;
11061114
int i;
1107-
Jsonb *res;
11081115
bool have_object = false,
11091116
have_array = false;
11101117
JsonbValue *jbvp = NULL;
@@ -1120,6 +1127,13 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
11201127
deconstruct_array(path, TEXTOID, -1, false, 'i',
11211128
&pathtext, &pathnulls, &npath);
11221129

1130+
/*
1131+
* If the array is empty, return NULL; this is dubious but it's what 9.3
1132+
* did.
1133+
*/
1134+
if (npath <= 0)
1135+
PG_RETURN_NULL();
1136+
11231137
if (JB_ROOT_IS_OBJECT(jb))
11241138
have_object = true;
11251139
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))

src/test/regress/expected/json.out

Lines changed: 166 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,45 @@ where json_type = 'array';
653653
t
654654
(1 row)
655655

656+
-- corner cases
657+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> null::text;
658+
?column?
659+
----------
660+
661+
(1 row)
662+
663+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> null::int;
664+
?column?
665+
----------
666+
667+
(1 row)
668+
669+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 1;
670+
ERROR: cannot extract array element from a non-array
671+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json -> 'z';
672+
?column?
673+
----------
674+
675+
(1 row)
676+
677+
select '[{"b": "c"}, {"b": "cc"}]'::json -> 1;
678+
?column?
679+
-------------
680+
{"b": "cc"}
681+
(1 row)
682+
683+
select '[{"b": "c"}, {"b": "cc"}]'::json -> 3;
684+
?column?
685+
----------
686+
687+
(1 row)
688+
689+
select '[{"b": "c"}, {"b": "cc"}]'::json -> 'z';
690+
ERROR: cannot extract field from a non-object
691+
select '"foo"'::json -> 1;
692+
ERROR: cannot extract element from a scalar
693+
select '"foo"'::json -> 'z';
694+
ERROR: cannot extract element from a scalar
656695
-- array length
657696
SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
658697
json_array_length
@@ -831,53 +870,161 @@ select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>>array['f2','1'];
831870
1
832871
(1 row)
833872

834-
-- same using array literals
835-
select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>'{f4,f6}';
836-
?column?
837-
-----------
838-
"stringy"
873+
-- corner cases for same
874+
select '{"a": {"b":{"c": "foo"}}}'::json #> array[]::text[];
875+
?column?
876+
----------
877+
878+
(1 row)
879+
880+
select '{"a": {"b":{"c": "foo"}}}'::json #> array['a'];
881+
?column?
882+
--------------------
883+
{"b":{"c": "foo"}}
884+
(1 row)
885+
886+
select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b'];
887+
?column?
888+
--------------
889+
{"c": "foo"}
839890
(1 row)
840891

841-
select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>'{f2}';
892+
select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b','c'];
842893
?column?
843894
----------
844-
{"f3":1}
895+
"foo"
845896
(1 row)
846897

847-
select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>'{f2,0}';
898+
select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','b','c','d'];
848899
?column?
849900
----------
850-
"f3"
901+
851902
(1 row)
852903

853-
select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>'{f2,1}';
904+
select '{"a": {"b":{"c": "foo"}}}'::json #> array['a','z','c'];
854905
?column?
855906
----------
856-
1
907+
857908
(1 row)
858909

859-
select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>>'{f4,f6}';
910+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> array['a','1','b'];
860911
?column?
861912
----------
862-
stringy
913+
"cc"
863914
(1 row)
864915

865-
select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>>'{f2}';
916+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #> array['a','z','b'];
866917
?column?
867918
----------
868-
{"f3":1}
919+
869920
(1 row)
870921

871-
select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>>'{f2,0}';
922+
select '[{"b": "c"}, {"b": "cc"}]'::json #> array['1','b'];
872923
?column?
873924
----------
874-
f3
925+
"cc"
875926
(1 row)
876927

877-
select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::json#>>'{f2,1}';
928+
select '[{"b": "c"}, {"b": "cc"}]'::json #> array['z','b'];
878929
?column?
879930
----------
880-
1
931+
932+
(1 row)
933+
934+
select '"foo"'::json #> array['z'];
935+
?column?
936+
----------
937+
938+
(1 row)
939+
940+
select '42'::json #> array['f2'];
941+
?column?
942+
----------
943+
944+
(1 row)
945+
946+
select '42'::json #> array['0'];
947+
?column?
948+
----------
949+
950+
(1 row)
951+
952+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array[]::text[];
953+
?column?
954+
----------
955+
956+
(1 row)
957+
958+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a'];
959+
?column?
960+
--------------------
961+
{"b":{"c": "foo"}}
962+
(1 row)
963+
964+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b'];
965+
?column?
966+
--------------
967+
{"c": "foo"}
968+
(1 row)
969+
970+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b','c'];
971+
?column?
972+
----------
973+
foo
974+
(1 row)
975+
976+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','b','c','d'];
977+
?column?
978+
----------
979+
980+
(1 row)
981+
982+
select '{"a": {"b":{"c": "foo"}}}'::json #>> array['a','z','c'];
983+
?column?
984+
----------
985+
986+
(1 row)
987+
988+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> array['a','1','b'];
989+
?column?
990+
----------
991+
cc
992+
(1 row)
993+
994+
select '{"a": [{"b": "c"}, {"b": "cc"}]}'::json #>> array['a','z','b'];
995+
?column?
996+
----------
997+
998+
(1 row)
999+
1000+
select '[{"b": "c"}, {"b": "cc"}]'::json #>> array['1','b'];
1001+
?column?
1002+
----------
1003+
cc
1004+
(1 row)
1005+
1006+
select '[{"b": "c"}, {"b": "cc"}]'::json #>> array['z','b'];
1007+
?column?
1008+
----------
1009+
1010+
(1 row)
1011+
1012+
select '"foo"'::json #>> array['z'];
1013+
?column?
1014+
----------
1015+
1016+
(1 row)
1017+
1018+
select '42'::json #>> array['f2'];
1019+
?column?
1020+
----------
1021+
1022+
(1 row)
1023+
1024+
select '42'::json #>> array['0'];
1025+
?column?
1026+
----------
1027+
8811028
(1 row)
8821029

8831030
-- array_elements

0 commit comments

Comments
 (0)