Skip to content

Conversation

@martin-georgiev
Copy link
Owner

@martin-georgiev martin-georgiev commented Apr 17, 2025

Summary by CodeRabbit

  • Refactor

    • Streamlined function argument validation and function name handling for many query functions by introducing explicit methods for function names and argument count constraints.
    • Simplified and unified error messages for invalid arguments across multiple functions.
    • Improved consistency in function metadata and validation logic.
    • Updated test classes to extend a new base variadic function test case, standardizing fixture creation and test structure.
    • Added a new test variadic function class for testing purposes.
    • Enhanced argument count validation with centralized enforcement and improved parsing error handling.
  • Bug Fixes

    • Enhanced error handling with clearer, more consistent exception messages for invalid argument scenarios.
  • Tests

    • Updated tests to reflect new exception message formats and improved error messaging.
    • Added fixture creation methods in tests for consistent setup.
    • Adjusted test inheritance to use a base variadic function test class.

@coveralls
Copy link

coveralls commented Apr 17, 2025

Coverage Status

coverage: 94.387% (-0.7%) from 95.043%
when pulling e53af4a on regex
into 6a5ba9e on main.

@coderabbitai
Copy link

coderabbitai bot commented Apr 17, 2025

Walkthrough

This set of changes refactors the implementation of custom Doctrine AST function classes to centralize and standardize argument count validation and function metadata. Instead of inline validation and prototype string construction, each function class now overrides methods to specify its name, minimum, and maximum argument counts. The base classes (BaseVariadicFunction and BaseComparisonFunction) provide concrete mechanisms for argument validation and prototype construction. Exception handling for invalid argument counts is improved with more specific messages and a new static factory method. Associated tests are updated to expect revised exception messages and, in some cases, to adapt to new base class structures.

Changes

File(s) Change Summary
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php,
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php
Refactored base classes to introduce or implement getFunctionName(), getMinArgumentCount(), getMaxArgumentCount(), and centralized argument validation logic.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php,
Row.php
Simplified by removing explicit argument validation and prototype construction; now override methods for function name and argument count.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php,
DateAdd.php,
DateSubtract.php,
JsonBuildObject.php,
JsonbBuildObject.php,
JsonbInsert.php,
JsonbPathExists.php,
JsonbPathMatch.php,
JsonbPathQuery.php,
JsonbPathQueryArray.php,
JsonbPathQueryFirst.php,
JsonbSet.php,
RowToJson.php
Refactored to remove manual argument count checks and prototype setting; now implement standardized methods for function name and argument count, delegating validation to base classes. Exception messages now use dynamic function names.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php,
Least.php,
ToTsquery.php,
ToTsvector.php,
Unaccent.php
Simplified to only provide function name via getFunctionName(), removing explicit validation and prototype customization.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php Added a static factory method for creating exceptions with detailed messages about unsupported argument combinations.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJsonTest.php,
DateAddTest.php,
DateSubtractTest.php,
JsonbInsertTest.php,
JsonbPathExistsTest.php,
JsonbPathMatchTest.php,
JsonbPathQueryArrayTest.php,
JsonbPathQueryFirstTest.php,
JsonbPathQueryTest.php,
JsonbSetTest.php,
RowToJsonTest.php
Updated expected exception messages in tests to reflect new function name casing and revised wording.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php,
LeastTest.php
Changed test base class from BaseComparisonFunctionTestCase to BaseVariadicFunctionTestCase and updated fixture creation accordingly.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php Renamed from BaseComparisonFunctionTestCase to BaseVariadicFunctionTestCase; updated abstract method signature and test method to reflect new base class and method names.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrTest.php,
JsonBuildObjectTest.php,
JsonbBuildObjectTest.php,
RowTest.php,
ToTsqueryTest.php,
ToTsvectorTest.php,
UnaccentTest.php
Changed test classes to extend BaseVariadicFunctionTestCase and added createFixture() methods returning instances of their respective function classes with proper names.
fixtures/MartinGeorgiev/Doctrine/Function/TestVariadicFunction.php Added a new test variadic function class implementing function name, argument count bounds, and node mapping pattern for testing purposes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DoctrineQuery
    participant ASTFunctionClass
    participant BaseFunction
    participant Exception

    User->>DoctrineQuery: Executes DQL with custom function
    DoctrineQuery->>ASTFunctionClass: Parses function call
    ASTFunctionClass->>BaseFunction: Calls getFunctionName(), getMinArgumentCount(), getMaxArgumentCount()
    BaseFunction->>ASTFunctionClass: Validates argument count
    alt Invalid argument count
        BaseFunction->>Exception: Throws InvalidArgumentForVariadicFunctionException
    else Valid arguments
        ASTFunctionClass->>DoctrineQuery: Returns parsed function node
    end
Loading

Possibly related PRs

Suggested labels

enhancement, dx

Poem

Refactored code, so neat and spry,
Functions now tell Doctrine why
Their names and counts, so clear and bright,
No more manual checks at night.
With base class help, exceptions fly—
🐇 Cheers to code that hops so high!
(And all the tests still pass—oh my!)

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 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 2aa63ee and b0c19e7.

