Skip to content
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
16 changes: 14 additions & 2 deletions modules/sql-core/src/main/scala/FailedJoin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
package grackle.sql

/**
* A sentinal value representing the empty column values from a failed join.
* A sentinel value representing the empty column values from a failed join.
*/
case object FailedJoin
case object FailedJoin {

/**
* Cheap equality check for the `FailedJoin` sentinel.
*
* Column values are `Any` and may be a `scala.math.BigDecimal`, whose `equals` throws and
* catches an `ArithmeticException` whenever compared to a value of another type (see
* `BigDecimal.isValidLong`). `a == b` calls `a.equals(b)`, so keeping `FailedJoin` on the
* left runs the singleton's own cheap `equals` instead of the column value's -- do not
* swap the operands here.
*/
def isFailedJoin(v: Any): Boolean = FailedJoin == v
}
21 changes: 13 additions & 8 deletions modules/sql-core/src/main/scala/SqlMapping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import grackle.Predicate._
import grackle.Query._
import grackle.ValidationFailure.Severity
import grackle.circe.CirceMappingLike
import grackle.sql.FailedJoin.isFailedJoin
import grackle.syntax._

abstract class SqlMapping[F[_]](implicit val M: MonadThrow[F])
Expand Down Expand Up @@ -4267,6 +4268,10 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self
}

object Table {
// Same rationale as `FailedJoin.isFailedJoin`: keeps `None` on the left of `==` so its
// cheap equals runs instead of a possibly-`BigDecimal` column value's.
private def isNone(v: Any): Boolean = None == v

def apply(rows: Vector[Array[Any]]): Table = {
if (rows.sizeCompare(1) == 0) OneRowTable(rows.head)
else if (rows.isEmpty) EmptyTable
Expand Down Expand Up @@ -4304,7 +4309,7 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self

def definesAll(cols: List[Int]): Boolean = {
val cs = cols
cs.forall(c => row(c) != FailedJoin)
cs.forall(c => !isFailedJoin(row(c)))
}

def group(cols: List[Int]): Iterator[Table] = {
Expand Down Expand Up @@ -4337,10 +4342,10 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self
while (ir.hasNext) {
ir.next()(c) match {
case FailedJoin =>
case v if value == FailedJoin => value = v
case v if isFailedJoin(value) => value = v
case v if value == v =>
case None =>
case v @ Some(_) if value == None => value = v
case v @ Some(_) if isNone(value) => value = v
case _ => return None
}
}
Expand All @@ -4349,12 +4354,12 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self

def filterDefined(cols: List[Int]): Table = {
val cs = cols
Table(rows.filter(r => cs.forall(c => r(c) != FailedJoin)))
Table(rows.filter(r => cs.forall(c => !isFailedJoin(r(c)))))
}

def definesAll(cols: List[Int]): Boolean = {
val cs = cols
rows.exists(r => cs.forall(c => r(c) != FailedJoin))
rows.exists(r => cs.forall(c => !isFailedJoin(r(c))))
}

def group(cols: List[Int]): Iterator[Table] = {
Expand All @@ -4369,7 +4374,7 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self
case cs => row => cs.map(c => row(c))
}

val nonNull = rows.filter(r => cs.forall(c => r(c) != FailedJoin))
val nonNull = rows.filter(r => cs.forall(c => !isFailedJoin(r(c))))
val grouped = nonNull.groupBy(discrim)
grouped.iterator.map { case (_, rows) => Table(rows) }
}
Expand All @@ -4387,7 +4392,7 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self
case cs => row => cs.map(c => row(c))
}

val nonNull = rows.filter(r => cs.forall(c => r(c) != FailedJoin))
val nonNull = rows.filter(r => cs.forall(c => !isFailedJoin(r(c))))
nonNull.map(discrim).distinct.size
}
}
Expand Down Expand Up @@ -4532,7 +4537,7 @@ trait SqlMappingLike[F[_]] extends CirceMappingLike[F] with SqlModule[F] { self
case Some(f) if tpe.variantField(fieldName) && !fieldTpe.isNullable => f
case other => other
}
assert(leafFocus != FailedJoin)
assert(!isFailedJoin(leafFocus))
LeafCursor(fieldContext, leafFocus, Some(np), Env.empty)
})

Expand Down
Loading