Add builder join sub query support #59
|
@ -10,10 +10,10 @@ clone:
|
|||
|
||||
matrix:
|
||||
GO_VERSION:
|
||||
- 1.9
|
||||
- 1.10
|
||||
- 1.11
|
||||
- 1.12
|
||||
- 1.13
|
||||
|
||||
pipeline:
|
||||
test:
|
||||
|
@ -25,6 +25,7 @@ pipeline:
|
|||
- go get -u github.com/stretchr/testify/assert
|
||||
- go get -u github.com/go-xorm/sqlfiddle
|
||||
- golint ./...
|
||||
- go vet
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
96
builder.go
96
builder.go
|
@ -7,7 +7,6 @@ package builder
|
|||
import (
|
||||
sql2 "database/sql"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type optype byte
|
||||
|
@ -31,7 +30,7 @@ const (
|
|||
|
||||
type join struct {
|
||||
joinType string
|
||||
joinTable string
|
||||
joinTable interface{}
|
||||
joinCond Cond
|
||||
}
|
||||
|
||||
|
@ -143,18 +142,6 @@ func (b *Builder) Into(tableName string) *Builder {
|
|||
return b
|
||||
}
|
||||
|
||||
// Join sets join table and conditions
|
||||
func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builder {
|
||||
switch joinCond.(type) {
|
||||
case Cond:
|
||||
b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)})
|
||||
case string:
|
||||
b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))})
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Union sets union conditions
|
||||
func (b *Builder) Union(unionTp string, unionCond *Builder) *Builder {
|
||||
var builder *Builder
|
||||
|
@ -199,31 +186,6 @@ func (b *Builder) Limit(limitN int, offset ...int) *Builder {
|
|||
return b
|
||||
}
|
||||
|
||||
// InnerJoin sets inner join
|
||||
func (b *Builder) InnerJoin(joinTable string, joinCond interface{}) *Builder {
|
||||
return b.Join("INNER", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// LeftJoin sets left join SQL
|
||||
func (b *Builder) LeftJoin(joinTable string, joinCond interface{}) *Builder {
|
||||
return b.Join("LEFT", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// RightJoin sets right join SQL
|
||||
func (b *Builder) RightJoin(joinTable string, joinCond interface{}) *Builder {
|
||||
return b.Join("RIGHT", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// CrossJoin sets cross join SQL
|
||||
func (b *Builder) CrossJoin(joinTable string, joinCond interface{}) *Builder {
|
||||
return b.Join("CROSS", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// FullJoin sets full join SQL
|
||||
func (b *Builder) FullJoin(joinTable string, joinCond interface{}) *Builder {
|
||||
return b.Join("FULL", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// Select sets select SQL
|
||||
func (b *Builder) Select(cols ...string) *Builder {
|
||||
b.selects = cols
|
||||
|
@ -245,62 +207,6 @@ func (b *Builder) Or(cond Cond) *Builder {
|
|||
return b
|
||||
}
|
||||
|
||||
type insertColsSorter struct {
|
||||
cols []string
|
||||
vals []interface{}
|
||||
}
|
||||
|
||||
func (s insertColsSorter) Len() int {
|
||||
return len(s.cols)
|
||||
}
|
||||
func (s insertColsSorter) Swap(i, j int) {
|
||||
s.cols[i], s.cols[j] = s.cols[j], s.cols[i]
|
||||
s.vals[i], s.vals[j] = s.vals[j], s.vals[i]
|
||||
}
|
||||
|
||||
func (s insertColsSorter) Less(i, j int) bool {
|
||||
return s.cols[i] < s.cols[j]
|
||||
}
|
||||
|
||||
// Insert sets insert SQL
|
||||
func (b *Builder) Insert(eq ...interface{}) *Builder {
|
||||
if len(eq) > 0 {
|
||||
var paramType = -1
|
||||
for _, e := range eq {
|
||||
switch t := e.(type) {
|
||||
case Eq:
|
||||
if paramType == -1 {
|
||||
paramType = 0
|
||||
}
|
||||
if paramType != 0 {
|
||||
break
|
||||
}
|
||||
for k, v := range t {
|
||||
b.insertCols = append(b.insertCols, k)
|
||||
b.insertVals = append(b.insertVals, v)
|
||||
}
|
||||
case string:
|
||||
if paramType == -1 {
|
||||
paramType = 1
|
||||
}
|
||||
if paramType != 1 {
|
||||
break
|
||||
}
|
||||
b.insertCols = append(b.insertCols, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(b.insertCols) == len(b.insertVals) {
|
||||
sort.Sort(insertColsSorter{
|
||||
cols: b.insertCols,
|
||||
vals: b.insertVals,
|
||||
})
|
||||
}
|
||||
b.optype = insertType
|
||||
return b
|
||||
}
|
||||
|
||||
// Update sets update SQL
|
||||
func (b *Builder) Update(updates ...Cond) *Builder {
|
||||
b.updates = make([]UpdateCond, 0, len(updates))
|
||||
|
|
|
@ -7,6 +7,7 @@ package builder
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Insert creates an insert Builder
|
||||
|
@ -87,3 +88,60 @@ func (b *Builder) insertWriteTo(w Writer) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
type insertColsSorter struct {
|
||||
cols []string
|
||||
vals []interface{}
|
||||
}
|
||||
|
||||
func (s insertColsSorter) Len() int {
|
||||
return len(s.cols)
|
||||
}
|
||||
|
||||
func (s insertColsSorter) Swap(i, j int) {
|
||||
s.cols[i], s.cols[j] = s.cols[j], s.cols[i]
|
||||
s.vals[i], s.vals[j] = s.vals[j], s.vals[i]
|
||||
}
|
||||
|
||||
func (s insertColsSorter) Less(i, j int) bool {
|
||||
return s.cols[i] < s.cols[j]
|
||||
}
|
||||
|
||||
// Insert sets insert SQL
|
||||
func (b *Builder) Insert(eq ...interface{}) *Builder {
|
||||
if len(eq) > 0 {
|
||||
var paramType = -1
|
||||
for _, e := range eq {
|
||||
switch t := e.(type) {
|
||||
case Eq:
|
||||
if paramType == -1 {
|
||||
paramType = 0
|
||||
}
|
||||
if paramType != 0 {
|
||||
break
|
||||
}
|
||||
for k, v := range t {
|
||||
b.insertCols = append(b.insertCols, k)
|
||||
b.insertVals = append(b.insertVals, v)
|
||||
}
|
||||
case string:
|
||||
if paramType == -1 {
|
||||
paramType = 1
|
||||
}
|
||||
if paramType != 1 {
|
||||
break
|
||||
}
|
||||
b.insertCols = append(b.insertCols, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(b.insertCols) == len(b.insertVals) {
|
||||
sort.Sort(insertColsSorter{
|
||||
cols: b.insertCols,
|
||||
vals: b.insertVals,
|
||||
})
|
||||
}
|
||||
b.optype = insertType
|
||||
return b
|
||||
}
|
||||
|
|
42
builder_join.go
Normal file
42
builder_join.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2019 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
// InnerJoin sets inner join
|
||||
func (b *Builder) InnerJoin(joinTable, joinCond interface{}) *Builder {
|
||||
return b.Join("INNER", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// LeftJoin sets left join SQL
|
||||
func (b *Builder) LeftJoin(joinTable, joinCond interface{}) *Builder {
|
||||
return b.Join("LEFT", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// RightJoin sets right join SQL
|
||||
func (b *Builder) RightJoin(joinTable, joinCond interface{}) *Builder {
|
||||
return b.Join("RIGHT", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// CrossJoin sets cross join SQL
|
||||
func (b *Builder) CrossJoin(joinTable, joinCond interface{}) *Builder {
|
||||
return b.Join("CROSS", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// FullJoin sets full join SQL
|
||||
func (b *Builder) FullJoin(joinTable, joinCond interface{}) *Builder {
|
||||
return b.Join("FULL", joinTable, joinCond)
|
||||
}
|
||||
|
||||
// Join sets join table and conditions
|
||||
func (b *Builder) Join(joinType string, joinTable, joinCond interface{}) *Builder {
|
||||
switch joinCond.(type) {
|
||||
case Cond:
|
||||
b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)})
|
||||
case string:
|
||||
b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))})
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
50
builder_join_test.go
Normal file
50
builder_join_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2018 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
sql, args, err := Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
RightJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? RIGHT JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
FullJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? FULL JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
CrossJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? CROSS JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
InnerJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? INNER JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
subQuery2 := Select("e").From("table2").Where(Gt{"e": 1})
|
||||
subQuery3 := Select("f").From("table3").Where(Gt{"f": "2"})
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin(subQuery2, Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
InnerJoin(subQuery3, "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN (SELECT e FROM table2 WHERE e>?) ON table1.id=? AND table2.id<? INNER JOIN (SELECT f FROM table3 WHERE f>?) ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 1, 3, "2", 1}, args)
|
||||
}
|
|
@ -80,8 +80,21 @@ func (b *Builder) selectWriteTo(w Writer) error {
|
|||
}
|
||||
|
||||
for _, v := range b.joins {
|
||||
if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil {
|
||||
return err
|
||||
b, ok := v.joinTable.(*Builder)
|
||||
if ok {
|
||||
if _, err := fmt.Fprintf(w, " %s JOIN (", v.joinType); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, ") ON "); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := v.joinCond.WriteTo(w); err != nil {
|
||||
|
|
|
@ -22,34 +22,6 @@ func TestBuilder_Select(t *testing.T) {
|
|||
assert.EqualValues(t, "SELECT c, d FROM table1 WHERE a=?", sql)
|
||||
assert.EqualValues(t, []interface{}{1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
RightJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? RIGHT JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
FullJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? FULL JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
CrossJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? CROSS JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
|
||||
InnerJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? INNER JOIN table3 ON table2.id = table3.tid WHERE a=?",
|
||||
sql)
|
||||
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
|
||||
|
||||
_, _, err = Select("c, d").ToSQL()
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, ErrNoTableName, err)
|
||||
|
|
5
cond.go
5
cond.go
|
@ -6,6 +6,7 @@ package builder
|
|||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Writer defines the interface
|
||||
|
@ -18,14 +19,14 @@ var _ Writer = NewWriter()
|
|||
|
||||
// BytesWriter implments Writer and save SQL in bytes.Buffer
|
||||
type BytesWriter struct {
|
||||
writer *StringBuilder
|
||||
writer *strings.Builder
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
// NewWriter creates a new string writer
|
||||
func NewWriter() *BytesWriter {
|
||||
w := &BytesWriter{
|
||||
writer: &StringBuilder{},
|
||||
writer: &strings.Builder{},
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,5 +1,7 @@
|
|||
module xorm.io/builder
|
||||
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a
|
||||
github.com/stretchr/testify v1.3.0
|
||||
|
|
4
sql.go
4
sql.go
|
@ -93,7 +93,7 @@ func noSQLQuoteNeeded(a interface{}) bool {
|
|||
|
||||
// ConvertToBoundSQL will convert SQL and args to a bound SQL
|
||||
func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
|
||||
buf := StringBuilder{}
|
||||
buf := strings.Builder{}
|
||||
var i, j, start int
|
||||
for ; i < len(sql); i++ {
|
||||
if sql[i] == '?' {
|
||||
|
@ -135,7 +135,7 @@ func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
|
|||
|
||||
// ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
|
||||
func ConvertPlaceholder(sql, prefix string) (string, error) {
|
||||
buf := StringBuilder{}
|
||||
buf := strings.Builder{}
|
||||
var i, j, start int
|
||||
for ; i < len(sql); i++ {
|
||||
if sql[i] == '?' {
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A StringBuilder is used to efficiently build a string using Write methods.
|
||||
// It minimizes memory copying. The zero value is ready to use.
|
||||
// Do not copy a non-zero Builder.
|
||||
type StringBuilder struct {
|
||||
addr *StringBuilder // of receiver, to detect copies by value
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// noescape hides a pointer from escape analysis. noescape is
|
||||
// the identity function but escape analysis doesn't think the
|
||||
// output depends on the input. noescape is inlined and currently
|
||||
// compiles down to zero instructions.
|
||||
// USE CAREFULLY!
|
||||
// This was copied from the runtime; see issues 23382 and 7921.
|
||||
//go:nosplit
|
||||
func noescape(p unsafe.Pointer) unsafe.Pointer {
|
||||
x := uintptr(p)
|
||||
return unsafe.Pointer(x ^ 0)
|
||||
}
|
||||
|
||||
func (b *StringBuilder) copyCheck() {
|
||||
if b.addr == nil {
|
||||
// This hack works around a failing of Go's escape analysis
|
||||
// that was causing b to escape and be heap allocated.
|
||||
// See issue 23382.
|
||||
// TODO: once issue 7921 is fixed, this should be reverted to
|
||||
// just "b.addr = b".
|
||||
b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b)))
|
||||
} else if b.addr != b {
|
||||
panic("strings: illegal use of non-zero Builder copied by value")
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the accumulated string.
|
||||
func (b *StringBuilder) String() string {
|
||||
return *(*string)(unsafe.Pointer(&b.buf))
|
||||
}
|
||||
|
||||
// Len returns the number of accumulated bytes; b.Len() == len(b.String()).
|
||||
func (b *StringBuilder) Len() int { return len(b.buf) }
|
||||
|
||||
// Reset resets the Builder to be empty.
|
||||
func (b *StringBuilder) Reset() {
|
||||
b.addr = nil
|
||||
b.buf = nil
|
||||
}
|
||||
|
||||
// grow copies the buffer to a new, larger buffer so that there are at least n
|
||||
// bytes of capacity beyond len(b.buf).
|
||||
func (b *StringBuilder) grow(n int) {
|
||||
buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
|
||||
copy(buf, b.buf)
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// Grow grows b's capacity, if necessary, to guarantee space for
|
||||
// another n bytes. After Grow(n), at least n bytes can be written to b
|
||||
// without another allocation. If n is negative, Grow panics.
|
||||
func (b *StringBuilder) Grow(n int) {
|
||||
b.copyCheck()
|
||||
if n < 0 {
|
||||
panic("strings.Builder.Grow: negative count")
|
||||
}
|
||||
if cap(b.buf)-len(b.buf) < n {
|
||||
b.grow(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Write appends the contents of p to b's buffer.
|
||||
// Write always returns len(p), nil.
|
||||
func (b *StringBuilder) Write(p []byte) (int, error) {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteByte appends the byte c to b's buffer.
|
||||
// The returned error is always nil.
|
||||
func (b *StringBuilder) WriteByte(c byte) error {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, c)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
|
||||
// It returns the length of r and a nil error.
|
||||
func (b *StringBuilder) WriteRune(r rune) (int, error) {
|
||||
b.copyCheck()
|
||||
if r < utf8.RuneSelf {
|
||||
b.buf = append(b.buf, byte(r))
|
||||
return 1, nil
|
||||
}
|
||||
l := len(b.buf)
|
||||
if cap(b.buf)-l < utf8.UTFMax {
|
||||
b.grow(utf8.UTFMax)
|
||||
}
|
||||
n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
|
||||
b.buf = b.buf[:l+n]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// WriteString appends the contents of s to b's buffer.
|
||||
// It returns the length of s and a nil error.
|
||||
func (b *StringBuilder) WriteString(s string) (int, error) {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, s...)
|
||||
return len(s), nil
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2018 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStringBuilderWriteByte(t *testing.T) {
|
||||
var b StringBuilder
|
||||
err := b.WriteByte('c')
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "c", b.String())
|
||||
|
||||
b.Reset()
|
||||
|
||||
var content = "123456"
|
||||
for _, c := range content {
|
||||
_, err = b.WriteRune(c)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.EqualValues(t, 6, b.Len())
|
||||
assert.EqualValues(t, content, b.String())
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for _, c := range content {
|
||||
_, err = b.WriteRune(c)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
b.Grow(600)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for _, c := range content {
|
||||
_, err = b.WriteRune(c)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user