Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSONB support #513

Merged
merged 15 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 296 additions & 0 deletions test/regression/expected/json_operator_support.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
-- Create Table
-- Create table for JSON testing
CREATE TABLE test_json (
id SERIAL PRIMARY KEY,
data JSONB
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially you had some tests for the JSON type too. I think these tests would be more useful if they used the json type. Since jsonb is currently not supported at all, all the tests return a warning.

Suggested change
data JSONB
data JSON

To be clear I think the jsonb tests are good to have, but they should probably be added together with some basic jsonb support. Having them all throw a warning in the same way is not super useful.

);
-- Insert test data
INSERT INTO test_json (data) VALUES
('{"a": 1, "b": {"c": 2, "d": [3, 4]}, "e": "hello"}'),
('{"f": 10, "g": {"h": 20, "i": 30}, "j": [40, 50, 60]}'),
('{"k": true, "l": null, "m": {"n": "world", "o": [7, 8, 9]}}');
-- Test Case 1: Access JSON Object Field (->)
SELECT id, data->'a' AS a_value
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Binder Error: No function matches the given name and argument types 'json_extract("UnsupportedPostgresType (Oid=3802)", VARCHAR)'. You might need to add explicit type casts.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be really good to start supporting JSONB.

Candidate functions:
json_extract(VARCHAR, BIGINT) -> JSON
json_extract(VARCHAR, VARCHAR) -> JSON
json_extract(VARCHAR, VARCHAR[]) -> JSON[]
json_extract(JSON, BIGINT) -> JSON
json_extract(JSON, VARCHAR) -> JSON
json_extract(JSON, VARCHAR[]) -> JSON[]

id | a_value
----+---------
1 | 1
2 |
3 |
(3 rows)

-- Test Case 2: Access JSON Object Field as Text (->>)
SELECT id, data->>'e' AS e_value
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Binder Error: No function matches the given name and argument types '->>("UnsupportedPostgresType (Oid=3802)", VARCHAR)'. You might need to add explicit type casts.
Candidate functions:
->>(VARCHAR, BIGINT) -> VARCHAR
->>(VARCHAR, VARCHAR) -> VARCHAR
->>(VARCHAR, VARCHAR[]) -> VARCHAR[]
->>(JSON, BIGINT) -> VARCHAR
->>(JSON, VARCHAR) -> VARCHAR
->>(JSON, VARCHAR[]) -> VARCHAR[]

LINE 1: SELECT id, (data ->> 'e'::text) AS e_value FROM pgduckdb...
^
id | e_value
----+---------
1 | hello
2 |
3 |
(3 rows)

