Skip to content

Commit

Permalink
zio#154: Implement renderUpdate for Oracle (zio#700)
Browse files Browse the repository at this point in the history
* 154: (Draft) Implement `renderUpate` for Oracle

* zio#154: (Draft) Implement `renderUpdate` for Oracle

* zio#154: Implement `renderUpdate` for Oracle

* zio#154: Extend tests of `renderUpdate` for Oracle

* zio#154: fix formatting

* zio#154: fix 2.12.15 compatibility

* zio#154: adjust docs

* zio#154: fix non-related flaky test
  • Loading branch information
baldram authored May 29, 2022
1 parent 7d2f448 commit da4cfcf
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 53 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ Connection pool | :white_check_mark:

#### Db-specific features:

Feature | PostgreSQL | SQL Server | Oracle | MySQL
:------------ | :-------------| :-------------| :-------------| :-------------
Feature | PostgreSQL | SQL Server | Oracle | MySQL
:------------ | :-------------| :-------------|:-------------------| :-------------
Render Read | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
Render Delete | :heavy_check_mark: | :heavy_check_mark: | | :white_check_mark: |
Render Update | :heavy_check_mark: | | | :white_check_mark: |
Render Insert | :heavy_check_mark: | | |
Render Delete | :heavy_check_mark: | :heavy_check_mark: | :white_check_mark: | :white_check_mark: |
Render Update | :heavy_check_mark: | | :white_check_mark: | :white_check_mark: |
Render Insert | :heavy_check_mark: | | |
Functions | :heavy_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Types | :white_check_mark: | | | :white_check_mark: |
Operators | | | |
Types | :white_check_mark: | | | :white_check_mark: |
Operators | | | |

## What is ZIO SQL?
ZIO SQL lets you write type-safe, type-inferred, and composable SQL queries in ordinary Scala, helping you prevent persistence bugs before they happen, and leverage your IDE to make writing SQL productive, safe, and fun.
Expand Down
5 changes: 5 additions & 0 deletions driver/src/main/scala/zio/sql/driver/Renderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ private[sql] class Renderer(val builder: StringBuilder) extends AnyVal {

private[sql] object Renderer {
def apply(): Renderer = new Renderer(new StringBuilder)

implicit class Extensions(val value: String) {
def doubleQuoted: String = s""""$value""""
def singleQuoted: String = s"'$value'"
}
}
63 changes: 57 additions & 6 deletions oracle/src/main/scala/zio/sql/oracle/OracleRenderModule.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package zio.sql.oracle

import zio.schema.Schema
import zio.sql.driver.Renderer
import zio.sql.driver.Renderer.Extensions

import scala.collection.mutable

trait OracleRenderModule extends OracleSqlModule { self =>

Expand All @@ -18,8 +22,13 @@ trait OracleRenderModule extends OracleSqlModule { self =>
builder.toString()
}

override def renderUpdate(update: self.Update[_]): String = ???
override def renderUpdate(update: self.Update[_]): String = {
implicit val render: Renderer = Renderer()
OracleRender.renderUpdateImpl(update)
render.toString
}

// TODO: to consider the refactoring and using the implicit `Renderer`, see `renderExpr` in `PostgresRenderModule`
private def buildExpr[A, B](expr: self.Expr[_, A, B], builder: StringBuilder): Unit = expr match {
case Expr.Subselect(subselect) =>
builder.append(" (")
Expand Down Expand Up @@ -49,16 +58,20 @@ trait OracleRenderModule extends OracleSqlModule { self =>
case Expr.Relational(left, right, op) =>
buildExpr(left, builder)
builder.append(" ").append(op.symbol).append(" ")
buildExpr(right, builder)
right.asInstanceOf[Expr[_, A, B]] match {
case Expr.Literal(true) => val _ = builder.append("1")
case Expr.Literal(false) => val _ = builder.append("0")
case otherValue => buildExpr(otherValue, builder)
}
case Expr.In(value, set) =>
buildExpr(value, builder)
buildReadString(set, builder)
case Expr.Literal(true) =>
val _ = builder.append("1")
val _ = builder.append("1 = 1")
case Expr.Literal(false) =>
val _ = builder.append("0")
val _ = builder.append("0 = 1")
case Expr.Literal(value) =>
val _ = builder.append(value.toString) // todo fix escaping
val _ = builder.append(value.toString.singleQuoted)
case Expr.AggregationCall(param, aggregation) =>
builder.append(aggregation.name.name)
builder.append("(")
Expand Down Expand Up @@ -313,7 +326,7 @@ trait OracleRenderModule extends OracleSqlModule { self =>
val _ = builder.append(" ")
}

private def buildDeleteString(delete: Delete[_], builder: StringBuilder) = {
private def buildDeleteString(delete: Delete[_], builder: mutable.StringBuilder): Unit = {
builder.append("DELETE FROM ")
buildTable(delete.table, builder)
delete.whereExpr match {
Expand All @@ -324,4 +337,42 @@ trait OracleRenderModule extends OracleSqlModule { self =>
}
}

private[oracle] object OracleRender {

def renderUpdateImpl(update: Update[_])(implicit render: Renderer): Unit =
update match {
case Update(table, set, whereExpr) =>
render("UPDATE ")
buildTable(table, render.builder)
render(" SET ")
renderSet(set)
render(" WHERE ")
buildExpr(whereExpr, render.builder)
}

def renderSet(set: List[Set[_, _]])(implicit render: Renderer): Unit =
set match {
case head :: tail =>
renderSetLhs(head.lhs)
render(" = ")
buildExpr(head.rhs, render.builder)
tail.foreach { setEq =>
render(", ")
renderSetLhs(setEq.lhs)
render(" = ")
buildExpr(setEq.rhs, render.builder)
}
case Nil => // TODO restrict Update to not allow empty set
}

private[zio] def renderSetLhs[A, B](expr: self.Expr[_, A, B])(implicit render: Renderer): Unit =
expr match {
case Expr.Source(table, column) =>
(table, column.name) match {
case (tableName, Some(columnName)) => val _ = render(tableName, ".", columnName)
case _ => ()
}
case _ => ()
}
}
}
33 changes: 0 additions & 33 deletions oracle/src/test/scala/zio/sql/oracle/OracleModuleSpec.scala

This file was deleted.

62 changes: 62 additions & 0 deletions oracle/src/test/scala/zio/sql/oracle/OracleSqlModuleSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package zio.sql.oracle

import zio.test.Assertion._
import zio.test.TestAspect._
import zio.test._

import scala.language.postfixOps

object OracleSqlModuleSpec extends OracleRunnableSpec with ShopSchema {

import Customers._

override def specLayered: Spec[SqlDriver, Exception] = suite("Oracle module")(
test("Can update selected rows") {

/**
* UPDATE customers SET customers.first_name = 'Antek'
* WHERE 1 = 1 and customers.verified = 0 and customers.verified <> 1
*/
val query =
update(customers)
.set(fName, "Antek")
.where(verified isNotTrue)
.where(verified <> true) // we intentionally verify two syntax variants

assertZIO(execute(query))(equalTo(1))
},
test("Can update all rows") {

/**
* UPDATE customers SET customers.first_name = 'Antek' WHERE 1 = 1
*/
val query = update(customers).set(fName, "Antek")

assertZIO(execute(query))(equalTo(5))
},
test("Can delete from single table with a condition") {

/**
* DELETE FROM customers WHERE customers.verified = 0
*/
val query = deleteFrom(customers) where (verified isNotTrue)

val expected = 1
val result = execute(query)

assertZIO(result)(equalTo(expected))
},
test("Can delete all from a single table") {

/**
* DELETE FROM customers
*/
val query = deleteFrom(customers)

val expected = 4
val result = execute(query)

assertZIO(result)(equalTo(expected))
}
) @@ sequential
}
8 changes: 5 additions & 3 deletions oracle/src/test/scala/zio/sql/oracle/ShopSchema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import zio.sql.Jdbc
trait ShopSchema extends Jdbc { self =>
import self.ColumnSet._

object Customers {
object Customers {

val customers =
(uuid("id") ++ localDate("dob") ++ string("first_name") ++ string("last_name") ++ boolean("verified"))
(uuid("id") ++ localDate("dob") ++ string("first_name") ++ string("last_name") ++
boolean("verified") ++ zonedDateTime("Created_timestamp"))
.table("customers")

val (customerId, dob, fName, lName, verified) = customers.columns
val (customerId, dob, fName, lName, verified, createdTimestamp) = customers.columns
}
object Orders {
val orders = (uuid("id") ++ uuid("customer_id") ++ localDate("order_date")).table("orders")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ object FunctionDefSpec extends PostgresRunnableSpec with DbSchema {
)
},
test("localtime with precision") {
val precision = 0
val precision = 3
assertZIO(execute(select(LocaltimeWithPrecision(precision))).runHead.some.map(_.toString))(
matchesRegex(
s"([0-9]{2}):[0-9]{2}:[0-9].[0-9]{$precision}"
s"\\d{2}:\\d{2}:\\d{2}\\.\\d{$precision}"
)
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import scala.language.postfixOps
import zio.schema.Schema
import java.time.format.DateTimeFormatter

object PostgresModuleSpec extends PostgresRunnableSpec with DbSchema {
object PostgresSqlModuleSpec extends PostgresRunnableSpec with DbSchema {

import AggregationDef._
import Customers._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package zio.sql.sqlserver

import zio.schema.Schema
import zio.sql.driver.Renderer
import zio.sql.driver.Renderer.Extensions

trait SqlServerRenderModule extends SqlServerSqlModule { self =>

Expand Down Expand Up @@ -153,7 +154,7 @@ trait SqlServerRenderModule extends SqlServerSqlModule { self =>
.asInstanceOf[java.time.OffsetDateTime]
.format(java.time.format.DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"))
s"'$x'"
case _ => s"'${value.toString}'"
case _ => value.toString.singleQuoted
}
render(lit)
case Expr.AggregationCall(param, aggregation) =>
Expand Down

0 comments on commit da4cfcf

Please sign in to comment.