Merge branch 'master' into master

This commit is contained in:
zxysilent 2023-03-11 11:57:03 +08:00
commit 09a7d22108
17 changed files with 246 additions and 158 deletions

View File

@ -11,7 +11,7 @@ trigger:
- refs/pull/*/head - refs/pull/*/head
steps: steps:
- name: test-vet - name: test-vet
image: golang:1.15 image: golang:1.17
pull: always pull: always
volumes: volumes:
- name: cache - name: cache
@ -19,7 +19,7 @@ steps:
commands: commands:
- make vet - make vet
- name: test-sqlite3 - name: test-sqlite3
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -31,7 +31,7 @@ steps:
- make test-sqlite3 - make test-sqlite3
- TEST_CACHE_ENABLE=true make test-sqlite3 - TEST_CACHE_ENABLE=true make test-sqlite3
- name: test-sqlite - name: test-sqlite
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -41,7 +41,7 @@ steps:
- make test-sqlite - make test-sqlite
- TEST_QUOTE_POLICY=reserved make test-sqlite - TEST_QUOTE_POLICY=reserved make test-sqlite
- name: test-mysql - name: test-mysql
image: golang:1.15 image: golang:1.17
pull: never pull: never
volumes: volumes:
- name: cache - name: cache
@ -58,7 +58,7 @@ steps:
- TEST_CACHE_ENABLE=true make test-mysql - TEST_CACHE_ENABLE=true make test-mysql
- name: test-mysql-utf8mb4 - name: test-mysql-utf8mb4
image: golang:1.15 image: golang:1.17
pull: never pull: never
volumes: volumes:
- name: cache - name: cache
@ -98,7 +98,7 @@ trigger:
- refs/pull/*/head - refs/pull/*/head
steps: steps:
- name: test-mysql8 - name: test-mysql8
image: golang:1.15 image: golang:1.17
pull: never pull: never
volumes: volumes:
- name: cache - name: cache
@ -136,7 +136,7 @@ trigger:
- refs/pull/*/head - refs/pull/*/head
steps: steps:
- name: test-mariadb - name: test-mariadb
image: golang:1.15 image: golang:1.17
pull: never pull: never
volumes: volumes:
- name: cache - name: cache
@ -175,7 +175,7 @@ trigger:
steps: steps:
- name: test-postgres - name: test-postgres
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -190,7 +190,7 @@ steps:
- name: test-postgres-schema - name: test-postgres-schema
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -207,7 +207,7 @@ steps:
- name: test-pgx - name: test-pgx
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -225,7 +225,7 @@ steps:
- name: test-pgx-schema - name: test-pgx-schema
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -267,7 +267,7 @@ trigger:
steps: steps:
- name: test-mssql - name: test-mssql
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -306,7 +306,7 @@ trigger:
steps: steps:
- name: test-tidb - name: test-tidb
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -339,7 +339,7 @@ trigger:
steps: steps:
- name: test-cockroach - name: test-cockroach
pull: never pull: never
image: golang:1.15 image: golang:1.17
volumes: volumes:
- name: cache - name: cache
path: /go/pkg/mod path: /go/pkg/mod
@ -375,7 +375,7 @@ services:
# steps: # steps:
# - name: test-dameng # - name: test-dameng
# pull: never # pull: never
# image: golang:1.15 # image: golang:1.17
# volumes: # volumes:
# - name: cache # - name: cache
# path: /go/pkg/mod # path: /go/pkg/mod
@ -416,7 +416,7 @@ trigger:
- refs/pull/*/head - refs/pull/*/head
steps: steps:
- name: merge_coverage - name: merge_coverage
image: golang:1.15 image: golang:1.17
commands: commands:
- make coverage - make coverage

View File

@ -53,6 +53,7 @@ Drivers for Go's sql package which currently support database/sql includes:
* Oracle * Oracle
- [github.com/godror/godror](https://github.com/godror/godror) (experiment) - [github.com/godror/godror](https://github.com/godror/godror) (experiment)
- [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
- [github.com/sijms/go-ora](https://github.com/sijms/go-ora) (experiment)
## Installation ## Installation

View File

@ -290,6 +290,7 @@ func regDrvsNDialects() bool {
"sqlite": {"sqlite3", func() Driver { return &sqlite3Driver{} }, func() Dialect { return &sqlite3{} }}, "sqlite": {"sqlite3", func() Driver { return &sqlite3Driver{} }, func() Dialect { return &sqlite3{} }},
"oci8": {"oracle", func() Driver { return &oci8Driver{} }, func() Dialect { return &oracle{} }}, "oci8": {"oracle", func() Driver { return &oci8Driver{} }, func() Dialect { return &oracle{} }},
"godror": {"oracle", func() Driver { return &godrorDriver{} }, func() Dialect { return &oracle{} }}, "godror": {"oracle", func() Driver { return &godrorDriver{} }, func() Dialect { return &oracle{} }},
"oracle": {"oracle", func() Driver { return &oracleDriver{} }, func() Dialect { return &oracle{} }},
} }
for driverName, v := range providedDrvsNDialects { for driverName, v := range providedDrvsNDialects {

View File

@ -738,8 +738,9 @@ func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) {
} }
func (p *mysqlDriver) GenScanResult(colType string) (interface{}, error) { func (p *mysqlDriver) GenScanResult(colType string) (interface{}, error) {
colType = strings.Replace(colType, "UNSIGNED ", "", -1)
switch colType { switch colType {
case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET": case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET", "JSON":
var s sql.NullString var s sql.NullString
return &s, nil return &s, nil
case "BIGINT": case "BIGINT":

View File

@ -939,3 +939,7 @@ func (o *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) {
} }
return db, nil return db, nil
} }
type oracleDriver struct {
godrorDriver
}

251
doc.go
View File

@ -3,247 +3,246 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
/* /*
Package xorm is a simple and powerful ORM for Go. Package xorm is a simple and powerful ORM for Go.
Installation # Installation
Make sure you have installed Go 1.11+ and then: Make sure you have installed Go 1.11+ and then:
go get xorm.io/xorm go get xorm.io/xorm
Create Engine # Create Engine
Firstly, we should create an engine for a database Firstly, we should create an engine for a database
engine, err := xorm.NewEngine(driverName, dataSourceName) engine, err := xorm.NewEngine(driverName, dataSourceName)
Method NewEngine's parameters are the same as sql.Open which depend drivers' implementation. Method NewEngine's parameters are the same as sql.Open which depend drivers' implementation.
Generally, one engine for an application is enough. You can define it as a package variable. Generally, one engine for an application is enough. You can define it as a package variable.
Raw Methods # Raw Methods
XORM supports raw SQL execution: XORM supports raw SQL execution:
1. query with a SQL string, the returned results is []map[string][]byte 1. query with a SQL string, the returned results is []map[string][]byte
results, err := engine.Query("select * from user") results, err := engine.Query("select * from user")
2. query with a SQL string, the returned results is []map[string]string 2. query with a SQL string, the returned results is []map[string]string
results, err := engine.QueryString("select * from user") results, err := engine.QueryString("select * from user")
3. query with a SQL string, the returned results is []map[string]interface{} 3. query with a SQL string, the returned results is []map[string]interface{}
results, err := engine.QueryInterface("select * from user") results, err := engine.QueryInterface("select * from user")
4. execute with a SQL string, the returned results 4. execute with a SQL string, the returned results
affected, err := engine.Exec("update user set .... where ...") affected, err := engine.Exec("update user set .... where ...")
ORM Methods # ORM Methods
There are 8 major ORM methods and many helpful methods to use to operate database. There are 8 major ORM methods and many helpful methods to use to operate database.
1. Insert one or multiple records to database 1. Insert one or multiple records to database
affected, err := engine.Insert(&struct) affected, err := engine.Insert(&struct)
// INSERT INTO struct () values () // INSERT INTO struct () values ()
affected, err := engine.Insert(&struct1, &struct2) affected, err := engine.Insert(&struct1, &struct2)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values () // INSERT INTO struct2 () values ()
affected, err := engine.Insert(&sliceOfStruct) affected, err := engine.Insert(&sliceOfStruct)
// INSERT INTO struct () values (),(),() // INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&struct1, &sliceOfStruct2) affected, err := engine.Insert(&struct1, &sliceOfStruct2)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),() // INSERT INTO struct2 () values (),(),()
2. Query one record or one variable from database 2. Query one record or one variable from database
has, err := engine.Get(&user) has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1 // SELECT * FROM user LIMIT 1
var id int64 var id int64
has, err := engine.Table("user").Where("name = ?", name).Get(&id) has, err := engine.Table("user").Where("name = ?", name).Get(&id)
// SELECT id FROM user WHERE name = ? LIMIT 1 // SELECT id FROM user WHERE name = ? LIMIT 1
var id int64 var id int64
var name string var name string
has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name)
// SELECT id, name FROM user LIMIT 1 // SELECT id, name FROM user LIMIT 1
3. Query multiple records from database 3. Query multiple records from database
var sliceOfStructs []Struct var sliceOfStructs []Struct
err := engine.Find(&sliceOfStructs) err := engine.Find(&sliceOfStructs)
// SELECT * FROM user // SELECT * FROM user
var mapOfStructs = make(map[int64]Struct) var mapOfStructs = make(map[int64]Struct)
err := engine.Find(&mapOfStructs) err := engine.Find(&mapOfStructs)
// SELECT * FROM user // SELECT * FROM user
var int64s []int64 var int64s []int64
err := engine.Table("user").Cols("id").Find(&int64s) err := engine.Table("user").Cols("id").Find(&int64s)
// SELECT id FROM user // SELECT id FROM user
4. Query multiple records and record by record handle, there two methods, one is Iterate, 4. Query multiple records and record by record handle, there two methods, one is Iterate,
another is Rows another is Rows
err := engine.Iterate(new(User), func(i int, bean interface{}) error { err := engine.Iterate(new(User), func(i int, bean interface{}) error {
// do something // do something
}) })
// SELECT * FROM user // SELECT * FROM user
rows, err := engine.Rows(...) rows, err := engine.Rows(...)
// SELECT * FROM user // SELECT * FROM user
defer rows.Close() defer rows.Close()
bean := new(Struct) bean := new(Struct)
for rows.Next() { for rows.Next() {
err = rows.Scan(bean) err = rows.Scan(bean)
} }
or or
rows, err := engine.Cols("name", "age").Rows(...) rows, err := engine.Cols("name", "age").Rows(...)
// SELECT * FROM user // SELECT * FROM user
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var name string var name string
var age int var age int
err = rows.Scan(&name, &age) err = rows.Scan(&name, &age)
} }
5. Update one or more records 5. Update one or more records
affected, err := engine.ID(...).Update(&user) affected, err := engine.ID(...).Update(&user)
// UPDATE user SET ... // UPDATE user SET ...
6. Delete one or more records, Delete MUST has condition 6. Delete one or more records, Delete MUST has condition
affected, err := engine.Where(...).Delete(&user) affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ... // DELETE FROM user Where ...
7. Count records 7. Count records
counts, err := engine.Count(&user) counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user // SELECT count(*) AS total FROM user
counts, err := engine.SQL("select count(*) FROM user").Count() counts, err := engine.SQL("select count(*) FROM user").Count()
// select count(*) FROM user // select count(*) FROM user
8. Sum records 8. Sum records
sumFloat64, err := engine.Sum(&user, "id") sumFloat64, err := engine.Sum(&user, "id")
// SELECT sum(id) from user // SELECT sum(id) from user
sumFloat64s, err := engine.Sums(&user, "id1", "id2") sumFloat64s, err := engine.Sums(&user, "id1", "id2")
// SELECT sum(id1), sum(id2) from user // SELECT sum(id1), sum(id2) from user
sumInt64s, err := engine.SumsInt(&user, "id1", "id2") sumInt64s, err := engine.SumsInt(&user, "id1", "id2")
// SELECT sum(id1), sum(id2) from user // SELECT sum(id1), sum(id2) from user
Conditions # Conditions
The above 8 methods could use with condition methods chainable. The above 8 methods could use with condition methods chainable.
Notice: the above 8 methods should be the last chainable method. Notice: the above 8 methods should be the last chainable method.
1. ID, In 1. ID, In
engine.ID(1).Get(&user) // for single primary key engine.ID(1).Get(&user) // for single primary key
// SELECT * FROM user WHERE id = 1 // SELECT * FROM user WHERE id = 1
engine.ID(schemas.PK{1, 2}).Get(&user) // for composite primary keys engine.ID(schemas.PK{1, 2}).Get(&user) // for composite primary keys
// SELECT * FROM user WHERE id1 = 1 AND id2 = 2 // SELECT * FROM user WHERE id1 = 1 AND id2 = 2
engine.In("id", 1, 2, 3).Find(&users) engine.In("id", 1, 2, 3).Find(&users)
// SELECT * FROM user WHERE id IN (1, 2, 3) // SELECT * FROM user WHERE id IN (1, 2, 3)
engine.In("id", []int{1, 2, 3}).Find(&users) engine.In("id", []int{1, 2, 3}).Find(&users)
// SELECT * FROM user WHERE id IN (1, 2, 3) // SELECT * FROM user WHERE id IN (1, 2, 3)
2. Where, And, Or 2. Where, And, Or
engine.Where().And().Or().Find() engine.Where().And().Or().Find()
// SELECT * FROM user WHERE (.. AND ..) OR ... // SELECT * FROM user WHERE (.. AND ..) OR ...
3. OrderBy, Asc, Desc 3. OrderBy, Asc, Desc
engine.Asc().Desc().Find() engine.Asc().Desc().Find()
// SELECT * FROM user ORDER BY .. ASC, .. DESC // SELECT * FROM user ORDER BY .. ASC, .. DESC
engine.OrderBy().Find() engine.OrderBy().Find()
// SELECT * FROM user ORDER BY .. // SELECT * FROM user ORDER BY ..
4. Limit, Top 4. Limit, Top
engine.Limit().Find() engine.Limit().Find()
// SELECT * FROM user LIMIT .. OFFSET .. // SELECT * FROM user LIMIT .. OFFSET ..
engine.Top(5).Find() engine.Top(5).Find()
// SELECT TOP 5 * FROM user // for mssql // SELECT TOP 5 * FROM user // for mssql
// SELECT * FROM user LIMIT .. OFFSET 0 //for other databases // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
5. SQL, let you custom SQL 5. SQL, let you custom SQL
var users []User var users []User
engine.SQL("select * from user").Find(&users) engine.SQL("select * from user").Find(&users)
6. Cols, Omit, Distinct 6. Cols, Omit, Distinct
var users []*User var users []*User
engine.Cols("col1, col2").Find(&users) engine.Cols("col1, col2").Find(&users)
// SELECT col1, col2 FROM user // SELECT col1, col2 FROM user
engine.Cols("col1", "col2").Where().Update(user) engine.Cols("col1", "col2").Where().Update(user)
// UPDATE user set col1 = ?, col2 = ? Where ... // UPDATE user set col1 = ?, col2 = ? Where ...
engine.Omit("col1").Find(&users) engine.Omit("col1").Find(&users)
// SELECT col2, col3 FROM user // SELECT col2, col3 FROM user
engine.Omit("col1").Insert(&user) engine.Omit("col1").Insert(&user)
// INSERT INTO table (non-col1) VALUES () // INSERT INTO table (non-col1) VALUES ()
engine.Distinct("col1").Find(&users) engine.Distinct("col1").Find(&users)
// SELECT DISTINCT col1 FROM user // SELECT DISTINCT col1 FROM user
7. Join, GroupBy, Having 7. Join, GroupBy, Having
engine.GroupBy("name").Having("name='xlw'").Find(&users) engine.GroupBy("name").Having("name='xlw'").Find(&users)
//SELECT * FROM user GROUP BY name HAVING name='xlw' //SELECT * FROM user GROUP BY name HAVING name='xlw'
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users)
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
Builder # Builder
xorm could work with xorm.io/builder directly. xorm could work with xorm.io/builder directly.
1. With Where 1. With Where
var cond = builder.Eq{"a":1, "b":2} var cond = builder.Eq{"a":1, "b":2}
engine.Where(cond).Find(&users) engine.Where(cond).Find(&users)
2. With In 2. With In
var subQuery = builder.Select("name").From("group") var subQuery = builder.Select("name").From("group")
engine.In("group_name", subQuery).Find(&users) engine.In("group_name", subQuery).Find(&users)
3. With Join 3. With Join
var subQuery = builder.Select("name").From("group") var subQuery = builder.Select("name").From("group")
engine.Join("INNER", subQuery, "group.id = user.group_id").Find(&users) engine.Join("INNER", subQuery, "group.id = user.group_id").Find(&users)
4. With SetExprs 4. With SetExprs
var subQuery = builder.Select("name").From("group") var subQuery = builder.Select("name").From("group")
engine.ID(1).SetExprs("name", subQuery).Update(new(User)) engine.ID(1).SetExprs("name", subQuery).Update(new(User))
5. With SQL 5. With SQL
var query = builder.Select("name").From("group") var query = builder.Select("name").From("group")
results, err := engine.SQL(query).Find(&groups) results, err := engine.SQL(query).Find(&groups)
6. With Query 6. With Query
var query = builder.Select("name").From("group") var query = builder.Select("name").From("group")
results, err := engine.Query(query) results, err := engine.Query(query)
results, err := engine.QueryString(query) results, err := engine.QueryString(query)
results, err := engine.QueryInterface(query) results, err := engine.QueryInterface(query)
7. With Exec 7. With Exec
var query = builder.Insert("a, b").Into("table1").Select("b, c").From("table2") var query = builder.Insert("a, b").Into("table1").Select("b, c").From("table2")
results, err := engine.Exec(query) results, err := engine.Exec(query)
More usage, please visit http://xorm.io/docs More usage, please visit http://xorm.io/docs
*/ */

View File

@ -254,6 +254,11 @@ func (engine *Engine) SetConnMaxLifetime(d time.Duration) {
engine.DB().SetConnMaxLifetime(d) engine.DB().SetConnMaxLifetime(d)
} }
// SetConnMaxIdleTime sets the maximum amount of time a connection may be idle.
func (engine *Engine) SetConnMaxIdleTime(d time.Duration) {
engine.DB().SetConnMaxIdleTime(d)
}
// SetMaxOpenConns is only available for go 1.2+ // SetMaxOpenConns is only available for go 1.2+
func (engine *Engine) SetMaxOpenConns(conns int) { func (engine *Engine) SetMaxOpenConns(conns int) {
engine.DB().SetMaxOpenConns(conns) engine.DB().SetMaxOpenConns(conns)
@ -815,6 +820,9 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w
return err return err
} }
} }
// !datbeohbbh! if no error, manually close
rows.Close()
sess.Close()
} }
return nil return nil
} }
@ -1230,12 +1238,21 @@ func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64
} }
// Delete records, bean's non-empty fields are conditions // Delete records, bean's non-empty fields are conditions
// At least one condition must be set.
func (engine *Engine) Delete(beans ...interface{}) (int64, error) { func (engine *Engine) Delete(beans ...interface{}) (int64, error) {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
return session.Delete(beans...) return session.Delete(beans...)
} }
// Truncate records, bean's non-empty fields are conditions
// In contrast to Delete this method allows deletes without conditions.
func (engine *Engine) Truncate(beans ...interface{}) (int64, error) {
session := engine.NewSession()
defer session.Close()
return session.Truncate(beans...)
}
// Get retrieve one record from table, bean's non-empty fields // Get retrieve one record from table, bean's non-empty fields
// are conditions // are conditions
func (engine *Engine) Get(beans ...interface{}) (bool, error) { func (engine *Engine) Get(beans ...interface{}) (bool, error) {

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build dm
// +build dm // +build dm
package integrations package integrations

View File

@ -208,7 +208,7 @@ func TestUnscopeDelete(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, cnt) assert.EqualValues(t, 1, cnt)
var nowUnix = time.Now().Unix() nowUnix := time.Now().Unix()
var s UnscopeDeleteStruct var s UnscopeDeleteStruct
cnt, err = testEngine.ID(1).Delete(&s) cnt, err = testEngine.ID(1).Delete(&s)
assert.NoError(t, err) assert.NoError(t, err)
@ -266,3 +266,28 @@ func TestDelete2(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, has) assert.False(t, has)
} }
func TestTruncate(t *testing.T) {
assert.NoError(t, PrepareEngine())
type TruncateUser struct {
Uid int64 `xorm:"id pk not null autoincr"`
}
assert.NoError(t, testEngine.Sync(new(TruncateUser)))
cnt, err := testEngine.Insert(&TruncateUser{})
assert.NoError(t, err)
assert.EqualValues(t, 1, cnt)
_, err = testEngine.Delete(&TruncateUser{})
assert.Error(t, err)
_, err = testEngine.Truncate(&TruncateUser{})
assert.NoError(t, err)
user2 := TruncateUser{}
has, err := testEngine.ID(1).Get(&user2)
assert.NoError(t, err)
assert.False(t, has)
}

View File

@ -744,7 +744,8 @@ func TestInsertMap(t *testing.T) {
assert.EqualValues(t, "lunny", ims[3].Name) assert.EqualValues(t, "lunny", ims[3].Name)
} }
/*INSERT INTO `issue` (`repo_id`, `poster_id`, ... ,`name`, `content`, ... ,`index`) /*
INSERT INTO `issue` (`repo_id`, `poster_id`, ... ,`name`, `content`, ... ,`index`)
SELECT $1, $2, ..., $14, $15, ..., MAX(`index`) + 1 FROM `issue` WHERE `repo_id` = $1; SELECT $1, $2, ..., $14, $15, ..., MAX(`index`) + 1 FROM `issue` WHERE `repo_id` = $1;
*/ */
func TestInsertWhere(t *testing.T) { func TestInsertWhere(t *testing.T) {

View File

@ -31,6 +31,7 @@ type Interface interface {
Decr(column string, arg ...interface{}) *Session Decr(column string, arg ...interface{}) *Session
Desc(...string) *Session Desc(...string) *Session
Delete(...interface{}) (int64, error) Delete(...interface{}) (int64, error)
Truncate(...interface{}) (int64, error)
Distinct(columns ...string) *Session Distinct(columns ...string) *Session
DropIndexes(bean interface{}) error DropIndexes(bean interface{}) error
Exec(sqlOrArgs ...interface{}) (sql.Result, error) Exec(sqlOrArgs ...interface{}) (sql.Result, error)

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build gojson
// +build gojson // +build gojson
package json package json

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build jsoniter
// +build jsoniter // +build jsoniter
package json package json

View File

@ -163,17 +163,18 @@ func (q Quoter) quoteWordTo(buf *strings.Builder, word string) error {
} }
// QuoteTo quotes the table or column names. i.e. if the quotes are [ and ] // QuoteTo quotes the table or column names. i.e. if the quotes are [ and ]
// name -> [name] //
// `name` -> [name] // name -> [name]
// [name] -> [name] // `name` -> [name]
// schema.name -> [schema].[name] // [name] -> [name]
// `schema`.`name` -> [schema].[name] // schema.name -> [schema].[name]
// `schema`.name -> [schema].[name] // `schema`.`name` -> [schema].[name]
// schema.`name` -> [schema].[name] // `schema`.name -> [schema].[name]
// [schema].name -> [schema].[name] // schema.`name` -> [schema].[name]
// schema.[name] -> [schema].[name] // [schema].name -> [schema].[name]
// name AS a -> [name] AS a // schema.[name] -> [schema].[name]
// schema.name AS a -> [schema].[name] AS a // name AS a -> [name] AS a
// schema.name AS a -> [schema].[name] AS a
func (q Quoter) QuoteTo(buf *strings.Builder, value string) error { func (q Quoter) QuoteTo(buf *strings.Builder, value string) error {
var i int var i int
for i < len(value) { for i < len(value) {

View File

@ -91,7 +91,18 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri
} }
// Delete records, bean's non-empty fields are conditions // Delete records, bean's non-empty fields are conditions
// At least one condition must be set.
func (session *Session) Delete(beans ...interface{}) (int64, error) { func (session *Session) Delete(beans ...interface{}) (int64, error) {
return session.delete(beans, true)
}
// Truncate records, bean's non-empty fields are conditions
// In contrast to Delete this method allows deletes without conditions.
func (session *Session) Truncate(beans ...interface{}) (int64, error) {
return session.delete(beans, false)
}
func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (int64, error) {
if session.isAutoClose { if session.isAutoClose {
defer session.Close() defer session.Close()
} }
@ -127,7 +138,7 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) {
} }
pLimitN := session.statement.LimitN pLimitN := session.statement.LimitN
if condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { if mustHaveConditions && condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) {
return 0, ErrNeedDeletedCond return 0, ErrNeedDeletedCond
} }

View File

@ -557,6 +557,29 @@ func TestParseWithJSON(t *testing.T) {
assert.True(t, table.Columns()[0].IsJSON) assert.True(t, table.Columns()[0].IsJSON)
} }
func TestParseWithJSONB(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("postgres"),
names.GonicMapper{
"JSONB": true,
},
names.SnakeMapper{},
caches.NewManager(),
)
type StructWithJSONB struct {
Default1 []string `db:"jsonb"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithJSONB)))
assert.NoError(t, err)
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)
}
func TestParseWithSQLType(t *testing.T) { func TestParseWithSQLType(t *testing.T) {
parser := NewParser( parser := NewParser(
"db", "db",

View File

@ -285,7 +285,7 @@ func CommentTagHandler(ctx *Context) error {
// SQLTypeTagHandler describes SQL Type tag handler // SQLTypeTagHandler describes SQL Type tag handler
func SQLTypeTagHandler(ctx *Context) error { func SQLTypeTagHandler(ctx *Context) error {
ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname} ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname}
if ctx.tagUname == "JSON" { if ctx.tagUname == "JSON" || ctx.tagUname == "JSONB" {
ctx.col.IsJSON = true ctx.col.IsJSON = true
} }
if len(ctx.params) == 0 { if len(ctx.params) == 0 {