ignore bySegment query context for SQL queries (#11352)

* ignore bySegment query context for SQL queries

* revert unintended change
diff --git a/processing/src/main/java/org/apache/druid/query/QueryContexts.java b/processing/src/main/java/org/apache/druid/query/QueryContexts.java
index f6528a6d..4979d7c 100644
--- a/processing/src/main/java/org/apache/druid/query/QueryContexts.java
+++ b/processing/src/main/java/org/apache/druid/query/QueryContexts.java
@@ -65,6 +65,7 @@
   public static final String RETURN_PARTIAL_RESULTS_KEY = "returnPartialResults";
   public static final String USE_CACHE_KEY = "useCache";
   public static final String SECONDARY_PARTITION_PRUNING_KEY = "secondaryPartitionPruning";
+  public static final String BY_SEGMENT_KEY = "bySegment";
 
   public static final boolean DEFAULT_BY_SEGMENT = false;
   public static final boolean DEFAULT_POPULATE_CACHE = true;
@@ -139,7 +140,7 @@
 
   public static <T> boolean isBySegment(Query<T> query, boolean defaultValue)
   {
-    return parseBoolean(query, "bySegment", defaultValue);
+    return parseBoolean(query, BY_SEGMENT_KEY, defaultValue);
   }
 
   public static <T> boolean isPopulateCache(Query<T> query)
diff --git a/processing/src/test/java/org/apache/druid/query/groupby/DefaultGroupByQueryMetricsTest.java b/processing/src/test/java/org/apache/druid/query/groupby/DefaultGroupByQueryMetricsTest.java
index 9e8d4ae..bc2109e 100644
--- a/processing/src/test/java/org/apache/druid/query/groupby/DefaultGroupByQueryMetricsTest.java
+++ b/processing/src/test/java/org/apache/druid/query/groupby/DefaultGroupByQueryMetricsTest.java
@@ -26,6 +26,7 @@
 import org.apache.druid.query.CachingEmitter;
 import org.apache.druid.query.DefaultQueryMetricsTest;
 import org.apache.druid.query.DruidMetrics;
+import org.apache.druid.query.QueryContexts;
 import org.apache.druid.query.QueryRunnerTestHelper;
 import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
 import org.apache.druid.query.dimension.ExtractionDimensionSpec;
@@ -69,7 +70,7 @@
         )).setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT, new LongSumAggregatorFactory("idx", "index"))
         .setGranularity(new PeriodGranularity(new Period("P1M"), null, null))
         .setDimFilter(new SelectorDimFilter("quality", "mezzanine", null))
-        .setContext(ImmutableMap.of("bySegment", true));
+        .setContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true));
     GroupByQuery query = builder.build();
     queryMetrics.query(query);
 
@@ -87,7 +88,7 @@
     Assert.assertEquals("true", actualEvent.get("hasFilters"));
     Assert.assertEquals(expectedInterval.toDuration().toString(), actualEvent.get("duration"));
     Assert.assertEquals("", actualEvent.get(DruidMetrics.ID));
-    Assert.assertEquals(ImmutableMap.of("bySegment", true), actualEvent.get("context"));
+    Assert.assertEquals(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true), actualEvent.get("context"));
 
     // GroupBy-specific dimensions
     Assert.assertEquals("1", actualEvent.get("numDimensions"));
diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
index b20fe00..bfd40ce 100644
--- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
+++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java
@@ -7916,7 +7916,7 @@
         .setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT, new LongSumAggregatorFactory("idx", "index"))
         .setGranularity(new PeriodGranularity(new Period("P1M"), null, null))
         .setDimFilter(new SelectorDimFilter("quality", "mezzanine", null))
-        .setContext(ImmutableMap.of("bySegment", true));
+        .setContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true));
     final GroupByQuery fullQuery = builder.build();
 
     int segmentCount = 32;
@@ -7984,7 +7984,7 @@
         )).setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT, new LongSumAggregatorFactory("idx", "index"))
         .setGranularity(new PeriodGranularity(new Period("P1M"), null, null))
         .setDimFilter(new SelectorDimFilter("quality", "mezzanine", null))
-        .setContext(ImmutableMap.of("bySegment", true));
+        .setContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true));
     final GroupByQuery fullQuery = builder.build();
 
     int segmentCount = 32;
@@ -8051,7 +8051,7 @@
         )).setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT, new LongSumAggregatorFactory("idx", "index"))
         .setGranularity(new PeriodGranularity(new Period("P1M"), null, null))
         .setDimFilter(new SelectorDimFilter("quality", "mezzanine", null))
-        .overrideContext(ImmutableMap.of("bySegment", true));
+        .overrideContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true));
     final GroupByQuery fullQuery = builder.build();
 
     int segmentCount = 32;
@@ -8581,7 +8581,7 @@
         .setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT, new LongSumAggregatorFactory("idx", "index"))
         .setGranularity(new PeriodGranularity(new Period("P1M"), null, null))
         .setDimFilter(superFilter)
-        .overrideContext(ImmutableMap.of("bySegment", true));
+        .overrideContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true));
     final GroupByQuery fullQuery = builder.build();
 
     int segmentCount = 32;
diff --git a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java
index 00846eb..7d226fb 100644
--- a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java
+++ b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java
@@ -35,6 +35,7 @@
 import org.apache.druid.query.Druids;
 import org.apache.druid.query.FinalizeResultsQueryRunner;
 import org.apache.druid.query.Query;
+import org.apache.druid.query.QueryContexts;
 import org.apache.druid.query.QueryPlus;
 import org.apache.druid.query.QueryRunner;
 import org.apache.druid.query.QueryRunnerFactory;
@@ -915,7 +916,7 @@
 
     TestHelper.assertExpectedObjects(
         ImmutableList.of(bySegmentResult, bySegmentResult),
-        myRunner.run(QueryPlus.wrap(testQuery.withOverriddenContext(ImmutableMap.of("bySegment", true)))),
+        myRunner.run(QueryPlus.wrap(testQuery.withOverriddenContext(ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, true)))),
         "failed SegmentMetadata bySegment query"
     );
     exec.shutdownNow();
diff --git a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java
index eb8709d..0f1c95c 100644
--- a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java
+++ b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java
@@ -43,6 +43,7 @@
 import org.apache.druid.query.BySegmentResultValue;
 import org.apache.druid.query.BySegmentResultValueClass;
 import org.apache.druid.query.FinalizeResultsQueryRunner;
+import org.apache.druid.query.QueryContexts;
 import org.apache.druid.query.QueryPlus;
 import org.apache.druid.query.QueryRunner;
 import org.apache.druid.query.QueryRunnerTestHelper;
@@ -1148,7 +1149,7 @@
   {
 
     final HashMap<String, Object> specialContext = new HashMap<String, Object>();
-    specialContext.put("bySegment", "true");
+    specialContext.put(QueryContexts.BY_SEGMENT_KEY, "true");
     TopNQuery query = new TopNQueryBuilder()
         .dataSource(QueryRunnerTestHelper.DATA_SOURCE)
         .granularity(QueryRunnerTestHelper.ALL_GRAN)
@@ -3639,7 +3640,7 @@
             QueryRunnerTestHelper.ADD_ROWS_INDEX_CONSTANT,
             QueryRunnerTestHelper.DEPENDENT_POST_AGG
         )
-        .context(ImmutableMap.of("finalize", true, "bySegment", true))
+        .context(ImmutableMap.of(QueryContexts.FINALIZE_KEY, true, QueryContexts.BY_SEGMENT_KEY, true))
         .build();
     TopNResultValue topNResult = new TopNResultValue(
         Arrays.<Map<String, Object>>asList(
diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java
index 369ba2a..4481849 100644
--- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java
+++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java
@@ -316,7 +316,7 @@
       if (populateCache) {
         // prevent down-stream nodes from caching results as well if we are populating the cache
         contextBuilder.put(CacheConfig.POPULATE_CACHE, false);
-        contextBuilder.put("bySegment", true);
+        contextBuilder.put(QueryContexts.BY_SEGMENT_KEY, true);
       }
       return contextBuilder.build();
     }
diff --git a/server/src/test/java/org/apache/druid/client/CachingClusteredClientCacheKeyManagerTest.java b/server/src/test/java/org/apache/druid/client/CachingClusteredClientCacheKeyManagerTest.java
index 7970c8c..c49b02c 100644
--- a/server/src/test/java/org/apache/druid/client/CachingClusteredClientCacheKeyManagerTest.java
+++ b/server/src/test/java/org/apache/druid/client/CachingClusteredClientCacheKeyManagerTest.java
@@ -25,6 +25,7 @@
 import org.apache.druid.client.selector.ServerSelector;
 import org.apache.druid.query.CacheStrategy;
 import org.apache.druid.query.Query;
+import org.apache.druid.query.QueryContexts;
 import org.apache.druid.query.planning.DataSourceAnalysis;
 import org.apache.druid.segment.join.JoinableFactoryWrapper;
 import org.apache.druid.timeline.DataSegment;
@@ -65,7 +66,7 @@
   public void setup()
   {
     expect(strategy.computeCacheKey(query)).andReturn(QUERY_CACHE_KEY).anyTimes();
-    expect(query.getContextValue("bySegment")).andReturn(false).anyTimes();
+    expect(query.getContextValue(QueryContexts.BY_SEGMENT_KEY)).andReturn(false).anyTimes();
   }
 
   @After
@@ -201,7 +202,7 @@
   {
     expect(dataSourceAnalysis.isJoin()).andReturn(false);
     reset(query);
-    expect(query.getContextValue("bySegment")).andReturn(true).anyTimes();
+    expect(query.getContextValue(QueryContexts.BY_SEGMENT_KEY)).andReturn(true).anyTimes();
     replayAll();
     CachingClusteredClient.CacheKeyManager<Object> keyManager = makeKeyManager();
     Set<SegmentServerSelector> selectors = ImmutableSet.of(
@@ -270,7 +271,7 @@
   public void testSegmentQueryCacheKey_noCachingIfBySegment()
   {
     reset(query);
-    expect(query.getContextValue("bySegment")).andReturn(true).anyTimes();
+    expect(query.getContextValue(QueryContexts.BY_SEGMENT_KEY)).andReturn(true).anyTimes();
     replayAll();
     byte[] cacheKey = makeKeyManager().computeSegmentLevelQueryCacheKey();
     Assert.assertNull(cacheKey);
diff --git a/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java b/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java
index 5b96d7a..cda0170 100644
--- a/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java
+++ b/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java
@@ -2299,11 +2299,11 @@
         QueryPlus capturedQueryPlus = (QueryPlus) queryCapture.getValue();
         Query capturedQuery = capturedQueryPlus.getQuery();
         if (expectBySegment) {
-          Assert.assertEquals(true, capturedQuery.getContextValue("bySegment"));
+          Assert.assertEquals(true, capturedQuery.getContextValue(QueryContexts.BY_SEGMENT_KEY));
         } else {
           Assert.assertTrue(
-              capturedQuery.getContextValue("bySegment") == null ||
-              capturedQuery.getContextValue("bySegment").equals(false)
+              capturedQuery.getContextValue(QueryContexts.BY_SEGMENT_KEY) == null ||
+              capturedQuery.getContextValue(QueryContexts.BY_SEGMENT_KEY).equals(false)
           );
         }
       }
diff --git a/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java b/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
index c09de8d..c30aba0 100644
--- a/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
+++ b/sql/src/main/java/org/apache/druid/sql/SqlLifecycle.java
@@ -37,6 +37,7 @@
 import org.apache.druid.java.util.common.logger.Logger;
 import org.apache.druid.java.util.emitter.service.ServiceEmitter;
 import org.apache.druid.java.util.emitter.service.ServiceMetricEvent;
+import org.apache.druid.query.QueryContexts;
 import org.apache.druid.query.QueryInterruptedException;
 import org.apache.druid.query.QueryTimeoutException;
 import org.apache.druid.server.QueryStats;
@@ -151,6 +152,11 @@
     if (queryContext != null) {
       newContext.putAll(queryContext);
     }
+    // "bySegment" results are never valid to use with SQL because the result format is incompatible
+    // so, overwrite any user specified context to avoid exceptions down the line
+    if (newContext.remove(QueryContexts.BY_SEGMENT_KEY) != null) {
+      log.warn("'bySegment' results are not supported for SQL queries, ignoring query context parameter");
+    }
     newContext.computeIfAbsent(PlannerContext.CTX_SQL_QUERY_ID, k -> UUID.randomUUID().toString());
     return newContext;
   }
@@ -459,6 +465,22 @@
     }
   }
 
