以mysql為例,官網(wǎng)說明insert語(yǔ)句的寫法如下:
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
{VALUES | VALUE} (value_list) [, (value_list)] ...
[ON DUPLICATE KEY UPDATE assignment_list]
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
SET assignment_list
[ON DUPLICATE KEY UPDATE assignment_list]
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
SELECT ...
[ON DUPLICATE KEY UPDATE assignment_list]
value:
{expr | DEFAULT}
value_list:
value [, value] ...
assignment:
col_name = value
assignment_list:
assignment [, assignment] ...
簡(jiǎn)單來講渔呵,支持一下三種:
--第一種Insert into values
insert into table(column1,column2,column3...) values (data1,data2,data3...)
--第二種Insert into set
insert into table set column1=data1,column2=data2,column3=data3...
--第三種
insert into table select ...
目前來講,sharding-sphere并不支持第三種忧额。原因在這里:
//如果詞法解析器解析表名后面為select關(guān)鍵字厘肮,則拋異常愧口。
if (lexerEngine.equalAny(DefaultKeyword.SELECT, Symbol.LEFT_PAREN)) {
throw new UnsupportedOperationException("Cannot INSERT SELECT");
}
接下來睦番,就以第一種為例,介紹一下整個(gè)的語(yǔ)法解析過程:
解析sql:
INSERT INTO `table` (`field1`, `field2`) VALUES (10, 1)
語(yǔ)法解析器會(huì)將sql最終解析成SqlStatement耍属,sql的解析都會(huì)被Sql語(yǔ)法解析引擎SQLParsingEngine去解析托嚣,從SQL解析引擎看起,看這條insert sql都經(jīng)歷了些什么故事厚骗?
@Test
public void insertValuesTest() {
ShardingRule shardingRule = createShardingRuleByLocalHost();
String insertSQL="INSERT INTO `table` (`field1`, `field2`) VALUES (10, 1)";
//初始化一個(gè)語(yǔ)法解析器
SQLParsingEngine statementParser = new SQLParsingEngine(DatabaseType.MySQL,insertSQL, shardingRule, null);
//語(yǔ)法解析器解析
InsertStatement insertStatement = (InsertStatement) statementParser.parse(false);
System.out.println(insertStatement);
}
在SQLParsingEngine解析過程中示启,首先判斷是否需要讀緩存,如果需要?jiǎng)t從緩存中取领舰,如果不需要夫嗓,則初始化一個(gè)詞法解析器LexerEngine,獲取第一個(gè)分詞,根據(jù)分詞冲秽,數(shù)據(jù)庫(kù)類型獲取真實(shí)的SqlParser舍咖,這里為MySQLInsertParser,具體過程在鏈接:
詞法解析器
public SQLStatement parse(final boolean useCache) {
//是否讀緩存
Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(useCache);
if (cachedSQLStatement.isPresent()) {
return cachedSQLStatement.get();
}
//詞法解析引擎初始化
LexerEngine lexerEngine = LexerEngineFactory.newInstance(dbType, sql);
//分詞锉桑,獲取第一個(gè)分詞排霉,此處為insert
lexerEngine.nextToken(); //此處為insert分詞
//獲取語(yǔ)法解析器,并解析
SQLStatement result = SQLParserFactory.newInstance(dbType, lexerEngine.getCurrentToken().getType(), shardingRule, lexerEngine, shardingMetaData).parse();
if (useCache) {
ParsingResultCache.getInstance().put(sql, result);
}
return result;
}
在MySQLInsertParser解析過程中民轴,首先獲取insert之后的下一個(gè)分詞攻柠,然后交由下一個(gè)從句解析器去解析,比如:insert之后后裸,會(huì)解析into瑰钮, 表名,交由InsertIntoClauseParser去解析微驶,然后解析(
,)
里的字段信息浪谴,交由InsertColumnsClauseParser去解析,然后判斷是否是insert...select語(yǔ)句,如果是较店,則拋異常士八,表示不支持,否則梁呈,當(dāng)做insert..values語(yǔ)句處理婚度,交由InsertValuesClauseParser處理,若不是insert..values語(yǔ)句官卡,則交由InsertSetClauseParser蝗茁,當(dāng)做insert...set語(yǔ)句處理。
@Override
public final DMLStatement parse() {
//獲取下一分詞
lexerEngine.nextToken();
//初始化返回的結(jié)果
InsertStatement result = new InsertStatement();
//InsertIntoClauseParser insertInto從句解析器解析結(jié)果
insertClauseParserFacade.getInsertIntoClauseParser().parse(result);
//解析()及里面的字段信息
insertClauseParserFacade.getInsertColumnsClauseParser().parse(result, shardingMetaData);
//如果是insert select 語(yǔ)句寻咒,則不支持
if (lexerEngine.equalAny(DefaultKeyword.SELECT, Symbol.LEFT_PAREN)) {
throw new UnsupportedOperationException("Cannot INSERT SELECT");
}
//InsertValuesClauseParser解析insert value信息
insertClauseParserFacade.getInsertValuesClauseParser().parse(result, shardingMetaData);
//InsertSetClauseParser 解析insert...set信息
insertClauseParserFacade.getInsertSetClauseParser().parse(result);
//ON DUPLICATE KEY UPDATE 解析
insertClauseParserFacade.getInsertDuplicateKeyUpdateClauseParser().parse(result);
//處理需要自動(dòng)生成值的列
processGeneratedKey(result);
return result;
}
InsertIntoClauseParser的處理流程:
public void parse(final InsertStatement insertStatement) {
//如果出現(xiàn)不支持的詞在into之前哮翘,則終止解析
lexerEngine.unsupportedIfEqual(getUnsupportedKeywordsBeforeInto());
//跳過所有字符,直到出現(xiàn)into
lexerEngine.skipUntil(DefaultKeyword.INTO);
//獲取into后的下一分詞毛秘,當(dāng)然這里就是表名了
lexerEngine.nextToken();
//解析表引用關(guān)系
tableReferencesClauseParser.parse(insertStatement, true);
skipBetweenTableAndValues(insertStatement);
}
而在解析表引用關(guān)系時(shí)饭寺,實(shí)質(zhì)是使用MySQLTableReferencesClauseParser,mysql的table從句解析器解析,具體如下:
@Override
protected void parseTableReference(final SQLStatement sqlStatement, final boolean isSingleTableOnly) {
//解析表
parseTableFactor(sqlStatement, isSingleTableOnly);
//PARTITION 解析
parsePartition();
parseIndexHint(sqlStatement);
}
protected final void parseTableFactor(final SQLStatement sqlStatement, final boolean isSingleTableOnly) {
final int beginPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
String literals = lexerEngine.getCurrentToken().getLiterals();
int skippedSchemaNameLength = 0;
//獲取下一分詞
lexerEngine.nextToken();
//跳過如果是. 則在點(diǎn)之前的是schema的名稱叫挟,跳過.
if (lexerEngine.skipIfEqual(Symbol.DOT)) {
//跳過schema的長(zhǎng)度為skippedSchemaNameLength
skippedSchemaNameLength = literals.length() + Symbol.DOT.getLiterals().length();
//表名為當(dāng)前分詞
literals = lexerEngine.getCurrentToken().getLiterals();
}
//獲取表名 如果為`tablename`,則返回tablename
String tableName = SQLUtil.getExactlyValue(literals);
if (Strings.isNullOrEmpty(tableName)) {
return;
}
//解析別名
Optional<String> alias = aliasExpressionParser.parseTableAlias();
//如果是單表艰匙,或者能根據(jù)邏輯實(shí)體表名獲取到表規(guī)則等其他條件
if (isSingleTableOnly || shardingRule.tryFindTableRuleByLogicTable(tableName).isPresent() || shardingRule.findBindingTableRule(tableName).isPresent()
|| shardingRule.getShardingDataSourceNames().getDataSourceNames().contains(shardingRule.getShardingDataSourceNames().getDefaultDataSourceName())) {
//表的信息記錄在返回值里,有數(shù)據(jù)庫(kù)的schema名稱抹恳,表名稱员凝,開始位置坐標(biāo)
sqlStatement.getSqlTokens().add(new TableToken(beginPosition, skippedSchemaNameLength, literals));
//表名和別名記錄下來
sqlStatement.getTables().add(new Table(tableName, alias));
}
//解析是否強(qiáng)制索引,insert語(yǔ)句直接跳過奋献,不會(huì)使用
parseForceIndex(tableName, sqlStatement);
//表關(guān)聯(lián)解析健霹,直接跳過
parseJoinTable(sqlStatement);
//如果參數(shù)是單表,且解析結(jié)果不是單表瓶蚂,則直接拋異常
if (isSingleTableOnly && !sqlStatement.getTables().isSingleTable()) {
throw new UnsupportedOperationException("Cannot support Multiple-Table.");
}
}
insert into table具體如何解析結(jié)束看完了糖埋,接下來,到解析表字段了,InsertColumnsClauseParser出場(chǎng)了扬跋。在InsertColumnsClauseParser處理邏輯中阶捆,從(開始,解析一個(gè)又一個(gè)的分詞钦听,直到碰到)或者結(jié)束分詞洒试。如果沒有(,則獲取全部字段朴上。
public void parse(final InsertStatement insertStatement, final ShardingMetaData shardingMetaData) {
Collection<Column> result = new LinkedList<>();
//獲取表名
String tableName = insertStatement.getTables().getSingleTableName();
//獲取需要自動(dòng)生成字段的值
Optional<Column> generateKeyColumn = shardingRule.getGenerateKeyColumn(tableName);
int count = 0;
//如果是`(`
if (lexerEngine.equalAny(Symbol.LEFT_PAREN)) {
do {
//獲取下一個(gè)分詞垒棋,就是獲取insert into table(field1,filed2...)中`(`后面的fileld1,或者是`,`后面的field2.
lexerEngine.nextToken();
//具體解析里面的字段痪宰,返回值name為具體屬性
SQLExpression sqlExpression = basicExpressionParser.parse(insertStatement);
//根據(jù)不同類型叼架,獲取列屬性名稱
String columnName = null;
if (sqlExpression instanceof SQLPropertyExpression) {
columnName = SQLUtil.getExactlyValue(((SQLPropertyExpression) sqlExpression).getName());
}
if (sqlExpression instanceof SQLIdentifierExpression) {
columnName = SQLUtil.getExactlyValue(((SQLIdentifierExpression) sqlExpression).getName());
}
if (sqlExpression instanceof SQLIgnoreExpression) {
columnName = SQLUtil.getExactlyValue(((SQLIgnoreExpression) sqlExpression).getExpression());
}
//返回值添加列信息
result.add(new Column(columnName, tableName));
if (generateKeyColumn.isPresent() && generateKeyColumn.get().getName().equalsIgnoreCase(columnName)) {
//如果有需要自動(dòng)生成列信息的字段畔裕,記錄字段位置
insertStatement.setGenerateKeyColumnIndex(count);
}
count++;
} while (!lexerEngine.equalAny(Symbol.RIGHT_PAREN) && !lexerEngine.equalAny(Assist.END));
insertStatement.setColumnsListLastPosition(lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length());
lexerEngine.nextToken();
} else {
//忽略若干代碼
}
insertStatement.getColumns().addAll(result);
}
接下來要解析values后面的值了,慣例是InsertValuesClauseParser乖订,該從句首先判斷是否是values分詞的語(yǔ)句扮饶,如果是,則解析乍构,如果不是則直接跳過甜无,等待后面insert ...set處理。
public void parse(final InsertStatement insertStatement, final ShardingMetaData shardingMetaData) {
Collection<Keyword> valueKeywords = new LinkedList<>();
valueKeywords.add(DefaultKeyword.VALUES);
valueKeywords.addAll(Arrays.asList(getSynonymousKeywordsForValues()));
//是否是value,或者是values的關(guān)鍵字哥遮,如果是則解析岂丘,如果不是,則跳過
if (lexerEngine.skipIfEqual(valueKeywords.toArray(new Keyword[valueKeywords.size()]))) {
//解析value值
parseValues(insertStatement);
}
}
private void parseValues(final InsertStatement insertStatement) {
int beginPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
int endPosition;
insertStatement.getSqlTokens().add(new InsertValuesToken(beginPosition, insertStatement.getTables().getSingleTableName()));
do {
beginPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
//分詞器解析必須是`(`開頭
lexerEngine.accept(Symbol.LEFT_PAREN);
List<SQLExpression> sqlExpressions = new LinkedList<>();
int columnsCount = 0;
do {
//解析每一個(gè)分詞sql后面的具體值眠饮,對(duì)應(yīng)sql中的INSERT INTO `table` (`field1`, `field2`) VALUES (10, 1)的10和1
sqlExpressions.add(basicExpressionParser.parse(insertStatement));
//跳過::
skipsDoubleColon();
//列個(gè)數(shù)+1
columnsCount++;
//當(dāng)解析器遇到的分詞為,號(hào)時(shí)奥帘,繼續(xù)解析
} while (lexerEngine.skipIfEqual(Symbol.COMMA));
//刪除自動(dòng)生成的鍵
removeGenerateKeyColumn(insertStatement, columnsCount);
columnsCount = 0;
int parametersCount = 0;
AndCondition andCondition = new AndCondition();
for (Column each : insertStatement.getColumns()) {
SQLExpression sqlExpression = sqlExpressions.get(columnsCount);
//如果是分片項(xiàng),則添加Condition
if (shardingRule.isShardingColumn(each)) {
andCondition.getConditions().add(new Condition(each, sqlExpression));
}
//如果是自動(dòng)生成的列
if (insertStatement.getGenerateKeyColumnIndex() == columnsCount) {
//生成Condition
insertStatement.getGeneratedKeyConditions().add(createGeneratedKeyCondition(each, sqlExpression));
}
//列個(gè)數(shù)+1
columnsCount++;
if (sqlExpression instanceof SQLPlaceholderExpression) {
parametersCount++;
}
}
endPosition = lexerEngine.getCurrentToken().getEndPosition();
//分詞解析器必須以)結(jié)束
lexerEngine.accept(Symbol.RIGHT_PAREN);
//組裝數(shù)據(jù)
insertStatement.getInsertValues().getInsertValues().add(new InsertValue(DefaultKeyword.VALUES, lexerEngine.getInput().substring(beginPosition, endPosition), parametersCount));
insertStatement.getConditions().getOrCondition().getAndConditions().add(andCondition);
} while (lexerEngine.skipIfEqual(Symbol.COMMA));
insertStatement.setInsertValuesListLastPosition(endPosition);
}
如果是insert...set語(yǔ)句,則會(huì)走到InsertSetClauseParser,去解析set語(yǔ)句仪召,如果發(fā)現(xiàn)不是set開頭的語(yǔ)句寨蹋,直接return。
public void parse(final InsertStatement insertStatement) {
//判斷是否是set返咱,如不是钥庇,直接返回
if (!lexerEngine.skipIfEqual(getCustomizedInsertKeywords())) {
return;
}
//刪除無用的Token
removeUnnecessaryToken(insertStatement);
int beginPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
insertStatement.getSqlTokens().add(new InsertValuesToken(beginPosition, insertStatement.getTables().getSingleTableName()));
int parametersCount = 0;
do {
//解析set之后的數(shù)據(jù)庫(kù)列名稱
SQLExpression sqlExpression = basicExpressionParser.parse(insertStatement);
Column column = null;
if (sqlExpression instanceof SQLPropertyExpression) {
column = new Column(SQLUtil.getExactlyValue(((SQLPropertyExpression) sqlExpression).getName()), insertStatement.getTables().getSingleTableName());
}
if (sqlExpression instanceof SQLIdentifierExpression) {
column = new Column(SQLUtil.getExactlyValue(((SQLIdentifierExpression) sqlExpression).getName()), insertStatement.getTables().getSingleTableName());
}
if (sqlExpression instanceof SQLIgnoreExpression) {
column = new Column(SQLUtil.getExactlyValue(((SQLIgnoreExpression) sqlExpression).getExpression()), insertStatement.getTables().getSingleTableName());
}
//列屬性后面必須緊跟=號(hào)
lexerEngine.accept(Symbol.EQ);
//獲取=號(hào)下一分詞類型牍鞠,根據(jù)類型組裝
if (lexerEngine.equalAny(Literals.INT)) {
sqlExpression = new SQLNumberExpression(Integer.parseInt(lexerEngine.getCurrentToken().getLiterals()));
} else if (lexerEngine.equalAny(Literals.FLOAT)) {
sqlExpression = new SQLNumberExpression(Double.parseDouble(lexerEngine.getCurrentToken().getLiterals()));
} else if (lexerEngine.equalAny(Literals.CHARS)) {
sqlExpression = new SQLTextExpression(lexerEngine.getCurrentToken().getLiterals());
} else if (lexerEngine.equalAny(DefaultKeyword.NULL)) {
sqlExpression = new SQLIgnoreExpression(DefaultKeyword.NULL.name());
} else if (lexerEngine.equalAny(Symbol.QUESTION)) {
sqlExpression = new SQLPlaceholderExpression(insertStatement.getParametersIndex());
insertStatement.increaseParametersIndex();
parametersCount++;
} else {
throw new UnsupportedOperationException("");
}
//獲取下一分詞
lexerEngine.nextToken();
if (lexerEngine.equalAny(Symbol.COMMA, DefaultKeyword.ON, Assist.END)) {
//組裝Condition
insertStatement.getConditions().add(new Condition(column, sqlExpression), shardingRule);
} else {
//跳過咖摹,直到出現(xiàn),on
lexerEngine.skipUntil(Symbol.COMMA, DefaultKeyword.ON);
}
//如果是,結(jié)尾,繼續(xù)循環(huán)难述,解析其他的字段
} while (lexerEngine.skipIfEqual(Symbol.COMMA));
//組裝返回?cái)?shù)據(jù)
int endPosition = lexerEngine.getCurrentToken().getEndPosition() - lexerEngine.getCurrentToken().getLiterals().length();
insertStatement.getInsertValues().getInsertValues().add(new InsertValue(DefaultKeyword.VALUES, lexerEngine.getInput().substring(beginPosition, endPosition), parametersCount));
insertStatement.setInsertValuesListLastPosition(endPosition);
}
insert語(yǔ)句解析過程結(jié)束萤晴,解析過程復(fù)雜,能用如此簡(jiǎn)潔的代碼寫完胁后,代碼功底深厚令人折服店读。大寫的服。但其實(shí)還是有一些能優(yōu)化的點(diǎn)攀芯。
最后屯断,再看一下InsertStatement的信息,解析之后侣诺,該對(duì)象組裝了些啥殖演。
對(duì)應(yīng)sql
INSERT INTO `table` (`field1`, `field2`) VALUES (10, 1)
public final class InsertStatement extends DMLStatement {
//列屬性信息
private final Collection<Column> columns = new LinkedList<>();
//自動(dòng)生成的表字段信息
private List<GeneratedKeyCondition> generatedKeyConditions = new LinkedList<>();
//插入的值
private final InsertValues insertValues = new InsertValues();
//數(shù)據(jù)庫(kù)字段屬性最后結(jié)束的位置
private int columnsListLastPosition;
//自動(dòng)生成鍵的位置
private int generateKeyColumnIndex = -1;
//insert values最后結(jié)束的位置
private int insertValuesListLastPosition;
}
public abstract class AbstractSQLStatement implements SQLStatement {
//sql類型
private final SQLType type;
//表信息
private final Tables tables = new Tables();
//分片信息
private final Conditions conditions = new Conditions();
//sql分詞信息
private final List<SQLToken> sqlTokens = new LinkedList<>();
private int parametersIndex;
}