diff --git a/bun.go b/bun.go index 2a788c4c1..97514465c 100644 --- a/bun.go +++ b/bun.go @@ -81,6 +81,11 @@ func SetLogger(logger internal.Logging) { internal.Logger = logger } +// SetWarnLogger overwriters default Bun warn logger. +func SetWarnLogger(logger internal.Logging) { + internal.Warn = logger +} + //------------------------------------------------------------------------------ type InValues struct { diff --git a/internal/dbtest/db_test.go b/internal/dbtest/db_test.go index 8cf15d671..0f72b77bc 100644 --- a/internal/dbtest/db_test.go +++ b/internal/dbtest/db_test.go @@ -684,7 +684,7 @@ func testRunInTx(t *testing.T, db *bun.DB) { func testJSONSpecialChars(t *testing.T, db *bun.DB) { type Model struct { - ID int + ID int `bun:",pk"` Attrs map[string]interface{} `bun:"type:json"` } @@ -718,7 +718,7 @@ func testJSONSpecialChars(t *testing.T, db *bun.DB) { func testJSONInterface(t *testing.T, db *bun.DB) { type Model struct { - ID int + ID int `bun:",pk"` Value interface{} `bun:"type:json"` } @@ -762,7 +762,7 @@ func (v *JSONValue) Value() (driver.Value, error) { func testJSONValuer(t *testing.T, db *bun.DB) { type Model struct { - ID int + ID int `bun:",pk"` Value JSONValue `bun:"type:json"` } @@ -794,12 +794,12 @@ func testSelectBool(t *testing.T, db *bun.DB) { func testFKViolation(t *testing.T, db *bun.DB) { type Deck struct { - ID int + ID int `bun:",pk"` UserID int } type User struct { - ID int + ID int `bun:",pk"` } if db.Dialect().Name() == dialect.SQLite { @@ -959,7 +959,7 @@ func testInterfaceJSON(t *testing.T, db *bun.DB) { func testScanRawMessage(t *testing.T, db *bun.DB) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Value json.RawMessage } @@ -988,7 +988,7 @@ func testScanRawMessage(t *testing.T, db *bun.DB) { func testPointers(t *testing.T, db *bun.DB) { type Model struct { - ID *int64 `bun:",default:0"` + ID *int64 `bun:",pk,default:0"` Str *string } @@ -1046,7 +1046,7 @@ func testModelNonPointer(t *testing.T, db *bun.DB) { func testBinaryData(t *testing.T, db *bun.DB) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Data []byte } @@ -1066,7 +1066,7 @@ func testBinaryData(t *testing.T, db *bun.DB) { func testUpsert(t *testing.T, db *bun.DB) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Str string } @@ -1103,7 +1103,7 @@ func testMultiUpdate(t *testing.T, db *bun.DB) { } type Model struct { - ID int64 + ID int64 `bun:",pk"` Str string } @@ -1131,7 +1131,7 @@ func testMultiUpdate(t *testing.T, db *bun.DB) { func testTxScanAndCount(t *testing.T, db *bun.DB) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Str string } diff --git a/internal/dbtest/model_hook_test.go b/internal/dbtest/model_hook_test.go index 4757db95c..a611260d0 100644 --- a/internal/dbtest/model_hook_test.go +++ b/internal/dbtest/model_hook_test.go @@ -120,7 +120,7 @@ func testModelHook(t *testing.T, dbName string, db *bun.DB) { } type ModelHookTest struct { - ID int + ID int `bun:",pk"` Value string } diff --git a/internal/dbtest/orm_test.go b/internal/dbtest/orm_test.go index d40dcd9ce..0fd943268 100644 --- a/internal/dbtest/orm_test.go +++ b/internal/dbtest/orm_test.go @@ -348,7 +348,7 @@ func testRelationBelongsToSelf(t *testing.T, db *bun.DB) { type Model struct { bun.BaseModel `bun:"alias:m"` - ID int64 + ID int64 `bun:",pk"` ModelID int64 Model *Model `bun:"rel:belongs-to"` } @@ -374,13 +374,13 @@ func testRelationBelongsToSelf(t *testing.T, db *bun.DB) { func testM2MRelationExcludeColumn(t *testing.T, db *bun.DB) { type Item struct { - ID int64 + ID int64 `bun:",pk"` CreatedAt time.Time `bun:",notnull,nullzero"` UpdatedAt time.Time `bun:",notnull,nullzero"` } type Order struct { - ID int64 + ID int64 `bun:",pk"` Items []Item `bun:"m2m:order_to_items"` } @@ -430,7 +430,7 @@ func testM2MRelationExcludeColumn(t *testing.T, db *bun.DB) { } type Genre struct { - ID int + ID int `bun:",pk"` Name string Rating int `bun:",scanonly"` @@ -445,12 +445,12 @@ func (g Genre) String() string { } type Image struct { - ID int + ID int `bun:",pk"` Path string } type Author struct { - ID int + ID int `bun:",pk"` Name string `bun:",unique"` Books []*Book `bun:"rel:has-many"` @@ -480,7 +480,7 @@ type BookGenre struct { } type Book struct { - ID int + ID int `bun:",pk"` Title string AuthorID int Author Author `bun:"rel:belongs-to"` @@ -516,7 +516,7 @@ type BookWithCommentCount struct { type Translation struct { bun.BaseModel `bun:"alias:tr"` - ID int + ID int `bun:",pk"` BookID int `bun:"unique:book_id_lang"` Book *Book `bun:"rel:belongs-to"` Lang string `bun:"unique:book_id_lang"` diff --git a/internal/dbtest/pg_test.go b/internal/dbtest/pg_test.go index c4f9b1383..5b0e4943c 100644 --- a/internal/dbtest/pg_test.go +++ b/internal/dbtest/pg_test.go @@ -21,7 +21,7 @@ import ( func TestPGArray(t *testing.T) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Array1 []string `bun:",array"` Array2 *[]string `bun:",array"` Array3 *[]string `bun:",array"` @@ -96,7 +96,7 @@ func (h Hash) Value() (driver.Value, error) { func TestPGArrayValuer(t *testing.T) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Array []Hash `bun:",array"` } @@ -122,14 +122,14 @@ func TestPGArrayValuer(t *testing.T) { type Recipe struct { bun.BaseModel `bun:"?tenant.recipes"` - ID int + ID int `bun:",pk"` Ingredients []*Ingredient `bun:"m2m:?tenant.ingredients_recipes"` } type Ingredient struct { bun.BaseModel `bun:"?tenant.ingredients"` - ID int + ID int `bun:",pk"` Recipes []*Recipe `bun:"m2m:?tenant.ingredients_recipes"` } @@ -187,7 +187,7 @@ func TestPGMultiTenant(t *testing.T) { func TestPGInsertNoRows(t *testing.T) { type User struct { - ID int64 + ID int64 `bun:",pk"` } db := pg(t) @@ -287,7 +287,7 @@ func TestPGTransaction(t *testing.T) { db := pg(t) type Model struct { - ID int64 + ID int64 `bun:",pk"` } _, err := db.NewDropTable().Model((*Model)(nil)).IfExists().Exec(ctx) @@ -316,7 +316,7 @@ func TestPGScanWithoutResult(t *testing.T) { defer db.Close() type Model struct { - ID int64 + ID int64 `bun:",pk"` } err := db.ResetModel(ctx, (*Model)(nil)) @@ -428,7 +428,7 @@ func TestPGTimetz(t *testing.T) { func TestPGOnConflictDoUpdate(t *testing.T) { type Model struct { - ID int64 + ID int64 `bun:",pk"` UpdatedAt time.Time } diff --git a/internal/dbtest/query_test.go b/internal/dbtest/query_test.go index e6825ff66..1f377bc61 100644 --- a/internal/dbtest/query_test.go +++ b/internal/dbtest/query_test.go @@ -21,17 +21,17 @@ func init() { func TestQuery(t *testing.T) { type Model struct { - ID int64 + ID int64 `bun:",pk"` Str string } type User struct { - ID int64 + ID int64 `bun:",pk"` Name string } type Story struct { - ID int64 + ID int64 `bun:",pk"` Name string UserID int64 User *User `bun:"rel:belongs-to"` @@ -40,14 +40,14 @@ func TestQuery(t *testing.T) { type SoftDelete1 struct { bun.BaseModel `bun:"soft_deletes,alias:soft_delete"` - ID int64 + ID int64 `bun:",pk"` DeletedAt time.Time `bun:",soft_delete"` } type SoftDelete2 struct { bun.BaseModel `bun:"soft_deletes,alias:soft_delete"` - ID int64 + ID int64 `bun:",pk"` DeletedAt time.Time `bun:",soft_delete,allowzero"` } @@ -234,7 +234,7 @@ func TestQuery(t *testing.T) { }, func(db *bun.DB) schema.QueryAppender { type Model struct { - ID uint64 + ID uint64 `bun:",pk"` Struct struct{} Map map[string]interface{} Slice []string @@ -499,7 +499,7 @@ func TestQuery(t *testing.T) { }, func(db *bun.DB) schema.QueryAppender { type Model struct { - ID int64 + ID int64 `bun:",pk"` Str1 string Str2 string } @@ -555,7 +555,7 @@ func TestQuery(t *testing.T) { }, func(db *bun.DB) schema.QueryAppender { type Model struct { - ID int64 `bun:",allowzero"` + ID int64 `bun:",pk,allowzero"` } return db.NewInsert().Model(new(Model)) }, @@ -567,7 +567,7 @@ func TestQuery(t *testing.T) { }, func(db *bun.DB) schema.QueryAppender { type Model struct { - ID int64 + ID int64 `bun:",pk"` Time time.Time } return db.NewInsert().Model(&Model{ID: 123, Time: time.Unix(0, 0)}) @@ -635,7 +635,7 @@ func TestQuery(t *testing.T) { }, func(db *bun.DB) schema.QueryAppender { type Model struct { - ID int64 + ID int64 `bun:",pk"` Int int64 `bun:",nullzero"` Uint uint64 `bun:",nullzero"` Str string `bun:",nullzero"` @@ -652,7 +652,7 @@ func TestQuery(t *testing.T) { func(db *bun.DB) schema.QueryAppender { type ID string type Model struct { - ID + ID `bun:",pk"` } return db.NewInsert().Model(&Model{ID: ID("embed")}) }, @@ -677,7 +677,7 @@ func TestQuery(t *testing.T) { Bar string } type Model struct { - ID int64 + ID int64 `bun:",pk"` Slice []Item `bun:",nullzero"` } return db.NewInsert().Model(&Model{ID: 123, Slice: make([]Item, 0)}) diff --git a/internal/dbtest/soft_delete_test.go b/internal/dbtest/soft_delete_test.go index 5c8bb0781..d58232be2 100644 --- a/internal/dbtest/soft_delete_test.go +++ b/internal/dbtest/soft_delete_test.go @@ -31,9 +31,9 @@ func TestSoftDelete(t *testing.T) { } type Video struct { - ID int64 + ID int64 `bun:",pk"` Name string - DeletedAt time.Time `bun:",soft_delete"` + DeletedAt time.Time `bun:",soft_delete,nullzero"` } func testSoftDeleteNilModel(t *testing.T, db *bun.DB) { diff --git a/internal/logger.go b/internal/logger.go index 2e22a0893..52244c47c 100644 --- a/internal/logger.go +++ b/internal/logger.go @@ -6,9 +6,9 @@ import ( "os" ) -var Warn = log.New(os.Stderr, "WARN: bun: ", log.LstdFlags) +var Warn Logging = log.New(os.Stderr, "WARN: bun: ", log.LstdFlags) -var Deprecated = log.New(os.Stderr, "DEPRECATED: bun: ", log.LstdFlags) +var Deprecated Logging = log.New(os.Stderr, "DEPRECATED: bun: ", log.LstdFlags) type Logging interface { Printf(format string, v ...interface{}) diff --git a/migrate/migration.go b/migrate/migration.go index 7d2d318eb..849b72074 100644 --- a/migrate/migration.go +++ b/migrate/migration.go @@ -16,7 +16,7 @@ import ( type Migration struct { bun.BaseModel - ID int64 + ID int64 `bun:",pk"` Name string GroupID int64 MigratedAt time.Time `bun:",notnull,nullzero,default:current_timestamp"` diff --git a/migrate/migrator.go b/migrate/migrator.go index 447e4e9ca..931e0b1c2 100644 --- a/migrate/migrator.go +++ b/migrate/migrator.go @@ -341,7 +341,7 @@ func (m *Migrator) validate() error { //------------------------------------------------------------------------------ type migrationLock struct { - ID int64 + ID int64 `bun:",pk"` TableName string `bun:",unique"` } diff --git a/schema/table.go b/schema/table.go index f8e911064..df04d620c 100644 --- a/schema/table.go +++ b/schema/table.go @@ -208,6 +208,10 @@ func (t *Table) initFields() { if len(t.PKs) == 0 { for _, name := range []string{"id", "uuid", "pk_" + t.ModelName} { if field, ok := t.FieldMap[name]; ok { + if !field.Tag.HasOption("nopk") { + internal.Warn.Printf("missing `bun:\",pk\" on %s.%s field`", + t.TypeName, field.GoName) + } field.markAsPK() t.PKs = []*Field{field} t.DataFields = removeField(t.DataFields, field) @@ -223,8 +227,10 @@ func (t *Table) initFields() { } switch pk.IndirectType.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + case reflect.Int, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint32, reflect.Uint64: + internal.Warn.Printf("missing `bun:\",autoincrement\" on %s.%s field`", + t.TypeName, pk.GoName) pk.AutoIncrement = true } } @@ -416,6 +422,10 @@ func (t *Table) newField(f reflect.StructField, prefix string, index []int) *Fie } if _, ok := tag.Options["soft_delete"]; ok { + if !tag.HasOption("allowzero") { + internal.Warn.Printf("missing `bun:\",nullzero\" on %s.%s field`", + t.TypeName, field.GoName) + } field.NullZero = true t.SoftDeleteField = field t.UpdateSoftDeleteField = softDeleteFieldUpdater(field) @@ -887,6 +897,7 @@ func isKnownFieldOption(name string) bool { "scanonly", "pk", + "nopk", "autoincrement", "rel", "join",