-
Notifications
You must be signed in to change notification settings - Fork 73
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
JSONB support #513
Changes from 4 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7fd7b67
Add tests for json operators
ritwizsinha 17815d1
Remove queries without from clause
ritwizsinha 6df562e
Merge branch 'main' into json-operator-support
ritwizsinha f789b73
Merge branch 'main' into json-operator-support
ritwizsinha 350ec28
Use new clang-format in CI (#518)
JelteF 09081bf
Update year in license file to 2025 (#519)
szarnyasg 3db93d2
Use AddStringOrBlob to create to duckdb string from blob value (#521)
mkaruza 7cde7f6
Fix temporary tables CTAS test (#520)
JelteF 8743bb4
Use PostgreSQL nodes for scanning tables (#477)
mkaruza 1cb6e6d
Fix approx_count_distinct for queries without a FROM (#524)
JelteF 35a4a02
Add jsonb support
ritwizsinha 3f5d5b1
Merge branch 'main' into json-operator-support
ritwizsinha 04d25c5
Fix tests
ritwizsinha 215ae4e
Add tests for jsonb and jsonb[] and format tests
ritwizsinha 888d54c
Merge branch 'main' into json-operator-support
ritwizsinha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
-- 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
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.