diff options
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon/db')
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 - } - -} |