good morning!!!!

Skip to content
Snippets Groups Projects
Commit 15fcc3a7 authored by José Nieto's avatar José Nieto
Browse files

optimize expandQuery and ReplaceWithDollarSign

parent 5ac579c8
No related branches found
No related tags found
No related merge requests found
......@@ -99,7 +99,7 @@ func (*database) CompileStatement(sess sqladapter.Session, stmt *exql.Statement,
}
query, args := sqlbuilder.Preprocess(compiled, args)
query = sqladapter.ReplaceWithDollarSign(query)
query = string(sqladapter.ReplaceWithDollarSign([]byte(query)))
return query, args, nil
}
......
......@@ -76,10 +76,10 @@ func BenchmarkJoinColumnValues(b *testing.B) {
func BenchmarkColumnValuesHash(b *testing.B) {
cvs := JoinColumnValues(
&ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(&Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(&Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(&Raw{Value: "NOW()"})},
)
b.ResetTimer()
for i := 0; i < b.N; i++ {
......@@ -90,10 +90,10 @@ func BenchmarkColumnValuesHash(b *testing.B) {
func BenchmarkColumnValuesCompile(b *testing.B) {
cvs := JoinColumnValues(
&ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(&Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(&Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(&Raw{Value: "NOW()"})},
)
b.ResetTimer()
for i := 0; i < b.N; i++ {
......@@ -105,10 +105,10 @@ func BenchmarkColumnValuesCompileNoCache(b *testing.B) {
for i := 0; i < b.N; i++ {
cvs := JoinColumnValues(
&ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(&Raw{Value: "100"})},
&ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(&Raw{Value: "NOW()"})},
&ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(&Raw{Value: "NOW()"})},
)
_, _ = cvs.Compile(defaultTemplate)
}
......
package sqladapter
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"log"
"math"
"reflect"
"strconv"
......@@ -657,7 +657,6 @@ func (sess *sessionWithContext) Collection(name string) db.Collection {
h := cache.NewHashable(hashTypeCollection, name)
cachedCol, ok := sess.cachedCollections.ReadRaw(h)
log.Printf("CACHED: %v, ok: %v", cachedCol, ok)
if ok {
return cachedCol.(db.Collection)
}
......@@ -1004,29 +1003,37 @@ func (sess *sessionWithContext) WaitForConnection(connectFn func() error) error
// ReplaceWithDollarSign turns a SQL statament with '?' placeholders into
// dollar placeholders, like $1, $2, ..., $n
func ReplaceWithDollarSign(in string) string {
buf := []byte(in)
out := make([]byte, 0, len(buf))
i, j, k, t := 0, 1, 0, len(buf)
for i < t {
func ReplaceWithDollarSign(buf []byte) []byte {
z := bytes.Count(buf, []byte{'?'})
// the capacity is a quick estimation of the total memory required, this
// reduces reallocations
out := make([]byte, 0, len(buf)+z*3)
var i, k = 0, 1
for i < len(buf) {
if buf[i] == '?' {
out = append(out, buf[k:i]...)
k = i + 1
out = append(out, buf[:i]...)
buf = buf[i+1:]
i = 0
if k < t && buf[k] == '?' {
i = k
} else {
out = append(out, []byte("$"+strconv.Itoa(j))...)
j++
if len(buf) > 0 && buf[0] == '?' {
out = append(out, '?')
buf = buf[1:]
continue
}
out = append(out, '$')
out = append(out, []byte(strconv.Itoa(k))...)
k = k + 1
continue
}
i++
i = i + 1
}
out = append(out, buf[k:i]...)
return string(out)
out = append(out, buf[:len(buf)]...)
buf = nil
return out
}
func copySettings(from Session, into Session) {
......
package sqlbuilder
import (
"bytes"
"database/sql/driver"
"reflect"
"strings"
"github.com/upper/db/v4/internal/adapter"
"github.com/upper/db/v4/internal/sqladapter/exql"
......@@ -13,31 +13,75 @@ var (
sqlDefault = &exql.Raw{Value: "DEFAULT"}
)
func expandQuery(in string, args []interface{}, fn func(interface{}) (string, []interface{})) (string, []interface{}) {
argn := 0
argx := make([]interface{}, 0, len(args))
for i := 0; i < len(in); i++ {
if in[i] != '?' {
func expandQuery(in []byte, inArgs []interface{}) ([]byte, []interface{}) {
out := make([]byte, 0, len(in))
outArgs := make([]interface{}, 0, len(inArgs))
i := 0
for i < len(in) && len(inArgs) > 0 {
if in[i] == '?' {
out = append(out, in[:i]...)
in = in[i+1:]
i = 0
replace, replaceArgs := expandArgument(inArgs[0])
inArgs = inArgs[1:]
if len(replace) > 0 {
replace, replaceArgs = expandQuery(replace, replaceArgs)
out = append(out, replace...)
} else {
out = append(out, '?')
}
outArgs = append(outArgs, replaceArgs...)
continue
}
if len(args) > argn {
k, values := fn(args[argn])
k, values = expandQuery(k, values, fn)
i = i + 1
}
if len(out) < 1 {
return in, inArgs
}
out = append(out, in[:len(in)]...)
in = nil
outArgs = append(outArgs, inArgs[:len(inArgs)]...)
inArgs = nil
return out, outArgs
}
if k != "" {
in = in[:i] + k + in[i+1:]
i += len(k) - 1
func expandArgument(arg interface{}) ([]byte, []interface{}) {
values, isSlice := toInterfaceArguments(arg)
if isSlice {
if len(values) == 0 {
return []byte("(NULL)"), nil
}
if len(values) > 0 {
argx = append(argx, values...)
buf := bytes.Repeat([]byte(" ?,"), len(values))
buf[0] = '('
buf[len(buf)-1] = ')'
return buf, values
}
argn++
if len(values) == 1 {
switch t := arg.(type) {
case *adapter.RawExpr:
return expandQuery([]byte(t.Raw()), t.Arguments())
case compilable:
s, err := t.Compile()
if err == nil {
return append([]byte{'('}, append([]byte(s), ')')...), t.Arguments()
}
panic(err.Error())
}
if len(argx) < len(args) {
argx = append(argx, args[argn:]...)
} else if len(values) == 0 {
return []byte("NULL"), nil
}
return in, argx
return nil, []interface{}{arg}
}
// toInterfaceArguments converts the given value into an array of interfaces.
......@@ -145,5 +189,6 @@ func preprocessFn(arg interface{}) (string, []interface{}) {
// Preprocess expands arguments that needs to be expanded and compiles a query
// into a single string.
func Preprocess(in string, args []interface{}) (string, []interface{}) {
return expandQuery(in, args, preprocessFn)
b, args := expandQuery([]byte(in), args)
return string(b), args
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment