This is an automated email from the ASF dual-hosted git repository. rongr pushed a commit to branch multi_stage_query_engine in repository https://gitbox.apache.org/repos/asf/pinot.git
commit fa2b2fa7cfa5410b2d5d5451942d1404ab72209e Author: Rong Rong <ro...@apache.org> AuthorDate: Thu May 12 15:00:54 2022 -0700 [hotfix] multi stage engine serde (#8689) * [hotfix] serde issue with RelDataType from Calcite - fix serde issue with calcite data object - adding in correct serde test * remove rowType, as it is encoded in RexExpression already Co-authored-by: Rong Rong <ro...@startree.ai> --- .../query/parser/CalciteRexExpressionParser.java | 7 +- .../query/planner/logical/RelToStageConverter.java | 8 +- .../pinot/query/planner/logical/RexExpression.java | 131 ++++++++++++++------- .../pinot/query/planner/logical/StagePlanner.java | 14 +-- .../query/planner/stage/AbstractStageNode.java | 10 -- .../pinot/query/planner/stage/FilterNode.java | 4 +- .../apache/pinot/query/planner/stage/JoinNode.java | 4 +- .../query/planner/stage/MailboxReceiveNode.java | 5 +- .../pinot/query/planner/stage/MailboxSendNode.java | 11 +- .../pinot/query/planner/stage/ProjectNode.java | 8 +- .../pinot/query/planner/stage/TableScanNode.java | 4 +- .../pinot/query/planner/stage/SerDeUtilsTest.java | 50 ++++---- 12 files changed, 136 insertions(+), 120 deletions(-) diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/parser/CalciteRexExpressionParser.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/parser/CalciteRexExpressionParser.java index 91d573c89a..a4f5753557 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/parser/CalciteRexExpressionParser.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/parser/CalciteRexExpressionParser.java @@ -22,7 +22,6 @@ import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; import org.apache.pinot.common.request.Expression; import org.apache.pinot.common.request.ExpressionType; @@ -139,11 +138,7 @@ public class CalciteRexExpressionParser { } private static Expression rexLiteralToExpression(RexExpression.Literal rexLiteral) { - RelDataType type = rexLiteral.getDataType(); - switch (type.getSqlTypeName()) { - default: - return RequestUtils.getLiteralExpression(rexLiteral.getValue()); - } + return RequestUtils.getLiteralExpression(rexLiteral.getValue()); } private static Expression inputRefToIdentifier(RexExpression.InputRef inputRef, PinotQuery pinotQuery) { diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java index 3750437f7f..297dce68e5 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java @@ -73,18 +73,18 @@ public final class RelToStageConverter { } private static StageNode convertLogicalProject(LogicalProject node, int currentStageId) { - return new ProjectNode(currentStageId, node.getRowType(), node.getProjects()); + return new ProjectNode(currentStageId, node.getProjects()); } private static StageNode convertLogicalFilter(LogicalFilter node, int currentStageId) { - return new FilterNode(currentStageId, node.getRowType(), node.getCondition()); + return new FilterNode(currentStageId, node.getCondition()); } private static StageNode convertLogicalTableScan(LogicalTableScan node, int currentStageId) { String tableName = node.getTable().getQualifiedName().get(0); List<String> columnNames = node.getRowType().getFieldList().stream() .map(RelDataTypeField::getName).collect(Collectors.toList()); - return new TableScanNode(currentStageId, node.getRowType(), tableName, columnNames); + return new TableScanNode(currentStageId, tableName, columnNames); } private static StageNode convertLogicalJoin(LogicalJoin node, int currentStageId) { @@ -102,7 +102,7 @@ public final class RelToStageConverter { FieldSelectionKeySelector leftFieldSelectionKeySelector = new FieldSelectionKeySelector(leftOperandIndex); FieldSelectionKeySelector rightFieldSelectionKeySelector = new FieldSelectionKeySelector(rightOperandIndex - leftRowType.getFieldNames().size()); - return new JoinNode(currentStageId, node.getRowType(), joinType, Collections.singletonList(new JoinNode.JoinClause( + return new JoinNode(currentStageId, joinType, Collections.singletonList(new JoinNode.JoinClause( leftFieldSelectionKeySelector, rightFieldSelectionKeySelector))); } } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RexExpression.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RexExpression.java index 899636ab4d..17e472c811 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RexExpression.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RexExpression.java @@ -30,72 +30,80 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.NlsString; import org.apache.pinot.query.planner.serde.ProtoProperties; +import org.apache.pinot.spi.data.FieldSpec; import org.checkerframework.checker.nullness.qual.Nullable; /** * {@code RexExpression} is the serializable format of the {@link RexNode}. */ -public abstract class RexExpression { - @ProtoProperties - protected SqlKind _sqlKind; - @ProtoProperties - protected RelDataType _dataType; - - public SqlKind getKind() { - return _sqlKind; - } +public interface RexExpression { - public RelDataType getDataType() { - return _dataType; - } + SqlKind getKind(); - public static RexExpression toRexExpression(RexNode rexNode) { + FieldSpec.DataType getDataType(); + + static RexExpression toRexExpression(RexNode rexNode) { if (rexNode instanceof RexInputRef) { return new RexExpression.InputRef(((RexInputRef) rexNode).getIndex()); } else if (rexNode instanceof RexLiteral) { RexLiteral rexLiteral = ((RexLiteral) rexNode); - return new RexExpression.Literal(rexLiteral.getType(), rexLiteral.getTypeName(), rexLiteral.getValue()); + FieldSpec.DataType dataType = toDataType(rexLiteral.getType()); + return new RexExpression.Literal(dataType, rexLiteral.getTypeName(), + toRexValue(dataType, rexLiteral.getValue())); } else if (rexNode instanceof RexCall) { RexCall rexCall = (RexCall) rexNode; List<RexExpression> operands = rexCall.getOperands().stream().map(RexExpression::toRexExpression) .collect(Collectors.toList()); - return new RexExpression.FunctionCall(rexCall.getKind(), rexCall.getType(), rexCall.getOperator().getName(), - operands); + return new RexExpression.FunctionCall(rexCall.getKind(), toDataType(rexCall.getType()), + rexCall.getOperator().getName(), operands); } else { throw new IllegalArgumentException("Unsupported RexNode type with SqlKind: " + rexNode.getKind()); } } - private static Comparable convertLiteral(Comparable value, SqlTypeName sqlTypeName, RelDataType dataType) { - switch (sqlTypeName) { - case BOOLEAN: - return (boolean) value; - case DECIMAL: - switch (dataType.getSqlTypeName()) { - case INTEGER: - return ((BigDecimal) value).intValue(); - case BIGINT: - return ((BigDecimal) value).longValue(); - case FLOAT: - return ((BigDecimal) value).floatValue(); - case DOUBLE: - default: - return ((BigDecimal) value).doubleValue(); - } - case CHAR: - switch (dataType.getSqlTypeName()) { - case VARCHAR: - return ((NlsString) value).getValue(); - default: - return value; - } + static Object toRexValue(FieldSpec.DataType dataType, Comparable value) { + switch (dataType) { + case INT: + return ((BigDecimal) value).intValue(); + case LONG: + return ((BigDecimal) value).longValue(); + case FLOAT: + return ((BigDecimal) value).floatValue(); + case DOUBLE: + return ((BigDecimal) value).doubleValue(); + case STRING: + return ((NlsString) value).getValue(); default: return value; } } - public static class InputRef extends RexExpression { + static FieldSpec.DataType toDataType(RelDataType type) { + switch (type.getSqlTypeName()) { + case INTEGER: + return FieldSpec.DataType.INT; + case BIGINT: + return FieldSpec.DataType.LONG; + case FLOAT: + return FieldSpec.DataType.FLOAT; + case DOUBLE: + return FieldSpec.DataType.DOUBLE; + case VARCHAR: + return FieldSpec.DataType.STRING; + case BOOLEAN: + return FieldSpec.DataType.BOOLEAN; + default: + // TODO: do not assume byte type. + return FieldSpec.DataType.BYTES; + } + } + + class InputRef implements RexExpression { + @ProtoProperties + private SqlKind _sqlKind; + @ProtoProperties + private FieldSpec.DataType _dataType; @ProtoProperties private int _index; @@ -110,27 +118,51 @@ public abstract class RexExpression { public int getIndex() { return _index; } + + public SqlKind getKind() { + return _sqlKind; + } + + public FieldSpec.DataType getDataType() { + return _dataType; + } } - public static class Literal extends RexExpression { + class Literal implements RexExpression { + @ProtoProperties + private SqlKind _sqlKind; + @ProtoProperties + private FieldSpec.DataType _dataType; @ProtoProperties private Object _value; public Literal() { } - public Literal(RelDataType dataType, SqlTypeName sqlTypeName, @Nullable Comparable value) { + public Literal(FieldSpec.DataType dataType, SqlTypeName sqlTypeName, @Nullable Object value) { _sqlKind = SqlKind.LITERAL; _dataType = dataType; - _value = convertLiteral(value, sqlTypeName, dataType); + _value = value; } public Object getValue() { return _value; } + + public SqlKind getKind() { + return _sqlKind; + } + + public FieldSpec.DataType getDataType() { + return _dataType; + } } - public static class FunctionCall extends RexExpression { + class FunctionCall implements RexExpression { + @ProtoProperties + private SqlKind _sqlKind; + @ProtoProperties + private FieldSpec.DataType _dataType; @ProtoProperties private String _functionName; @ProtoProperties @@ -139,7 +171,8 @@ public abstract class RexExpression { public FunctionCall() { } - public FunctionCall(SqlKind sqlKind, RelDataType type, String functionName, List<RexExpression> functionOperands) { + public FunctionCall(SqlKind sqlKind, FieldSpec.DataType type, String functionName, + List<RexExpression> functionOperands) { _sqlKind = sqlKind; _dataType = type; _functionName = functionName; @@ -153,5 +186,13 @@ public abstract class RexExpression { public List<RexExpression> getFunctionOperands() { return _functionOperands; } + + public SqlKind getKind() { + return _sqlKind; + } + + public FieldSpec.DataType getDataType() { + return _dataType; + } } } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/StagePlanner.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/StagePlanner.java index b5dfab8689..db7fd63d83 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/StagePlanner.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/StagePlanner.java @@ -72,10 +72,9 @@ public class StagePlanner { // global root needs to send results back to the ROOT, a.k.a. the client response node. the last stage only has one // receiver so doesn't matter what the exchange type is. setting it to SINGLETON by default. StageNode globalReceiverNode = - new MailboxReceiveNode(0, relRoot.getRowType(), globalStageRoot.getStageId(), - RelDistribution.Type.SINGLETON); - StageNode globalSenderNode = new MailboxSendNode(globalStageRoot.getStageId(), relRoot.getRowType(), - globalReceiverNode.getStageId(), RelDistribution.Type.SINGLETON); + new MailboxReceiveNode(0, globalStageRoot.getStageId(), RelDistribution.Type.SINGLETON); + StageNode globalSenderNode = new MailboxSendNode(globalStageRoot.getStageId(), globalReceiverNode.getStageId(), + RelDistribution.Type.SINGLETON); globalSenderNode.addInput(globalStageRoot); _queryStageMap.put(globalSenderNode.getStageId(), globalSenderNode); StageMetadata stageMetadata = _stageMetadataMap.get(globalSenderNode.getStageId()); @@ -103,10 +102,9 @@ public class StagePlanner { RelDistribution.Type exchangeType = distribution.getType(); // 2. make an exchange sender and receiver node pair - StageNode mailboxReceiver = new MailboxReceiveNode(currentStageId, node.getRowType(), nextStageRoot.getStageId(), - exchangeType); - StageNode mailboxSender = new MailboxSendNode(nextStageRoot.getStageId(), node.getRowType(), - mailboxReceiver.getStageId(), exchangeType, exchangeType == RelDistribution.Type.HASH_DISTRIBUTED + StageNode mailboxReceiver = new MailboxReceiveNode(currentStageId, nextStageRoot.getStageId(), exchangeType); + StageNode mailboxSender = new MailboxSendNode(nextStageRoot.getStageId(), mailboxReceiver.getStageId(), + exchangeType, exchangeType == RelDistribution.Type.HASH_DISTRIBUTED ? new FieldSelectionKeySelector(distribution.getKeys().get(0)) : null); mailboxSender.addInput(nextStageRoot); diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/AbstractStageNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/AbstractStageNode.java index bdcccea355..0f84a10f68 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/AbstractStageNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/AbstractStageNode.java @@ -20,21 +20,15 @@ package org.apache.pinot.query.planner.stage; import java.util.ArrayList; import java.util.List; -import org.apache.calcite.rel.type.RelDataType; import org.apache.pinot.common.proto.Plan; -import org.apache.pinot.query.planner.serde.ProtoProperties; import org.apache.pinot.query.planner.serde.ProtoSerializable; import org.apache.pinot.query.planner.serde.ProtoSerializationUtils; public abstract class AbstractStageNode implements StageNode, ProtoSerializable { - @ProtoProperties protected final int _stageId; - @ProtoProperties protected final List<StageNode> _inputs; - @ProtoProperties - protected RelDataType _rowType; public AbstractStageNode(int stageId) { _stageId = stageId; @@ -65,8 +59,4 @@ public abstract class AbstractStageNode implements StageNode, ProtoSerializable public Plan.ObjectField toObjectField() { return ProtoSerializationUtils.convertObjectToObjectField(this); } - - public RelDataType getRowType() { - return _rowType; - } } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/FilterNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/FilterNode.java index 52df4ed5d3..c169a61970 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/FilterNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/FilterNode.java @@ -18,7 +18,6 @@ */ package org.apache.pinot.query.planner.stage; -import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; import org.apache.pinot.query.planner.logical.RexExpression; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -32,9 +31,8 @@ public class FilterNode extends AbstractStageNode { super(stageId); } - public FilterNode(int currentStageId, RelDataType rowType, RexNode condition) { + public FilterNode(int currentStageId, RexNode condition) { super(currentStageId); - super._rowType = rowType; _condition = RexExpression.toRexExpression(condition); } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/JoinNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/JoinNode.java index 96b6c43a95..223f0addee 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/JoinNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/JoinNode.java @@ -20,7 +20,6 @@ package org.apache.pinot.query.planner.stage; import java.util.List; import org.apache.calcite.rel.core.JoinRelType; -import org.apache.calcite.rel.type.RelDataType; import org.apache.pinot.query.planner.partitioning.FieldSelectionKeySelector; import org.apache.pinot.query.planner.partitioning.KeySelector; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -36,9 +35,8 @@ public class JoinNode extends AbstractStageNode { super(stageId); } - public JoinNode(int stageId, RelDataType rowType, JoinRelType joinRelType, List<JoinClause> criteria) { + public JoinNode(int stageId, JoinRelType joinRelType, List<JoinClause> criteria) { super(stageId); - super._rowType = rowType; _joinRelType = joinRelType; _criteria = criteria; } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxReceiveNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxReceiveNode.java index 1c01d8de5c..edadf30570 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxReceiveNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxReceiveNode.java @@ -19,7 +19,6 @@ package org.apache.pinot.query.planner.stage; import org.apache.calcite.rel.RelDistribution; -import org.apache.calcite.rel.type.RelDataType; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -33,9 +32,9 @@ public class MailboxReceiveNode extends AbstractStageNode { super(stageId); } - public MailboxReceiveNode(int stageId, RelDataType rowType, int senderStageId, RelDistribution.Type exchangeType) { + public MailboxReceiveNode(int stageId, int senderStageId, + RelDistribution.Type exchangeType) { super(stageId); - super._rowType = rowType; _senderStageId = senderStageId; _exchangeType = exchangeType; } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxSendNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxSendNode.java index c3f540aa0e..2d7eb816f7 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxSendNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/MailboxSendNode.java @@ -20,7 +20,6 @@ package org.apache.pinot.query.planner.stage; import javax.annotation.Nullable; import org.apache.calcite.rel.RelDistribution; -import org.apache.calcite.rel.type.RelDataType; import org.apache.pinot.query.planner.partitioning.KeySelector; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -37,15 +36,15 @@ public class MailboxSendNode extends AbstractStageNode { super(stageId); } - public MailboxSendNode(int stageId, RelDataType rowType, int receiverStageId, RelDistribution.Type exchangeType) { + public MailboxSendNode(int stageId, int receiverStageId, + RelDistribution.Type exchangeType) { // When exchangeType is not HASH_DISTRIBUTE, no partitionKeySelector is needed. - this(stageId, rowType, receiverStageId, exchangeType, null); + this(stageId, receiverStageId, exchangeType, null); } - public MailboxSendNode(int stageId, RelDataType rowType, int receiverStageId, RelDistribution.Type exchangeType, - @Nullable KeySelector<Object[], Object> partitionKeySelector) { + public MailboxSendNode(int stageId, int receiverStageId, + RelDistribution.Type exchangeType, @Nullable KeySelector<Object[], Object> partitionKeySelector) { super(stageId); - super._rowType = rowType; _receiverStageId = receiverStageId; _exchangeType = exchangeType; _partitionKeySelector = partitionKeySelector; diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/ProjectNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/ProjectNode.java index 4ee9be6c0a..9a026aae18 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/ProjectNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/ProjectNode.java @@ -20,7 +20,6 @@ package org.apache.pinot.query.planner.stage; import java.util.List; import java.util.stream.Collectors; -import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; import org.apache.pinot.query.planner.logical.RexExpression; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -33,17 +32,12 @@ public class ProjectNode extends AbstractStageNode { public ProjectNode(int stageId) { super(stageId); } - public ProjectNode(int currentStageId, RelDataType rowType, List<RexNode> projects) { + public ProjectNode(int currentStageId, List<RexNode> projects) { super(currentStageId); - super._rowType = rowType; _projects = projects.stream().map(RexExpression::toRexExpression).collect(Collectors.toList()); } public List<RexExpression> getProjects() { return _projects; } - - public RelDataType getRowType() { - return _rowType; - } } diff --git a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/TableScanNode.java b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/TableScanNode.java index 9ba36d34f3..7151f84f56 100644 --- a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/TableScanNode.java +++ b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/stage/TableScanNode.java @@ -19,7 +19,6 @@ package org.apache.pinot.query.planner.stage; import java.util.List; -import org.apache.calcite.rel.type.RelDataType; import org.apache.pinot.query.planner.serde.ProtoProperties; @@ -33,9 +32,8 @@ public class TableScanNode extends AbstractStageNode { super(stageId); } - public TableScanNode(int stageId, RelDataType rowType, String tableName, List<String> tableScanColumns) { + public TableScanNode(int stageId, String tableName, List<String> tableScanColumns) { super(stageId); - super._rowType = rowType; _tableName = tableName; _tableScanColumns = tableScanColumns; } diff --git a/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/stage/SerDeUtilsTest.java b/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/stage/SerDeUtilsTest.java index 21031cd303..db9294252c 100644 --- a/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/stage/SerDeUtilsTest.java +++ b/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/stage/SerDeUtilsTest.java @@ -46,35 +46,41 @@ public class SerDeUtilsTest extends QueryEnvironmentTestBase { private boolean isObjectEqual(Object left, Object right) throws IllegalAccessException { Class<?> clazz = left.getClass(); - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(ProtoProperties.class)) { - field.setAccessible(true); - Object l = field.get(left); - Object r = field.get(right); - if (l instanceof List) { - if (((List) l).size() != ((List) r).size()) { - return false; - } - for (int i = 0; i < ((List) l).size(); i++) { - if (!isObjectEqual(((List) l).get(i), ((List) r).get(i))) { + while (Object.class != clazz) { + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(ProtoProperties.class)) { + field.setAccessible(true); + Object l = field.get(left); + Object r = field.get(right); + if (l instanceof List) { + if (((List) l).size() != ((List) r).size()) { return false; } - } - } else if (l instanceof Map) { - if (((Map) l).size() != ((Map) r).size()) { - return false; - } - for (Object key : ((Map) l).keySet()) { - if (!isObjectEqual(((Map) l).get(key), ((Map) r).get(key))) { + for (int i = 0; i < ((List) l).size(); i++) { + if (!isObjectEqual(((List) l).get(i), ((List) r).get(i))) { + return false; + } + } + } else if (l instanceof Map) { + if (((Map) l).size() != ((Map) r).size()) { + return false; + } + for (Object key : ((Map) l).keySet()) { + if (!isObjectEqual(((Map) l).get(key), ((Map) r).get(key))) { + return false; + } + } + } else { + if (l == null && r != null || l != null && r == null) { + return false; + } + if (!(l == null && r == null || l != null && l.equals(r) || isObjectEqual(l, r))) { return false; } - } - } else { - if (!(l == null && r == null || l != null && l.equals(r) || isObjectEqual(l, r))) { - return false; } } } + clazz = clazz.getSuperclass(); } return true; } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org