Skip to content

Commit

Permalink
docs for dbfunction and query
Browse files Browse the repository at this point in the history
  • Loading branch information
salamonpavel committed Dec 8, 2023
1 parent 320073e commit 62f62b7
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 61 deletions.
8 changes: 5 additions & 3 deletions core/src/main/scala/za/co/absa/fadb/DBEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,22 @@ abstract class DBEngine[F[_]: Monad] {
protected def run[R](query: QueryType[R]): F[Seq[R]]

/**
* The actual query executioner of the queries of the engine
* The actual query executioner of the queries of the engine with status
* @param query - the query to execute
* @tparam R - return the of the query
* @return - sequence of the results of database query
*/
def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]]
def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]]

/**
* Public method to execute when query is expected to return multiple results
* @param query - the query to execute
* @tparam R - return the of the query
* @return - sequence of the results of database query
*/
def fetchAll[R](query: QueryType[R]): F[Seq[R]] = run(query)
def fetchAll[R](query: QueryType[R]): F[Seq[R]] = {
run(query)
}

/**
* Public method to execute when query is expected to return exactly one row
Expand Down
138 changes: 85 additions & 53 deletions core/src/main/scala/za/co/absa/fadb/DBFunction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import za.co.absa.fadb.status.handling.QueryStatusHandling
import scala.language.higherKinds

/**
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
* possibility of override
* @param schema - the schema the function belongs into
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
* connection to the database
* @tparam I - the type covering the input fields of the database function
* @tparam R - the type covering the returned fields from the database function
* @tparam E - the type of the [[DBEngine]] engine
* `DBFunction` is an abstract class that represents a database function.
* @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name.
* @param schema - The schema the function belongs to.
* @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database).
* @tparam I - The type covering the input fields of the database function.
* @tparam R - The type covering the returned fields from the database function.
* @tparam E - The type of the [[DBEngine]] engine.
* @tparam F - The type of the context in which the database function is executed.
*/
abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)(implicit
override val schema: DBSchema,
Expand All @@ -44,19 +44,47 @@ abstract class DBFunction[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverr
def this(functionName: String)(implicit schema: DBSchema, dBEngine: E) = this(Some(functionName))

/**
* Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the
* DBEngine specific mix-in.
* @param values - the values to pass over to the database function
* @return - the SQL query in the format specific to the provided [[DBEngine]]
* Function to create the DB function call specific to the provided [[DBEngine]].
* Expected to be implemented by the DBEngine specific mix-in.
* @param values - The values to pass over to the database function.
* @return - The SQL query in the format specific to the provided [[DBEngine]].
*/
protected def query(values: I): dBEngine.QueryType[R]

/*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/
/**
* Executes the database function and returns multiple results.
* @param values - The values to pass over to the database function.
* @return - A sequence of results from the database function.
*/
protected def multipleResults(values: I): F[Seq[R]] = dBEngine.fetchAll(query(values))

/**
* Executes the database function and returns a single result.
* @param values - The values to pass over to the database function.
* @return - A single result from the database function.
*/
protected def singleResult(values: I): F[R] = dBEngine.fetchHead(query(values))

/**
* Executes the database function and returns an optional result.
* @param values - The values to pass over to the database function.
* @return - An optional result from the database function.
*/
protected def optionalResult(values: I): F[Option[R]] = dBEngine.fetchHeadOption(query(values))
}

/**
* `DBFunctionWithStatus` is an abstract class that represents a database function with a status.
* It extends the [[DBFunction]] class and adds handling for the status of the function invocation.
* @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name.
* @param schema - The schema the function belongs to.
* @param dBEngine - The database engine that is supposed to execute the function (contains connection to the database).
* @param queryStatusHandling - The [[QueryStatusHandling]] instance that handles the status of the function invocation.
* @tparam I - The type covering the input fields of the database function.
* @tparam R - The type covering the returned fields from the database function.
* @tparam E - The type of the [[DBEngine]] engine.
* @tparam F - The type of the context in which the database function is executed.
*/
abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functionNameOverride: Option[String] = None)(
implicit
override val schema: DBSchema,
Expand All @@ -73,20 +101,24 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio
/**
* Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the
* DBEngine specific mix-in.
* @param values - the values to pass over to the database function
* @return - the SQL query in the format specific to the provided [[DBEngine]]
* @param values the values to pass over to the database function
* @return the SQL query in the format specific to the provided [[DBEngine]]
*/
protected def query(values: I): dBEngine.QueryWithStatusType[R]

def apply(values: I): F[Either[StatusException, R]] = dBEngine.fetchHeadWithStatus(query(values))
/**
* Executes the database function and returns multiple results.
* @param values
* @return A sequence of results from the database function.
*/
def apply(values: I): F[Either[StatusException, R]] = dBEngine.runWithStatus(query(values))

val defaultStatusField = "status"
val defaultStatusTextField = "statusText"

/**
* A mix-in to add the status fields into the SELECT statement
*
* @return a sequence of fields to use in SELECT
* The fields to select from the database function call
* @return the fields to select from the database function call
*/
override def fieldsToSelect: Seq[String] = {
Seq(
Expand All @@ -95,23 +127,23 @@ abstract class DBFunctionWithStatus[I, R, E <: DBEngine[F], F[_]: Monad](functio
) ++ super.fieldsToSelect
}

// implementation to be mixed in
// To be provided by an implementation of QueryStatusHandling
override def checkStatus[A](statusWithData: FunctionStatusWithData[A]): Either[StatusException, A]
}

object DBFunction {

/**
* Represents a function returning a set (in DB sense) of rows
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
* possibility of override
* @param schema - the schema the function belongs into
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
* connection to the database
* @tparam I - the type covering the input fields of the database function
* @tparam R - the type covering the returned fields from the database function
* @tparam E - the type of the [[DBEngine]] engine
*/
/**
* `DBMultipleResultFunction` is an abstract class that represents a database function returning multiple results.
* It extends the [[DBFunction]] class and overrides the apply method to return a sequence of results.
* @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name.
* @param schema - The schema the function belongs to.
* @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database).
* @tparam I - The type covering the input fields of the database function.
* @tparam R - The type covering the returned fields from the database function.
* @tparam E - The type of the [[DBEngine]] engine.
* @tparam F - The type of the context in which the database function is executed.
*/
abstract class DBMultipleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](
functionNameOverride: Option[String] = None
)(implicit schema: DBSchema, dBEngine: E)
Expand All @@ -132,17 +164,17 @@ object DBFunction {
def apply(values: I): F[Seq[R]] = multipleResults(values)
}

/**
* Represents a function returning exactly one record
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
* possibility of override
* @param schema - the schema the function belongs into
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
* connection to the database
* @tparam I - the type covering the input fields of the database function
* @tparam R - the type covering the returned fields from the database function
* @tparam E - the type of the [[DBEngine]] engine
*/
/**
* `DBSingleResultFunction` is an abstract class that represents a database function returning a single result.
* It extends the [[DBFunction]] class and overrides the apply method to return a single result.
* @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name.
* @param schema - The schema the function belongs to.
* @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database).
* @tparam I - The type covering the input fields of the database function.
* @tparam R - The type covering the returned fields from the database function.
* @tparam E - The type of the [[DBEngine]] engine.
* @tparam F - The type of the context in which the database function is executed.
*/
abstract class DBSingleResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](
functionNameOverride: Option[String] = None
)(implicit schema: DBSchema, dBEngine: E)
Expand All @@ -162,17 +194,17 @@ object DBFunction {
def apply(values: I): F[R] = singleResult(values)
}

/**
* Represents a function returning one optional record
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
* possibility of override
* @param schema - the schema the function belongs into
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
* connection to the database
* @tparam I - the type covering the input fields of the database function
* @tparam R - the type covering the returned fields from the database function
* @tparam E - the type of the [[DBEngine]] engine
*/
/**
* `DBOptionalResultFunction` is an abstract class that represents a database function returning an optional result.
* It extends the [[DBFunction]] class and overrides the apply method to return an optional result.
* @param functionNameOverride - Optional parameter to override the class name if it does not match the database function name.
* @param schema - The schema the function belongs to.
* @param dBEngine - The database engine that is supposed to execute the function (presumably contains connection to the database).
* @tparam I - The type covering the input fields of the database function.
* @tparam R - The type covering the returned fields from the database function.
* @tparam E - The type of the [[DBEngine]] engine.
* @tparam F - The type of the context in which the database function is executed.
*/
abstract class DBOptionalResultFunction[I, R, E <: DBEngine[F], F[_]: Monad](
functionNameOverride: Option[String] = None
)(implicit schema: DBSchema, dBEngine: E)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ package za.co.absa.fadb

import za.co.absa.fadb.status.FunctionStatus

/**
* Represents a function status with data.
* @param functionStatus the function status
* @param data the data
* @tparam A the type of the data
*/
case class FunctionStatusWithData[A](functionStatus: FunctionStatus, data: A)
22 changes: 21 additions & 1 deletion core/src/main/scala/za/co/absa/fadb/Query.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,34 @@ import za.co.absa.fadb.exceptions.StatusException

/**
* The basis for all query types of [[DBEngine]] implementations
*
* @tparam R - the return type of the query
*/
trait Query[R]

/**
* The basis for all query types of [[DBEngine]] implementations with status
* @tparam R - the return type of the query
*/
trait QueryWithStatus[A, B, R] {
/**
* Processes the status of the query and returns the status with data
* @param initialResult - the initial result of the query
* @return the status with data
*/
def processStatus(initialResult: A): FunctionStatusWithData[B]

/**
* Converts the status with data to either a status exception or the data
* @param statusWithData - the status with data
* @return either a status exception or the data
*/
def toStatusExceptionOrData(statusWithData: FunctionStatusWithData[B]): Either[StatusException, R]

/**
* Returns the result of the query or a status exception
* @param initialResult - the initial result of the query
* @return the result of the query or a status exception
*/
def getResultOrException(initialResult: A): Either[StatusException, R] = toStatusExceptionOrData(
processStatus(initialResult)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB
query.fragment.query[R].to[Seq].transact(transactor)
}

private def executeQueryWithStatusHandling[R](
private def executeQueryWithStatus[R](
query: QueryWithStatusType[R]
)(implicit readStatusWithDataR: Read[StatusWithData[R]]): F[Either[StatusException, R]] = {
query.fragment.query[StatusWithData[R]].unique.transact(transactor).map(query.getResultOrException)
Expand All @@ -68,7 +68,7 @@ class DoobieEngine[F[_]: Async: Monad](val transactor: Transactor[F]) extends DB
override def run[R](query: QueryType[R]): F[Seq[R]] =
executeQuery(query)(query.readR)

override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = {
executeQueryWithStatusHandling(query)(query.readStatusWithDataR)
override def runWithStatus[R](query: QueryWithStatusType[R]): F[Either[StatusException, R]] = {
executeQueryWithStatus(query)(query.readStatusWithDataR)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class SlickPgEngine(val db: Database)(implicit val executor: ExecutionContext) e
db.run(slickAction)
}

override def fetchHeadWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = {
override def runWithStatus[R](query: QueryWithStatusType[R]): Future[Either[StatusException, R]] = {
val slickAction = query.sql.as[Either[StatusException, R]](query.getStatusExceptionOrData).head
db.run(slickAction)
}
Expand Down

0 comments on commit 62f62b7

Please sign in to comment.