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

borinquenkid pushed a commit to branch merge-hibernate6
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 268d99263fbb32c78f3a8342aa189687abcbbde2
Author: Walter Duque de Estrada <wbdu...@mac.com>
AuthorDate: Sat Aug 23 09:16:42 2025 -0500

    sql queries fixed
---
 .../orm/hibernate/query/HibernateHqlQuery.java     | 159 ++++++++++++++++++++-
 1 file changed, 152 insertions(+), 7 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateHqlQuery.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateHqlQuery.java
index d0a4e48c68..e0d9900aa6 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateHqlQuery.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/query/HibernateHqlQuery.java
@@ -70,8 +70,11 @@ public class HibernateHqlQuery extends Query {
             , String sqlString
             , boolean isNative
         ) {
-        var clazz = getTarget(sqlString,persistentEntity.getJavaClass());
-        var q = isNative ? session.createNativeQuery(sqlString,clazz) : 
session.createQuery(sqlString, clazz);
+
+        // Normalize only for HQL (not for native SQL)
+        String hqlToUse = isNative ? sqlString : 
normalizeNonAliasedSelect(sqlString);
+        var clazz = getTarget(hqlToUse, persistentEntity.getJavaClass());
+        var q = isNative ? session.createNativeQuery(sqlString, clazz) : 
session.createQuery(hqlToUse, clazz);
         var hibernateSession = new HibernateSession( dataStore, 
sessionFactory);
         HibernateHqlQuery hibernateHqlQuery = new 
HibernateHqlQuery(hibernateSession, null, q);
         hibernateHqlQuery.setFlushMode(session.getHibernateFlushMode());
@@ -165,22 +168,164 @@ public class HibernateHqlQuery extends Query {
 
 
     static Class getTarget(CharSequence hql, Class clazz) {
-        int projections = countHqlProjections(hql);
+        // Normalize non-aliased queries to an aliased form, then reuse the 
logic
+        String normalized = normalizeNonAliasedSelect(hql == null ? null : 
hql.toString());
+
+        int projections = countHqlProjections(normalized);
         switch(projections) {
             case 0:
                 return clazz; // No explicit SELECT - implicit entity 
projection
             case 1:
-                // Single projection - check if it's a property access 
(contains dot)
-                if (isPropertyProjection(hql)) {
-                    return Object.class; // For scalar results like "select 
h.name"
+                // Single projection - property vs entity
+                if (isPropertyProjection(normalized)) {
+                    return Object.class; // Scalar result
                 } else {
-                    return clazz; // For entity projections like "select h"
+                    return clazz;        // Entity result
                 }
             default:
                 return Object[].class; // Multiple projections
         }
     }
 
+    /**
+     * If the HQL query has no alias in the FROM clause, inject a synthetic 
alias ("e")
+     * and qualify the SELECT projection accordingly. Only SELECT is adjusted; 
the rest
+     * of the query is left intact (WHERE/JOIN conditions are valid as-is in 
HQL).
+     *
+     * Examples:
+     *   "select nameType from NameType where lower(nameType)=:nameType"
+     *      -> "select e.nameType from NameType e where 
lower(nameType)=:nameType"
+     *   "select NameType from NameType"
+     *      -> "select e from NameType e"
+     *   "select name from Person"
+     *      -> "select e.name from Person e"
+     */
+    private static String normalizeNonAliasedSelect(String hql) {
+        if (hql == null) return null;
+        String s = hql.trim();
+        if (s.isEmpty()) return s;
+
+        String lower = s.toLowerCase();
+        int selectIdx = lower.indexOf("select ");
+        if (selectIdx < 0) {
+            // No explicit select -> nothing to normalize for target detection
+            return s;
+        }
+        int fromIdx = lower.indexOf(" from ", selectIdx);
+        if (fromIdx < 0) {
+            // Malformed or incomplete; leave as-is
+            return s;
+        }
+
+        // Extract SELECT clause original text
+        int selectStart = selectIdx + "select ".length();
+        String selectClauseOrig = s.substring(selectStart, fromIdx).trim();
+        String selectClauseLower = lower.substring(selectStart, 
fromIdx).trim();
+
+        // Extract FROM head to detect alias: "from Entity [as] alias ..."
+        int afterFrom = fromIdx + " from ".length();
+        int endOfFromHead = afterFrom;
+        // read entity name token
+        while (endOfFromHead < s.length()) {
+            char ch = s.charAt(endOfFromHead);
+            if (Character.isWhitespace(ch)) break;
+            endOfFromHead++;
+        }
+        String entityName = s.substring(afterFrom, endOfFromHead).trim();
+        if (entityName.isEmpty()) return s;
+
+        // Skip spaces
+        int cur = endOfFromHead;
+        while (cur < s.length() && Character.isWhitespace(s.charAt(cur))) 
cur++;
+
+        // Optional "as"
+        boolean hasAlias = false;
+        String alias = null;
+        int aliasStart = cur;
+        if (cur + 2 < s.length()) {
+            String nextWord = s.substring(cur, Math.min(cur + 2, 
s.length())).toLowerCase();
+        }
+        if (cur + 2 < s.length() && s.substring(cur, Math.min(cur + 2, 
s.length())).equalsIgnoreCase("as")) {
+            cur += 2;
+            while (cur < s.length() && Character.isWhitespace(s.charAt(cur))) 
cur++;
+        }
+
+        // Try to read alias token unless we hit a clause keyword
+        int aliasEnd = cur;
+        while (aliasEnd < s.length()) {
+            char ch = s.charAt(aliasEnd);
+            if (Character.isWhitespace(ch)) break;
+            aliasEnd++;
+        }
+        String maybeAlias = (cur < aliasEnd) ? s.substring(cur, aliasEnd) : "";
+
+        // Keywords that indicate no alias present
+        String maybeAliasLower = maybeAlias.toLowerCase();
+        boolean isClauseKeyword = maybeAliasLower.isEmpty()
+                || maybeAliasLower.equals("where")
+                || maybeAliasLower.equals("join")
+                || maybeAliasLower.equals("left")
+                || maybeAliasLower.equals("right")
+                || maybeAliasLower.equals("inner")
+                || maybeAliasLower.equals("outer")
+                || maybeAliasLower.equals("group")
+                || maybeAliasLower.equals("order")
+                || maybeAliasLower.equals("having");
+
+        if (!isClauseKeyword) {
+            hasAlias = true;
+            alias = maybeAlias;
+        }
+
+        if (hasAlias) {
+            // Already aliased; no normalization needed
+            return s;
+        }
+
+        // Inject synthetic alias
+        String syntheticAlias = "e";
+
+        // Adjust SELECT clause:
+        // Preserve DISTINCT/ALL prefix
+        String prefix = "";
+        String projOrig = selectClauseOrig;
+        String projLower = selectClauseLower;
+        if (projLower.startsWith("distinct ")) {
+            prefix = selectClauseOrig.substring(0, selectClauseOrig.length() - 
projOrig.substring("distinct ".length()).length());
+            projOrig = selectClauseOrig.substring("distinct ".length()).trim();
+            projLower = projLower.substring("distinct ".length()).trim();
+            prefix = "distinct ";
+        } else if (projLower.startsWith("all ")) {
+            prefix = "all ";
+            projOrig = selectClauseOrig.substring("all ".length()).trim();
+            projLower = projLower.substring("all ".length()).trim();
+        }
+
+        String adjustedProjection = projOrig;
+        // If projection equals entity name -> "select e"
+        if (projLower.equals(entityName.toLowerCase())) {
+            adjustedProjection = syntheticAlias;
+        }
+        // If projection has no dot, treat as property -> qualify with alias
+        else if (!projLower.contains("(") && !projLower.contains(".") && 
!projLower.startsWith("new ")) {
+            adjustedProjection = syntheticAlias + "." + projOrig;
+        }
+        // else leave as-is (functions, constructor expr, already-qualified, 
etc.)
+
+        // Build normalized SELECT ... FROM ... (inject alias right after 
entity name)
+        StringBuilder out = new StringBuilder();
+        out.append("select ").append(prefix).append(adjustedProjection);
+        // Original FROM and the rest
+        String tail = s.substring(fromIdx); // starts with " from "
+        // Insert alias after entity name in tail
+        StringBuilder tailOut = new StringBuilder();
+        tailOut.append(" from ").append(entityName).append(" 
").append(syntheticAlias);
+        // Append the remainder after entity name in the FROM head
+        tailOut.append(s.substring(endOfFromHead));
+        out.append(tailOut);
+        return out.toString();
+    }
+
     private static boolean isPropertyProjection(CharSequence hql) {
         String s = hql.toString().toLowerCase().trim();
         int selectIdx = s.indexOf("select ");

Reply via email to