Skip to content
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

Part 2 of changes for SQL Compatible Null Handling #5958

Merged
merged 23 commits into from
Aug 2, 2018

Conversation

nishantmonu51
Copy link
Member

@nishantmonu51 nishantmonu51 commented Jul 6, 2018

Part 2 of changes for SQL Compatible Null Handling -
This PR contains changes to improve handling of null values in druid by treating nulls as missing values which don’t actually exist. This will make it more compatible to SQL standard and help with integration with other existing BI systems which support ODBC/JDBC. (Detailed description in proposal - #4349)

NOTE - NULL and empty strings are treated differently in entire druid query processing system.

Nullability for Aggregators/Metrics

As per the current implementation, most aggregators are coded to replace null values with default e.g. sum treats them as zeroes, min treats them as positive infinity, etc.
To match the new semantics and make aggregators nullable, where if an aggregator encounters only the null values the result will be null value following changes are made -

  • Added a NullableAggregator/NullableBufferAggregator which is be used as a decorator for existing aggregator implementations to make them nullable. This will return null values if all the values aggregated are null, If any of the aggregated value is non-null, it will have same behavior as the delegate aggregator.
  • If new null behavior is enabled, aggregator factories will decorate the aggregators with NullableAggregator/NullableBufferAggregator.
  • Added a new abstract class NullableAggregatorFactory which helps in reducing duplicate code and decide to switch between nullable and non-nullable aggregators.
  • Cardinality and HyperUnique aggregators ignore null values and only count non-null values, If all the encountered values are null, the result will be 0. This is different from current behavior where null values are also counted.
  • Count aggregator - since count aggregator is not associated with any column and just a count of the number of rows, it has the same behavior as before.

Math Expressions Null Handling

  • For general calculations like sum, full expression is be considered as null if any of the components is null.
  • StringLiteral now supports null and and expressions containing non-quoted null is parsed as StringLiteral with null
  • Specifying a default value for null is supported by the use of NVL or IF clause to assign default values at query time.
  • Comparisons between null and non-null values will follow three-valued logic and the result of a comparison involving NULL is not a boolean value – it is a null value.

Filtering on Null values

  • SelectorDimFilter currently specifies filtering on null values but the implementation assumes null and empty strings as equivalent. The implementation is changed to consider null and empty string differently. Generation of cache key for selectorDimFilter is also modified to support null.
  • InFilter/ExpressionFilter/LikeFilter are also modified to not treat nulls as empty strings.

Changes to Druid build-in SQL layer

  • NULL and empty strings are treated differently.
  • IS NULL and IS NOT NULL filters against null values instead of empty strings.
  • NVL, IFNULL and COALESCE work as per SQL standard.
  • count now skips null values and works as per SQL standard.

Backwards Compatibility

  • "druid.generic.useDefaultValueForNull" is added as a new config. The default value is set to true for backwards compatibility.
  • code places in query/ingestion where we converted emptyToNull or nullToEmpty now use NullHandlingHelper which only does the conversion if backwards compatibility is enabled.
  • Storage layer changes are backwards compatible for GenericIndexed/String columns.
  • For Numeric columns new V2 serde implementations are introduced which also supports serde of nullRowsBitmaps. Previous implementations are kept unchanged to read any existing segments.

Testing

  • Most of the unit tests that tested nulls are modified to have two branches of expected results to test new/old behavior both.
  • For CI have added new runs to test matrix to test new behavior for nulls.
  • To run the tests with new behavior run "-Ddruid.generic.useDefaultValueForNull=false"

Pending work -
https://github.com/apache/incubator-druid/labels/Area%20-%20Null%20Handling

@nishantmonu51
Copy link
Member Author

@leventov : Created a new PR, please review.

@leventov
Copy link
Member

leventov commented Jul 7, 2018

@nishantmonu51 as far as I can see you didn't address all comments that I left in the last batch in the previous PR, please answer to them there (if you are able to open that PR)

@nishantmonu51
Copy link
Member Author

@leventov: last batch of comments was re lines more than 120 cols, add line breaks and reformatted code. Also replied to your comments on previous PR, If you have any more comments can you please add them here ?

@jihoonson
Copy link
Contributor

Since this PR is tagged as Design Review, I'm going to review this PR too.

@jihoonson
Copy link
Contributor

@nishantmonu51 is this the last PR for #4349? Would you let me know which are not done yet in #5278 and this PR?

@jihoonson
Copy link
Contributor

Some comments and questions for the PR description.

Aggrgator/BufferAggregator - Added a new method boolean isNull() which returns false by default. aggregators that support nullability can choose to override this and return true if the aggregated result is null.

This should be moved to #5278.

Changes to Druid build-in SQL layer

  • NULL and empty strings are treated differently.

I haven't checked through the whole patch, so am wondering this is true only for the SQL layer or the entire Druid query processing system.

Copy link
Contributor

@jihoonson jihoonson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great work. Thanks @nishantmonu51!

Reviewed up to LoadingLookup and still reviewing.

@@ -99,10 +98,10 @@ public Object value()
return value;
}

