From 4f36771f8462996052fa6110c7692b02bcec4ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20K=C3=A5veland?= Date: Thu, 31 Oct 2024 14:08:30 +0100 Subject: [PATCH] Generate update set on conflict for upserts (#145) When upserting to tables with only key columns, typo would generate `do nothing` as a conflict resolution. This has the disadvantage of making the query not execute the `returning` part, which meant that in cases where an upsert was executed targeting a single row, no rows would return and the generated code would cause an error. As a workaround, we generate `update set k = excluded.k` for an arbitrary key column k instead. This causes the `returning` part of the query to run but shouldn't change the value of the row as typo sees it. - Add failing test case for upserting on an existing row - Make the test case work by generating `update set` instead of `do nothing` --- .../MaritalStatusRepoImpl.scala | 2 +- .../testdb/hardcoded/package.scala | 2 +- .../MaritalStatusRepoImpl.scala | 2 +- .../testdb/hardcoded/package.scala | 2 +- .../MaritalStatusRepoImpl.scala | 2 +- .../MaritalStatusRepoImpl.scala | 2 +- .../MaritalStatusRepoImpl.scala | 2 +- .../MaritalStatusRepoImpl.scala | 2 +- init/data/test-tables.sql | 6 + .../EmployeedepartmenthistoryRepoImpl.scala | 2 +- .../EmployeepayhistoryRepoImpl.scala | 2 +- .../adventureworks/package.scala | 2 +- .../BusinessentityaddressRepoImpl.scala | 2 +- .../BusinessentitycontactRepoImpl.scala | 2 +- .../emailaddress/EmailaddressRepoImpl.scala | 2 +- .../personphone/PersonphoneRepoImpl.scala | 2 +- .../ProductcosthistoryRepoImpl.scala | 2 +- .../ProductdocumentRepoImpl.scala | 2 +- .../ProductinventoryRepoImpl.scala | 2 +- .../ProductlistpricehistoryRepoImpl.scala | 2 +- .../ProductmodelillustrationRepoImpl.scala | 2 +- ...delproductdescriptioncultureRepoImpl.scala | 2 +- .../ProductproductphotoRepoImpl.scala | 2 +- .../WorkorderroutingRepoImpl.scala | 2 +- .../public/flaff/FlaffRepoImpl.scala | 2 +- .../public/issue142/Issue142RepoImpl.scala | 2 +- .../public/issue142_2/Issue1422RepoImpl.scala | 2 +- .../only_pk_columns/OnlyPkColumnsFields.scala | 48 +++++++ .../only_pk_columns/OnlyPkColumnsId.scala | 41 ++++++ .../only_pk_columns/OnlyPkColumnsRepo.scala | 31 +++++ .../OnlyPkColumnsRepoImpl.scala | 127 ++++++++++++++++++ .../OnlyPkColumnsRepoMock.scala | 82 +++++++++++ .../only_pk_columns/OnlyPkColumnsRow.scala | 63 +++++++++ .../TableWithGeneratedColumnsRepoImpl.scala | 2 +- .../public/title/TitleRepoImpl.scala | 2 +- .../title_domain/TitleDomainRepoImpl.scala | 2 +- .../productvendor/ProductvendorRepoImpl.scala | 2 +- .../PurchaseorderdetailRepoImpl.scala | 2 +- .../CountryregioncurrencyRepoImpl.scala | 2 +- .../PersoncreditcardRepoImpl.scala | 2 +- .../SalesorderdetailRepoImpl.scala | 2 +- .../SalesorderheadersalesreasonRepoImpl.scala | 2 +- .../SalespersonquotahistoryRepoImpl.scala | 2 +- .../SalesterritoryhistoryRepoImpl.scala | 2 +- .../SpecialofferproductRepoImpl.scala | 2 +- .../adventureworks/testInsert.scala | 3 + .../adventureworks/UpsertTwiceTest.scala | 15 +++ .../EmployeedepartmenthistoryRepoImpl.scala | 2 +- .../EmployeepayhistoryRepoImpl.scala | 2 +- .../BusinessentityaddressRepoImpl.scala | 2 +- .../BusinessentitycontactRepoImpl.scala | 2 +- .../emailaddress/EmailaddressRepoImpl.scala | 2 +- .../personphone/PersonphoneRepoImpl.scala | 2 +- .../ProductcosthistoryRepoImpl.scala | 2 +- .../ProductdocumentRepoImpl.scala | 2 +- .../ProductinventoryRepoImpl.scala | 2 +- .../ProductlistpricehistoryRepoImpl.scala | 2 +- .../ProductmodelillustrationRepoImpl.scala | 2 +- ...delproductdescriptioncultureRepoImpl.scala | 2 +- .../ProductproductphotoRepoImpl.scala | 2 +- .../WorkorderroutingRepoImpl.scala | 2 +- .../public/flaff/FlaffRepoImpl.scala | 2 +- .../public/issue142/Issue142RepoImpl.scala | 2 +- .../public/issue142_2/Issue1422RepoImpl.scala | 2 +- .../only_pk_columns/OnlyPkColumnsFields.scala | 48 +++++++ .../only_pk_columns/OnlyPkColumnsId.scala | 22 +++ .../only_pk_columns/OnlyPkColumnsRepo.scala | 32 +++++ .../OnlyPkColumnsRepoImpl.scala | 112 +++++++++++++++ .../OnlyPkColumnsRepoMock.scala | 100 ++++++++++++++ .../only_pk_columns/OnlyPkColumnsRow.scala | 62 +++++++++ .../TableWithGeneratedColumnsRepoImpl.scala | 2 +- .../public/title/TitleRepoImpl.scala | 2 +- .../title_domain/TitleDomainRepoImpl.scala | 2 +- .../productvendor/ProductvendorRepoImpl.scala | 2 +- .../PurchaseorderdetailRepoImpl.scala | 2 +- .../CountryregioncurrencyRepoImpl.scala | 2 +- .../PersoncreditcardRepoImpl.scala | 2 +- .../SalesorderdetailRepoImpl.scala | 2 +- .../SalesorderheadersalesreasonRepoImpl.scala | 2 +- .../SalespersonquotahistoryRepoImpl.scala | 2 +- .../SalesterritoryhistoryRepoImpl.scala | 2 +- .../SpecialofferproductRepoImpl.scala | 2 +- .../adventureworks/testInsert.scala | 3 + .../adventureworks/UpsertTwiceTest.scala | 19 +++ .../public/issue142/Issue142RepoImpl.scala | 2 +- .../public/issue142_2/Issue1422RepoImpl.scala | 2 +- .../only_pk_columns/OnlyPkColumnsFields.scala | 48 +++++++ .../only_pk_columns/OnlyPkColumnsId.scala | 40 ++++++ .../only_pk_columns/OnlyPkColumnsRepo.scala | 34 +++++ .../OnlyPkColumnsRepoImpl.scala | 98 ++++++++++++++ .../OnlyPkColumnsRepoMock.scala | 91 +++++++++++++ .../only_pk_columns/OnlyPkColumnsRow.scala | 61 +++++++++ .../TableWithGeneratedColumnsRepoImpl.scala | 2 +- .../public/title/TitleRepoImpl.scala | 2 +- .../title_domain/TitleDomainRepoImpl.scala | 2 +- .../adventureworks/testInsert.scala | 3 + .../adventureworks/UpsertTwiceTest.scala | 19 +++ .../typo/internal/codegen/DbLibAnorm.scala | 8 +- .../typo/internal/codegen/DbLibDoobie.scala | 6 +- .../typo/internal/codegen/DbLibZioJdbc.scala | 4 +- 100 files changed, 1292 insertions(+), 78 deletions(-) create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala create mode 100644 typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala create mode 100644 typo-tester-anorm/src/scala/adventureworks/UpsertTwiceTest.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala create mode 100644 typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala create mode 100644 typo-tester-doobie/src/scala/adventureworks/UpsertTwiceTest.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala create mode 100644 typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala create mode 100644 typo-tester-zio-jdbc/src/scala/adventureworks/UpsertTwiceTest.scala diff --git a/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 0a21a7918..118094448 100644 --- a/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -98,7 +98,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${ParameterValue(unsaved.id, null, MaritalStatusId.toStatement)}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id" """ .executeInsert(MaritalStatusRow.rowParser(1).single) diff --git a/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala b/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala index 873fbccb5..918da4374 100644 --- a/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala +++ b/.bleep/generated-sources/typo-tester-anorm@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala @@ -29,7 +29,7 @@ package object hardcoded { case "VARCHAR" => "text[]" case other => s"${other}[]" } - + override def jdbcType: scala.Int = java.sql.Types.ARRAY } } diff --git a/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 0a21a7918..118094448 100644 --- a/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -98,7 +98,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${ParameterValue(unsaved.id, null, MaritalStatusId.toStatement)}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id" """ .executeInsert(MaritalStatusRow.rowParser(1).single) diff --git a/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala b/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala index 873fbccb5..918da4374 100644 --- a/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala +++ b/.bleep/generated-sources/typo-tester-anorm@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/package.scala @@ -29,7 +29,7 @@ package object hardcoded { case "VARCHAR" => "text[]" case other => s"${other}[]" } - + override def jdbcType: scala.Int = java.sql.Types.ARRAY } } diff --git a/.bleep/generated-sources/typo-tester-doobie@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-doobie@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 336b945a2..251789c0a 100644 --- a/.bleep/generated-sources/typo-tester-doobie@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-doobie@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -76,7 +76,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${fromWrite(unsaved.id)(Write.fromPut(MaritalStatusId.put))}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id" """.query(using MaritalStatusRow.read).unique } diff --git a/.bleep/generated-sources/typo-tester-doobie@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-doobie@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 336b945a2..251789c0a 100644 --- a/.bleep/generated-sources/typo-tester-doobie@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-doobie@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -76,7 +76,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${fromWrite(unsaved.id)(Write.fromPut(MaritalStatusId.put))}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id" """.query(using MaritalStatusRow.read).unique } diff --git a/.bleep/generated-sources/typo-tester-zio-jdbc@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-zio-jdbc@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 0f972db66..b0a7bf663 100644 --- a/.bleep/generated-sources/typo-tester-zio-jdbc@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-zio-jdbc@jvm213/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -78,7 +78,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${Segment.paramSegment(unsaved.id)(MaritalStatusId.setter)}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id"""".insertReturning(using MaritalStatusRow.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/.bleep/generated-sources/typo-tester-zio-jdbc@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala b/.bleep/generated-sources/typo-tester-zio-jdbc@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala index 0f972db66..b0a7bf663 100644 --- a/.bleep/generated-sources/typo-tester-zio-jdbc@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala +++ b/.bleep/generated-sources/typo-tester-zio-jdbc@jvm3/scripts.GenHardcodedFiles/testdb/hardcoded/myschema/marital_status/MaritalStatusRepoImpl.scala @@ -78,7 +78,7 @@ class MaritalStatusRepoImpl extends MaritalStatusRepo { ${Segment.paramSegment(unsaved.id)(MaritalStatusId.setter)}::int8 ) on conflict ("id") - do nothing + do update set "id" = EXCLUDED."id" returning "id"""".insertReturning(using MaritalStatusRow.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/init/data/test-tables.sql b/init/data/test-tables.sql index ecdf073a7..bbbd8e2b2 100644 --- a/init/data/test-tables.sql +++ b/init/data/test-tables.sql @@ -273,3 +273,9 @@ INSERT INTO issue142 (tabellkode) VALUES ('aa'), ('bb') ; + +create table only_pk_columns( + key_column_1 text not null, + key_column_2 int not null, + constraint only_pk_columns_pk primary key (key_column_1, key_column_2) +); diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala index 097fbba65..e14ae9e38 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala @@ -111,7 +111,7 @@ class EmployeedepartmenthistoryRepoImpl extends EmployeedepartmenthistoryRepo { val shiftid = compositeIds.map(_.shiftid) SQL"""select "businessentityid", "departmentid", "shiftid", "startdate"::text, "enddate"::text, "modifieddate"::text from "humanresources"."employeedepartmenthistory" - where ("businessentityid", "startdate", "departmentid", "shiftid") + where ("businessentityid", "startdate", "departmentid", "shiftid") in (select unnest(${businessentityid}), unnest(${startdate}), unnest(${departmentid}), unnest(${shiftid})) """.as(EmployeedepartmenthistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala index e7df369fd..842c05f22 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala @@ -104,7 +104,7 @@ class EmployeepayhistoryRepoImpl extends EmployeepayhistoryRepo { val ratechangedate = compositeIds.map(_.ratechangedate) SQL"""select "businessentityid", "ratechangedate"::text, "rate", "payfrequency", "modifieddate"::text from "humanresources"."employeepayhistory" - where ("businessentityid", "ratechangedate") + where ("businessentityid", "ratechangedate") in (select unnest(${businessentityid}), unnest(${ratechangedate})) """.as(EmployeepayhistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/package.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/package.scala index 4d0453651..24fd9e2a6 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/package.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/package.scala @@ -29,7 +29,7 @@ package object adventureworks { case "VARCHAR" => "text[]" case other => s"${other}[]" } - + override def jdbcType: scala.Int = java.sql.Types.ARRAY } } diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala index 43ae2488c..aceec6b89 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala @@ -110,7 +110,7 @@ class BusinessentityaddressRepoImpl extends BusinessentityaddressRepo { val addresstypeid = compositeIds.map(_.addresstypeid) SQL"""select "businessentityid", "addressid", "addresstypeid", "rowguid", "modifieddate"::text from "person"."businessentityaddress" - where ("businessentityid", "addressid", "addresstypeid") + where ("businessentityid", "addressid", "addresstypeid") in (select unnest(${businessentityid}), unnest(${addressid}), unnest(${addresstypeid})) """.as(BusinessentityaddressRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala index 307c96bf2..428f6b96d 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala @@ -109,7 +109,7 @@ class BusinessentitycontactRepoImpl extends BusinessentitycontactRepo { val contacttypeid = compositeIds.map(_.contacttypeid) SQL"""select "businessentityid", "personid", "contacttypeid", "rowguid", "modifieddate"::text from "person"."businessentitycontact" - where ("businessentityid", "personid", "contacttypeid") + where ("businessentityid", "personid", "contacttypeid") in (select unnest(${businessentityid}), unnest(${personid}), unnest(${contacttypeid})) """.as(BusinessentitycontactRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala index 81f398ba5..8b4c7ccec 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala @@ -111,7 +111,7 @@ class EmailaddressRepoImpl extends EmailaddressRepo { val emailaddressid = compositeIds.map(_.emailaddressid) SQL"""select "businessentityid", "emailaddressid", "emailaddress", "rowguid", "modifieddate"::text from "person"."emailaddress" - where ("businessentityid", "emailaddressid") + where ("businessentityid", "emailaddressid") in (select unnest(${businessentityid}), unnest(${emailaddressid})) """.as(EmailaddressRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala index d3c32d9e4..483aeea7c 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala @@ -105,7 +105,7 @@ class PersonphoneRepoImpl extends PersonphoneRepo { val phonenumbertypeid = compositeIds.map(_.phonenumbertypeid) SQL"""select "businessentityid", "phonenumber", "phonenumbertypeid", "modifieddate"::text from "person"."personphone" - where ("businessentityid", "phonenumber", "phonenumbertypeid") + where ("businessentityid", "phonenumber", "phonenumbertypeid") in (select unnest(${businessentityid}), unnest(${phonenumber}), unnest(${phonenumbertypeid})) """.as(PersonphoneRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala index 6ba3d2e50..2b29a6fa8 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala @@ -103,7 +103,7 @@ class ProductcosthistoryRepoImpl extends ProductcosthistoryRepo { val startdate = compositeIds.map(_.startdate) SQL"""select "productid", "startdate"::text, "enddate"::text, "standardcost", "modifieddate"::text from "production"."productcosthistory" - where ("productid", "startdate") + where ("productid", "startdate") in (select unnest(${productid}), unnest(${startdate})) """.as(ProductcosthistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala index 16f6fe17f..81075790f 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala @@ -104,7 +104,7 @@ class ProductdocumentRepoImpl extends ProductdocumentRepo { val documentnode = compositeIds.map(_.documentnode) SQL"""select "productid", "modifieddate"::text, "documentnode" from "production"."productdocument" - where ("productid", "documentnode") + where ("productid", "documentnode") in (select unnest(${productid}), unnest(${documentnode})) """.as(ProductdocumentRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala index b919e2c9e..7324b96e6 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala @@ -114,7 +114,7 @@ class ProductinventoryRepoImpl extends ProductinventoryRepo { val locationid = compositeIds.map(_.locationid) SQL"""select "productid", "locationid", "shelf", "bin", "quantity", "rowguid", "modifieddate"::text from "production"."productinventory" - where ("productid", "locationid") + where ("productid", "locationid") in (select unnest(${productid}), unnest(${locationid})) """.as(ProductinventoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala index d3719c47d..78e9aed67 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala @@ -103,7 +103,7 @@ class ProductlistpricehistoryRepoImpl extends ProductlistpricehistoryRepo { val startdate = compositeIds.map(_.startdate) SQL"""select "productid", "startdate"::text, "enddate"::text, "listprice", "modifieddate"::text from "production"."productlistpricehistory" - where ("productid", "startdate") + where ("productid", "startdate") in (select unnest(${productid}), unnest(${startdate})) """.as(ProductlistpricehistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala index a02c59e5e..a2088ea42 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala @@ -101,7 +101,7 @@ class ProductmodelillustrationRepoImpl extends ProductmodelillustrationRepo { val illustrationid = compositeIds.map(_.illustrationid) SQL"""select "productmodelid", "illustrationid", "modifieddate"::text from "production"."productmodelillustration" - where ("productmodelid", "illustrationid") + where ("productmodelid", "illustrationid") in (select unnest(${productmodelid}), unnest(${illustrationid})) """.as(ProductmodelillustrationRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala index 19171304b..2e74fe62d 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala @@ -105,7 +105,7 @@ class ProductmodelproductdescriptioncultureRepoImpl extends Productmodelproductd val cultureid = compositeIds.map(_.cultureid) SQL"""select "productmodelid", "productdescriptionid", "cultureid", "modifieddate"::text from "production"."productmodelproductdescriptionculture" - where ("productmodelid", "productdescriptionid", "cultureid") + where ("productmodelid", "productdescriptionid", "cultureid") in (select unnest(${productmodelid}), unnest(${productdescriptionid}), unnest(${cultureid})) """.as(ProductmodelproductdescriptioncultureRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala index deeec4a3c..b42aaf63e 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala @@ -106,7 +106,7 @@ class ProductproductphotoRepoImpl extends ProductproductphotoRepo { val productphotoid = compositeIds.map(_.productphotoid) SQL"""select "productid", "productphotoid", "primary", "modifieddate"::text from "production"."productproductphoto" - where ("productid", "productphotoid") + where ("productid", "productphotoid") in (select unnest(${productid}), unnest(${productphotoid})) """.as(ProductproductphotoRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala index fc1d8f416..8264a51e6 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala @@ -115,7 +115,7 @@ class WorkorderroutingRepoImpl extends WorkorderroutingRepo { val operationsequence = compositeIds.map(_.operationsequence) SQL"""select "workorderid", "productid", "operationsequence", "locationid", "scheduledstartdate"::text, "scheduledenddate"::text, "actualstartdate"::text, "actualenddate"::text, "actualresourcehrs", "plannedcost", "actualcost", "modifieddate"::text from "production"."workorderrouting" - where ("workorderid", "productid", "operationsequence") + where ("workorderid", "productid", "operationsequence") in (select unnest(${workorderid}), unnest(${productid}), unnest(${operationsequence})) """.as(WorkorderroutingRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala index 6b6b093a1..24452830a 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala @@ -70,7 +70,7 @@ class FlaffRepoImpl extends FlaffRepo { val specifier = compositeIds.map(_.specifier) SQL"""select "code", "another_code", "some_number", "specifier", "parentspecifier" from "public"."flaff" - where ("code", "another_code", "some_number", "specifier") + where ("code", "another_code", "some_number", "specifier") in (select unnest(${code}), unnest(${anotherCode}), unnest(${someNumber}), unnest(${specifier})) """.as(FlaffRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala index 9ea04cec6..156460ea7 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala @@ -77,7 +77,7 @@ class Issue142RepoImpl extends Issue142Repo { ${ParameterValue(unsaved.tabellkode, null, Issue142Id.toStatement)} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode" """ .executeInsert(Issue142Row.rowParser(1).single) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala index dc5941c12..32e028c43 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala @@ -78,7 +78,7 @@ class Issue1422RepoImpl extends Issue1422Repo { ${ParameterValue(unsaved.tabellkode, null, Issue142Id.toStatement)} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode" """ .executeInsert(Issue1422Row.rowParser(1).single) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala new file mode 100644 index 000000000..dd87eb881 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala @@ -0,0 +1,48 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import typo.dsl.Path +import typo.dsl.Required +import typo.dsl.SqlExpr +import typo.dsl.SqlExpr.CompositeIn +import typo.dsl.SqlExpr.CompositeIn.TuplePart +import typo.dsl.SqlExpr.FieldLikeNoHkt +import typo.dsl.SqlExpr.IdField +import typo.dsl.Structure.Relation + +trait OnlyPkColumnsFields { + def keyColumn1: IdField[String, OnlyPkColumnsRow] + def keyColumn2: IdField[Int, OnlyPkColumnsRow] + def compositeIdIs(compositeId: OnlyPkColumnsId): SqlExpr[Boolean, Required] = + keyColumn1.isEqual(compositeId.keyColumn1).and(keyColumn2.isEqual(compositeId.keyColumn2)) + def compositeIdIn(compositeIds: Array[OnlyPkColumnsId]): SqlExpr[Boolean, Required] = + new CompositeIn(compositeIds)(TuplePart(keyColumn1)(_.keyColumn1), TuplePart(keyColumn2)(_.keyColumn2)) + +} + +object OnlyPkColumnsFields { + lazy val structure: Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] = + new Impl(Nil) + + private final class Impl(val _path: List[Path]) + extends Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] { + + override lazy val fields: OnlyPkColumnsFields = new OnlyPkColumnsFields { + override def keyColumn1 = IdField[String, OnlyPkColumnsRow](_path, "key_column_1", None, None, x => x.keyColumn1, (row, value) => row.copy(keyColumn1 = value)) + override def keyColumn2 = IdField[Int, OnlyPkColumnsRow](_path, "key_column_2", None, Some("int4"), x => x.keyColumn2, (row, value) => row.copy(keyColumn2 = value)) + } + + override lazy val columns: List[FieldLikeNoHkt[?, OnlyPkColumnsRow]] = + List[FieldLikeNoHkt[?, OnlyPkColumnsRow]](fields.keyColumn1, fields.keyColumn2) + + override def copy(path: List[Path]): Impl = + new Impl(path) + } + +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala new file mode 100644 index 000000000..805fcb676 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala @@ -0,0 +1,41 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import play.api.libs.json.JsObject +import play.api.libs.json.JsResult +import play.api.libs.json.JsValue +import play.api.libs.json.OWrites +import play.api.libs.json.Reads +import play.api.libs.json.Writes +import scala.collection.immutable.ListMap +import scala.util.Try + +/** Type for the composite primary key of table `public.only_pk_columns` */ +case class OnlyPkColumnsId( + keyColumn1: String, + keyColumn2: Int +) +object OnlyPkColumnsId { + implicit lazy val ordering: Ordering[OnlyPkColumnsId] = Ordering.by(x => (x.keyColumn1, x.keyColumn2)) + implicit lazy val reads: Reads[OnlyPkColumnsId] = Reads[OnlyPkColumnsId](json => JsResult.fromTry( + Try( + OnlyPkColumnsId( + keyColumn1 = json.\("key_column_1").as(Reads.StringReads), + keyColumn2 = json.\("key_column_2").as(Reads.IntReads) + ) + ) + ), + ) + implicit lazy val writes: OWrites[OnlyPkColumnsId] = OWrites[OnlyPkColumnsId](o => + new JsObject(ListMap[String, JsValue]( + "key_column_1" -> Writes.StringWrites.writes(o.keyColumn1), + "key_column_2" -> Writes.IntWrites.writes(o.keyColumn2) + )) + ) +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala new file mode 100644 index 000000000..f24f8bae8 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala @@ -0,0 +1,31 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import java.sql.Connection +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.UpdateBuilder + +trait OnlyPkColumnsRepo { + def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def deleteById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Boolean + def deleteByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Int + def insert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow + def insertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Long + def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def selectAll(implicit c: Connection): List[OnlyPkColumnsRow] + def selectById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Option[OnlyPkColumnsRow] + def selectByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): List[OnlyPkColumnsRow] + def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Map[OnlyPkColumnsId, OnlyPkColumnsRow] + def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def upsert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow + def upsertBatch(unsaved: Iterable[OnlyPkColumnsRow])(implicit c: Connection): List[OnlyPkColumnsRow] + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + def upsertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Int +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala new file mode 100644 index 000000000..e34e45a56 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala @@ -0,0 +1,127 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import anorm.BatchSql +import anorm.NamedParameter +import anorm.ParameterValue +import anorm.SqlStringInterpolation +import anorm.ToStatement +import java.sql.Connection +import scala.annotation.nowarn +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderSql +import typo.dsl.UpdateBuilder + +class OnlyPkColumnsRepoImpl extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure) + } + override def deleteById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Boolean = { + SQL"""delete from "public"."only_pk_columns" where "key_column_1" = ${ParameterValue(compositeId.keyColumn1, null, ToStatement.stringToStatement)} AND "key_column_2" = ${ParameterValue(compositeId.keyColumn2, null, ToStatement.intToStatement)}""".executeUpdate() > 0 + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Int = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + SQL"""delete + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.executeUpdate() + + } + override def insert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow = { + SQL"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values (${ParameterValue(unsaved.keyColumn1, null, ToStatement.stringToStatement)}, ${ParameterValue(unsaved.keyColumn2, null, ToStatement.intToStatement)}::int4) + returning "key_column_1", "key_column_2" + """ + .executeInsert(OnlyPkColumnsRow.rowParser(1).single) + + } + override def insertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Long = { + streamingInsert(s"""COPY "public"."only_pk_columns"("key_column_1", "key_column_2") FROM STDIN""", batchSize, unsaved)(OnlyPkColumnsRow.text, c) + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderSql(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.rowParser) + } + override def selectAll(implicit c: Connection): List[OnlyPkColumnsRow] = { + SQL"""select "key_column_1", "key_column_2" + from "public"."only_pk_columns" + """.as(OnlyPkColumnsRow.rowParser(1).*) + } + override def selectById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Option[OnlyPkColumnsRow] = { + SQL"""select "key_column_1", "key_column_2" + from "public"."only_pk_columns" + where "key_column_1" = ${ParameterValue(compositeId.keyColumn1, null, ToStatement.stringToStatement)} AND "key_column_2" = ${ParameterValue(compositeId.keyColumn2, null, ToStatement.intToStatement)} + """.as(OnlyPkColumnsRow.rowParser(1).singleOpt) + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): List[OnlyPkColumnsRow] = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + SQL"""select "key_column_1", "key_column_2" + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.as(OnlyPkColumnsRow.rowParser(1).*) + + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Map[OnlyPkColumnsId, OnlyPkColumnsRow] = { + val byId = selectByIds(compositeIds).view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.rowParser) + } + override def upsert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow = { + SQL"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values ( + ${ParameterValue(unsaved.keyColumn1, null, ToStatement.stringToStatement)}, + ${ParameterValue(unsaved.keyColumn2, null, ToStatement.intToStatement)}::int4 + ) + on conflict ("key_column_1", "key_column_2") + do update set "key_column_1" = EXCLUDED."key_column_1" + returning "key_column_1", "key_column_2" + """ + .executeInsert(OnlyPkColumnsRow.rowParser(1).single) + + } + override def upsertBatch(unsaved: Iterable[OnlyPkColumnsRow])(implicit c: Connection): List[OnlyPkColumnsRow] = { + def toNamedParameter(row: OnlyPkColumnsRow): List[NamedParameter] = List( + NamedParameter("key_column_1", ParameterValue(row.keyColumn1, null, ToStatement.stringToStatement)), + NamedParameter("key_column_2", ParameterValue(row.keyColumn2, null, ToStatement.intToStatement)) + ) + unsaved.toList match { + case Nil => Nil + case head :: rest => + new anorm.adventureworks.ExecuteReturningSyntax.Ops( + BatchSql( + s"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values ({key_column_1}, {key_column_2}::int4) + on conflict ("key_column_1", "key_column_2") + do nothing + returning "key_column_1", "key_column_2" + """, + toNamedParameter(head), + rest.map(toNamedParameter)* + ) + ).executeReturning(OnlyPkColumnsRow.rowParser(1).*) + } + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Int = { + SQL"""create temporary table only_pk_columns_TEMP (like "public"."only_pk_columns") on commit drop""".execute(): @nowarn + streamingInsert(s"""copy only_pk_columns_TEMP("key_column_1", "key_column_2") from stdin""", batchSize, unsaved)(OnlyPkColumnsRow.text, c): @nowarn + SQL"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + select * from only_pk_columns_TEMP + on conflict ("key_column_1", "key_column_2") + do nothing + ; + drop table only_pk_columns_TEMP;""".executeUpdate() + } +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala new file mode 100644 index 000000000..1517ad1e9 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala @@ -0,0 +1,82 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import java.sql.Connection +import scala.annotation.nowarn +import typo.dsl.DeleteBuilder +import typo.dsl.DeleteBuilder.DeleteBuilderMock +import typo.dsl.DeleteParams +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderMock +import typo.dsl.SelectParams +import typo.dsl.UpdateBuilder +import typo.dsl.UpdateBuilder.UpdateBuilderMock +import typo.dsl.UpdateParams + +class OnlyPkColumnsRepoMock(map: scala.collection.mutable.Map[OnlyPkColumnsId, OnlyPkColumnsRow] = scala.collection.mutable.Map.empty) extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilderMock(DeleteParams.empty, OnlyPkColumnsFields.structure, map) + } + override def deleteById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Boolean = { + map.remove(compositeId).isDefined + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Int = { + compositeIds.map(id => map.remove(id)).count(_.isDefined) + } + override def insert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow = { + val _ = if (map.contains(unsaved.compositeId)) + sys.error(s"id ${unsaved.compositeId} already exists") + else + map.put(unsaved.compositeId, unsaved) + + unsaved + } + override def insertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Long = { + unsaved.foreach { row => + map += (row.compositeId -> row) + } + unsaved.size.toLong + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderMock(OnlyPkColumnsFields.structure, () => map.values.toList, SelectParams.empty) + } + override def selectAll(implicit c: Connection): List[OnlyPkColumnsRow] = { + map.values.toList + } + override def selectById(compositeId: OnlyPkColumnsId)(implicit c: Connection): Option[OnlyPkColumnsRow] = { + map.get(compositeId) + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): List[OnlyPkColumnsRow] = { + compositeIds.flatMap(map.get).toList + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId])(implicit c: Connection): Map[OnlyPkColumnsId, OnlyPkColumnsRow] = { + val byId = selectByIds(compositeIds).view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilderMock(UpdateParams.empty, OnlyPkColumnsFields.structure, map) + } + override def upsert(unsaved: OnlyPkColumnsRow)(implicit c: Connection): OnlyPkColumnsRow = { + map.put(unsaved.compositeId, unsaved): @nowarn + unsaved + } + override def upsertBatch(unsaved: Iterable[OnlyPkColumnsRow])(implicit c: Connection): List[OnlyPkColumnsRow] = { + unsaved.map { row => + map += (row.compositeId -> row) + row + }.toList + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: Iterator[OnlyPkColumnsRow], batchSize: Int = 10000)(implicit c: Connection): Int = { + unsaved.foreach { row => + map += (row.compositeId -> row) + } + unsaved.size + } +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala new file mode 100644 index 000000000..57bf4bd19 --- /dev/null +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala @@ -0,0 +1,63 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import anorm.Column +import anorm.RowParser +import anorm.Success +import play.api.libs.json.JsObject +import play.api.libs.json.JsResult +import play.api.libs.json.JsValue +import play.api.libs.json.OWrites +import play.api.libs.json.Reads +import play.api.libs.json.Writes +import scala.collection.immutable.ListMap +import scala.util.Try + +/** Table: public.only_pk_columns + Composite primary key: key_column_1, key_column_2 */ +case class OnlyPkColumnsRow( + keyColumn1: String, + keyColumn2: Int +){ + val compositeId: OnlyPkColumnsId = OnlyPkColumnsId(keyColumn1, keyColumn2) + val id = compositeId + } + +object OnlyPkColumnsRow { + def apply(compositeId: OnlyPkColumnsId) = + new OnlyPkColumnsRow(compositeId.keyColumn1, compositeId.keyColumn2) + implicit lazy val reads: Reads[OnlyPkColumnsRow] = Reads[OnlyPkColumnsRow](json => JsResult.fromTry( + Try( + OnlyPkColumnsRow( + keyColumn1 = json.\("key_column_1").as(Reads.StringReads), + keyColumn2 = json.\("key_column_2").as(Reads.IntReads) + ) + ) + ), + ) + def rowParser(idx: Int): RowParser[OnlyPkColumnsRow] = RowParser[OnlyPkColumnsRow] { row => + Success( + OnlyPkColumnsRow( + keyColumn1 = row(idx + 0)(Column.columnToString), + keyColumn2 = row(idx + 1)(Column.columnToInt) + ) + ) + } + implicit lazy val text: Text[OnlyPkColumnsRow] = Text.instance[OnlyPkColumnsRow]{ (row, sb) => + Text.stringInstance.unsafeEncode(row.keyColumn1, sb) + sb.append(Text.DELIMETER) + Text.intInstance.unsafeEncode(row.keyColumn2, sb) + } + implicit lazy val writes: OWrites[OnlyPkColumnsRow] = OWrites[OnlyPkColumnsRow](o => + new JsObject(ListMap[String, JsValue]( + "key_column_1" -> Writes.StringWrites.writes(o.keyColumn1), + "key_column_2" -> Writes.IntWrites.writes(o.keyColumn2) + )) + ) +} diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala index 91bf2d1e3..a0d967114 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala @@ -104,7 +104,7 @@ class TableWithGeneratedColumnsRepoImpl extends TableWithGeneratedColumnsRepo { ${ParameterValue(unsaved.name, null, TableWithGeneratedColumnsId.toStatement)} ) on conflict ("name") - do nothing + do update set "name" = EXCLUDED."name" returning "name", "name-type-always" """ .executeInsert(TableWithGeneratedColumnsRow.rowParser(1).single) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala index 68200f8b9..342927ca2 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala @@ -77,7 +77,7 @@ class TitleRepoImpl extends TitleRepo { ${ParameterValue(unsaved.code, null, TitleId.toStatement)} ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code" """ .executeInsert(TitleRow.rowParser(1).single) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala index 4fb7f43cf..a04f28e26 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala @@ -77,7 +77,7 @@ class TitleDomainRepoImpl extends TitleDomainRepo { ${ParameterValue(unsaved.code, null, TitleDomainId.toStatement)}::text ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code" """ .executeInsert(TitleDomainRow.rowParser(1).single) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala index 463ba64e2..90d4fdca8 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala @@ -112,7 +112,7 @@ class ProductvendorRepoImpl extends ProductvendorRepo { val businessentityid = compositeIds.map(_.businessentityid) SQL"""select "productid", "businessentityid", "averageleadtime", "standardprice", "lastreceiptcost", "lastreceiptdate"::text, "minorderqty", "maxorderqty", "onorderqty", "unitmeasurecode", "modifieddate"::text from "purchasing"."productvendor" - where ("productid", "businessentityid") + where ("productid", "businessentityid") in (select unnest(${productid}), unnest(${businessentityid})) """.as(ProductvendorRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala index 14839fa18..8c7135243 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala @@ -35,7 +35,7 @@ class PurchaseorderdetailRepoImpl extends PurchaseorderdetailRepo { val purchaseorderdetailid = compositeIds.map(_.purchaseorderdetailid) SQL"""select "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text from "purchasing"."purchaseorderdetail" - where ("purchaseorderid", "purchaseorderdetailid") + where ("purchaseorderid", "purchaseorderdetailid") in (select unnest(${purchaseorderid}), unnest(${purchaseorderdetailid})) """.as(PurchaseorderdetailRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala index 96c50f2ce..44105778b 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala @@ -101,7 +101,7 @@ class CountryregioncurrencyRepoImpl extends CountryregioncurrencyRepo { val currencycode = compositeIds.map(_.currencycode) SQL"""select "countryregioncode", "currencycode", "modifieddate"::text from "sales"."countryregioncurrency" - where ("countryregioncode", "currencycode") + where ("countryregioncode", "currencycode") in (select unnest(${countryregioncode}), unnest(${currencycode})) """.as(CountryregioncurrencyRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala index 14cb2e941..4231292ba 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala @@ -102,7 +102,7 @@ class PersoncreditcardRepoImpl extends PersoncreditcardRepo { val creditcardid = compositeIds.map(_.creditcardid) SQL"""select "businessentityid", "creditcardid", "modifieddate"::text from "sales"."personcreditcard" - where ("businessentityid", "creditcardid") + where ("businessentityid", "creditcardid") in (select unnest(${businessentityid}), unnest(${creditcardid})) """.as(PersoncreditcardRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala index 6aa4576f5..0c38e0278 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala @@ -122,7 +122,7 @@ class SalesorderdetailRepoImpl extends SalesorderdetailRepo { val salesorderdetailid = compositeIds.map(_.salesorderdetailid) SQL"""select "salesorderid", "salesorderdetailid", "carriertrackingnumber", "orderqty", "productid", "specialofferid", "unitprice", "unitpricediscount", "rowguid", "modifieddate"::text from "sales"."salesorderdetail" - where ("salesorderid", "salesorderdetailid") + where ("salesorderid", "salesorderdetailid") in (select unnest(${salesorderid}), unnest(${salesorderdetailid})) """.as(SalesorderdetailRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala index 2c8507b46..c79712287 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala @@ -101,7 +101,7 @@ class SalesorderheadersalesreasonRepoImpl extends SalesorderheadersalesreasonRep val salesreasonid = compositeIds.map(_.salesreasonid) SQL"""select "salesorderid", "salesreasonid", "modifieddate"::text from "sales"."salesorderheadersalesreason" - where ("salesorderid", "salesreasonid") + where ("salesorderid", "salesreasonid") in (select unnest(${salesorderid}), unnest(${salesreasonid})) """.as(SalesorderheadersalesreasonRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala index 49476d833..5aa323dc2 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala @@ -107,7 +107,7 @@ class SalespersonquotahistoryRepoImpl extends SalespersonquotahistoryRepo { val quotadate = compositeIds.map(_.quotadate) SQL"""select "businessentityid", "quotadate"::text, "salesquota", "rowguid", "modifieddate"::text from "sales"."salespersonquotahistory" - where ("businessentityid", "quotadate") + where ("businessentityid", "quotadate") in (select unnest(${businessentityid}), unnest(${quotadate})) """.as(SalespersonquotahistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala index 109f63f89..12caf16f3 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala @@ -111,7 +111,7 @@ class SalesterritoryhistoryRepoImpl extends SalesterritoryhistoryRepo { val territoryid = compositeIds.map(_.territoryid) SQL"""select "businessentityid", "territoryid", "startdate"::text, "enddate"::text, "rowguid", "modifieddate"::text from "sales"."salesterritoryhistory" - where ("businessentityid", "startdate", "territoryid") + where ("businessentityid", "startdate", "territoryid") in (select unnest(${businessentityid}), unnest(${startdate}), unnest(${territoryid})) """.as(SalesterritoryhistoryRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala index c3bf1a00c..eff535aa4 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala @@ -106,7 +106,7 @@ class SpecialofferproductRepoImpl extends SpecialofferproductRepo { val productid = compositeIds.map(_.productid) SQL"""select "specialofferid", "productid", "rowguid", "modifieddate"::text from "sales"."specialofferproduct" - where ("specialofferid", "productid") + where ("specialofferid", "productid") in (select unnest(${specialofferid}), unnest(${productid})) """.as(SpecialofferproductRow.rowParser(1).*) diff --git a/typo-tester-anorm/generated-and-checked-in/adventureworks/testInsert.scala b/typo-tester-anorm/generated-and-checked-in/adventureworks/testInsert.scala index 4523188d3..8a9129eae 100644 --- a/typo-tester-anorm/generated-and-checked-in/adventureworks/testInsert.scala +++ b/typo-tester-anorm/generated-and-checked-in/adventureworks/testInsert.scala @@ -209,6 +209,8 @@ import adventureworks.public.issue142.Issue142RepoImpl import adventureworks.public.issue142.Issue142Row import adventureworks.public.issue142_2.Issue1422RepoImpl import adventureworks.public.issue142_2.Issue1422Row +import adventureworks.public.only_pk_columns.OnlyPkColumnsRepoImpl +import adventureworks.public.only_pk_columns.OnlyPkColumnsRow import adventureworks.public.pgtest.PgtestRepoImpl import adventureworks.public.pgtest.PgtestRow import adventureworks.public.pgtestnull.PgtestnullRepoImpl @@ -650,6 +652,7 @@ class TestInsert(random: Random, domainInsert: TestDomainInsert) { def publicIdentityTest(name: IdentityTestId, defaultGenerated: Defaulted[Int] = Defaulted.UseDefault)(implicit c: Connection): IdentityTestRow = (new IdentityTestRepoImpl).insert(new IdentityTestRowUnsaved(name = name, defaultGenerated = defaultGenerated)) def publicIssue142(tabellkode: Issue142Id = Issue142Id(random.alphanumeric.take(20).mkString))(implicit c: Connection): Issue142Row = (new Issue142RepoImpl).insert(new Issue142Row(tabellkode = tabellkode)) def publicIssue1422(tabellkode: Issue142Id = Issue142Id.All(random.nextInt(2)))(implicit c: Connection): Issue1422Row = (new Issue1422RepoImpl).insert(new Issue1422Row(tabellkode = tabellkode)) + def publicOnlyPkColumns(keyColumn1: String = random.alphanumeric.take(20).mkString, keyColumn2: Int = random.nextInt())(implicit c: Connection): OnlyPkColumnsRow = (new OnlyPkColumnsRepoImpl).insert(new OnlyPkColumnsRow(keyColumn1 = keyColumn1, keyColumn2 = keyColumn2)) def publicPgtest(box: TypoBox, bytea: TypoBytea, circle: TypoCircle, diff --git a/typo-tester-anorm/src/scala/adventureworks/UpsertTwiceTest.scala b/typo-tester-anorm/src/scala/adventureworks/UpsertTwiceTest.scala new file mode 100644 index 000000000..52a49400a --- /dev/null +++ b/typo-tester-anorm/src/scala/adventureworks/UpsertTwiceTest.scala @@ -0,0 +1,15 @@ +package adventureworks + +import adventureworks.public.only_pk_columns.{OnlyPkColumnsRepoImpl, OnlyPkColumnsRow} +import org.scalatest.funsuite.AnyFunSuite + +class UpsertTwiceTest extends AnyFunSuite { + val onlyPkColumnsRepo = new OnlyPkColumnsRepoImpl + + test("second upsert should not error") { + val row = OnlyPkColumnsRow("the answer is", 42) + withConnection { implicit c => + assert(onlyPkColumnsRepo.upsert(row) == onlyPkColumnsRepo.upsert(row)) + } + } +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala index f92fae273..8827049c3 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeedepartmenthistory/EmployeedepartmenthistoryRepoImpl.scala @@ -102,7 +102,7 @@ class EmployeedepartmenthistoryRepoImpl extends EmployeedepartmenthistoryRepo { val shiftid = compositeIds.map(_.shiftid) sql"""select "businessentityid", "departmentid", "shiftid", "startdate"::text, "enddate"::text, "modifieddate"::text from "humanresources"."employeedepartmenthistory" - where ("businessentityid", "startdate", "departmentid", "shiftid") + where ("businessentityid", "startdate", "departmentid", "shiftid") in (select unnest(${businessentityid}), unnest(${startdate}), unnest(${departmentid}), unnest(${shiftid})) """.query(using EmployeedepartmenthistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala index 203686463..c4ae881da 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/humanresources/employeepayhistory/EmployeepayhistoryRepoImpl.scala @@ -96,7 +96,7 @@ class EmployeepayhistoryRepoImpl extends EmployeepayhistoryRepo { val ratechangedate = compositeIds.map(_.ratechangedate) sql"""select "businessentityid", "ratechangedate"::text, "rate", "payfrequency", "modifieddate"::text from "humanresources"."employeepayhistory" - where ("businessentityid", "ratechangedate") + where ("businessentityid", "ratechangedate") in (select unnest(${businessentityid}), unnest(${ratechangedate})) """.query(using EmployeepayhistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala index 2e8cd418f..4d51bd889 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentityaddress/BusinessentityaddressRepoImpl.scala @@ -102,7 +102,7 @@ class BusinessentityaddressRepoImpl extends BusinessentityaddressRepo { val addresstypeid = compositeIds.map(_.addresstypeid) sql"""select "businessentityid", "addressid", "addresstypeid", "rowguid", "modifieddate"::text from "person"."businessentityaddress" - where ("businessentityid", "addressid", "addresstypeid") + where ("businessentityid", "addressid", "addresstypeid") in (select unnest(${businessentityid}), unnest(${addressid}), unnest(${addresstypeid})) """.query(using BusinessentityaddressRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala index 1961c380a..55850f074 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/businessentitycontact/BusinessentitycontactRepoImpl.scala @@ -101,7 +101,7 @@ class BusinessentitycontactRepoImpl extends BusinessentitycontactRepo { val contacttypeid = compositeIds.map(_.contacttypeid) sql"""select "businessentityid", "personid", "contacttypeid", "rowguid", "modifieddate"::text from "person"."businessentitycontact" - where ("businessentityid", "personid", "contacttypeid") + where ("businessentityid", "personid", "contacttypeid") in (select unnest(${businessentityid}), unnest(${personid}), unnest(${contacttypeid})) """.query(using BusinessentitycontactRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala index 99586377b..da85c2bb2 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/emailaddress/EmailaddressRepoImpl.scala @@ -102,7 +102,7 @@ class EmailaddressRepoImpl extends EmailaddressRepo { val emailaddressid = compositeIds.map(_.emailaddressid) sql"""select "businessentityid", "emailaddressid", "emailaddress", "rowguid", "modifieddate"::text from "person"."emailaddress" - where ("businessentityid", "emailaddressid") + where ("businessentityid", "emailaddressid") in (select unnest(${businessentityid}), unnest(${emailaddressid})) """.query(using EmailaddressRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala index 6c89d0843..bf3c5a31d 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/person/personphone/PersonphoneRepoImpl.scala @@ -97,7 +97,7 @@ class PersonphoneRepoImpl extends PersonphoneRepo { val phonenumbertypeid = compositeIds.map(_.phonenumbertypeid) sql"""select "businessentityid", "phonenumber", "phonenumbertypeid", "modifieddate"::text from "person"."personphone" - where ("businessentityid", "phonenumber", "phonenumbertypeid") + where ("businessentityid", "phonenumber", "phonenumbertypeid") in (select unnest(${businessentityid}), unnest(${phonenumber}), unnest(${phonenumbertypeid})) """.query(using PersonphoneRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala index 11d55438f..742cdef0c 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productcosthistory/ProductcosthistoryRepoImpl.scala @@ -95,7 +95,7 @@ class ProductcosthistoryRepoImpl extends ProductcosthistoryRepo { val startdate = compositeIds.map(_.startdate) sql"""select "productid", "startdate"::text, "enddate"::text, "standardcost", "modifieddate"::text from "production"."productcosthistory" - where ("productid", "startdate") + where ("productid", "startdate") in (select unnest(${productid}), unnest(${startdate})) """.query(using ProductcosthistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala index 735c60f1d..7952a7362 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productdocument/ProductdocumentRepoImpl.scala @@ -96,7 +96,7 @@ class ProductdocumentRepoImpl extends ProductdocumentRepo { val documentnode = compositeIds.map(_.documentnode) sql"""select "productid", "modifieddate"::text, "documentnode" from "production"."productdocument" - where ("productid", "documentnode") + where ("productid", "documentnode") in (select unnest(${productid}), unnest(${documentnode})) """.query(using ProductdocumentRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala index 7db8f4cab..1eebb09ab 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productinventory/ProductinventoryRepoImpl.scala @@ -106,7 +106,7 @@ class ProductinventoryRepoImpl extends ProductinventoryRepo { val locationid = compositeIds.map(_.locationid) sql"""select "productid", "locationid", "shelf", "bin", "quantity", "rowguid", "modifieddate"::text from "production"."productinventory" - where ("productid", "locationid") + where ("productid", "locationid") in (select unnest(${productid}), unnest(${locationid})) """.query(using ProductinventoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala index 14aeef7ee..0864cf7d2 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productlistpricehistory/ProductlistpricehistoryRepoImpl.scala @@ -95,7 +95,7 @@ class ProductlistpricehistoryRepoImpl extends ProductlistpricehistoryRepo { val startdate = compositeIds.map(_.startdate) sql"""select "productid", "startdate"::text, "enddate"::text, "listprice", "modifieddate"::text from "production"."productlistpricehistory" - where ("productid", "startdate") + where ("productid", "startdate") in (select unnest(${productid}), unnest(${startdate})) """.query(using ProductlistpricehistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala index 6e06cfcb9..3bc339743 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelillustration/ProductmodelillustrationRepoImpl.scala @@ -93,7 +93,7 @@ class ProductmodelillustrationRepoImpl extends ProductmodelillustrationRepo { val illustrationid = compositeIds.map(_.illustrationid) sql"""select "productmodelid", "illustrationid", "modifieddate"::text from "production"."productmodelillustration" - where ("productmodelid", "illustrationid") + where ("productmodelid", "illustrationid") in (select unnest(${productmodelid}), unnest(${illustrationid})) """.query(using ProductmodelillustrationRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala index 33cab68b7..2b7ffe45b 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productmodelproductdescriptionculture/ProductmodelproductdescriptioncultureRepoImpl.scala @@ -97,7 +97,7 @@ class ProductmodelproductdescriptioncultureRepoImpl extends Productmodelproductd val cultureid = compositeIds.map(_.cultureid) sql"""select "productmodelid", "productdescriptionid", "cultureid", "modifieddate"::text from "production"."productmodelproductdescriptionculture" - where ("productmodelid", "productdescriptionid", "cultureid") + where ("productmodelid", "productdescriptionid", "cultureid") in (select unnest(${productmodelid}), unnest(${productdescriptionid}), unnest(${cultureid})) """.query(using ProductmodelproductdescriptioncultureRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala index c8676dbb1..230c5a270 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/productproductphoto/ProductproductphotoRepoImpl.scala @@ -98,7 +98,7 @@ class ProductproductphotoRepoImpl extends ProductproductphotoRepo { val productphotoid = compositeIds.map(_.productphotoid) sql"""select "productid", "productphotoid", "primary", "modifieddate"::text from "production"."productproductphoto" - where ("productid", "productphotoid") + where ("productid", "productphotoid") in (select unnest(${productid}), unnest(${productphotoid})) """.query(using ProductproductphotoRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala index 1102c2cd5..a64e56fbe 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/production/workorderrouting/WorkorderroutingRepoImpl.scala @@ -106,7 +106,7 @@ class WorkorderroutingRepoImpl extends WorkorderroutingRepo { val operationsequence = compositeIds.map(_.operationsequence) sql"""select "workorderid", "productid", "operationsequence", "locationid", "scheduledstartdate"::text, "scheduledenddate"::text, "actualstartdate"::text, "actualenddate"::text, "actualresourcehrs", "plannedcost", "actualcost", "modifieddate"::text from "production"."workorderrouting" - where ("workorderid", "productid", "operationsequence") + where ("workorderid", "productid", "operationsequence") in (select unnest(${workorderid}), unnest(${productid}), unnest(${operationsequence})) """.query(using WorkorderroutingRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala index 4c2bca693..b40b4d8a1 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/flaff/FlaffRepoImpl.scala @@ -65,7 +65,7 @@ class FlaffRepoImpl extends FlaffRepo { val specifier = compositeIds.map(_.specifier) sql"""select "code", "another_code", "some_number", "specifier", "parentspecifier" from "public"."flaff" - where ("code", "another_code", "some_number", "specifier") + where ("code", "another_code", "some_number", "specifier") in (select unnest(${code}), unnest(${anotherCode}), unnest(${someNumber}), unnest(${specifier})) """.query(using FlaffRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala index ecd3db505..d28df9a3e 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala @@ -66,7 +66,7 @@ class Issue142RepoImpl extends Issue142Repo { ${fromWrite(unsaved.tabellkode)(Write.fromPut(Issue142Id.put))} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode" """.query(using Issue142Row.read).unique } diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala index a5b8ae0eb..57480b676 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala @@ -67,7 +67,7 @@ class Issue1422RepoImpl extends Issue1422Repo { ${fromWrite(unsaved.tabellkode)(Write.fromPut(Issue142Id.put))} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode" """.query(using Issue1422Row.read).unique } diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala new file mode 100644 index 000000000..dd87eb881 --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala @@ -0,0 +1,48 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import typo.dsl.Path +import typo.dsl.Required +import typo.dsl.SqlExpr +import typo.dsl.SqlExpr.CompositeIn +import typo.dsl.SqlExpr.CompositeIn.TuplePart +import typo.dsl.SqlExpr.FieldLikeNoHkt +import typo.dsl.SqlExpr.IdField +import typo.dsl.Structure.Relation + +trait OnlyPkColumnsFields { + def keyColumn1: IdField[String, OnlyPkColumnsRow] + def keyColumn2: IdField[Int, OnlyPkColumnsRow] + def compositeIdIs(compositeId: OnlyPkColumnsId): SqlExpr[Boolean, Required] = + keyColumn1.isEqual(compositeId.keyColumn1).and(keyColumn2.isEqual(compositeId.keyColumn2)) + def compositeIdIn(compositeIds: Array[OnlyPkColumnsId]): SqlExpr[Boolean, Required] = + new CompositeIn(compositeIds)(TuplePart(keyColumn1)(_.keyColumn1), TuplePart(keyColumn2)(_.keyColumn2)) + +} + +object OnlyPkColumnsFields { + lazy val structure: Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] = + new Impl(Nil) + + private final class Impl(val _path: List[Path]) + extends Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] { + + override lazy val fields: OnlyPkColumnsFields = new OnlyPkColumnsFields { + override def keyColumn1 = IdField[String, OnlyPkColumnsRow](_path, "key_column_1", None, None, x => x.keyColumn1, (row, value) => row.copy(keyColumn1 = value)) + override def keyColumn2 = IdField[Int, OnlyPkColumnsRow](_path, "key_column_2", None, Some("int4"), x => x.keyColumn2, (row, value) => row.copy(keyColumn2 = value)) + } + + override lazy val columns: List[FieldLikeNoHkt[?, OnlyPkColumnsRow]] = + List[FieldLikeNoHkt[?, OnlyPkColumnsRow]](fields.keyColumn1, fields.keyColumn2) + + override def copy(path: List[Path]): Impl = + new Impl(path) + } + +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala new file mode 100644 index 000000000..13405ef9a --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala @@ -0,0 +1,22 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import io.circe.Decoder +import io.circe.Encoder + +/** Type for the composite primary key of table `public.only_pk_columns` */ +case class OnlyPkColumnsId( + keyColumn1: String, + keyColumn2: Int +) +object OnlyPkColumnsId { + implicit lazy val decoder: Decoder[OnlyPkColumnsId] = Decoder.forProduct2[OnlyPkColumnsId, String, Int]("key_column_1", "key_column_2")(OnlyPkColumnsId.apply)(Decoder.decodeString, Decoder.decodeInt) + implicit lazy val encoder: Encoder[OnlyPkColumnsId] = Encoder.forProduct2[OnlyPkColumnsId, String, Int]("key_column_1", "key_column_2")(x => (x.keyColumn1, x.keyColumn2))(Encoder.encodeString, Encoder.encodeInt) + implicit lazy val ordering: Ordering[OnlyPkColumnsId] = Ordering.by(x => (x.keyColumn1, x.keyColumn2)) +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala new file mode 100644 index 000000000..c0273c452 --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala @@ -0,0 +1,32 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import doobie.free.connection.ConnectionIO +import fs2.Stream +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.UpdateBuilder + +trait OnlyPkColumnsRepo { + def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def deleteById(compositeId: OnlyPkColumnsId): ConnectionIO[Boolean] + def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Int] + def insert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] + def insertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Long] + def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def selectAll: Stream[ConnectionIO, OnlyPkColumnsRow] + def selectById(compositeId: OnlyPkColumnsId): ConnectionIO[Option[OnlyPkColumnsRow]] + def selectByIds(compositeIds: Array[OnlyPkColumnsId]): Stream[ConnectionIO, OnlyPkColumnsRow] + def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Map[OnlyPkColumnsId, OnlyPkColumnsRow]] + def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def upsert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] + def upsertBatch(unsaved: List[OnlyPkColumnsRow]): Stream[ConnectionIO, OnlyPkColumnsRow] + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + def upsertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Int] +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala new file mode 100644 index 000000000..5d26a1c57 --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala @@ -0,0 +1,112 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import cats.instances.list.catsStdInstancesForList +import doobie.free.connection.ConnectionIO +import doobie.postgres.syntax.FragmentOps +import doobie.syntax.SqlInterpolator.SingleFragment.fromWrite +import doobie.syntax.string.toSqlInterpolator +import doobie.util.Write +import doobie.util.meta.Meta +import doobie.util.update.Update +import fs2.Stream +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderSql +import typo.dsl.UpdateBuilder + +class OnlyPkColumnsRepoImpl extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure) + } + override def deleteById(compositeId: OnlyPkColumnsId): ConnectionIO[Boolean] = { + sql"""delete from "public"."only_pk_columns" where "key_column_1" = ${fromWrite(compositeId.keyColumn1)(Write.fromPut(Meta.StringMeta.put))} AND "key_column_2" = ${fromWrite(compositeId.keyColumn2)(Write.fromPut(Meta.IntMeta.put))}""".update.run.map(_ > 0) + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Int] = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + sql"""delete + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.update.run + + } + override def insert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] = { + sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values (${fromWrite(unsaved.keyColumn1)(Write.fromPut(Meta.StringMeta.put))}, ${fromWrite(unsaved.keyColumn2)(Write.fromPut(Meta.IntMeta.put))}::int4) + returning "key_column_1", "key_column_2" + """.query(using OnlyPkColumnsRow.read).unique + } + override def insertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Long] = { + new FragmentOps(sql"""COPY "public"."only_pk_columns"("key_column_1", "key_column_2") FROM STDIN""").copyIn(unsaved, batchSize)(using OnlyPkColumnsRow.text) + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderSql(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.read) + } + override def selectAll: Stream[ConnectionIO, OnlyPkColumnsRow] = { + sql"""select "key_column_1", "key_column_2" from "public"."only_pk_columns"""".query(using OnlyPkColumnsRow.read).stream + } + override def selectById(compositeId: OnlyPkColumnsId): ConnectionIO[Option[OnlyPkColumnsRow]] = { + sql"""select "key_column_1", "key_column_2" from "public"."only_pk_columns" where "key_column_1" = ${fromWrite(compositeId.keyColumn1)(Write.fromPut(Meta.StringMeta.put))} AND "key_column_2" = ${fromWrite(compositeId.keyColumn2)(Write.fromPut(Meta.IntMeta.put))}""".query(using OnlyPkColumnsRow.read).option + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId]): Stream[ConnectionIO, OnlyPkColumnsRow] = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + sql"""select "key_column_1", "key_column_2" + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.query(using OnlyPkColumnsRow.read).stream + + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Map[OnlyPkColumnsId, OnlyPkColumnsRow]] = { + selectByIds(compositeIds).compile.toList.map { rows => + val byId = rows.view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.read) + } + override def upsert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] = { + sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values ( + ${fromWrite(unsaved.keyColumn1)(Write.fromPut(Meta.StringMeta.put))}, + ${fromWrite(unsaved.keyColumn2)(Write.fromPut(Meta.IntMeta.put))}::int4 + ) + on conflict ("key_column_1", "key_column_2") + do update set "key_column_1" = EXCLUDED."key_column_1" + returning "key_column_1", "key_column_2" + """.query(using OnlyPkColumnsRow.read).unique + } + override def upsertBatch(unsaved: List[OnlyPkColumnsRow]): Stream[ConnectionIO, OnlyPkColumnsRow] = { + Update[OnlyPkColumnsRow]( + s"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values (?,?::int4) + on conflict ("key_column_1", "key_column_2") + do nothing + returning "key_column_1", "key_column_2"""" + )(using OnlyPkColumnsRow.write) + .updateManyWithGeneratedKeys[OnlyPkColumnsRow]("key_column_1", "key_column_2")(unsaved)(using catsStdInstancesForList, OnlyPkColumnsRow.read) + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Int] = { + for { + _ <- sql"""create temporary table only_pk_columns_TEMP (like "public"."only_pk_columns") on commit drop""".update.run + _ <- new FragmentOps(sql"""copy only_pk_columns_TEMP("key_column_1", "key_column_2") from stdin""").copyIn(unsaved, batchSize)(using OnlyPkColumnsRow.text) + res <- sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + select * from only_pk_columns_TEMP + on conflict ("key_column_1", "key_column_2") + do nothing + ; + drop table only_pk_columns_TEMP;""".update.run + } yield res + } +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala new file mode 100644 index 000000000..44aeb8d18 --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala @@ -0,0 +1,100 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import doobie.free.connection.ConnectionIO +import doobie.free.connection.delay +import fs2.Stream +import scala.annotation.nowarn +import typo.dsl.DeleteBuilder +import typo.dsl.DeleteBuilder.DeleteBuilderMock +import typo.dsl.DeleteParams +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderMock +import typo.dsl.SelectParams +import typo.dsl.UpdateBuilder +import typo.dsl.UpdateBuilder.UpdateBuilderMock +import typo.dsl.UpdateParams + +class OnlyPkColumnsRepoMock(map: scala.collection.mutable.Map[OnlyPkColumnsId, OnlyPkColumnsRow] = scala.collection.mutable.Map.empty) extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilderMock(DeleteParams.empty, OnlyPkColumnsFields.structure, map) + } + override def deleteById(compositeId: OnlyPkColumnsId): ConnectionIO[Boolean] = { + delay(map.remove(compositeId).isDefined) + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Int] = { + delay(compositeIds.map(id => map.remove(id)).count(_.isDefined)) + } + override def insert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] = { + delay { + val _ = if (map.contains(unsaved.compositeId)) + sys.error(s"id ${unsaved.compositeId} already exists") + else + map.put(unsaved.compositeId, unsaved) + + unsaved + } + } + override def insertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Long] = { + unsaved.compile.toList.map { rows => + var num = 0L + rows.foreach { row => + map += (row.compositeId -> row) + num += 1 + } + num + } + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderMock(OnlyPkColumnsFields.structure, delay(map.values.toList), SelectParams.empty) + } + override def selectAll: Stream[ConnectionIO, OnlyPkColumnsRow] = { + Stream.emits(map.values.toList) + } + override def selectById(compositeId: OnlyPkColumnsId): ConnectionIO[Option[OnlyPkColumnsRow]] = { + delay(map.get(compositeId)) + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId]): Stream[ConnectionIO, OnlyPkColumnsRow] = { + Stream.emits(compositeIds.flatMap(map.get).toList) + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ConnectionIO[Map[OnlyPkColumnsId, OnlyPkColumnsRow]] = { + selectByIds(compositeIds).compile.toList.map { rows => + val byId = rows.view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilderMock(UpdateParams.empty, OnlyPkColumnsFields.structure, map) + } + override def upsert(unsaved: OnlyPkColumnsRow): ConnectionIO[OnlyPkColumnsRow] = { + delay { + map.put(unsaved.compositeId, unsaved): @nowarn + unsaved + } + } + override def upsertBatch(unsaved: List[OnlyPkColumnsRow]): Stream[ConnectionIO, OnlyPkColumnsRow] = { + Stream.emits { + unsaved.map { row => + map += (row.compositeId -> row) + row + } + } + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: Stream[ConnectionIO, OnlyPkColumnsRow], batchSize: Int = 10000): ConnectionIO[Int] = { + unsaved.compile.toList.map { rows => + var num = 0 + rows.foreach { row => + map += (row.compositeId -> row) + num += 1 + } + num + } + } +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala new file mode 100644 index 000000000..636138729 --- /dev/null +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala @@ -0,0 +1,62 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import doobie.enumerated.Nullability +import doobie.postgres.Text +import doobie.util.Read +import doobie.util.Write +import doobie.util.meta.Meta +import io.circe.Decoder +import io.circe.Encoder +import java.sql.ResultSet + +/** Table: public.only_pk_columns + Composite primary key: key_column_1, key_column_2 */ +case class OnlyPkColumnsRow( + keyColumn1: String, + keyColumn2: Int +){ + val compositeId: OnlyPkColumnsId = OnlyPkColumnsId(keyColumn1, keyColumn2) + val id = compositeId + } + +object OnlyPkColumnsRow { + def apply(compositeId: OnlyPkColumnsId) = + new OnlyPkColumnsRow(compositeId.keyColumn1, compositeId.keyColumn2) + implicit lazy val decoder: Decoder[OnlyPkColumnsRow] = Decoder.forProduct2[OnlyPkColumnsRow, String, Int]("key_column_1", "key_column_2")(OnlyPkColumnsRow.apply)(Decoder.decodeString, Decoder.decodeInt) + implicit lazy val encoder: Encoder[OnlyPkColumnsRow] = Encoder.forProduct2[OnlyPkColumnsRow, String, Int]("key_column_1", "key_column_2")(x => (x.keyColumn1, x.keyColumn2))(Encoder.encodeString, Encoder.encodeInt) + implicit lazy val read: Read[OnlyPkColumnsRow] = new Read[OnlyPkColumnsRow]( + gets = List( + (Meta.StringMeta.get, Nullability.NoNulls), + (Meta.IntMeta.get, Nullability.NoNulls) + ), + unsafeGet = (rs: ResultSet, i: Int) => OnlyPkColumnsRow( + keyColumn1 = Meta.StringMeta.get.unsafeGetNonNullable(rs, i + 0), + keyColumn2 = Meta.IntMeta.get.unsafeGetNonNullable(rs, i + 1) + ) + ) + implicit lazy val text: Text[OnlyPkColumnsRow] = Text.instance[OnlyPkColumnsRow]{ (row, sb) => + Text.stringInstance.unsafeEncode(row.keyColumn1, sb) + sb.append(Text.DELIMETER) + Text.intInstance.unsafeEncode(row.keyColumn2, sb) + } + implicit lazy val write: Write[OnlyPkColumnsRow] = new Write[OnlyPkColumnsRow]( + puts = List((Meta.StringMeta.put, Nullability.NoNulls), + (Meta.IntMeta.put, Nullability.NoNulls)), + toList = x => List(x.keyColumn1, x.keyColumn2), + unsafeSet = (rs, i, a) => { + Meta.StringMeta.put.unsafeSetNonNullable(rs, i + 0, a.keyColumn1) + Meta.IntMeta.put.unsafeSetNonNullable(rs, i + 1, a.keyColumn2) + }, + unsafeUpdate = (ps, i, a) => { + Meta.StringMeta.put.unsafeUpdateNonNullable(ps, i + 0, a.keyColumn1) + Meta.IntMeta.put.unsafeUpdateNonNullable(ps, i + 1, a.keyColumn2) + } + ) +} diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala index 00cd42dce..c952fffe0 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala @@ -90,7 +90,7 @@ class TableWithGeneratedColumnsRepoImpl extends TableWithGeneratedColumnsRepo { ${fromWrite(unsaved.name)(Write.fromPut(TableWithGeneratedColumnsId.put))} ) on conflict ("name") - do nothing + do update set "name" = EXCLUDED."name" returning "name", "name-type-always" """.query(using TableWithGeneratedColumnsRow.read).unique } diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala index 4652de636..7d9066772 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala @@ -66,7 +66,7 @@ class TitleRepoImpl extends TitleRepo { ${fromWrite(unsaved.code)(Write.fromPut(TitleId.put))} ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code" """.query(using TitleRow.read).unique } diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala index 5879cd358..8ceddcd01 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala @@ -66,7 +66,7 @@ class TitleDomainRepoImpl extends TitleDomainRepo { ${fromWrite(unsaved.code)(Write.fromPut(TitleDomainId.put))}::text ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code" """.query(using TitleDomainRow.read).unique } diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala index 26733c305..cf2f09973 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/productvendor/ProductvendorRepoImpl.scala @@ -103,7 +103,7 @@ class ProductvendorRepoImpl extends ProductvendorRepo { val businessentityid = compositeIds.map(_.businessentityid) sql"""select "productid", "businessentityid", "averageleadtime", "standardprice", "lastreceiptcost", "lastreceiptdate"::text, "minorderqty", "maxorderqty", "onorderqty", "unitmeasurecode", "modifieddate"::text from "purchasing"."productvendor" - where ("productid", "businessentityid") + where ("productid", "businessentityid") in (select unnest(${productid}), unnest(${businessentityid})) """.query(using ProductvendorRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala index 892507e68..9b98552f7 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/purchasing/purchaseorderdetail/PurchaseorderdetailRepoImpl.scala @@ -32,7 +32,7 @@ class PurchaseorderdetailRepoImpl extends PurchaseorderdetailRepo { val purchaseorderdetailid = compositeIds.map(_.purchaseorderdetailid) sql"""select "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text from "purchasing"."purchaseorderdetail" - where ("purchaseorderid", "purchaseorderdetailid") + where ("purchaseorderid", "purchaseorderdetailid") in (select unnest(${purchaseorderid}), unnest(${purchaseorderdetailid})) """.query(using PurchaseorderdetailRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala index a7b90506f..dedc082ce 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/countryregioncurrency/CountryregioncurrencyRepoImpl.scala @@ -93,7 +93,7 @@ class CountryregioncurrencyRepoImpl extends CountryregioncurrencyRepo { val currencycode = compositeIds.map(_.currencycode) sql"""select "countryregioncode", "currencycode", "modifieddate"::text from "sales"."countryregioncurrency" - where ("countryregioncode", "currencycode") + where ("countryregioncode", "currencycode") in (select unnest(${countryregioncode}), unnest(${currencycode})) """.query(using CountryregioncurrencyRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala index 9bb2148e1..96459eba2 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/personcreditcard/PersoncreditcardRepoImpl.scala @@ -94,7 +94,7 @@ class PersoncreditcardRepoImpl extends PersoncreditcardRepo { val creditcardid = compositeIds.map(_.creditcardid) sql"""select "businessentityid", "creditcardid", "modifieddate"::text from "sales"."personcreditcard" - where ("businessentityid", "creditcardid") + where ("businessentityid", "creditcardid") in (select unnest(${businessentityid}), unnest(${creditcardid})) """.query(using PersoncreditcardRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala index d73164f95..4c890d085 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderdetail/SalesorderdetailRepoImpl.scala @@ -113,7 +113,7 @@ class SalesorderdetailRepoImpl extends SalesorderdetailRepo { val salesorderdetailid = compositeIds.map(_.salesorderdetailid) sql"""select "salesorderid", "salesorderdetailid", "carriertrackingnumber", "orderqty", "productid", "specialofferid", "unitprice", "unitpricediscount", "rowguid", "modifieddate"::text from "sales"."salesorderdetail" - where ("salesorderid", "salesorderdetailid") + where ("salesorderid", "salesorderdetailid") in (select unnest(${salesorderid}), unnest(${salesorderdetailid})) """.query(using SalesorderdetailRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala index 1a372f9be..65178ce56 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesorderheadersalesreason/SalesorderheadersalesreasonRepoImpl.scala @@ -93,7 +93,7 @@ class SalesorderheadersalesreasonRepoImpl extends SalesorderheadersalesreasonRep val salesreasonid = compositeIds.map(_.salesreasonid) sql"""select "salesorderid", "salesreasonid", "modifieddate"::text from "sales"."salesorderheadersalesreason" - where ("salesorderid", "salesreasonid") + where ("salesorderid", "salesreasonid") in (select unnest(${salesorderid}), unnest(${salesreasonid})) """.query(using SalesorderheadersalesreasonRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala index 79d83fdd1..a11b974bd 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salespersonquotahistory/SalespersonquotahistoryRepoImpl.scala @@ -99,7 +99,7 @@ class SalespersonquotahistoryRepoImpl extends SalespersonquotahistoryRepo { val quotadate = compositeIds.map(_.quotadate) sql"""select "businessentityid", "quotadate"::text, "salesquota", "rowguid", "modifieddate"::text from "sales"."salespersonquotahistory" - where ("businessentityid", "quotadate") + where ("businessentityid", "quotadate") in (select unnest(${businessentityid}), unnest(${quotadate})) """.query(using SalespersonquotahistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala index be9121da6..b402c74ea 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/salesterritoryhistory/SalesterritoryhistoryRepoImpl.scala @@ -102,7 +102,7 @@ class SalesterritoryhistoryRepoImpl extends SalesterritoryhistoryRepo { val territoryid = compositeIds.map(_.territoryid) sql"""select "businessentityid", "territoryid", "startdate"::text, "enddate"::text, "rowguid", "modifieddate"::text from "sales"."salesterritoryhistory" - where ("businessentityid", "startdate", "territoryid") + where ("businessentityid", "startdate", "territoryid") in (select unnest(${businessentityid}), unnest(${startdate}), unnest(${territoryid})) """.query(using SalesterritoryhistoryRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala index 08a63386d..a13025391 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/sales/specialofferproduct/SpecialofferproductRepoImpl.scala @@ -98,7 +98,7 @@ class SpecialofferproductRepoImpl extends SpecialofferproductRepo { val productid = compositeIds.map(_.productid) sql"""select "specialofferid", "productid", "rowguid", "modifieddate"::text from "sales"."specialofferproduct" - where ("specialofferid", "productid") + where ("specialofferid", "productid") in (select unnest(${specialofferid}), unnest(${productid})) """.query(using SpecialofferproductRow.read).stream diff --git a/typo-tester-doobie/generated-and-checked-in/adventureworks/testInsert.scala b/typo-tester-doobie/generated-and-checked-in/adventureworks/testInsert.scala index f945b9864..e670b898c 100644 --- a/typo-tester-doobie/generated-and-checked-in/adventureworks/testInsert.scala +++ b/typo-tester-doobie/generated-and-checked-in/adventureworks/testInsert.scala @@ -209,6 +209,8 @@ import adventureworks.public.issue142.Issue142RepoImpl import adventureworks.public.issue142.Issue142Row import adventureworks.public.issue142_2.Issue1422RepoImpl import adventureworks.public.issue142_2.Issue1422Row +import adventureworks.public.only_pk_columns.OnlyPkColumnsRepoImpl +import adventureworks.public.only_pk_columns.OnlyPkColumnsRow import adventureworks.public.pgtest.PgtestRepoImpl import adventureworks.public.pgtest.PgtestRow import adventureworks.public.pgtestnull.PgtestnullRepoImpl @@ -650,6 +652,7 @@ class TestInsert(random: Random, domainInsert: TestDomainInsert) { def publicIdentityTest(name: IdentityTestId, defaultGenerated: Defaulted[Int] = Defaulted.UseDefault): ConnectionIO[IdentityTestRow] = (new IdentityTestRepoImpl).insert(new IdentityTestRowUnsaved(name = name, defaultGenerated = defaultGenerated)) def publicIssue142(tabellkode: Issue142Id = Issue142Id(random.alphanumeric.take(20).mkString)): ConnectionIO[Issue142Row] = (new Issue142RepoImpl).insert(new Issue142Row(tabellkode = tabellkode)) def publicIssue1422(tabellkode: Issue142Id = Issue142Id.All(random.nextInt(2))): ConnectionIO[Issue1422Row] = (new Issue1422RepoImpl).insert(new Issue1422Row(tabellkode = tabellkode)) + def publicOnlyPkColumns(keyColumn1: String = random.alphanumeric.take(20).mkString, keyColumn2: Int = random.nextInt()): ConnectionIO[OnlyPkColumnsRow] = (new OnlyPkColumnsRepoImpl).insert(new OnlyPkColumnsRow(keyColumn1 = keyColumn1, keyColumn2 = keyColumn2)) def publicPgtest(box: TypoBox, bytea: TypoBytea, circle: TypoCircle, diff --git a/typo-tester-doobie/src/scala/adventureworks/UpsertTwiceTest.scala b/typo-tester-doobie/src/scala/adventureworks/UpsertTwiceTest.scala new file mode 100644 index 000000000..f7bf3fda8 --- /dev/null +++ b/typo-tester-doobie/src/scala/adventureworks/UpsertTwiceTest.scala @@ -0,0 +1,19 @@ +package adventureworks + +import adventureworks.public.only_pk_columns.{OnlyPkColumnsRepoImpl, OnlyPkColumnsRow} +import org.scalatest.funsuite.AnyFunSuite + +class UpsertTwiceTest extends AnyFunSuite { + val onlyPkColumnsRepo = new OnlyPkColumnsRepoImpl + + test("second upsert should not error") { + val row = OnlyPkColumnsRow("the answer is", 42) + val (first, second) = withConnection { + for { + first <- onlyPkColumnsRepo.upsert(row) + second <- onlyPkColumnsRepo.upsert(row) + } yield (first, second) + } + assert(first == second) + } +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala index 07d9e34cd..51216470b 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142/Issue142RepoImpl.scala @@ -64,7 +64,7 @@ class Issue142RepoImpl extends Issue142Repo { ${Segment.paramSegment(unsaved.tabellkode)(Issue142Id.setter)} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode"""".insertReturning(using Issue142Row.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala index 88d88fdb6..95f3ca660 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/issue142_2/Issue1422RepoImpl.scala @@ -65,7 +65,7 @@ class Issue1422RepoImpl extends Issue1422Repo { ${Segment.paramSegment(unsaved.tabellkode)(Issue142Id.setter)} ) on conflict ("tabellkode") - do nothing + do update set "tabellkode" = EXCLUDED."tabellkode" returning "tabellkode"""".insertReturning(using Issue1422Row.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala new file mode 100644 index 000000000..dd87eb881 --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsFields.scala @@ -0,0 +1,48 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import typo.dsl.Path +import typo.dsl.Required +import typo.dsl.SqlExpr +import typo.dsl.SqlExpr.CompositeIn +import typo.dsl.SqlExpr.CompositeIn.TuplePart +import typo.dsl.SqlExpr.FieldLikeNoHkt +import typo.dsl.SqlExpr.IdField +import typo.dsl.Structure.Relation + +trait OnlyPkColumnsFields { + def keyColumn1: IdField[String, OnlyPkColumnsRow] + def keyColumn2: IdField[Int, OnlyPkColumnsRow] + def compositeIdIs(compositeId: OnlyPkColumnsId): SqlExpr[Boolean, Required] = + keyColumn1.isEqual(compositeId.keyColumn1).and(keyColumn2.isEqual(compositeId.keyColumn2)) + def compositeIdIn(compositeIds: Array[OnlyPkColumnsId]): SqlExpr[Boolean, Required] = + new CompositeIn(compositeIds)(TuplePart(keyColumn1)(_.keyColumn1), TuplePart(keyColumn2)(_.keyColumn2)) + +} + +object OnlyPkColumnsFields { + lazy val structure: Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] = + new Impl(Nil) + + private final class Impl(val _path: List[Path]) + extends Relation[OnlyPkColumnsFields, OnlyPkColumnsRow] { + + override lazy val fields: OnlyPkColumnsFields = new OnlyPkColumnsFields { + override def keyColumn1 = IdField[String, OnlyPkColumnsRow](_path, "key_column_1", None, None, x => x.keyColumn1, (row, value) => row.copy(keyColumn1 = value)) + override def keyColumn2 = IdField[Int, OnlyPkColumnsRow](_path, "key_column_2", None, Some("int4"), x => x.keyColumn2, (row, value) => row.copy(keyColumn2 = value)) + } + + override lazy val columns: List[FieldLikeNoHkt[?, OnlyPkColumnsRow]] = + List[FieldLikeNoHkt[?, OnlyPkColumnsRow]](fields.keyColumn1, fields.keyColumn2) + + override def copy(path: List[Path]): Impl = + new Impl(path) + } + +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala new file mode 100644 index 000000000..3d02739ac --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsId.scala @@ -0,0 +1,40 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import zio.json.JsonDecoder +import zio.json.JsonEncoder +import zio.json.ast.Json +import zio.json.internal.Write + +/** Type for the composite primary key of table `public.only_pk_columns` */ +case class OnlyPkColumnsId( + keyColumn1: String, + keyColumn2: Int +) +object OnlyPkColumnsId { + implicit lazy val jsonDecoder: JsonDecoder[OnlyPkColumnsId] = JsonDecoder[Json.Obj].mapOrFail { jsonObj => + val keyColumn1 = jsonObj.get("key_column_1").toRight("Missing field 'key_column_1'").flatMap(_.as(JsonDecoder.string)) + val keyColumn2 = jsonObj.get("key_column_2").toRight("Missing field 'key_column_2'").flatMap(_.as(JsonDecoder.int)) + if (keyColumn1.isRight && keyColumn2.isRight) + Right(OnlyPkColumnsId(keyColumn1 = keyColumn1.toOption.get, keyColumn2 = keyColumn2.toOption.get)) + else Left(List[Either[String, Any]](keyColumn1, keyColumn2).flatMap(_.left.toOption).mkString(", ")) + } + implicit lazy val jsonEncoder: JsonEncoder[OnlyPkColumnsId] = new JsonEncoder[OnlyPkColumnsId] { + override def unsafeEncode(a: OnlyPkColumnsId, indent: Option[Int], out: Write): Unit = { + out.write("{") + out.write(""""key_column_1":""") + JsonEncoder.string.unsafeEncode(a.keyColumn1, indent, out) + out.write(",") + out.write(""""key_column_2":""") + JsonEncoder.int.unsafeEncode(a.keyColumn2, indent, out) + out.write("}") + } + } + implicit lazy val ordering: Ordering[OnlyPkColumnsId] = Ordering.by(x => (x.keyColumn1, x.keyColumn2)) +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala new file mode 100644 index 000000000..7e75985d7 --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepo.scala @@ -0,0 +1,34 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.UpdateBuilder +import zio.ZIO +import zio.jdbc.UpdateResult +import zio.jdbc.ZConnection +import zio.stream.ZStream + +trait OnlyPkColumnsRepo { + def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def deleteById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Boolean] + def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Long] + def insert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, OnlyPkColumnsRow] + def insertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] + def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def selectAll: ZStream[ZConnection, Throwable, OnlyPkColumnsRow] + def selectById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Option[OnlyPkColumnsRow]] + def selectByIds(compositeIds: Array[OnlyPkColumnsId]): ZStream[ZConnection, Throwable, OnlyPkColumnsRow] + def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Map[OnlyPkColumnsId, OnlyPkColumnsRow]] + def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] + def upsert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, UpdateResult[OnlyPkColumnsRow]] + // Not implementable for zio-jdbc: upsertBatch + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + def upsertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala new file mode 100644 index 000000000..4575cb8e8 --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoImpl.scala @@ -0,0 +1,98 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import typo.dsl.DeleteBuilder +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderSql +import typo.dsl.UpdateBuilder +import zio.ZIO +import zio.jdbc.SqlFragment.Segment +import zio.jdbc.SqlFragment.Setter +import zio.jdbc.UpdateResult +import zio.jdbc.ZConnection +import zio.jdbc.sqlInterpolator +import zio.stream.ZStream + +class OnlyPkColumnsRepoImpl extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure) + } + override def deleteById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Boolean] = { + sql"""delete from "public"."only_pk_columns" where "key_column_1" = ${Segment.paramSegment(compositeId.keyColumn1)(Setter.stringSetter)} AND "key_column_2" = ${Segment.paramSegment(compositeId.keyColumn2)(Setter.intSetter)}""".delete.map(_ > 0) + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Long] = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + sql"""delete + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.delete + + } + override def insert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, OnlyPkColumnsRow] = { + sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values (${Segment.paramSegment(unsaved.keyColumn1)(Setter.stringSetter)}, ${Segment.paramSegment(unsaved.keyColumn2)(Setter.intSetter)}::int4) + returning "key_column_1", "key_column_2" + """.insertReturning(using OnlyPkColumnsRow.jdbcDecoder).map(_.updatedKeys.head) + } + override def insertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] = { + streamingInsert(s"""COPY "public"."only_pk_columns"("key_column_1", "key_column_2") FROM STDIN""", batchSize, unsaved)(OnlyPkColumnsRow.text) + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderSql(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.jdbcDecoder) + } + override def selectAll: ZStream[ZConnection, Throwable, OnlyPkColumnsRow] = { + sql"""select "key_column_1", "key_column_2" from "public"."only_pk_columns"""".query(using OnlyPkColumnsRow.jdbcDecoder).selectStream() + } + override def selectById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Option[OnlyPkColumnsRow]] = { + sql"""select "key_column_1", "key_column_2" from "public"."only_pk_columns" where "key_column_1" = ${Segment.paramSegment(compositeId.keyColumn1)(Setter.stringSetter)} AND "key_column_2" = ${Segment.paramSegment(compositeId.keyColumn2)(Setter.intSetter)}""".query(using OnlyPkColumnsRow.jdbcDecoder).selectOne + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId]): ZStream[ZConnection, Throwable, OnlyPkColumnsRow] = { + val keyColumn1 = compositeIds.map(_.keyColumn1) + val keyColumn2 = compositeIds.map(_.keyColumn2) + sql"""select "key_column_1", "key_column_2" + from "public"."only_pk_columns" + where ("key_column_1", "key_column_2") + in (select unnest(${keyColumn1}), unnest(${keyColumn2})) + """.query(using OnlyPkColumnsRow.jdbcDecoder).selectStream() + + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Map[OnlyPkColumnsId, OnlyPkColumnsRow]] = { + selectByIds(compositeIds).runCollect.map { rows => + val byId = rows.view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilder(""""public"."only_pk_columns"""", OnlyPkColumnsFields.structure, OnlyPkColumnsRow.jdbcDecoder) + } + override def upsert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, UpdateResult[OnlyPkColumnsRow]] = { + sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + values ( + ${Segment.paramSegment(unsaved.keyColumn1)(Setter.stringSetter)}, + ${Segment.paramSegment(unsaved.keyColumn2)(Setter.intSetter)}::int4 + ) + on conflict ("key_column_1", "key_column_2") + do update set "key_column_1" = EXCLUDED."key_column_1" + returning "key_column_1", "key_column_2"""".insertReturning(using OnlyPkColumnsRow.jdbcDecoder) + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] = { + val created = sql"""create temporary table only_pk_columns_TEMP (like "public"."only_pk_columns") on commit drop""".execute + val copied = streamingInsert(s"""copy only_pk_columns_TEMP("key_column_1", "key_column_2") from stdin""", batchSize, unsaved)(OnlyPkColumnsRow.text) + val merged = sql"""insert into "public"."only_pk_columns"("key_column_1", "key_column_2") + select * from only_pk_columns_TEMP + on conflict ("key_column_1", "key_column_2") + do nothing + ; + drop table only_pk_columns_TEMP;""".update + created *> copied *> merged + } +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala new file mode 100644 index 000000000..2f87d04dc --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRepoMock.scala @@ -0,0 +1,91 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import scala.annotation.nowarn +import typo.dsl.DeleteBuilder +import typo.dsl.DeleteBuilder.DeleteBuilderMock +import typo.dsl.DeleteParams +import typo.dsl.SelectBuilder +import typo.dsl.SelectBuilderMock +import typo.dsl.SelectParams +import typo.dsl.UpdateBuilder +import typo.dsl.UpdateBuilder.UpdateBuilderMock +import typo.dsl.UpdateParams +import zio.Chunk +import zio.ZIO +import zio.jdbc.UpdateResult +import zio.jdbc.ZConnection +import zio.stream.ZStream + +class OnlyPkColumnsRepoMock(map: scala.collection.mutable.Map[OnlyPkColumnsId, OnlyPkColumnsRow] = scala.collection.mutable.Map.empty) extends OnlyPkColumnsRepo { + override def delete: DeleteBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + DeleteBuilderMock(DeleteParams.empty, OnlyPkColumnsFields.structure, map) + } + override def deleteById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Boolean] = { + ZIO.succeed(map.remove(compositeId).isDefined) + } + override def deleteByIds(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Long] = { + ZIO.succeed(compositeIds.map(id => map.remove(id)).count(_.isDefined).toLong) + } + override def insert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, OnlyPkColumnsRow] = { + ZIO.succeed { + val _ = + if (map.contains(unsaved.compositeId)) + sys.error(s"id ${unsaved.compositeId} already exists") + else + map.put(unsaved.compositeId, unsaved) + + unsaved + } + } + override def insertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] = { + unsaved.scanZIO(0L) { case (acc, row) => + ZIO.succeed { + map += (row.compositeId -> row) + acc + 1 + } + }.runLast.map(_.getOrElse(0L)) + } + override def select: SelectBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + SelectBuilderMock(OnlyPkColumnsFields.structure, ZIO.succeed(Chunk.fromIterable(map.values)), SelectParams.empty) + } + override def selectAll: ZStream[ZConnection, Throwable, OnlyPkColumnsRow] = { + ZStream.fromIterable(map.values) + } + override def selectById(compositeId: OnlyPkColumnsId): ZIO[ZConnection, Throwable, Option[OnlyPkColumnsRow]] = { + ZIO.succeed(map.get(compositeId)) + } + override def selectByIds(compositeIds: Array[OnlyPkColumnsId]): ZStream[ZConnection, Throwable, OnlyPkColumnsRow] = { + ZStream.fromIterable(compositeIds.flatMap(map.get)) + } + override def selectByIdsTracked(compositeIds: Array[OnlyPkColumnsId]): ZIO[ZConnection, Throwable, Map[OnlyPkColumnsId, OnlyPkColumnsRow]] = { + selectByIds(compositeIds).runCollect.map { rows => + val byId = rows.view.map(x => (x.compositeId, x)).toMap + compositeIds.view.flatMap(id => byId.get(id).map(x => (id, x))).toMap + } + } + override def update: UpdateBuilder[OnlyPkColumnsFields, OnlyPkColumnsRow] = { + UpdateBuilderMock(UpdateParams.empty, OnlyPkColumnsFields.structure, map) + } + override def upsert(unsaved: OnlyPkColumnsRow): ZIO[ZConnection, Throwable, UpdateResult[OnlyPkColumnsRow]] = { + ZIO.succeed { + map.put(unsaved.compositeId, unsaved): @nowarn + UpdateResult(1, Chunk.single(unsaved)) + } + } + /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ + override def upsertStreaming(unsaved: ZStream[ZConnection, Throwable, OnlyPkColumnsRow], batchSize: Int = 10000): ZIO[ZConnection, Throwable, Long] = { + unsaved.scanZIO(0L) { case (acc, row) => + ZIO.succeed { + map += (row.compositeId -> row) + acc + 1 + } + }.runLast.map(_.getOrElse(0L)) + } +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala new file mode 100644 index 000000000..dd8339ec9 --- /dev/null +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/only_pk_columns/OnlyPkColumnsRow.scala @@ -0,0 +1,61 @@ +/** + * File has been automatically generated by `typo`. + * + * IF YOU CHANGE THIS FILE YOUR CHANGES WILL BE OVERWRITTEN. + */ +package adventureworks +package public +package only_pk_columns + +import java.sql.ResultSet +import zio.jdbc.JdbcDecoder +import zio.json.JsonDecoder +import zio.json.JsonEncoder +import zio.json.ast.Json +import zio.json.internal.Write + +/** Table: public.only_pk_columns + Composite primary key: key_column_1, key_column_2 */ +case class OnlyPkColumnsRow( + keyColumn1: String, + keyColumn2: Int +){ + val compositeId: OnlyPkColumnsId = OnlyPkColumnsId(keyColumn1, keyColumn2) + val id = compositeId + } + +object OnlyPkColumnsRow { + def apply(compositeId: OnlyPkColumnsId) = + new OnlyPkColumnsRow(compositeId.keyColumn1, compositeId.keyColumn2) + implicit lazy val jdbcDecoder: JdbcDecoder[OnlyPkColumnsRow] = new JdbcDecoder[OnlyPkColumnsRow] { + override def unsafeDecode(columIndex: Int, rs: ResultSet): (Int, OnlyPkColumnsRow) = + columIndex + 1 -> + OnlyPkColumnsRow( + keyColumn1 = JdbcDecoder.stringDecoder.unsafeDecode(columIndex + 0, rs)._2, + keyColumn2 = JdbcDecoder.intDecoder.unsafeDecode(columIndex + 1, rs)._2 + ) + } + implicit lazy val jsonDecoder: JsonDecoder[OnlyPkColumnsRow] = JsonDecoder[Json.Obj].mapOrFail { jsonObj => + val keyColumn1 = jsonObj.get("key_column_1").toRight("Missing field 'key_column_1'").flatMap(_.as(JsonDecoder.string)) + val keyColumn2 = jsonObj.get("key_column_2").toRight("Missing field 'key_column_2'").flatMap(_.as(JsonDecoder.int)) + if (keyColumn1.isRight && keyColumn2.isRight) + Right(OnlyPkColumnsRow(keyColumn1 = keyColumn1.toOption.get, keyColumn2 = keyColumn2.toOption.get)) + else Left(List[Either[String, Any]](keyColumn1, keyColumn2).flatMap(_.left.toOption).mkString(", ")) + } + implicit lazy val jsonEncoder: JsonEncoder[OnlyPkColumnsRow] = new JsonEncoder[OnlyPkColumnsRow] { + override def unsafeEncode(a: OnlyPkColumnsRow, indent: Option[Int], out: Write): Unit = { + out.write("{") + out.write(""""key_column_1":""") + JsonEncoder.string.unsafeEncode(a.keyColumn1, indent, out) + out.write(",") + out.write(""""key_column_2":""") + JsonEncoder.int.unsafeEncode(a.keyColumn2, indent, out) + out.write("}") + } + } + implicit lazy val text: Text[OnlyPkColumnsRow] = Text.instance[OnlyPkColumnsRow]{ (row, sb) => + Text.stringInstance.unsafeEncode(row.keyColumn1, sb) + sb.append(Text.DELIMETER) + Text.intInstance.unsafeEncode(row.keyColumn2, sb) + } +} diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala index 232bf216f..45c6d9d69 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/table_with_generated_columns/TableWithGeneratedColumnsRepoImpl.scala @@ -86,7 +86,7 @@ class TableWithGeneratedColumnsRepoImpl extends TableWithGeneratedColumnsRepo { ${Segment.paramSegment(unsaved.name)(TableWithGeneratedColumnsId.setter)} ) on conflict ("name") - do nothing + do update set "name" = EXCLUDED."name" returning "name", "name-type-always"""".insertReturning(using TableWithGeneratedColumnsRow.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala index efc033cc4..f59d81f6b 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title/TitleRepoImpl.scala @@ -64,7 +64,7 @@ class TitleRepoImpl extends TitleRepo { ${Segment.paramSegment(unsaved.code)(TitleId.setter)} ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code"""".insertReturning(using TitleRow.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala index 41a00f3c2..1e49d6fc4 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/public/title_domain/TitleDomainRepoImpl.scala @@ -64,7 +64,7 @@ class TitleDomainRepoImpl extends TitleDomainRepo { ${Segment.paramSegment(unsaved.code)(TitleDomainId.setter)}::text ) on conflict ("code") - do nothing + do update set "code" = EXCLUDED."code" returning "code"""".insertReturning(using TitleDomainRow.jdbcDecoder) } /* NOTE: this functionality is not safe if you use auto-commit mode! it runs 3 SQL statements */ diff --git a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/testInsert.scala b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/testInsert.scala index cbf4631cb..0bc9ac468 100644 --- a/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/testInsert.scala +++ b/typo-tester-zio-jdbc/generated-and-checked-in/adventureworks/testInsert.scala @@ -209,6 +209,8 @@ import adventureworks.public.issue142.Issue142RepoImpl import adventureworks.public.issue142.Issue142Row import adventureworks.public.issue142_2.Issue1422RepoImpl import adventureworks.public.issue142_2.Issue1422Row +import adventureworks.public.only_pk_columns.OnlyPkColumnsRepoImpl +import adventureworks.public.only_pk_columns.OnlyPkColumnsRow import adventureworks.public.pgtest.PgtestRepoImpl import adventureworks.public.pgtest.PgtestRow import adventureworks.public.pgtestnull.PgtestnullRepoImpl @@ -651,6 +653,7 @@ class TestInsert(random: Random, domainInsert: TestDomainInsert) { def publicIdentityTest(name: IdentityTestId, defaultGenerated: Defaulted[Int] = Defaulted.UseDefault): ZIO[ZConnection, Throwable, IdentityTestRow] = (new IdentityTestRepoImpl).insert(new IdentityTestRowUnsaved(name = name, defaultGenerated = defaultGenerated)) def publicIssue142(tabellkode: Issue142Id = Issue142Id(random.alphanumeric.take(20).mkString)): ZIO[ZConnection, Throwable, Issue142Row] = (new Issue142RepoImpl).insert(new Issue142Row(tabellkode = tabellkode)) def publicIssue1422(tabellkode: Issue142Id = Issue142Id.All(random.nextInt(2))): ZIO[ZConnection, Throwable, Issue1422Row] = (new Issue1422RepoImpl).insert(new Issue1422Row(tabellkode = tabellkode)) + def publicOnlyPkColumns(keyColumn1: String = random.alphanumeric.take(20).mkString, keyColumn2: Int = random.nextInt()): ZIO[ZConnection, Throwable, OnlyPkColumnsRow] = (new OnlyPkColumnsRepoImpl).insert(new OnlyPkColumnsRow(keyColumn1 = keyColumn1, keyColumn2 = keyColumn2)) def publicPgtest(box: TypoBox, bytea: TypoBytea, circle: TypoCircle, diff --git a/typo-tester-zio-jdbc/src/scala/adventureworks/UpsertTwiceTest.scala b/typo-tester-zio-jdbc/src/scala/adventureworks/UpsertTwiceTest.scala new file mode 100644 index 000000000..f7bf3fda8 --- /dev/null +++ b/typo-tester-zio-jdbc/src/scala/adventureworks/UpsertTwiceTest.scala @@ -0,0 +1,19 @@ +package adventureworks + +import adventureworks.public.only_pk_columns.{OnlyPkColumnsRepoImpl, OnlyPkColumnsRow} +import org.scalatest.funsuite.AnyFunSuite + +class UpsertTwiceTest extends AnyFunSuite { + val onlyPkColumnsRepo = new OnlyPkColumnsRepoImpl + + test("second upsert should not error") { + val row = OnlyPkColumnsRow("the answer is", 42) + val (first, second) = withConnection { + for { + first <- onlyPkColumnsRepo.upsert(row) + second <- onlyPkColumnsRepo.upsert(row) + } yield (first, second) + } + assert(first == second) + } +} diff --git a/typo/src/scala/typo/internal/codegen/DbLibAnorm.scala b/typo/src/scala/typo/internal/codegen/DbLibAnorm.scala index dbfb7ce68..388f6d238 100644 --- a/typo/src/scala/typo/internal/codegen/DbLibAnorm.scala +++ b/typo/src/scala/typo/internal/codegen/DbLibAnorm.scala @@ -280,7 +280,7 @@ class DbLibAnorm(pkg: sc.QIdent, inlineImplicits: Boolean, default: ComputedDefa val sql = SQL { code"""|select ${joinedColNames} |from $relName - |where (${x.cols.map(col => col.dbCol.name.code).mkCode(", ")}) + |where (${x.cols.map(col => col.dbCol.name.code).mkCode(", ")}) |in (select ${x.cols.map(col => code"unnest(${runtimeInterpolateValue(col.name, col.tpe, forbidInline = true)})").mkCode(", ")}) |""".stripMargin } @@ -416,7 +416,9 @@ class DbLibAnorm(pkg: sc.QIdent, inlineImplicits: Boolean, default: ComputedDefa } val conflictAction = writeableColumnsNotId match { - case Nil => code"do nothing" + case Nil => + val arbitraryColumn = id.cols.head + code"do update set ${arbitraryColumn.dbName.code} = EXCLUDED.${arbitraryColumn.dbName.code}" case nonEmpty => code"""|do update set | ${nonEmpty.map { c => code"${c.dbName.code} = EXCLUDED.${c.dbName.code}" }.mkCode(",\n")}""".stripMargin @@ -889,7 +891,7 @@ class DbLibAnorm(pkg: sc.QIdent, inlineImplicits: Boolean, default: ComputedDefa | case "VARCHAR" => "text[]" | case other => s"$${other}[]" | } - | + | | override def jdbcType: ${TypesScala.Int} = ${TypesJava.SqlTypes}.ARRAY |}""".stripMargin ) diff --git a/typo/src/scala/typo/internal/codegen/DbLibDoobie.scala b/typo/src/scala/typo/internal/codegen/DbLibDoobie.scala index 5bbbcac0c..13e8e0a4b 100644 --- a/typo/src/scala/typo/internal/codegen/DbLibDoobie.scala +++ b/typo/src/scala/typo/internal/codegen/DbLibDoobie.scala @@ -166,7 +166,7 @@ class DbLibDoobie(pkg: sc.QIdent, inlineImplicits: Boolean, default: ComputedDef val sql = SQL { code"""|select ${dbNames(cols, isRead = true)} |from $relName - |where (${x.cols.map(col => col.dbCol.name.code).mkCode(", ")}) + |where (${x.cols.map(col => col.dbCol.name.code).mkCode(", ")}) |in (select ${x.cols.map(col => code"unnest(${runtimeInterpolateValue(col.name, col.tpe, forbidInline = true)})").mkCode(", ")}) |""".stripMargin } @@ -306,7 +306,9 @@ class DbLibDoobie(pkg: sc.QIdent, inlineImplicits: Boolean, default: ComputedDef code"${runtimeInterpolateValue(code"${unsavedParam.name}.${c.name}", c.tpe)}${SqlCast.toPgCode(c)}" } val conflictAction = writeableColumnsNotId match { - case Nil => code"do nothing" + case Nil => + val arbitraryColumn = id.cols.head + code"do update set ${arbitraryColumn.dbName.code} = EXCLUDED.${arbitraryColumn.dbName.code}" case nonEmpty => code"""|do update set | ${nonEmpty.map { c => code"${c.dbName.code} = EXCLUDED.${c.dbName.code}" }.mkCode(",\n")}""".stripMargin diff --git a/typo/src/scala/typo/internal/codegen/DbLibZioJdbc.scala b/typo/src/scala/typo/internal/codegen/DbLibZioJdbc.scala index ac3c09fd8..d2a776eba 100644 --- a/typo/src/scala/typo/internal/codegen/DbLibZioJdbc.scala +++ b/typo/src/scala/typo/internal/codegen/DbLibZioJdbc.scala @@ -406,7 +406,9 @@ class DbLibZioJdbc(pkg: sc.QIdent, inlineImplicits: Boolean, dslEnabled: Boolean } val conflictAction = writeableColumnsNotId match { - case Nil => code"do nothing" + case Nil => + val arbitraryColumn = id.cols.head + code"do update set ${arbitraryColumn.dbName.code} = EXCLUDED.${arbitraryColumn.dbName.code}" case nonEmpty => code"""|do update set | ${nonEmpty.map { c => code"${c.dbName.code} = EXCLUDED.${c.dbName.code}" }.mkCode(",\n")}""".stripMargin