Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi committed Nov 30, 2023
1 parent ac4c01b commit f312d71
Show file tree
Hide file tree
Showing 10 changed files with 344 additions and 82 deletions.
270 changes: 248 additions & 22 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -8068,23 +8068,83 @@ Expr("Hello").contains("ll")
Basic operations on all the data types that ScalaSql supports mapping between Database types and Scala types
### DataTypes.constant

This example demonstrates a range of different data types being written
and read back via ScalaSQL

```scala
object MyEnum extends Enumeration {
val foo, bar, baz = Value
implicit def make: String => Value = withName
}
case class DataTypes[+T[_]](
myTinyInt: T[Byte],
mySmallInt: T[Short],
myInt: T[Int],
myBigInt: T[Long],
myDouble: T[Double],
myBoolean: T[Boolean],
myLocalDate: T[LocalDate],
myLocalTime: T[LocalTime],
myLocalDateTime: T[LocalDateTime],
myInstant: T[Instant],
myVarBinary: T[geny.Bytes],
myUUID: T[java.util.UUID],
myEnum: T[MyEnum.Value]
)
object DataTypes extends Table[DataTypes]
val value = DataTypes[Id](
myTinyInt = 123.toByte,
mySmallInt = 12345.toShort,
myInt = 12345678,
myBigInt = 12345678901L,
myDouble = 3.14,
myBoolean = true,
myLocalDate = LocalDate.parse("2023-12-20"),
myLocalTime = LocalTime.parse("10:15:30"),
myLocalDateTime = LocalDateTime.parse("2011-12-03T10:15:30"),
myInstant = Instant.parse("2011-12-03T10:15:30Z"),
myVarBinary = new geny.Bytes(Array[Byte](1, 2, 3, 4, 5, 6, 7, 8)),
myUUID = new java.util.UUID(1234567890L, 9876543210L),
myEnum = MyEnum.bar
)
db.run(
DataTypes.insert.columns(
_.myTinyInt := value.myTinyInt,
_.mySmallInt := value.mySmallInt,
_.myInt := value.myInt,
_.myBigInt := value.myBigInt,
_.myDouble := value.myDouble,
_.myBoolean := value.myBoolean,
_.myLocalDate := value.myLocalDate,
_.myLocalTime := value.myLocalTime,
_.myLocalDateTime := value.myLocalDateTime,
_.myInstant := value.myInstant,
_.myVarBinary := value.myVarBinary,
_.myUUID := value.myUUID,
_.myEnum := value.myEnum
)
) ==> 1
db.run(DataTypes.select) ==> Seq(value)
```






### DataTypes.nonRoundTrip



```scala
DataTypes.insert.columns(
_.myTinyInt := value.myTinyInt,
_.mySmallInt := value.mySmallInt,
_.myInt := value.myInt,
_.myBigInt := value.myBigInt,
_.myDouble := value.myDouble,
_.myBoolean := value.myBoolean,
_.myLocalDate := value.myLocalDate,
_.myLocalTime := value.myLocalTime,
_.myLocalDateTime := value.myLocalDateTime,
_.myInstant := value.myInstant,
_.myVarBinary := value.myVarBinary,
_.myUUID := value.myUUID,
_.myEnum := value.myEnum
NonRoundTripTypes.insert.columns(
_.myOffsetDateTime := value.myOffsetDateTime,
_.myZonedDateTime := value.myZonedDateTime
)
```

