Skip to content

Commit dd814f1

Browse files
authored
Fixing onConflict case with existing column used (#3137)
1 parent 8e43a94 commit dd814f1

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

.github/workflows/site.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ jobs:
1818
if: ${{ github.event_name == 'pull_request' }}
1919
steps:
2020
- name: Git Checkout
21-
uses: actions/checkout@v4.2.2
21+
uses: actions/checkout@v3.3.0
2222
with:
2323
fetch-depth: '0'
2424
- name: Setup Scala
25-
uses: actions/setup-java@v4.5.0
25+
uses: actions/setup-java@v3.9.0
2626
with:
2727
distribution: temurin
2828
java-version: 17
@@ -39,11 +39,11 @@ jobs:
3939
if: ${{ ((github.event_name == 'release') && (github.event.action == 'published')) || (github.event_name == 'workflow_dispatch') }}
4040
steps:
4141
- name: Git Checkout
42-
uses: actions/checkout@v4.2.2
42+
uses: actions/checkout@v3.3.0
4343
with:
4444
fetch-depth: '0'
4545
- name: Setup Scala
46-
uses: actions/setup-java@v4.5.0
46+
uses: actions/setup-java@v3.9.0
4747
with:
4848
distribution: temurin
4949
java-version: 17
@@ -63,12 +63,12 @@ jobs:
6363
if: ${{ (github.event_name == 'push') || ((github.event_name == 'release') && (github.event.action == 'published')) }}
6464
steps:
6565
- name: Git Checkout
66-
uses: actions/checkout@v4.2.2
66+
uses: actions/checkout@v3.3.0
6767
with:
6868
ref: ${{ github.head_ref }}
6969
fetch-depth: '0'
7070
- name: Setup Scala
71-
uses: actions/setup-java@v4.5.0
71+
uses: actions/setup-java@v3.9.0
7272
with:
7373
distribution: temurin
7474
java-version: 17
@@ -82,7 +82,7 @@ jobs:
8282
git add README.md
8383
git commit -m "Update README.md" || echo "No changes to commit"
8484
- name: Create Pull Request
85-
uses: peter-evans/create-pull-request@v7.0.5
85+
uses: peter-evans/create-pull-request@v4.2.3
8686
with:
8787
body: |-
8888
Autogenerated changes after running the `sbt docs/generateReadme` command of the [zio-sbt-website](https://zio.dev/zio-sbt) plugin.

quill-engine/src/main/scala/io/getquill/sql/idiom/OnConflictSupport.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,32 @@ trait OnConflictSupport {
1515
strategy: NamingStrategy,
1616
idiomContext: IdiomContext
1717
): Tokenizer[OnConflict] = {
18+
val entityAlias = "t"
1819

1920
val customEntityTokenizer = Tokenizer[Entity] { case Entity.Opinionated(name, _, _, renameable) =>
20-
stmt"INTO ${renameable.fixedOr(name.token)(strategy.table(name).token)} AS t"
21+
stmt"INTO ${renameable.fixedOr(name.token)(strategy.table(name).token)} AS ${entityAlias.token}"
2122
}
2223

2324
val customAstTokenizer =
2425
Tokenizer.withFallback[Ast](self.astTokenizer(_, strategy, idiomContext)) {
26+
case Property(_: OnConflict.Excluded, value) => stmt"EXCLUDED.${value.token}"
27+
28+
// At first glance it might be hard to understand why this is doing `case OnConflict.Existing(a) => stmt"${entityAlias}"`
29+
// but consider that this is a situation where multiple aliases are used in multiple update clauses e.g. the `tt` in the below example
30+
// wouldn't even exist as a variable because in the query produced (also below) it would not even exist
31+
// i.e. since the table will be aliased as the first `existing table` variable i.e. `t`.
32+
// The second one `tt` wouldn't even exist.
33+
// ins.onConflictUpdate(_.i, _.s)(
34+
// (t, e) => t.l -> foo(t.l, e.l), (tt, ee) => tt.l -> bar(tt.l, ee.l)
35+
// )
36+
// This doesn't exist!!
37+
// v
38+
// > INSERT INTO TestEntity AS t (s,i,l,o,b) VALUES (?, ?, ?, ?, ?) ON CONFLICT (i,s) DO UPDATE SET l = foo(t.l, EXCLUDED.l), l = bar(tt.l, EXCLUDED.l)
39+
//
40+
// So instead of the situation we use the original alias of the table as it was computed by Quill i.e. `t`.
41+
// See the "cols target - update + infix" example for more detail
2542
case _: OnConflict.Excluded => stmt"EXCLUDED"
26-
case OnConflict.Existing(a) => stmt"${a.token}"
43+
case _: OnConflict.Existing => stmt"${entityAlias.token}"
2744
case a: Action =>
2845
self.actionTokenizer(customEntityTokenizer)(actionAstTokenizer, strategy, idiomContext).token(a)
2946
}

quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/OnConflictSpec.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ trait OnConflictSpec extends Spec {
4040
def `cols target - update`(ins: Quoted[Insert[TestEntity]]) = quote {
4141
ins.onConflictUpdate(_.i, _.s)((t, e) => t.l -> (t.l + e.l) / 2, _.s -> _.s)
4242
}
43+
// In this situation the `tt` variable that the "existing" row is pointing to (in the second clause) wouldn't
44+
// even be created so clearly the variable name itself must be dropped and only the column reference should be used
45+
def `cols target - update + infix`(ins: Quoted[Insert[TestEntity]]) = quote {
46+
ins.onConflictUpdate(_.i, _.s)(
47+
(t, e) => t.l -> sql"foo(${t.l}, ${e.l})".as[Long],
48+
(tt, ee) => tt.l -> sql"bar(${tt.l}, ${ee.l})".as[Long]
49+
)
50+
}
4351
def insBatch = quote(liftQuery(List(e, TestEntity("s2", 1, 2L, Some(1), true))))
4452

4553
def `no target - ignore batch` = quote {

quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/PostgresDialectSpec.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,9 @@ class PostgresDialectSpec extends OnConflictSpec {
6262
ctx.run(`cols target - update`(i)).string mustEqual
6363
"INSERT INTO TestEntity AS t (s,i,l,o,b) VALUES (?, ?, ?, ?, ?) ON CONFLICT (i,s) DO UPDATE SET l = ((t.l + EXCLUDED.l) / 2), s = EXCLUDED.s"
6464
}
65+
"cols target - update + infix" in {
66+
ctx.run(`cols target - update + infix`(i)).string mustEqual
67+
"INSERT INTO TestEntity AS t (s,i,l,o,b) VALUES (?, ?, ?, ?, ?) ON CONFLICT (i,s) DO UPDATE SET l = foo(t.l, EXCLUDED.l), l = bar(t.l, EXCLUDED.l)"
68+
}
6569
}
6670
}

quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/SqliteDialectSpec.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,9 @@ class SqliteDialectSpec extends OnConflictSpec {
5353
ctx.run(`cols target - update`(i)).string mustEqual
5454
"INSERT INTO TestEntity AS t (s,i,l,o,b) VALUES (?, ?, ?, ?, ?) ON CONFLICT (i,s) DO UPDATE SET l = ((t.l + EXCLUDED.l) / 2), s = EXCLUDED.s"
5555
}
56+
"cols target - update + infix" in {
57+
ctx.run(`cols target - update + infix`(i)).string mustEqual
58+
"INSERT INTO TestEntity AS t (s,i,l,o,b) VALUES (?, ?, ?, ?, ?) ON CONFLICT (i,s) DO UPDATE SET l = foo(t.l, EXCLUDED.l), l = bar(t.l, EXCLUDED.l)"
59+
}
5660
}
5761
}

0 commit comments

Comments
 (0)