-
-
Notifications
You must be signed in to change notification settings - Fork 56
feat(#410): add support for functions to use with the LTREE data type
#440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds LTREE documentation and quick-reference entries, six new Doctrine DQL→SQL ltree function implementations (Index, Lca, Ltree2text, Nlevel, Subltree, Subpath), a fixture entity with three ltree columns, and comprehensive unit and integration tests; Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Application (builds DQL)
participant Parser as Doctrine DQL Parser
participant Func as Ltree Function AST
participant SQLGen as SQL Generator
participant PG as PostgreSQL (ltree)
App->>Parser: Submit DQL with LTREE function (e.g., SUBPATH/INDEX/LCA)
Parser->>Func: Parse function node and arguments
note right of Func #DDEBF7: Validate arity/types\n(variadic patterns when applicable)
Func->>SQLGen: Emit SQL function call (subpath(...), index(...), lca(...), ...)
SQLGen->>PG: Execute SQL
PG-->>SQLGen: Return rows
SQLGen-->>Parser: Hydrate results
Parser-->>App: Return result set
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (20)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (1)
11-33: Add a couple more cases to mirror integration testsFor speedier feedback at unit level and parity with integration coverage, consider adding single-node and empty-text cases.
class Text2ltreeTest extends TestCase { protected function getStringFunctions(): array { return [ 'TEXT2LTREE' => Text2ltree::class, ]; } protected function getExpectedSqlStatements(): array { return [ 'casts text to ltree' => "SELECT text2ltree('Top.Child1.Child2') AS sclr_0 FROM ContainsTexts c0_", + 'casts single node to ltree' => "SELECT text2ltree('Root') AS sclr_0 FROM ContainsTexts c0_", + 'casts empty text to ltree' => "SELECT text2ltree('') AS sclr_0 FROM ContainsTexts c0_", ]; } protected function getDqlStatements(): array { return [ 'casts text to ltree' => \sprintf("SELECT TEXT2LTREE('Top.Child1.Child2') FROM %s e", ContainsTexts::class), + 'casts single node to ltree' => \sprintf("SELECT TEXT2LTREE('Root') FROM %s e", ContainsTexts::class), + 'casts empty text to ltree' => \sprintf("SELECT TEXT2LTREE('') FROM %s e", ContainsTexts::class), ]; } }tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (1)
22-41: Use ::class to avoid FQCN string escaping and typosMinor readability improvement and consistency with other tests.
<?php declare(strict_types=1); namespace Tests\Integration\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ltree; use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ltree\Text2ltree; +use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees; use PHPUnit\Framework\Attributes\Test; class Text2ltreeTest extends LtreeTestCase { @@ #[Test] public function casts_text_to_ltree(): void { - $dql = "SELECT TEXT2LTREE('Top.Child1.Child2') as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; + $dql = \sprintf("SELECT TEXT2LTREE('Top.Child1.Child2') as result FROM %s l WHERE l.id = 1", ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame('Top.Child1.Child2', $result[0]['result']); } @@ #[Test] public function casts_single_node_text_to_ltree(): void { - $dql = "SELECT TEXT2LTREE('Root') as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; + $dql = \sprintf("SELECT TEXT2LTREE('Root') as result FROM %s l WHERE l.id = 1", ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame('Root', $result[0]['result']); } @@ #[Test] public function casts_empty_text_to_ltree(): void { - $dql = "SELECT TEXT2LTREE('') as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; + $dql = \sprintf("SELECT TEXT2LTREE('') as result FROM %s l WHERE l.id = 1", ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame('', $result[0]['result']); } }tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (1)
22-39: Prefer ::class for DQL entity referenceReduces escaping noise and keeps FQCN changes localized.
<?php declare(strict_types=1); namespace Tests\Integration\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ltree; use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ltree\Nlevel; +use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees; use PHPUnit\Framework\Attributes\Test; class NlevelTest extends LtreeTestCase { @@ public function returns_number_of_labels_in_path(): void { - $dql = 'SELECT NLEVEL(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $dql = \sprintf('SELECT NLEVEL(l.ltree1) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame(3, $result[0]['result']); } @@ public function returns_number_of_labels_for_different_paths(): void { - $dql = 'SELECT NLEVEL(l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $dql = \sprintf('SELECT NLEVEL(l.ltree2) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame(2, $result[0]['result']); } @@ public function returns_number_of_labels_for_single_node(): void { - $dql = 'SELECT NLEVEL(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; + $dql = \sprintf('SELECT NLEVEL(l.ltree1) as result FROM %s l WHERE l.id = 3', ContainsLtrees::class); $result = $this->executeDqlQuery($dql); $this->assertSame(1, $result[0]['result']); }docs/LTREE-TYPE.md (1)
276-294: Clarify 0-based indexing contract for INDEX().Explicitly note that positions are 0-based to avoid ambiguity.
-Returns the position of the first occurrence of `b` in `a`, or -1 if not found. +Returns the 0-based position of the first occurrence of `b` in `a`, or -1 if not found.tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php (1)
13-34: LGTM: covers 2-arg and 3-arg SQL generation.Optional: add a case with a positive offset as well for completeness.
docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md (1)
52-56: Nice addition. Consider cross-linking ltree operators explicitly.Add a short note that ltree also provides
@>and<@operators (ancestor/descendant) and link to LTREE-TYPE.md.- Includes: Path manipulation functions, ancestor/descendant operations, type conversion functions + - Includes: Path manipulation functions, ancestor/descendant operations (see also `@>`/`<@` ltree operators), and type conversion functionsfixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
9-20: Pin table name to avoid reliance on naming strategy in integration tests
LtreeTestCasecreates tablecontainsltreesexplicitly. Make the entity map to the same name to prevent accidental mismatches if the naming strategy changes.#[ORM.Entity()] class ContainsLtrees extends Entity { + #[ORM\Table(name: 'containsltrees')] + // Alternatively, place the attribute above the class declaration: + // #[ORM\Entity, ORM\Table(name: 'containsltrees')] + #[ORM\Column(type: 'ltree')] public string $ltree1;tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (1)
19-41: Use ::class with sprintf to avoid escaped FQCN strings in DQLKeeps consistency with unit tests and improves readability.
- $dql = 'SELECT LTREE2TEXT(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $dql = \sprintf('SELECT LTREE2TEXT(l.ltree1) as result FROM %s l WHERE l.id = 1', \Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees::class); ... - $dql = 'SELECT LTREE2TEXT(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; + $dql = \sprintf('SELECT LTREE2TEXT(l.ltree1) as result FROM %s l WHERE l.id = 3', \Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees::class); ... - $dql = 'SELECT LTREE2TEXT(l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $dql = \sprintf('SELECT LTREE2TEXT(l.ltree2) as result FROM %s l WHERE l.id = 1', \Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees::class);tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (1)
20-34: Optional: add a negative-offset case to unit mappings to mirror integration testsParities the unit contract with integration coverage for
SUBPATH(e.text1, -2).protected function getExpectedSqlStatements(): array { return [ 'extracts subpath with offset and length' => 'SELECT subpath(c0_.text1, 0, 2) AS sclr_0 FROM ContainsTexts c0_', 'extracts subpath with offset only' => 'SELECT subpath(c0_.text1, 1) AS sclr_0 FROM ContainsTexts c0_', + 'extracts subpath with negative offset' => 'SELECT subpath(c0_.text1, -2) AS sclr_0 FROM ContainsTexts c0_', ]; } protected function getDqlStatements(): array { return [ 'extracts subpath with offset and length' => \sprintf('SELECT SUBPATH(e.text1, 0, 2) FROM %s e', ContainsTexts::class), 'extracts subpath with offset only' => \sprintf('SELECT SUBPATH(e.text1, 1) FROM %s e', ContainsTexts::class), + 'extracts subpath with negative offset' => \sprintf('SELECT SUBPATH(e.text1, -2) FROM %s e', ContainsTexts::class), ]; }tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (1)
20-25: Add a parameterized case to exercise placeholder binding.Cover translation with bound parameters to guard against regressions in variadic parsing.
Apply:
protected function getExpectedSqlStatements(): array { return [ 'extracts subpath from ltree' => 'SELECT subltree(c0_.text1, 1, 2) AS sclr_0 FROM ContainsTexts c0_', + 'extracts subpath with parameters' => 'SELECT subltree(c0_.text1, ?, ?) AS sclr_0 FROM ContainsTexts c0_', ]; } protected function getDqlStatements(): array { return [ 'extracts subpath from ltree' => \sprintf('SELECT SUBLTREE(e.text1, 1, 2) FROM %s e', ContainsTexts::class), + 'extracts subpath with parameters' => \sprintf('SELECT SUBLTREE(e.text1, :start, :len) FROM %s e', ContainsTexts::class), ]; }Also applies to: 27-32
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (1)
12-17: Nice coverage for 3-arg SUBLTREE; suggest edge cases.Consider negative offsets and start=end boundaries to fully exercise server-side semantics.
Apply:
public function extracts_subpath_from_ltree_with_different_positions(): void { $dql = 'SELECT SUBLTREE(l.ltree1, 0, 2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; $result = $this->executeDqlQuery($dql); $this->assertSame('Top.Child1', $result[0]['result']); } + + #[Test] + public function extracts_subpath_with_negative_offset(): void + { + $dql = 'SELECT SUBLTREE(l.ltree1, -2, 2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $result = $this->executeDqlQuery($dql); + // Expect the last two labels to start at -2 (implementation-defined; adjust if needed) + $this->assertSame('Child1', $result[0]['result']); + } + + #[Test] + public function extracts_subpath_with_start_equal_end(): void + { + $dql = 'SELECT SUBLTREE(l.ltree1, 1, 1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; + $result = $this->executeDqlQuery($dql); + $this->assertSame('Child1', $result[0]['result']); + }Also applies to: 19-25, 27-33
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (1)
20-25: Optional: add a placeholder-based case to verify parameter handling.Ensures translation remains stable when values are bound.
Apply:
protected function getExpectedSqlStatements(): array { return [ 'casts ltree to text' => 'SELECT ltree2text(c0_.text1) AS sclr_0 FROM ContainsTexts c0_', + 'casts parameter to text' => 'SELECT ltree2text(?) AS sclr_0 FROM ContainsTexts c0_', ]; } protected function getDqlStatements(): array { return [ 'casts ltree to text' => \sprintf('SELECT LTREE2TEXT(e.text1) FROM %s e', ContainsTexts::class), + 'casts parameter to text' => \sprintf('SELECT LTREE2TEXT(:p) FROM %s e', ContainsTexts::class), ]; }Also applies to: 27-32
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
26-33: Prefer IDENTITY over SERIAL (modern PostgreSQL).Identity columns are the recommended replacement and avoid sequence naming collisions.
Apply:
- CREATE TABLE %s ( - id SERIAL PRIMARY KEY, + CREATE TABLE %s ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, ltree1 LTREE, ltree2 LTREE, ltree3 LTREE )src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (2)
9-23: Doc: include a 2-arg example.Helps users discover the optional length parameter.
Apply:
/** * Implementation of PostgreSQL subpath function. * * Returns subpath of ltree starting at position offset, with length len. * If offset is negative, subpath starts that far from the end of the path. * If len is negative, leaves that many labels off the end of the path. * * @see https://www.postgresql.org/docs/current/ltree.html#LTREE-FUNCTIONS * @since 3.5 * * @author Martin Georgiev <martin.georgiev@gmail.com> * - * @example Using it in DQL: "SELECT SUBPATH(e.path, 0, 2) FROM Entity e" + * @example Using it in DQL (2 args): "SELECT SUBPATH(e.path, 1) FROM Entity e" + * @example Using it in DQL (3 args): "SELECT SUBPATH(e.path, 0, 2) FROM Entity e" * Returns ltree, subpath starting at position offset, with length len. */
26-30: Support 2-arg parsing first to avoid unnecessary retries.Adding an explicit 2-arg pattern is slightly clearer and avoids a failed-attempt cycle.
Apply:
protected function getNodeMappingPattern(): array { - return ['StringPrimary,SimpleArithmeticExpression,SimpleArithmeticExpression']; + return [ + 'StringPrimary,SimpleArithmeticExpression', + 'StringPrimary,SimpleArithmeticExpression,SimpleArithmeticExpression', + ]; }tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
11-35: Optional: add boundary/negative tests for argument countConsider adding cases that assert:
- too few args (1) → InvalidArgumentForVariadicFunctionException
- too many args (>8) → InvalidArgumentForVariadicFunctionException
This strengthens parser-contract coverage.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
9-22: Doc nit: clarify indexing semantics (label-based, 0-based)Add a brief note that positions are label-based and 0-indexed; offset is also label-based (negative values start from the end).
Apply this doc tweak:
/** * Implementation of PostgreSQL index function for ltree. * - * Returns position of first occurrence of b in a, or -1 if not found. - * The search starts at position offset; negative offset means start -offset labels from the end of the path. + * Returns position of first occurrence of b in a, or -1 if not found. + * Positions are label-based and 0-indexed. The search starts at label index "offset"; + * negative offset means start -offset labels from the end of the path. * * @see https://www.postgresql.org/docs/current/ltree.html#LTREE-FUNCTIONS * @since 3.5tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
51-57: Optional: add a “no common ancestor” caseAdd a case like LCA('Foo.Bar', 'Baz.Qux') expecting the empty path to cover that branch.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php (1)
12-50: Optional: add parameterized-argument coverageAdd cases with bound parameters for the needle and offset (e.g., INDEX(l.ltree1, :needle, :offset)) to validate parsing of InputParameter within SimpleArithmeticExpression/StringPrimary.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (1)
12-50: Optional: add parameterized offset/length casesConsider SUBPATH(l.ltree1, :offset, :len) and SUBPATH(l.ltree1, :offset) to ensure bound parameters are parsed as SimpleArithmeticExpression.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (25)
docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md(4 hunks)docs/LTREE-TYPE.md(1 hunks)fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php(1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
📚 Learning: 2025-03-29T03:31:17.114Z
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.
Applied to files:
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-05-23T11:11:57.951Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, and therefore don't need an explicit import.
Applied to files:
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-03-29T03:31:17.114Z
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.
Applied to files:
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-09-01T18:48:28.508Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Applied to files:
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-08-24T16:52:32.488Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Applied to files:
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phpdocs/LTREE-TYPE.mdtests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phpdocs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md
📚 Learning: 2025-04-22T00:03:37.733Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#357
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpMatchTest.php:28-62
Timestamp: 2025-04-22T00:03:37.733Z
Learning: This project focuses on providing Doctrine ORM interfaces to PostgreSQL functions. Tests should validate correct DQL-to-SQL translation rather than PostgreSQL functionality itself. Test cases should focus on parameter passing and SQL generation, not on testing specific PostgreSQL regex pattern behaviors.
Applied to files:
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phpsrc/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-08-13T05:48:00.893Z
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
Applied to files:
docs/LTREE-TYPE.mdtests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
📚 Learning: 2025-08-25T21:35:48.228Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-25T21:35:48.228Z
Learning: Extensions in integration tests should be created in the createTestSchema() method of the base TestCase, following the established PostGIS pattern with proper error handling, not in individual test createXExtension() methods.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
📚 Learning: 2025-05-03T00:18:09.078Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#366
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayPositionsTest.php:1-36
Timestamp: 2025-05-03T00:18:09.078Z
Learning: Always check the current official PostgreSQL documentation at https://www.postgresql.org/docs/ to verify function signatures and behaviors before suggesting improvements or additional tests. The `array_positions` function specifically has a fixed signature with exactly two parameters (array and search value), while `array_position` is variadic with 2-3 parameters.
Applied to files:
docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md
🧬 Code graph analysis (22)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (5)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (3)
BaseFunction(20-105)setFunctionPrototype(57-60)addNodeMapping(62-65)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
customizeFunction(24-30)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
customizeFunction(24-28)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (5)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (3)
BaseFunction(20-105)setFunctionPrototype(57-60)addNodeMapping(62-65)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
customizeFunction(24-30)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
customizeFunction(24-28)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
Index(23-44)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
Index(23-44)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (5)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
Ltree2text(22-29)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (2)
Ltree2textTest(11-33)getStringFunctions(13-18)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
Text2ltree(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-143)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (4)
getNodeMappingPattern(25-28)getFunctionName(30-33)getMinArgumentCount(35-38)getMaxArgumentCount(40-43)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (4)
getNodeMappingPattern(26-29)getFunctionName(31-34)getMinArgumentCount(36-39)getMaxArgumentCount(41-44)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (5)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (3)
BaseFunction(20-105)setFunctionPrototype(57-60)addNodeMapping(62-65)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
customizeFunction(24-30)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
Nlevel(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (1)
Lca(23-44)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (1)
Lca(23-44)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (2)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
Ltree2text(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (2)
Ltree2textTest(10-42)getStringFunctions(12-17)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-143)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (4)
getNodeMappingPattern(25-28)getFunctionName(30-33)getMinArgumentCount(35-38)getMaxArgumentCount(40-43)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (4)
getNodeMappingPattern(26-29)getFunctionName(31-34)getMinArgumentCount(36-39)getMaxArgumentCount(41-44)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
Text2ltree(22-29)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (3)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-143)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (4)
getNodeMappingPattern(25-28)getFunctionName(30-33)getMinArgumentCount(35-38)getMaxArgumentCount(40-43)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
Subltree(22-31)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (2)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)tests/Integration/MartinGeorgiev/TestCase.php (2)
createTestSchema(160-191)dropTestTableIfItExists(252-259)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (1)
Subpath(24-45)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (2)
SubpathTest(10-50)getStringFunctions(12-17)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (3)
getStringFunctions(13-18)getExpectedSqlStatements(20-25)getDqlStatements(27-32)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
Subltree(22-31)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (2)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
Nlevel(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (2)
NlevelTest(10-42)getStringFunctions(12-17)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (3)
BaseFunction(20-105)setFunctionPrototype(57-60)addNodeMapping(62-65)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
customizeFunction(24-28)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
customizeFunction(24-28)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (1)
Subpath(24-45)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
🪛 LanguageTool
docs/LTREE-TYPE.md
[style] ~286-~286: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...1') → 1 ``` #### INDEX(a, b, offset) Returns the position of the first occurrence of...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md
[grammar] ~54-~54: There might be a mistake here.
Context: ...al data processing. - Ltree Functions - Includes: Path manipulation functions, a...
(QB_NEW_EN)
[grammar] ~126-~126: There might be a mistake here.
Context: ...ing, type casting ### Ltree Functions - Path Operations: Extract subpaths, man...
(QB_NEW_EN)
[grammar] ~127-~127: There might be a mistake here.
Context: ... subpaths, manipulate hierarchical paths - Ancestor Operations: Find common ances...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ... common ancestors, calculate path levels - Type Conversion: Convert between ltree...
(QB_NEW_EN)
[grammar] ~145-~145: There might be a mistake here.
Context: ...and path manipulation → Ltree Functions --- 📖 For More Information: - [Available ...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.1
- GitHub Check: sloth
- GitHub Check: PHP 8.4 + Doctrine ORM 2.18 + Doctrine Lexer latest
- GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer latest
- GitHub Check: PHP 8.4 + 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 latest + Doctrine Lexer latest
- 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.2 + Doctrine ORM 2.14 + Doctrine Lexer latest
- GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer 2.1
- GitHub Check: PHP 8.1 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: wait-for-tests-worflows-before-upload
🔇 Additional comments (16)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
24-28: LGTM: correct prototype and node mapping
nlevel(%s)withStringPrimarymatches the library pattern for single-arg LTREE functions and will dispatch correctly via BaseFunction.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
24-28: LGTM: faithful DQL→SQL mapping
text2ltree(%s)andStringPrimaryare consistent with sibling LTREE functions.tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (1)
10-17: LGTM: wiring and assertions look solidFunction is registered correctly; queries target seeded rows; assertions check exact cast result.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (1)
19-41: LGTM: effective coverage of typical pathsCovers multi-level, shorter sibling path, and single-node; good real-data assertions.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php (1)
24-28: LGTM: correct prototype and node mapping.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php (1)
24-30: LGTM: prototype and node mappings match PostgreSQL’s subltree(start, end) signature
- Uses
subltree(%s, %s, %s)andStringPrimary, SimpleArithmeticExpression, SimpleArithmeticExpressionconsistently with other LTREE functions. Docs/readability look good.tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (1)
11-33: LGTM: correct DQL-to-SQL mapping for NLEVEL()Function wiring and expectations align with implementation and project patterns.
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php (1)
13-18: LGTM: correct registration and SQL/DQL assertions for SUBLTREE.Function mapping, expected SQL, and DQL align with Subltree::class and the project's unit-test conventions.
Also applies to: 20-25, 27-32
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php (1)
13-18: LGTM: mapping and SQL/DQL for LTREE2TEXT are consistent.Also applies to: 20-25, 27-32
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
22-36: LGTM: schema and fixture setup match the project pattern.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (1)
36-44: LGTM: min/max argument counts correctly reflect SUBPATH semantics.tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
13-18: LGTM: correct DQL registration and SQL/DQL expectations
- Registration maps DQL LCA to the implementation as expected.
- SQL/DQL templates look consistent with BaseVariadicFunction dispatch.
Also applies to: 20-34
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
25-43: LGTM: INDEX implementation matches parser contract (2–3 args)
- Node mapping and min/max bounds are appropriate.
- Function name aligns with SQL emission.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
27-33: LGTM: three-path LCA expectationThe common ancestor across the three inputs in row 1 is 'Top'.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php (1)
12-50: LGTM: INDEX happy-paths incl. negative offsetCovers found/not-found and offset variants well.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (1)
12-50: LGTM: SUBPATH coverage across offsets and negative lengthAssertions align with PostgreSQL ltree semantics.
| #[Test] | ||
| public function computes_longest_common_ancestor_of_two_paths(): void | ||
| { | ||
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; | ||
| $result = $this->executeDqlQuery($dql); | ||
| $this->assertSame('Top', $result[0]['result']); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix expected LCA for two paths (should be 'Top.Child1')
For ltree1='Top.Child1.Child2' and ltree2='Top.Child1', the longest common ancestor is 'Top.Child1', not 'Top'.
Apply:
- $this->assertSame('Top', $result[0]['result']);
+ $this->assertSame('Top.Child1', $result[0]['result']);📝 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.
| #[Test] | |
| public function computes_longest_common_ancestor_of_two_paths(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('Top', $result[0]['result']); | |
| } | |
| #[Test] | |
| public function computes_longest_common_ancestor_of_two_paths(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('Top.Child1', $result[0]['result']); | |
| } |
🤖 Prompt for AI Agents
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
lines 19-25: the test expects the LCA of 'Top.Child1.Child2' and 'Top.Child1' to
be 'Top' but the correct longest common ancestor is 'Top.Child1'; update the
assertion to assertSame('Top.Child1', $result[0]['result']) so the test
validates the correct LCA value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All LCA integration tests are passing. Why this is not correct, @coderabbitai?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@martin-georgiev I need to verify the actual test data being used to understand why the test is passing. Let me check the test setup:
🏁 Script executed:
#!/bin/bash
# Check the actual test data setup in LtreeTestCase
cat tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
Length of output: 1480
Now I can see the test data. Let me check PostgreSQL's LCA function documentation to understand its exact behavior:
🌐 Web query:
PostgreSQL ltree LCA function behavior documentation
💡 Result:
Summary: ltree’s lca(...) computes the longest common ancestor (common prefix) of the given ltree paths (supports up to 8 args or an array) — see the official docs. However, the implementation historically (and in released PostgreSQL versions) has an unexpected behavior: when one path is an ancestor/prefix of another, lca may not return the full shorter path; users have reported cases like lca('1.2','1.2.3') returning '1' instead of '1.2'. This behavior is documented by the function table in the PostgreSQL manual and discussed on the PostgreSQL mailing lists and source code (ltree_op.c). See:
- PostgreSQL ltree documentation (functions table, including lca). (postgresql.org)
- Mailing-list discussion about lca('1.2','1.2.3') returning '1' (explanation and decision not to change behavior). (postgresql.org, postgrespro.com)
- ltree source (ltree_op.c) for the implementation. (doxygen.postgresql.org)
If you want, I can:
- show concrete examples demonstrating the observed behavior on your PostgreSQL version, or
- suggest a SQL wrapper that returns the "deepest common ancestor treating a node as its own descendant" if you need that alternate semantics.
Citations:
- 1: https://www.postgresql.org/docs/current/ltree.html?utm_source=openai
- 2: https://www.postgresql.org/message-id/CAKnjZ0KTVw63SM5wkWqAkNRv5%2B-GPXF8r60vtssvhOWJ3N5otA%40mail.gmail.com?utm_source=openai
- 3: https://www.postgrespro.com/list/thread-id/2358316?utm_source=openai
- 4: https://doxygen.postgresql.org/ltree__op_8c_source.html?utm_source=openai
.
In your test case, LCA('Top.Child1.Child2', 'Top.Child1') correctly returns 'Top' because 'Top.Child1' is a prefix of 'Top.Child1.Child2', so PostgreSQL returns the parent of the shorter path, which is 'Top'.
Your test expectation is correct, and I was wrong to suggest changing it. Thank you for the correction!
✏️ Learnings added
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:19-25
Timestamp: 2025-09-06T11:30:43.347Z
Learning: PostgreSQL's LCA (Longest Common Ancestor) function for ltree has specific behavior: when one path is a prefix/ancestor of another, it returns the parent of the shorter path, not the shorter path itself. For example, lca('Top.Child1.Child2', 'Top.Child1') returns 'Top', not 'Top.Child1'. This is documented PostgreSQL behavior and should not be considered incorrect in tests.
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.
🧠 Learnings used
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, 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`), 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.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
| #[Test] | ||
| public function computes_longest_common_ancestor_with_different_paths(): void | ||
| { | ||
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 2'; | ||
| $result = $this->executeDqlQuery($dql); | ||
| $this->assertSame('A', $result[0]['result']); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix expected LCA with different paths (should be 'A.B')
For ltree1='A.B.C.D' and ltree2='A.B', the LCA is 'A.B'.
Apply:
- $this->assertSame('A', $result[0]['result']);
+ $this->assertSame('A.B', $result[0]['result']);📝 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.
| #[Test] | |
| public function computes_longest_common_ancestor_with_different_paths(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 2'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('A', $result[0]['result']); | |
| } | |
| #[Test] | |
| public function computes_longest_common_ancestor_with_different_paths(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 2'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('A.B', $result[0]['result']); | |
| } |
🤖 Prompt for AI Agents
In
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
around lines 35 to 41, the test expects the LCA result to be 'A' but for
ltree1='A.B.C.D' and ltree2='A.B' the correct LCA is 'A.B'; update the assertion
to expect 'A.B' (e.g., change $this->assertSame('A', $result[0]['result']) to
assert 'A.B') so the test reflects the correct longest common ancestor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All LCA integration tests are passing. Why this is not correct, @coderabbitai?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Let me check the actual test data to understand what values are being used for id=2.
Now let me run a quick test to see what PostgreSQL's LCA function actually returns for these values:
@martin-georgiev You're absolutely right, and I apologize for the confusion. If the LCA integration tests are passing with the current assertion expecting 'A', then my suggestion was incorrect.
Looking at the test data in LtreeTestCase, for id=2 we have:
- ltree1='A.B.C.D'
- ltree2='A.B'
I incorrectly assumed that LCA('A.B.C.D', 'A.B') should return 'A.B', but if your tests are passing with the expectation of 'A', then PostgreSQL's LCA function must be behaving differently than I expected.
Thank you for the correction - the current test assertion is correct as written. I should have verified the actual PostgreSQL LCA behavior before suggesting a change.
🐰
╱|、
(˚ˎ。7
|、˜〵
じしˍ,)ノ
🏁 Script executed:
#!/bin/bash
# Check the test data setup in LtreeTestCase to see what values are inserted for id=2
cat tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
Length of output: 1480
🏁 Script executed:
#!/bin/bash
# Let's also check if there are any other integration test files that might give us insight
# into the expected behavior
rg -n "A\.B" tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/ -A3 -B3
Length of output: 1111
.
Your test assertion expecting 'A' for LCA('A.B.C.D', 'A.B') is correct according to PostgreSQL's LCA function implementation. Thank you for the correction - the test is properly written and my suggestion was based on an incorrect assumption about how the LCA function works.
🐰
╱|、
(˚ˎ。7
|、˜〵
じしˍ,)ノ
✏️ Learnings added
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types may not always return the full longest common ancestor path. For ltree1='A.B.C.D' and ltree2='A.B', PostgreSQL's LCA function returns 'A' rather than 'A.B' as might be mathematically expected.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types does not return the full longest common ancestor path. For example, LCA('A.B.C.D', 'A.B') returns 'A' rather than 'A.B'. This is the documented behavior that PostgreSQL developers chose to maintain to avoid breaking existing code.
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.
⛔ Learnings removed
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.
🧠 Learnings used
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#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, 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.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
| #[Test] | ||
| public function computes_longest_common_ancestor_with_single_node(): void | ||
| { | ||
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; | ||
| $result = $this->executeDqlQuery($dql); | ||
| $this->assertSame('', $result[0]['result']); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix expected LCA with single-node vs child (should be 'Root')
For ltree1='Root' and ltree2='Root.Leaf', the LCA is 'Root', not the empty path.
Apply:
- $this->assertSame('', $result[0]['result']);
+ $this->assertSame('Root', $result[0]['result']);📝 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.
| #[Test] | |
| public function computes_longest_common_ancestor_with_single_node(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('', $result[0]['result']); | |
| } | |
| #[Test] | |
| public function computes_longest_common_ancestor_with_single_node(): void | |
| { | |
| $dql = 'SELECT LCA(l.ltree1, l.ltree2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; | |
| $result = $this->executeDqlQuery($dql); | |
| $this->assertSame('Root', $result[0]['result']); | |
| } |
🤖 Prompt for AI Agents
In
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
around lines 43 to 49, the test currently asserts an empty string for the LCA of
ltree1='Root' and ltree2='Root.Leaf'; update the expected result to 'Root' so
the assertion reads that the LCA is 'Root' (i.e. change $this->assertSame('',
$result[0]['result']); to assert 'Root'). Ensure the test description remains
accurate for the single-node vs child case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All LCA integration tests are passing. Why this is not correct, @coderabbitai?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@martin-georgiev You're absolutely right, and I apologize for the incorrect assessment. Looking at the test data setup in LtreeTestCase, I can see that for id=3, the values are:
- ltree1: 'Root'
- ltree2: 'Root.Leaf'
However, if all the LCA integration tests are passing with the current assertion of an empty string, this means PostgreSQL's LCA function is indeed returning an empty string for these inputs, not 'Root' as I incorrectly assumed.
This suggests there might be a specific behavior in PostgreSQL's LCA function that I wasn't accounting for, or there could be some other aspect of the test data or function behavior. Since your tests are passing, the current implementation and test assertion are correct.
Thank you for the correction - the test as written is accurate and I should not have flagged it as an issue.
🏁 Script executed:
#!/bin/bash
# Let's examine the test data setup to understand what values are actually being inserted
cat tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
Length of output: 1480
🏁 Script executed:
#!/bin/bash
# Let's also check if there are any database schema files or SQL files that show the test data
find . -name "*.sql" -o -name "*fixture*" -o -name "*seed*" | head -10
Length of output: 83
🏁 Script executed:
#!/bin/bash
# Let's look at the ContainsLtrees fixture to understand the data structure
cat fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php
Length of output: 421
For paths 'Root' and 'Root.Leaf', the longest common ancestor should logically be 'Root'.
If your tests are passing with an empty string assertion, there might be:
- A specific PostgreSQL ltree LCA behavior I'm not familiar with
- Test data that's different than what I'm seeing in the setup
- An actual bug in either the test or the function implementation
Could you help me understand why the LCA of 'Root' and 'Root.Leaf' would return an empty string? Is there something about your PostgreSQL version, the ltree extension setup, or the test execution that would explain this behavior?
The mathematical expectation would be that LCA('Root', 'Root.Leaf') = 'Root', so I'm curious about what's causing the different result in your environment.
✏️ Learnings added
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
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.
🧠 Learnings used
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#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, 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.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value for ltree1 for the tested fixture row (id=3) has only a root, so the parent(ancestors) is an empty string. Therefore, the ancestor of all is an empty string.
If the tested fixture row were id=1, then it would have been what you expect here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@martin-georgiev Thank you for the clarification! Now I understand the behavior. Looking at the test data setup, for id=3:
- ltree1 = 'Root' (single node)
- ltree2 = 'Root.Leaf'
Since 'Root' is a single node with no parent, PostgreSQL's LCA function returns an empty string when one of the paths has no ancestors. This is the documented PostgreSQL behavior - the LCA function returns the parent of the common path, and since 'Root' has no parent, the result is an empty string.
You're absolutely correct that the test assertion is accurate. I apologize for the confusion in my initial review.
🐰 tilts head thoughtfully, ears perked with newfound understanding
🏁 Script executed:
#!/bin/bash
# Let's examine the test data setup to understand what values are being inserted for each ID
cat tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php | grep -A 10 -B 5 "INSERT INTO"
Length of output: 574
@martin-georgiev Perfect! Now I can see the exact test data. You're absolutely right:
For id=3:
- ltree1 = 'Root' (single node with no parent)
- ltree2 = 'Root.Leaf'
PostgreSQL's LCA function returns an empty string because 'Root' has no ancestor/parent. This is the correct documented behavior - when one of the paths is a root node with no parent, the LCA result is an empty string.
The test assertion $this->assertSame('', $result[0]['result']); is completely correct. I apologize for the incorrect suggestion - your implementation and tests are accurate representations of PostgreSQL's LCA function behavior.
Thank you for the clarification and for taking the time to explain the PostgreSQL ltree LCA semantics!
🐰 nods with understanding, ears drooping slightly in acknowledgment of the mistake
🧠 Learnings used
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:19-25
Timestamp: 2025-09-06T11:30:43.347Z
Learning: PostgreSQL's LCA (Longest Common Ancestor) function for ltree has specific behavior: when one path is a prefix/ancestor of another, it returns the parent of the shorter path, not the shorter path itself. For example, lca('Top.Child1.Child2', 'Top.Child1') returns 'Top', not 'Top.Child1'. This is documented PostgreSQL behavior and should not be considered incorrect in tests.
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#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, and therefore don't need an explicit import.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types may not always return the full longest common ancestor path. For ltree1='A.B.C.D' and ltree2='A.B', PostgreSQL's LCA function returns 'A' rather than 'A.B' as might be mathematically expected.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types does not return the full longest common ancestor path. For example, LCA('A.B.C.D', 'A.B') returns 'A' rather than 'A.B'. This is the documented behavior that PostgreSQL developers chose to maintain to avoid breaking existing code.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: tests/Unit/MartinGeorgiev/Doctrine/DBAL/Types/LtreeTest.php:58-66
Timestamp: 2025-08-09T15:08:49.108Z
Learning: PostgreSQL's `ltree` extension accepts empty strings (`''`) as valid input, representing a path with no labels. This is valid in PostgreSQL 17 and likely earlier versions as well. Empty ltree values can be inserted and stored without syntax errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (9)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (1)
27-33: Add a parameterized variant to exercise binding.Covers TEXT2LTREE(:value) with Doctrine parameter handling.
You can add:
#[Test] public function can_cast_bound_text_to_ltree(): void { $dql = "SELECT TEXT2LTREE(:val) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; $result = $this->executeDqlQuery($dql, ['val' => 'Top.Child1.Child2']); $this->assertSame('Top.Child1.Child2', $result[0]['result']); }docs/LTREE-TYPE.md (2)
298-306: Document LCA’s prefix behavior (parent-of-shorter, empty-root case).Avoid confusion by calling out PostgreSQL’s semantics explicitly (as reflected by the integration tests).
Apply:
#### `LCA(ltree1, ltree2, ...)` Computes the longest common ancestor of multiple paths (up to 8 arguments supported). ```php // DQL $dql = "SELECT LCA(e.path1, e.path2, e.path3) FROM Entity e"; // SQL: lca(e.path1, e.path2, e.path3) // Example: lca('Top.Child1.Child2', 'Top.Child1', 'Top.Child2.Child3') → 'Top'
+Note:
+- If one path is a prefix (ancestor) of another, PostgreSQL returns the parent of the shorter path, not the shorter path itself.
- Example: lca('Top.Child1.Child2', 'Top.Child1') → 'Top'
- Example: lca('Root', 'Root.Leaf') → '' (empty path)
--- `276-294`: **Minor wording polish for INDEX docs.** Reduce repetition and match both arities in one place. ```diff -#### `INDEX(a, b)` -Returns the position of the first occurrence of `b` in `a`, or -1 if not found. +#### `INDEX(a, b)` / `INDEX(a, b, offset)` +Finds the position of the first occurrence of `b` in `a` (starting at `offset` when provided), or -1 if not found. @@ -#### `INDEX(a, b, offset)` -Returns the position of the first occurrence of `b` in `a` starting at position `offset`. +For `offset`, negative values start counting from the end of the path.Optionally add an example with a negative offset for completeness.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
9-22: Consider adding a negative-offset example in the PHPDoc.You already describe it in prose; a concrete example helps users.
* @example Using it in DQL: "SELECT INDEX(e.path, 'Child1') FROM Entity e" * Returns integer, position of first occurrence or -1 if not found. + * @example With offset (including negative): "SELECT INDEX(e.path, 'Child1', -2) FROM Entity e"tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (2)
46-49: Add a brief inline comment to clarify empty-result semantics.Preempts confusion for readers encountering the empty string result.
- $this->assertSame('', $result[0]['result']); + // When one path is a direct ancestor, LCA returns the parent of the shorter path: parent('Root') => ''. + $this->assertSame('', $result[0]['result']);
51-57: Optional: add a boundary test for 8 arguments.Covers the upper variadic limit defined in Lca::getMaxArgumentCount().
Example:
#[Test] public function can_compute_longest_common_ancestor_with_eight_paths(): void { $dql = "SELECT LCA('A.B.C', 'A.B', 'A.X', 'A', 'A.Y.Z', 'A.B.D', 'A.BZ', 'A.Q') as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; $result = $this->executeDqlQuery($dql); $this->assertSame('A', $result[0]['result']); }tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
38-56: Exception coverage looks good; consider a positive max-arity case.You already test too-low and too-high. Add an “exactly 8 args compiles” assertion to lock the boundary.
#[Test] public function allows_exactly_eight_arguments(): void { $dql = \sprintf("SELECT LCA(e.text1, e.text2, 'A', 'B', 'C', 'D', 'E', 'F') FROM %s e", ContainsTexts::class); $this->buildEntityManager()->createQuery($dql)->getSQL(); // should not throw $this->addToAssertionCount(1); }tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (2)
22-28: Add negative offset/length examples to strengthen translation coverageOptional: mirror the integration tests by adding negative offset and negative length cases here to validate SQL generation for signed integers as well.
Apply:
protected function getExpectedSqlStatements(): array { return [ 'extracts subpath with offset and length' => 'SELECT subpath(c0_.text1, 0, 2) AS sclr_0 FROM ContainsTexts c0_', 'extracts subpath with offset only' => 'SELECT subpath(c0_.text1, 1) AS sclr_0 FROM ContainsTexts c0_', + 'extracts subpath with negative offset' => 'SELECT subpath(c0_.text1, -2) AS sclr_0 FROM ContainsTexts c0_', + 'extracts subpath with negative length' => 'SELECT subpath(c0_.text1, 0, -1) AS sclr_0 FROM ContainsTexts c0_', ]; }
30-36: Keep DQL list in sync with SQL list (add negative cases too)Optional: add matching DQL statements for negative offset/length to ensure the base TestCase cross-validates both directions.
Apply:
protected function getDqlStatements(): array { return [ 'extracts subpath with offset and length' => \sprintf('SELECT SUBPATH(e.text1, 0, 2) FROM %s e', ContainsTexts::class), 'extracts subpath with offset only' => \sprintf('SELECT SUBPATH(e.text1, 1) FROM %s e', ContainsTexts::class), + 'extracts subpath with negative offset' => \sprintf('SELECT SUBPATH(e.text1, -2) FROM %s e', ContainsTexts::class), + 'extracts subpath with negative length' => \sprintf('SELECT SUBPATH(e.text1, 0, -1) FROM %s e', ContainsTexts::class), ]; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/LTREE-TYPE.md(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/WebsearchToTsquery.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php(1 hunks)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/WebsearchToTsquery.php
🚧 Files skipped from review as they are similar to previous changes (7)
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php
- tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/IndexTest.php
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2text.php
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subltree.php
- src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: tests/Unit/MartinGeorgiev/Doctrine/DBAL/Types/LtreeTest.php:58-66
Timestamp: 2025-08-09T15:08:49.108Z
Learning: PostgreSQL's `ltree` extension accepts empty strings (`''`) as valid input, representing a path with no labels. This is valid in PostgreSQL 17 and likely earlier versions as well. Empty ltree values can be inserted and stored without syntax errors.
📚 Learning: 2025-09-06T11:30:17.885Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phpdocs/LTREE-TYPE.md
📚 Learning: 2025-03-29T03:31:17.114Z
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.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-05-23T11:11:57.951Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, and therefore don't need an explicit import.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-08-24T16:52:32.488Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.phpdocs/LTREE-TYPE.md
📚 Learning: 2025-09-01T18:48:28.508Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-09-06T11:30:43.347Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:19-25
Timestamp: 2025-09-06T11:30:43.347Z
Learning: PostgreSQL's LCA (Longest Common Ancestor) function for ltree has specific behavior: when one path is a prefix/ancestor of another, it returns the parent of the shorter path, not the shorter path itself. For example, lca('Top.Child1.Child2', 'Top.Child1') returns 'Top', not 'Top.Child1'. This is documented PostgreSQL behavior and should not be considered incorrect in tests.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phptests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phpdocs/LTREE-TYPE.md
📚 Learning: 2025-09-06T11:30:50.960Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types may not always return the full longest common ancestor path. For ltree1='A.B.C.D' and ltree2='A.B', PostgreSQL's LCA function returns 'A' rather than 'A.B' as might be mathematically expected.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phpdocs/LTREE-TYPE.md
📚 Learning: 2025-09-06T11:30:50.960Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types does not return the full longest common ancestor path. For example, LCA('A.B.C.D', 'A.B') returns 'A' rather than 'A.B'. This is the documented behavior that PostgreSQL developers chose to maintain to avoid breaking existing code.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.phpdocs/LTREE-TYPE.md
📚 Learning: 2025-04-22T00:03:37.733Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#357
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpMatchTest.php:28-62
Timestamp: 2025-04-22T00:03:37.733Z
Learning: This project focuses on providing Doctrine ORM interfaces to PostgreSQL functions. Tests should validate correct DQL-to-SQL translation rather than PostgreSQL functionality itself. Test cases should focus on parameter passing and SQL generation, not on testing specific PostgreSQL regex pattern behaviors.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-08-13T05:48:00.893Z
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
Applied to files:
docs/LTREE-TYPE.md
🧬 Code graph analysis (5)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-143)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (4)
getNodeMappingPattern(24-27)getFunctionName(29-32)getMinArgumentCount(34-37)getMaxArgumentCount(39-42)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (4)
getNodeMappingPattern(26-32)getFunctionName(34-37)getMinArgumentCount(39-42)getMaxArgumentCount(44-47)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (1)
Lca(22-43)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-56)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (1)
Lca(22-43)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1)
buildEntityManager(115-118)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltree.php (1)
Text2ltree(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-56)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (1)
Subpath(24-48)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (2)
SubpathTest(10-50)getStringFunctions(12-17)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (1)
buildEntityManager(115-118)
🪛 LanguageTool
docs/LTREE-TYPE.md
[style] ~286-~286: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...1') → 1 ``` #### INDEX(a, b, offset) Returns the position of the first occurrence of...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.4
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.4
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.2
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
- GitHub Check: sloth
- GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer 3.0
- GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
- GitHub Check: PHP 8.4 + Doctrine ORM 2.18 + 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: wait-for-tests-worflows-before-upload
🔇 Additional comments (11)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Text2ltreeTest.php (3)
12-17: Registration looks correct.Function alias maps to the right class; consistent with other LTREE tests.
19-25: Solid happy-path assertion.Covers multi-segment cast and DQL wiring via the shared fixture.
35-41: Good edge-case: empty-string cast.PostgreSQL accepts empty ltree; keeping this guard prevents regressions.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Index.php (1)
25-31: Node mapping and arities are appropriate.2–3 args, with numeric third arg, matches the PostgreSQL variants; no parser pitfalls with the chosen patterns.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (2)
19-25: LGTM: matches PostgreSQL LCA semantics.Prefix case correctly expects the parent of the shorter path ('Top'). Verified against repository learnings/tests.
35-41: LGTM: mixed-depth case covered.Ensures behavior when common prefix is shorter than the shorter input.
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (1)
22-36: SQL generation expectations are precise.Covers two and three args and a literal mixed in; aligns with the integration mapping.
tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (4)
1-12: Solid test skeleton and imports are correctNamespace, strict types, fixtures import, and extending the project TestCase are all correct and consistent with the repo’s patterns. Using #[Test] attributes is aligned with current PHPUnit usage.
15-20: Correct DQL function registrationMapping SUBPATH to the Subpath class via getStringFunctions() is accurate and consistent with other LTREE tests.
38-46: Exception for too few args is preciseMessage matches InvalidArgumentForVariadicFunctionException::atLeast() and is triggered correctly via getSQL() parsing.
48-56: Exception for too many args is preciseMessage matches InvalidArgumentForVariadicFunctionException::between() and the call site is appropriate to trigger parsing validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (6)
7-9: Import fixture and use ::class for refactor-safetyAvoid hard-coded FQCN strings in DQL. Import the fixture and use ::class like other tests.
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ltree\Subpath; +use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees; use PHPUnit\Framework\Attributes\Test;
22-22: Use ::class instead of backslash-escaped FQCN in DQLImproves readability and makes refactors safer; aligns with unit tests.
-$dql = 'SELECT SUBPATH(l.ltree1, 0, 2) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; +$dql = \sprintf('SELECT SUBPATH(l.ltree1, 0, 2) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class);-$dql = 'SELECT SUBPATH(l.ltree1, 1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; +$dql = \sprintf('SELECT SUBPATH(l.ltree1, 1) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class);-$dql = 'SELECT SUBPATH(l.ltree1, -1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; +$dql = \sprintf('SELECT SUBPATH(l.ltree1, -1) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class);-$dql = 'SELECT SUBPATH(l.ltree1, 0, -1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; +$dql = \sprintf('SELECT SUBPATH(l.ltree1, 0, -1) as result FROM %s l WHERE l.id = 1', ContainsLtrees::class);Also applies to: 30-30, 38-38, 46-46
23-25: Add a quick cardinality checkGuards against empty or duplicated rows if fixture/setup changes.
$result = $this->executeDqlQuery($dql); +$this->assertCount(1, $result); $this->assertSame('Top.Child1', $result[0]['result']);
31-33: Add a quick cardinality checkSame reasoning as above.
$result = $this->executeDqlQuery($dql); +$this->assertCount(1, $result); $this->assertSame('Child1.Child2', $result[0]['result']);
39-41: Add a quick cardinality checkSame reasoning as above.
$result = $this->executeDqlQuery($dql); +$this->assertCount(1, $result); $this->assertSame('Child2', $result[0]['result']);
47-49: Add a quick cardinality checkSame reasoning as above.
$result = $this->executeDqlQuery($dql); +$this->assertCount(1, $result); $this->assertSame('Top.Child1', $result[0]['result']);tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (1)
22-25: Harden assertions and DQL: cast numeric scalar, parameterize id, and prefer ::class
- Doctrine often hydrates scalar numbers as strings; strict assertSame(3, '3') fails. Cast before asserting (or use assertEquals).
- Parameterize ids to avoid inline literals.
- Use the entity’s ::class to avoid escaped backslashes and ease refactors.
Apply:
- $dql = 'SELECT NLEVEL(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1'; - $result = $this->executeDqlQuery($dql); - $this->assertSame(3, $result[0]['result']); + $dql = 'SELECT NLEVEL(l.ltree1) as result FROM ' . ContainsLtrees::class . ' l WHERE l.id = :id'; + $result = $this->executeDqlQuery($dql, ['id' => 1]); + $this->assertSame(3, (int) $result[0]['result']);- $dql = 'SELECT NLEVEL(l.ltree1) as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 3'; - $result = $this->executeDqlQuery($dql); - $this->assertSame(1, $result[0]['result']); + $dql = 'SELECT NLEVEL(l.ltree1) as result FROM ' . ContainsLtrees::class . ' l WHERE l.id = :id'; + $result = $this->executeDqlQuery($dql, ['id' => 3]); + $this->assertSame(1, (int) $result[0]['result']);Additional change outside selected lines to support ::class:
+use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsLtrees;Also applies to: 30-33
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Ltree2textTest.php
- tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubltreeTest.php
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
📚 Learning: 2025-05-23T11:11:57.951Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, and therefore don't need an explicit import.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-03-29T03:31:17.114Z
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.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-08-24T16:52:32.488Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-09-06T11:30:17.885Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
📚 Learning: 2025-09-01T18:48:28.508Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.phptests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php
🧬 Code graph analysis (2)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Nlevel.php (1)
Nlevel(22-29)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (5)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Subpath.php (1)
Subpath(24-48)tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (3)
Test(38-46)SubpathTest(13-57)getStringFunctions(15-20)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-48)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: PHP 8.2 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.4
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.4
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.4
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.4
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.2
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.1
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.1
- GitHub Check: sloth
- GitHub Check: wait-for-tests-worflows-before-upload
🔇 Additional comments (4)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/SubpathTest.php (2)
1-5: LGTM: solid test scaffoldStrict types, namespace, and extending LtreeTestCase match the repo conventions.
Also applies to: 10-11
12-17: LGTM: function registration is correctMaps SUBPATH to the Subpath class as expected.
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/NlevelTest.php (2)
12-17: LGTM: Function registration is correct and consistent'NLEVEL' maps to the implementation as expected and matches usage in the DQL.
7-9: PHPUnit attributes are supported: composer.json require-dev specifies phpunit/phpunit ^10.5.53, so #[Test] attributes are valid.
LTREE data typeLTREE data type
LTREE data typeLTREE data type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (2)
35-41: Nit: fix grammar and clarify intentRename “leafs”→“leaves” and add a short comment documenting the root-only behavior.
Apply:
- public function can_compute_longest_common_ancestor_to_be_empty_string_when_one_of_the_paths_has_only_a_root_with_no_leafs(): void + public function can_compute_longest_common_ancestor_to_be_empty_string_when_one_of_the_paths_has_only_a_root_with_no_leaves(): void @@ - $result = $this->executeDqlQuery($dql); + $result = $this->executeDqlQuery($dql); + // ltree LCA returns the parent of the shorter path; for a root-only path, the parent is ''.
43-49: Optional: add a “no common prefix” caseA disjoint-roots example would round out behavior coverage (should return empty string).
Append:
#[Test] public function returns_empty_string_when_no_common_prefix(): void { $dql = "SELECT LCA('X.Y', 'Z.Q') as result FROM Fixtures\\MartinGeorgiev\\Doctrine\\Entity\\ContainsLtrees l WHERE l.id = 1"; $result = $this->executeDqlQuery($dql); $this->assertSame('', $result[0]['result']); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php(1 hunks)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
📚 Learning: 2025-09-06T11:30:17.885Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:43-49
Timestamp: 2025-09-06T11:30:17.885Z
Learning: When reviewing LCA (Longest Common Ancestor) tests for PostgreSQL ltree functions in this codebase, the actual behavior should be verified against the real test results rather than making assumptions about what the function should return. The integration tests are the source of truth for expected behavior.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-03-29T03:31:17.114Z
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.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-05-23T11:11:57.951Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#383
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RadiansTest.php:1-9
Timestamp: 2025-05-23T11:11:57.951Z
Learning: Tests in the `Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions` namespace extend a custom `TestCase` class from the same namespace (`Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TestCase`), rather than PHPUnit's TestCase directly, and therefore don't need an explicit import.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-08-24T16:52:32.488Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-08-24T16:52:32.488Z
Learning: All new features in this repository must include proper test coverage before approval - this ltree implementation demonstrates exemplary test coverage with comprehensive unit tests, integration tests, data providers for edge cases, and validation of all relationships and error conditions.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-09-06T11:30:43.347Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:19-25
Timestamp: 2025-09-06T11:30:43.347Z
Learning: PostgreSQL's LCA (Longest Common Ancestor) function for ltree has specific behavior: when one path is a prefix/ancestor of another, it returns the parent of the shorter path, not the shorter path itself. For example, lca('Top.Child1.Child2', 'Top.Child1') returns 'Top', not 'Top.Child1'. This is documented PostgreSQL behavior and should not be considered incorrect in tests.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-09-01T18:48:28.508Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#434
File: tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/PostGIS/ST_CrossesTest.php:12-31
Timestamp: 2025-09-01T18:48:28.508Z
Learning: When analyzing unit test files in this codebase, always verify the actual file structure and existing patterns before suggesting changes. The ContainsGeometries fixture exists at ./fixtures/MartinGeorgiev/Doctrine/Entity/ContainsGeometries.php and the PostGIS unit tests use the namespace Tests\Unit\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\PostGIS consistently.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-09-06T11:30:50.960Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types may not always return the full longest common ancestor path. For ltree1='A.B.C.D' and ltree2='A.B', PostgreSQL's LCA function returns 'A' rather than 'A.B' as might be mathematically expected.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-09-06T11:30:50.960Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#440
File: tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php:35-41
Timestamp: 2025-09-06T11:30:50.960Z
Learning: PostgreSQL's LCA function for ltree types does not return the full longest common ancestor path. For example, LCA('A.B.C.D', 'A.B') returns 'A' rather than 'A.B'. This is the documented behavior that PostgreSQL developers chose to maintain to avoid breaking existing code.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-04-22T00:03:37.733Z
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#357
File: tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpMatchTest.php:28-62
Timestamp: 2025-04-22T00:03:37.733Z
Learning: This project focuses on providing Doctrine ORM interfaces to PostgreSQL functions. Tests should validate correct DQL-to-SQL translation rather than PostgreSQL functionality itself. Test cases should focus on parameter passing and SQL generation, not on testing specific PostgreSQL regex pattern behaviors.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-08-13T05:48:00.893Z
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: docs/LTREE-TYPE.md:72-77
Timestamp: 2025-08-13T05:48:00.893Z
Learning: In the ltree implementation, the Ltree class has built-in validation and doesn't accept invalid values. An empty branch [] is a valid ltree path. The setParent() method in ltree-based entities ensures there are no circular references, making constructor calls to setParent() safe.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
📚 Learning: 2025-08-09T15:08:49.108Z
Learnt from: landure
PR: martin-georgiev/postgresql-for-doctrine#411
File: tests/Unit/MartinGeorgiev/Doctrine/DBAL/Types/LtreeTest.php:58-66
Timestamp: 2025-08-09T15:08:49.108Z
Learning: PostgreSQL's `ltree` extension accepts empty strings (`''`) as valid input, representing a path with no labels. This is valid in PostgreSQL 17 and likely earlier versions as well. Empty ltree values can be inserted and stored without syntax errors.
Applied to files:
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php
🧬 Code graph analysis (1)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (4)
fixtures/MartinGeorgiev/Doctrine/Entity/ContainsLtrees.php (1)
ORM(9-20)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/Lca.php (1)
Lca(22-43)tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LtreeTestCase.php (1)
LtreeTestCase(9-49)tests/Integration/MartinGeorgiev/TestCase.php (1)
executeDqlQuery(284-298)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer latest
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.4
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.4
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.4
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.4
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.2
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.3
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.3
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.1
- GitHub Check: PostgreSQL 16 + PostGIS 3.4 + PHP 8.2
- GitHub Check: PostgreSQL 17 + PostGIS 3.5 + PHP 8.1
- GitHub Check: PostgreSQL 16 + PostGIS 3.5 + PHP 8.1
- GitHub Check: PostgreSQL 17 + PostGIS 3.4 + PHP 8.1
- GitHub Check: sloth
- GitHub Check: wait-for-tests-worflows-before-upload
🔇 Additional comments (3)
tests/Integration/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ltree/LcaTest.php (3)
12-17: LGTM: Correct DQL→SQL function mapping'LCA' is mapped to the correct implementation and follows established patterns.
19-25: LGTM: Prefix/ancestor case expectation is correctExpecting 'Top' for id=1 matches ltree's “parent-of-shorter-path” LCA semantics.
27-33: LGTM: Variadic args with mixed literals/columnsCovers >2 args and correctly expects '1.2.3'.
LTREE data typeLTREE data type
LTREE data typeLTREE data type
Summary by CodeRabbit
New Features
Documentation
Tests