This is an automated email from the ASF dual-hosted git repository.
gortiz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new e1328a3bf5 Improve query options validation and error handling.
(#14158)
e1328a3bf5 is described below
commit e1328a3bf51e63e8f96f00908fdde617a5837f43
Author: Bolek Ziobrowski <[email protected]>
AuthorDate: Mon Oct 7 15:20:53 2024 +0200
Improve query options validation and error handling. (#14158)
This PR adds the missing validation and improves error messages, e.g.
`numGroupsLimit must be a number between 0 and 2^31-1, got: 10000000000`
for the following options:
numGroupsLimit
maxInitialResultHolderCapacity
multiStageLeafLimit
groupTrimThreshold
maxStreamingPendingBlocks
maxRowsInJoin
maxRowsInWindow
numReplicaGroupsToQuery
maxExecutionThreads > 0
minSegmentGroupTrimSize > -1
minServerGroupTrimSize
minBrokerGroupTrimSize > 0, v1 only
timeoutMs
maxServerResponseSizeBytes
maxQueryResponseSizeBytes
---
.../broker/requesthandler/QueryValidationTest.java | 2 +-
.../common/utils/config/QueryOptionsUtils.java | 93 ++++++----
.../common/utils/config/QueryOptionsUtilsTest.java | 95 +++++++++++
.../pinot/queries/WithOptionQueriesTest.java | 190 +++++++++++++++++++++
.../query/runtime/queries/QueryRunnerTest.java | 49 +++++-
5 files changed, 384 insertions(+), 45 deletions(-)
diff --git
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/QueryValidationTest.java
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/QueryValidationTest.java
index f46833b078..a5a4c469a4 100644
---
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/QueryValidationTest.java
+++
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/QueryValidationTest.java
@@ -117,7 +117,7 @@ public class QueryValidationTest {
public void testReplicaGroupToQueryInvalidQuery() {
PinotQuery pinotQuery =
CalciteSqlParser.compileToPinotQuery("SET
numReplicaGroupsToQuery='illegal'; SELECT COUNT(*) FROM MY_TABLE");
- Assert.assertThrows(IllegalStateException.class,
+ Assert.assertThrows(IllegalArgumentException.class,
() -> BaseSingleStageBrokerRequestHandler.validateRequest(pinotQuery,
10));
}
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/utils/config/QueryOptionsUtils.java
b/pinot-common/src/main/java/org/apache/pinot/common/utils/config/QueryOptionsUtils.java
index d4f8e7edbb..12ad735865 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/utils/config/QueryOptionsUtils.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/utils/config/QueryOptionsUtils.java
@@ -18,7 +18,6 @@
*/
package org.apache.pinot.common.utils.config;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -99,37 +98,19 @@ public class QueryOptionsUtils {
@Nullable
public static Long getTimeoutMs(Map<String, String> queryOptions) {
String timeoutMsString = queryOptions.get(QueryOptionKey.TIMEOUT_MS);
- if (timeoutMsString != null) {
- long timeoutMs = Long.parseLong(timeoutMsString);
- Preconditions.checkState(timeoutMs > 0, "Query timeout must be positive,
got: %s", timeoutMs);
- return timeoutMs;
- } else {
- return null;
- }
+ return checkedParseLong(QueryOptionKey.TIMEOUT_MS, timeoutMsString, 1);
}
@Nullable
public static Long getMaxServerResponseSizeBytes(Map<String, String>
queryOptions) {
String responseSize =
queryOptions.get(QueryOptionKey.MAX_SERVER_RESPONSE_SIZE_BYTES);
- if (responseSize != null) {
- long maxSize = Long.parseLong(responseSize);
- Preconditions.checkState(maxSize > 0, "maxServerResponseSize must be
positive. got %s", maxSize);
- return maxSize;
- }
-
- return null;
+ return checkedParseLong(QueryOptionKey.MAX_SERVER_RESPONSE_SIZE_BYTES,
responseSize, 1);
}
@Nullable
public static Long getMaxQueryResponseSizeBytes(Map<String, String>
queryOptions) {
String responseSize =
queryOptions.get(QueryOptionKey.MAX_QUERY_RESPONSE_SIZE_BYTES);
- if (responseSize != null) {
- long maxSize = Long.parseLong(responseSize);
- Preconditions.checkState(maxSize > 0, "maxQueryResponseSize must be
positive. got %s", maxSize);
- return maxSize;
- }
-
- return null;
+ return checkedParseLong(QueryOptionKey.MAX_QUERY_RESPONSE_SIZE_BYTES,
responseSize, 1);
}
public static boolean isAndScanReorderingEnabled(Map<String, String>
queryOptions) {
@@ -146,7 +127,7 @@ public class QueryOptionsUtils {
public static long getUpsertViewFreshnessMs(Map<String, String>
queryOptions) {
String freshnessMsString =
queryOptions.get(QueryOptionKey.UPSERT_VIEW_FRESHNESS_MS);
- return freshnessMsString != null ? Long.parseLong(freshnessMsString) : -1;
+ return freshnessMsString != null ? Long.parseLong(freshnessMsString) : -1;
//can blow up with NFE
}
public static boolean isScanStarTreeNodes(Map<String, String> queryOptions) {
@@ -198,7 +179,7 @@ public class QueryOptionsUtils {
@Nullable
public static Integer getNumReplicaGroupsToQuery(Map<String, String>
queryOptions) {
String numReplicaGroupsToQuery =
queryOptions.get(QueryOptionKey.NUM_REPLICA_GROUPS_TO_QUERY);
- return numReplicaGroupsToQuery != null ?
Integer.parseInt(numReplicaGroupsToQuery) : null;
+ return checkedParseInt(QueryOptionKey.NUM_REPLICA_GROUPS_TO_QUERY,
numReplicaGroupsToQuery);
}
public static boolean isExplainPlanVerbose(Map<String, String> queryOptions)
{
@@ -220,25 +201,25 @@ public class QueryOptionsUtils {
@Nullable
public static Integer getMaxExecutionThreads(Map<String, String>
queryOptions) {
String maxExecutionThreadsString =
queryOptions.get(QueryOptionKey.MAX_EXECUTION_THREADS);
- return maxExecutionThreadsString != null ?
Integer.parseInt(maxExecutionThreadsString) : null;
+ return checkedParseInt(QueryOptionKey.MAX_EXECUTION_THREADS,
maxExecutionThreadsString);
}
@Nullable
public static Integer getMinSegmentGroupTrimSize(Map<String, String>
queryOptions) {
String minSegmentGroupTrimSizeString =
queryOptions.get(QueryOptionKey.MIN_SEGMENT_GROUP_TRIM_SIZE);
- return minSegmentGroupTrimSizeString != null ?
Integer.parseInt(minSegmentGroupTrimSizeString) : null;
+ return checkedParseInt(QueryOptionKey.MIN_SEGMENT_GROUP_TRIM_SIZE,
minSegmentGroupTrimSizeString);
}
@Nullable
public static Integer getMinServerGroupTrimSize(Map<String, String>
queryOptions) {
String minServerGroupTrimSizeString =
queryOptions.get(QueryOptionKey.MIN_SERVER_GROUP_TRIM_SIZE);
- return minServerGroupTrimSizeString != null ?
Integer.parseInt(minServerGroupTrimSizeString) : null;
+ return checkedParseInt(QueryOptionKey.MIN_SERVER_GROUP_TRIM_SIZE,
minServerGroupTrimSizeString);
}
@Nullable
public static Integer getMinBrokerGroupTrimSize(Map<String, String>
queryOptions) {
String minBrokerGroupTrimSizeString =
queryOptions.get(QueryOptionKey.MIN_BROKER_GROUP_TRIM_SIZE);
- return minBrokerGroupTrimSizeString != null ?
Integer.parseInt(minBrokerGroupTrimSizeString) : null;
+ return checkedParseInt(QueryOptionKey.MIN_BROKER_GROUP_TRIM_SIZE,
minBrokerGroupTrimSizeString);
}
public static boolean isNullHandlingEnabled(Map<String, String>
queryOptions) {
@@ -261,25 +242,67 @@ public class QueryOptionsUtils {
@Nullable
public static Integer getMultiStageLeafLimit(Map<String, String>
queryOptions) {
String maxLeafLimitStr =
queryOptions.get(QueryOptionKey.MULTI_STAGE_LEAF_LIMIT);
- return maxLeafLimitStr != null ? Integer.parseInt(maxLeafLimitStr) : null;
+ return checkedParseInt(QueryOptionKey.MULTI_STAGE_LEAF_LIMIT,
maxLeafLimitStr);
}
@Nullable
public static Integer getNumGroupsLimit(Map<String, String> queryOptions) {
String maxNumGroupLimit =
queryOptions.get(QueryOptionKey.NUM_GROUPS_LIMIT);
- return maxNumGroupLimit != null ? Integer.parseInt(maxNumGroupLimit) :
null;
+ return checkedParseInt(QueryOptionKey.NUM_GROUPS_LIMIT, maxNumGroupLimit);
}
@Nullable
public static Integer getMaxInitialResultHolderCapacity(Map<String, String>
queryOptions) {
String maxInitResultCap =
queryOptions.get(QueryOptionKey.MAX_INITIAL_RESULT_HOLDER_CAPACITY);
- return maxInitResultCap != null ? Integer.parseInt(maxInitResultCap) :
null;
+ return checkedParseInt(QueryOptionKey.MAX_INITIAL_RESULT_HOLDER_CAPACITY,
maxInitResultCap);
}
@Nullable
public static Integer getGroupTrimThreshold(Map<String, String>
queryOptions) {
String groupByTrimThreshold =
queryOptions.get(QueryOptionKey.GROUP_TRIM_THRESHOLD);
- return groupByTrimThreshold != null ?
Integer.parseInt(groupByTrimThreshold) : null;
+ return checkedParseInt(QueryOptionKey.GROUP_TRIM_THRESHOLD,
groupByTrimThreshold);
+ }
+
+ private static Long checkedParseLong(String optionName, String optionValue,
int minValue) {
+ try {
+ if (optionValue != null) {
+ Long value = Long.parseLong(optionValue);
+ if (value < minValue) {
+ throw longParseException(optionName, optionValue, minValue);
+ }
+ return value;
+ } else {
+ return null;
+ }
+ } catch (NumberFormatException nfe) {
+ throw longParseException(optionName, optionValue, minValue);
+ }
+ }
+
+ private static IllegalArgumentException longParseException(String
optionName, String optionValue, int minValue) {
+ return new IllegalArgumentException(
+ String.format("%s must be a number between %d and 2^63-1, got: %s",
optionName, minValue, optionValue));
+ }
+
+ private static Integer checkedParseInt(String optionName, String
optionValue) {
+ try {
+ if (optionValue != null) {
+ int value = Integer.parseInt(optionValue);
+ if (value < 0) {
+ throw intParseException(optionName, optionValue);
+ }
+ return value;
+ } else {
+ return null;
+ }
+ } catch (NumberFormatException nfe) {
+ throw intParseException(optionName, optionValue);
+ }
+ }
+
+ private static IllegalArgumentException intParseException(String optionName,
String optionValue) {
+ return new IllegalArgumentException(
+ String.format("%s must be a number between 0 and 2^31-1, got: %s",
optionName, optionValue));
}
public static boolean shouldDropResults(Map<String, String> queryOptions) {
@@ -289,13 +312,13 @@ public class QueryOptionsUtils {
@Nullable
public static Integer getMaxStreamingPendingBlocks(Map<String, String>
queryOptions) {
String maxStreamingPendingBlocks =
queryOptions.get(QueryOptionKey.MAX_STREAMING_PENDING_BLOCKS);
- return maxStreamingPendingBlocks != null ?
Integer.parseInt(maxStreamingPendingBlocks) : null;
+ return checkedParseInt(QueryOptionKey.MAX_STREAMING_PENDING_BLOCKS,
maxStreamingPendingBlocks);
}
@Nullable
public static Integer getMaxRowsInJoin(Map<String, String> queryOptions) {
String maxRowsInJoin = queryOptions.get(QueryOptionKey.MAX_ROWS_IN_JOIN);
- return maxRowsInJoin != null ? Integer.parseInt(maxRowsInJoin) : null;
+ return checkedParseInt(QueryOptionKey.MAX_ROWS_IN_JOIN, maxRowsInJoin);
}
@Nullable
@@ -307,7 +330,7 @@ public class QueryOptionsUtils {
@Nullable
public static Integer getMaxRowsInWindow(Map<String, String> queryOptions) {
String maxRowsInWindow =
queryOptions.get(QueryOptionKey.MAX_ROWS_IN_WINDOW);
- return maxRowsInWindow != null ? Integer.parseInt(maxRowsInWindow) : null;
+ return checkedParseInt(QueryOptionKey.MAX_ROWS_IN_WINDOW, maxRowsInWindow);
}
@Nullable
diff --git
a/pinot-common/src/test/java/org/apache/pinot/common/utils/config/QueryOptionsUtilsTest.java
b/pinot-common/src/test/java/org/apache/pinot/common/utils/config/QueryOptionsUtilsTest.java
index 4f0985569b..6f0f469c5b 100644
---
a/pinot-common/src/test/java/org/apache/pinot/common/utils/config/QueryOptionsUtilsTest.java
+++
b/pinot-common/src/test/java/org/apache/pinot/common/utils/config/QueryOptionsUtilsTest.java
@@ -20,6 +20,8 @@
package org.apache.pinot.common.utils.config;
import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.pinot.spi.config.table.FieldConfig;
@@ -27,6 +29,8 @@ import org.apache.pinot.spi.utils.CommonConstants;
import org.testng.Assert;
import org.testng.annotations.Test;
+import static
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey.*;
+
public class QueryOptionsUtilsTest {
@@ -65,4 +69,95 @@ public class QueryOptionsUtilsTest {
Map.of(CommonConstants.Broker.Request.QueryOptionKey.SKIP_INDEXES,
skipIndexesStr);
QueryOptionsUtils.getSkipIndexes(queryOptions);
}
+
+ @Test
+ public void testIntegerSettingParseSuccess() {
+ HashMap<String, String> map = new HashMap<>();
+
+ for (String setting : Arrays.asList(NUM_GROUPS_LIMIT,
MAX_INITIAL_RESULT_HOLDER_CAPACITY, MULTI_STAGE_LEAF_LIMIT,
+ GROUP_TRIM_THRESHOLD, MAX_STREAMING_PENDING_BLOCKS, MAX_ROWS_IN_JOIN,
MAX_STREAMING_PENDING_BLOCKS,
+ MAX_EXECUTION_THREADS, MIN_SEGMENT_GROUP_TRIM_SIZE,
MIN_SERVER_GROUP_TRIM_SIZE)) {
+ map.clear();
+ for (Integer val : new Integer[]{null, 1, 10, Integer.MAX_VALUE}) {
+ map.put(setting, val != null ? String.valueOf(val) : null);
+ Assert.assertEquals(getValue(map, setting), val);
+ }
+ }
+
+ for (String setting : Arrays.asList(TIMEOUT_MS,
MAX_SERVER_RESPONSE_SIZE_BYTES, MAX_QUERY_RESPONSE_SIZE_BYTES)) {
+ map.clear();
+ for (Long val : new Long[]{null, 1L, 10L, Long.MAX_VALUE}) {
+ map.put(setting, val != null ? String.valueOf(val) : null);
+ Assert.assertEquals(getValue(map, setting), val);
+ }
+ }
+ }
+
+ @Test
+ public void testIntegerSettingParseErrors() {
+ HashMap<String, String> map = new HashMap<>();
+
+ for (String setting : Arrays.asList(NUM_GROUPS_LIMIT,
MAX_INITIAL_RESULT_HOLDER_CAPACITY, MULTI_STAGE_LEAF_LIMIT,
+ GROUP_TRIM_THRESHOLD, MAX_STREAMING_PENDING_BLOCKS, MAX_ROWS_IN_JOIN,
MAX_STREAMING_PENDING_BLOCKS,
+ MAX_EXECUTION_THREADS, MIN_SEGMENT_GROUP_TRIM_SIZE,
MIN_SERVER_GROUP_TRIM_SIZE)) {
+ for (String val : new String[]{"-10000000000", "-2147483648", "-1",
"2147483648", "10000000000"}) {
+ map.clear();
+ map.put(setting, val);
+ try {
+ getValue(map, setting);
+ Assert.fail();
+ } catch (IllegalArgumentException ise) {
+ Assert.assertEquals(ise.getMessage(), setting + " must be a number
between 0 and 2^31-1, got: " + val);
+ }
+ }
+ }
+
+ for (String setting : Arrays.asList(TIMEOUT_MS,
MAX_SERVER_RESPONSE_SIZE_BYTES, MAX_QUERY_RESPONSE_SIZE_BYTES)) {
+ for (String val : new String[]{
+ "-100000000000000000000", "-9223372036854775809", "-1", "0",
"9223372036854775808", "100000000000000000000"
+ }) {
+ map.clear();
+ map.put(setting, val);
+ try {
+ getValue(map, setting);
+ Assert.fail();
+ } catch (IllegalArgumentException ise) {
+ Assert.assertEquals(ise.getMessage(), setting + " must be a number
between 1 and 2^63-1, got: " + val);
+ }
+ }
+ }
+ }
+
+ private static Object getValue(Map<String, String> map, String key) {
+ switch (key) {
+ //ints
+ case NUM_GROUPS_LIMIT:
+ return QueryOptionsUtils.getNumGroupsLimit(map);
+ case MAX_INITIAL_RESULT_HOLDER_CAPACITY:
+ return QueryOptionsUtils.getMaxInitialResultHolderCapacity(map);
+ case MULTI_STAGE_LEAF_LIMIT:
+ return QueryOptionsUtils.getMultiStageLeafLimit(map);
+ case GROUP_TRIM_THRESHOLD:
+ return QueryOptionsUtils.getGroupTrimThreshold(map);
+ case MAX_STREAMING_PENDING_BLOCKS:
+ return QueryOptionsUtils.getMaxStreamingPendingBlocks(map);
+ case MAX_ROWS_IN_JOIN:
+ return QueryOptionsUtils.getMaxRowsInJoin(map);
+ case MAX_EXECUTION_THREADS:
+ return QueryOptionsUtils.getMaxExecutionThreads(map);
+ case MIN_SEGMENT_GROUP_TRIM_SIZE:
+ return QueryOptionsUtils.getMinSegmentGroupTrimSize(map);
+ case MIN_SERVER_GROUP_TRIM_SIZE:
+ return QueryOptionsUtils.getMinServerGroupTrimSize(map);
+ //longs
+ case TIMEOUT_MS:
+ return QueryOptionsUtils.getTimeoutMs(map);
+ case MAX_SERVER_RESPONSE_SIZE_BYTES:
+ return QueryOptionsUtils.getMaxServerResponseSizeBytes(map);
+ case MAX_QUERY_RESPONSE_SIZE_BYTES:
+ return QueryOptionsUtils.getMaxQueryResponseSizeBytes(map);
+ default:
+ throw new IllegalArgumentException("Unexpected key!");
+ }
+ }
}
diff --git
a/pinot-core/src/test/java/org/apache/pinot/queries/WithOptionQueriesTest.java
b/pinot-core/src/test/java/org/apache/pinot/queries/WithOptionQueriesTest.java
new file mode 100644
index 0000000000..654a0add08
--- /dev/null
+++
b/pinot-core/src/test/java/org/apache/pinot/queries/WithOptionQueriesTest.java
@@ -0,0 +1,190 @@
+/**
+ * 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.queries;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import
org.apache.pinot.segment.local.indexsegment.immutable.ImmutableSegmentLoader;
+import
org.apache.pinot.segment.local.segment.creator.impl.SegmentIndexCreationDriverImpl;
+import org.apache.pinot.segment.local.segment.readers.GenericRowRecordReader;
+import org.apache.pinot.segment.spi.ImmutableSegment;
+import org.apache.pinot.segment.spi.IndexSegment;
+import org.apache.pinot.segment.spi.creator.SegmentGeneratorConfig;
+import org.apache.pinot.spi.config.table.TableConfig;
+import org.apache.pinot.spi.config.table.TableType;
+import org.apache.pinot.spi.data.FieldSpec;
+import org.apache.pinot.spi.data.Schema;
+import org.apache.pinot.spi.data.readers.GenericRow;
+import
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
+import org.apache.pinot.spi.utils.ReadMode;
+import org.apache.pinot.spi.utils.builder.TableConfigBuilder;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey.MAX_QUERY_RESPONSE_SIZE_BYTES;
+import static
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey.MAX_SERVER_RESPONSE_SIZE_BYTES;
+import static
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey.TIMEOUT_MS;
+
+
+public class WithOptionQueriesTest extends BaseQueriesTest {
+
+ private static final File INDEX_DIR = new File(FileUtils.getTempDirectory(),
"WithOptionQueriesTest");
+ private static final String RAW_TABLE_NAME = "testTable";
+ private static final String SEGMENT_NAME = "testSegment";
+
+ private static final int NUM_RECORDS = 10;
+ private static final String X_COL = "x";
+ private static final String Y_COL = "y";
+
+ private static final Schema SCHEMA = new
Schema.SchemaBuilder().addSingleValueDimension(X_COL, FieldSpec.DataType.INT)
+ .addSingleValueDimension(Y_COL, FieldSpec.DataType.DOUBLE).build();
+
+ private static final TableConfig TABLE_CONFIG =
+ new
TableConfigBuilder(TableType.OFFLINE).setTableName(RAW_TABLE_NAME).build();
+
+ private IndexSegment _indexSegment;
+ private List<IndexSegment> _indexSegments;
+
+ @Override
+ protected String getFilter() {
+ return "";
+ }
+
+ @Override
+ protected IndexSegment getIndexSegment() {
+ return _indexSegment;
+ }
+
+ @Override
+ protected List<IndexSegment> getIndexSegments() {
+ return _indexSegments;
+ }
+
+ private final List<Object[]> _allRecords = new ArrayList<>();
+
+ @BeforeClass
+ public void setUp()
+ throws Exception {
+ FileUtils.deleteQuietly(INDEX_DIR);
+
+ List<GenericRow> records = new ArrayList<>(NUM_RECORDS);
+ for (int i = 0; i < NUM_RECORDS; i++) {
+ GenericRow record = new GenericRow();
+ record.putValue(X_COL, i);
+ record.putValue(Y_COL, 0.25);
+ records.add(record);
+ _allRecords.add(new Object[]{i, 0.25});
+ }
+
+ SegmentGeneratorConfig segmentGeneratorConfig = new
SegmentGeneratorConfig(TABLE_CONFIG, SCHEMA);
+ segmentGeneratorConfig.setTableName(RAW_TABLE_NAME);
+ segmentGeneratorConfig.setSegmentName(SEGMENT_NAME);
+ segmentGeneratorConfig.setOutDir(INDEX_DIR.getPath());
+
+ SegmentIndexCreationDriverImpl driver = new
SegmentIndexCreationDriverImpl();
+ driver.init(segmentGeneratorConfig, new GenericRowRecordReader(records));
+ driver.build();
+
+ ImmutableSegment immutableSegment = ImmutableSegmentLoader.load(new
File(INDEX_DIR, SEGMENT_NAME), ReadMode.mmap);
+ _indexSegment = immutableSegment;
+ _indexSegments = Arrays.asList(immutableSegment, immutableSegment);
+ }
+
+ @Test
+ public void testOptionParsingFailure() {
+ HashMap<String, String> options = new HashMap<>();
+
+ // int values
+ for (String setting : Arrays.asList(QueryOptionKey.NUM_GROUPS_LIMIT,
+ QueryOptionKey.MAX_INITIAL_RESULT_HOLDER_CAPACITY,
QueryOptionKey.GROUP_TRIM_THRESHOLD,
+ QueryOptionKey.MAX_EXECUTION_THREADS,
QueryOptionKey.MIN_SEGMENT_GROUP_TRIM_SIZE,
+ QueryOptionKey.MIN_SERVER_GROUP_TRIM_SIZE,
QueryOptionKey.MIN_BROKER_GROUP_TRIM_SIZE)) {
+
+ options.clear();
+ for (String value : new String[]{"-10000000000", "-2147483648", "-1",
"2147483648", "10000000000"}) {
+ options.put(setting, value);
+
+ IllegalArgumentException exception =
Assert.expectThrows(IllegalArgumentException.class, () -> {
+ getBrokerResponse("SELECT x, count(*) FROM " + RAW_TABLE_NAME + "
GROUP BY x", options);
+ });
+ Assert.assertEquals(setting + " must be a number between 0 and 2^31-1,
got: " + value, exception.getMessage());
+ }
+ }
+ }
+
+ @Test
+ public void testOptionParsingSuccess() {
+ HashMap<String, String> options = new HashMap<>();
+ List<Object> groupRows = new ArrayList();
+ groupRows.add(new Object[]{0d, 40L}); //four times 10 records because
segment gets multiplied under the hood
+
+ // int values
+ for (String setting : Arrays.asList(QueryOptionKey.NUM_GROUPS_LIMIT,
+ QueryOptionKey.MAX_INITIAL_RESULT_HOLDER_CAPACITY,
QueryOptionKey.MULTI_STAGE_LEAF_LIMIT,
+ QueryOptionKey.GROUP_TRIM_THRESHOLD,
QueryOptionKey.MAX_STREAMING_PENDING_BLOCKS,
+ QueryOptionKey.MAX_ROWS_IN_JOIN, QueryOptionKey.MAX_EXECUTION_THREADS,
+ QueryOptionKey.MIN_SEGMENT_GROUP_TRIM_SIZE,
QueryOptionKey.MIN_SERVER_GROUP_TRIM_SIZE,
+ QueryOptionKey.MIN_BROKER_GROUP_TRIM_SIZE,
QueryOptionKey.NUM_REPLICA_GROUPS_TO_QUERY)) {
+
+ options.clear();
+ for (String value : new String[]{"0", "1", "10000", "2147483647"}) {
+
+ options.put(setting, value);
+ List<Object[]> rows =
+ getBrokerResponse("SELECT mod(x,1), count(*) FROM " +
RAW_TABLE_NAME + " GROUP BY mod(x,1)",
+ options).getResultTable().getRows();
+ if (QueryOptionKey.NUM_GROUPS_LIMIT == setting && "0".equals(value)) {
+ Assert.assertEquals(0, rows.size());
+ } else {
+ assertEquals(rows, groupRows);
+ }
+ }
+ }
+
+ //long values
+ for (String setting : Arrays.asList(TIMEOUT_MS,
MAX_SERVER_RESPONSE_SIZE_BYTES, MAX_QUERY_RESPONSE_SIZE_BYTES)) {
+ options.clear();
+ for (String value : new String[]{"1", "10000", "9223372036854775807"}) {
+ options.put(setting, value);
+ List<Object[]> rows = getBrokerResponse("SELECT * FROM " +
RAW_TABLE_NAME, options).getResultTable().getRows();
+ assertEquals(rows, _allRecords);
+ }
+ }
+ }
+
+ private void assertEquals(List actual, List expected) {
+ if (actual == expected) {
+ return;
+ }
+
+ if (actual == null || expected == null || actual.size() !=
expected.size()) {
+ Assert.fail("Expected \n" + expected + "\n but got \n" + actual);
+ }
+
+ for (int i = 0; i < actual.size(); i++) {
+ Assert.assertEquals(actual.get(i), expected.get(i));
+ }
+ }
+}
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTest.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTest.java
index 76802c7b77..3e946d5eab 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTest.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/queries/QueryRunnerTest.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -38,6 +39,7 @@ import org.apache.pinot.spi.data.Schema;
import org.apache.pinot.spi.data.readers.GenericRow;
import org.apache.pinot.spi.env.PinotConfiguration;
import org.apache.pinot.spi.utils.CommonConstants;
+import
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
import org.apache.pinot.spi.utils.JsonUtils;
import org.apache.pinot.spi.utils.builder.TableNameBuilder;
import org.testng.Assert;
@@ -186,24 +188,25 @@ public class QueryRunnerTest extends QueryRunnerTestBase {
* Test compares against its desired exceptions.
*/
@Test(dataProvider = "testDataWithSqlExecutionExceptions")
- public void testSqlWithExceptionMsgChecker(String sql, String exceptionMsg) {
+ public void testSqlWithExceptionMsgChecker(String sql, String expectedError)
{
try {
// query pinot
ResultTable resultTable = queryRunner(sql, false).getResultTable();
- Assert.fail("Expected error with message '" + exceptionMsg + "'. But
instead rows were returned: "
+ Assert.fail("Expected error with message '" + expectedError + "'. But
instead rows were returned: "
+ JsonUtils.objectToPrettyString(resultTable));
} catch (Exception e) {
// NOTE: The actual message is (usually) something like:
// Received error query execution result block:
{200=QueryExecutionError:
// Query execution error on: Server_localhost_12345
// java.lang.IllegalArgumentException: Illegal Json Path: $['path']
does not match document
+ // In some cases there is no prefix.
String exceptionMessage = e.getMessage();
Assert.assertTrue(
exceptionMessage.startsWith("Received error query execution result
block: ") || exceptionMessage.startsWith(
- "Error occurred during stage submission"),
+ "Error occurred during stage submission") ||
exceptionMessage.equals(expectedError),
"Exception message didn't start with proper heading: " +
exceptionMessage);
- Assert.assertTrue(exceptionMessage.contains(exceptionMsg),
- "Exception should contain: " + exceptionMsg + ", but found: " +
exceptionMessage);
+ Assert.assertTrue(exceptionMessage.contains(expectedError),
+ "Exception should contain: " + expectedError + ", but found: " +
exceptionMessage);
}
}
@@ -289,9 +292,11 @@ public class QueryRunnerTest extends QueryRunnerTestBase {
}
@DataProvider(name = "testDataWithSqlExecutionExceptions")
- protected Object[][] provideTestSqlWithExecutionException() {
+ protected Iterator<Object[]> provideTestSqlWithExecutionException() {
//@formatter:off
- return new Object[][]{
+ List<Object[]> testCases = new ArrayList();
+ testCases.addAll(
+ Arrays.asList(
// Missing index
new Object[]{"SELECT col1 FROM a WHERE textMatch(col1, 'f') LIMIT 10",
"without text index"},
@@ -327,8 +332,34 @@ public class QueryRunnerTest extends QueryRunnerTestBase {
new Object[]{
"SELECT CAST(json_extract_scalar(a.col1, b.col2, 'INT') AS INT)
FROM a JOIN b ON a.col1 = b.col1",
"Unsupported function: JSONEXTRACTSCALAR"
- }
- };
+ }));
//@formatter:on
+
+ // int values
+ for (String setting : Arrays.asList(QueryOptionKey.NUM_GROUPS_LIMIT,
+ QueryOptionKey.MAX_INITIAL_RESULT_HOLDER_CAPACITY,
QueryOptionKey.MULTI_STAGE_LEAF_LIMIT,
+ QueryOptionKey.GROUP_TRIM_THRESHOLD,
QueryOptionKey.MAX_STREAMING_PENDING_BLOCKS,
+ QueryOptionKey.MAX_ROWS_IN_JOIN, QueryOptionKey.MAX_EXECUTION_THREADS,
+ QueryOptionKey.MIN_SEGMENT_GROUP_TRIM_SIZE,
QueryOptionKey.MIN_SERVER_GROUP_TRIM_SIZE)) {
+
+ for (String val : new String[]{"-10000000000", "-2147483648", "-1",
"2147483648", "10000000000"}) {
+ testCases.add(new Object[]{
+ "set " + setting + " = " + val + "; SELECT col1, count(*) FROM a
GROUP BY col1",
+ setting + " must be a number between 0 and 2^31-1, got: " + val
+ });
+ }
+ }
+
+ // int values; triggered for query with window clause
+ for (String setting : Arrays.asList(QueryOptionKey.MAX_ROWS_IN_WINDOW)) {
+ for (String val : new String[]{"-10000000000", "-2147483648", "-1",
"2147483648", "10000000000"}) {
+ testCases.add(new Object[]{
+ "set " + setting + " = " + val + "; SELECT ROW_NUMBER() over
(PARTITION BY col1) FROM a",
+ setting + " must be a number between 0 and 2^31-1, got: " + val
+ });
+ }
+ }
+
+ return testCases.iterator();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]