Skip to content
Permalink
Browse files

Emulate empty join conditions where needed.

Appending the dummy condition "on 1=1" works almost everywhere, except
in Access, where the condition has to reference columns from both sides
of the join, so we have to thread the dummy "1" values through the
sub-queries.

Fixes Typesafe support ticket 3140.
Test in JoinTest.testNoJoinCondition.
  • Loading branch information
szeiger committed Mar 5, 2014
1 parent f8a64da commit c78ed2f72d1b3bd767f6fdc381e98083149725b3
@@ -128,4 +128,21 @@ class JoinTest extends TestkitTest[RelationalTestDB] {
q2.run.foreach(x => println(" "+x))
assertEquals(List((1,-1), (2,1), (3,2), (4,2)), q2.run)
}

def testNoJoinCondition {
class T(tag: Tag) extends Table[Int](tag, "t_nojoincondition") {
def id = column[Int]("id")
def * = id
}
lazy val ts = TableQuery[T]
ts.ddl.create
val q1 = ts leftJoin ts
q1.run
ifCap(rcap.joinRight) {
val q2 = ts rightJoin ts
q2.run
}
val q3 = ts innerJoin ts
q3.run
}
}
@@ -120,6 +120,24 @@ trait AccessDriver extends JdbcDriver { driver =>
override protected val hasPiFunction = false
override protected val hasRadDegConversion = false

protected final case class StarAnd(child: Node) extends UnaryNode with SimplyTypedNode {
type Self = StarAnd
protected[this] def nodeRebuild(child: Node) = StarAnd(child)
protected def buildType = UnassignedType
}

protected def extendWithDummyColumn(c: Comprehension, rn: AnonSymbol): Comprehension = c.select match {
case Some(Pure(StructNode(ch), _)) =>
c.copy(select = Some(Pure(StructNode(ch :+ (rn -> LiteralNode(1))))), fetch = None, offset = None)
case Some(Pure(ProductNode(ch), _)) =>
c.copy(select = Some(Pure(StructNode(ch.toIndexedSeq.map(n => newSym -> n) :+ (rn -> LiteralNode(1))))), fetch = None, offset = None)
case Some(Pure(n, _)) =>
c.copy(select = Some(Pure(StructNode(IndexedSeq(newSym -> n, rn -> LiteralNode(1))))), fetch = None, offset = None)
case None =>
// should not happen at the outermost layer, so copying an extra row does not matter
c.copy(select = Some(Pure(StructNode(IndexedSeq(rn -> StarAnd(LiteralNode(1)))))), fetch = None, offset = None)
}

override protected def buildComprehension(c: Comprehension) =
if(c.offset.isDefined) throw new SlickException("Access does not support drop(...) calls")
else super.buildComprehension(c)
@@ -128,7 +146,21 @@ trait AccessDriver extends JdbcDriver { driver =>
if(!c.fetch.isEmpty) b"top ${c.fetch.get} "
}

override protected def buildFrom(n: Node, alias: Option[Symbol], skipParens: Boolean = false): Unit = building(FromPart) {
n match {
case j @ Join(leftGen, rightGen, left: Comprehension, right: Comprehension, jt, LiteralNode(true)) =>
val sym = new AnonSymbol
buildFrom(extendWithDummyColumn(left, sym), Some(leftGen))
b" ${jt.sqlName} join "
buildFrom(extendWithDummyColumn(right, sym), Some(rightGen))
val on = Apply(Library.==, Seq(Select(Ref(leftGen), sym), Select(Ref(rightGen), sym)))(ScalaBaseType.booleanType)
b" on !$on"
case n => super.buildFrom(n, alias, skipParens)
}
}

override def expr(c: Node, skipParens: Boolean = false): Unit = c match {
case StarAnd(ch) => b"*, !$ch"
case c: ConditionalExpr => {
b"switch("
var first = true
@@ -42,6 +42,7 @@ trait HsqldbDriver extends JdbcDriver { driver =>
class QueryBuilder(tree: Node, state: CompilerState) extends super.QueryBuilder(tree, state) with OracleStyleRowNum {
override protected val scalarFrom = Some("(VALUES (0))")
override protected val concatOperator = Some("||")
override protected val supportsEmptyJoinConditions = false

override def expr(c: Node, skipParens: Boolean = false): Unit = c match {
case l @ LiteralNode(v: String) if (v ne null) && typeInfoFor(l.tpe).sqlType != Types.CHAR =>
@@ -34,6 +34,7 @@ trait JdbcStatementBuilderComponent { driver: JdbcDriver =>
protected val scalarFrom: Option[String] = None
protected val supportsTuples = true
protected val supportsCast = true
protected val supportsEmptyJoinConditions = true
protected val concatOperator: Option[String] = None
protected val hasPiFunction = true
protected val hasRadDegConversion = true
@@ -168,6 +169,7 @@ trait JdbcStatementBuilderComponent { driver: JdbcDriver =>
buildFrom(right, Some(rightGen))
on match {
case LiteralNode(true) =>
if(!supportsEmptyJoinConditions) b" on 1=1"
case _ => b" on !$on"
}
case Union(left, right, all, _, _) =>
@@ -49,6 +49,7 @@ trait MySQLDriver extends JdbcDriver { driver =>
class QueryBuilder(tree: Node, state: CompilerState) extends super.QueryBuilder(tree, state) {
override protected val scalarFrom = Some("DUAL")
override protected val supportsCast = false
override protected val supportsEmptyJoinConditions = false

final case class RowNum(sym: AnonSymbol, inc: Boolean) extends NullaryNode with TypedNode {
type Self = RowNum
@@ -46,6 +46,7 @@ trait PostgresDriver extends JdbcDriver { driver =>

class QueryBuilder(tree: Node, state: CompilerState) extends super.QueryBuilder(tree, state) {
override protected val concatOperator = Some("||")
override protected val supportsEmptyJoinConditions = false

override protected def buildFetchOffsetClause(fetch: Option[Long], offset: Option[Long]) = (fetch, offset) match {
case (Some(t), Some(d)) => b" limit $t offset $d"

0 comments on commit c78ed2f

Please sign in to comment.
You can’t perform that action at this time.