From b8dec9d9a06124696bd5ee2abbf33f19087174b6 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Wed, 13 Nov 2024 20:08:35 +0700 Subject: [PATCH] feat(mariadb): support RETURNING clause in DELETE statement --- dialect/feature/feature.go | 1 + dialect/mysqldialect/dialect.go | 3 +++ dialect/oracledialect/dialect.go | 3 ++- dialect/pgdialect/dialect.go | 3 ++- dialect/sqlitedialect/dialect.go | 3 ++- internal/dbtest/query_test.go | 6 ++++++ internal/dbtest/testdata/snapshots/TestQuery-mariadb-172 | 1 + .../dbtest/testdata/snapshots/TestQuery-mssql2019-172 | 1 + internal/dbtest/testdata/snapshots/TestQuery-mysql5-172 | 1 + internal/dbtest/testdata/snapshots/TestQuery-mysql8-172 | 1 + internal/dbtest/testdata/snapshots/TestQuery-pg-172 | 1 + internal/dbtest/testdata/snapshots/TestQuery-pgx-172 | 1 + internal/dbtest/testdata/snapshots/TestQuery-sqlite-172 | 1 + query_delete.go | 9 +++++++-- 14 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-mariadb-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-mssql2019-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-mysql5-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-mysql8-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-pg-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-pgx-172 create mode 100644 internal/dbtest/testdata/snapshots/TestQuery-sqlite-172 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/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 {