-- Test Case 3: Access Nested JSON Object Field (#>)
SELECT id, data#>'{b, c}' AS b_c_value
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "#"
LINE 1: SELECT id, (data #> '{b,c}'::text[]) AS b_c_value FROM p...
^
id | b_c_value
----+-----------
1 | 2
2 |
3 |
(3 rows)

-- Test Case 4: Access Nested JSON Object Field as Text (#>>)
SELECT id, data#>>'{m, n}' AS m_n_value
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "#"
LINE 1: SELECT id, (data #>> '{m,n}'::text[]) AS m_n_value FROM ...
^
id | m_n_value
----+-----------
1 |
2 |
3 | world
(3 rows)

-- Test Case 5: Check for Key Existence (?)
SELECT id, data ? 'k' AS has_k
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "?"
LINE 1: SELECT id, (data ? 'k'::text) AS has_k FROM pgduckdb.pub...
^
id | has_k
----+-------
1 | f
2 | f
3 | t
(3 rows)

-- Test Case 6: Check for Any Key in a List (?|)
SELECT id, data ?| ARRAY['a', 'f', 'x'] AS has_any_key
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "?"
LINE 1: SELECT id, (data ?| ARRAY['a'::text, 'f'::text, 'x'::tex...
^
id | has_any_key
----+-------------
1 | t
2 | t
3 | f
(3 rows)

-- Test Case 7: Check for All Keys in a List (?&)
SELECT id, data ?& ARRAY['a', 'b'] AS has_all_keys
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "?"
LINE 1: SELECT id, (data ?& ARRAY['a'::text, 'b'::text]) AS has_...
^
id | has_all_keys
----+--------------
1 | t
2 | f
3 | f
(3 rows)

-- Test Case 8: Concatenate JSON Objects (||)
SELECT id, data || '{"new_key": "new_value"}' AS updated_data
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Catalog Error: Type with name jsonb does not exist!
Did you mean "blob"?
id | updated_data
----+-------------------------------------------------------------------------------------
1 | {"a": 1, "b": {"c": 2, "d": [3, 4]}, "e": "hello", "new_key": "new_value"}
2 | {"f": 10, "g": {"h": 20, "i": 30}, "j": [40, 50, 60], "new_key": "new_value"}
3 | {"k": true, "l": null, "m": {"n": "world", "o": [7, 8, 9]}, "new_key": "new_value"}
(3 rows)

-- Test Case 9: Delete Key/Value Pair (-)
SELECT id, data - 'a' AS without_a
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Binder Error: No function matches the given name and argument types '-("UnsupportedPostgresType (Oid=3802)", VARCHAR)'. You might need to add explicit type casts.
Candidate functions:
-(TINYINT) -> TINYINT
-(TINYINT, TINYINT) -> TINYINT
-(SMALLINT) -> SMALLINT
-(SMALLINT, SMALLINT) -> SMALLINT
-(INTEGER) -> INTEGER
-(INTEGER, INTEGER) -> INTEGER
-(BIGINT) -> BIGINT
-(BIGINT, BIGINT) -> BIGINT
-(HUGEINT) -> HUGEINT
-(HUGEINT, HUGEINT) -> HUGEINT
-(FLOAT) -> FLOAT
-(FLOAT, FLOAT) -> FLOAT
-(DOUBLE) -> DOUBLE
-(DOUBLE, DOUBLE) -> DOUBLE
-(DECIMAL) -> DECIMAL
-(DECIMAL, DECIMAL) -> DECIMAL
-(UTINYINT) -> UTINYINT
-(UTINYINT, UTINYINT) -> UTINYINT
-(USMALLINT) -> USMALLINT
-(USMALLINT, USMALLINT) -> USMALLINT
-(UINTEGER) -> UINTEGER
-(UINTEGER, UINTEGER) -> UINTEGER
-(UBIGINT) -> UBIGINT
-(UBIGINT, UBIGINT) -> UBIGINT
-(UHUGEINT) -> UHUGEINT
-(UHUGEINT, UHUGEINT) -> UHUGEINT
-(DATE, DATE) -> BIGINT
-(DATE, INTEGER) -> DATE
-(TIMESTAMP, TIMESTAMP) -> INTERVAL
-(INTERVAL, INTERVAL) -> INTERVAL
-(DATE, INTERVAL) -> TIMESTAMP
-(TIME, INTERVAL) -> TIME
-(TIMESTAMP, INTERVAL) -> TIMESTAMP
-(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE
-(INTERVAL) -> INTERVAL
-(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE
-(TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE) -> INTERVAL

LINE 1: SELECT id, (data - 'a'::text) AS without_a FROM pgduckdb...
^
id | without_a
----+-------------------------------------------------------------
1 | {"b": {"c": 2, "d": [3, 4]}, "e": "hello"}
2 | {"f": 10, "g": {"h": 20, "i": 30}, "j": [40, 50, 60]}
3 | {"k": true, "l": null, "m": {"n": "world", "o": [7, 8, 9]}}
(3 rows)

-- Test Case 10: Delete Multiple Key/Value Pairs (- ARRAY[])
SELECT id, data - ARRAY['a', 'b'] AS without_a_b
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Binder Error: No function matches the given name and argument types '-("UnsupportedPostgresType (Oid=3802)", VARCHAR[])'. You might need to add explicit type casts.
Candidate functions:
-(TINYINT) -> TINYINT
-(TINYINT, TINYINT) -> TINYINT
-(SMALLINT) -> SMALLINT
-(SMALLINT, SMALLINT) -> SMALLINT
-(INTEGER) -> INTEGER
-(INTEGER, INTEGER) -> INTEGER
-(BIGINT) -> BIGINT
-(BIGINT, BIGINT) -> BIGINT
-(HUGEINT) -> HUGEINT
-(HUGEINT, HUGEINT) -> HUGEINT
-(FLOAT) -> FLOAT
-(FLOAT, FLOAT) -> FLOAT
-(DOUBLE) -> DOUBLE
-(DOUBLE, DOUBLE) -> DOUBLE
-(DECIMAL) -> DECIMAL
-(DECIMAL, DECIMAL) -> DECIMAL
-(UTINYINT) -> UTINYINT
-(UTINYINT, UTINYINT) -> UTINYINT
-(USMALLINT) -> USMALLINT
-(USMALLINT, USMALLINT) -> USMALLINT
-(UINTEGER) -> UINTEGER
-(UINTEGER, UINTEGER) -> UINTEGER
-(UBIGINT) -> UBIGINT
-(UBIGINT, UBIGINT) -> UBIGINT
-(UHUGEINT) -> UHUGEINT
-(UHUGEINT, UHUGEINT) -> UHUGEINT
-(DATE, DATE) -> BIGINT
-(DATE, INTEGER) -> DATE
-(TIMESTAMP, TIMESTAMP) -> INTERVAL
-(INTERVAL, INTERVAL) -> INTERVAL
-(DATE, INTERVAL) -> TIMESTAMP
-(TIME, INTERVAL) -> TIME
-(TIMESTAMP, INTERVAL) -> TIMESTAMP
-(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE
-(INTERVAL) -> INTERVAL
-(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE
-(TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE) -> INTERVAL

LINE 1: SELECT id, (data - ARRAY['a'::text, 'b'::text]) AS witho...
^
id | without_a_b
----+-------------------------------------------------------------
1 | {"e": "hello"}
2 | {"f": 10, "g": {"h": 20, "i": 30}, "j": [40, 50, 60]}
3 | {"k": true, "l": null, "m": {"n": "world", "o": [7, 8, 9]}}
(3 rows)

-- Test Case 11: Filter by JSON Value (@>)
SELECT id, data @> '{"a": 1}' AS contains_a_1
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Catalog Error: Type with name jsonb does not exist!
Did you mean "blob"?
id | contains_a_1
----+--------------
1 | t
2 | f
3 | f
(3 rows)

-- Test Case 12: Check if Contained Within (<@)
SELECT id, data <@ '{"a": 1, "b": {"c": 2, "d": [3, 4]}, "e": "hello"}' AS is_subset
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Catalog Error: Type with name jsonb does not exist!
Did you mean "blob"?
id | is_subset
----+-----------
1 | t
2 | f
3 | f
(3 rows)

-- Test Case 13: Extract Array Element by Index (#> for Arrays)
SELECT id, data#>'{b, d, 1}' AS second_element_in_d
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "#"
LINE 1: SELECT id, (data #> '{b,d,1}'::text[]) AS second_element...
^
id | second_element_in_d
----+---------------------
1 | 4
2 |
3 |
(3 rows)

-- Test Case 14: Filter Using JSON Path Queries (@?)
SELECT id, data @? '$.b.d[*] ? (@ > 3)' AS has_d_value_greater_than_3
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Parser Error: syntax error at or near "'$."b"."d"[*]?(@ > 3)'"
LINE 1: SELECT id, (data @? '$."b"."d"[*]?(@ > 3)'::jsonpath) AS ha...
^
id | has_d_value_greater_than_3
----+----------------------------
1 | t
2 | f
3 | f
(3 rows)

-- Test Case 15: Extract Value Using JSON Path Queries (@@)
SELECT id, data @@ '$.k == true' AS k_is_true
FROM test_json;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Catalog Error: Scalar Function with name @@ does not exist!
Did you mean "@"?
LINE 1: SELECT id, (data @@ '($."k" == true)'::jsonpath) AS k_is...
^
id | k_is_true
----+-----------
1 | f
2 | f
3 | t
(3 rows)

1 change: 1 addition & 0 deletions test/regression/schedule
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ test: transaction_errors
test: secrets
test: prepare
test: function
test: json_operator_support
ritwizsinha marked this conversation as resolved.
Show resolved Hide resolved
test: timestamp_with_interval
test: approx_count_distinct
72 changes: 72 additions & 0 deletions test/regression/sql/json_operator_support.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
-- Create Table
-- Create table for JSON testing
CREATE TABLE test_json (
ritwizsinha marked this conversation as resolved.
Show resolved Hide resolved
id SERIAL PRIMARY KEY,
data JSONB
);

-- Insert test data
INSERT INTO test_json (data) VALUES
('{"a": 1, "b": {"c": 2, "d": [3, 4]}, "e": "hello"}'),
('{"f": 10, "g": {"h": 20, "i": 30}, "j": [40, 50, 60]}'),
('{"k": true, "l": null, "m": {"n": "world", "o": [7, 8, 9]}}');

-- Test Case 1: Access JSON Object Field (->)
SELECT id, data->'a' AS a_value
FROM test_json;

-- Test Case 2: Access JSON Object Field as Text (->>)
SELECT id, data->>'e' AS e_value
FROM test_json;

-- Test Case 3: Access Nested JSON Object Field (#>)
SELECT id, data#>'{b, c}' AS b_c_value
FROM test_json;

-- Test Case 4: Access Nested JSON Object Field as Text (#>>)
SELECT id, data#>>'{m, n}' AS m_n_value
FROM test_json;

-- Test Case 5: Check for Key Existence (?)
SELECT id, data ? 'k' AS has_k
FROM test_json;

-- Test Case 6: Check for Any Key in a List (?|)
SELECT id, data ?| ARRAY['a', 'f', 'x'] AS has_any_key
FROM test_json;

-- Test Case 7: Check for All Keys in a List (?&)
SELECT id, data ?& ARRAY['a', 'b'] AS has_all_keys
FROM test_json;

-- Test Case 8: Concatenate JSON Objects (||)
SELECT id, data || '{"new_key": "new_value"}' AS updated_data
FROM test_json;

-- Test Case 9: Delete Key/Value Pair (-)
SELECT id, data - 'a' AS without_a
FROM test_json;

-- Test Case 10: Delete Multiple Key/Value Pairs (- ARRAY[])
SELECT id, data - ARRAY['a', 'b'] AS without_a_b
FROM test_json;

-- Test Case 11: Filter by JSON Value (@>)
SELECT id, data @> '{"a": 1}' AS contains_a_1
FROM test_json;

-- Test Case 12: Check if Contained Within (<@)
SELECT id, data <@ '{"a": 1, "b": {"c": 2, "d": [3, 4]}, "e": "hello"}' AS is_subset
FROM test_json;

-- Test Case 13: Extract Array Element by Index (#> for Arrays)
SELECT id, data#>'{b, d, 1}' AS second_element_in_d
FROM test_json;

-- Test Case 14: Filter Using JSON Path Queries (@?)
SELECT id, data @? '$.b.d[*] ? (@ > 3)' AS has_d_value_greater_than_3
FROM test_json;

-- Test Case 15: Extract Value Using JSON Path Queries (@@)
SELECT id, data @@ '$.k == true' AS k_is_true
FROM test_json;