Skip to content

Commit

Permalink
Add embedder (#17)
Browse files Browse the repository at this point in the history
* Add embedder

* Update coverage.svg and coverage.out

* Update Embedder description

* Move types over

* Update coverage.svg and coverage.out

* Add README

---------

Co-authored-by: nullism <nullism@users.noreply.github.com>
  • Loading branch information
nullism and nullism authored Jun 26, 2023
1 parent f23a817 commit 6b8f601
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 78 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ themselves to a sql driver value. See [examples/main.go:valuer](./examples/main.
q := bqb.New("?", valuer)
```

### Embedder

BQB provides an Embedder interface for directly replacing `?` with a string returned by the `RawValue` method on the Embedder implementation.

This can be useful for changing sort direction or embedding table and column names. See [examples/main.go:embedder](./examples/main.go#L122) for an example.

_Note: Since this is a raw value, special attention should be paid to ensure user-input is checked and sanitized._

## Query IN

Arguments of type `[]string`,`[]*string`, `[]int`,`[]*int`, or `[]interface{}` are automatically expanded.
Expand Down
107 changes: 54 additions & 53 deletions coverage.out
Original file line number Diff line number Diff line change
Expand Up @@ -47,64 +47,65 @@ github.com/nullism/bqb/query.go:168.2,168.28 1 1
github.com/nullism/bqb/query.go:168.28,172.23 3 1
github.com/nullism/bqb/query.go:172.23,174.4 1 1
github.com/nullism/bqb/query.go:177.2,177.44 1 1
github.com/nullism/bqb/utils.go:34.88,35.40 1 1
github.com/nullism/bqb/utils.go:35.40,37.3 1 1
github.com/nullism/bqb/utils.go:38.2,38.31 1 1
github.com/nullism/bqb/utils.go:38.31,39.21 1 1
github.com/nullism/bqb/utils.go:39.21,41.18 2 1
github.com/nullism/bqb/utils.go:41.18,43.5 1 1
github.com/nullism/bqb/utils.go:44.4,44.45 1 1
github.com/nullism/bqb/utils.go:45.9,45.30 1 1
github.com/nullism/bqb/utils.go:45.30,48.4 2 1
github.com/nullism/bqb/utils.go:50.2,50.17 1 1
github.com/nullism/bqb/utils.go:53.59,61.27 6 1
github.com/nullism/bqb/utils.go:61.27,62.26 1 1
github.com/nullism/bqb/utils.go:64.22,67.18 3 1
github.com/nullism/bqb/utils.go:67.18,69.5 1 1
github.com/nullism/bqb/utils.go:69.10,71.5 1 1
github.com/nullism/bqb/utils.go:72.14,74.24 2 1
github.com/nullism/bqb/utils.go:10.88,11.40 1 1
github.com/nullism/bqb/utils.go:11.40,13.3 1 1
github.com/nullism/bqb/utils.go:14.2,14.31 1 1
github.com/nullism/bqb/utils.go:14.31,15.21 1 1
github.com/nullism/bqb/utils.go:15.21,17.18 2 1
github.com/nullism/bqb/utils.go:17.18,19.5 1 1
github.com/nullism/bqb/utils.go:20.4,20.45 1 1
github.com/nullism/bqb/utils.go:21.9,21.30 1 1
github.com/nullism/bqb/utils.go:21.30,24.4 2 1
github.com/nullism/bqb/utils.go:26.2,26.17 1 1
github.com/nullism/bqb/utils.go:29.59,37.27 6 1
github.com/nullism/bqb/utils.go:37.27,38.26 1 1
github.com/nullism/bqb/utils.go:40.17,41.54 1 1
github.com/nullism/bqb/utils.go:43.22,46.18 3 1
github.com/nullism/bqb/utils.go:46.18,48.5 1 1
github.com/nullism/bqb/utils.go:48.10,50.5 1 1
github.com/nullism/bqb/utils.go:51.14,53.24 2 1
github.com/nullism/bqb/utils.go:53.24,56.5 2 1
github.com/nullism/bqb/utils.go:57.4,57.66 1 1
github.com/nullism/bqb/utils.go:59.15,61.24 2 1
github.com/nullism/bqb/utils.go:61.24,64.5 2 1
github.com/nullism/bqb/utils.go:65.4,65.22 1 1
github.com/nullism/bqb/utils.go:65.22,67.5 1 1
github.com/nullism/bqb/utils.go:67.10,70.5 2 1
github.com/nullism/bqb/utils.go:72.17,74.24 2 1
github.com/nullism/bqb/utils.go:74.24,77.5 2 1
github.com/nullism/bqb/utils.go:78.4,78.66 1 1
github.com/nullism/bqb/utils.go:80.15,82.24 2 1
github.com/nullism/bqb/utils.go:80.18,82.24 2 1
github.com/nullism/bqb/utils.go:82.24,85.5 2 1
github.com/nullism/bqb/utils.go:86.4,86.22 1 1
github.com/nullism/bqb/utils.go:86.22,88.5 1 1
github.com/nullism/bqb/utils.go:88.10,91.5 2 1
github.com/nullism/bqb/utils.go:93.17,95.24 2 1
github.com/nullism/bqb/utils.go:93.22,95.24 2 1
github.com/nullism/bqb/utils.go:95.24,98.5 2 1
github.com/nullism/bqb/utils.go:99.4,99.66 1 1
github.com/nullism/bqb/utils.go:101.18,103.24 2 1
github.com/nullism/bqb/utils.go:103.24,106.5 2 1
github.com/nullism/bqb/utils.go:107.4,107.22 1 1
github.com/nullism/bqb/utils.go:107.22,109.5 1 1
github.com/nullism/bqb/utils.go:109.10,112.5 2 1
github.com/nullism/bqb/utils.go:114.22,116.24 2 1
github.com/nullism/bqb/utils.go:116.24,119.5 2 1
github.com/nullism/bqb/utils.go:120.4,120.66 1 1
github.com/nullism/bqb/utils.go:122.15,123.16 1 1
github.com/nullism/bqb/utils.go:123.16,126.13 3 1
github.com/nullism/bqb/utils.go:128.4,130.40 3 1
github.com/nullism/bqb/utils.go:132.26,134.18 2 1
github.com/nullism/bqb/utils.go:134.18,135.55 1 1
github.com/nullism/bqb/utils.go:137.4,138.44 2 1
github.com/nullism/bqb/utils.go:140.28,142.18 2 1
github.com/nullism/bqb/utils.go:142.18,143.55 1 1
github.com/nullism/bqb/utils.go:145.4,146.44 2 1
github.com/nullism/bqb/utils.go:148.11,150.32 2 1
github.com/nullism/bqb/utils.go:153.2,154.20 2 1
github.com/nullism/bqb/utils.go:154.20,155.82 1 1
github.com/nullism/bqb/utils.go:158.2,159.31 2 1
github.com/nullism/bqb/utils.go:159.31,160.84 1 1
github.com/nullism/bqb/utils.go:163.2,169.3 2 1
github.com/nullism/bqb/utils.go:172.52,173.27 1 1
github.com/nullism/bqb/utils.go:174.12,175.35 1 1
github.com/nullism/bqb/utils.go:177.33,178.35 1 1
github.com/nullism/bqb/utils.go:179.12,180.15 1 1
github.com/nullism/bqb/utils.go:180.15,182.4 1 1
github.com/nullism/bqb/utils.go:183.3,183.36 1 1
github.com/nullism/bqb/utils.go:184.14,185.37 1 1
github.com/nullism/bqb/utils.go:186.15,187.15 1 1
github.com/nullism/bqb/utils.go:187.15,189.4 1 1
github.com/nullism/bqb/utils.go:190.3,190.38 1 1
github.com/nullism/bqb/utils.go:191.11,192.21 1 1
github.com/nullism/bqb/utils.go:193.10,194.65 1 1
github.com/nullism/bqb/utils.go:101.15,102.16 1 1
github.com/nullism/bqb/utils.go:102.16,105.13 3 1
github.com/nullism/bqb/utils.go:107.4,109.40 3 1
github.com/nullism/bqb/utils.go:111.26,113.18 2 1
github.com/nullism/bqb/utils.go:113.18,114.55 1 1
github.com/nullism/bqb/utils.go:116.4,117.44 2 1
github.com/nullism/bqb/utils.go:119.28,121.18 2 1
github.com/nullism/bqb/utils.go:121.18,122.55 1 1
github.com/nullism/bqb/utils.go:124.4,125.44 2 1
github.com/nullism/bqb/utils.go:127.11,129.32 2 1
github.com/nullism/bqb/utils.go:132.2,133.20 2 1
github.com/nullism/bqb/utils.go:133.20,134.82 1 1
github.com/nullism/bqb/utils.go:137.2,138.31 2 1
github.com/nullism/bqb/utils.go:138.31,139.84 1 1
github.com/nullism/bqb/utils.go:142.2,148.3 2 1
github.com/nullism/bqb/utils.go:151.52,152.27 1 1
github.com/nullism/bqb/utils.go:153.12,154.35 1 1
github.com/nullism/bqb/utils.go:156.33,157.35 1 1
github.com/nullism/bqb/utils.go:158.12,159.15 1 1
github.com/nullism/bqb/utils.go:159.15,161.4 1 1
github.com/nullism/bqb/utils.go:162.3,162.36 1 1
github.com/nullism/bqb/utils.go:163.14,164.37 1 1
github.com/nullism/bqb/utils.go:165.15,166.15 1 1
github.com/nullism/bqb/utils.go:166.15,168.4 1 1
github.com/nullism/bqb/utils.go:169.3,169.38 1 1
github.com/nullism/bqb/utils.go:170.11,171.21 1 1
github.com/nullism/bqb/utils.go:172.10,173.65 1 1
21 changes: 20 additions & 1 deletion examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"database/sql/driver"
"fmt"
"strings"

"github.com/nullism/bqb"
)
Expand Down Expand Up @@ -100,18 +101,36 @@ func (v valuerExample) Value() (driver.Value, error) {
}

func valuer() {

println("===[ driver.Valuer ]===")
example := &valuerExample{Name: "Bobby Tables", Age: 12}
q := bqb.New("info text = ?", example)
q.Print()
}

type embedderExample []string

func (e embedderExample) RawValue() string {
parts := []string{}
for _, v := range e {
part := fmt.Sprintf(`"%v"`, strings.ReplaceAll(v, `"`, `""`))
parts = append(parts, part)
}
return strings.Join(parts, ".")
}

