| /* | |
| * 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. | |
| */ | |
| // Druid catalog DDL. These statements write catalog metadata only; they do not create or delete data. | |
| // | |
| // CREATE TABLE is reached through the standard Calcite SqlCreate() production, which has already consumed | |
| // CREATE [OR REPLACE], so this production must not consume <EOF>: the enclosing SqlStmtList() handles statement | |
| // separators. ALTER TABLE is a top-level statement production instead, because Calcite's stock SqlAlter() mandates | |
| // a SYSTEM or SESSION scope that does not apply here. | |
| SqlCreate DruidSqlCreateTable(Span s, boolean replace) : | |
| { | |
| boolean ifNotExists = false; | |
| final SqlIdentifier id; | |
| final List<SqlNode> columns = new ArrayList<SqlNode>(); | |
| final List<SqlNode> projections = new ArrayList<SqlNode>(); | |
| Span elementSpan = null; | |
| SqlGranularityLiteral partitionedBy = null; | |
| SqlNodeList clusteredBy = null; | |
| boolean sealed = false; | |
| } | |
| { | |
| <TABLE> | |
| [ <IF> <NOT> <EXISTS> { ifNotExists = true; } ] | |
| id = CompoundTableIdentifier() | |
| [ | |
| // SEALED binds to the column list rather than trailing the statement: it declares that the list is the table's | |
| // whole schema, so it is only accepted when there is a list for it to describe. | |
| [ <SEALED> { sealed = true; } ] | |
| <LPAREN> { elementSpan = span(); } | |
| AddDruidTableElement(columns, projections) | |
| ( | |
| <COMMA> AddDruidTableElement(columns, projections) | |
| )* | |
| <RPAREN> | |
| ] | |
| [ | |
| <PARTITIONED> <BY> | |
| partitionedBy = PartitionGranularity() | |
| ] | |
| [ | |
| clusteredBy = ClusteredBy() | |
| ] | |
| { | |
| final SqlParserPos elementPos = elementSpan == null ? s.pos() : elementSpan.end(this); | |
| return new DruidSqlCreateTable( | |
| s.end(this), | |
| replace, | |
| ifNotExists, | |
| id, | |
| new SqlNodeList(columns, elementPos), | |
| new SqlNodeList(projections, elementPos), | |
| partitionedBy, | |
| clusteredBy, | |
| sealed | |
| ); | |
| } | |
| } | |
| // A table element is either a column declaration or a projection definition. A column may legitimately be named | |
| // "projection" (the keyword is non-reserved), and its type may itself start with a non-reserved keyword and a | |
| // parenthesis: "projection TYPE('COMPLEX<json>')" is a column. So neither two nor three tokens are enough to tell | |
| // the two apart. A projection is recognized by what follows its name: AS, or a parenthesized body starting with | |
| // SELECT; a column's TYPE(...) parenthesis holds a string literal, never SELECT. | |
| void AddDruidTableElement(List<SqlNode> columns, List<SqlNode> projections) : | |
| { | |
| final DruidSqlColumnDeclaration column; | |
| final SqlProjectionSpec projection; | |
| } | |
| { | |
| LOOKAHEAD(<PROJECTION> SimpleIdentifier() ( <AS> | <LPAREN> <SELECT> )) | |
| <PROJECTION> projection = DruidProjectionDefinition() | |
| { | |
| projections.add(projection); | |
| } | |
| | | |
| column = DruidColumnDeclaration() | |
| { | |
| columns.add(column); | |
| } | |
| } | |
| // The body is a SELECT with no FROM: the table the projection belongs to is implicit. Only a select list, WHERE and | |
| // GROUP BY are admitted; a projection has no way to express ordering, limits or having. | |
| SqlProjectionSpec DruidProjectionDefinition() : | |
| { | |
| final Span s; | |
| final Span bodySpan; | |
| final SqlIdentifier name; | |
| final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>(); | |
| final SqlNodeList keywordList; | |
| final List<SqlNode> selectList = new ArrayList<SqlNode>(); | |
| SqlLiteral keyword = null; | |
| final SqlNode where; | |
| final SqlNodeList groupBy; | |
| SqlNodeList clusteredBy = null; | |
| } | |
| { | |
| { s = span(); } | |
| name = SimpleIdentifier() | |
| [ <AS> ] | |
| <LPAREN> | |
| <SELECT> { bodySpan = span(); } | |
| [ keyword = AllOrDistinct() { keywords.add(keyword); } ] | |
| { keywordList = new SqlNodeList(keywords, bodySpan.addAll(keywords).pos()); } | |
| AddSelectItem(selectList) | |
| ( | |
| <COMMA> AddSelectItem(selectList) | |
| )* | |
| ( where = Where() | { where = null; } ) | |
| ( groupBy = GroupBy() | { groupBy = null; } ) | |
| // Only meaningful for the reserved __base projection, where it names the columns segments are clustered on. | |
| [ clusteredBy = ClusteredBy() ] | |
| <RPAREN> | |
| { | |
| return new SqlProjectionSpec( | |
| s.end(this), | |
| name, | |
| clusteredBy, | |
| new SqlSelect( | |
| bodySpan.end(this), | |
| keywordList, | |
| new SqlNodeList(selectList, Span.of(selectList).pos()), | |
| null, | |
| where, | |
| groupBy, | |
| null, | |
| null, | |
| null, | |
| null, | |
| null, | |
| null, | |
| null | |
| ) | |
| ); | |
| } | |
| } | |
| DruidSqlColumnDeclaration DruidColumnDeclaration() : | |
| { | |
| final SqlIdentifier name; | |
| final SqlDataTypeSpec dataType; | |
| } | |
| { | |
| name = SimpleIdentifier() | |
| dataType = DataType() | |
| // Nullability is accepted but ignored: all Druid columns are nullable, matching the EXTEND clause of INSERT. | |
| [ <NOT> <NULL> | <NULL> ] | |
| { | |
| return new DruidSqlColumnDeclaration( | |
| name.getParserPosition().plus(dataType.getParserPosition()), | |
| name, | |
| dataType | |
| ); | |
| } | |
| } | |
| SqlNode DruidSqlAlterTable() : | |
| { | |
| final Span s; | |
| final SqlIdentifier id; | |
| final SqlIdentifier columnName; | |
| final SqlDataTypeSpec dataType; | |
| final DruidSqlColumnDeclaration column; | |
| final SqlNodeList properties; | |
| final SqlProjectionSpec projection; | |
| final SqlIdentifier projectionName; | |
| boolean ifNotExists = false; | |
| boolean ifExists = false; | |
| } | |
| { | |
| <ALTER> { s = span(); } <TABLE> id = CompoundTableIdentifier() | |
| ( | |
| <ADD> | |
| ( | |
| <COLUMN> column = DruidColumnDeclaration() | |
| { | |
| return new DruidSqlAlterTable.AddColumn(s.end(this), id, column); | |
| } | |
| | | |
| // A projection may be named "if", so two tokens decide whether IF starts the modifier or the name. | |
| <PROJECTION> [ LOOKAHEAD(2) <IF> <NOT> <EXISTS> { ifNotExists = true; } ] projection = DruidProjectionDefinition() | |
| { | |
| return new DruidSqlAlterTable.AddProjection(s.end(this), id, projection, ifNotExists); | |
| } | |
| ) | |
| | | |
| <DROP> | |
| ( | |
| <COLUMN> columnName = SimpleIdentifier() | |
| { | |
| return new DruidSqlAlterTable.DropColumn(s.end(this), id, columnName); | |
| } | |
| | | |
| <PROJECTION> [ <IF> <EXISTS> { ifExists = true; } ] projectionName = SimpleIdentifier() | |
| { | |
| return new DruidSqlAlterTable.DropProjection(s.end(this), id, projectionName, ifExists); | |
| } | |
| ) | |
| | | |
| <ALTER> <COLUMN> columnName = SimpleIdentifier() <SET> <DATA> <TYPE> dataType = DataType() | |
| { | |
| return new DruidSqlAlterTable.AlterColumn( | |
| s.end(this), | |
| id, | |
| new DruidSqlColumnDeclaration( | |
| columnName.getParserPosition().plus(dataType.getParserPosition()), | |
| columnName, | |
| dataType | |
| ) | |
| ); | |
| } | |
| | | |
| <SET> <PROPERTIES> properties = DruidPropertyList() | |
| { | |
| return new DruidSqlAlterTable.SetProperties(s.end(this), id, properties); | |
| } | |
| ) | |
| } | |
| SqlNodeList DruidPropertyList() : | |
| { | |
| final Span s; | |
| final List<SqlNode> list = new ArrayList<SqlNode>(); | |
| } | |
| { | |
| <LPAREN> { s = span(); } | |
| AddDruidPropertyAssignment(list) | |
| ( | |
| <COMMA> AddDruidPropertyAssignment(list) | |
| )* | |
| <RPAREN> | |
| { | |
| return new SqlNodeList(list, s.end(this)); | |
| } | |
| } | |
| void AddDruidPropertyAssignment(List<SqlNode> list) : | |
| { | |
| final SqlIdentifier key; | |
| final SqlNode value; | |
| } | |
| { | |
| key = SimpleIdentifier() <EQ> value = Literal() | |
| { | |
| list.add( | |
| new DruidSqlPropertyAssignment( | |
| key.getParserPosition().plus(value.getParserPosition()), | |
| key, | |
| value | |
| ) | |
| ); | |
| } | |
| } |