-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the backfill implementation for MSSQL client (#74)
* Add the backfill implementation for MSSQL client ## Scope Part of #61 This PR implements the backfill behavior for MSSQL server client, where the client queries all data from the data source and returns this data as a LazyList. NOTE: the `LazyList` class is used because the `Stream` class is deprecated * Fix unit tests * Update framework/arcane-framework/src/main/scala/models/DataColumn.scala Co-authored-by: George Zubrienko <[email protected]> * Review fixes --------- Co-authored-by: George Zubrienko <[email protected]>
- Loading branch information
1 parent
35a58e5
commit 666cfd4
Showing
7 changed files
with
240 additions
and
28 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
framework/arcane-framework/src/main/resources/get_select_all_query.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
framework/arcane-framework/src/main/resources/get_select_all_query_date_partitioned.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
framework/arcane-framework/src/main/scala/models/DataCell.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.sneaksanddata.arcane.framework | ||
package models | ||
|
||
/** | ||
* Represents a row of data. | ||
*/ | ||
type DataRow = List[DataCell] | ||
|
||
/** | ||
* Represents a row of data. | ||
* | ||
* @param name The name of the row. | ||
* @param Type The type of the row. | ||
* @param value The value of the row. | ||
*/ | ||
case class DataCell(name: String, Type: ArcaneType, value: Any) | ||
|
||
/** | ||
* Companion object for [[DataCell]]. | ||
*/ | ||
object DataCell: | ||
def apply(name: String, Type: ArcaneType, value: Any): DataCell = new DataCell(name, Type, value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
framework/arcane-framework/src/main/scala/services/mssql/QueryResult.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package com.sneaksanddata.arcane.framework | ||
package services.mssql | ||
|
||
import models.{DataCell, DataRow} | ||
import services.mssql.MsSqlConnection.toArcaneType | ||
|
||
import java.sql.{ResultSet, Statement} | ||
import scala.annotation.tailrec | ||
import scala.util.{Failure, Success, Try} | ||
|
||
/** | ||
* Represents the result of a query to a SQL database. | ||
*/ | ||
trait QueryResult[Output] { | ||
|
||
type OutputType = Output | ||
|
||
/** | ||
* Reads the result of the SQL query mapped to an output type. | ||
* | ||
* @return The result of the query. | ||
*/ | ||
def read: OutputType | ||
|
||
} | ||
|
||
/** | ||
* Lazy-list based implementation of [[QueryResult]]. | ||
* | ||
* @param statement The statement used to execute the query. | ||
* @param resultSet The result set of the query. | ||
*/ | ||
class LazyQueryResult(statement: Statement, resultSet: ResultSet) extends QueryResult[LazyList[DataRow]] with AutoCloseable { | ||
|
||
/** | ||
* Reads the result of the query. | ||
* | ||
* @return The result of the query. | ||
*/ | ||
override def read: this.OutputType = | ||
val columns = resultSet.getMetaData.getColumnCount | ||
LazyList.continually(resultSet) | ||
.takeWhile(_.next()) | ||
.map(row => { | ||
toDataRow(row, columns, List.empty) match { | ||
case Success(dataRow) => dataRow | ||
case Failure(exception) => throw exception | ||
} | ||
}) | ||
|
||
|
||
/** | ||
* Closes the statement and the result set owned by this object. | ||
* When a Statement object is closed, its current ResultSet object, if one exists, is also closed. | ||
*/ | ||
override def close(): Unit = statement.close() | ||
|
||
@tailrec | ||
private def toDataRow(row: ResultSet, columns: Int, acc: DataRow): Try[DataRow] = | ||
if columns == 0 then Success(acc) | ||
else | ||
val name = row.getMetaData.getColumnName(columns) | ||
val value = row.getObject(columns) | ||
val dataType = row.getMetaData.getColumnType(columns) | ||
toArcaneType(dataType) match | ||
case Success(arcaneType) => toDataRow(row, columns - 1, DataCell(name, arcaneType, value) :: acc) | ||
case Failure(exception) => Failure(exception) | ||
} | ||
|
||
/** | ||
* Companion object for [[LazyQueryResult]]. | ||
*/ | ||
object LazyQueryResult { | ||
|
||
/** | ||
* The output type of the query result. | ||
*/ | ||
type OutputType = LazyList[DataRow] | ||
|
||
/** | ||
* Creates a new [[LazyQueryResult]] object. | ||
* | ||
* @param statement The statement used to execute the query. | ||
* @param resultSet The result set of the query. | ||
* @return The new [[LazyQueryResult]] object. | ||
*/ | ||
def apply(statement: Statement, resultSet: ResultSet): LazyQueryResult = new LazyQueryResult(statement, resultSet) | ||
} |
44 changes: 44 additions & 0 deletions
44
framework/arcane-framework/src/main/scala/services/mssql/QueryRunner.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.sneaksanddata.arcane.framework | ||
package services.mssql | ||
|
||
import java.sql.{Connection, ResultSet, Statement} | ||
import scala.concurrent.{Future, blocking} | ||
|
||
/** | ||
* A class that runs a query on a SQL database. | ||
* This class is intended to run a single query and traverse the results only once using the forward-only read-only | ||
* cursor. | ||
* | ||
*/ | ||
class QueryRunner: | ||
|
||
/** | ||
* A factory for creating a QueryResult object from a statement and a result set. | ||
* | ||
* @tparam Output The type of the output of the query. | ||
*/ | ||
private type ResultFactory[Output] = (Statement, ResultSet) => QueryResult[Output] | ||
|
||
private implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global | ||
|
||
/** | ||
* Executes the given query on the given connection. | ||
* | ||
* The QueryResult object produced by this method must not outlive the connection object passed to it. | ||
* | ||
* @param query The query to execute. | ||
* @param connection The connection to execute the query on. | ||
* @return The result of the query. | ||
*/ | ||
def executeQuery[Result](query: MsSqlQuery, connection: Connection, resultFactory: ResultFactory[Result]): Future[QueryResult[Result]] = | ||
Future { | ||
val statement = connection.createStatement() | ||
val resultSet = blocking { | ||
statement.executeQuery(query) | ||
} | ||
resultFactory(statement, resultSet) | ||
} | ||
|
||
|
||
object QueryRunner: | ||
def apply(): QueryRunner = new QueryRunner() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters