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 <[email protected]>
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) {