-
Notifications
You must be signed in to change notification settings - Fork 63
Adds support for FULL/RIGHT OUTER JOIN and improves performance of JOINs #1295
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9d03b03
Adds support for RIGHT JOIN and increases performance
johnedquinn c9a6316
Adds support for FULL OUTER JOIN and improves performance
johnedquinn 4fc9dc3
Checks truth of condition for FULL OUTER JOIN
johnedquinn c033453
Updates name of RelJoinNestedLoop API to join()
johnedquinn ed21f96
Updates parameter name to be condition
johnedquinn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 0 additions & 65 deletions
65
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoin.kt
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.partiql.eval.internal.operator.rel | ||
|
||
import org.partiql.eval.internal.Record | ||
import org.partiql.eval.internal.operator.Operator | ||
import org.partiql.value.BoolValue | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
import org.partiql.value.StructValue | ||
import org.partiql.value.nullValue | ||
import org.partiql.value.structValue | ||
|
||
internal abstract class RelJoinNestedLoop : Operator.Relation { | ||
|
||
abstract val lhs: Operator.Relation | ||
abstract val rhs: Operator.Relation | ||
abstract val condition: Operator.Expr | ||
|
||
private var rhsRecord: Record? = null | ||
|
||
override fun open() { | ||
lhs.open() | ||
rhs.open() | ||
rhsRecord = rhs.next() | ||
} | ||
|
||
abstract fun join(condition: Boolean, lhs: Record, rhs: Record): Record? | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
override fun next(): Record? { | ||
var lhsRecord = lhs.next() | ||
var toReturn: Record? = null | ||
do { | ||
// Acquire LHS and RHS Records | ||
if (lhsRecord == null) { | ||
lhs.close() | ||
rhsRecord = rhs.next() ?: return null | ||
lhs.open() | ||
lhsRecord = lhs.next() | ||
} | ||
// Return Joined Record | ||
if (lhsRecord != null && rhsRecord != null) { | ||
val input = lhsRecord + rhsRecord!! | ||
val result = condition.eval(input) | ||
toReturn = join(result.isTrue(), lhsRecord, rhsRecord!!) | ||
} | ||
} | ||
while (toReturn == null) | ||
return toReturn | ||
} | ||
|
||
override fun close() { | ||
lhs.close() | ||
rhs.close() | ||
} | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
private fun PartiQLValue.isTrue(): Boolean { | ||
return this is BoolValue && this.value == true | ||
} | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
internal fun Record.padNull() { | ||
this.values.indices.forEach { index -> | ||
this.values[index] = values[index].padNull() | ||
} | ||
} | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
private fun PartiQLValue.padNull(): PartiQLValue { | ||
return when (this) { | ||
is StructValue<*> -> { | ||
val newFields = this.fields?.map { it.first to nullValue() } | ||
structValue(newFields) | ||
} | ||
else -> nullValue() | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinOuterFull.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package org.partiql.eval.internal.operator.rel | ||
|
||
import org.partiql.eval.internal.Record | ||
import org.partiql.eval.internal.operator.Operator | ||
|
||
/** | ||
* Here's a simple implementation of FULL OUTER JOIN. The idea is fairly straightforward: | ||
* Iterate through LHS. For each iteration of the LHS, iterate through RHS. Now, check the condition. | ||
* - If the condition passes, return the merged record (equivalent to result of INNER JOIN) | ||
* - If the condition does not pass, we need a way to return two records (one where the LHS is padded with nulls, and | ||
* one where the RHS is padded with nulls). How we do this: | ||
* - We maintain the [previousLhs] and [previousRhs]. If they are null, we then compute the next LHS and RHS. We | ||
* store their values in-memory. Then we return a merged Record where the LHS is padded and the RHS is not (equivalent | ||
* to result of RIGHT OUTER JOIN). | ||
* - If they aren't null, then we pad the RHS with NULLS (we assume we've already padded the LHS) and return (equivalent | ||
* to result of LEFT OUTER JOIN). We also make sure [previousLhs] and [previousRhs] are now null. | ||
* | ||
* Performance Analysis: Assume that [lhs] has size M and [rhs] has size N. | ||
* - Time: O(M * N) | ||
* - Space: O(1) | ||
*/ | ||
internal class RelJoinOuterFull( | ||
override val lhs: Operator.Relation, | ||
override val rhs: Operator.Relation, | ||
override val condition: Operator.Expr | ||
) : RelJoinNestedLoop() { | ||
|
||
private var previousLhs: Record? = null | ||
private var previousRhs: Record? = null | ||
|
||
override fun next(): Record? { | ||
if (previousLhs != null && previousRhs != null) { | ||
previousRhs!!.padNull() | ||
val newRecord = previousLhs!! + previousRhs!! | ||
previousLhs = null | ||
previousRhs = null | ||
return newRecord | ||
} | ||
return super.next() | ||
} | ||
|
||
/** | ||
* Specifically, for FULL OUTER JOIN, when the JOIN Condition ([condition]) is TRUE, we need to return the | ||
* rows merged (without modification). When the JOIN Condition ([condition]) is FALSE, we need to return | ||
* the LHS padded (and merged with RHS not padded) and the RHS padded (merged with the LHS not padded). | ||
*/ | ||
override fun join(condition: Boolean, lhs: Record, rhs: Record): Record { | ||
when (condition) { | ||
true -> { | ||
previousLhs = null | ||
previousRhs = null | ||
} | ||
false -> { | ||
previousLhs = lhs.copy() | ||
previousRhs = rhs.copy() | ||
lhs.padNull() | ||
} | ||
} | ||
return lhs + rhs | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinRight.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package org.partiql.eval.internal.operator.rel | ||
|
||
import org.partiql.eval.internal.Record | ||
import org.partiql.eval.internal.operator.Operator | ||
|
||
internal class RelJoinRight( | ||
lhs: Operator.Relation, | ||
rhs: Operator.Relation, | ||
override val condition: Operator.Expr | ||
) : RelJoinNestedLoop() { | ||
|
||
override val lhs: Operator.Relation = rhs | ||
override val rhs: Operator.Relation = lhs | ||
|
||
override fun join(condition: Boolean, lhs: Record, rhs: Record): Record { | ||
if (condition.not()) { | ||
lhs.padNull() | ||
} | ||
return lhs + rhs | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about checking the join condition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch! Updated and I added a test to use TRUE as the condition.