aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon/db')
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbIo.scala13
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/EntityNotFoundException.scala10
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/JdbcDbIo.scala28
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/Pagination.scala19
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/SearchFilterExpr.scala208
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/SlickPostgresQueryBuilder.scala115
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala387
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/Sorting.scala61
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/TransactionalContext.scala11
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala3
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/RepositoryLogging.scala62
11 files changed, 0 insertions, 917 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/DbIo.scala b/src/main/scala/xyz/driver/pdsuicommon/db/DbIo.scala
deleted file mode 100644
index 7c290d1..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/DbIo.scala
+++ /dev/null
@@ -1,13 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.Future
-
-/**
- * Where queries should run
- */
-trait DbIo {
- def runAsync[T](f: => T): Future[T]
- def runSync[T](f: => T): T = f
- def runAsyncTx[T](f: => T): Future[T]
- def runSyncTx[T](f: => T): Unit
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/EntityNotFoundException.scala b/src/main/scala/xyz/driver/pdsuicommon/db/EntityNotFoundException.scala
deleted file mode 100644
index d765833..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/EntityNotFoundException.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import xyz.driver.pdsuicommon.domain.Id
-
-class EntityNotFoundException(id: String, tableName: String)
- extends RuntimeException(s"Entity with id $id is not found in $tableName table") {
-
- def this(id: Id[_], tableName: String) = this(id.toString, tableName)
- def this(id: Long, tableName: String) = this(id.toString, tableName)
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/JdbcDbIo.scala b/src/main/scala/xyz/driver/pdsuicommon/db/JdbcDbIo.scala
deleted file mode 100644
index 44f177c..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/JdbcDbIo.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import xyz.driver.pdsuicommon.logging._
-
-import scala.concurrent.Future
-import scala.util.{Failure, Success, Try}
-
-class JdbcDbIo(sqlContext: TransactionalContext) extends DbIo with PhiLogging {
-
- override def runAsync[T](f: => T): Future[T] = {
- Future(f)(sqlContext.executionContext)
- }
-
- override def runAsyncTx[T](f: => T): Future[T] = {
- import sqlContext.executionContext
-
- Future(sqlContext.transaction(f)).andThen {
- case Failure(e) => logger.error(phi"Can't run a transaction: $e")
- }
- }
-
- override def runSyncTx[T](f: => T): Unit = {
- Try(sqlContext.transaction(f)) match {
- case Success(_) =>
- case Failure(e) => logger.error(phi"Can't run a transaction: $e")
- }
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/Pagination.scala b/src/main/scala/xyz/driver/pdsuicommon/db/Pagination.scala
deleted file mode 100644
index 92689dd..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/Pagination.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import xyz.driver.pdsuicommon.logging._
-
-/**
- * @param pageNumber Starts with 1
- */
-final case class Pagination(pageSize: Int, pageNumber: Int)
-
-object Pagination {
-
- // @see https://driverinc.atlassian.net/wiki/display/RA/REST+API+Specification#RESTAPISpecification-CommonRequestQueryParametersForWebServices
- val Default = Pagination(pageSize = 100, pageNumber = 1)
-
- implicit def toPhiString(x: Pagination): PhiString = {
- import x._
- phi"Pagination(pageSize=${Unsafe(pageSize)}, pageNumber=${Unsafe(pageNumber)})"
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SearchFilterExpr.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SearchFilterExpr.scala
deleted file mode 100644
index 0577921..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/SearchFilterExpr.scala
+++ /dev/null
@@ -1,208 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import xyz.driver.pdsuicommon.logging._
-
-sealed trait SearchFilterExpr {
- def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr]
- def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr
-}
-
-object SearchFilterExpr {
-
- val Empty = Intersection.Empty
- val Forbid = Atom.Binary(
- dimension = Dimension(None, "true"),
- op = SearchFilterBinaryOperation.Eq,
- value = "false"
- )
-
- final case class Dimension(tableName: Option[String], name: String) {
- def isForeign: Boolean = tableName.isDefined
- }
-
- sealed trait Atom extends SearchFilterExpr {
- override def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr] = {
- if (p(this)) Some(this)
- else None
- }
-
- override def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr = {
- if (f.isDefinedAt(this)) f(this)
- else this
- }
- }
-
- object Atom {
- final case class Binary(dimension: Dimension, op: SearchFilterBinaryOperation, value: AnyRef) extends Atom
- object Binary {
- def apply(field: String, op: SearchFilterBinaryOperation, value: AnyRef): Binary =
- Binary(Dimension(None, field), op, value)
- }
-
- final case class NAry(dimension: Dimension, op: SearchFilterNAryOperation, values: Seq[AnyRef]) extends Atom
- object NAry {
- def apply(field: String, op: SearchFilterNAryOperation, values: Seq[AnyRef]): NAry =
- NAry(Dimension(None, field), op, values)
- }
-
- /** dimension.tableName extractor */
- object TableName {
- def unapply(value: Atom): Option[String] = value match {
- case Binary(Dimension(tableNameOpt, _), _, _) => tableNameOpt
- case NAry(Dimension(tableNameOpt, _), _, _) => tableNameOpt
- }
- }
- }
-
- final case class Intersection private (operands: Seq[SearchFilterExpr])
- extends SearchFilterExpr with SearchFilterExprSeqOps {
-
- override def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr = {
- if (f.isDefinedAt(this)) f(this)
- else {
- this.copy(operands.map(_.replace(f)))
- }
- }
-
- }
-
- object Intersection {
-
- val Empty = Intersection(Seq())
-
- def create(operands: SearchFilterExpr*): SearchFilterExpr = {
- val filtered = operands.filterNot(SearchFilterExpr.isEmpty)
- filtered.size match {
- case 0 => Empty
- case 1 => filtered.head
- case _ => Intersection(filtered)
- }
- }
- }
-
- final case class Union private (operands: Seq[SearchFilterExpr])
- extends SearchFilterExpr with SearchFilterExprSeqOps {
-
- override def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr = {
- if (f.isDefinedAt(this)) f(this)
- else {
- this.copy(operands.map(_.replace(f)))
- }
- }
-
- }
-
- object Union {
-
- val Empty = Union(Seq())
-
- def create(operands: SearchFilterExpr*): SearchFilterExpr = {
- val filtered = operands.filterNot(SearchFilterExpr.isEmpty)
- filtered.size match {
- case 0 => Empty
- case 1 => filtered.head
- case _ => Union(filtered)
- }
- }
-
- def create(dimension: Dimension, values: String*): SearchFilterExpr = values.size match {
- case 0 => SearchFilterExpr.Empty
- case 1 => SearchFilterExpr.Atom.Binary(dimension, SearchFilterBinaryOperation.Eq, values.head)
- case _ =>
- val filters = values.map { value =>
- SearchFilterExpr.Atom.Binary(dimension, SearchFilterBinaryOperation.Eq, value)
- }
-
- create(filters: _*)
- }
-
- def create(dimension: Dimension, values: Set[String]): SearchFilterExpr =
- create(dimension, values.toSeq: _*)
-
- // Backwards compatible API
-
- /** Create SearchFilterExpr with empty tableName */
- def create(field: String, values: String*): SearchFilterExpr =
- create(Dimension(None, field), values: _*)
-
- /** Create SearchFilterExpr with empty tableName */
- def create(field: String, values: Set[String]): SearchFilterExpr =
- create(Dimension(None, field), values)
- }
-
- case object AllowAll extends SearchFilterExpr {
- override def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr] = {
- if (p(this)) Some(this)
- else None
- }
-
- override def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr = {
- if (f.isDefinedAt(this)) f(this)
- else this
- }
- }
-
- case object DenyAll extends SearchFilterExpr {
- override def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr] = {
- if (p(this)) Some(this)
- else None
- }
-
- override def replace(f: PartialFunction[SearchFilterExpr, SearchFilterExpr]): SearchFilterExpr = {
- if (f.isDefinedAt(this)) f(this)
- else this
- }
- }
-
- def isEmpty(expr: SearchFilterExpr): Boolean = {
- expr == Intersection.Empty || expr == Union.Empty
- }
-
- sealed trait SearchFilterExprSeqOps { this: SearchFilterExpr =>
-
- val operands: Seq[SearchFilterExpr]
-
- override def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr] = {
- if (p(this)) Some(this)
- else {
- // Search the first expr among operands, which satisfy p
- // Is's ok to use foldLeft. If there will be performance issues, replace it by recursive loop
- operands.foldLeft(Option.empty[SearchFilterExpr]) {
- case (None, expr) => expr.find(p)
- case (x, _) => x
- }
- }
- }
-
- }
-
- // There is no case, when this is unsafe. At this time.
- implicit def toPhiString(x: SearchFilterExpr): PhiString = {
- if (isEmpty(x)) Unsafe("SearchFilterExpr.Empty")
- else Unsafe(x.toString)
- }
-
-}
-
-sealed trait SearchFilterBinaryOperation
-
-object SearchFilterBinaryOperation {
-
- case object Eq extends SearchFilterBinaryOperation
- case object NotEq extends SearchFilterBinaryOperation
- case object Like extends SearchFilterBinaryOperation
- case object Gt extends SearchFilterBinaryOperation
- case object GtEq extends SearchFilterBinaryOperation
- case object Lt extends SearchFilterBinaryOperation
- case object LtEq extends SearchFilterBinaryOperation
-
-}
-
-sealed trait SearchFilterNAryOperation
-
-object SearchFilterNAryOperation {
-
- case object In extends SearchFilterNAryOperation
- case object NotIn extends SearchFilterNAryOperation
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SlickPostgresQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SlickPostgresQueryBuilder.scala
deleted file mode 100644
index a56ab9a..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/SlickPostgresQueryBuilder.scala
+++ /dev/null
@@ -1,115 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.time.{LocalDateTime, ZoneOffset}
-
-import slick.jdbc.{JdbcProfile, GetResult}
-import xyz.driver.core.database.SlickDal
-import xyz.driver.pdsuicommon.logging._
-
-import scala.collection.breakOut
-import scala.concurrent.ExecutionContext
-
-object SlickPostgresQueryBuilder extends PhiLogging {
-
- import xyz.driver.pdsuicommon.db.SlickQueryBuilder._
-
- def apply[T](databaseName: String,
- tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink],
- runner: Runner[T],
- countRunner: CountRunner)(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
- val parameters = SlickPostgresQueryBuilderParameters(
- databaseName = databaseName,
- tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
- links = links.map(x => x.foreignTableName -> x)(breakOut)
- )
- new SlickPostgresQueryBuilder[T](parameters)(runner, countRunner)
- }
-
- def apply[T](databaseName: String,
- tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink])(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
- apply[T](databaseName,
- tableName,
- SlickQueryBuilderParameters.AllFields,
- lastUpdateFieldName,
- nullableFields,
- links)
- }
-
- def apply[T](databaseName: String,
- tableName: String,
- fields: Set[String],
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink])(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
-
- val runner: Runner[T] = { parameters =>
- val sql = parameters.toSql(countQuery = false, fields = fields).as[T]
- logger.debug(phi"${Unsafe(sql)}")
- sqlContext.execute(sql)
- }
-
- val countRunner: CountRunner = { parameters =>
- implicit val getCountResult: GetResult[(Int, Option[LocalDateTime])] = GetResult({ r =>
- val count = r.rs.getInt(1)
- val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
- Option(r.rs.getTimestamp(2)).map(timestampToLocalDateTime)
- } else None
- (count, lastUpdate)
- })
- val sql = parameters.toSql(countQuery = true).as[(Int, Option[LocalDateTime])]
- logger.debug(phi"${Unsafe(sql)}")
- sqlContext.execute(sql).map(_.head)
- }
-
- apply[T](
- databaseName = databaseName,
- tableName = tableName,
- lastUpdateFieldName = lastUpdateFieldName,
- nullableFields = nullableFields,
- links = links,
- runner = runner,
- countRunner = countRunner
- )
- }
-
- def timestampToLocalDateTime(timestamp: java.sql.Timestamp): LocalDateTime = {
- LocalDateTime.ofInstant(timestamp.toInstant, ZoneOffset.UTC)
- }
-}
-
-class SlickPostgresQueryBuilder[T](parameters: SlickPostgresQueryBuilderParameters)(
- implicit runner: SlickQueryBuilder.Runner[T],
- countRunner: SlickQueryBuilder.CountRunner)
- extends SlickQueryBuilder[T](parameters) {
-
- def withFilter(newFilter: SearchFilterExpr): SlickQueryBuilder[T] = {
- new SlickPostgresQueryBuilder[T](parameters.copy(filter = newFilter))
- }
-
- def withSorting(newSorting: Sorting): SlickQueryBuilder[T] = {
- new SlickPostgresQueryBuilder[T](parameters.copy(sorting = newSorting))
- }
-
- def withPagination(newPagination: Pagination): SlickQueryBuilder[T] = {
- new SlickPostgresQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
- }
-
- def resetPagination: SlickQueryBuilder[T] = {
- new SlickPostgresQueryBuilder[T](parameters.copy(pagination = None))
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
deleted file mode 100644
index 9962edf..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
+++ /dev/null
@@ -1,387 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.{JDBCType, PreparedStatement}
-import java.time.LocalDateTime
-
-import slick.jdbc.{JdbcProfile, PositionedParameters, SQLActionBuilder, SetParameter}
-import xyz.driver.pdsuicommon.db.Sorting.{Dimension, Sequential}
-import xyz.driver.pdsuicommon.db.SortingOrder.{Ascending, Descending}
-import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
-
-import scala.concurrent.{ExecutionContext, Future}
-
-object SlickQueryBuilder {
-
- type Runner[T] = SlickQueryBuilderParameters => Future[Seq[T]]
-
- type CountResult = Future[(Int, Option[LocalDateTime])]
-
- type CountRunner = SlickQueryBuilderParameters => CountResult
-
- /**
- * Binder for PreparedStatement
- */
- type Binder = PreparedStatement => PreparedStatement
-
- final case class TableData(tableName: String,
- lastUpdateFieldName: Option[String] = None,
- nullableFields: Set[String] = Set.empty)
-
- val AllFields = Set("*")
-
- implicit class SQLActionBuilderConcat(a: SQLActionBuilder) {
- def concat(b: SQLActionBuilder): SQLActionBuilder = {
- SQLActionBuilder(a.queryParts ++ b.queryParts, new SetParameter[Unit] {
- def apply(p: Unit, pp: PositionedParameters): Unit = {
- a.unitPConv.apply(p, pp)
- b.unitPConv.apply(p, pp)
- }
- })
- }
- }
-
- implicit object SetQueryParameter extends SetParameter[AnyRef] {
- def apply(v: AnyRef, pp: PositionedParameters) = {
- pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber)
- }
- }
-
- implicit def setLongIdQueryParameter[T]: SetParameter[LongId[T]] = SetParameter[LongId[T]] { (v, pp) =>
- pp.setLong(v.id)
- }
-
- implicit def setStringIdQueryParameter[T]: SetParameter[StringId[T]] = SetParameter[StringId[T]] { (v, pp) =>
- pp.setString(v.id)
- }
-
- implicit def setUuidIdQueryParameter[T]: SetParameter[UuidId[T]] = SetParameter[UuidId[T]] { (v, pp) =>
- pp.setObject(v.id, JDBCType.BINARY.getVendorTypeNumber)
- }
-}
-
-final case class SlickTableLink(keyColumnName: String, foreignTableName: String, foreignKeyColumnName: String)
-
-object SlickQueryBuilderParameters {
- val AllFields = Set("*")
-}
-
-sealed trait SlickQueryBuilderParameters {
- import SlickQueryBuilder._
-
- def databaseName: String
- def tableData: SlickQueryBuilder.TableData
- def links: Map[String, SlickTableLink]
- def filter: SearchFilterExpr
- def sorting: Sorting
- def pagination: Option[Pagination]
-
- def qs: String
-
- def findLink(tableName: String): SlickTableLink = links.get(tableName) match {
- case None => throw new IllegalArgumentException(s"Cannot find a link for `$tableName`")
- case Some(link) => link
- }
-
- def toSql(countQuery: Boolean = false)(implicit profile: JdbcProfile): SQLActionBuilder = {
- toSql(countQuery, SlickQueryBuilderParameters.AllFields)
- }
-
- def toSql(countQuery: Boolean, fields: Set[String])(implicit profile: JdbcProfile): SQLActionBuilder = {
- import profile.api._
- val escapedTableName = s"""$qs$databaseName$qs.$qs${tableData.tableName}$qs"""
- val fieldsSql: String = if (countQuery) {
- val suffix: String = tableData.lastUpdateFieldName match {
- case Some(lastUpdateField) => s", max($escapedTableName.$qs$lastUpdateField$qs)"
- case None => ""
- }
- s"count(*) $suffix"
- } else {
- if (fields == SlickQueryBuilderParameters.AllFields) {
- s"$escapedTableName.*"
- } else {
- fields
- .map { field =>
- s"$escapedTableName.$qs$field$qs"
- }
- .mkString(", ")
- }
- }
- val where = filterToSql(escapedTableName, filter)
- val orderBy = sortingToSql(escapedTableName, sorting)
-
- val limitSql = limitToSql()
-
- val sql = sql"""select #$fieldsSql from #$escapedTableName"""
-
- val filtersTableLinks: Seq[SlickTableLink] = {
- import SearchFilterExpr._
- def aux(expr: SearchFilterExpr): Seq[SlickTableLink] = expr match {
- case Atom.TableName(tableName) => List(findLink(tableName))
- case Intersection(xs) => xs.flatMap(aux)
- case Union(xs) => xs.flatMap(aux)
- case _ => Nil
- }
- aux(filter)
- }
-
- val sortingTableLinks: Seq[SlickTableLink] = Sorting.collect(sorting) {
- case Dimension(Some(foreignTableName), _, _) => findLink(foreignTableName)
- }
-
- // Combine links from sorting and filter without duplicates
- val foreignTableLinks = (filtersTableLinks ++ sortingTableLinks).distinct
-
- def fkSql(fkLinksSql: SQLActionBuilder, tableLinks: Seq[SlickTableLink]): SQLActionBuilder = {
- if (tableLinks.nonEmpty) {
- tableLinks.head match {
- case SlickTableLink(keyColumnName, foreignTableName, foreignKeyColumnName) =>
- val escapedForeignTableName = s"$qs$databaseName$qs.$qs$foreignTableName$qs"
- val join = sql""" inner join #$escapedForeignTableName
- on #$escapedTableName.#$qs#$keyColumnName#$qs=#$escapedForeignTableName.#$qs#$foreignKeyColumnName#$qs"""
- fkSql(fkLinksSql concat join, tableLinks.tail)
- }
- } else fkLinksSql
- }
- val foreignTableLinksSql = fkSql(sql"", foreignTableLinks)
-
- val whereSql = if (where.queryParts.size > 1) {
- sql" where " concat where
- } else sql""
-
- val orderSql = if (orderBy.nonEmpty && !countQuery) {
- sql" order by #$orderBy"
- } else sql""
-
- val limSql = if (limitSql.queryParts.size > 1 && !countQuery) {
- sql" " concat limitSql
- } else sql""
-
- sql concat foreignTableLinksSql concat whereSql concat orderSql concat limSql
- }
-
- /**
- * Converts filter expression to SQL expression.
- *
- * @return Returns SQL string and list of values for binding in prepared statement.
- */
- protected def filterToSql(escapedTableName: String, filter: SearchFilterExpr)(
- implicit profile: JdbcProfile): SQLActionBuilder = {
- import SearchFilterBinaryOperation._
- import SearchFilterExpr._
- import profile.api._
-
- def isNull(string: AnyRef) = Option(string).isEmpty || string.toString.toLowerCase == "null"
-
- def escapeDimension(dimension: SearchFilterExpr.Dimension) = {
- s"${dimension.tableName.map(t => s"$qs$databaseName$qs.$qs$t$qs").getOrElse(escapedTableName)}.$qs${dimension.name}$qs"
- }
-
- def filterToSqlMultiple(operands: Seq[SearchFilterExpr]) = operands.collect {
- case x if !SearchFilterExpr.isEmpty(x) => filterToSql(escapedTableName, x)
- }
-
- def multipleSqlToAction(first: Boolean,
- op: String,
- conditions: Seq[SQLActionBuilder],
- sql: SQLActionBuilder): SQLActionBuilder = {
- if (conditions.nonEmpty) {
- val condition = conditions.head
- if (first) {
- multipleSqlToAction(first = false, op, conditions.tail, condition)
- } else {
- multipleSqlToAction(first = false, op, conditions.tail, sql concat sql" #${op} " concat condition)
- }
- } else sql
- }
-
- def concatenateParameters(sql: SQLActionBuilder, first: Boolean, tail: Seq[AnyRef]): SQLActionBuilder = {
- if (tail.nonEmpty) {
- if (!first) {
- concatenateParameters(sql concat sql""",${tail.head}""", first = false, tail.tail)
- } else {
- concatenateParameters(sql"""(${tail.head}""", first = false, tail.tail)
- }
- } else sql concat sql")"
- }
-
- filter match {
- case x if isEmpty(x) =>
- sql""
-
- case AllowAll =>
- sql"1=1"
-
- case DenyAll =>
- sql"1=0"
-
- case Atom.Binary(dimension, Eq, value) if isNull(value) =>
- sql"#${escapeDimension(dimension)} is NULL"
-
- case Atom.Binary(dimension, NotEq, value) if isNull(value) =>
- sql"#${escapeDimension(dimension)} is not NULL"
-
- case Atom.Binary(dimension, NotEq, value) if tableData.nullableFields.contains(dimension.name) =>
- // In MySQL NULL <> Any === NULL
- // So, to handle NotEq for nullable fields we need to use more complex SQL expression.
- // http://dev.mysql.com/doc/refman/5.7/en/working-with-null.html
- val escapedColumn = escapeDimension(dimension)
- sql"(#${escapedColumn} is null or #${escapedColumn} != $value)"
-
- case Atom.Binary(dimension, op, value) =>
- val operator = op match {
- case Eq => sql"="
- case NotEq => sql"!="
- case Like => sql" like "
- case Gt => sql">"
- case GtEq => sql">="
- case Lt => sql"<"
- case LtEq => sql"<="
- }
- sql"#${escapeDimension(dimension)}" concat operator concat sql"""$value"""
-
- case Atom.NAry(dimension, op, values) =>
- val sqlOp = op match {
- case SearchFilterNAryOperation.In => sql" in "
- case SearchFilterNAryOperation.NotIn => sql" not in "
- }
-
- if (values.nonEmpty) {
- val formattedValues = concatenateParameters(sql"", first = true, values)
- sql"#${escapeDimension(dimension)}" concat sqlOp concat formattedValues
- } else {
- sql"1=0"
- }
-
- case Intersection(operands) =>
- val filter = multipleSqlToAction(first = true, "and", filterToSqlMultiple(operands), sql"")
- sql"(" concat filter concat sql")"
-
- case Union(operands) =>
- val filter = multipleSqlToAction(first = true, "or", filterToSqlMultiple(operands), sql"")
- sql"(" concat filter concat sql")"
- }
- }
-
- protected def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder
-
- /**
- * @param escapedMainTableName Should be escaped
- */
- protected def sortingToSql(escapedMainTableName: String, sorting: Sorting)(implicit profile: JdbcProfile): String = {
- sorting match {
- case Dimension(optSortingTableName, field, order) =>
- val sortingTableName =
- optSortingTableName.map(x => s"$qs$databaseName$qs.$qs$x$qs").getOrElse(escapedMainTableName)
- val fullName = s"$sortingTableName.$qs$field$qs"
-
- s"$fullName ${orderToSql(order)}"
-
- case Sequential(xs) =>
- xs.map(sortingToSql(escapedMainTableName, _)).mkString(", ")
- }
- }
-
- protected def orderToSql(x: SortingOrder): String = x match {
- case Ascending => "asc"
- case Descending => "desc"
- }
-
- protected def binder(bindings: List[AnyRef])(bind: PreparedStatement): PreparedStatement = {
- bindings.zipWithIndex.foreach {
- case (binding, index) =>
- bind.setObject(index + 1, binding)
- }
-
- bind
- }
-
-}
-
-final case class SlickPostgresQueryBuilderParameters(databaseName: String,
- tableData: SlickQueryBuilder.TableData,
- links: Map[String, SlickTableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends SlickQueryBuilderParameters {
-
- def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
- import profile.api._
- pagination.map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- sql"limit #${pagination.pageSize} OFFSET #$startFrom"
- } getOrElse (sql"")
- }
-
- val qs = """""""
-
-}
-
-/**
- * @param links Links to another tables grouped by foreignTableName
- */
-final case class SlickMysqlQueryBuilderParameters(databaseName: String,
- tableData: SlickQueryBuilder.TableData,
- links: Map[String, SlickTableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends SlickQueryBuilderParameters {
-
- def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
- import profile.api._
- pagination
- .map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- sql"limit #$startFrom, #${pagination.pageSize}"
- }
- .getOrElse(sql"")
- }
-
- val qs = """`"""
-
-}
-
-abstract class SlickQueryBuilder[T](val parameters: SlickQueryBuilderParameters)(
- implicit runner: SlickQueryBuilder.Runner[T],
- countRunner: SlickQueryBuilder.CountRunner) {
-
- def run()(implicit ec: ExecutionContext): Future[Seq[T]] = runner(parameters)
-
- def runCount()(implicit ec: ExecutionContext): SlickQueryBuilder.CountResult = countRunner(parameters)
-
- /**
- * Runs the query and returns total found rows without considering of pagination.
- */
- def runWithCount()(implicit ec: ExecutionContext): Future[(Seq[T], Int, Option[LocalDateTime])] = {
- for {
- all <- run
- (total, lastUpdate) <- runCount
- } yield (all, total, lastUpdate)
- }
-
- def withFilter(newFilter: SearchFilterExpr): SlickQueryBuilder[T]
-
- def withFilter(filter: Option[SearchFilterExpr]): SlickQueryBuilder[T] = {
- filter.fold(this)(withFilter)
- }
-
- def resetFilter: SlickQueryBuilder[T] = withFilter(SearchFilterExpr.Empty)
-
- def withSorting(newSorting: Sorting): SlickQueryBuilder[T]
-
- def withSorting(sorting: Option[Sorting]): SlickQueryBuilder[T] = {
- sorting.fold(this)(withSorting)
- }
-
- def resetSorting: SlickQueryBuilder[T] = withSorting(Sorting.Empty)
-
- def withPagination(newPagination: Pagination): SlickQueryBuilder[T]
-
- def withPagination(pagination: Option[Pagination]): SlickQueryBuilder[T] = {
- pagination.fold(this)(withPagination)
- }
-
- def resetPagination: SlickQueryBuilder[T]
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/Sorting.scala b/src/main/scala/xyz/driver/pdsuicommon/db/Sorting.scala
deleted file mode 100644
index 8adf629..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/Sorting.scala
+++ /dev/null
@@ -1,61 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import xyz.driver.pdsuicommon.logging._
-
-import scala.collection.generic.CanBuildFrom
-
-sealed trait SortingOrder
-object SortingOrder {
-
- case object Ascending extends SortingOrder
- case object Descending extends SortingOrder
-
-}
-
-sealed trait Sorting
-
-object Sorting {
-
- val Empty = Sequential(Seq.empty)
-
- /**
- * @param tableName None if the table is default (same)
- * @param name Dimension name
- * @param order Order
- */
- final case class Dimension(tableName: Option[String], name: String, order: SortingOrder) extends Sorting {
- def isForeign: Boolean = tableName.isDefined
- }
-
- final case class Sequential(sorting: Seq[Dimension]) extends Sorting {
- override def toString: String = if (isEmpty(this)) "Empty" else super.toString
- }
-
- def isEmpty(input: Sorting): Boolean = {
- input match {
- case Sequential(Seq()) => true
- case _ => false
- }
- }
-
- def filter(sorting: Sorting, p: Dimension => Boolean): Seq[Dimension] = sorting match {
- case x: Dimension if p(x) => Seq(x)
- case _: Dimension => Seq.empty
- case Sequential(xs) => xs.filter(p)
- }
-
- def collect[B, That](sorting: Sorting)(f: PartialFunction[Dimension, B])(
- implicit bf: CanBuildFrom[Seq[Dimension], B, That]): That = sorting match {
- case x: Dimension if f.isDefinedAt(x) =>
- val r = bf.apply()
- r += f(x)
- r.result()
-
- case _: Dimension => bf.apply().result()
- case Sequential(xs) => xs.collect(f)
- }
-
- // Contains dimensions and ordering only, thus it is safe.
- implicit def toPhiString(x: Sorting): PhiString = Unsafe(x.toString)
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/TransactionalContext.scala b/src/main/scala/xyz/driver/pdsuicommon/db/TransactionalContext.scala
deleted file mode 100644
index 9883b9e..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/TransactionalContext.scala
+++ /dev/null
@@ -1,11 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.ExecutionContext
-
-trait TransactionalContext {
-
- implicit def executionContext: ExecutionContext
-
- def transaction[T](f: => T): T
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
deleted file mode 100644
index e62238e..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
+++ /dev/null
@@ -1,3 +0,0 @@
-package xyz.driver.pdsuicommon.db.repositories
-
-trait Repository extends RepositoryLogging
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/RepositoryLogging.scala b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/RepositoryLogging.scala
deleted file mode 100644
index d1ec1da..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/RepositoryLogging.scala
+++ /dev/null
@@ -1,62 +0,0 @@
-package xyz.driver.pdsuicommon.db.repositories
-
-import xyz.driver.pdsuicommon.logging._
-
-trait RepositoryLogging extends PhiLogging {
-
- protected def logCreatedOne[T](x: T)(implicit toPhiString: T => PhiString): T = {
- logger.info(phi"An entity was created: $x")
- x
- }
-
- protected def logCreatedMultiple[T <: Iterable[_]](xs: T)(implicit toPhiString: T => PhiString): T = {
- if (xs.nonEmpty) {
- logger.info(phi"Entities were created: $xs")
- }
- xs
- }
-
- protected def logUpdatedOne(rowsAffected: Long): Long = {
- rowsAffected match {
- case 0 => logger.trace(phi"The entity is up to date")
- case 1 => logger.info(phi"The entity was updated")
- case x => logger.warn(phi"The ${Unsafe(x)} entities were updated")
- }
- rowsAffected
- }
-
- protected def logUpdatedOneUnimportant(rowsAffected: Long): Long = {
- rowsAffected match {
- case 0 => logger.trace(phi"The entity is up to date")
- case 1 => logger.trace(phi"The entity was updated")
- case x => logger.warn(phi"The ${Unsafe(x)} entities were updated")
- }
- rowsAffected
- }
-
- protected def logUpdatedMultiple(rowsAffected: Long): Long = {
- rowsAffected match {
- case 0 => logger.trace(phi"All entities are up to date")
- case x => logger.info(phi"The ${Unsafe(x)} entities were updated")
- }
- rowsAffected
- }
-
- protected def logDeletedOne(rowsAffected: Long): Long = {
- rowsAffected match {
- case 0 => logger.trace(phi"The entity does not exist")
- case 1 => logger.info(phi"The entity was deleted")
- case x => logger.warn(phi"Deleted ${Unsafe(x)} entities, expected one")
- }
- rowsAffected
- }
-
- protected def logDeletedMultiple(rowsAffected: Long): Long = {
- rowsAffected match {
- case 0 => logger.trace(phi"Entities do not exist")
- case x => logger.info(phi"Deleted ${Unsafe(x)} entities")
- }
- rowsAffected
- }
-
-}