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

scalikejdbc.SQL.scala Maven / Gradle / Ivy

package scalikejdbc

import java.sql.PreparedStatement
import scala.language.higherKinds
import scala.collection.generic.CanBuildFrom

/**
 * SQL abstraction's companion object
 *
 * {{{
 *   ConnectionPool.singleton("jdbc:...","user","password")
 *   case class User(id: Int, name: String)
 *
 *   val users = DB.readOnly { implicit session =>
 *     SQL("select * from user").map { rs =>
 *       User(rs.int("id"), rs.string("name"))
 *     }.list.apply()
 *   }
 *
 *   DB .autoCommit { implicit session =>
 *     SQL("insert into user values (?,?)").bind(123, "Alice").update.apply()
 *   }
 *
 *   DB localTx { implicit session =>
 *     SQL("insert into user values (?,?)").bind(123, "Alice").update.apply()
 *   }
 *
 *   using(DB(ConnectionPool.borrow())) { db =>
 *     db.begin()
 *     try {
 *       DB withTx { implicit session =>
 *         SQL("update user set name = ? where id = ?").bind("Alice", 123).update.apply()
 *       }
 *       db.commit()
 *     } catch { case e =>
 *       db.rollbackIfActive()
 *       throw e
 *     }
 *   }
 * }}}
 */
object SQL {

  private[scalikejdbc] def noExtractor[A](message: String): WrappedResultSet => A = { (rs: WrappedResultSet) =>
    throw new IllegalStateException(message)
  }

  def apply[A](sql: String): SQL[A, NoExtractor] = new SQLToTraversableImpl[A, NoExtractor](sql, Seq.empty)(noExtractor[A](
    "If you see this message, it's a ScalikeJDBC's bug. Please report us."
  ))

}

/**
 * Name binding [[scalikejdbc.SQL]] instance factory.
 */
private[scalikejdbc] object validateAndConvertToNormalStatement extends LogSupport {

  def apply(sql: String, parameters: Seq[(Symbol, Any)]): (String, Seq[Any]) = {
    val names = SQLTemplateParser.extractAllParameters(sql)

    // check all the parameters passed by #bindByName are actually used
    import scalikejdbc.globalsettings._
    GlobalSettings.nameBindingSQLValidator.ignoredParams match {
      case NoCheckForIgnoredParams => // no op
      case validation =>
        parameters.foreach {
          param =>
            if (!names.contains(param._1)) {
              validation match {
                case NoCheckForIgnoredParams => // no op
                case InfoLoggingForIgnoredParams => log.info(ErrorMessage.BINDING_IS_IGNORED + " (" + param._1 + ")")
                case WarnLoggingForIgnoredParams => log.warn(ErrorMessage.BINDING_IS_IGNORED + " (" + param._1 + ")")
                case ExceptionForIgnoredParams => throw new IllegalStateException(ErrorMessage.BINDING_IS_IGNORED + " (" + param._1 + ")")
              }
            }
        }
    }

    val sqlWithPlaceHolders = SQLTemplateParser.convertToSQLWithPlaceHolders(sql)
    (sqlWithPlaceHolders, names.map { name =>
      parameters match {
        case Nil => Nil
        case _ =>
          parameters.find(_._1 == name).orElse {
            throw new IllegalArgumentException(ErrorMessage.BINDING_PARAMETER_IS_MISSING + " (" + name + ")")
          }.map(_._2).orNull[Any]
      }
    })
  }
}

/**
 * Represents an extractor is already specified or not
 */
sealed trait WithExtractor

/**
 * Represents that this SQL already has an extractor
 */
trait HasExtractor extends WithExtractor

/**
 * Represents that this SQL doesn't have an extractor yet
 */
trait NoExtractor extends WithExtractor

/**
 * Generalized type constraints for WithExtractor
 */
object GeneralizedTypeConstraintsForWithExtractor {

  // customized error message
  @annotation.implicitNotFound(msg = "No extractor is specified. You have forgotten call #map(...) before #apply().")
  sealed abstract class =:=[From, To] extends (From => To) with Serializable
  private[this] final val singleton_=:= = new =:=[WithExtractor, WithExtractor] { def apply(x: WithExtractor): WithExtractor = x }
  object =:= {
    implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
  }

}

