org.squeryl.dsl.ast.QueryExpressionNode.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of squeryl_2.13 Show documentation
Show all versions of squeryl_2.13 Show documentation
A Scala ORM and DSL for talking with Databases using minimum verbosity and maximum type safety
The newest version!
/*******************************************************************************
* Copyright 2010 Maxime Lévesque
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************** */
package org.squeryl.dsl.ast
import org.squeryl.internals._
import org.squeryl.dsl.{QueryYield, AbstractQuery}
class QueryExpressionNode[R](
val _query: AbstractQuery[R],
_queryYield: QueryYield[R],
val subQueries: Iterable[QueryableExpressionNode],
val views: Iterable[ViewExpressionNode[_]]
) extends QueryExpressionElements
with QueryableExpressionNode {
private[squeryl] def cteRoot: Option[QueryExpressionElements] = {
def loop(current: Option[ExpressionNode]): Option[QueryExpressionElements] = {
current.flatMap { c =>
c match {
case value: QueryExpressionNode[_] =>
value.commonTableExpressions.find(sameRoot_?).orElse(loop(c.parent))
case _ =>
loop(c.parent)
}
}
}
loop(parent)
}
private[squeryl] def sameRoot_?(e: QueryExpressionNode[_]) =
_query.root.isDefined && _query.root == e._query.root
def tableExpressions: Iterable[QueryableExpressionNode] =
List(views.filter(v => !v.inhibited), subQueries.filter(v => !v.inhibited)).flatten
def isJoinForm = _queryYield.joinExpressions != Nil
val (whereClause, havingClause, groupByClause, orderByClause, ctes) =
_queryYield.queryElements
val commonTableExpressions: List[QueryExpressionNode[_]] = ctes.map { q =>
q.ast match {
case x: QueryExpressionNode[_] =>
x
case _ =>
Utils.throwError(
"A common table expression AST must be a QueryExpressionNode, not a " +
q.getClass.getSimpleName
)
}
}.toList
private[this] val unionClauses =
_query.unions map (kindAndQ => new UnionExpressionNode(kindAndQ._1, kindAndQ._2.ast))
private[this] var _selectList: Iterable[SelectElement] = Iterable.empty
private[this] var _sample: Option[AnyRef] = None
private def _isPrimitiveType(o: AnyRef) = // AnyRef can not be primitive.
List(
"java.lang.Boolean",
"java.lang.Character",
"java.lang.Byte",
"java.lang.Short",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Float",
"java.lang.Double",
"java.lang.Void"
).contains(o.getClass.getName)
def isUseableAsSubquery: Boolean =
_sample match {
case None => throw new IllegalStateException("method cannot be called before initialization")
case Some(p: Product) =>
if (p.getClass.getName.startsWith("scala.Tuple")) {
!(p.productIterator.exists(o => _isPrimitiveType(o.asInstanceOf[AnyRef])))
} else
true
case Some(a: AnyRef) => !_isPrimitiveType(a)
}
def sample: AnyRef = _sample.get
def owns(aSample: AnyRef) =
_sample.isDefined && _sample.get.eq(aSample)
def getOrCreateSelectElement(fmd: FieldMetaData, forScope: QueryExpressionElements): SelectElement =
throw new UnsupportedOperationException("implement me")
override def toString = {
val sb = new java.lang.StringBuilder
sb.append("'QueryExpressionNode[")
if (_query.isRoot)
sb.append("root:")
sb.append(id)
sb.append("]")
sb.append(":rsm=" + _query.resultSetMapper)
sb.toString
}
override def children =
List(
selectList.toList,
commonTableExpressions,
views.toList,
subQueries.toList,
tableExpressions.flatMap(_.joinExpression).toList,
whereClause.toList,
groupByClause.toList,
havingClause.toList,
orderByClause.toList,
unionClauses
).flatten
def isChild(q: QueryableExpressionNode): Boolean =
views.exists(n => n == q)
def selectDistinct = _query.selectDistinct
def isForUpdate = _query.isForUpdate
def page = _query.page
def unionIsForUpdate = _query.unionIsForUpdate
def unionPage = _query.unionPage
def alias = "q" + uniqueId.get
def getOrCreateAllSelectElements(forScope: QueryExpressionElements): Iterable[SelectElement] = {
_selectList.map(se => new ExportedSelectElement(se))
}
private def hasUnionQueryOptions = unionIsForUpdate || unionPage.isDefined
def setOutExpressionNodesAndSample(sl: Iterable[SelectElement], s: AnyRef) = {
_selectList = sl
_sample = Some(s)
if (_query.isRoot) {
var jdbcIndex = 1
for (oen <- selectList) {
oen.prepareMapper(jdbcIndex)
jdbcIndex += 1
}
var idGen = 0
visitDescendants((node, parent, i) => {
node.parent = parent
node match {
case nxn: UniqueIdInAliaseRequired =>
nxn.uniqueId = Some(idGen)
idGen += 1
case _ =>
}
})
if (commonTableExpressions.nonEmpty) {
relabelCtes()
}
}
}
private def relabelCtes(): Unit = {
val cteNodes = commonTableExpressions.map { cte =>
(cte, collectAliasedNodes(cte))
}
for {
ref <- collectCteRefs()
(cte, src) <- cteNodes.find(_._1.sameRoot_?(ref))
} {
copyUniqueIds(src, collectAliasedNodes(ref))
}
}
private def collectCteRefs(): List[QueryExpressionNode[_]] = {
val buf = new collection.mutable.ArrayBuffer[QueryExpressionNode[_]]()
visitDescendants((node, parent, i) => {
if (
!commonTableExpressions.contains(node) &&
node.isInstanceOf[QueryExpressionNode[_]] &&
commonTableExpressions.exists(e => e.sameRoot_?(node.asInstanceOf[QueryExpressionNode[_]]))
) {
buf += node.asInstanceOf[QueryExpressionNode[_]]
}
})
buf.toList
}
private def collectAliasedNodes(e: ExpressionNode): List[UniqueIdInAliaseRequired] = {
val buf = new collection.mutable.ArrayBuffer[UniqueIdInAliaseRequired]()
e.visitDescendants((node, parent, i) => {
if ((node ne e) && node.isInstanceOf[UniqueIdInAliaseRequired]) {
buf += node.asInstanceOf[UniqueIdInAliaseRequired]
}
})
buf.toList
}
private def copyUniqueIds(src: List[UniqueIdInAliaseRequired], dest: List[UniqueIdInAliaseRequired]): Unit = {
src.zip(dest).collect { case (e1, e2) => e2.uniqueId = e1.uniqueId }
}
def selectList: Iterable[SelectElement] = _selectList
def doWrite(sw: StatementWriter) = {
def writeCompleteQuery = {
val isNotRoot = parent.isDefined
val isContainedInUnion = parent map (_.isInstanceOf[UnionExpressionNode]) getOrElse (false)
if ((isNotRoot && !isContainedInUnion) || hasUnionQueryOptions) {
sw.write("(")
sw.indent(1)
}
if (unionClauses.nonEmpty) {
sw.write("(")
sw.nextLine
sw.indent(1)
}
sw.databaseAdapter.writeQuery(this, sw)
if (unionClauses.nonEmpty) {
sw.unindent(1)
sw.write(")")
sw.nextLine
}
unionClauses.foreach { u =>
u.write(sw)
}
if ((isNotRoot && !isContainedInUnion) || hasUnionQueryOptions) {
sw.unindent(1)
sw.write(") ")
}
if (hasUnionQueryOptions) {
sw.databaseAdapter.writeUnionQueryOptions(this, sw)
}
}
if (sw.databaseAdapter.supportsCommonTableExpressions) {
cteRoot.map { r =>
sw.databaseAdapter.writeCteReference(sw, r)
}.getOrElse(writeCompleteQuery)
} else {
writeCompleteQuery
}
}
}