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

anorm.SqlResult.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package anorm

/** Parsed SQL result. */
sealed trait SqlResult[+A] { self =>
  // TODO: Review along with MayErr (unify?)

  def flatMap[B](k: A => SqlResult[B]): SqlResult[B] = self match {
    case Success(a)   => k(a)
    case e @ Error(_) => e
  }

  def map[B](f: A => B): SqlResult[B] = self match {
    case Success(a)   => Success(f(a))
    case e @ Error(_) => e
  }

  def collect[B](f: PartialFunction[A, B]): SqlResult[B] = self match {
    case Success(a) =>
      f.lift(a) match {
        case Some(b) =>
          Success(b)

        case None =>
          Error(SqlMappingError(s"Value ${a} is not matching"))
      }

    case Error(cause) =>
      Error(cause)
  }

  /**
   * Either applies function `e` if result is erroneous,
   * or function `f` with successful result if any.
   */
  def fold[B](e: SqlRequestError => B, f: A => B): B = self match {
    case Success(a) => f(a)
    case Error(err) => e(err)
  }
}

/** Successfully parsed result. */
case class Success[A](a: A) extends SqlResult[A]

/** Erroneous result (failure while parsing). */
case class Error(msg: SqlRequestError) extends SqlResult[Nothing]

private[anorm] trait WithResult {
  import java.sql.Connection
  import scala.util.Try

  /** ResultSet is initialized on first row (JDBC degraded) */
  def resultSetOnFirstRow: Boolean

  /** Returns underlying result set */
  protected def resultSet(connection: Connection): resource.ManagedResource[java.sql.ResultSet]

  /**
   * Aggregates over all rows using the specified operator.
   *
   * @param z the start value
   * @param op Aggregate operator
   * @return Either list of failures at left, or aggregated value
   * @see #foldWhile
   * @see #withResult
   */
  @deprecated(message = "Use `fold` with empty [[ColumnAliaser]]", since = "2.5.1")
  def fold[T](z: => T)(op: (T, Row) => T)(implicit connection: Connection): Either[List[Throwable], T] = {
    @annotation.tailrec
    def go(c: Option[Cursor], cur: T): T = c match {
      case Some(cursor) => go(cursor.next, op(cur, cursor.row))
      case _            => cur
    }

    withResult(go(_, z))
  }

  /**
   * Aggregates over all rows using the specified operator.
   *
   * @param z the start value
   * @param aliaser the column aliaser
   * @param op Aggregate operator
   * @return Either list of failures at left, or aggregated value
   * @see #foldWhile
   * @see #withResult
   */
  def fold[T](z: => T, aliaser: ColumnAliaser)(
      op: (T, Row) => T
  )(implicit connection: Connection): Either[List[Throwable], T] = {
    @annotation.tailrec
    def go(c: Option[Cursor], cur: T): T = c match {
      case Some(cursor) => go(cursor.next, op(cur, cursor.row))
      case _            => cur
    }

    withResult(go(_, z), aliaser)
  }

  /**
   * Aggregates over part of or the while row stream,
   * using the specified operator.
   *
   * @param z the start value
   * @param op Aggregate operator. Returns aggregated value along with true if aggregation must process next value, or false to stop with current value.
   * @return Either list of failures at left, or aggregated value
   * @see #withResult
   */
  @deprecated(message = "Use `foldWhile` with empty [[ColumnAliaser]]", since = "2.5.1")
  def foldWhile[T](
      z: => T
  )(op: (T, Row) => (T, Boolean))(implicit connection: Connection): Either[List[Throwable], T] = {
    @annotation.tailrec
    def go(c: Option[Cursor], cur: T): T = c match {
      case Some(cursor) =>
        val (v, cont) = op(cur, cursor.row)
        if (!cont) v else go(cursor.next, v)
      case _ => cur
    }

    withResult(go(_, z))
  }

  /**
   * Aggregates over part of or the while row stream,
   * using the specified operator.
   *
   * @param z the start value
   * @param aliaser the column aliaser
   * @param op Aggregate operator. Returns aggregated value along with true if aggregation must process next value, or false to stop with current value.
   * @return Either list of failures at left, or aggregated value
   * @see #withResult
   */
  def foldWhile[T](z: => T, aliaser: ColumnAliaser)(
      op: (T, Row) => (T, Boolean)
  )(implicit connection: Connection): Either[List[Throwable], T] = {
    @annotation.tailrec
    def go(c: Option[Cursor], cur: T): T = c match {
      case Some(cursor) =>
        val (v, cont) = op(cur, cursor.row)
        if (!cont) v else go(cursor.next, v)
      case _ => cur
    }

    withResult(go(_, z), aliaser)
  }

  /**
   * Processes all or some rows from current result.
   *
   * @param op Operation applied with row cursor
   * @param aliaser the column aliaser
   *
   * {{{
   * import java.sql.Connection
   * import anorm._
   *
   * @annotation.tailrec
   * def go(c: Option[Cursor], l: List[Row]): List[Row] = c match {
   *   case Some(cursor) => go(cursor.next, l :+ cursor.row)
   *   case _ => l
   * }
   *
   * def l(implicit con: Connection): Either[List[Throwable], List[Row]] =
   *   SQL"SELECT * FROM Test".withResult(go(_, List.empty))
   * }}}
   */
  def withResult[T](op: Option[Cursor] => T)(implicit connection: Connection): Either[List[Throwable], T] =
    withResult[T](op, ColumnAliaser.empty)

  /**
   * Processes all or some rows from current result.
   *
   * @param op Operation applied with row cursor
   *
   * {{{
   * import java.sql.Connection
   * import anorm._
   *
   * @annotation.tailrec
   * def go(c: Option[Cursor], l: List[Row]): List[Row] = c match {
   *   case Some(cursor) => go(cursor.next, l :+ cursor.row)
   *   case _ => l
   * }
   *
   * def l(implicit con: Connection): Either[List[Throwable], List[Row]] =
   *   SQL"SELECT * FROM Test".withResult(go(_, List.empty))
   * }}}
   */
  def withResult[T](op: Option[Cursor] => T, aliaser: ColumnAliaser)(implicit
      connection: Connection
  ): Either[List[Throwable], T] = {
    import resource.extractedEitherToEither

    Sql.withResult(resultSet(connection), resultSetOnFirstRow, aliaser)(op).acquireFor(identity)
  }

  /**
   * Converts this query result as `T`, using parser.
   *
   * @param parser the result parser
   * @see #asTry
   */
  @SuppressWarnings(Array("TryGet" /* TODO: Make it safer */ ))
  def as[T](parser: ResultSetParser[T])(implicit connection: Connection): T =
    asTry[T](parser, ColumnAliaser.empty).get

  /**
   * Converts this query result as `T`, using parser.
   *
   * @param parser the result parser
   * @param aliaser the column aliaser
   */
  def asTry[T](parser: ResultSetParser[T], aliaser: ColumnAliaser = ColumnAliaser.empty)(implicit
      connection: Connection
  ): Try[T] = Sql.asTry(parser, resultSet(connection), resultSetOnFirstRow, aliaser)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy