Skip to content

Commit eeb62dd

Browse files
dialect/sql: added support for replace clause
1 parent f70e93a commit eeb62dd

File tree

2 files changed

+82
-4
lines changed

2 files changed

+82
-4
lines changed

dialect/sql/builder.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,10 @@ type InsertBuilder struct {
146146
returning []string
147147
values [][]any
148148
conflict *conflict
149-
isUpsert bool // YDB-specific: use UPSERT instead of INSERT
149+
150+
// YDB-specific:
151+
isUpsert bool // use UPSERT instead of INSERT
152+
isReplace bool // use REPLACE instead of INSERT
150153
}
151154

152155
// Insert creates a builder for the `INSERT INTO` statement.
@@ -174,6 +177,21 @@ func Upsert(table string) *InsertBuilder {
174177
return &InsertBuilder{table: table, isUpsert: true}
175178
}
176179

180+
// Replace creates a builder for the `REPLACE INTO` statement.
181+
// REPLACE overwrites entire rows based on primary key comparison.
182+
// For existing rows, the entire row is replaced (unspecified columns get default values).
183+
// For missing rows, new rows are inserted.
184+
//
185+
// Replace("users").
186+
// Columns("id", "name", "age").
187+
// Values(1, "a8m", 10).
188+
// Values(2, "foo", 20)
189+
//
190+
// Note: REPLACE is only supported in YDB dialect.
191+
func Replace(table string) *InsertBuilder {
192+
return &InsertBuilder{table: table, isReplace: true}
193+
}
194+
177195
// Schema sets the database name for the insert table.
178196
func (i *InsertBuilder) Schema(name string) *InsertBuilder {
179197
i.schema = name
@@ -454,17 +472,23 @@ func (i *InsertBuilder) Query() (string, []any) {
454472
return query, args
455473
}
456474

457-
// QueryErr returns query representation of an `INSERT INTO` or `UPSERT INTO`
458-
// statement and any error occurred in building the statement.
475+
// QueryErr returns query representation of an `INSERT INTO`, `UPSERT INTO`,
476+
// or `REPLACE INTO` statement and any error occurred in building the statement.
459477
func (i *InsertBuilder) QueryErr() (string, []any, error) {
460478
b := i.Builder.clone()
461479

462480
if i.isUpsert {
463481
if !b.ydb() {
464-
b.AddError(fmt.Errorf("UPSERT: unsupported dialect: %q", b.dialect))
482+
b.AddError(fmt.Errorf("UPSERT INTO: unsupported dialect: %q", b.dialect))
465483
return "", nil, b.Err()
466484
}
467485
b.WriteString("UPSERT INTO ")
486+
} else if i.isReplace {
487+
if !b.ydb() {
488+
b.AddError(fmt.Errorf("REPLACE INTO: unsupported dialect: %q", b.dialect))
489+
return "", nil, b.Err()
490+
}
491+
b.WriteString("REPLACE INTO ")
468492
} else {
469493
b.WriteString("INSERT INTO ")
470494
}
@@ -3657,6 +3681,19 @@ func (d *DialectBuilder) Upsert(table string) *InsertBuilder {
36573681
return b
36583682
}
36593683

3684+
// Replace creates an InsertBuilder for the REPLACE statement with the configured dialect.
3685+
// REPLACE is only supported in YDB dialect.
3686+
//
3687+
// Dialect(dialect.YDB).
3688+
// Replace("users").
3689+
// Columns("id", "name", "age").
3690+
// Values(1, "a8m", 10)
3691+
func (d *DialectBuilder) Replace(table string) *InsertBuilder {
3692+
b := Replace(table)
3693+
b.SetDialect(d.dialect)
3694+
return b
3695+
}
3696+
36603697
// Update creates a UpdateBuilder for the configured dialect.
36613698
//
36623699
// Dialect(dialect.Postgres).

dialect/sql/builder_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,47 @@ AND "users"."id1" < "users"."id2") AND "users"."id1" <= "users"."id2"`, "\n", ""
17341734
driver.NamedValue{Name: "p2", Value: "john@example.com"},
17351735
},
17361736
},
1737+
{
1738+
input: Dialect(dialect.YDB).
1739+
Replace("users").
1740+
Columns("id", "name", "age").
1741+
Values(1, "a8m", 10),
1742+
wantQuery: "REPLACE INTO `users` (`id`, `name`, `age`) VALUES ($p0, $p1, $p2)",
1743+
wantArgs: []any{
1744+
driver.NamedValue{Name: "p0", Value: 1},
1745+
driver.NamedValue{Name: "p1", Value: "a8m"},
1746+
driver.NamedValue{Name: "p2", Value: 10},
1747+
},
1748+
},
1749+
{
1750+
input: Dialect(dialect.YDB).
1751+
Replace("users").
1752+
Columns("id", "name", "age").
1753+
Values(1, "a8m", 10).
1754+
Values(2, "foo", 20),
1755+
wantQuery: "REPLACE INTO `users` (`id`, `name`, `age`) VALUES ($p0, $p1, $p2), ($p3, $p4, $p5)",
1756+
wantArgs: []any{
1757+
driver.NamedValue{Name: "p0", Value: 1},
1758+
driver.NamedValue{Name: "p1", Value: "a8m"},
1759+
driver.NamedValue{Name: "p2", Value: 10},
1760+
driver.NamedValue{Name: "p3", Value: 2},
1761+
driver.NamedValue{Name: "p4", Value: "foo"},
1762+
driver.NamedValue{Name: "p5", Value: 20},
1763+
},
1764+
},
1765+
{
1766+
input: Dialect(dialect.YDB).
1767+
Replace("orders").
1768+
Columns("order_id", "status", "amount").
1769+
Values(1001, "shipped", 500).
1770+
Returning("*"),
1771+
wantQuery: "REPLACE INTO `orders` (`order_id`, `status`, `amount`) VALUES ($p0, $p1, $p2) RETURNING *",
1772+
wantArgs: []any{
1773+
driver.NamedValue{Name: "p0", Value: 1001},
1774+
driver.NamedValue{Name: "p1", Value: "shipped"},
1775+
driver.NamedValue{Name: "p2", Value: 500},
1776+
},
1777+
},
17371778
{
17381779
input: Dialect(dialect.YDB).
17391780
Update("users").

0 commit comments

Comments
 (0)