[Analytics Backend / DataFusion] Onboard PPL regex command + regexp_match() to DataFusion analytics-engine route - #21524
Conversation
…gine route Signed-off-by: Jialiang Liang <jiallian@amazon.com>
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
|
❌ Gradle check result for a968223: FAILURE Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change? |
|
Transferring internal conversation with @penghuo to here:
Thats why this change is just a direct projection of mappings |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21524 +/- ##
============================================
- Coverage 73.44% 73.38% -0.07%
+ Complexity 74426 74404 -22
============================================
Files 5970 5970
Lines 338267 338267
Branches 48753 48753
============================================
- Hits 248451 248226 -225
- Misses 70042 70209 +167
- Partials 19774 19832 +58 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…gine route (opensearch-project#21524) Signed-off-by: Jialiang Liang <jiallian@amazon.com>
…gine route (opensearch-project#21524) Signed-off-by: Jialiang Liang <jiallian@amazon.com>
Description
Wires Calcite
REGEXP_CONTAINS— the lowering target for both PPLregexcommand andregexp_match()function — through the analytics-engine path, so PPL queries like... | regex firstname='Amber'and... | eval m = regexp_match(field, pattern)route through DataFusion natively when the indices are parquet-backed.This follows the same templated shape as #21472 (PPL
fillnullviaCOALESCE) — onboarding a Calcite scalar that DataFusion already knows how to execute, with no Rust-side or DataFusion-extension changes.How it failed before
CalciteRelNodeVisitor.visitRegexalready lowers the PPLregexcommand intoLogicalFilter(REGEXP_CONTAINS(field, pattern))(andregexp_match()into a project-sideREGEXP_CONTAINS) — verified byCalcitePPLRegexTest. But runningCalciteRegexCommandITagainst an analytics-engine-routed cluster yielded:Isthmus had no Calcite→Substrait mapping for
SqlLibraryOperators.REGEXP_CONTAINS, so every regex query failed at substrait serialization before reaching DataFusion.Changes
ScalarFunction.REGEXP_CONTAINS— new enum constant inCategory.FULL_TEXT,SqlKind.OTHER_FUNCTION. Resolves throughfromSqlFunction(name)→valueOf(name.toUpperCase())sinceREGEXP_CONTAINSis the operator's name in Calcite.DataFusionAnalyticsBackendPlugin.STANDARD_FILTER_OPS+STANDARD_PROJECT_OPS— declareREGEXP_CONTAINS. Filter side covers theregexcommand (lowered to a Filter); project side coversregexp_match()inevalprojections (returns BOOLEAN).opensearch_scalar_functions.yaml— add theregex_matchSubstrait extension declaration (varchar, varchar) → boolean, mirroring the existingilikeentry.DataFusionFragmentConvertor.ADDITIONAL_SCALAR_SIGS— addFunctionMappings.s(SqlLibraryOperators.REGEXP_CONTAINS, "regex_match"). This is the Calcite-op→Substrait-name bridge: Isthmus serializes the call as theregex_matchextension, anddatafusion-substrait52's consumer maps that name toOperator::RegexMatch(the binary regex operator backing PostgreSQL's~), which executes viaarrow-string's regex kernel and returns BOOLEAN.No Rust-side, convertor, or DataFusion-extension changes — DataFusion's substrait crate already resolves
regex_matchto a native operator.Test results
RegexCommandIT(new, self-contained, 13 tests on the parquet-backedcalcsdataset):Unable to convert call REGEXP_CONTAINS(string?, string)Coverage: exact match, contains-substring,
^anchor/$anchor, character classes, negated!=, full-row content check,regexp_match()ineval(BOOLEAN column), and the SQL-plugin preflight type-check error path (regex on numeric field).CalciteRegexCommandITfrom the SQL plugin, force-routed through analytics-engine viatests.analytics.{parquet_indices,force_routing}=true:Unable to convert call)CalciteNoPushdownITsuite variant)./gradlew check -p sandbox -Dsandbox.enabled=true: BUILD SUCCESSFUL (compile, unit tests, spotless, forbiddenApis, dependencyLicenses, licenseHeaders all green).Routing evidence (cluster log under force_routing=true)
The
backends=[datafusion]annotation confirms the analytics-engine planner selected DataFusion to execute the regex filter. Negated form (regex field!='X') round-trips correctly: the planner wraps the annotation inNOT(...)without losing backend viability.POC framing for future bucket-1 functions
Three knobs apply to any Calcite scalar that DataFusion executes natively:
ScalarFunctionenum entry (only ifSqlKindisOTHER_FUNCTIONand the name doesn't already map)STANDARD_FILTER_OPS/STANDARD_PROJECT_OPSADDITIONAL_SCALAR_SIGS+opensearch_scalar_functions.yamlif Isthmus's built-in mapping doesn't cover the operatorFor regex, all three were needed because
REGEXP_CONTAINSis BigQuery-named and not in Isthmus's default Calcite→Substrait function table. Once the Substrait extension is namedregex_match(the Substrait standard name DataFusion resolves), the rest is wiring.Check List
regex/regexp_matchsyntax unchanged).