diff --git a/coredb/txengine/tx_engine.go b/coredb/txengine/tx_engine.go new file mode 100644 index 0000000..c9637f1 --- /dev/null +++ b/coredb/txengine/tx_engine.go @@ -0,0 +1,115 @@ +package txengine + +import ( + "context" + "database/sql" + "errors" + "fmt" + + "github.com/olachat/gola/v2/coredb" +) + +type TypedTx[T any] sql.Tx +type Tx sql.Tx + +func WithTypedTx[T any](tx *sql.Tx) *TypedTx[T] { + return (*TypedTx[T])(tx) +} + +func WithTx(tx *sql.Tx) *Tx { + return (*Tx)(tx) +} + +func StartTx(ctx context.Context, tx *sql.Tx, fn func(ctx context.Context, sqlTx *sql.Tx) error) (err error) { + defer func() { + //nolint:gocritic + if r := recover(); r != nil { + _ = tx.Rollback() + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + } else if err != nil { + errRollback := tx.Rollback() + if errors.Is(errRollback, sql.ErrTxDone) && ctx.Err() != nil { + errRollback = nil + } + if errRollback != nil { + err = fmt.Errorf("%v encountered. but rollback failed: %w", err, errRollback) + } + } else { + err = tx.Commit() + } + }() + + err = fn(ctx, tx) + + return err +} + +// FindOne returns a row from given table type with where query. +// If no rows found, *T will be nil. No error will be returned. +func (o *TypedTx[T]) FindOne(ctx context.Context, tableName string, where coredb.WhereQuery) (*T, error) { + u := new(T) + columnsNames := coredb.GetColumnsNames[T]() + data := coredb.StrutForScan(u) + whereSQL, params := where.GetWhere() + query := fmt.Sprintf("SELECT %s FROM `%s` %s", columnsNames, + tableName, whereSQL) + err2 := (*sql.Tx)(o).QueryRowContext(ctx, query, params...).Scan(data...) + + if err2 != nil { + // It's on purpose the hide the error + // But should re-consider later + if err2 == sql.ErrNoRows { + return nil, nil + } + + return nil, err2 + } + + return u, nil +} + +// Exec given query with given db info & params +func (o *Tx) Exec(ctx context.Context, query string, params ...any) (sql.Result, error) { + return (*sql.Tx)(o).ExecContext(ctx, query, params...) +} + +// Find returns rows from given table type with where query +func (o *TypedTx[T]) Find(ctx context.Context, tableName string, where coredb.WhereQuery) ([]*T, error) { + columnsNames := coredb.GetColumnsNames[T]() + whereSQL, params := where.GetWhere() + query := fmt.Sprintf("SELECT %s FROM `%s` %s", columnsNames, + tableName, whereSQL) + + return o.Query(ctx, query, params...) +} + +// QueryInt single int result by query, handy for count(*) querys +func (o *Tx) QueryInt(ctx context.Context, query string, params ...any) (result int, err error) { + err = (*sql.Tx)(o).QueryRowContext(ctx, query, params...).Scan(&result) + return +} + +// Query rows from given table type with where query & params +func (o *TypedTx[T]) Query(ctx context.Context, query string, params ...any) (result []*T, err error) { + rows, err := (*sql.Tx)(o).QueryContext(ctx, query, params...) + if err != nil { + return + } + + var u *T + for rows.Next() { + u = new(T) + data := coredb.StrutForScan(u) + err = rows.Scan(data...) + if err != nil { + return + } + result = append(result, u) + } + + return +} diff --git a/golalib/golalib.go b/golalib/golalib.go index c033efe..841de44 100644 --- a/golalib/golalib.go +++ b/golalib/golalib.go @@ -190,6 +190,7 @@ func genORM(t *structs.Table) map[string][]byte { genFiles := map[string]string{ "00_struct.gogo": tableFolder + t.Name + ".go", "00_struct_ctx.gogo": tableFolder + t.Name + "_ctx.go", + "00_struct_tx.gogo": tableFolder + t.Name + "_tx.go", "01_struct_idx.gogo": tableFolder + t.Name + "_idx.go", "01_struct_idx_ctx.gogo": tableFolder + t.Name + "_idx_ctx.go", } diff --git a/golalib/testdata/account/account_tx.go b/golalib/testdata/account/account_tx.go new file mode 100644 index 0000000..31598f0 --- /dev/null +++ b/golalib/testdata/account/account_tx.go @@ -0,0 +1,128 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package account + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Account struct to `account` table with transaction +func (c *Account) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getUserIdForDB(), c.getTypeForDB(), c.getCountryCodeForDB(), c.getMoneyForDB()) + if err != nil { + return err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Account struct in `account` table with transaction +func (obj *Account) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Type.IsUpdated() { + updatedFields = append(updatedFields, "`type` = ?") + params = append(params, obj.getTypeForDB()) + } + if obj.Money.IsUpdated() { + updatedFields = append(updatedFields, "`money` = ?") + params = append(params, obj.getMoneyForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `account` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `country_code` = ?" + params = append(params, obj.GetUserId(), obj.GetCountryCode()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Account struct with given fields in `account` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Type: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`type` = ?") + params = append(params, c.getTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Money: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`money` = ?") + params = append(params, c.getMoneyForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `account` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `country_code` = ?" + params = append(params, obj.GetUserId(), obj.GetCountryCode()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/blogs/blogs_tx.go b/golalib/testdata/blogs/blogs_tx.go new file mode 100644 index 0000000..eacd119 --- /dev/null +++ b/golalib/testdata/blogs/blogs_tx.go @@ -0,0 +1,221 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package blogs + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Blog struct to `blogs` table with transaction +func (c *Blog) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getUserIdForDB(), c.getSlugForDB(), c.getTitleForDB(), c.getCategoryIdForDB(), c.getIsPinnedForDB(), c.getIsVipForDB(), c.getCountryForDB(), c.getCreatedAtForDB(), c.getUpdatedAtForDB(), c.getCountForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getUserIdForDB(), c.getSlugForDB(), c.getTitleForDB(), c.getCategoryIdForDB(), c.getIsPinnedForDB(), c.getIsVipForDB(), c.getCountryForDB(), c.getCreatedAtForDB(), c.getUpdatedAtForDB(), c.getCountForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = int(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Blog struct in `blogs` table with transaction +func (obj *Blog) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.UserId.IsUpdated() { + updatedFields = append(updatedFields, "`user_id` = ?") + params = append(params, obj.getUserIdForDB()) + } + if obj.Slug.IsUpdated() { + updatedFields = append(updatedFields, "`slug` = ?") + params = append(params, obj.getSlugForDB()) + } + if obj.Title.IsUpdated() { + updatedFields = append(updatedFields, "`title` = ?") + params = append(params, obj.getTitleForDB()) + } + if obj.CategoryId.IsUpdated() { + updatedFields = append(updatedFields, "`category_id` = ?") + params = append(params, obj.getCategoryIdForDB()) + } + if obj.IsPinned.IsUpdated() { + updatedFields = append(updatedFields, "`is_pinned` = ?") + params = append(params, obj.getIsPinnedForDB()) + } + if obj.IsVip.IsUpdated() { + updatedFields = append(updatedFields, "`is_vip` = ?") + params = append(params, obj.getIsVipForDB()) + } + if obj.Country.IsUpdated() { + updatedFields = append(updatedFields, "`country` = ?") + params = append(params, obj.getCountryForDB()) + } + if obj.CreatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, obj.getCreatedAtForDB()) + } + if obj.UpdatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, obj.getUpdatedAtForDB()) + } + if obj.Count_.IsUpdated() { + updatedFields = append(updatedFields, "`count` = ?") + params = append(params, obj.getCountForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `blogs` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Blog struct with given fields in `blogs` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *UserId: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`user_id` = ?") + params = append(params, c.getUserIdForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Slug: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`slug` = ?") + params = append(params, c.getSlugForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Title: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`title` = ?") + params = append(params, c.getTitleForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CategoryId: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`category_id` = ?") + params = append(params, c.getCategoryIdForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsPinned: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_pinned` = ?") + params = append(params, c.getIsPinnedForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsVip: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_vip` = ?") + params = append(params, c.getIsVipForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Country: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`country` = ?") + params = append(params, c.getCountryForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, c.getCreatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, c.getUpdatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Count_: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`count` = ?") + params = append(params, c.getCountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `blogs` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/gifts/gifts_tx.go b/golalib/testdata/gifts/gifts_tx.go new file mode 100644 index 0000000..1356636 --- /dev/null +++ b/golalib/testdata/gifts/gifts_tx.go @@ -0,0 +1,241 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package gifts + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Gift struct to `gifts` table with transaction +func (c *Gift) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Gift struct in `gifts` table with transaction +func (obj *Gift) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.IsFree.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, obj.getIsFreeForDB()) + } + if obj.GiftCount.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, obj.getGiftCountForDB()) + } + if obj.GiftType.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, obj.getGiftTypeForDB()) + } + if obj.CreateTime.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, obj.getCreateTimeForDB()) + } + if obj.Discount.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, obj.getDiscountForDB()) + } + if obj.Price.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, obj.getPriceForDB()) + } + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.Manifest.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, obj.getManifestForDB()) + } + if obj.Description.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, obj.getDescriptionForDB()) + } + if obj.UpdateTime.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, obj.getUpdateTimeForDB()) + } + if obj.Branches.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, obj.getBranchesForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Gift struct with given fields in `gifts` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsFree: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, c.getIsFreeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftCount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, c.getGiftCountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, c.getGiftTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, c.getCreateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Discount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, c.getDiscountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Price: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, c.getPriceForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Manifest: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, c.getManifestForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Description: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, c.getDescriptionForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, c.getUpdateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Branches: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, c.getBranchesForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/gifts_nn/gifts_nn_tx.go b/golalib/testdata/gifts_nn/gifts_nn_tx.go new file mode 100644 index 0000000..46ec98a --- /dev/null +++ b/golalib/testdata/gifts_nn/gifts_nn_tx.go @@ -0,0 +1,241 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package gifts_nn + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts GiftsNn struct to `gifts_nn` table with transaction +func (c *GiftsNn) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates GiftsNn struct in `gifts_nn` table with transaction +func (obj *GiftsNn) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.IsFree.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, obj.getIsFreeForDB()) + } + if obj.GiftCount.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, obj.getGiftCountForDB()) + } + if obj.GiftType.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, obj.getGiftTypeForDB()) + } + if obj.CreateTime.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, obj.getCreateTimeForDB()) + } + if obj.Discount.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, obj.getDiscountForDB()) + } + if obj.Price.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, obj.getPriceForDB()) + } + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.Manifest.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, obj.getManifestForDB()) + } + if obj.Description.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, obj.getDescriptionForDB()) + } + if obj.UpdateTime.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, obj.getUpdateTimeForDB()) + } + if obj.Branches.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, obj.getBranchesForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_nn` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates GiftsNn struct with given fields in `gifts_nn` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsFree: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, c.getIsFreeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftCount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, c.getGiftCountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, c.getGiftTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, c.getCreateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Discount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, c.getDiscountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Price: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, c.getPriceForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Manifest: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, c.getManifestForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Description: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, c.getDescriptionForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, c.getUpdateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Branches: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, c.getBranchesForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_nn` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/gifts_nn_with_default.sql b/golalib/testdata/gifts_nn_with_default.sql index 940aaae..f8226a7 100644 --- a/golalib/testdata/gifts_nn_with_default.sql +++ b/golalib/testdata/gifts_nn_with_default.sql @@ -10,7 +10,7 @@ CREATE TABLE `gifts_nn_with_default` ( `remark` varchar(128) NOT NULL default 'hope you like it', `manifest` varbinary(255) NOT NULL default 'manifest data', `description` text NOT NULL, - `update_time` timestamp NOT NULL default '2023-01-19 03:14:07.999999', + `update_time` timestamp NOT NULL default '2023-01-19 03:14:07.0', `update_time2` timestamp NOT NULL default CURRENT_TIMESTAMP, `branches` set('orchard','vivo','sentosa','changi') NOT NULL default 'sentosa,changi', PRIMARY KEY (`id`) diff --git a/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default.go b/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default.go index ce51b11..2a4b8ff 100644 --- a/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default.go +++ b/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default.go @@ -945,7 +945,7 @@ func New() *GiftsNnWithDefault { Remark{val: "hope you like it"}, Manifest{val: []byte("manifest data")}, Description{}, - UpdateTime{val: coredb.MustParseTime("2023-01-19 03:14:07.999999")}, + UpdateTime{val: coredb.MustParseTime("2023-01-19 03:14:07.0")}, UpdateTime2{val: time.Now()}, Branches{val: "sentosa,changi"}, } @@ -966,7 +966,7 @@ func NewWithPK(val uint) *GiftsNnWithDefault { Remark{val: "hope you like it"}, Manifest{val: []byte("manifest data")}, Description{}, - UpdateTime{val: coredb.MustParseTime("2023-01-19 03:14:07.999999")}, + UpdateTime{val: coredb.MustParseTime("2023-01-19 03:14:07.0")}, UpdateTime2{val: time.Now()}, Branches{val: "sentosa,changi"}, } diff --git a/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default_tx.go b/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default_tx.go new file mode 100644 index 0000000..bff7a57 --- /dev/null +++ b/golalib/testdata/gifts_nn_with_default/gifts_nn_with_default_tx.go @@ -0,0 +1,251 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package gifts_nn_with_default + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts GiftsNnWithDefault struct to `gifts_nn_with_default` table with transaction +func (c *GiftsNnWithDefault) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getUpdateTime2ForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getUpdateTime2ForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates GiftsNnWithDefault struct in `gifts_nn_with_default` table with transaction +func (obj *GiftsNnWithDefault) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.IsFree.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, obj.getIsFreeForDB()) + } + if obj.GiftCount.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, obj.getGiftCountForDB()) + } + if obj.GiftType.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, obj.getGiftTypeForDB()) + } + if obj.CreateTime.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, obj.getCreateTimeForDB()) + } + if obj.Discount.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, obj.getDiscountForDB()) + } + if obj.Price.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, obj.getPriceForDB()) + } + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.Manifest.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, obj.getManifestForDB()) + } + if obj.Description.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, obj.getDescriptionForDB()) + } + if obj.UpdateTime.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, obj.getUpdateTimeForDB()) + } + if obj.UpdateTime2.IsUpdated() { + updatedFields = append(updatedFields, "`update_time2` = ?") + params = append(params, obj.getUpdateTime2ForDB()) + } + if obj.Branches.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, obj.getBranchesForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_nn_with_default` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates GiftsNnWithDefault struct with given fields in `gifts_nn_with_default` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsFree: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, c.getIsFreeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftCount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, c.getGiftCountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, c.getGiftTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, c.getCreateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Discount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, c.getDiscountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Price: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, c.getPriceForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Manifest: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, c.getManifestForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Description: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, c.getDescriptionForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, c.getUpdateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime2: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time2` = ?") + params = append(params, c.getUpdateTime2ForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Branches: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, c.getBranchesForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_nn_with_default` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/gifts_with_default.sql b/golalib/testdata/gifts_with_default.sql index b26d287..2da0062 100644 --- a/golalib/testdata/gifts_with_default.sql +++ b/golalib/testdata/gifts_with_default.sql @@ -10,7 +10,7 @@ CREATE TABLE `gifts_with_default` ( `remark` varchar(128) default 'hope you like it', `manifest` varbinary(255) default 'manifest data', `description` text, - `update_time` timestamp default '2023-01-19 03:14:07.999999', + `update_time` timestamp default '2023-01-19 03:14:07.0', `update_time2` timestamp default CURRENT_TIMESTAMP, `branches` set('orchard','vivo','sentosa','changi') default 'sentosa,changi', PRIMARY KEY (`id`) diff --git a/golalib/testdata/gifts_with_default/gifts_with_default.go b/golalib/testdata/gifts_with_default/gifts_with_default.go index 073e40b..961cac2 100644 --- a/golalib/testdata/gifts_with_default/gifts_with_default.go +++ b/golalib/testdata/gifts_with_default/gifts_with_default.go @@ -970,7 +970,7 @@ func New() *GiftsWithDefault { Remark{val: goption.Some[string]("hope you like it")}, Manifest{val: goption.Some[[]byte]([]byte("manifest data"))}, Description{}, - UpdateTime{val: goption.Some[time.Time](coredb.MustParseTime("2023-01-19 03:14:07.999999"))}, + UpdateTime{val: goption.Some[time.Time](coredb.MustParseTime("2023-01-19 03:14:07.0"))}, UpdateTime2{val: goption.Some[time.Time](time.Now())}, Branches{val: goption.Some[string]("sentosa,changi")}, } @@ -991,7 +991,7 @@ func NewWithPK(val uint) *GiftsWithDefault { Remark{val: goption.Some[string]("hope you like it")}, Manifest{val: goption.Some[[]byte]([]byte("manifest data"))}, Description{}, - UpdateTime{val: goption.Some[time.Time](coredb.MustParseTime("2023-01-19 03:14:07.999999"))}, + UpdateTime{val: goption.Some[time.Time](coredb.MustParseTime("2023-01-19 03:14:07.0"))}, UpdateTime2{val: goption.Some[time.Time](time.Now())}, Branches{val: goption.Some[string]("sentosa,changi")}, } diff --git a/golalib/testdata/gifts_with_default/gifts_with_default_tx.go b/golalib/testdata/gifts_with_default/gifts_with_default_tx.go new file mode 100644 index 0000000..acc844f --- /dev/null +++ b/golalib/testdata/gifts_with_default/gifts_with_default_tx.go @@ -0,0 +1,251 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package gifts_with_default + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts GiftsWithDefault struct to `gifts_with_default` table with transaction +func (c *GiftsWithDefault) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getUpdateTime2ForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getIsFreeForDB(), c.getGiftCountForDB(), c.getGiftTypeForDB(), c.getCreateTimeForDB(), c.getDiscountForDB(), c.getPriceForDB(), c.getRemarkForDB(), c.getManifestForDB(), c.getDescriptionForDB(), c.getUpdateTimeForDB(), c.getUpdateTime2ForDB(), c.getBranchesForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates GiftsWithDefault struct in `gifts_with_default` table with transaction +func (obj *GiftsWithDefault) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.IsFree.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, obj.getIsFreeForDB()) + } + if obj.GiftCount.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, obj.getGiftCountForDB()) + } + if obj.GiftType.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, obj.getGiftTypeForDB()) + } + if obj.CreateTime.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, obj.getCreateTimeForDB()) + } + if obj.Discount.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, obj.getDiscountForDB()) + } + if obj.Price.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, obj.getPriceForDB()) + } + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.Manifest.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, obj.getManifestForDB()) + } + if obj.Description.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, obj.getDescriptionForDB()) + } + if obj.UpdateTime.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, obj.getUpdateTimeForDB()) + } + if obj.UpdateTime2.IsUpdated() { + updatedFields = append(updatedFields, "`update_time2` = ?") + params = append(params, obj.getUpdateTime2ForDB()) + } + if obj.Branches.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, obj.getBranchesForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_with_default` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates GiftsWithDefault struct with given fields in `gifts_with_default` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsFree: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_free` = ?") + params = append(params, c.getIsFreeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftCount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_count` = ?") + params = append(params, c.getGiftCountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *GiftType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`gift_type` = ?") + params = append(params, c.getGiftTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`create_time` = ?") + params = append(params, c.getCreateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Discount: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`discount` = ?") + params = append(params, c.getDiscountForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Price: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`price` = ?") + params = append(params, c.getPriceForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Manifest: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, c.getManifestForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Description: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`description` = ?") + params = append(params, c.getDescriptionForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time` = ?") + params = append(params, c.getUpdateTimeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdateTime2: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`update_time2` = ?") + params = append(params, c.getUpdateTime2ForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Branches: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`branches` = ?") + params = append(params, c.getBranchesForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `gifts_with_default` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/profile/profile_tx.go b/golalib/testdata/profile/profile_tx.go new file mode 100644 index 0000000..91b4c78 --- /dev/null +++ b/golalib/testdata/profile/profile_tx.go @@ -0,0 +1,128 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package profile + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Profile struct to `profile` table with transaction +func (c *Profile) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getUserIdForDB(), c.getLevelForDB(), c.getNickNameForDB()) + if err != nil { + return err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Profile struct in `profile` table with transaction +func (obj *Profile) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Level.IsUpdated() { + updatedFields = append(updatedFields, "`level` = ?") + params = append(params, obj.getLevelForDB()) + } + if obj.NickName.IsUpdated() { + updatedFields = append(updatedFields, "`nick_name` = ?") + params = append(params, obj.getNickNameForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `profile` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ?" + params = append(params, obj.GetUserId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Profile struct with given fields in `profile` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Level: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`level` = ?") + params = append(params, c.getLevelForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *NickName: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`nick_name` = ?") + params = append(params, c.getNickNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `profile` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ?" + params = append(params, obj.GetUserId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/room/room_tx.go b/golalib/testdata/room/room_tx.go new file mode 100644 index 0000000..e71f366 --- /dev/null +++ b/golalib/testdata/room/room_tx.go @@ -0,0 +1,161 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package room + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Room struct to `room` table with transaction +func (c *Room) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getGroupForDB(), c.getLangForDB(), c.getPriorityForDB(), c.getDeletedForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getGroupForDB(), c.getLangForDB(), c.getPriorityForDB(), c.getDeletedForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Room struct in `room` table with transaction +func (obj *Room) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Group.IsUpdated() { + updatedFields = append(updatedFields, "`group` = ?") + params = append(params, obj.getGroupForDB()) + } + if obj.Lang.IsUpdated() { + updatedFields = append(updatedFields, "`lang` = ?") + params = append(params, obj.getLangForDB()) + } + if obj.Priority.IsUpdated() { + updatedFields = append(updatedFields, "`priority` = ?") + params = append(params, obj.getPriorityForDB()) + } + if obj.Deleted.IsUpdated() { + updatedFields = append(updatedFields, "`deleted` = ?") + params = append(params, obj.getDeletedForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `room` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Room struct with given fields in `room` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Group: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`group` = ?") + params = append(params, c.getGroupForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Lang: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`lang` = ?") + params = append(params, c.getLangForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Priority: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`priority` = ?") + params = append(params, c.getPriorityForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Deleted: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`deleted` = ?") + params = append(params, c.getDeletedForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `room` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/song_user_favourites/song_user_favourites_tx.go b/golalib/testdata/song_user_favourites/song_user_favourites_tx.go new file mode 100644 index 0000000..1608fb8 --- /dev/null +++ b/golalib/testdata/song_user_favourites/song_user_favourites_tx.go @@ -0,0 +1,148 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package song_user_favourites + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts SongUserFavourite struct to `song_user_favourites` table with transaction +func (c *SongUserFavourite) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getUserIdForDB(), c.getSongIdForDB(), c.getRemarkForDB(), c.getIsFavouriteForDB(), c.getCreatedAtForDB(), c.getUpdatedAtForDB()) + if err != nil { + return err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates SongUserFavourite struct in `song_user_favourites` table with transaction +func (obj *SongUserFavourite) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.IsFavourite.IsUpdated() { + updatedFields = append(updatedFields, "`is_favourite` = ?") + params = append(params, obj.getIsFavouriteForDB()) + } + if obj.CreatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, obj.getCreatedAtForDB()) + } + if obj.UpdatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, obj.getUpdatedAtForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `song_user_favourites` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `song_id` = ?" + params = append(params, obj.GetUserId(), obj.GetSongId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates SongUserFavourite struct with given fields in `song_user_favourites` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *IsFavourite: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`is_favourite` = ?") + params = append(params, c.getIsFavouriteForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, c.getCreatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, c.getUpdatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `song_user_favourites` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `song_id` = ?" + params = append(params, obj.GetUserId(), obj.GetSongId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/songs/songs_tx.go b/golalib/testdata/songs/songs_tx.go new file mode 100644 index 0000000..4b3fc7c --- /dev/null +++ b/golalib/testdata/songs/songs_tx.go @@ -0,0 +1,181 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package songs + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Song struct to `songs` table with transaction +func (c *Song) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getTitleForDB(), c.getRankForDB(), c.getTypeForDB(), c.getHashForDB(), c.getRemarkForDB(), c.getManifestForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getTitleForDB(), c.getRankForDB(), c.getTypeForDB(), c.getHashForDB(), c.getRemarkForDB(), c.getManifestForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = uint(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Song struct in `songs` table with transaction +func (obj *Song) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Title.IsUpdated() { + updatedFields = append(updatedFields, "`title` = ?") + params = append(params, obj.getTitleForDB()) + } + if obj.Rank.IsUpdated() { + updatedFields = append(updatedFields, "`rank` = ?") + params = append(params, obj.getRankForDB()) + } + if obj.Type.IsUpdated() { + updatedFields = append(updatedFields, "`type` = ?") + params = append(params, obj.getTypeForDB()) + } + if obj.Hash.IsUpdated() { + updatedFields = append(updatedFields, "`hash` = ?") + params = append(params, obj.getHashForDB()) + } + if obj.Remark.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, obj.getRemarkForDB()) + } + if obj.Manifest.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, obj.getManifestForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `songs` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Song struct with given fields in `songs` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Title: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`title` = ?") + params = append(params, c.getTitleForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Rank: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`rank` = ?") + params = append(params, c.getRankForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Type: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`type` = ?") + params = append(params, c.getTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Hash: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`hash` = ?") + params = append(params, c.getHashForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Remark: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`remark` = ?") + params = append(params, c.getRemarkForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Manifest: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`manifest` = ?") + params = append(params, c.getManifestForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `songs` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/users.sql b/golalib/testdata/users.sql index 8eeb55e..d07bef4 100644 --- a/golalib/testdata/users.sql +++ b/golalib/testdata/users.sql @@ -8,9 +8,9 @@ CREATE TABLE `users` ( `double_type` double NOT NULL DEFAULT 0.0 COMMENT 'double', `hobby` enum ('swimming', 'running', 'singing') NOT NULL DEFAULT 'swimming' COMMENT 'user hobby', `hobby_no_default` enum ('swimming', 'running', 'singing') NOT NULL COMMENT 'user hobby', - `sports` SET('SWIM', 'TENNIS', 'BASKETBALL', 'FOOTBALL', 'SQUASH', 'BADMINTON') NOT NULL DEFAULT "SWIM,FOOTBALL" COMMENT 'user sports', - `sports2` SET('SWIM', 'TENNIS', 'BASKETBALL', 'FOOTBALL', 'SQUASH', 'BADMINTON') NOT NULL DEFAULT "SWIM,FOOTBALL" COMMENT 'user sports', - `sports_no_default` SET('SWIM', 'TENNIS', 'BASKETBALL', 'FOOTBALL', 'SQUASH', 'BADMINTON') NOT NULL COMMENT 'user sports', + `sports` SET('swim', 'tennis', 'basketball', 'football', 'squash', 'badminton') NOT NULL DEFAULT "swim,football" COMMENT 'user sports', + `sports2` SET('swim', 'tennis', 'basketball', 'football', 'squash', 'badminton') NOT NULL DEFAULT "swim,football" COMMENT 'user sports', + `sports_no_default` SET('swim', 'tennis', 'basketball', 'football', 'squash', 'badminton') NOT NULL COMMENT 'user sports', PRIMARY KEY (`id`), KEY `name` (`name`), UNIQUE KEY `email` (`email`) diff --git a/golalib/testdata/users/users_tx.go b/golalib/testdata/users/users_tx.go new file mode 100644 index 0000000..05dcc41 --- /dev/null +++ b/golalib/testdata/users/users_tx.go @@ -0,0 +1,231 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package users + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts User struct to `users` table with transaction +func (c *User) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getEmailForDB(), c.getCreatedAtForDB(), c.getUpdatedAtForDB(), c.getFloatTypeForDB(), c.getDoubleTypeForDB(), c.getHobbyForDB(), c.getHobbyNoDefaultForDB(), c.getSportsForDB(), c.getSports2ForDB(), c.getSportsNoDefaultForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getEmailForDB(), c.getCreatedAtForDB(), c.getUpdatedAtForDB(), c.getFloatTypeForDB(), c.getDoubleTypeForDB(), c.getHobbyForDB(), c.getHobbyNoDefaultForDB(), c.getSportsForDB(), c.getSports2ForDB(), c.getSportsNoDefaultForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = int(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates User struct in `users` table with transaction +func (obj *User) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.Email.IsUpdated() { + updatedFields = append(updatedFields, "`email` = ?") + params = append(params, obj.getEmailForDB()) + } + if obj.CreatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, obj.getCreatedAtForDB()) + } + if obj.UpdatedAt.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, obj.getUpdatedAtForDB()) + } + if obj.FloatType.IsUpdated() { + updatedFields = append(updatedFields, "`float_type` = ?") + params = append(params, obj.getFloatTypeForDB()) + } + if obj.DoubleType.IsUpdated() { + updatedFields = append(updatedFields, "`double_type` = ?") + params = append(params, obj.getDoubleTypeForDB()) + } + if obj.Hobby.IsUpdated() { + updatedFields = append(updatedFields, "`hobby` = ?") + params = append(params, obj.getHobbyForDB()) + } + if obj.HobbyNoDefault.IsUpdated() { + updatedFields = append(updatedFields, "`hobby_no_default` = ?") + params = append(params, obj.getHobbyNoDefaultForDB()) + } + if obj.Sports.IsUpdated() { + updatedFields = append(updatedFields, "`sports` = ?") + params = append(params, obj.getSportsForDB()) + } + if obj.Sports2.IsUpdated() { + updatedFields = append(updatedFields, "`sports2` = ?") + params = append(params, obj.getSports2ForDB()) + } + if obj.SportsNoDefault.IsUpdated() { + updatedFields = append(updatedFields, "`sports_no_default` = ?") + params = append(params, obj.getSportsNoDefaultForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `users` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates User struct with given fields in `users` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Email: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`email` = ?") + params = append(params, c.getEmailForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *CreatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`created_at` = ?") + params = append(params, c.getCreatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *UpdatedAt: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`updated_at` = ?") + params = append(params, c.getUpdatedAtForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *FloatType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`float_type` = ?") + params = append(params, c.getFloatTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *DoubleType: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`double_type` = ?") + params = append(params, c.getDoubleTypeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Hobby: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`hobby` = ?") + params = append(params, c.getHobbyForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *HobbyNoDefault: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`hobby_no_default` = ?") + params = append(params, c.getHobbyNoDefaultForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Sports: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`sports` = ?") + params = append(params, c.getSportsForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Sports2: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`sports2` = ?") + params = append(params, c.getSports2ForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *SportsNoDefault: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`sports_no_default` = ?") + params = append(params, c.getSportsNoDefaultForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `users` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/wallet/wallet_tx.go b/golalib/testdata/wallet/wallet_tx.go new file mode 100644 index 0000000..894031c --- /dev/null +++ b/golalib/testdata/wallet/wallet_tx.go @@ -0,0 +1,128 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package wallet + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Wallet struct to `wallet` table with transaction +func (c *Wallet) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getUserIdForDB(), c.getWalletTypeForDB(), c.getWalletNameForDB(), c.getMoneyForDB()) + if err != nil { + return err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Wallet struct in `wallet` table with transaction +func (obj *Wallet) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.WalletName.IsUpdated() { + updatedFields = append(updatedFields, "`wallet_name` = ?") + params = append(params, obj.getWalletNameForDB()) + } + if obj.Money.IsUpdated() { + updatedFields = append(updatedFields, "`money` = ?") + params = append(params, obj.getMoneyForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `wallet` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `wallet_type` = ?" + params = append(params, obj.GetUserId(), obj.GetWalletType()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Wallet struct with given fields in `wallet` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *WalletName: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`wallet_name` = ?") + params = append(params, c.getWalletNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Money: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`money` = ?") + params = append(params, c.getMoneyForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `wallet` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `user_id` = ? and `wallet_type` = ?" + params = append(params, obj.GetUserId(), obj.GetWalletType()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/golalib/testdata/worker/worker_tx.go b/golalib/testdata/worker/worker_tx.go new file mode 100644 index 0000000..3dedc43 --- /dev/null +++ b/golalib/testdata/worker/worker_tx.go @@ -0,0 +1,141 @@ +// Code generated by gola 0.1.1; DO NOT EDIT. + +package worker + +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" +) + +// InsertTx inserts Worker struct to `worker` table with transaction +func (c *Worker) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + if c.Id.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, c.getIdForDB(), c.getNameForDB(), c.getAgeForDB()) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, c.getNameForDB(), c.getAgeForDB()) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.Id.val = int(id) + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates Worker struct in `worker` table with transaction +func (obj *Worker) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + if obj.Name.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, obj.getNameForDB()) + } + if obj.Age.IsUpdated() { + updatedFields = append(updatedFields, "`age` = ?") + params = append(params, obj.getAgeForDB()) + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `worker` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates Worker struct with given fields in `worker` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + case *Name: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`name` = ?") + params = append(params, c.getNameForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + case *Age: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`age` = ?") + params = append(params, c.getAgeForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `worker` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE `id` = ?" + params = append(params, obj.GetId()) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/ormtpl/00_struct_tx.gogo b/ormtpl/00_struct_tx.gogo new file mode 100644 index 0000000..04be3aa --- /dev/null +++ b/ormtpl/00_struct_tx.gogo @@ -0,0 +1,150 @@ +// Code generated by gola {{.GetVersion}}; DO NOT EDIT. + +package {{.Package}} +{{$table := .}} +import ( + "context" + "database/sql" + "reflect" + "strings" + + "github.com/olachat/gola/v2/coredb/txengine" + "github.com/olachat/gola/v2/coredb" +) + +// InsertTx inserts {{.ClassName}} struct to `{{.Name}}` table with transaction +func (c *{{.ClassName}}) InsertTx(ctx context.Context, tx *sql.Tx) error { + var result sql.Result + var err error + + {{- if .IsPKAutoGenerated }} + if c.{{.GetPrimaryKey}}.isAssigned { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithPK, {{- range $i, $c := .Columns }} + {{- if $i}}, {{end}}c.get{{$c.GoName}}ForDB() + {{- end }}) + if err != nil { + return err + } + } else { + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, {{- range $i, $c := .GetNonAutoIncrementColumns }} + {{- if $i}}, {{end}}c.get{{$c.GoName}}ForDB() + {{- end }}) + if err != nil { + return err + } + + id, err := result.LastInsertId() + if err != nil { + return err + } + c.{{.GetPrimaryKey}}.val = {{.GetPrimaryKeyType}}(id) + } + {{else}} + result, err = txengine.WithTx(tx).Exec(ctx, insertWithoutPK, {{- range $i, $c := .GetNonAutoIncrementColumns }} + {{- if $i}}, {{end}}c.get{{$c.GoName}}ForDB() + {{- end }}) + if err != nil { + return err + } + {{- end}} + + affectedRows, err := result.RowsAffected() + if err != nil { + return err + } + if affectedRows == 0 { + return coredb.ErrAvoidInsert + } + + c.resetUpdated() + return nil +} + +// UpdateTx updates {{.ClassName}} struct in `{{.Name}}` table with transaction +func (obj *{{.ClassName}}) UpdateTx(ctx context.Context, tx *sql.Tx) (bool, error) { + var updatedFields []string + var params []any + + {{- range $i, $c := .GetNonPKColumns }} + if obj.{{ $c.GoTypeName }}.IsUpdated() { + updatedFields = append(updatedFields, "`{{ $c.Name }}` = ?") + params = append(params, obj.get{{$c.GoName}}ForDB()) + } + {{- end }} + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `{{.Name}}` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE {{.GetPrimaryKeySQL}}" + params = append(params, {{.GetPrimaryKeyParams}}) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + obj.resetUpdated() + return true, nil +} + +// UpdateTx updates {{.ClassName}} struct with given fields in `{{.Name}}` table with transaction +func UpdateTx(ctx context.Context, tx *sql.Tx, obj withPK) (bool, error) { + var updatedFields []string + var params []any + var resetFuncs []func() + + val := reflect.ValueOf(obj).Elem() + updatedFields = make([]string, 0, val.NumField()) + params = make([]any, 0, val.NumField()) + + for i := 0; i < val.NumField(); i++ { + col := val.Field(i).Addr().Interface() + + switch c := col.(type) { + {{- range $i, $c := .GetNonPKColumns }} + case *{{.GoTypeName}}: + if c.IsUpdated() { + updatedFields = append(updatedFields, "`{{ $c.Name }}` = ?") + params = append(params, c.get{{$c.GoName}}ForDB()) + resetFuncs = append(resetFuncs, c.resetUpdated) + } + {{- end }} + } + } + + if len(updatedFields) == 0 { + return false, nil + } + + sql := "UPDATE `{{.Name}}` SET " + sql = sql + strings.Join(updatedFields, ",") + " WHERE {{.GetPrimaryKeySQL}}" + params = append(params, {{.GetPrimaryKeyParams}}) + + result, err := txengine.WithTx(tx).Exec(ctx, sql, params...) + if err != nil { + return false, err + } + + affectedRows, err := result.RowsAffected() + if err != nil { + return false, err + } + if affectedRows == 0 { + return false, coredb.ErrAvoidUpdate + } + + for _, f := range resetFuncs { + f() + } + return true, nil +} diff --git a/tests/blog_test.go b/tests/blog_test.go index 31e2dd8..f7a79f7 100644 --- a/tests/blog_test.go +++ b/tests/blog_test.go @@ -1,8 +1,14 @@ package tests import ( + "context" + "database/sql" + "errors" + "fmt" "testing" + "github.com/olachat/gola/v2/coredb" + "github.com/olachat/gola/v2/coredb/txengine" "github.com/olachat/gola/v2/golalib/testdata/blogs" ) @@ -39,6 +45,7 @@ func TestBlogMethods(t *testing.T) { blog = blogs.New() blog.SetTitle("bar") blog.SetCount(88) + blog.SetSlug("slug") e = blog.Insert() if e != nil { t.Error(e) @@ -84,6 +91,128 @@ func TestBlogFind(t *testing.T) { } } +func TestBlogTx(t *testing.T) { + ctx := context.Background() + tx, err := coredb.BeginTx(ctx, blogs.DBName, &coredb.DefaultTxOpts) + if err != nil { + t.Fatalf("fail to start tx: %v", err) + } + err = txengine.StartTx(ctx, tx, func(ctx context.Context, sqlTx *sql.Tx) error { + blogRec, err := txengine.WithTypedTx[blogs.Blog](sqlTx).FindOne(ctx, blogs.TableName, coredb.NewWhere("where title = ?", "bar")) + if err != nil { + return err + } + if blogRec.GetId() != 3 { + t.Error("Find blog with title bar failed") + } + blogRec.SetCountry("GuaGua") + updateOk, err := blogRec.UpdateTx(ctx, sqlTx) + if err != nil { + return err + } + if !updateOk { + t.Error("fail to update blog in tx") + } + c, err := txengine.WithTx(sqlTx).QueryInt(ctx, "select count(*) from blogs where country = ?", "GuaGua") + if err != nil { + return fmt.Errorf("QueryInt failed: %w", err) + } + if c != 1 { + t.Error("expect count to be 1") + } + newBlog := blogs.New() + newBlog.SetCountry("GuaGua") + newBlog.SetSlug("oldSlug") + err = newBlog.InsertTx(ctx, sqlTx) + if err != nil { + return fmt.Errorf("fail to insert with Tx") + } + newBlog.SetSlug("slugadded123") + _, err = newBlog.UpdateTx(ctx, sqlTx) + if err != nil { + return fmt.Errorf("update failed: %w", err) + } + return nil + }) + if err != nil { + t.Fatal("error encountered", err) + } + obj := blogs.FindOneFromMaster("where title = ?", "bar") + if obj.GetId() != 3 { + t.Error("Find blog with title bar failed") + } + if obj.GetCountry() != "GuaGua" { + t.Error("blog not updated") + } + recs, err := blogs.FindCtx(ctx, "where country=? order by id desc", "GuaGua") + if err != nil { + t.Error(err) + } + if len(recs) != 2 { + t.Error("insertTx must have failed") + } + if recs[0].GetSlug() != "slugadded123" { + t.Error("wrong slug gotten") + } + + // start a new transaction + tx, err = coredb.BeginTx(ctx, blogs.DBName, &coredb.DefaultTxOpts) + if err != nil { + t.Fatalf("fail to start tx: %v", err) + } + err = txengine.StartTx(ctx, tx, func(ctx context.Context, sqlTx *sql.Tx) error { + blogRec, err := txengine.WithTypedTx[blogs.Blog](sqlTx).FindOne(ctx, blogs.TableName, coredb.NewWhere("where title = ?", "bar")) + if err != nil { + return err + } + if blogRec.GetId() != 3 { + t.Error("Find blog with title bar failed") + } + blogRec.SetCountry("PuaPua") + updateOk, err := blogRec.UpdateTx(ctx, sqlTx) + if err != nil { + return err + } + if !updateOk { + t.Error("fail to update blog in tx") + } + return errors.New("rollback") + }) + if err == nil { + t.Fatal("should get rollback error but err is nil") + } + // currently there is no way to test rollback using dolt in memory DB as transaction is not supported + + // start a new transaction + tx, err = coredb.BeginTx(ctx, blogs.DBName, &coredb.DefaultTxOpts) + if err != nil { + t.Fatalf("fail to start tx: %v", err) + } + err = txengine.StartTx(ctx, tx, func(ctx context.Context, sqlTx *sql.Tx) error { + blogRecs, err := txengine.WithTypedTx[struct { + blogs.Id + }](sqlTx).Find(ctx, blogs.TableName, coredb.NewWhere("where slug = ?", "slugadded123")) + if err != nil { + return err + } + if len(blogRecs) != 1 { + t.Errorf("expected to have 1 blog") + return errors.New("expected to have 1 blog") + } + id := blogRecs[0].GetId() + _, err = txengine.WithTx(sqlTx).Exec(ctx, "delete from blogs where id=?", id) + return err + }) + if err != nil { + t.Fatalf("tx delete failed") + } + r := blogs.FindOne("where slug = ?", "slugadded123") + if r != nil { + t.Error("r should already been deleted") + } + +} + func TestBlogFindT(t *testing.T) { obj := blogs.FindOneFieldsFromMaster[struct { blogs.Id diff --git a/tests/gifts_nn_test.go b/tests/gifts_nn_test.go index 7bf423f..5ee3bf3 100644 --- a/tests/gifts_nn_test.go +++ b/tests/gifts_nn_test.go @@ -145,7 +145,7 @@ func TestGiftNotNullInsertRetrieveUpdate(t *testing.T) { gOut.SetName("gift 2") gOut.SetPrice(65.555) gOut.SetRemark("remark 2") - now2 := time.Now().UTC().Truncate(time.Microsecond) + now2 := time.Now().UTC().Truncate(time.Second) gOut.SetUpdateTime(now2) ok, err := gOut.Update() if err != nil { @@ -198,7 +198,7 @@ func TestGiftNotNullInsertRetrieveUpdate(t *testing.T) { if g22.GetRemark() != "remark 2" { t.Error("wrong remark") } - if g22.GetUpdateTime() != now2 { - t.Error("wrong update time") + if !g22.GetUpdateTime().Equal(now2) { + t.Errorf("wrong update time. expected %s, gotten %s", now2, g22.GetUpdateTime()) } } diff --git a/tests/gifts_nn_with_default_test.go b/tests/gifts_nn_with_default_test.go index 6ca3cd4..66aa0f6 100644 --- a/tests/gifts_nn_with_default_test.go +++ b/tests/gifts_nn_with_default_test.go @@ -69,12 +69,12 @@ func assertDefaultGiftNnWithDefault(t *testing.T, gift *gifts_nn_with_default.Gi t.Error("wrong remark") } - ti, err := time.Parse("2006-01-02 15:04:05.999999", "2023-01-19 03:14:07.999999") + ti, err := time.Parse("2006-01-02 15:04:05.0", "2023-01-19 03:14:07.0") if err != nil { panic(err) } if !gift.GetUpdateTime().Equal(ti) { - t.Error("wrong update time") + t.Error("wrong update time", ti, gift.GetUpdateTime()) } fmt.Println(gift.GetUpdateTime2()) } @@ -118,7 +118,7 @@ func TestGiftNotNullWithDefaultInsertRetrieveUpdate(t *testing.T) { gift.SetName("gift 2") gift.SetPrice(65.555) gift.SetRemark("remark 2") - now2 := time.Now().UTC().Truncate(time.Microsecond) + now2 := time.Now().UTC().Truncate(time.Second) gift.SetUpdateTime(now2) ok, err := gift.Update() if err != nil { diff --git a/tests/gifts_test.go b/tests/gifts_test.go index 1a7b706..fc26464 100644 --- a/tests/gifts_test.go +++ b/tests/gifts_test.go @@ -182,7 +182,7 @@ func TestInsertRetrieveUpdate(t *testing.T) { g2.SetName(goption.Some("xmas gift")) g2.SetPrice(goption.Some(15.5)) g2.SetRemark(goption.Some("selling out soon")) - now := time.Now().UTC().Truncate(time.Microsecond) + now := time.Now().UTC().Truncate(time.Second) g2.SetUpdateTime(goption.Some(now)) err = g2.Insert() if err != nil { @@ -195,7 +195,7 @@ func TestInsertRetrieveUpdate(t *testing.T) { j2, _ := json.Marshal(g2) j2Out, _ := json.Marshal(g2out) if string(j2) != string(j2Out) { - t.Fatalf("gift fetched is not as expected") + t.Fatalf("gift fetched is not as expected %s %s", string(j2), string(j2Out)) } g2out.SetBranches(goption.Some([]gifts.GiftBranches{ @@ -212,7 +212,7 @@ func TestInsertRetrieveUpdate(t *testing.T) { g2out.SetName(goption.Some("gift 2")) g2out.SetPrice(goption.Some(65.555)) g2out.SetRemark(goption.Some("remark 2")) - now2 := time.Now().UTC().Truncate(time.Microsecond) + now2 := time.Now().UTC().Truncate(time.Second) g2out.SetUpdateTime(goption.Some(now2)) ok, err := g2out.Update() if err != nil { diff --git a/tests/gifts_with_default_test.go b/tests/gifts_with_default_test.go index 758fc9f..9bafb44 100644 --- a/tests/gifts_with_default_test.go +++ b/tests/gifts_with_default_test.go @@ -70,7 +70,7 @@ func assertDefaultGiftWithDefault(t *testing.T, gift *gifts_with_default.GiftsWi t.Error("wrong remark") } - ti, err := time.Parse("2006-01-02 15:04:05.999999", "2023-01-19 03:14:07.999999") + ti, err := time.Parse("2006-01-02 15:04:05.999999", "2023-01-19 03:14:07.0") if err != nil { panic(err) } @@ -175,7 +175,7 @@ func TestGiftWithDefaultInsertRetrieveUpdate(t *testing.T) { gOut.SetName(goption.Some("gift 2")) gOut.SetPrice(goption.Some(65.555)) gOut.SetRemark(goption.Some("remark 2")) - now2 := time.Now().UTC().Truncate(time.Microsecond) + now2 := time.Now().UTC().Truncate(time.Second) gOut.SetUpdateTime(goption.Some(now2)) ok, err := gOut.Update() if err != nil { diff --git a/tests/user_test.go b/tests/user_test.go index f4ed9c8..4f87757 100644 --- a/tests/user_test.go +++ b/tests/user_test.go @@ -70,13 +70,18 @@ func init() { db.Exec(string(query)) } + _, err = db.Exec("SET autocommit = 0") + if err != nil { + panic("fail to set autocommit mode") + } + // add data _, err = db.Exec(` insert into users (name, email, created_at, updated_at, float_type, double_type, hobby, hobby_no_default, sports_no_default, sports) values -("John Doe", "john@doe.com", NOW(), NOW(), 1.55555, 1.8729, 'running','swimming', ('SWIM,TENNIS'), ("TENNIS")), -("John Doe", "johnalt@doe.com", NOW(), NOW(), 2.5, 2.8239, 'swimming','running', ('BASKETBALL'), ("FOOTBALL")), -("Jane Doe", "jane@doe.com", NOW(), NOW(), 3.5, 334.8593, 'singing','swimming', ('SQUASH,BADMINTON'), ("SQUASH,TENNIS")), -("Evil Bob", "evilbob@gmail.com", NOW(), NOW(), 4.5, 42234.83, 'singing','running', 'tennis', 'BADMINTON,BASKETBALL') +("John Doe", "john@doe.com", NOW(), NOW(), 1.55555, 1.8729, 'running','swimming', ('swim,tennis'), ("tennis")), +("John Doe", "johnalt@doe.com", NOW(), NOW(), 2.5, 2.8239, 'swimming','running', ('basketball'), ("football")), +("Jane Doe", "jane@doe.com", NOW(), NOW(), 3.5, 334.8593, 'singing','swimming', ('squash,badminton'), ("squash,tennis")), +("Evil Bob", "evilbob@gmail.com", NOW(), NOW(), 4.5, 42234.83, 'singing','running', 'tennis', 'badminton,basketball') `) if err != nil { panic("insert failed " + err.Error())