Skip to content

Commit

Permalink
don't write to GENERATED ALWAYS columns (#130)
Browse files Browse the repository at this point in the history
* don't write to `GENERATED ALWAYS` columns

* meh 2.13

* tests for doobie/zio, bugfix for zio upsert, don't use streaming unsaved in test as it doesnt work for pg 12
  • Loading branch information
oyvindberg authored Aug 9, 2024
1 parent 7320e35 commit 0e708b3
Show file tree
Hide file tree
Showing 40 changed files with 367 additions and 408 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ class PersonRepoImpl extends PersonRepo {

}
override def insert(unsaved: PersonRow)(implicit c: Connection): PersonRow = {
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number")
values (${ParameterValue(unsaved.id, null, PersonId.toStatement)}::int8, ${ParameterValue(unsaved.favouriteFootballClubId, null, FootballClubId.toStatement)}, ${ParameterValue(unsaved.name, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.nickName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.blogUrl, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.email, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.phone, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.likesPizza, null, ToStatement.booleanToStatement)}, ${ParameterValue(unsaved.maritalStatusId, null, MaritalStatusId.toStatement)}, ${ParameterValue(unsaved.workEmail, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.sector, null, Sector.toStatement)}::myschema.sector, ${ParameterValue(unsaved.favoriteNumber, null, Number.toStatement)}::myschema.number)
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number")
values (${ParameterValue(unsaved.id, null, PersonId.toStatement)}::int8, ${ParameterValue(unsaved.favouriteFootballClubId, null, FootballClubId.toStatement)}, ${ParameterValue(unsaved.name, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.nickName, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.blogUrl, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.email, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.phone, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.likesPizza, null, ToStatement.booleanToStatement)}, ${ParameterValue(unsaved.maritalStatusId, null, MaritalStatusId.toStatement)}, ${ParameterValue(unsaved.workEmail, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))}, ${ParameterValue(unsaved.favoriteNumber, null, Number.toStatement)}::myschema.number)
returning "id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number"
"""
.executeInsert(PersonRow.rowParser(1).single)
Expand All @@ -67,10 +67,6 @@ class PersonRepoImpl extends PersonRepo {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("marital_status_id", ParameterValue(value, null, MaritalStatusId.toStatement)), ""))
},
unsaved.sector match {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("sector", ParameterValue(value, null, Sector.toStatement)), "::myschema.sector"))
},
unsaved.favoriteNumber match {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("favorite_number", ParameterValue(value, null, Number.toStatement)), "::myschema.number"))
Expand All @@ -93,11 +89,11 @@ class PersonRepoImpl extends PersonRepo {

}
override def insertStreaming(unsaved: Iterator[PersonRow], batchSize: Int = 10000)(implicit c: Connection): Long = {
streamingInsert(s"""COPY "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number") FROM STDIN""", batchSize, unsaved)(PersonRow.text, c)
streamingInsert(s"""COPY "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number") FROM STDIN""", batchSize, unsaved)(PersonRow.text, c)
}
/* NOTE: this functionality requires PostgreSQL 16 or later! */
override def insertUnsavedStreaming(unsaved: Iterator[PersonRowUnsaved], batchSize: Int = 10000)(implicit c: Connection): Long = {
streamingInsert(s"""COPY "myschema"."person"("favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "work_email", "id", "marital_status_id", "sector", "favorite_number") FROM STDIN (DEFAULT '__DEFAULT_VALUE__')""", batchSize, unsaved)(PersonRowUnsaved.text, c)
streamingInsert(s"""COPY "myschema"."person"("favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "work_email", "id", "marital_status_id", "favorite_number") FROM STDIN (DEFAULT '__DEFAULT_VALUE__')""", batchSize, unsaved)(PersonRowUnsaved.text, c)
}
override def select: SelectBuilder[PersonFields, PersonRow] = {
SelectBuilderSql(""""myschema"."person"""", PersonFields.structure, PersonRow.rowParser)
Expand Down Expand Up @@ -167,7 +163,6 @@ class PersonRepoImpl extends PersonRepo {
"likes_pizza" = ${ParameterValue(row.likesPizza, null, ToStatement.booleanToStatement)},
"marital_status_id" = ${ParameterValue(row.maritalStatusId, null, MaritalStatusId.toStatement)},
"work_email" = ${ParameterValue(row.workEmail, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))},
"sector" = ${ParameterValue(row.sector, null, Sector.toStatement)}::myschema.sector,
"favorite_number" = ${ParameterValue(row.favoriteNumber, null, Number.toStatement)}::myschema.number
where "id" = ${ParameterValue(id, null, PersonId.toStatement)}
""".executeUpdate() > 0
Expand Down Expand Up @@ -200,7 +195,7 @@ class PersonRepoImpl extends PersonRepo {

}
override def upsert(unsaved: PersonRow)(implicit c: Connection): PersonRow = {
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number")
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number")
values (
${ParameterValue(unsaved.id, null, PersonId.toStatement)}::int8,
${ParameterValue(unsaved.favouriteFootballClubId, null, FootballClubId.toStatement)},
Expand All @@ -212,7 +207,6 @@ class PersonRepoImpl extends PersonRepo {
${ParameterValue(unsaved.likesPizza, null, ToStatement.booleanToStatement)},
${ParameterValue(unsaved.maritalStatusId, null, MaritalStatusId.toStatement)},
${ParameterValue(unsaved.workEmail, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))},
${ParameterValue(unsaved.sector, null, Sector.toStatement)}::myschema.sector,
${ParameterValue(unsaved.favoriteNumber, null, Number.toStatement)}::myschema.number
)
on conflict ("id")
Expand All @@ -226,7 +220,6 @@ class PersonRepoImpl extends PersonRepo {
"likes_pizza" = EXCLUDED."likes_pizza",
"marital_status_id" = EXCLUDED."marital_status_id",
"work_email" = EXCLUDED."work_email",
"sector" = EXCLUDED."sector",
"favorite_number" = EXCLUDED."favorite_number"
returning "id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number"
"""
Expand All @@ -245,16 +238,15 @@ class PersonRepoImpl extends PersonRepo {
NamedParameter("likes_pizza", ParameterValue(row.likesPizza, null, ToStatement.booleanToStatement)),
NamedParameter("marital_status_id", ParameterValue(row.maritalStatusId, null, MaritalStatusId.toStatement)),
NamedParameter("work_email", ParameterValue(row.workEmail, null, ToStatement.optionToStatement(ToStatement.stringToStatement, ParameterMetaData.StringParameterMetaData))),
NamedParameter("sector", ParameterValue(row.sector, null, Sector.toStatement)),
NamedParameter("favorite_number", ParameterValue(row.favoriteNumber, null, Number.toStatement))
)
unsaved.toList match {
case Nil => Nil
case head :: rest =>
new anorm.testdb.hardcoded.ExecuteReturningSyntax.Ops(
BatchSql(
s"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number")
values ({id}::int8, {favourite_football_club_id}, {name}, {nick_name}, {blog_url}, {email}, {phone}, {likes_pizza}, {marital_status_id}, {work_email}, {sector}::myschema.sector, {favorite_number}::myschema.number)
s"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number")
values ({id}::int8, {favourite_football_club_id}, {name}, {nick_name}, {blog_url}, {email}, {phone}, {likes_pizza}, {marital_status_id}, {work_email}, {favorite_number}::myschema.number)
on conflict ("id")
do update set
"favourite_football_club_id" = EXCLUDED."favourite_football_club_id",
Expand All @@ -266,7 +258,6 @@ class PersonRepoImpl extends PersonRepo {
"likes_pizza" = EXCLUDED."likes_pizza",
"marital_status_id" = EXCLUDED."marital_status_id",
"work_email" = EXCLUDED."work_email",
"sector" = EXCLUDED."sector",
"favorite_number" = EXCLUDED."favorite_number"
returning "id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number"
""",
Expand All @@ -279,8 +270,8 @@ class PersonRepoImpl extends PersonRepo {
/* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */
override def upsertStreaming(unsaved: Iterator[PersonRow], batchSize: Int = 10000)(implicit c: Connection): Int = {
SQL"""create temporary table person_TEMP (like "myschema"."person") on commit drop""".execute(): @nowarn
streamingInsert(s"""copy person_TEMP("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number") from stdin""", batchSize, unsaved)(PersonRow.text, c): @nowarn
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "sector", "favorite_number")
streamingInsert(s"""copy person_TEMP("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number") from stdin""", batchSize, unsaved)(PersonRow.text, c): @nowarn
SQL"""insert into "myschema"."person"("id", "favourite_football_club_id", "name", "nick_name", "blog_url", "email", "phone", "likes_pizza", "marital_status_id", "work_email", "favorite_number")
select * from person_TEMP
on conflict ("id")
do update set
Expand All @@ -293,7 +284,6 @@ class PersonRepoImpl extends PersonRepo {
"likes_pizza" = EXCLUDED."likes_pizza",
"marital_status_id" = EXCLUDED."marital_status_id",
"work_email" = EXCLUDED."work_email",
"sector" = EXCLUDED."sector",
"favorite_number" = EXCLUDED."favorite_number"
;
drop table person_TEMP;""".executeUpdate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ case class PersonRow(
Points to [[marital_status.MaritalStatusRow.id]] */
maritalStatusId: MaritalStatusId,
workEmail: Option[/* max 254 chars */ String],
/** Default: PUBLIC */
/** Default: PUBLIC
Identity ALWAYS */
sector: Sector,
/** Default: one */
favoriteNumber: Number
){
def toUnsavedRow(id: Defaulted[PersonId], maritalStatusId: Defaulted[MaritalStatusId] = Defaulted.Provided(this.maritalStatusId), sector: Defaulted[Sector] = Defaulted.Provided(this.sector), favoriteNumber: Defaulted[Number] = Defaulted.Provided(this.favoriteNumber)): PersonRowUnsaved =
PersonRowUnsaved(favouriteFootballClubId, name, nickName, blogUrl, email, phone, likesPizza, workEmail, id, maritalStatusId, sector, favoriteNumber)
def toUnsavedRow(id: Defaulted[PersonId], maritalStatusId: Defaulted[MaritalStatusId] = Defaulted.Provided(this.maritalStatusId), favoriteNumber: Defaulted[Number] = Defaulted.Provided(this.favoriteNumber)): PersonRowUnsaved =
PersonRowUnsaved(favouriteFootballClubId, name, nickName, blogUrl, email, phone, likesPizza, workEmail, id, maritalStatusId, favoriteNumber)
}

object PersonRow {
Expand Down Expand Up @@ -109,8 +110,6 @@ object PersonRow {
sb.append(Text.DELIMETER)
Text.option(Text.stringInstance).unsafeEncode(row.workEmail, sb)
sb.append(Text.DELIMETER)
Sector.text.unsafeEncode(row.sector, sb)
sb.append(Text.DELIMETER)
Number.text.unsafeEncode(row.favoriteNumber, sb)
}
implicit lazy val writes: OWrites[PersonRow] = OWrites[PersonRow](o =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ case class PersonRowUnsaved(
/** Default: some-value
Points to [[marital_status.MaritalStatusRow.id]] */
maritalStatusId: Defaulted[MaritalStatusId] = Defaulted.UseDefault,
/** Default: PUBLIC */
sector: Defaulted[Sector] = Defaulted.UseDefault,
/** Default: one */
favoriteNumber: Defaulted[Number] = Defaulted.UseDefault
) {
def toRow(idDefault: => PersonId, maritalStatusIdDefault: => MaritalStatusId, sectorDefault: => Sector, favoriteNumberDefault: => Number): PersonRow =
def toRow(idDefault: => PersonId, maritalStatusIdDefault: => MaritalStatusId, favoriteNumberDefault: => Number, sectorDefault: => Sector): PersonRow =
PersonRow(
favouriteFootballClubId = favouriteFootballClubId,
name = name,
Expand All @@ -59,14 +57,11 @@ case class PersonRowUnsaved(
case Defaulted.UseDefault => maritalStatusIdDefault
case Defaulted.Provided(value) => value
},
sector = sector match {
case Defaulted.UseDefault => sectorDefault
case Defaulted.Provided(value) => value
},
favoriteNumber = favoriteNumber match {
case Defaulted.UseDefault => favoriteNumberDefault
case Defaulted.Provided(value) => value
}
},
sector = sectorDefault
)
}
object PersonRowUnsaved {
Expand All @@ -83,7 +78,6 @@ object PersonRowUnsaved {
workEmail = json.\("work_email").toOption.map(_.as(Reads.StringReads)),
id = json.\("id").as(Defaulted.reads(PersonId.reads)),
maritalStatusId = json.\("marital_status_id").as(Defaulted.reads(MaritalStatusId.reads)),
sector = json.\("sector").as(Defaulted.reads(Sector.reads)),
favoriteNumber = json.\("favorite_number").as(Defaulted.reads(Number.reads))
)
)
Expand All @@ -110,8 +104,6 @@ object PersonRowUnsaved {
sb.append(Text.DELIMETER)
Defaulted.text(MaritalStatusId.text).unsafeEncode(row.maritalStatusId, sb)
sb.append(Text.DELIMETER)
Defaulted.text(Sector.text).unsafeEncode(row.sector, sb)
sb.append(Text.DELIMETER)
Defaulted.text(Number.text).unsafeEncode(row.favoriteNumber, sb)
}
implicit lazy val writes: OWrites[PersonRowUnsaved] = OWrites[PersonRowUnsaved](o =>
Expand All @@ -126,7 +118,6 @@ object PersonRowUnsaved {
"work_email" -> Writes.OptionWrites(Writes.StringWrites).writes(o.workEmail),
"id" -> Defaulted.writes(PersonId.writes).writes(o.id),
"marital_status_id" -> Defaulted.writes(MaritalStatusId.writes).writes(o.maritalStatusId),
"sector" -> Defaulted.writes(Sector.writes).writes(o.sector),
"favorite_number" -> Defaulted.writes(Number.writes).writes(o.favoriteNumber)
))
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import testdb.hardcoded.compositepk.person.PersonRow
import testdb.hardcoded.compositepk.person.PersonRowUnsaved
import testdb.hardcoded.customtypes.Defaulted
import testdb.hardcoded.myschema.Number
import testdb.hardcoded.myschema.Sector
import testdb.hardcoded.myschema.football_club.FootballClubId
import testdb.hardcoded.myschema.football_club.FootballClubRepoImpl
import testdb.hardcoded.myschema.football_club.FootballClubRow
Expand All @@ -39,7 +38,6 @@ class TestInsert(random: Random) {
workEmail: Option[/* max 254 chars */ String] = if (random.nextBoolean()) None else Some(random.alphanumeric.take(20).mkString),
id: Defaulted[PersonId] = Defaulted.UseDefault,
maritalStatusId: Defaulted[MaritalStatusId] = Defaulted.UseDefault,
sector: Defaulted[Sector] = Defaulted.UseDefault,
favoriteNumber: Defaulted[Number] = Defaulted.UseDefault
)(implicit c: Connection): testdb.hardcoded.myschema.person.PersonRow = (new testdb.hardcoded.myschema.person.PersonRepoImpl).insert(new testdb.hardcoded.myschema.person.PersonRowUnsaved(favouriteFootballClubId = favouriteFootballClubId, name = name, nickName = nickName, blogUrl = blogUrl, email = email, phone = phone, likesPizza = likesPizza, workEmail = workEmail, id = id, maritalStatusId = maritalStatusId, sector = sector, favoriteNumber = favoriteNumber))
)(implicit c: Connection): testdb.hardcoded.myschema.person.PersonRow = (new testdb.hardcoded.myschema.person.PersonRepoImpl).insert(new testdb.hardcoded.myschema.person.PersonRowUnsaved(favouriteFootballClubId = favouriteFootballClubId, name = name, nickName = nickName, blogUrl = blogUrl, email = email, phone = phone, likesPizza = likesPizza, workEmail = workEmail, id = id, maritalStatusId = maritalStatusId, favoriteNumber = favoriteNumber))
}
Loading

0 comments on commit 0e708b3

Please sign in to comment.