-
Notifications
You must be signed in to change notification settings - Fork 357
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
[DRAFT] fix: Use CTE in Enrollment analytics queries [DHIS-16705] #19519
base: master
Are you sure you want to change the base?
Conversation
|
||
void contributeCTE( | ||
ProgramIndicator programIndicator, | ||
RelationshipType relationshipType, |
Check notice
Code scanning / CodeQL
Useless parameter Note
...-analytics/src/main/java/org/hisp/dhis/analytics/common/ProgramIndicatorSubqueryBuilder.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
.../hisp/dhis/analytics/event/data/programindicator/DefaultProgramIndicatorSubqueryBuilder.java
Fixed
Show fixed
Hide fixed
8c33952
to
3e070ce
Compare
5170115
to
d8119c0
Compare
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Fixed
Show fixed
Hide fixed
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Fixed
Show fixed
Hide fixed
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Fixed
Show fixed
Hide fixed
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Fixed
Show fixed
Hide fixed
...tics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
.../hisp/dhis/analytics/event/data/programindicator/DefaultProgramIndicatorSubqueryBuilder.java
Fixed
Show fixed
Hide fixed
5c4dba5
to
ef43c5b
Compare
...vices/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CteDefinition.java
Dismissed
Show dismissed
Hide dismissed
this.cteDefinition = cteDefinition; | ||
this.offsets.add(offset); | ||
// one alias per offset | ||
this.alias = new RandomStringGenerator.Builder().withinRange('a', 'z').build().generate(5); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
Builder.build
this.programIndicatorUid = programIndicatorUid; | ||
this.programStageUid = null; | ||
// ignore offset | ||
this.alias = new RandomStringGenerator.Builder().withinRange('a', 'z').build().generate(5); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
Builder.build
this.programIndicatorUid = null; | ||
this.programStageUid = programStageUid; | ||
// ignore offset | ||
this.alias = new RandomStringGenerator.Builder().withinRange('a', 'z').build().generate(5); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
Builder.build
} | ||
|
||
if (offset < 0) { | ||
return (-1 * offset); |
Check failure
Code scanning / CodeQL
User-controlled data in arithmetic expression High
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
This arithmetic expression depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 10 hours ago
To fix the problem, we need to validate the offset
parameter before performing any arithmetic operations. Specifically, we should ensure that the offset
value falls within a safe range to prevent overflow or underflow. We can achieve this by adding a validation step in the computeRowNumberOffset
method.
- Add a validation step to check if the
offset
value is within a safe range. - If the
offset
value is outside the safe range, handle it appropriately (e.g., throw an exception or set it to a default value).
-
Copy modified lines R1406-R1410
@@ -1405,2 +1405,7 @@ | ||
private int computeRowNumberOffset(int offset) { | ||
// Validate the offset value to prevent overflow/underflow | ||
if (offset < Integer.MIN_VALUE || offset > Integer.MAX_VALUE) { | ||
throw new IllegalArgumentException("Offset value is out of range: " + offset); | ||
} | ||
|
||
if (offset == 0) { |
aa533b4
to
5f05bb5
Compare
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Outdated
Show resolved
Hide resolved
...services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEContext.java
Outdated
Show resolved
Hide resolved
return ""; | ||
} | ||
|
||
StringBuilder sb = new StringBuilder("WITH "); |
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.
Let's use lower-case for SQL keywords in Java, i.e. with
.
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Outdated
Show resolved
Hide resolved
...ces/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java
Outdated
Show resolved
Hide resolved
4e085d3
to
62c2237
Compare
void contributeCTE( | ||
ProgramIndicator programIndicator, | ||
RelationshipType relationshipType, | ||
AnalyticsType outerSqlEntity, |
Check notice
Code scanning / CodeQL
Useless parameter Note
...tics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java
Fixed
Show fixed
Hide fixed
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Fixed
Show fixed
Hide fixed
79f7563
to
94ab16e
Compare
sql = buildEnrollmentQueryWithCte(params); | ||
} | ||
|
||
System.out.println("SQL: " + sql); // FIXME: Remove debug line |
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.
Remove println.
// Using a text block with .formatted() for clarity: | ||
String join = | ||
""" | ||
LEFT JOIN %s %s |
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.
Use lower-case keywords for SQL in Java.
...s-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/common/CTEUtils.java
Outdated
Show resolved
Hide resolved
import java.util.function.Predicate; | ||
import org.hisp.dhis.common.QueryFilter; | ||
|
||
/** Mimics the logic of @{@link org.hisp.dhis.common.InQueryFilter} to be used in CTEs */ |
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.
Remove outer @.
...alytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java
Outdated
Show resolved
Hide resolved
params.getLatestEndDate(), | ||
cteContext); | ||
} else { | ||
programIndicatorSubqueryBuilder.contributeCTE( |
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.
Rename to contributeCte
.
bc234ee
to
a4ff98c
Compare
f093346
to
3916833
Compare
3916833
to
c1a8dc6
Compare
} | ||
|
||
private static void extractColumnsFromExpression(Expression expression, Set<String> columns) { | ||
if (expression instanceof net.sf.jsqlparser.schema.Column column) { |
Check notice
Code scanning / CodeQL
Chain of 'instanceof' tests Note
8cb7b23
to
895d84c
Compare
.../dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/util/sql/SqlAliasReplacer.java
Fixed
Show fixed
Hide fixed
Quality Gate failedFailed conditions See analysis details on SonarQube Cloud Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE |
WIP
Changes
This PR addresses the generation of SQL queries for Enrollment analytics
Problem
Currently, the Enrollment queries are structured so that sub-queries are used to fetch events values from the
analytics_event_*
tables.For instance:
The above query works in Postgres but does not work in Doris, because correlation with outer layers of the parent query is not supported.
Mitigation
The current approach is trying to refactor the Enrollment query so that the subqueries (both as select and as where conditions) are "moved" into CTE (Common Table Expressions).
Common Table Expressions are temporary result sets in SQL, defined within a
WITH
clause, that simplify complex queries by improving readability and modularizing logic. They are particularly useful for recursive queries and can be referenced multiple times within the main query.The above query can be rewritten like so:
The above query structure is compatible with Doris and it makes the execution of the query faster.
As a comparison:
Original query with sub-select
Refactored query with CTEs
Testing strategy
I am using the
e2e
project and aim at having 100% green tests on both Postgres and Doris.