jsqlparser:修改语法定义(JSqlParserCC.jjt)实现UPSERT支持Phoenix语法ON DUPLICATE KEY IGNORE

  • Post author:
  • Post category:其他


最近在用

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

如果批准,就可以在下一个官方版本中更新了。



参考资料


《UPSERT》



《JavaCC》



版权声明:本文为10km原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。