This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git
The following commit(s) were added to refs/heads/master by this push:
new 141bcd6e8 [OPENJPA-2933] Implements new date JPA 3.1 JPQL functions
and equivalent Criteria API (#123)
141bcd6e8 is described below
commit 141bcd6e8e61486a4f1d653ec040c6bdc7976745
Author: Paulo Cristovão de Araújo Silva Filho <[email protected]>
AuthorDate: Sat Feb 8 10:49:14 2025 -0300
[OPENJPA-2933] Implements new date JPA 3.1 JPQL functions and equivalent
Criteria API (#123)
* OPENJPA-2908
* Bumped jakarta persistence API to 3.1.0
* Added necessary stub impl of methods (throws
UnsupportedOperationException)
* Tests passes, but actual impls must be made in probably different issues,
one for each new features
(https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#jakarta-persistence-3-1)
* [OPENJPA-2933] Implements LOCAL DATE, LOCAL TIME, LOCAL DATETIME and
EXTRACT functions
* Added JPA 3.1 date/time JPQL new functions LOCAL DATE, LOCAL TIME and
LOCAL DATETIME and Criteria equivalents
* Added JPQL EXTRACT date/time field and EXTRACT date/time (as a CAST) JPQL
functions
* Added tests to verify validity of BNF changes in JPQL.jjt and each new
function
* [OPENJPA-2933] Fixing typo and postgresql extract function
* [OPENJPA-2933] Decreasing lookahead instruction of extract for better
performance
* [OPENJPA-2933] Finishing implementation
* Updated round function usage and test due a corner case on postgresql
tests
* Made EXTRACT function test be resistent to date/time divergence between
db and test environments
* Updated manual BNF and Date Time functions chapters
---
.../openjpa/jdbc/kernel/exps/CurrentTemporal.java | 97 ++++
.../exps/{Math.java => ExtractDateTimeField.java} | 112 ++--
.../exps/{Math.java => ExtractDateTimePart.java} | 108 ++--
.../jdbc/kernel/exps/JDBCExpressionFactory.java | 22 +-
.../org/apache/openjpa/jdbc/kernel/exps/Math.java | 4 +
.../org/apache/openjpa/jdbc/kernel/exps/Val.java | 2 +
.../org/apache/openjpa/jdbc/sql/DBDictionary.java | 1 +
.../openjpa/kernel/exps/CurrentTemporal.java | 62 +++
.../openjpa/kernel/exps/DateTimeExtractField.java | 75 +++
.../openjpa/kernel/exps/DateTimeExtractPart.java | 35 ++
.../openjpa/kernel/exps/ExpressionFactory.java | 17 +
.../openjpa/kernel/exps/ExtractDateTimeField.java | 100 ++++
.../openjpa/kernel/exps/ExtractDateTimePart.java | 112 ++++
.../kernel/exps/InMemoryExpressionFactory.java | 16 +
.../openjpa/kernel/jpql/JPQLExpressionBuilder.java | 60 +++
.../jjtree/org/apache/openjpa/kernel/jpql/JPQL.jjt | 78 ++-
.../apache/openjpa/kernel/jpql/TestJPQLParser.java | 74 +++
.../TestContainerSpecCompatibilityOptions.java | 2 +-
.../compat/TestSpecCompatibilityOptions.java | 2 +-
.../persistence/criteria/TestTypesafeCriteria.java | 63 ++-
.../jpql/functions/TestEJBQLFunction.java | 233 +++++++-
.../persistence/simple/TestJava8TimeTypes.java | 30 ++
.../test/AbstractPersistenceTestCase.java | 2 +-
.../persistence/criteria/CriteriaBuilderImpl.java | 9 +-
.../openjpa/persistence/criteria/Expressions.java | 51 ++
.../src/doc/manual/jpa_overview_query.xml | 594 ++++++++++++++++-----
26 files changed, 1686 insertions(+), 275 deletions(-)
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CurrentTemporal.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CurrentTemporal.java
new file mode 100644
index 000000000..58b909b92
--- /dev/null
+++
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CurrentTemporal.java
@@ -0,0 +1,97 @@
+/*
+ * 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.jdbc.kernel.exps;
+
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.temporal.Temporal;
+
+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.util.InternalException;
+
+/**
+ * A literal current LOCALDATE, LOCALTIME or LOCALDATETIME value in a filter.
+ *
+ */
+class CurrentTemporal
+ extends Const {
+
+
+ private static final long serialVersionUID = 1L;
+ private final Class<? extends Temporal> _type;
+
+ public CurrentTemporal(Class<? extends Temporal> type) {
+ _type = type;
+ }
+
+ @Override
+ public Class<? extends Temporal> getType() {
+ return _type;
+ }
+
+ @Override
+ public void setImplicitType(Class type) {
+ }
+
+ @Override
+ public Object load(ExpContext ctx, ExpState state, Result res) throws
SQLException {
+ if (LocalDateTime.class.isAssignableFrom(_type)) {
+ return LocalDateTime.ofInstant(res.getTimestamp(this,
null).toInstant(), ZoneId.systemDefault());
+ } else if (LocalTime.class.isAssignableFrom(_type)) {
+ return res.getTime(this, null).toLocalTime();
+ } else if (LocalDate.class.isAssignableFrom(_type)) {
+ return res.getDate(this, null).toLocalDate();
+ } else {
+ throw new InternalException();
+ }
+ }
+
+ @Override
+ public Object getValue(Object[] params) {
+ if (LocalDateTime.class.isAssignableFrom(_type)) {
+ return LocalDateTime.now();
+ } else if (LocalDate.class.isAssignableFrom(_type)) {
+ return LocalDate.now();
+ } else if (LocalTime.class.isAssignableFrom(_type)) {
+ return LocalTime.now();
+ }
+ return null;
+ }
+
+ @Override
+ public void appendTo(Select sel, ExpContext ctx, ExpState state, SQLBuffer
sql, int index) {
+ if (LocalDateTime.class.isAssignableFrom(_type)) {
+ sql.append(ctx.store.getDBDictionary().currentTimestampFunction);
+ } else if (LocalTime.class.isAssignableFrom(_type)) {
+ sql.append(ctx.store.getDBDictionary().currentTimeFunction);
+ } else if (LocalDate.class.isAssignableFrom(_type)) {
+ sql.append(ctx.store.getDBDictionary().currentDateFunction);
+ } else {
+ throw new InternalException();
+ }
+ }
+}
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
similarity index 59%
copy from
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
copy to
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
index dfbab290d..9fe2a99ee 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
+++
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimeField.java
@@ -18,59 +18,42 @@
*/
package org.apache.openjpa.jdbc.kernel.exps;
+import java.lang.Math;
import java.sql.SQLException;
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.Joins;
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;
/**
- * Value produced by a mathematical operation on two values.
+ * Returns the temporal field of a given date or time.
*
- * @author Abe White
*/
-public class Math
+public class ExtractDateTimeField
extends AbstractVal {
-
private static final long serialVersionUID = 1L;
- public static final String ADD = "+";
- public static final String SUBTRACT = "-";
- public static final String MULTIPLY = "*";
- public static final String DIVIDE = "/";
- public static final String MOD = "MOD";
- public static final String POWER = "POWER";
- public static final String ROUND = "ROUND";
-
- private final Val _val1;
- private final Val _val2;
- private final String _op;
+ private final Val _val;
+ private final DateTimeExtractField _field;
private ClassMetaData _meta = null;
- private Class _cast = null;
/**
- * Constructor. Provide the values to operate on, and the operator.
+ * Constructor. Provide the date and the field to operate on.
*/
- public Math(Val val1, Val val2, String op) {
- _val1 = val1;
- _val2 = val2;
- _op = op;
+ public ExtractDateTimeField(Val val, DateTimeExtractField field) {
+ _val = val;
+ _field = field;
}
- public Val getVal1() {
- return _val1;
- }
-
- public Val getVal2() {
- return _val2;
- }
-
- public String getOperation() {
- return _op;
+ public Val getVal() {
+ return _val;
}
@Override
@@ -85,23 +68,31 @@ public class Math
@Override
public Class getType() {
- if (_cast != null)
- return _cast;
- Class c1 = _val1.getType();
- Class c2 = _val2.getType();
- return Filters.promote(c1, c2);
+ return _field == DateTimeExtractField.SECOND ? float.class : int.class;
}
@Override
public void setImplicitType(Class type) {
- _cast = type;
}
@Override
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
- ExpState s1 = _val1.initialize(sel, ctx, 0);
- ExpState s2 = _val2.initialize(sel, ctx, 0);
- return new BinaryOpExpState(sel.and(s1.joins, s2.joins), s1, s2);
+ ExpState valueState = _val.initialize(sel, ctx, 0);
+ return new ExtractDateTimeFieldExpState(valueState.joins, valueState);
+ }
+
+ /**
+ * Expression state.
+ */
+ private static class ExtractDateTimeFieldExpState
+ extends ExpState {
+
+ public final ExpState valueState;
+
+ public ExtractDateTimeFieldExpState(Joins joins, ExpState valueState) {
+ super(joins);
+ this.valueState = valueState;
+ }
}
@Override
@@ -111,11 +102,9 @@ public class Math
}
@Override
- public void selectColumns(Select sel, ExpContext ctx, ExpState state,
- boolean pks) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- _val1.selectColumns(sel, ctx, bstate.state1, true);
- _val2.selectColumns(sel, ctx, bstate.state2, true);
+ public void selectColumns(Select sel, ExpContext ctx, ExpState state,
boolean pks) {
+ ExtractDateTimeFieldExpState edtstate = (ExtractDateTimeFieldExpState)
state;
+ _val.selectColumns(sel, ctx, edtstate.valueState, true);
}
@Override
@@ -139,16 +128,15 @@ public class Math
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
- return Filters.convert(res.getObject(this, JavaSQLTypes.JDBC_DEFAULT,
- null), getType());
+ return Filters.convert(res.getObject(this,
+ JavaSQLTypes.JDBC_DEFAULT, null), getType());
}
@Override
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
Val other, ExpState otherState) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- _val1.calculateValue(sel, ctx, bstate.state1, _val2, bstate.state2);
- _val2.calculateValue(sel, ctx, bstate.state2, _val1, bstate.state1);
+ ExtractDateTimeFieldExpState edtstate = (ExtractDateTimeFieldExpState)
state;
+ _val.calculateValue(sel, ctx, edtstate.valueState, null, null);
}
@Override
@@ -159,23 +147,33 @@ public class Math
@Override
public void appendTo(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql, int index) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- ctx.store.getDBDictionary().mathFunction(sql, _op,
- new FilterValueImpl(sel, ctx, bstate.state1, _val1),
- new FilterValueImpl(sel, ctx, bstate.state2, _val2));
+ DBDictionary dict = ctx.store.getDBDictionary();
+ String func = dict.extractDateTimeFieldFunction;
+
+ int fieldPart = func.indexOf("{0}");
+ int fromPart = func.indexOf("{1}");
+ String part1 = func.substring(0, fieldPart);
+ String part2 = func.substring(fieldPart + 3, fromPart);
+ String part3 = func.substring(fromPart + 3);
+
+ ExtractDateTimeFieldExpState edtstate = (ExtractDateTimeFieldExpState)
state;
+ sql.append(part1);
+ sql.append(_field.name());
+ sql.append(part2);
+ _val.appendTo(sel, ctx, edtstate.valueState, sql, 0);
+ sql.append(part3);
}
@Override
public void acceptVisit(ExpressionVisitor visitor) {
visitor.enter(this);
- _val1.acceptVisit(visitor);
- _val2.acceptVisit(visitor);
+ _val.acceptVisit(visitor);
visitor.exit(this);
}
@Override
public int getId() {
- return Val.MATH_VAL;
+ return Val.EXTRACTDTF_VAL;
}
}
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimePart.java
similarity index 60%
copy from
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
copy to
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimePart.java
index dfbab290d..1634a5a2c 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
+++
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ExtractDateTimePart.java
@@ -18,59 +18,43 @@
*/
package org.apache.openjpa.jdbc.kernel.exps;
+import java.sql.Date;
import java.sql.SQLException;
+import java.sql.Time;
import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.Joins;
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.DateTimeExtractPart;
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
import org.apache.openjpa.meta.ClassMetaData;
/**
- * Value produced by a mathematical operation on two values.
+ * Returns the date or time part of a given temporal.
*
- * @author Abe White
*/
-public class Math
+public class ExtractDateTimePart
extends AbstractVal {
-
private static final long serialVersionUID = 1L;
- public static final String ADD = "+";
- public static final String SUBTRACT = "-";
- public static final String MULTIPLY = "*";
- public static final String DIVIDE = "/";
- public static final String MOD = "MOD";
- public static final String POWER = "POWER";
- public static final String ROUND = "ROUND";
-
- private final Val _val1;
- private final Val _val2;
- private final String _op;
+ private final Val _val;
+ private final DateTimeExtractPart _part;
private ClassMetaData _meta = null;
- private Class _cast = null;
/**
- * Constructor. Provide the values to operate on, and the operator.
+ * Constructor. Provide the date and the field to operate on.
*/
- public Math(Val val1, Val val2, String op) {
- _val1 = val1;
- _val2 = val2;
- _op = op;
+ public ExtractDateTimePart(Val val, DateTimeExtractPart part) {
+ _val = val;
+ _part = part;
}
- public Val getVal1() {
- return _val1;
- }
-
- public Val getVal2() {
- return _val2;
- }
-
- public String getOperation() {
- return _op;
+ public Val getVal() {
+ return _val;
}
@Override
@@ -85,23 +69,36 @@ public class Math
@Override
public Class getType() {
- if (_cast != null)
- return _cast;
- Class c1 = _val1.getType();
- Class c2 = _val2.getType();
- return Filters.promote(c1, c2);
+ if (_part == DateTimeExtractPart.TIME) {
+ return Time.class;
+ } else if (_part == DateTimeExtractPart.DATE) {
+ return Date.class;
+ }
+ throw new IllegalStateException();
}
@Override
public void setImplicitType(Class type) {
- _cast = type;
}
@Override
public ExpState initialize(Select sel, ExpContext ctx, int flags) {
- ExpState s1 = _val1.initialize(sel, ctx, 0);
- ExpState s2 = _val2.initialize(sel, ctx, 0);
- return new BinaryOpExpState(sel.and(s1.joins, s2.joins), s1, s2);
+ ExpState valueState = _val.initialize(sel, ctx, 0);
+ return new ExtractDateTimePartExpState(valueState.joins, valueState);
+ }
+
+ /**
+ * Expression state.
+ */
+ private static class ExtractDateTimePartExpState
+ extends ExpState {
+
+ public final ExpState valueState;
+
+ public ExtractDateTimePartExpState(Joins joins, ExpState valueState) {
+ super(joins);
+ this.valueState = valueState;
+ }
}
@Override
@@ -111,11 +108,9 @@ public class Math
}
@Override
- public void selectColumns(Select sel, ExpContext ctx, ExpState state,
- boolean pks) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- _val1.selectColumns(sel, ctx, bstate.state1, true);
- _val2.selectColumns(sel, ctx, bstate.state2, true);
+ public void selectColumns(Select sel, ExpContext ctx, ExpState state,
boolean pks) {
+ ExtractDateTimePartExpState edtstate = (ExtractDateTimePartExpState)
state;
+ _val.selectColumns(sel, ctx, edtstate.valueState, true);
}
@Override
@@ -139,16 +134,15 @@ public class Math
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
- return Filters.convert(res.getObject(this, JavaSQLTypes.JDBC_DEFAULT,
- null), getType());
+ return Filters.convert(res.getObject(this,
+ JavaSQLTypes.JDBC_DEFAULT, null), getType());
}
@Override
public void calculateValue(Select sel, ExpContext ctx, ExpState state,
Val other, ExpState otherState) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- _val1.calculateValue(sel, ctx, bstate.state1, _val2, bstate.state2);
- _val2.calculateValue(sel, ctx, bstate.state2, _val1, bstate.state1);
+ ExtractDateTimePartExpState edtstate = (ExtractDateTimePartExpState)
state;
+ _val.calculateValue(sel, ctx, edtstate.valueState, null, null);
}
@Override
@@ -159,23 +153,23 @@ public class Math
@Override
public void appendTo(Select sel, ExpContext ctx, ExpState state,
SQLBuffer sql, int index) {
- BinaryOpExpState bstate = (BinaryOpExpState) state;
- ctx.store.getDBDictionary().mathFunction(sql, _op,
- new FilterValueImpl(sel, ctx, bstate.state1, _val1),
- new FilterValueImpl(sel, ctx, bstate.state2, _val2));
+ ExtractDateTimePartExpState edtstate = (ExtractDateTimePartExpState)
state;
+ sql.append("CAST( ");
+ _val.appendTo(sel, ctx, edtstate.valueState, sql, 0);
+ sql.append(" AS ");
+ sql.append(_part == DateTimeExtractPart.DATE ? "DATE)" : "TIME)");
}
@Override
public void acceptVisit(ExpressionVisitor visitor) {
visitor.enter(this);
- _val1.acceptVisit(visitor);
- _val2.acceptVisit(visitor);
+ _val.acceptVisit(visitor);
visitor.exit(this);
}
@Override
public int getId() {
- return Val.MATH_VAL;
+ return Val.EXTRACTDTP_VAL;
}
}
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 41e6f5b30..55812de59 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
@@ -19,6 +19,7 @@
package org.apache.openjpa.jdbc.kernel.exps;
import java.io.Serializable;
+import java.time.temporal.Temporal;
import java.util.Date;
import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -28,6 +29,8 @@ import
org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.kernel.exps.AggregateListener;
import org.apache.openjpa.kernel.exps.Arguments;
+import org.apache.openjpa.kernel.exps.DateTimeExtractField;
+import org.apache.openjpa.kernel.exps.DateTimeExtractPart;
import org.apache.openjpa.kernel.exps.Expression;
import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.FilterListener;
@@ -355,12 +358,27 @@ public class JDBCExpressionFactory
@Override
public <T extends Date> Value getCurrentTime(Class<T> dateType) {
- return new CurrentDate(dateType);
+ return new CurrentDate(dateType);
}
@Override
public <T extends Date> Value getCurrentTimestamp(Class<T> dateType) {
- return new CurrentDate(dateType);
+ return new CurrentDate(dateType);
+ }
+
+ @Override
+ public <T extends Temporal> Value getCurrentLocalDateTime(Class<T>
temporalType) {
+ return new CurrentTemporal(temporalType);
+ }
+
+ @Override
+ public Value getDateTimeField(DateTimeExtractField field, Value value) {
+ return new ExtractDateTimeField((Val) value, field);
+ }
+
+ @Override
+ public Value getDateTimePart(DateTimeExtractPart part, Value value) {
+ return new ExtractDateTimePart((Val) value, part);
}
@Override
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
index dfbab290d..5927fa446 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
@@ -59,6 +59,10 @@ public class Math
_val1 = val1;
_val2 = val2;
_op = op;
+ if (op == ROUND) {
+ _val1.setImplicitType(Double.class);
+ _val2.setImplicitType(Integer.class);
+ }
}
public Val getVal1() {
diff --git
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
index 958bb024d..ccc6aaf87 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Val.java
@@ -80,6 +80,8 @@ public interface Val
int FLOOR_VAL = 21;
int LN_VAL = 22;
int SIGN_VAL = 23;
+ int EXTRACTDTF_VAL = 24;
+ int EXTRACTDTP_VAL = 25;
/**
* Initialize the value. This method should recursively initialize any
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 9dbba34c9..537c6a286 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
@@ -298,6 +298,7 @@ public class DBDictionary
public String currentTimeFunction = "CURRENT_TIME";
public String currentTimestampFunction = "CURRENT_TIMESTAMP";
public String dropTableSQL = "DROP TABLE {0}";
+ public String extractDateTimeFieldFunction = "EXTRACT({0} FROM {1})";
// types
public boolean storageLimitationsFatal = false;
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CurrentTemporal.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CurrentTemporal.java
new file mode 100644
index 000000000..a44431546
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CurrentTemporal.java
@@ -0,0 +1,62 @@
+/*
+ * 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.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.Temporal;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Represents the current temporal.
+ *
+ */
+class CurrentTemporal
+ extends Val {
+
+ private static final long serialVersionUID = 1L;
+ private final Class<? extends Temporal> _type;
+
+ public CurrentTemporal(Class<? extends Temporal> type) {
+ _type = type;
+ }
+
+ @Override
+ public Class getType() {
+ return _type;
+ }
+
+ @Override
+ public void setImplicitType(Class type) {
+ }
+
+ @Override
+ protected Object eval(Object candidate, Object orig, StoreContext ctx,
Object[] params) {
+ if (LocalDateTime.class.isAssignableFrom(_type)) {
+ return LocalDateTime.now();
+ } else if (LocalDate.class.isAssignableFrom(_type)) {
+ return LocalDate.now();
+ } else if (LocalTime.class.isAssignableFrom(_type)) {
+ return LocalTime.now();
+ }
+ return null;
+ }
+}
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractField.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractField.java
new file mode 100644
index 000000000..94db6b6f7
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractField.java
@@ -0,0 +1,75 @@
+/*
+ * 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.time.temporal.ChronoField;
+
+/**
+ * Type identifiers used by EXTRACT function
+ */
+public enum DateTimeExtractField {
+
+ /**
+ * Means the calendar year
+ */
+ YEAR(ChronoField.YEAR),
+ /**
+ * Means the calendar quarter, numbered from 1 to 4
+ */
+ QUARTER,
+ /**
+ * Means the calendar month of the year, numbered from 1
+ */
+ MONTH(ChronoField.MONTH_OF_YEAR),
+ /**
+ * Means the ISO-8601 week number
+ */
+ WEEK(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR),
+ /**
+ * Means the calendar day of the month, numbered from 1
+ */
+ DAY(ChronoField.DAY_OF_MONTH),
+ /**
+ * Means the hour of the day in 24-hour time, numbered from 0 to 23
+ */
+ HOUR(ChronoField.HOUR_OF_DAY),
+ /**
+ * Mans the minute of the hour, numbered from 0 to 59
+ */
+ MINUTE(ChronoField.MINUTE_OF_HOUR),
+ /**
+ * Means the second of the minute, numbered from 0 to 59, including a
fractional part representing fractions of a second.
+ */
+ SECOND(ChronoField.SECOND_OF_MINUTE);
+
+ private final ChronoField equivalent;
+
+ private DateTimeExtractField() {
+ this.equivalent = null;
+ }
+
+ private DateTimeExtractField(ChronoField equivalent) {
+ this.equivalent = equivalent;
+ }
+
+ public ChronoField getEquivalent() {
+ return equivalent;
+ }
+
+}
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractPart.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractPart.java
new file mode 100644
index 000000000..91d6558a6
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/DateTimeExtractPart.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Date and time identifiers used by EXTRACT DATETIME PART
+ */
+public enum DateTimeExtractPart {
+
+ /**
+ * Means the date
+ */
+ DATE,
+ /**
+ * Means the time
+ */
+ TIME;
+
+}
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 38f433935..ded5ab1cf 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
@@ -18,6 +18,8 @@
*/
package org.apache.openjpa.kernel.exps;
+import java.time.temporal.ChronoField;
+import java.time.temporal.Temporal;
import java.util.Date;
import org.apache.openjpa.meta.ClassMetaData;
@@ -242,6 +244,21 @@ public interface ExpressionFactory {
*/
<T extends Date> Value getCurrentTimestamp(Class<T> timestampType);
+ /**
+ * Return a value representing the current local temporal.
+ */
+ <T extends Temporal> Value getCurrentLocalDateTime(Class<T> temporalType);
+
+ /**
+ * Returns the integer or double value of the required ChronoField from
the temporal value
+ */
+ Value getDateTimeField(DateTimeExtractField field, Value value);
+
+ /**
+ * Return the Date or time part of the given temporal value
+ */
+ Value getDateTimePart(DateTimeExtractPart part, Value value);
+
/**
* Return a value representing a parameter for the given value. The
* type may be <code>Object</code> if the parameter is not declared.
diff --git
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExtractDateTimeField.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExtractDateTimeField.java
new file mode 100644
index 000000000..33c25b284
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExtractDateTimeField.java
@@ -0,0 +1,100 @@
+/*
+ * 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.temporal.ChronoField;
+import java.time.temporal.Temporal;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Extract the field value of a temporal type
+ *
+ */
+class ExtractDateTimeField
+ extends Val {
+
+
+ private static final long serialVersionUID = 1L;
+ private final DateTimeExtractField _field;
+ private final Val _val;
+
+ /**
+ * Constructor. Provide target field and the value.
+ */
+ public ExtractDateTimeField(DateTimeExtractField field, Val val) {
+ _field = field;
+ _val = val;
+ }
+
+ @Override
+ public Class getType() {
+ if (_field == DateTimeExtractField.SECOND) {
+ return float.class;
+ } else {
+ return int.class;
+ }
+ }
+
+ @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);
+ Temporal t = null;
+ if (Date.class.isAssignableFrom(r.getClass())) {
+ t = ((Date) r).toLocalDate();
+ } else if (Time.class.isAssignableFrom(r.getClass())) {
+ t = ((Time) r).toLocalTime();
+ } else if (Timestamp.class.isAssignableFrom(r.getClass())) {
+ t = ((Timestamp) r).toInstant();
+ } else if (Temporal.class.isAssignableFrom(r.getClass())) {
+ t = (Temporal) r;
+ }
+ if (t == null) {
+ throw new IllegalArgumentException();
+ }
+ switch (_field) {
+ case QUARTER:
+ int month = t.get(ChronoField.MONTH_OF_YEAR);
+ return (int) Math.round(Math.ceil(month/3f));
+ case SECOND:
+ int seconds = t.get(ChronoField.SECOND_OF_MINUTE);
+ int mili = t.get(ChronoField.MILLI_OF_SECOND);
+ return (float) seconds + (float) (mili/1000f);
+ default:
+ return t.get(_field.getEquivalent());
+ }
+ }
+
+ @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/ExtractDateTimePart.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExtractDateTimePart.java
new file mode 100644
index 000000000..86cea17a9
--- /dev/null
+++
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExtractDateTimePart.java
@@ -0,0 +1,112 @@
+/*
+ * 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 ExtractDateTimePart
+ extends Val {
+
+
+ private static final long serialVersionUID = 1L;
+ private final DateTimeExtractPart _part;
+ private final Val _val;
+
+ /**
+ * Constructor. Provide target field and the value.
+ */
+ public ExtractDateTimePart(DateTimeExtractPart part, Val val) {
+ _part = part;
+ _val = val;
+ }
+
+ @Override
+ public Class getType() {
+ if (_part == DateTimeExtractPart.TIME) {
+ return Time.class;
+ } else if (_part == DateTimeExtractPart.DATE) {
+ return Date.class;
+ }
+ throw new IllegalStateException();
+ }
+
+ @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 (_part == DateTimeExtractPart.TIME) {
+ if (Time.class.isAssignableFrom(clazz)) {
+ return (Time) r;
+ } else if (Timestamp.class.isAssignableFrom(clazz)) {
+ return Time.valueOf(((Timestamp)
r).toLocalDateTime().toLocalTime());
+ } else if (LocalDateTime.class.isAssignableFrom(clazz)) {
+ return Time.valueOf(((LocalDateTime) r).toLocalTime());
+ } else if (LocalTime.class.isAssignableFrom(clazz)) {
+ return Time.valueOf((LocalTime) r);
+ } else if (Instant.class.isAssignableFrom(clazz)) {
+ LocalDateTime ldt = LocalDateTime.ofInstant((Instant) r,
ZoneId.systemDefault());
+ return Time.valueOf(ldt.toLocalTime());
+ }
+ } else if (_part == DateTimeExtractPart.DATE) {
+ if (Date.class.isAssignableFrom(clazz)) {
+ return (Date) r;
+ } else if (Timestamp.class.isAssignableFrom(clazz)) {
+ return Date.valueOf(((Timestamp)
r).toLocalDateTime().toLocalDate());
+ } else if (LocalDateTime.class.isAssignableFrom(clazz)) {
+ return Date.valueOf(((LocalDateTime) r).toLocalDate());
+ } else if (LocalDate.class.isAssignableFrom(clazz)) {
+ return Date.valueOf((LocalDate) r);
+ } else if (Instant.class.isAssignableFrom(clazz)) {
+ LocalDateTime ldt = LocalDateTime.ofInstant((Instant) r,
ZoneId.systemDefault());
+ return Date.valueOf(ldt.toLocalDate());
+ }
+ }
+ 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/InMemoryExpressionFactory.java
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
index 00a399d8c..e58a737f6 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
@@ -18,6 +18,7 @@
*/
package org.apache.openjpa.kernel.exps;
+import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -539,6 +540,21 @@ public class InMemoryExpressionFactory
return new CurrentDate(dateType);
}
+ @Override
+ public <T extends Temporal> Value getCurrentLocalDateTime(Class<T>
temporalType) {
+ return new CurrentTemporal(temporalType);
+ }
+
+ @Override
+ public Value getDateTimeField(DateTimeExtractField field, Value value) {
+ return new ExtractDateTimeField(field, (Val) value);
+ }
+
+ @Override
+ public Value getDateTimePart(DateTimeExtractPart part, Value value) {
+ return new ExtractDateTimePart(part, (Val) value);
+ }
+
@Override
public Parameter newParameter(Object name, Class type) {
return new Param(name, type);
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 68c71456e..f93c60345 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
@@ -25,6 +25,10 @@ import java.math.BigDecimal;
import java.security.AccessController;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -47,6 +51,8 @@ import org.apache.openjpa.kernel.ResultShape;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.kernel.exps.AbstractExpressionBuilder;
import org.apache.openjpa.kernel.exps.Context;
+import org.apache.openjpa.kernel.exps.DateTimeExtractField;
+import org.apache.openjpa.kernel.exps.DateTimeExtractPart;
import org.apache.openjpa.kernel.exps.Expression;
import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.Literal;
@@ -1460,6 +1466,51 @@ public class JPQLExpressionBuilder
case JJTCURRENTTIMESTAMP:
return factory.getCurrentTimestamp(Timestamp.class);
+ case JJTLOCALDATETIME:
+ return factory.getCurrentLocalDateTime(LocalDateTime.class);
+
+ case JJTLOCALDATE:
+ return factory.getCurrentLocalDateTime(LocalDate.class);
+
+ case JJTLOCALTIME:
+ return factory.getCurrentLocalDateTime(LocalTime.class);
+
+ case JJTYEAR:
+ return DateTimeExtractField.YEAR;
+
+ case JJTQUARTER:
+ return DateTimeExtractField.QUARTER;
+
+ case JJTMONTH:
+ return DateTimeExtractField.MONTH;
+
+ case JJTWEEK:
+ return DateTimeExtractField.WEEK;
+
+ case JJTDAY:
+ return DateTimeExtractField.DAY;
+
+ case JJTHOUR:
+ return DateTimeExtractField.HOUR;
+
+ case JJTMINUTE:
+ return DateTimeExtractField.MINUTE;
+
+ case JJTSECOND:
+ return DateTimeExtractField.SECOND;
+
+ case JJTDATE:
+ return DateTimeExtractPart.DATE;
+
+ case JJTTIME:
+ return DateTimeExtractPart.TIME;
+
+ case JJTEXTRACTDATETIMEFIELD:
+ return factory.getDateTimeField((DateTimeExtractField)
eval(firstChild(node)), getValue(secondChild(node)));
+
+ case JJTEXTRACTDATETIMEPART:
+ return factory.getDateTimePart((DateTimeExtractPart)
eval(firstChild(node)), getValue(secondChild(node)));
+
case JJTSELECTEXTENSION:
assertQueryExtensions("SELECT");
return eval(onlyChild(node));
@@ -2526,5 +2577,14 @@ public class JPQLExpressionBuilder
}
}
}
+
+ private DateTimeExtractField resolveDateTimeExtractFieldType(JPQLNode
node) {
+ String value = node.text;
+ try {
+ return DateTimeExtractField.valueOf(value.toUpperCase());
+ } catch (IllegalArgumentException ex) {
+ return null;
+ }
+ }
}
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 a14aec2ea..a746ceffe 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
@@ -168,6 +168,8 @@ TOKEN [ IGNORE_CASE ]: /* basics */
| < VALUE: "VALUE" >
| < TYPE: "TYPE" >
| < ENTRY: "ENTRY" >
+ | < LOCAL: "LOCAL" >
+ | < EXTRACT: "EXTRACT" >
}
TOKEN [ IGNORE_CASE ]: /* aggregates */
@@ -221,12 +223,33 @@ TOKEN [ IGNORE_CASE ]: /* functions returning numerics */
| < INDEX: "INDEX" >
}
+TOKEN [ IGNORE_CASE ]: /* datetime fields */
+{
+ < YEAR: "YEAR" >
+ | < QUARTER: "QUARTER" >
+ | < MONTH: "MONTH" >
+ | < WEEK: "WEEK" >
+ | < DAY: "DAY" >
+ | < HOUR: "HOUR" >
+ | < MINUTE: "MINUTE" >
+ | < SECOND: "SECOND" >
+}
+
+TOKEN [ IGNORE_CASE ]: /* datetime part */
+{
+ < DATE: "DATE" >
+ | < TIME: "TIME" >
+}
+
TOKEN [ IGNORE_CASE ]: /* functions returning datetime */
{
< CURRENT_DATE: "CURRENT_DATE" >
| < CURRENT_TIME: "CURRENT_TIME" >
| < CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" >
+ | < LOCAL_DATETIME: <LOCAL> (" ")+ "DATETIME" >
+ | < LOCAL_DATE: <LOCAL> (" ")+ "DATE" >
+ | < LOCAL_TIME: <LOCAL> (" ")+ "TIME" >
}
TOKEN [ IGNORE_CASE ]: /* type of query */
@@ -933,14 +956,14 @@ void datetime_comp() : { }
void scalar_function() : { }
{
- functions_returning_numerics()
- | functions_returning_datetime()
+ LOOKAHEAD(2) functions_returning_numerics()
+ | LOOKAHEAD(2) functions_returning_datetime()
| functions_returning_strings()
}
void arithmetic_value() : { }
{
- path() | functions_returning_numerics() | "(" subquery() ")"
+ path() | LOOKAHEAD(2) functions_returning_numerics() | "(" subquery()
")"
}
@@ -1240,7 +1263,7 @@ void trim_specification() : { }
void functions_returning_numerics() : { }
{
- length() | locate() | abs() | ceiling() | exp() | floor() | ln() |
sign() | power() | round() | sqrt() | mod() | size() | index()
+ LOOKAHEAD(2) length() | locate() | abs() | ceiling() | exp() | floor()
| ln() | sign() | power() | round() | sqrt() | mod() | size() | index() |
extract_datetime_field()
}
@@ -1332,11 +1355,38 @@ void index() #INDEX : { }
<INDEX> "(" identification_variable() ")"
}
+void datetime_field() : { }
+{
+ ( <YEAR> #YEAR | <QUARTER> #QUARTER | <MONTH> #MONTH | <WEEK> #WEEK |
<DAY> #DAY | <HOUR> #HOUR | <MINUTE> #MINUTE | <SECOND> #SECOND )
+}
+
+void extract_datetime_field() #EXTRACTDATETIMEFIELD : {}
+{
+ <EXTRACT> "(" datetime_field() <FROM> datetime_expression() ")"
+
+}
+
+void datetime_part() : {}
+{
+ ( <DATE> #DATE | <TIME> #TIME )
+
+}
+
+void extract_datetime_part() #EXTRACTDATETIMEPART : {}
+{
+ <EXTRACT> "(" datetime_part() <FROM> datetime_expression() ")"
+
+}
+
void functions_returning_datetime() : { }
{
- (<CURRENT_DATE> #CURRENTDATE)
- | (<CURRENT_TIME> #CURRENTTIME)
- | (<CURRENT_TIMESTAMP> #CURRENTTIMESTAMP)
+ LOOKAHEAD(2) <CURRENT_DATE> #CURRENTDATE
+ | <CURRENT_TIME> #CURRENTTIME
+ | <CURRENT_TIMESTAMP> #CURRENTTIMESTAMP
+ | <LOCAL_DATETIME> #LOCALDATETIME
+ | <LOCAL_DATE> #LOCALDATE
+ | <LOCAL_TIME> #LOCALTIME
+ | extract_datetime_part()
}
@@ -1437,6 +1487,20 @@ void path_component() #IDENTIFICATIONVARIABLE :
| t = <CURRENT_DATE>
| t = <CURRENT_TIME>
| t = <CURRENT_TIMESTAMP>
+ | t = <LOCAL_DATETIME>
+ | t = <LOCAL_TIME>
+ | t = <LOCAL_DATE>
+ | t = <LOCAL>
+ | t = <DATE>
+ | t = <TIME>
+ | t = <YEAR>
+ | t = <QUARTER>
+ | t = <MONTH>
+ | t = <WEEK>
+ | t = <DAY>
+ | t = <HOUR>
+ | t = <MINUTE>
+ | t = <SECOND>
| t = <SELECT>
| t = <DISTINCT>
| t = <FROM>
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
new file mode 100644
index 000000000..3b9fb2109
--- /dev/null
+++
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
@@ -0,0 +1,74 @@
+/*
+ * 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.jpql;
+
+import static org.junit.Assert.*;
+
+import org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder.JPQLNode;
+import org.junit.Test;
+
+public class TestJPQLParser {
+
+ @Test
+ public void testSimpleJPQLExtractFieldFromPath() {
+ try {
+ String query = "SELECT a FROM Usuario AS a where (extract(year
from a.dateOfBirth) - 2000) < 25";
+ JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+ assertNotNull(node);
+ } catch (ParseException ex) {
+ fail();
+ }
+ }
+
+ @Test
+ public void testSimpleJPQLExtractFieldFromDate() {
+ try {
+ String query = "SELECT a FROM Usuario AS a where extract (DAY from
{d '2005-04-13'}) = 10";
+ JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+ assertNotNull(node);
+ } catch (ParseException ex) {
+ ex.printStackTrace();
+ fail();
+ }
+ }
+
+ @Test
+ public void testJPQL() {
+ try {
+ String query = "SELECT c FROM CompUser AS u WHERE EXTRACT (YEAR
FROM {d '2006-03-21'}) > 2005";
+ assertNotNull(new JPQL(query).parseQuery());
+ } catch (ParseException ex) {
+ ex.printStackTrace();
+ fail();
+ }
+ }
+
+ @Test
+ public void testSimpleJPQLExtractPart() {
+ try {
+ String query = "SELECT a FROM Usuario AS a where extract(date from
a.dateOfBirth) = {d '2025-07-12'}";
+ 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/compat/TestContainerSpecCompatibilityOptions.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
index a391fbf2a..e73734d9e 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestContainerSpecCompatibilityOptions.java
@@ -421,7 +421,7 @@ public class TestContainerSpecCompatibilityOptions
em.getTransaction().commit();
// on some databases KEY is a forbidden name for columns.
- String keyColumn =
getDbDictioary(emf).getInvalidColumnWordSet().contains("KEY")
+ String keyColumn =
getDbDictionary(emf).getInvalidColumnWordSet().contains("KEY")
? "KEY0"
: "KEY";
assertSQLFragnments(sql, "CREATE TABLE C_U1M_Map_FK",
"Uni1MFK_ID", keyColumn);
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
index 53b50ba81..1dcf79041 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
@@ -412,7 +412,7 @@ extends AbstractCachedEMFTestCase {
em.getTransaction().commit();
// on some databases KEY is a forbidden name for columns.
- String keyColumn =
getDbDictioary(emf).getInvalidColumnWordSet().contains("KEY")
+ String keyColumn =
getDbDictionary(emf).getInvalidColumnWordSet().contains("KEY")
? "KEY0"
: "KEY";
assertSQLFragnments(sql, "CREATE TABLE C_U1M_Map_FK",
"Uni1MFK_ID", keyColumn);
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypesafeCriteria.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypesafeCriteria.java
index 80ddda975..aca4d1bcc 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypesafeCriteria.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestTypesafeCriteria.java
@@ -22,6 +22,9 @@ import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -1656,7 +1659,65 @@ public class TestTypesafeCriteria extends CriteriaTest {
}
-// public void testInMemoryAccessPath() {
+ public void testBasicLocalDateTime() {
+ em.getTransaction().begin();
+ Order pc = new Order();
+ em.persist(pc);
+ em.getTransaction().commit();
+
+ int oid = pc.getId();
+
+ CriteriaQuery<LocalDateTime> cquery =
cb.createQuery(LocalDateTime.class);
+ Root<Order> order = cquery.from(Order.class);
+ cquery.select(cb.localDateTime());
+ cquery.where(cb.equal(order.get(Order_.id), oid));
+
+ TypedQuery<LocalDateTime> tq = em.createQuery(cquery);
+ Object result = tq.getSingleResult();
+ assertTrue(result.getClass() + " not instance of LocalDateTime",
result instanceof LocalDateTime);
+
+ }
+
+ public void testBasicLocalTime() {
+ em.getTransaction().begin();
+ Order pc = new Order();
+ em.persist(pc);
+ em.getTransaction().commit();
+
+ int oid = pc.getId();
+
+ CriteriaQuery<LocalTime> cquery = cb.createQuery(LocalTime.class);
+ Root<Order> order = cquery.from(Order.class);
+ cquery.select(cb.localTime());
+ cquery.where(cb.equal(order.get(Order_.id), oid));
+
+ TypedQuery<LocalTime> tq = em.createQuery(cquery);
+ Object result = tq.getSingleResult();
+ assertTrue(result.getClass() + " not instance of LocalTime", result
instanceof LocalTime);
+
+ }
+
+ public void testBasicLocalDate() {
+ em.getTransaction().begin();
+ Order pc = new Order();
+ em.persist(pc);
+ em.getTransaction().commit();
+
+ int oid = pc.getId();
+
+ CriteriaQuery<LocalDate> cquery = cb.createQuery(LocalDate.class);
+ Root<Order> order = cquery.from(Order.class);
+ cquery.select(cb.localDate());
+ cquery.where(cb.equal(order.get(Order_.id), oid));
+
+ TypedQuery<LocalDate> tq = em.createQuery(cquery);
+ Object result = tq.getSingleResult();
+ assertTrue(result.getClass() + " not instance of LocalDate", result
instanceof LocalDate);
+
+ }
+
+
+ // public void testInMemoryAccessPath() {
// em.getTransaction().begin();
// // must have new/dirty managed instances to exercise the code path
// em.persist(new Customer());
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 845f84276..35d652292 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
@@ -19,6 +19,9 @@
package org.apache.openjpa.persistence.jpql.functions;
import java.math.BigDecimal;
+import java.sql.Time;
+import java.time.LocalTime;
+import java.time.temporal.ChronoField;
import java.util.List;
import jakarta.persistence.EntityManager;
@@ -596,13 +599,13 @@ public class TestEJBQLFunction extends AbstractTestCase {
public void testROUNDFunc() {
EntityManager em = currentEntityManager();
- String query = "SELECT ROUND(SQRT(MIN(c.age)), 3) FROM CompUser c";
+ String query = "SELECT ROUND(SUM(c.age)/7.0, 3) FROM CompUser c";
List result = em.createQuery(query).getResultList();
assertNotNull(result);
assertEquals(1, result.size());
- assertEquals(3.162, (double) result.get(0));
+ assertEquals(21.857, (double) result.get(0));
endEm(em);
}
@@ -635,6 +638,232 @@ public class TestEJBQLFunction extends AbstractTestCase {
endEm(em);
}
+ public void testExtractDateFromInstant() {
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT(DATE FROM
{ts '2005-03-21 01:32:20'}) > {d '2005-02-10'}";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractTimeFromInstant() {
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT(TIME FROM
{ts '2005-03-21 01:32:20'}) = {t '01:32:20'}";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractDateFromLocalDateTime() {
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT(DATE FROM
LOCAL DATETIME) > {d '2025-01-10'}";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractTimeFromLocalTime() {
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT(TIME FROM
LOCAL TIME) = {t '01:32:20'}";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ endEm(em);
+ }
+
+ public void testExtractYear() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (YEAR FROM
{d '2006-03-21'}) > 2005";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractBirthYear() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT EXTRACT(YEAR FROM {d '2025-01-23'}) - c.age
FROM CompUser AS c";
+
+ List result = em.createQuery(query).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ assertEquals(1989, (int) result.get(0));
+ assertEquals(1989, (int) result.get(1));
+ assertEquals(2006, (int) result.get(2));
+ assertEquals(2015, (int) result.get(3));
+ assertEquals(1996, (int) result.get(4));
+ assertEquals(2002, (int) result.get(5));
+ endEm(em);
+ }
+
+ public void testExtractQUARTER() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (QUARTER
FROM {d '2006-03-21'}) = 2";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ endEm(em);
+ }
+
+ public void testExtractMONTH() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (MONTH FROM
{d '2006-03-21'}) <= 3";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractWEEK() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (WEEK FROM
{d '2006-03-21'}) <= 12";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractDAY() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (DAY FROM
{ts '2006-03-21 18:19:23'}) = 21";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractHOUR() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT (HOUR FROM
{ts '2006-03-21 18:19:23'}) <> 18";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ endEm(em);
+ }
+
+ public void testExtractMINUTE() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT c FROM CompUser AS c WHERE EXTRACT(MINUTE FROM
{ts '2006-03-21 18:19:23'}) = 19";
+
+ List<CompUser> result = em.createQuery(query,
CompUser.class).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ endEm(em);
+ }
+
+ public void testExtractSECOND() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+
+ String query = "SELECT EXTRACT(SECOND FROM {ts '2006-03-21 18:19:23'})
- c.age FROM CompUser AS c ORDER BY c.age";
+
+ List result = em.createQuery(query).getResultList();
+
+ assertNotNull(result);
+ assertEquals(6, result.size());
+ assertEquals(13f, (float) result.get(0));
+ assertEquals(4f, (float) result.get(1));
+ assertEquals(0f, (float) result.get(2));
+ assertEquals(-6f, (float) result.get(3));
+ assertEquals(-13f, (float) result.get(4));
+ assertEquals(-13f, (float) result.get(5));
+ endEm(em);
+ }
+
+ public void testExtractHourFromLocalTime() {
+ if (getDbDictionary(getEmf()) instanceof DerbyDictionary) {
+ // Derby does not support EXTRACT
+ return;
+ }
+ EntityManager em = currentEntityManager();
+ String query = "SELECT CURRENT_TIME, (EXTRACT(HOUR FROM LOCAL TIME) -
c.age) FROM CompUser as c WHERE c.age = 23";
+
+ List result = em.createQuery(query).getResultList();
+
+ assertEquals(1, result.size());
+ Object[] ret = (Object[]) result.get(0);
+ assertEquals(2, ret.length);
+ Time time = (Time) ret[0];
+ LocalTime serverTime = time.toLocalTime();
+ int expected = serverTime.get(ChronoField.HOUR_OF_DAY) - 23;
+
+ assertEquals(expected, (int) ret[1]);
+
+ endEm(em);
+ }
+
public CompUser createUser(String name, String cName, Address add, int age,
boolean isMale) {
CompUser user = null;
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
index 0b75bacdd..a22fec797 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
@@ -199,4 +199,34 @@ public class TestJava8TimeTypes extends SingleEMFTestCase {
em.close();
}
+ public void testGetCurrentLocalDate() {
+ EntityManager em = emf.createEntityManager();
+ final TypedQuery<Java8TimeTypes> qry
+ = em.createQuery("select j from Java8TimeTypes AS j where
j.localDateField < LOCAL DATE", Java8TimeTypes.class);
+ final List<Java8TimeTypes> times = qry.getResultList();
+ assertNotNull(times);
+ assertTrue(!times.isEmpty());
+ em.close();
+ }
+
+ public void testGetCurrentLocalDateTime() {
+ EntityManager em = emf.createEntityManager();
+ final TypedQuery<Java8TimeTypes> qry
+ = em.createQuery("select j from Java8TimeTypes AS j where
j.localDateTimeField < LOCAL DATETIME", Java8TimeTypes.class);
+ final List<Java8TimeTypes> times = qry.getResultList();
+ assertNotNull(times);
+ assertTrue(!times.isEmpty());
+ em.close();
+ }
+
+ public void testGetCurrentLocalTime() {
+ EntityManager em = emf.createEntityManager();
+ final TypedQuery<Java8TimeTypes> qry
+ = em.createQuery("select j from Java8TimeTypes AS j where
j.localTimeField < LOCAL TIME", Java8TimeTypes.class);
+ final List<Java8TimeTypes> times = qry.getResultList();
+ assertNotNull(times);
+ assertTrue(!times.isEmpty());
+ em.close();
+ }
+
}
diff --git
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
index a11067b89..6f11cb527 100644
---
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
+++
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
@@ -358,7 +358,7 @@ public abstract class AbstractPersistenceTestCase extends
TestCase {
}
}
- protected DBDictionary getDbDictioary(EntityManagerFactory emf) {
+ protected DBDictionary getDbDictionary(EntityManagerFactory emf) {
return ((JDBCConfiguration)((OpenJPAEntityManagerFactory)
emf).getConfiguration()).getDBDictionaryInstance();
}
diff --git
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilderImpl.java
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilderImpl.java
index c9c0e2bf0..68d2e89c4 100644
---
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilderImpl.java
+++
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaBuilderImpl.java
@@ -1030,20 +1030,17 @@ public class CriteriaBuilderImpl implements
OpenJPACriteriaBuilder, ExpressionPa
@Override
public Expression<LocalDate> localDate() {
- // TODO Implement localDate
- throw new UnsupportedOperationException();
+ return new Expressions.CurrentLocalDate();
}
@Override
public Expression<LocalDateTime> localDateTime() {
- // TODO Implement ceiling op
- throw new UnsupportedOperationException();
+ return new Expressions.CurrentLocalDateTime();
}
@Override
public Expression<LocalTime> localTime() {
- // TODO Implement localTime op
- throw new UnsupportedOperationException();
+ return new Expressions.CurrentLocalTime();
}
}
diff --git
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java
index e8502a5b0..7cb4f23ba 100644
---
a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java
+++
b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/Expressions.java
@@ -21,6 +21,9 @@ package org.apache.openjpa.persistence.criteria;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -979,6 +982,54 @@ class Expressions {
}
}
+ public static class CurrentLocalDateTime extends
ExpressionImpl<java.time.LocalDateTime> {
+ public CurrentLocalDateTime() {
+ super(java.time.LocalDateTime.class);
+ }
+
+ @Override
+ public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?>
q) {
+ return factory.getCurrentLocalDateTime(LocalDateTime.class);
+ }
+
+ @Override
+ public StringBuilder asValue(AliasContext q) {
+ return new StringBuilder("LOCAL DATETIME");
+ }
+ }
+
+ public static class CurrentLocalDate extends
ExpressionImpl<java.time.LocalDate> {
+ public CurrentLocalDate() {
+ super(java.time.LocalDate.class);
+ }
+
+ @Override
+ public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?>
q) {
+ return factory.getCurrentLocalDateTime(LocalDate.class);
+ }
+
+ @Override
+ public StringBuilder asValue(AliasContext q) {
+ return new StringBuilder("LOCAL DATE");
+ }
+ }
+
+ public static class CurrentLocalTime extends
ExpressionImpl<java.time.LocalTime> {
+ public CurrentLocalTime() {
+ super(java.time.LocalTime.class);
+ }
+
+ @Override
+ public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?>
q) {
+ return factory.getCurrentLocalDateTime(LocalTime.class);
+ }
+
+ @Override
+ public StringBuilder asValue(AliasContext q) {
+ return new StringBuilder("LOCAL TIME");
+ }
+ }
+
public static class Equal extends BinaryLogicalExpression {
public <X,Y> Equal(Expression<X> x, Expression<Y> y) {
super(x,y);
diff --git a/openjpa-project/src/doc/manual/jpa_overview_query.xml
b/openjpa-project/src/doc/manual/jpa_overview_query.xml
index f681f7374..edfffdc39 100644
--- a/openjpa-project/src/doc/manual/jpa_overview_query.xml
+++ b/openjpa-project/src/doc/manual/jpa_overview_query.xml
@@ -747,7 +747,65 @@ SELECT w.name FROM Course c JOIN c.studentWaitlist w WHERE
c.name = ‘Calculus
<literal>CURRENT_TIMESTAMP</literal>: Returns the current timestamp.
</para>
</listitem>
- </itemizedlist>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary>
+ LOCAL DATE function
+ </primary>
+ </indexterm>
+<literal>LOCAL DATE</literal>: returns the value of current date on the
database server.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary>
+ LOCAL TIME function
+ </primary>
+ </indexterm>
+<literal>LOCAL TIME</literal>: returns the value of current time on the
database server.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary>
+ LOCAL DATETIME function
+ </primary>
+ </indexterm>
+<literal>LOCAL DATETIME</literal>: returns the value of current timestamp on
the database server.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary>
+ EXTRACT date field function
+ </primary>
+ </indexterm>
+<literal>EXTRACT</literal>(date_field <literal>FROM</literal> date_expression)
: extracts a given field
+(year, quarter, month, week, day, hour, minute or second) from a datetime.
+ </para>
+<programlisting>
+SELECT c FROM Customer AS c WHERE EXTRACT(YEAR FROM c.birthDate) <
(EXTRACT(YEAR FROM CURRENT_DATE) - 13)
+</programlisting>
+ </listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary>
+ EXTRACT date part function
+ </primary>
+ </indexterm>
+<literal>EXTRACT</literal>(date_part <literal>FROM</literal> date_expression)
: extracts a date part
+(date or time) from a datetime.
+ </para>
+<programlisting>
+SELECT c FROM Customer AS c WHERE EXTRACT(DATE FROM c.createdTimestamp) < { d
'1969-07-20' }
+</programlisting>
+ </listitem>
+ </itemizedlist>
</section>
<section id="jpa_overview_query_inheritance">
<title>
@@ -1637,6 +1695,11 @@ query language. The following are reserved identifiers:
<para>
<literal>BETWEEN</literal>
</para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>BIT_LENGTH</literal>
+ </para>
</listitem>
<listitem>
<para>
@@ -1655,6 +1718,21 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>CEILING</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>CHAR_LENGTH</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>CHARACTER_LENGTH</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>CLASS</literal>
</para>
</listitem>
@@ -1731,10 +1809,20 @@ query language. The following are reserved identifiers:
<listitem>
<para>
<literal>EXISTS</literal>
- </para>
- </listitem>
- <listitem>
- <para>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>EXP</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>EXTRACT</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>FALSE</literal>
</para>
</listitem>
@@ -1745,6 +1833,11 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>FLOOR</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>FROM</literal>
</para>
</listitem>
@@ -1810,21 +1903,31 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>LOCAL</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>LN</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>LOCATE</literal>
</para>
</listitem>
<listitem>
<para>
<literal>LOWER</literal>
- </para>
- </listitem>
- <listitem>
- <para>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>MAX</literal>
- </para>
- </listitem>
- <listitem>
- <para>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>MEMBER</literal>
</para>
</listitem>
@@ -1870,6 +1973,11 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>ON</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>OR</literal>
</para>
</listitem>
@@ -1885,6 +1993,21 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>POSITION</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>POWER</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>ROUND</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>SELECT</literal>
</para>
</listitem>
@@ -1895,6 +2018,11 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>SIGN</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>SIZE</literal>
</para>
</listitem>
@@ -1930,6 +2058,11 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>TREAT</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>TRIM</literal>
</para>
</listitem>
@@ -1945,6 +2078,11 @@ query language. The following are reserved identifiers:
</listitem>
<listitem>
<para>
+<literal>UNKNOWN</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>UPDATE</literal>
</para>
</listitem>
@@ -1968,31 +2106,6 @@ query language. The following are reserved identifiers:
<literal>WHERE</literal>
</para>
</listitem>
- <listitem>
- <para>
-<literal>CHARACTER_LENGTH</literal>
- </para>
- </listitem>
- <listitem>
- <para>
-<literal>CHAR_LENGTH</literal>
- </para>
- </listitem>
- <listitem>
- <para>
-<literal>BIT_LENGTH</literal>
- </para>
- </listitem>
- <listitem>
- <para>
-<literal>POSITION</literal>
- </para>
- </listitem>
- <listitem>
- <para>
-<literal>UNKNOWN</literal>
- </para>
- </listitem>
</itemizedlist>
<para>
Reserved identifiers are case insensitive. Reserved identifiers must not be
@@ -3547,12 +3660,98 @@ which an order column has been specified.
JPQL Datetime Functions
</title>
<para>
-functions_returning_datetime:= CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP
+functions_returning_datetime:= CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP
| LOCAL DATE
+| LOCAL TIME | LOCAL DATETIME | extract_datetime_part
+ </para>
+ <para>
+The functions LOCAL DATE, LOCAL TIME, and LOCAL DATETIME return the value of
the current date, time,
+or timestamp on the database server, respectively. Their types are
java.time.LocalDate,
+java.time.LocalTime, and java.time.LocalDateTime respectively.
</para>
<para>
-The datetime functions return the value of current date, time, and timestamp on
-the database server.
+The functions CURRENT_DATE, CURRENT_TIME, and CURRENT_TIMESTAMP return the
value of the current date,
+time, or timestamp on the database server, respectively. Their types are
java.sql.Date, java.sql.Time,
+and java.sql.Timestamp respectively.
</para>
+ <para>
+The EXTRACT function takes a datetime argument and one of the following field
type identifiers: YEAR,
+QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, DATE, TIME.
+ </para>
+ <para>
+EXTRACT returns the value of the corresponding field or part of the datetime.
+ </para>
+ <para>
+For the following field type identifiers, EXTRACT returns an integer value:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+<literal>YEAR</literal> means the calendar year.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>QUARTER</literal> means the calendar quarter, numbered from 1 to 4.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>MONTH</literal> means the calendar month of the year, numbered from 1.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>WEEK</literal> means the ISO-8601 week number.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>DAY</literal> means the calendar day of the month, numbered from 1.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>HOUR</literal> means the hour of the day in 24-hour time, numbered
from 0 to 23.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>MINUTE</literal> means the minute of the hour, numbered from 0 to 59.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+For the SECOND field type identifier, EXTRACT returns a floating point value:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+<literal>SECOND</literal> means the second of the minute, numbered from 0 to
59, including a
+ fractional part representing fractions of a second.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+It is illegal to pass a datetime argument which does not have the given field
type to EXTRACT.
+ </para>
+ <para>
+For the following field type identifiers, EXTRACT returns a part of the
datetime value:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+<literal>DATE</literal> means the date part of a datetime.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+<literal>TIME</literal> means the time part of a datetime.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+It is illegal to pass a datetime argument which does not have the given part
to EXTRACT.
+ </para>
</section>
</section>
<section id="jpa_langref_case_expressions">
@@ -4540,8 +4739,28 @@ Comparisons over instances of embeddable class or map
entry types are not suppor
JPQL BNF
</title>
<para>
-The following is the BNF for the Java Persistence query language, from section
-4.14 of the JSR 317 specification.
+BNF notation summary:
+ </para>
+ <itemizedlist>
+ <listitem>
+{ ... } grouping
+ </listitem>
+ <listitem>
+[ ... ] optional constructs
+ </listitem>
+ <listitem>
+* zero or more
+ </listitem>
+ <listitem>
++ one or more
+ </listitem>
+ <listitem>
+| alternates
+ </listitem>
+ </itemizedlist>
+ <para>
+The following is the BNF for the Jakarta Persistence query language, from
section
+4.14 of the Jarkata Persistence 3.1 specification.
</para>
<itemizedlist>
<listitem>
@@ -4586,7 +4805,7 @@ identification_variable
<listitem>
<para>
join ::= join_spec join_association_path_expression [ <literal>AS</literal> ]
-identification_variable
+identification_variable [join_conition]
</para>
</listitem>
<listitem>
@@ -4600,23 +4819,28 @@ join_association_path_expression
join_spec ::= [ <literal>LEFT</literal> [ <literal>OUTER</literal> ]| <literal>
INNER</literal> ] <literal>JOIN</literal>
</para>
+ </listitem>
+ <listitem>
+ <para>
+join_condition ::= ON conditional_expression
+ </para>
</listitem>
<listitem>
<para>
join_association_path_expression ::= join_collection_valued_path_expression |
-join_single_valued_path_expression
+join_single_valued_path_expression |
<literal>TREAT</literal>(join_collection_valued_path_expression AS subtype) |
<literal>TREAT</literal>(join_single_valued_path_expression AS subtype)
</para>
</listitem>
<listitem>
<para>
join_collection_valued_path_expression ::=
-identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field
+identification_variable.{single_valued_embeddable_object_field.}*
collection_valued_field
</para>
</listitem>
<listitem>
<para>
join_single_valued_path_expression ::=
-identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field
+identification_variable.{single_valued_embeddable_object_field.}*
single_valued_object_field
</para>
</listitem>
<listitem>
@@ -4629,15 +4853,21 @@ identification_variable
<listitem>
<para>
qualified_identification_variable ::=
-KEY(identification_variable) |
-VALUE(identification_variable) |
-ENTRY(identification_variable)
+map_field_identification_variable |
<literal>ENTRY</literal>(identification_variable)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+map_field_identification_variable ::=
+<literal>KEY</literal>(identification_variable) |
+<literal>VALUE</literal>(identification_variable)
</para>
</listitem>
<listitem>
<para>
single_valued_path_expression ::=
qualified_identification_variable |
+<literal>TREAT</literal>(qualified_identification_variable
<literal>AS</literal> subtype) |
state_field_path_expression |
single_valued_object_path_expression
</para>
@@ -4646,27 +4876,46 @@ single_valued_object_path_expression
<para>
general_identification_variable ::=
identification_variable |
-KEY(identification_variable) |
-VALUE(identification_variable)
+map_field_identification_variable
</para>
</listitem>
<listitem>
<para>
-state_field_path_expression ::=
-general_identification_variable.{single_valued_object_field.}*state_field
+general_subpath ::= simple_subpath |
treated_subpath{.single_valued_object_field}*
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+simple_subpath ::=
+ general_identification_variable |
+ general_identification_variable{.single_valued_object_field}*
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+treated_subpath ::= <literal>TREAT</literal>(general_subpath AS subtype)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+state_field_path_expression ::= general_subpath.state_field
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+state_valued_path_expression ::=
+ state_field_path_expression | general_identification_variable
</para>
</listitem>
<listitem>
<para>
single_valued_object_path_expression ::=
-general_identification_variable.{single_valued_object_field.}*
-single_valued_object_field
+ general_subpath.single_valued_object_field
</para>
</listitem>
<listitem>
<para>
-collection_valued_path_expression ::=
-general_identification_variable.{single_valued_object_field.}*collection_valued_field
+collection_valued_path_expression ::= general_subpath.{collection_valued_field}
</para>
</listitem>
<listitem>
@@ -4678,15 +4927,14 @@ update_item}*
</listitem>
<listitem>
<para>
-update_item ::= [identification_variable.]{state_field |
-single_valued_object_field}= new_value
+update_item ::=
[identification_variable.]{single_valued_embeddable_object_field.}*
+ {state_field | single_valued_object_field} = new_value
</para>
</listitem>
<listitem>
<para>
new_value ::= scalar_expression |
-simple_entity_expression | <literal>NULL
-</literal>
+simple_entity_expression | <literal>NULL</literal>
</para>
</listitem>
<listitem>
@@ -4708,11 +4956,13 @@ select_item ::= select_expression [[AS] result_variable]
</listitem>
<listitem>
<para>
-select_expression ::= single_valued_path_expression |
-scalar_expression |
-aggregate_expression |
-identification_variable | <literal>OBJECT</literal> (identification_variable)|
-constructor_expression
+select_expression ::=
+ single_valued_path_expression |
+ scalar_expression |
+ aggregate_expression |
+ identification_variable |
+ <literal>OBJECT</literal> (identification_variable) |
+ constructor_expression
</para>
</listitem>
<listitem>
@@ -4735,7 +4985,7 @@ aggregate_expression ::= { <literal>AVG</literal> |
<literal>MAX</literal> |
<literal>MIN</literal> | <literal>SUM</literal> }([ <literal>DISTINCT</literal>
] state_field_path_expression) | <literal>COUNT</literal> ([ <literal>DISTINCT
</literal> ] identification_variable | state_field_path_expression |
-single_valued_object_path_expression)
+single_valued_object_path_expression) | function_invocation
</para>
</listitem>
<listitem>
@@ -4767,7 +5017,9 @@ orderby_item}*
</listitem>
<listitem>
<para>
-orderby_item ::= state_field_path_expression | result_variable [
<literal>ASC</literal> |
+orderby_item ::= state_field_path_expression |
+ general_identification_variable |
+ result_variable [ <literal>ASC</literal> |
<literal>DESC</literal> ]
</para>
</listitem>
@@ -4795,14 +5047,31 @@ identification_variable_declaration |
derived_path_expression [ <literal>AS
<listitem>
<para>
derived_path_expression ::=
-superquery_identification_variable.{single_valued_object_field.}*collection_valued_field
|
-superquery_identification_variable.{single_valued_object_field.}*single_valued_object_field
+ general_derived_path.single_valued_object_field |
+ general_derived_path.collection_valued_field
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+general_derived_path ::=
+ simple_derived_path |
+ treated_derived_path{.single_valued_object_field}*
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+simple_derived_path ::=
superquery_identification_variable{.single_valued_object_field}*
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+treated_derived_path ::= <literal>TREAT</literal>(general_derived_path AS
subtype)
</para>
</listitem>
<listitem>
<para>
derived_collection_member_declaration ::=
-IN
superquery_identification_variable.{single_valued_object_field.}*collection_valued_field
+<literal>IN</literal>
superquery_identification_variable.{single_valued_object_field.}*collection_valued_field
</para>
</listitem>
<listitem>
@@ -4820,11 +5089,11 @@ scalar_expression | aggregate_expression |
identification_variable
<listitem>
<para>
scalar_expression ::=
-simple_arithmetic_expression |
-string_primary |
-enum_primary |
-datetime_primary |
-boolean_primary |
+arithmetic_expression |
+string_expression |
+enum_expression |
+datetime_expression |
+boolean_expression |
case_expression |
entity_type_expression
</para>
@@ -4848,13 +5117,13 @@ conditional_factor ::= [ <literal>NOT</literal> ]
conditional_primary
</listitem>
<listitem>
<para>
-conditional_primary ::= simple_cond_expression |(conditional_expression)
+conditional_primary ::= simple_cond_expression | (conditional_expression)
</para>
</listitem>
<listitem>
<para>
simple_cond_expression ::= comparison_expression | between_expression |
-like_expression | in_expression | null_comparison_expression |
+in_expression | like_expression | null_comparison_expression |
empty_collection_comparison_expression | collection_member_expression |
exists_expression
</para>
@@ -4936,158 +5205,203 @@ all_or_any_expression ::= { <literal>ALL</literal> |
<literal>ANY</literal> |
<listitem>
<para>
comparison_expression ::=
-string_expressioncomparison_operator{string_expression|all_or_any_expression}|
-boolean_expression {=|<>} {boolean_expression | all_or_any_expression} |
-enum_expression {=|<>} {enum_expression | all_or_any_expression} |
+string_expression comparison_operator {string_expression |
all_or_any_expression} |
+boolean_expression {= | <>} {boolean_expression | all_or_any_expression}
|
+enum_expression {= |<>} {enum_expression | all_or_any_expression} |
datetime_expression comparison_operator {datetime_expression |
-all_or_any_expression} | entity_expression {= |<> } {entity_expression |
+all_or_any_expression} | entity_expression {= | <>} {entity_expression |
all_or_any_expression} | arithmetic_expression comparison_operator
{arithmetic_expression | all_or_any_expression} |
-entity_type_expression { =|<>>} entity_type_expression}
+entity_type_expression {= |<>} entity_type_expression}
</para>
</listitem>
<listitem>
<para>
-comparison_operator ::== |> |>= |< |<= |<>
+comparison_operator ::= = | > | >= | < | <= | <>
</para>
</listitem>
<listitem>
<para>
-arithmetic_expression ::= simple_arithmetic_expression |(subquery)
+arithmetic_expression ::= arithmetic_term | arithmetic_expression {+ | -}
arithmetic_term
</para>
</listitem>
<listitem>
<para>
-simple_arithmetic_expression ::= arithmetic_term | simple_arithmetic_expression
-{+ |- } arithmetic_term
+arithmetic_term ::= arithmetic_factor | arithmetic_term {* | /}
arithmetic_factor
</para>
</listitem>
<listitem>
<para>
-arithmetic_term ::= arithmetic_factor | arithmetic_term {* |/ }
-arithmetic_factor
+arithmetic_factor ::= [{+ | -}] arithmetic_primary
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+arithmetic_primary ::=
+ state_valued_path_expression | numeric_literal |
+ (arithmetic_expression) |
+ input_parameter |
+ functions_returning_numerics |
+ aggregate_expression |
+ case_expression |
+ function_invocation |
+ (subquery)
</para>
</listitem>
<listitem>
<para>
-arithmetic_factor ::= [{+ |-}] arithmetic_primary
+string_expression ::=
+ state_valued_path_expression |
+ string_literal |
+ input_parameter |
+ functions_returning_strings |
+ aggregate_expression |
+ case_expression |
+ function_invocation |
+ (subquery)
</para>
</listitem>
<listitem>
<para>
-arithmetic_primary ::= state_field_path_expression | numeric_literal |
-(simple_arithmetic_expression) | input_parameter | functions_returning_numerics
-| aggregate_expression |
-case_expression
+datetime_expression ::=
+ state_valued_path_expression |
+ input_parameter |
+ functions_returning_datetime |
+ aggregate_expression |
+ case_expression |
+ function_invocation |
+ date_time_timestamp_literal |
+ (subquery)
</para>
</listitem>
<listitem>
<para>
-string_expression ::= string_primary |(subquery)
+boolean_expression ::=
+ state_valued_path_expression |
+ boolean_literal |
+ input_parameter |
+ case_expression |
+ function_invocation |
+ (subquery)
</para>
</listitem>
<listitem>
<para>
-string_primary ::= state_field_path_expression | string_literal |
-input_parameter | functions_returning_strings | aggregate_expression |
-case_expression
+enum_expression ::=
+ state_valued_path_expression |
+ enum_literal |
+ input_parameter |
+ case_expression |
+ (subquery)
</para>
</listitem>
<listitem>
<para>
-datetime_expression ::= datetime_primary |(subquery)
+entity_expression ::= single_valued_object_path_expression |
+simple_entity_expression
</para>
</listitem>
<listitem>
<para>
-datetime_primary ::= state_field_path_expression | input_parameter |
-functions_returning_datetime | aggregate_expression |
-case_expression |
-date_time_timestamp_literal
+simple_entity_expression ::= identification_variable | input_parameter
</para>
</listitem>
<listitem>
<para>
-boolean_expression ::= boolean_primary |(subquery)
+entity_type_expression ::=
+type_discriminator |
+entity_type_literal |
+input_parameter
</para>
</listitem>
<listitem>
<para>
-boolean_primary ::= state_field_path_expression | boolean_literal |
-input_parameter |
-case_expression
+type_discriminator ::=
+<literal>TYPE</literal>(identification_variable |
+single_valued_object_path_expression |
+input_parameter)
</para>
</listitem>
<listitem>
<para>
-enum_expression ::= enum_primary |(subquery)
+functions_returning_numerics ::= <literal>LENGTH</literal> (string_primary)|
+<literal>LOCATE</literal> (string_primary,string_primary [,
+arithmetic_expression]) | <literal>ABS</literal>
+(arithmetic_expression) | <literal>CEILING</literal>
+(arithmetic_expression) | <literal>EXP</literal>
+(arithmetic_expression) | <literal>FLOOR</literal>
+(arithmetic_expression) | <literal>LN</literal>
+(arithmetic_expression) | <literal>SIGN</literal>
+(arithmetic_expression) | <literal>SQRT</literal>
+(arithmetic_expression) | <literal>MOD</literal>
+(arithmetic_expression, arithmetic_expression) | <literal>POWER</literal>
+(arithmetic_expression) | <literal>ROUND</literal>
+(arithmetic_expression, arithmetic_expression) | <literal>SIZE
+</literal> (collection_valued_path_expression) |
+<literal>INDEX</literal>(identification_variable) |
+extract_datetime_field
</para>
</listitem>
<listitem>
<para>
-enum_primary ::= state_field_path_expression | enum_literal | input_parameter |
-case_expression
+functions_returning_datetime ::= <literal>CURRENT_DATE</literal> | <literal>
+CURRENT_TIME</literal> | <literal>CURRENT_TIMESTAMP</literal> |
+<literal>LOCAL DATE</literal> |
+<literal>LOCAL TIME</literal> |
+<literal>LOCAL DATETIME</literal> |
+extract_datetime_part
</para>
</listitem>
<listitem>
<para>
-entity_expression ::= single_valued_object_path_expression |
-simple_entity_expression
+functions_returning_strings ::= <literal>CONCAT</literal> (string_expression,
+string_expression) | <literal>SUBSTRING</literal> (string_expression,
+arithmetic_expression[,arithmetic_expression])| <literal>TRIM
+</literal> ([[trim_specification] [trim_character] <literal>FROM</literal> ]
+string_expression) | <literal>LOWER</literal> (string_expression) |
<literal>UPPER
+</literal> (string_expression)
</para>
</listitem>
<listitem>
<para>
-simple_entity_expression ::= identification_variable | input_parameter
+trim_specification ::= <literal>LEADING</literal> | <literal>TRAILING</literal>
+| <literal>BOTH</literal>
</para>
</listitem>
<listitem>
<para>
-entity_type_expression ::=
-type_discriminator |
-entity_type_literal |
-input_parameter
+function_invocation ::= <literal>FUNCTION</literal>(function_name{,
function_arg}*)
</para>
</listitem>
<listitem>
<para>
-type_discriminator ::=
-<literal>TYPE</literal>(identification_variable |
-single_valued_object_path_expression |
-input_parameter)
+extract_datetime_field :=
+ <literal>EXTRACT</literal>(datetime_field FROM datetime_expression)
</para>
</listitem>
<listitem>
<para>
-functions_returning_numerics ::= <literal>LENGTH</literal> (string_primary)|
-<literal>LOCATE</literal> (string_primary,string_primary [,
-simple_arithmetic_expression]) | <literal>ABS</literal>
-(simple_arithmetic_expression) | <literal>SQRT</literal>
-(simple_arithmetic_expression) | <literal>MOD</literal>
-(simple_arithmetic_expression, simple_arithmetic_expression) | <literal>SIZE
-</literal> (collection_valued_path_expression) |
-<literal>INDEX</literal>(identification_variable)
+datetime_field := identification_variable
</para>
</listitem>
<listitem>
<para>
-functions_returning_datetime ::= <literal>CURRENT_DATE</literal> | <literal>
-CURRENT_TIME</literal> | <literal>CURRENT_TIMESTAMP</literal>
+extract_datetime_part :=
+ <literal>EXTRACT</literal>(datetime_part FROM datetime_expression)
</para>
</listitem>
<listitem>
<para>
-functions_returning_strings ::= <literal>CONCAT</literal> (string_primary,
-string_primary) | <literal>SUBSTRING</literal> (string_primary,
-simple_arithmetic_expression[,simple_arithmetic_expression])| <literal>TRIM
-</literal> ([[trim_specification] [trim_character] <literal>FROM</literal> ]
-string_primary) | <literal>LOWER</literal> (string_primary) | <literal>UPPER
-</literal> (string_primary)
+datetime_part := identification_variable
</para>
</listitem>
<listitem>
<para>
-trim_specification ::= <literal>LEADING</literal> | <literal>TRAILING</literal>
-| <literal>BOTH</literal>
+function_arg ::=
+ literal |
+ state_valued_path_expression |
+ input_parameter |
+ scalar_expression
</para>
</listitem>
<listitem>