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

scalikejdbc.interpolation.SQLSyntax.scala Maven / Gradle / Ivy

The newest version!
package scalikejdbc
package interpolation

import java.util.Locale.ENGLISH

/**
 * Value as a part of SQL syntax.
 *
 * This value won't be treated as a binding parameter but will be appended as a part of SQL.
 *
 * Note: The constructor should NOT be used by library users at the considerable risk of SQL injection vulnerability.
 * [[https://github.com/scalikejdbc/scalikejdbc/issues/116]]
 */
class SQLSyntax private[scalikejdbc] (
  val value: String,
  private[scalikejdbc] val rawParameters: collection.Seq[Any] = Vector()
) {
  import Implicits._
  import SQLSyntax._

  override def equals(that: Any): Boolean = {
    that match {
      case thatSqls: SQLSyntax =>
        value == thatSqls.value && rawParameters == thatSqls.rawParameters
      case _ =>
        false
    }
  }

  override def hashCode: Int = (value, rawParameters).##

  override def toString(): String =
    s"SQLSyntax(value: ${value}, parameters: ${parameters})"

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

  def append(syntax: SQLSyntax): SQLSyntax = sqls"${this} ${syntax}"
  def +(syntax: SQLSyntax): SQLSyntax = this.append(syntax)

  def groupBy(columns: SQLSyntax*): SQLSyntax = {
    if (columns.isEmpty) this else sqls"${this} group by ${toCSV(columns)}"
  }
  def having(condition: SQLSyntax): SQLSyntax =
    sqls"${this} having ${condition}"

  def orderBy(columns: SQLSyntax*): SQLSyntax = {
    if (columns.isEmpty) this else sqls"${this} order by ${toCSV(columns)}"
  }
  def asc: SQLSyntax = sqls"${this} asc"
  def desc: SQLSyntax = sqls"${this} desc"

  def limit(n: Int): SQLSyntax = sqls"${this} limit ${SQLSyntax(n.toString)}"
  def offset(n: Int): SQLSyntax = sqls"${this} offset ${SQLSyntax(n.toString)}"

  def where: SQLSyntax = sqls"${this} where"
  def where(where: SQLSyntax): SQLSyntax = sqls"${this} where ${where}"
  def where(whereOpt: Option[SQLSyntax]): SQLSyntax =
    whereOpt.fold(this)(where(_))

  def and: SQLSyntax = sqls"${this} and"
  def and(sqlPart: SQLSyntax): SQLSyntax = sqls"$this and ($sqlPart)"
  def and(andOpt: Option[SQLSyntax]): SQLSyntax = andOpt.fold(this)(and(_))
  def or: SQLSyntax = sqls"${this} or"
  def or(sqlPart: SQLSyntax): SQLSyntax = sqls"$this or ($sqlPart)"
  def or(orOpt: Option[SQLSyntax]): SQLSyntax = orOpt.fold(this)(or(_))

  def roundBracket(inner: SQLSyntax): SQLSyntax = sqls"$this ($inner)"

  def eq[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = {
    value match {
      case null | None => sqls"${this} ${column} is null"
      case _           => sqls"${this} ${column} = ${ev(value)}"
    }
  }
  def ne[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = {
    value match {
      case null | None => sqls"${this} ${column} is not null"
      case _           => sqls"${this} ${column} <> ${ev(value)}"
    }
  }
  def gt[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = sqls"${this} ${column} > ${ev(value)}"
  def ge[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = sqls"${this} ${column} >= ${ev(value)}"
  def lt[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = sqls"${this} ${column} < ${ev(value)}"
  def le[A](column: SQLSyntax, value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = sqls"${this} ${column} <= ${ev(value)}"

  def isNull(column: SQLSyntax): SQLSyntax = sqls"${this} ${column} is null"
  def isNotNull(column: SQLSyntax): SQLSyntax =
    sqls"${this} ${column} is not null"

  def between[A, B, C](a: A, b: B, c: C)(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C]
  ): SQLSyntax = sqls"${this} ${ev1(a)} between ${ev2(b)} and ${ev3(c)}"
  def notBetween[A, B, C](a: A, b: B, c: C)(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C]
  ): SQLSyntax = sqls"${this} not ${ev1(a)} between ${ev2(b)} and ${ev3(c)}"

  def in[A](column: SQLSyntax, values: collection.Seq[A])(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = {
    if (values.isEmpty) {
      sqls"${this} FALSE"
    } else {
      sqls"${this} ${column} in (${values.map(ev.apply)})"
    }
  }
  def notIn[A](column: SQLSyntax, values: collection.Seq[A])(implicit
    ev: ParameterBinderFactory[A]
  ): SQLSyntax = {
    if (values.isEmpty) {
      sqls"${this} TRUE"
    } else {
      sqls"${this} ${column} not in (${values.map(ev.apply)})"
    }
  }

  def in(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax =
    sqls"${this} ${column} in (${subQuery})"
  def notIn(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax =
    sqls"${this} ${column} not in (${subQuery})"

  def in[A, B](
    columns: (SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} FALSE"
    } else {
      val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value})")
      val values = toCSV(valueSeqs.map { case (v1, v2) =>
        sqls"(${ev1(v1)}, ${ev2(v2)})"
      })
      val inClause = sqls"${column} in (${values})"
      sqls"${this} ${inClause}"
    }
  }
  def notIn[A, B](
    columns: (SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} TRUE"
    } else {
      val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value})")
      val values = toCSV(valueSeqs.map { case (v1, v2) =>
        sqls"(${ev1(v1)}, ${ev2(v2)})"
      })
      val inClause = sqls"${column} not in (${values})"
      sqls"${this} ${inClause}"
    }
  }

  def in[A, B, C](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} FALSE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)})"
      })
      val inClause = sqls"${column} in (${values})"
      sqls"${this} ${inClause}"
    }
  }
  def notIn[A, B, C](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} TRUE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)})"
      })
      val inClause = sqls"${column} not in (${values})"
      sqls"${this} ${inClause}"
    }
  }

  def in[A, B, C, D](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C],
    ev4: ParameterBinderFactory[D]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} FALSE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3, v4) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)})"
      })
      val inClause = sqls"${column} in (${values})"
      sqls"${this} ${inClause}"
    }
  }
  def notIn[A, B, C, D](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C],
    ev4: ParameterBinderFactory[D]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} TRUE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3, v4) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)})"
      })
      val inClause = sqls"${column} not in (${values})"
      sqls"${this} ${inClause}"
    }
  }

  def in[A, B, C, D, E](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D, E)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C],
    ev4: ParameterBinderFactory[D],
    ev5: ParameterBinderFactory[E]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} FALSE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value}, ${columns._5.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3, v4, v5) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)}, ${ev5(v5)})"
      })
      val inClause = sqls"${column} in (${values})"
      sqls"${this} ${inClause}"
    }
  }
  def notIn[A, B, C, D, E](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D, E)]
  )(implicit
    ev1: ParameterBinderFactory[A],
    ev2: ParameterBinderFactory[B],
    ev3: ParameterBinderFactory[C],
    ev4: ParameterBinderFactory[D],
    ev5: ParameterBinderFactory[E]
  ): SQLSyntax = {
    if (valueSeqs.isEmpty) {
      sqls"${this} TRUE"
    } else {
      val column = SQLSyntax(
        s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value}, ${columns._5.value})"
      )
      val values = toCSV(valueSeqs.map { case (v1, v2, v3, v4, v5) =>
        sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)}, ${ev5(v5)})"
      })
      val inClause = sqls"${column} not in (${values})"
      sqls"${this} ${inClause}"
    }
  }

  def like(column: SQLSyntax, value: String): SQLSyntax =
    sqls"${this} ${column} like ${value}"
  def notLike(column: SQLSyntax, value: String): SQLSyntax =
    sqls"${this} ${column} not like ${value}"

  def exists(sqlPart: SQLSyntax): SQLSyntax = sqls"${this} exists (${sqlPart})"
  def notExists(sqlPart: SQLSyntax): SQLSyntax =
    sqls"${this} not exists (${sqlPart})"

  def lower(column: SQLSyntax): SQLSyntax = sqls"lower($column)"
  def upper(column: SQLSyntax): SQLSyntax = sqls"upper($column)"

  def stripMargin: SQLSyntax = new SQLSyntax(value.stripMargin, rawParameters)

  def stripMargin(marginChar: Char): SQLSyntax =
    new SQLSyntax(value.stripMargin(marginChar), rawParameters)

  def ->[A](value: A)(implicit
    ev: ParameterBinderFactory[A]
  ): (SQLSyntax, ParameterBinder) = (this, ev(value))
  def ->(value: ParameterBinder): (SQLSyntax, ParameterBinder) = (this, value)
}

