Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLParser]列名外的引号在解析后丢失。 #682

Open
xuse opened this issue Oct 23, 2014 · 4 comments
Open

[SQLParser]列名外的引号在解析后丢失。 #682

xuse opened this issue Oct 23, 2014 · 4 comments

Comments

@xuse
Copy link

xuse commented Oct 23, 2014

使用Druid Parser编写了一个SQLServer2005的LimitHandler,用于将SQL语句改写为分页后的SQL语句。当数据库列使用SQLServer保留字以后,拟使用引号来修饰在列名。但发现解析后引号丢失。测试案例如下——

@Test
public void testDruid(){
    String sql="select top 3 t.\"desc\",t.\"top\",t.\"percent\",t.comment,t.\"order\" from keyword t";
    SQLServerSelectParser parser=new SQLServerSelectParser(sql);
    SQLSelect select=parser.select();
    SQLServerOutputVisitor ov=new SQLServerOutputVisitor(new StringBuilder());
    ov.setPrettyFormat(false);
    select.accept(ov);
    System.out.println(ov.getAppender());
    Assert.assertEquals("SELECT TOP 3 t.\"desc\", t.\"top\", t.\"percent\", t.comment, t.\"order\" FROM keyword t", ov.getAppender().toString());
}
@xuse xuse closed this as completed Oct 23, 2014
@xuse xuse reopened this Oct 23, 2014
@xuse
Copy link
Author

xuse commented Oct 23, 2014

补充一下,MySQL的转义符就没这个问题——

@Test
public void testDruidMySQL(){
    String sql="select t.`desc`,t.top,t.`percent`,t.comment,t.`order` from keyword t";
    MySqlSelectParser parser=new MySqlSelectParser(sql);
    SQLSelect select=parser.select();
    MySqlOutputVisitor ov=new MySqlOutputVisitor(new StringBuilder());
    ov.setPrettyFormat(false);
    select.accept(ov);
    System.out.println(ov.getAppender());
    Assert.assertEquals("SELECT t.`desc`, t.top, t.`percent`, t.comment, t.`order` FROM keyword t", ov.getAppender().toString());
}

希望能一视同仁。

@yakolee
Copy link
Contributor

yakolee commented Nov 21, 2014

SQL Server使用sql关键字做字段名时,可以加上[]

@xuse
Copy link
Author

xuse commented Nov 22, 2014

To yakolee: 是的,你的建议很好的解决了我的问题。因为Oracle、MYSQL等数据库的分页改写规则非常简单,无需用到AST,唯有SQLServer的分页改写复杂一些。

不过我重新测试了一下相关功能以后,建议你们还是在

SQLExprParser.java : 653的地方,即方法

com.alibaba.druid.sql.parser.SQLExprParser.dotRest(SQLExpr) 

中,将Token.IDENTIFIER和Token.LITERAL_CHARS两种Token分开处理。
修改后代码如下:

        if (lexer.token() == Token.IDENTIFIER) {
            name = lexer.stringVal();
            lexer.nextToken();
        }else if (lexer.token() == Token.LITERAL_CHARS
            || lexer.token() == Token.LITERAL_ALIAS) {
            name = '"'+lexer.stringVal()+'"';
            lexer.nextToken();

即可解决我提出的问题。为什么这样说呢?你们不妨运行一下

    String sql = "select top 3 t.\"desc\",\"top\"  from keyword t";
    SQLServerSelectParser parser = new SQLServerSelectParser(sql);
    SQLSelect select = parser.select();
    SQLServerOutputVisitor ov = new SQLServerOutputVisitor(new StringBuilder());
    ov.setPrettyFormat(false);
    select.accept(ov);
    System.out.println(ov.getAppender());

会发现 t."desc"中的引号丢掉了,而"top"的引号缺没有丢掉。因为在SQLExprParser.java : 554行是这样写的

        case LITERAL_ALIAS:
            sqlExpr = new SQLIdentifierExpr('"' + lexer.stringVal() + '"');
            lexer.nextToken();
            break;

这就是说,当没有表名限定符时返回SQLIdentifierExpr时保留了引号,当加上表名限定符后,返回的SQLPropertyExpr中丢失了引号。这造成了同一个框架内的行为不一致。
正确的处理是要么都不要保留引号,要么都保留引号。

@xuse
Copy link
Author

xuse commented Nov 22, 2014

接上贴。因为同样是列名,有时是SQLIdentifierExpr,有时是SQLPropertyExpr中的name属性。这样的AST访问较为困难,让Visitor的访问行为变得难以编写,不利于扩展。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants