Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/go-gorm/gorm into go-gorm…
Browse files Browse the repository at this point in the history
…-master
  • Loading branch information
simoneserra93 committed Oct 8, 2024
2 parents e883d8a + 62bd0b9 commit 618afd4
Show file tree
Hide file tree
Showing 33 changed files with 800 additions and 83 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/release-on-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Create Release on Tag

on:
push:
tags:
- '*'

jobs:
create_release:
runs-on: ubuntu-latest

steps:
- name: Create Release
uses: actions/create-release@v1
with:
tag_name: ${{ github.ref_name }}
release_name: ${{ github.ref_name }}
body: |
Release ${{ github.ref_name }} of GORM.
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 changes: 13 additions & 15 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
sqlite:
strategy:
matrix:
go: ['1.21', '1.20', '1.19']
go: ['1.22', '1.21', '1.20']
platform: [ubuntu-latest] # can not run in windows OS
runs-on: ${{ matrix.platform }}

Expand All @@ -41,8 +41,8 @@ jobs:
mysql:
strategy:
matrix:
dbversion: ['mysql:latest', 'mysql:5.7']
go: ['1.21', '1.20', '1.19']
dbversion: ['mysql/mysql-server:latest', 'mysql:5.7']
go: ['1.22', '1.21', '1.20']
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}

Expand Down Expand Up @@ -85,7 +85,7 @@ jobs:
strategy:
matrix:
dbversion: [ 'mariadb:latest' ]
go: ['1.21', '1.20', '1.19']
go: ['1.22', '1.21', '1.20']
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}

Expand Down Expand Up @@ -127,8 +127,8 @@ jobs:
postgres:
strategy:
matrix:
dbversion: ['postgres:latest', 'postgres:13', 'postgres:12', 'postgres:11', 'postgres:10']
go: ['1.21', '1.20', '1.19']
dbversion: ['postgres:latest', 'postgres:15', 'postgres:14', 'postgres:13']
go: ['1.22', '1.21', '1.20']
platform: [ubuntu-latest] # can not run in macOS and Windows
runs-on: ${{ matrix.platform }}

Expand Down Expand Up @@ -170,23 +170,21 @@ jobs:
sqlserver:
strategy:
matrix:
go: ['1.21', '1.20', '1.19']
go: ['1.22', '1.21', '1.20']
platform: [ubuntu-latest] # can not run test in macOS and windows
runs-on: ${{ matrix.platform }}

services:
mssql:
image: mcmoe/mssqldocker:latest
image: mcr.microsoft.com/mssql/server:2022-latest
env:
TZ: Asia/Shanghai
ACCEPT_EULA: Y
SA_PASSWORD: LoremIpsum86
MSSQL_DB: gorm
MSSQL_USER: gorm
MSSQL_PASSWORD: LoremIpsum86
MSSQL_SA_PASSWORD: LoremIpsum86
ports:
- 9930:1433
options: >-
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
--health-cmd="/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P ${MSSQL_SA_PASSWORD} -N -C -l 30 -Q \"SELECT 1\" || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s
Expand All @@ -208,13 +206,13 @@ jobs:
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}

- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=sqlserver GORM_DSN="sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm" ./tests/tests_all.sh
run: GITHUB_ACTION=true GORM_DIALECT=sqlserver GORM_DSN="sqlserver://sa:LoremIpsum86@localhost:9930?database=master" ./tests/tests_all.sh

tidb:
strategy:
matrix:
dbversion: [ 'v6.5.0' ]
go: ['1.21', '1.20', '1.19']
go: ['1.22', '1.21', '1.20']
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}

Expand Down
14 changes: 14 additions & 0 deletions association.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,10 @@ func (association *Association) saveAssociation(clear bool, values ...interface{
}
}
case reflect.Struct:
if !rv.CanAddr() {
association.Error = ErrInvalidValue
return
}
association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Addr().Interface())

