aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon/db
diff options
context:
space:
mode:
authorKseniya Tomskikh <ktomskih@datamonsters.co>2017-10-23 15:34:13 +0700
committerKseniya Tomskikh <ktomskih@datamonsters.co>2017-10-23 17:15:39 +0700
commite3b8e6ce8f659bff81b9a4bc3a79e3d2c3dd734f (patch)
tree430986fa7935b1bd03659f4542ce15803b617e51 /src/main/scala/xyz/driver/pdsuicommon/db
parent1d2131e98330284ce5a3dcfc0cfd13e012914d6a (diff)
downloadrest-query-e3b8e6ce8f659bff81b9a4bc3a79e3d2c3dd734f.tar.gz
rest-query-e3b8e6ce8f659bff81b9a4bc3a79e3d2c3dd734f.tar.bz2
rest-query-e3b8e6ce8f659bff81b9a4bc3a79e3d2c3dd734f.zip
Removed quill query builder
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon/db')
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala15
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala14
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbIo.scala13
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala66
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala9
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/JdbcDbIo.scala28
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala90
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala88
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala27
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala108
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala340
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala2
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala21
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/RepositoryLogging.scala62
15 files changed, 2 insertions, 885 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala b/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala
deleted file mode 100644
index 0af104e..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.Future
-
-trait DbCommand {
- def runSync(): Unit
- def runAsync(transactions: DbIo): Future[Unit]
-}
-
-object DbCommand {
- val Empty: DbCommand = new DbCommand {
- override def runSync(): Unit = {}
- override def runAsync(transactions: DbIo): Future[Unit] = Future.successful(())
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala b/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala
deleted file mode 100644
index 1e2fb74..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.{ExecutionContext, Future}
-
-trait DbCommandFactory[T] {
- def createCommand(orig: T)(implicit ec: ExecutionContext): Future[DbCommand]
-}
-
-object DbCommandFactory {
- def empty[T]: DbCommandFactory[T] = new DbCommandFactory[T] {
- override def createCommand(orig: T)(implicit ec: ExecutionContext): Future[DbCommand] =
- Future.successful(DbCommand.Empty)
- }
-}
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/EntityExtractorDerivation.scala b/src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala
deleted file mode 100644
index 02ba322..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala
+++ /dev/null
@@ -1,66 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import io.getquill.NamingStrategy
-import io.getquill.dsl.EncodingDsl
-
-import scala.language.experimental.macros
-import scala.reflect.macros.blackbox
-
-trait EntityExtractorDerivation[Naming <: NamingStrategy] { this: EncodingDsl =>
-
- /*
- Simple Quill extractor derivation for [[T]]
- Only case classes available. Type parameters is not supported
- */
- def entityExtractor[T]: (ResultSet => T) = macro EntityExtractorDerivation.impl[T]
-}
-
-object EntityExtractorDerivation {
- def impl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
- import c.universe._
- val namingStrategy = c.prefix.actualType
- .baseType(c.weakTypeOf[EntityExtractorDerivation[NamingStrategy]].typeSymbol)
- .typeArgs
- .head
- .typeSymbol
- .companion
- val functionBody = {
- val tpe = weakTypeOf[T]
- val resultOpt = tpe.decls.collectFirst {
- // Find first constructor of T
- case cons: MethodSymbol if cons.isConstructor =>
- // Create param list for constructor
- val params = cons.paramLists.flatten.map { param =>
- val t = param.typeSignature
- val paramName = param.name.toString
- val col = q"$namingStrategy.column($paramName)"
- // Resolve implicit decoders (from SqlContext) and apply ResultSet for each
- val d = q"implicitly[${c.prefix}.Decoder[$t]]"
- // Minus 1 cause Quill JDBC decoders make plus one.
- // ¯\_(ツ)_/¯
- val i = q"row.findColumn($col) - 1"
- val decoderName = TermName(paramName + "Decoder")
- val valueName = TermName(paramName + "Value")
- (
- q"val $decoderName = $d",
- q"val $valueName = $decoderName($i, row)",
- valueName
- )
- }
- // Call constructor with param list
- q"""
- ..${params.map(_._1)}
- ..${params.map(_._2)}
- new $tpe(..${params.map(_._3)})
- """
- }
- resultOpt match {
- case Some(result) => result
- case None => c.abort(c.enclosingPosition, s"Can not derive extractor for $tpe. Constructor not found.")
- }
- }
- q"(row: java.sql.ResultSet) => $functionBody"
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala b/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala
deleted file mode 100644
index ac42a34..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.Future
-
-object FakeDbIo extends DbIo {
- override def runAsync[T](f: => T): Future[T] = Future.successful(f)
- override def runAsyncTx[T](f: => T): Future[T] = Future.successful(f)
- override def runSyncTx[T](f: => T): Unit = f
-}
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/MySqlContext.scala b/src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala
deleted file mode 100644
index 9d2664d..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala
+++ /dev/null
@@ -1,90 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.io.Closeable
-import java.time._
-import java.util.concurrent.Executors
-import javax.sql.DataSource
-
-import com.typesafe.config.Config
-import io.getquill._
-import xyz.driver.pdsuicommon.concurrent.MdcExecutionContext
-import xyz.driver.pdsuicommon.db.MySqlContext.Settings
-import xyz.driver.pdsuicommon.error.IncorrectIdException
-import xyz.driver.pdsuicommon.logging._
-
-import scala.concurrent.ExecutionContext
-import scala.util.control.NonFatal
-import scala.util.{Failure, Success, Try}
-
-object MySqlContext extends PhiLogging {
-
- final case class DbCredentials(user: String,
- password: String,
- host: String,
- port: Int,
- dbName: String,
- dbCreateFlag: Boolean,
- dbContext: String,
- connectionParams: String,
- url: String)
-
- final case class Settings(credentials: DbCredentials, connection: Config, threadPoolSize: Int)
-
- def apply(settings: Settings): MySqlContext = {
- // Prevent leaking credentials to a log
- Try(JdbcContextConfig(settings.connection).dataSource) match {
- case Success(dataSource) => new MySqlContext(dataSource, settings)
- case Failure(NonFatal(e)) =>
- logger.error(phi"Can not load dataSource, error: ${Unsafe(e.getClass.getName)}")
- throw new IllegalArgumentException("Can not load dataSource from config. Check your database and config")
- }
- }
-}
-
-class MySqlContext(dataSource: DataSource with Closeable, settings: Settings)
- extends MysqlJdbcContext[MysqlEscape](dataSource) with TransactionalContext
- with EntityExtractorDerivation[Literal] {
-
- private val tpe = Executors.newFixedThreadPool(settings.threadPoolSize)
-
- implicit val executionContext: ExecutionContext = {
- val orig = ExecutionContext.fromExecutor(tpe)
- MdcExecutionContext.from(orig)
- }
-
- override def close(): Unit = {
- super.close()
- tpe.shutdownNow()
- }
-
- /**
- * Overrode, because Quill JDBC optionDecoder pass null inside decoders.
- * If custom decoder don't have special null handler, it will failed.
- *
- * @see https://github.com/getquill/quill/issues/535
- */
- implicit override def optionDecoder[T](implicit d: Decoder[T]): Decoder[Option[T]] =
- decoder(
- sqlType = d.sqlType,
- row =>
- index => {
- try {
- val res = d(index - 1, row)
- if (row.wasNull) {
- None
- } else {
- Some(res)
- }
- } catch {
- case _: NullPointerException => None
- case _: IncorrectIdException => None
- }
- }
- )
-
- final implicit class LocalDateTimeDbOps(val left: LocalDateTime) {
-
- // scalastyle:off
- def <=(right: LocalDateTime): Quoted[Boolean] = quote(infix"$left <= $right".as[Boolean])
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala
deleted file mode 100644
index e2936e3..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala
+++ /dev/null
@@ -1,88 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import xyz.driver.pdsuicommon.logging._
-import io.getquill.{MySQLDialect, MysqlEscape}
-
-import scala.collection.breakOut
-
-object MysqlQueryBuilder extends PhiLogging {
- import xyz.driver.pdsuicommon.db.QueryBuilder._
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- runner: Runner[T],
- countRunner: CountRunner): MysqlQueryBuilder[T] = {
- val parameters = MysqlQueryBuilderParameters(
- tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
- links = links.map(x => x.foreignTableName -> x)(breakOut)
- )
- new MysqlQueryBuilder[T](parameters)(runner, countRunner)
- }
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: (ResultSet) => T)(implicit sqlContext: MySqlContext): MysqlQueryBuilder[T] = {
-
- val runner: Runner[T] = { parameters =>
- val (sql, binder) = parameters.toSql(namingStrategy = MysqlEscape)
- logger.trace(phi"Query for execute: ${Unsafe(sql)}")
- sqlContext.executeQuery[T](sql, binder, { resultSet =>
- extractor(resultSet)
- })
- }
-
- val countRunner: CountRunner = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = true, namingStrategy = MysqlEscape)
- logger.trace(phi"Query for execute: ${Unsafe(sql)}")
- sqlContext
- .executeQuery[CountResult](
- sql,
- binder, { resultSet =>
- val count = resultSet.getInt(1)
- val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
- Option(sqlContext.localDateTimeDecoder.decoder(2, resultSet))
- } else None
-
- (count, lastUpdate)
- }
- )
- .head
- }
-
- apply[T](
- tableName = tableName,
- lastUpdateFieldName = lastUpdateFieldName,
- nullableFields = nullableFields,
- links = links,
- runner = runner,
- countRunner = countRunner
- )
- }
-}
-
-class MysqlQueryBuilder[T](parameters: MysqlQueryBuilderParameters)(implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner)
- extends QueryBuilder[T, MySQLDialect, MysqlEscape](parameters) {
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(filter = newFilter))
- }
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(sorting = newSorting))
- }
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
- }
-
- def resetPagination: QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(pagination = None))
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
index 7bdfd1b..bb8d322 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
@@ -1,15 +1,12 @@
package xyz.driver.pdsuicommon.db
import java.io.Closeable
-import java.time._
-import java.util.UUID
import java.util.concurrent.Executors
import javax.sql.DataSource
import io.getquill._
import xyz.driver.pdsuicommon.concurrent.MdcExecutionContext
import xyz.driver.pdsuicommon.db.PostgresContext.Settings
-import xyz.driver.pdsuicommon.domain.UuidId
import xyz.driver.pdsuicommon.logging._
import scala.concurrent.ExecutionContext
@@ -35,8 +32,7 @@ object PostgresContext extends PhiLogging {
}
class PostgresContext(val dataSource: DataSource with Closeable, settings: Settings)
- extends PostgresJdbcContext[SnakeCase](dataSource) with TransactionalContext
- with EntityExtractorDerivation[SnakeCase] {
+ extends PostgresJdbcContext[SnakeCase](dataSource) with TransactionalContext {
private val tpe = Executors.newFixedThreadPool(settings.threadPoolSize)
@@ -49,25 +45,4 @@ class PostgresContext(val dataSource: DataSource with Closeable, settings: Setti
super.close()
tpe.shutdownNow()
}
-
- /**
- * Usable for QueryBuilder's extractors
- */
- def timestampToLocalDateTime(timestamp: java.sql.Timestamp): LocalDateTime = {
- LocalDateTime.ofInstant(timestamp.toInstant, ZoneOffset.UTC)
- }
-
- implicit def encodeUuidId[T] = MappedEncoding[UuidId[T], String](_.toString)
- implicit def decodeUuidId[T] = MappedEncoding[String, UuidId[T]] { uuid =>
- UuidId[T](UUID.fromString(uuid))
- }
-
- def decodeOptUuidId[T] = MappedEncoding[Option[String], Option[UuidId[T]]] {
- case Some(x) => Option(x).map(y => UuidId[T](UUID.fromString(y)))
- case None => None
- }
-
- implicit def decodeUuid[T] = MappedEncoding[String, UUID] { uuid =>
- UUID.fromString(uuid)
- }
}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala
deleted file mode 100644
index 0ddf811..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala
+++ /dev/null
@@ -1,108 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import io.getquill.{PostgresDialect, PostgresEscape}
-import xyz.driver.pdsuicommon.db.PostgresQueryBuilder.SmartPostgresEscape
-
-import scala.collection.breakOut
-
-object PostgresQueryBuilder {
-
- import xyz.driver.pdsuicommon.db.QueryBuilder._
-
- trait SmartPostgresEscape extends PostgresEscape {
- override def column(s: String): String =
- if (s.startsWith("$")) s else super.column(s)
- override def default(s: String): String =
- s.split("\\.").map(ss => s""""$ss"""").mkString(".")
- }
-
- object SmartPostgresEscape extends SmartPostgresEscape
-
- type Escape = SmartPostgresEscape
- val Escape = SmartPostgresEscape
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- runner: Runner[T],
- countRunner: CountRunner): PostgresQueryBuilder[T] = {
- val parameters = PostgresQueryBuilderParameters(
- tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
- links = links.map(x => x.foreignTableName -> x)(breakOut)
- )
- new PostgresQueryBuilder[T](parameters)(runner, countRunner)
- }
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: ResultSet => T)(implicit sqlContext: PostgresContext): PostgresQueryBuilder[T] = {
- apply(tableName, QueryBuilderParameters.AllFields, lastUpdateFieldName, nullableFields, links, extractor)
- }
-
- def apply[T](tableName: String,
- fields: Set[String],
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: ResultSet => T)(implicit sqlContext: PostgresContext): PostgresQueryBuilder[T] = {
-
- val runner: Runner[T] = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = false, fields = fields, namingStrategy = SmartPostgresEscape)
- sqlContext.executeQuery[T](sql, binder, { resultSet =>
- extractor(resultSet)
- })
- }
-
- val countRunner: CountRunner = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = true, namingStrategy = SmartPostgresEscape)
- sqlContext
- .executeQuery[CountResult](
- sql,
- binder, { resultSet =>
- val count = resultSet.getInt(1)
- val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
- Option(resultSet.getTimestamp(2)).map(sqlContext.timestampToLocalDateTime)
- } else None
-
- (count, lastUpdate)
- }
- )
- .head
- }
-
- apply[T](
- tableName = tableName,
- lastUpdateFieldName = lastUpdateFieldName,
- nullableFields = nullableFields,
- links = links,
- runner = runner,
- countRunner = countRunner
- )
- }
-}
-
-class PostgresQueryBuilder[T](parameters: PostgresQueryBuilderParameters)(implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner)
- extends QueryBuilder[T, PostgresDialect, PostgresQueryBuilder.Escape](parameters) {
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(filter = newFilter))
- }
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(sorting = newSorting))
- }
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
- }
-
- def resetPagination: QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(pagination = None))
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala
deleted file mode 100644
index 0bf1ed6..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala
+++ /dev/null
@@ -1,340 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.PreparedStatement
-import java.time.LocalDateTime
-
-import io.getquill.NamingStrategy
-import io.getquill.context.sql.idiom.SqlIdiom
-import xyz.driver.pdsuicommon.db.Sorting.{Dimension, Sequential}
-import xyz.driver.pdsuicommon.db.SortingOrder.{Ascending, Descending}
-
-import scala.collection.mutable.ListBuffer
-
-object QueryBuilder {
-
- type Runner[T] = QueryBuilderParameters => Seq[T]
-
- type CountResult = (Int, Option[LocalDateTime])
-
- type CountRunner = QueryBuilderParameters => 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("*")
-
-}
-
-final case class TableLink(keyColumnName: String, foreignTableName: String, foreignKeyColumnName: String)
-
-object QueryBuilderParameters {
- val AllFields = Set("*")
-}
-
-sealed trait QueryBuilderParameters {
-
- def tableData: QueryBuilder.TableData
- def links: Map[String, TableLink]
- def filter: SearchFilterExpr
- def sorting: Sorting
- def pagination: Option[Pagination]
-
- def findLink(tableName: String): TableLink = 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, namingStrategy: NamingStrategy): (String, QueryBuilder.Binder) = {
- toSql(countQuery, QueryBuilderParameters.AllFields, namingStrategy)
- }
-
- def toSql(countQuery: Boolean, fields: Set[String], namingStrategy: NamingStrategy): (String, QueryBuilder.Binder) = {
- val escapedTableName = namingStrategy.table(tableData.tableName)
- val fieldsSql: String = if (countQuery) {
- val suffix: String = tableData.lastUpdateFieldName match {
- case Some(lastUpdateField) => s", max($escapedTableName.${namingStrategy.column(lastUpdateField)})"
- case None => ""
- }
- "count(*)" + suffix
- } else {
- if (fields == QueryBuilderParameters.AllFields) {
- s"$escapedTableName.*"
- } else {
- fields
- .map { field =>
- s"$escapedTableName.${namingStrategy.column(field)}"
- }
- .mkString(", ")
- }
- }
- val (where, bindings) = filterToSql(escapedTableName, filter, namingStrategy)
- val orderBy = sortingToSql(escapedTableName, sorting, namingStrategy)
-
- val limitSql = limitToSql()
-
- val sql = new StringBuilder()
- sql.append("select ")
- sql.append(fieldsSql)
- sql.append("\nfrom ")
- sql.append(escapedTableName)
-
- val filtersTableLinks: Seq[TableLink] = {
- import SearchFilterExpr._
- def aux(expr: SearchFilterExpr): Seq[TableLink] = 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[TableLink] = Sorting.collect(sorting) {
- case Dimension(Some(foreignTableName), _, _) => findLink(foreignTableName)
- }
-
- // Combine links from sorting and filter without duplicates
- val foreignTableLinks = (filtersTableLinks ++ sortingTableLinks).distinct
-
- foreignTableLinks.foreach {
- case TableLink(keyColumnName, foreignTableName, foreignKeyColumnName) =>
- val escapedForeignTableName = namingStrategy.table(foreignTableName)
-
- sql.append("\ninner join ")
- sql.append(escapedForeignTableName)
- sql.append(" on ")
-
- sql.append(escapedTableName)
- sql.append('.')
- sql.append(namingStrategy.column(keyColumnName))
-
- sql.append(" = ")
-
- sql.append(escapedForeignTableName)
- sql.append('.')
- sql.append(namingStrategy.column(foreignKeyColumnName))
- }
-
- if (where.nonEmpty) {
- sql.append("\nwhere ")
- sql.append(where)
- }
-
- if (orderBy.nonEmpty && !countQuery) {
- sql.append("\norder by ")
- sql.append(orderBy)
- }
-
- if (limitSql.nonEmpty && !countQuery) {
- sql.append("\n")
- sql.append(limitSql)
- }
-
- (sql.toString, binder(bindings))
- }
-
- /**
- * 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,
- namingStrategy: NamingStrategy): (String, List[AnyRef]) = {
- import SearchFilterBinaryOperation._
- import SearchFilterExpr._
-
- def isNull(string: AnyRef) = Option(string).isEmpty || string.toString.toLowerCase == "null"
-
- def placeholder(field: String) = "?"
-
- def escapeDimension(dimension: SearchFilterExpr.Dimension) = {
- val tableName = dimension.tableName.fold(escapedTableName)(namingStrategy.table)
- s"$tableName.${namingStrategy.column(dimension.name)}"
- }
-
- def filterToSqlMultiple(operands: Seq[SearchFilterExpr]) = operands.collect {
- case x if !SearchFilterExpr.isEmpty(x) => filterToSql(escapedTableName, x, namingStrategy)
- }
-
- filter match {
- case x if isEmpty(x) =>
- ("", List.empty)
-
- case AllowAll =>
- ("1", List.empty)
-
- case DenyAll =>
- ("0", List.empty)
-
- case Atom.Binary(dimension, Eq, value) if isNull(value) =>
- (s"${escapeDimension(dimension)} is NULL", List.empty)
-
- case Atom.Binary(dimension, NotEq, value) if isNull(value) =>
- (s"${escapeDimension(dimension)} is not NULL", List.empty)
-
- 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)
- val sql = s"($escapedColumn is null or $escapedColumn != ${placeholder(dimension.name)})"
- (sql, List(value))
-
- case Atom.Binary(dimension, op, value) =>
- val operator = op match {
- case Eq => "="
- case NotEq => "!="
- case Like => "like"
- case Gt => ">"
- case GtEq => ">="
- case Lt => "<"
- case LtEq => "<="
- }
- (s"${escapeDimension(dimension)} $operator ${placeholder(dimension.name)}", List(value))
-
- case Atom.NAry(dimension, op, values) =>
- val sqlOp = op match {
- case SearchFilterNAryOperation.In => "in"
- case SearchFilterNAryOperation.NotIn => "not in"
- }
-
- val bindings = ListBuffer[AnyRef]()
- val sqlPlaceholder = placeholder(dimension.name)
- val formattedValues = if (values.nonEmpty) {
- values
- .map { value =>
- bindings += value
- sqlPlaceholder
- }
- .mkString(", ")
- } else "NULL"
- (s"${escapeDimension(dimension)} $sqlOp ($formattedValues)", bindings.toList)
-
- case Intersection(operands) =>
- val (sql, bindings) = filterToSqlMultiple(operands).unzip
- (sql.mkString("(", " and ", ")"), bindings.flatten.toList)
-
- case Union(operands) =>
- val (sql, bindings) = filterToSqlMultiple(operands).unzip
- (sql.mkString("(", " or ", ")"), bindings.flatten.toList)
- }
- }
-
- protected def limitToSql(): String
-
- /**
- * @param escapedMainTableName Should be escaped
- */
- protected def sortingToSql(escapedMainTableName: String, sorting: Sorting, namingStrategy: NamingStrategy): String = {
- sorting match {
- case Dimension(optSortingTableName, field, order) =>
- val sortingTableName = optSortingTableName.map(namingStrategy.table).getOrElse(escapedMainTableName)
- val fullName = s"$sortingTableName.${namingStrategy.column(field)}"
-
- s"$fullName ${orderToSql(order)}"
-
- case Sequential(xs) =>
- xs.map(sortingToSql(escapedMainTableName, _, namingStrategy)).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 PostgresQueryBuilderParameters(tableData: QueryBuilder.TableData,
- links: Map[String, TableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends QueryBuilderParameters {
-
- def limitToSql(): String = {
- pagination.map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- s"limit ${pagination.pageSize} OFFSET $startFrom"
- } getOrElse ""
- }
-
-}
-
-/**
- * @param links Links to another tables grouped by foreignTableName
- */
-final case class MysqlQueryBuilderParameters(tableData: QueryBuilder.TableData,
- links: Map[String, TableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends QueryBuilderParameters {
-
- def limitToSql(): String =
- pagination
- .map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- s"limit $startFrom, ${pagination.pageSize}"
- }
- .getOrElse("")
-
-}
-
-abstract class QueryBuilder[T, D <: SqlIdiom, N <: NamingStrategy](val parameters: QueryBuilderParameters)(
- implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner) {
-
- def run: Seq[T] = runner(parameters)
-
- def runCount: QueryBuilder.CountResult = countRunner(parameters)
-
- /**
- * Runs the query and returns total found rows without considering of pagination.
- */
- def runWithCount: (Seq[T], Int, Option[LocalDateTime]) = {
- val (total, lastUpdate) = runCount
- (run, total, lastUpdate)
- }
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, D, N]
-
- def withFilter(filter: Option[SearchFilterExpr]): QueryBuilder[T, D, N] = {
- filter.fold(this)(withFilter)
- }
-
- def resetFilter: QueryBuilder[T, D, N] = withFilter(SearchFilterExpr.Empty)
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, D, N]
-
- def withSorting(sorting: Option[Sorting]): QueryBuilder[T, D, N] = {
- sorting.fold(this)(withSorting)
- }
-
- def resetSorting: QueryBuilder[T, D, N] = withSorting(Sorting.Empty)
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, D, N]
-
- def withPagination(pagination: Option[Pagination]): QueryBuilder[T, D, N] = {
- pagination.fold(this)(withPagination)
- }
-
- def resetPagination: QueryBuilder[T, D, N]
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
index dc03a52..7366151 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
@@ -70,7 +70,7 @@ sealed trait SlickQueryBuilderParameters {
}
def toSql(countQuery: Boolean = false)(implicit profile: JdbcProfile): SQLActionBuilder = {
- toSql(countQuery, QueryBuilderParameters.AllFields)
+ toSql(countQuery, SlickQueryBuilderParameters.AllFields)
}
def toSql(countQuery: Boolean, fields: Set[String])(implicit profile: JdbcProfile): SQLActionBuilder = {
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala
deleted file mode 100644
index 4c25afa..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package xyz.driver.pdsuicommon.db.repositories
-
-import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue
-import xyz.driver.pdsuicommon.db.MysqlQueryBuilder
-
-trait BridgeUploadQueueRepository extends Repository {
-
- type EntityT = BridgeUploadQueue.Item
-
- def add(draft: EntityT): EntityT
-
- def getById(kind: String, tag: String): Option[EntityT]
-
- def getOne(kind: String): Option[BridgeUploadQueue.Item]
-
- def update(entity: EntityT): EntityT
-
- def delete(kind: String, tag: String): Unit
-
- def buildQuery: MysqlQueryBuilder[EntityT]
-}
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 d671e80..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
+++ /dev/null
@@ -1,4 +0,0 @@
-package xyz.driver.pdsuicommon.db.repositories
-
-// For further usage and migration to Postgres and slick
-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
- }
-
-}