From cf2f095224e67fced1e5853f7228a560a8059d03 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 5 Aug 2026 15:30:56 -0700 Subject: [PATCH 1/5] Support MariaDB JDBC driver --- .../org/labkey/api/data/dialect/sqlKeywords.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/api/src/org/labkey/api/data/dialect/sqlKeywords.txt b/api/src/org/labkey/api/data/dialect/sqlKeywords.txt index 82cfb83bfc6..95d8a640eec 100644 --- a/api/src/org/labkey/api/data/dialect/sqlKeywords.txt +++ b/api/src/org/labkey/api/data/dialect/sqlKeywords.txt @@ -1,7 +1,7 @@ # A list of SQL keyword candidates -- all known and potential future keywords across all databases. We use this for testing. # Sources include the old, hard-coded keyword lists in the dialects, the reserved word lists in the PostgreSQL, MySQL, and # SQL Server documentation, and the return values of DatabaseMetaData.getSqlKeywords() for every database we support. -# Note: build is required after altering this file. +# Note: a build is required after altering this file. a abort abs @@ -241,6 +241,7 @@ degree delay_key_write delayed delete +delete_domain_id delimiter delimiters dense_rank @@ -281,6 +282,7 @@ dlurlscheme dlurlserver dlvalue do +do_domain_ids document domain double @@ -300,6 +302,7 @@ elseif empty enable enclosed +enclosedescaped encoding encrypted encryption @@ -323,6 +326,7 @@ event events every except +exceptexists exception exchange exclude @@ -380,6 +384,7 @@ freetext freetexttable freeze from +fromfulltext fs full fulltext @@ -432,6 +437,7 @@ identity_insert identitycol if ignore +ignore_domain_ids ignore_server_ids ilike immediate @@ -474,6 +480,7 @@ int8 integer integrity intersect +intersectinterval intersection interval into @@ -706,6 +713,7 @@ of off offline offset +offseton offsets oids old @@ -749,6 +757,7 @@ p pack_keys pad page +page_checksum parallel parameter parameter_mode @@ -759,6 +768,7 @@ parameter_specific_name parameter_specific_schema parameters parse_gcol_expr +parse_vcol_expr parser partial partition @@ -839,6 +849,7 @@ read_write reads readtext real +realrecursive reassign rebuild recheck @@ -851,6 +862,7 @@ redo_buffer_size redofile redundant ref +ref_system_id reference references referencing @@ -928,6 +940,7 @@ row row_count row_format row_number +row_numberrows rowcnt rowcount rowguidcol @@ -1232,6 +1245,7 @@ while whitespace width_bucket window +windowwith with within without From 881fee0264405a41bae2c497ab67e2110e069477 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 10 Aug 2026 15:34:28 -0700 Subject: [PATCH 2/5] Remove errant keywords (caused by bug in MariaDB JDBC driver getSQLKeywords()) --- api/src/org/labkey/api/data/dialect/sqlKeywords.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/api/src/org/labkey/api/data/dialect/sqlKeywords.txt b/api/src/org/labkey/api/data/dialect/sqlKeywords.txt index 95d8a640eec..59fc0a37dbb 100644 --- a/api/src/org/labkey/api/data/dialect/sqlKeywords.txt +++ b/api/src/org/labkey/api/data/dialect/sqlKeywords.txt @@ -302,7 +302,6 @@ elseif empty enable enclosed -enclosedescaped encoding encrypted encryption @@ -326,7 +325,6 @@ event events every except -exceptexists exception exchange exclude @@ -384,7 +382,6 @@ freetext freetexttable freeze from -fromfulltext fs full fulltext @@ -480,7 +477,6 @@ int8 integer integrity intersect -intersectinterval intersection interval into @@ -713,7 +709,6 @@ of off offline offset -offseton offsets oids old @@ -849,7 +844,6 @@ read_write reads readtext real -realrecursive reassign rebuild recheck @@ -940,7 +934,6 @@ row row_count row_format row_number -row_numberrows rowcnt rowcount rowguidcol @@ -1245,7 +1238,6 @@ while whitespace width_bucket window -windowwith with within without From 00db8c68ebf24e6f8f876b6a2583b87c501db884 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 11 Aug 2026 10:22:00 -0700 Subject: [PATCH 3/5] Test mysql.sakila if it's present --- .../api/data/TableSelectorTestCase.java | 123 +++++++++--------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/api/src/org/labkey/api/data/TableSelectorTestCase.java b/api/src/org/labkey/api/data/TableSelectorTestCase.java index 4d1969eea0b..075f9dcc500 100644 --- a/api/src/org/labkey/api/data/TableSelectorTestCase.java +++ b/api/src/org/labkey/api/data/TableSelectorTestCase.java @@ -38,84 +38,87 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Stream; -/** -* User: adam -* Date: 1/19/12 -* Time: 5:54 PM -*/ public class TableSelectorTestCase extends AbstractSelectorTestCase { @Test public void testTableSelector() throws SQLException { -// Calls below can be used to test that Oracle and MySQL dialects behave as expected, following our maxRows, offset, -// and other rules. Uncomment these lines and their corresponding bean classes below. + +// Call below can be used to test that Oracle dialect behaves as expected, following our maxRows, offset, and other +// rules. Uncomment this line and the corresponding bean class below. // testTableSelector(DbSchema.get("oracle.granite", DbSchemaType.Bare).getTable("account"), Account.class); -// testTableSelector(DbSchema.get("mySql.sakila", DbSchemaType.Bare).getTable("country"), Country.class); + + // Test MySQL database, if present + DbSchema sakila = DbSchema.get("mySql.sakila", DbSchemaType.Bare); + if (sakila.existsInDatabase()) + testTableSelector(sakila.getTable("country"), Country.class); testTableSelector(CoreSchema.getInstance().getTableInfoActiveUsers(), User.class); testTableSelector(CoreSchema.getInstance().getTableInfoModules(), ModuleContext.class); } -// public static class Country -// { -// private int _country_id; -// private String _country; -// private Date _last_update; -// -// public int getCountry_id() -// { -// return _country_id; -// } -// -// public void setCountry_id(int country_id) -// { -// _country_id = country_id; -// } -// -// public String getCountry() -// { -// return _country; -// } -// -// public void setCountry(String country) -// { -// _country = country; -// } -// -// public Date getLast_update() -// { -// return _last_update; -// } -// -// public void setLast_update(Date last_update) -// { -// _last_update = last_update; -// } -// -// @Override -// public boolean equals(Object o) -// { -// if (this == o) return true; -// if (o == null || getClass() != o.getClass()) return false; -// Country country = (Country) o; -// return _country_id == country._country_id && Objects.equals(_country, country._country) && Objects.equals(_last_update, country._last_update); -// } -// -// @Override -// public int hashCode() -// { -// return Objects.hash(_country_id, _country, _last_update); -// } -// } -// + @SuppressWarnings("unused") + public static class Country + { + private int _country_id; + private String _country; + private Date _last_update; + + public int getCountry_id() + { + return _country_id; + } + + public void setCountry_id(int country_id) + { + _country_id = country_id; + } + + public String getCountry() + { + return _country; + } + + public void setCountry(String country) + { + _country = country; + } + + public Date getLast_update() + { + return _last_update; + } + + public void setLast_update(Date last_update) + { + _last_update = last_update; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Country country = (Country) o; + return _country_id == country._country_id && Objects.equals(_country, country._country) && Objects.equals(_last_update, country._last_update); + } + + @Override + public int hashCode() + { + return Objects.hash(_country_id, _country, _last_update); + } + } + // public static class Account // { // private int _account_id; From a9c6ddce9a38af85746219e2e499462aabfd1206 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 11 Aug 2026 11:56:23 -0700 Subject: [PATCH 4/5] Mask password parameter values in the log --- api/src/org/labkey/api/data/DbScope.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/src/org/labkey/api/data/DbScope.java b/api/src/org/labkey/api/data/DbScope.java index 022e7ef2870..5c8e1f2be51 100644 --- a/api/src/org/labkey/api/data/DbScope.java +++ b/api/src/org/labkey/api/data/DbScope.java @@ -591,7 +591,7 @@ private static LabKeyDataSource setPrimaryDataSource(Map driverClass) { try From 91577dbf3b31d72790e623f5edd3cbd7c5c241b5 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 11 Aug 2026 11:58:21 -0700 Subject: [PATCH 5/5] Stricter JDBC URL parsing to support MariaDB no database + no slash option --- .../labkey/api/data/dialect/StandardJdbcHelper.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/api/src/org/labkey/api/data/dialect/StandardJdbcHelper.java b/api/src/org/labkey/api/data/dialect/StandardJdbcHelper.java index f2355b6bb7e..8f452070a9c 100644 --- a/api/src/org/labkey/api/data/dialect/StandardJdbcHelper.java +++ b/api/src/org/labkey/api/data/dialect/StandardJdbcHelper.java @@ -44,12 +44,11 @@ protected String parseDatabase(String url) if (-1 == dbEnd) dbEnd = url.length(); - // Database name starts after the last '/' or ':' - int slash = url.lastIndexOf('/', dbEnd); - int colon = url.lastIndexOf(':', dbEnd); + // Last '/' is the database delimiter, except for "jdbc:postgresql:database" + char dbDelimiter = url.contains("/") ? '/' : ':'; + int dbDelimiterIndex = url.lastIndexOf(dbDelimiter, dbEnd); - int dbDelimiter = Math.max(slash, colon); - - return url.substring(dbDelimiter + 1, dbEnd); + // dbDelimiterIndex == 1 means no database name, so return empty string in that case + return dbDelimiterIndex != 1 ? url.substring(dbDelimiterIndex + 1, dbEnd) : ""; } }