Fix bug when json with a real SQLType in the tag (#2519)

Fix a bug when the tag have both `longtext json`. It should be `longtext` column type with JSON support.

Reviewed-on: https://gitea.com/xorm/xorm/pulls/2519
This commit is contained in:
Lunny Xiao 2025-07-21 18:19:39 +00:00
parent 89d1238248
commit b72e98f60e
5 changed files with 116 additions and 16 deletions

View File

@ -26,6 +26,7 @@ type Column struct {
FieldIndex []int // Available only when parsed from a struct
SQLType SQLType
IsJSON bool
IsJSONB bool
Length int64
Length2 int64
Nullable bool

View File

@ -250,10 +250,16 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, f
}
if col.SQLType.Name == "" {
var err error
col.SQLType, err = parser.getSQLTypeByType(field.Type)
if err != nil {
return nil, err
if col.IsJSONB { // check is jsonb first because it is also json
col.SQLType = schemas.SQLType{Name: schemas.Jsonb}
} else if col.IsJSON {
col.SQLType = schemas.SQLType{Name: schemas.Json}
} else {
var err error
col.SQLType, err = parser.getSQLTypeByType(field.Type)
if err != nil {
return nil, err
}
}
}
if ctx.isUnsigned && col.SQLType.IsNumeric() && !strings.HasPrefix(col.SQLType.Name, "UNSIGNED") {

View File

@ -577,7 +577,7 @@ func TestParseWithJSONB(t *testing.T) {
assert.EqualValues(t, "struct_with_jsonb", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "default1", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].IsJSON)
assert.True(t, table.Columns()[0].IsJSONB)
}
func TestParseWithSQLType(t *testing.T) {
@ -617,3 +617,53 @@ func TestParseWithSQLType(t *testing.T) {
assert.EqualValues(t, "DATETIME", table.Columns()[3].SQLType.Name)
assert.EqualValues(t, "UUID", table.Columns()[4].SQLType.Name)
}
func TestParseWithJSONLongText(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.GonicMapper{
"JSON": true,
},
names.GonicMapper{
"JSON": true,
},
caches.NewManager(),
)
type StructWithJSONLongText struct {
Col1 string `db:"LongText json"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithJSONLongText)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_json_long_text", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "col1", table.Columns()[0].Name)
assert.EqualValues(t, "LONGTEXT", table.Columns()[0].SQLType.Name)
assert.EqualValues(t, true, table.Columns()[0].IsJSON)
type StructWithJSONLongText2 struct {
Col1 string `db:"json"`
}
table, err = parser.Parse(reflect.ValueOf(new(StructWithJSONLongText2)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_json_long_text2", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "col1", table.Columns()[0].Name)
assert.EqualValues(t, "JSON", table.Columns()[0].SQLType.Name)
assert.EqualValues(t, true, table.Columns()[0].IsJSON)
type StructWithJSONLongText3 struct {
Col1 string `db:"jsonb"`
}
table, err = parser.Parse(reflect.ValueOf(new(StructWithJSONLongText3)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_json_long_text3", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "col1", table.Columns()[0].Name)
assert.EqualValues(t, "JSONB", table.Columns()[0].SQLType.Name)
assert.EqualValues(t, true, table.Columns()[0].IsJSONB)
}

View File

@ -124,10 +124,16 @@ var defaultTagHandlers = map[string]Handler{
"EXTENDS": ExtendsTagHandler,
"UNSIGNED": UnsignedTagHandler,
"COLLATE": CollateTagHandler,
"JSON": JSONTagHandler,
"JSONB": JSONBTagHandler,
}
func init() {
for k := range schemas.SqlTypes {
// don't override default tag handlers
if _, ok := defaultTagHandlers[k]; ok {
continue
}
defaultTagHandlers[k] = SQLTypeTagHandler
}
}
@ -293,12 +299,20 @@ func CollateTagHandler(ctx *Context) error {
return nil
}
func JSONTagHandler(ctx *Context) error {
ctx.col.IsJSON = true
return nil
}
func JSONBTagHandler(ctx *Context) error {
ctx.col.IsJSONB = true
ctx.col.IsJSON = true // jsonb is also json
return nil
}
// SQLTypeTagHandler describes SQL Type tag handler
func SQLTypeTagHandler(ctx *Context) error {
ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname}
if ctx.tagUname == "JSON" || ctx.tagUname == "JSONB" {
ctx.col.IsJSON = true
}
if len(ctx.params) == 0 {
return nil
}

View File

@ -752,20 +752,18 @@ func getKeysFromMap(m map[string]*schemas.Index) []string {
return ss
}
type SyncTestUser struct {
Id int64 `xorm:"pk autoincr 'id' comment('primary key 1')"`
Name string `xorm:"'name' notnull comment('nickname')" json:"name"`
Id int64 `xorm:"pk autoincr 'id' comment('primary key 1')"`
Name string `xorm:"'name' notnull comment('nickname')" json:"name"`
}
func (m *SyncTestUser) TableName() string {
return "sync_test_user"
}
type SyncTestUser2 struct {
Id int64 `xorm:"pk autoincr 'id' comment('primary key 2')"`
Name string `xorm:"'name' notnull comment('nickname')" json:"name"`
Id int64 `xorm:"pk autoincr 'id' comment('primary key 2')"`
Name string `xorm:"'name' notnull comment('nickname')" json:"name"`
}
func (m *SyncTestUser2) TableName() string {
@ -789,5 +787,36 @@ func TestSync2_3(t *testing.T) {
assert.EqualValues(t, tables[0].GetColumn("id").Comment, tableInfo.GetColumn("id").Comment)
}
}
}
func TestSyncJSON(t *testing.T) {
type SyncTestJSON struct {
Id int64
Value string `xorm:"LONGTEXT JSON 'value' comment('json value')"`
}
assert.NoError(t, PrepareEngine())
assertSync(t, new(SyncTestJSON))
assert.NoError(t, testEngine.Sync(new(SyncTestJSON)))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
tableInfo, err := testEngine.TableInfo(new(SyncTestJSON))
assert.NoError(t, err)
assert.EqualValues(t, tables[0].GetColumn("id").IsAutoIncrement, tableInfo.GetColumn("id").IsAutoIncrement)
assert.EqualValues(t, tables[0].GetColumn("id").Name, tableInfo.GetColumn("id").Name)
if testEngine.Dialect().URI().DBType == schemas.MYSQL {
assert.EqualValues(t, tables[0].GetColumn("id").SQLType.Name, tableInfo.GetColumn("id").SQLType.Name)
}
assert.EqualValues(t, tables[0].GetColumn("id").Nullable, tableInfo.GetColumn("id").Nullable)
assert.EqualValues(t, tables[0].GetColumn("value").IsAutoIncrement, tableInfo.GetColumn("value").IsAutoIncrement)
assert.EqualValues(t, tables[0].GetColumn("value").Name, tableInfo.GetColumn("value").Name)
assert.EqualValues(t, tables[0].GetColumn("value").Nullable, tableInfo.GetColumn("value").Nullable)
if testEngine.Dialect().URI().DBType == schemas.MYSQL {
assert.EqualValues(t, tables[0].GetColumn("value").SQLType.Name, tableInfo.GetColumn("value").SQLType.Name)
}
}