Skip to content

Commit 04b0620

Browse files
authored
Merge pull request #3 from Zxc4wonder/codex/fix-line-comment-roundtrip
fix: preserve SQL semantics after line comments
2 parents b9c9262 + f0a0fae commit 04b0620

3 files changed

Lines changed: 143 additions & 2 deletions

File tree

core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ protected final void print0(char value) {
295295
return;
296296
}
297297

298+
if (endLineComment && value != '\n' && value != '\r') {
299+
println();
300+
}
298301
this.appender.append(value);
299302
}
300303

@@ -366,6 +369,10 @@ protected void print0(String text) {
366369
return;
367370
}
368371

372+
if (endLineComment
373+
&& (text == null || (!text.isEmpty() && text.charAt(0) != '\n' && text.charAt(0) != '\r'))) {
374+
println();
375+
}
369376
this.appender.append(text);
370377
}
371378

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 1999-2017 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.druid.bvt.sql.visitor;
17+
18+
import com.alibaba.druid.DbType;
19+
import com.alibaba.druid.sql.SQLUtils;
20+
import com.alibaba.druid.sql.ast.SQLExpr;
21+
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
22+
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
23+
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
24+
import com.alibaba.druid.util.Utils;
25+
import org.junit.Test;
26+
27+
import java.io.InputStream;
28+
29+
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertNotNull;
31+
import static org.junit.Assert.assertTrue;
32+
33+
public class SQLASTOutputVisitorLineCommentTest {
34+
private static final String SELECT_ITEM_LINE_COMMENT =
35+
"SELECT (id - 1) -- comment\n, name FROM users;";
36+
37+
@Test
38+
public void test_selectItemLineComment_mysql() {
39+
assertSelectItemRoundTrip(SELECT_ITEM_LINE_COMMENT, DbType.mysql, 2);
40+
}
41+
42+
@Test
43+
public void test_selectItemLineComment_postgresql() {
44+
assertSelectItemRoundTrip(SELECT_ITEM_LINE_COMMENT, DbType.postgresql, 2);
45+
}
46+
47+
@Test
48+
public void test_selectItemLineComment_oracle() {
49+
assertSelectItemRoundTrip(SELECT_ITEM_LINE_COMMENT, DbType.oracle, 2);
50+
}
51+
52+
@Test
53+
public void test_selectItemLineComment_clickhouse() {
54+
assertSelectItemRoundTrip(SELECT_ITEM_LINE_COMMENT, DbType.clickhouse, 2);
55+
}
56+
57+
@Test
58+
public void test_lineCommentBeforeFrom() {
59+
assertSelectItemRoundTrip("SELECT id -- comment\nFROM users;", DbType.mysql, 1);
60+
}
61+
62+
@Test
63+
public void test_lineCommentAfterComma() {
64+
String sql = "SELECT\n"
65+
+ " id, -- comment\n"
66+
+ " name\n"
67+
+ "FROM users;";
68+
assertSelectItemRoundTrip(sql, DbType.mysql, 2);
69+
}
70+
71+
@Test
72+
public void test_blockCommentBeforeComma() {
73+
String sql = "SELECT\n"
74+
+ " id /* comment */,\n"
75+
+ " name\n"
76+
+ "FROM users;";
77+
String output = assertSelectItemRoundTrip(sql, DbType.mysql, 2);
78+
assertTrue(output, output.contains("SELECT id, name"));
79+
}
80+
81+
@Test
82+
public void test_lineCommentInFunctionArguments() {
83+
String sql = "SELECT\n"
84+
+ " func(a -- comment\n"
85+
+ " , b)\n"
86+
+ "FROM t;";
87+
SQLSelectStatement statement = parseSelect(sql, DbType.mysql);
88+
SQLSelectQueryBlock queryBlock = queryBlock(statement);
89+
assertEquals(2, methodArguments(queryBlock));
90+
91+
String output = SQLUtils.toSQLString(statement, DbType.mysql);
92+
assertTrue(output, output.contains("-- comment"));
93+
94+
SQLSelectQueryBlock reparsed = queryBlock(parseSelect(output, DbType.mysql));
95+
assertEquals(output, 2, methodArguments(reparsed));
96+
}
97+
98+
@Test
99+
public void test_oracleNumbersResource() {
100+
String resource = "bvt/parser/antlr_grammers_v4_plsql/examples/numbers01.sql";
101+
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
102+
assertNotNull(resource, input);
103+
104+
assertSelectItemRoundTrip(Utils.read(input), DbType.oracle, 12);
105+
}
106+
107+
private String assertSelectItemRoundTrip(String sql, DbType dbType, int expectedItemCount) {
108+
SQLSelectStatement statement = parseSelect(sql, dbType);
109+
SQLSelectQueryBlock queryBlock = queryBlock(statement);
110+
assertEquals(expectedItemCount, queryBlock.getSelectList().size());
111+
112+
String output = SQLUtils.toSQLString(statement, dbType);
113+
if (sql.contains("--")) {
114+
assertTrue(output, output.contains("--"));
115+
}
116+
117+
SQLSelectQueryBlock reparsed = queryBlock(parseSelect(output, dbType));
118+
assertEquals(output, expectedItemCount, reparsed.getSelectList().size());
119+
return output;
120+
}
121+
122+
private SQLSelectStatement parseSelect(String sql, DbType dbType) {
123+
return (SQLSelectStatement) SQLUtils.parseSingleStatement(sql, dbType);
124+
}
125+
126+
private SQLSelectQueryBlock queryBlock(SQLSelectStatement statement) {
127+
return (SQLSelectQueryBlock) statement.getSelect().getQuery();
128+
}
129+
130+
private int methodArguments(SQLSelectQueryBlock queryBlock) {
131+
SQLExpr expr = queryBlock.getSelectList().get(0).getExpr();
132+
return ((SQLMethodInvokeExpr) expr).getArguments().size();
133+
}
134+
}

core/src/test/resources/bvt/parser/clickhouse/0.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ SELECT CASE
9090
-- '${date}'
9191
-- origin end
9292
-- new
93-
'2025-07-08' -- new end, 4
94-
), '1231'))
93+
'2025-07-08' -- new end
94+
, 4), '1231'))
9595
END AS date_e
9696
------------------------------------------------------------------------------------------------------------------------
9797
select physical_wh--,wh_type

0 commit comments

Comments
 (0)