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

anorm.package.scala Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/*
 * Copyright (C) 2009-2013 Typesafe Inc. 
 */

import java.util.StringTokenizer

import java.sql.SQLException

/**
 * Anorm API
 *
 * Use the SQL method to start an SQL query
 *
 * {{{
 * import anorm._
 *
 * SQL("Select 1")
 * }}}
 */
package object anorm {
  import scala.language.implicitConversions

  /** Structural type for timestamp wrapper. */
  type TimestampWrapper1 = { def getTimestamp: java.sql.Timestamp }

  object TimestampWrapper1 {
    import scala.language.reflectiveCalls

    @SuppressWarnings(Array("AsInstanceOf"))
    def unapply(that: Any): Option[java.sql.Timestamp] = try {
      Some(that.asInstanceOf[TimestampWrapper1].getTimestamp)
    } catch {
      case _: NoSuchMethodException => None
    }
  }

  /** Structural type for oracle.sql.TIMESTAMP wrapper. */
  type TimestampWrapper2 = { def timestampValue: java.sql.Timestamp }

  object TimestampWrapper2 {
    import scala.language.reflectiveCalls

    @SuppressWarnings(Array("AsInstanceOf"))
    def unapply(that: Any): Option[java.sql.Timestamp] = try {
      Some(that.asInstanceOf[TimestampWrapper2].timestampValue)
    } catch {
      case _: NoSuchMethodException => None
      case _: SQLException => None
    }
  }

  /** Structural type for oracle.sql.ROWID wrapper. */
  type StringWrapper2 = { def stringValue: String }

  object StringWrapper2 {
    import scala.language.reflectiveCalls

    @SuppressWarnings(Array("AsInstanceOf"))
    def unapply(that: Any): Option[String] = try {
      Some(that.asInstanceOf[StringWrapper2].stringValue)
    } catch {
      case _: NoSuchMethodException => None
      case _: SQLException => None
    }
  }

  // TODO: Review implicit usage there
  // (add explicit functions on SqlQuery?)
  implicit def sqlToSimple(sql: SqlQuery): SimpleSql[Row] = sql.asSimple

  /**
   * Creates an SQL query with given statement.
   * @param stmt SQL statement
   *
   * {{{
   * val query = anorm.SQL("SELECT * FROM Country")
   * }}}
   */
  @SuppressWarnings(Array("MethodNames", "TryGet" /* TODO: Make it safer */ ))
  def SQL(stmt: String): SqlQuery = SqlStatementParser.parse(stmt).
    map(ts => SqlQuery.prepare(ts, ts.names)).get

  /**
   * Creates an SQL query using String Interpolation feature.
   * It is a 1-step alternative for SQL("...").on(...) functions.
   *
   * {{{
   * import java.util.Date
   * import java.sql.Connection
   *
   * import anorm._
   *
   * case class Computer(
   *   name: String,
   *   introduced: Date,
   *   discontinued: Date,
   *   companyId: String)
   *
   * def foo(computer: Computer, id: String)(implicit con: Connection) =
   *   SQL"""
   *     UPDATE computer SET name = \\${computer.name},
   *     introduced = \\${computer.introduced},
   *     discontinued = \\${computer.discontinued},
   *     company_id = \\${computer.companyId}
   *     WHERE id = \\$id
   *   """.executeUpdate()
   * }}}
   */
  implicit class SqlStringInterpolation(val sc: StringContext) extends AnyVal {
    def SQL(args: ParameterValue*) = {
      val (ts, ps) = TokenizedStatement.stringInterpolation(sc.parts, args)
      SimpleSql(SqlQuery.prepare(ts, ts.names), ps, RowParser(Success(_)))
    }
  }

  @annotation.tailrec
  private[anorm] def tokenize(ti: Iterator[Any], tks: List[StatementToken], parts: Seq[String], ps: Seq[ParameterValue], gs: Seq[TokenGroup], ns: Seq[String], m: Map[String, ParameterValue]): (TokenizedStatement, Map[String, ParameterValue]) = if (ti.hasNext) ti.next() match {
    case "%" => tokenize(ti, PercentToken :: tks, parts, ps, gs, ns, m)
    case s: String =>
      tokenize(ti, StringToken(s) :: tks, parts, ps, gs, ns, m)
    case _ => /* should not occur */ tokenize(ti, tks, parts, ps, gs, ns, m)
  }
  else {
    if (tks.nonEmpty) {
      gs match {
        case prev :: groups => ps.headOption match {
          case Some(v) => prev match {
            case TokenGroup(StringToken(str) :: gts, pl) if (
              str endsWith "#" /* escaped part */ ) =>

              val before = if (str == "#") gts.reverse else {
                StringToken(str dropRight 1) :: gts.reverse
              }
              val ng = TokenGroup((tks ::: StringToken(v.show) ::
                before), pl)

              tokenize(ti, tks.tail, parts, ps.tail, (ng :: groups), ns, m)

            case _ =>
              val ng = TokenGroup(tks, None)
              val n = '_'.toString + ns.size
              tokenize(ti, tks.tail, parts, ps.tail,
                (ng :: prev.copy(placeholder = Some(n)) :: groups),
                (n +: ns), m + (n -> v))
          }
          case _ =>
            sys.error(s"No parameter value for placeholder: ${gs.size}")
        }
        case _ => tokenize(ti, tks.tail, parts, ps,
          List(TokenGroup(tks, None)), ns, m)
      }
    } else parts.headOption match {
      case Some(part) =>
        val it = Compat.javaEnumIterator[java.lang.Object](
          new StringTokenizer(part, "%", true))

        if (!it.hasNext /* empty */ ) {
          tokenize(it, List(StringToken("")), parts.tail, ps, gs, ns, m)
        } else tokenize(it, tks, parts.tail, ps, gs, ns, m)

      case _ =>
        val groups = ((gs match {
          case TokenGroup(List(StringToken("")), None) :: tgs => tgs // trim end
          case _ => gs
        }) map {
          case TokenGroup(pr, pl) => TokenGroup(pr.reverse, pl)
        }).reverse

        TokenizedStatement(groups, ns.reverse) -> m
    }
  }

  // Optimized resource typeclass not using reflection
  object StatementResource
    extends resource.Resource[java.sql.PreparedStatement] {

    def close(stmt: java.sql.PreparedStatement) = stmt.close()

    @deprecated("Deprecated by Scala-ARM upgrade", "2.5.4")
    def fatalExceptions = Seq[Class[_]](classOf[Exception])
  }

  // Optimized resource typeclass not using reflection
  object ResultSetResource extends resource.Resource[java.sql.ResultSet] {
    def close(rs: java.sql.ResultSet) = rs.close()

    @deprecated("Deprecated by Scala-ARM upgrade", "2.5.4")
    def fatalExceptions = Seq[Class[_]](classOf[Exception])
  }

  /** Activable features */
  object features {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy