* upgrade xorm to v0.7.9 to fix some bugs * upgrade xormstore to v1.3.1tags/v1.10.0-rc1
@@ -48,7 +48,7 @@ require ( | |||
github.com/go-redis/redis v6.15.2+incompatible | |||
github.com/go-sql-driver/mysql v1.4.1 | |||
github.com/go-swagger/go-swagger v0.20.1 | |||
github.com/go-xorm/xorm v0.7.8 | |||
github.com/go-xorm/xorm v0.7.9 | |||
github.com/gobwas/glob v0.2.3 | |||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 | |||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 | |||
@@ -64,7 +64,7 @@ require ( | |||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f | |||
github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc // indirect | |||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 // indirect | |||
github.com/lafriks/xormstore v1.3.0 | |||
github.com/lafriks/xormstore v1.3.1 | |||
github.com/lib/pq v1.2.0 | |||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 | |||
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e |
@@ -249,8 +249,8 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l | |||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= | |||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | |||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | |||
github.com/go-xorm/xorm v0.7.8 h1:rKxZJB9mWQ9Nw2TbjsepiThR031jkGePOWXwTtEAU08= | |||
github.com/go-xorm/xorm v0.7.8/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= | |||
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0= | |||
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= | |||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | |||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= | |||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | |||
@@ -384,8 +384,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= | |||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= | |||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | |||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | |||
github.com/lafriks/xormstore v1.3.0 h1:9A2wAZrdEXtTgfjCFtclPz3pwnmmxY7sJxQgIi62li4= | |||
github.com/lafriks/xormstore v1.3.0/go.mod h1:RAhtOztWBjK9xeZpXwKq59rhUxoRgo1zfYl0H1mtK7A= | |||
github.com/lafriks/xormstore v1.3.1 h1:KpzRUamSV3zmA85Kzw+PZOU9wgMbYsNzuDzLuBMbxpA= | |||
github.com/lafriks/xormstore v1.3.1/go.mod h1:qALRD4Vto2Ic7/A5eplMpu5V62mugtSqFysRwz8FETs= | |||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | |||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= | |||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= |
@@ -114,7 +114,18 @@ steps: | |||
commands: | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
- pull_request | |||
- name: test-tidb | |||
pull: default | |||
image: golang:1.10 | |||
commands: | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic" | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
@@ -133,6 +144,15 @@ services: | |||
- tag | |||
- pull_request | |||
- name: tidb | |||
pull: default | |||
image: pingcap/tidb:v3.0.3 | |||
when: | |||
event: | |||
- push | |||
- tag | |||
- pull_request | |||
- name: pgsql | |||
pull: default | |||
image: postgres:9.5 | |||
@@ -309,8 +329,23 @@ steps: | |||
commands: | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" | |||
when: | |||
event: | |||
- push | |||
- pull_request | |||
- name: test-tidb | |||
pull: default | |||
image: golang:1.11 | |||
environment: | |||
GO111MODULE: "on" | |||
GOPROXY: "https://goproxy.cn" | |||
commands: | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic" | |||
- go get github.com/wadey/gocovmerge | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt > coverage.txt | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
@@ -329,6 +364,15 @@ services: | |||
- tag | |||
- pull_request | |||
- name: tidb | |||
pull: default | |||
image: pingcap/tidb:v3.0.3 | |||
when: | |||
event: | |||
- push | |||
- tag | |||
- pull_request | |||
- name: pgsql | |||
pull: default | |||
image: postgres:9.5 | |||
@@ -377,13 +421,6 @@ steps: | |||
depth: 50 | |||
tags: true | |||
- name: init_postgres | |||
pull: default | |||
image: postgres:9.5 | |||
commands: | |||
- "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" | |||
- "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" | |||
- name: build | |||
pull: default | |||
image: golang:1.12 | |||
@@ -505,8 +542,22 @@ steps: | |||
commands: | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" | |||
- go get -u github.com/wadey/gocovmerge | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
- pull_request | |||
- name: test-tidb | |||
pull: default | |||
image: golang:1.12 | |||
environment: | |||
GO111MODULE: "on" | |||
GOPROXY: "https://goproxy.cn" | |||
commands: | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic" | |||
- go get github.com/wadey/gocovmerge | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
@@ -525,6 +576,15 @@ services: | |||
- tag | |||
- pull_request | |||
- name: tidb | |||
pull: default | |||
image: pingcap/tidb:v3.0.3 | |||
when: | |||
event: | |||
- push | |||
- tag | |||
- pull_request | |||
- name: pgsql | |||
pull: default | |||
image: postgres:9.5 | |||
@@ -573,13 +633,6 @@ steps: | |||
depth: 50 | |||
tags: true | |||
- name: init_postgres | |||
pull: default | |||
image: postgres:9.5 | |||
commands: | |||
- "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" | |||
- "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" | |||
- name: build | |||
pull: default | |||
image: golang:1.13 | |||
@@ -701,8 +754,22 @@ steps: | |||
commands: | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" | |||
- go get -u github.com/wadey/gocovmerge | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
- pull_request | |||
- name: test-tidb | |||
pull: default | |||
image: golang:1.13 | |||
environment: | |||
GO111MODULE: "on" | |||
GOPROXY: "https://goproxy.cn" | |||
commands: | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic" | |||
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic" | |||
- go get github.com/wadey/gocovmerge | |||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt | |||
when: | |||
event: | |||
- push | |||
@@ -721,6 +788,15 @@ services: | |||
- tag | |||
- pull_request | |||
- name: tidb | |||
pull: default | |||
image: pingcap/tidb:v3.0.3 | |||
when: | |||
event: | |||
- push | |||
- tag | |||
- pull_request | |||
- name: pgsql | |||
pull: default | |||
image: postgres:9.5 |
@@ -338,8 +338,9 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) { | |||
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { | |||
args := []interface{}{} | |||
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, | |||
ISNULL(i.is_primary_key, 0) | |||
ISNULL(i.is_primary_key, 0), a.is_identity as is_identity | |||
from sys.columns a | |||
left join sys.types b on a.user_type_id=b.user_type_id | |||
left join sys.syscomments c on a.default_object_id=c.id | |||
@@ -361,8 +362,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column | |||
for rows.Next() { | |||
var name, ctype, vdefault string | |||
var maxLen, precision, scale int | |||
var nullable, isPK bool | |||
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault, &isPK) | |||
var nullable, isPK, defaultIsNull, isIncrement bool | |||
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
@@ -371,8 +372,12 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column | |||
col.Indexes = make(map[string]int) | |||
col.Name = strings.Trim(name, "` ") | |||
col.Nullable = nullable | |||
col.Default = vdefault | |||
col.DefaultIsEmpty = defaultIsNull | |||
if !defaultIsNull { | |||
col.Default = vdefault | |||
} | |||
col.IsPrimaryKey = isPK | |||
col.IsAutoIncrement = isIncrement | |||
ct := strings.ToUpper(ctype) | |||
if ct == "DECIMAL" { | |||
col.Length = precision | |||
@@ -395,15 +400,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 | |||
colSeq = append(colSeq, col.Name) | |||
} |
@@ -345,9 +345,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column | |||
if colDefault != nil { | |||
col.Default = *colDefault | |||
if col.Default == "" { | |||
col.DefaultIsEmpty = true | |||
} | |||
col.DefaultIsEmpty = false | |||
} else { | |||
col.DefaultIsEmpty = true | |||
} | |||
cts := strings.Split(colType, "(") | |||
@@ -411,13 +411,11 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column | |||
col.IsAutoIncrement = true | |||
} | |||
if col.SQLType.IsText() || col.SQLType.IsTime() { | |||
if col.Default != "" { | |||
if !col.DefaultIsEmpty { | |||
if col.SQLType.IsText() { | |||
col.Default = "'" + col.Default + "'" | |||
} else if col.SQLType.IsTime() && col.Default != "CURRENT_TIMESTAMP" { | |||
col.Default = "'" + col.Default + "'" | |||
} else { | |||
if col.DefaultIsEmpty { | |||
col.Default = "''" | |||
} | |||
} | |||
} | |||
cols[col.Name] = col |
@@ -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, `" `) | |||
if colDefault != nil || isPK { | |||
if isPK { | |||
col.IsPrimaryKey = true | |||
} else { | |||
col.Default = *colDefault | |||
if colDefault != nil { | |||
col.Default = *colDefault | |||
col.DefaultIsEmpty = false | |||
if strings.HasPrefix(col.Default, "nextval(") { | |||
col.IsAutoIncrement = true | |||
} | |||
} else { | |||
col.DefaultIsEmpty = true | |||
} | |||
if colDefault != nil && strings.HasPrefix(*colDefault, "nextval(") { | |||
col.IsAutoIncrement = true | |||
if isPK { | |||
col.IsPrimaryKey = true | |||
} | |||
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 | |||
if col.SQLType.IsText() || col.SQLType.IsTime() { | |||
if col.Default != "" { | |||
col.Default = "'" + col.Default + "'" | |||
} else { | |||
if col.DefaultIsEmpty { | |||
col.Default = "''" | |||
if !col.DefaultIsEmpty { | |||
if col.SQLType.IsText() { | |||
if strings.HasSuffix(col.Default, "::character varying") { | |||
col.Default = strings.TrimRight(col.Default, "::character varying") | |||
} else if !strings.HasPrefix(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") | |||
} | |||
} | |||
} |
@@ -270,6 +270,68 @@ func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) { | |||
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 parseString(colStr string) (*core.Column, error) { | |||
fields := splitColStr(colStr) | |||
col := new(core.Column) | |||
col.Indexes = make(map[string]int) | |||
col.Nullable = true | |||
col.DefaultIsEmpty = true | |||
for idx, field := range fields { | |||
if idx == 0 { | |||
col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`) | |||
continue | |||
} else if idx == 1 { | |||
col.SQLType = core.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0} | |||
continue | |||
} | |||
switch field { | |||
case "PRIMARY": | |||
col.IsPrimaryKey = true | |||
case "AUTOINCREMENT": | |||
col.IsAutoIncrement = true | |||
case "NULL": | |||
if fields[idx-1] == "NOT" { | |||
col.Nullable = false | |||
} else { | |||
col.Nullable = true | |||
} | |||
case "DEFAULT": | |||
col.Default = fields[idx+1] | |||
col.DefaultIsEmpty = false | |||
} | |||
} | |||
return col, nil | |||
} | |||
func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { | |||
args := []interface{}{tableName} | |||
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" | |||
@@ -299,6 +361,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu | |||
colCreates := reg.FindAllString(name[nStart+1:nEnd], -1) | |||
cols := make(map[string]*core.Column) | |||
colSeq := make([]string, 0) | |||
for _, colStr := range colCreates { | |||
reg = regexp.MustCompile(`,\s`) | |||
colStr = reg.ReplaceAllString(colStr, ",") | |||
@@ -315,38 +378,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu | |||
continue | |||
} | |||
fields := strings.Fields(strings.TrimSpace(colStr)) | |||
col := new(core.Column) | |||
col.Indexes = make(map[string]int) | |||
col.Nullable = true | |||
col.DefaultIsEmpty = true | |||
for idx, field := range fields { | |||
if idx == 0 { | |||
col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`) | |||
continue | |||
} else if idx == 1 { | |||
col.SQLType = core.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0} | |||
} | |||
switch field { | |||
case "PRIMARY": | |||
col.IsPrimaryKey = true | |||
case "AUTOINCREMENT": | |||
col.IsAutoIncrement = true | |||
case "NULL": | |||
if fields[idx-1] == "NOT" { | |||
col.Nullable = false | |||
} else { | |||
col.Nullable = true | |||
} | |||
case "DEFAULT": | |||
col.Default = fields[idx+1] | |||
col.DefaultIsEmpty = false | |||
} | |||
} | |||
if !col.SQLType.IsNumeric() && !col.DefaultIsEmpty { | |||
col.Default = "'" + col.Default + "'" | |||
col, err := parseString(colStr) | |||
if err != nil { | |||
return colSeq, cols, err | |||
} | |||
cols[col.Name] = col | |||
colSeq = append(colSeq, col.Name) | |||
} |
@@ -377,6 +377,32 @@ func (engine *Engine) NoAutoCondition(no ...bool) *Session { | |||
return session.NoAutoCondition(no...) | |||
} | |||
func (engine *Engine) loadTableInfo(table *core.Table) error { | |||
colSeq, cols, err := engine.dialect.GetColumns(table.Name) | |||
if err != nil { | |||
return err | |||
} | |||
for _, name := range colSeq { | |||
table.AddColumn(cols[name]) | |||
} | |||
indexes, err := engine.dialect.GetIndexes(table.Name) | |||
if err != nil { | |||
return err | |||
} | |||
table.Indexes = indexes | |||
for _, index := range indexes { | |||
for _, name := range index.Cols { | |||
if col := table.GetColumn(name); col != nil { | |||
col.Indexes[index.Name] = index.Type | |||
} else { | |||
return fmt.Errorf("Unknown col %s in index %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq()) | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
// DBMetas Retrieve all tables, columns, indexes' informations from database. | |||
func (engine *Engine) DBMetas() ([]*core.Table, error) { | |||
tables, err := engine.dialect.GetTables() | |||
@@ -385,28 +411,9 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { | |||
} | |||
for _, table := range tables { | |||
colSeq, cols, err := engine.dialect.GetColumns(table.Name) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, name := range colSeq { | |||
table.AddColumn(cols[name]) | |||
} | |||
indexes, err := engine.dialect.GetIndexes(table.Name) | |||
if err != nil { | |||
if err = engine.loadTableInfo(table); err != nil { | |||
return nil, err | |||
} | |||
table.Indexes = indexes | |||
for _, index := range indexes { | |||
for _, name := range index.Cols { | |||
if col := table.GetColumn(name); col != nil { | |||
col.Indexes[index.Name] = index.Type | |||
} else { | |||
return nil, fmt.Errorf("Unknown col %s in index %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq()) | |||
} | |||
} | |||
} | |||
} | |||
return tables, nil | |||
} | |||
@@ -907,8 +914,15 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { | |||
fieldType := fieldValue.Type() | |||
if ormTagStr != "" { | |||
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, | |||
IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]int)} | |||
col = &core.Column{ | |||
FieldName: t.Field(i).Name, | |||
Nullable: true, | |||
IsPrimaryKey: false, | |||
IsAutoIncrement: false, | |||
MapType: core.TWOSIDES, | |||
Indexes: make(map[string]int), | |||
DefaultIsEmpty: true, | |||
} | |||
tags := splitTag(ormTagStr) | |||
if len(tags) > 0 { |
@@ -228,7 +228,7 @@ func (session *Session) Sync2(beans ...interface{}) error { | |||
defer session.Close() | |||
} | |||
tables, err := engine.DBMetas() | |||
tables, err := engine.dialect.GetTables() | |||
if err != nil { | |||
return err | |||
} | |||
@@ -239,26 +239,29 @@ func (session *Session) Sync2(beans ...interface{}) error { | |||
session.resetStatement() | |||
}() | |||
var structTables []*core.Table | |||
for _, bean := range beans { | |||
v := rValue(bean) | |||
table, err := engine.mapType(v) | |||
if err != nil { | |||
return err | |||
} | |||
structTables = append(structTables, table) | |||
tbName := engine.TableName(bean) | |||
tbNameWithSchema := engine.TableName(tbName, true) | |||
var tbName string | |||
if len(session.statement.AltTableName) > 0 { | |||
tbName = session.statement.AltTableName | |||
} else { | |||
tbName = engine.TableName(bean) | |||
} | |||
tbNameWithSchema := engine.tbNameWithSchema(tbName) | |||
var oriTable *core.Table | |||
for _, tb := range tables { | |||
if strings.EqualFold(tb.Name, tbName) { | |||
if strings.EqualFold(engine.tbNameWithSchema(tb.Name), engine.tbNameWithSchema(tbName)) { | |||
oriTable = tb | |||
break | |||
} | |||
} | |||
// this is a new table | |||
if oriTable == nil { | |||
err = session.StoreEngine(session.statement.StoreEngine).createTable(bean) | |||
if err != nil { | |||
@@ -274,148 +277,154 @@ func (session *Session) Sync2(beans ...interface{}) error { | |||
if err != nil { | |||
return err | |||
} | |||
} else { | |||
for _, col := range table.Columns() { | |||
var oriCol *core.Column | |||
for _, col2 := range oriTable.Columns() { | |||
if strings.EqualFold(col.Name, col2.Name) { | |||
oriCol = col2 | |||
break | |||
} | |||
} | |||
continue | |||
} | |||
if oriCol != nil { | |||
expectedType := engine.dialect.SqlType(col) | |||
curType := engine.dialect.SqlType(oriCol) | |||
if expectedType != curType { | |||
if expectedType == core.Text && | |||
strings.HasPrefix(curType, core.Varchar) { | |||
// currently only support mysql & postgres | |||
if engine.dialect.DBType() == core.MYSQL || | |||
engine.dialect.DBType() == core.POSTGRES { | |||
engine.logger.Infof("Table %s column %s change type from %s to %s\n", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} else { | |||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
} | |||
} else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { | |||
if engine.dialect.DBType() == core.MYSQL { | |||
if oriCol.Length < col.Length { | |||
engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", | |||
tbNameWithSchema, col.Name, oriCol.Length, col.Length) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} | |||
} | |||
} else { | |||
if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { | |||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
} | |||
} | |||
} else if expectedType == core.Varchar { | |||
if engine.dialect.DBType() == core.MYSQL { | |||
if oriCol.Length < col.Length { | |||
engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", | |||
tbNameWithSchema, col.Name, oriCol.Length, col.Length) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} | |||
} | |||
} | |||
if col.Default != oriCol.Default { | |||
engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s", | |||
tbName, col.Name, oriCol.Default, col.Default) | |||
} | |||
if col.Nullable != oriCol.Nullable { | |||
engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v", | |||
tbName, col.Name, oriCol.Nullable, col.Nullable) | |||
} | |||
} else { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
err = session.addColumn(col.Name) | |||
// this will modify an old table | |||
if err = engine.loadTableInfo(oriTable); err != nil { | |||
return err | |||
} | |||
// check columns | |||
for _, col := range table.Columns() { | |||
var oriCol *core.Column | |||
for _, col2 := range oriTable.Columns() { | |||
if strings.EqualFold(col.Name, col2.Name) { | |||
oriCol = col2 | |||
break | |||
} | |||
if err != nil { | |||
} | |||
// column is not exist on table | |||
if oriCol == nil { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
if err = session.addColumn(col.Name); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
var foundIndexNames = make(map[string]bool) | |||
var addedNames = make(map[string]*core.Index) | |||
for name, index := range table.Indexes { | |||
var oriIndex *core.Index | |||
for name2, index2 := range oriTable.Indexes { | |||
if index.Equal(index2) { | |||
oriIndex = index2 | |||
foundIndexNames[name2] = true | |||
break | |||
err = nil | |||
expectedType := engine.dialect.SqlType(col) | |||
curType := engine.dialect.SqlType(oriCol) | |||
if expectedType != curType { | |||
if expectedType == core.Text && | |||
strings.HasPrefix(curType, core.Varchar) { | |||
// currently only support mysql & postgres | |||
if engine.dialect.DBType() == core.MYSQL || | |||
engine.dialect.DBType() == core.POSTGRES { | |||
engine.logger.Infof("Table %s column %s change type from %s to %s\n", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} else { | |||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
} | |||
} | |||
if oriIndex != nil { | |||
if oriIndex.Type != index.Type { | |||
sql := engine.dialect.DropIndexSql(tbNameWithSchema, oriIndex) | |||
_, err = session.exec(sql) | |||
if err != nil { | |||
return err | |||
} else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { | |||
if engine.dialect.DBType() == core.MYSQL { | |||
if oriCol.Length < col.Length { | |||
engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", | |||
tbNameWithSchema, col.Name, oriCol.Length, col.Length) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} | |||
oriIndex = nil | |||
} | |||
} else { | |||
if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { | |||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", | |||
tbNameWithSchema, col.Name, curType, expectedType) | |||
} | |||
} | |||
} else if expectedType == core.Varchar { | |||
if engine.dialect.DBType() == core.MYSQL { | |||
if oriCol.Length < col.Length { | |||
engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", | |||
tbNameWithSchema, col.Name, oriCol.Length, col.Length) | |||
_, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col)) | |||
} | |||
} | |||
} | |||
if oriIndex == nil { | |||
addedNames[name] = index | |||
if col.Default != oriCol.Default { | |||
if (col.SQLType.Name == core.Bool || col.SQLType.Name == core.Boolean) && | |||
((strings.EqualFold(col.Default, "true") && oriCol.Default == "1") || | |||
(strings.EqualFold(col.Default, "false") && oriCol.Default == "0")) { | |||
} else { | |||
engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s", | |||
tbName, col.Name, oriCol.Default, col.Default) | |||
} | |||
} | |||
if col.Nullable != oriCol.Nullable { | |||
engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v", | |||
tbName, col.Name, oriCol.Nullable, col.Nullable) | |||
} | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
var foundIndexNames = make(map[string]bool) | |||
var addedNames = make(map[string]*core.Index) | |||
for name, index := range table.Indexes { | |||
var oriIndex *core.Index | |||
for name2, index2 := range oriTable.Indexes { | |||
if _, ok := foundIndexNames[name2]; !ok { | |||
sql := engine.dialect.DropIndexSql(tbNameWithSchema, index2) | |||
if index.Equal(index2) { | |||
oriIndex = index2 | |||
foundIndexNames[name2] = true | |||
break | |||
} | |||
} | |||
if oriIndex != nil { | |||
if oriIndex.Type != index.Type { | |||
sql := engine.dialect.DropIndexSql(tbNameWithSchema, oriIndex) | |||
_, err = session.exec(sql) | |||
if err != nil { | |||
return err | |||
} | |||
oriIndex = nil | |||
} | |||
} | |||
for name, index := range addedNames { | |||
if index.Type == core.UniqueType { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
err = session.addUnique(tbNameWithSchema, name) | |||
} else if index.Type == core.IndexType { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
err = session.addIndex(tbNameWithSchema, name) | |||
} | |||
if oriIndex == nil { | |||
addedNames[name] = index | |||
} | |||
} | |||
for name2, index2 := range oriTable.Indexes { | |||
if _, ok := foundIndexNames[name2]; !ok { | |||
sql := engine.dialect.DropIndexSql(tbNameWithSchema, index2) | |||
_, err = session.exec(sql) | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
} | |||
for _, table := range tables { | |||
var oriTable *core.Table | |||
for _, structTable := range structTables { | |||
if strings.EqualFold(table.Name, session.tbNameNoSchema(structTable)) { | |||
oriTable = structTable | |||
break | |||
for name, index := range addedNames { | |||
if index.Type == core.UniqueType { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
err = session.addUnique(tbNameWithSchema, name) | |||
} else if index.Type == core.IndexType { | |||
session.statement.RefTable = table | |||
session.statement.tableName = tbNameWithSchema | |||
err = session.addIndex(tbNameWithSchema, name) | |||
} | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
if oriTable == nil { | |||
//engine.LogWarnf("Table %s has no struct to mapping it", table.Name) | |||
continue | |||
} | |||
for _, colName := range table.ColumnsSeq() { | |||
if oriTable.GetColumn(colName) == nil { | |||
engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(table.Name, true), colName) | |||
// check all the columns which removed from struct fields but left on database tables. | |||
for _, colName := range oriTable.ColumnsSeq() { | |||
if table.GetColumn(colName) == nil { | |||
engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(oriTable.Name, true), colName) | |||
} | |||
} | |||
} | |||
return nil | |||
} |
@@ -266,6 +266,14 @@ func (statement *Statement) buildUpdates(bean interface{}, | |||
continue | |||
} | |||
if statement.incrColumns.isColExist(col.Name) { | |||
continue | |||
} else if statement.decrColumns.isColExist(col.Name) { | |||
continue | |||
} else if statement.exprColumns.isColExist(col.Name) { | |||
continue | |||
} | |||
fieldValuePtr, err := col.ValueOf(bean) | |||
if err != nil { | |||
engine.logger.Error(err) |
@@ -125,6 +125,7 @@ func DefaultTagHandler(ctx *tagContext) error { | |||
ctx.col.Default = ctx.nextTag | |||
ctx.ignoreNext = true | |||
} | |||
ctx.col.DefaultIsEmpty = false | |||
return nil | |||
} | |||
@@ -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" |
@@ -3,10 +3,9 @@ module github.com/lafriks/xormstore | |||
go 1.11 | |||
require ( | |||
github.com/davecgh/go-spew v1.1.1 // indirect | |||
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 | |||
github.com/go-sql-driver/mysql v1.4.1 | |||
github.com/go-xorm/xorm v0.7.8 | |||
github.com/go-xorm/xorm v0.7.9 | |||
github.com/gorilla/context v1.1.1 | |||
github.com/gorilla/securecookie v1.1.1 | |||
github.com/gorilla/sessions v1.2.0 |
@@ -29,8 +29,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG | |||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | |||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | |||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | |||
github.com/go-xorm/xorm v0.7.8 h1:rKxZJB9mWQ9Nw2TbjsepiThR031jkGePOWXwTtEAU08= | |||
github.com/go-xorm/xorm v0.7.8/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= | |||
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0= | |||
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= | |||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | |||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | |||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= |
@@ -198,7 +198,7 @@ github.com/go-swagger/go-swagger/cmd/swagger/commands/initcmd | |||
github.com/go-swagger/go-swagger/codescan | |||
github.com/go-swagger/go-swagger/generator | |||
github.com/go-swagger/go-swagger/scan | |||
# github.com/go-xorm/xorm v0.7.8 | |||
# github.com/go-xorm/xorm v0.7.9 | |||
github.com/go-xorm/xorm | |||
# github.com/gobwas/glob v0.2.3 | |||
github.com/gobwas/glob | |||
@@ -281,7 +281,7 @@ github.com/klauspost/crc32 | |||
github.com/kr/pretty | |||
# github.com/kr/text v0.1.0 | |||
github.com/kr/text | |||
# github.com/lafriks/xormstore v1.3.0 | |||
# github.com/lafriks/xormstore v1.3.1 | |||
github.com/lafriks/xormstore | |||
github.com/lafriks/xormstore/util | |||
# github.com/lib/pq v1.2.0 |