From 67a7d5a042b64777feef59fba1218fe3fead9136 Mon Sep 17 00:00:00 2001 From: sb Date: Fri, 5 Jun 2026 08:09:02 -0400 Subject: [PATCH 1/3] Fixed database metadata case sensitivity quirks --- .../rdbms/schema/RDBMSSchemaHandler.java | 82 ++++++++++++------- .../store/rdbms/table/ProbeTable.java | 7 +- .../store/rdbms/table/TableImpl.java | 9 +- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/datanucleus/store/rdbms/schema/RDBMSSchemaHandler.java b/src/main/java/org/datanucleus/store/rdbms/schema/RDBMSSchemaHandler.java index 18c00843..7ec1c0bf 100644 --- a/src/main/java/org/datanucleus/store/rdbms/schema/RDBMSSchemaHandler.java +++ b/src/main/java/org/datanucleus/store/rdbms/schema/RDBMSSchemaHandler.java @@ -42,6 +42,7 @@ import org.datanucleus.store.rdbms.table.Table; import org.datanucleus.store.schema.AbstractStoreSchemaHandler; import org.datanucleus.store.schema.StoreSchemaData; +import org.datanucleus.store.schema.naming.NamingCase; import org.datanucleus.transaction.TransactionIsolation; import org.datanucleus.util.Localiser; import org.datanucleus.util.NucleusLogger; @@ -418,9 +419,9 @@ public String getTableType(Connection conn, Table table) } String tableName = (c[2] != null) ? c[2] : table.getIdentifier().getName(); - catalogName = getIdentifierForUseWithDatabaseMetaData(catalogName); - schemaName = getIdentifierForUseWithDatabaseMetaData(schemaName); - tableName = getIdentifierForUseWithDatabaseMetaData(tableName); + catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalogName); + schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schemaName); + tableName = getIdentifierForUseWithDatabaseMetaData(conn, tableName); try { @@ -651,9 +652,9 @@ protected RDBMSTableFKInfo getRDBMSTableFKInfoForTable(Connection conn, Table ta } String tableName = (c[2] != null) ? c[2] : table.getIdentifier().getName(); - catalogName = getIdentifierForUseWithDatabaseMetaData(catalogName); - schemaName = getIdentifierForUseWithDatabaseMetaData(schemaName); - tableName = getIdentifierForUseWithDatabaseMetaData(tableName); + catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalogName); + schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schemaName); + tableName = getIdentifierForUseWithDatabaseMetaData(conn, tableName); return getRDBMSTableFKInfoForTable(conn, catalogName, schemaName, tableName); } @@ -722,9 +723,9 @@ protected RDBMSTablePKInfo getRDBMSTablePKInfoForTable(Connection conn, Table ta } String tableName = (c[2] != null) ? c[2] : table.getIdentifier().getName(); - catalogName = getIdentifierForUseWithDatabaseMetaData(catalogName); - schemaName = getIdentifierForUseWithDatabaseMetaData(schemaName); - tableName = getIdentifierForUseWithDatabaseMetaData(tableName); + catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalogName); + schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schemaName); + tableName = getIdentifierForUseWithDatabaseMetaData(conn, tableName); return getRDBMSTablePKInfoForTable(conn, catalogName, schemaName, tableName); } @@ -793,9 +794,9 @@ protected RDBMSTableIndexInfo getRDBMSTableIndexInfoForTable(Connection conn, Ta } String tableName = (c[2] != null) ? c[2] : table.getIdentifier().getName(); - catalogName = getIdentifierForUseWithDatabaseMetaData(catalogName); - schemaName = getIdentifierForUseWithDatabaseMetaData(schemaName); - tableName = getIdentifierForUseWithDatabaseMetaData(tableName); + catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalogName); + schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schemaName); + tableName = getIdentifierForUseWithDatabaseMetaData(conn, tableName); return getRDBMSTableIndexInfoForTable(conn, catalogName, schemaName, tableName); } @@ -822,7 +823,7 @@ protected RDBMSTableIndexInfo getRDBMSTableIndexInfoForTable(Connection conn, St { // This is a hack for the DatabaseAdapter method that requires a schema for Oracle schemaNameTmp = rdbmsStoreMgr.getDefaultSchemaName(); - schemaNameTmp = getIdentifierForUseWithDatabaseMetaData(schemaNameTmp); + schemaNameTmp = getIdentifierForUseWithDatabaseMetaData(conn, schemaNameTmp); } ResultSet rs = dba.getExistingIndexes(conn, catalogName, schemaNameTmp, tableName); if (rs == null) @@ -882,8 +883,8 @@ protected RDBMSSchemaInfo getRDBMSSchemaInfoForCatalogSchema(Connection conn, St ResultSet rs = null; try { - String catalogName = getIdentifierForUseWithDatabaseMetaData(catalog); - String schemaName = getIdentifierForUseWithDatabaseMetaData(schema); + String catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalog); + String schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schema); rs = getDatastoreAdapter().getColumns(conn, catalogName, schemaName, null, null); while (rs.next()) { @@ -972,9 +973,9 @@ protected RDBMSTableInfo getRDBMSTableInfoForTable(Connection conn, Table table) } String tableName = (c[2] != null) ? c[2] : table.getIdentifier().getName(); - catalogName = getIdentifierForUseWithDatabaseMetaData(catalogName); - schemaName = getIdentifierForUseWithDatabaseMetaData(schemaName); - tableName = getIdentifierForUseWithDatabaseMetaData(tableName); + catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalogName); + schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schemaName); + tableName = getIdentifierForUseWithDatabaseMetaData(conn, tableName); return getRDBMSTableInfoForTable(conn, catalogName, schemaName, tableName); } @@ -1109,12 +1110,12 @@ private void refreshTableData(Object connection, String catalog, String schema, try { Connection conn = (Connection)connection; - String catalogName = getIdentifierForUseWithDatabaseMetaData(catalog); - String schemaName = getIdentifierForUseWithDatabaseMetaData(schema); + String catalogName = getIdentifierForUseWithDatabaseMetaData(conn, catalog); + String schemaName = getIdentifierForUseWithDatabaseMetaData(conn, schema); if (tableNames.size() == 1) { // Single table to retrieve so restrict the query - String tableName = getIdentifierForUseWithDatabaseMetaData((String)tableNames.iterator().next()); + String tableName = getIdentifierForUseWithDatabaseMetaData(conn, (String)tableNames.iterator().next()); if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(Localiser.msg("050028", tableName, catalogName, schemaName)); @@ -1299,23 +1300,44 @@ private static String[] splitTableIdentifierName(String separator, String name) /** * Convenience method to convert the passed identifier into the correct case for use with this * datastore adapter, and removing any quote characters. + * @param conn database connection * @param identifier The raw identifier * @return The identifier for use */ - private String getIdentifierForUseWithDatabaseMetaData(String identifier) + public String getIdentifierForUseWithDatabaseMetaData(Connection conn, String identifier) { if (identifier == null) { return null; } - return identifier.replace(getDatastoreAdapter().getIdentifierQuoteString(), ""); - // TODO Really ought to do the case conversion so that we check in the case of the adapter - // This is needed where the user has provided an identifier but in the wrong case - // When you enable this the JDO2 TCK will likely go incredibly slow since Derby use of - // DatabaseMetaData.getColumns() see "http://issues.apache.org/jira/browse/DERBY-1996" -/* return JDBCUtils.getIdentifierNameStripped( - storeMgr.getIdentifierFactory().getIdentifierInAdapterCase(identifier), - storeMgr.getDatastoreAdapter());*/ + + identifier = rdbmsStoreMgr.getIdentifierFactory().getIdentifierInAdapterCase(identifier); + + if (identifier.startsWith(getDatastoreAdapter().getIdentifierQuoteString())) + { + + return identifier.replace(getDatastoreAdapter().getIdentifierQuoteString(), ""); + } + try + { + DatabaseMetaData dmd = conn.getMetaData(); + if (dmd.storesLowerCaseIdentifiers()) + { + return identifier.toLowerCase(); + } + else if (dmd.storesUpperCaseIdentifiers()) + { + return identifier.toUpperCase(); + } + else + { + return identifier; + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } } /** diff --git a/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java b/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java index d60ddf87..146d0638 100644 --- a/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java +++ b/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java @@ -93,14 +93,13 @@ public String[] findSchemaDetails(Connection conn) // Make sure the table name is in the correct case. // This is required by RDBMS such as PostgreSQL which allow creation in one format yet actually store it in another. + // This is for probe table so identifier will not be surrounded by quotes String table_name = identifier.getName(); - if (storeMgr.getIdentifierFactory().getNamingCase() == NamingCase.LOWER_CASE || - storeMgr.getIdentifierFactory().getNamingCase() == NamingCase.LOWER_CASE_QUOTED) + if (dmd.storesLowerCaseIdentifiers()) { table_name = table_name.toLowerCase(); } - else if (storeMgr.getIdentifierFactory().getNamingCase() == NamingCase.UPPER_CASE || - storeMgr.getIdentifierFactory().getNamingCase() == NamingCase.UPPER_CASE_QUOTED) + else if (dmd.storesUpperCaseIdentifiers()) { table_name = table_name.toUpperCase(); } diff --git a/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java b/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java index 69401f2a..77bf051c 100644 --- a/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java +++ b/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java @@ -193,6 +193,13 @@ public boolean validateColumns(Connection conn, boolean validateColumnStructure, throws SQLException { Map unvalidated = new HashMap<>(columnsByIdentifier); + Map schemaToLogical = new HashMap<>(); + unvalidated.keySet().forEach(key->{ + RDBMSSchemaHandler schemaHandler = (RDBMSSchemaHandler) storeMgr.getSchemaHandler(); + String schemaIdentifierName = schemaHandler.getIdentifierForUseWithDatabaseMetaData(conn, key.getName()); + schemaToLogical.put(schemaIdentifierName, key); + }); + List tableColInfo = storeMgr.getColumnInfoForTable(this, conn); Iterator i = tableColInfo.iterator(); while (i.hasNext()) @@ -202,7 +209,7 @@ public boolean validateColumns(Connection conn, boolean validateColumnStructure, // Create an identifier to use for the real column - use "CUSTOM" because we don't want truncation DatastoreIdentifier colIdentifier = storeMgr.getIdentifierFactory().newColumnIdentifier(ci.getColumnName(), this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(String.class), null, true); - Column col = unvalidated.get(colIdentifier); + Column col = unvalidated.get(schemaToLogical.get(ci.getColumnName())); if (col != null) { if (validateColumnStructure) From b5b5e966932840c7b240a052aa15e8afa011cee6 Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 6 Jun 2026 05:35:24 -0400 Subject: [PATCH 2/3] Fix --- .../java/org/datanucleus/store/rdbms/table/TableImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java b/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java index 77bf051c..6cf65392 100644 --- a/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java +++ b/src/main/java/org/datanucleus/store/rdbms/table/TableImpl.java @@ -216,11 +216,11 @@ public boolean validateColumns(Connection conn, boolean validateColumnStructure, { col.initializeColumnInfoFromDatastore(ci); col.validate(ci); - unvalidated.remove(colIdentifier); + unvalidated.remove(schemaToLogical.get(ci.getColumnName())); } else { - unvalidated.remove(colIdentifier); + unvalidated.remove(schemaToLogical.get(ci.getColumnName())); } } } From 6c93bca5811d50da9a8af971da8d256b34743b4b Mon Sep 17 00:00:00 2001 From: sb Date: Fri, 19 Jun 2026 11:34:28 -0400 Subject: [PATCH 3/3] Corrected assumption that probe table name will not be quoted --- .../datanucleus/store/rdbms/table/ProbeTable.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java b/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java index 146d0638..947f7c1c 100644 --- a/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java +++ b/src/main/java/org/datanucleus/store/rdbms/table/ProbeTable.java @@ -33,6 +33,7 @@ import org.datanucleus.metadata.AbstractMemberMetaData; import org.datanucleus.store.rdbms.adapter.DatastoreAdapter; import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping; +import org.datanucleus.store.rdbms.schema.RDBMSSchemaHandler; import org.datanucleus.store.rdbms.RDBMSStoreManager; import org.datanucleus.store.schema.naming.NamingCase; import org.datanucleus.util.Localiser; @@ -93,16 +94,7 @@ public String[] findSchemaDetails(Connection conn) // Make sure the table name is in the correct case. // This is required by RDBMS such as PostgreSQL which allow creation in one format yet actually store it in another. - // This is for probe table so identifier will not be surrounded by quotes - String table_name = identifier.getName(); - if (dmd.storesLowerCaseIdentifiers()) - { - table_name = table_name.toLowerCase(); - } - else if (dmd.storesUpperCaseIdentifiers()) - { - table_name = table_name.toUpperCase(); - } + String table_name = ((RDBMSSchemaHandler) storeMgr.getSchemaHandler()).getIdentifierForUseWithDatabaseMetaData(conn, identifier.getName()); // Utilise default catalog/schema if available and applicable String catalog_name = storeMgr.getStringProperty(PropertyNames.PROPERTY_MAPPING_CATALOG); @@ -122,7 +114,7 @@ else if (dmd.storesUpperCaseIdentifiers()) { if (!rs.next()) { - throw new NucleusDataStoreException(Localiser.msg("057027",identifier)); + throw new NucleusDataStoreException(Localiser.msg("057027", table_name)); } schemaDetails[0] = rs.getString(1);