public boolean isNull()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's better to call this method rather than doing value() == null in call sites.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is replaced with isNumericNull as that is what we need to check for Expressions.
isNumericNull will check whether the primitive long/float/double equivalent is null or not.
It is possible for an expression to have a non-null String value but it can return null when parsed as a primitive long/float/double.

return ExprEval.of(null);
}

if (leftVal.type() == ExprType.STRING && rightVal.type() == ExprType.STRING) {
return evalString(leftVal.asString(), rightVal.asString());
} else if (leftVal.type() == ExprType.LONG && rightVal.type() == ExprType.LONG) {
if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder why we check this here instead of changing evalLong() or evalDouble to return a nullable Long or Double, respectively. Is this to avoid boxing/unboxing? If so, I wonder how much it affects to the performance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about how much performance effect boxing/unboxing will have, in this form atleast the change is on safer side and does not affect performance for non-sql case.
We can run more benchmarks in a follow up PR and change this api to return boxed values if the performance diff is not much.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

return theLong == null ? 0L : theLong;
Number number = asNumber();
if (number == null) {
assert NullHandling.replaceWithDefault();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about checking and throwing an Exception rather than a assert statement? The assert statement wouldn't be super helpful for users if it happens. Same for all other assert statements in this class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, I was actually doing that before, but changed this to assert based on discussions on previous PRs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, would you point me out where the discussion is? I guess it's in #5278?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unable to find the discussion among lots of review comments.
I believe the intent here was to catch any coding errors/places where we may have missed handling nulls properly.
@leventov: can probably add more context here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -59,7 +60,9 @@ public ApproximateHistogramAggregator(
@Override
public void aggregate()
{
histogram.offer(selector.getFloat());
if (NullHandling.replaceWithDefault() || !selector.isNull()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does NullHandling.replaceWithDefault() mean here? Does that mean nulls have replaced with some default value at ingestion time? If so, I guess select.isNull() always return false and thus this check can be simplified into if (!selector.isNull()).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this means whether nulls should be replaced with default value.
In case of ExpressionColumnValueSelector isNull will compute the expression and then give the result, the check is there to not have any performance impact of calling isNull for default case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thanks. Would you add a comment here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

{
if (key == null) {
String keyEquivalent = NullHandling.nullToEmptyIfNeeded(key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this kind of null value conversion is needed only when retrieving values from the storage layer (like in ColumnValueSelector), so that we expect key is already converted to some default value if needed. But, apparently it seems not. Would you explain why this is needed after retrieving values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is to preserve backwards compatibility,
previously, nulls and empty strings were considered same.
and if a looking has mapping "" -> "someval" then all nulls/empty strings will be resolved to "someval".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@nishantmonu51 nishantmonu51 added this to the 0.13.0 milestone Jul 10, 2018
@nishantmonu51
Copy link
Member Author

Thanks @jihoonson for helping with the review and taking this PR forward.

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class FloatFirstAggregatorFactory extends AggregatorFactory
public class FloatFirstAggregatorFactory extends NullableAggregatorFactory<ColumnValueSelector>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment: #5452 (comment) is not addressed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a blocker for this PR, would like to do it a follow up PR - #6039

@@ -73,7 +74,14 @@ public static PeriodGranularity toPeriodGranularity(
} else {
Chronology chronology = timeZone == null ? ISOChronology.getInstanceUTC() : ISOChronology.getInstance(timeZone);
final Object value = originArg.eval(bindings).value();
origin = value != null ? new DateTime(value, chronology) : null;
if (value instanceof String && StringUtils.isEmpty((String) value)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment: #5452 (comment) is not addressed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#6040
Will fix this line in this PR and prohibit the api in future PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -90,7 +95,7 @@ public SelectorDimFilter(
@Override
public DimFilter optimize()
{
return new InDimFilter(dimension, ImmutableList.of(value), extractionFn).optimize();
return new InDimFilter(dimension, Arrays.asList(value), extractionFn).optimize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections.singletonList()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -27,5 +29,7 @@
// converted to strings. We should also add functions
// for these and modify ColumnComparisonFilter to handle
// comparing Long and Float columns to eachother.
// Returns null when the underlying Long/Float value is null.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not addressed: #5452 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -269,12 +270,22 @@ public int compare(Row left, Row right)

private Ordering<Row> metricOrdering(final String column, final Comparator comparator)
{
return Ordering.from(Comparator.comparing((Row row) -> row.getRaw(column), Comparator.nullsLast(comparator)));
if (NullHandling.sqlCompatible()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a comment explaining this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment.

@@ -93,7 +93,7 @@ public String getAnnouncementPath(String listenerName)
{
return ZKPaths.makePath(
getListenersPath(), Preconditions.checkNotNull(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each argument should be on a separate line

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -107,6 +108,7 @@ public static SchemaPlus createRootSchema(final Schema druidSchema, final Author

public static String escapeStringLiteral(final String s)
{
Preconditions.checkNotNull(s);
if (s == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check immediately after Preconditions.checkNotNull?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the == null check, it was not needed.

@@ -294,7 +295,11 @@ public RelOptCost computeSelfCost(final RelOptPlanner planner, final RelMetadata

for (int i : rightKeys) {
final Object value = row[i];
final String stringValue = value != null ? String.valueOf(value) : "";
if (value == null) {
// NULLS are not supposed to match NULLs in a join. So ignore them.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NULLs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -84,7 +84,7 @@ public void reduce(
if (sqlTypeName == SqlTypeName.BOOLEAN) {
literal = rexBuilder.makeLiteral(exprResult.asBoolean(), constExp.getType(), true);
} else if (sqlTypeName == SqlTypeName.DATE) {
if (!constExp.getType().isNullable() && exprResult.isNull()) {
if (!constExp.getType().isNullable() && exprResult.isNumericNull()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible for an expression to have a non-null String value but it can return null when parsed as a primitive long/float/double.
ExprEval.isNumericNull checks whether the parsed primitive value is null or not.
Added comment to code.

getCluster().getRexBuilder().makeLiteral(value)
)
);
// NULLS are not supposed to match NULLs in a join. So ignore them.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NULLs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@nishantmonu51
Copy link
Member Author

@leventov @jihoonson : Thanks for the review comments, addressed them.
Do you have any more comments ?
Also, can you please add some info on how much of the review is pending and when can we expect to merge this one in.
Its blocking feature development/patches for Apache Hive Integration for handling nulls coming from druid properly.

Copy link
Contributor

@jihoonson jihoonson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nishantmonu51 I finished my first review. You did a really nice job. I would like to thank you.

Two more comments:

if (NullHandling.sqlCompatible()) {
// empty string should also have same behavior
Assert.assertEquals(null, loadingLookup.apply(""));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be Assert.assertNull().

Also, do we need to test for non-sql-compatible mode? Like:

    Assert.assertNull(loadingLookup.apply(null));
    if (NullHandling.sqlCompatible()) {
      // empty string should also have same behavior
      Assert.assertNull(loadingLookup.apply(""));
    } else {
      Assert.assertEquals("", loadingLookup.apply(""));
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

if (NullHandling.sqlCompatible()) {
Assert.assertEquals(Collections.emptyList(), loadingLookup.unapply(null));
} else {
Assert.assertEquals(null, loadingLookup.unapply(null));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.assertNull()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Assert.assertEquals(ImmutableMap.of("value", Lists.newArrayList("key")), loadingLookup.unapplyAll(ImmutableSet.<String>of("value")));
Assert.assertEquals(
ImmutableMap.of("value", Lists.newArrayList("key")),
loadingLookup.unapplyAll(ImmutableSet.<String>of("value"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you please remove the unnecessary type argument?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

@@ -1466,7 +1505,7 @@ public void close()
);
}

public long sumMetric(final Task task, final DimFilter filter, final String metric)
public Long sumMetric(final Task task, final DimFilter filter, final String metric)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please annotate @Nullable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

@@ -1081,7 +1088,7 @@ public void close()
return toolboxFactory.build(task);
}

public long sumMetric(final Task task, final DimFilter filter, final String metric)
public Long sumMetric(final Task task, final DimFilter filter, final String metric)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please annotate @Nullable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

valuesBytesSize += valuesBytes[index].length + 1;
++index;
}
byte[] extractionFnBytes = extractionFn == null ? new byte[0] : extractionFn.getCacheKey();

ByteBuffer filterCacheKey = ByteBuffer.allocate(3
ByteBuffer filterCacheKey = ByteBuffer.allocate(5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we use CacheKeyBuilder here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -77,10 +81,12 @@ public SelectorDimFilter(
byte[] valueBytes = (value == null) ? new byte[]{} : StringUtils.toUtf8(value);
byte[] extractionFnBytes = extractionFn == null ? new byte[0] : extractionFn.getCacheKey();

return ByteBuffer.allocate(3 + dimensionBytes.length + valueBytes.length + extractionFnBytes.length)
return ByteBuffer.allocate(5 + dimensionBytes.length + valueBytes.length + extractionFnBytes.length)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we use CacheKeyBuilder here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -1700,6 +1712,78 @@ public BufferComparator getBufferComparator()
return bufferComparator;
}
}

// This class is only used when SQL compatible null handling is enabled.
private class NullableRowBasedKeySerdeHelper implements RowBasedKeySerdeHelper
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about the memory layout.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -90,7 +90,10 @@ public boolean supportsSelectivityEstimation(
{
if (isSimpleEquals()) {
// Verify that dimension equals prefix.
return ImmutableList.of(selector.getBitmapIndex(dimension, likeMatcher.getPrefix()));
return ImmutableList.of(selector.getBitmapIndex(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format

      return ImmutableList.of(
          selector.getBitmapIndex(
              dimension,
              NullHandling.emptyToNullIfNeeded(likeMatcher.getPrefix())
          )
      );

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -80,7 +80,7 @@ public DruidExpression toDruidExpression(
// So there is no simple extraction for this operator.
return DruidExpression.fromFunctionCall(
"timestamp_ceil",
ImmutableList.of(
Arrays.asList(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Stream.of() is better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed because ImmutableList doesn't support nulls, I suppose. Anyway, a parallel PR #5980 handles this in the whole codebase.

Copy link
Member

@leventov leventov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM besides @jihoonson's comments, and if you also assign #6039 to yourself and do that after this PR.

@nishantmonu51
Copy link
Member Author

Aggrgator/BufferAggregator - Added a new method boolean isNull() which returns false by default. aggregators that support nullability can choose to override this and return true if the aggregated result is null.
This should be moved to #5278.

Removed it from this PR.

Changes to Druid build-in SQL layer
NULL and empty strings are treated differently.
I haven't checked through the whole patch, so am wondering this is true only for the SQL layer or the entire Druid query processing system.

it applies to whole query processing. updated PR description to add a note.

@nishantmonu51
Copy link
Member Author

nishantmonu51 commented Jul 27, 2018

@jihoonson : For the pending work, I have labelled the issues with Area : Null Handling
https://github.com/apache/incubator-druid/labels/Area%20-%20Null%20Handling

Have updated the PR based on your comments, please check if i missed any comments.

@jihoonson
Copy link
Contributor

Thanks @nishantmonu51. Would you please check this comment: #5958 (comment)?

Also, I'm not sure what have done in #5278 + this PR and not. I think at least Comparisons between null and non-null values will follow three-valued logic and the result of a comparison involving NULL is not a boolean value – it is a null value. is not done yet? Would you please clarify what are remaining in #4349?

@nishantmonu51
Copy link
Member Author

nishantmonu51 commented Jul 31, 2018

Comparisons between null and non-null values will follow three-valued logic and the result of a comparison involving NULL is not a boolean value – it is a null value.

this actually works now for Expressions, See BinaryEvalOpExprBase.eval, if any of the values being compared is null it returns null.
Do you see any places where this is might not work ?

@nishantmonu51
Copy link
Member Author

@jihoonson In terms of functionality, After this PR, all things mentioned in the proposal except following should be working -

  1. Users will be able to specify default values to replace NULL values at ingestion time.
  • Handling of follow up comments perf related
  • Updating/Adding docs for the new behavior

@nishantmonu51
Copy link
Member Author

@jihoonson any more comments here ?

@jihoonson
Copy link
Contributor

@nishantmonu51 awesome! Thanks for clarifying. Everything looks good except the travis failure. Would you check it please?

@@ -67,7 +82,14 @@ public void testUnapplyAll() throws ExecutionException
.andReturn(Collections.singletonList("key"))
.once();
EasyMock.replay(reverseLookupCache);
<<<<<<< HEAD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please resolve the conflict..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@nishantmonu51
Copy link
Member Author

@jihoonson: travis is passing now.

Copy link
Contributor

@jihoonson jihoonson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nishantmonu51
Copy link
Member Author

@leventov @jihoonson @b-slim Thanks for the review.
This PR is ready for merge and has got required +1s.
can one of you please merge this ?

@b-slim b-slim merged commit 75c8a87 into apache:master Aug 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants