All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.squeryl.dsl.ast.SelectElement.scala Maven / Gradle / Ivy

Go to download

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.Session
import org.squeryl.dsl.TypedExpression
import scala.annotation.tailrec

/**
 * SelectElement are elements of a select list, for example they are a,b, and c in :
 *
 * select a,b,c from T
 *
 * they are either
 *  ValueSelectElement for composite expressions, i.e. select (x / 2) * y as Z from ....
 *  TupleSelectElement for group by or compute elements (TODO: document group by/compute)
 *  FieldSelectElement for table columns (that map to fields)
 *
 *  ExportSelectElement for a select element that refers to a SelectElement of an inner or outer query.
 *
 * SelectElementReference are nodes in any clause other than select (where, having, composite expression, order by, etc)
 *  that refer to a SelectElement  
 */
trait SelectElement extends ExpressionNode {
  outer =>

  /**
   * 
   * In the following select :
   *
   *   select t.x from t
   *
   *  t.x is a select element and t is it's origin
   *
   * Here q.z1 is a SelectElement who's origin is t
   *
   *   select q.z1
   *   from
   *     (select t.x as z1 from t) q
   *
   * 
*/ def origin: QueryableExpressionNode def parentQueryable = parent.get.asInstanceOf[QueryableExpressionNode] def resultSetMapper: ResultSetMapper def alias: String def aliasSegment: String = alias def actualSelectElement: SelectElement = this /** * Update, Insert, and Delete statements are always at the root of an AST, so they * are never aliased, but then can have sub queries, ex.: update ... where x in (subquery). * Name clashes are impossible since SelectElements of query are always aliased. * */ def inhibitAliasOnSelectElementReference: Boolean = { def shouldInhibit(e: ExpressionNode): Boolean = e.parent.forall { p => p match { case elements: QueryExpressionElements => elements.inhibitAliasOnSelectElementReference case _ => shouldInhibit(p) } } shouldInhibit(origin) } def realTableNamePrefix: Boolean = { def parent(e: ExpressionNode): ExpressionNode = (e.parent map (p => parent(p))) getOrElse e val p = parent(origin) p.isInstanceOf[UpdateStatement] || p.isInstanceOf[QueryExpressionElements] } def prepareColumnMapper(index: Int): Unit def prepareMapper(jdbcIndex: Int): Unit override def inhibited = origin.inhibited def isActive = _isActive protected[squeryl] var _isActive = false def expression: ExpressionNode /** * strictly for logging purposes, i.e. to display a more explicit AST */ def typeOfExpressionToString: String override def children = List(expression) def doWrite(sw: StatementWriter) = { expression.write(sw) sw.write(" as ") sw.databaseAdapter.writeSelectElementAlias(this, sw) } } class TupleSelectElement( val origin: QueryExpressionNode[_], val expression: ExpressionNode, indexInTuple: Int, isGroupTuple: Boolean ) extends SelectElement { def resultSetMapper: ResultSetMapper = throw new UnsupportedOperationException("refactor me") // TODO: normalize ? def alias = if (isGroupTuple) "g" + indexInTuple else "c" + indexInTuple var columnToTupleMapper: Option[ColumnToTupleMapper] = None def prepareColumnMapper(index: Int) = {} def typeOfExpressionToString: String = if (columnToTupleMapper.isEmpty) "unknown" else columnToTupleMapper.get.typeOfExpressionToString(indexInTuple) override def prepareMapper(jdbcIndex: Int) = if (columnToTupleMapper.isDefined) columnToTupleMapper.get.activate(indexInTuple, jdbcIndex) override def toString = "'TupleSelectElement:" + indexInTuple + ":" + writeToString } class FieldSelectElement( val origin: ViewExpressionNode[_], val fieldMetaData: FieldMetaData, val resultSetMapper: ResultSetMapper ) extends SelectElement with UniqueIdInAliaseRequired { def alias = if (inhibitAliasOnSelectElementReference) if (realTableNamePrefix) origin.view.name + "." + fieldMetaData.columnName else fieldMetaData.columnName else origin.alias + "." + fieldMetaData.columnName override def aliasSegment: String = Session.currentSession.databaseAdapter.fieldAlias(origin, this) // origin.alias + "_" + fieldMetaData.columnName val expression: ExpressionNode = new ExpressionNode { def doWrite(sw: StatementWriter) = sw.write(sw.quoteName(alias)) } def prepareColumnMapper(index: Int) = columnMapper = Some(new ColumnToFieldMapper(index, fieldMetaData, this)) private[this] var columnMapper: Option[ColumnToFieldMapper] = None def prepareMapper(jdbcIndex: Int) = if (columnMapper.isDefined) { resultSetMapper.addColumnMapper(columnMapper.get) resultSetMapper.isActive = true _isActive = true } def typeOfExpressionToString = fieldMetaData.displayType override def toString = "'FieldSelectElement:" + Utils.failSafeString(alias, fieldMetaData.nameOfProperty) } class ValueSelectElement( val expression: ExpressionNode, val resultSetMapper: ResultSetMapper, mapper: OutMapper[_], val origin: QueryExpressionNode[_] ) extends SelectElement with UniqueIdInAliaseRequired { def alias = "v" + uniqueId.get var yieldPusher: Option[YieldValuePusher] = None def prepareColumnMapper(index: Int) = yieldPusher = Some(new YieldValuePusher(index, this, mapper)) def typeOfExpressionToString = if (yieldPusher.isEmpty) "unknown" else yieldPusher.get.selectElement.typeOfExpressionToString override def prepareMapper(jdbcIndex: Int) = if (yieldPusher.isDefined) { resultSetMapper.addYieldValuePusher(yieldPusher.get) resultSetMapper.isActive = true _isActive = true } override def toString = "'ValueSelectElement:" + expression.writeToString } /** * All nodes that refer to a SelectElement are SelectElementReference, * with the exception of SelectElement that refer to an inner or outer query's SelectElement, * these are ExportedSelectElement */ class SelectElementReference[A, T](val selectElement: SelectElement, val mapper: OutMapper[A]) extends TypedExpression[A, T] { override def toString = "'SelectElementReference:" + Utils.failSafeString( delegateAtUseSite.alias ) + ":" + selectElement.typeOfExpressionToString + inhibitedFlagForAstDump override def inhibited = selectElement.inhibited private def _useSite: QueryExpressionNode[_] = { @tailrec def findQueryExpressionNode(e: ExpressionNode): QueryExpressionNode[_] = e match { case qe: QueryExpressionNode[_] => qe case _ => e.parent match { case Some(e_) => findQueryExpressionNode(e_) case _ => org.squeryl.internals.Utils.throwError("could not determine use site of " + this) } } findQueryExpressionNode(this) } lazy val delegateAtUseSite = if (selectElement.parent.isEmpty) selectElement else { val us = this._useSite if (selectElement.parentQueryable == us) selectElement else { val ese = new ExportedSelectElement(this.selectElement) ese.parent = Some(us) ese } } override def doWrite(sw: StatementWriter) = sw.write(sw.quoteName(delegateAtUseSite.alias)) } /** * SelectElement that refer to a SelectElement of an inner or outer query */ class ExportedSelectElement(val selectElement: SelectElement) extends SelectElement { def resultSetMapper = selectElement.resultSetMapper override def inhibited = selectElement.inhibited override def prepareMapper(jdbcIndex: Int) = selectElement.prepareMapper(jdbcIndex) def prepareColumnMapper(index: Int) = selectElement.prepareColumnMapper(index) def typeOfExpressionToString = selectElement.typeOfExpressionToString def origin = selectElement.origin val expression: ExpressionNode = new ExpressionNode { def doWrite(sw: StatementWriter) = sw.write(sw.quoteName(alias)) } override def toString = "'ExportedSelectElement:" + alias + ",(selectElement=" + selectElement + ")" def alias: String = if (isDirectOuterReference) selectElement.alias else target.parent.get.asInstanceOf[QueryableExpressionNode].alias + "." + target.aliasSegment override def aliasSegment: String = // target.parent.get.asInstanceOf[QueryableExpressionNode].alias + "_" + target.aliasSegment if (isDirectOuterReference) selectElement.aliasSegment else Session.currentSession.databaseAdapter .aliasExport(target.parent.get.asInstanceOf[QueryableExpressionNode], target) /** * A root level query that has nested queries (or refers to queries in an outer scope) will * have SelectElements that are ExportedSelectElement, the 'actualSelectElement' points directly * to the referred AST node, while 'target' refers to it indirectly (see target) */ override def actualSelectElement: SelectElement = selectElement.actualSelectElement /** * target points to the selectElement that this ExportSelectElement refers to, who can * also be an ExportSelectElement, whose target will point to its inner select element, * recursively, until it becomes equal to the 'end' target, the actualSelectElement * In other words : * exportSelectElement.target.target.,...,.target == exportSelectElement.actualSelectElement */ lazy val target: SelectElement = innerTarget.getOrElse( outerTarget.getOrElse(org.squeryl.internals.Utils.throwError("could not find the target of : " + selectElement)) ) private def outerScopes: List[QueryExpressionNode[_]] = outerScopes0(this, Nil) @tailrec private def outerScopes0( current: ExpressionNode, scopes: List[QueryExpressionNode[_]] ): List[QueryExpressionNode[_]] = { current.parent match { case Some(s: QueryExpressionNode[_]) => outerScopes0(s, scopes :+ s) case Some(n) => outerScopes0(n, scopes) case None => scopes } } private def isDirectOuterReference: Boolean = outerScopes.contains(selectElement.parentQueryable) private def outerTarget: Option[SelectElement] = { val q = for ( outer <- outerScopes; subQuery <- outer.subQueries; se <- subQuery.asInstanceOf[QueryExpressionElements].selectList if se == selectElement || se.actualSelectElement == selectElement ) yield se q.headOption } private def innerTarget: Option[SelectElement] = if (parent.isEmpty) None else { val parentOfThis = parent.get.asInstanceOf[QueryExpressionElements] if (selectElement.origin.parent.get == parentOfThis) { Some(selectElement) } else { val q = for ( q <- parentOfThis.subQueries; se <- q.asInstanceOf[QueryExpressionElements].selectList if se == selectElement || se.actualSelectElement == selectElement ) yield se q.headOption } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy