Repository: prestodb/presto
Issue: Bad error when aggregation function is used in lambda expression

```sql
SELECT transform(ARRAY[1], x -> max(x))
```

fails with:

```
java.lang.IllegalArgumentException: function must be of type com.facebook.presto.metadata.SqlScalarFunction, not com.facebook.presto.operator.aggregation.MaxAggregationFunction
	at com.google.common.base.Preconditions.checkArgument(Preconditions.java:145)
	at com.facebook.presto.util.Types.checkType(Types.java:28)
	at com.facebook.presto.metadata.FunctionRegistry$2.load(FunctionRegistry.java:348)
	at com.facebook.presto.metadata.FunctionRegistry$2.load(FunctionRegistry.java:342)
	at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
	at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4830)
	at com.facebook.presto.metadata.FunctionRegistry.getScalarFunctionImplementation(FunctionRegistry.java:862)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitCall(ExpressionOptimizer.java:146)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitCall(ExpressionOptimizer.java:72)
	at com.facebook.presto.sql.relational.CallExpression.accept(CallExpression.java:88)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitLambda(ExpressionOptimizer.java:191)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitLambda(ExpressionOptimizer.java:72)
	at com.facebook.presto.sql.relational.LambdaDefinitionExpression.accept(LambdaDefinitionExpression.java:93)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.lambda$visitCall$3(ExpressionOptimizer.java:151)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.Iterator.forEachRemaining(Iterator.java:116)
	at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitCall(ExpressionOptimizer.java:152)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer$Visitor.visitCall(ExpressionOptimizer.java:72)
	at com.facebook.presto.sql.relational.CallExpression.accept(CallExpression.java:88)
	at com.facebook.presto.sql.relational.optimizer.ExpressionOptimizer.optimize(ExpressionOptimizer.java:69)
	at com.facebook.presto.sql.relational.SqlToRowExpressionTranslator.translate(SqlToRowExpressionTranslator.java:146)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.toRowExpression(LocalExecutionPlanner.java:1119)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.lambda$visitScanFilterAndProject$5(LocalExecutionPlanner.java:1023)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.visitScanFilterAndProject(LocalExecutionPlanner.java:1024)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.visitProject(LocalExecutionPlanner.java:951)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.visitProject(LocalExecutionPlanner.java:536)
	at com.facebook.presto.sql.planner.plan.ProjectNode.accept(ProjectNode.java:81)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.visitOutput(LocalExecutionPlanner.java:575)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner$Visitor.visitOutput(LocalExecutionPlanner.java:536)
	at com.facebook.presto.sql.planner.plan.OutputNode.accept(OutputNode.java:81)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner.plan(LocalExecutionPlanner.java:351)
	at com.facebook.presto.sql.planner.LocalExecutionPlanner.plan(LocalExecutionPlanner.java:290)
	at com.facebook.presto.execution.SqlTaskExecution.<init>(SqlTaskExecution.java:161)
	at com.facebook.presto.execution.SqlTaskExecution.createSqlTaskExecution(SqlTaskExecution.java:120)
	at com.facebook.presto.execution.SqlTaskExecutionFactory.create(SqlTaskExecutionFactory.java:70)
	at com.facebook.presto.execution.SqlTask.updateTask(SqlTask.java:315)
	at com.facebook.presto.execution.SqlTaskManager.updateTask(SqlTaskManager.java:324)
	at com.facebook.presto.server.TaskResource.createOrUpdateTask(TaskResource.java:125)
	at sun.reflect.GeneratedMethodAccessor154.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
	at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
	at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1689)
	at io.airlift.http.server.TraceTokenFilter.doFilter(TraceTokenFilter.java:63)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
	at io.airlift.http.server.TimingFilter.doFilter(TimingFilter.java:52)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:395)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
	at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:169)
	at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
	at org.eclipse.jetty.server.Server.handle(Server.java:523)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
	at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
	at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
	at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
	at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
	at java.lang.Thread.run(Thread.java:745)
```

