fix(agents): sanitize backtick identifier quote for GBase 8s

This commit is contained in:
弈秋
2026-09-15 16:31:53 +08:00
committed by GitHub
parent aeafddfae7
commit dd99786c01
2 changed files with 104 additions and 1 deletions
@@ -454,7 +454,57 @@ public abstract class AbstractJdbcAgent extends BaseDatabaseAgent {
}
try {
String quote = connection.getMetaData().getIdentifierQuoteString();
return quote == null || quote.trim().isEmpty() ? "" : quote.trim();
if (quote == null || quote.trim().isEmpty()) {
return "";
}
return sanitizeInformixFamilyBacktickQuote(params, connection, quote.trim());
} catch (Exception ignored) {
return "";
}
}
// GBase 8s and other Informix-family JDBC drivers report "`" from
// getIdentifierQuoteString() even though their servers reject backtick-quoted
// identifiers with "illegal character in statement" outside MySQL compatibility
// mode (SQLMODE=mysql). An empty quote makes clients emit unquoted identifiers,
// which both SQL modes accept.
private static String sanitizeInformixFamilyBacktickQuote(ConnectParams params, Connection connection, String quote) {
if (!"`".equals(quote) || !isInformixFamilyJdbc(params, connection) || isInformixFamilyMysqlCompat(params, connection)) {
return quote;
}
return "";
}
private static boolean isInformixFamilyJdbc(ConnectParams params, Connection connection) {
StringBuilder identity = new StringBuilder();
appendJdbcIdentity(identity, params.getConnection_string());
appendJdbcIdentity(identity, params.getJdbc_driver_class());
List<String> driverPaths = params.getJdbc_driver_paths();
if (driverPaths != null) {
for (String path : driverPaths) {
appendJdbcIdentity(identity, path);
}
}
appendJdbcIdentity(identity, connectionUrl(connection));
String normalized = identity.toString().toLowerCase(Locale.ROOT);
return normalized.contains("jdbc:gbasedbt")
|| normalized.contains("jdbc:informix")
|| normalized.contains("com.gbasedbt")
|| normalized.contains("com.informix");
}
private static boolean isInformixFamilyMysqlCompat(ConnectParams params, Connection connection) {
if (params.isMysql_compat_mode()) {
return true;
}
String identity = (params.getConnection_string() + "\n" + connectionUrl(connection)).toLowerCase(Locale.ROOT);
return identity.contains("sqlmode=mysql");
}
private static String connectionUrl(Connection connection) {
try {
String url = connection.getMetaData().getURL();
return url == null ? "" : url;
} catch (Exception ignored) {
return "";
}
@@ -115,6 +115,55 @@ class AbstractJdbcAgentTest {
assertEquals(0, tracking.compatibilityQueryCount);
}
@Test
void ignoresGbase8sDriverBacktickIdentifierQuote() {
TrackingConnection tracking = new TrackingConnection();
tracking.identifierQuote = "`";
tracking.jdbcUrl = "jdbc:gbasedbt-sqli://localhost:9088/appdb:GBASEDBTSERVER=gbase8s";
TestAgent agent = new TestAgent(tracking);
agent.connect(new ConnectParams());
assertEquals("", agent.getIdentifierQuote());
}
@Test
void ignoresBacktickQuoteForInformixFamilyConnectionString() {
TrackingConnection tracking = new TrackingConnection();
tracking.identifierQuote = "`";
TestAgent agent = new TestAgent(tracking);
ConnectParams params = new ConnectParams();
params.setConnection_string("jdbc:informix-sqli://localhost:9088/appdb:INFORMIXSERVER=ol_informix");
agent.connect(params);
assertEquals("", agent.getIdentifierQuote());
}
@Test
void keepsInformixFamilyBacktickQuoteInMysqlCompatMode() {
TrackingConnection tracking = new TrackingConnection();
tracking.identifierQuote = "`";
tracking.jdbcUrl = "jdbc:gbasedbt-sqli://localhost:9088/appdb:GBASEDBTSERVER=gbase8s;SQLMODE=mysql";
TestAgent agent = new TestAgent(tracking);
agent.connect(new ConnectParams());
assertEquals("`", agent.getIdentifierQuote());
}
@Test
void keepsNonBacktickQuotesForInformixFamilyDrivers() {
TrackingConnection tracking = new TrackingConnection();
tracking.identifierQuote = "\"";
tracking.jdbcUrl = "jdbc:gbasedbt-sqli://localhost:9088/appdb:GBASEDBTSERVER=gbase8s";
TestAgent agent = new TestAgent(tracking);
agent.connect(new ConnectParams());
assertEquals("\"", agent.getIdentifierQuote());
}
@Test
void testsConnectionsThroughSharedLifecycle() {
TrackingConnection tracking = new TrackingConnection();
@@ -469,6 +518,7 @@ class AbstractJdbcAgentTest {
private boolean autoCommit = true;
private String compatibilityMode;
private String identifierQuote = "\"";
private String jdbcUrl;
private boolean compatibilityQueryFails;
private int compatibilityQueryCount;
private boolean isValidUnsupported;
@@ -534,6 +584,9 @@ class AbstractJdbcAgentTest {
if ("getIdentifierQuoteString".equals(method.getName())) {
return identifierQuote;
}
if ("getURL".equals(method.getName())) {
return jdbcUrl;
}
return defaultValue(method.getReturnType());
}
});