Skip to content

Commit 185c626

Browse files
[jdbc-v2] Support multi-dot notation for database names
Fixes #2650 Changes: - Updated ANTLR grammar databaseIdentifier rule to support multiple dot-separated identifiers: identifier (DOT identifier)* - Added extractTableName() helper method in SqlParserFacade to properly handle multi-part table identifiers by unquoting each part separately - Added testMultiDotNotation() test with 3 test cases covering SELECT, INSERT, and quoted identifiers - All 367 existing tests continue to pass This allows database names like 'a.b' in table references such as 'a.b.c' or '`db.part1`.`table`' to be parsed correctly.
1 parent d38c31b commit 185c626

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

jdbc-v2/src/main/antlr4/com/clickhouse/jdbc/internal/parser/antlr4/ClickHouseParser.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,7 @@ tableArgExpr
11721172
// Databases
11731173

11741174
databaseIdentifier
1175-
: identifier
1175+
: identifier (DOT identifier)*
11761176
;
11771177

11781178
// Basics

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,47 @@ public void enterAssignmentValuesList(ClickHouseParser.AssignmentValuesListConte
265265
}
266266

267267

268+
private String extractTableName(ClickHouseParser.TableIdentifierContext tableId) {
269+
if (tableId == null) {
270+
return null;
271+
}
272+
273+
StringBuilder tableName = new StringBuilder();
274+
275+
// Handle database identifier if present
276+
if (tableId.databaseIdentifier() != null) {
277+
ClickHouseParser.DatabaseIdentifierContext dbCtx = tableId.databaseIdentifier();
278+
// Database identifier can have multiple parts: identifier (DOT identifier)*
279+
List<ClickHouseParser.IdentifierContext> dbParts = dbCtx.identifier();
280+
for (int i = 0; i < dbParts.size(); i++) {
281+
if (i > 0) {
282+
tableName.append('.');
283+
}
284+
tableName.append(SQLUtils.unquoteIdentifier(dbParts.get(i).getText()));
285+
}
286+
tableName.append('.');
287+
}
288+
289+
// Handle table identifier
290+
if (tableId.identifier() != null) {
291+
tableName.append(SQLUtils.unquoteIdentifier(tableId.identifier().getText()));
292+
}
293+
294+
return tableName.toString();
295+
}
296+
268297
@Override
269298
public void enterTableExprIdentifier(ClickHouseParser.TableExprIdentifierContext ctx) {
270299
if (ctx.tableIdentifier() != null) {
271-
parsedStatement.setTable(SQLUtils.unquoteIdentifier(ctx.tableIdentifier().getText()));
300+
parsedStatement.setTable(extractTableName(ctx.tableIdentifier()));
272301
}
273302
}
274303

275304
@Override
276305
public void enterInsertStmt(ClickHouseParser.InsertStmtContext ctx) {
277306
ClickHouseParser.TableIdentifierContext tableId = ctx.tableIdentifier();
278307
if (tableId != null) {
279-
parsedStatement.setTable(SQLUtils.unquoteIdentifier(tableId.getText()));
308+
parsedStatement.setTable(extractTableName(tableId));
280309
}
281310

282311
ClickHouseParser.ColumnsClauseContext columns = ctx.columnsClause();

jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/BaseSqlParserFacadeTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,31 @@ public void testStmtWithUUID() {
160160
Assert.assertFalse(stmt.isHasErrors());
161161
}
162162

163+
@Test
164+
public void testMultiDotNotation() {
165+
// Test with three parts: a.b.c where a.b is database and c is table
166+
String sql1 = "SELECT * FROM a.b.c WHERE id = ?";
167+
ParsedPreparedStatement stmt1 = parser.parsePreparedStatement(sql1);
168+
Assert.assertEquals(stmt1.getArgCount(), 1);
169+
Assert.assertFalse(stmt1.isHasErrors());
170+
Assert.assertEquals(stmt1.getTable(), "a.b.c");
171+
172+
// Test with quoted identifiers
173+
String sql2 = "SELECT * FROM `db.part1`.`table` WHERE id = ?";
174+
ParsedPreparedStatement stmt2 = parser.parsePreparedStatement(sql2);
175+
Assert.assertEquals(stmt2.getArgCount(), 1);
176+
Assert.assertFalse(stmt2.isHasErrors());
177+
Assert.assertEquals(stmt2.getTable(), "db.part1.table");
178+
179+
// Test INSERT with multi-dot notation
180+
String sql3 = "INSERT INTO a.b.c (col1, col2) VALUES (?, ?)";
181+
ParsedPreparedStatement stmt3 = parser.parsePreparedStatement(sql3);
182+
Assert.assertEquals(stmt3.getArgCount(), 2);
183+
Assert.assertFalse(stmt3.isHasErrors());
184+
Assert.assertTrue(stmt3.isInsert());
185+
Assert.assertEquals(stmt3.getTable(), "a.b.c");
186+
}
187+
163188
@Test(dataProvider = "testCreateStmtDP")
164189
public void testCreateStatement(String sql) {
165190
ParsedPreparedStatement stmt = parser.parsePreparedStatement(sql);

0 commit comments

Comments
 (0)