Skip to content

Conversation

@martin-georgiev
Copy link
Owner

@martin-georgiev martin-georgiev commented Mar 13, 2025

Summary by CodeRabbit

  • New Features

    • Advanced query functions now enforce stricter input validation, providing clearer and more consistent error messages when incorrect parameters are used.
  • Tests

    • Expanded the test suite to ensure that invalid input scenarios are correctly detected, enhancing overall query reliability.
  • Refactor

    • Improved internal accessibility of testing helpers for better support of extended validation scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2025

Walkthrough

The changes enhance argument validation across several ORM function classes by introducing a new abstract method validateArguments in a base class and implementing it in derived classes. A new exception class, InvalidArgumentForVariadicFunctionException, is added to handle invalid argument scenarios with specific helper methods. In addition, new tests ensure that invalid argument counts trigger exceptions in functions such as GREATEST, LEAST, JSON_BUILD_OBJECT, TO_TSQUERY, etc., and the visibility of a test utility method has been updated for better subclass access.

Changes

File(s) Change Summary
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php Added a call to validateArguments at the end of feedParserWithNodes; introduced an abstract validateArguments(array $arguments): void method.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php Introduced a new exception class with static methods: exactCount(), atLeast(), between(), and evenNumber() for generating error messages.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php, .../JsonBuildObject.php, .../JsonbBuildObject.php, .../Least.php, .../Row.php, .../ToTsquery.php, .../ToTsvector.php, .../Unaccent.php Added implementations of validateArguments(array $arguments): void with function-specific validation logic and exception throwing based on argument count.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/{GreatestTest.php, JsonBuildObjectTest.php, JsonbBuildObjectTest.php, LeastTest.php, ToTsqueryTest.php, ToTsvectorTest.php, UnaccentTest.php} Added new test methods to verify that providing an invalid number of arguments results in an InvalidArgumentForVariadicFunctionException.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php Changed the assertSqlFromDql method visibility from private to protected to allow access in subclasses.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (DQL Query)
    participant Function as Variadic Function Class
    participant Validator as validateArguments Method
    participant Exception as Exception Handler

    Client->>Function: call feedParserWithNodes()
    Function->>Function: parse nodes
    Function->>Validator: invoke validateArguments(nodes)
    alt Valid Argument Count
        Validator-->>Function: validation successful
        Function-->>Client: continue query processing
    else Invalid Argument Count
        Validator-->>Exception: throw InvalidArgumentForVariadicFunctionException
        Exception-->>Client: return error response
    end
Loading

Poem

I'm a hopping rabbit, joyful and bold,
In our code, validations now unfold.
Arguments get checked with a careful gaze,
Errors fly like leaves in a breezy daze.
Hooray for clean code—let the bugs be told!
🐇✨

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 877d929 and 03b4377.

