HIVE-11705 : refactor SARG stripe filtering for ORC into a separate method (Sergey Shelukhin, reviewed by Prasanth Jayachandran)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/ba0b33c1 Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/ba0b33c1 Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/ba0b33c1 Branch: refs/heads/spark Commit: ba0b33c1025625b92bd669da60d2789f315e27f7 Parents: bc62a46 Author: Sergey Shelukhin <[email protected]> Authored: Tue Sep 15 18:09:54 2015 -0700 Committer: Sergey Shelukhin <[email protected]> Committed: Tue Sep 15 18:09:54 2015 -0700 ---------------------------------------------------------------------- .../hadoop/hive/ql/io/orc/OrcInputFormat.java | 151 ++++++++++++------- .../apache/hadoop/hive/ql/io/orc/OrcSerde.java | 1 + .../hadoop/hive/ql/io/orc/RecordReaderImpl.java | 4 +- .../hive/ql/io/parquet/ProjectionPusher.java | 3 +- .../hive/ql/io/sarg/ConvertAstToSearchArg.java | 4 + .../ql/optimizer/ColumnPrunerProcFactory.java | 3 + .../hadoop/hive/ql/parse/SemanticAnalyzer.java | 2 + .../hive/serde2/ColumnProjectionUtils.java | 22 +++ .../hive/ql/io/sarg/SearchArgumentFactory.java | 5 +- .../hive/ql/io/sarg/SearchArgumentImpl.java | 7 +- 10 files changed, 142 insertions(+), 60 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcInputFormat.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcInputFormat.java b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcInputFormat.java index cf8694e..2500fb6 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcInputFormat.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcInputFormat.java @@ -58,11 +58,13 @@ import org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; import org.apache.hadoop.hive.ql.io.InputFormatChecker; import org.apache.hadoop.hive.ql.io.RecordIdentifier; import org.apache.hadoop.hive.ql.io.StatsProvidingRecordReader; +import org.apache.hadoop.hive.ql.io.orc.OrcFile.WriterVersion; import org.apache.hadoop.hive.ql.io.orc.OrcInputFormat.Context; import org.apache.hadoop.hive.ql.io.sarg.ConvertAstToSearchArg; import org.apache.hadoop.hive.ql.io.sarg.PredicateLeaf; import org.apache.hadoop.hive.ql.io.sarg.SearchArgument; import org.apache.hadoop.hive.ql.io.sarg.SearchArgument.TruthValue; +import org.apache.hadoop.hive.ql.io.sarg.SearchArgumentFactory; import org.apache.hadoop.hive.serde2.ColumnProjectionUtils; import org.apache.hadoop.hive.serde2.SerDeStats; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; @@ -265,8 +267,7 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, OrcProto.Type root = types.get(rootColumn); for(int i=0; i < root.getSubtypesCount(); ++i) { if (included.contains(i)) { - includeColumnRecursive(types, result, root.getSubtypes(i), - rootColumn); + includeColumnRecursive(types, result, root.getSubtypes(i), rootColumn); } } return result; @@ -292,6 +293,13 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, int rootColumn = getRootColumn(isOriginal); String[] columnNames = new String[types.size() - rootColumn]; int i = 0; + // The way this works is as such. originalColumnNames is the equivalent on getNeededColumns + // from TSOP. They are assumed to be in the same order as the columns in ORC file, AND they are + // assumed to be equivalent to the columns in includedColumns (because it was generated from + // the same column list at some point in the past), minus the subtype columns. Therefore, when + // we go thru all the top level ORC file columns that are included, in order, they match + // originalColumnNames. This way, we do not depend on names stored inside ORC for SARG leaf + // column name resolution (see mapSargColumns method). for(int columnId: types.get(rootColumn).getSubtypesList()) { if (includedColumns == null || includedColumns[columnId - rootColumn]) { // this is guaranteed to be positive because types only have children @@ -306,8 +314,8 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, List<OrcProto.Type> types, Configuration conf, boolean isOriginal) { - String columnNamesString = conf.get(ColumnProjectionUtils.READ_COLUMN_NAMES_CONF_STR); - if (columnNamesString == null) { + String neededColumnNames = getNeededColumnNamesString(conf); + if (neededColumnNames == null) { LOG.debug("No ORC pushdown predicate - no column names"); options.searchArgument(null, null); return; @@ -321,9 +329,39 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, LOG.info("ORC pushdown predicate: " + sarg); options.searchArgument(sarg, getSargColumnNames( - columnNamesString.split(","), types, options.getInclude(), isOriginal)); + neededColumnNames.split(","), types, options.getInclude(), isOriginal)); } + static boolean canCreateSargFromConf(Configuration conf) { + if (getNeededColumnNamesString(conf) == null) { + LOG.debug("No ORC pushdown predicate - no column names"); + return false; + } + if (!ConvertAstToSearchArg.canCreateFromConf(conf)) { + LOG.debug("No ORC pushdown predicate"); + return false; + } + return true; + } + + private static String[] extractNeededColNames( + List<OrcProto.Type> types, Configuration conf, boolean[] include, boolean isOriginal) { + return extractNeededColNames(types, getNeededColumnNamesString(conf), include, isOriginal); + } + + private static String[] extractNeededColNames( + List<OrcProto.Type> types, String columnNamesString, boolean[] include, boolean isOriginal) { + return getSargColumnNames(columnNamesString.split(","), types, include, isOriginal); + } + + private static String getNeededColumnNamesString(Configuration conf) { + return conf.get(ColumnProjectionUtils.READ_COLUMN_NAMES_CONF_STR); + } + + private static String getSargColumnIDsString(Configuration conf) { + return conf.getBoolean(ColumnProjectionUtils.READ_ALL_COLUMNS, true) ? null + : conf.get(ColumnProjectionUtils.READ_COLUMN_IDS_CONF_STR); + } @Override public boolean validateInput(FileSystem fs, HiveConf conf, ArrayList<FileStatus> files @@ -863,34 +901,11 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, // we can't eliminate stripes if there are deltas because the // deltas may change the rows making them match the predicate. - if (deltas.isEmpty()) { - Reader.Options options = new Reader.Options(); - options.include(includedCols); - setSearchArgument(options, types, context.conf, isOriginal); - // only do split pruning if HIVE-8732 has been fixed in the writer - if (options.getSearchArgument() != null && - writerVersion != OrcFile.WriterVersion.ORIGINAL) { - SearchArgument sarg = options.getSearchArgument(); - List<PredicateLeaf> sargLeaves = sarg.getLeaves(); - List<StripeStatistics> stripeStats = metadata.getStripeStatistics(); - int[] filterColumns = RecordReaderImpl.mapSargColumns(sargLeaves, - options.getColumnNames(), getRootColumn(isOriginal)); - - if (stripeStats != null) { - // eliminate stripes that doesn't satisfy the predicate condition - includeStripe = new boolean[stripes.size()]; - for (int i = 0; i < stripes.size(); ++i) { - includeStripe[i] = (i >= stripeStats.size()) || - isStripeSatisfyPredicate(stripeStats.get(i), sarg, - filterColumns); - if (isDebugEnabled && !includeStripe[i]) { - LOG.debug("Eliminating ORC stripe-" + i + " of file '" + - file.getPath() + "' as it did not satisfy " + - "predicate condition."); - } - } - } - } + if (deltas.isEmpty() && canCreateSargFromConf(context.conf)) { + SearchArgument sarg = ConvertAstToSearchArg.createFromConf(context.conf); + String[] sargColNames = extractNeededColNames(types, context.conf, includedCols, isOriginal); + includeStripe = pickStripes(sarg, sargColNames, writerVersion, isOriginal, + metadata.getStripeStatistics(), stripes.size(), file.getPath()); } // if we didn't have predicate pushdown, read everything @@ -990,28 +1005,6 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, } return orcReader.getRawDataSizeFromColIndices(internalColIds); } - - private boolean isStripeSatisfyPredicate(StripeStatistics stripeStatistics, - SearchArgument sarg, - int[] filterColumns) { - List<PredicateLeaf> predLeaves = sarg.getLeaves(); - TruthValue[] truthValues = new TruthValue[predLeaves.size()]; - for (int pred = 0; pred < truthValues.length; pred++) { - if (filterColumns[pred] != -1) { - - // column statistics at index 0 contains only the number of rows - ColumnStatistics stats = stripeStatistics.getColumnStatistics()[filterColumns[pred]]; - truthValues[pred] = RecordReaderImpl.evaluatePredicate(stats, predLeaves.get(pred), null); - } else { - - // parition column case. - // partition filter will be evaluated by partition pruner so - // we will not evaluate partition filter here. - truthValues[pred] = TruthValue.YES_NO_NULL; - } - } - return sarg.evaluate(truthValues).isNeeded(); - } } static List<OrcSplit> generateSplitsInfo(Configuration conf) @@ -1353,6 +1346,54 @@ public class OrcInputFormat implements InputFormat<NullWritable, OrcStruct>, directory); } + private static boolean[] pickStripes(SearchArgument sarg, String[] sargColNames, + WriterVersion writerVersion, boolean isOriginal, List<StripeStatistics> stripeStats, + int stripeCount, Path filePath) { + LOG.info("ORC pushdown predicate: " + sarg); + if (sarg == null || stripeStats == null || writerVersion == OrcFile.WriterVersion.ORIGINAL) { + return null; // only do split pruning if HIVE-8732 has been fixed in the writer + } + // eliminate stripes that doesn't satisfy the predicate condition + List<PredicateLeaf> sargLeaves = sarg.getLeaves(); + int[] filterColumns = RecordReaderImpl.mapSargColumnsToOrcInternalColIdx(sargLeaves, + sargColNames, getRootColumn(isOriginal)); + return pickStripesInternal(sarg, filterColumns, stripeStats, stripeCount, filePath); + } + + private static boolean[] pickStripesInternal(SearchArgument sarg, int[] filterColumns, + List<StripeStatistics> stripeStats, int stripeCount, Path filePath) { + boolean[] includeStripe = new boolean[stripeCount]; + for (int i = 0; i < includeStripe.length; ++i) { + includeStripe[i] = (i >= stripeStats.size()) || + isStripeSatisfyPredicate(stripeStats.get(i), sarg, filterColumns); + if (isDebugEnabled && !includeStripe[i]) { + LOG.debug("Eliminating ORC stripe-" + i + " of file '" + filePath + + "' as it did not satisfy predicate condition."); + } + } + return includeStripe; + } + + private static boolean isStripeSatisfyPredicate( + StripeStatistics stripeStatistics, SearchArgument sarg, int[] filterColumns) { + List<PredicateLeaf> predLeaves = sarg.getLeaves(); + TruthValue[] truthValues = new TruthValue[predLeaves.size()]; + for (int pred = 0; pred < truthValues.length; pred++) { + if (filterColumns[pred] != -1) { + + // column statistics at index 0 contains only the number of rows + ColumnStatistics stats = stripeStatistics.getColumnStatistics()[filterColumns[pred]]; + truthValues[pred] = RecordReaderImpl.evaluatePredicate(stats, predLeaves.get(pred), null); + } else { + + // parition column case. + // partition filter will be evaluated by partition pruner so + // we will not evaluate partition filter here. + truthValues[pred] = TruthValue.YES_NO_NULL; + } + } + return sarg.evaluate(truthValues).isNeeded(); + } @VisibleForTesting static SplitStrategy determineSplitStrategy(Context context, FileSystem fs, Path dir, http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcSerde.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcSerde.java b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcSerde.java index 8beff4b..595f3b3 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcSerde.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/OrcSerde.java @@ -108,6 +108,7 @@ public class OrcSerde implements SerDe, VectorizedSerde { ArrayList<TypeInfo> fieldTypes = TypeInfoUtils.getTypeInfosFromTypeString(columnTypeProperty); StructTypeInfo rootType = new StructTypeInfo(); + // The source column names for ORC serde that will be used in the schema. rootType.setAllStructFieldNames(columnNames); rootType.setAllStructFieldTypeInfos(fieldTypes); inspector = OrcStruct.createObjectInspector(rootType); http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java index fcb3746..ba304ba 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/orc/RecordReaderImpl.java @@ -137,7 +137,7 @@ class RecordReaderImpl implements RecordReader { * result * @return an array mapping the sarg leaves to concrete column numbers */ - public static int[] mapSargColumns(List<PredicateLeaf> sargLeaves, + public static int[] mapSargColumnsToOrcInternalColIdx(List<PredicateLeaf> sargLeaves, String[] columnNames, int rootColumn) { int[] result = new int[sargLeaves.size()]; @@ -693,7 +693,7 @@ class RecordReaderImpl implements RecordReader { List<OrcProto.Type> types, int includedCount) { this.sarg = sarg; sargLeaves = sarg.getLeaves(); - filterColumns = mapSargColumns(sargLeaves, columnNames, 0); + filterColumns = mapSargColumnsToOrcInternalColIdx(sargLeaves, columnNames, 0); this.rowIndexStride = rowIndexStride; // included will not be null, row options will fill the array with trues if null sargColumns = new boolean[includedCount]; http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/ProjectionPusher.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/ProjectionPusher.java b/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/ProjectionPusher.java index 4480600..4848efd 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/ProjectionPusher.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/parquet/ProjectionPusher.java @@ -65,6 +65,7 @@ public class ProjectionPusher { } } + @Deprecated // Uses deprecated methods on ColumnProjectionUtils private void pushProjectionsAndFilters(final JobConf jobConf, final String splitPath, final String splitPathWithNoSchema) { @@ -136,7 +137,7 @@ public class ProjectionPusher { filterExprSerialized); } - + @Deprecated // Uses deprecated methods on ColumnProjectionUtils public JobConf pushProjectionsAndFilters(JobConf jobConf, Path path) throws IOException { updateMrWork(jobConf); // TODO: refactor this in HIVE-6366 http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/io/sarg/ConvertAstToSearchArg.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/io/sarg/ConvertAstToSearchArg.java b/ql/src/java/org/apache/hadoop/hive/ql/io/sarg/ConvertAstToSearchArg.java index e034650..690b8c9 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/io/sarg/ConvertAstToSearchArg.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/io/sarg/ConvertAstToSearchArg.java @@ -433,4 +433,8 @@ public class ConvertAstToSearchArg { return null; } + public static boolean canCreateFromConf(Configuration conf) { + return conf.get(TableScanDesc.FILTER_EXPR_CONF_STR) != null || conf.get(SARG_PUSHDOWN) != null; + } + } http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ColumnPrunerProcFactory.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ColumnPrunerProcFactory.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ColumnPrunerProcFactory.java index 2dc15f9..b104a7d 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ColumnPrunerProcFactory.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ColumnPrunerProcFactory.java @@ -488,6 +488,9 @@ public final class ColumnPrunerProcFactory { } } + /** Sets up needed columns for TSOP. Mainly, transfers column names from input + * RowSchema as well as the needed virtual columns, into TableScanDesc. + */ public static void setupNeededColumns(TableScanOperator scanOp, RowSchema inputRS, List<String> cols) throws SemanticException { List<Integer> neededColumnIds = new ArrayList<Integer>(); http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java ---------------------------------------------------------------------- diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java index 16957b6..1076dfd 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java @@ -9324,6 +9324,8 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { } if (top == null) { + // Determine row schema for TSOP. + // Include column names from SerDe, the partition and virtual columns. rwsch = new RowResolver(); try { StructObjectInspector rowObjectInspector = (StructObjectInspector) tab http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/serde/src/java/org/apache/hadoop/hive/serde2/ColumnProjectionUtils.java ---------------------------------------------------------------------- diff --git a/serde/src/java/org/apache/hadoop/hive/serde2/ColumnProjectionUtils.java b/serde/src/java/org/apache/hadoop/hive/serde2/ColumnProjectionUtils.java index 10086c5..cbad3b2 100644 --- a/serde/src/java/org/apache/hadoop/hive/serde2/ColumnProjectionUtils.java +++ b/serde/src/java/org/apache/hadoop/hive/serde2/ColumnProjectionUtils.java @@ -22,9 +22,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.util.StringUtils; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.collect.Lists; @@ -33,6 +36,7 @@ import com.google.common.collect.Lists; * */ public final class ColumnProjectionUtils { + public static final Log LOG = LogFactory.getLog(ColumnProjectionUtils.class); public static final String READ_COLUMN_IDS_CONF_STR = "hive.io.file.readcolumn.ids"; public static final String READ_ALL_COLUMNS = "hive.io.file.read.all.columns"; @@ -54,6 +58,7 @@ public final class ColumnProjectionUtils { * and appendReadColumns */ @Deprecated + @VisibleForTesting public static void setReadColumnIDs(Configuration conf, List<Integer> ids) { setReadColumnIDConf(conf, READ_COLUMN_IDS_CONF_STR_DEFAULT); appendReadColumns(conf, ids); @@ -102,8 +107,21 @@ public final class ColumnProjectionUtils { conf.setBoolean(READ_ALL_COLUMNS, false); } + /** + * This method appends read column information to configuration to use for PPD. It is + * currently called with information from TSOP. Names come from TSOP input RowSchema, and + * IDs are the indexes inside the schema (which PPD assumes correspond to indexes inside the + * files to PPD in; something that would be invalid in many cases of schema evolution). + * @param conf Config to set values to. + * @param ids Column ids. + * @param names Column names. + */ public static void appendReadColumns( Configuration conf, List<Integer> ids, List<String> names) { + if (ids.size() != names.size()) { + LOG.warn("Read column counts do not match: " + + ids.size() + " ids, " + names.size() + " names"); + } appendReadColumns(conf, ids); appendReadColumnNames(conf, names); } @@ -125,9 +143,13 @@ public final class ColumnProjectionUtils { List<Integer> result = new ArrayList<Integer>(list.length); for (String element : list) { // it may contain duplicates, remove duplicates + // TODO: WTF? This would break many assumptions elsewhere if it did. + // Column names' and column ids' lists are supposed to be correlated. Integer toAdd = Integer.parseInt(element); if (!result.contains(toAdd)) { result.add(toAdd); + } else if (LOG.isInfoEnabled()) { + LOG.info("Duplicate ID " + toAdd + " in column ID list"); } } return result; http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentFactory.java ---------------------------------------------------------------------- diff --git a/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentFactory.java b/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentFactory.java index 0778935..8fda95c 100644 --- a/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentFactory.java +++ b/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentFactory.java @@ -19,10 +19,13 @@ package org.apache.hadoop.hive.ql.io.sarg; /** - * A factory for creating SearchArguments. + * A factory for creating SearchArguments, as well as modifying those created by this factory. */ public class SearchArgumentFactory { public static SearchArgument.Builder newBuilder() { return new SearchArgumentImpl.BuilderImpl(); } + public static void setPredicateLeafColumn(PredicateLeaf leaf, String newName) { + SearchArgumentImpl.PredicateLeafImpl.setColumnName(leaf, newName); + } } http://git-wip-us.apache.org/repos/asf/hive/blob/ba0b33c1/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java ---------------------------------------------------------------------- diff --git a/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java b/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java index d27ac16..a762b8b 100644 --- a/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java +++ b/storage-api/src/java/org/apache/hadoop/hive/ql/io/sarg/SearchArgumentImpl.java @@ -40,7 +40,7 @@ final class SearchArgumentImpl implements SearchArgument { static final class PredicateLeafImpl implements PredicateLeaf { private final Operator operator; private final Type type; - private final String columnName; + private String columnName; private final Object literal; private final List<Object> literalList; @@ -165,6 +165,11 @@ final class SearchArgumentImpl implements SearchArgument { (literalList == null ? 0 : literalList.hashCode()) * 103 * 101 * 3 * 17; } + + public static void setColumnName(PredicateLeaf leaf, String newName) { + assert leaf instanceof PredicateLeafImpl; + ((PredicateLeafImpl)leaf).columnName = newName; + } }
