Improve IN and NOT IN queries #76

Merged
lunny merged 10 commits from kolaente/builder:feature/in-performance into master 2022-07-07 00:18:02 +00:00
3 changed files with 335 additions and 100 deletions

View File

@ -38,133 +38,199 @@ func (condIn condIn) WriteTo(w Writer) error {
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int8]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int16:
vals := condIn.vals[0].([]int16)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int16]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int:
vals := condIn.vals[0].([]int)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int32:
vals := condIn.vals[0].([]int32)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int32]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int64:
vals := condIn.vals[0].([]int64)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int64]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint8:
vals := condIn.vals[0].([]uint8)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint8]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint16:
vals := condIn.vals[0].([]uint16)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint16]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint:
vals := condIn.vals[0].([]uint)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint32:
vals := condIn.vals[0].([]uint32)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint32]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint64:
vals := condIn.vals[0].([]uint64)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint64]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []string:
vals := condIn.vals[0].([]string)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[string]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []interface{}:
vals := condIn.vals[0].([]interface{})
if len(vals) <= 0 {
@ -205,15 +271,38 @@ func (condIn condIn) WriteTo(w Writer) error {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", l)
trackMap := make(map[interface{}]bool, l)
for i := 0; i < l; i++ {
val := v.Index(i).Interface()
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for i := 0; i < l; i++ {
w.Append(v.Index(i).Interface())
}
} else {
// Using a map for better efficiency
trackMap := make(map[interface{}]bool, len(condIn.vals))
i := 0
for in, val := range condIn.vals {
if _, exists := trackMap[val]; exists {
// This sets empty values to nil, they get sliced off later.
condIn.vals[in] = nil
continue
}
trackMap[val] = true
condIn.vals[i] = val
i++
}
// Here we slice the slice to only contain those values we defined as correct.
condIn.vals = condIn.vals[:i]
questionMark := strings.Repeat("?,", len(condIn.vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err

View File

@ -35,133 +35,199 @@ func (condNotIn condNotIn) WriteTo(w Writer) error {
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int8]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int16:
vals := condNotIn.vals[0].([]int16)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int16]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int:
vals := condNotIn.vals[0].([]int)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int32:
vals := condNotIn.vals[0].([]int32)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int32]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int64:
vals := condNotIn.vals[0].([]int64)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[int64]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint8:
vals := condNotIn.vals[0].([]uint8)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint8]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint16:
vals := condNotIn.vals[0].([]uint16)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint16]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint:
vals := condNotIn.vals[0].([]uint)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint32:
vals := condNotIn.vals[0].([]uint32)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint32]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint64:
vals := condNotIn.vals[0].([]uint64)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[uint64]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []string:
vals := condNotIn.vals[0].([]string)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
// We're using this map to track if a parameter was already added to the condition to not add the same multiple times.
trackMap := make(map[string]bool, len(vals))
for _, val := range vals {
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []interface{}:
vals := condNotIn.vals[0].([]interface{})
if len(vals) <= 0 {
@ -202,15 +268,38 @@ func (condNotIn condNotIn) WriteTo(w Writer) error {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", l)
trackMap := make(map[interface{}]bool, l)
for i := 0; i < l; i++ {
val := v.Index(i).Interface()
if _, exists := trackMap[val]; exists {
continue
}
w.Append(val)
trackMap[val] = true
}
questionMark := strings.Repeat("?,", len(trackMap))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for i := 0; i < l; i++ {
w.Append(v.Index(i).Interface())
}
} else {
// Using a map for better efficiency
trackMap := make(map[interface{}]bool, len(condNotIn.vals))
i := 0
for in, val := range condNotIn.vals {
if _, exists := trackMap[val]; exists {
// This sets empty values to nil, they get sliced off later.
condNotIn.vals[in] = nil
continue
}
trackMap[val] = true
condNotIn.vals[i] = val
i++
}
// Here we slice the slice to only contain those values we defined as correct.
condNotIn.vals = condNotIn.vals[:i]
questionMark := strings.Repeat("?,", len(condNotIn.vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err

View File

@ -4,8 +4,65 @@
package builder
import "testing"
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestCond_NotIn(t *testing.T) {
t.Run("normal", func(t *testing.T) {
cond := NotIn("test", 1, 2, 2, 3, 4, 4, 4)
buf := NewWriter()
err := cond.WriteTo(buf)
assert.NoError(t, err)
assert.Equal(t, "test NOT IN (?,?,?,?)", buf.String())
assert.Len(t, buf.args, 4)
assert.Equal(t, []interface{}{1, 2, 3, 4}, buf.args)
})
t.Run("slice", func(t *testing.T) {
cond := NotIn("test", []int{1, 2, 2, 3, 4, 4, 4})
buf := NewWriter()
err := cond.WriteTo(buf)
assert.NoError(t, err)
assert.Equal(t, "test NOT IN (?,?,?,?)", buf.String())
assert.Len(t, buf.args, 4)
assert.Equal(t, []interface{}{1, 2, 3, 4}, buf.args)
})
t.Run("blank", func(t *testing.T) {
cond := NotIn("test")
buf := NewWriter()
err := cond.WriteTo(buf)
// The "error" is written to the bytes buffer
assert.NoError(t, err)
assert.Equal(t, "0=0", buf.String())
})
}
func TestCond_In(t *testing.T) {
t.Run("normal", func(t *testing.T) {
cond := In("test", 1, 2, 2, 3, 4, 4, 4)
buf := NewWriter()
err := cond.WriteTo(buf)
assert.NoError(t, err)
assert.Equal(t, "test IN (?,?,?,?)", buf.String())
assert.Len(t, buf.args, 4)
assert.Equal(t, []interface{}{1, 2, 3, 4}, buf.args)
})
t.Run("slice", func(t *testing.T) {
cond := In("test", []int{1, 2, 2, 3, 4, 4, 4})
buf := NewWriter()
err := cond.WriteTo(buf)
assert.NoError(t, err)
assert.Equal(t, "test IN (?,?,?,?)", buf.String())
assert.Len(t, buf.args, 4)
assert.Equal(t, []interface{}{1, 2, 3, 4}, buf.args)
})
t.Run("blank", func(t *testing.T) {
cond := In("test")
buf := NewWriter()
err := cond.WriteTo(buf)
// The "error" is written to the bytes buffer
assert.NoError(t, err)
assert.Equal(t, "0=1", buf.String())
})
}