Expand All @@ -8103,31 +8163,108 @@ DataTypes.insert.columns(


```scala
DataTypes.select
NonRoundTripTypes.select
```




*
```scala
Seq(value)
Seq(normalize(value))
```



### DataTypes.nonRoundTrip
----

In general, databases do not store timezones and offsets together with their timestamps:
"TIMESTAMP WITH TIMEZONE" is a lie and it actually stores UTC and renders to whatever
timezone the client queries it from. Thus values of type `OffsetDateTime` can preserve
their instant, but cannot be round-tripped preserving the offset.

```scala
case class NonRoundTripTypes[+T[_]](
myZonedDateTime: T[ZonedDateTime],
myOffsetDateTime: T[OffsetDateTime]
)
object NonRoundTripTypes extends Table[NonRoundTripTypes]
val value = NonRoundTripTypes[Id](
myZonedDateTime = ZonedDateTime.parse("2011-12-03T10:15:30+01:00[Europe/Paris]"),
myOffsetDateTime = OffsetDateTime.parse("2011-12-03T10:15:30+00:00")
)
def normalize(v: NonRoundTripTypes[Id]) = v.copy[Id](
myZonedDateTime = v.myZonedDateTime.withZoneSameInstant(ZoneId.systemDefault),
myOffsetDateTime = v.myOffsetDateTime.withOffsetSameInstant(OffsetDateTime.now.getOffset)
)
checker(
query = NonRoundTripTypes.insert.columns(
_.myOffsetDateTime := value.myOffsetDateTime,
_.myZonedDateTime := value.myZonedDateTime
),
value = 1
)
checker(
query = NonRoundTripTypes.select,
value = Seq(normalize(value)),
normalize = (x: Seq[NonRoundTripTypes[Id]]) => x.map(normalize)
)
```






### DataTypes.enclosing



```scala
NonRoundTripTypes.insert.columns(
_.myOffsetDateTime := value.myOffsetDateTime,
_.myZonedDateTime := value.myZonedDateTime
Enclosing.insert.columns(
_.barId := value1.barId,
_.myString := value1.myString,
_.foo.fooId := value1.foo.fooId,
_.foo.myBoolean := value1.foo.myBoolean
)
```


*
```sql
INSERT INTO enclosing (bar_id, my_string, foo_id, my_boolean)
VALUES (?, ?, ?, ?)
```



*
```scala
1
```



----



```scala
Enclosing.insert.values(value2)
```


*
```sql
INSERT INTO enclosing (bar_id, my_string, foo_id, my_boolean)
VALUES (?, ?, ?, ?)
```



*
Expand All @@ -8142,19 +8279,108 @@ NonRoundTripTypes.insert.columns(


```scala
NonRoundTripTypes.select
Enclosing.select
```


*
```sql
SELECT
enclosing0.bar_id AS res__bar_id,
enclosing0.my_string AS res__my_string,
enclosing0.foo_id AS res__foo_id,
enclosing0.my_boolean AS res__my_boolean
FROM enclosing enclosing0
```



*
```scala
Seq(normalize(value))
Seq(value1, value2)
```



----

You can nest `case class`es in other `case class`es to DRY up common sets of
table columns. These nested `case class`es have their columns flattened out
into the enclosing `case class`'s columns, such that at the SQL level it is
all flattened out without nesting.
```scala
// case class Nested[+T[_]](
// fooId: T[Int],
// myBoolean: T[Boolean],
// )
// object Nested extends Table[Nested]
//
// case class Enclosing[+T[_]](
// barId: T[Int],
// myString: T[String],
// foo: Nested[T]
// )
// object Enclosing extends Table[Enclosing]
val value1 = Enclosing[Id](
barId = 1337,
myString = "hello",
foo = Nested[Id](
fooId = 271828,
myBoolean = true
)
)
val value2 = Enclosing[Id](
barId = 31337,
myString = "world",
foo = Nested[Id](
fooId = 1618,
myBoolean = false
)
)
checker(
query = Enclosing.insert.columns(
_.barId := value1.barId,
_.myString := value1.myString,
_.foo.fooId := value1.foo.fooId,
_.foo.myBoolean := value1.foo.myBoolean
),
sql = """
INSERT INTO enclosing (bar_id, my_string, foo_id, my_boolean)
VALUES (?, ?, ?, ?)
""",
value = 1
)
checker(
query = Enclosing.insert.values(value2),
sql = """
INSERT INTO enclosing (bar_id, my_string, foo_id, my_boolean)
VALUES (?, ?, ?, ?)
""",
value = 1
)
checker(
query = Enclosing.select,
sql = """
SELECT
enclosing0.bar_id AS res__bar_id,
enclosing0.my_string AS res__my_string,
enclosing0.foo_id AS res__foo_id,
enclosing0.my_boolean AS res__my_boolean
FROM enclosing enclosing0
""",
value = Seq(value1, value2)
)
```
## Optional
Queries using columns that may be `NULL`, `Expr[Option[T]]` or `Option[T]` in Scala
### Optional
Expand Down
Loading

0 comments on commit f312d71

Please sign in to comment.