From 9c493810ecae09ee19e5f2560b2aea7fcdf960ba Mon Sep 17 00:00:00 2001 From: Patrick O'brien Date: Sat, 23 Apr 2016 21:54:24 +1000 Subject: [PATCH] Finished most of delete functions * Fixed some template errors * Added IN helpers --- boil/helpers.go | 43 ++++++++++++++++++++++++ boil/helpers_test.go | 52 ++++++++++++++++++++++++++++++ boil/query_test.go | 12 ------- cmds/config.go | 3 ++ cmds/sqlboiler_test.go | 4 +-- cmds/template_funcs.go | 10 ++++++ cmds/templates/all.tpl | 9 +++--- cmds/templates/delete.tpl | 47 ++++++++++++++++++++++----- cmds/templates/find.tpl | 2 +- cmds/templates/helpers.tpl | 15 +++++++++ cmds/templates/singles/helpers.tpl | 5 +-- 11 files changed, 172 insertions(+), 30 deletions(-) create mode 100644 cmds/templates/helpers.tpl diff --git a/boil/helpers.go b/boil/helpers.go index 2fdca1c..dd19b98 100644 --- a/boil/helpers.go +++ b/boil/helpers.go @@ -9,6 +9,49 @@ import ( "unicode" ) +// WherePrimaryKeyIn generates a "in" string for where queries +// For example: (col1, col2) IN (($1, $2), ($3, $4)) +func WherePrimaryKeyIn(numRows int, keyNames ...string) string { + in := &bytes.Buffer{} + + if len(keyNames) == 0 { + return "" + } + + in.WriteByte('(') + for i := 0; i < len(keyNames); i++ { + in.WriteString(`"` + keyNames[i] + `"`) + if i < len(keyNames)-1 { + in.WriteByte(',') + } + } + + in.WriteString(") IN (") + + c := 1 + for i := 0; i < numRows; i++ { + for y := 0; y < len(keyNames); y++ { + if len(keyNames) > 1 && y == 0 { + in.WriteByte('(') + } + + in.WriteString(fmt.Sprintf("$%d", c)) + c++ + + if len(keyNames) > 1 && y == len(keyNames)-1 { + in.WriteByte(')') + } + + if i != numRows-1 || y != len(keyNames)-1 { + in.WriteByte(',') + } + } + } + in.WriteByte(')') + + return in.String() +} + // SelectNames returns the column names for a select statement // Eg: col1, col2, col3 func SelectNames(results interface{}) string { diff --git a/boil/helpers_test.go b/boil/helpers_test.go index 2f68966..988f56b 100644 --- a/boil/helpers_test.go +++ b/boil/helpers_test.go @@ -11,6 +11,58 @@ type testObj struct { HeadSize int } +func TestWherePrimaryKeyIn(t *testing.T) { + t.Parallel() + + x := WherePrimaryKeyIn(1, "aa") + expect := `("aa") IN ($1)` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(2, "aa") + expect = `("aa") IN ($1,$2)` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(3, "aa") + expect = `("aa") IN ($1,$2,$3)` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(1, "aa", "bb") + expect = `("aa","bb") IN (($1,$2))` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(2, "aa", "bb") + expect = `("aa","bb") IN (($1,$2),($3,$4))` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(3, "aa", "bb") + expect = `("aa","bb") IN (($1,$2),($3,$4),($5,$6))` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(4, "aa", "bb") + expect = `("aa","bb") IN (($1,$2),($3,$4),($5,$6),($7,$8))` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } + + x = WherePrimaryKeyIn(4, "aa", "bb", "cc") + expect = `("aa","bb","cc") IN (($1,$2,$3),($4,$5,$6),($7,$8,$9),($10,$11,$12))` + if x != expect { + t.Errorf("Expected %s, got %s\n", expect, x) + } +} + func TestGoVarToSQLName(t *testing.T) { t.Parallel() diff --git a/boil/query_test.go b/boil/query_test.go index 9950fc0..7ec84e2 100644 --- a/boil/query_test.go +++ b/boil/query_test.go @@ -56,18 +56,6 @@ func TestBuildQuery(t *testing.T) { } } -func TestExecQuery(t *testing.T) { - t.Parallel() -} - -func TestExecQueryOne(t *testing.T) { - t.Parallel() -} - -func TestExecQueryAll(t *testing.T) { - t.Parallel() -} - func TestSetLimit(t *testing.T) { t.Parallel() diff --git a/cmds/config.go b/cmds/config.go index 4156832..6641084 100644 --- a/cmds/config.go +++ b/cmds/config.go @@ -39,6 +39,7 @@ var sqlBoilerImports = imports{ }, thirdparty: importList{ `"github.com/pobri19/sqlboiler/boil"`, + `"github.com/pobri19/sqlboiler/boil/qs"`, }, } @@ -47,6 +48,7 @@ var sqlBoilerSinglesImports = map[string]imports{ standard: importList{}, thirdparty: importList{ `"github.com/pobri19/sqlboiler/boil"`, + `"github.com/pobri19/sqlboiler/boil/qs"`, }, }, } @@ -102,6 +104,7 @@ var sqlBoilerTemplateFuncs = template.FuncMap{ "primaryKeyFlagIndex": primaryKeyFlagIndex, "updateParamNames": updateParamNames, "updateParamVariables": updateParamVariables, + "primaryKeyStrList": primaryKeyStrList, } // LoadConfigFile loads the toml config file into the cfg object diff --git a/cmds/sqlboiler_test.go b/cmds/sqlboiler_test.go index 0bd56b4..b628c6a 100644 --- a/cmds/sqlboiler_test.go +++ b/cmds/sqlboiler_test.go @@ -176,12 +176,12 @@ func outputCompileErrors(buf *bytes.Buffer, outFolder string) { } scanner := bufio.NewScanner(fh) - throwaway := eObj.lineNumber - 2 + throwaway := eObj.lineNumber - 5 for throwaway > 0 && scanner.Scan() { throwaway-- } - for i := 0; i < 3; i++ { + for i := 0; i < 6; i++ { if scanner.Scan() { b := scanner.Bytes() if len(b) != 0 { diff --git a/cmds/template_funcs.go b/cmds/template_funcs.go index 9385d30..332b1c4 100644 --- a/cmds/template_funcs.go +++ b/cmds/template_funcs.go @@ -234,6 +234,16 @@ func wherePrimaryKey(pkeyCols []string, start int) string { return output } +// primaryKeyStrList returns a list of primary key column names in strings +// For example: "col1", "col2", "col3" +func primaryKeyStrList(pkeyCols []string) string { + for i, c := range pkeyCols { + pkeyCols[i] = fmt.Sprintf(`"%s"`, c) + } + + return strings.Join(pkeyCols, ", ") +} + // commaList returns a comma seperated list: "col1, col2, col3" func commaList(cols []string) string { return strings.Join(cols, ", ") diff --git a/cmds/templates/all.tpl b/cmds/templates/all.tpl index d0ee4e8..22f69e3 100644 --- a/cmds/templates/all.tpl +++ b/cmds/templates/all.tpl @@ -1,17 +1,16 @@ {{- $tableNameSingular := titleCaseSingular .Table.Name -}} {{- $tableNamePlural := titleCasePlural .Table.Name -}} {{- $varNameSingular := camelCaseSingular .Table.Name -}} -{{- $varNamePlural := camelCasePlural .Table.Name -}} type {{$varNameSingular}}Query struct { *boil.Query } // {{$tableNamePlural}}All retrieves all records. -func {{$tableNamePlural}}(mods ...QueryMod) {{$varNameSingular}}Query { +func {{$tableNamePlural}}(mods ...qs.QueryMod) {{$varNameSingular}}Query { return {{$tableNamePlural}}X(boil.GetDB(), mods...) } -func {{$tableNamePlural}}X(exec boil.Executor, mods ...QueryMod) {{$tableNameSingular}}Query { - mods = append(mods, boil.From("{{.Table.Name}}")) - return NewQueryX(exec, mods...) +func {{$tableNamePlural}}X(exec boil.Executor, mods ...qs.QueryMod) {{$varNameSingular}}Query { + mods = append(mods, qs.From("{{.Table.Name}}")) + return {{$varNameSingular}}Query{NewQueryX(exec, mods...)} } diff --git a/cmds/templates/delete.tpl b/cmds/templates/delete.tpl index cd737ab..bf9d73e 100644 --- a/cmds/templates/delete.tpl +++ b/cmds/templates/delete.tpl @@ -3,19 +3,22 @@ {{- $varNameSingular := camelCaseSingular .Table.Name -}} // Delete deletes a single {{$tableNameSingular}} record. // Delete will match against the primary key column to find the record to delete. -func (o *{{$tableNameSingular}}) Delete(mods ...QueryMod) error { - return o.DeleteX(boil.GetDB(), mods...) +func (o *{{$tableNameSingular}}) Delete() error { + return o.DeleteX(boil.GetDB()) } -func (o *{{$tableNameSingular}}) DeleteX(exec boil.Executor, mods ...QueryMod) error { +func (o *{{$tableNameSingular}}) DeleteX(exec boil.Executor) error { + var mods []qs.QueryMod + mods = append(mods, - boil.From("{{.table.Name}}"), - boil.Where("{{wherePrimaryKey .Table.Pkey.Columns 1}}", {{paramsPrimaryKey "o." .Table.PKey.Columns true}}), + qs.From("{{.Table.Name}}"), + qs.Where("{{wherePrimaryKey .Table.PKey.Columns 1}}", {{paramsPrimaryKey "o." .Table.PKey.Columns true}}), ) query := NewQueryX(exec, mods...) + boil.SetDelete(query) - _, err := exec.Exec("DELETE FROM {{.Table.Name}} WHERE {{wherePrimaryKey .Table.PKey.Columns 1}}", {{paramsPrimaryKey "o." .Table.PKey.Columns true}}) + _, err := boil.ExecQuery(query) if err != nil { return fmt.Errorf("{{.PkgName}}: unable to delete from {{.Table.Name}}: %s", err) } @@ -24,9 +27,37 @@ func (o *{{$tableNameSingular}}) DeleteX(exec boil.Executor, mods ...QueryMod) e } func (o {{$varNameSingular}}Query) DeleteAll() error { - _, err := db.Exec("DELETE FROM {{.Table.Name}} WHERE {{wherePrimaryKey .Table.PKey.Columns 1}}", {{paramsPrimaryKey "o." .Table.PKey.Columns true}}) + boil.SetDelete(o) + + _, err := boil.ExecQuery(query) if err != nil { - return fmt.Errorf("{{.PkgName}}: unable to delete from {{.Table.Name}}: %s", err) + return fmt.Errorf("{{.PkgName}}: unable to delete all from {{.Table.Name}}: %s", err) + } + + return nil +} + +func (o {{$varNameSingular}}Slice) DeleteAll() error { + return DeleteAllX(boil.GetDB()) +} + +func (o {{$varNameSingular}}Slice) DeleteAllX(exec boil.Executor) error { + var mods []qs.QueryMod + + args := o.inPrimaryKeyArgs() + in := boil.WherePrimaryKeyIn(len(o), {{primaryKeyStrList .Table.PKey.Columns}}) + + mods = append(mods, + qs.From("{{.Table.Name}}"), + qs.Where(in, args...), + ) + + query := NewQueryX(exec, mods...) + boil.SetDelete(query) + + _, err := boil.ExecQuery(query) + if err != nil { + return fmt.Errorf("{{.PkgName}}: unable to delete all from {{$varNameSingular}} slice: %s", err) } return nil diff --git a/cmds/templates/find.tpl b/cmds/templates/find.tpl index 52ae0cf..955c14b 100644 --- a/cmds/templates/find.tpl +++ b/cmds/templates/find.tpl @@ -21,7 +21,7 @@ func {{$tableNameSingular}}FindX(exec boil.Executor, id int64, selectList ...str return nil, errors.New("{{.PkgName}}: no id provided for {{.Table.Name}} select") } var {{$varNameSingular}} *{{$tableNameSingular}} - err := boil.GetDB().Select(&{{$varNameSingular}}, `SELECT {{selectParamNames $dbName .Table.Columns}} WHERE id=$1`, id) + //err := boil.GetDB().Select(&{{$varNameSingular}}, `SELECT {{selectParamNames $dbName .Table.Columns}} WHERE id=$1`, id) if err != nil { return nil, fmt.Errorf("{{.PkgName}}: unable to select from {{.Table.Name}}: %s", err) diff --git a/cmds/templates/helpers.tpl b/cmds/templates/helpers.tpl new file mode 100644 index 0000000..0ccb920 --- /dev/null +++ b/cmds/templates/helpers.tpl @@ -0,0 +1,15 @@ +{{if hasPrimaryKey .Table.PKey -}} +{{- $tableNameSingular := titleCaseSingular .Table.Name -}} +{{- $varNameSingular := camelCaseSingular .Table.Name -}} +func (o {{$varNameSingular}}Slice) inPrimaryKeyArgs() []interface{} { + var args []interface{} + + for i := 0; i < len(o); i++ { + {{- range $key, $value := .Table.PKey.Columns }} + args = append(args, o.{{titleCase $value}}) + {{ end -}} + } + + return args +} +{{- end}} diff --git a/cmds/templates/singles/helpers.tpl b/cmds/templates/singles/helpers.tpl index d8c1b8c..304b1ce 100644 --- a/cmds/templates/singles/helpers.tpl +++ b/cmds/templates/singles/helpers.tpl @@ -7,8 +7,9 @@ func NewQuery(mods ...qs.QueryMod) *boil.Query { } // NewQueryX initializes a new Query using the passed in QueryMods -func NewQueryX(executor boil.Executor, mods ...qs.QueryMod) *boil.Query { - q := &boil.Query{executor: executor} +func NewQueryX(exec boil.Executor, mods ...qs.QueryMod) *boil.Query { + q := &boil.Query{} + boil.SetExecutor(q, exec) qs.Apply(q, mods...) return q