result = new LinkedHashSet<>();
+ for (MetaColumn c : cfg.metaColumns.values()) {
+ if (c.asRangeBoundOf != null) {
+ result.add(c.asRangeBoundOf);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a mapping from data column names to their associated range bound metadata columns.
+ *
+ * Each entry maps a data column name to another map with keys {@code "lower"} and/or
+ * {@code "upper"}, representing the metadata column names that define those bounds.
+ *
+ * @param name the {@link SchemaTableName} of the target table
+ * @return a nested mapping from data column name → ("lower"/"upper" → metadata column name)
+ */
+ public Map> getDataColumnRangeMapping(SchemaTableName name)
+ {
+ TableConfig cfg = getTableConfig(name);
+ Map> mapping = new HashMap<>();
+ for (MetaColumn c : cfg.metaColumns.values()) {
+ if (c.asRangeBoundOf != null && c.boundType != null) {
+ mapping.computeIfAbsent(c.asRangeBoundOf, k -> new HashMap<>())
+ .put(c.boundType, c.name);
+ }
+ }
+ return mapping;
+ }
+
+ /**
+ * Merges and returns the effective {@link TableConfig} for the given table, taking into account
+ * the hierarchical configuration structure: global → schema → table.
+ *
+ * @param name the {@link SchemaTableName} of the target table
+ * @return the merged table configuration
+ */
+ private TableConfig getTableConfig(SchemaTableName name)
+ {
+ TableConfig merged = new TableConfig();
+
+ List namespaces = new ArrayList<>();
+ namespaces.add("");
+ namespaces.add(name.getSchemaName());
+ namespaces.add(name.getSchemaName() + "." + name.getTableName());
+
+ for (String ns : namespaces) {
+ TableConfig cfg = tableConfigs.get(ns);
+ if (cfg != null) {
+ merged.metaColumns.putAll(cfg.metaColumns);
+
+ for (FilterRule rule : cfg.filterRules) {
+ boolean exists = merged.filterRules.stream()
+ .anyMatch(r -> r.column.equals(rule.column));
+ if (!exists) {
+ merged.filterRules.add(rule);
+ }
+ }
+ }
+ }
+
+ return merged;
+ }
+}
diff --git a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpMySqlSplitFilterProvider.java b/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpMySqlSplitFilterProvider.java
deleted file mode 100644
index 31d24fd4df71c..0000000000000
--- a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpMySqlSplitFilterProvider.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.facebook.presto.plugin.clp.split.filter;
-
-import com.facebook.presto.plugin.clp.ClpConfig;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.collect.ImmutableMap;
-import com.google.inject.Inject;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import static com.facebook.presto.plugin.clp.split.filter.ClpSplitFilterConfig.CustomSplitFilterOptions;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static java.lang.String.format;
-
-/**
- * Implementation for the CLP package's MySQL metadata database.
- */
-public class ClpMySqlSplitFilterProvider
- extends ClpSplitFilterProvider
-{
- @Inject
- public ClpMySqlSplitFilterProvider(ClpConfig config)
- {
- super(config);
- }
-
- /**
- * Performs regex-based replacements to rewrite {@code pushDownExpression} according to the
- * {@code "rangeMapping"} field in {@link ClpMySqlCustomSplitFilterOptions}. For example:
- *
- * - {@code "msg.timestamp" >= 1234} → {@code end_timestamp >= 1234}
- * - {@code "msg.timestamp" <= 5678} → {@code begin_timestamp <= 5678}
- * - {@code "msg.timestamp" = 4567} →
- * {@code (begin_timestamp <= 4567 AND end_timestamp >= 4567)}
- *
- *
- * @param scope the filter's scope
- * @param pushDownExpression the expression to be rewritten
- * @return the rewritten expression
- */
- @Override
- public String remapSplitFilterPushDownExpression(String scope, String pushDownExpression)
- {
- String[] splitScope = scope.split("\\.");
-
- Map mappings = new HashMap<>(getAllMappingsFromFilters(filterMap.get(splitScope[0])));
-
- if (1 < splitScope.length) {
- mappings.putAll(getAllMappingsFromFilters(filterMap.get(splitScope[0] + "." + splitScope[1])));
- }
-
- if (3 == splitScope.length) {
- mappings.putAll(getAllMappingsFromFilters(filterMap.get(scope)));
- }
-
- String remappedSql = pushDownExpression;
- for (Map.Entry entry : mappings.entrySet()) {
- String key = entry.getKey();
- ClpMySqlCustomSplitFilterOptions.RangeMapping value = entry.getValue();
- remappedSql = remappedSql.replaceAll(
- format("\"(%s)\"\\s(>=?)\\s(-?[0-9]+(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)", key),
- format("%s $2 $3", value.upperBound));
- remappedSql = remappedSql.replaceAll(
- format("\"(%s)\"\\s(<=?)\\s(-?[0-9]+(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)", key),
- format("%s $2 $3", value.lowerBound));
- remappedSql = remappedSql.replaceAll(
- format("\"(%s)\"\\s(=)\\s(-?[0-9]+(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)", key),
- format("(%s <= $3 AND %s >= $3)", value.lowerBound, value.upperBound));
- }
- return remappedSql;
- }
-
- @Override
- protected Class extends CustomSplitFilterOptions> getCustomSplitFilterOptionsClass()
- {
- return ClpMySqlCustomSplitFilterOptions.class;
- }
-
- private Map getAllMappingsFromFilters(List filters)
- {
- return null != filters
- ? filters.stream()
- .filter(filter ->
- filter.customOptions instanceof ClpMySqlCustomSplitFilterOptions &&
- ((ClpMySqlCustomSplitFilterOptions) filter.customOptions).rangeMapping != null)
- .collect(toImmutableMap(
- filter -> filter.columnName,
- filter -> ((ClpMySqlCustomSplitFilterOptions) filter.customOptions).rangeMapping))
- : ImmutableMap.of();
- }
-
- /**
- * Custom options:
- *
- * - {@code rangeMapping} (optional): an object with the following properties:
- *
- * - {@code lowerBound}: The numeric metadata column that represents the lower bound
- * of values in a split for the numeric data column.
- * - {@code upperBound}: The numeric metadata column that represents the upper bound
- * of values in a split for the numeric data column.
- *
- *
- *
- */
- protected static class ClpMySqlCustomSplitFilterOptions
- implements CustomSplitFilterOptions
- {
- @JsonProperty("rangeMapping")
- public RangeMapping rangeMapping;
-
- public static class RangeMapping
- {
- @JsonProperty("lowerBound")
- public String lowerBound;
-
- @JsonProperty("upperBound")
- public String upperBound;
-
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (!(o instanceof RangeMapping)) {
- return false;
- }
- RangeMapping that = (RangeMapping) o;
- return Objects.equals(lowerBound, that.lowerBound) &&
- Objects.equals(upperBound, that.upperBound);
- }
-
- @Override
- public int hashCode()
- {
- return Objects.hash(lowerBound, upperBound);
- }
- }
- }
-}
diff --git a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfig.java b/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfig.java
deleted file mode 100644
index 0d7e37a803515..0000000000000
--- a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfig.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.facebook.presto.plugin.clp.split.filter;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Options for a how a column in a Presto query should be pushed down into a query against CLP's
- * metadata database (during split pruning):
- *
- * - {@code columnName}: The column's name in the Presto query.
- *
- * - {@code customOptions}: Options specific to the current
- * {@link ClpSplitFilterProvider}.
- *
- * - {@code required} (optional, defaults to {@code false}): Whether the filter must be
- * present in the generated metadata query. If a required filter is missing or cannot be added to
- * the metadata query, the original query will be rejected.
- *
- */
-public class ClpSplitFilterConfig
-{
- @JsonProperty("columnName")
- public String columnName;
-
- @JsonProperty("customOptions")
- public CustomSplitFilterOptions customOptions;
-
- @JsonProperty("required")
- public boolean required;
-
- public interface CustomSplitFilterOptions
- {}
-}
diff --git a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfigCustomOptionsDeserializer.java b/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfigCustomOptionsDeserializer.java
deleted file mode 100644
index 3464c14883e26..0000000000000
--- a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterConfigCustomOptionsDeserializer.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.facebook.presto.plugin.clp.split.filter;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import java.io.IOException;
-
-import static com.facebook.presto.plugin.clp.split.filter.ClpSplitFilterConfig.CustomSplitFilterOptions;
-
-/**
- * Uses the given {@link CustomSplitFilterOptions} implementation to deserialize the
- * {@code "customOptions"} field in a {@link ClpSplitFilterConfig}. The implementation is determined
- * by the implemented {@link ClpSplitFilterProvider}.
- */
-public class ClpSplitFilterConfigCustomOptionsDeserializer
- extends JsonDeserializer
-{
- private final Class extends CustomSplitFilterOptions> actualCustomSplitFilterOptionsClass;
-
- public ClpSplitFilterConfigCustomOptionsDeserializer(Class extends CustomSplitFilterOptions> actualCustomSplitFilterOptionsClass)
- {
- this.actualCustomSplitFilterOptionsClass = actualCustomSplitFilterOptionsClass;
- }
-
- @Override
- public CustomSplitFilterOptions deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
- {
- ObjectNode node = p.getCodec().readTree(p);
- ObjectMapper mapper = (ObjectMapper) p.getCodec();
-
- return mapper.treeToValue(node, actualCustomSplitFilterOptionsClass);
- }
-}
diff --git a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterProvider.java b/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterProvider.java
deleted file mode 100644
index 0609843aaf22f..0000000000000
--- a/presto-clp/src/main/java/com/facebook/presto/plugin/clp/split/filter/ClpSplitFilterProvider.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.facebook.presto.plugin.clp.split.filter;
-
-import com.facebook.presto.plugin.clp.ClpConfig;
-import com.facebook.presto.spi.PrestoException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-
-import static com.facebook.presto.plugin.clp.ClpErrorCode.CLP_MANDATORY_SPLIT_FILTER_NOT_VALID;
-import static com.facebook.presto.plugin.clp.ClpErrorCode.CLP_SPLIT_FILTER_CONFIG_NOT_FOUND;
-import static com.facebook.presto.plugin.clp.split.filter.ClpSplitFilterConfig.CustomSplitFilterOptions;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static java.util.Objects.requireNonNull;
-
-/**
- * Loads and manages {@link ClpSplitFilterConfig}s from a config file.
- *
- * The config file is specified by the {@code clp.split-filter-config} property.
- *
- * Filter configs can be declared at either a catalog, schema, or table scope. Filter configs under
- * a particular scope will apply to all child scopes (e.g., schema-level filter configs will apply
- * to all tables within that schema).
- *
- * Implementations of this class can customize filter configs through the {@code "customOptions"}
- * field within each {@link ClpSplitFilterConfig}.
- */
-public abstract class ClpSplitFilterProvider
-{
- protected final Map> filterMap;
-
- public ClpSplitFilterProvider(ClpConfig config)
- {
- requireNonNull(config, "config is null");
-
- if (null == config.getSplitFilterConfig()) {
- filterMap = ImmutableMap.of();
- return;
- }
- ObjectMapper mapper = new ObjectMapper();
- SimpleModule module = new SimpleModule();
- module.addDeserializer(
- CustomSplitFilterOptions.class,
- new ClpSplitFilterConfigCustomOptionsDeserializer(getCustomSplitFilterOptionsClass()));
- mapper.registerModule(module);
- try {
- filterMap = mapper.readValue(
- new File(config.getSplitFilterConfig()),
- new TypeReference