diff --git a/README.md b/README.md index 6ccf982..b8a6f73 100644 --- a/README.md +++ b/README.md @@ -128,14 +128,25 @@ This can be useful for changing sort direction or embedding table and column nam _Note: Since this is a raw value, special attention should be paid to ensure user-input is checked and sanitized._ +### Folded + +The `Folded` type and corresponding `ToFolded` generic function will prevent spreading of `[]string`, `[]int`, or `[]any`. For example, `bqb.New("?", []string{"a","b"})` will become `("?,?", "a", "b")` by default. + +```go +strns := []string{"a", "b"} +q := bqb.New("Folded: ? - Default: ?", bqb.ToFolded(strns), strns) +// sql = Folded: ? - Default: ?,? +// params = { []string{"a", "b"}, "a", "b" } +``` + ## Query IN -Arguments of type `[]string`,`[]*string`, `[]int`,`[]*int`, or `[]interface{}` are automatically expanded. +Arguments of type `[]string`,`[]*string`, `[]int`,`[]*int`, and `[]any` / `[]interface{}` are automatically expanded. ```golang q := bqb.New( "strs:(?) *strs:(?) ints:(?) *ints:(?) intfs:(?)", - []string{"a", "b"}, []*string{}, []int{1, 2}, []*int{}, []interface{}{3, true}, + []string{"a", "b"}, []*string{}, []int{1, 2}, []*int{}, []any{3, true}, ) sql, params, _ := q.ToSql() ``` @@ -197,7 +208,7 @@ SELECT id,age,email ### Advanced Example The `Optional(string)` function returns a query that resolves to an empty string if no query parts have -been added via methods on the query instance, and joins with a space to the next query part. +been added via methods on the query instance, and joins with a space to the next query part. For example `q := Optional("SELECT")` will resolve to an empty string unless parts have been added by one of the methods, e.g `q.Space("* FROM my_table")` would make `q.ToSql()` resolve to `SELECT * FROM my_table`. diff --git a/coverage.out b/coverage.out index ea8266c..9410535 100644 --- a/coverage.out +++ b/coverage.out @@ -50,6 +50,9 @@ github.com/nullism/bqb/query.go:176.2,176.28 1 1 github.com/nullism/bqb/query.go:176.28,180.23 3 1 github.com/nullism/bqb/query.go:180.23,182.4 1 1 github.com/nullism/bqb/query.go:185.2,185.44 1 1 +github.com/nullism/bqb/types.go:43.40,45.26 2 1 +github.com/nullism/bqb/types.go:45.26,47.3 1 1 +github.com/nullism/bqb/types.go:48.2,48.17 1 1 github.com/nullism/bqb/utils.go:11.80,18.17 2 1 github.com/nullism/bqb/utils.go:19.11,20.32 1 1 github.com/nullism/bqb/utils.go:20.32,22.18 2 1 @@ -97,29 +100,30 @@ github.com/nullism/bqb/utils.go:135.27,138.17 3 1 github.com/nullism/bqb/utils.go:138.17,140.4 1 1 github.com/nullism/bqb/utils.go:140.9,142.4 1 1 github.com/nullism/bqb/utils.go:144.16,145.50 1 1 -github.com/nullism/bqb/utils.go:147.10,149.31 2 1 -github.com/nullism/bqb/utils.go:152.2,152.28 1 1 -github.com/nullism/bqb/utils.go:155.64,157.20 2 1 -github.com/nullism/bqb/utils.go:157.20,159.3 1 1 -github.com/nullism/bqb/utils.go:161.2,162.28 2 1 -github.com/nullism/bqb/utils.go:162.28,164.3 1 1 -github.com/nullism/bqb/utils.go:165.2,165.12 1 1 -github.com/nullism/bqb/utils.go:168.51,176.27 6 1 -github.com/nullism/bqb/utils.go:176.27,178.23 2 1 -github.com/nullism/bqb/utils.go:178.23,180.4 1 1 -github.com/nullism/bqb/utils.go:181.3,182.51 2 1 -github.com/nullism/bqb/utils.go:185.2,185.70 1 1 -github.com/nullism/bqb/utils.go:185.70,187.3 1 1 -github.com/nullism/bqb/utils.go:189.2,195.3 2 1 -github.com/nullism/bqb/utils.go:198.44,199.27 1 1 -github.com/nullism/bqb/utils.go:200.12,201.35 1 1 -github.com/nullism/bqb/utils.go:203.33,204.35 1 1 -github.com/nullism/bqb/utils.go:205.12,206.15 1 1 -github.com/nullism/bqb/utils.go:206.15,208.4 1 1 -github.com/nullism/bqb/utils.go:209.3,209.36 1 1 -github.com/nullism/bqb/utils.go:210.14,211.37 1 1 -github.com/nullism/bqb/utils.go:212.15,213.15 1 1 -github.com/nullism/bqb/utils.go:213.15,215.4 1 1 -github.com/nullism/bqb/utils.go:216.3,216.38 1 1 -github.com/nullism/bqb/utils.go:217.11,218.21 1 1 -github.com/nullism/bqb/utils.go:219.10,220.65 1 1 +github.com/nullism/bqb/utils.go:147.23,149.31 2 1 +github.com/nullism/bqb/utils.go:151.10,153.31 2 1 +github.com/nullism/bqb/utils.go:156.2,156.28 1 1 +github.com/nullism/bqb/utils.go:159.64,161.20 2 1 +github.com/nullism/bqb/utils.go:161.20,163.3 1 1 +github.com/nullism/bqb/utils.go:165.2,166.28 2 1 +github.com/nullism/bqb/utils.go:166.28,168.3 1 1 +github.com/nullism/bqb/utils.go:169.2,169.12 1 1 +github.com/nullism/bqb/utils.go:172.51,180.27 6 1 +github.com/nullism/bqb/utils.go:180.27,182.23 2 1 +github.com/nullism/bqb/utils.go:182.23,184.4 1 1 +github.com/nullism/bqb/utils.go:185.3,186.51 2 1 +github.com/nullism/bqb/utils.go:189.2,189.70 1 1 +github.com/nullism/bqb/utils.go:189.70,191.3 1 1 +github.com/nullism/bqb/utils.go:193.2,199.3 2 1 +github.com/nullism/bqb/utils.go:202.44,203.27 1 1 +github.com/nullism/bqb/utils.go:204.12,205.35 1 1 +github.com/nullism/bqb/utils.go:207.33,208.35 1 1 +github.com/nullism/bqb/utils.go:209.12,210.15 1 1 +github.com/nullism/bqb/utils.go:210.15,212.4 1 1 +github.com/nullism/bqb/utils.go:213.3,213.36 1 1 +github.com/nullism/bqb/utils.go:214.14,215.37 1 1 +github.com/nullism/bqb/utils.go:216.15,217.15 1 1 +github.com/nullism/bqb/utils.go:217.15,219.4 1 1 +github.com/nullism/bqb/utils.go:220.3,220.38 1 1 +github.com/nullism/bqb/utils.go:221.11,222.21 1 1 +github.com/nullism/bqb/utils.go:223.10,224.65 1 1 diff --git a/query_test.go b/query_test.go index fd71948..4daf717 100644 --- a/query_test.go +++ b/query_test.go @@ -9,18 +9,43 @@ import ( "time" ) -func TestA(t *testing.T) { +func TestFolded(t *testing.T) { + valArr := ToFolded([]string{"a", "b", "c"}) + wantValArr := Folded{"a", "b", "c"} + + if valArr[0] != wantValArr[0] { + t.Errorf("got: %v, want: %v", valArr[0], wantValArr[0]) + } + + jsArr := JsonList{"a", "b", "c"} + fpArr := &Folded{"a", "b", "c"} + + q := New("(?) (?) (?) (?)", valArr, jsArr, []string{"a", "b", "c"}, fpArr) + sql, params, err := q.ToSql() + if err != nil { + t.Errorf("got error: %v", err) + } + + if len(params) != 6 { + t.Errorf("invalid params") + } + + want := "(?) (?) (?,?,?) (?)" + if sql != want { + t.Errorf("got: %q, want: %q", sql, want) + } } func TestArrays(t *testing.T) { - q := New("(?) (?) (?) (?) (?)", []string{"a", "b"}, []string{}, []*string{}, []int{1, 2}, []*int{}) + // float64 does not expand automatically + q := New("(?) (?) (?) (?) (?) (?) (?)", []string{"a", "b"}, []string{}, []*string{}, []int{1, 2}, []*int{}, []any{1.2, 1.3, 1.4}, []float64{1.1, 1.2}) sql, params, _ := q.ToSql() - if len(params) != 6 { + if len(params) != 10 { t.Errorf("invalid params") } - want := "(?,?) () (?) (?,?) (?)" + want := "(?,?) () (?) (?,?) (?) (?,?,?) (?)" if sql != want { t.Errorf("got: %q, want: %q", sql, want) } diff --git a/types.go b/types.go index 0f26a4e..c031d72 100644 --- a/types.go +++ b/types.go @@ -29,8 +29,21 @@ type Embedder interface { // JsonMap is a custom type which tells bqb to convert the parameter to // a JSON object without requiring reflection. -type JsonMap map[string]interface{} +type JsonMap map[string]any // JsonList is a type that tells bqb to convert the parameter to a JSON // list without requiring reflection. -type JsonList []interface{} +type JsonList []any + +// Folded is a type that tells bqb to NOT spread the list into individual +// parameters. +type Folded []any + +// ToFolded converts a slice to a ValueArray. +func ToFolded[T any](slice []T) Folded { + valueArr := make(Folded, len(slice)) + for i, v := range slice { + valueArr[i] = v + } + return valueArr +} diff --git a/utils.go b/utils.go index d9279b3..c5ba076 100644 --- a/utils.go +++ b/utils.go @@ -144,6 +144,10 @@ func convertArg(text string, arg any) (string, []any, []error) { case Embedded: text = strings.Replace(text, "?", string(v), 1) + case Folded, *Folded: + text = strings.Replace(text, "?", paramPh, 1) + newArgs = append(newArgs, v) + default: text = strings.Replace(text, "?", paramPh, 1) newArgs = append(newArgs, v)