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

pcristof pushed a commit to branch OPENJPA-2940
in repository https://gitbox.apache.org/repos/asf/openjpa.git


The following commit(s) were added to refs/heads/OPENJPA-2940 by this push:
     new 2d677d8c0 [WIP][OPENJPA-2940] Implementing CAST TO NUMBER JPQL function
2d677d8c0 is described below

commit 2d677d8c069cefee44984f5610b4ea9987437448
Author: Paulo Cristovão de Araújo Silva Filho <pcris...@gmail.com>
AuthorDate: Sun Sep 14 20:25:42 2025 -0300

    [WIP][OPENJPA-2940] Implementing CAST TO NUMBER JPQL function
    
    Added function and corresponding tests, but MySQL still fails in tests.
    Passed on h2-2, pg, mariadb.
---
 .../jdbc/kernel/exps/ExtractDateTimeField.java     |   1 -
 .../jdbc/kernel/exps/JDBCExpressionFactory.java    |  12 +++
 ...TypecastAsString.java => TypecastAsNumber.java} |  55 +++++++---
 .../openjpa/jdbc/kernel/exps/TypecastAsString.java |   6 +-
 .../org/apache/openjpa/jdbc/sql/DBDictionary.java  |   2 +
 .../apache/openjpa/jdbc/sql/MariaDBDictionary.java |   1 +
 .../apache/openjpa/jdbc/sql/MySQLDictionary.java   |   1 +
 .../openjpa/kernel/exps/ExpressionFactory.java     |   7 ++
 .../kernel/exps/InMemoryExpressionFactory.java     |   5 +
 .../openjpa/kernel/exps/TypecastAsNumber.java      | 115 +++++++++++++++++++++
 .../openjpa/kernel/exps/TypecastAsNumberPart.java  |  43 ++++++++
 .../openjpa/kernel/jpql/JPQLExpressionBuilder.java |  33 ++++++
 .../jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt |  12 +++
 .../apache/openjpa/kernel/jpql/TestJPQLParser.java |  97 +++++++++++++++++
 .../jpql/functions/TestEJBQLFunction.java          |  52 ++++++++++
 15 files changed, 425 insertions(+), 17 deletions(-)

diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
index 9fe2a99ee..7e95c6ab8 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
@@ -18,7 +18,6 @@
  */
 package org.apache.openjpa.jdbc.kernel.exps;
 
-import java.lang.Math;
 import java.sql.SQLException;
 
 import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
index 1a7cf122f..bb601a755 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
@@ -385,6 +385,18 @@ public class JDBCExpressionFactory
     public Value newTypecastAsString(Value value) {
        return new TypecastAsString((Val) value);
     }
