You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/07/27 02:16:44 UTC

(grails-core) 01/01: Strip backticks from resolved associated-entity table names

This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch naming-strategy-table-name-mapping-fix
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 546fea44a98c157e81c7739443514038f273c5ea
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sun Jul 26 19:40:05 2026 -0500

    Strip backticks from resolved associated-entity table names
    
    resolveAssociatedEntityTableName's result is only ever used as a
    column-identifier fragment (a join-table foreign-key or element
    column name) by its callers, never as a literal SQL identifier - so
    a backtick-quoted table mapping (e.g. table '`user`') produced a
    malformed column like `user`_id via resolveJoinTableForeignKeyColumnName,
    generating invalid DDL that fails silently without hbm2ddl.halt_on_error.
    
    Strips backticks once at the source instead of trusting each caller to
    do it themselves, since every caller wants the clean form. Adds a
    domain-class-only regression test (no mocks) reproducing the bug via a
    real unidirectional hasMany join-table binding.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
 .../hibernate/HibernateAssociation.java            |  8 ++++++-
 .../hibernate/HibernateToManyPropertySpec.groovy   | 27 ++++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociation.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociation.java
index 36d9da1084..b7dca09707 100644
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociation.java
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociation.java
@@ -29,6 +29,7 @@ import org.grails.datastore.mapping.model.PersistentProperty;
 import org.grails.orm.hibernate.cfg.Mapping;
 import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
 import org.grails.orm.hibernate.cfg.PropertyConfig;
+import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
 
 /**
  * Common interface for all Hibernate association properties (both ToOne and ToMany). Extends {@link
@@ -82,7 +83,12 @@ public interface HibernateAssociation extends HibernatePersistentProperty {
     }
 
     default String resolveAssociatedEntityTableName(PersistentEntityNamingStrategy namingStrategy) {
-        return getHibernateAssociatedEntity().getHibernateRootEntity().getTableName(namingStrategy);
+        // Every caller of this method uses the result as a column-identifier fragment (a join-table foreign-key
+        // or element column name), never as a literal, quotable SQL identifier - so the Groovy backtick-quoting
+        // convention is always invalid there and must be stripped once at the source, rather than trusted to
+        // each caller (a prior bug left one caller emitting a malformed column like `quoted_table`_id).
+        return new BackticksRemover().apply(
+                getHibernateAssociatedEntity().getHibernateRootEntity().getTableName(namingStrategy));
     }
 
     @Override
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateToManyPropertySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateToManyPropertySpec.groovy
index f0a3cfb3f6..86ef760c7b 100644
--- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateToManyPropertySpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateToManyPropertySpec.groovy
@@ -89,6 +89,16 @@ class HibernateToManyPropertySpec extends HibernateGormDatastoreSpec {
         property.resolveJoinTableForeignKeyColumnName(namingStrategy) == "htmp_book_id"
     }
 
+    void "resolveJoinTableForeignKeyColumnName strips backticks from a backtick-quoted associated entity table name"() {
+        given:
+        def property = createTestHibernateToManyProperty(HTMPQuotedTableAuthor, "books")
+        def namingStrategy = getGrailsDomainBinder().namingStrategy
+        hibernateFirstPass()
+
+        expect:
+        property.resolveJoinTableForeignKeyColumnName(namingStrategy) == "htmp_quoted_book_id"
+    }
+
     void "isAssociationColumnNullable returns false for ManyToMany"() {
         given: "Register only entities for this specific test"
         createPersistentEntity(HTMPCourse) // Course is needed because Student refers to it
@@ -640,6 +650,23 @@ class HTMPMappedTableAuthor {
     static hasMany = [books: Book]
 }
 
+@Entity
+class HTMPQuotedTableBook {
+    Long id
+    String title
+
+    static mapping = {
+        table '`htmp_quoted_book`'
+    }
+}
+
+@Entity
+class HTMPQuotedTableAuthor {
+    Long id
+    String name
+    static hasMany = [books: HTMPQuotedTableBook]
+}
+
 class HTMPPrefixRemovingPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
 
     @Override