diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..df7e1190ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,27 @@ + +**dbtype:** +**dbversion:** +**druid verion:** +**error sql:** +**testcase code:** +**stacktrace info:** +**error info:** +--- + diff --git a/core/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java b/core/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java index 79a0d00126..042b13eb86 100644 --- a/core/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java +++ b/core/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java @@ -24,6 +24,8 @@ public final class SQLSelectOrderByItem extends SQLObjectImpl implements SQLReplaceable { protected SQLExpr expr; protected String collate; + protected SQLExpr opclass; + protected SQLOrderingSpecification type; protected NullsOrderType nullsOrderType; @@ -60,6 +62,14 @@ public void setCollate(String collate) { this.collate = collate; } + public SQLExpr getOpclass() { + return opclass; + } + + public void setOpclass(SQLExpr opclass) { + this.opclass = opclass; + } + public SQLOrderingSpecification getType() { return this.type; } diff --git a/core/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/core/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index fa3db1d13b..7b6a005c6f 100644 --- a/core/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/core/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -2987,8 +2987,17 @@ public SQLSelectOrderByItem parseSelectOrderByItem() { String collate = lexer.stringVal(); item.setCollate(collate); lexer.nextToken(); + if (lexer.token == Token.DOT) { + lexer.nextToken(); + String collateOther = lexer.stringVal(); + item.setCollate(collate + "." + collateOther); + lexer.nextToken(); + } + } + if (lexer.token == Token.LITERAL_ALIAS) { + SQLExpr name = this.expr(); + item.setOpclass(name); } - if (lexer.token == Token.ASC) { lexer.nextToken(); item.setType(SQLOrderingSpecification.ASC); diff --git a/core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index b722372d10..4fdbd8e274 100644 --- a/core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -2804,18 +2804,20 @@ public boolean visit(SQLSelectOrderByItem x) { printExpr(expr, parameterized); } - SQLOrderingSpecification type = x.getType(); - if (type != null) { - print(' '); - print0(ucase ? type.name : type.nameLCase); - } - String collate = x.getCollate(); if (collate != null) { print0(ucase ? " COLLATE " : " collate "); print0(collate); } - + if (x.getOpclass() != null) { + print(' '); + x.getOpclass().accept(this); + } + SQLOrderingSpecification type = x.getType(); + if (type != null) { + print(' '); + print0(ucase ? type.name : type.nameLCase); + } SQLSelectOrderByItem.NullsOrderType nullsOrderType = x.getNullsOrderType(); if (nullsOrderType != null) { print(' '); diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/postgresql/issues/Issue5780.java b/core/src/test/java/com/alibaba/druid/bvt/sql/postgresql/issues/Issue5780.java new file mode 100644 index 0000000000..582982df83 --- /dev/null +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/postgresql/issues/Issue5780.java @@ -0,0 +1,59 @@ +package com.alibaba.druid.bvt.sql.postgresql.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 ALTER TABLE + * @see CREATE INDEX + */ +public class Issue5780 { + + @Test + public void test_parse_alter_table_sql() { + for (DbType dbType : new DbType[]{DbType.postgresql, DbType.greenplum, DbType.edb}) { + + for (String sql : new String[]{ + "CREATE INDEX \"index_log\" ON \"public\".\"check_log\" USING btree (\n" + + " \"t_no\" COLLATE \"pg_catalog\".\"default\" \"pg_catalog\".\"text_ops\" ASC NULLS LAST\n" + + ");", + //"CREATE UNIQUE INDEX title_idx ON films (title);", + //"CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);", +// "CREATE INDEX title_idx ON films (title) WITH (deduplicate_items = off);", +// "CREATE INDEX ON films ((lower(title)));", +// "CREATE INDEX title_idx_german ON films (title COLLATE \"de_DE\");", +// "CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST);", +// "CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70);", +// "CREATE INDEX gin_idx ON documents_table USING GIN (locations) WITH (fastupdate = off);", +// "CREATE INDEX code_idx ON films (code) TABLESPACE indexspace;", +// "CREATE INDEX pointloc\n" +// + " ON points USING gist (box(location,location));", +// "CREATE INDEX CONCURRENTLY sales_quantity_index ON sales_table (quantity);", + }) { + 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()); + } + } + } +}