From 471ba7e4dc837a15996fc3281df01af26f8f79a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Nieto?= Date: Sat, 12 Feb 2022 08:22:04 -0600 Subject: [PATCH] optimize prepareQueryForDisplay --- internal/sqlbuilder/builder.go | 55 ++++++++++++++------- internal/sqlbuilder/builder_test.go | 5 ++ internal/sqlbuilder/placeholder_test.go | 63 +++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/internal/sqlbuilder/builder.go b/internal/sqlbuilder/builder.go index 444c13da..da7ecb3c 100644 --- a/internal/sqlbuilder/builder.go +++ b/internal/sqlbuilder/builder.go @@ -29,7 +29,6 @@ import ( "fmt" "log" "reflect" - "regexp" "sort" "strconv" "strings" @@ -72,10 +71,6 @@ type fieldValue struct { values []interface{} } -var ( - reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) -) - var ( sqlPlaceholder = exql.RawValue(`?`) ) @@ -347,11 +342,10 @@ func Map(item interface{}, options *MapOptions) ([]string, []interface{}, error) } func columnFragments(columns []interface{}) ([]exql.Fragment, []interface{}, error) { - l := len(columns) - f := make([]exql.Fragment, l) + f := make([]exql.Fragment, len(columns)) args := []interface{}{} - for i := 0; i < l; i++ { + for i := range columns { switch v := columns[i].(type) { case compilable: c, err := v.Compile() @@ -393,19 +387,44 @@ func columnFragments(columns []interface{}) ([]exql.Fragment, []interface{}, err return f, args, nil } -func prepareQueryForDisplay(in string) (out string) { - j := 1 - for i := range in { +func prepareQueryForDisplay(in string) string { + out := make([]byte, 0, len(in)) + + offset := 0 + whitespace := true + placeholders := 1 + + for i := 0; i < len(in); i++ { + if in[i] == ' ' || in[i] == '\r' || in[i] == '\n' || in[i] == '\t' { + if whitespace { + offset = i + } else { + whitespace = true + out = append(out, in[offset:i]...) + offset = i + } + continue + } + if whitespace { + whitespace = false + if len(out) > 0 { + out = append(out, ' ') + } + offset = i + } if in[i] == '?' { - out = out + "$" + strconv.Itoa(j) - j++ - } else { - out = out + string(in[i]) + out = append(out, in[offset:i]...) + offset = i + 1 + + out = append(out, '$') + out = append(out, strconv.Itoa(placeholders)...) + placeholders++ } } - - out = reInvisibleChars.ReplaceAllString(out, ` `) - return strings.TrimSpace(out) + if !whitespace { + out = append(out, in[offset:len(in)]...) + } + return string(out) } func (iter *iterator) NextScan(dst ...interface{}) error { diff --git a/internal/sqlbuilder/builder_test.go b/internal/sqlbuilder/builder_test.go index 1425922f..971998e8 100644 --- a/internal/sqlbuilder/builder_test.go +++ b/internal/sqlbuilder/builder_test.go @@ -2,6 +2,7 @@ package sqlbuilder import ( "fmt" + "regexp" "strings" "testing" @@ -9,6 +10,10 @@ import ( db "github.com/upper/db/v4" ) +var ( + reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) +) + func TestSelect(t *testing.T) { b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)} diff --git a/internal/sqlbuilder/placeholder_test.go b/internal/sqlbuilder/placeholder_test.go index 6430ea9c..93317f5b 100644 --- a/internal/sqlbuilder/placeholder_test.go +++ b/internal/sqlbuilder/placeholder_test.go @@ -7,6 +7,69 @@ import ( db "github.com/upper/db/v4" ) +func TestPrepareForDisplay(t *testing.T) { + samples := []struct { + In string + Out string + }{ + { + In: "12345", + Out: "12345", + }, + { + In: "\r\n\t12345", + Out: "12345", + }, + { + In: "12345\r\n\t", + Out: "12345", + }, + { + In: "\r\n\t1\r2\n3\t4\r5\r\n\t", + Out: "1 2 3 4 5", + }, + { + In: "\r\n \t 1\r 2\n 3\t 4\r 5\r \n\t", + Out: "1 2 3 4 5", + }, + { + In: "\r\n \t 11\r 22\n 33\t 44 \r 55", + Out: "11 22 33 44 55", + }, + { + In: "11\r 22\n 33\t 44 \r 55", + Out: "11 22 33 44 55", + }, + { + In: "1 2 3 4 5", + Out: "1 2 3 4 5", + }, + { + In: "?", + Out: "$1", + }, + { + In: "? ?", + Out: "$1 $2", + }, + { + In: "? ? ?", + Out: "$1 $2 $3", + }, + { + In: " ? ? ? ", + Out: "$1 $2 $3", + }, + { + In: "???", + Out: "$1$2$3", + }, + } + for _, sample := range samples { + assert.Equal(t, sample.Out, prepareQueryForDisplay(sample.In)) + } +} + func TestPlaceholderSimple(t *testing.T) { { ret, _ := Preprocess("?", []interface{}{1})