最近在用
jsqlparser
4.5解析SQL时遇到了一个问题,
如下是
apache phoenix
的
UPSERT
语句
UPSERT INTO TEST (ID, COUNTER) VALUES (123, 0) ON DUPLICATE KEY IGNORE
ON DUPLICATE KEY IGNORE
即为当主键重复时忽略,这与MySQL的
IGNORE
语法不同:
INSERT IGNORE INTO TEST (ID, COUNTER) VALUES (123, 0)
jsqlparser目前的最新版本支持
UPSERT
语法,也支持
ON DUPLICATE KEY UPDATE
,但不支持
ON DUPLICATE KEY IGNORE
。
所以jsqlparser解析SQL遇到
ON DUPLICATE KEY IGNORE
语法时解析器会报错
expect "UPDATE"
JSqlParserCC.jjt
为了解决这个问题我翻了jsqlparser的源码,找到了jsqlparser的语法定义文件(JSqlParserCC.jjt),是基于
JavaCC
实现的。
JSqlParserCC.jjt
文件的位置在:
jsqlparser/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
打开它在1635行就能找到
UPSERT
语句分析
ON DUPLICATE KEY UPDATE
语法的位置,
为了看懂语法定义文件(JSqlParserCC.jjt),我又恶补了一下JavaCC的语法,基本上能看懂了。
原来的代码是
[ <K_ON> <K_DUPLICATE> <K_KEY> <K_UPDATE>
{ useDuplicate = true; }
tableColumn=Column() "=" exp=SimpleExpression()
{
duplicateUpdateColumns = new ArrayList<Column>();
duplicateUpdateExpressionList = new ArrayList<Expression>();
duplicateUpdateColumns.add(tableColumn);
duplicateUpdateExpressionList.add(exp);
}
("," tableColumn=Column() "=" exp=SimpleExpression()
{ duplicateUpdateColumns.add(tableColumn);
duplicateUpdateExpressionList.add(exp); } )*]
修改为如下:
[<K_ON> <K_DUPLICATE> <K_KEY>
(
<K_IGNORE>
{ modifierIgnore = true; }
|
<K_UPDATE>
{ useDuplicate = true; }
tableColumn=Column() "=" exp=SimpleExpression()
{
duplicateUpdateColumns = new ArrayList<Column>();
duplicateUpdateExpressionList = new ArrayList<Expression>();
duplicateUpdateColumns.add(tableColumn);
duplicateUpdateExpressionList.add(exp);
}
("," tableColumn=Column() "=" exp=SimpleExpression()
{ duplicateUpdateColumns.add(tableColumn);
duplicateUpdateExpressionList.add(exp); } )*
)
]
即允许
ON DUPLICATE
后为
IGNORE
或
UPDATE
,为
IGNORE
时将
modifierIgnore
设置为
true
modifierIgnore
为新增加的变量定义,后续会用到。
如下图为主要修改的前后对比
Upsert.java
不仅要修改语法定义文件,同时还要修改
src/main/java/net/sf/jsqlparser/statement/upsert/Upsert.java
,相应增加
modifierIgnore
字段,
完整代码参见我的码云仓库:
https://gitee.com/l0km/JSqlParser.git
【不是master分支,是gyd分支,gyd分支,gyd分支,重要的事情说三遍】
下载代码
git clone -b gyd https://gitee.com/l0km/JSqlParser.git
maven编译
cd JSqlParser
$ mvn clean install -DskipTests=true
单元测试
jsqlparser项目有比较完备的单元测试,上述修改完毕执行
UPSERT
语句的单元测试
UpsertTest.java
验证修改是否有效。
先执行原有的
ON DUPLICATE KEY UPDATE
语法测试,看看是否有影响
$ mvn -Dtest=UpsertTest#testUpsertDuplicate test
我在
src/test/java/net/sf/jsqlparser/statement/upsert/UpsertTest.java
增加了新的单元测试方法
testUpsertIgnore
以验证
ON DUPLICATE KEY IGNORE
语法有效性。
@Test
public void testUpsertIgnore() throws JSQLParserException {
String statement = "UPSERT INTO TEST (ID, COUNTER) VALUES (123, 0) ON DUPLICATE KEY IGNORE";
Upsert upsert = (Upsert) parserManager.parse(new StringReader(statement));
assertEquals("TEST", upsert.getTable().getName());
assertEquals(2, upsert.getColumns().size());
assertTrue(upsert.isUseValues());
assertEquals("ID", upsert.getColumns().get(0).getColumnName());
assertEquals("COUNTER", upsert.getColumns().get(1).getColumnName());
assertEquals(2, ((ExpressionList) upsert.getItemsList()).getExpressions().size());
assertEquals(123, ((LongValue) upsert.getItemsList(ExpressionList.class).getExpressions().get(0)).getValue());
assertEquals(0, ((LongValue) upsert.getItemsList(ExpressionList.class).getExpressions().get(1)).getValue());
assertTrue(upsert.isModifierIgnore());
assertEquals(statement, "" + upsert);
}
执行新增的
ON DUPLICATE KEY IGNORE
语法测试
$ mvn -Dtest=UpsertTest#testUpsertIgnore test
Pull Request
此问题修复已经向
jsqlparser
提交了Pull Request
参见
https://github.com/JSQLParser/JSqlParser/pull/1689
如果批准,就可以在下一个官方版本中更新了。
参考资料