Skip to content

Commit

Permalink
Merge pull request #651 from progress0407/feat/arith-power
Browse files Browse the repository at this point in the history
Implement power operator
  • Loading branch information
shouwn authored Mar 12, 2024
2 parents eb48523 + c4a8a6a commit 181aa4b
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 8 deletions.
8 changes: 4 additions & 4 deletions docs/en/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ locate("Book", path(Book::title))
* FLOOR (floor)
* INDEX (index)
* LN (ln)
* MOD (mod)
* POWER (power)
* SIGN (sign)
* SQRT (sqrt)
* ROUND (round)
Expand All @@ -247,6 +249,8 @@ ln(path(Book::price))

mod(path(Employee::age), 3)

power(path(Employee::age), 2)

sign(path(Book::price))

sqrt(path(Book::price))
Expand All @@ -256,10 +260,6 @@ round(path(Book::price), 2)
size(path(Book::authors))
```

| Function | DSL function |
|----------|--------------|
| POWER | not yet |

### Datetime functions

* CURRENT\_DATE (currentDate)
Expand Down
8 changes: 4 additions & 4 deletions docs/ko/jpql-with-kotlin-jdsl/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ locate("Book", path(Book::title))
* FLOOR (floor)
* INDEX (index)
* LN (ln)
* MOD (mod)
* POWER (power)
* SIGN (sign)
* SQRT (sqrt)
* ROUND (round)
Expand All @@ -243,6 +245,8 @@ ln(path(Book::price))

mod(path(Employee::age), 3)

power(path(Employee::age), 2)

sign(path(Book::price))

sqrt(path(Book::price))
Expand All @@ -252,10 +256,6 @@ round(path(Book::price), 2)
size(path(Book::authors))
```

| Function | DSL function |
|----------|--------------|
| POWER | not yet |

### Datetime functions