/**
 * A trait which has #resultAll: SQLSyntax
 */
trait ResultAllProvider {
  def resultAll: SQLSyntax
}

/**
 * A trait which has #asterisk: SQLSyntax
 */
trait AsteriskProvider {
  def asterisk: SQLSyntax
}

/*
 * SQLSyntax companion object
 */
object SQLSyntax {

  private val andRegExp = ".+\\s+and\\s+.+".r
  private val orRegExp = ".+\\s+or\\s+.+".r

  import Implicits._

  val empty: SQLSyntax = sqls""

  // #apply method should NOT be used by library users at the considerable risk of SQL injection vulnerability.
  // https://github.com/scalikejdbc/scalikejdbc/issues/116
  private[scalikejdbc] def apply(
    value: String,
    parameters: collection.Seq[Any] = Nil
  ): SQLSyntax = new SQLSyntax(value, parameters)

  /**
   * WARNING: Be aware of SQL injection vulnerability.
   */
  def createUnsafely(
    value: String,
    parameters: collection.Seq[Any] = Nil
  ): SQLSyntax = apply(value, parameters)

  def unapply(syntax: SQLSyntax): Some[(String, collection.Seq[Any])] = Some(
    (syntax.value, syntax.rawParameters)
  )

  def join(
    parts: collection.Seq[SQLSyntax],
    delimiter: SQLSyntax,
    spaceBeforeDelimiter: Boolean = true
  ): SQLSyntax = {
    val sep = if (spaceBeforeDelimiter) {
      s" ${delimiter.value} "
    } else {
      s"${delimiter.value} "
    }
    val value =
      parts.collect { case p if p.value.nonEmpty => p.value }.mkString(sep)
    val parameters = if (delimiter.rawParameters.isEmpty) {
      parts.flatMap(_.rawParameters)
    } else {
      parts.tail.foldLeft(
        parts.headOption.fold(collection.Seq.empty[Any])(_.rawParameters)
      ) { case (params, part) =>
        params ++ delimiter.rawParameters ++ part.rawParameters
      }
    }
    apply(value, parameters)
  }
  def csv(parts: SQLSyntax*): SQLSyntax = toCSV(parts)
  private[scalikejdbc] def toCSV(
    parts: scala.collection.Seq[SQLSyntax]
  ): SQLSyntax = join(parts, sqls",", false)