📒 Files selected for processing (1)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#350
File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php:78-88
Timestamp: 2025-04-18T10:33:52.403Z
Learning: In SQL generation code for the postgresql-for-doctrine library, it's better to fail explicitly with clear errors than to use fallback behavior that makes assumptions about node types, as this could lead to functional bugs or security issues.
🧬 Code Graph Analysis (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (6)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (2)
  • customizeFunction (43-46)
  • setFunctionPrototype (57-60)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (1)
  • getFunctionName (17-20)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (1)
  • getFunctionName (17-20)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php (3)
  • getNodeMappingPattern (14-17)
  • getMinArgumentCount (19-22)
  • getMaxArgumentCount (24-27)
src/MartinGeorgiev/Utils/DoctrineLexer.php (2)
  • DoctrineLexer (12-58)
  • getLookaheadType (29-38)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (4)
  • InvalidArgumentForVariadicFunctionException (7-56)
  • atLeast (19-27)
  • between (29-37)
  • unsupportedCombination (47-55)
🔇 Additional comments (6)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (6)

22-26: Function prototype standardization looks good.

The customizeFunction() implementation properly standardizes SQL function generation by creating a consistent function prototype pattern using the abstract getFunctionName() method. Keeping this method non-final is correct, as certain implementations (like Arr) need to override it to use different SQL syntax patterns such as square brackets.


27-27: Clean function name abstraction.

Adding the abstract getFunctionName() method is a good design choice, as it separates the function name from its implementation and ensures consistent naming across the codebase.


34-36: Strong argument count constraints are a welcome addition.

The abstract methods getMinArgumentCount() and getMaxArgumentCount() properly enforce that all concrete implementations explicitly declare their argument count constraints. This improves clarity, maintainability, and makes validation more robust.


78-96: Robust bounds checking prevents potential security issues.

The added validation logic properly addresses potential out-of-bounds access issues. By checking if the expected node index exists in the mapping array using array_key_exists() and throwing specific exceptions with clear error messages, the code follows the principle of failing explicitly rather than making potentially unsafe assumptions about node types.

This approach is especially important in SQL generation code where type confusion could lead to security vulnerabilities.


104-106: Good use of final validation.

Adding a call to validateArguments() after parsing all nodes serves as an important safety net that ensures all argument count constraints are enforced consistently. This also allows subclasses to implement additional validation rules if needed.


108-124: Well-structured centralized validation.

The new validateArguments() method effectively centralizes argument validation logic, which eliminates code duplication and ensures consistent error handling across implementations. The method properly uses the abstract min/max methods and throws appropriate exceptions with descriptive messages.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (1)

38-47: 💡 Verification agent

❓ Verification inconclusive

Test coverage for GREATEST function appears sufficient.

The test covers key use cases for PostgreSQL's GREATEST function including multiple column values, mixed expressions, and error handling for insufficient arguments.

According to PostgreSQL documentation, GREATEST selects the largest value from a list of any comparable expressions, which these tests demonstrate well.


🏁 Script executed:

#!/bin/bash
# Check if the tests cover NULL handling for GREATEST as mentioned in PostgreSQL docs
rg -A 3 "NULL.*GREATEST|GREATEST.*NULL" --type php tests/

Length of output: 59


Add NULL‐handling test for GREATEST

The existing tests cover multiple values, mixed expressions, and insufficient‐argument errors, but they don’t verify how GREATEST behaves when any input is NULL. According to PostgreSQL, GREATEST should return NULL if any argument is NULL.

Please add a test in tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php, for example:

  • A new method public function returns_null_when_any_argument_is_null(): void
  • Asserting that SELECT GREATEST(e.integer1, NULL) produces a NULL result or the appropriate SQL.
🧹 Nitpick comments (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php (1)

32-35: Note case difference between documentation and implementation

The documentation comment mentions "DATE_ADD()" (uppercase), but the implementation returns "date_add" (lowercase). This might be intentional if PostgreSQL functions are case-insensitive, but it's worth ensuring consistency in documentation.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5ba9e and b3678c4.

📒 Files selected for processing (37)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (1 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/JsonbInsert.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJsonTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAddTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtractTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsertTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJsonTest.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*.php`: Use the PostgreSQL official documentation to verify that tests include comprehensive use cases and example SQL que...

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/RowToJsonTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJsonTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsertTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAddTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtractTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php
🧠 Learnings (3)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
🧬 Code Graph Analysis (8)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (1)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1)
  • TestCase (15-120)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (1)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1)
  • TestCase (15-120)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (16)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php (1)
  • getFunctionName (22-25)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php (1)
  • getFunctionName (33-36)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php (1)
  • getFunctionName (32-35)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php (1)
  • getFunctionName (32-35)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (1)
  • getFunctionName (17-20)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php (1)
  • getFunctionName (25-28)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php (1)
  • getFunctionName (33-36)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php (1)
  • getFunctionName (25-28)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
  • getFunctionName (27-27)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (1)
  • getFunctionName (31-34)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php (1)
  • getFunctionName (37-40)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php (1)
  • getFunctionName (19-22)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (1)
  • getFunctionName (22-25)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php (1)
  • getFunctionName (32-35)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (1)
  • getFunctionName (22-25)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (1)
  • getFunctionName (21-24)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4)
  • getFunctionName (27-27)
  • getMinArgumentCount (34-34)
  • getMaxArgumentCount (36-36)
  • validateArguments (101-114)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php (4)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
  • validateArguments (47-55)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
  • validateBoolean (25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4)
  • getFunctionName (27-27)
  • getMinArgumentCount (34-34)
  • getMaxArgumentCount (36-36)
  • validateArguments (101-114)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (4)
  • getFunctionName (33-36)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
  • validateArguments (48-56)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
  • validateBoolean (25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (7)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4)
  • getFunctionName (27-27)
  • getMinArgumentCount (34-34)
  • getMaxArgumentCount (36-36)
  • validateArguments (101-114)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (4)
  • getFunctionName (31-34)
  • getMinArgumentCount (36-39)
  • getMaxArgumentCount (41-44)
  • validateArguments (46-54)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (4)
  • getFunctionName (33-36)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
  • validateArguments (48-56)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (4)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
  • validateArguments (47-55)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (4)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
  • validateArguments (47-55)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php (4)
  • getFunctionName (37-40)
  • getMinArgumentCount (42-45)
  • getMaxArgumentCount (47-50)
  • validateArguments (52-60)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
  • validateBoolean (25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (12)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php (3)
  • getFunctionName (33-36)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php (3)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php (3)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php (1)
  • getFunctionName (17-20)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php (3)
  • getFunctionName (33-36)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (3)
  • getFunctionName (27-27)
  • getMinArgumentCount (34-34)
  • getMaxArgumentCount (36-36)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (1)
  • getFunctionName (17-20)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php (3)
  • getFunctionName (37-40)
  • getMinArgumentCount (42-45)
  • getMaxArgumentCount (47-50)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (3)
  • getFunctionName (22-25)
  • getMinArgumentCount (27-30)
  • getMaxArgumentCount (32-35)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php (3)
  • getFunctionName (32-35)
  • getMinArgumentCount (37-40)
  • getMaxArgumentCount (42-45)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (3)
  • getFunctionName (22-25)
  • getMinArgumentCount (27-30)
  • getMaxArgumentCount (32-35)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (2)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php (4)
  • getFunctionName (33-36)
  • getMinArgumentCount (38-41)
  • getMaxArgumentCount (43-46)
  • validateArguments (48-56)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4)
  • getFunctionName (27-27)
  • getMinArgumentCount (34-34)
  • getMaxArgumentCount (36-36)
  • validateArguments (101-114)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
  • validateBoolean (25-37)
⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer latest
  • GitHub Check: PHP 8.4 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.3 + Doctrine ORM 3.0 + Doctrine Lexer latest
  • GitHub Check: PHP 8.3 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.4 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer latest
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.2 + Doctrine ORM latest + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.2 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.1 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer latest
  • GitHub Check: sloth
🔇 Additional comments (92)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)

101-113: Minor duplication – maximum‑argument check happens twice

$foundMoreNodesThanMappingExpected (lines 78‑85) already prevents the parser from ever collecting more than the maximum arguments, while validateArguments() (lines 107‑113) performs the same > $maxArgumentCount check afterwards.
Although harmless, the second check is redundant and can be removed to streamline the hot‑path.

[ suggest_optional_refactor ]

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

40-41: Test expectation updated correctly

The message now matches the new lowercase‑function convention introduced in the refactor. ✅

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

42-43: Test expectation updated correctly

Consistent with the new error‑message format. ✅

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (3)

44-44: Exception message updated to use lowercase function name.

The exception message has been standardized to use the lowercase version of the function name (jsonb_path_query instead of JSONB_PATH_QUERY), which is consistent with PostgreSQL's own documentation and error messages.


53-53: Exception message updated for argument count validation.

The exception message now uses "at least 2 arguments" instead of a fixed range, which better reflects the PostgreSQL function signature. According to PostgreSQL documentation, jsonb_path_query accepts 2-4 arguments: jsonb_path_query(target jsonb, path jsonpath [, vars jsonb [, silent boolean]]).


23-28: Test cases cover all variations of the PostgreSQL function.

These test cases provide excellent coverage of all possible argument variations for the jsonb_path_query function, including the basic form, with vars argument, and with both vars and silent arguments. This aligns well with the PostgreSQL documentation examples.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php (3)

44-44: Exception message updated to use lowercase function name.

The exception message has been standardized to use the lowercase version of the function name (jsonb_path_match instead of JSONB_PATH_MATCH), which is consistent with PostgreSQL's own documentation and error messages.


53-53: Exception message updated for argument count validation.

The exception message now uses "at least 2 arguments" instead of a fixed range, which better reflects the PostgreSQL function signature. According to PostgreSQL documentation, jsonb_path_match accepts 2-4 arguments: jsonb_path_match(target jsonb, path jsonpath [, vars jsonb [, silent boolean]]).


24-27: Test cases demonstrate various path matching patterns.

These test cases provide excellent coverage of different jsonb path matching patterns, including exists conditions, simple conditions, and various combinations with the optional parameters. This aligns well with real-world PostgreSQL usage examples.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetTest.php (3)

40-40: Exception message updated to use lowercase function name.

The exception message has been standardized to use the lowercase version of the function name (jsonb_set instead of JSONB_SET), which is consistent with PostgreSQL's own documentation and error messages.


49-49: Exception message updated for argument count validation.

The exception message now uses "at least 3 arguments" instead of a fixed range, which better reflects the PostgreSQL function signature. According to PostgreSQL documentation, jsonb_set requires at least 3 arguments: jsonb_set(target jsonb, path text[], new_value jsonb [, create_missing boolean]).


24-25: Test cases cover both basic and optional parameter scenarios.

The test cases demonstrate both the basic usage with 3 arguments and the extended usage with the optional create-if-missing parameter. This covers the full spectrum of functionality for this PostgreSQL function.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsertTest.php (3)

40-40: Exception message updated to use lowercase function name.

The exception message has been standardized to use the lowercase version of the function name (jsonb_insert instead of JSONB_INSERT), which is consistent with PostgreSQL's own documentation and error messages.


49-49: Exception message updated for argument count validation.

The exception message now uses "at least 3 arguments" instead of a fixed range, which better reflects the PostgreSQL function signature. According to PostgreSQL documentation, jsonb_insert requires at least 3 arguments: jsonb_insert(target jsonb, path text[], new_value jsonb [, insert_after boolean]).


24-25: Test cases cover both basic and optional parameter scenarios.

The test cases demonstrate both the basic usage with 3 arguments and the extended usage with the optional insert_after parameter. This provides comprehensive coverage for the PostgreSQL function's capabilities.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (3)

44-44: Test expectation updated to match new error message format

The exception message now references the lowercase function name (jsonb_path_query_first instead of JSONB_PATH_QUERY_FIRST), which aligns with the refactoring to standardize error handling and use consistent function naming across the codebase.


53-53: Test expectation updated to match new error message format

The exception message now states "requires at least 2 arguments" rather than "between 2 and 4 arguments", which aligns with the refactoring to centralize argument validation logic and improve error clarity.


21-39: Confirm test cases against PostgreSQL documentation

The test cases cover the essential use cases for jsonb_path_query_first:

  1. Basic path extraction with a condition
  2. Simple path access
  3. With vars argument
  4. With both vars and silent parameters

According to the PostgreSQL documentation, this comprehensive coverage addresses the main functionality of the jsonb_path_query_first function, which retrieves the first item from the JSON path query.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (3)

44-44: Test expectation updated to match new error message format

The exception message now references the lowercase function name (jsonb_path_query_array instead of JSONB_PATH_QUERY_ARRAY), which aligns with the refactoring to standardize error handling and use consistent function naming across the codebase.


53-53: Test expectation updated to match new error message format

The exception message now states "requires at least 2 arguments" rather than "between 2 and 4 arguments", which aligns with the refactoring to centralize argument validation logic and improve error clarity.


21-39: Confirm test cases against PostgreSQL documentation

The test cases cover the essential use cases for jsonb_path_query_array:

  1. Array extraction with a condition
  2. Path-based array extraction
  3. With vars argument
  4. With both vars and silent parameters

According to the PostgreSQL documentation, this comprehensive coverage addresses the main functionality of the jsonb_path_query_array function, which retrieves all JSON items from a path query and returns them as a JSON array.

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

11-11: Class inheritance updated to reflect refactored architecture

The test class now extends TestCase directly instead of BaseComparisonFunctionTestCase, which aligns with the refactoring to centralize validation logic in base classes. This change reflects the simplified structure where function classes no longer need complex inheritance hierarchies for argument validation.


20-36: Confirm test cases against PostgreSQL documentation

The test cases cover the essential use cases for the PostgreSQL LEAST function:

  1. Comparing multiple column values
  2. Comparing a column value with an expression
  3. Comparing multiple expressions

According to the PostgreSQL documentation, the LEAST function returns the smallest value from a list of any number of expressions, which these test cases appropriately verify.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtractTest.php (3)

42-42: Test expectation updated to match new error message format

The exception message now references the lowercase function name (date_subtract instead of DATE_SUBTRACT), which aligns with the refactoring to standardize error handling and use consistent function naming across the codebase.


51-51: Test expectation updated to match new error message format

The exception message now states "requires at least 2 arguments" rather than "between 2 and 3 arguments", which aligns with the refactoring to centralize argument validation logic and improve error clarity.


21-37: Verify test cases cover essential date_subtract functionality

The test cases cover the main variants of date_subtract usage:

  1. Subtracting days with a timezone
  2. Subtracting hours with a timezone
  3. Subtracting days without a timezone

These cases verify the function's ability to handle different time units and optional timezone handling, providing good coverage of the function's intended functionality.

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

11-11: Base class change is aligned with the centralized validation refactoring.

The change from extending a specific base test class to the more general TestCase class is appropriate since the specialized fixture creation method is no longer needed after the refactoring of the underlying Greatest function class.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php (3)

44-44: Error message updated to use lowercase function name.

This change aligns with the standardization of function names in error messages to lowercase format, which is consistent with the PostgreSQL documentation and makes error messages more uniform across the library.


53-53: Error message wording improved for argument count validation.

The message now states "requires at least 2 arguments" which is more precise than the previous wording, aligning with the centralized validation approach and making the error more helpful to users.


21-39: Test coverage for jsonb_path_exists looks comprehensive.

The tests cover the main use cases described in PostgreSQL documentation for jsonb_path_exists, including path expressions with conditions, nested paths, and the optional vars and silent arguments.

According to PostgreSQL documentation, jsonb_path_exists tests whether a JSON path expression returns any item for the specified JSON value, which these tests verify with various scenarios.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAddTest.php (3)

44-44: Error message updated to use lowercase function name.

This change aligns with the standardization of function names in error messages to lowercase format, consistent with PostgreSQL documentation and providing more uniform error messages across the library.


53-53: Error message wording improved for argument count validation.

The message now states "requires at least 2 arguments" which is more precise than the previous wording, aligning with the centralized validation approach in the base classes.


21-39: Test coverage for date_add appears comprehensive.

The tests cover the main use cases described in PostgreSQL documentation for date_add, including various interval types and the optional timezone parameter. The tests also verify the function's behavior in WHERE clauses.

According to PostgreSQL documentation, date_add adds an interval to a timestamp, which these tests verify with days, hours, and timezone variations.

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

47-55: Good addition of factory method for more specific error messaging.

The new unsupportedCombination factory method enhances error reporting by allowing more specific explanations of why certain argument combinations are not supported, beyond just count validation.

This method follows the consistent pattern of the existing factory methods and provides a clear, formatted error message to help developers understand what went wrong.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php (1)

17-20: Implementation looks good

The refactored code properly follows the new pattern of implementing getFunctionName() to return the SQL function name. This approach centralizes function metadata and argument validation in the parent class, which reduces code duplication across function implementations.

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

17-20: Implementation looks good

The refactored code properly follows the new pattern of implementing getFunctionName() to return the SQL function name. This approach centralizes function metadata and argument validation in the parent class, which reduces code duplication across function implementations.

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

33-46: Implementation follows the new pattern correctly

Good implementation of the new methods getFunctionName(), getMinArgumentCount(), and getMaxArgumentCount() to explicitly define function metadata. This declarative approach makes the constraints more visible and maintainable than the previous approach.


48-56: Good refactoring of validation logic

The validation logic has been improved by:

  1. Delegating basic argument count validation to the parent class
  2. Adding specific validation for the boolean parameter
  3. Using the dynamic function name instead of hardcoded strings

This makes the code more maintainable and consistent with other implementations.

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

25-38: Good implementation with helpful comments

The implementation of getFunctionName(), getMinArgumentCount(), and getMaxArgumentCount() is clear and includes helpful comments explaining the constraints (at least one key-value pair, no upper limit but must be even). This makes the code more self-documenting.


40-47: Validation logic looks good

The validation logic properly delegates to the parent class first, then adds the specific validation that the argument count must be even. Using $this->getFunctionName() for the exception message ensures consistency if the function name changes in the future.

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

25-38: Well-structured function metadata definition

The implementation nicely separates function metadata into dedicated methods:

  • getFunctionName() provides the PostgreSQL function name
  • getMinArgumentCount() enforces at least one key-value pair (2 arguments)
  • getMaxArgumentCount() allows unlimited arguments but with the comment indicating they must be even

This approach makes the class more maintainable and follows the single responsibility principle.


40-46: Good validation refactoring

Excellent improvements:

  1. Delegating basic argument count validation to the parent class
  2. Adding specific validation for even number of arguments
  3. Using $this->getFunctionName() in the exception for consistent error messages

This change reduces code duplication while maintaining the specific validation needs of this function.

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

33-46: Clean function metadata implementation

The implementation provides clear function metadata:

  • Function name is explicitly defined as 'array_to_json'
  • Minimum argument count is set to 1 (required array parameter)
  • Maximum argument count is set to 2 (optional pretty_bool parameter)

This approach makes the intent of the function clearer and follows the overall refactoring pattern.


48-55: Improved validation approach

Good refactoring of the validation logic:

  1. Delegating basic argument count validation to the parent class
  2. Maintaining specific boolean validation for the second parameter
  3. Using $this->getFunctionName() for dynamic error messages

This creates a consistent pattern across the codebase while preserving function-specific validation.

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

32-45: Clear function metadata definition

Good implementation of the metadata methods:

  • getFunctionName() returns the correct PostgreSQL function name
  • getMinArgumentCount() properly sets the minimum required arguments to 2
  • getMaxArgumentCount() correctly allows up to 4 arguments as per PostgreSQL documentation

This structure makes it easier to understand the function's requirements at a glance.


47-54: Well-refactored validation logic

The validation logic is properly refactored:

  1. Delegating basic argument count validation to the parent class
  2. Focusing only on the specific boolean validation for the fourth parameter
  3. Using dynamic function name retrieval for error messages

This maintains the special validation requirements while eliminating duplicate code.

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

19-22: Good implementation of customizeFunction

The implementation dynamically sets the function prototype using the function name from the derived class, which creates a consistent pattern and reduces the need for each subclass to implement this method.


24-32: Appropriate argument constraints for comparison functions

The class properly defines:

  • Minimum argument count of 2, as comparison functions need at least two values to compare
  • No maximum limit, allowing for flexible comparison operations

This provides reasonable defaults for all derived comparison functions while still allowing them to override these constraints if needed.

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

32-35: Consistent implementation of function name method.

The implementation correctly returns the lowercase function name, which aligns with PostgreSQL's function naming conventions and ensures consistent error messages.


37-45: Well-defined argument boundaries.

The implementation properly defines the minimum and maximum argument counts for jsonb_path_query, correctly documenting that this function requires at least 2 arguments but can accept up to 4 optional parameters.


47-55: Improved argument validation with better delegation to parent class.

The refactored validation logic now properly delegates the basic argument count check to the parent class's implementation, while maintaining the specific boolean validation for the fourth parameter when present. Using getFunctionName() instead of a hardcoded string improves maintainability.

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

22-35: Clean implementation of function metadata methods.

The implementation correctly defines the function name and argument constraints for the PostgreSQL to_tsquery function. This refactoring simplifies the code by removing redundant validation logic while maintaining proper function definition.

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

33-46: Well-defined function metadata methods.

The implementation correctly defines the function name and argument constraints for the PostgreSQL jsonb_path_match function, matching its documented behavior in PostgreSQL 14.


48-56: Improved argument validation pattern.

The validation logic now properly delegates basic argument count validation to the parent class while maintaining specific boolean validation for the fourth parameter. Using getFunctionName() ensures consistency in error messages and improves maintainability.

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

32-45: Consistent implementation of function metadata methods.

The implementation correctly defines the function name and argument constraints for the PostgreSQL row_to_json function, enforcing that it requires at least 1 argument but can accept up to 2. This matches PostgreSQL's documentation.


47-55: Improved argument validation with better error handling.

The refactored validation logic properly delegates the basic argument count checking to the parent class while maintaining the specific boolean validation for the second parameter. Using getFunctionName() dynamically ensures consistency in error messages and improves maintainability.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php (4)

37-40: Well-implemented function name method

The new getFunctionName() method provides a clean way to access the function name, which makes the code more maintainable by centralizing this information.


42-45: Correctly defined minimum argument count

The getMinArgumentCount() method properly defines the minimum number of arguments (3) required for the jsonb_set function, which aligns with PostgreSQL's requirements.


47-50: Correctly defined maximum argument count

The getMaxArgumentCount() method properly defines the maximum number of arguments (4) allowed for the jsonb_set function, which aligns with PostgreSQL's implementation.


52-60: Good refactoring of argument validation

The validation is now simplified by delegating to the parent class while maintaining the specialized boolean validation for the fourth parameter. Using $this->getFunctionName() instead of hardcoded strings is a good practice that reduces duplication and potential errors.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php (4)

32-35: Well-implemented function name method

The getFunctionName() method properly returns the PostgreSQL function name 'date_subtract', centralizing this information for better maintainability.


37-40: Correctly defined minimum argument count

The getMinArgumentCount() method properly defines the minimum number of arguments (2) required for the date_subtract function.


42-45: Correctly defined maximum argument count

The getMaxArgumentCount() method properly defines the maximum number of arguments (3) allowed for the date_subtract function.


47-55: Good refactoring of argument validation

The validation logic has been improved by delegating to the parent class while maintaining the specialized timezone validation for the third parameter. Using $this->getFunctionName() instead of a hardcoded string enhances maintainability and reduces duplication.

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

22-26: Well-implemented function name method

The getFunctionName() method correctly returns the PostgreSQL function name 'to_tsvector', following the same pattern as other AST function classes.


27-30: Correctly defined minimum argument count

The getMinArgumentCount() method properly defines the minimum number of arguments (1) required for the to_tsvector function.


32-35: Correctly defined maximum argument count

The getMaxArgumentCount() method properly defines the maximum number of arguments (2) allowed for the to_tsvector function, which aligns with PostgreSQL's implementation.

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

21-24: Well-implemented function name method

The getFunctionName() method correctly returns the PostgreSQL function name 'unaccent', following the consistent pattern used across other AST function classes.


26-29: Correctly defined minimum argument count

The getMinArgumentCount() method properly defines the minimum number of arguments (1) required for the unaccent function.


31-34: Correctly defined maximum argument count

The getMaxArgumentCount() method properly defines the maximum number of arguments (2) allowed for the unaccent function, which aligns with PostgreSQL's implementation.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (4)

31-34: Function name definition improves consistency

The implementation of getFunctionName() aligns with the PostgreSQL function naming convention and creates consistency across the codebase by moving from hard-coded strings to dedicated methods.


36-39: Minimum argument count properly defined

Setting the minimum argument count to 2 is correct according to PostgreSQL documentation - the function requires at minimum a target JSON document and a path expression.


41-44: Maximum argument count correctly constrained

The maximum of 4 arguments aligns with PostgreSQL's implementation, which can accept optional vars and silent flags in addition to the required parameters.


48-53:

❓ Verification inconclusive

Improved validation approach

Delegating to the parent class for basic argument validation while maintaining specific boolean validation for the fourth parameter (silent flag) is a good approach. Using getFunctionName() instead of a hardcoded string improves maintainability.


🏁 Script executed:

#!/bin/bash
# Check PostgreSQL documentation for jsonb_path_exists function
ast-grep --pattern '$jsonb_path_exists($_, $_, $_, $_)'

Length of output: 57


Improved validation for optional silent flag

Delegating to parent::validateArguments for basic arity checks while explicitly validating the fourth parameter as a boolean aligns with PostgreSQL’s jsonb_path_exists signature (which accepts an optional silent flag). Using getFunctionName() instead of a hardcoded string also enhances maintainability.

Please manually verify:

  • That parent::validateArguments correctly permits exactly 3 or 4 arguments for this function.
  • That PostgreSQL’s jsonb_path_exists indeed accepts an optional boolean as its fourth parameter.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2)

14-17: Good abstract test case design

The abstract test case provides a solid foundation for testing variadic function implementations. The abstract createFixture() method ensures that subclasses provide their specific function implementation.


18-41: Comprehensive test for parser behavior

This test properly verifies that variadic functions throw the appropriate exception when the lexer isn't properly configured. The test thoroughly sets up the mock environment and tests the internal feedParserWithNodesForNodeMappingPattern method.

Note that using reflection to access private/protected methods is appropriate in test cases.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (4)

31-34: Function name properly defined

The implementation of getFunctionName() correctly returns the PostgreSQL function name, matching the documented function.


36-39: Minimum argument count correctly defined

Setting the minimum argument count to 2 matches PostgreSQL's requirements - the function requires at least a target JSON document and a path expression.


41-44: Maximum argument count correctly constrained

The maximum of 4 arguments aligns with PostgreSQL's implementation which supports 4 parameters: JSON data, path expression, variables, and silent flag.


48-53:

✅ Verification successful

Improved validation approach

Delegating to the parent class for basic argument validation while maintaining specific boolean validation for the fourth parameter is consistent with other similar functions. Using getFunctionName() improves maintainability.


🌐 Web query:

What are the parameters for PostgreSQL's jsonb_path_query_array function?

💡 Result:

Parameters for PostgreSQL's jsonb_path_query_array Function

The jsonb_path_query_array function in PostgreSQL is used to extract all JSON items matching a specified JSONPath expression from a JSONB value, returning the results as a JSON array. Its parameters are as follows:

Parameter Type Required Description
target JSONB Yes The input JSONB value to be queried.
path JSONPath Yes The JSONPath expression that specifies which items to extract from the JSONB document.
vars JSONB No A JSON object providing named variables to be substituted into the JSONPath expression.
silent Boolean No If true, suppresses certain errors (such as missing keys or type mismatches) during parsing.

Syntax

jsonb_path_query_array(
  target JSONB,
  path JSONPATH
  [, vars JSONB
  [, silent BOOLEAN]]
) RETURNS JSONB

Details

  • target: The JSONB document you want to search in.
  • path: The JSONPath string to apply for extraction. It follows SQL/JSON path syntax.
  • vars: (Optional) Used to provide variables for the path expression, allowing dynamic queries. Must be a JSON object.
  • silent: (Optional) When set to true, certain errors are suppressed and the function returns an empty array instead of failing[5][4][7].

Usage Example

SELECT jsonb_path_query_array(
  '{"employees":[{"name":"John","age":30},{"name":"Alice","age":35},{"name":"Bob","age":25}]}',
  '$.employees[*] ? (@.age >= $min && @.age <= $max)',
  '{"min": 24, "max": 32}'
);

Result:

[{"age": 30, "name": "John"}, {"age": 25, "name": "Bob"}]

[4][5][7]

Notes

  • If any argument is NULL, the function returns NULL.
  • If no matches are found, the function returns an empty JSON array.
  • The function is available in PostgreSQL 12 and later[6].

This function is a powerful tool for querying and extracting multiple objects or values from complex JSON structures directly within PostgreSQL.

Citations:


Approve boolean validation for the fourth parameter
The fourth “silent” argument in jsonb_path_query_array(target, path, vars, silent) is indeed optional and Boolean. Delegating core checks to parent::validateArguments() and then specifically validating the fourth argument with validateBoolean($arguments[3], $this->getFunctionName()) is correct and maintainable.

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

19-22: Function name correctly defined

The implementation of getFunctionName() appropriately returns 'ROW', which matches PostgreSQL's row constructor syntax.


24-27: Minimum argument count correctly defined

Setting the minimum argument count to 1 is correct for row constructors, which need at least one element.


29-32:

✅ Verification successful

Maximum argument count properly set to unlimited

Using PHP_INT_MAX is an appropriate way to indicate no upper limit on arguments, which aligns with PostgreSQL's row constructor that can accept any number of elements.


🌐 Web query:

What is the syntax for PostgreSQL row constructors and how many arguments can they accept?

💡 Result:

PostgreSQL Row Constructor Syntax

A PostgreSQL row constructor is an expression that builds a composite value (a row) from a list of field values. The general syntax is:

ROW(value1, value2, ..., valueN)
  • The ROW keyword is optional if there is more than one field in the expression, so you can also just use parentheses for tuples with two or more values:
    (value1, value2, ..., valueN)
    
  • Each value is an expression, and types can differ, as long as their types match the context (table columns, composite types, function parameters, etc.)[1][2][7][10].

Examples:

  • SELECT ROW(1, 'foo', 3.14);
  • SELECT (1, 'foo', 3.14);
  • For table comparisons or WHERE clauses:
    SELECT * FROM table_name WHERE (col1, col2) = (val1, val2);
    ```[2]
    
    

Argument Limits for Row Constructors

There is no explicit, documented hard limit to the number of arguments (fields) a row constructor can accept in PostgreSQL. The number of expressions is primarily constrained by:

  • The maximum number of columns in a table (1600 columns as of recent PostgreSQL versions, though composite types and anonymous records follow similar limits)[1][2].
  • Practical limits such as parser memory and command length for extremely wide rows.

For typical use—defining or manipulating single rows—the number of fields in a row constructor should match the number of columns or attributes in the corresponding table or composite type. There is no arbitrary small cap (like 1000), as might be seen in other systems' bulk insert clauses[1][2].

Advanced Usage Notes

  • You can expand existing rows into new constructors with the .* syntax (e.g., ROW(t.*, 'A') expands all fields of row t and adds 'A')[3][7].
  • Row constructors are frequently used for:
    • Comparisons (tuple-wise in WHERE and JOIN clauses)
    • Batch inserts (as tuples in VALUES)
    • Building records for functions returning composite types[2][3].

Summary Table

Feature Syntax Example Notes
Basic row constructor ROW(val1, val2, ...) ROW keyword optional with multiple fields
Tuple for comparison WHERE (col1, col2) = (v1, v2) Field count and types must match
Argument limit No explicit limit; practical only Up to table/composite type column limits (~1600 fields)

Reference:

  • PostgreSQL Documentation, "Value Expressions" and "Composite Types"[1][7][10]
  • C# Corner, "Row Constructors in PostgreSQL"[2]
  • DatabaseRookies, "Exploring ROW Constructors in PostgreSQL"[3]

Row constructors in PostgreSQL are flexible, powerful, and can accept as many arguments as needed within the practical and type-matching constraints of the context.

Citations:


Approved: Unlimited argument count is appropriate

The getMaxArgumentCount() method returning PHP_INT_MAX correctly reflects PostgreSQL’s row constructors, which impose no small arbitrary cap (aside from practical limits like a table’s column count of ≈1600). No changes needed.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php (4)

22-25: Good implementation of getFunctionName()

Excellent approach to centralize the function name definition. This improves maintainability by eliminating hardcoded function names across the codebase.


29-30: Great use of dynamic function name

Using getFunctionName() instead of hardcoding 'ARRAY' improves maintainability and consistency. If the function name ever needs to change, it only needs to be updated in one place.


32-35: Good implementation of getMinArgumentCount()

Adding this explicit method to define the minimum argument count centralizes validation logic and makes the code more maintainable.


37-40: Good implementation of getMaxArgumentCount()

The method clearly defines the maximum argument count allowed. Using PHP_INT_MAX with a comment explaining "No upper limit" makes the intention clear.

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

32-35: Good implementation of getFunctionName()

Centralizing the function name definition improves maintainability by eliminating hardcoded strings.


37-40: Good implementation of getMinArgumentCount()

Explicitly defining the minimum argument count through this method centralizes validation logic and improves code clarity.


42-45: Good implementation of getMaxArgumentCount()

Explicitly defining the maximum argument count through this method centralizes validation logic and improves code clarity.


49-49: Good delegation to parent validation

Calling parent::validateArguments() is a clean approach that eliminates code duplication and centralizes basic argument validation logic in the parent class.


52-54: Good use of dynamic function name in error messages

Using getFunctionName() instead of hardcoding the function name for validation messages improves consistency and maintainability.

Comment on lines +22 to +26
protected function customizeFunction(): void
{
$this->setFunctionPrototype(\sprintf('%s(%%s)', $this->getFunctionName()));
}

Copy link

@coderabbitai coderabbitai bot Apr 17, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider declaring customizeFunction() as final

customizeFunction() is now fully implemented in the base class and should never be overridden by descendants – doing so would break the new centralised prototype logic you have just introduced. Marking it final eliminates that risk and makes the intended extension‑points (getFunctionName(), getMinArgumentCount(), getMaxArgumentCount()) explicit.

-    protected function customizeFunction(): void
+    final protected function customizeFunction(): void
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected function customizeFunction(): void
{
$this->setFunctionPrototype(\sprintf('%s(%%s)', $this->getFunctionName()));
}
final protected function customizeFunction(): void
{
$this->setFunctionPrototype(\sprintf('%s(%%s)', $this->getFunctionName()));
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is not true. The Arr class has a different prototype, and it overrides it accordingly for that.

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the implementation of the Arr class
cat src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php

Length of output: 910


@martin-georgiev You're absolutely right. Looking at the Arr class implementation, I can confirm it needs to override customizeFunction() to use a different prototype format:

// In Arr.php
protected function customizeFunction(): void
{
    $this->setFunctionPrototype(\sprintf('%s[%%s]', $this->getFunctionName()));
}

The Arr class uses square brackets (ARRAY[%s]) instead of the default parentheses pattern. Making customizeFunction() final would prevent this type of legitimate customization, which is needed for PostgreSQL array constructor syntax.

Thanks for the correction.


🧬 Code Graph Analysis Results

Relevant snippets for understanding src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php and the user comment:


File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php

Lines 17-40:

protected function getNodeMappingPattern(): array
{
    return ['StringPrimary'];
}

protected function getFunctionName(): string
{
    return 'ARRAY';
}

protected function customizeFunction(): void
{
    $this->setFunctionPrototype(\sprintf('%s[%%s]', $this->getFunctionName()));
}

protected function getMinArgumentCount(): int
{
    return 1;
}

protected function getMaxArgumentCount(): int
{
    return PHP_INT_MAX; // No upper limit
}
  • This class Arr extends or relates to variadic functions and overrides the function prototype to use square brackets (ARRAY[%s]) instead of parentheses.
  • It defines the function name as 'ARRAY'.
  • It sets minimum argument count to 1 and maximum to unlimited (PHP_INT_MAX).
  • The node mapping pattern is a single parser method 'StringPrimary'.

File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php

Lines 1-110 (entire file provided)

  • Abstract class extending BaseFunction to support variadic functions with flexible argument counts.
  • Defines abstract methods:
    • getFunctionName(): string
    • getNodeMappingPattern(): array
    • getMinArgumentCount(): int
    • getMaxArgumentCount(): int
  • Implements customizeFunction() to set the function prototype as functionName(%s) by default.
  • Implements feedParserWithNodes(Parser $parser) which tries multiple node mapping patterns to parse arguments.
  • Implements feedParserWithNodesForNodeMappingPattern(Parser $parser, string $nodeMappingPattern) which parses the arguments according to the pattern and validates argument count.
  • Validates argument count in validateArguments(Node ...$arguments).
  • getSql(SqlWalker $sqlWalker) generates SQL by dispatching nodes and formatting with the function prototype.
  • Uses DoctrineLexer and DoctrineOrm utilities to handle lexer differences between Doctrine versions.

File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php

Lines 20-105 (summary):

abstract class BaseFunction extends FunctionNode
{
    protected string $functionPrototype;
    protected array $nodesMapping = [];
    protected array $nodes = [];

    protected function customizeFunction(): void
    {
        // Void by default
    }

    protected function setFunctionPrototype(string $functionPrototype): void
    {
        $this->functionPrototype = $functionPrototype;
    }

    protected function addNodeMapping(string $parserMethod): void
    {
        $this->nodesMapping[] = $parserMethod;
    }

    public function parse(Parser $parser): void
    {
        $shouldUseLexer = DoctrineOrm::isPre219();

        $this->customizeFunction();

        $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER);
        $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS);
        $this->feedParserWithNodes($parser);
        $parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS);
    }

    protected function feedParserWithNodes(Parser $parser): void
    {
        $nodesMappingCount = \count($this->nodesMapping);
        $lastNode = $nodesMappingCount - 1;
        for ($i = 0; $i < $nodesMappingCount; $i++) {
            $parserMethod = $this->nodesMapping[$i];
            $this->nodes[$i] = $parser->{$parserMethod}();
            if ($i < $lastNode) {
                $parser->match(\class_exists(TokenType::class) ? TokenType::T_COMMA : Lexer::T_COMMA);
            }
        }
    }

    public function getSql(SqlWalker $sqlWalker): string
    {
        $dispatched = [];
        foreach ($this->nodes as $node) {
            $dispatched[] = $node instanceof Node ? $node->dispatch($sqlWalker) : 'null';
        }

        return \vsprintf($this->functionPrototype, $dispatched);
    }
}
  • Base class for all functions, providing basic parsing and SQL generation.
  • Uses parentheses by default for function calls.
  • feedParserWithNodes parses nodes according to a fixed mapping pattern.
  • customizeFunction() is empty by default but overridden in subclasses.

File: src/MartinGeorgiev/Utils/DoctrineOrm.php

Lines 12-18:

final class DoctrineOrm
{
    public static function isPre219(): bool
    {
        return !\class_exists(TokenType::class);
    }
}
  • Utility to detect Doctrine ORM version before 2.1.9 by checking existence of TokenType class.
  • Used in BaseVariadicFunction and BaseFunction to handle lexer differences.

File: src/MartinGeorgiev/Utils/DoctrineLexer.php

Lines 12-58 (summary):

final class DoctrineLexer
{
    public static function isPre200(Lexer $lexer): bool
    {
        return \is_array($lexer->lookahead);
    }

    public static function getLookaheadType(Lexer $lexer)
    {
        if (self::isPre200($lexer)) {
            return $lexer->lookahead['type'];
        }
        return $lexer->lookahead?->type;
    }

    public static function getTokenValue(Lexer $lexer)
    {
        if (self::isPre200($lexer)) {
            if ($lexer->token === null) {
                return null;
            }
            return $lexer->token['value'];
        }
        return $lexer->token?->value;
    }
}
  • Utility class to abstract differences in Doctrine Lexer versions before 2.0.0.
  • Provides methods to get lookahead token type and token value safely.

File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php

Lines 7-56 (summary):

class InvalidArgumentForVariadicFunctionException extends \InvalidArgumentException
{
    public static function exactCount(string $functionName, int $expected): self { ... }
    public static function atLeast(string $functionName, int $min): self { ... }
    public static function between(string $functionName, int $min, int $max): self { ... }
    public static function evenNumber(string $functionName): self { ... }
    public static function unsupportedCombination(string $functionName, int $count, string $note): self { ... }
}
  • Exception class for invalid argument counts or combinations in variadic functions.
  • Provides static factory methods for different error messages related to argument count validation.

Summary of relevance:

  • The user comment refers to the Arr class having a different prototype and overriding accordingly.
  • The snippet from Arr.php shows that it overrides customizeFunction() to set a prototype with square brackets (ARRAY[%s]), differing from the default parentheses in BaseVariadicFunction.
  • The BaseVariadicFunction class sets the default prototype with parentheses and handles variadic argument parsing and validation.
  • The base class BaseFunction uses parentheses by default and a fixed node mapping pattern.
  • Utilities DoctrineOrm and DoctrineLexer handle version differences in Doctrine ORM and Lexer.
  • The exception class is used in BaseVariadicFunction to throw errors on invalid argument counts.

These snippets provide the necessary context to understand the difference in function prototype handling between BaseVariadicFunction and the Arr class, which overrides the prototype accordingly.


✏️ Learnings added
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#350
File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php:22-26
Timestamp: 2025-04-18T10:20:43.983Z
Learning: The `customizeFunction()` method in `BaseVariadicFunction` should not be marked as final because some child classes like `Arr` need to override it to use different SQL syntax patterns (e.g., square brackets instead of parentheses).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)

87-88: ⚠️ Potential issue

Potential out-of-bounds array access issue.

If a subclass defines a multi-element node mapping pattern but allows more arguments than the pattern's length, accessing $nodeMapping[$expectedNodeIndex] could cause an undefined index error.

 $expectedNodeIndex = $isNodeMappingASimplePattern ? 0 : $nodeIndex;
+if (!isset($nodeMapping[$expectedNodeIndex])) {
+    // Implementation error - max argument count exceeds mapping pattern length
+    throw InvalidArgumentForVariadicFunctionException::unsupportedCombination(
+        $this->getFunctionName(),
+        count($this->nodes) + 1,
+        'implementation defines fewer node mapping patterns than maximum allowed arguments'
+    );
+}
 $this->nodes[] = $parser->{$nodeMapping[$expectedNodeIndex]}(); // @phpstan-ignore-line
🧹 Nitpick comments (5)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (1)

44-53: Add test for minimum argument validation.

While you've correctly tested for the maximum argument count (max 2), consider adding a test for the minimum argument count validation (min 1) to ensure complete coverage of the validation logic in BaseVariadicFunction.

/**
 * @test
 */
public function throws_exception_when_too_few_arguments_given(): void
{
    $this->expectException(InvalidArgumentForVariadicFunctionException::class);

    $dql = 'SELECT TO_TSVECTOR() FROM ' . ContainsTexts::class . ' e';
    $this->assertSqlFromDql('', $dql);
}
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (1)

44-53: Add test for minimum argument validation.

While you've correctly tested for the maximum argument count (max 2), consider adding a test for the minimum argument count validation (min 1) to ensure complete coverage of the validation logic in BaseVariadicFunction.

/**
 * @test
 */
public function throws_exception_when_too_few_arguments_given(): void
{
    $this->expectException(InvalidArgumentForVariadicFunctionException::class);

    $dql = 'SELECT TO_TSQUERY() FROM ' . ContainsTexts::class . ' e';
    $this->assertSqlFromDql('', $dql);
}
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (1)

44-53: Add test for minimum argument validation.

While you correctly test for odd numbers of arguments, consider adding a test for when zero arguments are provided, to verify the minimum argument count validation (min 2) in BaseVariadicFunction.

/**
 * @test
 */
public function throws_exception_when_too_few_arguments_given(): void
{
    $this->expectException(InvalidArgumentForVariadicFunctionException::class);

    $dql = 'SELECT JSON_BUILD_OBJECT() FROM ' . ContainsJsons::class . ' e';
    $this->assertSqlFromDql('', $dql);
}
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php (1)

44-53: Add test for minimum argument validation.

While you correctly test for odd numbers of arguments, consider adding a test for when zero arguments are provided, to verify the minimum argument count validation (min 2) in BaseVariadicFunction.