📒 Files selected for processing (18)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (2 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*....

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*.php: Use the PostgreSQL official documentation to verify that tests include comprehensive use cases and example SQL queries for the tested SQL functions and operators.

  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php
🔇 Additional comments (40)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (2)

7-7: Added necessary import for exception handling.

The import statement for the InvalidArgumentForVariadicFunctionException class is correctly added to support the new validation logic.


24-30: Implementation of argument validation for the LEAST function looks good.

The validation logic correctly enforces that the LEAST function must have at least 2 arguments to make a meaningful comparison, which aligns with PostgreSQL's implementation. The exception is thrown with an appropriate message using the static factory method.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php (2)

7-7: Added necessary import for exception handling.

The import statement for the InvalidArgumentForVariadicFunctionException class is correctly added to support the new validation logic.


23-29: Implementation of argument validation for the ROW function looks good.

The validation logic correctly ensures that the ROW constructor has at least 1 argument, which aligns with PostgreSQL's requirements. The exception is thrown with an appropriate message using the static factory method.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php (3)

7-7: Added necessary import for exception handling.

The import statement for the InvalidArgumentForVariadicFunctionException class is correctly added to support the new validation logic.


13-15: Updated documentation annotations.

The version annotation is simplified from '2.9.0' to '2.9', and an author annotation is added, maintaining consistent documentation across the codebase.


24-30: Implementation of argument validation for the JSON_BUILD_OBJECT function looks good.

The validation logic correctly enforces two important conditions:

  1. The function must have at least one key-value pair (argument count > 0)
  2. It must have an even number of arguments (each key must have a corresponding value)

This aligns with PostgreSQL's implementation, and the exception is thrown with an appropriate message.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php (3)

7-7: Added necessary import for exception handling.

The import statement for the InvalidArgumentForVariadicFunctionException class is correctly added to support the new validation logic.


13-15: Updated documentation annotations.

The version annotation is simplified from '2.9.0' to '2.9', and an author annotation is added, maintaining consistent documentation across the codebase.


24-30: Implementation of argument validation for the JSONB_BUILD_OBJECT function looks good.

The validation logic correctly enforces that the function must have an even number of arguments greater than zero, ensuring each key has a corresponding value. This is consistent with PostgreSQL's requirements and the implementation in the JSON_BUILD_OBJECT function.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1)

103-103: The private to protected visibility change is appropriate.

Changing the visibility of assertSqlFromDql from private to protected allows subclasses to access this method directly, which enables the new validation tests to be implemented in subclasses. This is a reasonable design change that improves code reusability within the test hierarchy.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (2)

7-8: Import statement appropriately added.

The import statement for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation feature.


24-30: Validation implementation aligns with PostgreSQL requirements.

The validateArguments method correctly ensures that the greatest function has at least 2 arguments, which aligns with PostgreSQL's requirement for the GREATEST function. According to the PostgreSQL documentation, GREATEST requires at least two arguments.

The implementation throws the appropriate exception with a clear message when insufficient arguments are provided.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (2)

8-8: Import statement appropriately added.

The import statement for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation test.


38-47: Test properly validates excessive arguments behavior.

This test correctly verifies that the TO_TSVECTOR function throws an InvalidArgumentForVariadicFunctionException when too many arguments are provided. According to the PostgreSQL documentation, to_tsvector should accept at most 2 arguments (configuration name and document text).

The test constructs a DQL with 3 arguments, which should trigger the exception as expected. The assertion is appropriately testing this behavior by expecting the exception to be thrown.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php (2)

8-8: Import statement appropriately added.

The import statement for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation test.


36-45: Test properly validates excessive arguments behavior.

This test correctly verifies that the UNACCENT function throws an InvalidArgumentForVariadicFunctionException when too many arguments are provided. According to the PostgreSQL documentation, unaccent should accept at most 2 arguments (optional dictionary name and input text).

The test constructs a DQL with 3 arguments, which exceeds the limit and should trigger the exception as expected. The assertion is appropriately testing this behavior by expecting the exception to be thrown.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (2)

7-7: Appropriate import added.

The import for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation logic.


25-31: Well-implemented validation logic.

The validation ensures the unaccent function receives the correct number of arguments (1 or 2) as specified in PostgreSQL documentation. The exception message clearly indicates the valid range of arguments.

This implementation correctly handles both function signatures:

  • unaccent(text) - with 1 argument
  • unaccent(regdictionary, text) - with 2 arguments
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (2)

9-9: Appropriate import added.

The import for InvalidArgumentForVariadicFunctionException is correctly added to support the new test case.


40-49: Good test coverage for invalid argument case.

This test correctly verifies that the GREATEST function throws an exception when provided with only one argument, which aligns with PostgreSQL's requirement that GREATEST needs at least two arguments to make a comparison.

The test setup is accurate - it expects the appropriate exception and constructs a DQL statement with a single argument that should trigger the validation failure.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (2)

7-7: Appropriate import added.

The import for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation logic.


24-30: Well-implemented validation logic.

The validation ensures the to_tsquery function receives the correct number of arguments (1 or 2) as specified in PostgreSQL documentation. The exception message clearly indicates the valid range of arguments.

This implementation correctly handles both function signatures:

  • to_tsquery(text) - Converts query text to a tsquery
  • to_tsquery(regconfig, text) - Converts query text to a tsquery using the specified configuration
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (2)

7-7: Appropriate import added.

The import for InvalidArgumentForVariadicFunctionException is correctly added to support the new validation logic.


26-32: Well-implemented validation logic.

The validation ensures the to_tsvector function receives the correct number of arguments (1 or 2) as specified in PostgreSQL documentation. The exception message clearly indicates the valid range of arguments.

This implementation correctly handles both function signatures:

  • to_tsvector(text) - Converts document text to a tsvector
  • to_tsvector(regconfig, text) - Converts document text to a tsvector using the specified configuration
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (2)

9-9: Good import addition for the new exception type.

The added import for InvalidArgumentForVariadicFunctionException is necessary for the new test case.


40-49:

✅ Verification successful

Well-structured test case for validating argument count.

This test correctly verifies that the LEAST function rejects a single argument, which aligns with PostgreSQL's specification requiring at least two arguments to make a meaningful comparison. According to PostgreSQL documentation, LEAST selects the smallest value from a list of expressions and needs multiple values to compare.

The test method follows best practices by:

  1. Clearly stating its purpose through the method name
  2. Setting appropriate expectations before executing the code
  3. Providing a minimal but complete test case

🌐 Web query:

PostgreSQL LEAST function syntax and arguments

💡 Result:

PostgreSQL's LEAST() function is used to determine the smallest value from a list of expressions. Here's a comprehensive breakdown of its functionality:

Syntax and Basic Usage

The syntax is:

LEAST(value1, value2, ..., valueN)
  • Accepts 1 or more arguments of comparable data types [1][2][7]
  • Returns the lexicographically smallest string or numerically smallest value [1][2]

Examples:

SELECT LEAST(5, 2, 9);  -- Returns 2 [1]
SELECT LEAST('Cat', 'Aardvark');  -- Returns 'Aardvark' [1]
SELECT LEAST(date '2030-01-01', date '2025-03-15');  -- Returns 2025-03-15 [1][7]

Key Characteristics

  1. Data Type Requirements:

    • All arguments must be convertible to a common data type [1][3]
    • Mixing incompatible types (e.g., integer and text) causes errors:
      SELECT LEAST(5, 'Two');  -- ERROR [1]
  2. NULL Handling:

    • Ignores NULL values unless all arguments are NULL [1][3]
    SELECT LEAST(5, NULL, 9);  -- Returns 5 [1]
    SELECT LEAST(NULL, NULL);  -- Returns NULL [1][3]
  3. Edge Cases:

    • Single argument returns itself:
      SELECT LEAST(100);  -- Returns 100 [1]
    • Empty argument list causes error:
      SELECT LEAST();  -- ERROR [1]

Practical Applications

  • Comparing columns in table data:
    SELECT LEAST(salary, bonus) FROM employees; [2][7]
  • Financial calculations:
    SELECT LEAST(base_price, discounted_price, seasonal_price) AS final_price [7]
  • Date comparisons:
    SELECT LEAST(start_date, promotion_date) AS effective_date [7]

This function is particularly useful when you need to compare multiple values of the same type programmatically, with proper handling of NULL values and type safety enforced by PostgreSQL [3][8].

Citations:


Clarification on LEAST() Argument Requirements

The test case in tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php is well structured and accurately verifies that our custom LEAST() function throws an exception when only a single argument is provided. This behavior is intentional—our implementation enforces the need for at least two arguments to ensure a meaningful comparison.

Note: Although PostgreSQL’s native LEAST() accepts a single argument (returning that argument), our design choice here is to require multiple arguments. It might be helpful to add a clarifying comment in the code explaining this discrepancy to aid future maintainers.

  • The test method clearly documents its purpose with a descriptive name.
  • Exception expectation is set using expectException(InvalidArgumentForVariadicFunctionException::class).
  • The test helps ensure that users of our DQL function adhere to our intended usage pattern.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (2)

8-8: Good import addition for the new exception type.

The added import for InvalidArgumentForVariadicFunctionException is necessary for the new test case.


38-47:

✅ Verification successful

Well-implemented test for argument validation.

This test correctly verifies that the TO_TSQUERY function rejects more than two arguments, which aligns with PostgreSQL's specification. According to the PostgreSQL documentation, TO_TSQUERY accepts either one argument (query text) or two arguments (configuration name and query text).

The test follows a consistent pattern with the other modifications in this PR:

  1. Expecting the appropriate exception type
  2. Attempting to use the function with an invalid number of arguments
  3. Asserting that the expected behavior occurs

🌐 Web query:

PostgreSQL TO_TSQUERY function syntax and arguments

💡 Result:

PostgreSQL's to_tsquery function converts search terms into a normalized tsquery structure for advanced full-text search operations. Here's a detailed breakdown:


Syntax and Parameters

to_tsquery([ config regconfig, ] query_text text) → tsquery
  • config (optional): Specifies the text search configuration (e.g., 'english'). If omitted, uses default_text_search_config.
  • query_text: The search string, which must include valid operators (&, |, !, <->).

Key Functionality

  1. Normalization:

    • Converts input terms to lexemes (base words) using dictionaries and removes stop words[1][5][9].
      Example:
      SELECT to_tsquery('english', 'The & Fat & Rats');'fat' & 'rat'[1][5][19].
  2. Operator Support:

    • & (AND): Terms must all appear.
      'quick & brown' matches documents containing both lexemes[18][19].
    • | (OR): At least one term must appear.
      'cat | dog' matches cat, dog, or both[18].
    • ! (NOT): Excludes terms.
      '!mouse' excludes documents containing mouse[19].
    • <-> (FOLLOWED BY): Terms must appear in sequence.
      'quick <-> brown' matches the phrase "quick brown"[12][18].
  3. Prefix Matching:

    • Use :* to match prefixes.
      SELECT to_tsquery('super:*');'super':* (matches "supernova", "superman")[11][12].

Examples

  1. Basic Usage:

    SELECT to_tsquery('english', 'Jumped & Over & Lazy'); 
    -- Output: 'jump' & 'lazi'

    Stop words like "over" are excluded, and "jumped" is stemmed to "jump"[7][9].

  2. Phrase Search:

    SELECT phraseto_tsquery('casual wear'); 
    -- Output: 'casual' <-> 'wear'

    Matches exact phrases like "casual wear"[2][12].

  3. Weighted Queries:

    SELECT to_tsquery('fat:AB & rat'); 
    -- Output: 'fat':AB & 'rat'

    Restricts matches to lexemes with specific weights[12][19].


Comparison with Similar Functions

Function Behavior Example Input Output
to_tsquery Requires explicit operators; stricter syntax. 'quick & brown' 'quick' & 'brown'
plainto_tsquery Inserts & between terms automatically; ignores operators. 'quick brown' 'quick' & 'brown'
phraseto_tsquery Uses <-> for phrase matching; accounts for stop words. 'quick brown' 'quick' <-> 'brown'
websearch_to_tsquery Converts web-style queries (e.g., "exact phrase" -exclude) to tsquery. '"fast car" -slow' 'fast' <-> 'car' & !'slow'

Best Practices

  • Use to_tsvector and to_tsquery together with the @@ match operator[1][2][6]:
    SELECT * FROM products 
    WHERE to_tsvector(product_name) @@ to_tsquery('MOUSE');
  • For non-English text, specify the config parameter to use language-specific dictionaries[8][9][12].
  • Avoid unformatted input in to_tsquery—use plainto_tsquery or websearch_to_tsquery for free-text queries[5][12][18].

By leveraging these features, to_tsquery enables precise, efficient full-text searches in PostgreSQL, filtering out irrelevant matches and handling linguistic nuances[1][5][12].

Citations:


Action: Test correctly validates TO_TSQUERY argument count

The test in tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (lines 38-47) is well implemented. It correctly expects an exception when invoking TO_TSQUERY with three arguments, which violates PostgreSQL’s syntactical requirement of either one or two arguments for this function. This aligns with the official PostgreSQL documentation where the function signature is defined as:

to_tsquery([ config regconfig, ] query_text text)
  • The test sets up a DQL statement that provides three arguments: a configuration, a query column, and an extra value.
  • It expects an InvalidArgumentForVariadicFunctionException to be thrown, thereby confirming that the additional argument is correctly being rejected.
  • The pattern used here is consistent with the approach adopted in other parts of this PR.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (2)

8-8: Good import addition for the new exception type.

The added import for InvalidArgumentForVariadicFunctionException is necessary for the new test case.


38-47:

✅ Verification successful

Excellent validation test for key-value pairing requirement.

This test correctly verifies that the JSON_BUILD_OBJECT function rejects an odd number of arguments, which aligns with PostgreSQL's specification. According to PostgreSQL documentation, json_build_object() builds a JSON object from key-value pairs, requiring an even number of arguments (each key needs a corresponding value).

The test method:

  1. Sets up the expectation for the appropriate exception
  2. Constructs a DQL statement with an odd number of arguments (3: 'key1', e.object1, 'key2')
  3. Attempts to execute it, which should trigger the validation

🌐 Web query:

PostgreSQL json_build_object function syntax and arguments

💡 Result:

The PostgreSQL json_build_object() function dynamically constructs JSON objects from key-value pairs. Here's a breakdown of its syntax, arguments, and usage:

Syntax

json_build_object(VARIADIC any_value) → JSON
  • Parameters:
    A variadic list of alternating keys (converted to text) and values (converted to JSON via to_jsonb()).
    • Must contain an even number of arguments (key-value pairs).
    • Supports all PostgreSQL data types for values.

Key Characteristics

  • Key handling: Non-text keys (e.g., numbers, booleans) are automatically coerced to text strings (e.g., 1 becomes "1", true becomes "true").
  • Value conversion: Values undergo JSON conversion equivalent to to_jsonb(), preserving data types like arrays, composites, and nested structures.

Examples

Basic Usage

SELECT json_build_object('id', 100, 'active', TRUE, 'data', row(5, 'test'));
-- Returns: {"id" : 100, "active" : true, "data" : {"f1":5,"f2":"test"}} [1][3]

Complex Structures

SELECT json_build_object(1, ARRAY[2,3], 'nested', json_build_object('a', false));
-- Returns: {"1" : [2,3], "nested" : {"a": false}} [6][7]

Error Case (Odd Arguments)

SELECT json_build_object('key1', 1, 'key2'); -- ERROR: argument list must have even number of elements [1][3]

Related Functions

  • jsonb_build_object(): Identical syntax but returns a JSONB type for binary storage and indexed operations [5][7].
  • json_object(): Constructs objects from text arrays instead of variadic arguments [2][13].

For nested JSON structures or combining multiple objects, use the || operator with jsonb types [16]. When working with table data, combine with json_agg for array aggregation [12][14].

Citations:


Approved: Exception Handling Test for JSON_BUILD_OBJECT is Correct

The test in tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (lines 38-47) correctly verifies that the JSON_BUILD_OBJECT function rejects an odd number of arguments by triggering the InvalidArgumentForVariadicFunctionException. This behavior aligns with PostgreSQL's documented requirement that json_build_object() must receive an even number of arguments (each key must have a corresponding value).

Key points:

  • The test sets up an exception expectation and executes a DQL statement with an odd number of arguments.
  • PostgreSQL’s json_build_object() function requires key-value pairs, and passing an odd number of arguments—similar to the provided example—results in an error.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php (2)

8-8: Good import addition for the new exception type.

The added import for InvalidArgumentForVariadicFunctionException is necessary for the new test case.


38-47:

✅ Verification successful

Well-implemented validation test for key-value pairing requirement.

This test correctly verifies that the JSONB_BUILD_OBJECT function rejects an odd number of arguments, which aligns with PostgreSQL's specification. According to PostgreSQL documentation, jsonb_build_object() builds a JSONB object from key-value pairs, requiring an even number of arguments (each key needs a corresponding value).

The test method:

  1. Sets up the expectation for the appropriate exception
  2. Constructs a DQL statement with an odd number of arguments (3: 'key1', e.object1, 'key2')
  3. Attempts to execute it, which should trigger the validation

🌐 Web query:

PostgreSQL jsonb_build_object function syntax and arguments

💡 Result:

The jsonb_build_object() function constructs JSONB objects from alternating key-value pairs. Here's a detailed breakdown of its syntax and arguments:

Syntax

jsonb_build_object(VARIADIC any_value) → JSONB
  • Parameters: A variadic list (any_value) containing alternating keys and values.
  • Key Rules:
    • Keys are coerced to text (e.g., true becomes "true", 1 becomes "1").
    • Values are converted to JSONB via to_jsonb() (e.g., rows become nested JSON objects, arrays become JSON arrays)[1][3][7][8].
  • Return Value: A JSONB object with key-value pairs derived from the input arguments[1][8].

Key Requirements

  1. Even Number of Arguments: The function requires an even number of parameters (keys and values in pairs). An odd count triggers an error:
    ERROR: argument list must have even number of elements[1][4][8].
  2. Flexible Data Types: Supports any SQL data type for both keys and values (e.g., integers, strings, booleans, arrays, or composite types like rows)[1][9][10].

Examples

  1. Basic Usage:

    SELECT jsonb_build_object(1, 'a', true, row(2, 'b', false));

    Returns:

    {"1": "a", "true": {"f1": 2, "f2": "b", "f3": false}}[1][3][7]

    Here, the row (2, 'b', false) becomes a nested JSON object.

  2. Nested Structures:

    SELECT jsonb_build_object('phone_numbers', 
        jsonb_build_object('home', '111-111-1111', 'cell', '222-222-2222'));

    Returns:

    {"phone_numbers": {"home": "111-111-1111", "cell": "222-222-2222"}}[9]

Common Errors

  • Non-Scalar Keys: Using arrays or composite types as keys raises an error:
    SELECT jsonb_build_object('{1,2}'::int[], '{3,4}'::int[]);
    -- ERROR: key value must be scalar[3][5][10].
  • Variadic Array Handling: When passing arrays via VARIADIC, ensure proper dimensionality. For example:
    SELECT jsonb_build_object(VARIADIC ARRAY['foo', '1', '2', 'bar']);
    Returns:
    {"2": "bar", "foo": 1}[3][12]

Comparison to Similar Functions

  • json_build_object(): Same syntax but returns JSON (text) instead of JSONB (binary)[1][7].
  • jsonb_object(): Accepts text arrays instead of variadic arguments for key-value pairs[11].

Use Cases

  • Dynamic JSON Construction: Useful for building nested JSON structures directly in SQL queries[6][13].
  • Performance: Prefer jsonb_build_object over application-side JSON generation for efficiency, especially with complex data[6][9].

By adhering to these rules, jsonb_build_object() provides a flexible way to generate JSONB objects programmatically in PostgreSQL.

Citations:


Validation Test Correctly Enforces Even-Arguments Requirement

The updated review confirms that the test in
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php
(lines 38–47) correctly ensures that an odd number of arguments for JSONB_BUILD_OBJECT (which requires key-value pairs) triggers the expected InvalidArgumentForVariadicFunctionException. This aligns with PostgreSQL’s documented requirement for an even number of parameters.

  • The test sets the exception expectation.
  • It builds a DQL using three parameters ('key1', e.object1, 'key2'), mimicking an odd-count scenario.
  • Execution of the DQL should correctly validate the pairing rule as per PostgreSQL documentation.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (2)

41-43: Implementation of argument validation looks good!

Adding the validation call after parsing all arguments ensures that the validation logic will execute at the right time - after all nodes have been collected, but before further processing.


54-61: Well-designed abstract method for argument validation

The abstract method clearly defines the contract that all subclasses must implement. The documentation provides a clear explanation of the method's purpose and parameters.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (5)

7-8: Good choice of exception inheritance

Extending \InvalidArgumentException is appropriate for this use case since it properly represents the error condition of receiving invalid function arguments.


9-17: Well-implemented factory method for exact count validation

The exactCount method creates a properly formatted error message with correct singular/plural handling.


19-27: Consistent implementation of the atLeast factory method

This method follows the same pattern as exactCount with appropriate message formatting and grammatical handling.


29-37: Clean implementation of the between factory method

The method provides a clear error message for when arguments must be within a specific range.


39-45: Useful factory method for even number validation

This method addresses a specific validation requirement in functions like JsonBuildObject where key-value pairs require an even number of arguments.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@martin-georgiev martin-georgiev merged commit 019f84d into main Mar 13, 2025
10 of 11 checks passed
@martin-georgiev martin-georgiev deleted the validate-arguments-for-variadics branch March 13, 2025 11:56
@github-actions github-actions bot mentioned this pull request Mar 12, 2025
@github-actions github-actions bot mentioned this pull request Mar 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants