diff --git a/presto-docs/src/main/sphinx/language/reserved.rst b/presto-docs/src/main/sphinx/language/reserved.rst index bc39537fdc5e..dafa509013f9 100644 --- a/presto-docs/src/main/sphinx/language/reserved.rst +++ b/presto-docs/src/main/sphinx/language/reserved.rst @@ -22,6 +22,7 @@ Keyword SQL:2016 SQL-92 ``CUBE`` reserved ``CURRENT_DATE`` reserved reserved ``CURRENT_PATH`` reserved +``CURRENT_ROLE`` reserved reserved ``CURRENT_TIME`` reserved reserved ``CURRENT_TIMESTAMP`` reserved reserved ``CURRENT_USER`` reserved diff --git a/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 b/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 index 926abcf20974..a519ccbc6912 100644 --- a/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 +++ b/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 @@ -60,6 +60,10 @@ statement (SECURITY (DEFINER | INVOKER))? AS query #createView | DROP VIEW (IF EXISTS)? qualifiedName #dropView | CALL qualifiedName '(' (callArgument (',' callArgument)*)? ')' #call + | CREATE ROLE name=identifier + (WITH ADMIN grantor)? + (IN catalog=identifier)? #createRole + | DROP ROLE name=identifier (IN catalog=identifier)? #dropRole | GRANT (privilege (',' privilege)* | ALL PRIVILEGES) ON TABLE? qualifiedName TO grantee=identifier @@ -437,6 +441,18 @@ qualifiedName : identifier ('.' identifier)* ; +grantor + : principal #specifiedPrincipal + | CURRENT_USER #currentUserGrantor + | CURRENT_ROLE #currentRoleGrantor + ; + +principal + : identifier #unspecifiedPrincipal + | USER identifier #userPrincipal + | ROLE identifier #rolePrincipal + ; + identifier : IDENTIFIER #unquotedIdentifier | QUOTED_IDENTIFIER #quotedIdentifier @@ -453,7 +469,7 @@ number nonReserved // IMPORTANT: this rule must only contain tokens. Nested rules are not supported. See SqlParser.exitNonReserved - : ADD | ALL | ANALYZE | ANY | ARRAY | ASC | AT + : ADD | ADMIN | ALL | ANALYZE | ANY | ARRAY | ASC | AT | BERNOULLI | CALL | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | CURRENT | DATA | DATE | DAY | DEFINER | DESC | DISTRIBUTED @@ -468,11 +484,11 @@ nonReserved | NFC | NFD | NFKC | NFKD | NO | NULLIF | NULLS | ONLY | OPTION | ORDINALITY | OUTPUT | OVER | PARTITION | PARTITIONS | PATH | POSITION | PRECEDING | PRIVILEGES | PROPERTIES - | RANGE | READ | RENAME | REPEATABLE | REPLACE | RESET | RESTRICT | REVOKE | ROLLBACK | ROW | ROWS + | RANGE | READ | RENAME | REPEATABLE | REPLACE | RESET | RESTRICT | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS | SCHEMA | SCHEMAS | SECOND | SECURITY | SERIALIZABLE | SESSION | SET | SETS | SHOW | SOME | START | STATS | SUBSTRING | SYSTEM | TABLES | TABLESAMPLE | TEXT | TIME | TIMESTAMP | TO | TRANSACTION | TRY_CAST | TYPE - | UNBOUNDED | UNCOMMITTED | USE + | UNBOUNDED | UNCOMMITTED | USE | USER | VALIDATE | VERBOSE | VIEW | WORK | WRITE | YEAR @@ -480,6 +496,7 @@ nonReserved ; ADD: 'ADD'; +ADMIN: 'ADMIN'; ALL: 'ALL'; ALTER: 'ALTER'; ANALYZE: 'ANALYZE'; @@ -509,6 +526,7 @@ CUBE: 'CUBE'; CURRENT: 'CURRENT'; CURRENT_DATE: 'CURRENT_DATE'; CURRENT_PATH: 'CURRENT_PATH'; +CURRENT_ROLE: 'CURRENT_ROLE'; CURRENT_TIME: 'CURRENT_TIME'; CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'; CURRENT_USER: 'CURRENT_USER'; @@ -613,6 +631,7 @@ RESET: 'RESET'; RESTRICT: 'RESTRICT'; REVOKE: 'REVOKE'; RIGHT: 'RIGHT'; +ROLE: 'ROLE'; ROLLBACK: 'ROLLBACK'; ROLLUP: 'ROLLUP'; ROW: 'ROW'; @@ -650,6 +669,7 @@ UNCOMMITTED: 'UNCOMMITTED'; UNION: 'UNION'; UNNEST: 'UNNEST'; USE: 'USE'; +USER: 'USER'; USING: 'USING'; VALIDATE: 'VALIDATE'; VALUES: 'VALUES'; diff --git a/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java b/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java index 91d5593ac1e9..2374378ab454 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java +++ b/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java @@ -23,6 +23,7 @@ import io.prestosql.sql.tree.CallArgument; import io.prestosql.sql.tree.ColumnDefinition; import io.prestosql.sql.tree.Commit; +import io.prestosql.sql.tree.CreateRole; import io.prestosql.sql.tree.CreateSchema; import io.prestosql.sql.tree.CreateTable; import io.prestosql.sql.tree.CreateTableAsSelect; @@ -32,6 +33,7 @@ import io.prestosql.sql.tree.DescribeInput; import io.prestosql.sql.tree.DescribeOutput; import io.prestosql.sql.tree.DropColumn; +import io.prestosql.sql.tree.DropRole; import io.prestosql.sql.tree.DropSchema; import io.prestosql.sql.tree.DropTable; import io.prestosql.sql.tree.DropView; @@ -43,6 +45,7 @@ import io.prestosql.sql.tree.ExplainType; import io.prestosql.sql.tree.Expression; import io.prestosql.sql.tree.Grant; +import io.prestosql.sql.tree.GrantorSpecification; import io.prestosql.sql.tree.Identifier; import io.prestosql.sql.tree.Insert; import io.prestosql.sql.tree.Intersect; @@ -57,6 +60,7 @@ import io.prestosql.sql.tree.Node; import io.prestosql.sql.tree.OrderBy; import io.prestosql.sql.tree.Prepare; +import io.prestosql.sql.tree.PrincipalSpecification; import io.prestosql.sql.tree.Property; import io.prestosql.sql.tree.QualifiedName; import io.prestosql.sql.tree.Query; @@ -872,6 +876,34 @@ private String formatColumnDefinition(ColumnDefinition column) formatPropertiesSingleLine(column.getProperties()); } + private static String formatGrantor(GrantorSpecification grantor) + { + GrantorSpecification.Type type = grantor.getType(); + switch (type) { + case CURRENT_ROLE: + case CURRENT_USER: + return type.name(); + case PRINCIPAL: + return formatPrincipal(grantor.getPrincipal().get()); + default: + throw new IllegalArgumentException("Unsupported principal type: " + type); + } + } + + private static String formatPrincipal(PrincipalSpecification principal) + { + PrincipalSpecification.Type type = principal.getType(); + switch (type) { + case UNSPECIFIED: + return principal.getName().toString(); + case USER: + case ROLE: + return format("%s %s", type.name(), principal.getName().toString()); + default: + throw new IllegalArgumentException("Unsupported principal type: " + type); + } + } + @Override protected Void visitDropTable(DropTable node, Integer context) { @@ -1061,6 +1093,29 @@ protected Void visitRollback(Rollback node, Integer context) return null; } + @Override + protected Void visitCreateRole(CreateRole node, Integer context) + { + builder.append("CREATE ROLE ").append(node.getName()); + if (node.getGrantor().isPresent()) { + builder.append(" WITH ADMIN ").append(formatGrantor(node.getGrantor().get())); + } + if (node.getCatalog().isPresent()) { + builder.append(" IN ").append(node.getCatalog().get()); + } + return null; + } + + @Override + protected Void visitDropRole(DropRole node, Integer context) + { + builder.append("DROP ROLE ").append(node.getName()); + if (node.getCatalog().isPresent()) { + builder.append(" IN ").append(node.getCatalog().get()); + } + return null; + } + @Override public Void visitGrant(Grant node, Integer indent) { diff --git a/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java b/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java index a560a6a0dc14..9f99da3f0843 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java +++ b/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java @@ -34,6 +34,7 @@ import io.prestosql.sql.tree.ColumnDefinition; import io.prestosql.sql.tree.Commit; import io.prestosql.sql.tree.ComparisonExpression; +import io.prestosql.sql.tree.CreateRole; import io.prestosql.sql.tree.CreateSchema; import io.prestosql.sql.tree.CreateTable; import io.prestosql.sql.tree.CreateTableAsSelect; @@ -50,6 +51,7 @@ import io.prestosql.sql.tree.DescribeOutput; import io.prestosql.sql.tree.DoubleLiteral; import io.prestosql.sql.tree.DropColumn; +import io.prestosql.sql.tree.DropRole; import io.prestosql.sql.tree.DropSchema; import io.prestosql.sql.tree.DropTable; import io.prestosql.sql.tree.DropView; @@ -66,6 +68,7 @@ import io.prestosql.sql.tree.FunctionCall; import io.prestosql.sql.tree.GenericLiteral; import io.prestosql.sql.tree.Grant; +import io.prestosql.sql.tree.GrantorSpecification; import io.prestosql.sql.tree.GroupBy; import io.prestosql.sql.tree.GroupingElement; import io.prestosql.sql.tree.GroupingOperation; @@ -102,6 +105,7 @@ import io.prestosql.sql.tree.PathElement; import io.prestosql.sql.tree.PathSpecification; import io.prestosql.sql.tree.Prepare; +import io.prestosql.sql.tree.PrincipalSpecification; import io.prestosql.sql.tree.Property; import io.prestosql.sql.tree.QualifiedName; import io.prestosql.sql.tree.QuantifiedComparisonExpression; @@ -814,6 +818,25 @@ public Node visitResetSession(SqlBaseParser.ResetSessionContext context) return new ResetSession(getLocation(context), getQualifiedName(context.qualifiedName())); } + @Override + public Node visitCreateRole(SqlBaseParser.CreateRoleContext context) + { + return new CreateRole( + getLocation(context), + (Identifier) visit(context.name), + getGrantorSpecificationIfPresent(context.grantor()), + getIdentifierIfPresent(context.catalog)); + } + + @Override + public Node visitDropRole(SqlBaseParser.DropRoleContext context) + { + return new DropRole( + getLocation(context), + (Identifier) visit(context.name), + getIdentifierIfPresent(context.catalog)); + } + @Override public Node visitGrant(SqlBaseParser.GrantContext context) { @@ -1875,6 +1898,11 @@ private static Optional getTextIfPresent(Token token) .map(Token::getText); } + private Optional getIdentifierIfPresent(ParserRuleContext context) + { + return Optional.ofNullable(context).map(c -> (Identifier) visit(c)); + } + private static ArithmeticBinaryExpression.Operator getArithmeticBinaryOperator(Token operator) { switch (operator.getType()) { @@ -2121,6 +2149,43 @@ private String typeParameterToString(SqlBaseParser.TypeParameterContext typePara throw new IllegalArgumentException("Unsupported typeParameter: " + typeParameter.getText()); } + private Optional getGrantorSpecificationIfPresent(SqlBaseParser.GrantorContext context) + { + return Optional.ofNullable(context).map(this::getGrantorSpecification); + } + + private GrantorSpecification getGrantorSpecification(SqlBaseParser.GrantorContext context) + { + if (context instanceof SqlBaseParser.SpecifiedPrincipalContext) { + return new GrantorSpecification(GrantorSpecification.Type.PRINCIPAL, Optional.of(getPrincipalSpecification(((SqlBaseParser.SpecifiedPrincipalContext) context).principal()))); + } + else if (context instanceof SqlBaseParser.CurrentUserGrantorContext) { + return new GrantorSpecification(GrantorSpecification.Type.CURRENT_USER, Optional.empty()); + } + else if (context instanceof SqlBaseParser.CurrentRoleGrantorContext) { + return new GrantorSpecification(GrantorSpecification.Type.CURRENT_ROLE, Optional.empty()); + } + else { + throw new IllegalArgumentException("Unsupported grantor: " + context); + } + } + + private PrincipalSpecification getPrincipalSpecification(SqlBaseParser.PrincipalContext context) + { + if (context instanceof SqlBaseParser.UnspecifiedPrincipalContext) { + return new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, (Identifier) visit(((SqlBaseParser.UnspecifiedPrincipalContext) context).identifier())); + } + else if (context instanceof SqlBaseParser.UserPrincipalContext) { + return new PrincipalSpecification(PrincipalSpecification.Type.USER, (Identifier) visit(((SqlBaseParser.UserPrincipalContext) context).identifier())); + } + else if (context instanceof SqlBaseParser.RolePrincipalContext) { + return new PrincipalSpecification(PrincipalSpecification.Type.ROLE, (Identifier) visit(((SqlBaseParser.RolePrincipalContext) context).identifier())); + } + else { + throw new IllegalArgumentException("Unsupported principal: " + context); + } + } + private static void check(boolean condition, String message, ParserRuleContext context) { if (!condition) { diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/AstVisitor.java b/presto-parser/src/main/java/io/prestosql/sql/tree/AstVisitor.java index f466e708d27d..68dc33e42a71 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/tree/AstVisitor.java +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/AstVisitor.java @@ -597,6 +597,16 @@ protected R visitStartTransaction(StartTransaction node, C context) return visitStatement(node, context); } + protected R visitCreateRole(CreateRole node, C context) + { + return visitStatement(node, context); + } + + protected R visitDropRole(DropRole node, C context) + { + return visitStatement(node, context); + } + protected R visitGrant(Grant node, C context) { return visitStatement(node, context); diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/CreateRole.java b/presto-parser/src/main/java/io/prestosql/sql/tree/CreateRole.java new file mode 100644 index 000000000000..fcfbb8c3dba3 --- /dev/null +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/CreateRole.java @@ -0,0 +1,107 @@ +/* + * 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 io.prestosql.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class CreateRole + extends Statement +{ + private final Identifier name; + private final Optional grantor; + private final Optional catalog; + + public CreateRole(Identifier name, Optional grantor, Optional catalog) + { + this(Optional.empty(), name, grantor, catalog); + } + + public CreateRole(NodeLocation location, Identifier name, Optional grantor, Optional catalog) + { + this(Optional.of(location), name, grantor, catalog); + } + + private CreateRole(Optional location, Identifier name, Optional grantor, Optional catalog) + { + super(location); + this.name = requireNonNull(name, "name is null"); + this.grantor = requireNonNull(grantor, "grantor is null"); + this.catalog = requireNonNull(catalog, "catalog is null"); + } + + public Identifier getName() + { + return name; + } + + public Optional getGrantor() + { + return grantor; + } + + public Optional getCatalog() + { + return catalog; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateRole that = (CreateRole) o; + return Objects.equals(name, that.name) && + Objects.equals(grantor, that.grantor) && + Objects.equals(catalog, that.catalog); + } + + @Override + public int hashCode() + { + return Objects.hash(name, grantor, catalog); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("name", name) + .add("grantor", grantor) + .add("catalog", catalog) + .toString(); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitCreateRole(this, context); + } +} diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/DropRole.java b/presto-parser/src/main/java/io/prestosql/sql/tree/DropRole.java new file mode 100644 index 000000000000..ee613b60787d --- /dev/null +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/DropRole.java @@ -0,0 +1,98 @@ +/* + * 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 io.prestosql.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class DropRole + extends Statement +{ + private final Identifier name; + private final Optional catalog; + + public DropRole(Identifier name, Optional catalog) + { + this(Optional.empty(), name, catalog); + } + + public DropRole(NodeLocation location, Identifier name, Optional catalog) + { + this(Optional.of(location), name, catalog); + } + + private DropRole(Optional location, Identifier name, Optional catalog) + { + super(location); + this.name = requireNonNull(name, "name is null"); + this.catalog = requireNonNull(catalog, "catalog is null"); + } + + public Identifier getName() + { + return name; + } + + public Optional getCatalog() + { + return catalog; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DropRole dropRole = (DropRole) o; + return Objects.equals(name, dropRole.name) && + Objects.equals(catalog, dropRole.catalog); + } + + @Override + public int hashCode() + { + return Objects.hash(name, catalog); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("name", name) + .add("catalog", catalog) + .toString(); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitDropRole(this, context); + } +} diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/GrantorSpecification.java b/presto-parser/src/main/java/io/prestosql/sql/tree/GrantorSpecification.java new file mode 100644 index 000000000000..ebe5abdaf3e5 --- /dev/null +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/GrantorSpecification.java @@ -0,0 +1,76 @@ +/* + * 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 io.prestosql.sql.tree; + +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class GrantorSpecification +{ + public enum Type + { + PRINCIPAL, CURRENT_USER, CURRENT_ROLE + } + + private final Type type; + private final Optional principal; + + public GrantorSpecification(Type type, Optional principal) + { + this.type = requireNonNull(type, "type is null"); + this.principal = requireNonNull(principal, "principal is null"); + } + + public Type getType() + { + return type; + } + + public Optional getPrincipal() + { + return principal; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GrantorSpecification that = (GrantorSpecification) o; + return type == that.type && + Objects.equals(principal, that.principal); + } + + @Override + public int hashCode() + { + return Objects.hash(type, principal); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("type", type) + .add("principal", principal) + .toString(); + } +} diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/PrincipalSpecification.java b/presto-parser/src/main/java/io/prestosql/sql/tree/PrincipalSpecification.java new file mode 100644 index 000000000000..4828eb857f13 --- /dev/null +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/PrincipalSpecification.java @@ -0,0 +1,75 @@ +/* + * 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 io.prestosql.sql.tree; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class PrincipalSpecification +{ + public enum Type + { + UNSPECIFIED, USER, ROLE + } + + private final Type type; + private final Identifier name; + + public PrincipalSpecification(Type type, Identifier name) + { + this.type = requireNonNull(type, "type is null"); + this.name = requireNonNull(name, "name is null"); + } + + public Type getType() + { + return type; + } + + public Identifier getName() + { + return name; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PrincipalSpecification that = (PrincipalSpecification) o; + return type == that.type && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() + { + return Objects.hash(type, name); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("type", type) + .add("name", name) + .toString(); + } +} diff --git a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java index 5a6f9d900469..e8f48ac8f710 100644 --- a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java +++ b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java @@ -32,6 +32,7 @@ import io.prestosql.sql.tree.ColumnDefinition; import io.prestosql.sql.tree.Commit; import io.prestosql.sql.tree.ComparisonExpression; +import io.prestosql.sql.tree.CreateRole; import io.prestosql.sql.tree.CreateSchema; import io.prestosql.sql.tree.CreateTable; import io.prestosql.sql.tree.CreateTableAsSelect; @@ -46,6 +47,7 @@ import io.prestosql.sql.tree.DescribeOutput; import io.prestosql.sql.tree.DoubleLiteral; import io.prestosql.sql.tree.DropColumn; +import io.prestosql.sql.tree.DropRole; import io.prestosql.sql.tree.DropSchema; import io.prestosql.sql.tree.DropTable; import io.prestosql.sql.tree.DropView; @@ -58,6 +60,7 @@ import io.prestosql.sql.tree.FunctionCall; import io.prestosql.sql.tree.GenericLiteral; import io.prestosql.sql.tree.Grant; +import io.prestosql.sql.tree.GrantorSpecification; import io.prestosql.sql.tree.GroupBy; import io.prestosql.sql.tree.GroupingOperation; import io.prestosql.sql.tree.GroupingSets; @@ -87,6 +90,7 @@ import io.prestosql.sql.tree.PathElement; import io.prestosql.sql.tree.PathSpecification; import io.prestosql.sql.tree.Prepare; +import io.prestosql.sql.tree.PrincipalSpecification; import io.prestosql.sql.tree.Property; import io.prestosql.sql.tree.QualifiedName; import io.prestosql.sql.tree.QuantifiedComparisonExpression; @@ -1984,6 +1988,92 @@ public void testAggregationWithOrderBy() Optional.empty())); } + @Test + public void testCreateRole() + throws Exception + { + assertStatement("CREATE ROLE role", new CreateRole(new Identifier("role"), Optional.empty(), Optional.empty())); + assertStatement("CREATE ROLE role1 WITH ADMIN admin", + new CreateRole( + new Identifier("role1"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("admin"))))), + Optional.empty())); + assertStatement("CREATE ROLE role2 WITH ADMIN admin1 IN catalog", + new CreateRole( + new Identifier("role2"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("admin1"))))), + Optional.of(new Identifier("catalog")))); + assertStatement("CREATE ROLE role3 IN catalog1", + new CreateRole( + new Identifier("role3"), + Optional.empty(), + Optional.of(new Identifier("catalog1")))); + assertStatement("CREATE ROLE \"role\" WITH ADMIN \"admin\" IN \"catalog\"", + new CreateRole( + new Identifier("role"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("admin"))))), + Optional.of(new Identifier("catalog")))); + assertStatement("CREATE ROLE \"ro le\" WITH ADMIN \"ad min\" IN \"ca talog\"", + new CreateRole( + new Identifier("ro le"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("ad min"))))), + Optional.of(new Identifier("ca talog")))); + assertStatement("CREATE ROLE \"!@#$%^&*'\" WITH ADMIN \"ад\"\"мін\" IN \"カタログ\"", + new CreateRole( + new Identifier("!@#$%^&*'"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("ад\"мін"))))), + Optional.of(new Identifier("カタログ")))); + assertStatement("CREATE ROLE role2 WITH ADMIN USER admin1 IN catalog", + new CreateRole( + new Identifier("role2"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("admin1"))))), + Optional.of(new Identifier("catalog")))); + assertStatement("CREATE ROLE role2 WITH ADMIN ROLE role1 IN catalog", + new CreateRole( + new Identifier("role2"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.PRINCIPAL, + Optional.of(new PrincipalSpecification(PrincipalSpecification.Type.ROLE, new Identifier("role1"))))), + Optional.of(new Identifier("catalog")))); + assertStatement("CREATE ROLE role2 WITH ADMIN CURRENT_USER IN catalog", + new CreateRole( + new Identifier("role2"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.CURRENT_USER, + Optional.empty())), + Optional.of(new Identifier("catalog")))); + assertStatement("CREATE ROLE role2 WITH ADMIN CURRENT_ROLE IN catalog", + new CreateRole( + new Identifier("role2"), + Optional.of(new GrantorSpecification( + GrantorSpecification.Type.CURRENT_ROLE, + Optional.empty())), + Optional.of(new Identifier("catalog")))); + } + + @Test + public void testDropRole() + throws Exception + { + assertStatement("DROP ROLE role", new DropRole(new Identifier("role"), Optional.empty())); + assertStatement("DROP ROLE role1 IN catalog", new DropRole(new Identifier("role1"), Optional.of(new Identifier("catalog")))); + assertStatement("DROP ROLE \"role\" IN \"catalog\"", new DropRole(new Identifier("role"), Optional.of(new Identifier("catalog")))); + assertStatement("DROP ROLE \"ro le\" IN \"ca talog\"", new DropRole(new Identifier("ro le"), Optional.of(new Identifier("ca talog")))); + assertStatement("DROP ROLE \"!@#$%^&*'ад\"\"мін\" IN \"カタログ\"", new DropRole(new Identifier("!@#$%^&*'ад\"мін"), Optional.of(new Identifier("カタログ")))); + } + private static void assertCast(String type) { assertCast(type, type); diff --git a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParserErrorHandling.java b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParserErrorHandling.java index 7cfbd5dfd61a..e097530cbf33 100644 --- a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParserErrorHandling.java +++ b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParserErrorHandling.java @@ -82,9 +82,11 @@ public Object[][] getStatements() {"select foo(DISTINCT ,1)", "line 1:21: mismatched input ','. Expecting: "}, {"CREATE TABLE foo () AS (VALUES 1)", - "line 1:19: mismatched input ')'. Expecting: 'OR', 'SCHEMA', 'TABLE', 'VIEW'"}, + "line 1:19: mismatched input ')'. Expecting: 'OR', 'ROLE', 'SCHEMA', 'TABLE', 'VIEW'"}, {"CREATE TABLE foo (*) AS (VALUES 1)", - "line 1:19: mismatched input '*'. Expecting: 'OR', 'SCHEMA', 'TABLE', 'VIEW'"}, + "line 1:19: mismatched input '*'. Expecting: 'OR', 'ROLE', 'SCHEMA', 'TABLE', 'VIEW'"}, + {"CREATE TABLE foo (*) AS (VALUES 1)", + "line 1:19: mismatched input '*'. Expecting: 'OR', 'ROLE', 'SCHEMA', 'TABLE', 'VIEW'"}, {"SELECT grouping(a+2) FROM (VALUES (1)) AS t (a) GROUP BY a+2", "line 1:18: mismatched input '+'. Expecting: ')', ','"}, {"SELECT x() over (ROWS select) FROM t",