* CURRENT\_DATE (currentDate)
Expand Down
32 changes: 32 additions & 0 deletions dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,38 @@ open class Jpql : JpqlDsl {
return Expressions.ln(value.toExpression())
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Any, V : Number> power(base: KProperty1<T, @Exact V>, exponent: Number): Expression<Double> {
return Expressions.power(Paths.path(base), Expressions.value(exponent))
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Any, V : Number> power(base: KProperty1<T, @Exact V>, exponent: Expressionable<V>): Expression<Double> {
return Expressions.power(Paths.path(base), exponent.toExpression())
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expressionable<T>, exponent: T): Expression<Double> {
return Expressions.power(base.toExpression(), Expressions.value(exponent))
}

/**
* Create expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expressionable<T>, exponent: Expressionable<T>): Expression<Double> {
return Expressions.power(base.toExpression(), exponent.toExpression())
}

/**
* Creates an expression that represents the sign of value.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.linecorp.kotlinjdsl.dsl.jpql.expression

import com.linecorp.kotlinjdsl.dsl.jpql.entity.employee.Employee
import com.linecorp.kotlinjdsl.dsl.jpql.queryPart
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths
import org.assertj.core.api.WithAssertions
import org.junit.jupiter.api.Test

class PowerDslTest : WithAssertions {
private val intExpression1 = Paths.path(Employee::age)
private val intExpression2 = Expressions.value(2)

private val int1 = 2

@Test
fun `power() with a property`() {
// when
val expression1 = queryPart {
power(Employee::age, int1)
}.toExpression()

val expression2 = queryPart {
power(Employee::age, intExpression2)
}.toExpression()

val actual1: Expression<Number> = expression1 // for type check
val actual2: Expression<Number> = expression2 // for type check

// then
val expected1 = Expressions.power(
base = Paths.path(Employee::age),
exponent = Expressions.value(int1),
)

val expected2 = Expressions.power(
base = Paths.path(Employee::age),
exponent = intExpression2,
)

assertThat(actual1).isEqualTo(expected1)
assertThat(actual2).isEqualTo(expected2)
}

@Test
fun `power() with a expression`() {
// when
val expression1 = queryPart {
power(intExpression1, int1)
}.toExpression()

val expression2 = queryPart {
power(intExpression1, intExpression2)
}.toExpression()

val actual1: Expression<Number> = expression1 // for type check
val actual2: Expression<Number> = expression2 // for type check

// then
val expected1 = Expressions.power(
base = intExpression1,
exponent = Expressions.value(int1),
)

val expected2 = Expressions.power(
base = intExpression1,
exponent = intExpression2,
)

assertThat(actual1).isEqualTo(expected1)
assertThat(actual2).isEqualTo(expected2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlNullIf
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlParam
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPathType
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPlus
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSign
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSize
Expand Down Expand Up @@ -277,6 +278,14 @@ object Expressions {
return JpqlLn(value)
}

/**
* Creates expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@SinceJdsl("3.4.0")
fun <T : Number> power(base: Expression<T>, exponent: Expression<T>): Expression<Double> {
return JpqlPower(base, exponent)
}

/**
* Creates an expression that represents the sign of a numeric value.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl

import com.linecorp.kotlinjdsl.Internal
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression

/**
* Expression that calculates the powering of a numeric [base] to a specified [exponent].
*/
@Internal
data class JpqlPower<T : Number> internal constructor(
val base: Expression<T>,
val exponent: Expression<T>,
) : Expression<Double>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlNullIf
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlParam
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPathType
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPlus
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlRound
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSign
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlSize
Expand Down Expand Up @@ -428,6 +429,20 @@ class ExpressionsTest : WithAssertions {
assertThat(actual).isEqualTo(expected)
}

@Test
fun power() {
// when
val actual = Expressions.power(doubleExpression1, intExpression1)

// then
val expected = JpqlPower(
doubleExpression1,
intExpression1,
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun sign() {
// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathPropertySeria
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathTreatSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPathTypeSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPlusSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPowerSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlPredicateParenthesesSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlRoundSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.impl.JpqlSelectQuerySerializer
Expand Down Expand Up @@ -367,6 +368,7 @@ private class DefaultModule : JpqlRenderModule {
JpqlPathTreatSerializer(),
JpqlPathTypeSerializer(),
JpqlPlusSerializer(),
JpqlPowerSerializer(),
JpqlPredicateParenthesesSerializer(),
JpqlRoundSerializer(),
JpqlSelectQuerySerializer(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.linecorp.kotlinjdsl.render.jpql.serializer.impl

import com.linecorp.kotlinjdsl.Internal
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPower
import com.linecorp.kotlinjdsl.render.RenderContext
import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlRenderSerializer
import com.linecorp.kotlinjdsl.render.jpql.serializer.JpqlSerializer
import com.linecorp.kotlinjdsl.render.jpql.writer.JpqlWriter
import kotlin.reflect.KClass

@Internal
class JpqlPowerSerializer : JpqlSerializer<JpqlPower<*>> {
override fun handledType(): KClass<JpqlPower<*>> {
return JpqlPower::class
}

override fun serialize(part: JpqlPower<*>, writer: JpqlWriter, context: RenderContext) {
val delegate = context.getValue(JpqlRenderSerializer)

writer.write("POWER")

writer.writeParentheses {
delegate.serialize(part.base, writer, context)

writer.write(",")
writer.write(" ")

delegate.serialize(part.exponent, writer, context)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.linecorp.kotlinjdsl.render.jpql.serializer.impl

import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.impl.JpqlPower
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Paths
import com.linecorp.kotlinjdsl.render.TestRenderContext
import com.linecorp.kotlinjdsl.render.jpql.entity.employee.Employee
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.impl.annotations.MockK
import io.mockk.verifySequence
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

@JpqlSerializerTest
class JpqlPowerSerializerTest {
private val sut = JpqlPowerSerializer()

@MockK
private lateinit var writer: JpqlWriter

@MockK
private lateinit var serializer: JpqlRenderSerializer

private val expression1 = Paths.path(Employee::age)
private val expression2 = Expressions.value(2)

@Test
fun handledType() {
// when
val actual = sut.handledType()

// then
assertThat(actual).isEqualTo(JpqlPower::class)
}

@Test
fun serialize() {
// given
val part = Expressions.power(
base = expression1,
exponent = expression2,
)
val context = TestRenderContext(serializer)

// when
sut.serialize(part as JpqlPower<*>, writer, context)

// then
verifySequence {
writer.write("POWER")
writer.writeParentheses(any())
serializer.serialize(expression1, writer, context)
writer.write(",")
writer.write(" ")
serializer.serialize(expression2, writer, context)
}
}
}

0 comments on commit 181aa4b

Please sign in to comment.