添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最近在用 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后为IGNOREUPDATE,为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》

https://gitee.com/l0km/JSqlParser.git 【gyd分支,gyd分支,gyd分支,重要的事情说三遍】为了这个我翻了jsqlparser的源码,找到了jsqlparser的语法定义文件(JSqlParserCC.jjt),是基于。为了看懂语法定义文件(JSqlParserCC.jjt),我又恶补了一下javacc,但是jsqlparser目前的最新版本支持UPSET语法,也支持。打开它在1635行就能找到UPSERT语句分析。语句的位置,如下修改增加对。
1、 jsqlparse介绍 JSqlParse是一款很精简的sql解析工具,它可以将常用的sql文本解析成具有层级结构的“语法树”,我们可以针对解析后的“树节点(也即官网里说的有层次结构的java类)”进行处理进而生成符合我们要求的sql形式。 官网给的介绍很简洁:JSqlParser 解析 SQL 语句并将其转换为 Java 类的层次结构。生成的层次结构可以使用访问者模式进行访问(官网地址:JSqlParser - Home)。 官网的介绍即是该中间件的全部,虽然介绍很短,但是其功能着实强悍。
熟悉了Jsql插件的一些基本使用情况以后,发现还有一些不足,目前把这几天写的东西放上来了。解决一些这样的问题:    1.给定的SQL语句是否满足基本语法问题?——我尝试将String转化为statement将Jsql的异常信息保存,并提取有效信息,返回错误单词+位置信息    2.insert语句批量插入数据的修改————正则匹配values并保存到List>中,replace函数替换原来S