From 4a53a865a0400b67b4a642903b4ff1478b75bcd4 Mon Sep 17 00:00:00 2001 From: Magnus Persson <1431685+magnusp@users.noreply.github.com> Date: Sat, 31 Oct 2026 19:32:17 +0100 Subject: [PATCH] fix(jdbc): fold transaction modes and table types with Locale.ROOT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TransactionMode.from and MetadataSql.tables called toUpperCase without a locale. Under a Turkish default locale, "immediate" folded to "İMMEDİATE" and the VIEW table-type literal to "VİEW", so the mode lookup threw and the getTables filter matched nothing. Use Locale.ROOT at both sites, matching the policy of the rest of the driver. Pin both call sites with Turkish-default tests, and switch the foreign-key locale pin to "restrict" so it is mutation-sensitive. Closes #286 --- .../magnusp/libsql/jdbc/LibsqlConnection.java | 2 +- .../magnusp/libsql/jdbc/MetadataSql.java | 4 ++- .../libsql/jdbc/LocaleFoldingTest.java | 25 +++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlConnection.java b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlConnection.java index b919943..19ba96b 100644 --- a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlConnection.java +++ b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/LibsqlConnection.java @@ -96,7 +96,7 @@ String beginSql() { public static TransactionMode from(String mode) { try { - return valueOf(mode.toUpperCase()); + return valueOf(mode.toUpperCase(java.util.Locale.ROOT)); } catch (RuntimeException e) { throw new IllegalArgumentException("Unknown transaction mode: " + mode, e); } diff --git a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/MetadataSql.java b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/MetadataSql.java index fd1bd5e..cc3a1a0 100644 --- a/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/MetadataSql.java +++ b/libsql-jdbc/src/main/java/io/github/magnusp/libsql/jdbc/MetadataSql.java @@ -1,5 +1,7 @@ package io.github.magnusp.libsql.jdbc; +import java.util.Locale; + /** * Query builders for the JDBC metadata surfaces. The SQL text is the contract: tests assert * fragments byte for byte, and the test doubles dispatch on substrings such as {@code JOIN @@ -33,7 +35,7 @@ static String tables(String tableNamePattern, String[] types) { if (i > 0) { sql.append(","); } - sql.append("'").append(literal(types[i].toUpperCase())).append("'"); + sql.append("'").append(literal(types[i].toUpperCase(Locale.ROOT))).append("'"); } sql.append(")"); } diff --git a/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LocaleFoldingTest.java b/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LocaleFoldingTest.java index df89ee1..8a83cec 100644 --- a/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LocaleFoldingTest.java +++ b/libsql-jdbc/src/test/java/io/github/magnusp/libsql/jdbc/LocaleFoldingTest.java @@ -158,6 +158,27 @@ void columnMetadataFoldsUnderATurkishDefaultLocale() { .isEqualTo("NUMERIC"); } + @Test + void transactionModeFoldsUnderATurkishDefaultLocale() { + Locale.setDefault(TURKISH); + + assertThat(LibsqlConnection.TransactionMode.from("immediate")) + .as("TransactionMode.from must fold with Locale.ROOT, not the default locale") + .isEqualTo(LibsqlConnection.TransactionMode.IMMEDIATE); + assertThat(LibsqlConnection.TransactionMode.from("exclusive")) + .as("TransactionMode.from must fold with Locale.ROOT, not the default locale") + .isEqualTo(LibsqlConnection.TransactionMode.EXCLUSIVE); + } + + @Test + void tableTypeFilterFoldsUnderATurkishDefaultLocale() { + Locale.setDefault(TURKISH); + + assertThat(MetadataSql.tables("%", new String[] {"table", "view"})) + .as("MetadataSql must fold table types with Locale.ROOT, not the default locale") + .contains("AND TABLE_TYPE IN ('TABLE','VIEW')"); + } + @Test void declaredTypeFoldsUnderATurkishDefaultLocale() { Locale.setDefault(TURKISH); @@ -178,7 +199,7 @@ void foreignKeyRulesFoldUnderATurkishDefaultLocale() { Value.of("parent_id"), Value.of("id"), Value.of("cascade"), // update rule - Value.of("set null"), // delete rule + Value.of("restrict"), // delete rule Value.of("CREATE TABLE child (parent_id INTEGER REFERENCES parent(id))"), Value.of("CREATE TABLE parent (id INTEGER PRIMARY KEY)"), Value.of("id")))), @@ -195,7 +216,7 @@ void foreignKeyRulesFoldUnderATurkishDefaultLocale() { .isEqualTo(DatabaseMetaData.importedKeyCascade); assertThat(integer(imported, FOREIGN_KEY_COLUMNS, "DELETE_RULE")) .as("ForeignKeyMetadata must fold the rule with Locale.ROOT, not the default locale") - .isEqualTo(DatabaseMetaData.importedKeySetNull); + .isEqualTo(DatabaseMetaData.importedKeyRestrict); } private static String typeName(StmtResult result) {