+    
+    @Override
+    public Value newTypecastAsNumber(Value value, Class<? extends Number> 
numberType) {
+       if (numberType == null ||
+                       (numberType != Integer.class && 
+                       numberType != Long.class &&
+                       numberType != Float.class &&
+                       numberType != Double.class)) {
+               throw new IllegalArgumentException("Unexpected target type 
class: " + numberType);
+               }
+       return new TypecastAsNumber((Val) value, numberType);
+    }
 
     @Override
     public Parameter newParameter(Object name, Class type) {
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsNumber.java
similarity index 71%
copy from 
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
copy to 
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsNumber.java
index 39cdd1e74..a0741a6b7 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsNumber.java
@@ -27,25 +27,28 @@ import org.apache.openjpa.jdbc.sql.Result;
 import org.apache.openjpa.jdbc.sql.SQLBuffer;
 import org.apache.openjpa.jdbc.sql.Select;
 import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.DateTimeExtractField;
 import org.apache.openjpa.kernel.exps.ExpressionVisitor;
 import org.apache.openjpa.meta.ClassMetaData;
 
 /**
- * Returns the temporal field of a given date or time.
+ * Returns the given value as a number
  *
  */
-public class TypecastAsString
+public class TypecastAsNumber
     extends AbstractVal {
 
     private static final long serialVersionUID = 1L;
     private final Val _val;
+    private final Class<? extends Number> _targetType;
     private ClassMetaData _meta = null;
 
     /**
-     * Constructor. Provides the value to be casted to string.
+     * Constructor. Provide the date and the field to operate on.
      */
-    public TypecastAsString(Val val) {
+    public TypecastAsNumber(Val val, Class<? extends Number> target) {
         _val = val;
+        _targetType = target;
     }
 
     public Val getVal() {
@@ -64,7 +67,17 @@ public class TypecastAsString
 
     @Override
     public Class getType() {
-        return String.class;
+       if (_targetType == Integer.class) {
+               return int.class;
+       } else if (_targetType == Long.class) {
+               return long.class;
+       } else if (_targetType == Float.class) {
+               return float.class;
+       } else if (_targetType == Double.class) {
+               return double.class;
+       } else {
+               return _targetType;
+       }
     }
 
     @Override
@@ -74,18 +87,18 @@ public class TypecastAsString
     @Override
     public ExpState initialize(Select sel, ExpContext ctx, int flags) {
         ExpState valueState =  _val.initialize(sel, ctx, 0);
-        return new TypecastAsStringExpState(valueState.joins, valueState);
+        return new ExtractTypecastToNumberExpState(valueState.joins, 
valueState);
     }
 
     /**
      * Expression state.
      */
-    private static class TypecastAsStringExpState
+    private static class ExtractTypecastToNumberExpState
         extends ExpState {
 
         public final ExpState valueState;
 
-        public TypecastAsStringExpState(Joins joins, ExpState valueState) {
+        public ExtractTypecastToNumberExpState(Joins joins, ExpState 
valueState) {
             super(joins);
             this.valueState = valueState;
         }
@@ -99,8 +112,8 @@ public class TypecastAsString
 
     @Override
     public void selectColumns(Select sel, ExpContext ctx, ExpState state, 
boolean pks) {
-       TypecastAsStringExpState casstate = (TypecastAsStringExpState) state;
-        _val.selectColumns(sel, ctx, casstate.valueState, true);
+        ExtractTypecastToNumberExpState etnstate = 
(ExtractTypecastToNumberExpState) state;
+        _val.selectColumns(sel, ctx, etnstate.valueState, true);
     }
 
     @Override
@@ -131,8 +144,8 @@ public class TypecastAsString
     @Override
     public void calculateValue(Select sel, ExpContext ctx, ExpState state,
         Val other, ExpState otherState) {
-       TypecastAsStringExpState casstate = (TypecastAsStringExpState) state;
-        _val.calculateValue(sel, ctx, casstate.valueState, null, null);
+        ExtractTypecastToNumberExpState etnstate = 
(ExtractTypecastToNumberExpState) state;
+        _val.calculateValue(sel, ctx, etnstate.valueState, null, null);
     }
 
     @Override
@@ -152,11 +165,11 @@ public class TypecastAsString
         String part2 = func.substring(fieldPart + 3, targetPart);
         String part3 = func.substring(targetPart + 3);
 
-        TypecastAsStringExpState casstate = (TypecastAsStringExpState) state;
+        ExtractTypecastToNumberExpState etnstate = 
(ExtractTypecastToNumberExpState) state;
         sql.append(part1);
-        _val.appendTo(sel, ctx, casstate.valueState, sql, 0);
+        _val.appendTo(sel, ctx, etnstate.valueState, sql, 0);
         sql.append(part2);
-       sql.append(dict.varcharTypeName);
+        sql.append(getDbNumberTargetTypeName(dict));
         sql.append(part3);
     }
 
@@ -171,5 +184,17 @@ public class TypecastAsString
     public int getId() {
         return Val.EXTRACTDTF_VAL;
     }
+    
+    private String getDbNumberTargetTypeName(DBDictionary dict) {
+       if (getType() == int.class) {
+               return dict.integerTypeName;
+       } else if (getType() == long.class) {
+               return dict.decimalTypeName;
+       } else if (getType() == float.class) {
+               return dict.floatTypeName;
+       } else {
+               return dict.doubleTypeName;
+       }
+    }
 }
 
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
index 39cdd1e74..f42256390 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/TypecastAsString.java
@@ -156,7 +156,11 @@ public class TypecastAsString
         sql.append(part1);
         _val.appendTo(sel, ctx, casstate.valueState, sql, 0);
         sql.append(part2);
-       sql.append(dict.varcharTypeName);
+        if (dict.supportsUnsizedCharOnCast) {
+               sql.append(dict.varcharTypeName);
+        } else {
+               sql.append(dict.charTypeName + "(" + dict.characterColumnSize + 
")");
+        }
         sql.append(part3);
     }
 
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
index 7f0346a39..c3b74c7ab 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
@@ -496,6 +496,8 @@ public class DBDictionary
     private String conversionKey = null;
 
     public boolean supportsUuidType = false;
+    
+    public boolean supportsUnsizedCharOnCast = true;
 
     // Naming utility and naming rules
     private DBIdentifierUtil namingUtil = null;
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MariaDBDictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MariaDBDictionary.java
index 4864012b3..bf3f7b6e3 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MariaDBDictionary.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MariaDBDictionary.java
@@ -179,6 +179,7 @@ public class MariaDBDictionary extends DBDictionary {
         fixedSizeTypeNameSet.remove("NUMERIC");
 
         dateFractionDigits = 0;
+        supportsUnsizedCharOnCast = false;
     }
 
     @Override
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java
index fba08a48c..571af1bf8 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MySQLDictionary.java
@@ -175,6 +175,7 @@ public class MySQLDictionary
         fixedSizeTypeNameSet.remove("NUMERIC");
 
         dateFractionDigits = 0;
+        supportsUnsizedCharOnCast = false;
     }
 
     @Override
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
index 8a517a155..170c3ec74 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
@@ -263,6 +263,13 @@ public interface ExpressionFactory {
      * 
      */
     Value newTypecastAsString(Value value);
+    
+    /**
+     * Returns the value typecasted as the given Number type
+     * @param value
+     * @return
+     */
+    Value newTypecastAsNumber(Value value, Class<? extends Number> numberType);
 
     /**
      * Return a value representing a parameter for the given value. The
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
index bc936b9cc..c9e4a90a1 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
@@ -559,6 +559,11 @@ public class InMemoryExpressionFactory
     public Value newTypecastAsString(Value value) {
        return new TypecastAsString((Val) value);
     }
+    
+    @Override
+    public Value newTypecastAsNumber(Value value, Class<? extends Number> 
numberType) {
+       return new TypecastAsNumber((Val) value, numberType);
+    }
 
     @Override
     public Parameter newParameter(Object name, Class type) {
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumber.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumber.java
new file mode 100644
index 000000000..e140ea448
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumber.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel.exps;
+
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.Temporal;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Extract the part value of a temporal type
+ *
+ */
+class TypecastAsNumber
+    extends Val {
+
+    
+    private static final long serialVersionUID = 1L;
+    private final Class<? extends Number> _targetType;
+    private final Val _val;
+
+    /**
+     * Constructor. Provide target field and the value.
+     */
+    public TypecastAsNumber(Val val, Class<? extends Number> target) {
+        _val = val;
+        _targetType = target;
+    }
+
+    @Override
+    public Class getType() {
+       if (_targetType == Integer.class) {
+               return int.class;
+       } else if (_targetType == Long.class) {
+               return long.class;
+       } else if (_targetType == Float.class) {
+               return float.class;
+       } else if (_targetType == Double.class) {
+               return double.class;
+       } else {
+               return _targetType;
+       }
+    }
+
+    @Override
+    public void setImplicitType(Class type) {
+    }
+
+    @Override
+    protected Object eval(Object candidate, Object orig,
+        StoreContext ctx, Object[] params) {
+
+        Object r = _val.eval(candidate, orig, ctx, params);
+        Class<?> clazz = r.getClass();
+        if (_targetType == Integer.class) {
+               if (r instanceof String s) {
+                       return Integer.valueOf(s);
+               } else if (clazz.isAssignableFrom(Number.class)) {
+                       return ((Number) r).intValue();
+               }
+        } else if (_targetType == Long.class) {
+               if (r instanceof String s) {
+                       return Long.valueOf(s);
+               } else if (clazz.isAssignableFrom(Number.class)) {
+                       return ((Number) r).longValue();
+               }
+        } else if (_targetType == Float.class) {
+               if (r instanceof String s) {
+                       return Float.valueOf(s);
+               } else if (clazz.isAssignableFrom(Number.class)) {
+                       return ((Number) r).floatValue();
+               }
+        } else if (_targetType == Double.class) {
+               if (r instanceof String s) {
+                       return Double.valueOf(s);
+               } else if (clazz.isAssignableFrom(Number.class)) {
+                       return ((Number) r).doubleValue();
+               }
+        }
+        throw new IllegalArgumentException();
+    }
+
+    @Override
+    public void acceptVisit(ExpressionVisitor visitor) {
+        visitor.enter(this);
+        _val.acceptVisit(visitor);
+        visitor.exit(this);
+    }
+
+}
+
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumberPart.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumberPart.java
new file mode 100644
index 000000000..26cee95b6
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/TypecastAsNumberPart.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel.exps;
+
+/**
+ * Identifiers used by CAST TO (INTEGER|LONG|FLOAT|DOUBLE)
+ */
+public enum TypecastAsNumberPart {
+
+    /**
+     * Integer
+     */
+    INTEGER,
+    /**
+     * LONG
+     */
+    LONG,
+    /**
+     * FLOAT
+     */
+    FLOAT,
+    /**
+     * DOUBLE
+     */
+    DOUBLE;
+    
+}
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
index 5b3f31864..f87eff846 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
@@ -59,6 +59,7 @@ import org.apache.openjpa.kernel.exps.Path;
 import org.apache.openjpa.kernel.exps.QueryExpressions;
 import org.apache.openjpa.kernel.exps.Resolver;
 import org.apache.openjpa.kernel.exps.Subquery;
+import org.apache.openjpa.kernel.exps.TypecastAsNumberPart;
 import org.apache.openjpa.kernel.exps.Value;
 import org.apache.openjpa.lib.log.Log;
 import org.apache.openjpa.lib.util.Localizer;
@@ -1524,6 +1525,22 @@ public class JPQLExpressionBuilder
                 
             case JJTSTRINGCAST:
                return factory.newTypecastAsString(getValue(onlyChild(node)));
+               
+            case JJTINTEGER:
+               return TypecastAsNumberPart.INTEGER;
+               
+            case JJTLONG:
+               return TypecastAsNumberPart.LONG;
+
+            case JJTFLOAT:
+               return TypecastAsNumberPart.FLOAT;
+            
+            case JJTDOUBLE:
+               return TypecastAsNumberPart.DOUBLE;
+               
+            case JJTCASTTONUMBER:
+               return factory.newTypecastAsNumber(getValue(firstChild(node)), 
+                               resolveNumberTargetType((TypecastAsNumberPart) 
eval(secondChild(node))));
 
             default:
                 throw parseException(EX_FATAL, "bad-tree",
@@ -2579,5 +2596,21 @@ public class JPQLExpressionBuilder
             return null;
         }
     }
+    
+    private Class<? extends Number> 
resolveNumberTargetType(TypecastAsNumberPart part) {
+       return switch (part) {
+               case INTEGER: {
+                       yield Integer.class;
+               }
+               case LONG: {
+                       yield Long.class;
+               }
+               case FLOAT: {
+                       yield Float.class;
+               }
+               default:
+                       yield Double.class;
+               };
+    }
 }
 
diff --git 
a/openjpa-kernel/src/main/jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt 
b/openjpa-kernel/src/main/jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt
index 5bcf0ccac..289f83acf 100644
--- a/openjpa-kernel/src/main/jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt
+++ b/openjpa-kernel/src/main/jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt
@@ -172,6 +172,10 @@ TOKEN [ IGNORE_CASE ]: /* basics */
        |   < EXTRACT: "EXTRACT" >
        |   < CAST: "CAST" >
        |   < STRING: "STRING" >
+       |   < INTEGER: "INTEGER" >
+       |   < LONG: "LONG" >
+       |   < FLOAT: "FLOAT" >
+       |   < DOUBLE: "DOUBLE" >
 }
 
 TOKEN [ IGNORE_CASE ]: /* aggregates */
@@ -997,6 +1001,7 @@ void arithmetic_factor() : { }
        LOOKAHEAD(identification_variable()) identification_variable() |
        LOOKAHEAD("("  arithmetic_expression()) "("  arithmetic_expression() 
")" |
        LOOKAHEAD(functions_returning_numerics()) 
functions_returning_numerics() |
+       LOOKAHEAD(arithmetic_cast_function()) arithmetic_cast_function() |
        LOOKAHEAD(aggregate_select_expression()) aggregate_select_expression() |
        LOOKAHEAD(case_expression()) case_expression() |
        LOOKAHEAD("(" subquery()) "(" subquery() ")"
@@ -1060,6 +1065,11 @@ void scalar_expression() #SCALAREXPRESSION : { }
     LOOKAHEAD(entity_type_expression()) entity_type_expression()
 }
 
+void arithmetic_cast_function() #CASTTONUMBER : { }
+{
+       <CAST> "(" string_expression() <AS> ( <INTEGER> #INTEGER | <LONG> #LONG 
| <FLOAT> #FLOAT | <DOUBLE> #DOUBLE ) ")"
+}
+
 void case_expression() #CASE : { }
 {
        <CASE>
@@ -1539,6 +1549,8 @@ void path_component() #IDENTIFICATIONVARIABLE :
        | t = <ENTRY>
        | t = <INDEX>
        | t = <TYPE>
+       | t = <CAST>
+       | t = <STRING>
        | t = <CLASS>
     ) { jjtThis.setToken (t); }
 }
diff --git 
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
 
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
index facfb9b3e..ec44f791a 100644
--- 
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
+++ 
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
@@ -94,4 +94,101 @@ public class TestJPQLParser {
                fail();
        }
     }
+    
+    @Test
+    public void testSimpleCastToInteger() {
+       try {
+               String query = "SELECT u FROM User AS u WHERE CAST('1983' AS 
INTEGER) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToIntegerOnSelect() {
+       try {
+               String query = "SELECT CAST(u.birthYear as Integer) FROM User 
AS u WHERE extract(year from u.dateOfBirth) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToLong() {
+       try {
+               String query = "SELECT u FROM User AS u WHERE CAST('1983' AS 
LONG) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToLongOnSelect() {
+       try {
+               String query = "SELECT CAST(u.birthYear as long) FROM User AS u 
WHERE extract(year from u.dateOfBirth) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToFloat() {
+       try {
+               String query = "SELECT u FROM User AS u WHERE CAST('1983' AS 
FLOAT) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToFloatOnSelect() {
+       try {
+               String query = "SELECT CAST(u.birthYear as FLOAT) FROM User AS 
u WHERE extract(year from u.dateOfBirth) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToDouble() {
+       try {
+               String query = "SELECT u FROM User AS u WHERE CAST(u.cast AS 
INTEGER) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
+    @Test
+    public void testSimpleCastToDoubleOnSelect() {
+       try {
+               String query = "SELECT CAST(u.birthYear as double) FROM User AS 
u WHERE extract(year from u.dateOfBirth) = 1983";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+               fail();
+       }
+    }
+
 }
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
index 432ed6723..1a194f6a1 100644
--- 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
@@ -894,6 +894,58 @@ public class TestEJBQLFunction extends AbstractTestCase {
        
        endEm(em);
     }
+    
+    public void testTypecastAsInteger() {
+       EntityManager em = currentEntityManager();
+       String query = "SELECT u FROM CompUser AS u WHERE 
CAST(u.address.zipcode as integer) = :value";
+       
+       List result = em.createQuery(query).setParameter("value", 
94104).getResultList();
+       
+       assertEquals(1, result.size());
+       assertEquals("Seetha", ((CompUser) result.get(0)).getName());
+       
+       endEm(em);
+       
+    }
+
+    public void testTypecastAsLong() {
+       EntityManager em = currentEntityManager();
+       String query = "SELECT u FROM CompUser AS u WHERE 
CAST(u.address.zipcode as LONG) = :value";
+       
+       List result = em.createQuery(query).setParameter("value", 
94104l).getResultList();
+       
+       assertEquals(1, result.size());
+       assertEquals("Seetha", ((CompUser) result.get(0)).getName());
+       
+       endEm(em);
+       
+    }
+
+    public void testTypecastAsFloat() {
+       EntityManager em = currentEntityManager();
+       String query = "SELECT u FROM CompUser AS u WHERE CAST(u.age as float) 
= :value";
+       
+       List result = em.createQuery(query).setParameter("value", 
29f).getResultList();
+       
+       assertEquals(1, result.size());
+       assertEquals("Famzy", ((CompUser) result.get(0)).getName());
+       
+       endEm(em);
+       
+    }
+
+    public void testTypecastAsDouble() {
+       EntityManager em = currentEntityManager();
+       String query = "SELECT CAST(u.age as double) FROM CompUser AS u WHERE 
CAST(u.age as double) = :value";
+       
+       List result = em.createQuery(query).setParameter("value", 
29.0d).getResultList();
+       
+       assertEquals(1, result.size());
+       assertEquals(29d, ((double) result.get(0)));
+       
+       endEm(em);
+       
+    }
 
     public CompUser createUser(String name, String cName, Address add, int age,
         boolean isMale) {

Reply via email to