Skip to content

Commit 1937d8e

Browse files
authored
Add support for RETURNING clause in bulk insert queries (#11)
- Add RETURNING clause detection to suffix boundary logic - Preserve RETURNING clause when building bulk insert queries - Add test cases for RETURNING clause with and without ON CONFLICT - Add additional test cases for ON DUPLICATE KEY UPDATE with case variations - Ensures RETURNING clause is properly handled similar to ON DUPLICATE KEY UPDATE and ON CONFLICT
1 parent ff55507 commit 1937d8e

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

templates/template.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func buildBulkInsertQuery(originalQuery string, numArgs int, numParamsPerArg int
6767
// Use LastIndex to find the main clause
6868
onDuplicateUpperIndex := strings.LastIndex(strings.ToUpper(trimmedQuery), "ON DUPLICATE KEY UPDATE")
6969
onConflictUpperIndex := strings.LastIndex(strings.ToUpper(trimmedQuery), "ON CONFLICT")
70+
returningUpperIndex := strings.LastIndex(strings.ToUpper(trimmedQuery), "RETURNING")
7071

7172
// Find the earliest starting position of any suffix keyword
7273
suffixBoundary := len(trimmedQuery)
@@ -76,6 +77,9 @@ func buildBulkInsertQuery(originalQuery string, numArgs int, numParamsPerArg int
7677
if onConflictUpperIndex != -1 && onConflictUpperIndex < suffixBoundary {
7778
suffixBoundary = onConflictUpperIndex
7879
}
80+
if returningUpperIndex != -1 && returningUpperIndex < suffixBoundary {
81+
suffixBoundary = returningUpperIndex
82+
}
7983

8084
if suffixBoundary < len(trimmedQuery) {
8185
// Suffix found

templates/template_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,30 @@ func TestBuildBulkInsertQuery(t *testing.T) {
161161
}
162162
},
163163
},
164+
"valid:upsert (ON DUPLICATE KEY UPDATE)": {
165+
arrange: func(t *testing.T) (Args, Expected) {
166+
return Args{
167+
originalQuery: "INSERT INTO users (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name);",
168+
numArgs: 2,
169+
numParamsPerArg: 2,
170+
}, Expected{
171+
query: "INSERT INTO users (id, name) VALUES (?,?),(?,?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)",
172+
err: nil,
173+
}
174+
},
175+
},
176+
"valid:upsert (ON DUPLICATE KEY UPDATE) case-insensitive": {
177+
arrange: func(t *testing.T) (Args, Expected) {
178+
return Args{
179+
originalQuery: "insert into users (id, name) values (?, ?) on duplicate key update id = values(id), name = values(name);",
180+
numArgs: 2,
181+
numParamsPerArg: 2,
182+
}, Expected{
183+
query: "insert into users (id, name) VALUES (?,?),(?,?) on duplicate key update id = values(id), name = values(name)",
184+
err: nil,
185+
}
186+
},
187+
},
164188
"valid:upsert (ON CONFLICT)": {
165189
arrange: func(t *testing.T) (Args, Expected) {
166190
return Args{
@@ -185,6 +209,30 @@ func TestBuildBulkInsertQuery(t *testing.T) {
185209
}
186210
},
187211
},
212+
"valid:RETURNING clause": {
213+
arrange: func(t *testing.T) (Args, Expected) {
214+
return Args{
215+
originalQuery: "INSERT INTO users (id, name) VALUES (?, ?) RETURNING id;",
216+
numArgs: 2,
217+
numParamsPerArg: 2,
218+
}, Expected{
219+
query: "INSERT INTO users (id, name) VALUES (?,?),(?,?) RETURNING id",
220+
err: nil,
221+
}
222+
},
223+
},
224+
"valid:RETURNING clause with ON CONFLICT": {
225+
arrange: func(t *testing.T) (Args, Expected) {
226+
return Args{
227+
originalQuery: "INSERT INTO users (id, name) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id;",
228+
numArgs: 2,
229+
numParamsPerArg: 2,
230+
}, Expected{
231+
query: "INSERT INTO users (id, name) VALUES (?,?),(?,?) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id",
232+
err: nil,
233+
}
234+
},
235+
},
188236
"valid:Squeeze spaces": {
189237
arrange: func(t *testing.T) (Args, Expected) {
190238
return Args{

0 commit comments

Comments
 (0)