diff --git a/dialect/feature/feature.go b/dialect/feature/feature.go index 536561b5d..0707c6f88 100644 --- a/dialect/feature/feature.go +++ b/dialect/feature/feature.go @@ -34,4 +34,5 @@ const ( CompositeIn // ... WHERE (A,B) IN ((N, NN), (N, NN)...) UpdateOrderLimit // UPDATE ... ORDER BY ... LIMIT ... DeleteOrderLimit // DELETE ... ORDER BY ... LIMIT ... + DeleteReturning ) diff --git a/dialect/mysqldialect/dialect.go b/dialect/mysqldialect/dialect.go index ca3e0eb78..90a2b9cd9 100644 --- a/dialect/mysqldialect/dialect.go +++ b/dialect/mysqldialect/dialect.go @@ -79,6 +79,9 @@ func (d *Dialect) Init(db *sql.DB) { if strings.Contains(version, "MariaDB") { version = semver.MajorMinor("v" + cleanupVersion(version)) + if semver.Compare(version, "v10.0.5") >= 0 { + d.features |= feature.DeleteReturning + } if semver.Compare(version, "v10.5.0") >= 0 { d.features |= feature.InsertReturning } diff --git a/dialect/oracledialect/dialect.go b/dialect/oracledialect/dialect.go index ddbdefdc6..c9d3d3dda 100644 --- a/dialect/oracledialect/dialect.go +++ b/dialect/oracledialect/dialect.go @@ -40,7 +40,8 @@ func New() *Dialect { feature.TableNotExists | feature.SelectExists | feature.AutoIncrement | - feature.CompositeIn + feature.CompositeIn | + feature.DeleteReturning return d } diff --git a/dialect/oracledialect/go.mod b/dialect/oracledialect/go.mod index fe16f9150..51ff987bd 100644 --- a/dialect/oracledialect/go.mod +++ b/dialect/oracledialect/go.mod @@ -1,8 +1,8 @@ module github.com/uptrace/bun/dialect/oracledialect -go 1.22.0 +go 1.23 -toolchain go1.22.6 +toolchain go1.23.2 replace github.com/uptrace/bun => ../.. @@ -14,5 +14,5 @@ require ( github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sys v0.27.0 // indirect ) diff --git a/dialect/oracledialect/go.sum b/dialect/oracledialect/go.sum index 1e3c492a4..045ab2f33 100644 --- a/dialect/oracledialect/go.sum +++ b/dialect/oracledialect/go.sum @@ -14,7 +14,7 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dialect/pgdialect/dialect.go b/dialect/pgdialect/dialect.go index e95b581fd..040163f98 100644 --- a/dialect/pgdialect/dialect.go +++ b/dialect/pgdialect/dialect.go @@ -53,7 +53,8 @@ func New() *Dialect { feature.InsertOnConflict | feature.SelectExists | feature.GeneratedIdentity | - feature.CompositeIn + feature.CompositeIn | + feature.DeleteReturning return d } diff --git a/dialect/sqlitedialect/dialect.go b/dialect/sqlitedialect/dialect.go index c2c676d05..92959482e 100644 --- a/dialect/sqlitedialect/dialect.go +++ b/dialect/sqlitedialect/dialect.go @@ -40,7 +40,8 @@ func New() *Dialect { feature.TableNotExists | feature.SelectExists | feature.AutoIncrement | - feature.CompositeIn + feature.CompositeIn | + feature.DeleteReturning return d } diff --git a/internal/dbtest/query_test.go b/internal/dbtest/query_test.go index d915de52a..777ad93c9 100644 --- a/internal/dbtest/query_test.go +++ b/internal/dbtest/query_test.go @@ -1590,6 +1590,12 @@ func TestQuery(t *testing.T) { return db.NewUpdate().Model(new(Story)).Set("name = ?", "new-name").WherePK().Order("id").Limit(1) }, }, + { + id: 172, + query: func(db *bun.DB) schema.QueryAppender { + return db.NewDelete().Model(&Model{}).WherePK().Returning("*") + }, + }, } timeRE := regexp.MustCompile(`'2\d{3}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?(\+\d{2}:\d{2})?'`) diff --git a/internal/dbtest/testdata/snapshots/TestQuery-mariadb-172 b/internal/dbtest/testdata/snapshots/TestQuery-mariadb-172 new file mode 100644 index 000000000..bcfee0476 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-mariadb-172 @@ -0,0 +1 @@ +DELETE FROM `models` WHERE (`id` = NULL) RETURNING * diff --git a/internal/dbtest/testdata/snapshots/TestQuery-mssql2019-172 b/internal/dbtest/testdata/snapshots/TestQuery-mssql2019-172 new file mode 100644 index 000000000..dcc567f12 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-mssql2019-172 @@ -0,0 +1 @@ +bun: returning is not supported for current dialect diff --git a/internal/dbtest/testdata/snapshots/TestQuery-mysql5-172 b/internal/dbtest/testdata/snapshots/TestQuery-mysql5-172 new file mode 100644 index 000000000..dcc567f12 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-mysql5-172 @@ -0,0 +1 @@ +bun: returning is not supported for current dialect diff --git a/internal/dbtest/testdata/snapshots/TestQuery-mysql8-172 b/internal/dbtest/testdata/snapshots/TestQuery-mysql8-172 new file mode 100644 index 000000000..dcc567f12 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-mysql8-172 @@ -0,0 +1 @@ +bun: returning is not supported for current dialect diff --git a/internal/dbtest/testdata/snapshots/TestQuery-pg-172 b/internal/dbtest/testdata/snapshots/TestQuery-pg-172 new file mode 100644 index 000000000..c5add9661 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-pg-172 @@ -0,0 +1 @@ +DELETE FROM "models" AS "model" WHERE ("model"."id" = NULL) RETURNING * diff --git a/internal/dbtest/testdata/snapshots/TestQuery-pgx-172 b/internal/dbtest/testdata/snapshots/TestQuery-pgx-172 new file mode 100644 index 000000000..c5add9661 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-pgx-172 @@ -0,0 +1 @@ +DELETE FROM "models" AS "model" WHERE ("model"."id" = NULL) RETURNING * diff --git a/internal/dbtest/testdata/snapshots/TestQuery-sqlite-172 b/internal/dbtest/testdata/snapshots/TestQuery-sqlite-172 new file mode 100644 index 000000000..c5add9661 --- /dev/null +++ b/internal/dbtest/testdata/snapshots/TestQuery-sqlite-172 @@ -0,0 +1 @@ +DELETE FROM "models" AS "model" WHERE ("model"."id" = NULL) RETURNING * diff --git a/query_delete.go b/query_delete.go index 5092a337e..ccfeb1997 100644 --- a/query_delete.go +++ b/query_delete.go @@ -163,6 +163,11 @@ func (q *DeleteQuery) Limit(n int) *DeleteQuery { // // To suppress the auto-generated RETURNING clause, use `Returning("NULL")`. func (q *DeleteQuery) Returning(query string, args ...interface{}) *DeleteQuery { + if !q.hasFeature(feature.DeleteReturning) { + q.err = errors.New("bun: returning is not supported for current dialect") + return q + } + q.addReturning(schema.SafeQuery(query, args)) return q } @@ -249,7 +254,7 @@ func (q *DeleteQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e return nil, err } - if q.hasFeature(feature.Returning) && q.hasReturning() { + if q.hasFeature(feature.DeleteReturning) && q.hasReturning() { b = append(b, " RETURNING "...) b, err = q.appendReturning(fmter, b) if err != nil { @@ -311,7 +316,7 @@ func (q *DeleteQuery) scanOrExec( return nil, err } - useScan := hasDest || (q.hasReturning() && q.hasFeature(feature.Returning|feature.Output)) + useScan := hasDest || (q.hasReturning() && q.hasFeature(feature.DeleteReturning|feature.Output)) var model Model if useScan {