Skip to content

Commit

Permalink
Merge pull request #54 from ergon/feature/dope-234-use-hash
Browse files Browse the repository at this point in the history
DOPE-234: added join hints, added CM extension for use keys hint, added tests
  • Loading branch information
martinagallati-ergon authored Oct 7, 2024
2 parents 1c4023a + c240fbc commit d8022a9
Show file tree
Hide file tree
Showing 13 changed files with 925 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ fun formatPathToQueryString(name: String, path: String) =
"`$path`.`$name`"
}

fun formatStringListToQueryStringWithBrackets(dopeQueries: List<String>, seperator: String = ", ", prefix: String = "(", postfix: String = ")") =
dopeQueries.joinToString(seperator, prefix, postfix)
fun formatStringListToQueryStringWithBrackets(dopeQueries: List<String>, separator: String = ", ", prefix: String = "(", postfix: String = ")") =
dopeQueries.joinToString(separator, prefix, postfix)

fun formatListToQueryStringWithBrackets(dopeQueries: List<DopeQuery>, seperator: String = ", ", prefix: String = "(", postfix: String = ")") =
dopeQueries.joinToString(seperator, prefix, postfix) { it.queryString }
fun formatListToQueryStringWithBrackets(dopeQueries: List<DopeQuery>, separator: String = ", ", prefix: String = "(", postfix: String = ")") =
dopeQueries.joinToString(separator, prefix, postfix) { it.queryString }

