Skip to content

GH-5310 Correctly evaluate constant grouping constants for empty result sets. #5351

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

JervenBolleman
Copy link
Contributor

GitHub issue resolved: #GH-5310

Briefly describe the changes proposed in this PR:

The logic to deal with group elements and empty results set was not correct for constant operator values.
This is fixed, plus minor code cleanups.


PR Author Checklist (see the contributor guidelines for more details):

  • my pull request is self-contained
  • I've added tests for the changes I made
  • I've applied code formatting (you can use mvn process-resources to format from the command line)
  • I've squashed my commits where necessary
  • every commit message starts with the issue number (GH-xxxx) followed by a meaningful description of the change

@JervenBolleman
Copy link
Contributor Author

@nguyenm100 would you mind reviewing this?

@nguyenm100
Copy link
Contributor

hey @odysa can you help Jervan with the review?

@@ -112,12 +115,49 @@ public void testFunctionOptimization() throws RDF4JException {

}

@Test
public void testAggregateOptimization() throws RDF4JException {
String query = "prefix ex: <ex:>" + "select (max(1) AS ?a) \n " + "where {\n" + "?x a ?z \n"
Copy link

Choose a reason for hiding this comment

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

Let's try to test all aggregate functions here.

String query = String.join("\n",
		"PREFIX ex: <ex:>",
		"SELECT",
		"  (MAX(1) AS ?a)",
		"  (MIN(1) AS ?b)",
		"  (AVG(1) AS ?c)",
		"  (COUNT(1) AS ?d)",
		"  (COUNT(DISTINCT 1) AS ?e)",
		"  (COUNT(*) AS ?f)",
		"WHERE {",
		"  ?x a ?z ;",
		"  ex:someProperty ?val .",
		"}"
);
.....

assertTrue(bindings.hasBinding("a")); 
assertTrue(bindings.hasBinding("b")); 
assertTrue(bindings.hasBinding("c"));
assertTrue(bindings.hasBinding("d")); 
assertTrue(bindings.hasBinding("e")); 
assertTrue(bindings.hasBinding("f"));
assertEquals(1, ((Literal) bindings.getBinding("a").getValue()).intValue());
assertEquals(1, ((Literal) bindings.getBinding("b").getValue()).intValue());
assertEquals(1, ((Literal) bindings.getBinding("c").getValue()).intValue());
assertEquals(1, ((Literal) bindings.getBinding("d").getValue()).intValue());
assertEquals(1, ((Literal) bindings.getBinding("e").getValue()).intValue());
assertEquals(0, ((Literal) bindings.getBinding("f").getValue()).intValue());

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I Will include that!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@odysa
I think this is not right for the empty case, but I am not sure.
" (COUNT(1) AS ?d)" != assertEquals(1, ((Literal) bindings.getBinding("d").getValue()).intValue());

for (int i = 0; i < aggregates.size(); i++) {
predicates.add(ALWAYS_TRUE_BINDING_SET);
for (var ag : aggregates) {
if (ag.agg instanceof WildCardCountAggregate) {
Copy link

Choose a reason for hiding this comment

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

Looks like there is no better way to test AggregateFunction<CountCollector, BindingSet> in Java. Just need to be careful when somebody in the future make a new BindingSet aggregator. (very unlikely?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, in a newer java we could make that a sealed class and we would get compilation time errors.

@odysa
Copy link

odysa commented Jun 13, 2025

Does this unblock the previous PR #5344 ?

aggregate functions.

Also do fix the logging issue in the ConstantOptimizer.
@JervenBolleman
Copy link
Contributor Author

Does this unblock the previous PR #5344 ?

No, previous PR#5344 is broken and is not the right solution.

@JervenBolleman
Copy link
Contributor Author

@odysa @hmottestad this is getting into the specification corners of how aggregates are supposed to behave in the face of the empty result set.

SELECT
(COUNT(1) AS ?d)
WHERE {
  ?s ?p ?o
}

on an empty result set should I believe return '0' not '1'.

SELECT
(MAX(1) AS ?d)
WHERE {
  ?s ?p ?o
}

I believe should '1', other stores return an UNDEF/null binding here.

If it should be an UNDEF the logic in the empty case can be simplified. To return a zero in the case of the count operators and a UNDEF for all other cases.

@odysa
Copy link

odysa commented Jun 16, 2025

@JervenBolleman I agree count should return 0 in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Possible bug with aggregates and fts (solr) search.
3 participants