From 2c68a758ad2d75af1245fcf80e0a1eaca81a6eae Mon Sep 17 00:00:00 2001 From: Remo Laubacher Date: Mon, 29 Jun 2026 13:03:10 +0200 Subject: [PATCH] Support $count on navigation properties in $filter (#871) Using `$count` on a collection-valued navigation property within a filter expression (e.g. `?$filter=passengers/$count eq 0`) previously threw a cryptic parser error ("Encountered an invalid symbol at: passengers/>$ WHERE )` subquery built from the navigation binding's referential constraints, so it can be compared like any other operand (eq, ne, gt, ge, lt, le) and combined with logical operators. This works on all SQL drivers including SQLite. Drivers that cannot express it (in-memory collection drivers and MongoDB) now throw a clear NotImplementedException instead of a parser error. Adds cross-dialect parser tests (generated SQL for MySQL, PostgreSQL, SQLite, SQLServer) and functional tests (Eloquent + SQL drivers). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Drivers/MongoEntitySet.php | 4 ++ src/Drivers/SQL/SQLExpression.php | 54 +++++++++++++++++++ .../Node/Property/Navigation/Count.php | 19 +++++++ src/Expression/Parser.php | 36 +++++++++++++ src/Expression/Parser/Common.php | 6 +++ src/Expression/Parser/Filter.php | 1 + tests/Filter/Database.php | 27 ++++++++++ tests/Filter/MongoTest.php | 15 ++++++ tests/Parser/Handlers/LoopbackEntitySet.php | 4 ++ tests/Parser/LambdaTest.php | 15 ++++++ ...t_filter_navigation_count_combined__1.json | 52 ++++++++++++++++++ ...t__test_filter_navigation_count_eq__1.json | 47 ++++++++++++++++ ...t__test_filter_navigation_count_gt__1.json | 38 +++++++++++++ ...t_filter_navigation_count_combined__1.json | 52 ++++++++++++++++++ ...t__test_filter_navigation_count_eq__1.json | 47 ++++++++++++++++ ...t__test_filter_navigation_count_gt__1.json | 38 +++++++++++++ ...est__test_count_logical_combination__1.txt | 2 + ...est__test_count_logical_combination__2.txt | 3 ++ ...est__test_count_logical_combination__3.txt | 3 ++ ...est__test_count_logical_combination__4.txt | 3 ++ ...est__test_count_logical_combination__5.txt | 3 ++ ...st__test_count_multiple_constraints__1.txt | 2 + ...st__test_count_multiple_constraints__2.txt | 3 ++ ...st__test_count_multiple_constraints__3.txt | 3 ++ ...st__test_count_multiple_constraints__4.txt | 3 ++ ...st__test_count_multiple_constraints__5.txt | 3 ++ ...aTest__test_count_single_constraint__1.txt | 2 + ...aTest__test_count_single_constraint__2.txt | 3 ++ ...aTest__test_count_single_constraint__3.txt | 3 ++ ...aTest__test_count_single_constraint__4.txt | 3 ++ ...aTest__test_count_single_constraint__5.txt | 3 ++ 31 files changed, 497 insertions(+) create mode 100644 src/Expression/Node/Property/Navigation/Count.php create mode 100644 tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_combined__1.json create mode 100644 tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_eq__1.json create mode 100644 tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_gt__1.json create mode 100644 tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_combined__1.json create mode 100644 tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_eq__1.json create mode 100644 tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_gt__1.json create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__1.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__2.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__3.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__4.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__5.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__1.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__2.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__3.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__4.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__5.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__1.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__2.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__3.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__4.txt create mode 100644 tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__5.txt diff --git a/src/Drivers/MongoEntitySet.php b/src/Drivers/MongoEntitySet.php index 9a74d5b4c..c8c71dd3b 100644 --- a/src/Drivers/MongoEntitySet.php +++ b/src/Drivers/MongoEntitySet.php @@ -419,6 +419,10 @@ protected function evaluateFilter(?Node $node) case $node instanceof Node\Literal: return $node->getValue()->get(); + case $node instanceof Node\Property\Navigation\Count: + $node->notImplemented(); + + // no break case $node instanceof Node\Property: return '$'.$node->getValue()->getName(); } diff --git a/src/Drivers/SQL/SQLExpression.php b/src/Drivers/SQL/SQLExpression.php index d8d9b9bfd..e2e0c33c1 100644 --- a/src/Drivers/SQL/SQLExpression.php +++ b/src/Drivers/SQL/SQLExpression.php @@ -103,6 +103,10 @@ public function evaluate(Node $node): void $this->lambdaExpression($node); break; + case $node instanceof Node\Property\Navigation\Count: + $this->navigationPropertyCountExpression($node); + break; + case $node instanceof Property: $this->propertyExpression($node); break; @@ -1289,6 +1293,56 @@ protected function literalExpression(Literal $node): void * @param Lambda $node Node * @return void */ + /** + * Expand a navigation property count expression into a correlated COUNT subquery + * @param Node\Property\Navigation\Count $node Node + * @return void + */ + protected function navigationPropertyCountExpression(Node\Property\Navigation\Count $node): void + { + /** @var NavigationProperty $navigationProperty */ + $navigationProperty = $node->getValue(); + + /** @var NavigationBinding $navigationBinding */ + $navigationBinding = $this->entitySet->getBindingByNavigationProperty($navigationProperty); + + /** @var SQLEntitySet $targetSet */ + $targetSet = $navigationBinding->getTarget(); + + /** @var ReferentialConstraint[] $constraints */ + $constraints = $navigationBinding->getPath()->getConstraints()->all(); + + if (!$constraints) { + $node->notImplemented(); + } + + $this->pushStatement( + sprintf( + '( SELECT COUNT(*) FROM %s WHERE', + $targetSet->quoteSingleIdentifier($targetSet->getTable()) + ) + ); + + $first = true; + + foreach ($constraints as $constraint) { + if (!$first) { + $this->pushStatement('AND'); + } + + $first = false; + + $referencedField = $targetSet->propertyToExpression($constraint->getReferencedProperty()); + $localField = $this->entitySet->propertyToExpression($constraint->getProperty()); + + $this->pushStatement(sprintf('%s = %s', $referencedField->getStatement(), $localField->getStatement())); + $this->pushParameters($referencedField->getParameters()); + $this->pushParameters($localField->getParameters()); + } + + $this->pushStatement(')'); + } + protected function lambdaExpression(Lambda $node): void { $driver = $this->entitySet->getDriver(); diff --git a/src/Expression/Node/Property/Navigation/Count.php b/src/Expression/Node/Property/Navigation/Count.php new file mode 100644 index 000000000..15da64982 --- /dev/null +++ b/src/Expression/Node/Property/Navigation/Count.php @@ -0,0 +1,19 @@ +getCurrentResource(); + + if (!$currentResource) { + return false; + } + + $navigationProperties = $currentResource->getType()->getNavigationProperties(); + + $token = $this->lexer->with(function () use ($navigationProperties) { + $identifier = $this->lexer->identifier(); + $this->lexer->char(Lexer::pathSeparator); + $this->lexer->literal('$count'); + + return $navigationProperties->get($identifier) ? $identifier : null; + }); + + if (!$token) { + return false; + } + + $property = $navigationProperties->get($token); + + $operand = new Node\Property\Navigation\Count($this); + $operand->setValue($property); + $this->operandStack[] = $operand; + $this->tokens[] = $operand; + + return true; + } + /** * Tokenize a navigation property path * @return bool diff --git a/src/Expression/Parser/Common.php b/src/Expression/Parser/Common.php index 2882b9c9d..d196b5659 100644 --- a/src/Expression/Parser/Common.php +++ b/src/Expression/Parser/Common.php @@ -85,6 +85,12 @@ public static function evaluate(Node $node, ?Entity $entity = null): ?Primitive } switch (true) { + case $node instanceof Node\Property\Navigation\Count: + throw new NotImplementedException( + 'unsupported_expression', + 'This entity set does not support counting a navigation property within an expression' + ); + // Deserialization case $node instanceof Node\Property: $propertyValue = $entity[$node->getValue()]; diff --git a/src/Expression/Parser/Filter.php b/src/Expression/Parser/Filter.php index 13d11b109..43e966bca 100644 --- a/src/Expression/Parser/Filter.php +++ b/src/Expression/Parser/Filter.php @@ -113,6 +113,7 @@ protected function findToken(): bool $this->tokenizeDeclaredProperty() || $this->tokenizeComputedProperty() || $this->tokenizeOperator() || + $this->tokenizeNavigationPropertyCount() || $this->tokenizeNavigationPropertyPath(); } diff --git a/tests/Filter/Database.php b/tests/Filter/Database.php index feaf8f83b..a0d3be8e1 100644 --- a/tests/Filter/Database.php +++ b/tests/Filter/Database.php @@ -94,4 +94,31 @@ public function test_filter_all() ->expand('MyPets') ); } + + public function test_filter_navigation_count_eq() + { + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySet) + ->filter('MyPets/$count eq 0') + ); + } + + public function test_filter_navigation_count_gt() + { + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySet) + ->filter('MyPets/$count gt 1') + ); + } + + public function test_filter_navigation_count_combined() + { + $this->assertJsonResponseSnapshot( + (new Request) + ->path($this->entitySet) + ->filter('MyPets/$count gt 1 or id eq 2') + ); + } } diff --git a/tests/Filter/MongoTest.php b/tests/Filter/MongoTest.php index bfaebb48a..1c49078fb 100644 --- a/tests/Filter/MongoTest.php +++ b/tests/Filter/MongoTest.php @@ -29,4 +29,19 @@ public function test_path_query_filter_search() { $this->markTestSkipped(); } + + public function test_filter_navigation_count_eq() + { + $this->markTestSkipped(); + } + + public function test_filter_navigation_count_gt() + { + $this->markTestSkipped(); + } + + public function test_filter_navigation_count_combined() + { + $this->markTestSkipped(); + } } diff --git a/tests/Parser/Handlers/LoopbackEntitySet.php b/tests/Parser/Handlers/LoopbackEntitySet.php index 6ae33e108..4b5912d0a 100644 --- a/tests/Parser/Handlers/LoopbackEntitySet.php +++ b/tests/Parser/Handlers/LoopbackEntitySet.php @@ -170,6 +170,10 @@ public function commonExpression(Node $node): void )); return; + case $node instanceof Node\Property\Navigation\Count: + $this->addCommon(sprintf('%s/$count', $node->getValue())); + return; + case $node instanceof Node\Property: $this->addCommon($node->getValue()); return; diff --git a/tests/Parser/LambdaTest.php b/tests/Parser/LambdaTest.php index 39b293df2..dc81df0f8 100644 --- a/tests/Parser/LambdaTest.php +++ b/tests/Parser/LambdaTest.php @@ -28,4 +28,19 @@ public function test_5g() { $this->assertLambda("airports/any(d:d/name eq 'hello') and 1 eq 2 or airports/all(d:d/name eq 'hello')"); } + + public function test_count_single_constraint() + { + $this->assertLambda('da/$count eq 1'); + } + + public function test_count_multiple_constraints() + { + $this->assertLambda('airports/$count gt 0'); + } + + public function test_count_logical_combination() + { + $this->assertLambda('da/$count ge 2 or airports/$count eq 0'); + } } diff --git a/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_combined__1.json b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_combined__1.json new file mode 100644 index 000000000..36aec5702 --- /dev/null +++ b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_combined__1.json @@ -0,0 +1,52 @@ +{ + "@context": "http://localhost/odata/$metadata#Passengers", + "value": [ + { + "id": 1, + "flight_id": 1, + "name": "Alpha", + "dob": "2000-01-01T04:04:04+00:00", + "age": 4, + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 2, + "flight_id": null, + "name": "Beta", + "dob": "2001-02-02T05:05:05+00:00", + "age": 3, + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 3, + "flight_id": 1, + "name": "Gamma", + "dob": "2002-03-03T06:06:06+00:00", + "age": 2, + "chips": true, + "dq": "2002-03-03", + "in_role": "P4DT32M41S", + "open_time": "07:07:07.000000", + "colour": "Blue", + "sock_colours": "Red,Green,Blue", + "emails": [ + "gamma@example.com" + ] + } + ] +} diff --git a/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_eq__1.json b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_eq__1.json new file mode 100644 index 000000000..60ea2143e --- /dev/null +++ b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_eq__1.json @@ -0,0 +1,47 @@ +{ + "@context": "http://localhost/odata/$metadata#Passengers", + "value": [ + { + "id": 2, + "flight_id": null, + "name": "Beta", + "dob": "2001-02-02T05:05:05+00:00", + "age": 3, + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 4, + "flight_id": null, + "name": "Delta", + "dob": null, + "age": null, + "chips": null, + "dq": null, + "in_role": "PT2M7S", + "open_time": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 5, + "flight_id": null, + "name": "Epsilon", + "dob": "2003-04-04T07:07:07+00:00", + "age": 2.4, + "chips": null, + "dq": "2003-04-04", + "in_role": "PT14M48.9S", + "open_time": "23:11:33.000000", + "colour": null, + "sock_colours": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_gt__1.json b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_gt__1.json new file mode 100644 index 000000000..ea1370e43 --- /dev/null +++ b/tests/__snapshots__/Filter/EloquentTest__test_filter_navigation_count_gt__1.json @@ -0,0 +1,38 @@ +{ + "@context": "http://localhost/odata/$metadata#Passengers", + "value": [ + { + "id": 1, + "flight_id": 1, + "name": "Alpha", + "dob": "2000-01-01T04:04:04+00:00", + "age": 4, + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 3, + "flight_id": 1, + "name": "Gamma", + "dob": "2002-03-03T06:06:06+00:00", + "age": 2, + "chips": true, + "dq": "2002-03-03", + "in_role": "P4DT32M41S", + "open_time": "07:07:07.000000", + "colour": "Blue", + "sock_colours": "Red,Green,Blue", + "emails": [ + "gamma@example.com" + ] + } + ] +} diff --git a/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_combined__1.json b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_combined__1.json new file mode 100644 index 000000000..d3add092e --- /dev/null +++ b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_combined__1.json @@ -0,0 +1,52 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": 1, + "name": "Alpha", + "age": 4, + "dob": "2000-01-01T04:04:04+00:00", + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "flight_id": 1, + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 2, + "name": "Beta", + "age": 3, + "dob": "2001-02-02T05:05:05+00:00", + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "flight_id": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 3, + "name": "Gamma", + "age": 2, + "dob": "2002-03-03T06:06:06+00:00", + "chips": true, + "dq": "2002-03-03", + "in_role": "P4DT32M41S", + "open_time": "07:07:07.000000", + "flight_id": 1, + "colour": "Blue", + "sock_colours": "Red,Green,Blue", + "emails": [ + "gamma@example.com" + ] + } + ] +} diff --git a/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_eq__1.json b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_eq__1.json new file mode 100644 index 000000000..9e5999490 --- /dev/null +++ b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_eq__1.json @@ -0,0 +1,47 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": 2, + "name": "Beta", + "age": 3, + "dob": "2001-02-02T05:05:05+00:00", + "chips": false, + "dq": "2001-02-02", + "in_role": "P2DT5H5M5.2999999999884S", + "open_time": null, + "flight_id": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 4, + "name": "Delta", + "age": null, + "dob": null, + "chips": null, + "dq": null, + "in_role": "PT2M7S", + "open_time": null, + "flight_id": null, + "colour": null, + "sock_colours": null, + "emails": [] + }, + { + "id": 5, + "name": "Epsilon", + "age": 2.4, + "dob": "2003-04-04T07:07:07+00:00", + "chips": null, + "dq": "2003-04-04", + "in_role": "PT14M48.9S", + "open_time": "23:11:33.000000", + "flight_id": null, + "colour": null, + "sock_colours": null, + "emails": [] + } + ] +} diff --git a/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_gt__1.json b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_gt__1.json new file mode 100644 index 000000000..a0b2dcffb --- /dev/null +++ b/tests/__snapshots__/Filter/SQLTest__test_filter_navigation_count_gt__1.json @@ -0,0 +1,38 @@ +{ + "@context": "http://localhost/odata/$metadata#passengers", + "value": [ + { + "id": 1, + "name": "Alpha", + "age": 4, + "dob": "2000-01-01T04:04:04+00:00", + "chips": true, + "dq": "2000-01-01", + "in_role": "P1DT0S", + "open_time": "05:05:05.000000", + "flight_id": 1, + "colour": "Green", + "sock_colours": "Green,Blue", + "emails": [ + "alpha@example.com", + "alpha@beta.com" + ] + }, + { + "id": 3, + "name": "Gamma", + "age": 2, + "dob": "2002-03-03T06:06:06+00:00", + "chips": true, + "dq": "2002-03-03", + "in_role": "P4DT32M41S", + "open_time": "07:07:07.000000", + "flight_id": 1, + "colour": "Blue", + "sock_colours": "Red,Green,Blue", + "emails": [ + "gamma@example.com" + ] + } + ] +} diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__1.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__1.txt new file mode 100644 index 000000000..976597f5e --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__1.txt @@ -0,0 +1,2 @@ +expression: da/$count ge 2 or airports/$count eq 0 +result: ( ( da/$count ge 2 ) or ( airports/$count eq 0 ) ) diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__2.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__2.txt new file mode 100644 index 000000000..68eba11f2 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__2.txt @@ -0,0 +1,3 @@ +expression: da/$count ge 2 or airports/$count eq 0 +result: ( ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) >= ? ) OR ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) = ? ) ) +parameters: 2,0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__3.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__3.txt new file mode 100644 index 000000000..68eba11f2 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__3.txt @@ -0,0 +1,3 @@ +expression: da/$count ge 2 or airports/$count eq 0 +result: ( ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) >= ? ) OR ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) = ? ) ) +parameters: 2,0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__4.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__4.txt new file mode 100644 index 000000000..68eba11f2 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__4.txt @@ -0,0 +1,3 @@ +expression: da/$count ge 2 or airports/$count eq 0 +result: ( ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) >= ? ) OR ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) = ? ) ) +parameters: 2,0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__5.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__5.txt new file mode 100644 index 000000000..68eba11f2 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_logical_combination__5.txt @@ -0,0 +1,3 @@ +expression: da/$count ge 2 or airports/$count eq 0 +result: ( ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) >= ? ) OR ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) = ? ) ) +parameters: 2,0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__1.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__1.txt new file mode 100644 index 000000000..f1e6ac4b6 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__1.txt @@ -0,0 +1,2 @@ +expression: airports/$count gt 0 +result: ( airports/$count gt 0 ) diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__2.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__2.txt new file mode 100644 index 000000000..2fb69fe38 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__2.txt @@ -0,0 +1,3 @@ +expression: airports/$count gt 0 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) > ? ) +parameters: 0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__3.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__3.txt new file mode 100644 index 000000000..2fb69fe38 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__3.txt @@ -0,0 +1,3 @@ +expression: airports/$count gt 0 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) > ? ) +parameters: 0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__4.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__4.txt new file mode 100644 index 000000000..2fb69fe38 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__4.txt @@ -0,0 +1,3 @@ +expression: airports/$count gt 0 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) > ? ) +parameters: 0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__5.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__5.txt new file mode 100644 index 000000000..2fb69fe38 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_multiple_constraints__5.txt @@ -0,0 +1,3 @@ +expression: airports/$count gt 0 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."origin" AND "airports"."code" = "flights"."destination" ) > ? ) +parameters: 0 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__1.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__1.txt new file mode 100644 index 000000000..291682466 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__1.txt @@ -0,0 +1,2 @@ +expression: da/$count eq 1 +result: ( da/$count eq 1 ) diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__2.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__2.txt new file mode 100644 index 000000000..b96ac0509 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__2.txt @@ -0,0 +1,3 @@ +expression: da/$count eq 1 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) = ? ) +parameters: 1 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__3.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__3.txt new file mode 100644 index 000000000..b96ac0509 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__3.txt @@ -0,0 +1,3 @@ +expression: da/$count eq 1 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) = ? ) +parameters: 1 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__4.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__4.txt new file mode 100644 index 000000000..b96ac0509 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__4.txt @@ -0,0 +1,3 @@ +expression: da/$count eq 1 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) = ? ) +parameters: 1 diff --git a/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__5.txt b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__5.txt new file mode 100644 index 000000000..b96ac0509 --- /dev/null +++ b/tests/__snapshots__/Parser/LambdaTest__test_count_single_constraint__5.txt @@ -0,0 +1,3 @@ +expression: da/$count eq 1 +result: ( ( SELECT COUNT(*) FROM "airports" WHERE "airports"."code" = "flights"."destination" ) = ? ) +parameters: 1