Repository: phoenix Updated Branches: refs/heads/calcite 5962a9362 -> 28ec5c872
PHOENIX-2230 Support CREATE INDEX in Phoenix-Calcite Integration Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/28ec5c87 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/28ec5c87 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/28ec5c87 Branch: refs/heads/calcite Commit: 28ec5c872d2ac9b00d728b903e6797ad72e193bb Parents: 5962a93 Author: maryannxue <maryann....@gmail.com> Authored: Wed Aug 3 15:25:53 2016 -0400 Committer: maryannxue <maryann....@gmail.com> Committed: Wed Aug 3 15:25:53 2016 -0400 ---------------------------------------------------------------------- .../apache/phoenix/calcite/CalciteDDLIT.java | 9 ++ phoenix-core/src/main/codegen/data/Parser.tdd | 13 ++- .../src/main/codegen/includes/parserImpls.ftl | 112 +++++++++++++++++++ .../calcite/sql/SqlIndexExpressionNode.java | 63 +++++++++++ .../calcite/jdbc/PhoenixPrepareImpl.java | 86 ++++++++++++++ .../phoenix/calcite/parse/SqlCreateIndex.java | 86 ++++++++++++++ 6 files changed, 365 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDDLIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDDLIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDDLIT.java index e0330dc..d3c45ae 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDDLIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDDLIT.java @@ -29,6 +29,15 @@ public class CalciteDDLIT extends BaseCalciteIT { @Test public void testCreateTableWithTableOptionsAndSplits() throws Exception { start(PROPS).sql("create table t4(a bigint not null ROW_TIMESTAMP, b integer not null, c double constraint pk primary key(a,b)) SALT_BUCKET=4,VERSIONS=5 SPLIT ON('a','b')").execute(); } + + @Test public void testCreateAndDropIndex() throws Exception { + start(PROPS).sql("create table itest(a varchar(20) not null primary key, b integer, c decimal(10, 2), d bigint, e varchar)").execute(); + start(PROPS).sql("create index idx1 on itest(b desc, c) include (d)").execute(); + start(PROPS).sql("create local index idx2 on itest(d, e desc)").execute(); + start(PROPS).sql("create index if not exists idx3 on itest(b + c) include (d, e)").execute(); + //start(PROPS).sql("drop index idx1").execute(); + //start(PROPS).sql("drop index if exists idx2").execute(); + } @Test public void testCreateAndDropSequence() throws Exception { start(PROPS).sql("create sequence if not exists s0 start with 2 increment 3 minvalue 2 maxvalue 90 cycle cache 3").execute().close(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/main/codegen/data/Parser.tdd ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/codegen/data/Parser.tdd b/phoenix-core/src/main/codegen/data/Parser.tdd index 8ace311..8bfe486 100644 --- a/phoenix-core/src/main/codegen/data/Parser.tdd +++ b/phoenix-core/src/main/codegen/data/Parser.tdd @@ -28,6 +28,7 @@ "org.apache.phoenix.parse.ColumnDefInPkConstraint", "org.apache.calcite.sql.SqlColumnDefNode" "org.apache.calcite.sql.SqlColumnDefInPkConstraintNode" + "org.apache.calcite.sql.SqlIndexExpressionNode" "org.apache.calcite.sql.SqlDataTypeNode" "org.apache.calcite.sql.SqlTableOptionNode" "java.util.*" @@ -35,10 +36,13 @@ # List of keywords. keywords: [ - "IF" - "ROW_TIMESTAMP" - "SPLIT" - "CACHE" + "ASYNC" + "CACHE" + "IF" + "INCLUDE" + "INDEX" + "ROW_TIMESTAMP" + "SPLIT" ] # List of keywords from "keywords" section that are not reserved. @@ -51,6 +55,7 @@ "SqlPhoenixExplain()", "SqlCreateView()", "SqlCreateTable()", + "SqlCreateIndex()", "SqlCreateSequence()", "SqlDropTableOrDropView()", "SqlDropSequence()", http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/main/codegen/includes/parserImpls.ftl ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/codegen/includes/parserImpls.ftl b/phoenix-core/src/main/codegen/includes/parserImpls.ftl index a6ae9a2..cdcded1 100644 --- a/phoenix-core/src/main/codegen/includes/parserImpls.ftl +++ b/phoenix-core/src/main/codegen/includes/parserImpls.ftl @@ -178,6 +178,63 @@ SqlNode SqlCreateTable() : /** * Parses statement + * CREATE INDEX + */ +SqlNode SqlCreateIndex() : +{ + SqlParserPos pos; + SqlIdentifier indexName; + boolean isLocal = false; + boolean ifNotExists = false; + SqlIdentifier dataTableName; + SqlNodeList expressions; + SqlNodeList includeColumns = SqlNodeList.EMPTY; + boolean async = false; + SqlNodeList indexOptions = SqlNodeList.EMPTY; + SqlNodeList splitKeys = SqlNodeList.EMPTY; +} +{ + <CREATE> { pos = getPos(); } + [ + <LOCAL> { isLocal = true; } + ] + <INDEX> + [ + <IF> <NOT> <EXISTS> { ifNotExists = true; } + ] + indexName = SimpleIdentifier() + <ON> dataTableName = DualIdentifier() + <LPAREN> + expressions = IndexExpressionList() + <RPAREN> + [ + <INCLUDE> <LPAREN> + includeColumns = IndexIncludeList() + <RPAREN> + ] + [ + <ASYNC> { async = true; } + ] + [ + indexOptions = TableOptionList() + ] + [ + <SPLIT> <ON> <LPAREN> + splitKeys = SplitKeyList() + <RPAREN> + ] + { + return new SqlCreateIndex(pos.plus(getPos()), indexName, + SqlLiteral.createBoolean(isLocal, SqlParserPos.ZERO), + SqlLiteral.createBoolean(ifNotExists, SqlParserPos.ZERO), + dataTableName, expressions, includeColumns, + SqlLiteral.createBoolean(async, SqlParserPos.ZERO), + indexOptions, splitKeys); + } +} + +/** + * Parses statement * CREATE SEQUENCE */ SqlNode SqlCreateSequence() : @@ -331,6 +388,40 @@ SqlNodeList PkConstraintColumnDefList() : } } +SqlNodeList IndexExpressionList() : +{ + SqlParserPos pos; + SqlNode e; + List<SqlNode> indexExpressionList; +} +{ + { pos = getPos(); } + e = IndexExpression() { indexExpressionList = startList(e); } + ( + <COMMA> e = IndexExpression() { indexExpressionList.add(e); } + ) * + { + return new SqlNodeList(indexExpressionList, pos.plus(getPos())); + } +} + +SqlNodeList IndexIncludeList() : +{ + SqlParserPos pos; + SqlIdentifier e; + List<SqlNode> indexIncludeList; +} +{ + { pos = getPos(); } + e = DualIdentifier() { indexIncludeList = startList(e); } + ( + <COMMA> e = DualIdentifier() { indexIncludeList.add(e); } + ) * + { + return new SqlNodeList(indexIncludeList, pos.plus(getPos())); + } +} + SqlNodeList TableOptionList() : { SqlParserPos pos; @@ -468,6 +559,27 @@ SqlColumnDefInPkConstraintNode ColumnDefInPkConstraint() : } } +SqlIndexExpressionNode IndexExpression() : +{ + SqlNode expression; + SortOrder sortOrder = SortOrder.getDefault(); + SqlParserPos pos; +} +{ + expression = Expression(ExprContext.ACCEPT_NONQUERY) + [ + <ASC> + {sortOrder = SortOrder.ASC;} + | + <DESC> + {sortOrder = SortOrder.DESC;} + ] + { + pos = expression.getParserPosition().plus(getPos()); + return new SqlIndexExpressionNode(pos, expression, sortOrder); + } +} + SqlTableOptionNode TableOption() : { SqlIdentifier key; http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/main/java/org/apache/calcite/sql/SqlIndexExpressionNode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/calcite/sql/SqlIndexExpressionNode.java b/phoenix-core/src/main/java/org/apache/calcite/sql/SqlIndexExpressionNode.java new file mode 100644 index 0000000..e0c34d8 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/calcite/sql/SqlIndexExpressionNode.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql; + +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.sql.util.SqlVisitor; +import org.apache.calcite.sql.validate.SqlValidator; +import org.apache.calcite.sql.validate.SqlValidatorScope; +import org.apache.calcite.util.Litmus; +import org.apache.phoenix.schema.SortOrder; + +public class SqlIndexExpressionNode extends SqlNode{ + public final SqlNode expression; + public final SortOrder sortOrder; + + public SqlIndexExpressionNode( + SqlParserPos pos, + SqlNode expression, + SortOrder sortOrder) { + super(pos); + this.expression = expression; + this.sortOrder = sortOrder; + } + + @Override + public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + // TODO Auto-generated method stub + } + + @Override + public void validate(SqlValidator validator, SqlValidatorScope scope) { + // TODO Auto-generated method stub + } + + @Override + public <R> R accept(SqlVisitor<R> visitor) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean equalsDeep(SqlNode node, Litmus litmus) { + // TODO Auto-generated method stub + return false; + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/main/java/org/apache/phoenix/calcite/jdbc/PhoenixPrepareImpl.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/jdbc/PhoenixPrepareImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/jdbc/PhoenixPrepareImpl.java index 0c95a8c..e4afbe0 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/jdbc/PhoenixPrepareImpl.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/jdbc/PhoenixPrepareImpl.java @@ -1,7 +1,9 @@ package org.apache.phoenix.calcite.jdbc; import java.sql.SQLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.calcite.adapter.enumerable.EnumerableRules; import org.apache.calcite.jdbc.CalcitePrepare; @@ -22,6 +24,8 @@ import org.apache.calcite.runtime.Hook; import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.sql.SqlColumnDefInPkConstraintNode; import org.apache.calcite.sql.SqlColumnDefNode; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlIndexExpressionNode; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; @@ -36,6 +40,7 @@ import org.apache.calcite.util.Holder; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Pair; import org.apache.phoenix.calcite.PhoenixSchema; +import org.apache.phoenix.calcite.parse.SqlCreateIndex; import org.apache.phoenix.calcite.parse.SqlCreateSequence; import org.apache.phoenix.calcite.parse.SqlCreateTable; import org.apache.phoenix.calcite.parse.SqlDropSequence; @@ -52,6 +57,7 @@ import org.apache.phoenix.calcite.rules.PhoenixOrderedAggregateRule; import org.apache.phoenix.calcite.rules.PhoenixReverseTableScanRule; import org.apache.phoenix.calcite.rules.PhoenixSortServerJoinTransposeRule; import org.apache.phoenix.calcite.rules.PhoenixTableScanColumnRefRule; +import org.apache.phoenix.compile.CreateIndexCompiler; import org.apache.phoenix.compile.CreateSequenceCompiler; import org.apache.phoenix.compile.CreateTableCompiler; import org.apache.phoenix.compile.MutationPlan; @@ -60,17 +66,25 @@ import org.apache.phoenix.jdbc.PhoenixStatement; import org.apache.phoenix.jdbc.PhoenixStatement.Operation; import org.apache.phoenix.parse.ColumnDef; import org.apache.phoenix.parse.ColumnDefInPkConstraint; +import org.apache.phoenix.parse.ColumnName; +import org.apache.phoenix.parse.CreateIndexStatement; import org.apache.phoenix.parse.CreateSequenceStatement; import org.apache.phoenix.parse.CreateTableStatement; import org.apache.phoenix.parse.DropSequenceStatement; import org.apache.phoenix.parse.DropTableStatement; +import org.apache.phoenix.parse.IndexKeyConstraint; +import org.apache.phoenix.parse.NamedNode; +import org.apache.phoenix.parse.NamedTableNode; import org.apache.phoenix.parse.ParseNode; import org.apache.phoenix.parse.ParseNodeFactory; import org.apache.phoenix.parse.PrimaryKeyConstraint; import org.apache.phoenix.parse.SQLParser; import org.apache.phoenix.parse.TableName; +import org.apache.phoenix.parse.UDFParseNode; import org.apache.phoenix.schema.MetaDataClient; +import org.apache.phoenix.schema.PTable.IndexType; import org.apache.phoenix.schema.PTableType; +import org.apache.phoenix.schema.SortOrder; import com.google.common.base.Function; import com.google.common.collect.ArrayListMultimap; @@ -256,6 +270,78 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl { } break; } + case CREATE_INDEX: { + final SqlCreateIndex index = (SqlCreateIndex) node; + final NamedNode name = NamedNode.caseSensitiveNamedNode(index.indexName.getSimple()); + final IndexType indexType = index.isLocal.booleanValue() ? IndexType.LOCAL : IndexType.GLOBAL; + final TableName dataTableName; + if (index.dataTableName.isSimple()) { + dataTableName = TableName.create(null, index.dataTableName.getSimple()); + } else { + dataTableName = TableName.create(index.dataTableName.names.get(0), index.dataTableName.names.get(1)); + } + final NamedTableNode dataTable = NamedTableNode.create(dataTableName); + final List<org.apache.hadoop.hbase.util.Pair<ParseNode, SortOrder>> indexKeys = Lists.newArrayList(); + for (SqlNode e : index.expressions) { + SqlIndexExpressionNode indexExpression = (SqlIndexExpressionNode) e; + String sql = THREAD_SQL_STRING.get(); + SqlParserPos exprPos = indexExpression.expression.getParserPosition(); + int start = SqlParserUtil.lineColToIndex(sql, exprPos.getLineNum(), exprPos.getColumnNum()); + int end = SqlParserUtil.lineColToIndex(sql, exprPos.getEndLineNum(), exprPos.getEndColumnNum()); + String exprString = sql.substring(start, end + 1); + ParseNode exprNode = new SQLParser(exprString).parseExpression(); + indexKeys.add(new org.apache.hadoop.hbase.util.Pair<ParseNode, SortOrder>(exprNode, indexExpression.sortOrder)); + } + final IndexKeyConstraint indexKeyConstraint = nodeFactory.indexKey(indexKeys); + final List<ColumnName> includeColumns; + if (SqlNodeList.isEmptyList(index.includeColumns)) { + includeColumns = null; + } else { + includeColumns = Lists.newArrayList(); + for (SqlNode e : index.includeColumns) { + SqlIdentifier n = (SqlIdentifier) e; + ColumnName columnName; + if (n.isSimple()) { + columnName = ColumnName.caseSensitiveColumnName(n.getSimple()); + } else { + columnName = ColumnName.caseSensitiveColumnName(n.names.get(0), n.names.get(1)); + } + includeColumns.add(columnName); + } + } + final ListMultimap<String, org.apache.hadoop.hbase.util.Pair<String, Object>> props; + if (SqlNodeList.isEmptyList(index.indexOptions)) { + props = null; + } else { + props = ArrayListMultimap.<String, org.apache.hadoop.hbase.util.Pair<String, Object>>create(); + for (SqlNode tableOption : index.indexOptions) { + SqlTableOptionNode option = (SqlTableOptionNode) tableOption; + props.put(option.familyName, new org.apache.hadoop.hbase.util.Pair<String, Object>(option.propertyName, option.value)); + } + } + final List<ParseNode> splitNodes; + if (SqlNodeList.isEmptyList(index.splitKeyList)) { + splitNodes = null; + } else { + splitNodes = Lists.newArrayList(); + for (SqlNode splitKey : index.splitKeyList) { + final SqlLiteral key = (SqlLiteral) splitKey; + splitNodes.add(nodeFactory.literal(((NlsString) key.getValue()).toString())); + } + } + // TODO + final Map<String, UDFParseNode> udfParseNodes = new HashMap<String, UDFParseNode>(); + final CreateIndexStatement create = nodeFactory.createIndex( + name, dataTable, indexKeyConstraint, includeColumns, + splitNodes, props, index.ifNotExists.booleanValue(), + indexType, index.async.booleanValue(), 0, udfParseNodes); + try (final PhoenixStatement stmt = new PhoenixStatement(connection)) { + final CreateIndexCompiler compiler = new CreateIndexCompiler(stmt, Operation.UPSERT); + final MutationPlan plan = compiler.compile(create); + plan.execute(); + } + break; + } case CREATE_SEQUENCE: { final SqlCreateSequence sequence = (SqlCreateSequence) node; final TableName name; http://git-wip-us.apache.org/repos/asf/phoenix/blob/28ec5c87/phoenix-core/src/main/java/org/apache/phoenix/calcite/parse/SqlCreateIndex.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/parse/SqlCreateIndex.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/parse/SqlCreateIndex.java new file mode 100644 index 0000000..865e835 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/parse/SqlCreateIndex.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.calcite.parse; + +import com.google.common.collect.ImmutableList; + +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLiteral; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; +import java.util.List; + +/** + * Parse tree node for SQL {@code CREATE INDEX} command. + */ +public class SqlCreateIndex extends SqlCall { + public static final SqlOperator OPERATOR = new SqlDdlOperator("CREATE INDEX", SqlKind.CREATE_INDEX); + + public final SqlIdentifier indexName; + public final SqlLiteral isLocal; + public final SqlLiteral ifNotExists; + public final SqlIdentifier dataTableName; + public final SqlNodeList expressions; + public final SqlNodeList includeColumns; + public final SqlLiteral async; + public final SqlNodeList indexOptions; + public final SqlNodeList splitKeyList; + + + /** Creates a CREATE INDEX. */ + public SqlCreateIndex( + SqlParserPos pos, + SqlIdentifier indexName, + SqlLiteral isLocal, + SqlLiteral ifNotExists, + SqlIdentifier dataTableName, + SqlNodeList expressions, + SqlNodeList includeColumns, + SqlLiteral async, + SqlNodeList indexOptions, + SqlNodeList splitKeyList) { + super(pos); + this.indexName = indexName; + this.isLocal = isLocal; + this.ifNotExists = ifNotExists; + this.dataTableName = dataTableName; + this.expressions = expressions; + this.includeColumns = includeColumns; + this.async = async; + this.indexOptions = indexOptions; + this.splitKeyList = splitKeyList; + } + + public SqlOperator getOperator() { + return OPERATOR; + } + + public List<SqlNode> getOperandList() { + return ImmutableList.of(indexName, isLocal, ifNotExists, dataTableName, expressions, includeColumns, async, indexOptions, splitKeyList); + } + + @Override + public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + // TODO Auto-generated method stub + } +}