/*
* Copyright 1999-2015 dangdang.com.
*
* 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.dangdang.ddframe.rdb.sharding.parsing.parser.statement.insert;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.ConditionContext;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.InsertSQLContext;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.ItemsToken;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.ShardingColumnContext;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.expr.SQLExpr;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.expr.SQLNumberExpr;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.expr.SQLPlaceholderExpr;
import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Assist;
import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.DefaultKeyword;
import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.Symbol;
import com.dangdang.ddframe.rdb.sharding.parsing.lexer.token.TokenType;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.exception.SQLParsingUnsupportedException;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.SQLParser;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.statement.SQLStatementParser;
import com.dangdang.ddframe.rdb.sharding.util.SQLUtil;
import com.google.common.collect.Sets;
import lombok.AccessLevel;
import lombok.Getter;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Insert语句解析器.
*
* @author zhangliang
*/
@Getter(AccessLevel.PROTECTED)
public abstract class AbstractInsertParser implements SQLStatementParser {
private final SQLParser sqlParser;
private final ShardingRule shardingRule;
private final List parameters;
private final InsertSQLContext sqlContext;
public AbstractInsertParser(final ShardingRule shardingRule, final List parameters, final SQLParser sqlParser) {
this.sqlParser = sqlParser;
this.shardingRule = shardingRule;
this.parameters = parameters;
sqlContext = new InsertSQLContext();
sqlContext.setSqlBuilderContext(sqlParser.getSqlBuilderContext());
}
@Override
public final InsertSQLContext parse() {
sqlParser.getLexer().nextToken();
parseInto();
Collection shardingColumnContexts = parseColumns();
if (sqlParser.equalAny(DefaultKeyword.SELECT, Symbol.LEFT_PAREN)) {
throw new UnsupportedOperationException("Cannot support subquery");
}
if (getValuesKeywords().contains(sqlParser.getLexer().getCurrentToken().getType())) {
parseValues(shardingColumnContexts);
} else if (getCustomizedInsertKeywords().contains(sqlParser.getLexer().getCurrentToken().getType())) {
parseCustomizedInsert();
}
return sqlContext;
}
protected Set getUnsupportedKeywords() {
return Collections.emptySet();
}
private void parseInto() {
if (getUnsupportedKeywords().contains(sqlParser.getLexer().getCurrentToken().getType())) {
throw new SQLParsingUnsupportedException(sqlParser.getLexer().getCurrentToken().getType());
}
sqlParser.skipUntil(DefaultKeyword.INTO);
sqlParser.getLexer().nextToken();
sqlParser.parseSingleTable(sqlContext);
skipBetweenTableAndValues();
}
private void skipBetweenTableAndValues() {
while (getSkippedKeywordsBetweenTableAndValues().contains(sqlParser.getLexer().getCurrentToken().getType())) {
sqlParser.getLexer().nextToken();
if (sqlParser.equalAny(Symbol.LEFT_PAREN)) {
sqlParser.skipParentheses();
}
}
}
protected Set getSkippedKeywordsBetweenTableAndValues() {
return Collections.emptySet();
}
private Collection parseColumns() {
Collection result = new LinkedList<>();
Collection autoIncrementColumns = shardingRule.getAutoIncrementColumns(sqlContext.getTables().get(0).getName());
if (sqlParser.equalAny(Symbol.LEFT_PAREN)) {
do {
sqlParser.getLexer().nextToken();
result.add(getColumn(autoIncrementColumns));
sqlParser.getLexer().nextToken();
} while (!sqlParser.equalAny(Symbol.RIGHT_PAREN) && !sqlParser.equalAny(Assist.END));
ItemsToken itemsToken = new ItemsToken(sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length());
for (String each : autoIncrementColumns) {
itemsToken.getItems().add(each);
result.add(new ShardingColumnContext(each, sqlContext.getTables().get(0).getName(), true));
}
if (!itemsToken.getItems().isEmpty()) {
sqlParser.getSqlBuilderContext().getSqlTokens().add(itemsToken);
}
sqlParser.getLexer().nextToken();
}
return result;
}
protected final ShardingColumnContext getColumn(final Collection autoIncrementColumns) {
String columnName = SQLUtil.getExactlyValue(sqlParser.getLexer().getCurrentToken().getLiterals());
if (autoIncrementColumns.contains(columnName)) {
autoIncrementColumns.remove(columnName);
}
return new ShardingColumnContext(columnName, sqlContext.getTables().get(0).getName());
}
protected Set getValuesKeywords() {
return Sets.newHashSet(DefaultKeyword.VALUES);
}
private void parseValues(final Collection shardingColumnContexts) {
boolean parsed = false;
do {
if (parsed) {
throw new UnsupportedOperationException("Cannot support multiple insert");
}
sqlParser.getLexer().nextToken();
sqlParser.accept(Symbol.LEFT_PAREN);
List sqlExprs = new LinkedList<>();
ConditionContext conditionContext = new ConditionContext();
do {
sqlExprs.add(sqlParser.parseExpression());
} while (sqlParser.skipIfEqual(Symbol.COMMA));
ItemsToken itemsToken = new ItemsToken(sqlParser.getLexer().getCurrentToken().getEndPosition() - sqlParser.getLexer().getCurrentToken().getLiterals().length());
int count = 0;
for (ShardingColumnContext each : shardingColumnContexts) {
if (each.isAutoIncrement()) {
Number autoIncrementedValue = (Number) getShardingRule().findTableRule(sqlContext.getTables().get(0).getName()).generateId(each.getColumnName());
if (parameters.isEmpty()) {
itemsToken.getItems().add(autoIncrementedValue.toString());
sqlExprs.add(new SQLNumberExpr(autoIncrementedValue));
} else {
itemsToken.getItems().add("?");
parameters.add(autoIncrementedValue);
sqlExprs.add(new SQLPlaceholderExpr(parameters.size() - 1));
}
sqlContext.getGeneratedKeyContext().getColumns().add(each.getColumnName());
sqlContext.getGeneratedKeyContext().putValue(each.getColumnName(), autoIncrementedValue);
}
if (getShardingRule().isShardingColumn(each)) {
conditionContext.add(new ConditionContext.Condition(each, sqlExprs.get(count)));
}
count++;
}
if (!itemsToken.getItems().isEmpty()) {
sqlParser.getSqlBuilderContext().getSqlTokens().add(itemsToken);
}
sqlParser.accept(Symbol.RIGHT_PAREN);
parsed = true;
sqlContext.setConditionContext(conditionContext);
}
while (sqlParser.equalAny(Symbol.COMMA));
}
protected Set getCustomizedInsertKeywords() {
return Collections.emptySet();
}
protected void parseCustomizedInsert() {
}
}