Skip to content

Commit

Permalink
Merge branch 'master' into update/sbt-scoverage-2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jczuchnowski committed Jul 19, 2022
2 parents 068dbb6 + f20d67b commit 4e0a7cc
Show file tree
Hide file tree
Showing 10 changed files with 573 additions and 19 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: CI

env:
JDK_JAVA_OPTIONS: -XX:+PrintCommandLineFlags -XX:+UseG1GC -Xmx4g -Xms4g
JVM_OPTS: -XX:+PrintCommandLineFlags -XX:+UseG1GC -Xmx4g -Xms4g

on:
pull_request:
push:
Expand Down Expand Up @@ -31,7 +35,7 @@ jobs:
fail-fast: false
matrix:
java: ['adopt@1.8', 'adopt@1.11']
scala: ['2.12.15', '2.13.8']
scala: ['2.12.16', '2.13.8']
steps:
- name: Checkout current branch
uses: actions/checkout@v2.3.4
Expand Down
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.5.3"
version = "3.5.8"
maxColumn = 120
align.preset = most
continuationIndent.defnSite = 2
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ lazy val jdbc = project
libraryDependencies ++= Seq(
"dev.zio" %% "zio-test" % zioVersion % Test,
"dev.zio" %% "zio-test-sbt" % zioVersion % Test,
"org.postgresql" % "postgresql" % "42.3.6" % Test,
"org.postgresql" % "postgresql" % "42.4.0" % Test,
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersScalaVersion % Test
)
)
Expand Down Expand Up @@ -162,7 +162,7 @@ lazy val oracle = project
"org.testcontainers" % "database-commons" % testcontainersVersion % Test,
"org.testcontainers" % "oracle-xe" % testcontainersVersion % Test,
"org.testcontainers" % "jdbc" % testcontainersVersion % Test,
"com.oracle.database.jdbc" % "ojdbc8" % "21.5.0.0" % Test,
"com.oracle.database.jdbc" % "ojdbc8" % "21.6.0.0.1" % Test,
"com.dimafeng" %% "testcontainers-scala-oracle-xe" % testcontainersScalaVersion % Test,
"ch.qos.logback" % "logback-classic" % logbackVersion % Test
)
Expand All @@ -181,7 +181,7 @@ lazy val postgres = project
"org.testcontainers" % "database-commons" % testcontainersVersion % Test,
"org.testcontainers" % "postgresql" % testcontainersVersion % Test,
"org.testcontainers" % "jdbc" % testcontainersVersion % Test,
"org.postgresql" % "postgresql" % "42.3.6" % Compile,
"org.postgresql" % "postgresql" % "42.4.0" % Compile,
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersScalaVersion % Test,
"ch.qos.logback" % "logback-classic" % logbackVersion % Test
)
Expand Down
4 changes: 2 additions & 2 deletions project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import BuildInfoKeys._
import scalafix.sbt.ScalafixPlugin.autoImport.scalafixSemanticdb

object BuildHelper {
val SilencerVersion = "1.7.8"
val Scala212 = "2.12.15"
val SilencerVersion = "1.7.9"
val Scala212 = "2.12.16"
val Scala213 = "2.13.8"
val ScalaDotty = "3.0.0-RC3"

Expand Down
6 changes: 3 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" %
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.0")
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.22")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.0")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.2")
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.9")
addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.2.16")
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.0")
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.3.1")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.1")
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.3.3")
179 changes: 172 additions & 7 deletions sqlserver/src/main/scala/zio/sql/sqlserver/SqlServerRenderModule.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package zio.sql.sqlserver

import zio.schema.Schema
import zio.Chunk
import zio.schema.StandardType._
import zio.schema._
import zio.sql.driver.Renderer
import zio.sql.driver.Renderer.Extensions

import java.time.format.{ DateTimeFormatter, DateTimeFormatterBuilder }
import java.time._

