Fix default value parse bugs #1437

Merged
lunny merged 6 commits from lunny/parse_default into master 2019-09-29 04:31:06 +00:00
10 changed files with 321 additions and 131 deletions

View File

@ -338,6 +338,7 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{} args := []interface{}{}
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable, s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
"default_is_null" = (CASE WHEN c.text is null THEN 1 ELSE 0 END),
replace(replace(isnull(c.text,''),'(',''),')','') as vdefault, replace(replace(isnull(c.text,''),'(',''),')','') as vdefault,
ISNULL(i.is_primary_key, 0) ISNULL(i.is_primary_key, 0)
from sys.columns a from sys.columns a
@ -361,8 +362,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
for rows.Next() { for rows.Next() {
var name, ctype, vdefault string var name, ctype, vdefault string
var maxLen, precision, scale int var maxLen, precision, scale int
var nullable, isPK bool var nullable, isPK, defaultIsNull bool
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault, &isPK) err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -371,7 +372,10 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
col.Indexes = make(map[string]int) col.Indexes = make(map[string]int)
col.Name = strings.Trim(name, "` ") col.Name = strings.Trim(name, "` ")
col.Nullable = nullable col.Nullable = nullable
col.Default = vdefault col.DefaultIsEmpty = defaultIsNull
if !defaultIsNull {
col.Default = vdefault
}
col.IsPrimaryKey = isPK col.IsPrimaryKey = isPK
ct := strings.ToUpper(ctype) ct := strings.ToUpper(ctype)
if ct == "DECIMAL" { if ct == "DECIMAL" {
@ -395,15 +399,6 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
} }
} }
if col.SQLType.IsText() || col.SQLType.IsTime() {
if col.Default != "" {
col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
}
}
cols[col.Name] = col cols[col.Name] = col
colSeq = append(colSeq, col.Name) colSeq = append(colSeq, col.Name)
} }

View File

@ -345,9 +345,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column
if colDefault != nil { if colDefault != nil {
col.Default = *colDefault col.Default = *colDefault
if col.Default == "" { col.DefaultIsEmpty = false
col.DefaultIsEmpty = true } else {
} col.DefaultIsEmpty = true
} }
cts := strings.Split(colType, "(") cts := strings.Split(colType, "(")
@ -411,13 +411,11 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column
col.IsAutoIncrement = true col.IsAutoIncrement = true
} }
if col.SQLType.IsText() || col.SQLType.IsTime() { if !col.DefaultIsEmpty {
if col.Default != "" { if col.SQLType.IsText() {
col.Default = "'" + col.Default + "'"
} else if col.SQLType.IsTime() && col.Default != "CURRENT_TIMESTAMP" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col

View File

@ -1005,16 +1005,18 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att
col.Name = strings.Trim(colName, `" `) col.Name = strings.Trim(colName, `" `)
if colDefault != nil || isPK { if colDefault != nil {
if isPK { col.Default = *colDefault
col.IsPrimaryKey = true col.DefaultIsEmpty = false
} else { if strings.HasPrefix(col.Default, "nextval(") {
col.Default = *colDefault col.IsAutoIncrement = true
} }
} else {
col.DefaultIsEmpty = true
} }
if colDefault != nil && strings.HasPrefix(*colDefault, "nextval(") { if isPK {
col.IsAutoIncrement = true col.IsPrimaryKey = true
} }
col.Nullable = (isNullable == "YES") col.Nullable = (isNullable == "YES")
@ -1043,12 +1045,16 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att
col.Length = maxLen col.Length = maxLen
if col.SQLType.IsText() || col.SQLType.IsTime() { if !col.DefaultIsEmpty {
if col.Default != "" { if col.SQLType.IsText() {
col.Default = "'" + col.Default + "'" if strings.HasSuffix(col.Default, "::character varying") {
} else { col.Default = strings.TrimRight(col.Default, "::character varying")
if col.DefaultIsEmpty { } else if !strings.HasPrefix(col.Default, "'") {
col.Default = "''" col.Default = "'" + col.Default + "'"
}
} else if col.SQLType.IsTime() {
if strings.HasSuffix(col.Default, "::timestamp without time zone") {
col.Default = strings.TrimRight(col.Default, "::timestamp without time zone")
} }
} }
} }

View File

@ -270,6 +270,34 @@ func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) {
return false, nil return false, nil
} }
// splitColStr splits a sqlite col strings as fields
func splitColStr(colStr string) []string {
colStr = strings.TrimSpace(colStr)
var results = make([]string, 0, 10)
var lastIdx int
var hasC, hasQuote bool
for i, c := range colStr {
if c == ' ' && !hasQuote {
if hasC {
results = append(results, colStr[lastIdx:i])
hasC = false
}
} else {
if c == '\'' {
hasQuote = !hasQuote
}
if !hasC {
lastIdx = i
}
hasC = true
if i == len(colStr)-1 {
results = append(results, colStr[lastIdx:i+1])
}
}
}
return results
}
func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{tableName} args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
@ -315,7 +343,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu
continue continue
} }
fields := strings.Fields(strings.TrimSpace(colStr)) fields := splitColStr(colStr)
col := new(core.Column) col := new(core.Column)
col.Indexes = make(map[string]int) col.Indexes = make(map[string]int)
col.Nullable = true col.Nullable = true
@ -344,9 +372,6 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu
col.DefaultIsEmpty = false col.DefaultIsEmpty = false
} }
} }
if !col.SQLType.IsNumeric() && !col.DefaultIsEmpty {
col.Default = "'" + col.Default + "'"
}
cols[col.Name] = col cols[col.Name] = col
colSeq = append(colSeq, col.Name) colSeq = append(colSeq, col.Name)
} }

