[ASTERIXDB-2598][RT] Add Support For Runtime Warnings
- user model changes: yes
- storage format changes: no
- interface changes: yes
Details:
- Add the ability to add runtime warnings per task
and return the generated warnings as part of the
task profile on task completion.
- On successful job completion, aggregate warnings
from all task profiles of a job.
- Return the generated warnings in the query service
response as an array of "warnings" each with a code,
which is currently hard-coded to 1, and a message.
- Fix propagating source location to scalar aggregate
functions.
- Add a flag in test cases definition to indicate whether
or not to check for expected warnings and default it to
false.
- Generate warnings when min/max functions encounter
incomparable type or unsupported input.
- Add support in test framework to extract warnings
along with extracting the result field.
- Add support in test framework to validate generated
and expected warnings.
- Add test cases for min/max generated warnings.
Change-Id: I52fa5b807799487d62e67a8861068e1547aa629a
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3451
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Murtadha Hubail <mhubail@apache.org>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
index c27a30f..c5e9beb 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
@@ -37,6 +37,7 @@
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.api.client.IClusterInfoCollector;
import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.job.JobId;
import org.apache.hyracks.api.job.JobSpecification;
import org.apache.hyracks.api.result.ResultSetId;
@@ -163,4 +164,11 @@
* @return the responer printer
*/
IResponsePrinter getResponsePrinter();
+
+ /**
+ * Gets the warnings generated during compiling and exeucting a request
+ *
+ * @return the list of warnings
+ */
+ List<Warning> getWarnings();
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/ResultMetadata.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/ResultMetadata.java
index 69f46a4..6fb37d3 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/ResultMetadata.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/ResultMetadata.java
@@ -18,7 +18,10 @@
*/
package org.apache.asterix.api.common;
+import java.util.Set;
+
import org.apache.asterix.translator.SessionConfig;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.result.IResultMetadata;
public class ResultMetadata implements IResultMetadata {
@@ -26,6 +29,7 @@
private final SessionConfig.OutputFormat format;
private long jobDuration;
private long processedObjects;
+ private Set<Warning> warnings;
public ResultMetadata(SessionConfig.OutputFormat format) {
this.format = format;
@@ -47,10 +51,18 @@
this.jobDuration = jobDuration;
}
+ public void setWarnings(Set<Warning> warnings) {
+ this.warnings = warnings;
+ }
+
public long getJobDuration() {
return jobDuration;
}
+ public Set<Warning> getWarnings() {
+ return warnings;
+ }
+
@Override
public String toString() {
return "ResultMetadata{" + "format=" + format + ", jobDuration=" + jobDuration + ", processedObjects="
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
index 254f92a..b1361d9 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
@@ -19,6 +19,7 @@
package org.apache.asterix.api.http.server;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
@@ -45,6 +46,7 @@
import org.apache.asterix.translator.ResultProperties;
import org.apache.asterix.translator.SessionOutput;
import org.apache.hyracks.api.application.INCServiceContext;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.http.api.IChannelClosedHandler;
import org.apache.hyracks.http.api.IServletRequest;
import org.apache.hyracks.http.server.HttpServer;
@@ -71,7 +73,7 @@
SessionOutput sessionOutput, ResultProperties resultProperties, IStatementExecutor.Stats stats,
QueryServiceRequestParameters param, RequestExecutionState execution,
Map<String, String> optionalParameters, Map<String, byte[]> statementParameters,
- ResponsePrinter responsePrinter) throws Exception {
+ ResponsePrinter responsePrinter, List<Warning> warnings) throws Exception {
// Running on NC -> send 'execute' message to CC
INCServiceContext ncCtx = (INCServiceContext) serviceCtx;
INCMessageBroker ncMb = (INCMessageBroker) ncCtx.getMessageBroker();
@@ -121,7 +123,8 @@
responsePrinter.addResultPrinter(
new NcResultPrinter(appCtx, responseMsg, getResultSet(), delivery, sessionOutput, stats));
}
- buildResponseResults(responsePrinter, sessionOutput, responseMsg.getExecutionPlans());
+ warnings.addAll(responseMsg.getWarnings());
+ buildResponseResults(responsePrinter, sessionOutput, responseMsg.getExecutionPlans(), warnings);
}
private void cancelQuery(INCMessageBroker messageBroker, String nodeId, String uuid, String clientContextID,
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 4eb3524..0d5de57 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -31,6 +31,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -43,6 +44,7 @@
import org.apache.asterix.algebra.base.ILangExtension;
import org.apache.asterix.app.result.ExecutionError;
+import org.apache.asterix.app.result.ExecutionWarning;
import org.apache.asterix.app.result.ResponseMertics;
import org.apache.asterix.app.result.ResponsePrinter;
import org.apache.asterix.app.result.fields.ClientContextIdPrinter;
@@ -88,6 +90,7 @@
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.api.application.IServiceContext;
import org.apache.hyracks.api.exceptions.HyracksException;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.control.common.controllers.CCConfig;
import org.apache.hyracks.http.api.IServletRequest;
import org.apache.hyracks.http.api.IServletResponse;
@@ -468,7 +471,7 @@
long errorCount = 1;
Stats stats = new Stats();
RequestExecutionState execution = new RequestExecutionState();
- List<ICodedMessage> warnings = Collections.emptyList();
+ List<Warning> warnings = new ArrayList<>();
Charset resultCharset = HttpUtil.setContentType(response, HttpUtil.ContentType.APPLICATION_JSON, request);
PrintWriter httpWriter = response.writer();
SessionOutput sessionOutput = createSessionOutput(httpWriter);
@@ -503,7 +506,7 @@
setAccessControlHeaders(request, response);
response.setStatus(execution.getHttpStatus());
executeStatement(requestRef, statementsText, sessionOutput, resultProperties, stats, param, execution,
- optionalParams, statementParams, responsePrinter);
+ optionalParams, statementParams, responsePrinter, warnings);
}
errorCount = 0;
} catch (Exception | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError e) {
@@ -539,20 +542,22 @@
}
protected void buildResponseResults(ResponsePrinter responsePrinter, SessionOutput sessionOutput,
- ExecutionPlans plans) {
+ ExecutionPlans plans, List<Warning> warnings) {
responsePrinter.addResultPrinter(new PlansPrinter(plans, sessionOutput.config().getPlanFormat()));
+ if (!warnings.isEmpty()) {
+ List<ICodedMessage> codedWarnings = new ArrayList<>();
+ warnings.forEach(warn -> codedWarnings.add(ExecutionWarning.of(warn)));
+ responsePrinter.addResultPrinter(new WarningsPrinter(codedWarnings));
+ }
}
protected void buildResponseFooters(long elapsedStart, long errorCount, Stats stats,
- RequestExecutionState execution, List<ICodedMessage> warnings, Charset resultCharset,
+ RequestExecutionState execution, List<Warning> warnings, Charset resultCharset,
ResponsePrinter responsePrinter, ResultDelivery delivery) {
if (ResultDelivery.ASYNC != delivery) {
// in case of ASYNC delivery, the status is printed by query translator
responsePrinter.addFooterPrinter(new StatusPrinter(execution.getResultStatus()));
}
- if (!warnings.isEmpty()) {
- responsePrinter.addFooterPrinter(new WarningsPrinter(warnings));
- }
final ResponseMertics mertics = ResponseMertics.of(System.nanoTime() - elapsedStart, execution.duration(),
stats.getCount(), stats.getSize(), stats.getProcessedObjects(), errorCount, warnings.size());
responsePrinter.addFooterPrinter(new MetricsPrinter(mertics, resultCharset));
@@ -580,7 +585,7 @@
SessionOutput sessionOutput, ResultProperties resultProperties, Stats stats,
QueryServiceRequestParameters param, RequestExecutionState execution,
Map<String, String> optionalParameters, Map<String, byte[]> statementParameters,
- ResponsePrinter responsePrinter) throws Exception {
+ ResponsePrinter responsePrinter, List<Warning> warnings) throws Exception {
IClusterManagementWork.ClusterState clusterState =
((ICcApplicationContext) appCtx).getClusterStateManager().getState();
if (clusterState != IClusterManagementWork.ClusterState.ACTIVE) {
@@ -600,7 +605,8 @@
optionalParameters, stmtParams, param.isMultiStatement());
translator.compileAndExecute(getHyracksClientConnection(), requestParameters);
execution.end();
- buildResponseResults(responsePrinter, sessionOutput, translator.getExecutionPlans());
+ warnings.addAll(translator.getWarnings());
+ buildResponseResults(responsePrinter, sessionOutput, translator.getExecutionPlans(), warnings);
}
protected void handleExecuteStatementException(Throwable t, RequestExecutionState state,
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
index b0b94c5..5663b12 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
@@ -143,6 +143,7 @@
responseMsg.setMetadata(outMetadata);
responseMsg.setStats(stats);
responseMsg.setExecutionPlans(translator.getExecutionPlans());
+ responseMsg.setWarnings(translator.getWarnings());
} catch (AlgebricksException | HyracksException | TokenMgrError
| org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
// we trust that "our" exceptions are serializable and have a comprehensible error message
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementResponseMessage.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementResponseMessage.java
index 94dd541..7a7661d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementResponseMessage.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementResponseMessage.java
@@ -19,6 +19,8 @@
package org.apache.asterix.app.message;
+import java.util.List;
+
import org.apache.asterix.common.api.INcApplicationContext;
import org.apache.asterix.common.messaging.api.INcAddressedMessage;
import org.apache.asterix.common.messaging.api.MessageFuture;
@@ -26,6 +28,7 @@
import org.apache.asterix.translator.ExecutionPlans;
import org.apache.asterix.translator.IStatementExecutor;
import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.Warning;
public final class ExecuteStatementResponseMessage implements INcAddressedMessage {
private static final long serialVersionUID = 1L;
@@ -42,6 +45,8 @@
private ExecutionPlans executionPlans;
+ private List<Warning> warnings;
+
public ExecuteStatementResponseMessage(long requestMessageId) {
this.requestMessageId = requestMessageId;
}
@@ -95,6 +100,14 @@
this.executionPlans = executionPlans;
}
+ public List<Warning> getWarnings() {
+ return warnings;
+ }
+
+ public void setWarnings(List<Warning> warnings) {
+ this.warnings = warnings;
+ }
+
@Override
public String toString() {
return String.format("%s(id=%s): %d characters", getClass().getSimpleName(), requestMessageId,
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
index 29eb098..5552bb8 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
@@ -19,6 +19,7 @@
package org.apache.asterix.app.result;
import org.apache.asterix.common.api.ICodedMessage;
+import org.apache.hyracks.api.exceptions.Warning;
public class ExecutionWarning implements ICodedMessage {
@@ -30,6 +31,10 @@
this.message = message;
}
+ public static ICodedMessage of(Warning warning) {
+ return new ExecutionWarning(1, warning.getMessage());
+ }
+
@Override
public int getCode() {
return code;
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/JobResultCallback.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/JobResultCallback.java
index ecf92a0..ab17cdd 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/JobResultCallback.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/JobResultCallback.java
@@ -19,9 +19,12 @@
package org.apache.asterix.app.result;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import org.apache.asterix.api.common.ResultMetadata;
import org.apache.asterix.common.dataflow.ICcApplicationContext;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.job.JobId;
import org.apache.hyracks.api.result.IJobResultCallback;
import org.apache.hyracks.api.result.ResultJobRecord;
@@ -60,11 +63,12 @@
}
final ResultMetadata metadata = (ResultMetadata) resultSetMetaData.getMetadata();
metadata.setJobDuration(resultJobRecord.getJobDuration());
- metadata.setProcessedObjects(getJobProccssedObjects(jobId));
+ aggregateJobStats(jobId, metadata);
}
- private long getJobProccssedObjects(JobId jobId) {
+ private void aggregateJobStats(JobId jobId, ResultMetadata metadata) {
long processedObjects = 0;
+ Set<Warning> warnings = new HashSet<>();
IJobManager jobManager =
((ClusterControllerService) appCtx.getServiceContext().getControllerService()).getJobManager();
final JobRun run = jobManager.get(jobId);
@@ -75,9 +79,11 @@
final Collection<TaskProfile> jobletTasksProfile = jp.getTaskProfiles().values();
for (TaskProfile tp : jobletTasksProfile) {
processedObjects += tp.getStatsCollector().getAggregatedStats().getTupleCounter().get();
+ warnings.addAll(tp.getWarnings());
}
}
}
- return processedObjects;
+ metadata.setProcessedObjects(processedObjects);
+ metadata.setWarnings(warnings);
}
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index 60e98f9..37c7d82 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -194,6 +194,7 @@
import org.apache.hyracks.api.dataflow.value.ITypeTraits;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.io.FileSplit;
import org.apache.hyracks.api.io.UnmanagedFileSplit;
import org.apache.hyracks.api.job.JobFlag;
@@ -230,6 +231,7 @@
protected final EnumSet<JobFlag> jobFlags = EnumSet.noneOf(JobFlag.class);
protected final IMetadataLockManager lockManager;
protected final IResponsePrinter responsePrinter;
+ protected final List<Warning> warnings;
public QueryTranslator(ICcApplicationContext appCtx, List<Statement> statements, SessionOutput output,
ILangCompilationProvider compilationProvider, ExecutorService executorService,
@@ -246,6 +248,7 @@
activeDataverse = MetadataBuiltinEntities.DEFAULT_DATAVERSE;
this.executorService = executorService;
this.responsePrinter = responsePrinter;
+ warnings = new ArrayList<>();
if (appCtx.getServiceContext().getAppConfig().getBoolean(CCConfig.Option.ENFORCE_FRAME_WRITER_PROTOCOL)) {
this.jobFlags.add(JobFlag.ENFORCE_CONTRACT);
}
@@ -2544,6 +2547,7 @@
(org.apache.asterix.api.common.ResultMetadata) controllerService.getResultDirectoryService()
.getResultMetadata(jobId, rsId);
stats.setProcessedObjects(resultMetadata.getProcessedObjects());
+ warnings.addAll(resultMetadata.getWarnings());
}
private void asyncCreateAndRunJob(IHyracksClientConnection hcc, IStatementCompiler compiler, IMetadataLocker locker,
@@ -2917,6 +2921,11 @@
return getActiveDataverseName(dataverse != null ? dataverse.getValue() : null);
}
+ @Override
+ public List<Warning> getWarnings() {
+ return warnings;
+ }
+
/**
* Abort the ongoing metadata transaction logging the error cause
*
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
index fe64dd1..1624db3 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
@@ -77,7 +77,8 @@
boolean exceptionThrown = false;
try {
// ensure result is valid json and error will be returned and not results.
- ResultExtractor.extract(IOUtils.toInputStream(resultStr, StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+ ResultExtractor.extract(IOUtils.toInputStream(resultStr, StandardCharsets.UTF_8), StandardCharsets.UTF_8)
+ .getResult();
} catch (Exception e) {
exceptionThrown = true;
Assert.assertTrue(e.getMessage().contains(expectedException.getMessage()));
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/CancellationTestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/CancellationTestExecutor.java
index 564672a..a34a29c 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/CancellationTestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/CancellationTestExecutor.java
@@ -126,4 +126,10 @@
return true;
}
}
+
+ @Override
+ protected void ensureWarnings(int actualWarnCount, int expectedWarnCount, TestCase.CompilationUnit cUnit)
+ throws Exception {
+ // skip checking warnings as currently cancelled queries with warnings might not run successfully at all
+ }
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ExtractedResult.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ExtractedResult.java
new file mode 100644
index 0000000..96f82a3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ExtractedResult.java
@@ -0,0 +1,44 @@
+/*
+ * 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.asterix.test.common;
+
+import java.io.InputStream;
+import java.util.List;
+
+public class ExtractedResult {
+
+ private InputStream result;
+ private List<String> warnings;
+
+ public void setResult(InputStream result) {
+ this.result = result;
+ }
+
+ public void setWarnings(List<String> warnings) {
+ this.warnings = warnings;
+ }
+
+ public InputStream getResult() {
+ return result;
+ }
+
+ public List<String> getWarnings() {
+ return warnings;
+ }
+}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java
index 6d32518..ab90244 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java
@@ -43,9 +43,11 @@
* @param expectedResultFileCtxs
* @param testFile
* @param actualPath
+ * @param actualWarnCount
*/
void execute(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx, String statement,
boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit, MutableInt queryCount,
- List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath) throws Exception;
+ List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath, MutableInt actualWarnCount)
+ throws Exception;
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
index 864a339..eb708ce 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
@@ -20,9 +20,11 @@
import java.io.InputStream;
import java.nio.charset.Charset;
+import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.apache.asterix.common.exceptions.AsterixException;
@@ -34,6 +36,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Iterators;
@@ -82,16 +85,16 @@
private static final Logger LOGGER = LogManager.getLogger();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- public static InputStream extract(InputStream resultStream, Charset resultCharset) throws Exception {
- return extract(resultStream, EnumSet.of(ResultField.RESULTS), resultCharset);
+ public static ExtractedResult extract(InputStream resultStream, Charset resultCharset) throws Exception {
+ return extract(resultStream, EnumSet.of(ResultField.RESULTS, ResultField.WARNINGS), resultCharset);
}
public static InputStream extractMetrics(InputStream resultStream, Charset resultCharset) throws Exception {
- return extract(resultStream, EnumSet.of(ResultField.METRICS), resultCharset);
+ return extract(resultStream, EnumSet.of(ResultField.METRICS), resultCharset).getResult();
}
public static InputStream extractPlans(InputStream resultStream, Charset resultCharset) throws Exception {
- return extract(resultStream, EnumSet.of(ResultField.PLANS), resultCharset);
+ return extract(resultStream, EnumSet.of(ResultField.PLANS), resultCharset).getResult();
}
public static String extractHandle(InputStream resultStream, Charset responseCharset) throws Exception {
@@ -110,8 +113,9 @@
return null;
}
- private static InputStream extract(InputStream resultStream, EnumSet<ResultField> resultFields,
+ private static ExtractedResult extract(InputStream resultStream, EnumSet<ResultField> resultFields,
Charset resultCharset) throws Exception {
+ ExtractedResult extractedResult = new ExtractedResult();
final String resultStr = IOUtils.toString(resultStream, resultCharset);
final ObjectNode result = OBJECT_MAPPER.readValue(resultStr, ObjectNode.class);
@@ -168,14 +172,16 @@
case STATUS:
case TYPE:
case PLANS:
- case WARNINGS:
resultBuilder.append(OBJECT_MAPPER.writeValueAsString(fieldValue));
+ case WARNINGS:
+ extractWarnings(fieldValue, extractedResult);
break;
default:
throw new IllegalStateException("Unexpected result field: " + fieldKind);
}
}
- return IOUtils.toInputStream(resultBuilder, resultCharset);
+ extractedResult.setResult(IOUtils.toInputStream(resultBuilder, resultCharset));
+ return extractedResult;
}
private static void checkForErrors(ObjectNode result) throws Exception {
@@ -188,4 +194,15 @@
throw new Exception(errors.asText());
}
}
+
+ private static void extractWarnings(JsonNode warningsValue, ExtractedResult exeResult) {
+ List<String> warnings = new ArrayList<>();
+ if (warningsValue.isArray()) {
+ final ArrayNode warningsArray = (ArrayNode) warningsValue;
+ for (JsonNode warn : warningsArray) {
+ warnings.add(warn.get("msg").asText());
+ }
+ }
+ exeResult.setWarnings(warnings);
+ }
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index cbe41e8..5da5199 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -152,7 +152,10 @@
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("store", "validate")));
private static final int MAX_NON_UTF_8_STATEMENT_SIZE = 64 * 1024;
- private final IPollTask plainExecutor = this::executeTestFile;
+ private final IPollTask plainExecutor = (testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit,
+ queryCount, expectedResultFileCtxs, testFile, actualPath, actualWarnCount) -> executeTestFile(testCaseCtx,
+ ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, expectedResultFileCtxs,
+ testFile, actualPath, actualWarnCount);
public static final String DELIVERY_ASYNC = "async";
public static final String DELIVERY_DEFERRED = "deferred";
@@ -903,9 +906,8 @@
public void executeTestFile(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx,
String statement, boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit,
- MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath)
- throws Exception {
- URI uri;
+ MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath,
+ MutableInt actualWarnCount) throws Exception {
InputStream resultStream;
File qbcFile;
boolean failed = false;
@@ -933,11 +935,11 @@
case "pollquery":
poll(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount,
expectedResultFileCtxs, testFile, actualPath, ctx.getType().substring("poll".length()),
- plainExecutor);
+ actualWarnCount, plainExecutor);
break;
case "polldynamic":
polldynamic(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount,
- expectedResultFileCtxs, testFile, actualPath);
+ expectedResultFileCtxs, testFile, actualPath, actualWarnCount);
break;
case "query":
case "async":
@@ -957,9 +959,12 @@
: expectedResultFileCtxs.get(queryCount.intValue()).getFile();
File actualResultFile = expectedResultFile == null ? null
: testCaseCtx.getActualResultFile(cUnit, expectedResultFile, new File(actualPath));
- executeQuery(OutputFormat.forCompilationUnit(cUnit), statement, variableCtx, ctx.getType(), testFile,
- expectedResultFile, actualResultFile, queryCount, expectedResultFileCtxs.size(),
- cUnit.getParameter(), ComparisonEnum.TEXT);
+ ExtractedResult extractedResult = executeQuery(OutputFormat.forCompilationUnit(cUnit), statement,
+ variableCtx, ctx.getType(), testFile, expectedResultFile, actualResultFile, queryCount,
+ expectedResultFileCtxs.size(), cUnit.getParameter(), ComparisonEnum.TEXT);
+ if (testCaseCtx.getTestCase().isCheckWarnings()) {
+ validateWarnings(extractedResult.getWarnings(), cUnit.getExpectedWarn(), actualWarnCount);
+ }
break;
case "store":
// This is a query that returns the expected output of a subsequent query
@@ -1232,7 +1237,7 @@
} else if ("uri".equals(extension)) {
resultStream = executeURI(reqType, URI.create(variablesReplaced), fmt, params, statusCodePredicate, body);
if (extracResult) {
- resultStream = ResultExtractor.extract(resultStream, UTF_8);
+ resultStream = ResultExtractor.extract(resultStream, UTF_8).getResult();
}
} else {
throw new IllegalArgumentException("Unexpected format for method " + reqType + ": " + extension);
@@ -1261,9 +1266,9 @@
queryCount.increment();
}
- public void executeQuery(OutputFormat fmt, String statement, Map<String, Object> variableCtx, String reqType,
- File testFile, File expectedResultFile, File actualResultFile, MutableInt queryCount, int numResultFiles,
- List<Parameter> params, ComparisonEnum compare) throws Exception {
+ public ExtractedResult executeQuery(OutputFormat fmt, String statement, Map<String, Object> variableCtx,
+ String reqType, File testFile, File expectedResultFile, File actualResultFile, MutableInt queryCount,
+ int numResultFiles, List<Parameter> params, ComparisonEnum compare) throws Exception {
String delivery = DELIVERY_IMMEDIATE;
if (reqType.equalsIgnoreCase("async")) {
delivery = DELIVERY_ASYNC;
@@ -1275,6 +1280,7 @@
boolean isJsonEncoded = isJsonEncoded(extractHttpRequestType(statement));
Charset responseCharset = expectedResultFile == null ? UTF_8 : nextCharset();
InputStream resultStream;
+ ExtractedResult extractedResult = null;
if (DELIVERY_IMMEDIATE.equals(delivery)) {
resultStream = executeQueryService(statement, fmt, uri, params, isJsonEncoded, responseCharset, null,
isCancellable(reqType));
@@ -1286,7 +1292,8 @@
resultStream = ResultExtractor.extractPlans(resultStream, responseCharset);
break;
default:
- resultStream = ResultExtractor.extract(resultStream, responseCharset);
+ extractedResult = ResultExtractor.extract(resultStream, responseCharset);
+ resultStream = extractedResult.getResult();
break;
}
} else {
@@ -1308,7 +1315,7 @@
writeOutputToFile(actualResultFile, resultStream);
if (expectedResultFile == null) {
if (reqType.equals("store")) {
- return;
+ return extractedResult;
}
Assert.fail("no result file for " + testFile.toString() + "; queryCount: " + queryCount
+ ", filectxs.size: " + numResultFiles);
@@ -1320,22 +1327,23 @@
}
// Deletes the matched result file.
actualResultFile.getParentFile().delete();
+ return extractedResult;
}
private void polldynamic(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx,
String statement, boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit,
- MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath)
- throws Exception {
+ MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath,
+ MutableInt actualWarnCount) throws Exception {
IExpectedResultPoller poller = getExpectedResultPoller(statement);
final String key = getKey(statement);
poll(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, expectedResultFileCtxs,
- testFile, actualPath, "validate", new IPollTask() {
+ testFile, actualPath, "validate", actualWarnCount, new IPollTask() {
@Override
public void execute(TestCaseContext testCaseCtx, TestFileContext ctx,
Map<String, Object> variableCtx, String statement, boolean isDmlRecoveryTest,
ProcessBuilder pb, CompilationUnit cUnit, MutableInt queryCount,
- List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath)
- throws Exception {
+ List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath,
+ MutableInt actualWarnCount) throws Exception {
File actualResultFile = new File(actualPath, testCaseCtx.getTestCase().getFilePath()
+ File.separatorChar + cUnit.getName() + '.' + ctx.getSeqNum() + ".polled.adm");
if (actualResultFile.exists() && !actualResultFile.delete()) {
@@ -1373,7 +1381,7 @@
private void poll(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx,
String statement, boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit,
MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath,
- String newType, IPollTask pollTask) throws Exception {
+ String newType, MutableInt actualWarnCount, IPollTask pollTask) throws Exception {
// polltimeoutsecs=nnn, polldelaysecs=nnn
int timeoutSecs = getTimeoutSecs(statement);
int retryDelaySecs = getRetryDelaySecs(statement);
@@ -1396,7 +1404,7 @@
try {
startSemaphore.release();
pollTask.execute(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit,
- queryCount, expectedResultFileCtxs, testFile, actualPath);
+ queryCount, expectedResultFileCtxs, testFile, actualPath, actualWarnCount);
} finally {
endSemaphore.release();
}
@@ -1475,7 +1483,7 @@
private InputStream executeUpdateOrDdl(String statement, OutputFormat outputFormat, URI serviceUri)
throws Exception {
InputStream resultStream = executeQueryService(statement, serviceUri, outputFormat, UTF_8);
- return ResultExtractor.extract(resultStream, UTF_8);
+ return ResultExtractor.extract(resultStream, UTF_8).getResult();
}
protected static boolean isExpected(Exception e, CompilationUnit cUnit) {
@@ -1684,6 +1692,8 @@
List<CompilationUnit> cUnits = testCaseCtx.getTestCase().getCompilationUnit();
for (CompilationUnit cUnit : cUnits) {
List<String> expectedErrors = cUnit.getExpectedError();
+ int expectedWarnCount = cUnit.getExpectedWarn().size();
+ MutableInt actualWarnCount = new MutableInt(0);
LOGGER.info(
"Starting [TEST]: " + testCaseCtx.getTestCase().getFilePath() + "/" + cUnit.getName() + " ... ");
Map<String, Object> variableCtx = new HashMap<>();
@@ -1703,7 +1713,7 @@
try {
if (!testFile.getName().startsWith(DIAGNOSE)) {
executeTestFile(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit,
- queryCount, expectedResultFileCtxs, testFile, actualPath);
+ queryCount, expectedResultFileCtxs, testFile, actualPath, actualWarnCount);
}
} catch (TestLoop loop) {
// rewind the iterator until we find our target
@@ -1735,6 +1745,7 @@
throw new Exception(
"Test \"" + cUnit.getName() + "\" FAILED; expected exception was not thrown...");
}
+ ensureWarnings(actualWarnCount.getValue(), expectedWarnCount, cUnit);
LOGGER.info(
"[TEST]: " + testCaseCtx.getTestCase().getFilePath() + "/" + cUnit.getName() + " PASSED ");
if (passedGroup != null) {
@@ -1757,7 +1768,7 @@
final File file = ctx.getFile();
final String statement = readTestFile(file);
executeTestFile(testCaseCtx, ctx, variableCtx, statement, false, pb, cUnit, new MutableInt(-1),
- Collections.emptyList(), file, null);
+ Collections.emptyList(), file, null, new MutableInt(-1));
}
}
} catch (Exception diagnosticFailure) {
@@ -1954,6 +1965,13 @@
LOGGER.info("Cluster state now " + desiredState);
}
+ protected void ensureWarnings(int actualWarnCount, int expectedWarnCount, CompilationUnit cUnit) throws Exception {
+ if (actualWarnCount < expectedWarnCount) {
+ LOGGER.error("Test {} failed to raise (an) expected warning(s)", cUnit.getName());
+ throw new Exception("Test \"" + cUnit.getName() + "\" FAILED; expected warning(s) was not returned...");
+ }
+ }
+
private void executeStorageCommand(String[] command) throws Exception {
String srcNode = command[0];
String api = command[1];
@@ -2078,13 +2096,27 @@
final URI uri = getQueryServiceUri(testFile);
final InputStream inputStream = executeQueryService(statement, OutputFormat.forCompilationUnit(cUnit), uri,
cUnit.getParameter(), true, responseCharset, null, false);
- return ResultExtractor.extract(inputStream, responseCharset);
+ return ResultExtractor.extract(inputStream, responseCharset).getResult();
}
private URI getQueryServiceUri(String extension) throws URISyntaxException {
return extension.endsWith(AQL) ? getEndpoint(Servlets.QUERY_AQL) : getEndpoint(Servlets.QUERY_SERVICE);
}
+ private void validateWarnings(List<String> actualWarnings, List<String> expectedWarn, MutableInt actualWarnCount)
+ throws Exception {
+ if (actualWarnCount.getValue() > expectedWarn.size()) {
+ throw new IllegalStateException("returned warnings exceeded expected warnings");
+ }
+ for (String actualWarn : actualWarnings) {
+ if (expectedWarn.stream().anyMatch(actualWarn::contains)) {
+ actualWarnCount.increment();
+ } else {
+ throw new Exception("unexpected warning was encountered (" + actualWarn + ")");
+ }
+ }
+ }
+
private static String toQueryServiceHandle(String handle) {
return handle.replace("/aql/", "/service/");
}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.1.query.sqlpp
new file mode 100644
index 0000000..a392016
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.1.query.sqlpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+SELECT VALUE ARRAY_MIN([1, '2']);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.2.query.sqlpp
new file mode 100644
index 0000000..b6eb3ed
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/warnings/min-max-incompatible-types/min-max-incompatible-types.2.query.sqlpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+SELECT VALUE MIN(ds.InternalDetails) FROM Metadata.`Dataset` ds;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.1.adm
new file mode 100644
index 0000000..ec747fa
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.1.adm
@@ -0,0 +1 @@
+null
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.2.adm
new file mode 100644
index 0000000..ec747fa
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/warnings/min-max-incompatible-types/min-max-incompatible-types.2.adm
@@ -0,0 +1 @@
+null
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 349da99..282c03a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -12354,4 +12354,13 @@
</compilation-unit>
</test-case>
</test-group>
+ <test-group name="warnings">
+ <test-case FilePath="warnings" check-warnings="true">
+ <compilation-unit name="min-max-incompatible-types">
+ <output-dir compare="Text">min-max-incompatible-types</output-dir>
+ <expected-warn>ASX0003: Type incompatibility: function min/max gets incompatible input values: bigint and string</expected-warn>
+ <expected-warn>ASX0004: Unsupported type: min/max cannot process input type object</expected-warn>
+ </compilation-unit>
+ </test-case>
+ </test-group>
</test-suite>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/WarningUtil.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/WarningUtil.java
new file mode 100644
index 0000000..e52df21
--- /dev/null
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/WarningUtil.java
@@ -0,0 +1,37 @@
+/*
+ * 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.asterix.common.utils;
+
+import java.io.Serializable;
+
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.api.exceptions.Warning;
+import org.apache.hyracks.api.util.ErrorMessageUtil;
+
+public class WarningUtil {
+
+ private WarningUtil() {
+ }
+
+ public static Warning forAsterix(SourceLocation srcLocation, int code, Serializable... params) {
+ return Warning.of(ErrorCode.ASTERIX, srcLocation, code, ErrorMessageUtil.formatMessage(ErrorCode.ASTERIX, code,
+ ErrorCode.getErrorMessage(code), srcLocation, params));
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
index db31b1c..0b0f8d8 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
@@ -38,6 +38,7 @@
import org.apache.hyracks.algebricks.runtime.evaluators.ColumnAccessEvalFactory;
import org.apache.hyracks.api.context.IHyracksTaskContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
public abstract class AbstractScalarAggregateDescriptor extends AbstractScalarFunctionDynamicDescriptor {
private static final long serialVersionUID = 1L;
@@ -49,6 +50,12 @@
}
@Override
+ public void setSourceLocation(SourceLocation sourceLoc) {
+ super.setSourceLocation(sourceLoc);
+ aggFuncDesc.setSourceLocation(sourceLoc);
+ }
+
+ @Override
public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args)
throws AlgebricksException {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
index e76b8c9..463a676 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java
@@ -20,6 +20,8 @@
import java.io.IOException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.utils.WarningUtil;
import org.apache.asterix.dataflow.data.common.ILogicalBinaryComparator;
import org.apache.asterix.dataflow.data.nontagged.comparators.ComparatorUtil;
import org.apache.asterix.om.types.ATypeTag;
@@ -47,12 +49,14 @@
private final boolean isMin;
private final IAType aggFieldType;
protected final Type type;
+ protected final IHyracksTaskContext context;
protected ATypeTag aggType;
private ILogicalBinaryComparator cmp;
AbstractMinMaxAggregateFunction(IScalarEvaluatorFactory[] args, IHyracksTaskContext context, boolean isMin,
SourceLocation sourceLoc, Type type, IAType aggFieldType) throws HyracksDataException {
super(sourceLoc);
+ this.context = context;
this.eval = args[0].createScalarEvaluator(context);
this.isMin = isMin;
this.aggFieldType = aggFieldType;
@@ -83,29 +87,29 @@
} else if (aggType == ATypeTag.SYSTEM_NULL) {
// First value encountered. Set type, comparator, and initial value.
if (ILogicalBinaryComparator.inequalityUndefined(typeTag)) {
- handleInvalidInput();
+ handleUnsupportedInput(typeTag);
return;
}
aggType = typeTag;
cmp = ComparatorUtil.createLogicalComparator(aggFieldType, aggFieldType, false);
outputVal.assign(inputVal);
} else if (!ATypeHierarchy.isCompatible(typeTag, aggType)) {
- handleInvalidInput();
+ handleIncompatibleInput(typeTag);
} else {
// the two values are compatible non-null/non-missing values
if (aggType == typeTag) {
- compareAndUpdate(cmp, inputVal, outputVal);
+ compareAndUpdate(cmp, inputVal, outputVal, typeTag);
return;
}
if (ATypeHierarchy.canPromote(aggType, typeTag)) {
// switch to new comp & aggregation type (i.e. current min/max is int and new input is double)
castValue(ATypeHierarchy.getTypePromoteComputer(aggType, typeTag), outputVal, tempValForCasting);
outputVal.assign(tempValForCasting);
- compareAndUpdate(cmp, inputVal, outputVal);
+ compareAndUpdate(cmp, inputVal, outputVal, typeTag);
aggType = typeTag;
} else {
castValue(ATypeHierarchy.getTypePromoteComputer(typeTag, aggType), inputVal, tempValForCasting);
- compareAndUpdate(cmp, tempValForCasting, outputVal);
+ compareAndUpdate(cmp, tempValForCasting, outputVal, typeTag);
}
}
}
@@ -151,12 +155,18 @@
return aggType == ATypeTag.NULL;
}
- private void handleInvalidInput() {
- aggType = ATypeTag.NULL;
+ private void handleIncompatibleInput(ATypeTag typeTag) {
+ context.warn(WarningUtil.forAsterix(sourceLoc, ErrorCode.TYPE_INCOMPATIBLE, "min/max", aggType, typeTag));
+ this.aggType = ATypeTag.NULL;
}
- private void compareAndUpdate(ILogicalBinaryComparator c, IPointable newVal, ArrayBackedValueStorage currentVal)
- throws HyracksDataException {
+ private void handleUnsupportedInput(ATypeTag typeTag) {
+ context.warn(WarningUtil.forAsterix(sourceLoc, ErrorCode.TYPE_UNSUPPORTED, "min/max", typeTag));
+ this.aggType = ATypeTag.NULL;
+ }
+
+ private void compareAndUpdate(ILogicalBinaryComparator c, IPointable newVal, ArrayBackedValueStorage currentVal,
+ ATypeTag typeTag) throws HyracksDataException {
// newVal is never NULL/MISSING here. it's already checked up. current value is the first encountered non-null.
ILogicalBinaryComparator.Result result = c.compare(newVal, currentVal);
switch (result) {
@@ -179,7 +189,7 @@
aggType = ATypeTag.NULL;
return;
case INCOMPARABLE:
- handleInvalidInput();
+ handleIncompatibleInput(typeTag);
return;
default:
// EQ, do nothing
diff --git a/asterixdb/asterix-test-framework/src/main/resources/Catalog.xsd b/asterixdb/asterix-test-framework/src/main/resources/Catalog.xsd
index b6bc7bf..5212244 100644
--- a/asterixdb/asterix-test-framework/src/main/resources/Catalog.xsd
+++ b/asterixdb/asterix-test-framework/src/main/resources/Catalog.xsd
@@ -109,6 +109,10 @@
<xs:selector xpath=".//test:expected-error"/>
<xs:field xpath="."/>
</xs:unique>
+ <xs:unique name="unique-expected-warn">
+ <xs:selector xpath=".//test:expected-warn"/>
+ <xs:field xpath="."/>
+ </xs:unique>
</xs:element>
<xs:element ref="test:test-group" minOccurs="0" maxOccurs="unbounded"/>
@@ -180,6 +184,15 @@
</xs:annotation>
</xs:element>
+ <!-- Zero or more expected warnings for this test -->
+
+ <xs:element name="expected-warn" type="xs:string" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>
+ Zero or more expected warnings for this query.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
</xs:sequence>
<!-- This name is always equal to the name of the test case -->
@@ -200,6 +213,7 @@
<xs:attribute name="FilePath" type="test:SimplifiedRelativeFilePath" use="required"/>
<xs:attribute name="category" type="test:category-enum"/>
<xs:attribute name="repeat" type="xs:positiveInteger" default="1" />
+ <xs:attribute name="check-warnings" type="xs:boolean" default="false"/>
</xs:complexType>
<!-- category-enum type -->
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/context/IHyracksTaskContext.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/context/IHyracksTaskContext.java
index 930c31d..abe901a 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/context/IHyracksTaskContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/context/IHyracksTaskContext.java
@@ -25,6 +25,7 @@
import org.apache.hyracks.api.dataflow.TaskAttemptId;
import org.apache.hyracks.api.deployment.DeploymentId;
import org.apache.hyracks.api.exceptions.HyracksException;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.io.IWorkspaceFileFactory;
import org.apache.hyracks.api.job.IOperatorEnvironment;
import org.apache.hyracks.api.job.JobFlag;
@@ -53,9 +54,16 @@
Object getSharedObject();
- public byte[] getJobParameter(byte[] name, int start, int length) throws HyracksException;
+ byte[] getJobParameter(byte[] name, int start, int length) throws HyracksException;
Set<JobFlag> getJobFlags();
IStatsCollector getStatsCollector();
+
+ /**
+ * Adds a warning to this {@link IHyracksTaskContext}
+ *
+ * @param warning
+ */
+ void warn(Warning warning);
}
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/IFormattedException.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/IFormattedException.java
index 994915f..0f873e1 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/IFormattedException.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/IFormattedException.java
@@ -33,4 +33,11 @@
* @return the error code
*/
int getErrorCode();
+
+ /**
+ * Gets the message of this exception
+ *
+ * @return the exception message
+ */
+ String getMessage();
}
\ No newline at end of file
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/SourceLocation.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/SourceLocation.java
index fd3992c..3accc38 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/SourceLocation.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/SourceLocation.java
@@ -19,7 +19,11 @@
package org.apache.hyracks.api.exceptions;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
import java.io.Serializable;
+import java.util.Objects;
public final class SourceLocation implements Serializable {
@@ -38,7 +42,33 @@
return line;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SourceLocation that = (SourceLocation) o;
+ return line == that.line && column == that.column;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(line, column);
+ }
+
public int getColumn() {
return column;
}
-}
+
+ public void writeFields(DataOutput output) throws IOException {
+ output.writeInt(line);
+ output.writeInt(column);
+ }
+
+ public static SourceLocation create(DataInput dataInput) throws IOException {
+ return new SourceLocation(dataInput.readInt(), dataInput.readInt());
+ }
+}
\ No newline at end of file
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/Warning.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/Warning.java
new file mode 100644
index 0000000..78fe3d8
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/Warning.java
@@ -0,0 +1,106 @@
+/*
+ * 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.hyracks.api.exceptions;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Objects;
+
+import org.apache.hyracks.api.util.ErrorMessageUtil;
+
+public class Warning implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final String component;
+ private final SourceLocation srcLocation;
+ private final int code;
+ private final String message;
+
+ private Warning(String component, SourceLocation srcLocation, int code, String message) {
+ this.component = component;
+ this.srcLocation = srcLocation;
+ this.code = code;
+ this.message = message;
+ }
+
+ public static Warning of(String component, SourceLocation srcLocation, int code, String message) {
+ Objects.requireNonNull(srcLocation, "warnings must have source location");
+ return new Warning(component, srcLocation, code, message);
+ }
+
+ public static Warning forHyracks(SourceLocation srcLocation, int code, Serializable... params) {
+ return Warning.of(ErrorCode.HYRACKS, srcLocation, code, ErrorMessageUtil.formatMessage(ErrorCode.HYRACKS, code,
+ ErrorCode.getErrorMessage(code), srcLocation, params));
+ }
+
+ public String getComponent() {
+ return component;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public SourceLocation getSourceLocation() {
+ return srcLocation;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Warning warning = (Warning) o;
+ return Objects.equals(message, warning.message);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(message);
+ }
+
+ public void writeFields(DataOutput output) throws IOException {
+ output.writeUTF(component);
+ output.writeInt(code);
+ output.writeUTF(message);
+ srcLocation.writeFields(output);
+ }
+
+ public static Warning create(DataInput input) throws IOException {
+ String comp = input.readUTF();
+ int code = input.readInt();
+ String msg = input.readUTF();
+ return new Warning(comp, SourceLocation.create(input), code, msg);
+ }
+
+ @Override
+ public String toString() {
+ return "Warning{" + "component='" + component + '\'' + ", srcLocation=" + srcLocation + ", code=" + code
+ + ", message='" + message + '\'' + '}';
+ }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/job/profiling/om/TaskProfile.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/job/profiling/om/TaskProfile.java
index f977654..2a20624 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/job/profiling/om/TaskProfile.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/job/profiling/om/TaskProfile.java
@@ -22,10 +22,13 @@
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import org.apache.hyracks.api.dataflow.TaskAttemptId;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.job.profiling.IStatsCollector;
import org.apache.hyracks.api.partitions.PartitionId;
import org.apache.hyracks.control.common.job.profiling.StatsCollector;
@@ -44,6 +47,8 @@
private IStatsCollector statsCollector;
+ private Set<Warning> warnings;
+
public static TaskProfile create(DataInput dis) throws IOException {
TaskProfile taskProfile = new TaskProfile();
taskProfile.readFields(dis);
@@ -55,10 +60,11 @@
}
public TaskProfile(TaskAttemptId taskAttemptId, Map<PartitionId, PartitionProfile> partitionSendProfile,
- IStatsCollector statsCollector) {
+ IStatsCollector statsCollector, Set<Warning> warnings) {
this.taskAttemptId = taskAttemptId;
this.partitionSendProfile = new HashMap<>(partitionSendProfile);
this.statsCollector = statsCollector;
+ this.warnings = warnings;
}
public TaskAttemptId getTaskId() {
@@ -115,6 +121,10 @@
return statsCollector;
}
+ public Set<Warning> getWarnings() {
+ return warnings;
+ }
+
@Override
public void readFields(DataInput input) throws IOException {
super.readFields(input);
@@ -127,6 +137,8 @@
partitionSendProfile.put(key, value);
}
statsCollector = StatsCollector.create(input);
+ warnings = new HashSet<>();
+ deserializeWarnings(input, warnings);
}
@Override
@@ -139,5 +151,20 @@
entry.getValue().writeFields(output);
}
statsCollector.writeFields(output);
+ serializeWarnings(output);
+ }
+
+ private void serializeWarnings(DataOutput output) throws IOException {
+ output.writeInt(warnings.size());
+ for (Warning warning : warnings) {
+ warning.writeFields(output);
+ }
+ }
+
+ private static void deserializeWarnings(DataInput input, Set<Warning> warnings) throws IOException {
+ int warnCount = input.readInt();
+ for (int i = 0; i < warnCount; i++) {
+ warnings.add(Warning.create(input));
+ }
}
}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Joblet.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Joblet.java
index 1d2b77a..8f02f2d 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Joblet.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Joblet.java
@@ -206,7 +206,7 @@
counterMap.forEach((key, value) -> counters.put(key, value.get()));
for (Task task : taskMap.values()) {
TaskProfile taskProfile = new TaskProfile(task.getTaskAttemptId(),
- new Hashtable<>(task.getPartitionSendProfile()), new StatsCollector());
+ new Hashtable<>(task.getPartitionSendProfile()), new StatsCollector(), task.getWarnings());
task.dumpProfile(taskProfile);
jProfile.getTaskProfiles().put(task.getTaskAttemptId(), taskProfile);
}
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Task.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Task.java
index 252fe97..b8c1496 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Task.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/Task.java
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
@@ -46,6 +47,7 @@
import org.apache.hyracks.api.deployment.DeploymentId;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.exceptions.HyracksException;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.io.FileReference;
import org.apache.hyracks.api.io.IIOManager;
import org.apache.hyracks.api.io.IWorkspaceFileFactory;
@@ -115,6 +117,8 @@
private volatile boolean completed = false;
+ private final Set<Warning> warnings;
+
public Task(Joblet joblet, Set<JobFlag> jobFlags, TaskAttemptId taskId, String displayName,
ExecutorService executor, NodeControllerService ncs,
List<List<PartitionChannel>> inputChannelsFromConnectors) {
@@ -133,6 +137,7 @@
this.ncs = ncs;
this.inputChannelsFromConnectors = inputChannelsFromConnectors;
statsCollector = new StatsCollector();
+ warnings = ConcurrentHashMap.newKeySet();
}
public void setTaskRuntime(IPartitionCollector[] collectors, IOperatorNodePushable operator) {
@@ -468,10 +473,19 @@
return statsCollector;
}
+ @Override
+ public void warn(Warning warning) {
+ warnings.add(warning);
+ }
+
public boolean isCompleted() {
return completed;
}
+ public Set<Warning> getWarnings() {
+ return warnings;
+ }
+
@Override
public String toString() {
return "{ \"class\" : \"" + getClass().getSimpleName() + "\", \"node\" : \"" + ncs.getId() + "\" \"jobId\" : \""
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/NotifyTaskCompleteWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/NotifyTaskCompleteWork.java
index 554c660..b8c5030 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/NotifyTaskCompleteWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-nc/src/main/java/org/apache/hyracks/control/nc/work/NotifyTaskCompleteWork.java
@@ -38,8 +38,8 @@
@Override
public void run() {
- TaskProfile taskProfile =
- new TaskProfile(task.getTaskAttemptId(), task.getPartitionSendProfile(), task.getStatsCollector());
+ TaskProfile taskProfile = new TaskProfile(task.getTaskAttemptId(), task.getPartitionSendProfile(),
+ task.getStatsCollector(), task.getWarnings());
try {
ncs.getClusterController(task.getJobletContext().getJobId().getCcId()).notifyTaskComplete(
task.getJobletContext().getJobId(), task.getTaskAttemptId(), ncs.getId(), taskProfile);
diff --git a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestTaskContext.java b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestTaskContext.java
index 174d5cd..e328e3d 100644
--- a/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestTaskContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-test-support/src/main/java/org/apache/hyracks/test/support/TestTaskContext.java
@@ -22,6 +22,7 @@
import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -32,6 +33,7 @@
import org.apache.hyracks.api.dataflow.state.IStateObject;
import org.apache.hyracks.api.deployment.DeploymentId;
import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.io.FileReference;
import org.apache.hyracks.api.io.IIOManager;
import org.apache.hyracks.api.job.JobFlag;
@@ -49,6 +51,7 @@
private Map<Object, IStateObject> stateObjectMap = new HashMap<>();
private Object sharedObject;
private final IStatsCollector statsCollector = new StatsCollector();
+ private final Set<Warning> warnings = new HashSet<>();
public TestTaskContext(TestJobletContext jobletContext, TaskAttemptId taskId) {
this.jobletContext = jobletContext;
@@ -175,4 +178,9 @@
public IStatsCollector getStatsCollector() {
return statsCollector;
}
+
+ @Override
+ public void warn(Warning warning) {
+ warnings.add(warning);
+ }
}
diff --git a/hyracks-fullstack/hyracks/hyracks-tests/hyracks-storage-am-lsm-invertedindex-test/src/test/java/org/apache/hyracks/storage/am/lsm/invertedindex/util/LSMInvertedIndexTestUtils.java b/hyracks-fullstack/hyracks/hyracks-tests/hyracks-storage-am-lsm-invertedindex-test/src/test/java/org/apache/hyracks/storage/am/lsm/invertedindex/util/LSMInvertedIndexTestUtils.java
index 87c2d8f..9021fc5 100644
--- a/hyracks-fullstack/hyracks/hyracks-tests/hyracks-storage-am-lsm-invertedindex-test/src/test/java/org/apache/hyracks/storage/am/lsm/invertedindex/util/LSMInvertedIndexTestUtils.java
+++ b/hyracks-fullstack/hyracks/hyracks-tests/hyracks-storage-am-lsm-invertedindex-test/src/test/java/org/apache/hyracks/storage/am/lsm/invertedindex/util/LSMInvertedIndexTestUtils.java
@@ -48,6 +48,7 @@
import org.apache.hyracks.api.exceptions.ErrorCode;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.exceptions.HyracksException;
+import org.apache.hyracks.api.exceptions.Warning;
import org.apache.hyracks.api.io.FileReference;
import org.apache.hyracks.api.io.IIOManager;
import org.apache.hyracks.api.job.JobFlag;
@@ -764,6 +765,11 @@
public IStatsCollector getStatsCollector() {
return null;
}
+
+ @Override
+ public void warn(Warning warning) {
+ // no-op
+ }
}
}