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

anorm.TopLevelDefinitions.scala Maven / Gradle / Ivy

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

package anorm

import java.util.StringTokenizer

import java.lang.reflect.InvocationTargetException
import java.sql.{ PreparedStatement, ResultSet, SQLException }

import scala.reflect.ClassTag

private[anorm] trait TopLevelDefinitions extends PackageCompat {

  /** 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

      case ie: InvocationTargetException => {
        val cause = ie.getCause

        if (cause != null) {
          throw cause
        }

        throw ie
      }
    }
  }

  /** 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

      case ie: InvocationTargetException => {
        val cause = ie.getCause

        if (cause != null) {
          throw cause
        }

        throw ie
      }
    }
  }

  /** 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

      case ie: InvocationTargetException => {
        val cause = ie.getCause

        if (cause != null) {
          throw cause
        }

        throw ie
      }
    }
  }

  /**
   * 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

  @annotation.tailrec
  private[anorm] final 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
            }).collect {
              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[PreparedStatement] {
    def close(stmt: PreparedStatement) = stmt.close()

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

  private[anorm] lazy val statementClassTag =
    implicitly[ClassTag[PreparedStatement]]

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

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

  private[anorm] lazy val resultSetClassTag = implicitly[ClassTag[ResultSet]]

  /** Activable features */
  object features {

    /**
     * Column conversion that will accept `Byte` and `Short` values to represent booleans.
     * This is useful if the underlying database or driver does not support boolean datatype.
     * For example, the MariaDB JDBC driver v3 will return TINYINT metadata even for columns declared as BOOLEAN.
     *
     * 0 is false, 1 is true, anything else is an error.
     *
     * Note that the column is not limited to byte or short, boolean types are transparently accepted as well.
     *
     * {{{
     * import anorm.features.columnByteToBoolean
     * }}}
     */

    implicit val columnByteToBoolean: Column[Boolean] = Column.columnByteToBoolean
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy