Skip to content

Commit

Permalink
v3.0.1
Browse files Browse the repository at this point in the history
* Add literal bytes and update to c2fo testify
[#15](#15) -
[@TechnotronicOz](https://github.com/TechnotronicOz)
* Refactored escaping of text types to prevent duplication of logic
  • Loading branch information
doug-martin committed Aug 25, 2015
1 parent 47a9715 commit 95fc6b7
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 71 deletions.
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v3.0.1

* Add literal bytes and update to c2fo testify [#15](https://github.com/doug-martin/goqu/pull/15) - [@TechnotronicOz](https://github.com/TechnotronicOz)

## v3.0.0

* Added support for embedded structs when inserting or updating. [#13](https://github.com/doug-martin/goqu/pull/13) - [@andymoon](https://github.com/andymoon)
Expand Down
36 changes: 36 additions & 0 deletions adapters/mysql/dataset_adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,42 @@ func (me *datasetAdapterTest) TestLiteralString() {
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')")
}

func (me *datasetAdapterTest) TestLiteralBytes() {
t := me.T()
ds := me.GetDs("test")
sql, _, err := ds.Where(goqu.I("a").Eq([]byte("test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test'test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\'test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte(`test"test`))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\\"test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte(`test\test`))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\\\test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\ntest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\ntest')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\rtest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\rtest')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\x00test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x00test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\x1atest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')")
}

func (me *datasetAdapterTest) TestBooleanOperations() {
t := me.T()
ds := me.GetDs("test")
Expand Down
42 changes: 11 additions & 31 deletions adapters/mysql/mysql.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package mysql

import (
"gopkg.in/doug-martin/goqu.v3"
)
import "gopkg.in/doug-martin/goqu.v3"

var (
placeholder_rune = '?'
Expand Down Expand Up @@ -32,6 +30,15 @@ var (
goqu.REGEXP_I_LIKE_OP: []byte("REGEXP"),
goqu.REGEXP_NOT_I_LIKE_OP: []byte("NOT REGEXP"),
}
escape_runes = map[rune][]byte{
'\'': []byte("\\'"),
'"': []byte("\\\""),
'\\': []byte("\\\\"),
'\n': []byte("\\n"),
'\r': []byte("\\r"),
0: []byte("\\x00"),
0x1a: []byte("\\x1a"),
}
)

type DatasetAdapter struct {
Expand All @@ -58,34 +65,6 @@ func (me *DatasetAdapter) SupportsOrderByOnUpdate() bool {
return true
}

func (me *DatasetAdapter) LiteralString(buf *goqu.SqlBuilder, s string) error {
if buf.IsPrepared {
return me.PlaceHolderSql(buf, s)
}
buf.WriteRune(singlq_quote)
for _, char := range s {
if char == '\'' { // single quote: ' -> \'
buf.WriteString("\\'")
} else if char == '"' { // double quote: " -> \"
buf.WriteString("\\\"")
} else if char == '\\' { // slash: \ -> "\\"
buf.WriteString("\\\\")
} else if char == '\n' { // control: newline: \n -> "\n"
buf.WriteString("\\n")
} else if char == '\r' { // control: return: \r -> "\r"
buf.WriteString("\\r")
} else if char == 0 { // control: NUL: 0 -> "\x00"
buf.WriteString("\\x00")
} else if char == 0x1a { // control: \x1a -> "\x1a"
buf.WriteString("\\x1a")
} else {
buf.WriteRune(char)
}
}
buf.WriteRune(singlq_quote)
return nil
}

func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
def := goqu.NewDefaultAdapter(ds).(*goqu.DefaultAdapter)
def.PlaceHolderRune = placeholder_rune
Expand All @@ -96,6 +75,7 @@ func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
def.False = mysql_false
def.TimeFormat = time_format
def.BooleanOperatorLookup = operator_lookup
def.EscapedRunes = escape_runes
return &DatasetAdapter{def}
}

Expand Down
38 changes: 37 additions & 1 deletion adapters/sqlite3/dataset_adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"regexp"
"testing"

"github.com/c2fo/testify/suite"
"github.com/c2fo/testify/assert"
"github.com/c2fo/testify/suite"
"gopkg.in/doug-martin/goqu.v3"
)

Expand Down Expand Up @@ -107,6 +107,42 @@ func (me *datasetAdapterTest) TestLiteralString() {
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')")
}

func (me *datasetAdapterTest) TestLiteralBytes() {
t := me.T()
ds := me.GetDs("test")
sql, _, err := ds.Where(goqu.I("a").Eq([]byte("test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test'test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\'test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte(`test"test`))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\\"test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte(`test\test`))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\\\test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\ntest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\ntest')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\rtest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\rtest')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\x00test"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x00test')")

sql, _, err = ds.Where(goqu.I("a").Eq([]byte("test\x1atest"))).ToSql()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')")
}

func (me *datasetAdapterTest) TestBooleanOperations() {
t := me.T()
ds := me.GetDs("test")
Expand Down
42 changes: 11 additions & 31 deletions adapters/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package sqlite3

import (
"gopkg.in/doug-martin/goqu.v3"
)
import "gopkg.in/doug-martin/goqu.v3"

var (
placeholder_rune = '?'
Expand Down Expand Up @@ -32,6 +30,15 @@ var (
goqu.REGEXP_I_LIKE_OP: []byte("REGEXP"),
goqu.REGEXP_NOT_I_LIKE_OP: []byte("NOT REGEXP"),
}
escape_runes = map[rune][]byte{
'\'': []byte("\\'"),
'"': []byte("\\\""),
'\\': []byte("\\\\"),
'\n': []byte("\\n"),
'\r': []byte("\\r"),
0: []byte("\\x00"),
0x1a: []byte("\\x1a"),
}
)

type DatasetAdapter struct {
Expand All @@ -58,34 +65,6 @@ func (me *DatasetAdapter) SupportsOrderByOnUpdate() bool {
return true
}

func (me *DatasetAdapter) LiteralString(buf *goqu.SqlBuilder, s string) error {
if buf.IsPrepared {
return me.PlaceHolderSql(buf, s)
}
buf.WriteRune(singlq_quote)
for _, char := range s {
if char == '\'' { // single quote: ' -> \'
buf.WriteString("\\'")
} else if char == '"' { // double quote: " -> \"
buf.WriteString("\\\"")
} else if char == '\\' { // slash: \ -> "\\"
buf.WriteString("\\\\")
} else if char == '\n' { // control: newline: \n -> "\n"
buf.WriteString("\\n")
} else if char == '\r' { // control: return: \r -> "\r"
buf.WriteString("\\r")
} else if char == 0 { // control: NUL: 0 -> "\x00"
buf.WriteString("\\x00")
} else if char == 0x1a { // control: \x1a -> "\x1a"
buf.WriteString("\\x1a")
} else {
buf.WriteRune(char)
}
}
buf.WriteRune(singlq_quote)
return nil
}

func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
def := goqu.NewDefaultAdapter(ds).(*goqu.DefaultAdapter)
def.PlaceHolderRune = placeholder_rune
Expand All @@ -97,6 +76,7 @@ func newDatasetAdapter(ds *goqu.Dataset) goqu.Adapter {
def.TimeFormat = time_format
def.BooleanOperatorLookup = operator_lookup
def.UseLiteralIsBools = false
def.EscapedRunes = escape_runes
return &DatasetAdapter{def}
}

Expand Down
18 changes: 11 additions & 7 deletions default_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ var (
NATURAL_FULL_JOIN: []byte(" NATURAL FULL JOIN "),
CROSS_JOIN: []byte(" CROSS JOIN "),
}
default_escape_runes = map[rune][]byte{
'\'': []byte("''"),
}
)

type (
Expand Down Expand Up @@ -129,7 +132,7 @@ type (
WhereFragment []byte
//The SQL GROUP BY clause fragment(DEFAULT=[]byte(" GROUP BY "))
GroupByFragment []byte
//The SQL HAVING clause fragment(DEFAULT=[]byte(" HAVING "))
//The SQL HAVING clause fragment(DELiFAULT=[]byte(" HAVING "))
HavingFragment []byte
//The SQL ORDER BY clause fragment(DEFAULT=[]byte(" ORDER BY "))
OrderByFragment []byte
Expand Down Expand Up @@ -183,6 +186,8 @@ type (
JoinTypeLookup map[JoinType][]byte
//Whether or not to use literal TRUE or FALSE for IS statements (e.g. IS TRUE or IS 0)
UseLiteralIsBools bool
//EscapedRunes is a map of a rune and the corresponding escape sequence in bytes. Used when escaping text types.
EscapedRunes map[rune][]byte
}
)

Expand Down Expand Up @@ -231,6 +236,7 @@ func NewDefaultAdapter(ds *Dataset) Adapter {
JoinTypeLookup: default_join_lookup,
TimeFormat: time.RFC3339Nano,
UseLiteralIsBools: true,
EscapedRunes: default_escape_runes,
}
}

Expand Down Expand Up @@ -626,9 +632,8 @@ func (me *DefaultAdapter) LiteralString(buf *SqlBuilder, s string) error {
}
buf.WriteRune(me.StringQuote)
for _, char := range s {
if char == me.StringQuote { // single quote: ' -> \'
buf.WriteRune(me.StringQuote)
buf.WriteRune(me.StringQuote)
if e, ok := me.EscapedRunes[char]; ok {
buf.Write(e)
} else {
buf.WriteRune(char)
}
Expand All @@ -647,9 +652,8 @@ func (me *DefaultAdapter) LiteralBytes(buf *SqlBuilder, bs []byte) error {
i := 0
for len(bs) > 0 {
char, l := utf8.DecodeRune(bs)
if char == me.StringQuote { // single quote: ' -> \'
buf.WriteRune(me.StringQuote)
buf.WriteRune(me.StringQuote)
if e, ok := me.EscapedRunes[char]; ok {
buf.Write(e)
} else {
buf.WriteRune(char)
}
Expand Down
5 changes: 4 additions & 1 deletion expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,10 @@ func checkBoolExpType(op BooleanOperation, lhs Expression, rhs interface{}, inve
case reflect.Bool:
op = IS_OP
case reflect.Slice:
op = IN_OP
//if its a slice of bytes dont treat as an IN
if _, ok := rhs.([]byte); !ok {
op = IN_OP
}
case reflect.Struct:
switch rhs.(type) {
case SqlExpression:
Expand Down

0 comments on commit 95fc6b7

Please sign in to comment.