  private[this] def hasAndOr(s: SQLSyntax): Boolean = {
    val statement = s.value.toLowerCase(ENGLISH)
    andRegExp.pattern.matcher(statement).matches ||
    orRegExp.pattern.matcher(statement).matches
  }

  def joinWithAnd(parts: SQLSyntax*): SQLSyntax =
    join(parts.map(p => if (hasAndOr(p)) sqls"(${p})" else p), sqls"and")
  def joinWithOr(parts: SQLSyntax*): SQLSyntax =
    join(parts.map(p => if (hasAndOr(p)) sqls"(${p})" else p), sqls"or")

  def groupBy(columns: SQLSyntax*): SQLSyntax =
    SQLSyntax.empty.groupBy(columns.filterNot(_.value.trim.isEmpty)*)
  def having(condition: SQLSyntax): SQLSyntax =
    SQLSyntax.empty.having(condition)

  def orderBy(columns: SQLSyntax*): SQLSyntax =
    SQLSyntax.empty.orderBy(columns.filterNot(_.value.trim.isEmpty)*)
  val asc: SQLSyntax = SQLSyntax.empty.asc
  val desc: SQLSyntax = SQLSyntax.empty.desc

  def limit(n: Int): SQLSyntax = SQLSyntax.empty.limit(n)
  def offset(n: Int): SQLSyntax = SQLSyntax.empty.offset(n)

