Skip to content

Commit

Permalink
Refinement of FOR JSON PATH & Nested Functionality (#1929)
Browse files Browse the repository at this point in the history
Currently FOR JSON PATH is implemented in Babelfish using string manipulation. This change will implement FOR JSON PATH using PostgreSQL's JsonbValue and HTAB. The reason for this change is because the current implementation of FOR JSON PATH does not support the full functionality of the feature. Nested outputs determined by aliases in the SELECT statement are not supported. By implementing FOR JSON PATH using JsonbValue and HTAB, we provide an efficient solution that also supports nested json objects, while also setting the foundation for FOR JSON AUTO to be implemented.

BABELFISH_EXTENSIONS CHANGES:

Switched from String Manipulation implementation of FOR JSON PATH to an implementation using JsonbValue.
Wrapper function to add a value to JsonbValue and handle datatype checks.
Supporting functions to determine path to insert a JsonbValue object and key for HashTable.
Create json and insert into existing json functions that are used when dealing with nested json objects.
Implemented Nested Output tracking using PostgreSQL's HTAB library (HashTable)
Corrected the tests to showcase the new functionality of nested json objects
Added another set of tests to more thoroughly test the nested json object functionality.

POSTGRESQL_MODIFIED_FOR_BABELFISH CHANGES:

Created a public wrapper function in jsonb.h to call add_jsonb() from forjson.c

TEST CASES:

Added a test that checks the functionality and accuracy of the nested json feature. Includes cases with multiple layers, deeply nested layers, values in different stages of json layers, nested null outputs.
Organized version upgrade tests so previous versions use previous implementation of FOR JSON PATH and updated the schedule for each version.
Modified existing test cases to correctly showcase the nested functionality if the given test uses alias that cause nested json objects to be created.
Changed the expected outputs to include spaces as JsonbValue does when converted to a string.

Task: BABEL-3407
Signed-off-by: Jake Owen <[email protected]>
  • Loading branch information
Jakeowen1 authored Oct 17, 2023
1 parent 9003c60 commit b9ceb87
Show file tree
Hide file tree
Showing 32 changed files with 2,959 additions and 85 deletions.
400 changes: 349 additions & 51 deletions contrib/babelfishpg_tsql/src/tsql_for/forjson.c

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions test/JDBC/expected/forjson-before-14_10-or-15_5-vu-cleanup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- FOR JSON PATH clause without nested support
DROP VIEW forjson_vu_v_people
GO

DROP VIEW forjson_vu_v_countries
GO

-- Multiple tables without nested support
DROP VIEW forjson_vu_v_join
GO

-- ROOT directive without specifying value
DROP VIEW forjson_vu_v_root
GO

-- ROOT directive with specifying ROOT value
DROP VIEW forjson_vu_v_root_value
GO

-- ROOT directive with specifying ROOT value with empty string
DROP VIEW forjson_vu_v_empty_root
GO

-- WITHOUT_ARRAY_WRAPPERS directive
DROP VIEW forjson_vu_v_without_array_wrapper
GO

-- INCLUDE_NULL_VALUES directive
DROP VIEW forjson_vu_v_include_null_values
GO

-- Multiple Directives
DROP VIEW forjson_vu_v_root_include_null_values
GO

DROP VIEW forjson_vu_v_without_array_wrapper_include_null_values
GO


-- Test case with parameters
DROP PROCEDURE forjson_vu_p_params1
GO

DROP PROCEDURE forjson_vu_p_params2
GO

-- All null values test
DROP VIEW forjson_vu_v_nulls
GO

-- Test for all parser rules
DROP VIEW forjson_vu_v_order_by
GO

-- Display Table Contents
DROP TABLE forjson_vu_t_people
GO

DROP TABLE forjson_vu_t_countries
GO

DROP TABLE forjson_vu_t_values
GO
208 changes: 208 additions & 0 deletions test/JDBC/expected/forjson-before-14_10-or-15_5-vu-prepare.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
CREATE TABLE forjson_vu_t_people (
[Id] INT,
[FirstName] VARCHAR(25),
[LastName] VARCHAR(25),
[State] VARCHAR(25) )
GO

INSERT INTO forjson_vu_t_people values
(1,'Divya','Kumar',NULL),
(2,NULL,'Khanna','Bengaluru'),
(3,'Tom','Mehta','Kolkata'),
(4,'Kane',NULL,'Delhi')
GO
~~ROW COUNT: 4~~


CREATE TABLE forjson_vu_t_countries (
[Id] INT,
[Age] INT,
[Country] VARCHAR(25))
GO

INSERT INTO forjson_vu_t_countries values
(1,25, 'India'),
(2,40, 'USA'),
(3,30, 'India'),
(4,20, NULL),
(5,10, 'USA')
GO
~~ROW COUNT: 5~~


CREATE TABLE forjson_vu_t_values (
[Id] INT,
[value] VARCHAR(25) )
GO

INSERT INTO forjson_vu_t_values values
(1,NULL),
(2,NULL),
(3,NULL)
GO
~~ROW COUNT: 3~~


-- FOR JSON PATH clause without nested support
CREATE VIEW forjson_vu_v_people AS
SELECT (
SELECT Id AS EmpId,
FirstName AS "Name.FirstName",
LastName AS "Name.LastName",
State
FROM forjson_vu_t_people
FOR JSON PATH
) c1
GO

CREATE VIEW forjson_vu_v_countries AS
SELECT (
SELECT Id,
Age,
Country
FROM forjson_vu_t_countries
FOR JSON PATH
) c1
GO

-- Multiple tables without nested support
CREATE VIEW forjson_vu_v_join AS
SELECT (
SELECT E.FirstName AS 'Person.Name',
E.LastName AS 'Person.Surname',
D.Age AS 'Employee.Price',
D.Country AS 'Employee.Quantity'
FROM forjson_vu_t_people E
INNER JOIN forjson_vu_t_countries D
ON E.Id = D.Id
FOR JSON PATH
) c1
GO

-- ROOT directive without specifying value
CREATE VIEW forjson_vu_v_root AS
SELECT (
SELECT FirstName,
LastName
FROM forjson_vu_t_people
FOR JSON PATH, ROOT
) c1
GO

-- ROOT directive with specifying ROOT value
CREATE VIEW forjson_vu_v_root_value AS
SELECT (
SELECT FirstName,
LastName
FROM forjson_vu_t_people
FOR JSON PATH, ROOT('Employee')
) c1
GO

-- ROOT directive with specifying ROOT value with empty string
CREATE VIEW forjson_vu_v_empty_root AS
SELECT (
SELECT FirstName,
LastName
FROM forjson_vu_t_people
FOR JSON PATH, ROOT('')
) c1
GO

-- WITHOUT_ARRAY_WRAPPERS directive
CREATE VIEW forjson_vu_v_without_array_wrapper AS
SELECT (
SELECT FirstName,
LastName
FROM forjson_vu_t_people
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) c1
GO

-- INCLUDE_NULL_VALUES directive
CREATE VIEW forjson_vu_v_include_null_values AS
SELECT (
SELECT FirstName,
LastName
FROM forjson_vu_t_people
FOR JSON PATH, INCLUDE_NULL_VALUES
) c1
GO

-- Multiple Directives
CREATE VIEW forjson_vu_v_root_include_null_values AS
SELECT (
SELECT Id,
Age,
Country
FROM forjson_vu_t_countries
FOR JSON PATH, ROOT('Employee'), INCLUDE_NULL_VALUES
) c1
GO

CREATE VIEW forjson_vu_v_without_array_wrapper_include_null_values AS
SELECT (
SELECT Id,
Age,
Country
FROM forjson_vu_t_countries
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES
) c1
GO

-- Throws error as ROOT and WITHOUT_ARRAY_WRAPPER cannot be used together
CREATE VIEW forjson_vu_v_root_and_without_array_wrapper AS
SELECT (
SELECT Id,
Age,
Country
FROM forjson_vu_t_countries
FOR JSON PATH, ROOT, WITHOUT_ARRAY_WRAPPER
) c1
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: ROOT option and WITHOUT_ARRAY_WRAPPER option cannot be used together in FOR JSON. Remove one of these options)~~


-- Test case with parameters
CREATE PROCEDURE forjson_vu_p_params1 @id int AS
SELECT (
SELECT Firstname AS [Name],
State
FROM forjson_vu_t_people
WHERE Id = @id
FOR JSON PATH
) c1
GO

CREATE PROCEDURE forjson_vu_p_params2 @id int AS
SELECT (
SELECT Firstname AS [nam"@e],
State AS [State"@]
FROM forjson_vu_t_people
WHERE Id = @id
FOR JSON PATH
) c1
GO

-- All null values test
CREATE VIEW forjson_vu_v_nulls AS
SELECT (
SELECT value
FROM forjson_vu_t_values
FOR JSON PATH
) c1
GO

-- Test for all parser rules
CREATE VIEW forjson_vu_v_order_by AS
SELECT (
SELECT Id,
Age,
Country
FROM forjson_vu_t_countries
ORDER BY Age
FOR JSON PATH
) C1
GO
Loading

0 comments on commit b9ceb87

Please sign in to comment.