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 <pcris...@gmail.com>
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 {=|&lt;&gt;} {boolean_expression | all_or_any_expression} |
-enum_expression {=|&lt;&gt;} {enum_expression | all_or_any_expression} |
+string_expression comparison_operator {string_expression | 
all_or_any_expression} | 
+boolean_expression {= | &lt;&gt;} {boolean_expression | all_or_any_expression} 
|
+enum_expression {= |&lt;&gt;} {enum_expression | all_or_any_expression} |
 datetime_expression comparison_operator {datetime_expression |
-all_or_any_expression} | entity_expression {= |&lt;&gt; } {entity_expression |
+all_or_any_expression} | entity_expression {= | &lt;&gt;} {entity_expression |
 all_or_any_expression} | arithmetic_expression comparison_operator
 {arithmetic_expression | all_or_any_expression} |
-entity_type_expression { =|&lt;&gt;>} entity_type_expression}
+entity_type_expression {= |&lt;&gt;} entity_type_expression}
                     </para>
                 </listitem>
                 <listitem>
                     <para>
-comparison_operator ::== |&gt; |&gt;= |&lt; |&lt;= |&lt;&gt;
+comparison_operator ::= = | &gt; | &gt;= | &lt; | &lt;= | &lt;&gt;
                     </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>

Reply via email to