func embedder() {
println("===[ Embedder ]===")
emb := embedderExample{"one", "two", `"three"`}
q := bqb.New("embedded: ?, unembedded: ?", emb, "bound")
q.Print()
}

func main() {
customTypes()
basic()
builder()
json()
nilQuery()
valuer()
embedder()
}
32 changes: 32 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package bqb

// Dialect holds the Query dialect
type Dialect string

const (
// PGSQL postgres dialect
PGSQL Dialect = "postgres"
// MYSQL MySQL dialect
MYSQL Dialect = "mysql"
// RAW dialect uses no parameter conversion
RAW Dialect = "raw"
// SQL generic dialect
SQL Dialect = "sql"

paramPh = "{{xX_PARAM_Xx}}"
)

// Embedder embeds a value directly into a query string.
// Note: Since this is embedded and not bound,
// attention must be paid to sanitizing this input.
type Embedder interface {
RawValue() string
}

// JsonMap is a custom type which tells bqb to convert the parameter to
// a JSON object without requiring reflection.
type JsonMap map[string]interface{}

// JsonList is a type that tells bqb to convert the parameter to a JSON
// list without requiring reflection.
type JsonList []interface{}
64 changes: 64 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package bqb

import (
"reflect"
"strings"
"testing"
)

type embedder []string

func (e embedder) RawValue() string {
return strings.Join(e, ".")
}

type sortEmbedder string

const (
down sortEmbedder = "down"
up sortEmbedder = "up"
)

func (s sortEmbedder) RawValue() string {
if s == down {
return "DESC"
}
if s == up {
return "ASC"
}
panic("invalid sort direction: " + s)
}

func TestEmbedder(t *testing.T) {
emb := embedder{"one", "two", "three"}
want := "one.two.three"

if emb.RawValue() != want {
t.Errorf("Embedder error: want=%v got=%v", want, emb.RawValue())
}

q := New("SELECT ? FROM ? WHERE ?=?", embedder{"id"}, embedder{"schema", "table"}, embedder{"name"}, "bound")
sql, args, err := q.ToSql()

if err != nil {
t.Errorf("got error: %v", err)
}

want = "SELECT id FROM schema.table WHERE name=?"
if want != sql {
t.Errorf("\n got:%v\nwant:%v", sql, want)
}

wantArgs := []any{"bound"}
if !reflect.DeepEqual(args, wantArgs) {
t.Errorf("\n got:%v\nwant:%v", args, wantArgs)
}

sortq := New("SELECT * FROM my_table ORDER BY name ?,?", down, up)
want = "SELECT * FROM my_table ORDER BY name DESC,ASC"
got, _, _ := sortq.ToSql()
if got != want {
t.Errorf("\n got:%v\nwant:%v", got, want)
}

}
27 changes: 3 additions & 24 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,6 @@ import (
"strings"
)

// Dialect holds the Query dialect
type Dialect string

const (
// PGSQL postgres dialect
PGSQL Dialect = "postgres"
// MYSQL MySQL dialect
MYSQL Dialect = "mysql"
// RAW dialect uses no parameter conversion
RAW Dialect = "raw"
// SQL generic dialect
SQL Dialect = "sql"

paramPh = "{{xX_PARAM_Xx}}"
)

// JsonMap is a custom type which tells bqb to convert the parameter to
// a JSON object without requiring reflection.
type JsonMap map[string]interface{}

// JsonList is a type that tells bqb to convert the parameter to a JSON
// list without requiring reflection.
type JsonList []interface{}

func dialectReplace(dialect Dialect, sql string, params []interface{}) (string, error) {
if dialect == MYSQL || dialect == SQL {
sql = strings.ReplaceAll(sql, paramPh, "?")
Expand Down Expand Up @@ -61,6 +37,9 @@ func makePart(text string, args ...interface{}) QueryPart {
for _, arg := range args {
switch v := arg.(type) {

case Embedder:
text = strings.Replace(text, "?", v.RawValue(), 1)

case driver.Valuer:
text = strings.Replace(text, "?", paramPh, 1)
val, err := v.Value()
Expand Down

0 comments on commit 6b8f601

Please sign in to comment.