From 908be01bfa7fb30fd9476c3a5a0bf49aa7af5a58 Mon Sep 17 00:00:00 2001
From: Gyeongtae Noh <ilgolc@naver.com>
Date: Tue, 6 Feb 2024 17:49:15 +0900
Subject: [PATCH] feat: supporting localDateTime function for datetime function

---
 .../com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt  | 11 ++++
 .../dsl/jpql/JpqlLocalDateTimeDslTest.kt      | 25 +++++++++
 .../querymodel/jpql/expression/Expressions.kt | 10 ++++
 .../jpql/expression/impl/JpqlLocalDateTime.kt |  8 +++
 .../jpql/expression/ExpressionsTest.kt        | 12 +++++
 .../render/jpql/JpqlRenderContext.kt          |  2 +
 .../impl/JpqlLocalDateTimeSerializer.kt       | 20 +++++++
 .../JpqlJpqlLocalDateTimeSerializerTest.kt    | 52 +++++++++++++++++++
 8 files changed, 140 insertions(+)
 create mode 100644 dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/JpqlLocalDateTimeDslTest.kt
 create mode 100644 query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlLocalDateTime.kt
 create mode 100644 render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt
 create mode 100644 render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlJpqlLocalDateTimeSerializerTest.kt

diff --git a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt
index 038cb47b8..bc58af815 100644
--- a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt
+++ b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt
@@ -47,6 +47,7 @@ import java.math.BigInteger
 import java.sql.Date
 import java.sql.Time
 import java.time.LocalDate
+import java.time.LocalDateTime
 import kotlin.internal.Exact
 import kotlin.internal.LowPriorityInOverloadResolution
 import kotlin.reflect.KClass
@@ -3113,4 +3114,14 @@ open class Jpql : JpqlDsl {
     fun <T : Any> deleteFrom(entity: Entityable<T>): DeleteQueryWhereStep<T> {
         return DeleteQueryDsl(entity.toEntity())
     }
+
+    /**
+     * Creates an expression that represents the local datetime.
+     *
+     * This is the same as ```LOCAL DATETIME```.
+     */
+    @SinceJdsl("3.4.0")
+    fun localDateTime(): Expression<LocalDateTime> {
+        return Expressions.localDateTime()
+    }
 }
diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/JpqlLocalDateTimeDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/JpqlLocalDateTimeDslTest.kt
new file mode 100644
index 000000000..388529ab0
--- /dev/null
+++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/JpqlLocalDateTimeDslTest.kt
@@ -0,0 +1,25 @@
+package com.linecorp.kotlinjdsl.dsl.jpql
+
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import java.time.LocalDateTime
+
+class JpqlLocalDateTimeDslTest {
+
+    @Test
+    fun localDateTime() {
+        // when
+        val expression = queryPart {
+            localDateTime()
+        }.toExpression()
+
+        val actual: Expression<LocalDateTime> = expression // for type check
+
+        // then
+        val expected = Expressions.localDateTime()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+}
diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt
index 13a9e0502..e6947c1a5 100644
--- a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt
+++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/Expressions.kt
@@ -25,6 +25,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLength
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLiteral
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLn
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDate
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDateTime
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocate
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLower
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlMax
@@ -60,6 +61,7 @@ import java.math.BigInteger
 import java.sql.Date
 import java.sql.Time
 import java.time.LocalDate
+import java.time.LocalDateTime
 import kotlin.internal.Exact
 import kotlin.reflect.KClass
 
@@ -753,4 +755,12 @@ object Expressions {
     fun <T : Any> parentheses(expr: Expression<T>): Expression<T> {
         return JpqlExpressionParentheses(expr)
     }
+
+    /**
+     * Creates an expression that represents the local datetime in jpql.
+     */
+    @SinceJdsl("3.4.0")
+    fun localDateTime(): Expression<LocalDateTime> {
+        return JpqlLocalDateTime
+    }
 }
diff --git a/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlLocalDateTime.kt b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlLocalDateTime.kt
new file mode 100644
index 000000000..6c72cc7e4
--- /dev/null
+++ b/query-model/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/impl/JpqlLocalDateTime.kt
@@ -0,0 +1,8 @@
+package com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl
+
+import com.linecorp.kotlinjdsl.Internal
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
+import java.time.LocalDateTime
+
+@Internal
+object JpqlLocalDateTime : Expression<LocalDateTime>
diff --git a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt
index 2b40d3e3e..4198c3614 100644
--- a/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt
+++ b/query-model/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/querymodel/jpql/expression/ExpressionsTest.kt
@@ -27,6 +27,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLength
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLiteral
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLn
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDate
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDateTime
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocate
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLower
 import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlMax
@@ -1065,4 +1066,15 @@ class ExpressionsTest : WithAssertions {
 
         assertThat(actual).isEqualTo(expected)
     }
+
+    @Test
+    fun localDateTime() {
+        // when
+        val actual = Expressions.localDateTime()
+
+        // then
+        val expected = JpqlLocalDateTime.toExpression()
+
+        assertThat(actual).isEqualTo(expected)
+    }
 }
diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt
index 7bacf304f..97dbc3a9a 100644
--- a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt
+++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/JpqlRenderContext.kt
@@ -78,6 +78,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLikeSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLiteralSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLnSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLocalDateSerializer
+import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLocalDateTimeSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLocateSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlLowerSerializer
 import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlMaxSerializer
@@ -336,6 +337,7 @@ private class DefaultModule : JpqlRenderModule {
             JpqlLiteralSerializer(),
             JpqlLnSerializer(),
             JpqlLocalDateSerializer(),
+            JpqlLocalDateTimeSerializer(),
             JpqlLocateSerializer(),
             JpqlLowerSerializer(),
             JpqlMaxSerializer(),
diff --git a/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt
new file mode 100644
index 000000000..f7a7caa33
--- /dev/null
+++ b/render/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlLocalDateTimeSerializer.kt
@@ -0,0 +1,20 @@
+package com.linecorp.kotlinjdsl.render.jpql.serializer.impl
+
+import com.linecorp.kotlinjdsl.Internal
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDateTime
+import com.linecorp.kotlinjdsl.render.RenderContext
+import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer
+import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter
+import kotlin.reflect.KClass
+
+@Internal
+class JpqlLocalDateTimeSerializer : JpqlSerializer<JpqlLocalDateTime> {
+
+    override fun handledType(): KClass<JpqlLocalDateTime> {
+        return JpqlLocalDateTime::class
+    }
+
+    override fun serialize(part: JpqlLocalDateTime, writer: JpqlWriter, context: RenderContext) {
+        writer.write("LOCAL DATETIME")
+    }
+}
diff --git a/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlJpqlLocalDateTimeSerializerTest.kt b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlJpqlLocalDateTimeSerializerTest.kt
new file mode 100644
index 000000000..e97ae377b
--- /dev/null
+++ b/render/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/render/jpql/serializer/impl/JpqlJpqlLocalDateTimeSerializerTest.kt
@@ -0,0 +1,52 @@
+package com.linecorp.kotlinjdsl.render.jpql.serializer.impl
+
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
+import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlLocalDateTime
+import com.linecorp.kotlinjdsl.render.TestRenderContext
+import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer
+import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializerTest
+import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.runs
+import io.mockk.verifySequence
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+@JpqlSerializerTest
+class JpqlJpqlLocalDateTimeSerializerTest {
+    private val sut = JpqlLocalDateTimeSerializer()
+
+    @MockK
+    private lateinit var writer: JpqlWriter
+
+    @MockK
+    private lateinit var serializer: JpqlRenderSerializer
+
+    @Test
+    fun handle() {
+        // when
+        val actual = sut.handledType()
+
+        // then
+        assertThat(actual).isEqualTo(JpqlLocalDateTime::class)
+    }
+
+    @Test
+    fun serialize() {
+        // given
+        every { writer.write(any<String>()) } just runs
+
+        val part = Expressions.localDateTime() as JpqlLocalDateTime
+        val context = TestRenderContext(serializer)
+
+        // when
+        sut.serialize(part, writer, context)
+
+        // then
+        verifySequence {
+            writer.write("LOCAL DATETIME")
+        }
+    }
+}