diff --git a/agents/common/src/main/java/com/dbx/agent/AbstractJdbcAgent.java b/agents/common/src/main/java/com/dbx/agent/AbstractJdbcAgent.java index 8006768c7..72141f56b 100644 --- a/agents/common/src/main/java/com/dbx/agent/AbstractJdbcAgent.java +++ b/agents/common/src/main/java/com/dbx/agent/AbstractJdbcAgent.java @@ -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 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 ""; } diff --git a/agents/common/src/test/java/com/dbx/agent/AbstractJdbcAgentTest.java b/agents/common/src/test/java/com/dbx/agent/AbstractJdbcAgentTest.java index 09bd975cf..9b0e6abca 100644 --- a/agents/common/src/test/java/com/dbx/agent/AbstractJdbcAgentTest.java +++ b/agents/common/src/test/java/com/dbx/agent/AbstractJdbcAgentTest.java @@ -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()); } });