From 2ada35059d3a6bcff8b6c955d73a6678c042cb07 Mon Sep 17 00:00:00 2001 From: LiZongbo Date: Sat, 16 Mar 2024 01:07:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMySQL=20create=20user?= =?UTF-8?q?=E7=9A=84sql=E8=A7=A3=E6=9E=90bug=E5=B9=B6=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E9=83=A8=E5=88=86=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91=20#5774?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 先前解析是有误的,因此修复,并根据官方语法,增加了几种情况的识别解析 --- .../statement/MySqlCreateUserStatement.java | 9 +++ .../mysql/parser/MySqlStatementParser.java | 66 +++++++++++-------- .../mysql/visitor/MySqlOutputVisitor.java | 5 ++ .../druid/bvt/sql/mysql/issues/Issue5774.java | 63 ++++++++++++++++++ 4 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 core/src/test/java/com/alibaba/druid/bvt/sql/mysql/issues/Issue5774.java diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlCreateUserStatement.java b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlCreateUserStatement.java index 7e99dfae97..fc91aec92e 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlCreateUserStatement.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlCreateUserStatement.java @@ -59,6 +59,7 @@ public void accept0(MySqlASTVisitor visitor) { public static class UserSpecification extends MySqlObjectImpl { private SQLExpr user; private boolean passwordHash; + private boolean randomPassword; private SQLExpr password; private SQLExpr authPlugin; private boolean pluginAs; @@ -71,6 +72,14 @@ public void setUser(SQLExpr user) { this.user = (SQLName) user; } + public boolean isRandomPassword() { + return randomPassword; + } + + public void setRandomPassword(boolean randomPassword) { + this.randomPassword = randomPassword; + } + public boolean isPasswordHash() { return passwordHash; } diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index 1672f3712f..72fd0a76bc 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -911,40 +911,41 @@ public SQLStatement parseCreateUser() { stmt.setIfNotExists(true); } - SQLExpr expr = exprParser.primary(); - if (expr instanceof SQLCharExpr) { - expr = new SQLIdentifierExpr(((SQLCharExpr) expr).getText()); - } - - if (expr instanceof SQLIdentifierExpr - && lexer.token() == Token.VARIANT - && lexer.stringVal().charAt(0) == '@' - ) { - String str = lexer.stringVal(); - MySqlUserName mySqlUserName = new MySqlUserName(); - mySqlUserName.setUserName(((SQLIdentifierExpr) expr).getName()); - mySqlUserName.setHost(str.substring(1)); - expr = mySqlUserName; + MySqlUserName mySqlUserName = new MySqlUserName(); + mySqlUserName.setUserName(trimQuotesBeginAndEnd(lexer.stringVal())); + lexer.nextToken(); + String maybeHost=lexer.stringVal(); + if ("@".equals(maybeHost)) { + lexer.nextToken(); + mySqlUserName.setHost(trimQuotesBeginAndEnd(lexer.stringVal())); + lexer.nextToken(); + } else if (maybeHost.startsWith("@")) { // eg: @localhost + maybeHost = maybeHost.substring(1); + mySqlUserName.setHost(trimQuotesBeginAndEnd(maybeHost)); lexer.nextToken(); } - userSpec.setUser(expr); + userSpec.setUser(mySqlUserName); if (lexer.identifierEquals(FnvHash.Constants.IDENTIFIED)) { lexer.nextToken(); if (lexer.token() == Token.BY) { lexer.nextToken(); - - if (lexer.identifierEquals("PASSWORD")) { + if (lexer.identifierEquals("RANDOM")) { lexer.nextToken(); - userSpec.setPasswordHash(true); - } - - SQLExpr password = this.exprParser.expr(); - if (password instanceof SQLIdentifierExpr || password instanceof SQLCharExpr) { - userSpec.setPassword(password); + acceptIdentifier("PASSWORD"); + userSpec.setRandomPassword(true); } else { - throw new ParserException("syntax error. invalid " + password + " expression."); + if (lexer.identifierEquals("PASSWORD")) { + lexer.nextToken(); + userSpec.setPasswordHash(true); + } + SQLExpr password = this.exprParser.expr(); + if (password instanceof SQLIdentifierExpr || password instanceof SQLCharExpr) { + userSpec.setPassword(password); + } else { + throw new ParserException("syntax error. invalid " + password + " expression."); + } } } else if (lexer.token() == Token.WITH) { @@ -961,11 +962,7 @@ public SQLStatement parseCreateUser() { if (userSpec.isPluginAs()) { // Remove ' because lexer don't remove it when token after as. String psw = lexer.stringVal(); - if (psw.length() >= 2 && '\'' == psw.charAt(0) && '\'' == psw.charAt(psw.length() - 1)) { - userSpec.setPassword(new SQLCharExpr(psw.substring(1, psw.length() - 1))); - } else { - userSpec.setPassword(new SQLCharExpr(psw)); - } + userSpec.setPassword(new SQLCharExpr(trimQuotesBeginAndEnd(psw))); lexer.nextToken(); } else { userSpec.setPassword(this.exprParser.charExpr()); @@ -987,6 +984,17 @@ public SQLStatement parseCreateUser() { return stmt; } + static String trimQuotesBeginAndEnd(String str) { + if (str == null || str.length() < 2) { + return str; + } + char beginChar = str.charAt(0); + char endChar = str.charAt(str.length() - 1); + if ((beginChar == '\'' && endChar == '\'') || (beginChar == '\"' && endChar == '\"')) { + return str.substring(1, str.length() - 1); + } + return str; + } public SQLStatement parseKill() { accept(Token.KILL); diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index ee8bb8830e..5ac52f0f5d 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -2012,6 +2012,11 @@ public boolean visit(UserSpecification x) { print0(ucase ? "PASSWORD " : "password "); } x.getPassword().accept(this); + } else { + if (x.isRandomPassword()) { + print0(ucase ? " IDENTIFIED BY " : " identified by "); + print0(ucase ? "RANDOM PASSWORD " : "random password "); + } } return false; } diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/issues/Issue5774.java b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/issues/Issue5774.java new file mode 100644 index 0000000000..5ed43ea766 --- /dev/null +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/issues/Issue5774.java @@ -0,0 +1,63 @@ +package com.alibaba.druid.bvt.sql.mysql.issues; + +import java.util.List; + +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author lizongbo + * @see Issue来源 + * @see CREATE USER Statement + * @see Specifying Account Names + */ +public class Issue5774 { + + + @Test + public void test_createuser_sql() { + for (String sql : new String[]{ + "create user IF NOT EXISTS \"ptscr-2kaq\"@\"%\" identified by \"asdasdasdasd\";", + "create user IF NOT EXISTS \"ptscr-2kaq\" identified by \"asdasdasdasd\";", + "create user \"ptscr-2kaq\"@\"%\" identified by \"asdasdasdasd\";", + "create user \"ptscr-2kaq\"@\"%\" identified by RANDOM PASSWORD;", + "CREATE USER 'jeffrey'@'localhost' IDENTIFIED BY 'password';", + "CREATE USER 'jeffrey'@'localhost'\n" + + " IDENTIFIED BY 'password';", + "CREATE USER 'jeffrey'@localhost IDENTIFIED BY 'password';", +// "CREATE USER 'jeffrey'@'localhost'\n" +// + " IDENTIFIED BY 'new_password' PASSWORD EXPIRE;", + "CREATE USER 'jeffrey'@'localhost'\n" + + " IDENTIFIED WITH mysql_native_password BY 'password';", +// "CREATE USER 'u1'@'localhost'\n" +// + " IDENTIFIED WITH caching_sha2_password\n" +// + " BY 'sha2_password'\n" +// + " AND IDENTIFIED WITH authentication_ldap_sasl\n" +// + " AS 'uid=u1_ldap,ou=People,dc=example,dc=com';", +// "CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE;", +// "CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT;", +// "CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;", + }) { + DbType dbType = DbType.mysql; + System.out.println("原始的sql===" + sql); + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + List statementList = parser.parseStatementList(); + System.out.println("生成的sql===" + statementList); + StringBuilder sb = new StringBuilder(); + for (SQLStatement statement : statementList) { + sb.append(statement.toString()).append(";"); + } + sb.deleteCharAt(sb.length() - 1); + parser = SQLParserUtils.createSQLStatementParser(sb.toString(), dbType); + List statementListNew = parser.parseStatementList(); + System.out.println("再生成sql===" + statementListNew); + assertEquals(statementList.toString(), statementListNew.toString()); + } + } +}