Changed files:
presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregateExtractor.java
presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java
presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java
presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregationAnalyzer.java
presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java

diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregateExtractor.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregateExtractor.java
index 3cca1c8353..488bc316e4 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregateExtractor.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregateExtractor.java
@@ -13,7 +13,7 @@
  */
 package com.facebook.presto.sql.analyzer;
 
-import com.facebook.presto.metadata.Metadata;
+import com.facebook.presto.metadata.FunctionRegistry;
 import com.facebook.presto.sql.tree.DefaultExpressionTraversalVisitor;
 import com.facebook.presto.sql.tree.FunctionCall;
 import com.google.common.collect.ImmutableList;
@@ -25,21 +25,19 @@ import static java.util.Objects.requireNonNull;
 class AggregateExtractor
         extends DefaultExpressionTraversalVisitor<Void, Void>
 {
-    private final Metadata metadata;
+    private final FunctionRegistry functionRegistry;
 
     private final ImmutableList.Builder<FunctionCall> aggregates = ImmutableList.builder();
 
-    public AggregateExtractor(Metadata metadata)
+    public AggregateExtractor(FunctionRegistry functionRegistry)
     {
-        requireNonNull(metadata, "metadata is null");
-
-        this.metadata = metadata;
+        this.functionRegistry = requireNonNull(functionRegistry, "functionRegistry is null");
     }
 
     @Override
     protected Void visitFunctionCall(FunctionCall node, Void context)
     {
-        if (metadata.isAggregationFunction(node.getName()) && !node.getWindow().isPresent()) {
+        if (functionRegistry.isAggregationFunction(node.getName()) && !node.getWindow().isPresent()) {
             aggregates.add(node);
             return null;
         }
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregationAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregationAnalyzer.java
index 510424e048..a025d8a802 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregationAnalyzer.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/AggregationAnalyzer.java
@@ -278,7 +278,7 @@ class AggregationAnalyzer
         protected Boolean visitFunctionCall(FunctionCall node, Void context)
         {
             if (!node.getWindow().isPresent() && metadata.isAggregationFunction(node.getName())) {
-                AggregateExtractor aggregateExtractor = new AggregateExtractor(metadata);
+                AggregateExtractor aggregateExtractor = new AggregateExtractor(metadata.getFunctionRegistry());
                 WindowFunctionExtractor windowExtractor = new WindowFunctionExtractor();
 
                 for (Expression argument : node.getArguments()) {
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java
index 947ae045d9..0b8aefaf31 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java
@@ -14,6 +14,7 @@
 package com.facebook.presto.sql.analyzer;
 
 import com.facebook.presto.Session;
+import com.facebook.presto.metadata.FunctionRegistry;
 import com.facebook.presto.metadata.Metadata;
 import com.facebook.presto.security.AccessControl;
 import com.facebook.presto.sql.parser.SqlParser;
@@ -68,9 +69,9 @@ public class Analyzer
         return analysis;
     }
 
-    static void verifyNoAggregatesOrWindowFunctions(Metadata metadata, Expression predicate, String clause)
+    static void verifyNoAggregatesOrWindowFunctions(FunctionRegistry functionRegistry, Expression predicate, String clause)
     {
-        AggregateExtractor extractor = new AggregateExtractor(metadata);
+        AggregateExtractor extractor = new AggregateExtractor(functionRegistry);
         extractor.process(predicate, null);
 
         WindowFunctionExtractor windowExtractor = new WindowFunctionExtractor();
@@ -79,7 +80,7 @@ public class Analyzer
         List<FunctionCall> found = ImmutableList.copyOf(Iterables.concat(extractor.getAggregates(), windowExtractor.getWindowFunctions()));
 
         if (!found.isEmpty()) {
-            throw new SemanticException(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, predicate, "%s clause cannot contain aggregations or window functions: %s", clause, found);
+            throw new SemanticException(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, predicate, "%s cannot contain aggregations or window functions: %s", clause, found);
         }
     }
 }
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java
index 2a09ff69d9..a225c9497d 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java
@@ -121,6 +121,7 @@ import static com.facebook.presto.spi.type.TinyintType.TINYINT;
 import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature;
 import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY;
 import static com.facebook.presto.spi.type.VarcharType.VARCHAR;
+import static com.facebook.presto.sql.analyzer.Analyzer.verifyNoAggregatesOrWindowFunctions;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.EXPRESSION_NOT_CONSTANT;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_LITERAL;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_PARAMETER_USAGE;
@@ -783,6 +784,7 @@ public class ExpressionAnalyzer
             for (Expression expression : node.getArguments()) {
                 if (expression instanceof LambdaExpression) {
                     LambdaExpression lambdaExpression = (LambdaExpression) expression;
+                    verifyNoAggregatesOrWindowFunctions(functionRegistry, lambdaExpression.getBody(), "Lambda expression");
 
                     // captures are not supported for now, use empty tuple descriptor
                     Expression lambdaBody = lambdaExpression.getBody();
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java
index 608bf8378d..4470a22e2b 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java
@@ -944,7 +944,7 @@ class StatementAnalyzer
                 analysis.addCoercion(expression, BOOLEAN, false);
             }
 
-            Analyzer.verifyNoAggregatesOrWindowFunctions(metadata, expression, "JOIN");
+            Analyzer.verifyNoAggregatesOrWindowFunctions(metadata.getFunctionRegistry(), expression, "JOIN clause");
 
             // expressionInterpreter/optimizer only understands a subset of expression types
             // TODO: remove this when the new expression tree is implemented
@@ -1514,7 +1514,7 @@ class StatementAnalyzer
                 groupByExpression = groupingColumn;
             }
 
-            Analyzer.verifyNoAggregatesOrWindowFunctions(metadata, groupByExpression, "GROUP BY");
+            Analyzer.verifyNoAggregatesOrWindowFunctions(metadata.getFunctionRegistry(), groupByExpression, "GROUP BY clause");
             Type type = analysis.getType(groupByExpression);
             if (!type.isComparable()) {
                 throw new SemanticException(TYPE_MISMATCH, node, "%s is not comparable, and therefore cannot be used in GROUP BY", type);
@@ -1631,7 +1631,7 @@ class StatementAnalyzer
 
     public void analyzeWhere(Node node, Scope scope, Expression predicate)
     {
-        Analyzer.verifyNoAggregatesOrWindowFunctions(metadata, predicate, "WHERE");
+        Analyzer.verifyNoAggregatesOrWindowFunctions(metadata.getFunctionRegistry(), predicate, "WHERE clause");
 
         ExpressionAnalysis expressionAnalysis = analyzeExpression(predicate, scope);
         analysis.recordSubqueries(node, expressionAnalysis);
@@ -1664,7 +1664,7 @@ class StatementAnalyzer
             Set<Expression> columnReferences,
             List<Expression> expressions)
     {
-        AggregateExtractor extractor = new AggregateExtractor(metadata);
+        AggregateExtractor extractor = new AggregateExtractor(metadata.getFunctionRegistry());
         for (Expression expression : expressions) {
             extractor.process(expression);
         }
@@ -1690,7 +1690,7 @@ class StatementAnalyzer
 
     private boolean hasAggregates(QuerySpecification node)
     {
-        AggregateExtractor extractor = new AggregateExtractor(metadata);
+        AggregateExtractor extractor = new AggregateExtractor(metadata.getFunctionRegistry());
 
         node.getSelect()
                 .getSelectItems().stream()
diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
index 5d2e0b5a37..900c96af74 100644
--- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
+++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
@@ -203,6 +203,12 @@ public abstract class AbstractTestQueries
         assertQuery("SELECT apply((SELECT 10), i -> i * i)", "SELECT 100");
     }
 
+    @Test
+    public void testLambdaWithAggregation()
+    {
+        assertQueryFails("SELECT transform(ARRAY[1], x -> max(x))", ".* Lambda expression cannot contain aggregations or window functions: .*");
+    }
+
     @Test
     public void testNonDeterministicFilter()
     {