Add USING clause support for DELETE statements

This commit is contained in:
go-jet 2021-12-08 18:14:57 +01:00
parent 72e8d7d584
commit 60ffd004c5
6 changed files with 134 additions and 11 deletions

View file

@ -6,6 +6,7 @@ import "github.com/go-jet/jet/v2/internal/jet"
type DeleteStatement interface { type DeleteStatement interface {
Statement Statement
USING(tables ...ReadableTable) DeleteStatement
WHERE(expression BoolExpression) DeleteStatement WHERE(expression BoolExpression) DeleteStatement
ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
LIMIT(limit int64) DeleteStatement LIMIT(limit int64) DeleteStatement
@ -15,6 +16,7 @@ type deleteStatementImpl struct {
jet.SerializerStatement jet.SerializerStatement
Delete jet.ClauseStatementBegin Delete jet.ClauseStatementBegin
Using jet.ClauseFrom
Where jet.ClauseWhere Where jet.ClauseWhere
OrderBy jet.ClauseOrderBy OrderBy jet.ClauseOrderBy
Limit jet.ClauseLimit Limit jet.ClauseLimit
@ -22,10 +24,15 @@ type deleteStatementImpl struct {
func newDeleteStatement(table Table) DeleteStatement { func newDeleteStatement(table Table) DeleteStatement {
newDelete := &deleteStatementImpl{} newDelete := &deleteStatementImpl{}
newDelete.SerializerStatement = jet.NewStatementImpl(Dialect, jet.DeleteStatementType, newDelete, &newDelete.Delete, newDelete.SerializerStatement = jet.NewStatementImpl(Dialect, jet.DeleteStatementType, newDelete,
&newDelete.Where, &newDelete.OrderBy, &newDelete.Limit) &newDelete.Delete,
&newDelete.Using,
&newDelete.Where,
&newDelete.OrderBy,
&newDelete.Limit)
newDelete.Delete.Name = "DELETE FROM" newDelete.Delete.Name = "DELETE FROM"
newDelete.Using.Name = "USING"
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table) newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
newDelete.Where.Mandatory = true newDelete.Where.Mandatory = true
newDelete.Limit.Count = -1 newDelete.Limit.Count = -1
@ -33,6 +40,11 @@ func newDeleteStatement(table Table) DeleteStatement {
return newDelete return newDelete
} }
func (d *deleteStatementImpl) USING(tables ...ReadableTable) DeleteStatement {
d.Using.Tables = readableTablesToSerializerList(tables)
return d
}
func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement { func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement {
d.Where.Condition = expression d.Where.Condition = expression
return d return d

View file

@ -106,10 +106,7 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement {
} }
func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement { func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement {
s.From.Tables = nil s.From.Tables = readableTablesToSerializerList(tables)
for _, table := range tables {
s.From.Tables = append(s.From.Tables, table)
}
return s return s
} }
@ -189,3 +186,11 @@ func toJetFrameOffset(offset interface{}) jet.Serializer {
return jet.FixedLiteral(offset) return jet.FixedLiteral(offset)
} }
func readableTablesToSerializerList(tables []ReadableTable) []jet.Serializer {
var ret []jet.Serializer
for _, table := range tables {
ret = append(ret, table)
}
return ret
}

View file

@ -6,8 +6,8 @@ import "github.com/go-jet/jet/v2/internal/jet"
type DeleteStatement interface { type DeleteStatement interface {
jet.SerializerStatement jet.SerializerStatement
USING(tables ...ReadableTable) DeleteStatement
WHERE(expression BoolExpression) DeleteStatement WHERE(expression BoolExpression) DeleteStatement
RETURNING(projections ...jet.Projection) DeleteStatement RETURNING(projections ...jet.Projection) DeleteStatement
} }
@ -15,22 +15,32 @@ type deleteStatementImpl struct {
jet.SerializerStatement jet.SerializerStatement
Delete jet.ClauseStatementBegin Delete jet.ClauseStatementBegin
Using jet.ClauseFrom
Where jet.ClauseWhere Where jet.ClauseWhere
Returning jet.ClauseReturning Returning jet.ClauseReturning
} }
func newDeleteStatement(table WritableTable) DeleteStatement { func newDeleteStatement(table WritableTable) DeleteStatement {
newDelete := &deleteStatementImpl{} newDelete := &deleteStatementImpl{}
newDelete.SerializerStatement = jet.NewStatementImpl(Dialect, jet.DeleteStatementType, newDelete, &newDelete.Delete, newDelete.SerializerStatement = jet.NewStatementImpl(Dialect, jet.DeleteStatementType, newDelete,
&newDelete.Where, &newDelete.Returning) &newDelete.Delete,
&newDelete.Using,
&newDelete.Where,
&newDelete.Returning)
newDelete.Delete.Name = "DELETE FROM" newDelete.Delete.Name = "DELETE FROM"
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table) newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
newDelete.Using.Name = "USING"
newDelete.Where.Mandatory = true newDelete.Where.Mandatory = true
return newDelete return newDelete
} }
func (d *deleteStatementImpl) USING(tables ...ReadableTable) DeleteStatement {
d.Using.Tables = readableTablesToSerializerList(tables)
return d
}
func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement { func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement {
d.Where.Condition = expression d.Where.Condition = expression
return d return d

View file

@ -9,7 +9,7 @@ type DeleteStatement interface {
WHERE(expression BoolExpression) DeleteStatement WHERE(expression BoolExpression) DeleteStatement
ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
LIMIT(limit int64) DeleteStatement LIMIT(limit int64) DeleteStatement
RETURNING(projections ...jet.Projection) DeleteStatement RETURNING(projections ...Projection) DeleteStatement
} }
type deleteStatementImpl struct { type deleteStatementImpl struct {

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/mysql" . "github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -91,3 +92,29 @@ func initForDeleteTest(t *testing.T) {
testutils.AssertExec(t, stmt, db, 2) testutils.AssertExec(t, stmt, db, 2)
} }
func TestDeleteWithUsing(t *testing.T) {
tx := beginTx(t)
defer tx.Rollback()
stmt := table.Rental.DELETE().
USING(
table.Rental.
INNER_JOIN(table.Staff, table.Rental.StaffID.EQ(table.Staff.StaffID)),
table.Actor,
).WHERE(
table.Staff.StaffID.EQ(Int(2)).
AND(table.Rental.RentalID.LT(Int(10))),
)
testutils.AssertStatementSql(t, stmt, `
DELETE FROM dvds.rental
USING dvds.rental
INNER JOIN dvds.staff ON (rental.staff_id = staff.staff_id),
dvds.actor
WHERE (staff.staff_id = ?) AND (rental.rental_id < ?);
`)
_, err := stmt.Exec(tx)
require.NoError(t, err)
}

View file

@ -4,6 +4,8 @@ import (
"context" "context"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
model2 "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -103,3 +105,70 @@ func TestDeleteExecContext(t *testing.T) {
require.Error(t, err, "context deadline exceeded") require.Error(t, err, "context deadline exceeded")
requireLogged(t, deleteStmt) requireLogged(t, deleteStmt)
} }
func TestDeleteFrom(t *testing.T) {
tx := beginTx(t)
defer tx.Rollback()
stmt := table.Rental.DELETE().
USING(
table.Staff.
INNER_JOIN(table.Store, table.Store.StoreID.EQ(table.Staff.StaffID)),
table.Actor,
).WHERE(
table.Staff.StaffID.EQ(table.Rental.StaffID).
AND(table.Staff.StaffID.EQ(Int(2))).
AND(table.Rental.RentalID.LT(Int(10))),
).RETURNING(
table.Rental.AllColumns,
table.Store.AllColumns,
)
testutils.AssertStatementSql(t, stmt, `
DELETE FROM dvds.rental
USING dvds.staff
INNER JOIN dvds.store ON (store.store_id = staff.staff_id),
dvds.actor
WHERE ((staff.staff_id = rental.staff_id) AND (staff.staff_id = $1)) AND (rental.rental_id < $2)
RETURNING rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update",
store.store_id AS "store.store_id",
store.manager_staff_id AS "store.manager_staff_id",
store.address_id AS "store.address_id",
store.last_update AS "store.last_update";
`)
var dest []struct {
Rental model2.Rental
Store model2.Store
}
err := stmt.Query(tx, &dest)
require.NoError(t, err)
require.Len(t, dest, 3)
testutils.AssertJSON(t, dest[0], `
{
"Rental": {
"RentalID": 4,
"RentalDate": "2005-05-24T23:04:41Z",
"InventoryID": 2452,
"CustomerID": 333,
"ReturnDate": "2005-06-03T01:43:41Z",
"StaffID": 2,
"LastUpdate": "2006-02-16T02:30:53Z"
},
"Store": {
"StoreID": 2,
"ManagerStaffID": 2,
"AddressID": 2,
"LastUpdate": "2006-02-15T09:57:12Z"
}
}
`)
}