Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOPE-271: added missing returning clause functionality #73

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.resetDatabase
import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.testBucket
import ch.ergon.dope.integrationTest.toMapValues
import ch.ergon.dope.integrationTest.tryUntil
import ch.ergon.dope.resolvable.expression.AsteriskExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.arithmetic.add
import ch.ergon.dope.resolvable.expression.unaliased.type.relational.isEqualTo
import ch.ergon.dope.resolvable.fromable.useKeys
Expand All @@ -28,7 +29,9 @@ class DeleteIntegrationTest : BaseIntegrationTest() {
)
.returning(
idField,
).build()
AsteriskExpression(),
)
.build()

tryUntil {
val queryResult = queryWithoutParameters(dopeQuery)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class UpdateIntegrationTest : BaseIntegrationTest() {
.returning(
newField,
nameField,
).build()
)
.build()

tryUntil {
val queryResult = queryWithoutParameters(dopeQuery)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,33 @@ package ch.ergon.dope.resolvable.clause
import ch.ergon.dope.resolvable.clause.model.DeleteLimitClause
import ch.ergon.dope.resolvable.clause.model.DeleteOffsetClause
import ch.ergon.dope.resolvable.clause.model.DeleteReturningClause
import ch.ergon.dope.resolvable.clause.model.DeleteReturningSingleClause
import ch.ergon.dope.resolvable.clause.model.DeleteWhereClause
import ch.ergon.dope.resolvable.clause.model.ReturningType.ELEMENT
import ch.ergon.dope.resolvable.clause.model.ReturningType.RAW
import ch.ergon.dope.resolvable.clause.model.ReturningType.VALUE
import ch.ergon.dope.resolvable.expression.AsteriskExpression
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.expression.unaliased.type.toDopeType
import ch.ergon.dope.resolvable.fromable.Bucket
import ch.ergon.dope.resolvable.fromable.Returnable
import ch.ergon.dope.validtype.BooleanType
import ch.ergon.dope.validtype.NumberType
import ch.ergon.dope.validtype.ValidType

interface IDeleteReturningClause : Clause

interface IDeleteOffsetClause : IDeleteReturningClause {
fun returning(field: Field<out ValidType>, vararg fields: Field<out ValidType>) = DeleteReturningClause(field, *fields, parentClause = this)
fun returning(returningExpression: Returnable, vararg additionalReturningExpressions: Returnable) =
martinagallati-ergon marked this conversation as resolved.
Show resolved Hide resolved
DeleteReturningClause(returningExpression, *additionalReturningExpressions, parentClause = this)
fun returningAsterisk(bucket: Bucket? = null) = DeleteReturningClause(AsteriskExpression(bucket), parentClause = this)

fun returningRaw(returningExpression: TypeExpression<out ValidType>) =
DeleteReturningSingleClause(returningExpression, returningType = RAW, parentClause = this)
fun returningValue(returningExpression: TypeExpression<out ValidType>) =
DeleteReturningSingleClause(returningExpression, returningType = VALUE, parentClause = this)
fun returningElement(returningExpression: TypeExpression<out ValidType>) =
DeleteReturningSingleClause(returningExpression, returningType = ELEMENT, parentClause = this)
}

interface IDeleteLimitClause : IDeleteOffsetClause {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import ch.ergon.dope.resolvable.clause.model.StandardJoinClause
import ch.ergon.dope.resolvable.clause.model.UnnestClause
import ch.ergon.dope.resolvable.clause.model.joinHint.HashOrNestedLoopHint
import ch.ergon.dope.resolvable.clause.model.joinHint.KeysOrIndexHint
import ch.ergon.dope.resolvable.expression.AliasedExpression
import ch.ergon.dope.resolvable.expression.AliasedTypeExpression
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.expression.unaliased.type.toDopeType
Expand Down Expand Up @@ -140,7 +140,7 @@ interface ISelectJoinClause<T : ValidType> : ISelectLetClause<T> {

interface ISelectUnnestClause<T : ValidType> : ISelectJoinClause<T> {
fun <U : ValidType> unnest(arrayField: Field<ArrayType<U>>) = UnnestClause(arrayField, this)
fun <U : ValidType> unnest(aliasedArrayExpression: AliasedExpression<ArrayType<U>>) =
fun <U : ValidType> unnest(aliasedArrayExpression: AliasedTypeExpression<ArrayType<U>>) =
AliasedUnnestClause(aliasedArrayExpression, this)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package ch.ergon.dope.resolvable.clause

import ch.ergon.dope.resolvable.clause.model.ReturningType.ELEMENT
import ch.ergon.dope.resolvable.clause.model.ReturningType.RAW
import ch.ergon.dope.resolvable.clause.model.ReturningType.VALUE
import ch.ergon.dope.resolvable.clause.model.SetClause
import ch.ergon.dope.resolvable.clause.model.UnsetClause
import ch.ergon.dope.resolvable.clause.model.UpdateLimitClause
import ch.ergon.dope.resolvable.clause.model.UpdateReturningClause
import ch.ergon.dope.resolvable.clause.model.UpdateReturningSingleClause
import ch.ergon.dope.resolvable.clause.model.UpdateWhereClause
import ch.ergon.dope.resolvable.clause.model.to
import ch.ergon.dope.resolvable.expression.AsteriskExpression
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.expression.unaliased.type.toDopeType
import ch.ergon.dope.resolvable.fromable.Bucket
import ch.ergon.dope.resolvable.fromable.Returnable
import ch.ergon.dope.validtype.BooleanType
import ch.ergon.dope.validtype.NumberType
import ch.ergon.dope.validtype.StringType
Expand All @@ -17,8 +24,16 @@ import ch.ergon.dope.validtype.ValidType
interface IUpdateReturningClause : Clause

interface IUpdateLimitClause : IUpdateReturningClause {
fun returning(field: Field<out ValidType>, vararg fields: Field<out ValidType>) =
UpdateReturningClause(field, *fields, parentClause = this)
fun returning(returningExpression: Returnable, vararg additionalReturningExpressions: Returnable) =
UpdateReturningClause(returningExpression, *additionalReturningExpressions, parentClause = this)
fun returningAsterisk(bucket: Bucket? = null) = UpdateReturningClause(AsteriskExpression(bucket), parentClause = this)

fun returningRaw(returningExpression: TypeExpression<out ValidType>) =
UpdateReturningSingleClause(returningExpression, returningType = RAW, parentClause = this)
fun returningValue(returningExpression: TypeExpression<out ValidType>) =
UpdateReturningSingleClause(returningExpression, returningType = VALUE, parentClause = this)
fun returningElement(returningExpression: TypeExpression<out ValidType>) =
UpdateReturningSingleClause(returningExpression, returningType = ELEMENT, parentClause = this)
}

interface IUpdateWhereClause : IUpdateLimitClause {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,98 @@ package ch.ergon.dope.resolvable.clause.model

import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.Resolvable
import ch.ergon.dope.resolvable.clause.Clause
import ch.ergon.dope.resolvable.clause.IDeleteOffsetClause
import ch.ergon.dope.resolvable.clause.IDeleteReturningClause
import ch.ergon.dope.resolvable.clause.IUpdateLimitClause
import ch.ergon.dope.resolvable.clause.IUpdateReturningClause
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.formatToQueryStringWithSymbol
import ch.ergon.dope.validtype.ValidType
import ch.ergon.dope.resolvable.fromable.AliasedSelectClause
import ch.ergon.dope.resolvable.fromable.Returnable
import ch.ergon.dope.resolvable.fromable.SingleReturnable

private const val RETURNING = "RETURNING"

enum class ReturningType(val queryString: String) {
ELEMENT("ELEMENT"),
RAW("RAW"),
VALUE("VALUE"),
}

sealed class ReturningClause(
private val field: Field<out ValidType>,
private vararg val fields: Field<out ValidType>,
private val returnable: Returnable,
private vararg val additionalReturnables: Returnable,
private val parentClause: Clause,
) {
fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val fieldsDopeQuery = fields.map { it.toDopeQuery(manager) }
val fieldDopeQuery = field.toDopeQuery(manager)
) : Resolvable {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val returnables = arrayOf(returnable) + additionalReturnables
val parentDopeQuery = parentClause.toDopeQuery(manager)
val returnablesDopeQuery = returnables.map {
when (it) {
is AliasedSelectClause<*> -> it.asAliasedSelectClauseDefinition().toDopeQuery(manager)
else -> it.toDopeQuery(manager)
}
}
return DopeQuery(
queryString = formatToQueryStringWithSymbol(
parentDopeQuery.queryString,
"RETURNING",
fieldDopeQuery.queryString,
*fieldsDopeQuery.map { it.queryString }.toTypedArray(),
RETURNING,
*returnablesDopeQuery.map { it.queryString }.toTypedArray(),
),
parameters = parentDopeQuery.parameters.merge(
fieldDopeQuery.parameters,
*fieldsDopeQuery.map { it.parameters }.toTypedArray(),
*returnablesDopeQuery.map { it.parameters }.toTypedArray(),
),
)
}
}

class DeleteReturningClause(
field: Field<out ValidType>,
vararg fields: Field<out ValidType>,
returnable: Returnable,
vararg additionalReturnables: Returnable,
parentClause: IDeleteOffsetClause,
) : IDeleteReturningClause, ReturningClause(field, *fields, parentClause = parentClause)
) : IDeleteReturningClause, ReturningClause(
returnable,
*additionalReturnables,
parentClause = parentClause,
)

class UpdateReturningClause(
field: Field<out ValidType>,
vararg fields: Field<out ValidType>,
returnable: Returnable,
vararg additionalReturnables: Returnable,
parentClause: IUpdateLimitClause,
) : IUpdateReturningClause, ReturningClause(
returnable,
*additionalReturnables,
parentClause = parentClause,
)

sealed class ReturningSingleClause(
private val singleReturnable: SingleReturnable,
private val returningType: ReturningType,
private val parentClause: Clause,
) : Resolvable {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val parentDopeQuery = parentClause.toDopeQuery(manager)
val singleReturnableDopeQuery = singleReturnable.toDopeQuery(manager)
return DopeQuery(
queryString = "${parentDopeQuery.queryString} $RETURNING " +
"${returningType.queryString} ${singleReturnableDopeQuery.queryString}",
parameters = parentDopeQuery.parameters.merge(
singleReturnableDopeQuery.parameters,
),
)
}
}

class DeleteReturningSingleClause(
martinagallati-ergon marked this conversation as resolved.
Show resolved Hide resolved
singleReturnable: SingleReturnable,
returningType: ReturningType,
parentClause: IDeleteOffsetClause,
) : IDeleteReturningClause, ReturningSingleClause(singleReturnable, returningType, parentClause = parentClause)

class UpdateReturningSingleClause(
singleReturnable: SingleReturnable,
returningType: ReturningType,
parentClause: IUpdateLimitClause,
) : IUpdateReturningClause, ReturningClause(field, *fields, parentClause = parentClause)
) : IUpdateReturningClause, ReturningSingleClause(singleReturnable, returningType, parentClause = parentClause)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package ch.ergon.dope.resolvable.clause.model
import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.clause.ISelectUnnestClause
import ch.ergon.dope.resolvable.expression.AliasedExpression
import ch.ergon.dope.resolvable.expression.AliasedTypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.formatToQueryStringWithSymbol
import ch.ergon.dope.validtype.ArrayType
Expand All @@ -26,15 +26,15 @@ class UnnestClause<T : ValidType, U : ValidType>(
}

class AliasedUnnestClause<T : ValidType, U : ValidType>(
private val aliasedExpression: AliasedExpression<ArrayType<T>>,
private val aliasedTypeExpression: AliasedTypeExpression<ArrayType<T>>,
private val parentClause: ISelectUnnestClause<U>,
) : ISelectUnnestClause<U> {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val parentDopeQuery = parentClause.toDopeQuery(manager)
val aliasedExpressionDopeQuery = aliasedExpression.toDopeQuery(manager)
val aliasedTypeExpressionDopeQuery = aliasedTypeExpression.toDopeQuery(manager)
return DopeQuery(
queryString = formatToQueryStringWithSymbol(parentDopeQuery.queryString, UNNEST, aliasedExpressionDopeQuery.queryString),
parameters = parentDopeQuery.parameters.merge(aliasedExpressionDopeQuery.parameters),
queryString = formatToQueryStringWithSymbol(parentDopeQuery.queryString, UNNEST, aliasedTypeExpressionDopeQuery.queryString),
parameters = parentDopeQuery.parameters.merge(aliasedTypeExpressionDopeQuery.parameters),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.expression.unaliased.type.toDopeType
import ch.ergon.dope.resolvable.formatToQueryStringWithSymbol
import ch.ergon.dope.resolvable.fromable.SingleReturnable
import ch.ergon.dope.validtype.BooleanType
import ch.ergon.dope.validtype.NumberType
import ch.ergon.dope.validtype.ObjectType
import ch.ergon.dope.validtype.StringType
import ch.ergon.dope.validtype.ValidType

class AliasedExpression<T : ValidType>(
Expand All @@ -21,10 +26,25 @@ class AliasedExpression<T : ValidType>(

fun <T : ValidType> UnaliasedExpression<T>.alias(string: String): AliasedExpression<T> = AliasedExpression(this, string)

fun Number.alias(string: String) = toDopeType().alias(string)
class AliasedTypeExpression<T : ValidType>(
private val typeExpression: TypeExpression<T>,
private val alias: String,
) : SingleExpression<T>, SingleReturnable {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val typeExpressionDopeQuery = typeExpression.toDopeQuery(manager)
return DopeQuery(
queryString = formatToQueryStringWithSymbol(typeExpressionDopeQuery.queryString, "AS", "`$alias`"),
parameters = typeExpressionDopeQuery.parameters,
)
}
}

fun <T : ValidType> TypeExpression<T>.alias(alias: String): AliasedTypeExpression<T> = AliasedTypeExpression(this, alias)

fun Number.alias(string: String): AliasedTypeExpression<NumberType> = toDopeType().alias(string)

fun String.alias(string: String) = toDopeType().alias(string)
fun String.alias(string: String): AliasedTypeExpression<StringType> = toDopeType().alias(string)

fun Boolean.alias(string: String) = toDopeType().alias(string)
fun Boolean.alias(string: String): AliasedTypeExpression<BooleanType> = toDopeType().alias(string)

fun <V> Map<String, V>.alias(string: String) = toDopeType().alias(string)
fun <V> Map<String, V>.alias(string: String): AliasedTypeExpression<ObjectType> = toDopeType().alias(string)
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package ch.ergon.dope.resolvable.expression
import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.fromable.Bucket
import ch.ergon.dope.resolvable.fromable.Returnable

const val ASTERISK_STRING = "*"

class AsteriskExpression(private val bucket: Bucket? = null) : Expression {
class AsteriskExpression(private val bucket: Bucket? = null) : Expression, Returnable {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val queryString = bucket?.toDopeQuery(manager)?.queryString?.let { "$it.$ASTERISK_STRING" } ?: ASTERISK_STRING
return DopeQuery(queryString = queryString)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ch.ergon.dope.resolvable.expression

import ch.ergon.dope.resolvable.Resolvable
import ch.ergon.dope.resolvable.fromable.SingleReturnable
import ch.ergon.dope.validtype.ValidType

interface Expression : Resolvable
Expand All @@ -9,4 +10,4 @@ interface SingleExpression<T : ValidType> : Expression

interface UnaliasedExpression<T : ValidType> : SingleExpression<T>

interface TypeExpression<T : ValidType> : UnaliasedExpression<T>
interface TypeExpression<T : ValidType> : UnaliasedExpression<T>, SingleReturnable
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.merge
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.UnaliasedExpression
import ch.ergon.dope.resolvable.operator.FunctionOperator
import ch.ergon.dope.validtype.ValidType

abstract class FunctionExpression<T : ValidType>(
private val symbol: String,
private vararg val expressions: UnaliasedExpression<out ValidType>?,
private vararg val expressions: TypeExpression<out ValidType>?,
) : TypeExpression<T>, FunctionOperator {
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
val expressionsDopeQuery = expressions.mapNotNull { it?.toDopeQuery(manager) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package ch.ergon.dope.resolvable.expression.unaliased.type.function.comparison

import ch.ergon.dope.resolvable.expression.UnaliasedExpression
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.function.FunctionExpression
import ch.ergon.dope.validtype.ComparableType

class GreatestExpression<T : ComparableType>(
firstExpression: UnaliasedExpression<T>,
secondExpression: UnaliasedExpression<T>,
vararg additionalExpressions: UnaliasedExpression<T>,
firstExpression: TypeExpression<T>,
secondExpression: TypeExpression<T>,
vararg additionalExpressions: TypeExpression<T>,
) : FunctionExpression<T>("GREATEST", firstExpression, secondExpression, *additionalExpressions)

fun <T : ComparableType> greatestOf(
firstExpression: UnaliasedExpression<T>,
secondExpression: UnaliasedExpression<T>,
vararg additionalExpressions: UnaliasedExpression<T>,
firstExpression: TypeExpression<T>,
secondExpression: TypeExpression<T>,
vararg additionalExpressions: TypeExpression<T>,
) = GreatestExpression(firstExpression, secondExpression, *additionalExpressions)
Loading
Loading