35
dialect_sqlite3_test.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2019 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xorm
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplitColStr(t *testing.T) {
var kases = []struct {
colStr string
fields []string
}{
{
colStr: "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
fields: []string{
"`id`", "INTEGER", "PRIMARY", "KEY", "AUTOINCREMENT", "NOT", "NULL",
},
},
{
colStr: "`created` DATETIME DEFAULT '2006-01-02 15:04:05' NULL",
fields: []string{
"`created`", "DATETIME", "DEFAULT", "'2006-01-02 15:04:05'", "NULL",
},
},
}
for _, kase := range kases {
assert.EqualValues(t, kase.fields, splitColStr(kase.colStr))
}
}

View File

@ -907,8 +907,15 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
fieldType := fieldValue.Type() fieldType := fieldValue.Type()
if ormTagStr != "" { if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, col = &core.Column{
IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]int)} FieldName: t.Field(i).Name,
Nullable: true,
IsPrimaryKey: false,
IsAutoIncrement: false,
MapType: core.TWOSIDES,
Indexes: make(map[string]int),
DefaultIsEmpty: true,
}
tags := splitTag(ormTagStr) tags := splitTag(ormTagStr)
if len(tags) > 0 { if len(tags) > 0 {

View File

@ -10,25 +10,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestSplitTag(t *testing.T) {
var cases = []struct {
tag string
tags []string
}{
{"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}},
{"TEXT", []string{"TEXT"}},
{"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}},
{"json binary", []string{"json", "binary"}},
}
for _, kase := range cases {
tags := splitTag(kase.tag)
if !sliceEq(tags, kase.tags) {
t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags)
}
}
}
func TestEraseAny(t *testing.T) { func TestEraseAny(t *testing.T) {
raw := "SELECT * FROM `table`.[table_name]" raw := "SELECT * FROM `table`.[table_name]"
assert.EqualValues(t, raw, eraseAny(raw)) assert.EqualValues(t, raw, eraseAny(raw))

1
tag.go
View File

@ -125,6 +125,7 @@ func DefaultTagHandler(ctx *tagContext) error {
ctx.col.Default = ctx.nextTag ctx.col.Default = ctx.nextTag
ctx.ignoreNext = true ctx.ignoreNext = true
} }
ctx.col.DefaultIsEmpty = false
return nil return nil
} }

View File

@ -5,7 +5,6 @@
package xorm package xorm
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
"testing" "testing"
@ -27,58 +26,27 @@ func TestCreatedAndUpdated(t *testing.T) {
u := new(UserCU) u := new(UserCU)
err := testEngine.DropTables(u) err := testEngine.DropTables(u)
if err != nil { assert.NoError(t, err)
t.Error(err)
panic(err)
}
err = testEngine.CreateTables(u) err = testEngine.CreateTables(u)
if err != nil { assert.NoError(t, err)
t.Error(err)
panic(err)
}
u.Name = "sss" u.Name = "sss"
cnt, err := testEngine.Insert(u) cnt, err := testEngine.Insert(u)
if err != nil { assert.NoError(t, err)
t.Error(err) assert.EqualValues(t, 1, cnt)
panic(err)
}
if cnt != 1 {
err = errors.New("insert not returned 1")
t.Error(err)
panic(err)
return
}
u.Name = "xxx" u.Name = "xxx"
cnt, err = testEngine.ID(u.Id).Update(u) cnt, err = testEngine.ID(u.Id).Update(u)
if err != nil { assert.NoError(t, err)
t.Error(err) assert.EqualValues(t, 1, cnt)
panic(err)
}
if cnt != 1 {
err = errors.New("update not returned 1")
t.Error(err)
panic(err)
return
}
u.Id = 0 u.Id = 0
u.Created = time.Now().Add(-time.Hour * 24 * 365) u.Created = time.Now().Add(-time.Hour * 24 * 365)
u.Updated = u.Created u.Updated = u.Created
fmt.Println(u)
cnt, err = testEngine.NoAutoTime().Insert(u) cnt, err = testEngine.NoAutoTime().Insert(u)
if err != nil { assert.NoError(t, err)
t.Error(err) assert.EqualValues(t, 1, cnt)
panic(err)
}
if cnt != 1 {
err = errors.New("insert not returned 1")
t.Error(err)
panic(err)
return
}
} }
type StrangeName struct { type StrangeName struct {
@ -90,25 +58,17 @@ func TestStrangeName(t *testing.T) {
assert.NoError(t, prepareEngine()) assert.NoError(t, prepareEngine())
err := testEngine.DropTables(new(StrangeName)) err := testEngine.DropTables(new(StrangeName))
if err != nil { assert.NoError(t, err)
t.Error(err)
}
err = testEngine.CreateTables(new(StrangeName)) err = testEngine.CreateTables(new(StrangeName))
if err != nil { assert.NoError(t, err)
t.Error(err)
}
_, err = testEngine.Insert(&StrangeName{Name: "sfsfdsfds"}) _, err = testEngine.Insert(&StrangeName{Name: "sfsfdsfds"})
if err != nil { assert.NoError(t, err)
t.Error(err)
}
beans := make([]StrangeName, 0) beans := make([]StrangeName, 0)
err = testEngine.Find(&beans) err = testEngine.Find(&beans)
if err != nil { assert.NoError(t, err)
t.Error(err)
}
} }
func TestCreatedUpdated(t *testing.T) { func TestCreatedUpdated(t *testing.T) {
@ -179,29 +139,17 @@ func TestLowerCase(t *testing.T) {
assert.NoError(t, prepareEngine()) assert.NoError(t, prepareEngine())
err := testEngine.Sync2(&Lowercase{}) err := testEngine.Sync2(&Lowercase{})
_, err = testEngine.Where("(id) > 0").Delete(&Lowercase{}) assert.NoError(t, err)
if err != nil { _, err = testEngine.Where("id > 0").Delete(&Lowercase{})
t.Error(err) assert.NoError(t, err)
panic(err)
}
_, err = testEngine.Insert(&Lowercase{ended: 1}) _, err = testEngine.Insert(&Lowercase{ended: 1})
if err != nil { assert.NoError(t, err)
t.Error(err)
panic(err)
}
ls := make([]Lowercase, 0) ls := make([]Lowercase, 0)
err = testEngine.Find(&ls) err = testEngine.Find(&ls)
if err != nil { assert.NoError(t, err)
t.Error(err) assert.EqualValues(t, 1, len(ls))
panic(err)
}
if len(ls) != 1 {
err = errors.New("should be 1")
t.Error(err)
panic(err)
}
} }
func TestAutoIncrTag(t *testing.T) { func TestAutoIncrTag(t *testing.T) {
@ -297,6 +245,24 @@ func TestTagDefault(t *testing.T) {
assertSync(t, new(DefaultStruct)) assertSync(t, new(DefaultStruct))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("age")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.True(t, isDefaultExist)
assert.EqualValues(t, "10", defaultVal)
cnt, err := testEngine.Omit("age").Insert(&DefaultStruct{ cnt, err := testEngine.Omit("age").Insert(&DefaultStruct{
Name: "test", Name: "test",
Age: 20, Age: 20,
@ -312,6 +278,163 @@ func TestTagDefault(t *testing.T) {
assert.EqualValues(t, "test", s.Name) assert.EqualValues(t, "test", s.Name)
} }
func TestTagDefault2(t *testing.T) {
assert.NoError(t, prepareEngine())
type DefaultStruct2 struct {
Id int64
Name string
}
assertSync(t, new(DefaultStruct2))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct2")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("name")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.False(t, isDefaultExist, fmt.Sprintf("default value is --%v--", defaultVal))
assert.EqualValues(t, "", defaultVal)
}
func TestTagDefault3(t *testing.T) {
assert.NoError(t, prepareEngine())
type DefaultStruct3 struct {
Id int64
Name string `xorm:"default('myname')"`
}
assertSync(t, new(DefaultStruct3))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct3")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("name")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.True(t, isDefaultExist)
assert.EqualValues(t, "'myname'", defaultVal)
}
func TestTagDefault4(t *testing.T) {
assert.NoError(t, prepareEngine())
type DefaultStruct4 struct {
Id int64
Created time.Time `xorm:"default(CURRENT_TIMESTAMP)"`
}
assertSync(t, new(DefaultStruct4))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct4")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("created")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.True(t, isDefaultExist)
assert.True(t, "CURRENT_TIMESTAMP" == defaultVal ||
"now()" == defaultVal ||
"getdate" == defaultVal, defaultVal)
}
func TestTagDefault5(t *testing.T) {
assert.NoError(t, prepareEngine())
type DefaultStruct5 struct {
Id int64
Created time.Time `xorm:"default('2006-01-02 15:04:05')"`
}
assertSync(t, new(DefaultStruct5))
table := testEngine.TableInfo(new(DefaultStruct5))
createdCol := table.GetColumn("created")
assert.NotNil(t, createdCol)
assert.EqualValues(t, "'2006-01-02 15:04:05'", createdCol.Default)
assert.False(t, createdCol.DefaultIsEmpty)
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct5")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("created")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.True(t, isDefaultExist)
assert.EqualValues(t, "'2006-01-02 15:04:05'", defaultVal)
}
func TestTagDefault6(t *testing.T) {
assert.NoError(t, prepareEngine())
type DefaultStruct6 struct {
Id int64
IsMan bool `xorm:"default(true)"`
}
assertSync(t, new(DefaultStruct6))
tables, err := testEngine.DBMetas()
assert.NoError(t, err)
var defaultVal string
var isDefaultExist bool
tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct6")
for _, table := range tables {
if table.Name == tableName {
col := table.GetColumn("is_man")
assert.NotNil(t, col)
defaultVal = col.Default
isDefaultExist = !col.DefaultIsEmpty
break
}
}
assert.True(t, isDefaultExist)
if defaultVal == "1" {
defaultVal = "true"
} else if defaultVal == "0" {
defaultVal = "false"
}
assert.EqualValues(t, "true", defaultVal)
}
func TestTagsDirection(t *testing.T) { func TestTagsDirection(t *testing.T) {
assert.NoError(t, prepareEngine()) assert.NoError(t, prepareEngine())
@ -407,3 +530,22 @@ func TestTagTime(t *testing.T) {
assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"),
strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1))
} }
func TestSplitTag(t *testing.T) {
var cases = []struct {
tag string
tags []string
}{
{"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}},
{"TEXT", []string{"TEXT"}},
{"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}},
{"json binary", []string{"json", "binary"}},
}
for _, kase := range cases {
tags := splitTag(kase.tag)
if !sliceEq(tags, kase.tags) {
t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags)
}
}
}

View File

@ -1 +1 @@
go test -db=mssql -conn_str="server=localhost;user id=sa;password=MwantsaSecurePassword1;database=xorm_test" go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test"