fun formatIndexToQueryString(indexName: String?, indexType: String?) =
listOfNotNull(indexName?.let { "`$it`" }, indexType).joinToString(separator = " ")
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import ch.ergon.dope.resolvable.clause.model.SelectOrderByTypeClause
import ch.ergon.dope.resolvable.clause.model.SelectWhereClause
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.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
Expand Down Expand Up @@ -42,38 +44,94 @@ interface ISelectOrderByClause : ISelectLimitClause {

interface ISelectGroupByClause : ISelectOrderByClause {
fun orderBy(stringField: Field<StringType>) = SelectOrderByClause(stringField, this)
fun orderBy(stringField: Field<StringType>, orderByType: OrderByType) = SelectOrderByTypeClause(stringField, orderByType, this)
fun orderBy(stringField: Field<StringType>, orderByType: OrderByType) =
SelectOrderByTypeClause(stringField, orderByType, this)
}

interface ISelectWhereClause : ISelectGroupByClause {
fun groupBy(field: Field<out ValidType>, vararg fields: Field<out ValidType>) = GroupByClause(field, *fields, parentClause = this)
fun groupBy(field: Field<out ValidType>, vararg fields: Field<out ValidType>) =
GroupByClause(field, *fields, parentClause = this)
}

interface ISelectFromClause : ISelectWhereClause {
fun where(whereExpression: TypeExpression<BooleanType>) = SelectWhereClause(whereExpression, this)
}

interface ISelectJoinClause : ISelectFromClause {
fun join(joinable: Joinable, onCondition: TypeExpression<BooleanType>) = StandardJoinClause(joinable, onCondition, this)
fun join(joinable: Joinable, onKeys: Field<out ValidType>) = StandardJoinClause(joinable, onKeys, this)
fun join(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket) = StandardJoinClause(joinable, onKey, forBucket, this)
fun join(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = StandardJoinClause(joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, this)
fun join(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = StandardJoinClause(joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, this)
fun join(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = StandardJoinClause(joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, this)

fun innerJoin(joinable: Joinable, onCondition: TypeExpression<BooleanType>) = InnerJoinClause(joinable, onCondition, this)
fun innerJoin(joinable: Joinable, onKeys: Field<out ValidType>) = InnerJoinClause(joinable, onKeys, this)
fun innerJoin(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket) = InnerJoinClause(joinable, onKey, forBucket, this)
fun innerJoin(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = InnerJoinClause(joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, this)
fun innerJoin(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = InnerJoinClause(joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, this)
fun innerJoin(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = InnerJoinClause(joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, this)

fun leftJoin(joinable: Joinable, onCondition: TypeExpression<BooleanType>) = LeftJoinClause(joinable, onCondition, this)
fun leftJoin(joinable: Joinable, onKeys: Field<out ValidType>) = LeftJoinClause(joinable, onKeys, this)
fun leftJoin(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket) = LeftJoinClause(joinable, onKey, forBucket, this)
fun leftJoin(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = LeftJoinClause(joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, this)
fun leftJoin(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = LeftJoinClause(joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, this)
fun leftJoin(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = LeftJoinClause(joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, this)

fun rightJoin(joinable: Joinable, onCondition: TypeExpression<BooleanType>) = RightJoinClause(joinable, onCondition, this)
fun rightJoin(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
) = RightJoinClause(joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, this)

fun alias(alias: String) = AliasedSelectClause(alias, this)
}

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

interface ISelectClause : ISelectFromClause {
Expand Down
131 changes: 108 additions & 23 deletions core/src/main/kotlin/ch/ergon/dope/resolvable/clause/model/JoinClause.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import ch.ergon.dope.resolvable.clause.model.JoinType.RIGHT_JOIN
import ch.ergon.dope.resolvable.clause.model.OnType.ON
import ch.ergon.dope.resolvable.clause.model.OnType.ON_KEYS
import ch.ergon.dope.resolvable.clause.model.OnType.ON_KEY_FOR
import ch.ergon.dope.resolvable.clause.model.joinHint.HashOrNestedLoopHint
import ch.ergon.dope.resolvable.clause.model.joinHint.KeysOrIndexHint
import ch.ergon.dope.resolvable.expression.TypeExpression
import ch.ergon.dope.resolvable.expression.unaliased.type.Field
import ch.ergon.dope.resolvable.fromable.AliasedBucket
Expand Down Expand Up @@ -39,13 +41,17 @@ sealed class SelectJoinClause : ISelectJoinClause {
private val onKeys: Field<out ValidType>?
private val onKey: Field<out ValidType>?
private val forBucket: Bucket?
private val hashOrNestedLoopHint: HashOrNestedLoopHint?
private val keysOrIndexHint: KeysOrIndexHint?
private val parentClause: ISelectFromClause
private val onType: OnType

constructor(
joinType: JoinType,
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) {
this.onType = ON
Expand All @@ -56,12 +62,16 @@ sealed class SelectJoinClause : ISelectJoinClause {
this.onKeys = null
this.onKey = null
this.forBucket = null
this.hashOrNestedLoopHint = hashOrNestedLoopHint
this.keysOrIndexHint = keysOrIndexHint
}

constructor(
joinType: JoinType,
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) {
this.onType = ON_KEYS
Expand All @@ -72,13 +82,17 @@ sealed class SelectJoinClause : ISelectJoinClause {
this.onCondition = null
this.onKey = null
this.forBucket = null
this.hashOrNestedLoopHint = hashOrNestedLoopHint
this.keysOrIndexHint = keysOrIndexHint
}

constructor(
joinType: JoinType,
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) {
this.onType = ON_KEY_FOR
Expand All @@ -89,6 +103,8 @@ sealed class SelectJoinClause : ISelectJoinClause {
this.parentClause = parentClause
this.onCondition = null
this.onKeys = null
this.hashOrNestedLoopHint = hashOrNestedLoopHint
this.keysOrIndexHint = keysOrIndexHint
}

override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
Expand All @@ -97,8 +113,23 @@ sealed class SelectJoinClause : ISelectJoinClause {
is AliasedBucket -> joinable.asBucketDefinition().toDopeQuery(manager)
else -> joinable.toDopeQuery(manager)
}
val joinQueryString = "${parentDopeQuery.queryString} ${joinType.type} ${joinableDopeQuery.queryString}"
val joinParameters = parentDopeQuery.parameters + joinableDopeQuery.parameters
val joinHintsDopeQuery = if (hashOrNestedLoopHint != null || keysOrIndexHint != null) {
val hashOrNestedLoopHintDopeQuery = hashOrNestedLoopHint?.toDopeQuery(manager)
val keysOrIndexHintDopeQuery = keysOrIndexHint?.toDopeQuery(manager)
DopeQuery(
queryString = "USE" +
hashOrNestedLoopHint?.let { " ${hashOrNestedLoopHintDopeQuery?.queryString}" }.orEmpty() +
keysOrIndexHint?.let { " ${keysOrIndexHintDopeQuery?.queryString}" }.orEmpty(),
parameters = hashOrNestedLoopHintDopeQuery?.parameters.orEmpty() +
keysOrIndexHintDopeQuery?.parameters.orEmpty(),
)
} else {
null
}
val joinQueryString = "${parentDopeQuery.queryString} ${joinType.type} ${joinableDopeQuery.queryString}" +
joinHintsDopeQuery?.let { " ${joinHintsDopeQuery.queryString}" }.orEmpty()
val joinParameters = parentDopeQuery.parameters + joinableDopeQuery.parameters +
joinHintsDopeQuery?.parameters.orEmpty()

return when (onType) {
ON -> {
Expand All @@ -121,7 +152,8 @@ sealed class SelectJoinClause : ISelectJoinClause {
val keyDopeQuery = onKey?.toDopeQuery(manager)
val forBucketDopeQuery = forBucket?.toDopeQuery(manager)
DopeQuery(
queryString = "$joinQueryString ON KEY ${keyDopeQuery?.queryString} FOR ${forBucketDopeQuery?.queryString}",
queryString = "$joinQueryString ON KEY ${keyDopeQuery?.queryString} " +
"FOR ${forBucketDopeQuery?.queryString}",
parameters = joinParameters + keyDopeQuery?.parameters.orEmpty() +
forBucketDopeQuery?.parameters.orEmpty(),
)
Expand All @@ -131,37 +163,90 @@ sealed class SelectJoinClause : ISelectJoinClause {
}

class StandardJoinClause : SelectJoinClause {
constructor(joinable: Joinable, onCondition: TypeExpression<BooleanType>, parentClause: ISelectFromClause) :
super(JOIN, joinable, onCondition, parentClause)
constructor(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(JOIN, joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKeys: Field<out ValidType>, parentClause: ISelectFromClause) :
super(JOIN, joinable, onKeys, parentClause)
constructor(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(JOIN, joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket, parentClause: ISelectFromClause) :
super(JOIN, joinable, onKey, forBucket, parentClause)
constructor(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(JOIN, joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)
}

class LeftJoinClause : SelectJoinClause {
constructor(joinable: Joinable, onCondition: TypeExpression<BooleanType>, parentClause: ISelectFromClause) :
super(LEFT_JOIN, joinable, onCondition, parentClause)
constructor(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(LEFT_JOIN, joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKeys: Field<out ValidType>, parentClause: ISelectFromClause) :
super(LEFT_JOIN, joinable, onKeys, parentClause)
constructor(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(LEFT_JOIN, joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket, parentClause: ISelectFromClause) :
super(LEFT_JOIN, joinable, onKey, forBucket, parentClause)
constructor(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(LEFT_JOIN, joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)
}

class InnerJoinClause : SelectJoinClause {
constructor(joinable: Joinable, onCondition: TypeExpression<BooleanType>, parentClause: ISelectFromClause) :
super(INNER_JOIN, joinable, onCondition, parentClause)
constructor(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(INNER_JOIN, joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKeys: Field<out ValidType>, parentClause: ISelectFromClause) :
super(INNER_JOIN, joinable, onKeys, parentClause)
constructor(
joinable: Joinable,
onKeys: Field<out ValidType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(INNER_JOIN, joinable, onKeys, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)

constructor(joinable: Joinable, onKey: Field<out ValidType>, forBucket: Bucket, parentClause: ISelectFromClause) :
super(INNER_JOIN, joinable, onKey, forBucket, parentClause)
constructor(
joinable: Joinable,
onKey: Field<out ValidType>,
forBucket: Bucket,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : super(INNER_JOIN, joinable, onKey, forBucket, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)
}

class RightJoinClause(joinable: Joinable, onCondition: TypeExpression<BooleanType>, parentClause: ISelectFromClause) :
SelectJoinClause(RIGHT_JOIN, joinable, onCondition, parentClause)
class RightJoinClause(
joinable: Joinable,
onCondition: TypeExpression<BooleanType>,
hashOrNestedLoopHint: HashOrNestedLoopHint? = null,
keysOrIndexHint: KeysOrIndexHint? = null,
parentClause: ISelectFromClause,
) : SelectJoinClause(RIGHT_JOIN, joinable, onCondition, hashOrNestedLoopHint, keysOrIndexHint, parentClause = parentClause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ch.ergon.dope.resolvable.clause.model.joinHint

import ch.ergon.dope.DopeQuery
import ch.ergon.dope.DopeQueryManager
import ch.ergon.dope.resolvable.Resolvable

enum class HashOrNestedLoopHint : Resolvable {
HASH_BUILD {
override fun toDopeQuery(manager: DopeQueryManager) = DopeQuery(
"HASH (BUILD)",
emptyMap(),
)
},
HASH_PROBE {
override fun toDopeQuery(manager: DopeQueryManager) = DopeQuery(
"HASH (PROBE)",
emptyMap(),
)
},
NESTED_LOOP {
override fun toDopeQuery(manager: DopeQueryManager) = DopeQuery(
"NL",
emptyMap(),
)
},
}
Loading

0 comments on commit d8022a9

Please sign in to comment.