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

v5.3.4 #644

Merged
merged 20 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f36afb5
fix: improve model ID field customization (#604)
zepatrik Oct 29, 2020
4d0d826
Ensure uninitialized map is initialized when unmarshaling json
naemono Dec 15, 2020
81e5f2d
exclude migration_table_name from connection string
kyrozetera Dec 23, 2020
0e3d2e2
add test for OptionsString
kyrozetera Dec 23, 2020
b2918a3
Add support for pointer FKs when preloading a belongs_to association …
reggieriser Jan 5, 2021
0fb7635
feat: support context-aware tablenames (#614)
aeneasr Jan 18, 2021
119c0d2
Bump pg deps (#616)
paganotoni Jan 27, 2021
f252caa
Latest from master (#620)
paganotoni Jan 28, 2021
ad18e4d
Resolve issues in UPDATE and DELETE when using schemas (#618)
aeneasr Feb 6, 2021
e314840
Use `PaginatorPageKey` and `PaginatorPerPageKey` variables (#615)
bhb603 Feb 6, 2021
46cfd45
Pass Time structure into timestamp update functions. (#625)
mpontillo Mar 8, 2021
fbf43b4
Allow nullable JSONB and resolve MySQL regression (#639)
Apr 13, 2021
1b7b5ef
Allow passing args to `Order` (#630)
duckbrain Apr 13, 2021
83cf49c
Added connection maximum idle time configuration (#635)
ArthurKnoep Apr 14, 2021
51f7117
Bump sqlite to 3.35.4 / 1.14.7 (#642)
aeneasr Apr 27, 2021
cf0a600
Update pg, pgx, sqlx (#643)
paganotoni May 5, 2021
e947d1b
merging master
paganotoni May 5, 2021
96f8e48
Fix Inner has many associations when passing on multiple arguments (#…
larrymjordan May 5, 2021
aeb9b56
Remove many to many TX condition for EagerPreload (#645)
larrymjordan May 6, 2021
bef765a
Added fix/tests for has_many with pointer foreign key (#647)
reggieriser May 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
name: Release
runs-on: ubuntu-latest
container:
image: bepsays/ci-goreleaser:1.13-4
image: bepsays/ci-goreleaser:1.15.1
steps:
- name: Checkout Code
uses: actions/checkout@master
Expand All @@ -24,4 +24,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
with:
version: latest
args: release --rm-dist
args: release --rm-dist
22 changes: 11 additions & 11 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.15
id: go
- name: Checkout Code
uses: actions/checkout@v1
Expand Down Expand Up @@ -64,7 +64,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.15
id: go
- name: Checkout Code
uses: actions/checkout@v1
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.15
id: go
- name: Checkout Code
uses: actions/checkout@v1
Expand All @@ -110,12 +110,12 @@ jobs:
run: |
mkdir -p crdb/certs
pushd crdb
wget -qO- https://binaries.cockroachdb.com/cockroach-v2.1.0.linux-amd64.tgz | tar zxv
mv cockroach-v2.1.0.linux-amd64/cockroach .
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.2.4.linux-amd64.tgz | tar zxv
mv cockroach-v20.2.4.linux-amd64/cockroach .
./cockroach cert create-ca --certs-dir certs --ca-key key
./cockroach cert create-client root --certs-dir certs --ca-key key
./cockroach cert create-node localhost 127.0.0.1 `hostname -s` `hostname -f` --certs-dir certs --ca-key key
./cockroach start --certs-dir certs --listen-addr localhost --port 26259 --http-port 8089 --background
./cockroach start-single-node --certs-dir certs --listen-addr localhost --port 26259 --http-port 8089 --background
popd
- name: Build and run soda
env:
Expand All @@ -139,7 +139,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.15
id: go
- name: Checkout Code
uses: actions/checkout@v1
Expand All @@ -152,9 +152,9 @@ jobs:
run: |
mkdir -p crdb
pushd crdb
wget -qO- https://binaries.cockroachdb.com/cockroach-v2.1.0.linux-amd64.tgz | tar zxv
mv cockroach-v2.1.0.linux-amd64/cockroach .
./cockroach start --insecure --background
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.2.4.linux-amd64.tgz | tar zxv
mv cockroach-v20.2.4.linux-amd64/cockroach .
./cockroach start-single-node --insecure --background
popd
- name: Build and run soda
env:
Expand All @@ -181,7 +181,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.15
id: go
- name: Checkout Code
uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion associations/association.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (a *associationComposite) InnerAssociations() InnerAssociations {
// association for Song.
type InnerAssociation struct {
Name string
Fields string
Fields []string
}

// InnerAssociations is a group of InnerAssociation.
Expand Down
39 changes: 29 additions & 10 deletions associations/associations_for_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,39 @@ func ForStruct(s interface{}, fields ...string) (Associations, error) {
}
fields = trimFields(fields)
associations := Associations{}
innerAssociations := InnerAssociations{}
fieldsWithInnerAssociation := map[string]InnerAssociations{}

// validate if fields contains a non existing field in struct.
// and verify is it has inner associations.
for i := range fields {
var innerField, field string
var innerField string

if !validAssociationExpRegexp.MatchString(fields[i]) {
return associations, fmt.Errorf("association '%s' does not match the format %s", fields[i], "'<field>' or '<field>.<nested-field>'")
}

if strings.Contains(fields[i], ".") {
dotIndex := strings.Index(fields[i], ".")
field = fields[i][:dotIndex]
innerField = fields[i][dotIndex+1:]
fields[i] = field
}
fields[i], innerField = extractFieldAndInnerFields(fields[i])

if _, ok := t.FieldByName(fields[i]); !ok {
return associations, fmt.Errorf("field %s does not exist in model %s", fields[i], t.Name())
}

if innerField != "" {
innerAssociations = append(innerAssociations, InnerAssociation{fields[i], innerField})
var found bool
innerF, _ := extractFieldAndInnerFields(innerField)

for j := range fieldsWithInnerAssociation[fields[i]] {
f, _ := extractFieldAndInnerFields(fieldsWithInnerAssociation[fields[i]][j].Fields[0])
if innerF == f {
fieldsWithInnerAssociation[fields[i]][j].Fields = append(fieldsWithInnerAssociation[fields[i]][j].Fields, innerField)
found = true
break
}
}

if !found {
fieldsWithInnerAssociation[fields[i]] = append(fieldsWithInnerAssociation[fields[i]], InnerAssociation{fields[i], []string{innerField}})
}
}
}

Expand All @@ -79,7 +89,7 @@ func ForStruct(s interface{}, fields ...string) (Associations, error) {
modelType: t,
modelValue: v,
popTags: tags,
innerAssociations: innerAssociations,
innerAssociations: fieldsWithInnerAssociation[f.Name],
}

a, err := builder(params)
Expand Down Expand Up @@ -121,3 +131,12 @@ func fieldIgnoredIn(fields []string, field string) bool {
}
return true
}

func extractFieldAndInnerFields(field string) (string, string) {
if !strings.Contains(field, ".") {
return field, ""
}

dotIndex := strings.Index(field, ".")
return field[:dotIndex], field[dotIndex+1:]
}
6 changes: 5 additions & 1 deletion connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"sync/atomic"
"time"

"github.com/pkg/errors"

"github.com/gobuffalo/pop/v5/internal/defaults"
"github.com/gobuffalo/pop/v5/internal/randx"
"github.com/pkg/errors"
)

// Connections contains all available connections
Expand Down Expand Up @@ -117,6 +118,9 @@ func (c *Connection) Open() error {
if details.ConnMaxLifetime > 0 {
db.SetConnMaxLifetime(details.ConnMaxLifetime)
}
if details.ConnMaxIdleTime > 0 {
db.SetConnMaxIdleTime(details.ConnMaxIdleTime)
}
if details.Unsafe {
db = db.Unsafe()
}
Expand Down
2 changes: 2 additions & 0 deletions connection_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type ConnectionDetails struct {
IdlePool int
// Defaults to 0 "unlimited". See https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime
ConnMaxLifetime time.Duration
// Defaults to 0 "unlimited". See https://golang.org/pkg/database/sql/#DB.SetConnMaxIdleTime
ConnMaxIdleTime time.Duration
// Defaults to `false`. See https://godoc.org/github.com/jmoiron/sqlx#DB.Unsafe
Unsafe bool
Options map[string]string
Expand Down
2 changes: 1 addition & 1 deletion dialect_cockroach.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (p *cockroach) Update(s store, model *Model, cols columns.Columns) error {
}

func (p *cockroach) Destroy(s store, model *Model) error {
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s WHERE %s", p.Quote(model.TableName()), model.whereID()))
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.alias(), model.whereID()))
_, err := genericExec(s, stmt, model.ID())
return err
}
Expand Down
4 changes: 2 additions & 2 deletions dialect_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable)
}

func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable) error {
stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s", quoter.Quote(model.TableName()), cols.Writeable().QuotedUpdateString(quoter), model.whereNamedID())
stmt := fmt.Sprintf("UPDATE %s AS %s SET %s WHERE %s", quoter.Quote(model.TableName()), model.alias(), cols.Writeable().QuotedUpdateString(quoter), model.whereNamedID())
log(logging.SQL, stmt, model.ID())
_, err := s.NamedExec(stmt, model.Value)
if err != nil {
Expand All @@ -109,7 +109,7 @@ func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable)
}

func genericDestroy(s store, model *Model, quoter quotable) error {
stmt := fmt.Sprintf("DELETE FROM %s WHERE %s", quoter.Quote(model.TableName()), model.whereID())
stmt := fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", quoter.Quote(model.TableName()), model.alias(), model.whereID())
_, err := genericExec(s, stmt, model.ID())
if err != nil {
return err
Expand Down
9 changes: 6 additions & 3 deletions dialect_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
}

func (m *mysql) Destroy(s store, model *Model) error {
return errors.Wrap(genericDestroy(s, model, m), "mysql destroy")
stmt := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.Quote(model.TableName()), model.IDField())
_, err := genericExec(s, stmt, model.ID())
return errors.Wrap(err, "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
Expand Down Expand Up @@ -155,9 +157,10 @@ func (m *mysql) FizzTranslator() fizz.Translator {

func (m *mysql) DumpSchema(w io.Writer) error {
deets := m.Details()
cmd := exec.Command("mysqldump", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
// Github CI is currently using mysql:5.7 but the mysqldump version doesn't seem to match
cmd := exec.Command("mysqldump", "--column-statistics=0", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
if deets.Port == "socket" {
cmd = exec.Command("mysqldump", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
cmd = exec.Command("mysqldump", "--column-statistics=0", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
}
return genericDumpSchema(deets, cmd, w)
}
Expand Down
2 changes: 1 addition & 1 deletion dialect_postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (p *postgresql) Update(s store, model *Model, cols columns.Columns) error {
}

func (p *postgresql) Destroy(s store, model *Model) error {
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s WHERE %s", p.Quote(model.TableName()), model.whereID()))
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.alias(), model.whereID()))
_, err := genericExec(s, stmt, model.ID())
if err != nil {
return err
Expand Down
5 changes: 2 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ services:
volumes:
- ./sqldumps:/docker-entrypoint-initdb.d
cockroach:
image: cockroachdb/cockroach:v2.1.0
image: cockroachdb/cockroach:v20.2.4
ports:
- "26257:26257"
- "8080:8080"
volumes:
- "./cockroach-data/roach1:/cockroach/cockroach-data"
command: start --insecure
command: start-single-node --insecure
12 changes: 8 additions & 4 deletions executors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pop

import (
"reflect"
"time"

"github.com/gobuffalo/pop/v5/associations"
"github.com/gobuffalo/pop/v5/columns"
Expand Down Expand Up @@ -234,8 +235,9 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
cols.Remove(excludeColumns...)
}

m.touchCreatedAt()
m.touchUpdatedAt()
now := nowFunc().Truncate(time.Microsecond)
m.setUpdatedAt(now)
m.setCreatedAt(now)

if err = c.Dialect.Create(c.Store, m, cols); err != nil {
return err
Expand Down Expand Up @@ -357,7 +359,8 @@ func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
cols.Remove(excludeColumns...)
}

m.touchUpdatedAt()
now := nowFunc().Truncate(time.Microsecond)
m.setUpdatedAt(now)

if err = c.Dialect.Update(c.Store, m, cols); err != nil {
return err
Expand Down Expand Up @@ -401,7 +404,8 @@ func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) err
}
cols.Remove("id", "created_at")

m.touchUpdatedAt()
now := nowFunc().Truncate(time.Microsecond)
m.setUpdatedAt(now)

if err = c.Dialect.Update(c.Store, m, cols); err != nil {
return err
Expand Down
6 changes: 5 additions & 1 deletion finders.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,16 @@ func (q *Query) eagerDefaultAssociations(model interface{}) error {
return err
}

if err == sql.ErrNoRows {
continue
}

// load all inner associations.
innerAssociations := association.InnerAssociations()
for _, inner := range innerAssociations {
v = reflect.Indirect(reflect.ValueOf(model)).FieldByName(inner.Name)
innerQuery := Q(query.Connection)
innerQuery.eagerFields = []string{inner.Fields}
innerQuery.eagerFields = inner.Fields
err = innerQuery.eagerAssociations(v.Addr().Interface())
if err != nil {
return err
Expand Down
40 changes: 40 additions & 0 deletions finders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -908,3 +908,43 @@ func Test_FindManyToMany(t *testing.T) {
r.NoError(err)
})
}

func Test_FindMultipleInnerHasMany(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

user := User{Name: nulls.NewString("Mark")}
err := tx.Create(&user)
r.NoError(err)

book := Book{Title: "Pop Book", Isbn: "PB1", UserID: nulls.NewInt(user.ID)}
err = tx.Create(&book)
r.NoError(err)

writer := Writer{Name: "Jhon", BookID: book.ID}
err = tx.Create(&writer)
r.NoError(err)

friend := Friend{FirstName: "Frank", LastName: "Kafka", WriterID: writer.ID}
err = tx.Create(&friend)
r.NoError(err)

address := Address{Street: "St 27", HouseNumber: 27, WriterID: writer.ID}
err = tx.Create(&address)
r.NoError(err)

u := User{}
err = tx.Eager("Books.Writers.Addresses", "Books.Writers.Friends").Find(&u, user.ID)
r.NoError(err)

r.Len(u.Books, 1)
r.Len(u.Books[0].Writers, 1)
r.Len(u.Books[0].Writers[0].Addresses, 1)
r.Equal(u.Books[0].Writers[0].Addresses[0].HouseNumber, 27)
r.Len(u.Books[0].Writers[0].Friends, 1)
r.Equal(u.Books[0].Writers[0].Friends[0].FirstName, "Frank")
})
}
Loading