trait SqlServerRenderModule extends SqlServerSqlModule { self =>

override def renderRead(read: self.Read[_]): String = {
Expand Down Expand Up @@ -32,6 +37,17 @@ trait SqlServerRenderModule extends SqlServerSqlModule { self =>

object SqlServerRenderer {

private val fmtDateTimeOffset = new DateTimeFormatterBuilder().parseCaseInsensitive
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendOffset("+HH:MM", "Z")
.toFormatter()

private val fmtTimeOffset = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendOffset("+HH:MM", "Z")
.toFormatter()

private[zio] def renderReadImpl(read: self.Read[_])(implicit render: Renderer): Unit =
read match {
// case Read.Mapped(read, _) => renderReadImpl(read.asInstanceOf[Read[Out]])
Expand Down Expand Up @@ -63,7 +79,7 @@ trait SqlServerRenderModule extends SqlServerSqlModule { self =>
buildWhereExpr(whereExpr)
groupByExprs match {
case Read.ExprSet.ExprCons(_, _) =>
render(" GROUP BT ")
render(" GROUP BY ")
buildExprList(groupByExprs)

havingExpr match {
Expand Down Expand Up @@ -370,18 +386,167 @@ trait SqlServerRenderModule extends SqlServerSqlModule { self =>
buildExpr(expr)
}

private def buildColumnNames(sources: SelectionSet[_])(implicit render: Renderer): Unit =
sources match {
case SelectionSet.Empty => ()
case SelectionSet.Cons(columnSelection, tail) =>
val _ = columnSelection.name.map { name =>
render(name)
}
tail.asInstanceOf[SelectionSet[_]] match {
case SelectionSet.Empty => ()
case next @ SelectionSet.Cons(_, _) =>
render(", ")
buildColumnNames(next.asInstanceOf[SelectionSet[_]])(render)
}
}

private def buildInsertValues[A](col: Seq[A])(implicit render: Renderer, schema: Schema[A]): Unit =
col.toList match {
case head :: Nil =>
render("(")
buildInsertValue(head)
render(");")
case head :: next =>
render("(")
buildInsertValue(head)(render, schema)
render(" ),")
buildInsertValues(next)
case Nil => ()
}

private def buildInsertValue[Z](z: Z)(implicit render: Renderer, schema: Schema[Z]): Unit =
schema.toDynamic(z) match {
case DynamicValue.Record(listMap) =>
listMap.values.toList match {
case head :: Nil => buildDynamicValue(head)
case head :: next =>
buildDynamicValue(head)
render(", ")
buildDynamicValues(next)
case Nil => ()
}
case value => buildDynamicValue(value)
}

private def buildDynamicValues(dynValues: List[DynamicValue])(implicit render: Renderer): Unit =
dynValues match {
case head :: Nil => buildDynamicValue(head)
case head :: tail =>
buildDynamicValue(head)
render(", ")
buildDynamicValues(tail)
case Nil => ()
}

// TODO render each type according to their specifics & test it
private def buildDynamicValue(dynValue: DynamicValue)(implicit render: Renderer): Unit =
dynValue match {
case DynamicValue.Primitive(value, typeTag) =>
// need to do this since StandardType is invariant in A
StandardType.fromString(typeTag.tag) match {
case Some(v) =>
v match {
case BigDecimalType =>
render(value)
case StandardType.InstantType(formatter) =>
render(s"'${formatter.format(value.asInstanceOf[Instant])}'")
case CharType => render(s"N'${value}'")
case IntType => render(value)
case StandardType.MonthDayType => render(s"'${value}'")
case BinaryType =>
val chunk = value.asInstanceOf[Chunk[Object]]
render("CONVERT(VARBINARY(MAX),'")
for (b <- chunk)
render(String.format("%02x", b))
render("', 2)")
case StandardType.MonthType => render(s"'${value}'")
case StandardType.LocalDateTimeType(formatter) =>
render(s"'${formatter.format(value.asInstanceOf[LocalDateTime])}'")
case UnitType => render("null") // None is encoded as Schema[Unit].transform(_ => None, _ => ())
case StandardType.YearMonthType => render(s"'${value}'")
case DoubleType => render(value)
case StandardType.YearType => render(s"'${value}'")
case StandardType.OffsetDateTimeType(_) =>
render(s"'${fmtDateTimeOffset.format(value.asInstanceOf[OffsetDateTime])}'")
case StandardType.ZonedDateTimeType(_) =>
render(s"'${fmtDateTimeOffset.format(value.asInstanceOf[ZonedDateTime])}'")
case BigIntegerType => render(value)
case UUIDType => render(s"'${value}'")
case StandardType.ZoneOffsetType => render(s"'${value}'")
case ShortType => render(value)
case StandardType.LocalTimeType(_) =>
render(s"'${DateTimeFormatter.ISO_LOCAL_TIME.format(value.asInstanceOf[LocalTime])}'")
case StandardType.OffsetTimeType(_) =>
render(s"'${fmtTimeOffset.format(value.asInstanceOf[OffsetTime])}'")
case LongType => render(value)
case StringType => render(s"N'${value}'")
case StandardType.PeriodType => render(s"'${value}'")
case StandardType.ZoneIdType => render(s"'${value}'")
case StandardType.LocalDateType(_) =>
render(s"'${DateTimeFormatter.ISO_LOCAL_DATE.format(value.asInstanceOf[LocalDate])}'")
case BoolType =>
val b = value.asInstanceOf[Boolean]
if (b) {
render('1')
} else {
render('0')
}
case DayOfWeekType => render(s"'${value}'")
case FloatType => render(value)
case StandardType.DurationType => render(s"'${value}'")
}
case None => ()
}
case DynamicValue.Tuple(left, right) =>
buildDynamicValue(left)
render(", ")
buildDynamicValue(right)
case DynamicValue.SomeValue(value) => buildDynamicValue(value)
case DynamicValue.NoneValue => render("null")
case _ => ()
}

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

def renderDeleteImpl(delete: Delete[_])(implicit render: Renderer) = {
render("DELETE FROM ")
buildTable(delete.table)
buildWhereExpr(delete.whereExpr)
}

// TODO https://github.com/zio/zio-sql/issues/160
def renderUpdateImpl(update: Update[_])(implicit render: Renderer) =
???
update match {
case Update(table, set, whereExpr) =>
render("UPDATE ")
buildTable(table)
render(" SET ")
buildSet(set)
buildWhereExpr(whereExpr)
}

def renderInsertImpl[A](insert: Insert[_, A])(implicit render: Renderer, schema: Schema[A]) = {
render("INSERT INTO ")
buildTable(insert.table)

// TODO https://github.com/zio/zio-sql/issues/160
def renderInsertImpl[A](insert: Insert[_, A])(implicit render: Renderer, schema: Schema[A]) =
???
render(" (")
buildColumnNames(insert.sources)
render(") VALUES ")

buildInsertValues(insert.values)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package zio.sql.sqlserver

import zio.schema.Schema
import zio.sql.Sql

import java.time.{ Instant, LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime, ZonedDateTime }
import java.time.format.DateTimeFormatter

trait SqlServerSqlModule extends Sql { self =>

override type TableExtension[A] = SqlServerSpecific.SqlServerTable[A]
Expand Down Expand Up @@ -89,4 +93,24 @@ trait SqlServerSqlModule extends Sql { self =>
}
}

implicit val localDateSchema =
Schema.primitive[LocalDate](zio.schema.StandardType.LocalDateType(DateTimeFormatter.ISO_LOCAL_DATE))

implicit val instantSchema =
Schema.primitive[Instant](zio.schema.StandardType.InstantType(DateTimeFormatter.ISO_OFFSET_DATE_TIME))

implicit val localTimeSchema =
Schema.primitive[LocalTime](zio.schema.StandardType.LocalTimeType(DateTimeFormatter.ISO_LOCAL_TIME))

implicit val localDateTimeSchema =
Schema.primitive[LocalDateTime](zio.schema.StandardType.LocalDateTimeType(DateTimeFormatter.ISO_LOCAL_DATE_TIME))

implicit val offsetTimeSchema =
Schema.primitive[OffsetTime](zio.schema.StandardType.OffsetTimeType(DateTimeFormatter.ISO_OFFSET_TIME))

implicit val offsetDateTimeSchema =
Schema.primitive[OffsetDateTime](zio.schema.StandardType.OffsetDateTimeType(DateTimeFormatter.ISO_OFFSET_DATE_TIME))

implicit val zonedDatetimeSchema =
Schema.primitive[ZonedDateTime](zio.schema.StandardType.ZonedDateTimeType(DateTimeFormatter.ISO_ZONED_DATE_TIME))
}
24 changes: 24 additions & 0 deletions sqlserver/src/test/resources/db_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ create table order_details
unit_price money not null
);

create table all_types(
id varchar(36) not null primary key,
bytearray varbinary(100) not null,
bigdecimal decimal(28) not null,
boolean_ bit not null,
char_ char(1) not null,
double_ float not null,
float_ real not null,
instant datetimeoffset not null,
int_ int not null,
optional_int int,
localdate date not null,
localdatetime datetime2 not null,
localtime time not null,
long_ bigint not null,
offsetdatetime datetimeoffset not null,
offsettime datetimeoffset not null,
short smallint not null,
string varchar(max) not null,
uuid varchar(36) not null,
zoneddatetime datetimeoffset not null,
nchar_ nchar(1) not null,
nvarchar_ nvarchar(30) not null
);

insert into customers
(id, first_name, last_name, verified, dob)
Expand Down
Loading

0 comments on commit 4e0a7cc

Please sign in to comment.