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) {