Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method chaining corrupts data due to slice capacity increasing #3007

Closed
tekkamanendless opened this issue May 6, 2020 · 1 comment
Closed
Labels
type:missing reproduction steps missing reproduction steps

Comments

@tekkamanendless
Copy link
Contributor

Method chaining corrupts data due to the fact that search.clone() doesn't actually copy its slices.

The first time that you call Where, whereConditions goes from a nil slice to a new slice with length=1, capacity=1.

The second time that you call Where, whereConditions goes from a slice with length=1, capacity=1 to a new slice with length=2, capacity=2.

The third time that you call Where, whereConditions goes from a slice with length=2, capacity=2 to a new slice with length=3, capacity=4. It is here that Go's slice creation has allocated more capacity than we are using.

The fourth time that you call Where, whereConditions remains the same; its length merely grows to 4 and the fourth element is set.

If you used the first 3 Where calls to create a template query, and then if you create two queries that each perform one more Where independently of each other, then the results will be corrupt.

Here's a simple test case to show that this is what's happening. It starts creates a "base" query with 1, 2, 3, and then 4 initial Where calls, and then it creates two additional queries that each use that base to perform a single additional Where call.

In all cases, the text THING1 should only be present in query q1, and THING2 should only be present in query q2. However, this is not the case when w=3, meaning that the base query has 3 Where calls to start with.

package main

import (
	"fmt"
	"strings"
	"testing"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/stretchr/testify/assert"
)

func TestWhereClone(t *testing.T) {
	databaseType := "sqlite3"
	databaseURL := "file:no-provided-database?mode=memory"

	db, _ := gorm.Open(databaseType, databaseURL)
	db.LogMode(true)

	for whereCount := 1; whereCount <= 4; whereCount++ {
		t.Run(fmt.Sprintf("w=%d", whereCount), func(t *testing.T) {
			query := db.Table("myTable")
			for w := 0; w < whereCount; w++ {
				query = query.Where(fmt.Sprintf("w%d = ?", w), fmt.Sprintf("value%d", w))
			}

			q1 := query.Where("finalThing = ?", "THING1")
			q2 := query.Where("finalThing = ?", "THING2")

			e1 := fmt.Sprintf("%v", q1.QueryExpr())
			e2 := fmt.Sprintf("%v", q2.QueryExpr())

			assert.True(t, strings.Contains(e1, "THING1"))
			assert.False(t, strings.Contains(e1, "THING2"))

			assert.False(t, strings.Contains(e2, "THING1"))
			assert.True(t, strings.Contains(e2, "THING2"))
		})
	}
}

I plan to submit a PR to address this sometime soon.

@tekkamanendless
Copy link
Contributor Author

I submitted PR #3009 to address this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:missing reproduction steps missing reproduction steps
Projects
None yet
Development

No branches or pull requests

2 participants