/**
 * Extractor
 */
private[scalikejdbc] trait Extractor[A] {

  def extractor: (WrappedResultSet) => A

}

/**
 * SQL abstraction.
 *
 * @param statement SQL template
 * @param rawParameters parameters
 * @param f  extractor function
 * @tparam A return type
 */
abstract class SQL[A, E <: WithExtractor](
  val statement: String,
  private[scalikejdbc] val rawParameters: Seq[Any]
)(f: WrappedResultSet => A)
    extends Extractor[A] {

  final lazy val parameters: Seq[Any] = rawParameters.map {
    case ParameterBinder(v) => v
    case x => x
  }

  override def extractor: (WrappedResultSet) => A = f

  private[this] var _fetchSize: Option[Int] = None
  private[this] val _tags: scala.collection.mutable.ListBuffer[String] = new scala.collection.mutable.ListBuffer[String]()
  private[this] var _queryTimeout: Option[Int] = None

  type ThisSQL = SQL[A, E]
  type SQLWithExtractor = SQL[A, HasExtractor]

  protected def withParameters(params: Seq[Any]): SQL[A, E] = ???

  protected def withStatementAndParameters(state: String, params: Seq[Any]): SQL[A, E] = ???

  protected def withExtractor[B](f: WrappedResultSet => B): SQL[B, HasExtractor] = ???

  /**
   * Set fetchSize for this query.
   *
   * @param fetchSize fetch size
   * @return this
   */
  def fetchSize(fetchSize: Int): this.type = {
    this._fetchSize = Some(fetchSize)
    this
  }

  def fetchSize(fetchSize: Option[Int]): this.type = {
    this._fetchSize = fetchSize
    this
  }

  /**
   * Appends tags to this SQL object.
   *
   * @param tags tags
   * @return this
   */
  def tags(tags: String*): this.type = {
    this._tags ++= tags
    this
  }

  /**
   * Returns tags for this SQL object.
   *
   * @return tags
   */
  def tags: Seq[String] = this._tags.toSeq

  /**
   * Returns fetchSize for this query.
   *
   * @return fetch size
   */
  def fetchSize: Option[Int] = this._fetchSize

  /**
   * Set queryTimeout for this query.
   *
   * @param seconds query timeout seconds
   * @return this
   */
  def queryTimeout(seconds: Int): this.type = {
    this._queryTimeout = Some(seconds)
    this
  }

  def queryTimeout(seconds: Option[Int]): this.type = {
    this._queryTimeout = seconds
    this
  }

  /**
   * Returns queryTimeout for this query.
   *
   * @return query timeout seconds
   */
  def queryTimeout: Option[Int] = this._queryTimeout

  /**
   * Returns One-to-X API builder.
   */
  def one[Z](f: (WrappedResultSet) => A): OneToXSQL[A, E, Z] = {
    val q: OneToXSQL[A, E, Z] = new OneToXSQL[A, E, Z](statement, rawParameters)(f)
    q.queryTimeout(queryTimeout)
    q.fetchSize(fetchSize)
    q.tags(tags: _*)
    q
  }

  /**
   * Binds parameters to SQL template in order.
   *
   * @param parameters parameters
   * @return SQL instance
   */
  def bind(parameters: Any*): SQL[A, E] = {
    withParameters(parameters).fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Binds named parameters to SQL template.
   *
   * @param parametersByName named parameters
   * @return SQL instance
   */
  def bindByName(parametersByName: (Symbol, Any)*): SQL[A, E] = {
    val (_statement, _parameters) = validateAndConvertToNormalStatement(statement, parametersByName)
    withStatementAndParameters(_statement, _parameters).fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Binds parameters for batch
   *
   * @param parameters parameters
   * @return SQL for batch
   */
  def batch(parameters: Seq[Any]*): SQLBatch = {
    new SQLBatch(statement, parameters, tags)
  }

  /**
   * Binds parameters for batch
   *
   * @param parameters parameters
   * @return SQL for batch
   */
  def batchAndReturnGeneratedKey(parameters: Seq[Any]*): SQLBatchWithGeneratedKey = {
    new SQLBatchWithGeneratedKey(statement, parameters, tags)(None)
  }

  /**
   * Binds parameters for batch
   *
   * @param generatedKeyName generated key name
   * @param parameters parameters
   * @return SQL for batch
   */
  def batchAndReturnGeneratedKey(generatedKeyName: String, parameters: Seq[Any]*): SQLBatchWithGeneratedKey = {
    new SQLBatchWithGeneratedKey(statement, parameters, tags)(Some(generatedKeyName))
  }

  /**
   * Binds parameters for batch
   *
   * @param parameters parameters
   * @return SQL for batch
   */
  def batchByName(parameters: Seq[(Symbol, Any)]*): SQLBatch = {
    val _sql = validateAndConvertToNormalStatement(statement, parameters.headOption.getOrElse(Seq.empty))._1
    val _parameters: Seq[Seq[Any]] = parameters.map { p =>
      validateAndConvertToNormalStatement(statement, p)._2
    }
    new SQLBatch(_sql, _parameters, tags)
  }

  /**
   * Apply the operation to all elements of result set
   *
   * @param op operation
   */
  def foreach(op: WrappedResultSet => Unit)(implicit session: DBSession): Unit = {
    val f: DBSession => Unit =
      _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foreach(statement, rawParameters: _*)(op)
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

  /**
   * folding into one value
   *
   * @param z initial value
   * @param op operation
   */
  def foldLeft[A](z: A)(op: (A, WrappedResultSet) => A)(implicit session: DBSession): A = {
    val f: DBSession => A =
      _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foldLeft(statement, rawParameters: _*)(z)(op)
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

  /**
   * Maps values from each [[scalikejdbc.WrappedResultSet]] object.
   *
   * @param f extractor function
   * @tparam A return type
   * @return SQL instance
   */
  def map[A](f: WrappedResultSet => A): SQL[A, HasExtractor] = {
    withExtractor[A](f).fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Maps values as a Map value from each [[scalikejdbc.WrappedResultSet]] object.
   *
   * @return SQL instance
   */
  def toMap(): SQL[Map[String, Any], HasExtractor] = map(_.toMap)

  /**
   * Same as #single.
   *
   * @return SQL instance
   */
  def toOption(): SQLToOption[A, E] = {
    new SQLToOptionImpl[A, E](statement, rawParameters)(extractor)(isSingle = true)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Set execution type as single.
   *
   * @return SQL instance
   */
  def single(): SQLToOption[A, E] = toOption()

  /**
   * Same as #first.
   *
   * @return SQL instance
   */
  def headOption(): SQLToOption[A, E] = {
    new SQLToOptionImpl[A, E](statement, rawParameters)(extractor)(isSingle = false)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Set execution type as first.
   *
   * @return SQL instance
   */
  def first(): SQLToOption[A, E] = headOption()

  /**
   * Same as #list
   *
   * @return SQL instance
   */
  def toList(): SQLToList[A, E] = {
    new SQLToListImpl[A, E](statement, rawParameters)(extractor)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Set execution type as list.
   *
   * @return SQL instance
   */
  def list(): SQLToList[A, E] = toList()

  /**
   * Same as #collection
   *
   * @return SQL instance
   */
  def toCollection: SQLToCollection[A, E] = {
    new SQLToCollectionImpl[A, E](statement, rawParameters)(extractor)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Set execution type as collection.
   *
   * @return SQL instance
   */
  def collection: SQLToCollection[A, E] = toCollection

  /**
   * Same as #traversable.
   *
   * @return SQL instance
   */
  def toTraversable(): SQLToTraversable[A, E] = {
    new SQLToTraversableImpl[A, E](statement, rawParameters)(extractor)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)
  }

  /**
   * Set execution type as traversable.
   *
   * @return SQL instance
   */
  def traversable(): SQLToTraversable[A, E] = toTraversable()

  /**
   * Set execution type as execute
   *
   * @return SQL instance
   */
  def execute(): SQLExecution = {
    new SQLExecution(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {})
  }

  /**
   * Set execution type as execute with filters
   *
   * @param before before filter
   * @param after after filter
   * @return SQL instance
   */
  def executeWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit) = {
    new SQLExecution(statement, rawParameters, tags)(before)(after)
  }

  /**
   * Set execution type as executeUpdate
   *
   * @return SQL instance
   */
  def executeUpdate(): SQLUpdate = update()

  /**
   * Set execution type as executeUpdate with filters
   *
   * @param before before filter
   * @param after after filter
   * @return SQL instance
   */
  def executeUpdateWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLUpdate = {
    updateWithFilters(before, after)
  }

  /**
   * Set execution type as executeUpdate
   *
   * @return SQL instance
   */
  def update(): SQLUpdate = {
    new SQLUpdate(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {})
  }

  /**
   * Set execution type as executeUpdate with filters
   *
   * @param before before filter
   * @param after after filter
   * @return SQL instance
   */
  def updateWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLUpdate = {
    new SQLUpdate(statement, rawParameters, tags)(before)(after)
  }

  /**
   * Set execution type as updateAndReturnGeneratedKey
   *
   * @return SQL instance
   */
  def updateAndReturnGeneratedKey(): SQLUpdateWithGeneratedKey = {
    updateAndReturnGeneratedKey(1)
  }

  def updateAndReturnGeneratedKey(name: String): SQLUpdateWithGeneratedKey = {
    new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(name)
  }

  def updateAndReturnGeneratedKey(index: Int): SQLUpdateWithGeneratedKey = {
    new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(index)
  }

  def stripMargin(marginChar: Char): SQL[A, E] =
    withStatementAndParameters(statement.stripMargin(marginChar), rawParameters)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)

  def stripMargin: SQL[A, E] =
    withStatementAndParameters(statement.stripMargin, rawParameters)
      .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout)

}

/**
 * SQL which execute java.sql.Statement#executeBatch().
 *
 * @param statement SQL template
 * @param parameters parameters
 */
class SQLBatch(val statement: String, val parameters: Seq[Seq[Any]], val tags: Seq[String] = Nil) {

  def apply[C[_]]()(implicit session: DBSession, cbf: CanBuildFrom[Nothing, Int, C[Int]]): C[Int] = {
    val f: DBSession => C[Int] = _.tags(tags: _*).batch(statement, parameters: _*)
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

}

class SQLBatchWithGeneratedKey(val statement: String, val parameters: Seq[Seq[Any]], val tags: Seq[String] = Nil)(key: Option[String]) {

  def apply[C[_]]()(implicit session: DBSession, cbf: CanBuildFrom[Nothing, Long, C[Long]]): C[Long] = {
    val f: DBSession => C[Long] = (session) => {
      key match {
        case Some(k) => session.tags(tags: _*).batchAndReturnSpecifiedGeneratedKey(statement, k, parameters: _*)
        case _ => session.tags(tags: _*).batchAndReturnGeneratedKey(statement, parameters: _*)
      }
    }
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

}

/**
 * SQL which execute java.sql.Statement#execute().
 *
 * @param statement SQL template
 * @param parameters parameters
 * @param before before filter
 * @param after after filter
 */
class SQLExecution(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
    before: (PreparedStatement) => Unit
)(
    after: (PreparedStatement) => Unit
) {

  def apply()(implicit session: DBSession): Boolean = {
    val f: DBSession => Boolean = _.tags(tags: _*).executeWithFilters(before, after, statement, parameters: _*)
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

}

/**
 * SQL which execute java.sql.Statement#executeUpdate().
 *
 * @param statement SQL template
 * @param parameters parameters
 * @param before before filter
 * @param after after filter
 */
class SQLUpdate(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
    before: (PreparedStatement) => Unit
)(
    after: (PreparedStatement) => Unit
) {

  def apply()(implicit session: DBSession): Int = session match {
    case AutoSession =>
      DB.autoCommit(_.tags(tags: _*).updateWithFilters(before, after, statement, parameters: _*))
    case NamedAutoSession(name) =>
      NamedDB(name).autoCommit(_.tags(tags: _*).updateWithFilters(before, after, statement, parameters: _*))
    case ReadOnlyAutoSession =>
      DB.readOnly(_.tags(tags: _*).updateWithFilters(before, after, statement, parameters: _*))
    case ReadOnlyNamedAutoSession(name) =>
      NamedDB(name).readOnly(_.tags(tags: _*).updateWithFilters(before, after, statement, parameters: _*))
    case _ =>
      session.tags(tags: _*).updateWithFilters(before, after, statement, parameters: _*)
  }

}

/**
 * SQL which execute java.sql.Statement#executeUpdate() and get generated key value.
 *
 * @param statement SQL template
 * @param parameters parameters
 */
class SQLUpdateWithGeneratedKey(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(key: Any) {

  def apply()(implicit session: DBSession): Long = {
    val f: DBSession => Long = _.tags(tags: _*).updateAndReturnSpecifiedGeneratedKey(statement, parameters: _*)(key)
    // format: OFF
    session match {
      case AutoSession                    => DB.autoCommit(f)
      case NamedAutoSession(name)         => NamedDB(name).autoCommit(f)
      case ReadOnlyAutoSession            => DB.readOnly(f)
      case ReadOnlyNamedAutoSession(name) => NamedDB(name).readOnly(f)
      case _                              => f(session)
    }
    // format: ON
  }

}

trait SQLToResult[A, E <: WithExtractor, C[_]] extends SQL[A, E] with Extractor[A] {
  import GeneralizedTypeConstraintsForWithExtractor._

  def result[AA](f: WrappedResultSet => AA, session: DBSession): C[AA]
  val statement: String
  private[scalikejdbc] val rawParameters: Seq[Any]
  def apply()(
    implicit
    session: DBSession,
    context: ConnectionPoolContext = NoConnectionPoolContext,
    hasExtractor: ThisSQL =:= SQLWithExtractor
  ): C[A] = {
    val f: DBSession => C[A] = s => result[A](extractor, s.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout))
    // format: OFF
    session match {
      case AutoSession | ReadOnlyAutoSession => DB.readOnly(f)
      case NamedAutoSession(name)            => NamedDB(name).readOnly(f)
      case ReadOnlyNamedAutoSession(name)    => NamedDB(name).readOnly(f)
      case _                                 => f(session)
    }
    // format: ON
  }

}

/**
 * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.Traversable value.
 *
 * @tparam A return type
 */
trait SQLToTraversable[A, E <: WithExtractor] extends SQLToResult[A, E, Traversable] {

  def result[AA](f: WrappedResultSet => AA, session: DBSession): Traversable[AA] = {
    session.traversable[AA](statement, rawParameters: _*)(f)
  }

}

/**
 * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.Traversable value.
 *
 * @param statement SQL template
 * @param rawParameters parameters
 * @param extractor  extractor function
 * @tparam A return type
 */
class SQLToTraversableImpl[A, E <: WithExtractor](
  override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any]
)(
  override val extractor: WrappedResultSet => A
)
    extends SQL[A, E](statement, rawParameters)(extractor)
    with SQLToTraversable[A, E] {

  override protected def withParameters(params: Seq[Any]): SQLToResult[A, E, Traversable] = {
    new SQLToTraversableImpl[A, E](statement, params)(extractor)
  }

  override protected def withStatementAndParameters(state: String, params: Seq[Any]): SQLToResult[A, E, Traversable] = {
    new SQLToTraversableImpl[A, E](state, params)(extractor)
  }

  override protected def withExtractor[B](f: WrappedResultSet => B): SQLToResult[B, HasExtractor, Traversable] = {
    new SQLToTraversableImpl[B, HasExtractor](statement, rawParameters)(f)
  }

}

/**
 * SQL to Collection
 *
 * @tparam A return type
 * @tparam E extractor settings
 */
trait SQLToCollection[A, E <: WithExtractor] extends SQL[A, E] with Extractor[A] {
  import GeneralizedTypeConstraintsForWithExtractor._
  val statement: String
  private[scalikejdbc] val rawParameters: Seq[Any]
  def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, A, C[A]]): C[A] = {
    val f: DBSession => C[A] = _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).collection[A, C](statement, rawParameters: _*)(extractor)
    // format: OFF
    session match {
      case AutoSession | ReadOnlyAutoSession => DB.readOnly(f)
      case NamedAutoSession(name)            => NamedDB(name).readOnly(f)
      case ReadOnlyNamedAutoSession(name)    => NamedDB(name).readOnly(f)
      case _                                 => f(session)
    }
    // format: ON
  }

}

class SQLToCollectionImpl[A, E <: WithExtractor](
  override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any]
)(
  override val extractor: WrappedResultSet => A
)
    extends SQL[A, E](statement, rawParameters)(extractor)
    with SQLToCollection[A, E] {

  override protected def withParameters(params: Seq[Any]): SQLToCollection[A, E] = {
    new SQLToCollectionImpl[A, E](statement, params)(extractor)
  }

  override protected def withStatementAndParameters(state: String, params: Seq[Any]): SQLToCollection[A, E] = {
    new SQLToCollectionImpl[A, E](state, params)(extractor)
  }

  override protected def withExtractor[B](f: WrappedResultSet => B): SQLToCollection[B, HasExtractor] = {
    new SQLToCollectionImpl[B, HasExtractor](statement, rawParameters)(f)
  }

}

/**
 * SQL to List
 *
 * @tparam A return type
 * @tparam E extractor settings
 */
trait SQLToList[A, E <: WithExtractor] extends SQLToResult[A, E, List] {

  def result[AA](f: WrappedResultSet => AA, session: DBSession): List[AA] = {
    session.list[AA](statement, rawParameters: _*)(f)
  }

}

/**
 * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.immutable.List value.
 *
 * @param statement SQL template
 * @param rawParameters parameters
 * @param extractor  extractor function
 * @tparam A return type
 */
class SQLToListImpl[A, E <: WithExtractor](
  override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any]
)(
  override val extractor: WrappedResultSet => A
)
    extends SQL[A, E](statement, rawParameters)(extractor)
    with SQLToList[A, E] {

  override protected def withParameters(params: Seq[Any]): SQLToList[A, E] = {
    new SQLToListImpl[A, E](statement, params)(extractor)
  }

  override protected def withStatementAndParameters(state: String, params: Seq[Any]): SQLToList[A, E] = {
    new SQLToListImpl[A, E](state, params)(extractor)
  }

  override protected def withExtractor[B](f: WrappedResultSet => B): SQLToList[B, HasExtractor] = {
    new SQLToListImpl[B, HasExtractor](statement, rawParameters)(f)
  }

}

/**
 * SQL to Option
 *
 * @tparam A return type
 * @tparam E extractor settings
 */
trait SQLToOption[A, E <: WithExtractor] extends SQLToResult[A, E, Option] {

  protected def isSingle: Boolean

  def result[AA](f: WrappedResultSet => AA, session: DBSession): Option[AA] = {
    if (isSingle) {
      session.single[AA](statement, rawParameters: _*)(f)
    } else {
      session.first[AA](statement, rawParameters: _*)(f)
    }
  }

}

/**
 * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.Option value.
 *
 * @param statement SQL template
 * @param rawParameters parameters
 * @param extractor  extractor function
 * @tparam A return type
 */
class SQLToOptionImpl[A, E <: WithExtractor](
  override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any]
)(
  override val extractor: WrappedResultSet => A
)(protected val isSingle: Boolean = true)
    extends SQL[A, E](statement, rawParameters)(extractor)
    with SQLToOption[A, E] {

  override protected def withParameters(params: Seq[Any]): SQLToOption[A, E] = {
    new SQLToOptionImpl[A, E](statement, params)(extractor)(isSingle)
  }

  override protected def withStatementAndParameters(state: String, params: Seq[Any]): SQLToOption[A, E] = {
    new SQLToOptionImpl[A, E](state, params)(extractor)(isSingle)
  }

  override protected def withExtractor[B](f: WrappedResultSet => B): SQLToOption[B, HasExtractor] = {
    new SQLToOptionImpl[B, HasExtractor](statement, rawParameters)(f)(isSingle)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy