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

anorm.Row.scala Maven / Gradle / Ivy

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

package anorm

import scala.util.{ Success => TrySuccess, Try }

trait Row {
  private[anorm] def metaData: MetaData

  /** Raw data */
  private[anorm] val data: List[Any]

  /**
   * Returns row as list of column values.
   *
   * {{{
   * // Row first column is string "str", second one is integer 2
   * def l(row: anorm.Row): List[Any] = row.asList
   * // l == List[Any]("str", 2)
   * }}}
   *
   * @see #as
   */
  lazy val asList: List[Any] = Compat
    .lazyZip(data, metaData.ms)
    .map { (v, m) =>
      if (m.nullable) Option(v) else v
    }
    .toList

  /**
   * Returns row as dictionary of value per column name
   *
   * {{{
   * // Row column named 'A' is string "str", column named 'B' is integer 2
   * def m(row: anorm.Row): Map[String, Any] = row.asMap
   * // l == Map[String, Any]("table.A" -> "str", "table.B" -> 2)
   * }}}
   *
   * @see #as
   */
  lazy val asMap: Map[String, Any] =
    Compat.toMap(Compat.lazyZip(data, metaData.ms)) {
      case (v, m) =>
        val k = m.column.qualified

        if (m.nullable) k -> Option(v) else k -> v
    }

  /**
   * Returns row as `T`.
   *
   * {{{
   * import anorm._, SqlParser.{ int, str }
   *
   * def foo(implicit con: java.sql.Connection) = {
   *  val parseOnlyFirstRow =
   *      SQL"SELECT * FROM Table".withResult(_.map(_.row.as(
   *       str("foo") ~ int(2) map {
   *         case a ~ b => b -> a
   *       })))
   *   // Either[List[Throwable], Option[Try[(Int, String)]]]
   *
   *   val optionalParseRes =
   *     parseOnlyFirstRow.right.map(_.flatMap(_.toOption))
   *     // Either[List[Throwable], Option[(Int, String)]]
   * }
   * }}}
   *
   * @param parser Row parser
   */
  def as[T](parser: RowParser[T]): Try[T] =
    parser(this).fold(_.toFailure, TrySuccess(_))

  /**
   * Returns parsed column.
   *
   * @param name Column name
   * @param c Column mapping
   *
   * {{{
   * import anorm._, Column.columnToString // mapping column to string
   *
   * val res = SQL("SELECT * FROM Test").map { row =>
   *   // string columns 'code' and 'label'
   *   row[String]("code") -> row[String]("label")
   * }
   * }}}
   */
  def apply[B](name: String)(implicit c: Column[B]): B =
    unsafeGet(SqlParser.get(name)(c))

  /**
   * Returns parsed column.
   *
   * @param position Column position from 1 to n
   * @param c Column mapping
   *
   * {{{
   * import anorm._, Column.columnToString // mapping column to string
   *
   * val res = SQL("SELECT * FROM Test").map { row =>
   *   row(1) -> row(2) // string columns #1 and #2
   * }
   * }}}
   */
  def apply[B](position: Int)(implicit c: Column[B]): B =
    unsafeGet(SqlParser.get(position)(c))

  @inline def unsafeGet[T](rowparser: => RowParser[T]): T =
    rowparser(this) match {
      case Success(v) => v
      case Error(err) => throw err.toFailure.exception
    }

  // Data per column name
  private lazy val columnsDictionary: Map[String, Any] =
    Compat.toMap(Compat.lazyZip(metaData.ms, data)) {
      case (m, v) =>
        m.column.qualified.toUpperCase -> v
    }

  // Data per column alias
  private lazy val aliasesDictionary: Map[String, Any] = {
    @annotation.tailrec
    def loop(meta: Seq[MetaDataItem], dt: List[Any], r: Map[String, Any]): Map[String, Any] = (meta, dt) match {
      case (m :: ms, d :: ds) => loop(ms, ds, m.column.alias.fold(r) { c => r + (c.toUpperCase -> d) })
      case _                  => r
    }

    loop(metaData.ms, data, Map.empty)
  }

  /**
   * Try to get data matching name.
   * @param a Column qualified name, or label/alias
   */
  private[anorm] def get(a: String): Either[SqlRequestError, (Any, MetaDataItem)] =
    Compat.rightFlatMap(metaData.get(a.toUpperCase).toRight(ColumnNotFound(a, this))) { m =>
      def d = if (a.indexOf(".") > 0) {
        // if expected to be a qualified (dotted) name
        columnsDictionary.get(m.column.qualified.toUpperCase).orElse(m.column.alias.flatMap(aliasesDictionary.get(_)))

      } else {
        m.column.alias
          .flatMap(a => aliasesDictionary.get(a.toUpperCase))
          .orElse(columnsDictionary.get(m.column.qualified.toUpperCase))
      }

      Compat.rightMap(d.toRight(ColumnNotFound(m.column.qualified, metaData.availableColumns))) { _ -> m }
    }

  /** Try to get data matching index. */
  private[anorm] def getIndexed(i: Int): Either[SqlRequestError, (Any, MetaDataItem)] =
    Compat.rightFlatMap(metaData.ms.lift(i).toRight(ColumnNotFound(s"#${i + 1}", metaData.availableColumns))) { m =>
      Compat.rightMap(data.lift(i).toRight(ColumnNotFound(m.column.qualified, metaData.availableColumns))) { _ -> m }
    }
}

/** Companion object for row. */
object Row {

  /**
   * Row extractor.
   *
   * {{{
   * import java.util.Locale
   *
   * import anorm._
   *
   * def l(implicit con: java.sql.Connection): Option[Locale] =
   *   SQL("Select name,population from Country").
   *     as(RowParser[Option[Locale]] {
   *       case Row("France", _) => Success(Some(Locale.FRANCE))
   *       case _ => Success(Option.empty[Locale])
   *     }.single)
   * }}}
   */
  def unapplySeq(row: Row): Option[List[Any]] = Some(row.asList)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy