blob: 7a2b48b0326fd6e6051286deb57f85eb639464ed [file] [log] [blame]
/*
* 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.ignite.internal.sql.command;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.internal.sql.SqlLexer;
import org.apache.ignite.internal.sql.SqlLexerToken;
import org.apache.ignite.internal.sql.SqlLexerTokenType;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.sql.SqlKeyword.ASC;
import static org.apache.ignite.internal.sql.SqlKeyword.DESC;
import static org.apache.ignite.internal.sql.SqlKeyword.IF;
import static org.apache.ignite.internal.sql.SqlKeyword.INLINE_SIZE;
import static org.apache.ignite.internal.sql.SqlKeyword.ON;
import static org.apache.ignite.internal.sql.SqlKeyword.PARALLEL;
import static org.apache.ignite.internal.sql.SqlParserUtils.error;
import static org.apache.ignite.internal.sql.SqlParserUtils.errorUnexpectedToken;
import static org.apache.ignite.internal.sql.SqlParserUtils.matchesKeyword;
import static org.apache.ignite.internal.sql.SqlParserUtils.parseIdentifier;
import static org.apache.ignite.internal.sql.SqlParserUtils.parseIfNotExists;
import static org.apache.ignite.internal.sql.SqlParserUtils.parseInt;
import static org.apache.ignite.internal.sql.SqlParserUtils.parseQualifiedIdentifier;
import static org.apache.ignite.internal.sql.SqlParserUtils.skipCommaOrRightParenthesis;
import static org.apache.ignite.internal.sql.SqlParserUtils.skipIfMatchesKeyword;
/**
* CREATE INDEX command.
*/
public class SqlCreateIndexCommand implements SqlCommand {
/** Schema name. */
private String schemaName;
/** Table name. */
private String tblName;
/** Index name. */
private String idxName;
/** IF NOT EXISTS flag. */
private boolean ifNotExists;
/** Spatial index flag. */
private boolean spatial;
/**
* Parallelism level. <code>parallel=0</code> means that a default number
* of cores will be used during index creation (e.g. 25% of available cores).
*/
private int parallel;
/** Columns. */
@GridToStringInclude
private Collection<SqlIndexColumn> cols;
/** Column names. */
@GridToStringExclude
private Set<String> colNames;
/** Inline size. Zero effectively disables inlining. */
private int inlineSize = QueryIndex.DFLT_INLINE_SIZE;
/** {@inheritDoc} */
@Override public String schemaName() {
return schemaName;
}
/** {@inheritDoc} */
@Override public void schemaName(String schemaName) {
this.schemaName = schemaName;
}
/**
* @return Table name.
*/
public String tableName() {
return tblName;
}
/**
* @return Index name.
*/
public String indexName() {
return idxName;
}
/**
* @return IF NOT EXISTS flag.
*/
public boolean ifNotExists() {
return ifNotExists;
}
/**
* @return Parallelism level.
*/
public int parallel() {
return parallel;
}
/**
* @return Spatial index flag.
*/
public boolean spatial() {
return spatial;
}
/**
* @return Inline size.
*/
public int inlineSize() {
return inlineSize;
}
/**
* @param spatial Spatial index flag.
* @return This instance.
*/
public SqlCreateIndexCommand spatial(boolean spatial) {
this.spatial = spatial;
return this;
}
/**
* @return Columns.
*/
public Collection<SqlIndexColumn> columns() {
return cols != null ? cols : Collections.<SqlIndexColumn>emptySet();
}
/** {@inheritDoc} */
@Override public SqlCommand parse(SqlLexer lex) {
ifNotExists = parseIfNotExists(lex);
idxName = parseIndexName(lex);
skipIfMatchesKeyword(lex, ON);
SqlQualifiedName tblQName = parseQualifiedIdentifier(lex);
schemaName = tblQName.schemaName();
tblName = tblQName.name();
parseColumnList(lex);
parseIndexProperties(lex);
return this;
}
/**
* Parse index name.
*
* @param lex Lexer.
* @return Index name.
*/
@Nullable private static String parseIndexName(SqlLexer lex) {
if (matchesKeyword(lex.lookAhead(), ON))
return null;
return parseIdentifier(lex, IF);
}
/**
* @param lex Lexer.
*/
private void parseColumnList(SqlLexer lex) {
if (!lex.shift() || lex.tokenType() != SqlLexerTokenType.PARENTHESIS_LEFT)
throw errorUnexpectedToken(lex, "(");
while (true) {
parseIndexColumn(lex);
if (skipCommaOrRightParenthesis(lex))
break;
}
}
/**
* @param lex Lexer.
*/
private void parseIndexColumn(SqlLexer lex) {
String name = parseIdentifier(lex);
boolean desc = false;
SqlLexerToken nextToken = lex.lookAhead();
if (matchesKeyword(nextToken, ASC) || matchesKeyword(nextToken, DESC)) {
lex.shift();
if (matchesKeyword(lex, DESC))
desc = true;
}
addColumn(lex, new SqlIndexColumn(name, desc));
}
/**
* @param lex Lexer.
* @param col Column.
*/
private void addColumn(SqlLexer lex, SqlIndexColumn col) {
if (cols == null) {
cols = new LinkedList<>();
colNames = new HashSet<>();
}
if (!colNames.add(col.name()))
throw error(lex, "Column already defined: " + col.name());
cols.add(col);
}
/**
* Parses CREATE INDEX command properties.
*
* @param lex Lexer.
*/
private void parseIndexProperties(SqlLexer lex) {
Set<String> foundProps = new HashSet<>();
while (true) {
SqlLexerToken token = lex.lookAhead();
if (token.tokenType() != SqlLexerTokenType.DEFAULT)
break;
switch (token.token()) {
case PARALLEL:
parallel = getIntProperty(lex, PARALLEL, foundProps);
if (parallel < 0)
throw error(lex, "Illegal " + PARALLEL + " value. Should be positive: " + parallel);
break;
case INLINE_SIZE:
inlineSize = getIntProperty(lex, INLINE_SIZE, foundProps);
if (inlineSize < 0)
throw error(lex, "Illegal " + INLINE_SIZE +
" value. Should be positive: " + inlineSize);
break;
default:
return;
}
}
}
/**
* Parses <code>Integer</code> property by its keyword.
* @param lex Lexer.
* @param keyword Keyword.
* @param foundProps Set of properties to check if one has already been found in SQL clause.
* @return parsed value;
*/
private Integer getIntProperty(SqlLexer lex, String keyword, Set<String> foundProps) {
if (foundProps.contains(keyword))
throw error(lex, "Only one " + keyword + " clause may be specified.");
foundProps.add(keyword);
lex.shift();
return parseInt(lex);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(SqlCreateIndexCommand.class, this);
}
}