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 faa74c30178 Fail fast on EXPR_MIN/EXPR_MAX queries that the servers
cannot execute (#19883)
faa74c30178 is described below
commit faa74c301783366f53fd1f29fc173c7310e4863d
Author: Gonzalo Ortiz Jaureguizar <[email protected]>
AuthorDate: Fri Oct 16 13:14:11 2026 +0200
Fail fast on EXPR_MIN/EXPR_MAX queries that the servers cannot execute
(#19883)
- InstanceRequestHandler: when building the ServerQueryRequest fails after
Thrift
deserialization succeeded, send an error response with the known request
id instead
of no response. The broker no longer waits for the timeout and reports
427.
- BaseSingleStageBrokerRequestHandler#validateRequest: reject
EXPR_MIN/EXPR_MAX with
QUERY_VALIDATION when ExprMinMaxRewriter is not enabled, when the call
has an alias,
is nested or is outside the select list, or when
ParentAggregationResultRewriter is
not enabled. The message names the missing rewriter and its config key.
- Compile failures on the server now count in QUERY_EXECUTION_EXCEPTIONS
instead of
REQUEST_DESERIALIZATION_EXCEPTIONS.
---
.../BaseSingleStageBrokerRequestHandler.java | 97 ++++++++++++++++++++++
.../broker/requesthandler/QueryValidationTest.java | 57 +++++++++++++
.../core/transport/InstanceRequestHandler.java | 36 ++++++--
.../core/transport/InstanceRequestHandlerTest.java | 46 ++++++++++
4 files changed, 230 insertions(+), 6 deletions(-)
diff --git
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
index f1039aaf4f2..38bd566f434 100644
---
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
+++
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
@@ -90,6 +90,8 @@ import
org.apache.pinot.core.query.reduce.BaseGapfillProcessor;
import org.apache.pinot.core.query.reduce.GapfillProcessorFactory;
import org.apache.pinot.core.query.request.context.QueryContext;
import
org.apache.pinot.core.query.request.context.utils.QueryContextConverterUtils;
+import
org.apache.pinot.core.query.utils.rewriter.ParentAggregationResultRewriter;
+import org.apache.pinot.core.query.utils.rewriter.ResultRewriterFactory;
import org.apache.pinot.core.routing.ImplicitHybridTableRouteInfo;
import org.apache.pinot.core.routing.ImplicitHybridTableRouteProvider;
import org.apache.pinot.core.routing.LogicalTableRouteProvider;
@@ -137,6 +139,7 @@ import org.apache.pinot.sql.FilterKind;
import org.apache.pinot.sql.parsers.CalciteSqlCompiler;
import org.apache.pinot.sql.parsers.CalciteSqlParser;
import org.apache.pinot.sql.parsers.SqlNodeAndOptions;
+import org.apache.pinot.sql.parsers.rewriter.ExprMinMaxRewriter;
import org.apache.pinot.sql.parsers.rewriter.RlsFiltersRewriter;
import org.apache.pinot.sql.parsers.rewriter.RlsUtils;
import org.slf4j.Logger;
@@ -155,6 +158,12 @@ public abstract class BaseSingleStageBrokerRequestHandler
extends BaseBrokerRequ
/// Canonical name (see [RequestUtils#canonicalizeFunctionName]) of the
`lookup()` transform function, which reads a
/// dimension table named by a string literal argument instead of by the
FROM clause.
private static final String LOOKUP_FUNCTION = "lookup";
+ private static final String EXPR_MIN = "exprmin";
+ private static final String EXPR_MAX = "exprmax";
+ private static final Set<String> EXPR_MIN_MAX_FUNCTIONS = Set.of(EXPR_MIN,
EXPR_MAX);
+ private static final Set<String> CHILD_EXPR_MIN_MAX_FUNCTIONS =
+ Set.of(CommonConstants.RewriterConstants.CHILD_AGGREGATION_NAME_PREFIX +
EXPR_MIN,
+ CommonConstants.RewriterConstants.CHILD_AGGREGATION_NAME_PREFIX +
EXPR_MAX);
private static final int
MAX_UNAVAILABLE_SEGMENTS_TO_PRINT_IN_QUERY_EXCEPTION = 10;
protected final QueryOptimizer _queryOptimizer = new QueryOptimizer();
@@ -2455,6 +2464,7 @@ public abstract class BaseSingleStageBrokerRequestHandler
extends BaseBrokerRequ
/// - Value for 'LIMIT' <= configured value
/// - Query options must be set to SQL mode
/// - Check if numReplicaGroupsToQuery option provided is valid
+ /// - EXPR_MIN/EXPR_MAX can be executed with the configured rewriters, see
[#validateExprMinMax(PinotQuery)]
@VisibleForTesting
static void validateRequest(PinotQuery pinotQuery, int queryResponseLimit) {
// Verify LIMIT
@@ -2464,11 +2474,98 @@ public abstract class
BaseSingleStageBrokerRequestHandler extends BaseBrokerRequ
"Value for 'LIMIT' (" + limit + ") exceeds maximum allowed value of
" + queryResponseLimit);
}
QueryOptionsUtils.getNumReplicaGroupsToQuery(pinotQuery.getQueryOptions());
+ validateExprMinMax(pinotQuery);
if (pinotQuery.getDataSource().getSubquery() != null) {
validateRequest(pinotQuery.getDataSource().getSubquery(),
queryResponseLimit);
}
}
+ /// Servers cannot execute EXPR_MIN/EXPR_MAX directly. The broker must
rewrite them into parent and child
+ /// aggregations with [ExprMinMaxRewriter], and merge the parent result back
into the child columns with
+ /// [ParentAggregationResultRewriter]. Neither rewriter is enabled by
default.
+ ///
+ /// Without this check, a query that still has EXPR_MIN/EXPR_MAX fails on
the servers, and a query without the
+ /// result rewriter returns the placeholder values of the child aggregations
instead of the expected values.
+ @VisibleForTesting
+ static void validateExprMinMax(PinotQuery pinotQuery) {
+ String exprMinMax = findFunction(pinotQuery, EXPR_MIN_MAX_FUNCTIONS);
+ if (exprMinMax != null) {
+ String functionName = exprMinMax.equals(EXPR_MIN) ? "EXPR_MIN" :
"EXPR_MAX";
+ boolean queryRewriterEnabled =
+
CalciteSqlParser.QUERY_REWRITERS.stream().anyMatch(ExprMinMaxRewriter.class::isInstance);
+ if (!queryRewriterEnabled) {
+ throw new BadQueryRequestException(
+ functionName + " is not enabled on this broker. To enable it, add
'" + ExprMinMaxRewriter.class.getName()
+ + "' to '" +
Broker.CONFIG_OF_BROKER_QUERY_REWRITER_CLASS_NAMES + "' and '"
+ + ParentAggregationResultRewriter.class.getName() + "' to '"
+ + Broker.CONFIG_OF_BROKER_RESULT_REWRITER_CLASS_NAMES + "'");
+ }
+ // The rewriter only handles the top-level expressions of the select
list, so the function has an alias, is nested
+ // in another expression, or is in another clause.
+ throw new BadQueryRequestException(functionName + " is only supported as
a top-level expression in the select "
+ + "list, without an alias. It is not supported in other expressions
or in the WHERE, GROUP BY, HAVING or "
+ + "ORDER BY clauses");
+ }
+ if (findFunction(pinotQuery, CHILD_EXPR_MIN_MAX_FUNCTIONS) != null) {
+ boolean resultRewriterEnabled =
ResultRewriterFactory.getResultRewriter().stream()
+ .anyMatch(ParentAggregationResultRewriter.class::isInstance);
+ if (!resultRewriterEnabled) {
+ throw new BadQueryRequestException(
+ "EXPR_MIN and EXPR_MAX require the result rewriter '" +
ParentAggregationResultRewriter.class.getName()
+ + "'. To enable it, add it to '" +
Broker.CONFIG_OF_BROKER_RESULT_REWRITER_CLASS_NAMES + "'");
+ }
+ }
+ }
+
+ /// Returns the first function of the given names in the select, filter,
group-by, having or order-by expressions, or
+ /// `null` if there is none.
+ @Nullable
+ private static String findFunction(PinotQuery pinotQuery, Set<String>
functionNames) {
+ String functionName = findFunction(pinotQuery.getSelectList(),
functionNames);
+ if (functionName == null) {
+ functionName = findFunction(pinotQuery.getFilterExpression(),
functionNames);
+ }
+ if (functionName == null) {
+ functionName = findFunction(pinotQuery.getGroupByList(), functionNames);
+ }
+ if (functionName == null) {
+ functionName = findFunction(pinotQuery.getHavingExpression(),
functionNames);
+ }
+ if (functionName == null) {
+ functionName = findFunction(pinotQuery.getOrderByList(), functionNames);
+ }
+ return functionName;
+ }
+
+ @Nullable
+ private static String findFunction(@Nullable List<Expression> expressions,
Set<String> functionNames) {
+ if (expressions == null) {
+ return null;
+ }
+ for (Expression expression : expressions) {
+ String functionName = findFunction(expression, functionNames);
+ if (functionName != null) {
+ return functionName;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private static String findFunction(@Nullable Expression expression,
Set<String> functionNames) {
+ if (expression == null) {
+ return null;
+ }
+ Function function = expression.getFunctionCall();
+ if (function == null) {
+ return null;
+ }
+ if (functionNames.contains(function.getOperator())) {
+ return function.getOperator();
+ }
+ return findFunction(function.getOperands(), functionNames);
+ }
+
/// Broker-side helper invoked by the MV handler's split-dispatcher
callback. Owns the generic
/// Execute a SPLIT_REWRITE plan via the configured MV handler. Returns a
finalized
/// `BrokerResponseNative` on success, or `null` to signal the caller should
fall through to
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 9512ebf8e15..7650cebe545 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
@@ -24,8 +24,13 @@ import java.util.Map;
import org.apache.pinot.common.evaluator.GroovyFunctionEvaluator;
import org.apache.pinot.common.evaluator.GroovyStaticAnalyzerConfig;
import org.apache.pinot.common.request.PinotQuery;
+import
org.apache.pinot.core.query.utils.rewriter.ParentAggregationResultRewriter;
+import org.apache.pinot.core.query.utils.rewriter.ResultRewriterFactory;
import org.apache.pinot.sql.parsers.CalciteSqlParser;
+import org.apache.pinot.sql.parsers.rewriter.ExprMinMaxRewriter;
+import org.apache.pinot.sql.parsers.rewriter.QueryRewriterFactory;
import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
@@ -167,6 +172,58 @@ public class QueryValidationTest {
() -> BaseSingleStageBrokerRequestHandler.validateRequest(pinotQuery,
10));
}
+ /// Restores the default query and result rewriters, which the
EXPR_MIN/EXPR_MAX tests change.
+ @AfterMethod
+ public void resetRewriters() {
+ QueryRewriterFactory.init(null);
+ ResultRewriterFactory.init(null);
+ }
+
+ @Test
+ public void testExprMinMaxWithoutRewriters() {
+ testUnsupportedQuery("SELECT EXPR_MIN(a, b) FROM testTable",
+ "EXPR_MIN is not enabled on this broker. To enable it, add '" +
ExprMinMaxRewriter.class.getName()
+ + "' to 'pinot.broker.query.rewriter.class.names' and '" +
ParentAggregationResultRewriter.class.getName()
+ + "' to 'pinot.broker.result.rewriter.class.names'");
+ }
+
+ @Test
+ public void testExprMinMaxWithRewriters() {
+ enableExprMinMaxQueryRewriter();
+
ResultRewriterFactory.init(ParentAggregationResultRewriter.class.getName());
+ validQuery("SELECT EXPR_MIN(a, b), EXPR_MAX(c, b, d), COUNT(*) FROM
testTable");
+ testUnsupportedExprMinMaxPosition("SELECT COUNT(*), EXPR_MIN(a, b) AS minA
FROM testTable", "EXPR_MIN");
+ testUnsupportedExprMinMaxPosition("SELECT EXPR_MAX(a, b) + 1 FROM
testTable", "EXPR_MAX");
+ testUnsupportedExprMinMaxPosition("SELECT COUNT(*) FROM testTable WHERE
EXPR_MIN(a, b) > 1", "EXPR_MIN");
+ testUnsupportedExprMinMaxPosition("SELECT c, COUNT(*) FROM testTable GROUP
BY c HAVING EXPR_MAX(a, b) > 1",
+ "EXPR_MAX");
+ testUnsupportedExprMinMaxPosition("SELECT c, COUNT(*) FROM testTable GROUP
BY c ORDER BY EXPR_MIN(a, b)",
+ "EXPR_MIN");
+ }
+
+ @Test
+ public void testExprMinMaxWithoutResultRewriter() {
+ enableExprMinMaxQueryRewriter();
+ testUnsupportedQuery("SELECT EXPR_MIN(a, b) FROM testTable",
+ "EXPR_MIN and EXPR_MAX require the result rewriter '" +
ParentAggregationResultRewriter.class.getName()
+ + "'. To enable it, add it to
'pinot.broker.result.rewriter.class.names'");
+ }
+
+ private void testUnsupportedExprMinMaxPosition(String query, String
functionName) {
+ testUnsupportedQuery(query, functionName + " is only supported as a
top-level expression in the select list, "
+ + "without an alias. It is not supported in other expressions or in
the WHERE, GROUP BY, HAVING or ORDER BY "
+ + "clauses");
+ }
+
+ private static void enableExprMinMaxQueryRewriter() {
+ QueryRewriterFactory.init(String.join(",",
QueryRewriterFactory.DEFAULT_QUERY_REWRITERS_CLASS_NAMES) + ","
+ + ExprMinMaxRewriter.class.getName());
+ }
+
+ private static void validQuery(String query) {
+
BaseSingleStageBrokerRequestHandler.validateRequest(CalciteSqlParser.compileToPinotQuery(query),
1000);
+ }
+
private void testValidateGroovyQuery(String query, boolean
queryContainsGroovy) {
try {
PinotQuery pinotQuery = CalciteSqlParser.compileToPinotQuery(query);
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/transport/InstanceRequestHandler.java
b/pinot-core/src/main/java/org/apache/pinot/core/transport/InstanceRequestHandler.java
index 2068e31dbc1..0c4e239c626 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/transport/InstanceRequestHandler.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/transport/InstanceRequestHandler.java
@@ -43,6 +43,7 @@ import org.apache.pinot.common.metrics.ServerMeter;
import org.apache.pinot.common.metrics.ServerMetrics;
import org.apache.pinot.common.metrics.ServerQueryPhase;
import org.apache.pinot.common.metrics.ServerTimer;
+import org.apache.pinot.common.request.BrokerRequest;
import org.apache.pinot.common.request.InstanceRequest;
import org.apache.pinot.core.common.datatable.DataTableBuilderFactory;
import org.apache.pinot.core.query.request.ServerQueryRequest;
@@ -123,20 +124,31 @@ public class InstanceRequestHandler extends
SimpleChannelInboundHandler<ByteBuf>
_serverMetrics.addMeteredGlobalValue(ServerMeter.NETTY_CONNECTION_BYTES_RECEIVED,
requestSize);
byte[] requestBytes = new byte[requestSize];
msg.readBytes(requestBytes);
- ServerQueryRequest queryRequest;
+ InstanceRequest instanceRequest = new InstanceRequest();
try {
- InstanceRequest instanceRequest = new InstanceRequest();
THREAD_LOCAL_T_DESERIALIZER.get().deserialize(instanceRequest,
requestBytes);
- queryRequest = new ServerQueryRequest(instanceRequest, _serverMetrics,
queryArrivalTimeMs);
- _serverMetrics.addMeteredTableValue(queryRequest.getTableNameWithType(),
ServerMeter.QUERIES_ON_TABLE, 1);
-
queryRequest.getTimerContext().startNewPhaseTimer(ServerQueryPhase.REQUEST_DESERIALIZATION,
queryArrivalTimeMs)
- .stopAndRecord();
} catch (Exception e) {
// Do not send error response because request id is unknown.
LOGGER.error("Failed to deserialize instance request: {}",
BytesUtils.toHexString(requestBytes), e);
_serverMetrics.addMeteredGlobalValue(ServerMeter.REQUEST_DESERIALIZATION_EXCEPTIONS,
1);
return;
}
+ ServerQueryRequest queryRequest;
+ try {
+ queryRequest = new ServerQueryRequest(instanceRequest, _serverMetrics,
queryArrivalTimeMs);
+ } catch (Exception e) {
+ // The request id is known, so reply with the error. Without a response,
the broker waits until the query times
+ // out and reports the server as not responded, which hides the actual
error (e.g. an unsupported function).
+ long requestId = instanceRequest.getRequestId();
+ String tableNameWithType = getTableNameWithType(instanceRequest);
+ LOGGER.error("Failed to compile query request: {} on table: {}",
requestId, tableNameWithType, e);
+ sendErrorResponse(ctx, requestId, tableNameWithType, queryArrivalTimeMs,
+ DataTableBuilderFactory.getEmptyDataTable(), e);
+ return;
+ }
+ _serverMetrics.addMeteredTableValue(queryRequest.getTableNameWithType(),
ServerMeter.QUERIES_ON_TABLE, 1);
+
queryRequest.getTimerContext().startNewPhaseTimer(ServerQueryPhase.REQUEST_DESERIALIZATION,
queryArrivalTimeMs)
+ .stopAndRecord();
try {
submitQuery(queryRequest, ctx, queryArrivalTimeMs);
} catch (Exception e) {
@@ -258,6 +270,18 @@ public class InstanceRequestHandler extends
SimpleChannelInboundHandler<ByteBuf>
return new HashSet<>(_executionContexts.keySet());
}
+ /// Returns the table name of a deserialized request that could not be
compiled, or `null` if the request does not
+ /// carry one.
+ @Nullable
+ private static String getTableNameWithType(InstanceRequest instanceRequest) {
+ BrokerRequest brokerRequest = instanceRequest.getQuery();
+ if (brokerRequest == null || brokerRequest.getPinotQuery() == null
+ || brokerRequest.getPinotQuery().getDataSource() == null) {
+ return null;
+ }
+ return brokerRequest.getPinotQuery().getDataSource().getTableName();
+ }
+
/// Send an exception back to broker as response to the query request.
private void sendErrorResponse(ChannelHandlerContext ctx, long requestId,
String tableNameWithType,
long queryArrivalTimeMs, DataTable dataTable, Exception e) {
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/transport/InstanceRequestHandlerTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/transport/InstanceRequestHandlerTest.java
index a9298e3ba95..b83f19e6e80 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/transport/InstanceRequestHandlerTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/transport/InstanceRequestHandlerTest.java
@@ -20,15 +20,23 @@ package org.apache.pinot.core.transport;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAccumulator;
+import org.apache.pinot.common.datatable.DataTable;
+import org.apache.pinot.common.datatable.DataTable.MetadataKey;
+import org.apache.pinot.common.datatable.DataTableFactory;
import org.apache.pinot.common.metrics.ServerMetrics;
+import org.apache.pinot.common.request.BrokerRequest;
+import org.apache.pinot.common.request.InstanceRequest;
import org.apache.pinot.core.query.executor.QueryExecutor;
import org.apache.pinot.core.query.request.ServerQueryRequest;
import org.apache.pinot.core.query.scheduler.QueryScheduler;
@@ -37,10 +45,14 @@ import
org.apache.pinot.core.query.scheduler.resources.UnboundedResourceManager;
import org.apache.pinot.server.access.AccessControl;
import org.apache.pinot.spi.accounting.ThreadAccountantUtils;
import org.apache.pinot.spi.env.PinotConfiguration;
+import org.apache.pinot.spi.exception.QueryErrorCode;
import org.apache.pinot.spi.metrics.PinotMetricUtils;
import org.apache.pinot.spi.metrics.PinotMetricsRegistry;
import org.apache.pinot.spi.query.QueryExecutionContext;
+import org.apache.pinot.sql.parsers.CalciteSqlParser;
import org.apache.pinot.util.TestUtils;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TCompactProtocol;
import org.mockito.ArgumentCaptor;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -144,6 +156,40 @@ public class InstanceRequestHandlerTest {
verify(ctx).close();
}
+ @Test
+ public void testRepliesWithErrorWhenQueryCannotBeCompiled()
+ throws Exception {
+ PinotConfiguration config = new PinotConfiguration();
+ QueryScheduler queryScheduler = createQueryScheduler(config, new
CountDownLatch(0));
+ InstanceRequestHandler handler =
+ new InstanceRequestHandler("server01", config, queryScheduler,
mock(AccessControl.class),
+ ThreadAccountantUtils.getNoOpAccountant());
+
+ // EXPR_MIN cannot be executed without the broker-side ExprMinMaxRewriter,
so the server fails to build the
+ // aggregation function while it compiles the request.
+ BrokerRequest brokerRequest = new BrokerRequest();
+ brokerRequest.setPinotQuery(CalciteSqlParser.compileToPinotQuery("SELECT
EXPR_MIN(a, b) FROM testTable_OFFLINE"));
+ InstanceRequest instanceRequest = new InstanceRequest(123L, brokerRequest);
+ byte[] requestBytes = new TSerializer(new
TCompactProtocol.Factory()).serialize(instanceRequest);
+
+ ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+ when(ctx.writeAndFlush(any())).thenReturn(mock(ChannelFuture.class));
+ handler.channelRead0(ctx, Unpooled.wrappedBuffer(requestBytes));
+
+ ArgumentCaptor<ByteBuf> responseCaptor =
ArgumentCaptor.forClass(ByteBuf.class);
+ verify(ctx).writeAndFlush(responseCaptor.capture());
+ ByteBuf response = responseCaptor.getValue();
+ byte[] responseBytes = new byte[response.readableBytes()];
+ response.readBytes(responseBytes);
+ DataTable dataTable = DataTableFactory.getDataTable(responseBytes);
+
assertEquals(dataTable.getMetadata().get(MetadataKey.REQUEST_ID.getName()),
"123");
+ Map<Integer, String> exceptions = dataTable.getExceptions();
+ assertEquals(exceptions.size(), 1);
+ String message = exceptions.get(QueryErrorCode.QUERY_EXECUTION.getId());
+ // Thrown by AggregationFunctionFactory for an EXPR_MIN that the broker
did not rewrite
+ assertTrue(message.contains("Aggregation function: EXPRMIN is only
supported in selection without alias"), message);
+ }
+
private QueryScheduler createQueryScheduler(PinotConfiguration config,
CountDownLatch queryFinishLatch) {
ResourceManager resourceManager = new UnboundedResourceManager(config);
return new QueryScheduler(config, "serverId", mock(QueryExecutor.class),
ThreadAccountantUtils.getNoOpAccountant(),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]