/* * Licensed 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 com.facebook.presto.sql.analyzer; import com.facebook.presto.Session; import com.facebook.presto.block.BlockEncodingManager; import com.facebook.presto.connector.ConnectorId; import com.facebook.presto.connector.informationSchema.InformationSchemaConnector; import com.facebook.presto.connector.system.SystemConnector; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.InMemoryNodeManager; import com.facebook.presto.metadata.InternalNodeManager; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.metadata.SchemaPropertyManager; import com.facebook.presto.metadata.SessionPropertyManager; import com.facebook.presto.metadata.TablePropertyManager; import com.facebook.presto.metadata.ViewDefinition; import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.ConnectorTableMetadata; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.connector.Connector; import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorSplitManager; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.transaction.IsolationLevel; import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.tree.NodeLocation; import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.testing.TestingMetadata; import com.facebook.presto.transaction.TransactionManager; import com.facebook.presto.type.ArrayType; import com.facebook.presto.type.TypeRegistry; import com.google.common.collect.ImmutableList; import io.airlift.json.JsonCodec; import org.intellij.lang.annotations.Language; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Optional; import java.util.function.Consumer; import static com.facebook.presto.connector.ConnectorId.createInformationSchemaConnectorId; import static com.facebook.presto.connector.ConnectorId.createSystemTablesConnectorId; import static com.facebook.presto.metadata.ViewDefinition.ViewColumn; import static com.facebook.presto.operator.scalar.ApplyFunction.APPLY_FUNCTION; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.AMBIGUOUS_ATTRIBUTE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.CATALOG_NOT_SPECIFIED; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.COLUMN_NAME_NOT_SPECIFIED; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.COLUMN_TYPE_UNKNOWN; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_COLUMN_NAME; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_RELATION; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_LITERAL; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_ORDINAL; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_SCHEMA_NAME; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_WINDOW_FRAME; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISMATCHED_COLUMN_ALIASES; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISMATCHED_SET_COLUMN_TYPES; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_ATTRIBUTE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_CATALOG; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_COLUMN; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_SCHEMA; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_TABLE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MULTIPLE_FIELDS_FROM_SUBQUERY; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MUST_BE_AGGREGATE_OR_GROUP_BY; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MUST_BE_AGGREGATION_FUNCTION; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NESTED_AGGREGATION; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NESTED_WINDOW; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NONDETERMINISTIC_ORDER_BY_EXPRESSION_WITH_SELECT_DISTINCT; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NON_NUMERIC_SAMPLE_PERCENTAGE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.ORDER_BY_MUST_BE_IN_SELECT; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.SAMPLE_PERCENTAGE_OUT_OF_RANGE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.SCHEMA_NOT_SPECIFIED; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.STANDALONE_LAMBDA; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.TYPE_MISMATCH; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_ANALYSIS_ERROR; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_IS_RECURSIVE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_IS_STALE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.WILDCARD_WITHOUT_FROM; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.WINDOW_REQUIRES_OVER; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.transaction.TransactionBuilder.transaction; import static com.facebook.presto.transaction.TransactionManager.createTestTransactionManager; import static java.lang.String.format; import static java.util.Collections.emptyList; import static org.testng.Assert.fail; @Test(singleThreaded = true) public class TestAnalyzer { private static final String TPCH_CATALOG = "tpch"; private static final ConnectorId TPCH_CONNECTOR_ID = new ConnectorId(TPCH_CATALOG); private static final String SECOND_CATALOG = "c2"; private static final ConnectorId SECOND_CONNECTOR_ID = new ConnectorId(SECOND_CATALOG); private static final String THIRD_CATALOG = "c3"; private static final ConnectorId THIRD_CONNECTOR_ID = new ConnectorId(THIRD_CATALOG); private static final Session SETUP_SESSION = testSessionBuilder() .setCatalog("c1") .setSchema("s1") .build(); private static final Session CLIENT_SESSION = testSessionBuilder() .setCatalog(TPCH_CATALOG) .setSchema("s1") .build(); private static final SqlParser SQL_PARSER = new SqlParser(); private TransactionManager transactionManager; private AccessControl accessControl; private Metadata metadata; @Test public void testNonComparableGroupBy() throws Exception { assertFails(TYPE_MISMATCH, "SELECT * FROM (SELECT approx_set(1)) GROUP BY 1"); } @Test public void testNonComparableWindowPartition() throws Exception { assertFails(TYPE_MISMATCH, "SELECT row_number() OVER (PARTITION BY t.x) FROM (VALUES(CAST (NULL AS HyperLogLog))) AS t(x)"); } @Test public void testNonComparableWindowOrder() throws Exception { assertFails(TYPE_MISMATCH, "SELECT row_number() OVER (ORDER BY t.x) FROM (VALUES(color('red'))) AS t(x)"); } @Test public void testNonComparableDistinctAggregation() throws Exception { assertFails(TYPE_MISMATCH, "SELECT count(DISTINCT x) FROM (SELECT approx_set(1) x)"); } @Test public void testNonComparableDistinct() throws Exception { assertFails(TYPE_MISMATCH, "SELECT DISTINCT * FROM (SELECT approx_set(1) x)"); assertFails(TYPE_MISMATCH, "SELECT DISTINCT x FROM (SELECT approx_set(1) x)"); } @Test public void testInSubqueryTypes() throws Exception { assertFails(TYPE_MISMATCH, "SELECT * FROM (VALUES 'a') t(y) WHERE y IN (VALUES 1)"); assertFails(TYPE_MISMATCH, "SELECT (VALUES true) IN (VALUES 1)"); } @Test public void testScalarSubQuery() throws Exception { analyze("SELECT 'a', (VALUES 1) GROUP BY 1"); analyze("SELECT 'a', (SELECT (1))"); analyze("SELECT * FROM t1 WHERE (VALUES 1) = 2"); analyze("SELECT * FROM t1 WHERE (VALUES 1) IN (VALUES 1)"); analyze("SELECT * FROM t1 WHERE (VALUES 1) IN (2)"); analyze("SELECT * FROM (SELECT 1) t1(x) WHERE x IN (SELECT 1)"); } @Test public void testReferenceToOutputColumnFromOrderByAggregation() throws Exception { assertFails(REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "SELECT max(a) AS a FROM (values (1,2)) t(a,b) GROUP BY b ORDER BY max(a+b)"); assertFails(REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "SELECT DISTINCT a AS a, max(a) AS c from (VALUES (1, 2)) t(a, b) GROUP BY a ORDER BY max(a)"); assertFails(REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "SELECT CAST(ROW(1) AS ROW(someField BIGINT)) AS a FROM (values (1,2)) t(a,b) GROUP BY b ORDER BY MAX(a.someField)"); assertFails(REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "SELECT 1 AS x FROM (values (1,2)) t(x, y) GROUP BY y ORDER BY sum(apply(1, z -> x))"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT row_number() over() as a from (values (41, 42), (-41, -42)) t(a,b) group by a+b order by a+b"); } @Test public void testHavingReferencesOutputAlias() throws Exception { assertFails(MISSING_ATTRIBUTE, "SELECT sum(a) x FROM t1 HAVING x > 5"); } @Test public void testWildcardWithInvalidPrefix() throws Exception { assertFails(MISSING_TABLE, "SELECT foo.* FROM t1"); } @Test public void testGroupByWithWildcard() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT * FROM t1 GROUP BY 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT u1.*, u2.* FROM (select a, b + 1 from t1) u1 JOIN (select a, b + 2 from t1) u2 USING (a) GROUP BY u1.a, u2.a, 3"); } @Test public void testGroupByInvalidOrdinal() throws Exception { assertFails(INVALID_ORDINAL, "SELECT * FROM t1 GROUP BY 10"); assertFails(INVALID_ORDINAL, "SELECT * FROM t1 GROUP BY 0"); } @Test public void testGroupByWithSubquerySelectExpression() throws Exception { analyze("SELECT (SELECT t1.a) FROM t1 GROUP BY a"); analyze("SELECT (SELECT a) FROM t1 GROUP BY t1.a"); // u.a is not GROUP-ed BY and it is used in select Subquery expression analyze("SELECT (SELECT u.a FROM (values 1) u(a)) " + "FROM t1 u GROUP BY b"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:16: Subquery uses '\"u\".\"a\"' which must appear in GROUP BY clause", "SELECT (SELECT u.a from (values 1) x(a)) FROM t1 u GROUP BY b"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:16: Subquery uses '\"a\"' which must appear in GROUP BY clause", "SELECT (SELECT a+2) FROM t1 GROUP BY a+1"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:36: Subquery uses '\"u\".\"a\"' which must appear in GROUP BY clause", "SELECT (SELECT 1 FROM t1 WHERE a = u.a) FROM t1 u GROUP BY b"); // (t1.)a is not part of GROUP BY assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT (SELECT a as a) FROM t1 GROUP BY b"); // u.a is not GROUP-ed BY but select Subquery expression is using a different (shadowing) u.a analyze("SELECT (SELECT 1 FROM t1 u WHERE a = u.a) FROM t1 u GROUP BY b"); } @Test public void testGroupByWithExistsSelectExpression() throws Exception { analyze("SELECT EXISTS(SELECT t1.a) FROM t1 GROUP BY a"); analyze("SELECT EXISTS(SELECT a) FROM t1 GROUP BY t1.a"); // u.a is not GROUP-ed BY and it is used in select Subquery expression analyze("SELECT EXISTS(SELECT u.a FROM (values 1) u(a)) " + "FROM t1 u GROUP BY b"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:22: Subquery uses '\"u\".\"a\"' which must appear in GROUP BY clause", "SELECT EXISTS(SELECT u.a from (values 1) x(a)) FROM t1 u GROUP BY b"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:22: Subquery uses '\"a\"' which must appear in GROUP BY clause", "SELECT EXISTS(SELECT a+2) FROM t1 GROUP BY a+1"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:42: Subquery uses '\"u\".\"a\"' which must appear in GROUP BY clause", "SELECT EXISTS(SELECT 1 FROM t1 WHERE a = u.a) FROM t1 u GROUP BY b"); // (t1.)a is not part of GROUP BY assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT EXISTS(SELECT a as a) FROM t1 GROUP BY b"); // u.a is not GROUP-ed BY but select Subquery expression is using a different (shadowing) u.a analyze("SELECT EXISTS(SELECT 1 FROM t1 u WHERE a = u.a) FROM t1 u GROUP BY b"); } @Test public void testGroupByWithSubquerySelectExpressionWithDereferenceExpression() { analyze("SELECT (SELECT t.a.someField) " + "FROM (VALUES ROW(CAST(ROW(1) AS ROW(someField BIGINT)), 2)) t(a, b) " + "GROUP BY a"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:16: Subquery uses '\"t\".\"a\"' which must appear in GROUP BY clause", "SELECT (SELECT t.a.someField) " + "FROM (VALUES ROW(CAST(ROW(1) AS ROW(someField BIGINT)), 2)) t(a, b) " + "GROUP BY b"); } @Test public void testOrderByInvalidOrdinal() throws Exception { assertFails(INVALID_ORDINAL, "SELECT * FROM t1 ORDER BY 10"); assertFails(INVALID_ORDINAL, "SELECT * FROM t1 ORDER BY 0"); } @Test public void testOrderByNonComparable() throws Exception { assertFails(TYPE_MISMATCH, "SELECT x FROM (SELECT approx_set(1) x) ORDER BY 1"); assertFails(TYPE_MISMATCH, "SELECT * FROM (SELECT approx_set(1) x) ORDER BY 1"); assertFails(TYPE_MISMATCH, "SELECT x FROM (SELECT approx_set(1) x) ORDER BY x"); } @Test public void testNestedAggregation() throws Exception { assertFails(NESTED_AGGREGATION, "SELECT sum(count(*)) FROM t1"); } @Test public void testAggregationsNotAllowed() throws Exception { assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 WHERE sum(a) > 1"); assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 GROUP BY sum(a)"); assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 JOIN t2 ON sum(t1.a) = t2.a"); } @Test public void testWindowsNotAllowed() throws Exception { assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 WHERE foo() over () > 1"); assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 GROUP BY rank() over ()"); assertFails(CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, "SELECT * FROM t1 JOIN t2 ON sum(t1.a) over () = t2.a"); } @Test public void testInvalidTable() throws Exception { assertFails(MISSING_CATALOG, "SELECT * FROM foo.bar.t"); assertFails(MISSING_SCHEMA, "SELECT * FROM foo.t"); assertFails(MISSING_TABLE, "SELECT * FROM foo"); } @Test public void testInvalidSchema() throws Exception { assertFails(MISSING_SCHEMA, "SHOW TABLES FROM NONEXISTENT_SCHEMA"); assertFails(MISSING_SCHEMA, "SHOW TABLES IN NONEXISTENT_SCHEMA LIKE '%'"); } @Test public void testNonAggregate() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT 'a', array[b][1] FROM t1 GROUP BY 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT a, sum(b) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT sum(b) / a FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT sum(b) / a FROM t1 GROUP BY c"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT sum(b) FROM t1 ORDER BY a + 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT a, sum(b) FROM t1 GROUP BY a HAVING c > 5"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT count(*) over (PARTITION BY a) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT count(*) over (ORDER BY a) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT count(*) over (ORDER BY count(*) ROWS a PRECEDING) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT count(*) over (ORDER BY count(*) ROWS BETWEEN b PRECEDING AND a PRECEDING) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT count(*) over (ORDER BY count(*) ROWS BETWEEN a PRECEDING AND UNBOUNDED PRECEDING) FROM t1 GROUP BY b"); } @Test public void testInvalidAttribute() throws Exception { assertFails(MISSING_ATTRIBUTE, "SELECT f FROM t1"); assertFails(MISSING_ATTRIBUTE, "SELECT * FROM t1 ORDER BY f"); assertFails(MISSING_ATTRIBUTE, "SELECT count(*) FROM t1 GROUP BY f"); assertFails(MISSING_ATTRIBUTE, "SELECT * FROM t1 WHERE f > 1"); } @Test(expectedExceptions = SemanticException.class, expectedExceptionsMessageRegExp = "line 1:8: Column 't.y' cannot be resolved") public void testInvalidAttributeCorrectErrorMessage() throws Exception { analyze("SELECT t.y FROM (VALUES 1) t(x)"); } @Test public void testOrderByMustAppearInSelectWithDistinct() throws Exception { assertFails(ORDER_BY_MUST_BE_IN_SELECT, "SELECT DISTINCT a FROM t1 ORDER BY b"); } @Test public void testNonDeterministicOrderBy() { analyze("SELECT DISTINCT random() as b FROM t1 ORDER BY b"); analyze("SELECT random() FROM t1 ORDER BY random()"); analyze("SELECT a FROM t1 ORDER BY random()"); assertFails(NONDETERMINISTIC_ORDER_BY_EXPRESSION_WITH_SELECT_DISTINCT, "SELECT DISTINCT random() FROM t1 ORDER BY random()"); } @Test public void testNonBooleanWhereClause() throws Exception { assertFails(TYPE_MISMATCH, "SELECT * FROM t1 WHERE a"); } @Test public void testDistinctAggregations() throws Exception { analyze("SELECT COUNT(DISTINCT a), SUM(a) FROM t1"); } @Test public void testMultipleDistinctAggregations() throws Exception { analyze("SELECT COUNT(DISTINCT a), COUNT(DISTINCT b) FROM t1"); } @Test public void testOrderByExpressionOnOutputColumn() throws Exception { // TODO: analyze output analyze("SELECT a x FROM t1 ORDER BY x + 1"); analyze("SELECT max(a) FROM (values (1,2), (2,1)) t(a,b) GROUP BY b ORDER BY max(b*1.0)"); analyze("SELECT CAST(ROW(1) AS ROW(someField BIGINT)) AS a FROM (values (1,2)) t(a,b) GROUP BY b ORDER BY a.someField"); analyze("SELECT 1 AS x FROM (values (1,2)) t(x, y) GROUP BY y ORDER BY sum(apply(1, x -> x))"); } @Test public void testOrderByExpressionOnOutputColumn2() throws Exception { // TODO: validate output analyze("SELECT a x FROM t1 ORDER BY a + 1"); assertFails(TYPE_MISMATCH, 3, 10, "SELECT x.c as x\n" + "FROM (VALUES 1) x(c)\n" + "ORDER BY x.c"); } @Test public void testOrderByWithWildcard() throws Exception { // TODO: validate output analyze("SELECT t1.* FROM t1 ORDER BY a"); } @Test public void testOrderByWithGroupByAndSubquerySelectExpression() throws Exception { analyze("SELECT a FROM t1 GROUP BY a ORDER BY (SELECT a)"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:46: Subquery uses '\"b\"' which must appear in GROUP BY clause", "SELECT a FROM t1 GROUP BY a ORDER BY (SELECT b)"); analyze("SELECT a AS b FROM t1 GROUP BY t1.a ORDER BY (SELECT b)"); assertFails( REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "line 2:22: Invalid reference to output projection attribute from ORDER BY aggregation", "SELECT a AS b FROM t1 GROUP BY t1.a \n" + "ORDER BY MAX((SELECT b))"); analyze("SELECT a FROM t1 GROUP BY a ORDER BY MAX((SELECT x FROM (VALUES 4) t(x)))"); analyze("SELECT CAST(ROW(1) AS ROW(someField BIGINT)) AS x\n" + "FROM (VALUES (1, 2)) t(a, b)\n" + "GROUP BY b\n" + "ORDER BY (SELECT x.someField)"); assertFails( REFERENCE_TO_OUTPUT_ATTRIBUTE_WITHIN_ORDER_BY_AGGREGATION, "line 4:22: Invalid reference to output projection attribute from ORDER BY aggregation", "SELECT CAST(ROW(1) AS ROW(someField BIGINT)) AS x\n" + "FROM (VALUES (1, 2)) t(a, b)\n" + "GROUP BY b\n" + "ORDER BY MAX((SELECT x.someField))"); } @Test public void testMismatchedColumnAliasCount() throws Exception { assertFails(MISMATCHED_COLUMN_ALIASES, "SELECT * FROM t1 u (x, y)"); } @Test public void testJoinOnConstantExpression() throws Exception { analyze("SELECT * FROM t1 JOIN t2 ON 1 = 1"); } @Test public void testJoinOnNonBooleanExpression() throws Exception { assertFails(TYPE_MISMATCH, "SELECT * FROM t1 JOIN t2 ON 5"); } @Test public void testJoinOnAmbiguousName() throws Exception { assertFails(AMBIGUOUS_ATTRIBUTE, "SELECT * FROM t1 JOIN t2 ON a = a"); } @Test public void testNonEquiOuterJoin() throws Exception { analyze("SELECT * FROM t1 LEFT JOIN t2 ON t1.a + t2.a = 1"); analyze("SELECT * FROM t1 RIGHT JOIN t2 ON t1.a + t2.a = 1"); analyze("SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a OR t1.b = t2.b"); } @Test public void testNonBooleanHaving() throws Exception { assertFails(TYPE_MISMATCH, "SELECT sum(a) FROM t1 HAVING sum(a)"); } @Test public void testAmbiguousReferenceInOrderBy() throws Exception { assertFails(AMBIGUOUS_ATTRIBUTE, "SELECT a x, b x FROM t1 ORDER BY x"); assertFails(AMBIGUOUS_ATTRIBUTE, "SELECT a x, a x FROM t1 ORDER BY x"); assertFails(AMBIGUOUS_ATTRIBUTE, "SELECT a, a FROM t1 ORDER BY a"); } @Test public void testImplicitCrossJoin() { // TODO: validate output analyze("SELECT * FROM t1, t2"); } @Test public void testNaturalJoinNotSupported() throws Exception { assertFails(NOT_SUPPORTED, "SELECT * FROM t1 NATURAL JOIN t2"); } @Test public void testNestedWindowFunctions() throws Exception { assertFails(NESTED_WINDOW, "SELECT avg(sum(a) OVER ()) FROM t1"); assertFails(NESTED_WINDOW, "SELECT sum(sum(a) OVER ()) OVER () FROM t1"); assertFails(NESTED_WINDOW, "SELECT avg(a) OVER (PARTITION BY sum(b) OVER ()) FROM t1"); assertFails(NESTED_WINDOW, "SELECT avg(a) OVER (ORDER BY sum(b) OVER ()) FROM t1"); } @Test public void testWindowFunctionWithoutOverClause() { assertFails(WINDOW_REQUIRES_OVER, "SELECT row_number()"); assertFails(WINDOW_REQUIRES_OVER, "SELECT coalesce(lead(a), 0) from (values(0)) t(a)"); } @Test public void testInvalidWindowFrame() throws Exception { assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS UNBOUNDED FOLLOWING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS 2 FOLLOWING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN UNBOUNDED FOLLOWING AND CURRENT ROW)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND 5 PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND 5 PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE 2 PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN 2 PRECEDING AND 5 FOLLOWING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 0.5 PRECEDING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 'foo' PRECEDING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND 0.5 FOLLOWING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND 'foo' FOLLOWING)"); } @Test public void testDistinctInWindowFunctionParameter() throws Exception { assertFails(NOT_SUPPORTED, "SELECT a, count(DISTINCT b) OVER () FROM t1"); } @Test public void testGroupByOrdinalsWithWildcard() throws Exception { // TODO: verify output analyze("SELECT t1.*, a FROM t1 GROUP BY 1,2,c,d"); } @Test public void testGroupByWithQualifiedName() throws Exception { // TODO: verify output analyze("SELECT a FROM t1 GROUP BY t1.a"); } @Test public void testGroupByWithQualifiedName2() throws Exception { // TODO: verify output analyze("SELECT t1.a FROM t1 GROUP BY a"); } @Test public void testGroupByWithQualifiedName3() throws Exception { // TODO: verify output analyze("SELECT * FROM t1 GROUP BY t1.a, t1.b, t1.c, t1.d"); } @Test public void testGroupByWithRowExpression() throws Exception { // TODO: verify output analyze("SELECT (a, b) FROM t1 GROUP BY a, b"); } @Test public void testHaving() throws Exception { // TODO: verify output analyze("SELECT sum(a) FROM t1 HAVING avg(a) - avg(b) > 10"); } @Test public void testWithCaseInsensitiveResolution() throws Exception { // TODO: verify output analyze("WITH AB AS (SELECT * FROM t1) SELECT * FROM ab"); } @Test public void testStartTransaction() throws Exception { analyze("START TRANSACTION"); analyze("START TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"); analyze("START TRANSACTION ISOLATION LEVEL READ COMMITTED"); analyze("START TRANSACTION ISOLATION LEVEL REPEATABLE READ"); analyze("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"); analyze("START TRANSACTION READ ONLY"); analyze("START TRANSACTION READ WRITE"); analyze("START TRANSACTION ISOLATION LEVEL READ COMMITTED, READ ONLY"); analyze("START TRANSACTION READ ONLY, ISOLATION LEVEL READ COMMITTED"); analyze("START TRANSACTION READ WRITE, ISOLATION LEVEL SERIALIZABLE"); } @Test public void testCommit() throws Exception { analyze("COMMIT"); analyze("COMMIT WORK"); } @Test public void testRollback() throws Exception { analyze("ROLLBACK"); analyze("ROLLBACK WORK"); } @Test public void testExplainAnalyze() throws Exception { analyze("EXPLAIN ANALYZE SELECT * FROM t1"); } @Test public void testInsert() throws Exception { assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t6 (a) SELECT b from t6"); analyze("INSERT INTO t1 SELECT * FROM t1"); analyze("INSERT INTO t3 SELECT * FROM t3"); analyze("INSERT INTO t3 SELECT a, b FROM t3"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 VALUES (1, 2)"); analyze("INSERT INTO t5 (a) VALUES(null)"); // ignore t5 hidden column analyze("INSERT INTO t5 VALUES (1)"); // fail if hidden column provided assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t5 VALUES (1, 2)"); // note b is VARCHAR, while a,c,d are BIGINT analyze("INSERT INTO t6 (a) SELECT a from t6"); analyze("INSERT INTO t6 (a) SELECT c from t6"); analyze("INSERT INTO t6 (a,b,c,d) SELECT * from t6"); analyze("INSERT INTO t6 (A,B,C,D) SELECT * from t6"); analyze("INSERT INTO t6 (a,b,c,d) SELECT d,b,c,a from t6"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t6 (a) SELECT b from t6"); assertFails(MISSING_COLUMN, "INSERT INTO t6 (unknown) SELECT * FROM t6"); assertFails(DUPLICATE_COLUMN_NAME, "INSERT INTO t6 (a, a) SELECT * FROM t6"); assertFails(DUPLICATE_COLUMN_NAME, "INSERT INTO t6 (a, A) SELECT * FROM t6"); // b is bigint, while a is double, coercion from b to a is possible analyze("INSERT INTO t7 (b) SELECT (a) FROM t7 "); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t7 (a) SELECT (b) FROM t7"); // d is array of bigints, while c is array of doubles, coercion from d to c is possible analyze("INSERT INTO t7 (d) SELECT (c) FROM t7 "); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t7 (c) SELECT (d) FROM t7 "); analyze("INSERT INTO t7 (d) VALUES (ARRAY[null])"); analyze("INSERT INTO t6 (d) VALUES (1), (2), (3)"); analyze("INSERT INTO t6 (a,b,c,d) VALUES (1, 'a', 1, 1), (2, 'b', 2, 2), (3, 'c', 3, 3), (4, 'd', 4, 4)"); } @Test public void testInvalidInsert() throws Exception { assertFails(MISSING_TABLE, "INSERT INTO foo VALUES (1)"); assertFails(NOT_SUPPORTED, "INSERT INTO v1 VALUES (1)"); // fail if inconsistent fields count assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a) VALUES (1), (1, 2)"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a, b) VALUES (1), (1, 2)"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a, b) VALUES (1, 2), (1, 2), (1, 2, 3)"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a, b) VALUES ('a', 'b'), ('a', 'b', 'c')"); // fail if mismatched column types assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a, b) VALUES ('a', 'b'), (1, 'b')"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "INSERT INTO t1 (a, b) VALUES ('a', 'b'), ('a', 'b'), (1, 'b')"); } @Test public void testDuplicateWithQuery() throws Exception { assertFails(DUPLICATE_RELATION, "WITH a AS (SELECT * FROM t1)," + " a AS (SELECT * FROM t1)" + "SELECT * FROM a"); } @Test public void testCaseInsensitiveDuplicateWithQuery() throws Exception { assertFails(DUPLICATE_RELATION, "WITH a AS (SELECT * FROM t1)," + " A AS (SELECT * FROM t1)" + "SELECT * FROM a"); } @Test public void testWithForwardReference() throws Exception { assertFails(MISSING_TABLE, "WITH a AS (SELECT * FROM b)," + " b AS (SELECT * FROM t1)" + "SELECT * FROM a"); } @Test public void testExpressions() throws Exception { // logical not assertFails(TYPE_MISMATCH, "SELECT NOT 1 FROM t1"); // logical and/or assertFails(TYPE_MISMATCH, "SELECT 1 AND TRUE FROM t1"); assertFails(TYPE_MISMATCH, "SELECT TRUE AND 1 FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 1 OR TRUE FROM t1"); assertFails(TYPE_MISMATCH, "SELECT TRUE OR 1 FROM t1"); // comparison assertFails(TYPE_MISMATCH, "SELECT 1 = 'a' FROM t1"); // nullif assertFails(TYPE_MISMATCH, "SELECT NULLIF(1, 'a') FROM t1"); // case assertFails(TYPE_MISMATCH, "SELECT CASE WHEN TRUE THEN 'a' ELSE 1 END FROM t1"); assertFails(TYPE_MISMATCH, "SELECT CASE WHEN '1' THEN 1 ELSE 2 END FROM t1"); assertFails(TYPE_MISMATCH, "SELECT CASE 1 WHEN 'a' THEN 2 END FROM t1"); assertFails(TYPE_MISMATCH, "SELECT CASE 1 WHEN 1 THEN 2 ELSE 'a' END FROM t1"); // coalesce assertFails(TYPE_MISMATCH, "SELECT COALESCE(1, 'a') FROM t1"); // cast assertFails(TYPE_MISMATCH, "SELECT CAST(date '2014-01-01' AS bigint)"); assertFails(TYPE_MISMATCH, "SELECT TRY_CAST(date '2014-01-01' AS bigint)"); assertFails(TYPE_MISMATCH, "SELECT CAST(null AS UNKNOWN)"); assertFails(TYPE_MISMATCH, "SELECT CAST(1 AS MAP)"); assertFails(TYPE_MISMATCH, "SELECT CAST(1 AS ARRAY)"); assertFails(TYPE_MISMATCH, "SELECT CAST(1 AS ROW)"); // arithmetic unary assertFails(TYPE_MISMATCH, "SELECT -'a' FROM t1"); assertFails(TYPE_MISMATCH, "SELECT +'a' FROM t1"); // arithmetic addition/subtraction assertFails(TYPE_MISMATCH, "SELECT 'a' + 1 FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 1 + 'a' FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 'a' - 1 FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 1 - 'a' FROM t1"); // like assertFails(TYPE_MISMATCH, "SELECT 1 LIKE 'a' FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 'a' LIKE 1 FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 'a' LIKE 'b' ESCAPE 1 FROM t1"); // extract assertFails(TYPE_MISMATCH, "SELECT EXTRACT(DAY FROM 'a') FROM t1"); // between assertFails(TYPE_MISMATCH, "SELECT 1 BETWEEN 'a' AND 2 FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 1 BETWEEN 0 AND 'b' FROM t1"); assertFails(TYPE_MISMATCH, "SELECT 1 BETWEEN 'a' AND 'b' FROM t1"); // in assertFails(TYPE_MISMATCH, "SELECT * FROM t1 WHERE 1 IN ('a')"); assertFails(TYPE_MISMATCH, "SELECT * FROM t1 WHERE 'a' IN (1)"); assertFails(TYPE_MISMATCH, "SELECT * FROM t1 WHERE 'a' IN (1, 'b')"); // row type assertFails(TYPE_MISMATCH, "SELECT t.x.f1 FROM (VALUES 1) t(x)"); assertFails(TYPE_MISMATCH, "SELECT x.f1 FROM (VALUES 1) t(x)"); } @Test(enabled = false) // TODO: need to support widening conversion for numbers public void testInWithNumericTypes() throws Exception { analyze("SELECT * FROM t1 WHERE 1 IN (1, 2, 3.5)"); } @Test public void testWildcardWithoutFrom() throws Exception { assertFails(WILDCARD_WITHOUT_FROM, "SELECT *"); } @Test public void testReferenceWithoutFrom() throws Exception { assertFails(MISSING_ATTRIBUTE, "SELECT dummy"); } @Test public void testGroupBy() throws Exception { // TODO: validate output analyze("SELECT a, SUM(b) FROM t1 GROUP BY a"); } @Test public void testGroupByEmpty() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT a FROM t1 GROUP BY ()"); } @Test public void testSingleGroupingSet() throws Exception { // TODO: validate output analyze("SELECT SUM(b) FROM t1 GROUP BY ()"); analyze("SELECT SUM(b) FROM t1 GROUP BY GROUPING SETS (())"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS (a)"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS (a)"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS ((a, b))"); } @Test public void testMultipleGroupingSetMultipleColumns() throws Exception { // TODO: validate output analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS ((a, b), (c, d))"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY a, b, GROUPING SETS ((c, d))"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS ((a), (c, d))"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS ((a, b)), ROLLUP (c, d)"); analyze("SELECT a, SUM(b) FROM t1 GROUP BY GROUPING SETS ((a, b)), CUBE (c, d)"); } @Test public void testAggregateWithWildcard() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "Column 1 not in GROUP BY clause", "SELECT * FROM (SELECT a + 1, b FROM t1) t GROUP BY b ORDER BY 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "Column 't.a' not in GROUP BY clause", "SELECT * FROM (SELECT a, b FROM t1) t GROUP BY b ORDER BY 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "Column 'a' not in GROUP BY clause", "SELECT * FROM (SELECT a, b FROM t1) GROUP BY b ORDER BY 1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "Column 1 not in GROUP BY clause", "SELECT * FROM (SELECT a + 1, b FROM t1) GROUP BY b ORDER BY 1"); } @Test public void testGroupByCase() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE a WHEN 1 THEN 'a' ELSE 'b' END, count(*) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE 1 WHEN 2 THEN a ELSE 0 END, count(*) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE 1 WHEN 2 THEN 0 ELSE a END, count(*) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE WHEN a = 1 THEN 'a' ELSE 'b' END, count(*) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE WHEN true THEN a ELSE 0 END, count(*) FROM t1"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT CASE WHEN true THEN 0 ELSE a END, count(*) FROM t1"); } @Test public void testMismatchedUnionQueries() throws Exception { assertFails(TYPE_MISMATCH, "SELECT 1 UNION SELECT 'a'"); assertFails(TYPE_MISMATCH, "SELECT a FROM t1 UNION SELECT 'a'"); assertFails(TYPE_MISMATCH, "(SELECT 1) UNION SELECT 'a'"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "SELECT 1, 2 UNION SELECT 1"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "SELECT 'a' UNION SELECT 'b', 'c'"); assertFails(MISMATCHED_SET_COLUMN_TYPES, "TABLE t2 UNION SELECT 'a'"); } @Test public void testUnionUnmatchedOrderByAttribute() throws Exception { assertFails(MISSING_ATTRIBUTE, "TABLE t2 UNION ALL SELECT c, d FROM t1 ORDER BY c"); } @Test public void testGroupByComplexExpressions() throws Exception { assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT IF(a IS NULL, 1, 0) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT IF(a IS NOT NULL, 1, 0) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT IF(CAST(a AS VARCHAR) LIKE 'a', 1, 0) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT a IN (1, 2, 3) FROM t1 GROUP BY b"); assertFails(MUST_BE_AGGREGATE_OR_GROUP_BY, "SELECT 1 IN (a, 2, 3) FROM t1 GROUP BY b"); } @Test public void testNonNumericTableSamplePercentage() throws Exception { assertFails(NON_NUMERIC_SAMPLE_PERCENTAGE, "SELECT * FROM t1 TABLESAMPLE BERNOULLI ('a')"); assertFails(NON_NUMERIC_SAMPLE_PERCENTAGE, "SELECT * FROM t1 TABLESAMPLE BERNOULLI (a + 1)"); } @Test public void testTableSampleOutOfRange() throws Exception { assertFails(SAMPLE_PERCENTAGE_OUT_OF_RANGE, "SELECT * FROM t1 TABLESAMPLE BERNOULLI (-1)"); assertFails(SAMPLE_PERCENTAGE_OUT_OF_RANGE, "SELECT * FROM t1 TABLESAMPLE BERNOULLI (-101)"); } @Test public void testCreateTableAsColumns() throws Exception { assertFails(COLUMN_NAME_NOT_SPECIFIED, "CREATE TABLE test AS SELECT 123"); assertFails(DUPLICATE_COLUMN_NAME, "CREATE TABLE test AS SELECT 1 a, 2 a"); assertFails(COLUMN_TYPE_UNKNOWN, "CREATE TABLE test AS SELECT null a"); } @Test public void testCreateViewColumns() throws Exception { assertFails(COLUMN_NAME_NOT_SPECIFIED, "CREATE VIEW test AS SELECT 123"); assertFails(DUPLICATE_COLUMN_NAME, "CREATE VIEW test AS SELECT 1 a, 2 a"); assertFails(COLUMN_TYPE_UNKNOWN, "CREATE VIEW test AS SELECT null a"); } @Test public void testCreateRecursiveView() throws Exception { assertFails(VIEW_IS_RECURSIVE, "CREATE OR REPLACE VIEW v1 AS SELECT * FROM v1"); } @Test public void testExistingRecursiveView() throws Exception { analyze("SELECT * FROM v1 a JOIN v1 b ON a.a = b.a"); analyze("SELECT * FROM v1 a JOIN (SELECT * from v1) b ON a.a = b.a"); assertFails(VIEW_ANALYSIS_ERROR, "SELECT * FROM v5"); } @Test public void testShowCreateView() { analyze("SHOW CREATE VIEW v1"); analyze("SHOW CREATE VIEW v2"); assertFails(NOT_SUPPORTED, "SHOW CREATE VIEW t1"); assertFails(MISSING_TABLE, "SHOW CREATE VIEW none"); } @Test public void testStaleView() throws Exception { assertFails(VIEW_IS_STALE, "SELECT * FROM v2"); } @Test public void testStoredViewAnalysisScoping() throws Exception { // the view must not be analyzed using the query context analyze("WITH t1 AS (SELECT 123 x) SELECT * FROM v1"); } @Test public void testStoredViewResolution() throws Exception { // the view must be analyzed relative to its own catalog/schema analyze("SELECT * FROM c3.s3.v3"); } @Test public void testQualifiedViewColumnResolution() throws Exception { // it should be possible to qualify the column reference with the view name analyze("SELECT v1.a FROM v1"); } @Test public void testViewWithUppercaseColumn() throws Exception { analyze("SELECT * FROM v4"); } @Test public void testUse() throws Exception { assertFails(NOT_SUPPORTED, "USE foo"); } @Test public void testNotNullInJoinClause() throws Exception { analyze("SELECT * FROM (VALUES (1)) a (x) JOIN (VALUES (2)) b ON a.x IS NOT NULL"); } @Test public void testIfInJoinClause() throws Exception { analyze("SELECT * FROM (VALUES (1)) a (x) JOIN (VALUES (2)) b ON IF(a.x = 1, true, false)"); } @Test public void testLiteral() throws Exception { assertFails(INVALID_LITERAL, "SELECT TIMESTAMP '2012-10-31 01:00:00 PT'"); } @Test public void testLambda() throws Exception { analyze("SELECT apply(5, x -> abs(x)) from t1"); assertFails(STANDALONE_LAMBDA, "SELECT x -> abs(x) from t1"); } @Test public void testLambdaCapture() throws Exception { analyze("SELECT apply(c1, x -> x + c2) FROM (VALUES (1, 2), (3, 4), (5, 6)) t(c1, c2)"); analyze("SELECT apply(c1 + 10, x -> apply(x + 100, y -> c1)) FROM (VALUES 1) t(c1)"); // reference lambda variable of the not-immediately-enclosing lambda analyze("SELECT apply(1, x -> apply(10, y -> x)) FROM (VALUES 1000) t(x)"); analyze("SELECT apply(1, x -> apply(10, y -> x)) FROM (VALUES 'abc') t(x)"); analyze("SELECT apply(1, x -> apply(10, y -> apply(100, z -> x))) FROM (VALUES 1000) t(x)"); analyze("SELECT apply(1, x -> apply(10, y -> apply(100, z -> x))) FROM (VALUES 'abc') t(x)"); } @Test public void testLambdaInAggregationContext() { analyze("SELECT apply(sum(x), i -> i * i) FROM (VALUES 1, 2, 3, 4, 5) t(x)"); analyze("SELECT apply(x, i -> i - 1), sum(y) FROM (VALUES (1, 10), (1, 20), (2, 50)) t(x,y) group by x"); analyze("SELECT x, apply(sum(y), i -> i * 10) FROM (VALUES (1, 10), (1, 20), (2, 50)) t(x,y) group by x"); analyze("SELECT apply(8, x -> x + 1) FROM (VALUES (1, 2)) t(x,y) GROUP BY y"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(sum(x), i -> i * x) FROM (VALUES 1, 2, 3, 4, 5) t(x)"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(1, y -> x) FROM (VALUES (1,2)) t(x,y) GROUP BY y"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(1, y -> x.someField) FROM (VALUES (CAST(ROW(1) AS ROW(someField BIGINT)), 2)) t(x,y) GROUP BY y"); analyze("SELECT apply(CAST(ROW(1) AS ROW(someField BIGINT)), x -> x.someField) FROM (VALUES (1,2)) t(x,y) GROUP BY y"); analyze("SELECT apply(sum(x), x -> x * x) FROM (VALUES 1, 2, 3, 4, 5) t(x)"); // nested lambda expression uses the same variable name analyze("SELECT apply(sum(x), x -> apply(x, x -> x * x)) FROM (VALUES 1, 2, 3, 4, 5) t(x)"); // illegal use of a column whose name is the same as a lambda variable name assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(sum(x), x -> x * x) + x FROM (VALUES 1, 2, 3, 4, 5) t(x)"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(sum(x), x -> apply(x, x -> x * x)) + x FROM (VALUES 1, 2, 3, 4, 5) t(x)"); // x + y within lambda should not be treated as group expression assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, ".* must be an aggregate expression or appear in GROUP BY clause", "SELECT apply(1, y -> x + y) FROM (VALUES (1,2)) t(x, y) GROUP BY x+y"); } @Test public void testLambdaInSubqueryContext() { analyze("SELECT apply(x, i -> i * i) FROM (SELECT 10 x)"); analyze("SELECT apply((SELECT 10), i -> i * i)"); // with capture analyze("SELECT apply(x, i -> i * x) FROM (SELECT 10 x)"); analyze("SELECT apply(x, y -> y * x) FROM (SELECT 10 x, 3 y)"); analyze("SELECT apply(x, z -> y * x) FROM (SELECT 10 x, 3 y)"); } @Test public void testLambdaWithAggregation() throws Exception { assertFails( CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, ".* Lambda expression cannot contain aggregations or window functions: .*", "SELECT transform(ARRAY[1], y -> max(x)) FROM (VALUES 10) t(x)"); // use of aggregation/window function on lambda variable assertFails( CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, ".* Lambda expression cannot contain aggregations or window functions: .*", "SELECT apply(1, x -> max(x)) FROM (VALUES (1,2)) t(x,y) GROUP BY y"); assertFails( CANNOT_HAVE_AGGREGATIONS_OR_WINDOWS, ".* Lambda expression cannot contain aggregations or window functions: .*", "SELECT apply(CAST(ROW(1) AS ROW(someField BIGINT)), x -> max(x.someField)) FROM (VALUES (1,2)) t(x,y) GROUP BY y"); } @Test public void testLambdaWithSubquery() { assertFails( NOT_SUPPORTED, ".* Lambda expression cannot contain subqueries", "SELECT apply(1, i -> (SELECT 3)) FROM (VALUES 1) t(x)"); assertFails( NOT_SUPPORTED, ".* Lambda expression cannot contain subqueries", "SELECT apply(1, i -> (SELECT i)) FROM (VALUES 1) t(x)"); // GROUP BY column captured in lambda analyze( "SELECT (SELECT apply(0, x -> x + b) FROM (VALUES 1) x(a)) FROM t1 u GROUP BY b"); // non-GROUP BY column captured in lambda assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:34: Subquery uses '\"a\"' which must appear in GROUP BY clause", "SELECT (SELECT apply(0, x -> x + a) FROM (VALUES 1) x(c)) " + "FROM t1 u GROUP BY b"); // TODO #7784 // assertFails( // MUST_BE_AGGREGATE_OR_GROUP_BY, // "line 1:34: Subquery uses '\"u.a\"' which must appear in GROUP BY clause", // "SELECT (SELECT apply(0, x -> x + u.a) from (values 1) x(a)) " + // "FROM t1 u GROUP BY b"); // name shadowing analyze("SELECT (SELECT apply(0, x -> x + a) FROM (VALUES 1) x(a)) FROM t1 u GROUP BY b"); analyze("SELECT (SELECT apply(0, a -> a + a)) FROM t1 u GROUP BY b"); } @Test public void testLambdaWithSubqueryInOrderBy() { analyze("SELECT a FROM t1 ORDER BY (SELECT apply(0, x -> x + a))"); analyze("SELECT a AS output_column FROM t1 ORDER BY (SELECT apply(0, x -> x + output_column))"); analyze("SELECT count(*) FROM t1 GROUP BY a ORDER BY (SELECT apply(0, x -> x + a))"); analyze("SELECT count(*) AS output_column FROM t1 GROUP BY a ORDER BY (SELECT apply(0, x -> x + output_column))"); assertFails( MUST_BE_AGGREGATE_OR_GROUP_BY, "line 1:71: Subquery uses '\"b\"' which must appear in GROUP BY clause", "SELECT count(*) FROM t1 GROUP BY a ORDER BY (SELECT apply(0, x -> x + b))"); } @Test public void testInvalidDelete() throws Exception { assertFails(MISSING_TABLE, "DELETE FROM foo"); assertFails(NOT_SUPPORTED, "DELETE FROM v1"); assertFails(NOT_SUPPORTED, "DELETE FROM v1 WHERE a = 1"); } @Test public void testInvalidShowTables() { assertFails(INVALID_SCHEMA_NAME, "SHOW TABLES FROM a.b.c"); Session session = testSessionBuilder() .setCatalog(null) .setSchema(null) .build(); assertFails(session, CATALOG_NOT_SPECIFIED, "SHOW TABLES"); assertFails(session, CATALOG_NOT_SPECIFIED, "SHOW TABLES FROM a"); assertFails(session, MISSING_SCHEMA, "SHOW TABLES FROM c2.unknown"); session = testSessionBuilder() .setCatalog(SECOND_CATALOG) .setSchema(null) .build(); assertFails(session, SCHEMA_NOT_SPECIFIED, "SHOW TABLES"); assertFails(session, MISSING_SCHEMA, "SHOW TABLES FROM unknown"); } @Test public void testInvalidAtTimeZone() throws Exception { assertFails(TYPE_MISMATCH, "SELECT 'abc' AT TIME ZONE 'America/Los_Angeles'"); } @Test public void testValidJoinOnClause() throws Exception { analyze("SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON TRUE"); analyze("SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON 1=1"); analyze("SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON a.x=b.x AND a.y=b.y"); analyze("SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON NULL"); } @Test public void testInValidJoinOnClause() throws Exception { assertFails(TYPE_MISMATCH, "SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON 1"); assertFails(TYPE_MISMATCH, "SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON a.x + b.x"); assertFails(TYPE_MISMATCH, "SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON ROW (TRUE)"); assertFails(TYPE_MISMATCH, "SELECT * FROM (VALUES (2, 2)) a(x,y) JOIN (VALUES (2, 2)) b(x,y) ON (a.x=b.x, a.y=b.y)"); } @Test public void testInvalidAggregationFilter() throws Exception { assertFails(NOT_SUPPORTED, "SELECT sum(x) FILTER (WHERE x > 1) OVER (PARTITION BY x) FROM (VALUES (1), (2), (2), (4)) t (x)"); assertFails(NOT_SUPPORTED, "SELECT count(DISTINCT x) FILTER (where y = 1) FROM (VALUES (1, 1)) t(x, y)"); assertFails(MUST_BE_AGGREGATION_FUNCTION, "SELECT abs(x) FILTER (where y = 1) FROM (VALUES (1, 1)) t(x, y)"); assertFails(MUST_BE_AGGREGATION_FUNCTION, "SELECT abs(x) FILTER (where y = 1) FROM (VALUES (1, 1, 1)) t(x, y, z) GROUP BY z"); } @Test public void testQuantifiedComparisonExpression() throws Exception { analyze("SELECT * FROM t1 WHERE t1.a <= ALL (VALUES 10, 20)"); assertFails(MULTIPLE_FIELDS_FROM_SUBQUERY, "SELECT * FROM t1 WHERE t1.a = ANY (SELECT 1, 2)"); assertFails(TYPE_MISMATCH, "SELECT * FROM t1 WHERE t1.a = SOME (VALUES ('abc'))"); // map is not orderable assertFails(TYPE_MISMATCH, ("SELECT map(ARRAY[1], ARRAY['hello']) < ALL (VALUES map(ARRAY[1], ARRAY['hello']))")); // but map is comparable analyze(("SELECT map(ARRAY[1], ARRAY['hello']) = ALL (VALUES map(ARRAY[1], ARRAY['hello']))")); // HLL is neither orderable nor comparable assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS HyperLogLog) < ALL (VALUES cast(NULL AS HyperLogLog))"); assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS HyperLogLog) = ANY (VALUES cast(NULL AS HyperLogLog))"); } @BeforeMethod(alwaysRun = true) public void setup() throws Exception { TypeManager typeManager = new TypeRegistry(); CatalogManager catalogManager = new CatalogManager(); transactionManager = createTestTransactionManager(catalogManager); accessControl = new AccessControlManager(transactionManager); metadata = new MetadataManager( new FeaturesConfig(), typeManager, new BlockEncodingManager(typeManager), new SessionPropertyManager(), new SchemaPropertyManager(), new TablePropertyManager(), transactionManager); metadata.getFunctionRegistry().addFunctions(ImmutableList.of(APPLY_FUNCTION)); catalogManager.registerCatalog(createTestingCatalog(TPCH_CATALOG, TPCH_CONNECTOR_ID)); catalogManager.registerCatalog(createTestingCatalog(SECOND_CATALOG, SECOND_CONNECTOR_ID)); catalogManager.registerCatalog(createTestingCatalog(THIRD_CATALOG, THIRD_CONNECTOR_ID)); SchemaTableName table1 = new SchemaTableName("s1", "t1"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table1, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT), new ColumnMetadata("c", BIGINT), new ColumnMetadata("d", BIGINT))))); SchemaTableName table2 = new SchemaTableName("s1", "t2"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table2, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT))))); SchemaTableName table3 = new SchemaTableName("s1", "t3"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table3, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT), new ColumnMetadata("x", BIGINT, null, true))))); // table in different catalog SchemaTableName table4 = new SchemaTableName("s2", "t4"); inSetupTransaction(session -> metadata.createTable(session, SECOND_CATALOG, new ConnectorTableMetadata(table4, ImmutableList.of( new ColumnMetadata("a", BIGINT))))); // table with a hidden column SchemaTableName table5 = new SchemaTableName("s1", "t5"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table5, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT, null, true))))); // table with a varchar column SchemaTableName table6 = new SchemaTableName("s1", "t6"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table6, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", VARCHAR), new ColumnMetadata("c", BIGINT), new ColumnMetadata("d", BIGINT))))); // table with bigint, double, array of bigints and array of doubles column SchemaTableName table7 = new SchemaTableName("s1", "t7"); inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table7, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", DOUBLE), new ColumnMetadata("c", new ArrayType(BIGINT)), new ColumnMetadata("d", new ArrayType(DOUBLE)))))); // valid view referencing table in same schema String viewData1 = JsonCodec.jsonCodec(ViewDefinition.class).toJson( new ViewDefinition( "select a from t1", Optional.of(TPCH_CATALOG), Optional.of("s1"), ImmutableList.of(new ViewColumn("a", BIGINT)), Optional.of("user"))); inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v1"), viewData1, false)); // stale view (different column type) String viewData2 = JsonCodec.jsonCodec(ViewDefinition.class).toJson( new ViewDefinition( "select a from t1", Optional.of(TPCH_CATALOG), Optional.of("s1"), ImmutableList.of(new ViewColumn("a", VARCHAR)), Optional.of("user"))); inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v2"), viewData2, false)); // view referencing table in different schema from itself and session String viewData3 = JsonCodec.jsonCodec(ViewDefinition.class).toJson( new ViewDefinition( "select a from t4", Optional.of(SECOND_CATALOG), Optional.of("s2"), ImmutableList.of(new ViewColumn("a", BIGINT)), Optional.of("owner"))); inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(THIRD_CATALOG, "s3", "v3"), viewData3, false)); // valid view with uppercase column name String viewData4 = JsonCodec.jsonCodec(ViewDefinition.class).toJson( new ViewDefinition( "select A from t1", Optional.of("tpch"), Optional.of("s1"), ImmutableList.of(new ViewColumn("a", BIGINT)), Optional.of("user"))); inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName("tpch", "s1", "v4"), viewData4, false)); // recursive view referencing to itself String viewData5 = JsonCodec.jsonCodec(ViewDefinition.class).toJson( new ViewDefinition( "select * from v5", Optional.of(TPCH_CATALOG), Optional.of("s1"), ImmutableList.of(new ViewColumn("a", BIGINT)), Optional.of("user"))); inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v5"), viewData5, false)); this.metadata = metadata; } private void inSetupTransaction(Consumer<Session> consumer) { transaction(transactionManager, accessControl) .singleStatement() .readUncommitted() .execute(SETUP_SESSION, consumer); } private static Analyzer createAnalyzer(Session session, Metadata metadata) { return new Analyzer( session, metadata, SQL_PARSER, new AllowAllAccessControl(), Optional.empty(), emptyList()); } private void analyze(@Language("SQL") String query) { analyze(CLIENT_SESSION, query); } private void analyze(Session clientSession, @Language("SQL") String query) { transaction(transactionManager, accessControl) .singleStatement() .readUncommitted() .readOnly() .execute(clientSession, session -> { Analyzer analyzer = createAnalyzer(session, metadata); Statement statement = SQL_PARSER.createStatement(query); analyzer.analyze(statement); }); } private void assertFails(SemanticErrorCode error, @Language("SQL") String query) { assertFails(CLIENT_SESSION, error, query); } private void assertFails(SemanticErrorCode error, int line, int column, @Language("SQL") String query) { assertFails(CLIENT_SESSION, error, Optional.of(new NodeLocation(line, column - 1)), query); } private void assertFails(SemanticErrorCode error, String message, @Language("SQL") String query) { assertFails(CLIENT_SESSION, error, message, query); } private void assertFails(Session session, SemanticErrorCode error, @Language("SQL") String query) { assertFails(session, error, Optional.empty(), query); } private void assertFails(Session session, SemanticErrorCode error, Optional<NodeLocation> location, @Language("SQL") String query) { try { analyze(session, query); fail(format("Expected error %s, but analysis succeeded", error)); } catch (SemanticException e) { if (e.getCode() != error) { fail(format("Expected error %s, but found %s: %s", error, e.getCode(), e.getMessage()), e); } if (location.isPresent()) { NodeLocation expected = location.get(); NodeLocation actual = e.getNode().getLocation().get(); if (expected.getLineNumber() != actual.getLineNumber() || expected.getColumnNumber() != actual.getColumnNumber()) { fail(format( "Expected error '%s' to occur at line %s, offset %s, but was: line %s, offset %s", e.getCode(), expected.getLineNumber(), expected.getColumnNumber(), actual.getLineNumber(), actual.getColumnNumber())); } } } } private void assertFails(Session session, SemanticErrorCode error, String message, @Language("SQL") String query) { try { analyze(session, query); fail(format("Expected error %s, but analysis succeeded", error)); } catch (SemanticException e) { if (e.getCode() != error) { fail(format("Expected error %s, but found %s: %s", error, e.getCode(), e.getMessage()), e); } if (!e.getMessage().matches(message)) { fail(format("Expected error '%s', but got '%s'", message, e.getMessage()), e); } } } private Catalog createTestingCatalog(String catalogName, ConnectorId connectorId) { ConnectorId systemId = createSystemTablesConnectorId(connectorId); Connector connector = createTestingConnector(); InternalNodeManager nodeManager = new InMemoryNodeManager(); return new Catalog( catalogName, connectorId, connector, createInformationSchemaConnectorId(connectorId), new InformationSchemaConnector(catalogName, nodeManager, metadata, accessControl), systemId, new SystemConnector( systemId, nodeManager, connector.getSystemTables(), transactionId -> transactionManager.getConnectorTransaction(transactionId, connectorId))); } private static Connector createTestingConnector() { return new Connector() { private final ConnectorMetadata metadata = new TestingMetadata(); @Override public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel, boolean readOnly) { return new ConnectorTransactionHandle() {}; } @Override public ConnectorMetadata getMetadata(ConnectorTransactionHandle transaction) { return metadata; } @Override public ConnectorSplitManager getSplitManager() { throw new UnsupportedOperationException(); } }; } }