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

dixitdeepak pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 3eaef2fdbf3cbd6c1c7ef61e81a4789df1597fc9
Author: Deepak Dixit <[email protected]>
AuthorDate: Tue Jun 30 20:17:08 2026 +0530

    Fixed: ComplexAliasField default value not quoted for non-numeric fields in 
SQL COALESCE (OFBIZ-5199)
    The defaultValue in ComplexAliasField.makeAliasColName() was inserted raw
    into COALESCE SQL, causing syntax errors for date-time and string field 
types.
    Numeric literals (0) worked fine but timestamp/string values were unquoted.
---
 .../apache/ofbiz/entity/model/ModelViewEntity.java | 13 ++-
 .../model/ModelViewEntityComplexAliasTests.java    | 93 ++++++++++++++++++++++
 2 files changed, 105 insertions(+), 1 deletion(-)

diff --git 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
index 5543310d85..20b69aa3f7 100644
--- 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
+++ 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
@@ -59,6 +59,9 @@ public class ModelViewEntity extends ModelEntity {
 
     private static final Map<String, String> FUNCTION_PREFIX_MAP = new 
HashMap<>();
     private static final Set<String> NUMERIC_FUNCTION_SET = new HashSet<>(); 
// names of functions that return a numeric type
+    // OFBiz field types that map to numeric Java types — default values for 
these do not need SQL quoting
+    private static final Set<String> NUMERIC_FIELD_TYPES = new 
HashSet<>(Arrays.asList(
+            "numeric", "integer", "floating-point", "fixed-point", 
"currency-amount", "currency-precise"));
     static {
         FUNCTION_PREFIX_MAP.put("min", "MIN(");
         FUNCTION_PREFIX_MAP.put("max", "MAX(");
@@ -1251,7 +1254,15 @@ public class ModelViewEntity extends ModelEntity {
                 String colName = entityAlias + "." + 
SqlJdbcUtil.filterColName(modelField.getColName());
 
                 if (UtilValidate.isNotEmpty(defaultValue)) {
-                    colName = "COALESCE(" + colName + "," + defaultValue + ")";
+                    String sqlDefault = defaultValue;
+                    // Quote plain string values for non-numeric fields.
+                    // Skip quoting when the value is already a SQL literal 
(starts with ')
+                    // or a SQL expression/function call (contains '('), e.g. 
TO_TIMESTAMP(...).
+                    boolean alreadySqlExpression = 
defaultValue.startsWith("'") || defaultValue.contains("(");
+                    if (!NUMERIC_FIELD_TYPES.contains(modelField.getType()) && 
!alreadySqlExpression) {
+                        sqlDefault = "'" + defaultValue + "'";
+                    }
+                    colName = "COALESCE(" + colName + "," + sqlDefault + ")";
                 }
 
                 if (UtilValidate.isNotEmpty(function)) {
diff --git 
a/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
 
b/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
index 20e71c9d95..7b80b85b2f 100644
--- 
a/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
+++ 
b/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
@@ -25,6 +25,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -32,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 /**
  * Tests for the Tier-2 complex-alias conversion binding:
@@ -44,6 +46,12 @@ public final class ModelViewEntityComplexAliasTests {
     @Mock
     private ModelViewEntity mockViewEntity;
 
+    @Mock
+    private ModelReader mockModelReader;
+
+    @Mock
+    private ModelEntity mockModelEntity;
+
     private AutoCloseable mocks;
 
     @BeforeEach
@@ -167,4 +175,89 @@ public final class ModelViewEntityComplexAliasTests {
 
         verify(mockViewEntity, 
never()).getOrCreateModelConversion(anyString());
     }
+
+    // ── ComplexAliasField.makeAliasColName() — default-value quoting ────────
+
+    @Test
+    void makeAliasColNameDateTimeDefaultValueIsWrappedInSingleQuotes() {
+        ModelField dateTimeField = ModelField.create(null, 
"estimatedDeliveryDate", "date-time", false);
+        when(mockViewEntity.getAliasedEntity("OI", 
mockModelReader)).thenReturn(mockModelEntity);
+        when(mockViewEntity.getAliasedField(mockModelEntity, 
"estimatedDeliveryDate", mockModelReader)).thenReturn(dateTimeField);
+
+        ModelViewEntity.ComplexAliasField caf =
+                new ModelViewEntity.ComplexAliasField("OI", 
"estimatedDeliveryDate", "2026-06-30 12:34:56.789", "min");
+
+        StringBuilder colNameBuffer = new StringBuilder();
+        StringBuilder fieldTypeBuffer = new StringBuilder();
+        caf.makeAliasColName(colNameBuffer, fieldTypeBuffer, mockViewEntity, 
mockModelReader);
+
+        assertEquals("MIN(COALESCE(OI.ESTIMATED_DELIVERY_DATE,'2026-06-30 
12:34:56.789'))", colNameBuffer.toString());
+        assertEquals("date-time", fieldTypeBuffer.toString());
+    }
+
+    @Test
+    void makeAliasColNameNumericDefaultValueIsNotQuoted() {
+        ModelField numericField = ModelField.create(null, "quantity", 
"numeric", false);
+        when(mockViewEntity.getAliasedEntity("OI", 
mockModelReader)).thenReturn(mockModelEntity);
+        when(mockViewEntity.getAliasedField(mockModelEntity, "quantity", 
mockModelReader)).thenReturn(numericField);
+
+        ModelViewEntity.ComplexAliasField caf =
+                new ModelViewEntity.ComplexAliasField("OI", "quantity", "0", 
null);
+
+        StringBuilder colNameBuffer = new StringBuilder();
+        StringBuilder fieldTypeBuffer = new StringBuilder();
+        caf.makeAliasColName(colNameBuffer, fieldTypeBuffer, mockViewEntity, 
mockModelReader);
+
+        assertEquals("COALESCE(OI.QUANTITY,0)", colNameBuffer.toString());
+    }
+
+    @Test
+    void makeAliasColNameAlreadyQuotedDefaultValueIsNotDoubleQuoted() {
+        ModelField dateTimeField = ModelField.create(null, 
"estimatedDeliveryDate", "date-time", false);
+        when(mockViewEntity.getAliasedEntity("OI", 
mockModelReader)).thenReturn(mockModelEntity);
+        when(mockViewEntity.getAliasedField(mockModelEntity, 
"estimatedDeliveryDate", mockModelReader)).thenReturn(dateTimeField);
+
+        ModelViewEntity.ComplexAliasField caf =
+                new ModelViewEntity.ComplexAliasField("OI", 
"estimatedDeliveryDate", "'2026-06-30 12:34:56.789'", null);
+
+        StringBuilder colNameBuffer = new StringBuilder();
+        StringBuilder fieldTypeBuffer = new StringBuilder();
+        caf.makeAliasColName(colNameBuffer, fieldTypeBuffer, mockViewEntity, 
mockModelReader);
+
+        assertEquals("COALESCE(OI.ESTIMATED_DELIVERY_DATE,'2026-06-30 
12:34:56.789')", colNameBuffer.toString());
+    }
+
+    @Test
+    void makeAliasColNameNoDefaultValueProducesPlainColumnName() {
+        ModelField dateTimeField = ModelField.create(null, 
"estimatedDeliveryDate", "date-time", false);
+        when(mockViewEntity.getAliasedEntity("OI", 
mockModelReader)).thenReturn(mockModelEntity);
+        when(mockViewEntity.getAliasedField(mockModelEntity, 
"estimatedDeliveryDate", mockModelReader)).thenReturn(dateTimeField);
+
+        ModelViewEntity.ComplexAliasField caf =
+                new ModelViewEntity.ComplexAliasField("OI", 
"estimatedDeliveryDate", null, "min");
+
+        StringBuilder colNameBuffer = new StringBuilder();
+        StringBuilder fieldTypeBuffer = new StringBuilder();
+        caf.makeAliasColName(colNameBuffer, fieldTypeBuffer, mockViewEntity, 
mockModelReader);
+
+        assertEquals("MIN(OI.ESTIMATED_DELIVERY_DATE)", 
colNameBuffer.toString());
+    }
+
+    @Test
+    void makeAliasColNameSqlFunctionDefaultValueIsNotQuoted() {
+        // Oracle requires TO_TIMESTAMP(...) — a function expression must pass 
through unmodified
+        ModelField dateTimeField = ModelField.create(null, 
"estimatedDeliveryDate", "date-time", false);
+        when(mockViewEntity.getAliasedEntity("OI", 
mockModelReader)).thenReturn(mockModelEntity);
+        when(mockViewEntity.getAliasedField(mockModelEntity, 
"estimatedDeliveryDate", mockModelReader)).thenReturn(dateTimeField);
+
+        ModelViewEntity.ComplexAliasField caf = new 
ModelViewEntity.ComplexAliasField(
+                "OI", "estimatedDeliveryDate", 
"TO_TIMESTAMP('2026-06-30','YYYY-MM-DD')", null);
+
+        StringBuilder colNameBuffer = new StringBuilder();
+        StringBuilder fieldTypeBuffer = new StringBuilder();
+        caf.makeAliasColName(colNameBuffer, fieldTypeBuffer, mockViewEntity, 
mockModelReader);
+
+        
assertEquals("COALESCE(OI.ESTIMATED_DELIVERY_DATE,TO_TIMESTAMP('2026-06-30','YYYY-MM-DD'))",
+                colNameBuffer.toString());
+    }
 }

Reply via email to