  val where: SQLSyntax = SQLSyntax.empty.where
  def where(where: SQLSyntax): SQLSyntax = SQLSyntax.empty.where(where)
  def where(whereOpt: Option[SQLSyntax]): SQLSyntax =
    SQLSyntax.empty.where(whereOpt)

  def and: SQLSyntax = SQLSyntax.empty.and
  def and(sqlPart: SQLSyntax): SQLSyntax = SQLSyntax.empty.and(sqlPart)
  def and(andOpt: Option[SQLSyntax]): SQLSyntax = SQLSyntax.empty.and(andOpt)
  def or: SQLSyntax = SQLSyntax.empty.or
  def or(sqlPart: SQLSyntax): SQLSyntax = SQLSyntax.empty.or(sqlPart)
  def or(orOpt: Option[SQLSyntax]): SQLSyntax = SQLSyntax.empty.or(orOpt)

  def eq[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.eq(column, value)
  def ne[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.ne(column, value)
  def gt[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.gt(column, value)
  def ge[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.ge(column, value)
  def lt[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.lt(column, value)
  def le[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax =
    SQLSyntax.empty.le(column, value)

  def isNull(column: SQLSyntax): SQLSyntax = SQLSyntax.empty.isNull(column)
  def isNotNull(column: SQLSyntax): SQLSyntax =
    SQLSyntax.empty.isNotNull(column)
  def between[A: ParameterBinderFactory, B: ParameterBinderFactory](
    column: SQLSyntax,
    a: A,
    b: B
  ): SQLSyntax = SQLSyntax.empty.between(column, a, b)
  def notBetween[A: ParameterBinderFactory, B: ParameterBinderFactory](
    column: SQLSyntax,
    a: A,
    b: B
  ): SQLSyntax = SQLSyntax.empty.notBetween(column, a, b)

  def in[A: ParameterBinderFactory](
    column: SQLSyntax,
    values: collection.Seq[A]
  ): SQLSyntax = SQLSyntax.empty.in(column, values)
  def notIn[A: ParameterBinderFactory](
    column: SQLSyntax,
    values: collection.Seq[A]
  ): SQLSyntax = SQLSyntax.empty.notIn(column, values)

  def in[A: ParameterBinderFactory, B: ParameterBinderFactory](
    columns: (SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B)]
  ): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs)
  def notIn[A: ParameterBinderFactory, B: ParameterBinderFactory](
    columns: (SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B)]
  ): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs)

  def in[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C)]
  ): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs)
  def notIn[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C)]
  ): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs)

  def in[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory,
    D: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D)]
  ): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs)
  def notIn[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory,
    D: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D)]
  ): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs)

  def in[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory,
    D: ParameterBinderFactory,
    E: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D, E)]
  ): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs)
  def notIn[
    A: ParameterBinderFactory,
    B: ParameterBinderFactory,
    C: ParameterBinderFactory,
    D: ParameterBinderFactory,
    E: ParameterBinderFactory
  ](
    columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax),
    valueSeqs: collection.Seq[(A, B, C, D, E)]
  ): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs)

  def in(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax =
    SQLSyntax.empty.in(column, subQuery)
  def notIn(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax =
    SQLSyntax.empty.notIn(column, subQuery)

  def like(column: SQLSyntax, value: String): SQLSyntax =
    SQLSyntax.empty.like(column, value)
  def notLike(column: SQLSyntax, value: String): SQLSyntax =
    SQLSyntax.empty.notLike(column, value)

  def exists(sqlPart: SQLSyntax): SQLSyntax = SQLSyntax.empty.exists(sqlPart)
  def notExists(sqlPart: SQLSyntax): SQLSyntax =
    SQLSyntax.empty.notExists(sqlPart)

  def lower(column: SQLSyntax): SQLSyntax = SQLSyntax.empty.lower(column)
  def upper(column: SQLSyntax): SQLSyntax = SQLSyntax.empty.upper(column)

  def distinct(columns: SQLSyntax*): SQLSyntax =
    sqls"distinct ${toCSV(columns)}"

  def avg(column: SQLSyntax): SQLSyntax = sqls"avg(${column})"

  val count: SQLSyntax = sqls"count(1)"
  def count(column: SQLSyntax): SQLSyntax = sqls"count(${column})"
  def count(asteriskProvider: AsteriskProvider): SQLSyntax =
    sqls"count(${asteriskProvider.asterisk})"

  def min(column: SQLSyntax): SQLSyntax = sqls"min(${column})"
  def max(column: SQLSyntax): SQLSyntax = sqls"max(${column})"
  def sum(column: SQLSyntax): SQLSyntax = sqls"sum(${column})"

  def abs(column: SQLSyntax): SQLSyntax = sqls"abs(${column})"
  def floor(column: SQLSyntax): SQLSyntax = sqls"floor(${column})"
  def ceil(column: SQLSyntax): SQLSyntax = sqls"ceil(${column})"
  def ceiling(column: SQLSyntax): SQLSyntax = sqls"ceiling(${column})"

  val ? : SQLSyntax = sqls"?"

  val currentDate: SQLSyntax = sqls"current_date"
  val currentTimestamp: SQLSyntax = sqls"current_timestamp"
  val dual: SQLSyntax = sqls"dual"

  /**
   * Returns an optional SQLSyntax which is flatten (from option array) and joined with 'and'.
   *
   * {{{
   *   val (id, name) = (123, "Alice")
   *   val cond: Option[SQLSyntax] = SQLSyntax.toAndConditionOpt(Some(sqls"id = ${id}"), None, Some(sqls"name = ${name} or name is null"))
   *   cond.get.value // "id = ? and (name = ? or name is null)"
   *   cond.get.parameters // Seq(123, "Alice")
   * }}}
   */
  def toAndConditionOpt(conditions: Option[SQLSyntax]*): Option[SQLSyntax] = {
    val cs: Seq[SQLSyntax] = conditions.flatten
    if (cs.isEmpty) None else Some(joinWithAnd(cs*))
  }

  /**
   * Returns an optional SQLSyntax which is flatten (from option array) and joined with 'or'.
   *
   * {{{
   *   val (id, name) = (123, "Alice")
   *   val cond: Option[SQLSyntax] = SQLSyntax.toOrConditionOpt(Some(sqls"id = ${id}"), None, Some(sqls"name = ${name} or name is null"))
   *   cond.get.value // "id = ? or (name = ? or name is null)"
   *   cond.get.parameters // Seq(123, "Alice")
   * }}}
   */
  def toOrConditionOpt(conditions: Option[SQLSyntax]*): Option[SQLSyntax] = {
    val cs: Seq[SQLSyntax] = conditions.flatten
    if (cs.isEmpty) None else Some(joinWithOr(cs*))
  }

  def roundBracket(inner: SQLSyntax): SQLSyntax = sqls"($inner)"

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy