Changed second fraction precision of LocalDateTime and OffsetDateTime MySQL related type. (#1456)

Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/MySQLPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/MySQLPlatform.java
index e7f855b..9d6d700 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/MySQLPlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/MySQLPlatform.java
@@ -215,7 +215,7 @@
         if (!isFractionalTimeSupported) {
             fd.setIsSizeAllowed(false);
         } else {
-            fd.setDefaultSize(3);
+            fd.setDefaultSize(6);
             fd.setIsSizeRequired(true);
         }
         fieldTypeMapping.put(java.time.LocalDateTime.class,fd); //no timezone info
@@ -224,7 +224,7 @@
         if (!isFractionalTimeSupported) {
             fd.setIsSizeAllowed(false);
         } else {
-            fd.setDefaultSize(3);
+            fd.setDefaultSize(6);
             fd.setIsSizeRequired(true);
         }
         fieldTypeMapping.put(java.time.LocalTime.class, fd);
@@ -233,7 +233,7 @@
         if (!isFractionalTimeSupported) {
             fd.setIsSizeAllowed(false);
         } else {
-            fd.setDefaultSize(3);
+            fd.setDefaultSize(6);
             fd.setIsSizeRequired(true);
         }
         fieldTypeMapping.put(java.time.OffsetDateTime.class, fd); //no timezone info
@@ -242,7 +242,7 @@
         if (!isFractionalTimeSupported) {
             fd.setIsSizeAllowed(false);
         } else {
-            fd.setDefaultSize(3);
+            fd.setDefaultSize(6);
             fd.setIsSizeRequired(true);
         }
         fieldTypeMapping.put(java.time.OffsetTime.class, fd);
@@ -384,6 +384,11 @@
     @Override
     protected void initializePlatformOperators() {
         super.initializePlatformOperators();
+        addOperator(currentTimeStamp());
+        addOperator(today());
+        addOperator(currentTime());
+        addOperator(localTime());
+        addOperator(localDateTime());
         addOperator(logOperator());
         addOperator(ExpressionOperator.simpleTwoArgumentFunction(ExpressionOperator.Atan2, "ATAN2"));
         addOperator(ExpressionOperator.simpleTwoArgumentFunction(ExpressionOperator.Concat, "CONCAT"));
@@ -399,6 +404,60 @@
 
     /**
      * INTERNAL:
+     * MySQL specific {@code currentTimeStamp} operator.
+     *
+     * @return new {@link ExpressionOperator} instance with {@code currentTimeStamp}
+     */
+    public static ExpressionOperator currentTimeStamp() {
+        return ExpressionOperator.simpleFunctionNoParentheses(
+                ExpressionOperator.Today,  "CURRENT_TIMESTAMP(6)");
+    }
+
+    /**
+     * INTERNAL:
+     * MySQL specific {@code today} operator.
+     *
+     * @return new {@link ExpressionOperator} instance with {@code today}
+     */
+    public static ExpressionOperator today() {
+        return currentTimeStamp();
+    }
+
+    /**
+     * INTERNAL:
+     * MySQL specific {@code currentTime} operator.
+     *
+     * @return new {@link ExpressionOperator} instance with {@code currentTime}
+     */
+    public static ExpressionOperator currentTime() {
+        return ExpressionOperator.simpleFunctionNoParentheses(
+                ExpressionOperator.CurrentTime, "CURRENT_TIME(6)");
+    }
+
+    /**
+     * INTERNAL:
+     * MySQL specific {@code localTime} operator.
+     *
+     * @return new {@link ExpressionOperator} instance with {@code localTime}
+     */
+    public static ExpressionOperator localTime() {
+        return ExpressionOperator.simpleFunctionNoParentheses(
+                ExpressionOperator.LocalTime, "CURRENT_TIME(6)");
+    }
+
+    /**
+     * INTERNAL:
+     * MySQL specific {@code localDateTime} operator.
+     *
+     * @return new {@link ExpressionOperator} instance with {@code localDateTime}
+     */
+    public static ExpressionOperator localDateTime() {
+        return ExpressionOperator.simpleFunctionNoParentheses(
+                ExpressionOperator.LocalDateTime,  "CURRENT_TIMESTAMP(6)");
+    }
+
+    /**
+     * INTERNAL:
      * Create the 10 based log operator for this platform.
      */
     protected ExpressionOperator logOperator() {
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestDateTimeFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestDateTimeFunctions.java
index d48bd9a..3a436d8 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestDateTimeFunctions.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestDateTimeFunctions.java
@@ -20,6 +20,7 @@
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.Period;
+import java.time.temporal.ChronoUnit;
 import java.util.List;
 
 import jakarta.persistence.EntityManager;
@@ -34,6 +35,8 @@
 import org.eclipse.persistence.jpa.test.framework.Emf;
 import org.eclipse.persistence.jpa.test.framework.EmfRunner;
 import org.eclipse.persistence.jpa.test.framework.Property;
+import org.eclipse.persistence.logging.AbstractSessionLog;
+import org.eclipse.persistence.logging.SessionLog;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.After;
@@ -80,6 +83,33 @@
                                                                                        //              testCriteriaQuerySelectLocalDateTime
     };
 
+    // Database vs. Java timezone offset in seconds. Must be applied to LocalDateTime calculations.
+    private long dbOffset = 0;
+
+    // Update database vs. Java timezone offset using current database time.
+    private void updateDbOffset() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<LocalTime> cq = cb.createQuery(LocalTime.class);
+            cq.select(cb.localTime());
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.where(cb.equal(entity.get("id"), 1));
+            LocalTime dbTime = em.createQuery(cq).getSingleResult();
+            LocalTime javaTime = LocalTime.now();
+            this.dbOffset = dbTime.truncatedTo(ChronoUnit.SECONDS).toSecondOfDay() - javaTime.truncatedTo(ChronoUnit.SECONDS).toSecondOfDay();
+        } catch (Throwable t) {
+            AbstractSessionLog.getLog().log(SessionLog.WARNING, "Can't update DB offset: " + t.getMessage());
+            t.printStackTrace();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+
+    }
+
     @Before
     public void setup() {
         final EntityManager em = emf.createEntityManager();
@@ -96,6 +126,7 @@
             }
             em.close();
         }
+        updateDbOffset();
     }
 
     @After
@@ -134,7 +165,7 @@
             em.getTransaction().commit();
             // Verify updated entity
             DateTimeEntity data = em.find(DateTimeEntity.class, 1);
-            long diffMilis = Duration.between(data.getTime(), LocalTime.now()).toMillis();
+            long diffMilis = Duration.between(data.getTime(), LocalTime.now().plusSeconds(dbOffset + 1)).toMillis();
             // Positive value means that test did not pass midnight.
             if (diffMilis > 0) {
                 MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
@@ -203,7 +234,7 @@
             em.getTransaction().commit();
             // Verify updated entity
             DateTimeEntity data = em.find(DateTimeEntity.class, 3);
-            long diffMilis = Duration.between(data.getDatetime(), LocalDateTime.now()).toMillis();
+            long diffMilis = Duration.between(data.getDatetime(), LocalDateTime.now().plusSeconds(dbOffset + 1)).toMillis();
             MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
         } finally {
             if (em.getTransaction().isActive()) {
@@ -370,7 +401,7 @@
             cq.where(cb.equal(entity.get("id"), 4));
             LocalTime time = em.createQuery(cq).getSingleResult();
             em.getTransaction().commit();
-            long diffMilis = Duration.between(time, LocalTime.now()).toMillis();
+            long diffMilis = Duration.between(time, LocalTime.now().plusSeconds(1 + dbOffset)).toMillis();
             // Positive value means that test did not pass midnight.
             if (diffMilis > 0) {
                 MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
@@ -424,7 +455,7 @@
             cq.where(cb.equal(entity.get("id"), 4));
             LocalDateTime datetime = em.createQuery(cq).getSingleResult();
             em.getTransaction().commit();
-            long diffMilis = Duration.between(datetime, LocalDateTime.now()).toMillis();
+            long diffMilis = Duration.between(datetime, LocalDateTime.now().plusSeconds(dbOffset + 1)).toMillis();
             MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
         } finally {
             if (em.getTransaction().isActive()) {
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java
index 6c481b0..89ca823 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java
@@ -16,6 +16,7 @@
 package org.eclipse.persistence.jpa.test.query;
 
 import java.time.LocalDateTime;
+import java.util.List;
 
 import jakarta.persistence.EntityManager;
 import jakarta.persistence.EntityManagerFactory;
@@ -52,11 +53,13 @@
     private EntityManagerFactory emf;
 
     private final LocalDateTime[] TS = {
-            LocalDateTime.of(2022, 3, 9, 14, 30, 25, 0)
+            LocalDateTime.of(2022, 3, 9, 14, 30, 25, 0),
+            LocalDateTime.now()
     };
 
     private final DateTimeQueryEntity[] ENTITY = {
-            new DateTimeQueryEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0])
+            new DateTimeQueryEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]),
+            new DateTimeQueryEntity(2, TS[1].toLocalTime(), TS[1].toLocalDate(), TS[1])
     };
 
     @Before
@@ -389,4 +392,26 @@
         }
     }
 
+    // Test LocalDateTime.now() in WHERE clause
+    // SELECT e FROM DateTimeQueryEntity e WHERE e.datetime = :dateTime
+    @Test
+    public void testLocalDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<DateTimeQueryEntity> query = em.createNamedQuery("DateTimeQueryEntity.findByLocalDateTime", DateTimeQueryEntity.class);
+            query.setParameter("dateTime", TS[1]);
+            List<DateTimeQueryEntity> result = query.getResultList();
+            MatcherAssert.assertThat(result.size(), Matchers.equalTo(1));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
 }
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java
index d754cac..127b83f 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java
@@ -21,11 +21,13 @@
 
 import jakarta.persistence.Entity;
 import jakarta.persistence.Id;
+import jakarta.persistence.NamedQuery;
 
 /**
  * JPA Entity used in {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} tests.
  */
 @Entity
+@NamedQuery(name = "DateTimeQueryEntity.findByLocalDateTime", query = "SELECT e FROM DateTimeQueryEntity e WHERE e.datetime = :dateTime")
 public class DateTimeQueryEntity {
 
     @Id