This is an automated email from the ASF dual-hosted git repository. richardstartin pushed a commit to branch compatibility-verifier-explain-plan in repository https://gitbox.apache.org/repos/asf/pinot.git
commit e608b9e59c6af6a25072725a1e9043d1e5430207 Author: Richard Startin <richardstar...@apache.org> AuthorDate: Thu Jan 5 18:35:31 2023 +0000 output explain plan when compatibility verifier rejects change in query execution --- .../main/java/org/apache/pinot/compat/QueryOp.java | 29 +++++++--- .../tests/ClusterIntegrationTestUtils.java | 36 +----------- .../apache/pinot/tools/utils/ExplainPlanUtils.java | 64 ++++++++++++++++++++++ 3 files changed, 88 insertions(+), 41 deletions(-) diff --git a/pinot-compatibility-verifier/src/main/java/org/apache/pinot/compat/QueryOp.java b/pinot-compatibility-verifier/src/main/java/org/apache/pinot/compat/QueryOp.java index 2489851d5d..0891194b93 100644 --- a/pinot-compatibility-verifier/src/main/java/org/apache/pinot/compat/QueryOp.java +++ b/pinot-compatibility-verifier/src/main/java/org/apache/pinot/compat/QueryOp.java @@ -24,8 +24,11 @@ import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.TreeMap; import org.apache.pinot.common.utils.SqlResultComparator; import org.apache.pinot.spi.utils.JsonUtils; +import org.apache.pinot.tools.utils.ExplainPlanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -126,27 +129,29 @@ public class QueryOp extends BaseOp { try { actualJson = Utils.postSqlQuery(query, ClusterDescriptor.getInstance().getBrokerUrl()); } catch (Exception e) { - LOGGER.error("Comparison FAILED: Line: {} Exception caught while running query: '{}'", queryLineNum, query, - e); + LOGGER.error("Comparison FAILED: Line: {} Exception caught while running query: '{}', explain plan: {}", + queryLineNum, query, getExplainPlan(query), e); } } if (expectedJson != null && actualJson != null) { try { - boolean passed = SqlResultComparator - .areEqual(actualJson, expectedJson, query); + boolean passed = SqlResultComparator.areEqual(actualJson, expectedJson, query); if (passed) { succeededQueryCount++; LOGGER.debug("Comparison PASSED: Line: {}, query: '{}', actual response: {}, expected response: {}", queryLineNum, query, actualJson, expectedJson); } else { - LOGGER.error("Comparison FAILED: Line: {}, query: '{}', actual response: {}, expected response: {}", - queryLineNum, query, actualJson, expectedJson); + LOGGER.error( + "Comparison FAILED: Line: {}, query: '{}', actual response: {}, expected response: {}, explain " + + "plan: {}", + queryLineNum, query, actualJson, expectedJson, getExplainPlan(query)); } } catch (Exception e) { LOGGER.error( "Comparison FAILED: Line: {} Exception caught while comparing query: '{}' actual response: {}, " - + "expected response: {}", queryLineNum, query, actualJson, expectedJson, e); + + "expected response: {}, explain plan: {}", queryLineNum, query, actualJson, expectedJson, + getExplainPlan(query), e); } } totalQueryCount++; @@ -159,4 +164,14 @@ public class QueryOp extends BaseOp { } return testPassed; } + + private static String getExplainPlan(String query) { + try { + JsonNode explainPlanResponse = + Utils.postSqlQuery("explain plan for " + query, ClusterDescriptor.getInstance().getBrokerUrl()); + return ExplainPlanUtils.formatExplainPlan(explainPlanResponse); + } catch (Throwable error) { + return error.getMessage(); + } + } } diff --git a/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterIntegrationTestUtils.java b/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterIntegrationTestUtils.java index e8f4d143e2..5f30e0c5a5 100644 --- a/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterIntegrationTestUtils.java +++ b/pinot-integration-test-base/src/test/java/org/apache/pinot/integration/tests/ClusterIntegrationTestUtils.java @@ -76,6 +76,7 @@ import org.apache.pinot.spi.stream.StreamDataProvider; import org.apache.pinot.spi.utils.JsonUtils; import org.apache.pinot.spi.utils.StringUtil; import org.apache.pinot.sql.parsers.CalciteSqlParser; +import org.apache.pinot.tools.utils.ExplainPlanUtils; import org.apache.pinot.tools.utils.KafkaStarterUtils; import org.testng.Assert; @@ -691,40 +692,7 @@ public class ClusterIntegrationTestUtils { throws Exception { JsonNode explainPlanForResponse = ClusterTest.postQuery("explain plan for " + pinotQuery, brokerUrl, headers, extraJsonProperties); - Map<Integer, String> nodesById = new TreeMap<>(); - JsonNode rows = explainPlanForResponse.get("resultTable").get("rows"); - int[] parentMapping = new int[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - JsonNode row = rows.get(i); - int id = row.get(1).asInt(); - if (id > 0) { - parentMapping[id] = row.get(2).asInt(); - } - nodesById.put(id, row.get(0).asText()); - } - int[] depths = new int[rows.size()]; - for (Map.Entry<Integer, String> pair : nodesById.entrySet()) { - int depth = 0; - int id = pair.getKey(); - int parentId = id; - while (parentId > 0) { - depth++; - parentId = parentMapping[parentId]; - } - if (id > 0) { - depths[id] = depth; - } - } - StringBuilder explainPlan = new StringBuilder(); - for (Map.Entry<Integer, String> pair : nodesById.entrySet()) { - explainPlan.append('\n'); - int id = pair.getKey(); - for (int i = 0; id > 0 && i < depths[id]; i++) { - explainPlan.append('\t'); - } - explainPlan.append(pair.getValue()); - } - return explainPlan.toString(); + return ExplainPlanUtils.formatExplainPlan(explainPlanForResponse); } private static int getH2ExpectedValues(Set<String> expectedValues, List<String> expectedOrderByValues, diff --git a/pinot-tools/src/main/java/org/apache/pinot/tools/utils/ExplainPlanUtils.java b/pinot-tools/src/main/java/org/apache/pinot/tools/utils/ExplainPlanUtils.java new file mode 100644 index 0000000000..809963918d --- /dev/null +++ b/pinot-tools/src/main/java/org/apache/pinot/tools/utils/ExplainPlanUtils.java @@ -0,0 +1,64 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.pinot.tools.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Map; +import java.util.TreeMap; + + +public class ExplainPlanUtils { + + public static String formatExplainPlan(JsonNode explainPlanJson) { + Map<Integer, String> nodesById = new TreeMap<>(); + JsonNode rows = explainPlanJson.get("resultTable").get("rows"); + int[] parentMapping = new int[rows.size()]; + for (int i = 0; i < rows.size(); i++) { + JsonNode row = rows.get(i); + int id = row.get(1).asInt(); + if (id > 0) { + parentMapping[id] = row.get(2).asInt(); + } + nodesById.put(id, row.get(0).asText()); + } + int[] depths = new int[rows.size()]; + for (Map.Entry<Integer, String> pair : nodesById.entrySet()) { + int depth = 0; + int id = pair.getKey(); + int parentId = id; + while (parentId > 0) { + depth++; + parentId = parentMapping[parentId]; + } + if (id > 0) { + depths[id] = depth; + } + } + StringBuilder explainPlan = new StringBuilder(); + for (Map.Entry<Integer, String> pair : nodesById.entrySet()) { + explainPlan.append('\n'); + int id = pair.getKey(); + for (int i = 0; id > 0 && i < depths[id]; i++) { + explainPlan.append('\t'); + } + explainPlan.append(pair.getValue()); + } + return explainPlan.toString(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org