+  @VisibleForTesting
+  public State getState()
+  {
+    synchronized (lock) {
+      return state;
+    }
+  }
+
+  @VisibleForTesting
+  public Map<String, Object> getQueryContext()
+  {
+    synchronized (lock) {
+      return queryContext;
+    }
+  }
+
   @GuardedBy("lock")
   private void transition(final State from, final State to)
   {
diff --git a/sql/src/test/java/org/apache/druid/sql/SqlLifecycleTest.java b/sql/src/test/java/org/apache/druid/sql/SqlLifecycleTest.java
new file mode 100644
index 0000000..6fd383b
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/SqlLifecycleTest.java
@@ -0,0 +1,279 @@
+/*
+ * 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.druid.sql;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.apache.calcite.avatica.SqlType;
+import org.apache.calcite.avatica.remote.TypedValue;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.tools.RelConversionException;
+import org.apache.calcite.tools.ValidationException;
+import org.apache.druid.java.util.common.guava.Sequences;
+import org.apache.druid.java.util.emitter.service.ServiceEmitter;
+import org.apache.druid.java.util.emitter.service.ServiceEventBuilder;
+import org.apache.druid.query.QueryContexts;
+import org.apache.druid.server.log.RequestLogger;
+import org.apache.druid.server.security.Access;
+import org.apache.druid.server.security.AuthConfig;
+import org.apache.druid.sql.calcite.planner.DruidPlanner;
+import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.planner.PlannerFactory;
+import org.apache.druid.sql.calcite.planner.PlannerResult;
+import org.apache.druid.sql.calcite.planner.PrepareResult;
+import org.apache.druid.sql.calcite.planner.ValidationResult;
+import org.apache.druid.sql.calcite.util.CalciteTests;
+import org.apache.druid.sql.http.SqlParameter;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class SqlLifecycleTest
+{
+  private PlannerFactory plannerFactory;
+  private ServiceEmitter serviceEmitter;
+  private RequestLogger requestLogger;
+  private SqlLifecycleFactory sqlLifecycleFactory;
+
+  @Before
+  public void setup()
+  {
+    this.plannerFactory = EasyMock.createMock(PlannerFactory.class);
+    this.serviceEmitter = EasyMock.createMock(ServiceEmitter.class);
+    this.requestLogger = EasyMock.createMock(RequestLogger.class);
+    this.sqlLifecycleFactory = new SqlLifecycleFactory(plannerFactory, serviceEmitter, requestLogger);
+  }
+
+  @Test
+  public void testIgnoredQueryContextParametersAreIgnored()
+  {
+    SqlLifecycle lifecycle = sqlLifecycleFactory.factorize();
+    final String sql = "select 1 + ?";
+    final Map<String, Object> queryContext = ImmutableMap.of(QueryContexts.BY_SEGMENT_KEY, "true");
+    lifecycle.initialize(sql, queryContext);
+    Assert.assertEquals(SqlLifecycle.State.INITIALIZED, lifecycle.getState());
+    Assert.assertEquals(1, lifecycle.getQueryContext().size());
+    // should contain only query id, not bySegment since it is not valid for SQL
+    Assert.assertTrue(lifecycle.getQueryContext().containsKey(PlannerContext.CTX_SQL_QUERY_ID));
+  }
+
+  @Test
+  public void testStateTransition()
+      throws ValidationException, SqlParseException, RelConversionException, IOException
+  {
+    SqlLifecycle lifecycle = sqlLifecycleFactory.factorize();
+    final String sql = "select 1 + ?";
+    final Map<String, Object> queryContext = Collections.emptyMap();
+    Assert.assertEquals(SqlLifecycle.State.NEW, lifecycle.getState());
+
+    // test initialize
+    lifecycle.initialize(sql, queryContext);
+    Assert.assertEquals(SqlLifecycle.State.INITIALIZED, lifecycle.getState());
+    List<TypedValue> parameters = ImmutableList.of(new SqlParameter(SqlType.BIGINT, 1L).getTypedValue());
+    lifecycle.setParameters(parameters);
+    // setting parameters should not change the state
+    Assert.assertEquals(SqlLifecycle.State.INITIALIZED, lifecycle.getState());
+
+    // test authorization
+    DruidPlanner mockPlanner = EasyMock.createMock(DruidPlanner.class);
+    PlannerContext mockPlannerContext = EasyMock.createMock(PlannerContext.class);
+    ValidationResult validationResult = new ValidationResult(Collections.emptySet());
+    EasyMock.expect(plannerFactory.createPlanner(EasyMock.anyObject())).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.getPlannerContext()).andReturn(mockPlannerContext).once();
+    mockPlannerContext.setAuthenticationResult(CalciteTests.REGULAR_USER_AUTH_RESULT);
+    EasyMock.expectLastCall();
+    mockPlannerContext.setParameters(parameters);
+    EasyMock.expectLastCall();
+    EasyMock.expect(plannerFactory.getAuthorizerMapper()).andReturn(CalciteTests.TEST_AUTHORIZER_MAPPER).once();
+    mockPlannerContext.setAuthorizationResult(Access.OK);
+    EasyMock.expectLastCall();
+    EasyMock.expect(mockPlanner.validate(sql)).andReturn(validationResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext);
+
+    lifecycle.validateAndAuthorize(CalciteTests.REGULAR_USER_AUTH_RESULT);
+    Assert.assertEquals(SqlLifecycle.State.AUTHORIZED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext);
+
+    // test prepare
+    PrepareResult mockPrepareResult = EasyMock.createMock(PrepareResult.class);
+    EasyMock.expect(plannerFactory.createPlannerWithContext(EasyMock.eq(mockPlannerContext))).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.prepare(sql)).andReturn(mockPrepareResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+    lifecycle.prepare();
+    // prepare doens't change lifecycle state
+    Assert.assertEquals(SqlLifecycle.State.AUTHORIZED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+
+    // test plan
+    PlannerResult mockPlanResult = EasyMock.createMock(PlannerResult.class);
+    EasyMock.expect(plannerFactory.createPlannerWithContext(EasyMock.eq(mockPlannerContext))).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.plan(sql)).andReturn(mockPlanResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    PlannerContext context = lifecycle.plan();
+    Assert.assertEquals(mockPlannerContext, context);
+    Assert.assertEquals(SqlLifecycle.State.PLANNED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    // test execute
+    EasyMock.expect(mockPlanResult.run()).andReturn(Sequences.simple(ImmutableList.of(new Object[]{2L}))).once();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    lifecycle.execute();
+    Assert.assertEquals(SqlLifecycle.State.EXECUTING, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    // test emit
+    EasyMock.expect(mockPlannerContext.getSqlQueryId()).andReturn("id").once();
+    EasyMock.expect(mockPlannerContext.getNativeQueryIds()).andReturn(ImmutableList.of("id")).times(2);
+    EasyMock.expect(mockPlannerContext.getAuthenticationResult()).andReturn(CalciteTests.REGULAR_USER_AUTH_RESULT).once();
+
+    serviceEmitter.emit(EasyMock.anyObject(ServiceEventBuilder.class));
+    EasyMock.expectLastCall();
+    serviceEmitter.emit(EasyMock.anyObject(ServiceEventBuilder.class));
+    EasyMock.expectLastCall();
+    requestLogger.logSqlQuery(EasyMock.anyObject());
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    lifecycle.emitLogsAndMetrics(null, null, 10);
+    Assert.assertEquals(SqlLifecycle.State.DONE, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+  }
+
+  @Test
+  public void testStateTransitionHttpRequest()
+      throws ValidationException, SqlParseException, RelConversionException, IOException
+  {
+    // this test is a duplicate of testStateTransition except with a slight variation of how validate and authorize
+    // is run
+    SqlLifecycle lifecycle = sqlLifecycleFactory.factorize();
+    final String sql = "select 1 + ?";
+    final Map<String, Object> queryContext = Collections.emptyMap();
+    Assert.assertEquals(SqlLifecycle.State.NEW, lifecycle.getState());
+
+    // test initialize
+    lifecycle.initialize(sql, queryContext);
+    Assert.assertEquals(SqlLifecycle.State.INITIALIZED, lifecycle.getState());
+    List<TypedValue> parameters = ImmutableList.of(new SqlParameter(SqlType.BIGINT, 1L).getTypedValue());
+    lifecycle.setParameters(parameters);
+    // setting parameters should not change the state
+    Assert.assertEquals(SqlLifecycle.State.INITIALIZED, lifecycle.getState());
+
+    // test authorization
+    DruidPlanner mockPlanner = EasyMock.createMock(DruidPlanner.class);
+    PlannerContext mockPlannerContext = EasyMock.createMock(PlannerContext.class);
+    ValidationResult validationResult = new ValidationResult(Collections.emptySet());
+    EasyMock.expect(plannerFactory.createPlanner(EasyMock.anyObject())).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.getPlannerContext()).andReturn(mockPlannerContext).once();
+    mockPlannerContext.setAuthenticationResult(CalciteTests.REGULAR_USER_AUTH_RESULT);
+    EasyMock.expectLastCall();
+    mockPlannerContext.setParameters(parameters);
+    EasyMock.expectLastCall();
+    EasyMock.expect(plannerFactory.getAuthorizerMapper()).andReturn(CalciteTests.TEST_AUTHORIZER_MAPPER).once();
+    mockPlannerContext.setAuthorizationResult(Access.OK);
+    EasyMock.expectLastCall();
+    EasyMock.expect(mockPlanner.validate(sql)).andReturn(validationResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+
+    HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+    EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)).andReturn(CalciteTests.REGULAR_USER_AUTH_RESULT).times(2);
+    EasyMock.expect(request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).once();
+    EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).once();
+    request.setAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED, true);
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, request);
+
+    lifecycle.validateAndAuthorize(request);
+    Assert.assertEquals(SqlLifecycle.State.AUTHORIZED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, request);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, request);
+
+    // test prepare
+    PrepareResult mockPrepareResult = EasyMock.createMock(PrepareResult.class);
+    EasyMock.expect(plannerFactory.createPlannerWithContext(EasyMock.eq(mockPlannerContext))).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.prepare(sql)).andReturn(mockPrepareResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+    lifecycle.prepare();
+    // prepare doens't change lifecycle state
+    Assert.assertEquals(SqlLifecycle.State.AUTHORIZED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult);
+
+    // test plan
+    PlannerResult mockPlanResult = EasyMock.createMock(PlannerResult.class);
+    EasyMock.expect(plannerFactory.createPlannerWithContext(EasyMock.eq(mockPlannerContext))).andReturn(mockPlanner).once();
+    EasyMock.expect(mockPlanner.plan(sql)).andReturn(mockPlanResult).once();
+    mockPlanner.close();
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    PlannerContext context = lifecycle.plan();
+    Assert.assertEquals(mockPlannerContext, context);
+    Assert.assertEquals(SqlLifecycle.State.PLANNED, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    // test execute
+    EasyMock.expect(mockPlanResult.run()).andReturn(Sequences.simple(ImmutableList.of(new Object[]{2L}))).once();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    lifecycle.execute();
+    Assert.assertEquals(SqlLifecycle.State.EXECUTING, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    // test emit
+    EasyMock.expect(mockPlannerContext.getSqlQueryId()).andReturn("id").once();
+    EasyMock.expect(mockPlannerContext.getNativeQueryIds()).andReturn(ImmutableList.of("id")).times(2);
+    EasyMock.expect(mockPlannerContext.getAuthenticationResult()).andReturn(CalciteTests.REGULAR_USER_AUTH_RESULT).once();
+
+    serviceEmitter.emit(EasyMock.anyObject(ServiceEventBuilder.class));
+    EasyMock.expectLastCall();
+    serviceEmitter.emit(EasyMock.anyObject(ServiceEventBuilder.class));
+    EasyMock.expectLastCall();
+    requestLogger.logSqlQuery(EasyMock.anyObject());
+    EasyMock.expectLastCall();
+    EasyMock.replay(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+
+    lifecycle.emitLogsAndMetrics(null, null, 10);
+    Assert.assertEquals(SqlLifecycle.State.DONE, lifecycle.getState());
+    EasyMock.verify(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+    EasyMock.reset(plannerFactory, serviceEmitter, requestLogger, mockPlanner, mockPlannerContext, mockPrepareResult, mockPlanResult);
+  }
+}