Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

UpdateQuery: marshal false as BOOLEAN if nullzero tag not set #937

Merged
merged 5 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions internal/dbtest/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,20 +331,27 @@ func TestQuery(t *testing.T) {
Where("model.id = _data.id")
},
func(db *bun.DB) schema.QueryAppender {
// "nullzero" marshals zero values as DEFAULT or NULL (if DEFAULT placeholder is not supported)
// DB drivers which support DEFAULT placeholder resolve it to NULL for columns that do not have a DEFAULT value.
type Model struct {
Int int64 `bun:",nullzero"`
Uint uint64 `bun:",nullzero"`
Str string `bun:",nullzero"`
Time time.Time `bun:",nullzero"`
Int int64 `bun:",nullzero"`
Uint uint64 `bun:",nullzero"`
Str string `bun:",nullzero"`
Time time.Time `bun:",nullzero"`
Bool bool `bun:",nullzero"`
EmptyStr string `bun:",nullzero"` // same as Str
}
return db.NewInsert().Model(new(Model))
},
func(db *bun.DB) schema.QueryAppender {
// "nullzero,default" is equivalent to "default", marshalling zero values to DEFAULT
type Model struct {
Int int64 `bun:",nullzero,default:42"`
Uint uint64 `bun:",nullzero,default:42"`
Str string `bun:",nullzero,default:'hello'"`
Time time.Time `bun:",nullzero,default:now()"`
Int int64 `bun:",nullzero,default:42"`
Uint uint64 `bun:",nullzero,default:42"`
Str string `bun:",nullzero,default:'hello'"`
Time time.Time `bun:",nullzero,default:now()"`
Bool bool `bun:",nullzero,default:true"`
EmptyStr string `bun:",nullzero,default:''"`
}
return db.NewInsert().Model(new(Model))
},
Expand Down Expand Up @@ -1016,6 +1023,28 @@ func TestQuery(t *testing.T) {
_ = q.String()
return q
},
func(db *bun.DB) schema.QueryAppender {
return db.NewUpdate().Model(&struct {
bun.BaseModel `bun:"table:accounts"`
ID int `bun:"id,pk,autoincrement"`
IsActive bool `bun:"is_active,notnull,default:true"`
}{
ID: 1,
IsActive: false,
}).Column("is_active").WherePK()
},
func(db *bun.DB) schema.QueryAppender {
// "default" marshals zero values as DEFAULT or the specified default value
type Model struct {
Int int64 `bun:",default:42"`
Uint uint64 `bun:",default:42"`
Str string `bun:",default:'hello'"`
Time time.Time `bun:",default:now()"`
Bool bool `bun:",default:true"`
EmptyStr string `bun:",default:''"`
}
return db.NewInsert().Model(new(Model))
},
}

timeRE := regexp.MustCompile(`'2\d{3}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?(\+\d{2}:\d{2})?'`)
Expand Down
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mariadb-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE `accounts` SET `is_active` = FALSE WHERE (`accounts`.`id` = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mariadb-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING `int`, `uint`, `str`, `time`, `bool`, `empty_str`
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mariadb-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING `int`, `uint`, `str`, `time`
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING `int`, `uint`, `str`, `time`, `bool`, `empty_str`
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mariadb-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING `int`, `uint`, `str`, `time`
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING `int`, `uint`, `str`, `time`, `bool`, `empty_str`
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mssql2019-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE "accounts" SET "is_active" = 0 WHERE ("id" = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mssql2019-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mssql2019-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mssql2019-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mysql5-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE `accounts` SET `is_active` = FALSE WHERE (`accounts`.`id` = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mysql5-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mysql5-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mysql5-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mysql8-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE `accounts` SET `is_active` = FALSE WHERE (`accounts`.`id` = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-mysql8-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mysql8-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-mysql8-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO `models` (`int`, `uint`, `str`, `time`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT)
INSERT INTO `models` (`int`, `uint`, `str`, `time`, `bool`, `empty_str`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-pg-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE "accounts" AS "accounts" SET "is_active" = FALSE WHERE ("accounts"."id" = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-pg-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-pg-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-pg-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-pgx-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE "accounts" AS "accounts" SET "is_active" = FALSE WHERE ("accounts"."id" = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-pgx-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-pgx-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-pgx-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-sqlite-165
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPDATE "accounts" AS "accounts" SET "is_active" = FALSE WHERE ("accounts"."id" = 1)
1 change: 1 addition & 0 deletions internal/dbtest/testdata/snapshots/TestQuery-sqlite-166
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (42, 42, 'hello', now(), true, '') RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-sqlite-51
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (NULL, NULL, NULL, NULL) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (NULL, NULL, NULL, NULL, NULL, NULL) RETURNING "int", "uint", "str", "time", "bool", "empty_str"
2 changes: 1 addition & 1 deletion internal/dbtest/testdata/snapshots/TestQuery-sqlite-52
Original file line number Diff line number Diff line change
@@ -1 +1 @@
INSERT INTO "models" ("int", "uint", "str", "time") VALUES (42, 42, 'hello', now()) RETURNING "int", "uint", "str", "time"
INSERT INTO "models" ("int", "uint", "str", "time", "bool", "empty_str") VALUES (42, 42, 'hello', now(), true, '') RETURNING "int", "uint", "str", "time", "bool", "empty_str"
19 changes: 12 additions & 7 deletions query_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ func (q *InsertQuery) appendStructValues(
switch {
case isTemplate:
b = append(b, '?')
case (f.IsPtr && f.HasNilValue(strct)) || (f.NullZero && f.HasZeroValue(strct)):
if q.db.features.Has(feature.DefaultPlaceholder) {
case q.marshalsToDefault(f, strct):
if q.db.HasFeature(feature.DefaultPlaceholder) {
b = append(b, "DEFAULT"...)
} else if f.SQLDefault != "" {
b = append(b, f.SQLDefault...)
Expand Down Expand Up @@ -410,18 +410,23 @@ func (q *InsertQuery) getFields() ([]*schema.Field, error) {
q.addReturningField(f)
continue
}
if f.NotNull && f.SQLDefault == "" {
if (f.IsPtr && f.HasNilValue(strct)) || (f.NullZero && f.HasZeroValue(strct)) {
q.addReturningField(f)
continue
}
if f.NotNull && q.marshalsToDefault(f, strct) {
q.addReturningField(f)
continue
}
fields = append(fields, f)
}

return fields, nil
}

// marshalsToDefault checks if the value will be marshaled as DEFAULT or NULL (if DEFAULT placeholder is not supported)
// when appending it to the VALUES clause in place of the given field.
func (q InsertQuery) marshalsToDefault(f *schema.Field, v reflect.Value) bool {
return (f.IsPtr && f.HasNilValue(v)) ||
(f.HasZeroValue(v) && (f.NullZero || f.SQLDefault != ""))
}

func (q *InsertQuery) appendFields(
fmter schema.Formatter, b []byte, fields []*schema.Field,
) []byte {
Expand Down
1 change: 0 additions & 1 deletion schema/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,6 @@ func (t *Table) newField(f reflect.StructField, prefix string, index []int) *Fie
}
if s, ok := tag.Option("default"); ok {
field.SQLDefault = s
field.NullZero = true
}
if s, ok := field.Tag.Option("type"); ok {
field.UserSQLType = s
Expand Down
Loading