Skip to content

Commit

Permalink
Add support for DELETE USING. Add check for missing WHERE clause in D…
Browse files Browse the repository at this point in the history
…ELETE. (#36)
  • Loading branch information
ncabatoff authored Apr 28, 2024
1 parent 603d284 commit 8ff20b7
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 9 deletions.
33 changes: 30 additions & 3 deletions pkg/vet/vet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import (
"fmt"
"reflect"

pg_query "github.com/pganalyze/pg_query_go/v4"

"github.com/houqp/sqlvet/pkg/schema"
pg_query "github.com/pganalyze/pg_query_go/v4"
)

type Schema struct {
Expand Down Expand Up @@ -503,6 +502,14 @@ func parseWhereClause(ctx VetContext, clause *pg_query.Node, parseRe *ParseResul
return err
}

func parseUsingClause(ctx VetContext, clause *pg_query.Node, parseRe *ParseResult) error {
err := parseExpression(ctx, clause, parseRe)
if err != nil {
err = fmt.Errorf("invalid USING clause: %w", err)
}
return err
}

func getUsedColumnsFromNodeList(nodelist []*pg_query.Node) []ColumnUsed {
usedCols := []ColumnUsed{}
for _, item := range nodelist {
Expand Down Expand Up @@ -820,12 +827,19 @@ func validateInsertStmt(ctx VetContext, stmt *pg_query.InsertStmt) ([]QueryParam

func validateDeleteStmt(ctx VetContext, stmt *pg_query.DeleteStmt) ([]QueryParam, error) {
tableName := stmt.Relation.Relname
var tableAlias string

if stmt.Relation.Alias != nil {
tableAlias = stmt.Relation.Alias.Aliasname
}

if err := validateTable(ctx, tableName, true); err != nil {
return nil, err
}

usedCols := []ColumnUsed{}
queryParams := []QueryParam{}
usedTables := []TableUsed{}

if stmt.WhereClause != nil {
re := &ParseResult{}
Expand All @@ -835,10 +849,23 @@ func validateDeleteStmt(ctx VetContext, stmt *pg_query.DeleteStmt) ([]QueryParam
}
if len(re.Columns) > 0 {
usedCols = append(usedCols, re.Columns...)
} else {
return nil, fmt.Errorf("no columns in DELETE's WHERE clause")
}
if len(re.Params) > 0 {
queryParams = re.Params
}
} else {
return nil, fmt.Errorf("no WHERE clause for DELETE")
}

for _, using := range stmt.UsingClause {
re := &ParseResult{}
err := parseUsingClause(ctx, using, re)
if err != nil {
return nil, err
}
usedTables = append(usedTables, re.Tables...)
}

if len(stmt.ReturningList) > 0 {
Expand All @@ -847,7 +874,7 @@ func validateDeleteStmt(ctx VetContext, stmt *pg_query.DeleteStmt) ([]QueryParam
}

if len(usedCols) > 0 {
usedTables := []TableUsed{{Name: tableName}}
usedTables = append(usedTables, TableUsed{Name: tableName, Alias: tableAlias})
if err := validateTableColumns(ctx, usedTables, usedCols); err != nil {
return nil, err
}
Expand Down
26 changes: 20 additions & 6 deletions pkg/vet/vet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,6 @@ func TestDelete(t *testing.T) {
Name string
Query string
}{
{
"delete all",
`DELETE FROM foo`,
},
{
"delete with where",
`DELETE FROM foo WHERE id=1 AND value='bar'`,
Expand All @@ -584,7 +580,15 @@ func TestDelete(t *testing.T) {
},
{
"delete with returning",
`DELETE FROM foo RETURNING id`,
`DELETE FROM foo WHERE id>1 RETURNING id`,
},
{
"delete using",
`DELETE FROM foo USING bar WHERE foo.id = bar.id`,
},
{
"delete using with aliases",
`DELETE FROM foo AS f USING bar b WHERE f.id = b.id`,
},
}

Expand Down Expand Up @@ -657,9 +661,19 @@ func TestInvalidDelete(t *testing.T) {
},
{
"invalid column in return clause",
`DELETE FROM foo RETURNING uid`,
`DELETE FROM foo WHERE id = 1 RETURNING uid`,
errors.New("column `uid` is not defined in table `foo`"),
},
{
"no where clause",
`DELETE FROM foo`,
errors.New("no WHERE clause for DELETE"),
},
{
"no columns in where clause",
`DELETE FROM foo WHERE 1=1`,
errors.New("no columns in DELETE's WHERE clause"),
},
}

for _, tcase := range testCases {
Expand Down

0 comments on commit 8ff20b7

Please sign in to comment.