if association.Relationship.Field.FieldType.Kind() == reflect.Struct {
Expand Down Expand Up @@ -433,6 +437,10 @@ func (association *Association) saveAssociation(clear bool, values ...interface{
appendToFieldValues(reflect.Indirect(rv.Index(i)).Addr())
}
case reflect.Struct:
if !rv.CanAddr() {
association.Error = ErrInvalidValue
return
}
appendToFieldValues(rv.Addr())
}

Expand Down Expand Up @@ -510,6 +518,9 @@ func (association *Association) saveAssociation(clear bool, values ...interface{

for i := 0; i < reflectValue.Len(); i++ {
appendToRelations(reflectValue.Index(i), reflect.Indirect(reflect.ValueOf(values[i])), clear)
if association.Error != nil {
return
}

// TODO support save slice data, sql with case?
association.Error = associationDB.Updates(reflectValue.Index(i).Addr().Interface()).Error
Expand All @@ -531,6 +542,9 @@ func (association *Association) saveAssociation(clear bool, values ...interface{
for idx, value := range values {
rv := reflect.Indirect(reflect.ValueOf(value))
appendToRelations(reflectValue, rv, clear && idx == 0)
if association.Error != nil {
return
}
}

if len(values) > 0 {
Expand Down
10 changes: 6 additions & 4 deletions callbacks/preload.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,15 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati
case reflect.Slice, reflect.Array:
if rv.Len() > 0 {
reflectValue := rel.FieldSchema.MakeSlice().Elem()
reflectValue.SetLen(rv.Len())
for i := 0; i < rv.Len(); i++ {
frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))
if frv.Kind() != reflect.Ptr {
reflectValue.Index(i).Set(frv.Addr())
reflectValue = reflect.Append(reflectValue, frv.Addr())
} else {
reflectValue.Index(i).Set(frv)
if frv.IsNil() {
continue
}
reflectValue = reflect.Append(reflectValue, frv)
}
}

Expand All @@ -140,7 +142,7 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati
return err
}
}
case reflect.Struct:
case reflect.Struct, reflect.Pointer:
reflectValue := rel.Field.ReflectValueOf(db.Statement.Context, rv)
tx := preloadDB(db, reflectValue, reflectValue.Interface())
if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {
Expand Down
6 changes: 5 additions & 1 deletion callbacks/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,11 @@ func Preload(db *gorm.DB) {

func AfterQuery(db *gorm.DB) {
// clear the joins after query because preload need it
db.Statement.Joins = nil
if v, ok := db.Statement.Clauses["FROM"].Expression.(clause.From); ok {
fromClause := db.Statement.Clauses["FROM"]
fromClause.Expression = clause.From{Tables: v.Tables, Joins: utils.RTrimSlice(v.Joins, len(db.Statement.Joins))} // keep the original From Joins
db.Statement.Clauses["FROM"] = fromClause
}
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterFind && db.RowsAffected > 0 {
callMethod(db, func(value interface{}, tx *gorm.DB) bool {
if i, ok := value.(AfterFindInterface); ok {
Expand Down
13 changes: 13 additions & 0 deletions chainable_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ func (db *DB) Omit(columns ...string) (tx *DB) {
return
}

// MapColumns modify the column names in the query results to facilitate align to the corresponding structural fields
func (db *DB) MapColumns(m map[string]string) (tx *DB) {
tx = db.getInstance()
tx.Statement.ColumnMapping = m
return
}

// Where add conditions
//
// See the [docs] for details on the various formats that where clauses can take. By default, where clauses chain with AND.
Expand Down Expand Up @@ -299,10 +306,16 @@ func (db *DB) Having(query interface{}, args ...interface{}) (tx *DB) {
//
// db.Order("name DESC")
// db.Order(clause.OrderByColumn{Column: clause.Column{Name: "name"}, Desc: true})
// db.Order(clause.OrderBy{Columns: []clause.OrderByColumn{
// {Column: clause.Column{Name: "name"}, Desc: true},
// {Column: clause.Column{Name: "age"}, Desc: true},
// }})
func (db *DB) Order(value interface{}) (tx *DB) {
tx = db.getInstance()

switch v := value.(type) {
case clause.OrderBy:
tx.Statement.AddClause(v)
case clause.OrderByColumn:
tx.Statement.AddClause(clause.OrderBy{
Columns: []clause.OrderByColumn{v},
Expand Down
6 changes: 4 additions & 2 deletions finisher_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"database/sql"
"errors"
"fmt"
"hash/maphash"
"reflect"
"strings"

Expand Down Expand Up @@ -623,14 +624,15 @@ func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err er
if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {
// nested transaction
if !db.DisableNestedTransaction {
err = db.SavePoint(fmt.Sprintf("sp%p", fc)).Error
spID := new(maphash.Hash).Sum64()
err = db.SavePoint(fmt.Sprintf("sp%d", spID)).Error
if err != nil {
return
}
defer func() {
// Make sure to rollback when panic, Block error or Commit error
if panicked || err != nil {
db.RollbackTo(fmt.Sprintf("sp%p", fc))
db.RollbackTo(fmt.Sprintf("sp%d", spID))
}
}()
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ go 1.18
require (
github.com/jinzhu/inflection v1.0.0
github.com/jinzhu/now v1.1.5
golang.org/x/text v0.14.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
11 changes: 11 additions & 0 deletions gorm.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ type Config struct {
TranslateError bool
// SkipOutputStatement disable OUTPUT clause when inserting a row
SkipOutputStatement bool
// PropagateUnscoped propagate Unscoped to every other nested statement
PropagateUnscoped bool

// ClauseBuilders clause builder
ClauseBuilders map[string]clause.ClauseBuilder
// ConnPool db conn pool
Expand Down Expand Up @@ -111,6 +114,7 @@ type Session struct {
DisableNestedTransaction bool
AllowGlobalUpdate bool
FullSaveAssociations bool
PropagateUnscoped bool
QueryFields bool
SkipOutputStatement bool
Context context.Context
Expand Down Expand Up @@ -248,6 +252,10 @@ func (db *DB) Session(config *Session) *DB {
txConfig.FullSaveAssociations = true
}

if config.PropagateUnscoped {
txConfig.PropagateUnscoped = true
}

if config.Context != nil || config.PrepareStmt || config.SkipHooks {
tx.Statement = tx.Statement.clone()
tx.Statement.DB = tx
Expand Down Expand Up @@ -424,6 +432,9 @@ func (db *DB) getInstance() *DB {
Vars: make([]interface{}, 0, 8),
SkipHooks: db.Statement.SkipHooks,
}
if db.Config.PropagateUnscoped {
tx.Statement.Unscoped = db.Statement.Unscoped
}
} else {
// with clone statement
tx.Statement = db.Statement.clone()
Expand Down
44 changes: 28 additions & 16 deletions prepare_stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ type Stmt struct {
}

type PreparedStmtDB struct {
Stmts map[string]*Stmt
PreparedSQL []string
Mux *sync.RWMutex
Stmts map[string]*Stmt
Mux *sync.RWMutex
ConnPool
}

func NewPreparedStmtDB(connPool ConnPool) *PreparedStmtDB {
return &PreparedStmtDB{
ConnPool: connPool,
Stmts: make(map[string]*Stmt),
Mux: &sync.RWMutex{},
PreparedSQL: make([]string, 0, 100),
ConnPool: connPool,
Stmts: make(map[string]*Stmt),
Mux: &sync.RWMutex{},
}
}

Expand All @@ -48,22 +46,32 @@ func (db *PreparedStmtDB) Close() {
db.Mux.Lock()
defer db.Mux.Unlock()

for _, query := range db.PreparedSQL {
if stmt, ok := db.Stmts[query]; ok {
delete(db.Stmts, query)
go stmt.Close()
}
for _, stmt := range db.Stmts {
go func(s *Stmt) {
// make sure the stmt must finish preparation first
<-s.prepared
if s.Stmt != nil {
_ = s.Close()
}
}(stmt)
}
// setting db.Stmts to nil to avoid further using
db.Stmts = nil
}

func (sdb *PreparedStmtDB) Reset() {
sdb.Mux.Lock()
defer sdb.Mux.Unlock()

for _, stmt := range sdb.Stmts {
go stmt.Close()
go func(s *Stmt) {
// make sure the stmt must finish preparation first
<-s.prepared
if s.Stmt != nil {
_ = s.Close()
}
}(stmt)
}
sdb.PreparedSQL = make([]string, 0, 100)
sdb.Stmts = make(map[string]*Stmt)
}

Expand Down Expand Up @@ -93,7 +101,12 @@ func (db *PreparedStmtDB) prepare(ctx context.Context, conn ConnPool, isTransact

return *stmt, nil
}

// check db.Stmts first to avoid Segmentation Fault(setting value to nil map)
// which cause by calling Close and executing SQL concurrently
if db.Stmts == nil {
db.Mux.Unlock()
return Stmt{}, ErrInvalidDB
}
// cache preparing stmt first
cacheStmt := Stmt{Transaction: isTransaction, prepared: make(chan struct{})}
db.Stmts[query] = &cacheStmt
Expand All @@ -118,7 +131,6 @@ func (db *PreparedStmtDB) prepare(ctx context.Context, conn ConnPool, isTransact

db.Mux.Lock()
cacheStmt.Stmt = stmt
db.PreparedSQL = append(db.PreparedSQL, query)
db.Mux.Unlock()

return cacheStmt, nil
Expand Down
Loading

0 comments on commit 618afd4

Please sign in to comment.