/**
 * @test
 */
public function throws_exception_when_too_few_arguments_given(): void
{
    $this->expectException(InvalidArgumentForVariadicFunctionException::class);

    $dql = 'SELECT JSONB_BUILD_OBJECT() FROM ' . ContainsJsons::class . ' e';
    $this->assertSqlFromDql('', $dql);
}
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)

22-26: Good centralization of function prototype definition.

The implementation provides a standardized way to define function prototypes using the abstract getFunctionName() method, which reduces code duplication across subclasses.

Consider adding a class-level comment documenting that this method can be overridden by subclasses that need specific syntax patterns (like Arr which uses square brackets instead of parentheses).

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b3678c4 and 2d95734.

📒 Files selected for processing (23)
  • fixtures/MartinGeorgiev/Doctrine/Function/TestVariadicFunction.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJsonTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAddTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtractTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetTest.php (3 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJsonTest.php (2 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • fixtures/MartinGeorgiev/Doctrine/Function/TestVariadicFunction.php
🚧 Files skipped from review as they are similar to previous changes (14)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJsonTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/GreatestTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJsonTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/LeastTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtractTest.php
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAddTest.php
🧰 Additional context used
📓 Path-based instructions (1)
`tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*.php`: Use the PostgreSQL official documentation to verify that tests include comprehensive use cases and example SQL que...

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/JsonbBuildObjectTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php
🧠 Learnings (8)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObjectTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObjectTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#350
File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php:78-88
Timestamp: 2025-04-18T10:33:52.403Z
Learning: In SQL generation code for the postgresql-for-doctrine library, it's better to fail explicitly with clear errors than to use fallback behavior that makes assumptions about node types, as this could lead to functional bugs or security issues.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (2)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#318
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/XmlAggTest.php:1-9
Timestamp: 2025-03-29T03:31:17.114Z
Learning: Tests in the `Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), not PHPUnit's TestCase, and therefore don't need an explicit import statement.
🧬 Code Graph Analysis (3)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/UnaccentTest.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
  • BaseVariadicFunction (20-125)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php (1)
  • Unaccent (14-35)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2)
  • BaseVariadicFunctionTestCase (14-42)
  • createFixture (16-16)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsTexts.php (1)
  • ORM (10-18)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
  • BaseVariadicFunction (20-125)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php (1)
  • ToTsvector (15-36)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2)
  • BaseVariadicFunctionTestCase (14-42)
  • createFixture (16-16)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsqueryTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsTexts.php (1)
  • ORM (10-18)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
  • BaseVariadicFunction (20-125)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php (1)
  • ToTsquery (15-36)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunctionTestCase.php (2)
  • BaseVariadicFunctionTestCase (14-42)
  • createFixture (16-16)
⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: PHP 8.4 + Doctrine ORM 2.14 + Doctrine Lexer latest
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer latest
  • GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.2 + Doctrine ORM latest + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.14 + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM latest + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM latest + Doctrine Lexer latest
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer 2.1
  • GitHub Check: PHP 8.1 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM latest + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM 2.18 + Doctrine Lexer latest
  • GitHub Check: PHP 8.1 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
  • GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
  • GitHub Check: sloth
🔇 Additional comments (15)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvectorTest.php (2)

8-8: LGTM! The refactoring adapts the test to the new variadic function framework.

The changes correctly switch to the new BaseVariadicFunctionTestCase parent class and provide the required createFixture() method implementation, aligning with the PR's objective to standardize variadic function argument validation.

Also applies to: 12-17


26-33: The test cases provide good coverage of PostgreSQL to_tsvector functionality.

The test cases correctly verify:

  1. Basic conversion of text to tsvector
  2. Conversion with text transformation (LOWER)
  3. Conversion with explicit dictionary configuration ('english')

These align well with the PostgreSQL documentation for to_tsvector().

Also applies to: 35-42

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

8-8: LGTM! The refactoring adapts the test to the new variadic function framework.

The changes correctly switch to the new BaseVariadicFunctionTestCase parent class and provide the required createFixture() method implementation, aligning with the PR's objective to standardize variadic function argument validation.

Also applies to: 12-17


26-33: The test cases provide good coverage of PostgreSQL to_tsquery functionality.

The test cases correctly verify:

  1. Basic conversion to tsquery
  2. Conversion with text transformation (UPPER)
  3. Conversion with explicit dictionary configuration ('english')

These align well with the PostgreSQL documentation for to_tsquery().

Also applies to: 35-42

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

8-8: LGTM! The refactoring adapts the test to the new variadic function framework.

The changes correctly switch to the new BaseVariadicFunctionTestCase parent class and provide the required createFixture() method implementation, aligning with the PR's objective to standardize variadic function argument validation.

Also applies to: 12-17


26-33: The test cases provide good coverage of PostgreSQL json_build_object functionality.

The test cases correctly verify:

  1. Building a JSON object with a single key-value pair
  2. Building a JSON object with multiple key-value pairs using literals and functions
  3. Building a JSON object with multiple key-value pairs using database columns

These align well with the PostgreSQL documentation for json_build_object().

Also applies to: 35-42

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

8-8: LGTM! The refactoring adapts the test to the new variadic function framework.

The changes correctly switch to the new BaseVariadicFunctionTestCase parent class and provide the required createFixture() method implementation, aligning with the PR's objective to standardize variadic function argument validation.

Also applies to: 12-17


26-33: The test cases provide good coverage of PostgreSQL jsonb_build_object functionality.

The test cases correctly verify:

  1. Building a JSONB object with a single key-value pair
  2. Building a JSONB object with multiple key-value pairs using literals and functions
  3. Building a JSONB object with multiple key-value pairs using database columns

These align well with the PostgreSQL documentation for jsonb_build_object().

Also applies to: 35-42

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

27-28: Well-designed abstract methods for function metadata.

These new abstract methods create a cleaner contract for subclasses by forcing them to explicitly define their function name and argument count requirements. This approach is more declarative and maintainable than having subclasses implement custom validation logic.

Also applies to: 34-37


78-85: Good early validation of argument count limits.

Adding validation before processing each additional argument is a smart approach to fail fast if maximum arguments would be exceeded. This prevents unnecessary parsing and provides clear error messages to users.


98-114: Excellent centralization of validation logic.

Moving the argument validation logic to the base class is a significant improvement. It:

  1. Eliminates code duplication across subclasses
  2. Ensures consistent validation behavior
  3. Centralizes exception throwing with standardized messages
  4. Makes subclasses simpler by removing boilerplate code

This is a good example of the "Don't Repeat Yourself" principle.

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

11-19: Appropriate test refactoring for new base class design.

The test class has been properly updated to:

  1. Import and use BaseVariadicFunction
  2. Extend the new BaseVariadicFunctionTestCase
  3. Implement the required createFixture() method that returns a new Row instance

This aligns with the refactored architecture where variadic functions now declare minimum and maximum argument counts explicitly.

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

9-16: Proper test adaptation to the new framework.

This test refactoring correctly implements the required changes to support the new base variadic function architecture:

  1. Added the necessary BaseVariadicFunction import
  2. Updated to extend BaseVariadicFunctionTestCase
  3. Implemented the required createFixture() method

This ensures that ArrTest works with the updated function architecture.

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

8-17: Correctly updated test to match the refactored architecture.

The UnaccentTest class has been properly updated to work with the refactored BaseVariadicFunction architecture by:

  1. Adding the import for BaseVariadicFunction
  2. Extending BaseVariadicFunctionTestCase
  3. Implementing the required createFixture() method

This ensures consistency with the new validation pattern and function metadata approach.


42-51:

✅ Verification successful

Test case effectively validates argument limit enforcement.

This test verifies that exceeding the maximum argument count correctly throws an exception. According to the relevant code snippet provided in the context, Unaccent defines a maximum of 2 arguments, and this test tries to use 3, triggering the expected validation failure.


🏁 Script executed:

#!/bin/bash
# Verify Unaccent's configuration matches the test
echo "Checking Unaccent implementation:"
grep -A 5 "getMaxArgumentCount" src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php

Length of output: 255


Approve argument limit enforcement test

The test correctly throws InvalidArgumentForVariadicFunctionException when passing three arguments to UNACCENT, which is limited to two (getMaxArgumentCount() returns 2).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (3)

87-88: Consider using array_key_exists for improved semantics

When checking for index existence in arrays, array_key_exists() might be semantically clearer than isset() as it won't return false for keys with null values.

-                $argumentCountExceedsMappingPatternExpectation = !isset($nodeMapping[$expectedNodeIndex]);
+                $argumentCountExceedsMappingPatternExpectation = !\array_key_exists($expectedNodeIndex, $nodeMapping);

66-66: Consider addressing phpstan-ignore annotations

There are multiple @phpstan-ignore-line annotations in the code. While sometimes necessary, they can hide potential type-related issues. Consider:

  1. Adding appropriate phpdoc type annotations
  2. Using explicit type assertions if needed
  3. Refactoring to make the code more type-safe

If these annotations are unavoidable due to Doctrine's API design, consider adding comments explaining why they're needed.

Also applies to: 97-97, 104-104


104-105: Potential redundant validation

This final validation call happens after all arguments are already parsed and previous checks were done at lines 83-85. While redundancy in validation can be beneficial for code safety, consider adding a comment explaining the purpose of this second validation pass to make the intention clear.

-        $this->validateArguments(...$this->nodes); // @phpstan-ignore-line
+        // Final validation ensures all arguments meet requirements, including any special rules in subclass implementations
+        $this->validateArguments(...$this->nodes); // @phpstan-ignore-line
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2d95734 and 2aa63ee.

📒 Files selected for processing (1)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#350
File: src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php:78-88
Timestamp: 2025-04-18T10:33:52.403Z
Learning: In SQL generation code for the postgresql-for-doctrine library, it's better to fail explicitly with clear errors than to use fallback behavior that makes assumptions about node types, as this could lead to functional bugs or security issues.
🔇 Additional comments (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (4)

22-27: Function name management improved via abstraction

Good refactoring to centralize function name management through the new abstract getFunctionName() method. This improves maintainability by requiring subclasses to explicitly define their function name, while ensuring consistent usage in the function prototype.


34-37: Well-structured argument count constraints

The addition of abstract getMinArgumentCount() and getMaxArgumentCount() methods improves the design by:

  1. Enforcing subclasses to explicitly define their argument constraints
  2. Providing centralized access to these constraints for validation logic
  3. Removing the need for validation code duplication across implementations

This change enhances both maintainability and consistency.


78-96: Robust defense against invalid argument counts and potential index errors

Excellent implementation of validation checks that:

  1. Prevents exceeding maximum allowed arguments early in the parsing process
  2. Guards against array index out-of-bounds access by checking isset($nodeMapping[$expectedNodeIndex])
  3. Provides clear, specific error messages when validation fails

This defensive programming approach aligns well with the principle of failing explicitly rather than using fallback behavior that could lead to security issues.


107-123: Well-designed centralized argument validation

The new validateArguments() method properly centralizes validation logic:

  1. It provides a consistent place to check argument counts
  2. Leverages the abstract methods to get constraints
  3. Uses consistent exception throwing patterns

This approach reduces code duplication and ensures consistent validation across all parsing paths.

@martin-georgiev martin-georgiev merged commit e111dd2 into main Apr 19, 2025
10 of 101 checks passed
@martin-georgiev martin-georgiev deleted the regex branch April 19, 2025 20:16
@github-actions github-actions bot mentioned this pull request Apr 19, 2025
seb-jean pushed a commit to seb-jean/postgresql-for-doctrine that referenced this pull request Apr 23, 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.

3 participants