feat: enable Calcite engine for Prometheus datasources with filter pushdown - #5706
Open
robertpaschedag wants to merge 7 commits into
Open
Conversation
Implement ScannableTable interface for PrometheusMetricTable so that Prometheus metrics can participate in Calcite query plans. This enables PPL commands like join, lookup, and other Calcite-only commands to work with Prometheus data sources. Changes: - PrometheusMetricTable now extends AbstractTable and implements both ScannableTable (Calcite) and Table (V2), providing dual-engine support - OpenSearchSchema.registerTable() uses instanceof check instead of blind cast, with descriptive error for non-Calcite tables - CalciteRelNodeVisitor.visitRelation() relaxed to allow non-default datasources when their table implements org.apache.calcite.schema.Table - Added unit tests for ScannableTable integration (getRowType, scan, empty results, error handling) Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…lcite When a PPL query references a non-default datasource (e.g., source = prometheus.up), Calcite's RelBuilder.scan() resolves the multi-part name as schema + table. Previously, only the flat OpenSearchSchema was registered, causing 'Table not found' errors for external datasources. This fix adds a DataSourceSubSchema inner class that is lazily created for any known datasource. The sub-schema resolves tables from the datasource's storage engine, enabling proper schema-qualified resolution: scan(["prometheus", "up"]) -> OpenSearchSchema sub-schema "prometheus" -> table "up" This works generically for any datasource name (prometheus, vmetrics, my-metric-backend, etc.) as long as the datasource's tables implement org.apache.calcite.schema.Table. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…h filter pushdown Replaces the simple ScannableTable implementation with a full TranslatableTable + pushdown architecture: - CalciteLogicalPrometheusScan: logical scan node (Convention.NONE) with filter pushdown support for time range and label matchers - CalciteEnumerablePrometheusScan: physical scan node implementing Scannable + EnumerableRel, executes PromQL via PrometheusClient - PrometheusPushDownContext: accumulates pushed-down state (time range, step, label matchers) and builds PromQL query strings - PrometheusFilterPushDownRule: planner rule that pushes LogicalFilter conditions into the logical scan - EnumerablePrometheusScanRule: converter rule (logical -> physical) - PrometheusRules: registry of all Prometheus planner rules PrometheusMetricTable now implements TranslatableTable and returns CalciteLogicalPrometheusScan from toRel(). Time range filters on @timestamp and label equality filters are pushed down to PromQL, reducing data transfer from Prometheus. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…er pushdown The VolcanoPlanner was not selecting the partially-pushed filter plan because CalciteEnumerablePrometheusScan did not override computeSelfCost(). Both pushed and unpushed physical scans reported identical cost, so the planner picked whichever it found first (the unpushed path). Adding cost reduction factors (0.7x for label pushdown, 0.5x for time range pushdown) makes the pushed scan cheaper, ensuring the planner prefers partial pushdown when available. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…lanner digest The VolcanoPlanner uses explainTerms() to compute node digests for equivalence detection. Without this override, pushed and unpushed Prometheus scans had identical digests (only table name), causing the planner to treat them as the same node and ignore filter pushdown transformations. Adding explainTerms() that includes the PushDownContext state ensures pushed scans have distinct digests, enabling the VolcanoPlanner to correctly register partial filter pushdowns as alternative plans. Also adds toString() to PrometheusPushDownContext for explain output visibility. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…selection Add PlanUtils.tryPruneRelNodes(call) after call.transformTo() in PrometheusFilterPushDownRule, matching the pattern used by all OpenSearch pushdown rules (FilterIndexScanRule, ProjectIndexScanRule, etc.). Without pruning, the VolcanoPlanner retains both the original (unpushed) and pushed alternatives and may choose the unpushed path for partial pushdown cases (e.g., 'where service=frontend AND @value > 0.5'). Pruning the original nodes forces the planner to use the pushed path, ensuring label filters are correctly pushed to PromQL while unsupported conditions remain as EnumerableCalc. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
…nt and filter.copy() - Add estimateRowCount() override to both CalciteLogicalPrometheusScan and CalciteEnumerablePrometheusScan so pushed scans report fewer rows, propagating cost savings to all ancestor nodes in the VolcanoPlanner. - Change pushDownFilter() to accept the Filter RelNode and use filter.copy() for partial pushdown (matching OpenSearch's pattern), ensuring proper equivalence registration in the VolcanoPlanner. - Update PrometheusFilterPushDownRule.onMatch() to pass the Filter object instead of just the condition RexNode. Signed-off-by: Robert Paschedag <robert.paschedag@sap.com>
robertpaschedag
marked this pull request as ready for review
August 19, 2026 21:11
robertpaschedag
requested review from
LantaoJin,
RyanL1997,
Swiddis,
acarbonetto,
ahkcs,
anirudha,
dai-chen,
joshuali925,
mengweieric,
noCharger,
penghuo,
ps48,
qianheng-aws,
songkant-aws,
vamsimanohar,
ykmr1224 and
yuancu
as code owners
August 19, 2026 21:11
Contributor
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit ce1362b.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Enables all Calcite-only PPL commands (join, lookup, flatten, expand, eventstats, etc.) to work with Prometheus datasources by making
PrometheusMetricTableimplement Calcite'sTranslatableTableinterface with a custom logical/physical scan operator and filter pushdown.Key changes:
PrometheusMetricTablenow extendsAbstractTableand implements bothTranslatableTable(Calcite) andTable(V2), preserving backward compatibilityCalciteLogicalPrometheusScan— custom logical scan node (Convention.NONE) with filter pushdown support for label matchers and time rangesCalciteEnumerablePrometheusScan— physical scan (EnumerableConvention) that executes PromQL viaPrometheusClient.queryRange()PrometheusFilterPushDownRule— Calcite optimizer rule that pushes label equality and time range filters into the scanOpenSearchSchema— dynamicDataSourceSubSchemaresolution for multi-part table references (e.g.,source = prometheus.metric_name)CalciteRelNodeVisitor.visitRelation()— relaxed datasource guard to allow non-default datasources when their table implements Calcite'sTableinterfaceFilter pushdown behavior:
service = 'frontend'{service="frontend"}@timestamp >= X AND @timestamp <= Y@value > 0.5EnumerableCalcservice = 'frontend' AND @value > 0.5@valueremains in-memoryExplain output examples:
Full pushdown:
CalciteEnumerablePrometheusScan(table=[OpenSearch, prometheus, request], PushDownContext=[LABELS->{service=frontend}])
Partial pushdown:
EnumerableCalc(condition=>($t4, 0.5))
CalciteEnumerablePrometheusScan(table=[OpenSearch, prometheus, request], PushDownContext=[LABELS->{service=frontend}])
Related Issues
Resolves #5705
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.
Yes..... the code got generated with assistence of AI. I was able to do some example PPL queries in local dev environment, using otel-demo sendings logs to opensearch, while sending metrics to prometheus (victoriametrics).
Screenshots
Example 1:

Example 2:

Example 3:

Example 4:

Example 5:

I generally hope, that this might be useful.