From f57afbe95670ff0ae2d88b0a153b57566fac9470 Mon Sep 17 00:00:00 2001 From: luoningift <847185992@qq.com> Date: Tue, 24 Feb 2026 11:25:03 +0800 Subject: [PATCH 1/6] Fix formatting of 'WHERE' clause in OracleOutputVisitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit where缺少空格导致SQLMergeStatement语句报错 --- .../druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 0d7ec8ba2a..762529c2bc 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -2991,7 +2991,7 @@ public boolean visit(SQLMergeStatement.WhenDelete x) { } SQLExpr where = x.getWhere(); if (where != null) { - print0(ucase ? " WHERE " : " where"); + print0(ucase ? " WHERE " : " where "); printExpr(where, parameterized); } return false; From e4fbf6121cdb325f8094a95800aa9349ad503538 Mon Sep 17 00:00:00 2001 From: luoningift <847185992@qq.com> Date: Tue, 24 Feb 2026 11:31:22 +0800 Subject: [PATCH 2/6] Fix formatting of timezone bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLUtils.toSQLString 格式化oracle timezone错误 --- .../druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 762529c2bc..08fff867d7 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -1088,7 +1088,7 @@ public boolean visit(OracleDatetimeExpr x) { if (timeZone instanceof SQLIdentifierExpr) { if (((SQLIdentifierExpr) timeZone).getName().equalsIgnoreCase("LOCAL")) { - print0(ucase ? " AT LOCAL" : "alter session set "); + print0(ucase ? " AT LOCAL " : " at local "); return false; } } From 7e447fc6c78d0660f64d21dcf7f8d0afa6314205 Mon Sep 17 00:00:00 2001 From: luoning <847185992@qq.com> Date: Tue, 10 Mar 2026 15:45:31 +0800 Subject: [PATCH 3/6] add test case for OracleOutputVisitor merge when delete where and timestamp --- .../oracle/visitor/OracleOutputVisitor.java | 2 +- ...acleOutputVisitorTest_MergeWhenDelete.java | 43 +++++++++++++++++++ ...cleOutputVisitorTest_timestampAtLocal.java | 40 +++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_MergeWhenDelete.java create mode 100644 core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 08fff867d7..8112fd2648 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -1088,7 +1088,7 @@ public boolean visit(OracleDatetimeExpr x) { if (timeZone instanceof SQLIdentifierExpr) { if (((SQLIdentifierExpr) timeZone).getName().equalsIgnoreCase("LOCAL")) { - print0(ucase ? " AT LOCAL " : " at local "); + print0(ucase ? " AT LOCAL" : " at local"); return false; } } diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_MergeWhenDelete.java b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_MergeWhenDelete.java new file mode 100644 index 0000000000..6776efe0f3 --- /dev/null +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_MergeWhenDelete.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.oracle.visitor; + +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import junit.framework.TestCase; + +import java.util.List; + +public class OracleOutputVisitorTest_MergeWhenDelete extends TestCase { + public void test_0() throws Exception { + String sql = "merge into target_table t using source_table s on (t.id = s.id) " + + "when matched then update set t.name = s.name " + + "where t.status = 'ACTIVE' " + + "delete where t.status = 'INACTIVE'" + + "when not matched then insert (id, name) values " + + "(s.id, s.name) where s.status = 'ACTIVE';"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + assertEquals(1, statementList.size()); + + String formatSql = SQLUtils.toSQLString(stmt, DbType.oracle, new SQLUtils.FormatOption(false, false, false)); + assertEquals("merge into target_table t using source_table s on (t.id = s.id) when matched then update set t.name = s.name where t.status = 'ACTIVE' delete where t.status = 'INACTIVE' when not matched then insert (id, name) values (s.id, s.name) where s.status = 'ACTIVE';", formatSql); + } +} diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java new file mode 100644 index 0000000000..eb4d6ccd01 --- /dev/null +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java @@ -0,0 +1,40 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.oracle.visitor; + +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import junit.framework.TestCase; + +import java.util.List; + +public class OracleOutputVisitorTest_timestampAtLocal extends TestCase { + public void test_0() throws Exception { + String sql = "INSERT INTO ALL_TYPE_FIELDS (\"TIMESTAMP WITH LOCAL TIME ZONE\", \"TIMESTAMP WITH LOCAL TIME ZONE\") VALUES (SYSTIMESTAMP AT LOCAL, SYSTIMESTAMP AT TIME ZONE 'UTC')"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + assertEquals(1, statementList.size()); + + String formatSql = SQLUtils.toSQLString(stmt, DbType.oracle, new SQLUtils.FormatOption(false, false, false)); + assertEquals("insert into ALL_TYPE_FIELDS (\"TIMESTAMP WITH LOCAL TIME ZONE\", \"TIMESTAMP WITH LOCAL TIME ZONE\") values (SYSTIMESTAMP at local, SYSTIMESTAMP at time zone 'UTC')", formatSql); + } +} From f6d5f2942ea5f9fda28b464f9a7eb62ca7d22b6c Mon Sep 17 00:00:00 2001 From: luoning <847185992@qq.com> Date: Tue, 10 Mar 2026 16:41:09 +0800 Subject: [PATCH 4/6] add test case for extractColumns subquery --- .../SchemaResolveVisitorFactory.java | 8 +++++-- .../mysql/resolve/Resolve_AllColumn_Test.java | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java b/core/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java index 440a5416c9..5ab886f82a 100644 --- a/core/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java +++ b/core/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java @@ -1699,10 +1699,14 @@ static void extractColumns(SchemaResolveVisitor visitor, return; // skip } } - + String subQueryAlias = from.getAlias(); for (SQLSelectItem subSelectItem : subSelectList) { String alias = subSelectItem.computeAlias(); - columns.add(new SQLSelectItem(new SQLIdentifierExpr(alias))); + if (subQueryAlias != null) { + columns.add(new SQLSelectItem(new SQLPropertyExpr(subQueryAlias, alias))); + } else { + columns.add(new SQLSelectItem(new SQLIdentifierExpr(alias))); + } } } else if (from instanceof SQLUnionQueryTableSource) { SQLSelectQueryBlock firstQueryBlock = ((SQLUnionQueryTableSource) from).getUnion().getFirstQueryBlock(); diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java index 9613414681..eacdda2fce 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java @@ -83,4 +83,25 @@ public void test_resolve_2() throws Exception { "\tFROM t_emp\n" + ") x", stmt.toString()); } + + public void test_resolve_3() throws Exception { + SchemaRepository repository = new SchemaRepository(DbType.mysql); + + repository.acceptDDL("create table t_emp(emp_id bigint, name varchar(20));"); + repository.acceptDDL("create table t_emp_copy(emp_copy_id bigint, name_copy varchar(20));"); + + + SQLStatement stmt = SQLUtils.parseSingleMysqlStatement("select * from (select * from t_emp) a, (select * from t_emp_copy) b where a.emp_id = b.emp_copy_id"); + repository.resolve(stmt, SchemaResolveVisitor.Option.ResolveAllColumn); + + assertEquals("SELECT a.emp_id, a.name, b.emp_copy_id, b.name_copy\n" + + "FROM (\n" + + "\tSELECT emp_id, name\n" + + "\tFROM t_emp\n" + + ") a, (\n" + + "\t\tSELECT emp_copy_id, name_copy\n" + + "\t\tFROM t_emp_copy\n" + + "\t) b\n" + + "WHERE a.emp_id = b.emp_copy_id", stmt.toString()); + } } From 5446a4eec66f2121a63d0d84227dc00479d54c4a Mon Sep 17 00:00:00 2001 From: luoning <847185992@qq.com> Date: Tue, 10 Mar 2026 16:44:20 +0800 Subject: [PATCH 5/6] DB2Lexer not assigned db2 type --- .../com/alibaba/druid/sql/dialect/db2/parser/DB2Lexer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2Lexer.java b/core/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2Lexer.java index 6269f3282d..1edce8acd2 100644 --- a/core/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2Lexer.java +++ b/core/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2Lexer.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.db2.parser; +import com.alibaba.druid.DbType; import com.alibaba.druid.sql.parser.DialectFeature; import com.alibaba.druid.sql.parser.Keywords; import com.alibaba.druid.sql.parser.Lexer; @@ -67,7 +68,7 @@ public DB2Lexer(String input) { } public DB2Lexer(String input, SQLParserFeature... features) { - super(input); + super(input, DbType.db2); for (SQLParserFeature feature : features) { config(feature, true); } From 63a126e353e35be3f4fe4be61d96889aaa70684a Mon Sep 17 00:00:00 2001 From: luoning <847185992@qq.com> Date: Wed, 11 Mar 2026 09:34:48 +0800 Subject: [PATCH 6/6] errors reported by Checkstyle 9.3 bug modify --- .../druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java | 1 - .../visitor/OracleOutputVisitorTest_timestampAtLocal.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java index eacdda2fce..79cc03c4c0 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/mysql/resolve/Resolve_AllColumn_Test.java @@ -90,7 +90,6 @@ public void test_resolve_3() throws Exception { repository.acceptDDL("create table t_emp(emp_id bigint, name varchar(20));"); repository.acceptDDL("create table t_emp_copy(emp_copy_id bigint, name_copy varchar(20));"); - SQLStatement stmt = SQLUtils.parseSingleMysqlStatement("select * from (select * from t_emp) a, (select * from t_emp_copy) b where a.emp_id = b.emp_copy_id"); repository.resolve(stmt, SchemaResolveVisitor.Option.ResolveAllColumn); diff --git a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java index eb4d6ccd01..6d8ef06590 100644 --- a/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java +++ b/core/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleOutputVisitorTest_timestampAtLocal.java @@ -19,8 +19,6 @@ import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; import junit.framework.TestCase; import java.util.List;