summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-xorm/xorm
diff options
context:
space:
mode:
authorThomas Boerger <thomas@webhippie.de>2016-11-03 23:16:01 +0100
committerThomas Boerger <thomas@webhippie.de>2016-11-04 08:43:11 +0100
commit1ebb35b98889ff77299f24d82da426b434b0cca0 (patch)
tree6dcb814d6df4d11c7e7a0ba6da8a6945628e2c5d /vendor/github.com/go-xorm/xorm
parent78f86abba45cb35018c58b8bd5f4c48a86cc8634 (diff)
downloadgitea-1ebb35b98889ff77299f24d82da426b434b0cca0.tar.gz
gitea-1ebb35b98889ff77299f24d82da426b434b0cca0.zip
Added all required dependencies
Diffstat (limited to 'vendor/github.com/go-xorm/xorm')
-rw-r--r--vendor/github.com/go-xorm/xorm/CONTRIBUTING.md49
-rw-r--r--vendor/github.com/go-xorm/xorm/LICENSE27
-rw-r--r--vendor/github.com/go-xorm/xorm/README.md289
-rw-r--r--vendor/github.com/go-xorm/xorm/README_CN.md288
-rw-r--r--vendor/github.com/go-xorm/xorm/VERSION1
-rw-r--r--vendor/github.com/go-xorm/xorm/doc.go158
-rw-r--r--vendor/github.com/go-xorm/xorm/engine.go1689
-rw-r--r--vendor/github.com/go-xorm/xorm/error.go19
-rwxr-xr-xvendor/github.com/go-xorm/xorm/gen_reserved.sh6
-rw-r--r--vendor/github.com/go-xorm/xorm/goracle_driver.go42
-rw-r--r--vendor/github.com/go-xorm/xorm/helpers.go580
-rw-r--r--vendor/github.com/go-xorm/xorm/logger.go162
-rw-r--r--vendor/github.com/go-xorm/xorm/lru_cacher.go289
-rw-r--r--vendor/github.com/go-xorm/xorm/memory_store.go47
-rw-r--r--vendor/github.com/go-xorm/xorm/mssql_dialect.go529
-rw-r--r--vendor/github.com/go-xorm/xorm/mymysql_driver.go65
-rw-r--r--vendor/github.com/go-xorm/xorm/mysql_dialect.go489
-rw-r--r--vendor/github.com/go-xorm/xorm/mysql_driver.go50
-rw-r--r--vendor/github.com/go-xorm/xorm/oci8_driver.go37
-rw-r--r--vendor/github.com/go-xorm/xorm/odbc_driver.go34
-rw-r--r--vendor/github.com/go-xorm/xorm/oracle_dialect.go846
-rw-r--r--vendor/github.com/go-xorm/xorm/pg_reserved.txt746
-rw-r--r--vendor/github.com/go-xorm/xorm/postgres_dialect.go1091
-rw-r--r--vendor/github.com/go-xorm/xorm/pq_driver.go119
-rw-r--r--vendor/github.com/go-xorm/xorm/processors.go50
-rw-r--r--vendor/github.com/go-xorm/xorm/rows.go146
-rw-r--r--vendor/github.com/go-xorm/xorm/session.go4144
-rw-r--r--vendor/github.com/go-xorm/xorm/sqlite3_dialect.go435
-rw-r--r--vendor/github.com/go-xorm/xorm/sqlite3_driver.go20
-rw-r--r--vendor/github.com/go-xorm/xorm/statement.go1359
-rw-r--r--vendor/github.com/go-xorm/xorm/syslogger.go77
-rw-r--r--vendor/github.com/go-xorm/xorm/types.go12
-rw-r--r--vendor/github.com/go-xorm/xorm/xorm.go104
33 files changed, 13999 insertions, 0 deletions
diff --git a/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md b/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md
new file mode 100644
index 0000000000..e0f6cfcdf2
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+## Contributing to xorm
+
+`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very
+much welcome. You can help with patch review, submitting bug reports,
+or adding new functionality. There is no formal style guide, but
+please conform to the style of existing code and general Go formatting
+conventions when submitting patches.
+
+* [fork a repo](https://help.github.com/articles/fork-a-repo)
+* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request)
+
+### Language
+
+Since `xorm` is a world-wide open source project, please describe your issues or code changes in English as soon as possible.
+
+### Sign your codes with comments
+```
+// !<you github id>! your comments
+
+e.g.,
+
+// !lunny! this is comments made by lunny
+```
+
+### Patch review
+
+Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or
+proposed functionality.
+
+### Bug reports
+
+We appreciate any bug reports, but especially ones with self-contained
+(doesn't depend on code outside of xorm), minimal (can't be simplified
+further) test cases. It's especially helpful if you can submit a pull
+request with just the failing test case (you'll probably want to
+pattern it after the tests in
+[base.go](https://github.com/go-xorm/tests/blob/master/base.go) AND
+[benchmark.go](https://github.com/go-xorm/tests/blob/master/benchmark.go).
+
+If you implements a new database interface, you maybe need to add a <databasename>_test.go file.
+For example, [mysql_test.go](https://github.com/go-xorm/tests/blob/master/mysql/mysql_test.go)
+
+### New functionality
+
+There are a number of pending patches for new functionality, so
+additional feature patches will take a while to merge. Still, patches
+are generally reviewed based on usefulness and complexity in addition
+to time-in-queue, so if you have a knockout idea, take a shot. Feel
+free to open an issue discussion your proposed patch beforehand.
diff --git a/vendor/github.com/go-xorm/xorm/LICENSE b/vendor/github.com/go-xorm/xorm/LICENSE
new file mode 100644
index 0000000000..84d2ae5386
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 - 2015 The Xorm Authors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the {organization} nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/go-xorm/xorm/README.md b/vendor/github.com/go-xorm/xorm/README.md
new file mode 100644
index 0000000000..7ee669e82e
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/README.md
@@ -0,0 +1,289 @@
+[中文](https://github.com/go-xorm/xorm/blob/master/README_CN.md)
+
+Xorm is a simple and powerful ORM for Go.
+
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest)
+
+# Notice
+
+The last master version is not backwards compatible. You should use `engine.ShowSQL()` and `engine.Logger().SetLevel()` instead of `engine.ShowSQL = `, `engine.ShowInfo = ` and so on.
+
+# Features
+
+* Struct <-> Table Mapping Support
+
+* Chainable APIs
+
+* Transaction Support
+
+* Both ORM and raw SQL operation Support
+
+* Sync database schema Support
+
+* Query Cache speed up
+
+* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
+
+* Simple cascade loading support
+
+* Optimistic Locking support
+
+* SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder)
+
+# Drivers Support
+
+Drivers for Go's sql package which currently support database/sql includes:
+
+* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
+
+* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
+
+* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
+
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
+* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
+
+* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
+
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
+
+# Changelog
+
+* **v0.6.0**
+ * remove support for ql
+ * add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or`
+methods can use `builder.Cond` as parameter
+ * add Sum, SumInt, SumInt64 and NotIn methods
+ * some bugs fixed
+
+* **v0.5.0**
+ * logging interface changed
+ * some bugs fixed
+
+* **v0.4.5**
+ * many bugs fixed
+ * extends support unlimited deepth
+ * Delete Limit support
+
+* **v0.4.4**
+ * ql database expriment support
+ * tidb database expriment support
+ * sql.NullString and etc. field support
+ * select ForUpdate support
+ * many bugs fixed
+
+[More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16)
+
+# Installation
+
+If you have [gopm](https://github.com/gpmgo/gopm) installed,
+
+ gopm get github.com/go-xorm/xorm
+
+Or
+
+ go get github.com/go-xorm/xorm
+
+# Documents
+
+* [Manual](http://xorm.io/docs)
+
+* [GoDoc](http://godoc.org/github.com/go-xorm/xorm)
+
+* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm)
+
+# Quick Start
+
+* Create Engine
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* Define a struct and Sync2 table struct to database
+
+```Go
+type User struct {
+ Id int64
+ Name string
+ Salt string
+ Age int
+ Passwd string `xorm:"varchar(200)"`
+ Created time.Time `xorm:"created"`
+ Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* Query a SQL string, the returned results is []map[string][]byte
+
+```Go
+results, err := engine.Query("select * from user")
+```
+
+* Execute a SQL string, the returned results
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* Insert one or multiple records to database
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* Query one record from database
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+```
+
+* Query multiple records from database, also you can use join and extends
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
+
+type Detail struct {
+ Id int64
+ UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+ User `xorm:"extends"`
+ Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*")
+ Join("INNER", "detail", "detail.user_id = user.id").
+ Where("user.name = ?", name).Limit(10, 0).
+ Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
+```
+
+* Query multiple records and record by record handle, there are two methods Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+ err = rows.Scan(bean)
+}
+```
+
+* Update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on.
+
+```Go
+affected, err := engine.Id(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In("id", ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.Id(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* Delete one or more records, Delete MUST have condition
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+affected, err := engine.Id(2).Delete(&user)
+```
+
+* Count records
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
+* Query conditions builder
+
+```Go
+err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
+// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
+```
+
+# Cases
+
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
+
+* [Wego](http://github.com/go-tango/wego)
+
+* [Docker.cn](https://docker.cn/)
+
+* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
+
+* [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel)
+
+* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
+
+* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
+
+* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
+
+* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
+
+* [YouGam](http://www.yougam.com/)
+
+* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
+
+* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
+
+* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
+
+# Discuss
+
+Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm)
+
+# Contributing
+
+If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
+
+# LICENSE
+
+ BSD License
+ [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
diff --git a/vendor/github.com/go-xorm/xorm/README_CN.md b/vendor/github.com/go-xorm/xorm/README_CN.md
new file mode 100644
index 0000000000..f71cd70593
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/README_CN.md
@@ -0,0 +1,288 @@
+# xorm
+
+[English](https://github.com/go-xorm/xorm/blob/master/README.md)
+
+xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
+
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm)
+
+# 注意
+
+最新的版本有不兼容的更新,您必须使用 `engine.ShowSQL()` 和 `engine.Logger().SetLevel()` 来替代 `engine.ShowSQL = `, `engine.ShowInfo = ` 等等。
+
+## 特性
+
+* 支持Struct和数据库表之间的灵活映射,并支持自动同步
+
+* 事务支持
+
+* 同时支持原始SQL语句和ORM操作的混合执行
+
+* 使用连写来简化调用
+
+* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
+
+* 支持级联加载Struct
+
+* 支持缓存
+
+* 支持根据数据库自动生成xorm的结构体
+
+* 支持记录版本(即乐观锁)
+
+* 内置SQL Builder支持
+
+## 驱动支持
+
+目前支持的Go数据库驱动和对应的数据库如下:
+
+* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
+
+* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
+
+* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
+
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
+* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
+
+* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
+
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
+
+## 更新日志
+
+* **v0.6.0**
+ * 去除对 ql 的支持
+ * 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数
+将可以用 `builder.Cond` 作为条件组合
+ * 新增 Sum, SumInt, SumInt64 和 NotIn 函数
+ * Bug修正
+
+* **v0.5.0**
+ * logging接口进行不兼容改变
+ * Bug修正
+
+* **v0.4.5**
+ * bug修正
+ * extends 支持无限级
+ * Delete Limit 支持
+
+* **v0.4.4**
+ * Tidb 数据库支持
+ * QL 试验性支持
+ * sql.NullString支持
+ * ForUpdate 支持
+ * bug修正
+
+[更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16)
+
+## 安装
+
+推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
+
+ gopm get github.com/go-xorm/xorm
+
+或者您也可以使用go工具进行安装:
+
+ go get github.com/go-xorm/xorm
+
+## 文档
+
+* [操作指南](http://xorm.io/docs)
+
+* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm)
+
+* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
+
+# 快速开始
+
+* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* 定义一个和表同步的结构体,并且自动同步结构体到数据库
+
+```Go
+type User struct {
+ Id int64
+ Name string
+ Salt string
+ Age int
+ Passwd string `xorm:"varchar(200)"`
+ Created time.Time `xorm:"created"`
+ Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte
+
+```Go
+results, err := engine.Query("select * from user")
+```
+
+* 执行一个SQL语句
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* 插入一条或者多条记录
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* 查询单条记录
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+```
+
+* 查询多条记录,当然可以使用Join和extends来组合使用
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
+
+type Detail struct {
+ Id int64
+ UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+ User `xorm:"extends"`
+ Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*")
+ Join("INNER", "detail", "detail.user_id = user.id").
+ Where("user.name = ?", name).Limit(10, 0).
+ Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
+```
+
+* 根据条件遍历数据库,可以有两种方式: Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+ err = rows.Scan(bean)
+}
+```
+
+* 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段
+
+```Go
+affected, err := engine.Id(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In(ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.Id(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+```
+
+* 获取记录条数
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
+* 条件编辑器
+
+```Go
+err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
+// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
+```
+
+# 案例
+
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
+
+* [Wego](http://github.com/go-tango/wego)
+
+* [Docker.cn](https://docker.cn/)
+
+* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
+
+* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
+
+* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
+
+* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
+
+* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
+
+* [YouGam](http://www.yougam.com/)
+
+* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
+
+* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
+
+* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
+
+## 讨论
+
+请加入QQ群:280360085 进行讨论。
+
+## 贡献
+
+如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
+
+## LICENSE
+
+BSD License
+[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
diff --git a/vendor/github.com/go-xorm/xorm/VERSION b/vendor/github.com/go-xorm/xorm/VERSION
new file mode 100644
index 0000000000..22c1aa4d02
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/VERSION
@@ -0,0 +1 @@
+xorm v0.6.0.1022 \ No newline at end of file
diff --git a/vendor/github.com/go-xorm/xorm/doc.go b/vendor/github.com/go-xorm/xorm/doc.go
new file mode 100644
index 0000000000..e5c35c6748
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/doc.go
@@ -0,0 +1,158 @@
+// Copyright 2013 - 2016 The XORM Authors. All rights reserved.
+// Use of this source code is governed by a BSD
+// license that can be found in the LICENSE file.
+
+/*
+
+Package xorm is a simple and powerful ORM for Go.
+
+Installation
+
+Make sure you have installed Go 1.1+ and then:
+
+ go get github.com/go-xorm/xorm
+
+Create Engine
+
+Firstly, we should new an engine for a database
+
+ engine, err := xorm.NewEngine(driverName, dataSourceName)
+
+Method NewEngine's parameters is the same as sql.Open. It depends
+drivers' implementation.
+Generally, one engine for an application is enough. You can set it as package variable.
+
+Raw Methods
+
+Xorm also support raw sql execution:
+
+1. query a SQL string, the returned results is []map[string][]byte
+
+ results, err := engine.Query("select * from user")
+
+2. execute a SQL string, the returned results
+
+ affected, err := engine.Exec("update user set .... where ...")
+
+ORM Methods
+
+There are 7 major ORM methods and many helpful methods to use to operate database.
+
+1. Insert one or multipe records to database
+
+ affected, err := engine.Insert(&struct)
+ // INSERT INTO struct () values ()
+ affected, err := engine.Insert(&struct1, &struct2)
+ // INSERT INTO struct1 () values ()
+ // INSERT INTO struct2 () values ()
+ affected, err := engine.Insert(&sliceOfStruct)
+ // INSERT INTO struct () values (),(),()
+ affected, err := engine.Insert(&struct1, &sliceOfStruct2)
+ // INSERT INTO struct1 () values ()
+ // INSERT INTO struct2 () values (),(),()
+
+2. Query one record from database
+
+ has, err := engine.Get(&user)
+ // SELECT * FROM user LIMIT 1
+
+3. Query multiple records from database
+
+ sliceOfStructs := new(Struct)
+ err := engine.Find(sliceOfStructs)
+ // SELECT * FROM user
+
+4. Query multiple records and record by record handle, there two methods, one is Iterate,
+another is Rows
+
+ err := engine.Iterate(...)
+ // SELECT * FROM user
+
+ rows, err := engine.Rows(...)
+ // SELECT * FROM user
+ defer rows.Close()
+ bean := new(Struct)
+ for rows.Next() {
+ err = rows.Scan(bean)
+ }
+
+5. Update one or more records
+
+ affected, err := engine.Id(...).Update(&user)
+ // UPDATE user SET ...
+
+6. Delete one or more records, Delete MUST has conditon
+
+ affected, err := engine.Where(...).Delete(&user)
+ // DELETE FROM user Where ...
+
+7. Count records
+
+ counts, err := engine.Count(&user)
+ // SELECT count(*) AS total FROM user
+
+Conditions
+
+The above 7 methods could use with condition methods chainable.
+Attention: the above 7 methods should be the last chainable method.
+
+1. Id, In
+
+ engine.Id(1).Get(&user) // for single primary key
+ // SELECT * FROM user WHERE id = 1
+ engine.Id(core.PK{1, 2}).Get(&user) // for composite primary keys
+ // SELECT * FROM user WHERE id1 = 1 AND id2 = 2
+ engine.In("id", 1, 2, 3).Find(&users)
+ // SELECT * FROM user WHERE id IN (1, 2, 3)
+ engine.In("id", []int{1, 2, 3})
+ // SELECT * FROM user WHERE id IN (1, 2, 3)
+
+2. Where, And, Or
+
+ engine.Where().And().Or().Find()
+ // SELECT * FROM user WHERE (.. AND ..) OR ...
+
+3. OrderBy, Asc, Desc
+
+ engine.Asc().Desc().Find()
+ // SELECT * FROM user ORDER BY .. ASC, .. DESC
+ engine.OrderBy().Find()
+ // SELECT * FROM user ORDER BY ..
+
+4. Limit, Top
+
+ engine.Limit().Find()
+ // SELECT * FROM user LIMIT .. OFFSET ..
+ engine.Top(5).Find()
+ // SELECT TOP 5 * FROM user // for mssql
+ // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
+
+5. Sql, let you custom SQL
+
+ var users []User
+ engine.Sql("select * from user").Find(&users)
+
+6. Cols, Omit, Distinct
+
+ var users []*User
+ engine.Cols("col1, col2").Find(&users)
+ // SELECT col1, col2 FROM user
+ engine.Cols("col1", "col2").Where().Update(user)
+ // UPDATE user set col1 = ?, col2 = ? Where ...
+ engine.Omit("col1").Find(&users)
+ // SELECT col2, col3 FROM user
+ engine.Omit("col1").Insert(&user)
+ // INSERT INTO table (non-col1) VALUES ()
+ engine.Distinct("col1").Find(&users)
+ // SELECT DISTINCT col1 FROM user
+
+7. Join, GroupBy, Having
+
+ engine.GroupBy("name").Having("name='xlw'").Find(&users)
+ //SELECT * FROM user GROUP BY name HAVING name='xlw'
+ engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users)
+ //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
+
+More usage, please visit http://xorm.io/docs
+*/
+package xorm
diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/github.com/go-xorm/xorm/engine.go
new file mode 100644
index 0000000000..7c2c2f4fff
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/engine.go
@@ -0,0 +1,1689 @@
+// Copyright 2015 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 (
+ "bufio"
+ "bytes"
+ "database/sql"
+ "encoding/gob"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+// Engine is the major struct of xorm, it means a database manager.
+// Commonly, an application only need one engine
+type Engine struct {
+ db *core.DB
+ dialect core.Dialect
+
+ ColumnMapper core.IMapper
+ TableMapper core.IMapper
+ TagIdentifier string
+ Tables map[reflect.Type]*core.Table
+
+ mutex *sync.RWMutex
+ Cacher core.Cacher
+
+ showSQL bool
+ showExecTime bool
+
+ logger core.ILogger
+ TZLocation *time.Location
+ DatabaseTZ *time.Location // The timezone of the database
+
+ disableGlobalCache bool
+}
+
+// ShowSQL show SQL statment or not on logger if log level is great than INFO
+func (engine *Engine) ShowSQL(show ...bool) {
+ engine.logger.ShowSQL(show...)
+ if len(show) == 0 {
+ engine.showSQL = true
+ } else {
+ engine.showSQL = show[0]
+ }
+}
+
+// ShowExecTime show SQL statment and execute time or not on logger if log level is great than INFO
+func (engine *Engine) ShowExecTime(show ...bool) {
+ if len(show) == 0 {
+ engine.showExecTime = true
+ } else {
+ engine.showExecTime = show[0]
+ }
+}
+
+// Logger return the logger interface
+func (engine *Engine) Logger() core.ILogger {
+ return engine.logger
+}
+
+// SetLogger set the new logger
+func (engine *Engine) SetLogger(logger core.ILogger) {
+ engine.logger = logger
+ engine.dialect.SetLogger(logger)
+}
+
+// SetDisableGlobalCache disable global cache or not
+func (engine *Engine) SetDisableGlobalCache(disable bool) {
+ if engine.disableGlobalCache != disable {
+ engine.disableGlobalCache = disable
+ }
+}
+
+// DriverName return the current sql driver's name
+func (engine *Engine) DriverName() string {
+ return engine.dialect.DriverName()
+}
+
+// DataSourceName return the current connection string
+func (engine *Engine) DataSourceName() string {
+ return engine.dialect.DataSourceName()
+}
+
+// SetMapper set the name mapping rules
+func (engine *Engine) SetMapper(mapper core.IMapper) {
+ engine.SetTableMapper(mapper)
+ engine.SetColumnMapper(mapper)
+}
+
+// SetTableMapper set the table name mapping rule
+func (engine *Engine) SetTableMapper(mapper core.IMapper) {
+ engine.TableMapper = mapper
+}
+
+// SetColumnMapper set the column name mapping rule
+func (engine *Engine) SetColumnMapper(mapper core.IMapper) {
+ engine.ColumnMapper = mapper
+}
+
+// SupportInsertMany If engine's database support batch insert records like
+// "insert into user values (name, age), (name, age)".
+// When the return is ture, then engine.Insert(&users) will
+// generate batch sql and exeute.
+func (engine *Engine) SupportInsertMany() bool {
+ return engine.dialect.SupportInsertMany()
+}
+
+// QuoteStr Engine's database use which charactor as quote.
+// mysql, sqlite use ` and postgres use "
+func (engine *Engine) QuoteStr() string {
+ return engine.dialect.QuoteStr()
+}
+
+// Quote Use QuoteStr quote the string sql
+func (engine *Engine) Quote(sql string) string {
+ return engine.quoteTable(sql)
+}
+
+func (engine *Engine) quote(sql string) string {
+ return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr()
+}
+
+func (engine *Engine) quoteTable(keyName string) string {
+ keyName = strings.TrimSpace(keyName)
+ if len(keyName) == 0 {
+ return keyName
+ }
+
+ if string(keyName[0]) == engine.dialect.QuoteStr() || keyName[0] == '`' {
+ return keyName
+ }
+
+ keyName = strings.Replace(keyName, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1)
+
+ return engine.dialect.QuoteStr() + keyName + engine.dialect.QuoteStr()
+}
+
+// SqlType will be depracated, please use SQLType instead
+func (engine *Engine) SqlType(c *core.Column) string {
+ return engine.dialect.SqlType(c)
+}
+
+// SQLType A simple wrapper to dialect's core.SqlType method
+func (engine *Engine) SQLType(c *core.Column) string {
+ return engine.dialect.SqlType(c)
+}
+
+// AutoIncrStr Database's autoincrement statement
+func (engine *Engine) AutoIncrStr() string {
+ return engine.dialect.AutoIncrStr()
+}
+
+// SetMaxOpenConns is only available for go 1.2+
+func (engine *Engine) SetMaxOpenConns(conns int) {
+ engine.db.SetMaxOpenConns(conns)
+}
+
+// SetMaxIdleConns set the max idle connections on pool, default is 2
+func (engine *Engine) SetMaxIdleConns(conns int) {
+ engine.db.SetMaxIdleConns(conns)
+}
+
+// SetDefaultCacher set the default cacher. Xorm's default not enable cacher.
+func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
+ engine.Cacher = cacher
+}
+
+// NoCache If you has set default cacher, and you want temporilly stop use cache,
+// you can use NoCache()
+func (engine *Engine) NoCache() *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.NoCache()
+}
+
+// NoCascade If you do not want to auto cascade load object
+func (engine *Engine) NoCascade() *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.NoCascade()
+}
+
+// MapCacher Set a table use a special cacher
+func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
+ v := rValue(bean)
+ tb := engine.autoMapType(v)
+ tb.Cacher = cacher
+}
+
+// NewDB provides an interface to operate database directly
+func (engine *Engine) NewDB() (*core.DB, error) {
+ return core.OpenDialect(engine.dialect)
+}
+
+// DB return the wrapper of sql.DB
+func (engine *Engine) DB() *core.DB {
+ return engine.db
+}
+
+// Dialect return database dialect
+func (engine *Engine) Dialect() core.Dialect {
+ return engine.dialect
+}
+
+// NewSession New a session
+func (engine *Engine) NewSession() *Session {
+ session := &Session{Engine: engine}
+ session.Init()
+ return session
+}
+
+// Close the engine
+func (engine *Engine) Close() error {
+ return engine.db.Close()
+}
+
+// Ping tests if database is alive
+func (engine *Engine) Ping() error {
+ session := engine.NewSession()
+ defer session.Close()
+ engine.logger.Infof("PING DATABASE %v", engine.DriverName())
+ return session.Ping()
+}
+
+// logging sql
+func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
+ if engine.showSQL && !engine.showExecTime {
+ if len(sqlArgs) > 0 {
+ engine.logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs)
+ } else {
+ engine.logger.Infof("[sql] %v", sqlStr)
+ }
+ }
+}
+
+func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executionBlock func() (*core.Stmt, *core.Rows, error)) (*core.Stmt, *core.Rows, error) {
+ if engine.showSQL && engine.showExecTime {
+ b4ExecTime := time.Now()
+ stmt, res, err := executionBlock()
+ execDuration := time.Since(b4ExecTime)
+ if len(args) > 0 {
+ engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration)
+ } else {
+ engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration)
+ }
+ return stmt, res, err
+ }
+ return executionBlock()
+}
+
+func (engine *Engine) logSQLExecutionTime(sqlStr string, args []interface{}, executionBlock func() (sql.Result, error)) (sql.Result, error) {
+ if engine.showSQL && engine.showExecTime {
+ b4ExecTime := time.Now()
+ res, err := executionBlock()
+ execDuration := time.Since(b4ExecTime)
+ if len(args) > 0 {
+ engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration)
+ } else {
+ engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration)
+ }
+ return res, err
+ }
+ return executionBlock()
+}
+
+// Sql will be depracated, please use SQL instead
+func (engine *Engine) Sql(querystring string, args ...interface{}) *Session {
+ return engine.SQL(querystring, args...)
+}
+
+// SQL method let's you manualy write raw SQL and operate
+// For example:
+//
+// engine.SQL("select * from user").Find(&users)
+//
+// This code will execute "select * from user" and set the records to users
+func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.SQL(query, args...)
+}
+
+// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields
+// will automatically be filled with current time when Insert or Update
+// invoked. Call NoAutoTime if you dont' want to fill automatically.
+func (engine *Engine) NoAutoTime() *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.NoAutoTime()
+}
+
+// NoAutoCondition disable auto generate Where condition from bean or not
+func (engine *Engine) NoAutoCondition(no ...bool) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.NoAutoCondition(no...)
+}
+
+// DBMetas Retrieve all tables, columns, indexes' informations from database.
+func (engine *Engine) DBMetas() ([]*core.Table, error) {
+ tables, err := engine.dialect.GetTables()
+ if err != nil {
+ return nil, err
+ }
+
+ 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])
+ }
+ //table.Columns = cols
+ //table.ColumnsSeq = colSeq
+ indexes, err := engine.dialect.GetIndexes(table.Name)
+ if 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 indexe %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq())
+ }
+ }
+ }
+ }
+ return tables, nil
+}
+
+// DumpAllToFile dump database all table structs and data to a file
+func (engine *Engine) DumpAllToFile(fp string) error {
+ f, err := os.Create(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return engine.DumpAll(f)
+}
+
+// DumpAll dump database all table structs and data to w
+func (engine *Engine) DumpAll(w io.Writer) error {
+ return engine.dumpAll(w, engine.dialect.DBType())
+}
+
+// DumpTablesToFile dump specified tables to SQL file.
+func (engine *Engine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error {
+ f, err := os.Create(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return engine.DumpTables(tables, f, tp...)
+}
+
+// DumpTables dump specify tables to io.Writer
+func (engine *Engine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
+ return engine.dumpTables(tables, w, tp...)
+}
+
+func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) {
+ v := rValue(beanOrTableName)
+ if v.Type().Kind() == reflect.String {
+ return beanOrTableName.(string), nil
+ } else if v.Type().Kind() == reflect.Struct {
+ return engine.tbName(v), nil
+ }
+ return "", errors.New("bean should be a struct or struct's point")
+}
+
+func (engine *Engine) tbName(v reflect.Value) string {
+ if tb, ok := v.Interface().(TableName); ok {
+ return tb.TableName()
+ }
+
+ if v.Type().Kind() == reflect.Ptr {
+ if tb, ok := reflect.Indirect(v).Interface().(TableName); ok {
+ return tb.TableName()
+ }
+ } else if v.CanAddr() {
+ if tb, ok := v.Addr().Interface().(TableName); ok {
+ return tb.TableName()
+ }
+ }
+ return engine.TableMapper.Obj2Table(reflect.Indirect(v).Type().Name())
+}
+
+// DumpAll dump database all table structs and data to w with specify db type
+func (engine *Engine) dumpAll(w io.Writer, tp ...core.DbType) error {
+ tables, err := engine.DBMetas()
+ if err != nil {
+ return err
+ }
+
+ var dialect core.Dialect
+ if len(tp) == 0 {
+ dialect = engine.dialect
+ } else {
+ dialect = core.QueryDialect(tp[0])
+ if dialect == nil {
+ return errors.New("Unsupported database type.")
+ }
+ dialect.Init(nil, engine.dialect.URI(), "", "")
+ }
+
+ _, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n",
+ Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05")))
+ if err != nil {
+ return err
+ }
+
+ for i, table := range tables {
+ if i > 0 {
+ _, err = io.WriteString(w, "\n")
+ if err != nil {
+ return err
+ }
+ }
+ _, err = io.WriteString(w, dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
+ if err != nil {
+ return err
+ }
+ for _, index := range table.Indexes {
+ _, err = io.WriteString(w, dialect.CreateIndexSql(table.Name, index)+";\n")
+ if err != nil {
+ return err
+ }
+ }
+
+ rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name))
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ cols, err := rows.Columns()
+ if err != nil {
+ return err
+ }
+ if len(cols) == 0 {
+ continue
+ }
+ for rows.Next() {
+ dest := make([]interface{}, len(cols))
+ err = rows.ScanSlice(&dest)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+dialect.Quote(strings.Join(cols, dialect.Quote(", ")))+") VALUES (")
+ if err != nil {
+ return err
+ }
+
+ var temp string
+ for i, d := range dest {
+ col := table.GetColumn(cols[i])
+ if d == nil {
+ temp += ", NULL"
+ } else if col.SQLType.IsText() || col.SQLType.IsTime() {
+ var v = fmt.Sprintf("%s", d)
+ temp += ", '" + strings.Replace(v, "'", "''", -1) + "'"
+ } else if col.SQLType.IsBlob() {
+ if reflect.TypeOf(d).Kind() == reflect.Slice {
+ temp += fmt.Sprintf(", %s", dialect.FormatBytes(d.([]byte)))
+ } else if reflect.TypeOf(d).Kind() == reflect.String {
+ temp += fmt.Sprintf(", '%s'", d.(string))
+ }
+ } else if col.SQLType.IsNumeric() {
+ switch reflect.TypeOf(d).Kind() {
+ case reflect.Slice:
+ temp += fmt.Sprintf(", %s", string(d.([]byte)))
+ default:
+ temp += fmt.Sprintf(", %v", d)
+ }
+ } else {
+ s := fmt.Sprintf("%v", d)
+ if strings.Contains(s, ":") || strings.Contains(s, "-") {
+ temp += fmt.Sprintf(", '%s'", s)
+ } else {
+ temp += fmt.Sprintf(", %s", s)
+ }
+ }
+ }
+ _, err = io.WriteString(w, temp[2:]+");\n")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// DumpAll dump database all table structs and data to w with specify db type
+func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
+ var dialect core.Dialect
+ if len(tp) == 0 {
+ dialect = engine.dialect
+ } else {
+ dialect = core.QueryDialect(tp[0])
+ if dialect == nil {
+ return errors.New("Unsupported database type.")
+ }
+ dialect.Init(nil, engine.dialect.URI(), "", "")
+ }
+
+ _, err := io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s, from %s to %s*/\n\n",
+ Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"), engine.dialect.DBType(), dialect.DBType()))
+ if err != nil {
+ return err
+ }
+
+ for i, table := range tables {
+ if i > 0 {
+ _, err = io.WriteString(w, "\n")
+ if err != nil {
+ return err
+ }
+ }
+ _, err = io.WriteString(w, dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
+ if err != nil {
+ return err
+ }
+ for _, index := range table.Indexes {
+ _, err = io.WriteString(w, dialect.CreateIndexSql(table.Name, index)+";\n")
+ if err != nil {
+ return err
+ }
+ }
+
+ rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name))
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ cols, err := rows.Columns()
+ if err != nil {
+ return err
+ }
+ if len(cols) == 0 {
+ continue
+ }
+ for rows.Next() {
+ dest := make([]interface{}, len(cols))
+ err = rows.ScanSlice(&dest)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+dialect.Quote(strings.Join(cols, dialect.Quote(", ")))+") VALUES (")
+ if err != nil {
+ return err
+ }
+
+ var temp string
+ for i, d := range dest {
+ col := table.GetColumn(cols[i])
+ if d == nil {
+ temp += ", NULL"
+ } else if col.SQLType.IsText() || col.SQLType.IsTime() {
+ var v = fmt.Sprintf("%s", d)
+ if strings.HasSuffix(v, " +0000 UTC") {
+ temp += fmt.Sprintf(", '%s'", v[0:len(v)-len(" +0000 UTC")])
+ } else {
+ temp += ", '" + strings.Replace(v, "'", "''", -1) + "'"
+ }
+ } else if col.SQLType.IsBlob() {
+ if reflect.TypeOf(d).Kind() == reflect.Slice {
+ temp += fmt.Sprintf(", %s", dialect.FormatBytes(d.([]byte)))
+ } else if reflect.TypeOf(d).Kind() == reflect.String {
+ temp += fmt.Sprintf(", '%s'", d.(string))
+ }
+ } else if col.SQLType.IsNumeric() {
+ switch reflect.TypeOf(d).Kind() {
+ case reflect.Slice:
+ temp += fmt.Sprintf(", %s", string(d.([]byte)))
+ default:
+ temp += fmt.Sprintf(", %v", d)
+ }
+ } else {
+ s := fmt.Sprintf("%v", d)
+ if strings.Contains(s, ":") || strings.Contains(s, "-") {
+ if strings.HasSuffix(s, " +0000 UTC") {
+ temp += fmt.Sprintf(", '%s'", s[0:len(s)-len(" +0000 UTC")])
+ } else {
+ temp += fmt.Sprintf(", '%s'", s)
+ }
+ } else {
+ temp += fmt.Sprintf(", %s", s)
+ }
+ }
+ }
+ _, err = io.WriteString(w, temp[2:]+");\n")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Cascade use cascade or not
+func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Cascade(trueOrFalse...)
+}
+
+// Where method provide a condition query
+func (engine *Engine) Where(query interface{}, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Where(query, args...)
+}
+
+// Id will be depracated, please use ID instead
+func (engine *Engine) Id(id interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Id(id)
+}
+
+// ID mehtod provoide a condition as (id) = ?
+func (engine *Engine) ID(id interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.ID(id)
+}
+
+// Before apply before Processor, affected bean is passed to closure arg
+func (engine *Engine) Before(closures func(interface{})) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Before(closures)
+}
+
+// After apply after insert Processor, affected bean is passed to closure arg
+func (engine *Engine) After(closures func(interface{})) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.After(closures)
+}
+
+// Charset set charset when create table, only support mysql now
+func (engine *Engine) Charset(charset string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Charset(charset)
+}
+
+// StoreEngine set store engine when create table, only support mysql now
+func (engine *Engine) StoreEngine(storeEngine string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.StoreEngine(storeEngine)
+}
+
+// Distinct use for distinct columns. Caution: when you are using cache,
+// distinct will not be cached because cache system need id,
+// but distinct will not provide id
+func (engine *Engine) Distinct(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Distinct(columns...)
+}
+
+// Select customerize your select columns or contents
+func (engine *Engine) Select(str string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Select(str)
+}
+
+// Cols only use the paramters as select or update columns
+func (engine *Engine) Cols(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Cols(columns...)
+}
+
+// AllCols indicates that all columns should be use
+func (engine *Engine) AllCols() *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.AllCols()
+}
+
+// MustCols specify some columns must use even if they are empty
+func (engine *Engine) MustCols(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.MustCols(columns...)
+}
+
+// UseBool xorm automatically retrieve condition according struct, but
+// if struct has bool field, it will ignore them. So use UseBool
+// to tell system to do not ignore them.
+// If no paramters, it will use all the bool field of struct, or
+// it will use paramters's columns
+func (engine *Engine) UseBool(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.UseBool(columns...)
+}
+
+// Omit only not use the paramters as select or update columns
+func (engine *Engine) Omit(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Omit(columns...)
+}
+
+// Nullable set null when column is zero-value and nullable for update
+func (engine *Engine) Nullable(columns ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Nullable(columns...)
+}
+
+// In will generate "column IN (?, ?)"
+func (engine *Engine) In(column string, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.In(column, args...)
+}
+
+// Incr provides a update string like "column = column + ?"
+func (engine *Engine) Incr(column string, arg ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Incr(column, arg...)
+}
+
+// Decr provides a update string like "column = column - ?"
+func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Decr(column, arg...)
+}
+
+// SetExpr provides a update string like "column = {expression}"
+func (engine *Engine) SetExpr(column string, expression string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.SetExpr(column, expression)
+}
+
+// Table temporarily change the Get, Find, Update's table
+func (engine *Engine) Table(tableNameOrBean interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Table(tableNameOrBean)
+}
+
+// Alias set the table alias
+func (engine *Engine) Alias(alias string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Alias(alias)
+}
+
+// Limit will generate "LIMIT start, limit"
+func (engine *Engine) Limit(limit int, start ...int) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Limit(limit, start...)
+}
+
+// Desc will generate "ORDER BY column1 DESC, column2 DESC"
+func (engine *Engine) Desc(colNames ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Desc(colNames...)
+}
+
+// Asc will generate "ORDER BY column1,column2 Asc"
+// This method can chainable use.
+//
+// engine.Desc("name").Asc("age").Find(&users)
+// // SELECT * FROM user ORDER BY name DESC, age ASC
+//
+func (engine *Engine) Asc(colNames ...string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Asc(colNames...)
+}
+
+// OrderBy will generate "ORDER BY order"
+func (engine *Engine) OrderBy(order string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.OrderBy(order)
+}
+
+// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Join(joinOperator, tablename, condition, args...)
+}
+
+// GroupBy generate group by statement
+func (engine *Engine) GroupBy(keys string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.GroupBy(keys)
+}
+
+// Having generate having statement
+func (engine *Engine) Having(conditions string) *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Having(conditions)
+}
+
+func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
+ t := v.Type()
+ engine.mutex.Lock()
+ defer engine.mutex.Unlock()
+ table, ok := engine.Tables[t]
+ if !ok {
+ table = engine.mapType(v)
+ engine.Tables[t] = table
+ if engine.Cacher != nil {
+ if v.CanAddr() {
+ engine.GobRegister(v.Addr().Interface())
+ } else {
+ engine.GobRegister(v.Interface())
+ }
+ }
+ }
+ return table
+}
+
+// GobRegister register one struct to gob for cache use
+func (engine *Engine) GobRegister(v interface{}) *Engine {
+ //fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v)
+ gob.Register(v)
+ return engine
+}
+
+// Table table struct
+type Table struct {
+ *core.Table
+ Name string
+}
+
+// TableInfo get table info according to bean's content
+func (engine *Engine) TableInfo(bean interface{}) *Table {
+ v := rValue(bean)
+ return &Table{engine.autoMapType(v), engine.tbName(v)}
+}
+
+func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
+ if index, ok := table.Indexes[indexName]; ok {
+ index.AddColumn(col.Name)
+ col.Indexes[index.Name] = indexType
+ } else {
+ index := core.NewIndex(indexName, indexType)
+ index.AddColumn(col.Name)
+ table.AddIndex(index)
+ col.Indexes[index.Name] = indexType
+ }
+}
+
+func (engine *Engine) newTable() *core.Table {
+ table := core.NewEmptyTable()
+
+ if !engine.disableGlobalCache {
+ table.Cacher = engine.Cacher
+ }
+ return table
+}
+
+// TableName table name interface to define customerize table name
+type TableName interface {
+ TableName() string
+}
+
+var (
+ tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()
+)
+
+func (engine *Engine) mapType(v reflect.Value) *core.Table {
+ t := v.Type()
+ table := engine.newTable()
+ if tb, ok := v.Interface().(TableName); ok {
+ table.Name = tb.TableName()
+ } else {
+ if v.CanAddr() {
+ if tb, ok = v.Addr().Interface().(TableName); ok {
+ table.Name = tb.TableName()
+ }
+ }
+ if table.Name == "" {
+ table.Name = engine.TableMapper.Obj2Table(t.Name())
+ }
+ }
+
+ table.Type = t
+
+ var idFieldColName string
+ var err error
+ var hasCacheTag, hasNoCacheTag bool
+
+ for i := 0; i < t.NumField(); i++ {
+ tag := t.Field(i).Tag
+
+ ormTagStr := tag.Get(engine.TagIdentifier)
+ var col *core.Column
+ fieldValue := v.Field(i)
+ 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)}
+ tags := splitTag(ormTagStr)
+
+ if len(tags) > 0 {
+ if tags[0] == "-" {
+ continue
+ }
+ if strings.ToUpper(tags[0]) == "EXTENDS" {
+ switch fieldValue.Kind() {
+ case reflect.Ptr:
+ f := fieldValue.Type().Elem()
+ if f.Kind() == reflect.Struct {
+ fieldPtr := fieldValue
+ fieldValue = fieldValue.Elem()
+ if !fieldValue.IsValid() || fieldPtr.IsNil() {
+ fieldValue = reflect.New(f).Elem()
+ }
+ }
+ fallthrough
+ case reflect.Struct:
+ parentTable := engine.mapType(fieldValue)
+ for _, col := range parentTable.Columns() {
+ col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName)
+ table.AddColumn(col)
+ for indexName, indexType := range col.Indexes {
+ addIndex(indexName, table, col, indexType)
+ }
+ }
+ continue
+ default:
+ //TODO: warning
+ }
+ }
+
+ indexNames := make(map[string]int)
+ var isIndex, isUnique bool
+ var preKey string
+ for j, key := range tags {
+ k := strings.ToUpper(key)
+ switch {
+ case k == "<-":
+ col.MapType = core.ONLYFROMDB
+ case k == "->":
+ col.MapType = core.ONLYTODB
+ case k == "PK":
+ col.IsPrimaryKey = true
+ col.Nullable = false
+ case k == "NULL":
+ if j == 0 {
+ col.Nullable = true
+ } else {
+ col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT")
+ }
+ // TODO: for postgres how add autoincr?
+ /*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
+ col.IsAutoIncrement = true
+
+ autoStart := k[len("AUTOINCR")+1 : len(k)-1]
+ autoStartInt, err := strconv.Atoi(autoStart)
+ if err != nil {
+ engine.LogError(err)
+ }
+ col.AutoIncrStart = autoStartInt*/
+ case k == "AUTOINCR":
+ col.IsAutoIncrement = true
+ //col.AutoIncrStart = 1
+ case k == "DEFAULT":
+ col.Default = tags[j+1]
+ case k == "CREATED":
+ col.IsCreated = true
+ case k == "VERSION":
+ col.IsVersion = true
+ col.Default = "1"
+ case k == "UTC":
+ col.TimeZone = time.UTC
+ case k == "LOCAL":
+ col.TimeZone = time.Local
+ case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"):
+ location := k[len("LOCALE")+1 : len(k)-1]
+ col.TimeZone, err = time.LoadLocation(location)
+ if err != nil {
+ engine.logger.Error(err)
+ }
+ case k == "UPDATED":
+ col.IsUpdated = true
+ case k == "DELETED":
+ col.IsDeleted = true
+ case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"):
+ indexName := k[len("INDEX")+1 : len(k)-1]
+ indexNames[indexName] = core.IndexType
+ case k == "INDEX":
+ isIndex = true
+ case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
+ indexName := k[len("UNIQUE")+1 : len(k)-1]
+ indexNames[indexName] = core.UniqueType
+ case k == "UNIQUE":
+ isUnique = true
+ case k == "NOTNULL":
+ col.Nullable = false
+ case k == "CACHE":
+ if !hasCacheTag {
+ hasCacheTag = true
+ }
+ case k == "NOCACHE":
+ if !hasNoCacheTag {
+ hasNoCacheTag = true
+ }
+ case k == "NOT":
+ default:
+ if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
+ if preKey != "DEFAULT" {
+ col.Name = key[1 : len(key)-1]
+ }
+ } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
+ fs := strings.Split(k, "(")
+
+ if _, ok := core.SqlTypes[fs[0]]; !ok {
+ preKey = k
+ continue
+ }
+ col.SQLType = core.SQLType{Name: fs[0]}
+ if fs[0] == core.Enum && fs[1][0] == '\'' { //enum
+ options := strings.Split(fs[1][0:len(fs[1])-1], ",")
+ col.EnumOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.EnumOptions[v] = k
+ }
+ } else if fs[0] == core.Set && fs[1][0] == '\'' { //set
+ options := strings.Split(fs[1][0:len(fs[1])-1], ",")
+ col.SetOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.SetOptions[v] = k
+ }
+ } else {
+ fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
+ if len(fs2) == 2 {
+ col.Length, err = strconv.Atoi(fs2[0])
+ if err != nil {
+ engine.logger.Error(err)
+ }
+ col.Length2, err = strconv.Atoi(fs2[1])
+ if err != nil {
+ engine.logger.Error(err)
+ }
+ } else if len(fs2) == 1 {
+ col.Length, err = strconv.Atoi(fs2[0])
+ if err != nil {
+ engine.logger.Error(err)
+ }
+ }
+ }
+ } else {
+ if _, ok := core.SqlTypes[k]; ok {
+ col.SQLType = core.SQLType{Name: k}
+ } else if key != col.Default {
+ col.Name = key
+ }
+ }
+ engine.dialect.SqlType(col)
+ }
+ preKey = k
+ }
+ if col.SQLType.Name == "" {
+ col.SQLType = core.Type2SQLType(fieldType)
+ }
+ if col.Length == 0 {
+ col.Length = col.SQLType.DefaultLength
+ }
+ if col.Length2 == 0 {
+ col.Length2 = col.SQLType.DefaultLength2
+ }
+
+ if col.Name == "" {
+ col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
+ }
+
+ if isUnique {
+ indexNames[col.Name] = core.UniqueType
+ } else if isIndex {
+ indexNames[col.Name] = core.IndexType
+ }
+
+ for indexName, indexType := range indexNames {
+ addIndex(indexName, table, col, indexType)
+ }
+ }
+ } else {
+ var sqlType core.SQLType
+ if fieldValue.CanAddr() {
+ if _, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ sqlType = core.SQLType{Name: core.Text}
+ }
+ }
+ if _, ok := fieldValue.Interface().(core.Conversion); ok {
+ sqlType = core.SQLType{Name: core.Text}
+ } else {
+ sqlType = core.Type2SQLType(fieldType)
+ }
+ col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
+ t.Field(i).Name, sqlType, sqlType.DefaultLength,
+ sqlType.DefaultLength2, true)
+ }
+ if col.IsAutoIncrement {
+ col.Nullable = false
+ }
+
+ table.AddColumn(col)
+
+ if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) {
+ idFieldColName = col.Name
+ }
+ } // end for
+
+ if idFieldColName != "" && len(table.PrimaryKeys) == 0 {
+ col := table.GetColumn(idFieldColName)
+ col.IsPrimaryKey = true
+ col.IsAutoIncrement = true
+ col.Nullable = false
+ table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
+ table.AutoIncrement = col.Name
+ }
+
+ if hasCacheTag {
+ if engine.Cacher != nil { // !nash! use engine's cacher if provided
+ engine.logger.Info("enable cache on table:", table.Name)
+ table.Cacher = engine.Cacher
+ } else {
+ engine.logger.Info("enable LRU cache on table:", table.Name)
+ table.Cacher = NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) // !nashtsai! HACK use LRU cacher for now
+ }
+ }
+ if hasNoCacheTag {
+ engine.logger.Info("no cache on table:", table.Name)
+ table.Cacher = nil
+ }
+
+ return table
+}
+
+// IsTableEmpty if a table has any reocrd
+func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.IsTableEmpty(bean)
+}
+
+// IsTableExist if a table is exist
+func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.IsTableExist(beanOrTableName)
+}
+
+// IdOf get id from one struct
+func (engine *Engine) IdOf(bean interface{}) core.PK {
+ return engine.IdOfV(reflect.ValueOf(bean))
+}
+
+// IdOfV get id from one value of struct
+func (engine *Engine) IdOfV(rv reflect.Value) core.PK {
+ v := reflect.Indirect(rv)
+ table := engine.autoMapType(v)
+ pk := make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ pkField := v.FieldByName(col.FieldName)
+ switch pkField.Kind() {
+ case reflect.String:
+ pk[i] = pkField.String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ pk[i] = pkField.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ pk[i] = pkField.Uint()
+ }
+ }
+ return core.PK(pk)
+}
+
+// CreateIndexes create indexes
+func (engine *Engine) CreateIndexes(bean interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.CreateIndexes(bean)
+}
+
+// CreateUniques create uniques
+func (engine *Engine) CreateUniques(bean interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.CreateUniques(bean)
+}
+
+func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
+ return table.Cacher
+}
+
+func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
+ if table := engine.autoMapType(v); table != nil {
+ return table.Cacher
+ }
+ return engine.Cacher
+}
+
+// ClearCacheBean if enabled cache, clear the cache bean
+func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
+ v := rValue(bean)
+ t := v.Type()
+ if t.Kind() != reflect.Struct {
+ return errors.New("error params")
+ }
+ tableName := engine.tbName(v)
+ table := engine.autoMapType(v)
+ cacher := table.Cacher
+ if cacher == nil {
+ cacher = engine.Cacher
+ }
+ if cacher != nil {
+ cacher.ClearIds(tableName)
+ cacher.DelBean(tableName, id)
+ }
+ return nil
+}
+
+// ClearCache if enabled cache, clear some tables' cache
+func (engine *Engine) ClearCache(beans ...interface{}) error {
+ for _, bean := range beans {
+ v := rValue(bean)
+ t := v.Type()
+ if t.Kind() != reflect.Struct {
+ return errors.New("error params")
+ }
+ tableName := engine.tbName(v)
+ table := engine.autoMapType(v)
+ cacher := table.Cacher
+ if cacher == nil {
+ cacher = engine.Cacher
+ }
+ if cacher != nil {
+ cacher.ClearIds(tableName)
+ cacher.ClearBeans(tableName)
+ }
+ }
+ return nil
+}
+
+// Sync the new struct changes to database, this method will automatically add
+// table, column, index, unique. but will not delete or change anything.
+// If you change some field, you should change the database manually.
+func (engine *Engine) Sync(beans ...interface{}) error {
+ for _, bean := range beans {
+ v := rValue(bean)
+ tableName := engine.tbName(v)
+ table := engine.autoMapType(v)
+
+ s := engine.NewSession()
+ defer s.Close()
+ isExist, err := s.Table(bean).isTableExist(tableName)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ err = engine.CreateTables(bean)
+ if err != nil {
+ return err
+ }
+ }
+ /*isEmpty, err := engine.IsEmptyTable(bean)
+ if err != nil {
+ return err
+ }*/
+ var isEmpty bool
+ if isEmpty {
+ err = engine.DropTables(bean)
+ if err != nil {
+ return err
+ }
+ err = engine.CreateTables(bean)
+ if err != nil {
+ return err
+ }
+ } else {
+ for _, col := range table.Columns() {
+ isExist, err := engine.dialect.IsColumnExist(tableName, col.Name)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ session := engine.NewSession()
+ session.Statement.setRefValue(v)
+ defer session.Close()
+ err = session.addColumn(col.Name)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ for name, index := range table.Indexes {
+ session := engine.NewSession()
+ session.Statement.setRefValue(v)
+ defer session.Close()
+ if index.Type == core.UniqueType {
+ //isExist, err := session.isIndexExist(table.Name, name, true)
+ isExist, err := session.isIndexExist2(tableName, index.Cols, true)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ session := engine.NewSession()
+ session.Statement.setRefValue(v)
+ defer session.Close()
+ err = session.addUnique(tableName, name)
+ if err != nil {
+ return err
+ }
+ }
+ } else if index.Type == core.IndexType {
+ isExist, err := session.isIndexExist2(tableName, index.Cols, false)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ session := engine.NewSession()
+ session.Statement.setRefValue(v)
+ defer session.Close()
+ err = session.addIndex(tableName, name)
+ if err != nil {
+ return err
+ }
+ }
+ } else {
+ return errors.New("unknow index type")
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Sync2 synchronize structs to database tables
+func (engine *Engine) Sync2(beans ...interface{}) error {
+ s := engine.NewSession()
+ defer s.Close()
+ return s.Sync2(beans...)
+}
+
+func (engine *Engine) unMap(beans ...interface{}) (e error) {
+ engine.mutex.Lock()
+ defer engine.mutex.Unlock()
+ for _, bean := range beans {
+ t := rType(bean)
+ if _, ok := engine.Tables[t]; ok {
+ delete(engine.Tables, t)
+ }
+ }
+ return
+}
+
+// Drop all mapped table
+func (engine *Engine) dropAll() error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ err := session.Begin()
+ if err != nil {
+ return err
+ }
+ err = session.dropAll()
+ if err != nil {
+ session.Rollback()
+ return err
+ }
+ return session.Commit()
+}
+
+// CreateTables create tabls according bean
+func (engine *Engine) CreateTables(beans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ err := session.Begin()
+ if err != nil {
+ return err
+ }
+
+ for _, bean := range beans {
+ err = session.CreateTable(bean)
+ if err != nil {
+ session.Rollback()
+ return err
+ }
+ }
+ return session.Commit()
+}
+
+// DropTables drop specify tables
+func (engine *Engine) DropTables(beans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ err := session.Begin()
+ if err != nil {
+ return err
+ }
+
+ for _, bean := range beans {
+ err = session.DropTable(bean)
+ if err != nil {
+ session.Rollback()
+ return err
+ }
+ }
+ return session.Commit()
+}
+
+func (engine *Engine) createAll() error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.createAll()
+}
+
+// Exec raw sql
+func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Exec(sql, args...)
+}
+
+// Query a raw sql and return records as []map[string][]byte
+func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Query(sql, paramStr...)
+}
+
+// Insert one or more records
+func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Insert(beans...)
+}
+
+// InsertOne insert only one record
+func (engine *Engine) InsertOne(bean interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.InsertOne(bean)
+}
+
+// Update records, bean's non-empty fields are updated contents,
+// condiBean' non-empty filds are conditions
+// CAUTION:
+// 1.bool will defaultly be updated content nor conditions
+// You should call UseBool if you have bool to use.
+// 2.float32 & float64 may be not inexact as conditions
+func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Update(bean, condiBeans...)
+}
+
+// Delete records, bean's non-empty fields are conditions
+func (engine *Engine) Delete(bean interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Delete(bean)
+}
+
+// Get retrieve one record from table, bean's non-empty fields
+// are conditions
+func (engine *Engine) Get(bean interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Get(bean)
+}
+
+// Find retrieve records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Find(beans, condiBeans...)
+}
+
+// Iterate record by record handle records from table, bean's non-empty fields
+// are conditions.
+func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Iterate(bean, fun)
+}
+
+// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
+// are conditions.
+func (engine *Engine) Rows(bean interface{}) (*Rows, error) {
+ session := engine.NewSession()
+ return session.Rows(bean)
+}
+
+// Count counts the records. bean's non-empty fields are conditions.
+func (engine *Engine) Count(bean interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Count(bean)
+}
+
+// Sum sum the records by some column. bean's non-empty fields are conditions.
+func (engine *Engine) Sum(bean interface{}, colName string) (float64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Sum(bean, colName)
+}
+
+// Sums sum the records by some columns. bean's non-empty fields are conditions.
+func (engine *Engine) Sums(bean interface{}, colNames ...string) ([]float64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Sums(bean, colNames...)
+}
+
+// SumsInt like Sums but return slice of int64 instead of float64.
+func (engine *Engine) SumsInt(bean interface{}, colNames ...string) ([]int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.SumsInt(bean, colNames...)
+}
+
+// ImportFile SQL DDL file
+func (engine *Engine) ImportFile(ddlPath string) ([]sql.Result, error) {
+ file, err := os.Open(ddlPath)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ return engine.Import(file)
+}
+
+// Import SQL DDL from io.Reader
+func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
+ var results []sql.Result
+ var lastError error
+ scanner := bufio.NewScanner(r)
+
+ semiColSpliter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ if i := bytes.IndexByte(data, ';'); i >= 0 {
+ return i + 1, data[0:i], nil
+ }
+ // If we're at EOF, we have a final, non-terminated line. Return it.
+ if atEOF {
+ return len(data), data, nil
+ }
+ // Request more data.
+ return 0, nil, nil
+ }
+
+ scanner.Split(semiColSpliter)
+
+ for scanner.Scan() {
+ query := strings.Trim(scanner.Text(), " \t\n\r")
+ if len(query) > 0 {
+ engine.logSQL(query)
+ result, err := engine.DB().Exec(query)
+ results = append(results, result)
+ if err != nil {
+ return nil, err
+ //lastError = err
+ }
+ }
+ }
+
+ return results, lastError
+}
+
+// TZTime change one time to xorm time location
+func (engine *Engine) TZTime(t time.Time) time.Time {
+ if !t.IsZero() { // if time is not initialized it's not suitable for Time.In()
+ return t.In(engine.TZLocation)
+ }
+ return t
+}
+
+// NowTime return current time
+func (engine *Engine) NowTime(sqlTypeName string) interface{} {
+ t := time.Now()
+ return engine.FormatTime(sqlTypeName, t)
+}
+
+// NowTime2 return current time
+func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) {
+ t := time.Now()
+ return engine.FormatTime(sqlTypeName, t), t
+}
+
+// FormatTime format time
+func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) {
+ return engine.formatTime(engine.TZLocation, sqlTypeName, t)
+}
+
+func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
+ if col.DisableTimeZone {
+ return engine.formatTime(nil, col.SQLType.Name, t)
+ } else if col.TimeZone != nil {
+ return engine.formatTime(col.TimeZone, col.SQLType.Name, t)
+ }
+ return engine.formatTime(engine.TZLocation, col.SQLType.Name, t)
+}
+
+func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.Time) (v interface{}) {
+ if engine.dialect.DBType() == core.ORACLE {
+ return t
+ }
+ if tz != nil {
+ t = engine.TZTime(t)
+ }
+ switch sqlTypeName {
+ case core.Time:
+ s := t.Format("2006-01-02 15:04:05") //time.RFC3339
+ v = s[11:19]
+ case core.Date:
+ v = t.Format("2006-01-02")
+ case core.DateTime, core.TimeStamp:
+ if engine.dialect.DBType() == "ql" {
+ v = t
+ } else if engine.dialect.DBType() == "sqlite3" {
+ v = t.UTC().Format("2006-01-02 15:04:05")
+ } else {
+ v = t.Format("2006-01-02 15:04:05")
+ }
+ case core.TimeStampz:
+ if engine.dialect.DBType() == core.MSSQL {
+ v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
+ } else if engine.DriverName() == "mssql" {
+ v = t
+ } else {
+ v = t.Format(time.RFC3339Nano)
+ }
+ case core.BigInt, core.Int:
+ v = t.Unix()
+ default:
+ v = t
+ }
+ return
+}
+
+// Unscoped always disable struct tag "deleted"
+func (engine *Engine) Unscoped() *Session {
+ session := engine.NewSession()
+ session.IsAutoClose = true
+ return session.Unscoped()
+}
diff --git a/vendor/github.com/go-xorm/xorm/error.go b/vendor/github.com/go-xorm/xorm/error.go
new file mode 100644
index 0000000000..61537a3462
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/error.go
@@ -0,0 +1,19 @@
+// Copyright 2015 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 (
+ "errors"
+)
+
+var (
+ ErrParamsType error = errors.New("Params type error")
+ ErrTableNotFound error = errors.New("Not found table")
+ ErrUnSupportedType error = errors.New("Unsupported type error")
+ ErrNotExist error = errors.New("Not exist error")
+ ErrCacheFailed error = errors.New("Cache failed")
+ ErrNeedDeletedCond error = errors.New("Delete need at least one condition")
+ ErrNotImplemented error = errors.New("Not implemented.")
+)
diff --git a/vendor/github.com/go-xorm/xorm/gen_reserved.sh b/vendor/github.com/go-xorm/xorm/gen_reserved.sh
new file mode 100755
index 0000000000..434a1bfcb0
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/gen_reserved.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ -f $1 ];then
+ cat $1| awk '{printf("\""$1"\":true,\n")}'
+else
+ echo "argument $1 if not a file!"
+fi
diff --git a/vendor/github.com/go-xorm/xorm/goracle_driver.go b/vendor/github.com/go-xorm/xorm/goracle_driver.go
new file mode 100644
index 0000000000..9fcde48f05
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/goracle_driver.go
@@ -0,0 +1,42 @@
+// Copyright 2015 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 (
+ "errors"
+ "regexp"
+
+ "github.com/go-xorm/core"
+)
+
+// func init() {
+// core.RegisterDriver("goracle", &goracleDriver{})
+// }
+
+type goracleDriver struct {
+}
+
+func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.ORACLE}
+ dsnPattern := regexp.MustCompile(
+ `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+ `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+ `\/(?P<dbname>.*?)` + // /dbname
+ `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ //tlsConfigRegister := make(map[string]*tls.Config)
+ names := dsnPattern.SubexpNames()
+
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ db.DbName = match
+ }
+ }
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+ return db, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/helpers.go b/vendor/github.com/go-xorm/xorm/helpers.go
new file mode 100644
index 0000000000..f59db48b1d
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/helpers.go
@@ -0,0 +1,580 @@
+// Copyright 2015 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 (
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+// str2PK convert string value to primary key value according to tp
+func str2PK(s string, tp reflect.Type) (interface{}, error) {
+ var err error
+ var result interface{}
+ switch tp.Kind() {
+ case reflect.Int:
+ result, err = strconv.Atoi(s)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as int: " + err.Error())
+ }
+ case reflect.Int8:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as int16: " + err.Error())
+ }
+ result = int8(x)
+ case reflect.Int16:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as int16: " + err.Error())
+ }
+ result = int16(x)
+ case reflect.Int32:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as int32: " + err.Error())
+ }
+ result = int32(x)
+ case reflect.Int64:
+ result, err = strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as int64: " + err.Error())
+ }
+ case reflect.Uint:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as uint: " + err.Error())
+ }
+ result = uint(x)
+ case reflect.Uint8:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as uint8: " + err.Error())
+ }
+ result = uint8(x)
+ case reflect.Uint16:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as uint16: " + err.Error())
+ }
+ result = uint16(x)
+ case reflect.Uint32:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as uint32: " + err.Error())
+ }
+ result = uint32(x)
+ case reflect.Uint64:
+ result, err = strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return nil, errors.New("convert " + s + " as uint64: " + err.Error())
+ }
+ case reflect.String:
+ result = s
+ default:
+ panic("unsupported convert type")
+ }
+ result = reflect.ValueOf(result).Convert(tp).Interface()
+ return result, nil
+}
+
+func splitTag(tag string) (tags []string) {
+ tag = strings.TrimSpace(tag)
+ var hasQuote = false
+ var lastIdx = 0
+ for i, t := range tag {
+ if t == '\'' {
+ hasQuote = !hasQuote
+ } else if t == ' ' {
+ if lastIdx < i && !hasQuote {
+ tags = append(tags, strings.TrimSpace(tag[lastIdx:i]))
+ lastIdx = i + 1
+ }
+ }
+ }
+ if lastIdx < len(tag) {
+ tags = append(tags, strings.TrimSpace(tag[lastIdx:len(tag)]))
+ }
+ return
+}
+
+type zeroable interface {
+ IsZero() bool
+}
+
+func isZero(k interface{}) bool {
+ switch k.(type) {
+ case int:
+ return k.(int) == 0
+ case int8:
+ return k.(int8) == 0
+ case int16:
+ return k.(int16) == 0
+ case int32:
+ return k.(int32) == 0
+ case int64:
+ return k.(int64) == 0
+ case uint:
+ return k.(uint) == 0
+ case uint8:
+ return k.(uint8) == 0
+ case uint16:
+ return k.(uint16) == 0
+ case uint32:
+ return k.(uint32) == 0
+ case uint64:
+ return k.(uint64) == 0
+ case float32:
+ return k.(float32) == 0
+ case float64:
+ return k.(float64) == 0
+ case bool:
+ return k.(bool) == false
+ case string:
+ return k.(string) == ""
+ case zeroable:
+ return k.(zeroable).IsZero()
+ }
+ return false
+}
+
+func isStructZero(v reflect.Value) bool {
+ if !v.IsValid() {
+ return true
+ }
+
+ for i := 0; i < v.NumField(); i++ {
+ field := v.Field(i)
+ switch field.Kind() {
+ case reflect.Ptr:
+ field = field.Elem()
+ fallthrough
+ case reflect.Struct:
+ if !isStructZero(field) {
+ return false
+ }
+ default:
+ if field.CanInterface() && !isZero(field.Interface()) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
+ var v interface{}
+ switch tp.Kind() {
+ case reflect.Int16:
+ v = int16(id)
+ case reflect.Int32:
+ v = int32(id)
+ case reflect.Int:
+ v = int(id)
+ case reflect.Int64:
+ v = id
+ case reflect.Uint16:
+ v = uint16(id)
+ case reflect.Uint32:
+ v = uint32(id)
+ case reflect.Uint64:
+ v = uint64(id)
+ case reflect.Uint:
+ v = uint(id)
+ }
+ return reflect.ValueOf(v).Convert(tp)
+}
+
+func int64ToInt(id int64, tp reflect.Type) interface{} {
+ return int64ToIntValue(id, tp).Interface()
+}
+
+func isPKZero(pk core.PK) bool {
+ for _, k := range pk {
+ if isZero(k) {
+ return true
+ }
+ }
+ return false
+}
+
+func indexNoCase(s, sep string) int {
+ return strings.Index(strings.ToLower(s), strings.ToLower(sep))
+}
+
+func splitNoCase(s, sep string) []string {
+ idx := indexNoCase(s, sep)
+ if idx < 0 {
+ return []string{s}
+ }
+ return strings.Split(s, s[idx:idx+len(sep)])
+}
+
+func splitNNoCase(s, sep string, n int) []string {
+ idx := indexNoCase(s, sep)
+ if idx < 0 {
+ return []string{s}
+ }
+ return strings.SplitN(s, s[idx:idx+len(sep)], n)
+}
+
+func makeArray(elem string, count int) []string {
+ res := make([]string, count)
+ for i := 0; i < count; i++ {
+ res[i] = elem
+ }
+ return res
+}
+
+func rValue(bean interface{}) reflect.Value {
+ return reflect.Indirect(reflect.ValueOf(bean))
+}
+
+func rType(bean interface{}) reflect.Type {
+ sliceValue := reflect.Indirect(reflect.ValueOf(bean))
+ //return reflect.TypeOf(sliceValue.Interface())
+ return sliceValue.Type()
+}
+
+func structName(v reflect.Type) string {
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ return v.Name()
+}
+
+func col2NewCols(columns ...string) []string {
+ newColumns := make([]string, 0, len(columns))
+ for _, col := range columns {
+ col = strings.Replace(col, "`", "", -1)
+ col = strings.Replace(col, `"`, "", -1)
+ ccols := strings.Split(col, ",")
+ for _, c := range ccols {
+ newColumns = append(newColumns, strings.TrimSpace(c))
+ }
+ }
+ return newColumns
+}
+
+func sliceEq(left, right []string) bool {
+ if len(left) != len(right) {
+ return false
+ }
+ sort.Sort(sort.StringSlice(left))
+ sort.Sort(sort.StringSlice(right))
+ for i := 0; i < len(left); i++ {
+ if left[i] != right[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func reflect2value(rawValue *reflect.Value) (str string, err error) {
+ aa := reflect.TypeOf((*rawValue).Interface())
+ vv := reflect.ValueOf((*rawValue).Interface())
+ switch aa.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ str = strconv.FormatInt(vv.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ str = strconv.FormatUint(vv.Uint(), 10)
+ case reflect.Float32, reflect.Float64:
+ str = strconv.FormatFloat(vv.Float(), 'f', -1, 64)
+ case reflect.String:
+ str = vv.String()
+ case reflect.Array, reflect.Slice:
+ switch aa.Elem().Kind() {
+ case reflect.Uint8:
+ data := rawValue.Interface().([]byte)
+ str = string(data)
+ default:
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ // time type
+ case reflect.Struct:
+ if aa.ConvertibleTo(core.TimeType) {
+ str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
+ } else {
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ case reflect.Bool:
+ str = strconv.FormatBool(vv.Bool())
+ case reflect.Complex128, reflect.Complex64:
+ str = fmt.Sprintf("%v", vv.Complex())
+ /* TODO: unsupported types below
+ case reflect.Map:
+ case reflect.Ptr:
+ case reflect.Uintptr:
+ case reflect.UnsafePointer:
+ case reflect.Chan, reflect.Func, reflect.Interface:
+ */
+ default:
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ return
+}
+
+func value2Bytes(rawValue *reflect.Value) (data []byte, err error) {
+ var str string
+ str, err = reflect2value(rawValue)
+ if err != nil {
+ return
+ }
+ data = []byte(str)
+ return
+}
+
+func value2String(rawValue *reflect.Value) (data string, err error) {
+ data, err = reflect2value(rawValue)
+ if err != nil {
+ return
+ }
+ return
+}
+
+func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ result, err := row2mapStr(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, result)
+ }
+
+ return resultsSlice, nil
+}
+
+func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ result, err := row2map(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, result)
+ }
+
+ return resultsSlice, nil
+}
+
+func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) {
+ result := make(map[string][]byte)
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for ii, key := range fields {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
+ //if row is null then ignore
+ if rawValue.Interface() == nil {
+ //fmt.Println("ignore ...", key, rawValue)
+ continue
+ }
+
+ if data, err := value2Bytes(&rawValue); err == nil {
+ result[key] = data
+ } else {
+ return nil, err // !nashtsai! REVIEW, should return err or just error log?
+ }
+ }
+ return result, nil
+}
+
+func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) {
+ result := make(map[string]string)
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for ii, key := range fields {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
+ //if row is null then ignore
+ if rawValue.Interface() == nil {
+ //fmt.Println("ignore ...", key, rawValue)
+ continue
+ }
+
+ if data, err := value2String(&rawValue); err == nil {
+ result[key] = data
+ } else {
+ return nil, err // !nashtsai! REVIEW, should return err or just error log?
+ }
+ }
+ return result, nil
+}
+
+func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
+ rows, err := tx.Query(sqlStr, params...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2Strings(rows)
+}
+
+func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
+ s, err := db.Prepare(sqlStr)
+ if err != nil {
+ return nil, err
+ }
+ defer s.Close()
+ rows, err := s.Query(params...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ return rows2Strings(rows)
+}
+
+func setColumnInt(bean interface{}, col *core.Column, t int64) {
+ v, err := col.ValueOf(bean)
+ if err != nil {
+ return
+ }
+ if v.CanSet() {
+ switch v.Type().Kind() {
+ case reflect.Int, reflect.Int64, reflect.Int32:
+ v.SetInt(t)
+ case reflect.Uint, reflect.Uint64, reflect.Uint32:
+ v.SetUint(uint64(t))
+ }
+ }
+}
+
+func setColumnTime(bean interface{}, col *core.Column, t time.Time) {
+ v, err := col.ValueOf(bean)
+ if err != nil {
+ return
+ }
+ if v.CanSet() {
+ switch v.Type().Kind() {
+ case reflect.Struct:
+ v.Set(reflect.ValueOf(t).Convert(v.Type()))
+ case reflect.Int, reflect.Int64, reflect.Int32:
+ v.SetInt(t.Unix())
+ case reflect.Uint, reflect.Uint64, reflect.Uint32:
+ v.SetUint(uint64(t.Unix()))
+ }
+ }
+}
+
+func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) {
+ colNames := make([]string, 0, len(table.ColumnsSeq()))
+ args := make([]interface{}, 0, len(table.ColumnsSeq()))
+
+ for _, col := range table.Columns() {
+ lColName := strings.ToLower(col.Name)
+ if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated {
+ if _, ok := session.Statement.columnMap[lColName]; !ok {
+ continue
+ }
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ return nil, nil, err
+ }
+ fieldValue := *fieldValuePtr
+
+ if col.IsAutoIncrement {
+ switch fieldValue.Type().Kind() {
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
+ if fieldValue.Int() == 0 {
+ continue
+ }
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
+ if fieldValue.Uint() == 0 {
+ continue
+ }
+ case reflect.String:
+ if len(fieldValue.String()) == 0 {
+ continue
+ }
+ }
+ }
+
+ if col.IsDeleted {
+ continue
+ }
+
+ if session.Statement.ColumnStr != "" {
+ if _, ok := session.Statement.columnMap[lColName]; !ok {
+ continue
+ }
+ }
+ if session.Statement.OmitStr != "" {
+ if _, ok := session.Statement.columnMap[lColName]; ok {
+ continue
+ }
+ }
+
+ // !evalphobia! set fieldValue as nil when column is nullable and zero-value
+ if _, ok := session.Statement.nullableMap[lColName]; ok {
+ if col.Nullable && isZero(fieldValue.Interface()) {
+ var nilValue *int
+ fieldValue = reflect.ValueOf(nilValue)
+ }
+ }
+
+ if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
+ // if time is non-empty, then set to auto time
+ val, t := session.Engine.NowTime2(col.SQLType.Name)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.Statement.checkVersion {
+ args = append(args, 1)
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return colNames, args, err
+ }
+ args = append(args, arg)
+ }
+
+ if includeQuote {
+ colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?")
+ } else {
+ colNames = append(colNames, col.Name)
+ }
+ }
+ return colNames, args, nil
+}
+
+func indexName(tableName, idxName string) string {
+ return fmt.Sprintf("IDX_%v_%v", tableName, idxName)
+}
diff --git a/vendor/github.com/go-xorm/xorm/logger.go b/vendor/github.com/go-xorm/xorm/logger.go
new file mode 100644
index 0000000000..b4f9737816
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/logger.go
@@ -0,0 +1,162 @@
+// Copyright 2015 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 (
+ "fmt"
+ "io"
+ "log"
+
+ "github.com/go-xorm/core"
+)
+
+const (
+ DEFAULT_LOG_PREFIX = "[xorm]"
+ DEFAULT_LOG_FLAG = log.Ldate | log.Lmicroseconds
+ DEFAULT_LOG_LEVEL = core.LOG_DEBUG
+)
+
+var _ core.ILogger = DiscardLogger{}
+
+type DiscardLogger struct{}
+
+func (DiscardLogger) Debug(v ...interface{}) {}
+func (DiscardLogger) Debugf(format string, v ...interface{}) {}
+func (DiscardLogger) Error(v ...interface{}) {}
+func (DiscardLogger) Errorf(format string, v ...interface{}) {}
+func (DiscardLogger) Info(v ...interface{}) {}
+func (DiscardLogger) Infof(format string, v ...interface{}) {}
+func (DiscardLogger) Warn(v ...interface{}) {}
+func (DiscardLogger) Warnf(format string, v ...interface{}) {}
+func (DiscardLogger) Level() core.LogLevel {
+ return core.LOG_UNKNOWN
+}
+func (DiscardLogger) SetLevel(l core.LogLevel) {}
+func (DiscardLogger) ShowSQL(show ...bool) {}
+func (DiscardLogger) IsShowSQL() bool {
+ return false
+}
+
+// SimpleLogger is the default implment of core.ILogger
+type SimpleLogger struct {
+ DEBUG *log.Logger
+ ERR *log.Logger
+ INFO *log.Logger
+ WARN *log.Logger
+ level core.LogLevel
+ showSQL bool
+}
+
+var _ core.ILogger = &SimpleLogger{}
+
+// NewSimpleLogger use a special io.Writer as logger output
+func NewSimpleLogger(out io.Writer) *SimpleLogger {
+ return NewSimpleLogger2(out, DEFAULT_LOG_PREFIX, DEFAULT_LOG_FLAG)
+}
+
+// NewSimpleLogger2 let you customrize your logger prefix and flag
+func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger {
+ return NewSimpleLogger3(out, prefix, flag, DEFAULT_LOG_LEVEL)
+}
+
+// NewSimpleLogger3 let you customrize your logger prefix and flag and logLevel
+func NewSimpleLogger3(out io.Writer, prefix string, flag int, l core.LogLevel) *SimpleLogger {
+ return &SimpleLogger{
+ DEBUG: log.New(out, fmt.Sprintf("%s [debug] ", prefix), flag),
+ ERR: log.New(out, fmt.Sprintf("%s [error] ", prefix), flag),
+ INFO: log.New(out, fmt.Sprintf("%s [info] ", prefix), flag),
+ WARN: log.New(out, fmt.Sprintf("%s [warn] ", prefix), flag),
+ level: l,
+ }
+}
+
+// Error implement core.ILogger
+func (s *SimpleLogger) Error(v ...interface{}) {
+ if s.level <= core.LOG_ERR {
+ s.ERR.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Errorf implement core.ILogger
+func (s *SimpleLogger) Errorf(format string, v ...interface{}) {
+ if s.level <= core.LOG_ERR {
+ s.ERR.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Debug implement core.ILogger
+func (s *SimpleLogger) Debug(v ...interface{}) {
+ if s.level <= core.LOG_DEBUG {
+ s.DEBUG.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Debugf implement core.ILogger
+func (s *SimpleLogger) Debugf(format string, v ...interface{}) {
+ if s.level <= core.LOG_DEBUG {
+ s.DEBUG.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Info implement core.ILogger
+func (s *SimpleLogger) Info(v ...interface{}) {
+ if s.level <= core.LOG_INFO {
+ s.INFO.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Infof implement core.ILogger
+func (s *SimpleLogger) Infof(format string, v ...interface{}) {
+ if s.level <= core.LOG_INFO {
+ s.INFO.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Warn implement core.ILogger
+func (s *SimpleLogger) Warn(v ...interface{}) {
+ if s.level <= core.LOG_WARNING {
+ s.WARN.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Warnf implement core.ILogger
+func (s *SimpleLogger) Warnf(format string, v ...interface{}) {
+ if s.level <= core.LOG_WARNING {
+ s.WARN.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Level implement core.ILogger
+func (s *SimpleLogger) Level() core.LogLevel {
+ return s.level
+}
+
+// SetLevel implement core.ILogger
+func (s *SimpleLogger) SetLevel(l core.LogLevel) {
+ s.level = l
+ return
+}
+
+// ShowSQL implement core.ILogger
+func (s *SimpleLogger) ShowSQL(show ...bool) {
+ if len(show) == 0 {
+ s.showSQL = true
+ return
+ }
+ s.showSQL = show[0]
+}
+
+// IsShowSQL implement core.ILogger
+func (s *SimpleLogger) IsShowSQL() bool {
+ return s.showSQL
+}
diff --git a/vendor/github.com/go-xorm/xorm/lru_cacher.go b/vendor/github.com/go-xorm/xorm/lru_cacher.go
new file mode 100644
index 0000000000..855cce2ee4
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/lru_cacher.go
@@ -0,0 +1,289 @@
+// Copyright 2015 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 (
+ "container/list"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+type LRUCacher struct {
+ idList *list.List
+ sqlList *list.List
+ idIndex map[string]map[string]*list.Element
+ sqlIndex map[string]map[string]*list.Element
+ store core.CacheStore
+ mutex sync.Mutex
+ // maxSize int
+ MaxElementSize int
+ Expired time.Duration
+ GcInterval time.Duration
+}
+
+func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher {
+ return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
+}
+
+func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
+ cacher := &LRUCacher{store: store, idList: list.New(),
+ sqlList: list.New(), Expired: expired,
+ GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize,
+ sqlIndex: make(map[string]map[string]*list.Element),
+ idIndex: make(map[string]map[string]*list.Element),
+ }
+ cacher.RunGC()
+ return cacher
+}
+
+//func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher {
+// return newLRUCacher(store, expired, maxSize, 0)
+//}
+
+// RunGC run once every m.GcInterval
+func (m *LRUCacher) RunGC() {
+ time.AfterFunc(m.GcInterval, func() {
+ m.RunGC()
+ m.GC()
+ })
+}
+
+// GC check ids lit and sql list to remove all element expired
+func (m *LRUCacher) GC() {
+ //fmt.Println("begin gc ...")
+ //defer fmt.Println("end gc ...")
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ var removedNum int
+ for e := m.idList.Front(); e != nil; {
+ if removedNum <= core.CacheGcMaxRemoved &&
+ time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
+ removedNum++
+ next := e.Next()
+ //fmt.Println("removing ...", e.Value)
+ node := e.Value.(*idNode)
+ m.delBean(node.tbName, node.id)
+ e = next
+ } else {
+ //fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.idList.Len())
+ break
+ }
+ }
+
+ removedNum = 0
+ for e := m.sqlList.Front(); e != nil; {
+ if removedNum <= core.CacheGcMaxRemoved &&
+ time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
+ removedNum++
+ next := e.Next()
+ //fmt.Println("removing ...", e.Value)
+ node := e.Value.(*sqlNode)
+ m.delIds(node.tbName, node.sql)
+ e = next
+ } else {
+ //fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.sqlList.Len())
+ break
+ }
+ }
+}
+
+// Get all bean's ids according to sql and parameter from cache
+func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ if _, ok := m.sqlIndex[tableName]; !ok {
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+ }
+ if v, err := m.store.Get(sql); err == nil {
+ if el, ok := m.sqlIndex[tableName][sql]; !ok {
+ el = m.sqlList.PushBack(newSqlNode(tableName, sql))
+ m.sqlIndex[tableName][sql] = el
+ } else {
+ lastTime := el.Value.(*sqlNode).lastVisit
+ // if expired, remove the node and return nil
+ if time.Now().Sub(lastTime) > m.Expired {
+ m.delIds(tableName, sql)
+ return nil
+ }
+ m.sqlList.MoveToBack(el)
+ el.Value.(*sqlNode).lastVisit = time.Now()
+ }
+ return v
+ } else {
+ m.delIds(tableName, sql)
+ }
+
+ return nil
+}
+
+// Get bean according tableName and id from cache
+func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ if _, ok := m.idIndex[tableName]; !ok {
+ m.idIndex[tableName] = make(map[string]*list.Element)
+ }
+ tid := genId(tableName, id)
+ if v, err := m.store.Get(tid); err == nil {
+ if el, ok := m.idIndex[tableName][id]; ok {
+ lastTime := el.Value.(*idNode).lastVisit
+ // if expired, remove the node and return nil
+ if time.Now().Sub(lastTime) > m.Expired {
+ m.delBean(tableName, id)
+ //m.clearIds(tableName)
+ return nil
+ }
+ m.idList.MoveToBack(el)
+ el.Value.(*idNode).lastVisit = time.Now()
+ } else {
+ el = m.idList.PushBack(newIdNode(tableName, id))
+ m.idIndex[tableName][id] = el
+ }
+ return v
+ } else {
+ // store bean is not exist, then remove memory's index
+ m.delBean(tableName, id)
+ //m.clearIds(tableName)
+ return nil
+ }
+}
+
+// Clear all sql-ids mapping on table tableName from cache
+func (m *LRUCacher) clearIds(tableName string) {
+ if tis, ok := m.sqlIndex[tableName]; ok {
+ for sql, v := range tis {
+ m.sqlList.Remove(v)
+ m.store.Del(sql)
+ }
+ }
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+}
+
+func (m *LRUCacher) ClearIds(tableName string) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ m.clearIds(tableName)
+}
+
+func (m *LRUCacher) clearBeans(tableName string) {
+ if tis, ok := m.idIndex[tableName]; ok {
+ for id, v := range tis {
+ m.idList.Remove(v)
+ tid := genId(tableName, id)
+ m.store.Del(tid)
+ }
+ }
+ m.idIndex[tableName] = make(map[string]*list.Element)
+}
+
+func (m *LRUCacher) ClearBeans(tableName string) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ m.clearBeans(tableName)
+}
+
+func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ if _, ok := m.sqlIndex[tableName]; !ok {
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+ }
+ if el, ok := m.sqlIndex[tableName][sql]; !ok {
+ el = m.sqlList.PushBack(newSqlNode(tableName, sql))
+ m.sqlIndex[tableName][sql] = el
+ } else {
+ el.Value.(*sqlNode).lastVisit = time.Now()
+ }
+ m.store.Put(sql, ids)
+ if m.sqlList.Len() > m.MaxElementSize {
+ e := m.sqlList.Front()
+ node := e.Value.(*sqlNode)
+ m.delIds(node.tbName, node.sql)
+ }
+}
+
+func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ var el *list.Element
+ var ok bool
+
+ if el, ok = m.idIndex[tableName][id]; !ok {
+ el = m.idList.PushBack(newIdNode(tableName, id))
+ m.idIndex[tableName][id] = el
+ } else {
+ el.Value.(*idNode).lastVisit = time.Now()
+ }
+
+ m.store.Put(genId(tableName, id), obj)
+ if m.idList.Len() > m.MaxElementSize {
+ e := m.idList.Front()
+ node := e.Value.(*idNode)
+ m.delBean(node.tbName, node.id)
+ }
+}
+
+func (m *LRUCacher) delIds(tableName, sql string) {
+ if _, ok := m.sqlIndex[tableName]; ok {
+ if el, ok := m.sqlIndex[tableName][sql]; ok {
+ delete(m.sqlIndex[tableName], sql)
+ m.sqlList.Remove(el)
+ }
+ }
+ m.store.Del(sql)
+}
+
+func (m *LRUCacher) DelIds(tableName, sql string) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ m.delIds(tableName, sql)
+}
+
+func (m *LRUCacher) delBean(tableName string, id string) {
+ tid := genId(tableName, id)
+ if el, ok := m.idIndex[tableName][id]; ok {
+ delete(m.idIndex[tableName], id)
+ m.idList.Remove(el)
+ m.clearIds(tableName)
+ }
+ m.store.Del(tid)
+}
+
+func (m *LRUCacher) DelBean(tableName string, id string) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ m.delBean(tableName, id)
+}
+
+type idNode struct {
+ tbName string
+ id string
+ lastVisit time.Time
+}
+
+type sqlNode struct {
+ tbName string
+ sql string
+ lastVisit time.Time
+}
+
+func genSqlKey(sql string, args interface{}) string {
+ return fmt.Sprintf("%v-%v", sql, args)
+}
+
+func genId(prefix string, id string) string {
+ return fmt.Sprintf("%v-%v", prefix, id)
+}
+
+func newIdNode(tbName string, id string) *idNode {
+ return &idNode{tbName, id, time.Now()}
+}
+
+func newSqlNode(tbName, sql string) *sqlNode {
+ return &sqlNode{tbName, sql, time.Now()}
+}
diff --git a/vendor/github.com/go-xorm/xorm/memory_store.go b/vendor/github.com/go-xorm/xorm/memory_store.go
new file mode 100644
index 0000000000..6033099570
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/memory_store.go
@@ -0,0 +1,47 @@
+// Copyright 2015 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 (
+ "sync"
+
+ "github.com/go-xorm/core"
+)
+
+var _ core.CacheStore = NewMemoryStore()
+
+// memory store
+type MemoryStore struct {
+ store map[interface{}]interface{}
+ mutex sync.RWMutex
+}
+
+func NewMemoryStore() *MemoryStore {
+ return &MemoryStore{store: make(map[interface{}]interface{})}
+}
+
+func (s *MemoryStore) Put(key string, value interface{}) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.store[key] = value
+ return nil
+}
+
+func (s *MemoryStore) Get(key string) (interface{}, error) {
+ s.mutex.RLock()
+ defer s.mutex.RUnlock()
+ if v, ok := s.store[key]; ok {
+ return v, nil
+ }
+
+ return nil, ErrNotExist
+}
+
+func (s *MemoryStore) Del(key string) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ delete(s.store, key)
+ return nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/mssql_dialect.go b/vendor/github.com/go-xorm/xorm/mssql_dialect.go
new file mode 100644
index 0000000000..fdbf199c09
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/mssql_dialect.go
@@ -0,0 +1,529 @@
+// Copyright 2015 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 (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+var (
+ mssqlReservedWords = map[string]bool{
+ "ADD": true,
+ "EXTERNAL": true,
+ "PROCEDURE": true,
+ "ALL": true,
+ "FETCH": true,
+ "PUBLIC": true,
+ "ALTER": true,
+ "FILE": true,
+ "RAISERROR": true,
+ "AND": true,
+ "FILLFACTOR": true,
+ "READ": true,
+ "ANY": true,
+ "FOR": true,
+ "READTEXT": true,
+ "AS": true,
+ "FOREIGN": true,
+ "RECONFIGURE": true,
+ "ASC": true,
+ "FREETEXT": true,
+ "REFERENCES": true,
+ "AUTHORIZATION": true,
+ "FREETEXTTABLE": true,
+ "REPLICATION": true,
+ "BACKUP": true,
+ "FROM": true,
+ "RESTORE": true,
+ "BEGIN": true,
+ "FULL": true,
+ "RESTRICT": true,
+ "BETWEEN": true,
+ "FUNCTION": true,
+ "RETURN": true,
+ "BREAK": true,
+ "GOTO": true,
+ "REVERT": true,
+ "BROWSE": true,
+ "GRANT": true,
+ "REVOKE": true,
+ "BULK": true,
+ "GROUP": true,
+ "RIGHT": true,
+ "BY": true,
+ "HAVING": true,
+ "ROLLBACK": true,
+ "CASCADE": true,
+ "HOLDLOCK": true,
+ "ROWCOUNT": true,
+ "CASE": true,
+ "IDENTITY": true,
+ "ROWGUIDCOL": true,
+ "CHECK": true,
+ "IDENTITY_INSERT": true,
+ "RULE": true,
+ "CHECKPOINT": true,
+ "IDENTITYCOL": true,
+ "SAVE": true,
+ "CLOSE": true,
+ "IF": true,
+ "SCHEMA": true,
+ "CLUSTERED": true,
+ "IN": true,
+ "SECURITYAUDIT": true,
+ "COALESCE": true,
+ "INDEX": true,
+ "SELECT": true,
+ "COLLATE": true,
+ "INNER": true,
+ "SEMANTICKEYPHRASETABLE": true,
+ "COLUMN": true,
+ "INSERT": true,
+ "SEMANTICSIMILARITYDETAILSTABLE": true,
+ "COMMIT": true,
+ "INTERSECT": true,
+ "SEMANTICSIMILARITYTABLE": true,
+ "COMPUTE": true,
+ "INTO": true,
+ "SESSION_USER": true,
+ "CONSTRAINT": true,
+ "IS": true,
+ "SET": true,
+ "CONTAINS": true,
+ "JOIN": true,
+ "SETUSER": true,
+ "CONTAINSTABLE": true,
+ "KEY": true,
+ "SHUTDOWN": true,
+ "CONTINUE": true,
+ "KILL": true,
+ "SOME": true,
+ "CONVERT": true,
+ "LEFT": true,
+ "STATISTICS": true,
+ "CREATE": true,
+ "LIKE": true,
+ "SYSTEM_USER": true,
+ "CROSS": true,
+ "LINENO": true,
+ "TABLE": true,
+ "CURRENT": true,
+ "LOAD": true,
+ "TABLESAMPLE": true,
+ "CURRENT_DATE": true,
+ "MERGE": true,
+ "TEXTSIZE": true,
+ "CURRENT_TIME": true,
+ "NATIONAL": true,
+ "THEN": true,
+ "CURRENT_TIMESTAMP": true,
+ "NOCHECK": true,
+ "TO": true,
+ "CURRENT_USER": true,
+ "NONCLUSTERED": true,
+ "TOP": true,
+ "CURSOR": true,
+ "NOT": true,
+ "TRAN": true,
+ "DATABASE": true,
+ "NULL": true,
+ "TRANSACTION": true,
+ "DBCC": true,
+ "NULLIF": true,
+ "TRIGGER": true,
+ "DEALLOCATE": true,
+ "OF": true,
+ "TRUNCATE": true,
+ "DECLARE": true,
+ "OFF": true,
+ "TRY_CONVERT": true,
+ "DEFAULT": true,
+ "OFFSETS": true,
+ "TSEQUAL": true,
+ "DELETE": true,
+ "ON": true,
+ "UNION": true,
+ "DENY": true,
+ "OPEN": true,
+ "UNIQUE": true,
+ "DESC": true,
+ "OPENDATASOURCE": true,
+ "UNPIVOT": true,
+ "DISK": true,
+ "OPENQUERY": true,
+ "UPDATE": true,
+ "DISTINCT": true,
+ "OPENROWSET": true,
+ "UPDATETEXT": true,
+ "DISTRIBUTED": true,
+ "OPENXML": true,
+ "USE": true,
+ "DOUBLE": true,
+ "OPTION": true,
+ "USER": true,
+ "DROP": true,
+ "OR": true,
+ "VALUES": true,
+ "DUMP": true,
+ "ORDER": true,
+ "VARYING": true,
+ "ELSE": true,
+ "OUTER": true,
+ "VIEW": true,
+ "END": true,
+ "OVER": true,
+ "WAITFOR": true,
+ "ERRLVL": true,
+ "PERCENT": true,
+ "WHEN": true,
+ "ESCAPE": true,
+ "PIVOT": true,
+ "WHERE": true,
+ "EXCEPT": true,
+ "PLAN": true,
+ "WHILE": true,
+ "EXEC": true,
+ "PRECISION": true,
+ "WITH": true,
+ "EXECUTE": true,
+ "PRIMARY": true,
+ "WITHIN": true,
+ "EXISTS": true,
+ "PRINT": true,
+ "WRITETEXT": true,
+ "EXIT": true,
+ "PROC": true,
+ }
+)
+
+type mssql struct {
+ core.Base
+}
+
+func (db *mssql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *mssql) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ res = core.TinyInt
+ if c.Default == "true" {
+ c.Default = "1"
+ } else if c.Default == "false" {
+ c.Default = "0"
+ }
+ case core.Serial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.Int
+ case core.BigSerial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.BigInt
+ case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob:
+ res = core.VarBinary
+ if c.Length == 0 {
+ c.Length = 50
+ }
+ case core.TimeStamp:
+ res = core.DateTime
+ case core.TimeStampz:
+ res = "DATETIMEOFFSET"
+ c.Length = 7
+ case core.MediumInt:
+ res = core.Int
+ case core.MediumText, core.TinyText, core.LongText, core.Json:
+ res = core.Text
+ case core.Double:
+ res = core.Real
+ case core.Uuid:
+ res = core.Varchar
+ c.Length = 40
+ default:
+ res = t
+ }
+
+ if res == core.Int {
+ return core.Int
+ }
+
+ var hasLen1 bool = (c.Length > 0)
+ var hasLen2 bool = (c.Length2 > 0)
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *mssql) SupportInsertMany() bool {
+ return true
+}
+
+func (db *mssql) IsReserved(name string) bool {
+ _, ok := mssqlReservedWords[name]
+ return ok
+}
+
+func (db *mssql) Quote(name string) string {
+ return "\"" + name + "\""
+}
+
+func (db *mssql) QuoteStr() string {
+ return "\""
+}
+
+func (db *mssql) SupportEngine() bool {
+ return false
+}
+
+func (db *mssql) AutoIncrStr() string {
+ return "IDENTITY"
+}
+
+func (db *mssql) DropTableSql(tableName string) string {
+ return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+
+ "object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+
+ "DROP TABLE \"%s\"", tableName, tableName)
+}
+
+func (db *mssql) SupportCharset() bool {
+ return false
+}
+
+func (db *mssql) IndexOnTable() bool {
+ return true
+}
+
+func (db *mssql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{idxName}
+ sql := "select name from sysindexes where id=object_id('" + tableName + "') and name=?"
+ return sql, args
+}
+
+/*func (db *mssql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{tableName, colName}
+ sql := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
+ return sql, args
+}*/
+
+func (db *mssql) IsColumnExist(tableName, colName string) (bool, error) {
+ query := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
+
+ return db.HasRecords(query, tableName, colName)
+}
+
+func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{}
+ sql := "select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1"
+ return sql, args
+}
+
+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,
+ replace(replace(isnull(c.text,''),'(',''),')','') as vdefault
+ 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
+ where a.object_id=object_id('` + tableName + `')`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ var name, ctype, vdefault string
+ var maxLen, precision, scale int
+ var nullable bool
+ err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+ col.Name = strings.Trim(name, "` ")
+ col.Nullable = nullable
+ col.Default = vdefault
+ ct := strings.ToUpper(ctype)
+ if ct == "DECIMAL" {
+ col.Length = precision
+ col.Length2 = scale
+ } else {
+ col.Length = maxLen
+ }
+ switch ct {
+ case "DATETIMEOFFSET":
+ col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
+ case "NVARCHAR":
+ col.SQLType = core.SQLType{core.NVarchar, 0, 0}
+ case "IMAGE":
+ col.SQLType = core.SQLType{core.VarBinary, 0, 0}
+ default:
+ if _, ok := core.SqlTypes[ct]; ok {
+ col.SQLType = core.SQLType{ct, 0, 0}
+ } else {
+ return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v",
+ ct, tableName, col.Name))
+ }
+ }
+
+ 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)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *mssql) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := `select name from sysobjects where xtype ='U'`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name string
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, err
+ }
+ table.Name = strings.Trim(name, "` ")
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *mssql) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := `SELECT
+IXS.NAME AS [INDEX_NAME],
+C.NAME AS [COLUMN_NAME],
+IXS.is_unique AS [IS_UNIQUE]
+FROM SYS.INDEXES IXS
+INNER JOIN SYS.INDEX_COLUMNS IXCS
+ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
+INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
+AND IXCS.COLUMN_ID=C.COLUMN_ID
+WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
+`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, isUnique string
+
+ err = rows.Scan(&indexName, &colName, &isUnique)
+ if err != nil {
+ return nil, err
+ }
+
+ i, err := strconv.ParseBool(isUnique)
+ if err != nil {
+ return nil, err
+ }
+
+ if i {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+
+ colName = strings.Trim(colName, "` ")
+
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ indexName = indexName[5+len(tableName):]
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.Type = indexType
+ index.Name = indexName
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
+ var sql string
+ if tableName == "" {
+ tableName = table.Name
+ }
+
+ sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE "
+
+ sql += db.QuoteStr() + tableName + db.QuoteStr() + " ("
+
+ pkList := table.PrimaryKeys
+
+ for _, colName := range table.ColumnsSeq() {
+ col := table.GetColumn(colName)
+ if col.IsPrimaryKey && len(pkList) == 1 {
+ sql += col.String(db)
+ } else {
+ sql += col.StringNoPk(db)
+ }
+ sql = strings.TrimSpace(sql)
+ sql += ", "
+ }
+
+ if len(pkList) > 1 {
+ sql += "PRIMARY KEY ( "
+ sql += strings.Join(pkList, ",")
+ sql += " ), "
+ }
+
+ sql = sql[:len(sql)-2] + ")"
+ sql += ";"
+ return sql
+}
+
+func (db *mssql) ForUpdateSql(query string) string {
+ return query
+}
+
+func (db *mssql) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
+}
diff --git a/vendor/github.com/go-xorm/xorm/mymysql_driver.go b/vendor/github.com/go-xorm/xorm/mymysql_driver.go
new file mode 100644
index 0000000000..ef3086a422
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/mymysql_driver.go
@@ -0,0 +1,65 @@
+// Copyright 2015 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 (
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+type mymysqlDriver struct {
+}
+
+func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.MYSQL}
+
+ pd := strings.SplitN(dataSourceName, "*", 2)
+ if len(pd) == 2 {
+ // Parse protocol part of URI
+ p := strings.SplitN(pd[0], ":", 2)
+ if len(p) != 2 {
+ return nil, errors.New("Wrong protocol part of URI")
+ }
+ db.Proto = p[0]
+ options := strings.Split(p[1], ",")
+ db.Raddr = options[0]
+ for _, o := range options[1:] {
+ kv := strings.SplitN(o, "=", 2)
+ var k, v string
+ if len(kv) == 2 {
+ k, v = kv[0], kv[1]
+ } else {
+ k, v = o, "true"
+ }
+ switch k {
+ case "laddr":
+ db.Laddr = v
+ case "timeout":
+ to, err := time.ParseDuration(v)
+ if err != nil {
+ return nil, err
+ }
+ db.Timeout = to
+ default:
+ return nil, errors.New("Unknown option: " + k)
+ }
+ }
+ // Remove protocol part
+ pd = pd[1:]
+ }
+ // Parse database part of URI
+ dup := strings.SplitN(pd[0], "/", 3)
+ if len(dup) != 3 {
+ return nil, errors.New("Wrong database part of URI")
+ }
+ db.DbName = dup[0]
+ db.User = dup[1]
+ db.Passwd = dup[2]
+
+ return db, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/mysql_dialect.go b/vendor/github.com/go-xorm/xorm/mysql_dialect.go
new file mode 100644
index 0000000000..5c789a145a
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/mysql_dialect.go
@@ -0,0 +1,489 @@
+// Copyright 2015 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 (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+var (
+ mysqlReservedWords = map[string]bool{
+ "ADD": true,
+ "ALL": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "AS": true,
+ "ASC": true,
+ "ASENSITIVE": true,
+ "BEFORE": true,
+ "BETWEEN": true,
+ "BIGINT": true,
+ "BINARY": true,
+ "BLOB": true,
+ "BOTH": true,
+ "BY": true,
+ "CALL": true,
+ "CASCADE": true,
+ "CASE": true,
+ "CHANGE": true,
+ "CHAR": true,
+ "CHARACTER": true,
+ "CHECK": true,
+ "COLLATE": true,
+ "COLUMN": true,
+ "CONDITION": true,
+ "CONNECTION": true,
+ "CONSTRAINT": true,
+ "CONTINUE": true,
+ "CONVERT": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CURRENT_DATE": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "CURRENT_USER": true,
+ "CURSOR": true,
+ "DATABASE": true,
+ "DATABASES": true,
+ "DAY_HOUR": true,
+ "DAY_MICROSECOND": true,
+ "DAY_MINUTE": true,
+ "DAY_SECOND": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DELAYED": true,
+ "DELETE": true,
+ "DESC": true,
+ "DESCRIBE": true,
+ "DETERMINISTIC": true,
+ "DISTINCT": true,
+ "DISTINCTROW": true,
+ "DIV": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DUAL": true,
+ "EACH": true,
+ "ELSE": true,
+ "ELSEIF": true,
+ "ENCLOSED": true,
+ "ESCAPED": true,
+ "EXISTS": true,
+ "EXIT": true,
+ "EXPLAIN": true,
+ "FALSE": true,
+ "FETCH": true,
+ "FLOAT": true,
+ "FLOAT4": true,
+ "FLOAT8": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FROM": true,
+ "FULLTEXT": true,
+ "GOTO": true,
+ "GRANT": true,
+ "GROUP": true,
+ "HAVING": true,
+ "HIGH_PRIORITY": true,
+ "HOUR_MICROSECOND": true,
+ "HOUR_MINUTE": true,
+ "HOUR_SECOND": true,
+ "IF": true,
+ "IGNORE": true,
+ "IN": true, "INDEX": true,
+ "INFILE": true, "INNER": true, "INOUT": true,
+ "INSENSITIVE": true, "INSERT": true, "INT": true,
+ "INT1": true, "INT2": true, "INT3": true,
+ "INT4": true, "INT8": true, "INTEGER": true,
+ "INTERVAL": true, "INTO": true, "IS": true,
+ "ITERATE": true, "JOIN": true, "KEY": true,
+ "KEYS": true, "KILL": true, "LABEL": true,
+ "LEADING": true, "LEAVE": true, "LEFT": true,
+ "LIKE": true, "LIMIT": true, "LINEAR": true,
+ "LINES": true, "LOAD": true, "LOCALTIME": true,
+ "LOCALTIMESTAMP": true, "LOCK": true, "LONG": true,
+ "LONGBLOB": true, "LONGTEXT": true, "LOOP": true,
+ "LOW_PRIORITY": true, "MATCH": true, "MEDIUMBLOB": true,
+ "MEDIUMINT": true, "MEDIUMTEXT": true, "MIDDLEINT": true,
+ "MINUTE_MICROSECOND": true, "MINUTE_SECOND": true, "MOD": true,
+ "MODIFIES": true, "NATURAL": true, "NOT": true,
+ "NO_WRITE_TO_BINLOG": true, "NULL": true, "NUMERIC": true,
+ "ON OPTIMIZE": true, "OPTION": true,
+ "OPTIONALLY": true, "OR": true, "ORDER": true,
+ "OUT": true, "OUTER": true, "OUTFILE": true,
+ "PRECISION": true, "PRIMARY": true, "PROCEDURE": true,
+ "PURGE": true, "RAID0": true, "RANGE": true,
+ "READ": true, "READS": true, "REAL": true,
+ "REFERENCES": true, "REGEXP": true, "RELEASE": true,
+ "RENAME": true, "REPEAT": true, "REPLACE": true,
+ "REQUIRE": true, "RESTRICT": true, "RETURN": true,
+ "REVOKE": true, "RIGHT": true, "RLIKE": true,
+ "SCHEMA": true, "SCHEMAS": true, "SECOND_MICROSECOND": true,
+ "SELECT": true, "SENSITIVE": true, "SEPARATOR": true,
+ "SET": true, "SHOW": true, "SMALLINT": true,
+ "SPATIAL": true, "SPECIFIC": true, "SQL": true,
+ "SQLEXCEPTION": true, "SQLSTATE": true, "SQLWARNING": true,
+ "SQL_BIG_RESULT": true, "SQL_CALC_FOUND_ROWS": true, "SQL_SMALL_RESULT": true,
+ "SSL": true, "STARTING": true, "STRAIGHT_JOIN": true,
+ "TABLE": true, "TERMINATED": true, "THEN": true,
+ "TINYBLOB": true, "TINYINT": true, "TINYTEXT": true,
+ "TO": true, "TRAILING": true, "TRIGGER": true,
+ "TRUE": true, "UNDO": true, "UNION": true,
+ "UNIQUE": true, "UNLOCK": true, "UNSIGNED": true,
+ "UPDATE": true, "USAGE": true, "USE": true,
+ "USING": true, "UTC_DATE": true, "UTC_TIME": true,
+ "UTC_TIMESTAMP": true, "VALUES": true, "VARBINARY": true,
+ "VARCHAR": true,
+ "VARCHARACTER": true,
+ "VARYING": true,
+ "WHEN": true,
+ "WHERE": true,
+ "WHILE": true,
+ "WITH": true,
+ "WRITE": true,
+ "X509": true,
+ "XOR": true,
+ "YEAR_MONTH": true,
+ "ZEROFILL": true,
+ }
+)
+
+type mysql struct {
+ core.Base
+ net string
+ addr string
+ params map[string]string
+ loc *time.Location
+ timeout time.Duration
+ tls *tls.Config
+ allowAllFiles bool
+ allowOldPasswords bool
+ clientFoundRows bool
+}
+
+func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *mysql) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ res = core.TinyInt
+ c.Length = 1
+ case core.Serial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.Int
+ case core.BigSerial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.BigInt
+ case core.Bytea:
+ res = core.Blob
+ case core.TimeStampz:
+ res = core.Char
+ c.Length = 64
+ case core.Enum: //mysql enum
+ res = core.Enum
+ res += "("
+ opts := ""
+ for v, _ := range c.EnumOptions {
+ opts += fmt.Sprintf(",'%v'", v)
+ }
+ res += strings.TrimLeft(opts, ",")
+ res += ")"
+ case core.Set: //mysql set
+ res = core.Set
+ res += "("
+ opts := ""
+ for v, _ := range c.SetOptions {
+ opts += fmt.Sprintf(",'%v'", v)
+ }
+ res += strings.TrimLeft(opts, ",")
+ res += ")"
+ case core.NVarchar:
+ res = core.Varchar
+ case core.Uuid:
+ res = core.Varchar
+ c.Length = 40
+ case core.Json:
+ res = core.Text
+ default:
+ res = t
+ }
+
+ var hasLen1 bool = (c.Length > 0)
+ var hasLen2 bool = (c.Length2 > 0)
+
+ if res == core.BigInt && !hasLen1 && !hasLen2 {
+ c.Length = 20
+ hasLen1 = true
+ }
+
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *mysql) SupportInsertMany() bool {
+ return true
+}
+
+func (db *mysql) IsReserved(name string) bool {
+ _, ok := mysqlReservedWords[name]
+ return ok
+}
+
+func (db *mysql) Quote(name string) string {
+ return "`" + name + "`"
+}
+
+func (db *mysql) QuoteStr() string {
+ return "`"
+}
+
+func (db *mysql) SupportEngine() bool {
+ return true
+}
+
+func (db *mysql) AutoIncrStr() string {
+ return "AUTO_INCREMENT"
+}
+
+func (db *mysql) SupportCharset() bool {
+ return true
+}
+
+func (db *mysql) IndexOnTable() bool {
+ return true
+}
+
+func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName, idxName}
+ sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
+ sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
+ return sql, args
+}
+
+/*func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName, colName}
+ sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
+ return sql, args
+}*/
+
+func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName}
+ sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
+ return sql, args
+}
+
+func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{db.DbName, tableName}
+ s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
+ " `COLUMN_KEY`, `EXTRA` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var columnName, isNullable, colType, colKey, extra string
+ var colDefault *string
+ err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra)
+ if err != nil {
+ return nil, nil, err
+ }
+ col.Name = strings.Trim(columnName, "` ")
+ if "YES" == isNullable {
+ col.Nullable = true
+ }
+
+ if colDefault != nil {
+ col.Default = *colDefault
+ if col.Default == "" {
+ col.DefaultIsEmpty = true
+ }
+ }
+
+ cts := strings.Split(colType, "(")
+ colName := cts[0]
+ colType = strings.ToUpper(colName)
+ var len1, len2 int
+ if len(cts) == 2 {
+ idx := strings.Index(cts[1], ")")
+ if colType == core.Enum && cts[1][0] == '\'' { //enum
+ options := strings.Split(cts[1][0:idx], ",")
+ col.EnumOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.EnumOptions[v] = k
+ }
+ } else if colType == core.Set && cts[1][0] == '\'' {
+ options := strings.Split(cts[1][0:idx], ",")
+ col.SetOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.SetOptions[v] = k
+ }
+ } else {
+ lens := strings.Split(cts[1][0:idx], ",")
+ len1, err = strconv.Atoi(strings.TrimSpace(lens[0]))
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(lens) == 2 {
+ len2, err = strconv.Atoi(lens[1])
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+ }
+ if colType == "FLOAT UNSIGNED" {
+ colType = "FLOAT"
+ }
+ col.Length = len1
+ col.Length2 = len2
+ if _, ok := core.SqlTypes[colType]; ok {
+ col.SQLType = core.SQLType{colType, len1, len2}
+ } else {
+ return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType))
+ }
+
+ if colKey == "PRI" {
+ col.IsPrimaryKey = true
+ }
+ if colKey == "UNI" {
+ //col.is
+ }
+
+ if extra == "auto_increment" {
+ col.IsAutoIncrement = true
+ }
+
+ 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)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *mysql) GetTables() ([]*core.Table, error) {
+ args := []interface{}{db.DbName}
+ s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from " +
+ "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name, engine, tableRows string
+ var autoIncr *string
+ err = rows.Scan(&name, &engine, &tableRows, &autoIncr)
+ if err != nil {
+ return nil, err
+ }
+
+ table.Name = name
+ table.StoreEngine = engine
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{db.DbName, tableName}
+ s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, nonUnique string
+ err = rows.Scan(&indexName, &nonUnique, &colName)
+ if err != nil {
+ return nil, err
+ }
+
+ if indexName == "PRIMARY" {
+ continue
+ }
+
+ if "YES" == nonUnique || nonUnique == "1" {
+ indexType = core.IndexType
+ } else {
+ indexType = core.UniqueType
+ }
+
+ colName = strings.Trim(colName, "` ")
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ indexName = indexName[5+len(tableName) : len(indexName)]
+ isRegular = true
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.IsRegular = isRegular
+ index.Type = indexType
+ index.Name = indexName
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *mysql) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}}
+}
diff --git a/vendor/github.com/go-xorm/xorm/mysql_driver.go b/vendor/github.com/go-xorm/xorm/mysql_driver.go
new file mode 100644
index 0000000000..6ceeed58f0
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/mysql_driver.go
@@ -0,0 +1,50 @@
+// Copyright 2015 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 (
+ "regexp"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+type mysqlDriver struct {
+}
+
+func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ dsnPattern := regexp.MustCompile(
+ `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+ `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+ `\/(?P<dbname>.*?)` + // /dbname
+ `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ //tlsConfigRegister := make(map[string]*tls.Config)
+ names := dsnPattern.SubexpNames()
+
+ uri := &core.Uri{DbType: core.MYSQL}
+
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ uri.DbName = match
+ case "params":
+ if len(match) > 0 {
+ kvs := strings.Split(match, "&")
+ for _, kv := range kvs {
+ splits := strings.Split(kv, "=")
+ if len(splits) == 2 {
+ switch splits[0] {
+ case "charset":
+ uri.Charset = splits[1]
+ }
+ }
+ }
+ }
+
+ }
+ }
+ return uri, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/oci8_driver.go b/vendor/github.com/go-xorm/xorm/oci8_driver.go
new file mode 100644
index 0000000000..ec5f20228d
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/oci8_driver.go
@@ -0,0 +1,37 @@
+// Copyright 2015 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 (
+ "errors"
+ "regexp"
+
+ "github.com/go-xorm/core"
+)
+
+type oci8Driver struct {
+}
+
+//dataSourceName=user/password@ipv4:port/dbname
+//dataSourceName=user/password@[ipv6]:port/dbname
+func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.ORACLE}
+ dsnPattern := regexp.MustCompile(
+ `^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
+ `(?P<net>.*)` + // ip:port
+ `\/(?P<dbname>.*)`) // dbname
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ names := dsnPattern.SubexpNames()
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ db.DbName = match
+ }
+ }
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+ return db, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/odbc_driver.go b/vendor/github.com/go-xorm/xorm/odbc_driver.go
new file mode 100644
index 0000000000..6770de6076
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/odbc_driver.go
@@ -0,0 +1,34 @@
+// Copyright 2015 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 (
+ "errors"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+type odbcDriver struct {
+}
+
+func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ kv := strings.Split(dataSourceName, ";")
+ var dbName string
+
+ for _, c := range kv {
+ vv := strings.Split(strings.TrimSpace(c), "=")
+ if len(vv) == 2 {
+ switch strings.ToLower(vv[0]) {
+ case "database":
+ dbName = vv[1]
+ }
+ }
+ }
+ if dbName == "" {
+ return nil, errors.New("no db name provided")
+ }
+ return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/oracle_dialect.go b/vendor/github.com/go-xorm/xorm/oracle_dialect.go
new file mode 100644
index 0000000000..f074a4ec5d
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/oracle_dialect.go
@@ -0,0 +1,846 @@
+// Copyright 2015 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 (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+var (
+ oracleReservedWords = map[string]bool{
+ "ACCESS": true,
+ "ACCOUNT": true,
+ "ACTIVATE": true,
+ "ADD": true,
+ "ADMIN": true,
+ "ADVISE": true,
+ "AFTER": true,
+ "ALL": true,
+ "ALL_ROWS": true,
+ "ALLOCATE": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "ANY": true,
+ "ARCHIVE": true,
+ "ARCHIVELOG": true,
+ "ARRAY": true,
+ "AS": true,
+ "ASC": true,
+ "AT": true,
+ "AUDIT": true,
+ "AUTHENTICATED": true,
+ "AUTHORIZATION": true,
+ "AUTOEXTEND": true,
+ "AUTOMATIC": true,
+ "BACKUP": true,
+ "BECOME": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BETWEEN": true,
+ "BFILE": true,
+ "BITMAP": true,
+ "BLOB": true,
+ "BLOCK": true,
+ "BODY": true,
+ "BY": true,
+ "CACHE": true,
+ "CACHE_INSTANCES": true,
+ "CANCEL": true,
+ "CASCADE": true,
+ "CAST": true,
+ "CFILE": true,
+ "CHAINED": true,
+ "CHANGE": true,
+ "CHAR": true,
+ "CHAR_CS": true,
+ "CHARACTER": true,
+ "CHECK": true,
+ "CHECKPOINT": true,
+ "CHOOSE": true,
+ "CHUNK": true,
+ "CLEAR": true,
+ "CLOB": true,
+ "CLONE": true,
+ "CLOSE": true,
+ "CLOSE_CACHED_OPEN_CURSORS": true,
+ "CLUSTER": true,
+ "COALESCE": true,
+ "COLUMN": true,
+ "COLUMNS": true,
+ "COMMENT": true,
+ "COMMIT": true,
+ "COMMITTED": true,
+ "COMPATIBILITY": true,
+ "COMPILE": true,
+ "COMPLETE": true,
+ "COMPOSITE_LIMIT": true,
+ "COMPRESS": true,
+ "COMPUTE": true,
+ "CONNECT": true,
+ "CONNECT_TIME": true,
+ "CONSTRAINT": true,
+ "CONSTRAINTS": true,
+ "CONTENTS": true,
+ "CONTINUE": true,
+ "CONTROLFILE": true,
+ "CONVERT": true,
+ "COST": true,
+ "CPU_PER_CALL": true,
+ "CPU_PER_SESSION": true,
+ "CREATE": true,
+ "CURRENT": true,
+ "CURRENT_SCHEMA": true,
+ "CURREN_USER": true,
+ "CURSOR": true,
+ "CYCLE": true,
+ "DANGLING": true,
+ "DATABASE": true,
+ "DATAFILE": true,
+ "DATAFILES": true,
+ "DATAOBJNO": true,
+ "DATE": true,
+ "DBA": true,
+ "DBHIGH": true,
+ "DBLOW": true,
+ "DBMAC": true,
+ "DEALLOCATE": true,
+ "DEBUG": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DEGREE": true,
+ "DELETE": true,
+ "DEREF": true,
+ "DESC": true,
+ "DIRECTORY": true,
+ "DISABLE": true,
+ "DISCONNECT": true,
+ "DISMOUNT": true,
+ "DISTINCT": true,
+ "DISTRIBUTED": true,
+ "DML": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DUMP": true,
+ "EACH": true,
+ "ELSE": true,
+ "ENABLE": true,
+ "END": true,
+ "ENFORCE": true,
+ "ENTRY": true,
+ "ESCAPE": true,
+ "EXCEPT": true,
+ "EXCEPTIONS": true,
+ "EXCHANGE": true,
+ "EXCLUDING": true,
+ "EXCLUSIVE": true,
+ "EXECUTE": true,
+ "EXISTS": true,
+ "EXPIRE": true,
+ "EXPLAIN": true,
+ "EXTENT": true,
+ "EXTENTS": true,
+ "EXTERNALLY": true,
+ "FAILED_LOGIN_ATTEMPTS": true,
+ "FALSE": true,
+ "FAST": true,
+ "FILE": true,
+ "FIRST_ROWS": true,
+ "FLAGGER": true,
+ "FLOAT": true,
+ "FLOB": true,
+ "FLUSH": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FREELIST": true,
+ "FREELISTS": true,
+ "FROM": true,
+ "FULL": true,
+ "FUNCTION": true,
+ "GLOBAL": true,
+ "GLOBALLY": true,
+ "GLOBAL_NAME": true,
+ "GRANT": true,
+ "GROUP": true,
+ "GROUPS": true,
+ "HASH": true,
+ "HASHKEYS": true,
+ "HAVING": true,
+ "HEADER": true,
+ "HEAP": true,
+ "IDENTIFIED": true,
+ "IDGENERATORS": true,
+ "IDLE_TIME": true,
+ "IF": true,
+ "IMMEDIATE": true,
+ "IN": true,
+ "INCLUDING": true,
+ "INCREMENT": true,
+ "INDEX": true,
+ "INDEXED": true,
+ "INDEXES": true,
+ "INDICATOR": true,
+ "IND_PARTITION": true,
+ "INITIAL": true,
+ "INITIALLY": true,
+ "INITRANS": true,
+ "INSERT": true,
+ "INSTANCE": true,
+ "INSTANCES": true,
+ "INSTEAD": true,
+ "INT": true,
+ "INTEGER": true,
+ "INTERMEDIATE": true,
+ "INTERSECT": true,
+ "INTO": true,
+ "IS": true,
+ "ISOLATION": true,
+ "ISOLATION_LEVEL": true,
+ "KEEP": true,
+ "KEY": true,
+ "KILL": true,
+ "LABEL": true,
+ "LAYER": true,
+ "LESS": true,
+ "LEVEL": true,
+ "LIBRARY": true,
+ "LIKE": true,
+ "LIMIT": true,
+ "LINK": true,
+ "LIST": true,
+ "LOB": true,
+ "LOCAL": true,
+ "LOCK": true,
+ "LOCKED": true,
+ "LOG": true,
+ "LOGFILE": true,
+ "LOGGING": true,
+ "LOGICAL_READS_PER_CALL": true,
+ "LOGICAL_READS_PER_SESSION": true,
+ "LONG": true,
+ "MANAGE": true,
+ "MASTER": true,
+ "MAX": true,
+ "MAXARCHLOGS": true,
+ "MAXDATAFILES": true,
+ "MAXEXTENTS": true,
+ "MAXINSTANCES": true,
+ "MAXLOGFILES": true,
+ "MAXLOGHISTORY": true,
+ "MAXLOGMEMBERS": true,
+ "MAXSIZE": true,
+ "MAXTRANS": true,
+ "MAXVALUE": true,
+ "MIN": true,
+ "MEMBER": true,
+ "MINIMUM": true,
+ "MINEXTENTS": true,
+ "MINUS": true,
+ "MINVALUE": true,
+ "MLSLABEL": true,
+ "MLS_LABEL_FORMAT": true,
+ "MODE": true,
+ "MODIFY": true,
+ "MOUNT": true,
+ "MOVE": true,
+ "MTS_DISPATCHERS": true,
+ "MULTISET": true,
+ "NATIONAL": true,
+ "NCHAR": true,
+ "NCHAR_CS": true,
+ "NCLOB": true,
+ "NEEDED": true,
+ "NESTED": true,
+ "NETWORK": true,
+ "NEW": true,
+ "NEXT": true,
+ "NOARCHIVELOG": true,
+ "NOAUDIT": true,
+ "NOCACHE": true,
+ "NOCOMPRESS": true,
+ "NOCYCLE": true,
+ "NOFORCE": true,
+ "NOLOGGING": true,
+ "NOMAXVALUE": true,
+ "NOMINVALUE": true,
+ "NONE": true,
+ "NOORDER": true,
+ "NOOVERRIDE": true,
+ "NOPARALLEL": true,
+ "NOREVERSE": true,
+ "NORMAL": true,
+ "NOSORT": true,
+ "NOT": true,
+ "NOTHING": true,
+ "NOWAIT": true,
+ "NULL": true,
+ "NUMBER": true,
+ "NUMERIC": true,
+ "NVARCHAR2": true,
+ "OBJECT": true,
+ "OBJNO": true,
+ "OBJNO_REUSE": true,
+ "OF": true,
+ "OFF": true,
+ "OFFLINE": true,
+ "OID": true,
+ "OIDINDEX": true,
+ "OLD": true,
+ "ON": true,
+ "ONLINE": true,
+ "ONLY": true,
+ "OPCODE": true,
+ "OPEN": true,
+ "OPTIMAL": true,
+ "OPTIMIZER_GOAL": true,
+ "OPTION": true,
+ "OR": true,
+ "ORDER": true,
+ "ORGANIZATION": true,
+ "OSLABEL": true,
+ "OVERFLOW": true,
+ "OWN": true,
+ "PACKAGE": true,
+ "PARALLEL": true,
+ "PARTITION": true,
+ "PASSWORD": true,
+ "PASSWORD_GRACE_TIME": true,
+ "PASSWORD_LIFE_TIME": true,
+ "PASSWORD_LOCK_TIME": true,
+ "PASSWORD_REUSE_MAX": true,
+ "PASSWORD_REUSE_TIME": true,
+ "PASSWORD_VERIFY_FUNCTION": true,
+ "PCTFREE": true,
+ "PCTINCREASE": true,
+ "PCTTHRESHOLD": true,
+ "PCTUSED": true,
+ "PCTVERSION": true,
+ "PERCENT": true,
+ "PERMANENT": true,
+ "PLAN": true,
+ "PLSQL_DEBUG": true,
+ "POST_TRANSACTION": true,
+ "PRECISION": true,
+ "PRESERVE": true,
+ "PRIMARY": true,
+ "PRIOR": true,
+ "PRIVATE": true,
+ "PRIVATE_SGA": true,
+ "PRIVILEGE": true,
+ "PRIVILEGES": true,
+ "PROCEDURE": true,
+ "PROFILE": true,
+ "PUBLIC": true,
+ "PURGE": true,
+ "QUEUE": true,
+ "QUOTA": true,
+ "RANGE": true,
+ "RAW": true,
+ "RBA": true,
+ "READ": true,
+ "READUP": true,
+ "REAL": true,
+ "REBUILD": true,
+ "RECOVER": true,
+ "RECOVERABLE": true,
+ "RECOVERY": true,
+ "REF": true,
+ "REFERENCES": true,
+ "REFERENCING": true,
+ "REFRESH": true,
+ "RENAME": true,
+ "REPLACE": true,
+ "RESET": true,
+ "RESETLOGS": true,
+ "RESIZE": true,
+ "RESOURCE": true,
+ "RESTRICTED": true,
+ "RETURN": true,
+ "RETURNING": true,
+ "REUSE": true,
+ "REVERSE": true,
+ "REVOKE": true,
+ "ROLE": true,
+ "ROLES": true,
+ "ROLLBACK": true,
+ "ROW": true,
+ "ROWID": true,
+ "ROWNUM": true,
+ "ROWS": true,
+ "RULE": true,
+ "SAMPLE": true,
+ "SAVEPOINT": true,
+ "SB4": true,
+ "SCAN_INSTANCES": true,
+ "SCHEMA": true,
+ "SCN": true,
+ "SCOPE": true,
+ "SD_ALL": true,
+ "SD_INHIBIT": true,
+ "SD_SHOW": true,
+ "SEGMENT": true,
+ "SEG_BLOCK": true,
+ "SEG_FILE": true,
+ "SELECT": true,
+ "SEQUENCE": true,
+ "SERIALIZABLE": true,
+ "SESSION": true,
+ "SESSION_CACHED_CURSORS": true,
+ "SESSIONS_PER_USER": true,
+ "SET": true,
+ "SHARE": true,
+ "SHARED": true,
+ "SHARED_POOL": true,
+ "SHRINK": true,
+ "SIZE": true,
+ "SKIP": true,
+ "SKIP_UNUSABLE_INDEXES": true,
+ "SMALLINT": true,
+ "SNAPSHOT": true,
+ "SOME": true,
+ "SORT": true,
+ "SPECIFICATION": true,
+ "SPLIT": true,
+ "SQL_TRACE": true,
+ "STANDBY": true,
+ "START": true,
+ "STATEMENT_ID": true,
+ "STATISTICS": true,
+ "STOP": true,
+ "STORAGE": true,
+ "STORE": true,
+ "STRUCTURE": true,
+ "SUCCESSFUL": true,
+ "SWITCH": true,
+ "SYS_OP_ENFORCE_NOT_NULL$": true,
+ "SYS_OP_NTCIMG$": true,
+ "SYNONYM": true,
+ "SYSDATE": true,
+ "SYSDBA": true,
+ "SYSOPER": true,
+ "SYSTEM": true,
+ "TABLE": true,
+ "TABLES": true,
+ "TABLESPACE": true,
+ "TABLESPACE_NO": true,
+ "TABNO": true,
+ "TEMPORARY": true,
+ "THAN": true,
+ "THE": true,
+ "THEN": true,
+ "THREAD": true,
+ "TIMESTAMP": true,
+ "TIME": true,
+ "TO": true,
+ "TOPLEVEL": true,
+ "TRACE": true,
+ "TRACING": true,
+ "TRANSACTION": true,
+ "TRANSITIONAL": true,
+ "TRIGGER": true,
+ "TRIGGERS": true,
+ "TRUE": true,
+ "TRUNCATE": true,
+ "TX": true,
+ "TYPE": true,
+ "UB2": true,
+ "UBA": true,
+ "UID": true,
+ "UNARCHIVED": true,
+ "UNDO": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UNLIMITED": true,
+ "UNLOCK": true,
+ "UNRECOVERABLE": true,
+ "UNTIL": true,
+ "UNUSABLE": true,
+ "UNUSED": true,
+ "UPDATABLE": true,
+ "UPDATE": true,
+ "USAGE": true,
+ "USE": true,
+ "USER": true,
+ "USING": true,
+ "VALIDATE": true,
+ "VALIDATION": true,
+ "VALUE": true,
+ "VALUES": true,
+ "VARCHAR": true,
+ "VARCHAR2": true,
+ "VARYING": true,
+ "VIEW": true,
+ "WHEN": true,
+ "WHENEVER": true,
+ "WHERE": true,
+ "WITH": true,
+ "WITHOUT": true,
+ "WORK": true,
+ "WRITE": true,
+ "WRITEDOWN": true,
+ "WRITEUP": true,
+ "XID": true,
+ "YEAR": true,
+ "ZONE": true,
+ }
+)
+
+type oracle struct {
+ core.Base
+}
+
+func (db *oracle) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *oracle) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial:
+ res = "NUMBER"
+ case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea:
+ return core.Blob
+ case core.Time, core.DateTime, core.TimeStamp:
+ res = core.TimeStamp
+ case core.TimeStampz:
+ res = "TIMESTAMP WITH TIME ZONE"
+ case core.Float, core.Double, core.Numeric, core.Decimal:
+ res = "NUMBER"
+ case core.Text, core.MediumText, core.LongText, core.Json:
+ res = "CLOB"
+ case core.Char, core.Varchar, core.TinyText:
+ res = "VARCHAR2"
+ default:
+ res = t
+ }
+
+ var hasLen1 bool = (c.Length > 0)
+ var hasLen2 bool = (c.Length2 > 0)
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *oracle) AutoIncrStr() string {
+ return "AUTO_INCREMENT"
+}
+
+func (db *oracle) SupportInsertMany() bool {
+ return true
+}
+
+func (db *oracle) IsReserved(name string) bool {
+ _, ok := oracleReservedWords[name]
+ return ok
+}
+
+func (db *oracle) Quote(name string) string {
+ return "\"" + name + "\""
+}
+
+func (db *oracle) QuoteStr() string {
+ return "\""
+}
+
+func (db *oracle) SupportEngine() bool {
+ return false
+}
+
+func (db *oracle) SupportCharset() bool {
+ return false
+}
+
+func (db *oracle) SupportDropIfExists() bool {
+ return false
+}
+
+func (db *oracle) IndexOnTable() bool {
+ return false
+}
+
+func (db *oracle) DropTableSql(tableName string) string {
+ return fmt.Sprintf("DROP TABLE `%s`", tableName)
+}
+
+func (b *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
+ var sql string
+ sql = "CREATE TABLE "
+ if tableName == "" {
+ tableName = table.Name
+ }
+
+ sql += b.Quote(tableName) + " ("
+
+ pkList := table.PrimaryKeys
+
+ for _, colName := range table.ColumnsSeq() {
+ col := table.GetColumn(colName)
+ /*if col.IsPrimaryKey && len(pkList) == 1 {
+ sql += col.String(b.dialect)
+ } else {*/
+ sql += col.StringNoPk(b)
+ //}
+ sql = strings.TrimSpace(sql)
+ sql += ", "
+ }
+
+ if len(pkList) > 0 {
+ sql += "PRIMARY KEY ( "
+ sql += b.Quote(strings.Join(pkList, b.Quote(",")))
+ sql += " ), "
+ }
+
+ sql = sql[:len(sql)-2] + ")"
+ if b.SupportEngine() && storeEngine != "" {
+ sql += " ENGINE=" + storeEngine
+ }
+ if b.SupportCharset() {
+ if len(charset) == 0 {
+ charset = b.URI().Charset
+ }
+ if len(charset) > 0 {
+ sql += " DEFAULT CHARSET " + charset
+ }
+ }
+ return sql
+}
+
+func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{tableName, idxName}
+ return `SELECT INDEX_NAME FROM USER_INDEXES ` +
+ `WHERE TABLE_NAME = :1 AND INDEX_NAME = :2`, args
+}
+
+func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ return `SELECT table_name FROM user_tables WHERE table_name = :1`, args
+}
+
+func (db *oracle) MustDropTable(tableName string) error {
+ sql, args := db.TableCheckSql(tableName)
+ db.LogSQL(sql, args)
+
+ rows, err := db.DB().Query(sql, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return nil
+ }
+
+ sql = "Drop Table \"" + tableName + "\""
+ db.LogSQL(sql, args)
+
+ _, err = db.DB().Exec(sql)
+ return err
+}
+
+/*func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)}
+ return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" +
+ " AND column_name = ?", args
+}*/
+
+func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{tableName, colName}
+ query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" +
+ " AND column_name = :2"
+ db.LogSQL(query, args)
+
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ return true, nil
+ }
+ return false, nil
+}
+
+func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{tableName}
+ s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
+ "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string
+ var dataLen int
+
+ err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision,
+ &dataScale, &nullable)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ col.Name = strings.Trim(*colName, `" `)
+ if colDefault != nil {
+ col.Default = *colDefault
+ col.DefaultIsEmpty = false
+ }
+
+ if *nullable == "Y" {
+ col.Nullable = true
+ } else {
+ col.Nullable = false
+ }
+
+ var ignore bool
+
+ var dt string
+ var len1, len2 int
+ dts := strings.Split(*dataType, "(")
+ dt = dts[0]
+ if len(dts) > 1 {
+ lens := strings.Split(dts[1][:len(dts[1])-1], ",")
+ if len(lens) > 1 {
+ len1, _ = strconv.Atoi(lens[0])
+ len2, _ = strconv.Atoi(lens[1])
+ } else {
+ len1, _ = strconv.Atoi(lens[0])
+ }
+ }
+
+ switch dt {
+ case "VARCHAR2":
+ col.SQLType = core.SQLType{core.Varchar, len1, len2}
+ case "NVARCHAR2":
+ col.SQLType = core.SQLType{core.NVarchar, len1, len2}
+ case "TIMESTAMP WITH TIME ZONE":
+ col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
+ case "NUMBER":
+ col.SQLType = core.SQLType{core.Double, len1, len2}
+ case "LONG", "LONG RAW":
+ col.SQLType = core.SQLType{core.Text, 0, 0}
+ case "RAW":
+ col.SQLType = core.SQLType{core.Binary, 0, 0}
+ case "ROWID":
+ col.SQLType = core.SQLType{core.Varchar, 18, 0}
+ case "AQ$_SUBSCRIBERS":
+ ignore = true
+ default:
+ col.SQLType = core.SQLType{strings.ToUpper(dt), len1, len2}
+ }
+
+ if ignore {
+ continue
+ }
+
+ if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
+ return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v %v", *dataType, col.SQLType))
+ }
+
+ col.Length = dataLen
+
+ if col.SQLType.IsText() || col.SQLType.IsTime() {
+ if !col.DefaultIsEmpty {
+ col.Default = "'" + col.Default + "'"
+ }
+ }
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+
+ return colSeq, cols, nil
+}
+
+func (db *oracle) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := "SELECT table_name FROM user_tables"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ err = rows.Scan(&table.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " +
+ "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, uniqueness string
+
+ err = rows.Scan(&colName, &uniqueness, &indexName)
+ if err != nil {
+ return nil, err
+ }
+
+ indexName = strings.Trim(indexName, `" `)
+
+ if uniqueness == "UNIQUE" {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.Type = indexType
+ index.Name = indexName
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *oracle) Filters() []core.Filter {
+ return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{":", 1}, &core.IdFilter{}}
+}
diff --git a/vendor/github.com/go-xorm/xorm/pg_reserved.txt b/vendor/github.com/go-xorm/xorm/pg_reserved.txt
new file mode 100644
index 0000000000..720ed377ba
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/pg_reserved.txt
@@ -0,0 +1,746 @@
+A non-reserved non-reserved
+ABORT non-reserved
+ABS reserved reserved
+ABSENT non-reserved non-reserved
+ABSOLUTE non-reserved non-reserved non-reserved reserved
+ACCESS non-reserved
+ACCORDING non-reserved non-reserved
+ACTION non-reserved non-reserved non-reserved reserved
+ADA non-reserved non-reserved non-reserved
+ADD non-reserved non-reserved non-reserved reserved
+ADMIN non-reserved non-reserved non-reserved
+AFTER non-reserved non-reserved non-reserved
+AGGREGATE non-reserved
+ALL reserved reserved reserved reserved
+ALLOCATE reserved reserved reserved
+ALSO non-reserved
+ALTER non-reserved reserved reserved reserved
+ALWAYS non-reserved non-reserved non-reserved
+ANALYSE reserved
+ANALYZE reserved
+AND reserved reserved reserved reserved
+ANY reserved reserved reserved reserved
+ARE reserved reserved reserved
+ARRAY reserved reserved reserved
+ARRAY_AGG reserved reserved
+ARRAY_MAX_CARDINALITY reserved
+AS reserved reserved reserved reserved
+ASC reserved non-reserved non-reserved reserved
+ASENSITIVE reserved reserved
+ASSERTION non-reserved non-reserved non-reserved reserved
+ASSIGNMENT non-reserved non-reserved non-reserved
+ASYMMETRIC reserved reserved reserved
+AT non-reserved reserved reserved reserved
+ATOMIC reserved reserved
+ATTRIBUTE non-reserved non-reserved non-reserved
+ATTRIBUTES non-reserved non-reserved
+AUTHORIZATION reserved (can be function or type) reserved reserved reserved
+AVG reserved reserved reserved
+BACKWARD non-reserved
+BASE64 non-reserved non-reserved
+BEFORE non-reserved non-reserved non-reserved
+BEGIN non-reserved reserved reserved reserved
+BEGIN_FRAME reserved
+BEGIN_PARTITION reserved
+BERNOULLI non-reserved non-reserved
+BETWEEN non-reserved (cannot be function or type) reserved reserved reserved
+BIGINT non-reserved (cannot be function or type) reserved reserved
+BINARY reserved (can be function or type) reserved reserved
+BIT non-reserved (cannot be function or type) reserved
+BIT_LENGTH reserved
+BLOB reserved reserved
+BLOCKED non-reserved non-reserved
+BOM non-reserved non-reserved
+BOOLEAN non-reserved (cannot be function or type) reserved reserved
+BOTH reserved reserved reserved reserved
+BREADTH non-reserved non-reserved
+BY non-reserved reserved reserved reserved
+C non-reserved non-reserved non-reserved
+CACHE non-reserved
+CALL reserved reserved
+CALLED non-reserved reserved reserved
+CARDINALITY reserved reserved
+CASCADE non-reserved non-reserved non-reserved reserved
+CASCADED non-reserved reserved reserved reserved
+CASE reserved reserved reserved reserved
+CAST reserved reserved reserved reserved
+CATALOG non-reserved non-reserved non-reserved reserved
+CATALOG_NAME non-reserved non-reserved non-reserved
+CEIL reserved reserved
+CEILING reserved reserved
+CHAIN non-reserved non-reserved non-reserved
+CHAR non-reserved (cannot be function or type) reserved reserved reserved
+CHARACTER non-reserved (cannot be function or type) reserved reserved reserved
+CHARACTERISTICS non-reserved non-reserved non-reserved
+CHARACTERS non-reserved non-reserved
+CHARACTER_LENGTH reserved reserved reserved
+CHARACTER_SET_CATALOG non-reserved non-reserved non-reserved
+CHARACTER_SET_NAME non-reserved non-reserved non-reserved
+CHARACTER_SET_SCHEMA non-reserved non-reserved non-reserved
+CHAR_LENGTH reserved reserved reserved
+CHECK reserved reserved reserved reserved
+CHECKPOINT non-reserved
+CLASS non-reserved
+CLASS_ORIGIN non-reserved non-reserved non-reserved
+CLOB reserved reserved
+CLOSE non-reserved reserved reserved reserved
+CLUSTER non-reserved
+COALESCE non-reserved (cannot be function or type) reserved reserved reserved
+COBOL non-reserved non-reserved non-reserved
+COLLATE reserved reserved reserved reserved
+COLLATION reserved (can be function or type) non-reserved non-reserved reserved
+COLLATION_CATALOG non-reserved non-reserved non-reserved
+COLLATION_NAME non-reserved non-reserved non-reserved
+COLLATION_SCHEMA non-reserved non-reserved non-reserved
+COLLECT reserved reserved
+COLUMN reserved reserved reserved reserved
+COLUMNS non-reserved non-reserved
+COLUMN_NAME non-reserved non-reserved non-reserved
+COMMAND_FUNCTION non-reserved non-reserved non-reserved
+COMMAND_FUNCTION_CODE non-reserved non-reserved
+COMMENT non-reserved
+COMMENTS non-reserved
+COMMIT non-reserved reserved reserved reserved
+COMMITTED non-reserved non-reserved non-reserved non-reserved
+CONCURRENTLY reserved (can be function or type)
+CONDITION reserved reserved
+CONDITION_NUMBER non-reserved non-reserved non-reserved
+CONFIGURATION non-reserved
+CONNECT reserved reserved reserved
+CONNECTION non-reserved non-reserved non-reserved reserved
+CONNECTION_NAME non-reserved non-reserved non-reserved
+CONSTRAINT reserved reserved reserved reserved
+CONSTRAINTS non-reserved non-reserved non-reserved reserved
+CONSTRAINT_CATALOG non-reserved non-reserved non-reserved
+CONSTRAINT_NAME non-reserved non-reserved non-reserved
+CONSTRAINT_SCHEMA non-reserved non-reserved non-reserved
+CONSTRUCTOR non-reserved non-reserved
+CONTAINS reserved non-reserved
+CONTENT non-reserved non-reserved non-reserved
+CONTINUE non-reserved non-reserved non-reserved reserved
+CONTROL non-reserved non-reserved
+CONVERSION non-reserved
+CONVERT reserved reserved reserved
+COPY non-reserved
+CORR reserved reserved
+CORRESPONDING reserved reserved reserved
+COST non-reserved
+COUNT reserved reserved reserved
+COVAR_POP reserved reserved
+COVAR_SAMP reserved reserved
+CREATE reserved reserved reserved reserved
+CROSS reserved (can be function or type) reserved reserved reserved
+CSV non-reserved
+CUBE reserved reserved
+CUME_DIST reserved reserved
+CURRENT non-reserved reserved reserved reserved
+CURRENT_CATALOG reserved reserved reserved
+CURRENT_DATE reserved reserved reserved reserved
+CURRENT_DEFAULT_TRANSFORM_GROUP reserved reserved
+CURRENT_PATH reserved reserved
+CURRENT_ROLE reserved reserved reserved
+CURRENT_ROW reserved
+CURRENT_SCHEMA reserved (can be function or type) reserved reserved
+CURRENT_TIME reserved reserved reserved reserved
+CURRENT_TIMESTAMP reserved reserved reserved reserved
+CURRENT_TRANSFORM_GROUP_FOR_TYPE reserved reserved
+CURRENT_USER reserved reserved reserved reserved
+CURSOR non-reserved reserved reserved reserved
+CURSOR_NAME non-reserved non-reserved non-reserved
+CYCLE non-reserved reserved reserved
+DATA non-reserved non-reserved non-reserved non-reserved
+DATABASE non-reserved
+DATALINK reserved reserved
+DATE reserved reserved reserved
+DATETIME_INTERVAL_CODE non-reserved non-reserved non-reserved
+DATETIME_INTERVAL_PRECISION non-reserved non-reserved non-reserved
+DAY non-reserved reserved reserved reserved
+DB non-reserved non-reserved
+DEALLOCATE non-reserved reserved reserved reserved
+DEC non-reserved (cannot be function or type) reserved reserved reserved
+DECIMAL non-reserved (cannot be function or type) reserved reserved reserved
+DECLARE non-reserved reserved reserved reserved
+DEFAULT reserved reserved reserved reserved
+DEFAULTS non-reserved non-reserved non-reserved
+DEFERRABLE reserved non-reserved non-reserved reserved
+DEFERRED non-reserved non-reserved non-reserved reserved
+DEFINED non-reserved non-reserved
+DEFINER non-reserved non-reserved non-reserved
+DEGREE non-reserved non-reserved
+DELETE non-reserved reserved reserved reserved
+DELIMITER non-reserved
+DELIMITERS non-reserved
+DENSE_RANK reserved reserved
+DEPTH non-reserved non-reserved
+DEREF reserved reserved
+DERIVED non-reserved non-reserved
+DESC reserved non-reserved non-reserved reserved
+DESCRIBE reserved reserved reserved
+DESCRIPTOR non-reserved non-reserved reserved
+DETERMINISTIC reserved reserved
+DIAGNOSTICS non-reserved non-reserved reserved
+DICTIONARY non-reserved
+DISABLE non-reserved
+DISCARD non-reserved
+DISCONNECT reserved reserved reserved
+DISPATCH non-reserved non-reserved
+DISTINCT reserved reserved reserved reserved
+DLNEWCOPY reserved reserved
+DLPREVIOUSCOPY reserved reserved
+DLURLCOMPLETE reserved reserved
+DLURLCOMPLETEONLY reserved reserved
+DLURLCOMPLETEWRITE reserved reserved
+DLURLPATH reserved reserved
+DLURLPATHONLY reserved reserved
+DLURLPATHWRITE reserved reserved
+DLURLSCHEME reserved reserved
+DLURLSERVER reserved reserved
+DLVALUE reserved reserved
+DO reserved
+DOCUMENT non-reserved non-reserved non-reserved
+DOMAIN non-reserved non-reserved non-reserved reserved
+DOUBLE non-reserved reserved reserved reserved
+DROP non-reserved reserved reserved reserved
+DYNAMIC reserved reserved
+DYNAMIC_FUNCTION non-reserved non-reserved non-reserved
+DYNAMIC_FUNCTION_CODE non-reserved non-reserved
+EACH non-reserved reserved reserved
+ELEMENT reserved reserved
+ELSE reserved reserved reserved reserved
+EMPTY non-reserved non-reserved
+ENABLE non-reserved
+ENCODING non-reserved non-reserved non-reserved
+ENCRYPTED non-reserved
+END reserved reserved reserved reserved
+END-EXEC reserved reserved reserved
+END_FRAME reserved
+END_PARTITION reserved
+ENFORCED non-reserved
+ENUM non-reserved
+EQUALS reserved non-reserved
+ESCAPE non-reserved reserved reserved reserved
+EVENT non-reserved
+EVERY reserved reserved
+EXCEPT reserved reserved reserved reserved
+EXCEPTION reserved
+EXCLUDE non-reserved non-reserved non-reserved
+EXCLUDING non-reserved non-reserved non-reserved
+EXCLUSIVE non-reserved
+EXEC reserved reserved reserved
+EXECUTE non-reserved reserved reserved reserved
+EXISTS non-reserved (cannot be function or type) reserved reserved reserved
+EXP reserved reserved
+EXPLAIN non-reserved
+EXPRESSION non-reserved
+EXTENSION non-reserved
+EXTERNAL non-reserved reserved reserved reserved
+EXTRACT non-reserved (cannot be function or type) reserved reserved reserved
+FALSE reserved reserved reserved reserved
+FAMILY non-reserved
+FETCH reserved reserved reserved reserved
+FILE non-reserved non-reserved
+FILTER reserved reserved
+FINAL non-reserved non-reserved
+FIRST non-reserved non-reserved non-reserved reserved
+FIRST_VALUE reserved reserved
+FLAG non-reserved non-reserved
+FLOAT non-reserved (cannot be function or type) reserved reserved reserved
+FLOOR reserved reserved
+FOLLOWING non-reserved non-reserved non-reserved
+FOR reserved reserved reserved reserved
+FORCE non-reserved
+FOREIGN reserved reserved reserved reserved
+FORTRAN non-reserved non-reserved non-reserved
+FORWARD non-reserved
+FOUND non-reserved non-reserved reserved
+FRAME_ROW reserved
+FREE reserved reserved
+FREEZE reserved (can be function or type)
+FROM reserved reserved reserved reserved
+FS non-reserved non-reserved
+FULL reserved (can be function or type) reserved reserved reserved
+FUNCTION non-reserved reserved reserved
+FUNCTIONS non-reserved
+FUSION reserved reserved
+G non-reserved non-reserved
+GENERAL non-reserved non-reserved
+GENERATED non-reserved non-reserved
+GET reserved reserved reserved
+GLOBAL non-reserved reserved reserved reserved
+GO non-reserved non-reserved reserved
+GOTO non-reserved non-reserved reserved
+GRANT reserved reserved reserved reserved
+GRANTED non-reserved non-reserved non-reserved
+GREATEST non-reserved (cannot be function or type)
+GROUP reserved reserved reserved reserved
+GROUPING reserved reserved
+GROUPS reserved
+HANDLER non-reserved
+HAVING reserved reserved reserved reserved
+HEADER non-reserved
+HEX non-reserved non-reserved
+HIERARCHY non-reserved non-reserved
+HOLD non-reserved reserved reserved
+HOUR non-reserved reserved reserved reserved
+ID non-reserved non-reserved
+IDENTITY non-reserved reserved reserved reserved
+IF non-reserved
+IGNORE non-reserved non-reserved
+ILIKE reserved (can be function or type)
+IMMEDIATE non-reserved non-reserved non-reserved reserved
+IMMEDIATELY non-reserved
+IMMUTABLE non-reserved
+IMPLEMENTATION non-reserved non-reserved
+IMPLICIT non-reserved
+IMPORT reserved reserved
+IN reserved reserved reserved reserved
+INCLUDING non-reserved non-reserved non-reserved
+INCREMENT non-reserved non-reserved non-reserved
+INDENT non-reserved non-reserved
+INDEX non-reserved
+INDEXES non-reserved
+INDICATOR reserved reserved reserved
+INHERIT non-reserved
+INHERITS non-reserved
+INITIALLY reserved non-reserved non-reserved reserved
+INLINE non-reserved
+INNER reserved (can be function or type) reserved reserved reserved
+INOUT non-reserved (cannot be function or type) reserved reserved
+INPUT non-reserved non-reserved non-reserved reserved
+INSENSITIVE non-reserved reserved reserved reserved
+INSERT non-reserved reserved reserved reserved
+INSTANCE non-reserved non-reserved
+INSTANTIABLE non-reserved non-reserved
+INSTEAD non-reserved non-reserved non-reserved
+INT non-reserved (cannot be function or type) reserved reserved reserved
+INTEGER non-reserved (cannot be function or type) reserved reserved reserved
+INTEGRITY non-reserved non-reserved
+INTERSECT reserved reserved reserved reserved
+INTERSECTION reserved reserved
+INTERVAL non-reserved (cannot be function or type) reserved reserved reserved
+INTO reserved reserved reserved reserved
+INVOKER non-reserved non-reserved non-reserved
+IS reserved (can be function or type) reserved reserved reserved
+ISNULL reserved (can be function or type)
+ISOLATION non-reserved non-reserved non-reserved reserved
+JOIN reserved (can be function or type) reserved reserved reserved
+K non-reserved non-reserved
+KEY non-reserved non-reserved non-reserved reserved
+KEY_MEMBER non-reserved non-reserved
+KEY_TYPE non-reserved non-reserved
+LABEL non-reserved
+LAG reserved reserved
+LANGUAGE non-reserved reserved reserved reserved
+LARGE non-reserved reserved reserved
+LAST non-reserved non-reserved non-reserved reserved
+LAST_VALUE reserved reserved
+LATERAL reserved reserved reserved
+LC_COLLATE non-reserved
+LC_CTYPE non-reserved
+LEAD reserved reserved
+LEADING reserved reserved reserved reserved
+LEAKPROOF non-reserved
+LEAST non-reserved (cannot be function or type)
+LEFT reserved (can be function or type) reserved reserved reserved
+LENGTH non-reserved non-reserved non-reserved
+LEVEL non-reserved non-reserved non-reserved reserved
+LIBRARY non-reserved non-reserved
+LIKE reserved (can be function or type) reserved reserved reserved
+LIKE_REGEX reserved reserved
+LIMIT reserved non-reserved non-reserved
+LINK non-reserved non-reserved
+LISTEN non-reserved
+LN reserved reserved
+LOAD non-reserved
+LOCAL non-reserved reserved reserved reserved
+LOCALTIME reserved reserved reserved
+LOCALTIMESTAMP reserved reserved reserved
+LOCATION non-reserved non-reserved non-reserved
+LOCATOR non-reserved non-reserved
+LOCK non-reserved
+LOWER reserved reserved reserved
+M non-reserved non-reserved
+MAP non-reserved non-reserved
+MAPPING non-reserved non-reserved non-reserved
+MATCH non-reserved reserved reserved reserved
+MATCHED non-reserved non-reserved
+MATERIALIZED non-reserved
+MAX reserved reserved reserved
+MAXVALUE non-reserved non-reserved non-reserved
+MAX_CARDINALITY reserved
+MEMBER reserved reserved
+MERGE reserved reserved
+MESSAGE_LENGTH non-reserved non-reserved non-reserved
+MESSAGE_OCTET_LENGTH non-reserved non-reserved non-reserved
+MESSAGE_TEXT non-reserved non-reserved non-reserved
+METHOD reserved reserved
+MIN reserved reserved reserved
+MINUTE non-reserved reserved reserved reserved
+MINVALUE non-reserved non-reserved non-reserved
+MOD reserved reserved
+MODE non-reserved
+MODIFIES reserved reserved
+MODULE reserved reserved reserved
+MONTH non-reserved reserved reserved reserved
+MORE non-reserved non-reserved non-reserved
+MOVE non-reserved
+MULTISET reserved reserved
+MUMPS non-reserved non-reserved non-reserved
+NAME non-reserved non-reserved non-reserved non-reserved
+NAMES non-reserved non-reserved non-reserved reserved
+NAMESPACE non-reserved non-reserved
+NATIONAL non-reserved (cannot be function or type) reserved reserved reserved
+NATURAL reserved (can be function or type) reserved reserved reserved
+NCHAR non-reserved (cannot be function or type) reserved reserved reserved
+NCLOB reserved reserved
+NESTING non-reserved non-reserved
+NEW reserved reserved
+NEXT non-reserved non-reserved non-reserved reserved
+NFC non-reserved non-reserved
+NFD non-reserved non-reserved
+NFKC non-reserved non-reserved
+NFKD non-reserved non-reserved
+NIL non-reserved non-reserved
+NO non-reserved reserved reserved reserved
+NONE non-reserved (cannot be function or type) reserved reserved
+NORMALIZE reserved reserved
+NORMALIZED non-reserved non-reserved
+NOT reserved reserved reserved reserved
+NOTHING non-reserved
+NOTIFY non-reserved
+NOTNULL reserved (can be function or type)
+NOWAIT non-reserved
+NTH_VALUE reserved reserved
+NTILE reserved reserved
+NULL reserved reserved reserved reserved
+NULLABLE non-reserved non-reserved non-reserved
+NULLIF non-reserved (cannot be function or type) reserved reserved reserved
+NULLS non-reserved non-reserved non-reserved
+NUMBER non-reserved non-reserved non-reserved
+NUMERIC non-reserved (cannot be function or type) reserved reserved reserved
+OBJECT non-reserved non-reserved non-reserved
+OCCURRENCES_REGEX reserved reserved
+OCTETS non-reserved non-reserved
+OCTET_LENGTH reserved reserved reserved
+OF non-reserved reserved reserved reserved
+OFF non-reserved non-reserved non-reserved
+OFFSET reserved reserved reserved
+OIDS non-reserved
+OLD reserved reserved
+ON reserved reserved reserved reserved
+ONLY reserved reserved reserved reserved
+OPEN reserved reserved reserved
+OPERATOR non-reserved
+OPTION non-reserved non-reserved non-reserved reserved
+OPTIONS non-reserved non-reserved non-reserved
+OR reserved reserved reserved reserved
+ORDER reserved reserved reserved reserved
+ORDERING non-reserved non-reserved
+ORDINALITY non-reserved non-reserved
+OTHERS non-reserved non-reserved
+OUT non-reserved (cannot be function or type) reserved reserved
+OUTER reserved (can be function or type) reserved reserved reserved
+OUTPUT non-reserved non-reserved reserved
+OVER reserved (can be function or type) reserved reserved
+OVERLAPS reserved (can be function or type) reserved reserved reserved
+OVERLAY non-reserved (cannot be function or type) reserved reserved
+OVERRIDING non-reserved non-reserved
+OWNED non-reserved
+OWNER non-reserved
+P non-reserved non-reserved
+PAD non-reserved non-reserved reserved
+PARAMETER reserved reserved
+PARAMETER_MODE non-reserved non-reserved
+PARAMETER_NAME non-reserved non-reserved
+PARAMETER_ORDINAL_POSITION non-reserved non-reserved
+PARAMETER_SPECIFIC_CATALOG non-reserved non-reserved
+PARAMETER_SPECIFIC_NAME non-reserved non-reserved
+PARAMETER_SPECIFIC_SCHEMA non-reserved non-reserved
+PARSER non-reserved
+PARTIAL non-reserved non-reserved non-reserved reserved
+PARTITION non-reserved reserved reserved
+PASCAL non-reserved non-reserved non-reserved
+PASSING non-reserved non-reserved non-reserved
+PASSTHROUGH non-reserved non-reserved
+PASSWORD non-reserved
+PATH non-reserved non-reserved
+PERCENT reserved
+PERCENTILE_CONT reserved reserved
+PERCENTILE_DISC reserved reserved
+PERCENT_RANK reserved reserved
+PERIOD reserved
+PERMISSION non-reserved non-reserved
+PLACING reserved non-reserved non-reserved
+PLANS non-reserved
+PLI non-reserved non-reserved non-reserved
+PORTION reserved
+POSITION non-reserved (cannot be function or type) reserved reserved reserved
+POSITION_REGEX reserved reserved
+POWER reserved reserved
+PRECEDES reserved
+PRECEDING non-reserved non-reserved non-reserved
+PRECISION non-reserved (cannot be function or type) reserved reserved reserved
+PREPARE non-reserved reserved reserved reserved
+PREPARED non-reserved
+PRESERVE non-reserved non-reserved non-reserved reserved
+PRIMARY reserved reserved reserved reserved
+PRIOR non-reserved non-reserved non-reserved reserved
+PRIVILEGES non-reserved non-reserved non-reserved reserved
+PROCEDURAL non-reserved
+PROCEDURE non-reserved reserved reserved reserved
+PROGRAM non-reserved
+PUBLIC non-reserved non-reserved reserved
+QUOTE non-reserved
+RANGE non-reserved reserved reserved
+RANK reserved reserved
+READ non-reserved non-reserved non-reserved reserved
+READS reserved reserved
+REAL non-reserved (cannot be function or type) reserved reserved reserved
+REASSIGN non-reserved
+RECHECK non-reserved
+RECOVERY non-reserved non-reserved
+RECURSIVE non-reserved reserved reserved
+REF non-reserved reserved reserved
+REFERENCES reserved reserved reserved reserved
+REFERENCING reserved reserved
+REFRESH non-reserved
+REGR_AVGX reserved reserved
+REGR_AVGY reserved reserved
+REGR_COUNT reserved reserved
+REGR_INTERCEPT reserved reserved
+REGR_R2 reserved reserved
+REGR_SLOPE reserved reserved
+REGR_SXX reserved reserved
+REGR_SXY reserved reserved
+REGR_SYY reserved reserved
+REINDEX non-reserved
+RELATIVE non-reserved non-reserved non-reserved reserved
+RELEASE non-reserved reserved reserved
+RENAME non-reserved
+REPEATABLE non-reserved non-reserved non-reserved non-reserved
+REPLACE non-reserved
+REPLICA non-reserved
+REQUIRING non-reserved non-reserved
+RESET non-reserved
+RESPECT non-reserved non-reserved
+RESTART non-reserved non-reserved non-reserved
+RESTORE non-reserved non-reserved
+RESTRICT non-reserved non-reserved non-reserved reserved
+RESULT reserved reserved
+RETURN reserved reserved
+RETURNED_CARDINALITY non-reserved non-reserved
+RETURNED_LENGTH non-reserved non-reserved non-reserved
+RETURNED_OCTET_LENGTH non-reserved non-reserved non-reserved
+RETURNED_SQLSTATE non-reserved non-reserved non-reserved
+RETURNING reserved non-reserved non-reserved
+RETURNS non-reserved reserved reserved
+REVOKE non-reserved reserved reserved reserved
+RIGHT reserved (can be function or type) reserved reserved reserved
+ROLE non-reserved non-reserved non-reserved
+ROLLBACK non-reserved reserved reserved reserved
+ROLLUP reserved reserved
+ROUTINE non-reserved non-reserved
+ROUTINE_CATALOG non-reserved non-reserved
+ROUTINE_NAME non-reserved non-reserved
+ROUTINE_SCHEMA non-reserved non-reserved
+ROW non-reserved (cannot be function or type) reserved reserved
+ROWS non-reserved reserved reserved reserved
+ROW_COUNT non-reserved non-reserved non-reserved
+ROW_NUMBER reserved reserved
+RULE non-reserved
+SAVEPOINT non-reserved reserved reserved
+SCALE non-reserved non-reserved non-reserved
+SCHEMA non-reserved non-reserved non-reserved reserved
+SCHEMA_NAME non-reserved non-reserved non-reserved
+SCOPE reserved reserved
+SCOPE_CATALOG non-reserved non-reserved
+SCOPE_NAME non-reserved non-reserved
+SCOPE_SCHEMA non-reserved non-reserved
+SCROLL non-reserved reserved reserved reserved
+SEARCH non-reserved reserved reserved
+SECOND non-reserved reserved reserved reserved
+SECTION non-reserved non-reserved reserved
+SECURITY non-reserved non-reserved non-reserved
+SELECT reserved reserved reserved reserved
+SELECTIVE non-reserved non-reserved
+SELF non-reserved non-reserved
+SENSITIVE reserved reserved
+SEQUENCE non-reserved non-reserved non-reserved
+SEQUENCES non-reserved
+SERIALIZABLE non-reserved non-reserved non-reserved non-reserved
+SERVER non-reserved non-reserved non-reserved
+SERVER_NAME non-reserved non-reserved non-reserved
+SESSION non-reserved non-reserved non-reserved reserved
+SESSION_USER reserved reserved reserved reserved
+SET non-reserved reserved reserved reserved
+SETOF non-reserved (cannot be function or type)
+SETS non-reserved non-reserved
+SHARE non-reserved
+SHOW non-reserved
+SIMILAR reserved (can be function or type) reserved reserved
+SIMPLE non-reserved non-reserved non-reserved
+SIZE non-reserved non-reserved reserved
+SMALLINT non-reserved (cannot be function or type) reserved reserved reserved
+SNAPSHOT non-reserved
+SOME reserved reserved reserved reserved
+SOURCE non-reserved non-reserved
+SPACE non-reserved non-reserved reserved
+SPECIFIC reserved reserved
+SPECIFICTYPE reserved reserved
+SPECIFIC_NAME non-reserved non-reserved
+SQL reserved reserved reserved
+SQLCODE reserved
+SQLERROR reserved
+SQLEXCEPTION reserved reserved
+SQLSTATE reserved reserved reserved
+SQLWARNING reserved reserved
+SQRT reserved reserved
+STABLE non-reserved
+STANDALONE non-reserved non-reserved non-reserved
+START non-reserved reserved reserved
+STATE non-reserved non-reserved
+STATEMENT non-reserved non-reserved non-reserved
+STATIC reserved reserved
+STATISTICS non-reserved
+STDDEV_POP reserved reserved
+STDDEV_SAMP reserved reserved
+STDIN non-reserved
+STDOUT non-reserved
+STORAGE non-reserved
+STRICT non-reserved
+STRIP non-reserved non-reserved non-reserved
+STRUCTURE non-reserved non-reserved
+STYLE non-reserved non-reserved
+SUBCLASS_ORIGIN non-reserved non-reserved non-reserved
+SUBMULTISET reserved reserved
+SUBSTRING non-reserved (cannot be function or type) reserved reserved reserved
+SUBSTRING_REGEX reserved reserved
+SUCCEEDS reserved
+SUM reserved reserved reserved
+SYMMETRIC reserved reserved reserved
+SYSID non-reserved
+SYSTEM non-reserved reserved reserved
+SYSTEM_TIME reserved
+SYSTEM_USER reserved reserved reserved
+T non-reserved non-reserved
+TABLE reserved reserved reserved reserved
+TABLES non-reserved
+TABLESAMPLE reserved reserved
+TABLESPACE non-reserved
+TABLE_NAME non-reserved non-reserved non-reserved
+TEMP non-reserved
+TEMPLATE non-reserved
+TEMPORARY non-reserved non-reserved non-reserved reserved
+TEXT non-reserved
+THEN reserved reserved reserved reserved
+TIES non-reserved non-reserved
+TIME non-reserved (cannot be function or type) reserved reserved reserved
+TIMESTAMP non-reserved (cannot be function or type) reserved reserved reserved
+TIMEZONE_HOUR reserved reserved reserved
+TIMEZONE_MINUTE reserved reserved reserved
+TO reserved reserved reserved reserved
+TOKEN non-reserved non-reserved
+TOP_LEVEL_COUNT non-reserved non-reserved
+TRAILING reserved reserved reserved reserved
+TRANSACTION non-reserved non-reserved non-reserved reserved
+TRANSACTIONS_COMMITTED non-reserved non-reserved
+TRANSACTIONS_ROLLED_BACK non-reserved non-reserved
+TRANSACTION_ACTIVE non-reserved non-reserved
+TRANSFORM non-reserved non-reserved
+TRANSFORMS non-reserved non-reserved
+TRANSLATE reserved reserved reserved
+TRANSLATE_REGEX reserved reserved
+TRANSLATION reserved reserved reserved
+TREAT non-reserved (cannot be function or type) reserved reserved
+TRIGGER non-reserved reserved reserved
+TRIGGER_CATALOG non-reserved non-reserved
+TRIGGER_NAME non-reserved non-reserved
+TRIGGER_SCHEMA non-reserved non-reserved
+TRIM non-reserved (cannot be function or type) reserved reserved reserved
+TRIM_ARRAY reserved reserved
+TRUE reserved reserved reserved reserved
+TRUNCATE non-reserved reserved reserved
+TRUSTED non-reserved
+TYPE non-reserved non-reserved non-reserved non-reserved
+TYPES non-reserved
+UESCAPE reserved reserved
+UNBOUNDED non-reserved non-reserved non-reserved
+UNCOMMITTED non-reserved non-reserved non-reserved non-reserved
+UNDER non-reserved non-reserved
+UNENCRYPTED non-reserved
+UNION reserved reserved reserved reserved
+UNIQUE reserved reserved reserved reserved
+UNKNOWN non-reserved reserved reserved reserved
+UNLINK non-reserved non-reserved
+UNLISTEN non-reserved
+UNLOGGED non-reserved
+UNNAMED non-reserved non-reserved non-reserved
+UNNEST reserved reserved
+UNTIL non-reserved
+UNTYPED non-reserved non-reserved
+UPDATE non-reserved reserved reserved reserved
+UPPER reserved reserved reserved
+URI non-reserved non-reserved
+USAGE non-reserved non-reserved reserved
+USER reserved reserved reserved reserved
+USER_DEFINED_TYPE_CATALOG non-reserved non-reserved
+USER_DEFINED_TYPE_CODE non-reserved non-reserved
+USER_DEFINED_TYPE_NAME non-reserved non-reserved
+USER_DEFINED_TYPE_SCHEMA non-reserved non-reserved
+USING reserved reserved reserved reserved
+VACUUM non-reserved
+VALID non-reserved non-reserved non-reserved
+VALIDATE non-reserved
+VALIDATOR non-reserved
+VALUE non-reserved reserved reserved reserved
+VALUES non-reserved (cannot be function or type) reserved reserved reserved
+VALUE_OF reserved
+VARBINARY reserved reserved
+VARCHAR non-reserved (cannot be function or type) reserved reserved reserved
+VARIADIC reserved
+VARYING non-reserved reserved reserved reserved
+VAR_POP reserved reserved
+VAR_SAMP reserved reserved
+VERBOSE reserved (can be function or type)
+VERSION non-reserved non-reserved non-reserved
+VERSIONING reserved
+VIEW non-reserved non-reserved non-reserved reserved
+VOLATILE non-reserved
+WHEN reserved reserved reserved reserved
+WHENEVER reserved reserved reserved
+WHERE reserved reserved reserved reserved
+WHITESPACE non-reserved non-reserved non-reserved
+WIDTH_BUCKET reserved reserved
+WINDOW reserved reserved reserved
+WITH reserved reserved reserved reserved
+WITHIN reserved reserved
+WITHOUT non-reserved reserved reserved
+WORK non-reserved non-reserved non-reserved reserved
+WRAPPER non-reserved non-reserved non-reserved
+WRITE non-reserved non-reserved non-reserved reserved
+XML non-reserved reserved reserved
+XMLAGG reserved reserved
+XMLATTRIBUTES non-reserved (cannot be function or type) reserved reserved
+XMLBINARY reserved reserved
+XMLCAST reserved reserved
+XMLCOMMENT reserved reserved
+XMLCONCAT non-reserved (cannot be function or type) reserved reserved
+XMLDECLARATION non-reserved non-reserved
+XMLDOCUMENT reserved reserved
+XMLELEMENT non-reserved (cannot be function or type) reserved reserved
+XMLEXISTS non-reserved (cannot be function or type) reserved reserved
+XMLFOREST non-reserved (cannot be function or type) reserved reserved
+XMLITERATE reserved reserved
+XMLNAMESPACES reserved reserved
+XMLPARSE non-reserved (cannot be function or type) reserved reserved
+XMLPI non-reserved (cannot be function or type) reserved reserved
+XMLQUERY reserved reserved
+XMLROOT non-reserved (cannot be function or type)
+XMLSCHEMA non-reserved non-reserved
+XMLSERIALIZE non-reserved (cannot be function or type) reserved reserved
+XMLTABLE reserved reserved
+XMLTEXT reserved reserved
+XMLVALIDATE reserved reserved
+YEAR non-reserved reserved reserved reserved
+YES non-reserved non-reserved non-reserved
+ZONE non-reserved non-reserved non-reserved reserved \ No newline at end of file
diff --git a/vendor/github.com/go-xorm/xorm/postgres_dialect.go b/vendor/github.com/go-xorm/xorm/postgres_dialect.go
new file mode 100644
index 0000000000..8316e29cd2
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/postgres_dialect.go
@@ -0,0 +1,1091 @@
+// Copyright 2015 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 (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
+var (
+ postgresReservedWords = map[string]bool{
+ "A": true,
+ "ABORT": true,
+ "ABS": true,
+ "ABSENT": true,
+ "ABSOLUTE": true,
+ "ACCESS": true,
+ "ACCORDING": true,
+ "ACTION": true,
+ "ADA": true,
+ "ADD": true,
+ "ADMIN": true,
+ "AFTER": true,
+ "AGGREGATE": true,
+ "ALL": true,
+ "ALLOCATE": true,
+ "ALSO": true,
+ "ALTER": true,
+ "ALWAYS": true,
+ "ANALYSE": true,
+ "ANALYZE": true,
+ "AND": true,
+ "ANY": true,
+ "ARE": true,
+ "ARRAY": true,
+ "ARRAY_AGG": true,
+ "ARRAY_MAX_CARDINALITY": true,
+ "AS": true,
+ "ASC": true,
+ "ASENSITIVE": true,
+ "ASSERTION": true,
+ "ASSIGNMENT": true,
+ "ASYMMETRIC": true,
+ "AT": true,
+ "ATOMIC": true,
+ "ATTRIBUTE": true,
+ "ATTRIBUTES": true,
+ "AUTHORIZATION": true,
+ "AVG": true,
+ "BACKWARD": true,
+ "BASE64": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BEGIN_FRAME": true,
+ "BEGIN_PARTITION": true,
+ "BERNOULLI": true,
+ "BETWEEN": true,
+ "BIGINT": true,
+ "BINARY": true,
+ "BIT": true,
+ "BIT_LENGTH": true,
+ "BLOB": true,
+ "BLOCKED": true,
+ "BOM": true,
+ "BOOLEAN": true,
+ "BOTH": true,
+ "BREADTH": true,
+ "BY": true,
+ "C": true,
+ "CACHE": true,
+ "CALL": true,
+ "CALLED": true,
+ "CARDINALITY": true,
+ "CASCADE": true,
+ "CASCADED": true,
+ "CASE": true,
+ "CAST": true,
+ "CATALOG": true,
+ "CATALOG_NAME": true,
+ "CEIL": true,
+ "CEILING": true,
+ "CHAIN": true,
+ "CHAR": true,
+ "CHARACTER": true,
+ "CHARACTERISTICS": true,
+ "CHARACTERS": true,
+ "CHARACTER_LENGTH": true,
+ "CHARACTER_SET_CATALOG": true,
+ "CHARACTER_SET_NAME": true,
+ "CHARACTER_SET_SCHEMA": true,
+ "CHAR_LENGTH": true,
+ "CHECK": true,
+ "CHECKPOINT": true,
+ "CLASS": true,
+ "CLASS_ORIGIN": true,
+ "CLOB": true,
+ "CLOSE": true,
+ "CLUSTER": true,
+ "COALESCE": true,
+ "COBOL": true,
+ "COLLATE": true,
+ "COLLATION": true,
+ "COLLATION_CATALOG": true,
+ "COLLATION_NAME": true,
+ "COLLATION_SCHEMA": true,
+ "COLLECT": true,
+ "COLUMN": true,
+ "COLUMNS": true,
+ "COLUMN_NAME": true,
+ "COMMAND_FUNCTION": true,
+ "COMMAND_FUNCTION_CODE": true,
+ "COMMENT": true,
+ "COMMENTS": true,
+ "COMMIT": true,
+ "COMMITTED": true,
+ "CONCURRENTLY": true,
+ "CONDITION": true,
+ "CONDITION_NUMBER": true,
+ "CONFIGURATION": true,
+ "CONNECT": true,
+ "CONNECTION": true,
+ "CONNECTION_NAME": true,
+ "CONSTRAINT": true,
+ "CONSTRAINTS": true,
+ "CONSTRAINT_CATALOG": true,
+ "CONSTRAINT_NAME": true,
+ "CONSTRAINT_SCHEMA": true,
+ "CONSTRUCTOR": true,
+ "CONTAINS": true,
+ "CONTENT": true,
+ "CONTINUE": true,
+ "CONTROL": true,
+ "CONVERSION": true,
+ "CONVERT": true,
+ "COPY": true,
+ "CORR": true,
+ "CORRESPONDING": true,
+ "COST": true,
+ "COUNT": true,
+ "COVAR_POP": true,
+ "COVAR_SAMP": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CSV": true,
+ "CUBE": true,
+ "CUME_DIST": true,
+ "CURRENT": true,
+ "CURRENT_CATALOG": true,
+ "CURRENT_DATE": true,
+ "CURRENT_DEFAULT_TRANSFORM_GROUP": true,
+ "CURRENT_PATH": true,
+ "CURRENT_ROLE": true,
+ "CURRENT_ROW": true,
+ "CURRENT_SCHEMA": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "CURRENT_TRANSFORM_GROUP_FOR_TYPE": true,
+ "CURRENT_USER": true,
+ "CURSOR": true,
+ "CURSOR_NAME": true,
+ "CYCLE": true,
+ "DATA": true,
+ "DATABASE": true,
+ "DATALINK": true,
+ "DATE": true,
+ "DATETIME_INTERVAL_CODE": true,
+ "DATETIME_INTERVAL_PRECISION": true,
+ "DAY": true,
+ "DB": true,
+ "DEALLOCATE": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DEFAULTS": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DEFINED": true,
+ "DEFINER": true,
+ "DEGREE": true,
+ "DELETE": true,
+ "DELIMITER": true,
+ "DELIMITERS": true,
+ "DENSE_RANK": true,
+ "DEPTH": true,
+ "DEREF": true,
+ "DERIVED": true,
+ "DESC": true,
+ "DESCRIBE": true,
+ "DESCRIPTOR": true,
+ "DETERMINISTIC": true,
+ "DIAGNOSTICS": true,
+ "DICTIONARY": true,
+ "DISABLE": true,
+ "DISCARD": true,
+ "DISCONNECT": true,
+ "DISPATCH": true,
+ "DISTINCT": true,
+ "DLNEWCOPY": true,
+ "DLPREVIOUSCOPY": true,
+ "DLURLCOMPLETE": true,
+ "DLURLCOMPLETEONLY": true,
+ "DLURLCOMPLETEWRITE": true,
+ "DLURLPATH": true,
+ "DLURLPATHONLY": true,
+ "DLURLPATHWRITE": true,
+ "DLURLSCHEME": true,
+ "DLURLSERVER": true,
+ "DLVALUE": true,
+ "DO": true,
+ "DOCUMENT": true,
+ "DOMAIN": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DYNAMIC": true,
+ "DYNAMIC_FUNCTION": true,
+ "DYNAMIC_FUNCTION_CODE": true,
+ "EACH": true,
+ "ELEMENT": true,
+ "ELSE": true,
+ "EMPTY": true,
+ "ENABLE": true,
+ "ENCODING": true,
+ "ENCRYPTED": true,
+ "END": true,
+ "END-EXEC": true,
+ "END_FRAME": true,
+ "END_PARTITION": true,
+ "ENFORCED": true,
+ "ENUM": true,
+ "EQUALS": true,
+ "ESCAPE": true,
+ "EVENT": true,
+ "EVERY": true,
+ "EXCEPT": true,
+ "EXCEPTION": true,
+ "EXCLUDE": true,
+ "EXCLUDING": true,
+ "EXCLUSIVE": true,
+ "EXEC": true,
+ "EXECUTE": true,
+ "EXISTS": true,
+ "EXP": true,
+ "EXPLAIN": true,
+ "EXPRESSION": true,
+ "EXTENSION": true,
+ "EXTERNAL": true,
+ "EXTRACT": true,
+ "FALSE": true,
+ "FAMILY": true,
+ "FETCH": true,
+ "FILE": true,
+ "FILTER": true,
+ "FINAL": true,
+ "FIRST": true,
+ "FIRST_VALUE": true,
+ "FLAG": true,
+ "FLOAT": true,
+ "FLOOR": true,
+ "FOLLOWING": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FORTRAN": true,
+ "FORWARD": true,
+ "FOUND": true,
+ "FRAME_ROW": true,
+ "FREE": true,
+ "FREEZE": true,
+ "FROM": true,
+ "FS": true,
+ "FULL": true,
+ "FUNCTION": true,
+ "FUNCTIONS": true,
+ "FUSION": true,
+ "G": true,
+ "GENERAL": true,
+ "GENERATED": true,
+ "GET": true,
+ "GLOBAL": true,
+ "GO": true,
+ "GOTO": true,
+ "GRANT": true,
+ "GRANTED": true,
+ "GREATEST": true,
+ "GROUP": true,
+ "GROUPING": true,
+ "GROUPS": true,
+ "HANDLER": true,
+ "HAVING": true,
+ "HEADER": true,
+ "HEX": true,
+ "HIERARCHY": true,
+ "HOLD": true,
+ "HOUR": true,
+ "ID": true,
+ "IDENTITY": true,
+ "IF": true,
+ "IGNORE": true,
+ "ILIKE": true,
+ "IMMEDIATE": true,
+ "IMMEDIATELY": true,
+ "IMMUTABLE": true,
+ "IMPLEMENTATION": true,
+ "IMPLICIT": true,
+ "IMPORT": true,
+ "IN": true,
+ "INCLUDING": true,
+ "INCREMENT": true,
+ "INDENT": true,
+ "INDEX": true,
+ "INDEXES": true,
+ "INDICATOR": true,
+ "INHERIT": true,
+ "INHERITS": true,
+ "INITIALLY": true,
+ "INLINE": true,
+ "INNER": true,
+ "INOUT": true,
+ "INPUT": true,
+ "INSENSITIVE": true,
+ "INSERT": true,
+ "INSTANCE": true,
+ "INSTANTIABLE": true,
+ "INSTEAD": true,
+ "INT": true,
+ "INTEGER": true,
+ "INTEGRITY": true,
+ "INTERSECT": true,
+ "INTERSECTION": true,
+ "INTERVAL": true,
+ "INTO": true,
+ "INVOKER": true,
+ "IS": true,
+ "ISNULL": true,
+ "ISOLATION": true,
+ "JOIN": true,
+ "K": true,
+ "KEY": true,
+ "KEY_MEMBER": true,
+ "KEY_TYPE": true,
+ "LABEL": true,
+ "LAG": true,
+ "LANGUAGE": true,
+ "LARGE": true,
+ "LAST": true,
+ "LAST_VALUE": true,
+ "LATERAL": true,
+ "LC_COLLATE": true,
+ "LC_CTYPE": true,
+ "LEAD": true,
+ "LEADING": true,
+ "LEAKPROOF": true,
+ "LEAST": true,
+ "LEFT": true,
+ "LENGTH": true,
+ "LEVEL": true,
+ "LIBRARY": true,
+ "LIKE": true,
+ "LIKE_REGEX": true,
+ "LIMIT": true,
+ "LINK": true,
+ "LISTEN": true,
+ "LN": true,
+ "LOAD": true,
+ "LOCAL": true,
+ "LOCALTIME": true,
+ "LOCALTIMESTAMP": true,
+ "LOCATION": true,
+ "LOCATOR": true,
+ "LOCK": true,
+ "LOWER": true,
+ "M": true,
+ "MAP": true,
+ "MAPPING": true,
+ "MATCH": true,
+ "MATCHED": true,
+ "MATERIALIZED": true,
+ "MAX": true,
+ "MAXVALUE": true,
+ "MAX_CARDINALITY": true,
+ "MEMBER": true,
+ "MERGE": true,
+ "MESSAGE_LENGTH": true,
+ "MESSAGE_OCTET_LENGTH": true,
+ "MESSAGE_TEXT": true,
+ "METHOD": true,
+ "MIN": true,
+ "MINUTE": true,
+ "MINVALUE": true,
+ "MOD": true,
+ "MODE": true,
+ "MODIFIES": true,
+ "MODULE": true,
+ "MONTH": true,
+ "MORE": true,
+ "MOVE": true,
+ "MULTISET": true,
+ "MUMPS": true,
+ "NAME": true,
+ "NAMES": true,
+ "NAMESPACE": true,
+ "NATIONAL": true,
+ "NATURAL": true,
+ "NCHAR": true,
+ "NCLOB": true,
+ "NESTING": true,
+ "NEW": true,
+ "NEXT": true,
+ "NFC": true,
+ "NFD": true,
+ "NFKC": true,
+ "NFKD": true,
+ "NIL": true,
+ "NO": true,
+ "NONE": true,
+ "NORMALIZE": true,
+ "NORMALIZED": true,
+ "NOT": true,
+ "NOTHING": true,
+ "NOTIFY": true,
+ "NOTNULL": true,
+ "NOWAIT": true,
+ "NTH_VALUE": true,
+ "NTILE": true,
+ "NULL": true,
+ "NULLABLE": true,
+ "NULLIF": true,
+ "NULLS": true,
+ "NUMBER": true,
+ "NUMERIC": true,
+ "OBJECT": true,
+ "OCCURRENCES_REGEX": true,
+ "OCTETS": true,
+ "OCTET_LENGTH": true,
+ "OF": true,
+ "OFF": true,
+ "OFFSET": true,
+ "OIDS": true,
+ "OLD": true,
+ "ON": true,
+ "ONLY": true,
+ "OPEN": true,
+ "OPERATOR": true,
+ "OPTION": true,
+ "OPTIONS": true,
+ "OR": true,
+ "ORDER": true,
+ "ORDERING": true,
+ "ORDINALITY": true,
+ "OTHERS": true,
+ "OUT": true,
+ "OUTER": true,
+ "OUTPUT": true,
+ "OVER": true,
+ "OVERLAPS": true,
+ "OVERLAY": true,
+ "OVERRIDING": true,
+ "OWNED": true,
+ "OWNER": true,
+ "P": true,
+ "PAD": true,
+ "PARAMETER": true,
+ "PARAMETER_MODE": true,
+ "PARAMETER_NAME": true,
+ "PARAMETER_ORDINAL_POSITION": true,
+ "PARAMETER_SPECIFIC_CATALOG": true,
+ "PARAMETER_SPECIFIC_NAME": true,
+ "PARAMETER_SPECIFIC_SCHEMA": true,
+ "PARSER": true,
+ "PARTIAL": true,
+ "PARTITION": true,
+ "PASCAL": true,
+ "PASSING": true,
+ "PASSTHROUGH": true,
+ "PASSWORD": true,
+ "PATH": true,
+ "PERCENT": true,
+ "PERCENTILE_CONT": true,
+ "PERCENTILE_DISC": true,
+ "PERCENT_RANK": true,
+ "PERIOD": true,
+ "PERMISSION": true,
+ "PLACING": true,
+ "PLANS": true,
+ "PLI": true,
+ "PORTION": true,
+ "POSITION": true,
+ "POSITION_REGEX": true,
+ "POWER": true,
+ "PRECEDES": true,
+ "PRECEDING": true,
+ "PRECISION": true,
+ "PREPARE": true,
+ "PREPARED": true,
+ "PRESERVE": true,
+ "PRIMARY": true,
+ "PRIOR": true,
+ "PRIVILEGES": true,
+ "PROCEDURAL": true,
+ "PROCEDURE": true,
+ "PROGRAM": true,
+ "PUBLIC": true,
+ "QUOTE": true,
+ "RANGE": true,
+ "RANK": true,
+ "READ": true,
+ "READS": true,
+ "REAL": true,
+ "REASSIGN": true,
+ "RECHECK": true,
+ "RECOVERY": true,
+ "RECURSIVE": true,
+ "REF": true,
+ "REFERENCES": true,
+ "REFERENCING": true,
+ "REFRESH": true,
+ "REGR_AVGX": true,
+ "REGR_AVGY": true,
+ "REGR_COUNT": true,
+ "REGR_INTERCEPT": true,
+ "REGR_R2": true,
+ "REGR_SLOPE": true,
+ "REGR_SXX": true,
+ "REGR_SXY": true,
+ "REGR_SYY": true,
+ "REINDEX": true,
+ "RELATIVE": true,
+ "RELEASE": true,
+ "RENAME": true,
+ "REPEATABLE": true,
+ "REPLACE": true,
+ "REPLICA": true,
+ "REQUIRING": true,
+ "RESET": true,
+ "RESPECT": true,
+ "RESTART": true,
+ "RESTORE": true,
+ "RESTRICT": true,
+ "RESULT": true,
+ "RETURN": true,
+ "RETURNED_CARDINALITY": true,
+ "RETURNED_LENGTH": true,
+ "RETURNED_OCTET_LENGTH": true,
+ "RETURNED_SQLSTATE": true,
+ "RETURNING": true,
+ "RETURNS": true,
+ "REVOKE": true,
+ "RIGHT": true,
+ "ROLE": true,
+ "ROLLBACK": true,
+ "ROLLUP": true,
+ "ROUTINE": true,
+ "ROUTINE_CATALOG": true,
+ "ROUTINE_NAME": true,
+ "ROUTINE_SCHEMA": true,
+ "ROW": true,
+ "ROWS": true,
+ "ROW_COUNT": true,
+ "ROW_NUMBER": true,
+ "RULE": true,
+ "SAVEPOINT": true,
+ "SCALE": true,
+ "SCHEMA": true,
+ "SCHEMA_NAME": true,
+ "SCOPE": true,
+ "SCOPE_CATALOG": true,
+ "SCOPE_NAME": true,
+ "SCOPE_SCHEMA": true,
+ "SCROLL": true,
+ "SEARCH": true,
+ "SECOND": true,
+ "SECTION": true,
+ "SECURITY": true,
+ "SELECT": true,
+ "SELECTIVE": true,
+ "SELF": true,
+ "SENSITIVE": true,
+ "SEQUENCE": true,
+ "SEQUENCES": true,
+ "SERIALIZABLE": true,
+ "SERVER": true,
+ "SERVER_NAME": true,
+ "SESSION": true,
+ "SESSION_USER": true,
+ "SET": true,
+ "SETOF": true,
+ "SETS": true,
+ "SHARE": true,
+ "SHOW": true,
+ "SIMILAR": true,
+ "SIMPLE": true,
+ "SIZE": true,
+ "SMALLINT": true,
+ "SNAPSHOT": true,
+ "SOME": true,
+ "SOURCE": true,
+ "SPACE": true,
+ "SPECIFIC": true,
+ "SPECIFICTYPE": true,
+ "SPECIFIC_NAME": true,
+ "SQL": true,
+ "SQLCODE": true,
+ "SQLERROR": true,
+ "SQLEXCEPTION": true,
+ "SQLSTATE": true,
+ "SQLWARNING": true,
+ "SQRT": true,
+ "STABLE": true,
+ "STANDALONE": true,
+ "START": true,
+ "STATE": true,
+ "STATEMENT": true,
+ "STATIC": true,
+ "STATISTICS": true,
+ "STDDEV_POP": true,
+ "STDDEV_SAMP": true,
+ "STDIN": true,
+ "STDOUT": true,
+ "STORAGE": true,
+ "STRICT": true,
+ "STRIP": true,
+ "STRUCTURE": true,
+ "STYLE": true,
+ "SUBCLASS_ORIGIN": true,
+ "SUBMULTISET": true,
+ "SUBSTRING": true,
+ "SUBSTRING_REGEX": true,
+ "SUCCEEDS": true,
+ "SUM": true,
+ "SYMMETRIC": true,
+ "SYSID": true,
+ "SYSTEM": true,
+ "SYSTEM_TIME": true,
+ "SYSTEM_USER": true,
+ "T": true,
+ "TABLE": true,
+ "TABLES": true,
+ "TABLESAMPLE": true,
+ "TABLESPACE": true,
+ "TABLE_NAME": true,
+ "TEMP": true,
+ "TEMPLATE": true,
+ "TEMPORARY": true,
+ "TEXT": true,
+ "THEN": true,
+ "TIES": true,
+ "TIME": true,
+ "TIMESTAMP": true,
+ "TIMEZONE_HOUR": true,
+ "TIMEZONE_MINUTE": true,
+ "TO": true,
+ "TOKEN": true,
+ "TOP_LEVEL_COUNT": true,
+ "TRAILING": true,
+ "TRANSACTION": true,
+ "TRANSACTIONS_COMMITTED": true,
+ "TRANSACTIONS_ROLLED_BACK": true,
+ "TRANSACTION_ACTIVE": true,
+ "TRANSFORM": true,
+ "TRANSFORMS": true,
+ "TRANSLATE": true,
+ "TRANSLATE_REGEX": true,
+ "TRANSLATION": true,
+ "TREAT": true,
+ "TRIGGER": true,
+ "TRIGGER_CATALOG": true,
+ "TRIGGER_NAME": true,
+ "TRIGGER_SCHEMA": true,
+ "TRIM": true,
+ "TRIM_ARRAY": true,
+ "TRUE": true,
+ "TRUNCATE": true,
+ "TRUSTED": true,
+ "TYPE": true,
+ "TYPES": true,
+ "UESCAPE": true,
+ "UNBOUNDED": true,
+ "UNCOMMITTED": true,
+ "UNDER": true,
+ "UNENCRYPTED": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UNKNOWN": true,
+ "UNLINK": true,
+ "UNLISTEN": true,
+ "UNLOGGED": true,
+ "UNNAMED": true,
+ "UNNEST": true,
+ "UNTIL": true,
+ "UNTYPED": true,
+ "UPDATE": true,
+ "UPPER": true,
+ "URI": true,
+ "USAGE": true,
+ "USER": true,
+ "USER_DEFINED_TYPE_CATALOG": true,
+ "USER_DEFINED_TYPE_CODE": true,
+ "USER_DEFINED_TYPE_NAME": true,
+ "USER_DEFINED_TYPE_SCHEMA": true,
+ "USING": true,
+ "VACUUM": true,
+ "VALID": true,
+ "VALIDATE": true,
+ "VALIDATOR": true,
+ "VALUE": true,
+ "VALUES": true,
+ "VALUE_OF": true,
+ "VARBINARY": true,
+ "VARCHAR": true,
+ "VARIADIC": true,
+ "VARYING": true,
+ "VAR_POP": true,
+ "VAR_SAMP": true,
+ "VERBOSE": true,
+ "VERSION": true,
+ "VERSIONING": true,
+ "VIEW": true,
+ "VOLATILE": true,
+ "WHEN": true,
+ "WHENEVER": true,
+ "WHERE": true,
+ "WHITESPACE": true,
+ "WIDTH_BUCKET": true,
+ "WINDOW": true,
+ "WITH": true,
+ "WITHIN": true,
+ "WITHOUT": true,
+ "WORK": true,
+ "WRAPPER": true,
+ "WRITE": true,
+ "XML": true,
+ "XMLAGG": true,
+ "XMLATTRIBUTES": true,
+ "XMLBINARY": true,
+ "XMLCAST": true,
+ "XMLCOMMENT": true,
+ "XMLCONCAT": true,
+ "XMLDECLARATION": true,
+ "XMLDOCUMENT": true,
+ "XMLELEMENT": true,
+ "XMLEXISTS": true,
+ "XMLFOREST": true,
+ "XMLITERATE": true,
+ "XMLNAMESPACES": true,
+ "XMLPARSE": true,
+ "XMLPI": true,
+ "XMLQUERY": true,
+ "XMLROOT": true,
+ "XMLSCHEMA": true,
+ "XMLSERIALIZE": true,
+ "XMLTABLE": true,
+ "XMLTEXT": true,
+ "XMLVALIDATE": true,
+ "YEAR": true,
+ "YES": true,
+ "ZONE": true,
+ }
+)
+
+type postgres struct {
+ core.Base
+}
+
+func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *postgres) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.TinyInt:
+ res = core.SmallInt
+ return res
+ case core.MediumInt, core.Int, core.Integer:
+ if c.IsAutoIncrement {
+ return core.Serial
+ }
+ return core.Integer
+ case core.Serial, core.BigSerial:
+ c.IsAutoIncrement = true
+ c.Nullable = false
+ res = t
+ case core.Binary, core.VarBinary:
+ return core.Bytea
+ case core.DateTime:
+ res = core.TimeStamp
+ case core.TimeStampz:
+ return "timestamp with time zone"
+ case core.Float:
+ res = core.Real
+ case core.TinyText, core.MediumText, core.LongText:
+ res = core.Text
+ case core.NVarchar:
+ res = core.Varchar
+ case core.Uuid:
+ res = core.Uuid
+ case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob:
+ return core.Bytea
+ case core.Double:
+ return "DOUBLE PRECISION"
+ default:
+ if c.IsAutoIncrement {
+ return core.Serial
+ }
+ res = t
+ }
+
+ var hasLen1 bool = (c.Length > 0)
+ var hasLen2 bool = (c.Length2 > 0)
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *postgres) SupportInsertMany() bool {
+ return true
+}
+
+func (db *postgres) IsReserved(name string) bool {
+ _, ok := postgresReservedWords[name]
+ return ok
+}
+
+func (db *postgres) Quote(name string) string {
+ name = strings.Replace(name, ".", `"."`, -1)
+ return "\"" + name + "\""
+}
+
+func (db *postgres) QuoteStr() string {
+ return "\""
+}
+
+func (db *postgres) AutoIncrStr() string {
+ return ""
+}
+
+func (db *postgres) SupportEngine() bool {
+ return false
+}
+
+func (db *postgres) SupportCharset() bool {
+ return false
+}
+
+func (db *postgres) IndexOnTable() bool {
+ return false
+}
+
+func (db *postgres) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{tableName, idxName}
+ return `SELECT indexname FROM pg_indexes ` +
+ `WHERE tablename = ? AND indexname = ?`, args
+}
+
+func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args
+}
+
+/*func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{tableName, colName}
+ return "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ?" +
+ " AND column_name = ?", args
+}*/
+
+func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string {
+ return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
+ tableName, col.Name, db.SqlType(col))
+}
+
+func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
+ quote := db.Quote
+ //var unique string
+ var idxName string = index.Name
+ if !strings.HasPrefix(idxName, "UQE_") &&
+ !strings.HasPrefix(idxName, "IDX_") {
+ if index.Type == core.UniqueType {
+ idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
+ } else {
+ idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
+ }
+ }
+ return fmt.Sprintf("DROP INDEX %v", quote(idxName))
+}
+
+func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{tableName, colName}
+ query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
+ " AND column_name = $2"
+ db.LogSQL(query, args)
+
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ return rows.Next(), nil
+}
+
+func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ // FIXME: the schema should be replaced by user custom's
+ args := []interface{}{tableName, "public"}
+ s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix ,
+ CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
+ CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
+FROM pg_attribute f
+ JOIN pg_class c ON c.oid = f.attrelid JOIN pg_type t ON t.oid = f.atttypid
+ LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
+ LEFT JOIN pg_class AS g ON p.confrelid = g.oid
+ LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
+WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var colName, isNullable, dataType string
+ var maxLenStr, colDefault, numPrecision, numRadix *string
+ var isPK, isUnique bool
+ err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix, &isPK, &isUnique)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ //fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, numPrecision, numRadix, isPK, isUnique)
+ var maxLen int
+ if maxLenStr != nil {
+ maxLen, err = strconv.Atoi(*maxLenStr)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ col.Name = strings.Trim(colName, `" `)
+
+ if colDefault != nil || isPK {
+ if isPK {
+ col.IsPrimaryKey = true
+ } else {
+ col.Default = *colDefault
+ }
+ }
+
+ if colDefault != nil && strings.HasPrefix(*colDefault, "nextval(") {
+ col.IsAutoIncrement = true
+ }
+
+ col.Nullable = (isNullable == "YES")
+
+ switch dataType {
+ case "character varying", "character":
+ col.SQLType = core.SQLType{core.Varchar, 0, 0}
+ case "timestamp without time zone":
+ col.SQLType = core.SQLType{core.DateTime, 0, 0}
+ case "timestamp with time zone":
+ col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
+ case "double precision":
+ col.SQLType = core.SQLType{core.Double, 0, 0}
+ case "boolean":
+ col.SQLType = core.SQLType{core.Bool, 0, 0}
+ case "time without time zone":
+ col.SQLType = core.SQLType{core.Time, 0, 0}
+ case "oid":
+ col.SQLType = core.SQLType{core.BigInt, 0, 0}
+ default:
+ col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0}
+ }
+ if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
+ return nil, nil, errors.New(fmt.Sprintf("unknow colType: %v", dataType))
+ }
+
+ col.Length = maxLen
+
+ 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)
+ }
+
+ return colSeq, cols, nil
+}
+
+func (db *postgres) GetTables() ([]*core.Table, error) {
+ // FIXME: replace public to user customrize schema
+ args := []interface{}{"public"}
+ s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1")
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name string
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, err
+ }
+ table.Name = name
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ // FIXME: replace the public schema to user specify schema
+ args := []interface{}{"public", tableName}
+ s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2")
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, indexdef string
+ var colNames []string
+ err = rows.Scan(&indexName, &indexdef)
+ if err != nil {
+ return nil, err
+ }
+ indexName = strings.Trim(indexName, `" `)
+ if strings.HasSuffix(indexName, "_pkey") {
+ continue
+ }
+ if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+ cs := strings.Split(indexdef, "(")
+ colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
+
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ newIdxName := indexName[5+len(tableName) : len(indexName)]
+ if newIdxName != "" {
+ indexName = newIdxName
+ }
+ }
+
+ index := &core.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
+ for _, colName := range colNames {
+ index.Cols = append(index.Cols, strings.Trim(colName, `" `))
+ }
+ indexes[index.Name] = index
+ }
+ return indexes, nil
+}
+
+func (db *postgres) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{"$", 1}}
+}
diff --git a/vendor/github.com/go-xorm/xorm/pq_driver.go b/vendor/github.com/go-xorm/xorm/pq_driver.go
new file mode 100644
index 0000000000..5d608f250a
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/pq_driver.go
@@ -0,0 +1,119 @@
+// Copyright 2015 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 (
+ "errors"
+ "fmt"
+ "net/url"
+ "sort"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+type pqDriver struct {
+}
+
+type values map[string]string
+
+func (vs values) Set(k, v string) {
+ vs[k] = v
+}
+
+func (vs values) Get(k string) (v string) {
+ return vs[k]
+}
+
+func errorf(s string, args ...interface{}) {
+ panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
+}
+
+func parseURL(connstr string) (string, error) {
+ u, err := url.Parse(connstr)
+ if err != nil {
+ return "", err
+ }
+
+ if u.Scheme != "postgresql" && u.Scheme != "postgres" {
+ return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
+ }
+
+ var kvs []string
+ escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
+ accrue := func(k, v string) {
+ if v != "" {
+ kvs = append(kvs, k+"="+escaper.Replace(v))
+ }
+ }
+
+ if u.User != nil {
+ v := u.User.Username()
+ accrue("user", v)
+
+ v, _ = u.User.Password()
+ accrue("password", v)
+ }
+
+ i := strings.Index(u.Host, ":")
+ if i < 0 {
+ accrue("host", u.Host)
+ } else {
+ accrue("host", u.Host[:i])
+ accrue("port", u.Host[i+1:])
+ }
+
+ if u.Path != "" {
+ accrue("dbname", u.Path[1:])
+ }
+
+ q := u.Query()
+ for k := range q {
+ accrue(k, q.Get(k))
+ }
+
+ sort.Strings(kvs) // Makes testing easier (not a performance concern)
+ return strings.Join(kvs, " "), nil
+}
+
+func parseOpts(name string, o values) {
+ if len(name) == 0 {
+ return
+ }
+
+ name = strings.TrimSpace(name)
+
+ ps := strings.Split(name, " ")
+ for _, p := range ps {
+ kv := strings.Split(p, "=")
+ if len(kv) < 2 {
+ errorf("invalid option: %q", p)
+ }
+ o.Set(kv[0], kv[1])
+ }
+}
+
+func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.POSTGRES}
+ o := make(values)
+ var err error
+ if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
+ dataSourceName, err = parseURL(dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+ }
+ parseOpts(dataSourceName, o)
+
+ db.DbName = o.Get("dbname")
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+ /*db.Schema = o.Get("schema")
+ if len(db.Schema) == 0 {
+ db.Schema = "public"
+ }*/
+ return db, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/processors.go b/vendor/github.com/go-xorm/xorm/processors.go
new file mode 100644
index 0000000000..8f95ae3be7
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/processors.go
@@ -0,0 +1,50 @@
+// Copyright 2015 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
+
+// Executed before an object is initially persisted to the database
+type BeforeInsertProcessor interface {
+ BeforeInsert()
+}
+
+// Executed before an object is updated
+type BeforeUpdateProcessor interface {
+ BeforeUpdate()
+}
+
+// Executed before an object is deleted
+type BeforeDeleteProcessor interface {
+ BeforeDelete()
+}
+
+type BeforeSetProcessor interface {
+ BeforeSet(string, Cell)
+}
+
+type AfterSetProcessor interface {
+ AfterSet(string, Cell)
+}
+
+// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
+//// Executed before an object is validated
+//type BeforeValidateProcessor interface {
+// BeforeValidate()
+//}
+// --
+
+// Executed after an object is persisted to the database
+type AfterInsertProcessor interface {
+ AfterInsert()
+}
+
+// Executed after an object has been updated
+type AfterUpdateProcessor interface {
+ AfterUpdate()
+}
+
+// Executed after an object has been deleted
+type AfterDeleteProcessor interface {
+ AfterDelete()
+}
diff --git a/vendor/github.com/go-xorm/xorm/rows.go b/vendor/github.com/go-xorm/xorm/rows.go
new file mode 100644
index 0000000000..d35040cdf2
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/rows.go
@@ -0,0 +1,146 @@
+// Copyright 2015 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 (
+ "database/sql"
+ "fmt"
+ "reflect"
+
+ "github.com/go-xorm/core"
+)
+
+// Rows rows wrapper a rows to
+type Rows struct {
+ NoTypeCheck bool
+
+ session *Session
+ stmt *core.Stmt
+ rows *core.Rows
+ fields []string
+ fieldsCount int
+ beanType reflect.Type
+ lastError error
+}
+
+func newRows(session *Session, bean interface{}) (*Rows, error) {
+ rows := new(Rows)
+ rows.session = session
+ rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type()
+
+ defer rows.session.resetStatement()
+
+ var sqlStr string
+ var args []interface{}
+
+ rows.session.Statement.setRefValue(rValue(bean))
+ if len(session.Statement.TableName()) <= 0 {
+ return nil, ErrTableNotFound
+ }
+
+ if rows.session.Statement.RawSQL == "" {
+ sqlStr, args = rows.session.Statement.genGetSQL(bean)
+ } else {
+ sqlStr = rows.session.Statement.RawSQL
+ args = rows.session.Statement.RawParams
+ }
+
+ for _, filter := range rows.session.Engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable)
+ }
+
+ rows.session.saveLastSQL(sqlStr, args...)
+ var err error
+ if rows.session.prepareStmt {
+ rows.stmt, err = rows.session.DB().Prepare(sqlStr)
+ if err != nil {
+ rows.lastError = err
+ rows.Close()
+ return nil, err
+ }
+
+ rows.rows, err = rows.stmt.Query(args...)
+ if err != nil {
+ rows.lastError = err
+ rows.Close()
+ return nil, err
+ }
+ } else {
+ rows.rows, err = rows.session.DB().Query(sqlStr, args...)
+ if err != nil {
+ rows.lastError = err
+ rows.Close()
+ return nil, err
+ }
+ }
+
+ rows.fields, err = rows.rows.Columns()
+ if err != nil {
+ rows.lastError = err
+ rows.Close()
+ return nil, err
+ }
+ rows.fieldsCount = len(rows.fields)
+
+ return rows, nil
+}
+
+// Next move cursor to next record, return false if end has reached
+func (rows *Rows) Next() bool {
+ if rows.lastError == nil && rows.rows != nil {
+ hasNext := rows.rows.Next()
+ if !hasNext {
+ rows.lastError = sql.ErrNoRows
+ }
+ return hasNext
+ }
+ return false
+}
+
+// Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close.
+func (rows *Rows) Err() error {
+ return rows.lastError
+}
+
+// Scan row record to bean properties
+func (rows *Rows) Scan(bean interface{}) error {
+ if rows.lastError != nil {
+ return rows.lastError
+ }
+
+ if !rows.NoTypeCheck && reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType {
+ return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType)
+ }
+
+ return rows.session.row2Bean(rows.rows, rows.fields, rows.fieldsCount, bean)
+}
+
+// Close session if session.IsAutoClose is true, and claimed any opened resources
+func (rows *Rows) Close() error {
+ if rows.session.IsAutoClose {
+ defer rows.session.Close()
+ }
+
+ if rows.lastError == nil {
+ if rows.rows != nil {
+ rows.lastError = rows.rows.Close()
+ if rows.lastError != nil {
+ defer rows.stmt.Close()
+ return rows.lastError
+ }
+ }
+ if rows.stmt != nil {
+ rows.lastError = rows.stmt.Close()
+ }
+ } else {
+ if rows.stmt != nil {
+ defer rows.stmt.Close()
+ }
+ if rows.rows != nil {
+ defer rows.rows.Close()
+ }
+ }
+ return rows.lastError
+}
diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go
new file mode 100644
index 0000000000..5495756283
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/session.go
@@ -0,0 +1,4144 @@
+// Copyright 2015 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 (
+ "database/sql"
+ "database/sql/driver"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/go-xorm/builder"
+ "github.com/go-xorm/core"
+)
+
+// Session keep a pointer to sql.DB and provides all execution of all
+// kind of database operations.
+type Session struct {
+ db *core.DB
+ Engine *Engine
+ Tx *core.Tx
+ Statement Statement
+ IsAutoCommit bool
+ IsCommitedOrRollbacked bool
+ TransType string
+ IsAutoClose bool
+
+ // Automatically reset the statement after operations that execute a SQL
+ // query such as Count(), Find(), Get(), ...
+ AutoResetStatement bool
+
+ // !nashtsai! storing these beans due to yet committed tx
+ afterInsertBeans map[interface{}]*[]func(interface{})
+ afterUpdateBeans map[interface{}]*[]func(interface{})
+ afterDeleteBeans map[interface{}]*[]func(interface{})
+ // --
+
+ beforeClosures []func(interface{})
+ afterClosures []func(interface{})
+
+ prepareStmt bool
+ stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
+ cascadeDeep int
+
+ // !evalphobia! stored the last executed query on this session
+ //beforeSQLExec func(string, ...interface{})
+ lastSQL string
+ lastSQLArgs []interface{}
+}
+
+// Clone copy all the session's content and return a new session
+func (session *Session) Clone() *Session {
+ var sess = *session
+ return &sess
+}
+
+// Init reset the session as the init status.
+func (session *Session) Init() {
+ session.Statement.Init()
+ session.Statement.Engine = session.Engine
+ session.IsAutoCommit = true
+ session.IsCommitedOrRollbacked = false
+ session.IsAutoClose = false
+ session.AutoResetStatement = true
+ session.prepareStmt = false
+
+ // !nashtsai! is lazy init better?
+ session.afterInsertBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.afterUpdateBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.beforeClosures = make([]func(interface{}), 0)
+ session.afterClosures = make([]func(interface{}), 0)
+
+ session.lastSQL = ""
+ session.lastSQLArgs = []interface{}{}
+}
+
+// Close release the connection from pool
+func (session *Session) Close() {
+ for _, v := range session.stmtCache {
+ v.Close()
+ }
+
+ if session.db != nil {
+ // When Close be called, if session is a transaction and do not call
+ // Commit or Rollback, then call Rollback.
+ if session.Tx != nil && !session.IsCommitedOrRollbacked {
+ session.Rollback()
+ }
+ session.Tx = nil
+ session.stmtCache = nil
+ session.Init()
+ session.db = nil
+ }
+}
+
+func (session *Session) resetStatement() {
+ if session.AutoResetStatement {
+ session.Statement.Init()
+ }
+}
+
+// Prepare set a flag to session that should be prepare statment before execute query
+func (session *Session) Prepare() *Session {
+ session.prepareStmt = true
+ return session
+}
+
+// Sql !DEPRECIATED! will be deprecated, please use SQL instead.
+func (session *Session) Sql(query string, args ...interface{}) *Session {
+ return session.SQL(query, args...)
+}
+
+// SQL provides raw sql input parameter. When you have a complex SQL statement
+// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
+func (session *Session) SQL(query interface{}, args ...interface{}) *Session {
+ session.Statement.SQL(query, args...)
+ return session
+}
+
+// Where provides custom query condition.
+func (session *Session) Where(query interface{}, args ...interface{}) *Session {
+ session.Statement.Where(query, args...)
+ return session
+}
+
+// And provides custom query condition.
+func (session *Session) And(query interface{}, args ...interface{}) *Session {
+ session.Statement.And(query, args...)
+ return session
+}
+
+// Or provides custom query condition.
+func (session *Session) Or(query interface{}, args ...interface{}) *Session {
+ session.Statement.Or(query, args...)
+ return session
+}
+
+// Id will be deprecated, please use ID instead
+func (session *Session) Id(id interface{}) *Session {
+ session.Statement.Id(id)
+ return session
+}
+
+// ID provides converting id as a query condition
+func (session *Session) ID(id interface{}) *Session {
+ session.Statement.Id(id)
+ return session
+}
+
+// Before Apply before Processor, affected bean is passed to closure arg
+func (session *Session) Before(closures func(interface{})) *Session {
+ if closures != nil {
+ session.beforeClosures = append(session.beforeClosures, closures)
+ }
+ return session
+}
+
+// After Apply after Processor, affected bean is passed to closure arg
+func (session *Session) After(closures func(interface{})) *Session {
+ if closures != nil {
+ session.afterClosures = append(session.afterClosures, closures)
+ }
+ return session
+}
+
+// Table can input a string or pointer to struct for special a table to operate.
+func (session *Session) Table(tableNameOrBean interface{}) *Session {
+ session.Statement.Table(tableNameOrBean)
+ return session
+}
+
+// Alias set the table alias
+func (session *Session) Alias(alias string) *Session {
+ session.Statement.Alias(alias)
+ return session
+}
+
+// In provides a query string like "id in (1, 2, 3)"
+func (session *Session) In(column string, args ...interface{}) *Session {
+ session.Statement.In(column, args...)
+ return session
+}
+
+// NotIn provides a query string like "id in (1, 2, 3)"
+func (session *Session) NotIn(column string, args ...interface{}) *Session {
+ session.Statement.NotIn(column, args...)
+ return session
+}
+
+// Incr provides a query string like "count = count + 1"
+func (session *Session) Incr(column string, arg ...interface{}) *Session {
+ session.Statement.Incr(column, arg...)
+ return session
+}
+
+// Decr provides a query string like "count = count - 1"
+func (session *Session) Decr(column string, arg ...interface{}) *Session {
+ session.Statement.Decr(column, arg...)
+ return session
+}
+
+// SetExpr provides a query string like "column = {expression}"
+func (session *Session) SetExpr(column string, expression string) *Session {
+ session.Statement.SetExpr(column, expression)
+ return session
+}
+
+// Select provides some columns to special
+func (session *Session) Select(str string) *Session {
+ session.Statement.Select(str)
+ return session
+}
+
+// Cols provides some columns to special
+func (session *Session) Cols(columns ...string) *Session {
+ session.Statement.Cols(columns...)
+ return session
+}
+
+// AllCols ask all columns
+func (session *Session) AllCols() *Session {
+ session.Statement.AllCols()
+ return session
+}
+
+// MustCols specify some columns must use even if they are empty
+func (session *Session) MustCols(columns ...string) *Session {
+ session.Statement.MustCols(columns...)
+ return session
+}
+
+// NoCascade indicate that no cascade load child object
+func (session *Session) NoCascade() *Session {
+ session.Statement.UseCascade = false
+ return session
+}
+
+// UseBool automatically retrieve condition according struct, but
+// if struct has bool field, it will ignore them. So use UseBool
+// to tell system to do not ignore them.
+// If no paramters, it will use all the bool field of struct, or
+// it will use paramters's columns
+func (session *Session) UseBool(columns ...string) *Session {
+ session.Statement.UseBool(columns...)
+ return session
+}
+
+// Distinct use for distinct columns. Caution: when you are using cache,
+// distinct will not be cached because cache system need id,
+// but distinct will not provide id
+func (session *Session) Distinct(columns ...string) *Session {
+ session.Statement.Distinct(columns...)
+ return session
+}
+
+// ForUpdate Set Read/Write locking for UPDATE
+func (session *Session) ForUpdate() *Session {
+ session.Statement.IsForUpdate = true
+ return session
+}
+
+// Omit Only not use the paramters as select or update columns
+func (session *Session) Omit(columns ...string) *Session {
+ session.Statement.Omit(columns...)
+ return session
+}
+
+// Nullable Set null when column is zero-value and nullable for update
+func (session *Session) Nullable(columns ...string) *Session {
+ session.Statement.Nullable(columns...)
+ return session
+}
+
+// NoAutoTime means do not automatically give created field and updated field
+// the current time on the current session temporarily
+func (session *Session) NoAutoTime() *Session {
+ session.Statement.UseAutoTime = false
+ return session
+}
+
+// NoAutoCondition disable generate SQL condition from beans
+func (session *Session) NoAutoCondition(no ...bool) *Session {
+ session.Statement.NoAutoCondition(no...)
+ return session
+}
+
+// Limit provide limit and offset query condition
+func (session *Session) Limit(limit int, start ...int) *Session {
+ session.Statement.Limit(limit, start...)
+ return session
+}
+
+// OrderBy provide order by query condition, the input parameter is the content
+// after order by on a sql statement.
+func (session *Session) OrderBy(order string) *Session {
+ session.Statement.OrderBy(order)
+ return session
+}
+
+// Desc provide desc order by query condition, the input parameters are columns.
+func (session *Session) Desc(colNames ...string) *Session {
+ session.Statement.Desc(colNames...)
+ return session
+}
+
+// Asc provide asc order by query condition, the input parameters are columns.
+func (session *Session) Asc(colNames ...string) *Session {
+ session.Statement.Asc(colNames...)
+ return session
+}
+
+// StoreEngine is only avialble mysql dialect currently
+func (session *Session) StoreEngine(storeEngine string) *Session {
+ session.Statement.StoreEngine = storeEngine
+ return session
+}
+
+// Charset is only avialble mysql dialect currently
+func (session *Session) Charset(charset string) *Session {
+ session.Statement.Charset = charset
+ return session
+}
+
+// Cascade indicates if loading sub Struct
+func (session *Session) Cascade(trueOrFalse ...bool) *Session {
+ if len(trueOrFalse) >= 1 {
+ session.Statement.UseCascade = trueOrFalse[0]
+ }
+ return session
+}
+
+// NoCache ask this session do not retrieve data from cache system and
+// get data from database directly.
+func (session *Session) NoCache() *Session {
+ session.Statement.UseCache = false
+ return session
+}
+
+// Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
+ session.Statement.Join(joinOperator, tablename, condition, args...)
+ return session
+}
+
+// GroupBy Generate Group By statement
+func (session *Session) GroupBy(keys string) *Session {
+ session.Statement.GroupBy(keys)
+ return session
+}
+
+// Having Generate Having statement
+func (session *Session) Having(conditions string) *Session {
+ session.Statement.Having(conditions)
+ return session
+}
+
+// DB db return the wrapper of sql.DB
+func (session *Session) DB() *core.DB {
+ if session.db == nil {
+ session.db = session.Engine.db
+ session.stmtCache = make(map[uint32]*core.Stmt, 0)
+ }
+ return session.db
+}
+
+// Cond return session's conditions
+func (session *Session) Conds() builder.Cond {
+ return session.Statement.cond
+}
+
+// Begin a transaction
+func (session *Session) Begin() error {
+ if session.IsAutoCommit {
+ tx, err := session.DB().Begin()
+ if err != nil {
+ return err
+ }
+ session.IsAutoCommit = false
+ session.IsCommitedOrRollbacked = false
+ session.Tx = tx
+ session.saveLastSQL("BEGIN TRANSACTION")
+ }
+ return nil
+}
+
+// Rollback When using transaction, you can rollback if any error
+func (session *Session) Rollback() error {
+ if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
+ session.saveLastSQL(session.Engine.dialect.RollBackStr())
+ session.IsCommitedOrRollbacked = true
+ return session.Tx.Rollback()
+ }
+ return nil
+}
+
+// Commit When using transaction, Commit will commit all operations.
+func (session *Session) Commit() error {
+ if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
+ session.saveLastSQL("COMMIT")
+ session.IsCommitedOrRollbacked = true
+ var err error
+ if err = session.Tx.Commit(); err == nil {
+ // handle processors after tx committed
+
+ closureCallFunc := func(closuresPtr *[]func(interface{}), bean interface{}) {
+
+ if closuresPtr != nil {
+ for _, closure := range *closuresPtr {
+ closure(bean)
+ }
+ }
+ }
+
+ for bean, closuresPtr := range session.afterInsertBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ }
+ for bean, closuresPtr := range session.afterUpdateBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
+ processor.AfterUpdate()
+ }
+ }
+ for bean, closuresPtr := range session.afterDeleteBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
+ processor.AfterDelete()
+ }
+ }
+ cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) {
+ if len(*slices) > 0 {
+ *slices = make(map[interface{}]*[]func(interface{}), 0)
+ }
+ }
+ cleanUpFunc(&session.afterInsertBeans)
+ cleanUpFunc(&session.afterUpdateBeans)
+ cleanUpFunc(&session.afterDeleteBeans)
+ }
+ return err
+ }
+ return nil
+}
+
+func cleanupProcessorsClosures(slices *[]func(interface{})) {
+ if len(*slices) > 0 {
+ *slices = make([]func(interface{}), 0)
+ }
+}
+
+func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
+ dataStruct := rValue(obj)
+ if dataStruct.Kind() != reflect.Struct {
+ return errors.New("Expected a pointer to a struct")
+ }
+
+ var col *core.Column
+ session.Statement.setRefValue(dataStruct)
+ table := session.Statement.RefTable
+ tableName := session.Statement.tableName
+
+ for key, data := range objMap {
+ if col = table.GetColumn(key); col == nil {
+ session.Engine.logger.Warnf("struct %v's has not field %v. %v",
+ table.Type.Name(), key, table.ColumnsSeq())
+ continue
+ }
+
+ fieldName := col.FieldName
+ fieldPath := strings.Split(fieldName, ".")
+ var fieldValue reflect.Value
+ if len(fieldPath) > 2 {
+ session.Engine.logger.Error("Unsupported mutliderive", fieldName)
+ continue
+ } else if len(fieldPath) == 2 {
+ parentField := dataStruct.FieldByName(fieldPath[0])
+ if parentField.IsValid() {
+ fieldValue = parentField.FieldByName(fieldPath[1])
+ }
+ } else {
+ fieldValue = dataStruct.FieldByName(fieldName)
+ }
+ if !fieldValue.IsValid() || !fieldValue.CanSet() {
+ session.Engine.logger.Warnf("table %v's column %v is not valid or cannot set", tableName, key)
+ continue
+ }
+
+ err := session.bytes2Value(col, &fieldValue, data)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Execute sql
+func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) {
+ if session.prepareStmt {
+ stmt, err := session.doPrepare(sqlStr)
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := stmt.Exec(args...)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+ }
+
+ return session.DB().Exec(sqlStr, args...)
+}
+
+func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) {
+ for _, filter := range session.Engine.dialect.Filters() {
+ // TODO: for table name, it's no need to RefTable
+ sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
+ }
+
+ session.saveLastSQL(sqlStr, args...)
+
+ return session.Engine.logSQLExecutionTime(sqlStr, args, func() (sql.Result, error) {
+ if session.IsAutoCommit {
+ // FIXME: oci8 can not auto commit (github.com/mattn/go-oci8)
+ if session.Engine.dialect.DBType() == core.ORACLE {
+ session.Begin()
+ r, err := session.Tx.Exec(sqlStr, args...)
+ session.Commit()
+ return r, err
+ }
+ return session.innerExec(sqlStr, args...)
+ }
+ return session.Tx.Exec(sqlStr, args...)
+ })
+}
+
+// Exec raw sql
+func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ return session.exec(sqlStr, args...)
+}
+
+// CreateTable create a table according a bean
+func (session *Session) CreateTable(bean interface{}) error {
+ v := rValue(bean)
+ session.Statement.setRefValue(v)
+
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ return session.createOneTable()
+}
+
+// CreateIndexes create indexes
+func (session *Session) CreateIndexes(bean interface{}) error {
+ v := rValue(bean)
+ session.Statement.setRefValue(v)
+
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ sqls := session.Statement.genIndexSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// CreateUniques create uniques
+func (session *Session) CreateUniques(bean interface{}) error {
+ v := rValue(bean)
+ session.Statement.setRefValue(v)
+
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ sqls := session.Statement.genUniqueSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (session *Session) createOneTable() error {
+ sqlStr := session.Statement.genCreateTableSQL()
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+// to be deleted
+func (session *Session) createAll() error {
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ for _, table := range session.Engine.Tables {
+ session.Statement.RefTable = table
+ session.Statement.tableName = table.Name
+ err := session.createOneTable()
+ session.resetStatement()
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DropIndexes drop indexes
+func (session *Session) DropIndexes(bean interface{}) error {
+ v := rValue(bean)
+ session.Statement.setRefValue(v)
+
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ sqls := session.Statement.genDelIndexSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DropTable drop table will drop table if exist, if drop failed, it will return error
+func (session *Session) DropTable(beanOrTableName interface{}) error {
+ tableName, err := session.Engine.tableName(beanOrTableName)
+ if err != nil {
+ return err
+ }
+
+ var needDrop = true
+ if !session.Engine.dialect.SupportDropIfExists() {
+ sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)
+ results, err := session.query(sqlStr, args...)
+ if err != nil {
+ return err
+ }
+ needDrop = len(results) > 0
+ }
+
+ if needDrop {
+ sqlStr := session.Engine.Dialect().DropTableSql(tableName)
+ _, err = session.exec(sqlStr)
+ return err
+ }
+ return nil
+}
+
+func (session *Session) canCache() bool {
+ if session.Statement.RefTable == nil ||
+ session.Statement.JoinStr != "" ||
+ session.Statement.RawSQL != "" ||
+ !session.Statement.UseCache ||
+ session.Tx != nil ||
+ len(session.Statement.selectStr) > 0 {
+ return false
+ }
+ return true
+}
+
+func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
+ // if has no reftable, then don't use cache currently
+ if !session.canCache() {
+ return false, ErrCacheFailed
+ }
+
+ for _, filter := range session.Engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
+ }
+ newsql := session.Statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return false, ErrCacheFailed
+ }
+
+ cacher := session.Engine.getCacher2(session.Statement.RefTable)
+ tableName := session.Statement.TableName()
+ session.Engine.logger.Debug("[cacheGet] find sql:", newsql, args)
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ table := session.Statement.RefTable
+ if err != nil {
+ var res = make([]string, len(table.PrimaryKeys))
+ rows, err := session.DB().Query(newsql, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ return false, ErrCacheFailed
+ }
+
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ if col.SQLType.IsText() {
+ pk[i] = res[i]
+ } else if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(res[i], 10, 64)
+ if err != nil {
+ return false, err
+ }
+ pk[i] = n
+ } else {
+ return false, errors.New("unsupported")
+ }
+ }
+
+ ids = []core.PK{pk}
+ session.Engine.logger.Debug("[cacheGet] cache ids:", newsql, ids)
+ err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ session.Engine.logger.Debug("[cacheGet] cache hit sql:", newsql)
+ }
+
+ if len(ids) > 0 {
+ structValue := reflect.Indirect(reflect.ValueOf(bean))
+ id := ids[0]
+ session.Engine.logger.Debug("[cacheGet] get bean:", tableName, id)
+ sid, err := id.ToString()
+ if err != nil {
+ return false, err
+ }
+ cacheBean := cacher.GetBean(tableName, sid)
+ if cacheBean == nil {
+ /*newSession := session.Engine.NewSession()
+ defer newSession.Close()
+ cacheBean = reflect.New(structValue.Type()).Interface()
+ newSession.Id(id).NoCache()
+ if session.Statement.AltTableName != "" {
+ newSession.Table(session.Statement.AltTableName)
+ }
+ if !session.Statement.UseCascade {
+ newSession.NoCascade()
+ }
+ has, err = newSession.Get(cacheBean)
+ */
+ cacheBean = bean
+ has, err = session.nocacheGet(cacheBean, sqlStr, args...)
+ if err != nil || !has {
+ return has, err
+ }
+
+ session.Engine.logger.Debug("[cacheGet] cache bean:", tableName, id, cacheBean)
+ cacher.PutBean(tableName, sid, cacheBean)
+ } else {
+ session.Engine.logger.Debug("[cacheGet] cache hit bean:", tableName, id, cacheBean)
+ has = true
+ }
+ structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean)))
+
+ return has, nil
+ }
+ return false, nil
+}
+
+func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) {
+ if !session.canCache() ||
+ indexNoCase(sqlStr, "having") != -1 ||
+ indexNoCase(sqlStr, "group by") != -1 {
+ return ErrCacheFailed
+ }
+
+ for _, filter := range session.Engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
+ }
+
+ newsql := session.Statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+
+ tableName := session.Statement.TableName()
+
+ table := session.Statement.RefTable
+ cacher := session.Engine.getCacher2(table)
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ if err != nil {
+ rows, err := session.DB().Query(newsql, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ var i int
+ ids = make([]core.PK, 0)
+ for rows.Next() {
+ i++
+ if i > 500 {
+ session.Engine.logger.Debug("[cacheFind] ids length > 500, no cache")
+ return ErrCacheFailed
+ }
+ var res = make([]string, len(table.PrimaryKeys))
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return err
+ }
+
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(res[i], 10, 64)
+ if err != nil {
+ return err
+ }
+ pk[i] = n
+ } else if col.SQLType.IsText() {
+ pk[i] = res[i]
+ } else {
+ return errors.New("not supported")
+ }
+ }
+
+ ids = append(ids, pk)
+ }
+
+ session.Engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, newsql, args)
+ err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
+ if err != nil {
+ return err
+ }
+ } else {
+ session.Engine.logger.Debug("[cacheFind] cache hit sql:", newsql, args)
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+
+ ididxes := make(map[string]int)
+ var ides []core.PK
+ var temps = make([]interface{}, len(ids))
+
+ for idx, id := range ids {
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ bean := cacher.GetBean(tableName, sid)
+ if bean == nil {
+ ides = append(ides, id)
+ ididxes[sid] = idx
+ } else {
+ session.Engine.logger.Debug("[cacheFind] cache hit bean:", tableName, id, bean)
+
+ pk := session.Engine.IdOf(bean)
+ xid, err := pk.ToString()
+ if err != nil {
+ return err
+ }
+
+ if sid != xid {
+ session.Engine.logger.Error("[cacheFind] error cache", xid, sid, bean)
+ return ErrCacheFailed
+ }
+ temps[idx] = bean
+ }
+ }
+
+ if len(ides) > 0 {
+ newSession := session.Engine.NewSession()
+ defer newSession.Close()
+
+ slices := reflect.New(reflect.SliceOf(t))
+ beans := slices.Interface()
+
+ if len(table.PrimaryKeys) == 1 {
+ ff := make([]interface{}, 0, len(ides))
+ for _, ie := range ides {
+ ff = append(ff, ie[0])
+ }
+
+ newSession.In("`"+table.PrimaryKeys[0]+"`", ff...)
+ } else {
+ for _, ie := range ides {
+ cond := builder.NewCond()
+ for i, name := range table.PrimaryKeys {
+ cond = cond.And(builder.Eq{"`" + name + "`": ie[i]})
+ }
+ newSession.Or(cond)
+ }
+ }
+
+ err = newSession.NoCache().Find(beans)
+ if err != nil {
+ return err
+ }
+
+ vs := reflect.Indirect(reflect.ValueOf(beans))
+ for i := 0; i < vs.Len(); i++ {
+ rv := vs.Index(i)
+ if rv.Kind() != reflect.Ptr {
+ rv = rv.Addr()
+ }
+ id := session.Engine.IdOfV(rv)
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+
+ bean := rv.Interface()
+ temps[ididxes[sid]] = bean
+ session.Engine.logger.Debug("[cacheFind] cache bean:", tableName, id, bean, temps)
+ cacher.PutBean(tableName, sid, bean)
+ }
+ }
+
+ for j := 0; j < len(temps); j++ {
+ bean := temps[j]
+ if bean == nil {
+ session.Engine.logger.Warn("[cacheFind] cache no hit:", tableName, ids[j], temps)
+ // return errors.New("cache error") // !nashtsai! no need to return error, but continue instead
+ continue
+ }
+ if sliceValue.Kind() == reflect.Slice {
+ if t.Kind() == reflect.Ptr {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(bean)))
+ } else {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean))))
+ }
+ } else if sliceValue.Kind() == reflect.Map {
+ var key = ids[j]
+ keyType := sliceValue.Type().Key()
+ var ikey interface{}
+ if len(key) == 1 {
+ ikey, err = str2PK(fmt.Sprintf("%v", key[0]), keyType)
+ if err != nil {
+ return err
+ }
+ } else {
+ if keyType.Kind() != reflect.Slice {
+ return errors.New("table have multiple primary keys, key is not core.PK or slice")
+ }
+ ikey = key
+ }
+
+ if t.Kind() == reflect.Ptr {
+ sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean))
+ } else {
+ sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean)))
+ }
+ }
+ }
+
+ return nil
+}
+
+// IterFunc only use by Iterate
+type IterFunc func(idx int, bean interface{}) error
+
+// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
+// are conditions.
+func (session *Session) Rows(bean interface{}) (*Rows, error) {
+ return newRows(session, bean)
+}
+
+// Iterate record by record handle records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
+ rows, err := session.Rows(bean)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ i := 0
+ for rows.Next() {
+ b := reflect.New(rows.beanType).Interface()
+ err = rows.Scan(b)
+ if err != nil {
+ return err
+ }
+ err = fun(i, b)
+ if err != nil {
+ return err
+ }
+ i++
+ }
+ return err
+}
+
+func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) {
+ crc := crc32.ChecksumIEEE([]byte(sqlStr))
+ // TODO try hash(sqlStr+len(sqlStr))
+ var has bool
+ stmt, has = session.stmtCache[crc]
+ if !has {
+ stmt, err = session.DB().Prepare(sqlStr)
+ if err != nil {
+ return nil, err
+ }
+ session.stmtCache[crc] = stmt
+ }
+ return
+}
+
+func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
+ var rawRows *core.Rows
+ var err error
+ session.queryPreprocess(&sqlStr, args...)
+ if session.IsAutoCommit {
+ _, rawRows, err = session.innerQuery(sqlStr, args...)
+ } else {
+ rawRows, err = session.Tx.Query(sqlStr, args...)
+ }
+ if err != nil {
+ return false, err
+ }
+
+ defer rawRows.Close()
+
+ if rawRows.Next() {
+ if fields, err := rawRows.Columns(); err == nil {
+ err = session.row2Bean(rawRows, fields, len(fields), bean)
+ }
+ return true, err
+ }
+ return false, nil
+}
+
+// Get retrieve one record from database, bean's non-empty fields
+// will be as conditions
+func (session *Session) Get(bean interface{}) (bool, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ session.Statement.setRefValue(rValue(bean))
+
+ var sqlStr string
+ var args []interface{}
+
+ if session.Statement.RawSQL == "" {
+ if len(session.Statement.TableName()) <= 0 {
+ return false, ErrTableNotFound
+ }
+ session.Statement.Limit(1)
+ sqlStr, args = session.Statement.genGetSQL(bean)
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ if session.canCache() {
+ if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil &&
+ !session.Statement.unscoped {
+ has, err := session.cacheGet(bean, sqlStr, args...)
+ if err != ErrCacheFailed {
+ return has, err
+ }
+ }
+ }
+
+ return session.nocacheGet(bean, sqlStr, args...)
+}
+
+// Count counts the records. bean's non-empty fields
+// are conditions.
+func (session *Session) Count(bean interface{}) (int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ var sqlStr string
+ var args []interface{}
+ if session.Statement.RawSQL == "" {
+ sqlStr, args = session.Statement.genCountSQL(bean)
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ var err error
+ var total int64
+ if session.IsAutoCommit {
+ err = session.DB().QueryRow(sqlStr, args...).Scan(&total)
+ } else {
+ err = session.Tx.QueryRow(sqlStr, args...).Scan(&total)
+ }
+ if err != nil {
+ return 0, err
+ }
+
+ return total, nil
+}
+
+// Sum call sum some column. bean's non-empty fields are conditions.
+func (session *Session) Sum(bean interface{}, columnName string) (float64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ var sqlStr string
+ var args []interface{}
+ if len(session.Statement.RawSQL) == 0 {
+ sqlStr, args = session.Statement.genSumSQL(bean, columnName)
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ var err error
+ var res float64
+ if session.IsAutoCommit {
+ err = session.DB().QueryRow(sqlStr, args...).Scan(&res)
+ } else {
+ err = session.Tx.QueryRow(sqlStr, args...).Scan(&res)
+ }
+ if err != nil {
+ return 0, err
+ }
+
+ return res, nil
+}
+
+// Sums call sum some columns. bean's non-empty fields are conditions.
+func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ var sqlStr string
+ var args []interface{}
+ if len(session.Statement.RawSQL) == 0 {
+ sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ var err error
+ var res = make([]float64, len(columnNames), len(columnNames))
+ if session.IsAutoCommit {
+ err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
+ } else {
+ err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+// SumsInt sum specify columns and return as []int64 instead of []float64
+func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ var sqlStr string
+ var args []interface{}
+ if len(session.Statement.RawSQL) == 0 {
+ sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ var err error
+ var res = make([]int64, 0, len(columnNames))
+ if session.IsAutoCommit {
+ err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
+ } else {
+ err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+func (session *Session) noCacheFind(sliceValue reflect.Value, sqlStr string, args ...interface{}) error {
+ var rawRows *core.Rows
+ var err error
+
+ session.queryPreprocess(&sqlStr, args...)
+ if session.IsAutoCommit {
+ _, rawRows, err = session.innerQuery(sqlStr, args...)
+ } else {
+ rawRows, err = session.Tx.Query(sqlStr, args...)
+ }
+ if err != nil {
+ return err
+ }
+ defer rawRows.Close()
+
+ fields, err := rawRows.Columns()
+ if err != nil {
+ return err
+ }
+
+ var newElemFunc func() reflect.Value
+ sliceElementType := sliceValue.Type().Elem()
+ if sliceElementType.Kind() == reflect.Ptr {
+ newElemFunc = func() reflect.Value {
+ return reflect.New(sliceElementType.Elem())
+ }
+ } else {
+ newElemFunc = func() reflect.Value {
+ return reflect.New(sliceElementType)
+ }
+ }
+
+ var sliceValueSetFunc func(*reflect.Value)
+
+ if sliceValue.Kind() == reflect.Slice {
+ if sliceElementType.Kind() == reflect.Ptr {
+ sliceValueSetFunc = func(newValue *reflect.Value) {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface())))
+ }
+ } else {
+ sliceValueSetFunc = func(newValue *reflect.Value) {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface()))))
+ }
+ }
+ }
+
+ var newValue = newElemFunc()
+ dataStruct := rValue(newValue.Interface())
+ if dataStruct.Kind() != reflect.Struct {
+ return errors.New("Expected a pointer to a struct")
+ }
+
+ return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, sliceValueSetFunc)
+}
+
+// Find retrieve records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map {
+ return errors.New("needs a pointer to a slice or a map")
+ }
+
+ sliceElementType := sliceValue.Type().Elem()
+
+ if session.Statement.RefTable == nil {
+ if sliceElementType.Kind() == reflect.Ptr {
+ if sliceElementType.Elem().Kind() == reflect.Struct {
+ pv := reflect.New(sliceElementType.Elem())
+ session.Statement.setRefValue(pv.Elem())
+ } else {
+ return errors.New("slice type")
+ }
+ } else if sliceElementType.Kind() == reflect.Struct {
+ pv := reflect.New(sliceElementType)
+ session.Statement.setRefValue(pv.Elem())
+ } else {
+ return errors.New("slice type")
+ }
+ }
+
+ var table = session.Statement.RefTable
+
+ var addedTableName = (len(session.Statement.JoinStr) > 0)
+ var autoCond builder.Cond
+ if !session.Statement.noAutoCondition && len(condiBean) > 0 {
+ var err error
+ autoCond, err = session.Statement.buildConds(table, condiBean[0], true, true, false, true, addedTableName)
+ if err != nil {
+ panic(err)
+ }
+ } else {
+ // !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
+ // See https://github.com/go-xorm/xorm/issues/179
+ if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled
+ var colName = session.Engine.Quote(col.Name)
+ if addedTableName {
+ var nm = session.Statement.TableName()
+ if len(session.Statement.TableAlias) > 0 {
+ nm = session.Statement.TableAlias
+ }
+ colName = session.Engine.Quote(nm) + "." + colName
+ }
+ autoCond = builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"})
+ }
+ }
+
+ var sqlStr string
+ var args []interface{}
+ if session.Statement.RawSQL == "" {
+ if len(session.Statement.TableName()) <= 0 {
+ return ErrTableNotFound
+ }
+
+ var columnStr = session.Statement.ColumnStr
+ if len(session.Statement.selectStr) > 0 {
+ columnStr = session.Statement.selectStr
+ } else {
+ if session.Statement.JoinStr == "" {
+ if columnStr == "" {
+ if session.Statement.GroupByStr != "" {
+ columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
+ } else {
+ columnStr = session.Statement.genColumnStr()
+ }
+ }
+ } else {
+ if columnStr == "" {
+ if session.Statement.GroupByStr != "" {
+ columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
+ } else {
+ columnStr = "*"
+ }
+ }
+ }
+ }
+
+ condSQL, condArgs, _ := builder.ToSQL(session.Statement.cond.And(autoCond))
+
+ args = append(session.Statement.joinArgs, condArgs...)
+ sqlStr = session.Statement.genSelectSQL(columnStr, condSQL)
+ // for mssql and use limit
+ qs := strings.Count(sqlStr, "?")
+ if len(args)*2 == qs {
+ args = append(args, args...)
+ }
+ } else {
+ sqlStr = session.Statement.RawSQL
+ args = session.Statement.RawParams
+ }
+
+ var err error
+ if session.canCache() {
+ if cacher := session.Engine.getCacher2(table); cacher != nil &&
+ !session.Statement.IsDistinct &&
+ !session.Statement.unscoped {
+ err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
+ if err != ErrCacheFailed {
+ return err
+ }
+ err = nil // !nashtsai! reset err to nil for ErrCacheFailed
+ session.Engine.logger.Warn("Cache Find Failed")
+ }
+ }
+
+ if sliceValue.Kind() != reflect.Map {
+ return session.noCacheFind(sliceValue, sqlStr, args...)
+ }
+
+ resultsSlice, err := session.query(sqlStr, args...)
+ if err != nil {
+ return err
+ }
+
+ keyType := sliceValue.Type().Key()
+
+ for _, results := range resultsSlice {
+ var newValue reflect.Value
+ if sliceElementType.Kind() == reflect.Ptr {
+ newValue = reflect.New(sliceElementType.Elem())
+ } else {
+ newValue = reflect.New(sliceElementType)
+ }
+ err := session.scanMapIntoStruct(newValue.Interface(), results)
+ if err != nil {
+ return err
+ }
+ var key interface{}
+ // if there is only one pk, we can put the id as map key.
+ if len(table.PrimaryKeys) == 1 {
+ key, err = str2PK(string(results[table.PrimaryKeys[0]]), keyType)
+ if err != nil {
+ return err
+ }
+ } else {
+ if keyType.Kind() != reflect.Slice {
+ panic("don't support multiple primary key's map has non-slice key type")
+ } else {
+ var keys core.PK = make([]interface{}, 0, len(table.PrimaryKeys))
+ for _, pk := range table.PrimaryKeys {
+ skey, err := str2PK(string(results[pk]), keyType)
+ if err != nil {
+ return err
+ }
+ keys = append(keys, skey)
+ }
+ key = keys
+ }
+ }
+
+ if sliceElementType.Kind() == reflect.Ptr {
+ sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValue.Interface()))
+ } else {
+ sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.Indirect(reflect.ValueOf(newValue.Interface())))
+ }
+ }
+
+ return nil
+}
+
+// Ping test if database is ok
+func (session *Session) Ping() error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ return session.DB().Ping()
+}
+
+// IsTableExist if a table is exist
+func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) {
+ tableName, err := session.Engine.tableName(beanOrTableName)
+ if err != nil {
+ return false, err
+ }
+
+ return session.isTableExist(tableName)
+}
+
+func (session *Session) isTableExist(tableName string) (bool, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+ sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)
+ results, err := session.query(sqlStr, args...)
+ return len(results) > 0, err
+}
+
+// IsTableEmpty if table have any records
+func (session *Session) IsTableEmpty(bean interface{}) (bool, error) {
+ v := rValue(bean)
+ t := v.Type()
+
+ if t.Kind() == reflect.String {
+ return session.isTableEmpty(bean.(string))
+ } else if t.Kind() == reflect.Struct {
+ rows, err := session.Count(bean)
+ return rows == 0, err
+ }
+ return false, errors.New("bean should be a struct or struct's point")
+}
+
+func (session *Session) isTableEmpty(tableName string) (bool, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ var total int64
+ sql := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName))
+ err := session.DB().QueryRow(sql).Scan(&total)
+ session.saveLastSQL(sql)
+ if err != nil {
+ return true, err
+ }
+
+ return total == 0, nil
+}
+
+func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+ var idx string
+ if unique {
+ idx = uniqueName(tableName, idxName)
+ } else {
+ idx = indexName(tableName, idxName)
+ }
+ sqlStr, args := session.Engine.dialect.IndexCheckSql(tableName, idx)
+ results, err := session.query(sqlStr, args...)
+ return len(results) > 0, err
+}
+
+// find if index is exist according cols
+func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ indexes, err := session.Engine.dialect.GetIndexes(tableName)
+ if err != nil {
+ return false, err
+ }
+
+ for _, index := range indexes {
+ if sliceEq(index.Cols, cols) {
+ if unique {
+ return index.Type == core.UniqueType, nil
+ }
+ return index.Type == core.IndexType, nil
+ }
+ }
+ return false, nil
+}
+
+func (session *Session) addColumn(colName string) error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ col := session.Statement.RefTable.GetColumn(colName)
+ sql, args := session.Statement.genAddColumnStr(col)
+ _, err := session.exec(sql, args...)
+ return err
+}
+
+func (session *Session) addIndex(tableName, idxName string) error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+ index := session.Statement.RefTable.Indexes[idxName]
+ sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)
+
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+func (session *Session) addUnique(tableName, uqeName string) error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+ index := session.Statement.RefTable.Indexes[uqeName]
+ sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+// To be deleted
+func (session *Session) dropAll() error {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ for _, table := range session.Engine.Tables {
+ session.Statement.Init()
+ session.Statement.RefTable = table
+ sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName())
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value {
+ var col *core.Column
+ if col = table.GetColumnIdx(key, idx); col == nil {
+ //session.Engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq())
+ return nil
+ }
+
+ fieldValue, err := col.ValueOfV(dataStruct)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return nil
+ }
+
+ if !fieldValue.IsValid() || !fieldValue.CanSet() {
+ session.Engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key)
+ return nil
+ }
+ return fieldValue
+}
+
+// Cell cell is a result of one column field
+type Cell *interface{}
+
+func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int,
+ table *core.Table, newElemFunc func() reflect.Value,
+ sliceValueSetFunc func(*reflect.Value)) error {
+ for rows.Next() {
+ var newValue = newElemFunc()
+ bean := newValue.Interface()
+ dataStruct := rValue(bean)
+ err := session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table)
+ if err != nil {
+ return err
+ }
+ sliceValueSetFunc(&newValue)
+ }
+ return nil
+}
+
+func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
+ dataStruct := rValue(bean)
+ if dataStruct.Kind() != reflect.Struct {
+ return errors.New("Expected a pointer to a struct")
+ }
+
+ session.Statement.setRefValue(dataStruct)
+
+ return session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, session.Statement.RefTable)
+}
+
+func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) error {
+ scanResults := make([]interface{}, fieldsCount)
+ for i := 0; i < len(fields); i++ {
+ var cell interface{}
+ scanResults[i] = &cell
+ }
+ if err := rows.Scan(scanResults...); err != nil {
+ return err
+ }
+
+ if b, hasBeforeSet := bean.(BeforeSetProcessor); hasBeforeSet {
+ for ii, key := range fields {
+ b.BeforeSet(key, Cell(scanResults[ii].(*interface{})))
+ }
+ }
+
+ defer func() {
+ if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet {
+ for ii, key := range fields {
+ b.AfterSet(key, Cell(scanResults[ii].(*interface{})))
+ }
+ }
+ }()
+
+ var tempMap = make(map[string]int)
+ for ii, key := range fields {
+ var idx int
+ var ok bool
+ var lKey = strings.ToLower(key)
+ if idx, ok = tempMap[lKey]; !ok {
+ idx = 0
+ } else {
+ idx = idx + 1
+ }
+ tempMap[lKey] = idx
+
+ if fieldValue := session.getField(dataStruct, key, table, idx); fieldValue != nil {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
+
+ // if row is null then ignore
+ if rawValue.Interface() == nil {
+ continue
+ }
+
+ if fieldValue.CanAddr() {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ if data, err := value2Bytes(&rawValue); err == nil {
+ structConvert.FromDB(data)
+ } else {
+ session.Engine.logger.Error(err)
+ }
+ continue
+ }
+ }
+
+ if _, ok := fieldValue.Interface().(core.Conversion); ok {
+ if data, err := value2Bytes(&rawValue); err == nil {
+ if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
+ fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
+ }
+ fieldValue.Interface().(core.Conversion).FromDB(data)
+ } else {
+ session.Engine.logger.Error(err)
+ }
+ continue
+ }
+
+ rawValueType := reflect.TypeOf(rawValue.Interface())
+ vv := reflect.ValueOf(rawValue.Interface())
+
+ fieldType := fieldValue.Type()
+ hasAssigned := false
+ col := table.GetColumnIdx(key, idx)
+
+ if col.SQLType.IsJson() {
+ var bs []byte
+ if rawValueType.Kind() == reflect.String {
+ bs = []byte(vv.String())
+ } else if rawValueType.ConvertibleTo(core.BytesType) {
+ bs = vv.Bytes()
+ } else {
+ return fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind())
+ }
+
+ hasAssigned = true
+
+ if len(bs) > 0 {
+ if fieldValue.CanAddr() {
+ err := json.Unmarshal(bs, fieldValue.Addr().Interface())
+ if err != nil {
+ session.Engine.logger.Error(key, err)
+ return err
+ }
+ } else {
+ x := reflect.New(fieldType)
+ err := json.Unmarshal(bs, x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(key, err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+
+ continue
+ }
+
+ switch fieldType.Kind() {
+ case reflect.Complex64, reflect.Complex128:
+ // TODO: reimplement this
+ var bs []byte
+ if rawValueType.Kind() == reflect.String {
+ bs = []byte(vv.String())
+ } else if rawValueType.ConvertibleTo(core.BytesType) {
+ bs = vv.Bytes()
+ }
+
+ hasAssigned = true
+ if len(bs) > 0 {
+ if fieldValue.CanAddr() {
+ err := json.Unmarshal(bs, fieldValue.Addr().Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ } else {
+ x := reflect.New(fieldType)
+ err := json.Unmarshal(bs, x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ switch rawValueType.Kind() {
+ case reflect.Slice, reflect.Array:
+ switch rawValueType.Elem().Kind() {
+ case reflect.Uint8:
+ if fieldType.Elem().Kind() == reflect.Uint8 {
+ hasAssigned = true
+ fieldValue.Set(vv)
+ }
+ }
+ }
+ case reflect.String:
+ if rawValueType.Kind() == reflect.String {
+ hasAssigned = true
+ fieldValue.SetString(vv.String())
+ }
+ case reflect.Bool:
+ if rawValueType.Kind() == reflect.Bool {
+ hasAssigned = true
+ fieldValue.SetBool(vv.Bool())
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch rawValueType.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ hasAssigned = true
+ fieldValue.SetInt(vv.Int())
+ }
+ case reflect.Float32, reflect.Float64:
+ switch rawValueType.Kind() {
+ case reflect.Float32, reflect.Float64:
+ hasAssigned = true
+ fieldValue.SetFloat(vv.Float())
+ }
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ switch rawValueType.Kind() {
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ hasAssigned = true
+ fieldValue.SetUint(vv.Uint())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ hasAssigned = true
+ fieldValue.SetUint(uint64(vv.Int()))
+ }
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ if rawValueType == core.TimeType {
+ hasAssigned = true
+
+ t := vv.Convert(core.TimeType).Interface().(time.Time)
+ z, _ := t.Zone()
+ if len(z) == 0 || t.Year() == 0 { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
+ dbTZ := session.Engine.DatabaseTZ
+ if dbTZ == nil {
+ dbTZ = time.Local
+ }
+ session.Engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
+ t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
+ t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
+ }
+ // !nashtsai! convert to engine location
+ if col.TimeZone == nil {
+ t = t.In(session.Engine.TZLocation)
+ } else {
+ t = t.In(col.TimeZone)
+ }
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+
+ // t = fieldValue.Interface().(time.Time)
+ // z, _ = t.Zone()
+ // session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
+ } else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
+ rawValueType == core.Int32Type {
+ hasAssigned = true
+ var tz *time.Location
+ if col.TimeZone == nil {
+ tz = session.Engine.TZLocation
+ } else {
+ tz = col.TimeZone
+ }
+ t := time.Unix(vv.Int(), 0).In(tz)
+ //vv = reflect.ValueOf(t)
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ } else {
+ if d, ok := vv.Interface().([]uint8); ok {
+ hasAssigned = true
+ t, err := session.byte2Time(col, d)
+ if err != nil {
+ session.Engine.logger.Error("byte2Time error:", err.Error())
+ hasAssigned = false
+ } else {
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ }
+ } else if d, ok := vv.Interface().(string); ok {
+ hasAssigned = true
+ t, err := session.str2Time(col, d)
+ if err != nil {
+ session.Engine.logger.Error("byte2Time error:", err.Error())
+ hasAssigned = false
+ } else {
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ }
+ } else {
+ panic(fmt.Sprintf("rawValueType is %v, value is %v", rawValueType, vv.Interface()))
+ }
+ }
+ } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
+ // !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
+ hasAssigned = true
+ if err := nulVal.Scan(vv.Interface()); err != nil {
+ session.Engine.logger.Error("sql.Sanner error:", err.Error())
+ hasAssigned = false
+ }
+ } else if col.SQLType.IsJson() {
+ if rawValueType.Kind() == reflect.String {
+ hasAssigned = true
+ x := reflect.New(fieldType)
+ if len([]byte(vv.String())) > 0 {
+ err := json.Unmarshal([]byte(vv.String()), x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ } else if rawValueType.Kind() == reflect.Slice {
+ hasAssigned = true
+ x := reflect.New(fieldType)
+ if len(vv.Bytes()) > 0 {
+ err := json.Unmarshal(vv.Bytes(), x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ } else if session.Statement.UseCascade {
+ table := session.Engine.autoMapType(*fieldValue)
+ if table != nil {
+ hasAssigned = true
+ if len(table.PrimaryKeys) != 1 {
+ panic("unsupported non or composited primary key cascade")
+ }
+ var pk = make(core.PK, len(table.PrimaryKeys))
+
+ switch rawValueType.Kind() {
+ case reflect.Int64:
+ pk[0] = vv.Int()
+ case reflect.Int:
+ pk[0] = int(vv.Int())
+ case reflect.Int32:
+ pk[0] = int32(vv.Int())
+ case reflect.Int16:
+ pk[0] = int16(vv.Int())
+ case reflect.Int8:
+ pk[0] = int8(vv.Int())
+ case reflect.Uint64:
+ pk[0] = vv.Uint()
+ case reflect.Uint:
+ pk[0] = uint(vv.Uint())
+ case reflect.Uint32:
+ pk[0] = uint32(vv.Uint())
+ case reflect.Uint16:
+ pk[0] = uint16(vv.Uint())
+ case reflect.Uint8:
+ pk[0] = uint8(vv.Uint())
+ case reflect.String:
+ pk[0] = vv.String()
+ case reflect.Slice:
+ pk[0], _ = strconv.ParseInt(string(rawValue.Interface().([]byte)), 10, 64)
+ default:
+ panic(fmt.Sprintf("unsupported primary key type: %v, %v", rawValueType, fieldValue))
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ structInter := reflect.New(fieldValue.Type())
+ newsession := session.Engine.NewSession()
+ defer newsession.Close()
+ has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+ if err != nil {
+ return err
+ }
+ if has {
+ //v := structInter.Elem().Interface()
+ //fieldValue.Set(reflect.ValueOf(v))
+ fieldValue.Set(structInter.Elem())
+ } else {
+ return errors.New("cascade obj is not exist")
+ }
+ }
+ } else {
+ session.Engine.logger.Error("unsupported struct type in Scan: ", fieldValue.Type().String())
+ }
+ }
+ case reflect.Ptr:
+ // !nashtsai! TODO merge duplicated codes above
+ //typeStr := fieldType.String()
+ switch fieldType {
+ // following types case matching ptr's native type, therefore assign ptr directly
+ case core.PtrStringType:
+ if rawValueType.Kind() == reflect.String {
+ x := vv.String()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrBoolType:
+ if rawValueType.Kind() == reflect.Bool {
+ x := vv.Bool()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrTimeType:
+ if rawValueType == core.PtrTimeType {
+ hasAssigned = true
+ var x = rawValue.Interface().(time.Time)
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrFloat64Type:
+ if rawValueType.Kind() == reflect.Float64 {
+ x := vv.Float()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUint64Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint64(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt64Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ x := vv.Int()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrFloat32Type:
+ if rawValueType.Kind() == reflect.Float64 {
+ var x = float32(vv.Float())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrIntType:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt32Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int32(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt8Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int8(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt16Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int16(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUintType:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUint32Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint32(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Uint8Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint8(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Uint16Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint16(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Complex64Type:
+ var x complex64
+ if len([]byte(vv.String())) > 0 {
+ err := json.Unmarshal([]byte(vv.String()), &x)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else {
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ }
+ hasAssigned = true
+ case core.Complex128Type:
+ var x complex128
+ if len([]byte(vv.String())) > 0 {
+ err := json.Unmarshal([]byte(vv.String()), &x)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else {
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ }
+ hasAssigned = true
+ } // switch fieldType
+ // default:
+ // session.Engine.LogError("unsupported type in Scan: ", reflect.TypeOf(v).String())
+ } // switch fieldType.Kind()
+
+ // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
+ if !hasAssigned {
+ data, err := value2Bytes(&rawValue)
+ if err == nil {
+ session.bytes2Value(col, fieldValue, data)
+ } else {
+ session.Engine.logger.Error(err.Error())
+ }
+ }
+ }
+ }
+ return nil
+
+}
+
+func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) {
+ for _, filter := range session.Engine.dialect.Filters() {
+ *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable)
+ }
+
+ session.saveLastSQL(*sqlStr, paramStr...)
+}
+
+func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+
+ session.queryPreprocess(&sqlStr, paramStr...)
+
+ if session.IsAutoCommit {
+ return session.innerQuery2(sqlStr, paramStr...)
+ }
+ return session.txQuery(session.Tx, sqlStr, paramStr...)
+}
+
+func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) {
+ rows, err := tx.Query(sqlStr, params...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2maps(rows)
+}
+
+func (session *Session) innerQuery(sqlStr string, params ...interface{}) (*core.Stmt, *core.Rows, error) {
+ var callback func() (*core.Stmt, *core.Rows, error)
+ if session.prepareStmt {
+ callback = func() (*core.Stmt, *core.Rows, error) {
+ stmt, err := session.doPrepare(sqlStr)
+ if err != nil {
+ return nil, nil, err
+ }
+ rows, err := stmt.Query(params...)
+ if err != nil {
+ return nil, nil, err
+ }
+ return stmt, rows, nil
+ }
+ } else {
+ callback = func() (*core.Stmt, *core.Rows, error) {
+ rows, err := session.DB().Query(sqlStr, params...)
+ if err != nil {
+ return nil, nil, err
+ }
+ return nil, rows, err
+ }
+ }
+ stmt, rows, err := session.Engine.logSQLQueryTime(sqlStr, params, callback)
+ if err != nil {
+ return nil, nil, err
+ }
+ return stmt, rows, nil
+}
+
+func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map[string][]byte, error) {
+ _, rows, err := session.innerQuery(sqlStr, params...)
+ if rows != nil {
+ defer rows.Close()
+ }
+ if err != nil {
+ return nil, err
+ }
+ return rows2maps(rows)
+}
+
+// Query a raw sql and return records as []map[string][]byte
+func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ return session.query(sqlStr, paramStr...)
+}
+
+// =============================
+// for string
+// =============================
+func (session *Session) query2(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) {
+ session.queryPreprocess(&sqlStr, paramStr...)
+
+ if session.IsAutoCommit {
+ return query2(session.DB(), sqlStr, paramStr...)
+ }
+ return txQuery2(session.Tx, sqlStr, paramStr...)
+}
+
+// Insert insert one or more beans
+func (session *Session) Insert(beans ...interface{}) (int64, error) {
+ var affected int64
+ var err error
+
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ for _, bean := range beans {
+ sliceValue := reflect.Indirect(reflect.ValueOf(bean))
+ if sliceValue.Kind() == reflect.Slice {
+ size := sliceValue.Len()
+ if size > 0 {
+ if session.Engine.SupportInsertMany() {
+ cnt, err := session.innerInsertMulti(bean)
+ session.resetStatement()
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ } else {
+ for i := 0; i < size; i++ {
+ cnt, err := session.innerInsert(sliceValue.Index(i).Interface())
+ session.resetStatement()
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ }
+ }
+ } else {
+ cnt, err := session.innerInsert(bean)
+ session.resetStatement()
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ }
+
+ return affected, err
+}
+
+func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) {
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice {
+ return 0, errors.New("needs a pointer to a slice")
+ }
+
+ if sliceValue.Len() <= 0 {
+ return 0, errors.New("could not insert a empty slice")
+ }
+
+ session.Statement.setRefValue(sliceValue.Index(0))
+
+ if len(session.Statement.TableName()) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ table := session.Statement.RefTable
+ size := sliceValue.Len()
+
+ var colNames []string
+ var colMultiPlaces []string
+ var args []interface{}
+ var cols []*core.Column
+
+ for i := 0; i < size; i++ {
+ v := sliceValue.Index(i)
+ vv := reflect.Indirect(v)
+ elemValue := v.Interface()
+ var colPlaces []string
+
+ // handle BeforeInsertProcessor
+ // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
+ for _, closure := range session.beforeClosures {
+ closure(elemValue)
+ }
+
+ if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok {
+ processor.BeforeInsert()
+ }
+ // --
+
+ if i == 0 {
+ for _, col := range table.Columns() {
+ ptrFieldValue, err := col.ValueOfV(&vv)
+ if err != nil {
+ return 0, err
+ }
+ fieldValue := *ptrFieldValue
+ if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
+ continue
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+ if col.IsDeleted {
+ continue
+ }
+ if session.Statement.ColumnStr != "" {
+ if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; !ok {
+ continue
+ }
+ }
+ if session.Statement.OmitStr != "" {
+ if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; ok {
+ continue
+ }
+ }
+ if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
+ val, t := session.Engine.NowTime2(col.SQLType.Name)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.Statement.checkVersion {
+ args = append(args, 1)
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnInt(bean, col, 1)
+ })
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return 0, err
+ }
+ args = append(args, arg)
+ }
+
+ colNames = append(colNames, col.Name)
+ cols = append(cols, col)
+ colPlaces = append(colPlaces, "?")
+ }
+ } else {
+ for _, col := range cols {
+ ptrFieldValue, err := col.ValueOfV(&vv)
+ if err != nil {
+ return 0, err
+ }
+ fieldValue := *ptrFieldValue
+
+ if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
+ continue
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+ if col.IsDeleted {
+ continue
+ }
+ if session.Statement.ColumnStr != "" {
+ if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; !ok {
+ continue
+ }
+ }
+ if session.Statement.OmitStr != "" {
+ if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; ok {
+ continue
+ }
+ }
+ if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
+ val, t := session.Engine.NowTime2(col.SQLType.Name)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.Statement.checkVersion {
+ args = append(args, 1)
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnInt(bean, col, 1)
+ })
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return 0, err
+ }
+ args = append(args, arg)
+ }
+
+ colPlaces = append(colPlaces, "?")
+ }
+ }
+ colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", "))
+ }
+ cleanupProcessorsClosures(&session.beforeClosures)
+
+ statement := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
+ session.Engine.Quote(session.Statement.TableName()),
+ session.Engine.QuoteStr(),
+ strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
+ session.Engine.QuoteStr(),
+ strings.Join(colMultiPlaces, "),("))
+
+ res, err := session.exec(statement, args...)
+ if err != nil {
+ return 0, err
+ }
+
+ if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
+ session.cacheInsert(session.Statement.TableName())
+ }
+
+ lenAfterClosures := len(session.afterClosures)
+ for i := 0; i < size; i++ {
+ elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface()
+
+ // handle AfterInsertProcessor
+ if session.IsAutoCommit {
+ // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
+ for _, closure := range session.afterClosures {
+ closure(elemValue)
+ }
+ if processor, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ } else {
+ if lenAfterClosures > 0 {
+ if value, has := session.afterInsertBeans[elemValue]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterInsertBeans[elemValue] = &afterClosures
+ }
+ } else {
+ if _, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
+ session.afterInsertBeans[elemValue] = nil
+ }
+ }
+ }
+ }
+
+ cleanupProcessorsClosures(&session.afterClosures)
+ return res.RowsAffected()
+}
+
+// InsertMulti insert multiple records
+func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice {
+ return 0, ErrParamsType
+
+ }
+
+ if sliceValue.Len() <= 0 {
+ return 0, nil
+ }
+
+ return session.innerInsertMulti(rowsSlicePtr)
+}
+
+func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
+ sdata := strings.TrimSpace(data)
+ var x time.Time
+ var err error
+
+ if sdata == "0000-00-00 00:00:00" ||
+ sdata == "0001-01-01 00:00:00" {
+ } else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
+ // time stamp
+ sd, err := strconv.ParseInt(sdata, 10, 64)
+ if err == nil {
+ x = time.Unix(sd, 0)
+ // !nashtsai! HACK mymysql driver is casuing Local location being change to CHAT and cause wrong time conversion
+ if col.TimeZone == nil {
+ x = x.In(session.Engine.TZLocation)
+ } else {
+ x = x.In(col.TimeZone)
+ }
+ session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else {
+ session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+ } else if len(sdata) > 19 && strings.Contains(sdata, "-") {
+ x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ if err != nil {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+ if err != nil {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+
+ } else if len(sdata) == 19 && strings.Contains(sdata, "-") {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
+ x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else if col.SQLType.Name == core.Time {
+ if strings.Contains(sdata, " ") {
+ ssd := strings.Split(sdata, " ")
+ sdata = ssd[1]
+ }
+
+ sdata = strings.TrimSpace(sdata)
+ if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
+ sdata = sdata[len(sdata)-8:]
+ }
+
+ st := fmt.Sprintf("2006-01-02 %v", sdata)
+ x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
+ session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else {
+ outErr = fmt.Errorf("unsupported time format %v", sdata)
+ return
+ }
+ if err != nil {
+ outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
+ return
+ }
+ outTime = x
+ return
+}
+
+func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) {
+ return session.str2Time(col, string(data))
+}
+
+// convert a db data([]byte) to a field value
+func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ return structConvert.FromDB(data)
+ }
+
+ if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ return structConvert.FromDB(data)
+ }
+
+ var v interface{}
+ key := col.Name
+ fieldType := fieldValue.Type()
+
+ switch fieldType.Kind() {
+ case reflect.Complex64, reflect.Complex128:
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := json.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ case reflect.Slice, reflect.Array, reflect.Map:
+ v = data
+ t := fieldType.Elem()
+ k := t.Kind()
+ if col.SQLType.IsText() {
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := json.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ } else if col.SQLType.IsBlob() {
+ if k == reflect.Uint8 {
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := json.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ } else {
+ return ErrUnSupportedType
+ }
+ case reflect.String:
+ fieldValue.SetString(string(data))
+ case reflect.Bool:
+ d := string(data)
+ v, err := strconv.ParseBool(d)
+ if err != nil {
+ return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(v))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ sdata := string(data)
+ var x int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
+ if len(data) == 1 {
+ x = int64(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x, err = strconv.ParseInt(sdata, 16, 64)
+ } else if strings.HasPrefix(sdata, "0") {
+ x, err = strconv.ParseInt(sdata, 8, 64)
+ } else if strings.EqualFold(sdata, "true") {
+ x = 1
+ } else if strings.EqualFold(sdata, "false") {
+ x = 0
+ } else {
+ x, err = strconv.ParseInt(sdata, 10, 64)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.SetInt(x)
+ case reflect.Float32, reflect.Float64:
+ x, err := strconv.ParseFloat(string(data), 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+ }
+ fieldValue.SetFloat(x)
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ x, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.SetUint(x)
+ //Currently only support Time type
+ case reflect.Struct:
+ // !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
+ if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
+ if err := nulVal.Scan(data); err != nil {
+ return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error())
+ }
+ } else {
+ if fieldType.ConvertibleTo(core.TimeType) {
+ x, err := session.byte2Time(col, data)
+ if err != nil {
+ return err
+ }
+ v = x
+ fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
+ } else if session.Statement.UseCascade {
+ table := session.Engine.autoMapType(*fieldValue)
+ if table != nil {
+ // TODO: current only support 1 primary key
+ if len(table.PrimaryKeys) > 1 {
+ panic("unsupported composited primary key cascade")
+ }
+ var pk = make(core.PK, len(table.PrimaryKeys))
+ rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+ var err error
+ pk[0], err = str2PK(string(data), rawValueType)
+ if err != nil {
+ return err
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ structInter := reflect.New(fieldValue.Type())
+ newsession := session.Engine.NewSession()
+ defer newsession.Close()
+ has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+ if err != nil {
+ return err
+ }
+ if has {
+ v = structInter.Elem().Interface()
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ return errors.New("cascade obj is not exist")
+ }
+ }
+ } else {
+ return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
+ }
+ }
+ }
+ case reflect.Ptr:
+ // !nashtsai! TODO merge duplicated codes above
+ //typeStr := fieldType.String()
+ switch fieldType.Elem().Kind() {
+ // case "*string":
+ case core.StringType.Kind():
+ x := string(data)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*bool":
+ case core.BoolType.Kind():
+ d := string(data)
+ v, err := strconv.ParseBool(d)
+ if err != nil {
+ return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType))
+ // case "*complex64":
+ case core.Complex64Type.Kind():
+ var x complex64
+ if len(data) > 0 {
+ err := json.Unmarshal(data, &x)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ }
+ // case "*complex128":
+ case core.Complex128Type.Kind():
+ var x complex128
+ if len(data) > 0 {
+ err := json.Unmarshal(data, &x)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ }
+ // case "*float64":
+ case core.Float64Type.Kind():
+ x, err := strconv.ParseFloat(string(data), 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*float32":
+ case core.Float32Type.Kind():
+ var x float32
+ x1, err := strconv.ParseFloat(string(data), 32)
+ if err != nil {
+ return fmt.Errorf("arg %v as float32: %s", key, err.Error())
+ }
+ x = float32(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint64":
+ case core.Uint64Type.Kind():
+ var x uint64
+ x, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint":
+ case core.UintType.Kind():
+ var x uint
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint32":
+ case core.Uint32Type.Kind():
+ var x uint32
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint32(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint8":
+ case core.Uint8Type.Kind():
+ var x uint8
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint8(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint16":
+ case core.Uint16Type.Kind():
+ var x uint16
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint16(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int64":
+ case core.Int64Type.Kind():
+ sdata := string(data)
+ var x int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.Engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int64(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x, err = strconv.ParseInt(sdata, 16, 64)
+ } else if strings.HasPrefix(sdata, "0") {
+ x, err = strconv.ParseInt(sdata, 8, 64)
+ } else {
+ x, err = strconv.ParseInt(sdata, 10, 64)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int":
+ case core.IntType.Kind():
+ sdata := string(data)
+ var x int
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.Engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int32":
+ case core.Int32Type.Kind():
+ sdata := string(data)
+ var x int32
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ session.Engine.dialect.DBType() == core.MYSQL {
+ if len(data) == 1 {
+ x = int32(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int32(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int32(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int32(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int8":
+ case core.Int8Type.Kind():
+ sdata := string(data)
+ var x int8
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.Engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int8(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int8(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int8(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int8(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int16":
+ case core.Int16Type.Kind():
+ sdata := string(data)
+ var x int16
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.Engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int16(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int16(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int16(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int16(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*SomeStruct":
+ case reflect.Struct:
+ switch fieldType {
+ // case "*.time.Time":
+ case core.PtrTimeType:
+ x, err := session.byte2Time(col, data)
+ if err != nil {
+ return err
+ }
+ v = x
+ fieldValue.Set(reflect.ValueOf(&x))
+ default:
+ if session.Statement.UseCascade {
+ structInter := reflect.New(fieldType.Elem())
+ table := session.Engine.autoMapType(structInter.Elem())
+ if table != nil {
+ if len(table.PrimaryKeys) > 1 {
+ panic("unsupported composited primary key cascade")
+ }
+ var pk = make(core.PK, len(table.PrimaryKeys))
+ var err error
+ rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+ pk[0], err = str2PK(string(data), rawValueType)
+ if err != nil {
+ return err
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ newsession := session.Engine.NewSession()
+ defer newsession.Close()
+ has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+ if err != nil {
+ return err
+ }
+ if has {
+ v = structInter.Interface()
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ return errors.New("cascade obj is not exist")
+ }
+ }
+ }
+ } else {
+ return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
+ }
+ }
+ default:
+ return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+ }
+ default:
+ return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+ }
+
+ return nil
+}
+
+// convert a field value of a struct to interface for put into db
+func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) {
+ if fieldValue.CanAddr() {
+ if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ data, err := fieldConvert.ToDB()
+ if err != nil {
+ return 0, err
+ }
+ return string(data), nil
+ }
+ }
+
+ if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ data, err := fieldConvert.ToDB()
+ if err != nil {
+ return 0, err
+ }
+ return string(data), nil
+ }
+
+ fieldType := fieldValue.Type()
+ k := fieldType.Kind()
+ if k == reflect.Ptr {
+ if fieldValue.IsNil() {
+ return nil, nil
+ } else if !fieldValue.IsValid() {
+ session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid")
+ return nil, nil
+ } else {
+ // !nashtsai! deference pointer type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = fieldValue.Type()
+ k = fieldType.Kind()
+ }
+ }
+
+ switch k {
+ case reflect.Bool:
+ return fieldValue.Bool(), nil
+ case reflect.String:
+ return fieldValue.String(), nil
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if session.Engine.dialect.DBType() == core.MSSQL {
+ if t.IsZero() {
+ return nil, nil
+ }
+ }
+ tf := session.Engine.FormatTime(col.SQLType.Name, t)
+ return tf, nil
+ }
+
+ if !col.SQLType.IsJson() {
+ // !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
+ if v, ok := fieldValue.Interface().(driver.Valuer); ok {
+ return v.Value()
+ }
+
+ fieldTable := session.Engine.autoMapType(fieldValue)
+ if len(fieldTable.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
+ return pkField.Interface(), nil
+ }
+ return 0, fmt.Errorf("no primary key for col %v", col.Name)
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ } else if col.SQLType.IsBlob() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return 0, err
+ }
+ return bytes, nil
+ }
+ return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
+ case reflect.Complex64, reflect.Complex128:
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if !fieldValue.IsValid() {
+ return fieldValue.Interface(), nil
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if (k == reflect.Array || k == reflect.Slice) &&
+ (fieldValue.Type().Elem().Kind() == reflect.Uint8) {
+ bytes = fieldValue.Bytes()
+ } else {
+ bytes, err = json.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.Engine.logger.Error(err)
+ return 0, err
+ }
+ }
+ return bytes, nil
+ }
+ return nil, ErrUnSupportedType
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return int64(fieldValue.Uint()), nil
+ default:
+ return fieldValue.Interface(), nil
+ }
+}
+
+func (session *Session) innerInsert(bean interface{}) (int64, error) {
+ session.Statement.setRefValue(rValue(bean))
+ if len(session.Statement.TableName()) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ table := session.Statement.RefTable
+
+ // handle BeforeInsertProcessor
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
+
+ if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok {
+ processor.BeforeInsert()
+ }
+ // --
+ colNames, args, err := genCols(session.Statement.RefTable, session, bean, false, false)
+ if err != nil {
+ return 0, err
+ }
+ // insert expr columns, override if exists
+ exprColumns := session.Statement.getExpr()
+ exprColVals := make([]string, 0, len(exprColumns))
+ for _, v := range exprColumns {
+ // remove the expr columns
+ for i, colName := range colNames {
+ if colName == v.colName {
+ colNames = append(colNames[:i], colNames[i+1:]...)
+ args = append(args[:i], args[i+1:]...)
+ }
+ }
+
+ // append expr column to the end
+ colNames = append(colNames, v.colName)
+ exprColVals = append(exprColVals, v.expr)
+ }
+
+ colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns))
+ if len(exprColVals) > 0 {
+ colPlaces = colPlaces + strings.Join(exprColVals, ", ")
+ } else {
+ colPlaces = colPlaces[0 : len(colPlaces)-2]
+ }
+
+ sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
+ session.Engine.Quote(session.Statement.TableName()),
+ session.Engine.QuoteStr(),
+ strings.Join(colNames, session.Engine.Quote(", ")),
+ session.Engine.QuoteStr(),
+ colPlaces)
+
+ handleAfterInsertProcessorFunc := func(bean interface{}) {
+ if session.IsAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterInsertBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterInsertBeans[bean] = &afterClosures
+ }
+
+ } else {
+ if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ session.afterInsertBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
+ }
+
+ // for postgres, many of them didn't implement lastInsertId, so we should
+ // implemented it ourself.
+ if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 {
+ //assert table.AutoIncrement != ""
+ res, err := session.query("select seq_atable.currval from dual", args...)
+ if err != nil {
+ return 0, err
+ }
+
+ handleAfterInsertProcessorFunc(bean)
+
+ if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
+ session.cacheInsert(session.Statement.TableName())
+ }
+
+ if table.Version != "" && session.Statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ verValue.SetInt(1)
+ }
+ }
+
+ if len(res) < 1 {
+ return 0, errors.New("insert no error but not returned id")
+ }
+
+ idByte := res[0][table.AutoIncrement]
+ id, err := strconv.ParseInt(string(idByte), 10, 64)
+ if err != nil || id <= 0 {
+ return 1, err
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return 1, nil
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return 1, nil
+ } else if session.Engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 {
+ //assert table.AutoIncrement != ""
+ sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement)
+ res, err := session.query(sqlStr, args...)
+
+ if err != nil {
+ return 0, err
+ }
+ handleAfterInsertProcessorFunc(bean)
+
+ if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
+ session.cacheInsert(session.Statement.TableName())
+ }
+
+ if table.Version != "" && session.Statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ verValue.SetInt(1)
+ }
+ }
+
+ if len(res) < 1 {
+ return 0, errors.New("insert no error but not returned id")
+ }
+
+ idByte := res[0][table.AutoIncrement]
+ id, err := strconv.ParseInt(string(idByte), 10, 64)
+ if err != nil || id <= 0 {
+ return 1, err
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return 1, nil
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return 1, nil
+ } else {
+ res, err := session.exec(sqlStr, args...)
+ if err != nil {
+ return 0, err
+ }
+
+ defer handleAfterInsertProcessorFunc(bean)
+
+ if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
+ session.cacheInsert(session.Statement.TableName())
+ }
+
+ if table.Version != "" && session.Statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ verValue.SetInt(1)
+ }
+ }
+
+ if table.AutoIncrement == "" {
+ return res.RowsAffected()
+ }
+
+ var id int64
+ id, err = res.LastInsertId()
+ if err != nil || id <= 0 {
+ return res.RowsAffected()
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return res.RowsAffected()
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return res.RowsAffected()
+ }
+}
+
+// InsertOne insert only one struct into database as a record.
+// The in parameter bean must a struct or a point to struct. The return
+// parameter is inserted and error
+func (session *Session) InsertOne(bean interface{}) (int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ return session.innerInsert(bean)
+}
+
+func (session *Session) cacheInsert(tables ...string) error {
+ if session.Statement.RefTable == nil {
+ return ErrCacheFailed
+ }
+
+ table := session.Statement.RefTable
+ cacher := session.Engine.getCacher2(table)
+
+ for _, t := range tables {
+ session.Engine.logger.Debug("[cache] clear sql:", t)
+ cacher.ClearIds(t)
+ }
+
+ return nil
+}
+
+func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
+ if session.Statement.RefTable == nil ||
+ session.Tx != nil {
+ return ErrCacheFailed
+ }
+
+ oldhead, newsql := session.Statement.convertUpdateSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+ for _, filter := range session.Engine.dialect.Filters() {
+ newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable)
+ }
+ session.Engine.logger.Debug("[cacheUpdate] new sql", oldhead, newsql)
+
+ var nStart int
+ if len(args) > 0 {
+ if strings.Index(sqlStr, "?") > -1 {
+ nStart = strings.Count(oldhead, "?")
+ } else {
+ // only for pq, TODO: if any other databse?
+ nStart = strings.Count(oldhead, "$")
+ }
+ }
+ table := session.Statement.RefTable
+ cacher := session.Engine.getCacher2(table)
+ tableName := session.Statement.TableName()
+ session.Engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:])
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:])
+ if err != nil {
+ rows, err := session.DB().Query(newsql, args[nStart:]...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ ids = make([]core.PK, 0)
+ for rows.Next() {
+ var res = make([]string, len(table.PrimaryKeys))
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return err
+ }
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(res[i], 10, 64)
+ if err != nil {
+ return err
+ }
+ pk[i] = n
+ } else if col.SQLType.IsText() {
+ pk[i] = res[i]
+ } else {
+ return errors.New("not supported")
+ }
+ }
+
+ ids = append(ids, pk)
+ }
+ session.Engine.logger.Debug("[cacheUpdate] find updated id", ids)
+ } /*else {
+ session.Engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args)
+ cacher.DelIds(tableName, genSqlKey(newsql, args))
+ }*/
+
+ for _, id := range ids {
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ if bean := cacher.GetBean(tableName, sid); bean != nil {
+ sqls := splitNNoCase(sqlStr, "where", 2)
+ if len(sqls) == 0 || len(sqls) > 2 {
+ return ErrCacheFailed
+ }
+
+ sqls = splitNNoCase(sqls[0], "set", 2)
+ if len(sqls) != 2 {
+ return ErrCacheFailed
+ }
+ kvs := strings.Split(strings.TrimSpace(sqls[1]), ",")
+ for idx, kv := range kvs {
+ sps := strings.SplitN(kv, "=", 2)
+ sps2 := strings.Split(sps[0], ".")
+ colName := sps2[len(sps2)-1]
+ if strings.Contains(colName, "`") {
+ colName = strings.TrimSpace(strings.Replace(colName, "`", "", -1))
+ } else if strings.Contains(colName, session.Engine.QuoteStr()) {
+ colName = strings.TrimSpace(strings.Replace(colName, session.Engine.QuoteStr(), "", -1))
+ } else {
+ session.Engine.logger.Debug("[cacheUpdate] cannot find column", tableName, colName)
+ return ErrCacheFailed
+ }
+
+ if col := table.GetColumn(colName); col != nil {
+ fieldValue, err := col.ValueOf(bean)
+ if err != nil {
+ session.Engine.logger.Error(err)
+ } else {
+ session.Engine.logger.Debug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface())
+ if col.IsVersion && session.Statement.checkVersion {
+ fieldValue.SetInt(fieldValue.Int() + 1)
+ } else {
+ fieldValue.Set(reflect.ValueOf(args[idx]))
+ }
+ }
+ } else {
+ session.Engine.logger.Errorf("[cacheUpdate] ERROR: column %v is not table %v's",
+ colName, table.Name)
+ }
+ }
+
+ session.Engine.logger.Debug("[cacheUpdate] update cache", tableName, id, bean)
+ cacher.PutBean(tableName, sid, bean)
+ }
+ }
+ session.Engine.logger.Debug("[cacheUpdate] clear cached table sql:", tableName)
+ cacher.ClearIds(tableName)
+ return nil
+}
+
+// Update records, bean's non-empty fields are updated contents,
+// condiBean' non-empty filds are conditions
+// CAUTION:
+// 1.bool will defaultly be updated content nor conditions
+// You should call UseBool if you have bool to use.
+// 2.float32 & float64 may be not inexact as conditions
+func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ v := rValue(bean)
+ t := v.Type()
+
+ var colNames []string
+ var args []interface{}
+
+ // handle before update processors
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
+ if processor, ok := interface{}(bean).(BeforeUpdateProcessor); ok {
+ processor.BeforeUpdate()
+ }
+ // --
+
+ var err error
+ var isMap = t.Kind() == reflect.Map
+ var isStruct = t.Kind() == reflect.Struct
+ if isStruct {
+ session.Statement.setRefValue(v)
+
+ if len(session.Statement.TableName()) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ if session.Statement.ColumnStr == "" {
+ colNames, args = buildUpdates(session.Engine, session.Statement.RefTable, bean, false, false,
+ false, false, session.Statement.allUseBool, session.Statement.useAllCols,
+ session.Statement.mustColumnMap, session.Statement.nullableMap,
+ session.Statement.columnMap, true, session.Statement.unscoped)
+ } else {
+ colNames, args, err = genCols(session.Statement.RefTable, session, bean, true, true)
+ if err != nil {
+ return 0, err
+ }
+ }
+ } else if isMap {
+ colNames = make([]string, 0)
+ args = make([]interface{}, 0)
+ bValue := reflect.Indirect(reflect.ValueOf(bean))
+
+ for _, v := range bValue.MapKeys() {
+ colNames = append(colNames, session.Engine.Quote(v.String())+" = ?")
+ args = append(args, bValue.MapIndex(v).Interface())
+ }
+ } else {
+ return 0, ErrParamsType
+ }
+
+ table := session.Statement.RefTable
+
+ if session.Statement.UseAutoTime && table != nil && table.Updated != "" {
+ colNames = append(colNames, session.Engine.Quote(table.Updated)+" = ?")
+ col := table.UpdatedColumn()
+ val, t := session.Engine.NowTime2(col.SQLType.Name)
+ args = append(args, val)
+
+ var colName = col.Name
+ if isStruct {
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ }
+ }
+
+ //for update action to like "column = column + ?"
+ incColumns := session.Statement.getInc()
+ for _, v := range incColumns {
+ colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" + ?")
+ args = append(args, v.arg)
+ }
+ //for update action to like "column = column - ?"
+ decColumns := session.Statement.getDec()
+ for _, v := range decColumns {
+ colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" - ?")
+ args = append(args, v.arg)
+ }
+ //for update action to like "column = expression"
+ exprColumns := session.Statement.getExpr()
+ for _, v := range exprColumns {
+ colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr)
+ }
+
+ session.Statement.processIdParam()
+
+ var autoCond builder.Cond
+ if !session.Statement.noAutoCondition && len(condiBean) > 0 {
+ var err error
+ autoCond, err = session.Statement.buildConds(session.Statement.RefTable, condiBean[0], true, true, false, true, false)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ st := session.Statement
+ defer session.resetStatement()
+
+ var sqlStr string
+ var condArgs []interface{}
+ var condSQL string
+ cond := session.Statement.cond.And(autoCond)
+
+ doIncVer := false
+ var verValue *reflect.Value
+ if table != nil && table.Version != "" && session.Statement.checkVersion {
+ verValue, err = table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ return 0, err
+ }
+
+ cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()})
+ condSQL, condArgs, _ = builder.ToSQL(cond)
+
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+
+ if st.LimitN > 0 {
+ condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+ }
+
+ sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v",
+ session.Engine.Quote(session.Statement.TableName()),
+ strings.Join(colNames, ", "),
+ session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1",
+ condSQL)
+
+ doIncVer = true
+ } else {
+ condSQL, condArgs, _ = builder.ToSQL(cond)
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+
+ if st.LimitN > 0 {
+ condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+ }
+
+ sqlStr = fmt.Sprintf("UPDATE %v SET %v %v",
+ session.Engine.Quote(session.Statement.TableName()),
+ strings.Join(colNames, ", "),
+ condSQL)
+ }
+
+ res, err := session.exec(sqlStr, append(args, condArgs...)...)
+ if err != nil {
+ return 0, err
+ } else if doIncVer {
+ if verValue != nil && verValue.IsValid() && verValue.CanSet() {
+ verValue.SetInt(verValue.Int() + 1)
+ }
+ }
+
+ if table != nil {
+ if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
+ cacher.ClearIds(session.Statement.TableName())
+ cacher.ClearBeans(session.Statement.TableName())
+ }
+ }
+
+ // handle after update processors
+ if session.IsAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
+ session.Engine.logger.Debug("[event]", session.Statement.TableName(), " has after update processor")
+ processor.AfterUpdate()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterUpdateBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ // FIXME: if bean is a map type, it will panic because map cannot be as map key
+ session.afterUpdateBeans[bean] = &afterClosures
+ }
+
+ } else {
+ if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ session.afterUpdateBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
+ // --
+
+ return res.RowsAffected()
+}
+
+func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
+ if session.Statement.RefTable == nil ||
+ session.Tx != nil {
+ return ErrCacheFailed
+ }
+
+ for _, filter := range session.Engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
+ }
+
+ newsql := session.Statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+
+ cacher := session.Engine.getCacher2(session.Statement.RefTable)
+ tableName := session.Statement.TableName()
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ if err != nil {
+ resultsSlice, err := session.query(newsql, args...)
+ if err != nil {
+ return err
+ }
+ ids = make([]core.PK, 0)
+ if len(resultsSlice) > 0 {
+ for _, data := range resultsSlice {
+ var id int64
+ var pk core.PK = make([]interface{}, 0)
+ for _, col := range session.Statement.RefTable.PKColumns() {
+ if v, ok := data[col.Name]; !ok {
+ return errors.New("no id")
+ } else if col.SQLType.IsText() {
+ pk = append(pk, string(v))
+ } else if col.SQLType.IsNumeric() {
+ id, err = strconv.ParseInt(string(v), 10, 64)
+ if err != nil {
+ return err
+ }
+ pk = append(pk, id)
+ } else {
+ return errors.New("not supported primary key type")
+ }
+ }
+ ids = append(ids, pk)
+ }
+ }
+ } /*else {
+ session.Engine.LogDebug("delete cache sql %v", newsql)
+ cacher.DelIds(tableName, genSqlKey(newsql, args))
+ }*/
+
+ for _, id := range ids {
+ session.Engine.logger.Debug("[cacheDelete] delete cache obj", tableName, id)
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ cacher.DelBean(tableName, sid)
+ }
+ session.Engine.logger.Debug("[cacheDelete] clear cache sql", tableName)
+ cacher.ClearIds(tableName)
+ return nil
+}
+
+// Delete records, bean's non-empty fields are conditions
+func (session *Session) Delete(bean interface{}) (int64, error) {
+ defer session.resetStatement()
+ if session.IsAutoClose {
+ defer session.Close()
+ }
+
+ session.Statement.setRefValue(rValue(bean))
+ var table = session.Statement.RefTable
+
+ // handle before delete processors
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures)
+
+ if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
+ processor.BeforeDelete()
+ }
+
+ // --
+ condSQL, condArgs, _ := session.Statement.genConds(bean)
+ if len(condSQL) == 0 && session.Statement.LimitN == 0 {
+ return 0, ErrNeedDeletedCond
+ }
+
+ var tableName = session.Engine.Quote(session.Statement.TableName())
+ var deleteSQL string
+ if len(condSQL) > 0 {
+ deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
+ } else {
+ deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName)
+ }
+
+ var orderSQL string
+ if len(session.Statement.OrderStr) > 0 {
+ orderSQL += fmt.Sprintf(" ORDER BY %s", session.Statement.OrderStr)
+ }
+ if session.Statement.LimitN > 0 {
+ orderSQL += fmt.Sprintf(" LIMIT %d", session.Statement.LimitN)
+ }
+
+ if len(orderSQL) > 0 {
+ switch session.Engine.dialect.DBType() {
+ case core.POSTGRES:
+ inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ deleteSQL += " AND " + inSQL
+ } else {
+ deleteSQL += " WHERE " + inSQL
+ }
+ case core.SQLITE:
+ inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ deleteSQL += " AND " + inSQL
+ } else {
+ deleteSQL += " WHERE " + inSQL
+ }
+ // TODO: how to handle delete limit on mssql?
+ case core.MSSQL:
+ return 0, ErrNotImplemented
+ default:
+ deleteSQL += orderSQL
+ }
+ }
+
+ var realSQL string
+ argsForCache := make([]interface{}, 0, len(condArgs)*2)
+ if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled
+ realSQL = deleteSQL
+ copy(argsForCache, condArgs)
+ argsForCache = append(condArgs, argsForCache...)
+ } else {
+ // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache.
+ copy(argsForCache, condArgs)
+ argsForCache = append(condArgs, argsForCache...)
+
+ deletedColumn := table.DeletedColumn()
+ realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
+ session.Engine.Quote(session.Statement.TableName()),
+ session.Engine.Quote(deletedColumn.Name),
+ condSQL)
+
+ if len(orderSQL) > 0 {
+ switch session.Engine.dialect.DBType() {
+ case core.POSTGRES:
+ inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ realSQL += " AND " + inSQL
+ } else {
+ realSQL += " WHERE " + inSQL
+ }
+ case core.SQLITE:
+ inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ realSQL += " AND " + inSQL
+ } else {
+ realSQL += " WHERE " + inSQL
+ }
+ // TODO: how to handle delete limit on mssql?
+ case core.MSSQL:
+ return 0, ErrNotImplemented
+ default:
+ realSQL += orderSQL
+ }
+ }
+
+ // !oinume! Insert NowTime to the head of session.Statement.Params
+ condArgs = append(condArgs, "")
+ paramsLen := len(condArgs)
+ copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
+
+ val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name)
+ condArgs[0] = val
+
+ var colName = deletedColumn.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ }
+
+ if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
+ session.cacheDelete(deleteSQL, argsForCache...)
+ }
+
+ res, err := session.exec(realSQL, condArgs...)
+ if err != nil {
+ return 0, err
+ }
+
+ // handle after delete processors
+ if session.IsAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
+ processor.AfterDelete()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterDeleteBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterDeleteBeans[bean] = &afterClosures
+ }
+ } else {
+ if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ session.afterDeleteBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures)
+ // --
+
+ return res.RowsAffected()
+}
+
+// saveLastSQL stores executed query information
+func (session *Session) saveLastSQL(sql string, args ...interface{}) {
+ session.lastSQL = sql
+ session.lastSQLArgs = args
+ session.Engine.logSQL(sql, args...)
+}
+
+// LastSQL returns last query information
+func (session *Session) LastSQL() (string, []interface{}) {
+ return session.lastSQL, session.lastSQLArgs
+}
+
+// tbName get some table's table name
+func (session *Session) tbNameNoSchema(table *core.Table) string {
+ if len(session.Statement.AltTableName) > 0 {
+ return session.Statement.AltTableName
+ }
+
+ return table.Name
+}
+
+// Sync2 synchronize structs to database tables
+func (session *Session) Sync2(beans ...interface{}) error {
+ engine := session.Engine
+
+ tables, err := engine.DBMetas()
+ if err != nil {
+ return err
+ }
+
+ var structTables []*core.Table
+
+ for _, bean := range beans {
+ v := rValue(bean)
+ table := engine.mapType(v)
+ structTables = append(structTables, table)
+ var tbName = session.tbNameNoSchema(table)
+
+ var oriTable *core.Table
+ for _, tb := range tables {
+ if strings.EqualFold(tb.Name, tbName) {
+ oriTable = tb
+ break
+ }
+ }
+
+ if oriTable == nil {
+ err = session.StoreEngine(session.Statement.StoreEngine).CreateTable(bean)
+ if err != nil {
+ return err
+ }
+
+ err = session.CreateUniques(bean)
+ if err != nil {
+ return err
+ }
+
+ err = session.CreateIndexes(bean)
+ 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
+ }
+ }
+
+ 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",
+ tbName, col.Name, curType, expectedType)
+ _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))
+ } else {
+ engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n",
+ tbName, 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",
+ tbName, col.Name, oriCol.Length, col.Length)
+ _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, 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",
+ tbName, 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",
+ tbName, col.Name, oriCol.Length, col.Length)
+ _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, 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 := engine.NewSession()
+ session.Statement.RefTable = table
+ session.Statement.tableName = tbName
+ defer session.Close()
+ err = session.addColumn(col.Name)
+ }
+ 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 index.Equal(index2) {
+ oriIndex = index2
+ foundIndexNames[name2] = true
+ break
+ }
+ }
+
+ if oriIndex != nil {
+ if oriIndex.Type != index.Type {
+ sql := engine.dialect.DropIndexSql(tbName, oriIndex)
+ _, err = engine.Exec(sql)
+ if err != nil {
+ return err
+ }
+ oriIndex = nil
+ }
+ }
+
+ if oriIndex == nil {
+ addedNames[name] = index
+ }
+ }
+
+ for name2, index2 := range oriTable.Indexes {
+ if _, ok := foundIndexNames[name2]; !ok {
+ sql := engine.dialect.DropIndexSql(tbName, index2)
+ _, err = engine.Exec(sql)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ for name, index := range addedNames {
+ if index.Type == core.UniqueType {
+ session := engine.NewSession()
+ session.Statement.RefTable = table
+ session.Statement.tableName = tbName
+ defer session.Close()
+ err = session.addUnique(tbName, name)
+ } else if index.Type == core.IndexType {
+ session := engine.NewSession()
+ session.Statement.RefTable = table
+ session.Statement.tableName = tbName
+ defer session.Close()
+ err = session.addIndex(tbName, name)
+ }
+ 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
+ }
+ }
+
+ 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", table.Name, colName)
+ }
+ }
+ }
+ return nil
+}
+
+// Unscoped always disable struct tag "deleted"
+func (session *Session) Unscoped() *Session {
+ session.Statement.Unscoped()
+ return session
+}
diff --git a/vendor/github.com/go-xorm/xorm/sqlite3_dialect.go b/vendor/github.com/go-xorm/xorm/sqlite3_dialect.go
new file mode 100644
index 0000000000..20c75a4a9f
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/sqlite3_dialect.go
@@ -0,0 +1,435 @@
+// Copyright 2015 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 (
+ "database/sql"
+ "errors"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/go-xorm/core"
+)
+
+// func init() {
+// RegisterDialect("sqlite3", &sqlite3{})
+// }
+
+var (
+ sqlite3ReservedWords = map[string]bool{
+ "ABORT": true,
+ "ACTION": true,
+ "ADD": true,
+ "AFTER": true,
+ "ALL": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "AS": true,
+ "ASC": true,
+ "ATTACH": true,
+ "AUTOINCREMENT": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BETWEEN": true,
+ "BY": true,
+ "CASCADE": true,
+ "CASE": true,
+ "CAST": true,
+ "CHECK": true,
+ "COLLATE": true,
+ "COLUMN": true,
+ "COMMIT": true,
+ "CONFLICT": true,
+ "CONSTRAINT": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CURRENT_DATE": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "DATABASE": true,
+ "DEFAULT": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DELETE": true,
+ "DESC": true,
+ "DETACH": true,
+ "DISTINCT": true,
+ "DROP": true,
+ "EACH": true,
+ "ELSE": true,
+ "END": true,
+ "ESCAPE": true,
+ "EXCEPT": true,
+ "EXCLUSIVE": true,
+ "EXISTS": true,
+ "EXPLAIN": true,
+ "FAIL": true,
+ "FOR": true,
+ "FOREIGN": true,
+ "FROM": true,
+ "FULL": true,
+ "GLOB": true,
+ "GROUP": true,
+ "HAVING": true,
+ "IF": true,
+ "IGNORE": true,
+ "IMMEDIATE": true,
+ "IN": true,
+ "INDEX": true,
+ "INDEXED": true,
+ "INITIALLY": true,
+ "INNER": true,
+ "INSERT": true,
+ "INSTEAD": true,
+ "INTERSECT": true,
+ "INTO": true,
+ "IS": true,
+ "ISNULL": true,
+ "JOIN": true,
+ "KEY": true,
+ "LEFT": true,
+ "LIKE": true,
+ "LIMIT": true,
+ "MATCH": true,
+ "NATURAL": true,
+ "NO": true,
+ "NOT": true,
+ "NOTNULL": true,
+ "NULL": true,
+ "OF": true,
+ "OFFSET": true,
+ "ON": true,
+ "OR": true,
+ "ORDER": true,
+ "OUTER": true,
+ "PLAN": true,
+ "PRAGMA": true,
+ "PRIMARY": true,
+ "QUERY": true,
+ "RAISE": true,
+ "RECURSIVE": true,
+ "REFERENCES": true,
+ "REGEXP": true,
+ "REINDEX": true,
+ "RELEASE": true,
+ "RENAME": true,
+ "REPLACE": true,
+ "RESTRICT": true,
+ "RIGHT": true,
+ "ROLLBACK": true,
+ "ROW": true,
+ "SAVEPOINT": true,
+ "SELECT": true,
+ "SET": true,
+ "TABLE": true,
+ "TEMP": true,
+ "TEMPORARY": true,
+ "THEN": true,
+ "TO": true,
+ "TRANSACTI": true,
+ "TRIGGER": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UPDATE": true,
+ "USING": true,
+ "VACUUM": true,
+ "VALUES": true,
+ "VIEW": true,
+ "VIRTUAL": true,
+ "WHEN": true,
+ "WHERE": true,
+ "WITH": true,
+ "WITHOUT": true,
+ }
+)
+
+type sqlite3 struct {
+ core.Base
+}
+
+func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *sqlite3) SqlType(c *core.Column) string {
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ if c.Default == "true" {
+ c.Default = "1"
+ } else if c.Default == "false" {
+ c.Default = "0"
+ }
+ return core.Integer
+ case core.Date, core.DateTime, core.TimeStamp, core.Time:
+ return core.DateTime
+ case core.TimeStampz:
+ return core.Text
+ case core.Char, core.Varchar, core.NVarchar, core.TinyText,
+ core.Text, core.MediumText, core.LongText, core.Json:
+ return core.Text
+ case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt:
+ return core.Integer
+ case core.Float, core.Double, core.Real:
+ return core.Real
+ case core.Decimal, core.Numeric:
+ return core.Numeric
+ case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
+ return core.Blob
+ case core.Serial, core.BigSerial:
+ c.IsPrimaryKey = true
+ c.IsAutoIncrement = true
+ c.Nullable = false
+ return core.Integer
+ default:
+ return t
+ }
+}
+
+func (db *sqlite3) FormatBytes(bs []byte) string {
+ return fmt.Sprintf("X'%x'", bs)
+}
+
+func (db *sqlite3) SupportInsertMany() bool {
+ return true
+}
+
+func (db *sqlite3) IsReserved(name string) bool {
+ _, ok := sqlite3ReservedWords[name]
+ return ok
+}
+
+func (db *sqlite3) Quote(name string) string {
+ return "`" + name + "`"
+}
+
+func (db *sqlite3) QuoteStr() string {
+ return "`"
+}
+
+func (db *sqlite3) AutoIncrStr() string {
+ return "AUTOINCREMENT"
+}
+
+func (db *sqlite3) SupportEngine() bool {
+ return false
+}
+
+func (db *sqlite3) SupportCharset() bool {
+ return false
+}
+
+func (db *sqlite3) IndexOnTable() bool {
+ return false
+}
+
+func (db *sqlite3) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{idxName}
+ return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
+}
+
+func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
+}
+
+func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string {
+ quote := db.Quote
+ //var unique string
+ var idxName string = index.Name
+ if !strings.HasPrefix(idxName, "UQE_") &&
+ !strings.HasPrefix(idxName, "IDX_") {
+ if index.Type == core.UniqueType {
+ idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
+ } else {
+ idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
+ }
+ }
+ return fmt.Sprintf("DROP INDEX %v", quote(idxName))
+}
+
+func (db *sqlite3) ForUpdateSql(query string) string {
+ return query
+}
+
+/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
+ return sql, args
+}*/
+
+func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{tableName}
+ query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
+ db.LogSQL(query, args)
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ return true, nil
+ }
+ return false, 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 = ?"
+ db.LogSQL(s, args)
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ var name string
+ for rows.Next() {
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, nil, err
+ }
+ break
+ }
+
+ if name == "" {
+ return nil, nil, errors.New("no table named " + tableName)
+ }
+
+ nStart := strings.Index(name, "(")
+ nEnd := strings.LastIndex(name, ")")
+ reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
+ 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, ",")
+ 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(field, "`[] ")
+ continue
+ } else if idx == 1 {
+ col.SQLType = core.SQLType{field, 0, 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 + "'"
+ }
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *sqlite3) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := "SELECT name FROM sqlite_master WHERE type='table'"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ err = rows.Scan(&table.Name)
+ if err != nil {
+ return nil, err
+ }
+ if table.Name == "sqlite_sequence" {
+ continue
+ }
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var tmpSql sql.NullString
+ err = rows.Scan(&tmpSql)
+ if err != nil {
+ return nil, err
+ }
+
+ if !tmpSql.Valid {
+ continue
+ }
+ sql := tmpSql.String
+
+ index := new(core.Index)
+ nNStart := strings.Index(sql, "INDEX")
+ nNEnd := strings.Index(sql, "ON")
+ if nNStart == -1 || nNEnd == -1 {
+ continue
+ }
+
+ indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ index.Name = indexName[5+len(tableName) : len(indexName)]
+ } else {
+ index.Name = indexName
+ }
+
+ if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
+ index.Type = core.UniqueType
+ } else {
+ index.Type = core.IndexType
+ }
+
+ nStart := strings.Index(sql, "(")
+ nEnd := strings.Index(sql, ")")
+ colIndexes := strings.Split(sql[nStart+1:nEnd], ",")
+
+ index.Cols = make([]string, 0)
+ for _, col := range colIndexes {
+ index.Cols = append(index.Cols, strings.Trim(col, "` []"))
+ }
+ indexes[index.Name] = index
+ }
+
+ return indexes, nil
+}
+
+func (db *sqlite3) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}}
+}
diff --git a/vendor/github.com/go-xorm/xorm/sqlite3_driver.go b/vendor/github.com/go-xorm/xorm/sqlite3_driver.go
new file mode 100644
index 0000000000..6ae19569ef
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/sqlite3_driver.go
@@ -0,0 +1,20 @@
+// Copyright 2015 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 (
+ "github.com/go-xorm/core"
+)
+
+// func init() {
+// core.RegisterDriver("sqlite3", &sqlite3Driver{})
+// }
+
+type sqlite3Driver struct {
+}
+
+func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
+}
diff --git a/vendor/github.com/go-xorm/xorm/statement.go b/vendor/github.com/go-xorm/xorm/statement.go
new file mode 100644
index 0000000000..82b86674e4
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/statement.go
@@ -0,0 +1,1359 @@
+// Copyright 2015 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 (
+ "bytes"
+ "database/sql/driver"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "time"
+
+ "github.com/go-xorm/builder"
+ "github.com/go-xorm/core"
+)
+
+type incrParam struct {
+ colName string
+ arg interface{}
+}
+
+type decrParam struct {
+ colName string
+ arg interface{}
+}
+
+type exprParam struct {
+ colName string
+ expr string
+}
+
+// Statement save all the sql info for executing SQL
+type Statement struct {
+ RefTable *core.Table
+ Engine *Engine
+ Start int
+ LimitN int
+ IdParam *core.PK
+ OrderStr string
+ JoinStr string
+ joinArgs []interface{}
+ GroupByStr string
+ HavingStr string
+ ColumnStr string
+ selectStr string
+ columnMap map[string]bool
+ useAllCols bool
+ OmitStr string
+ AltTableName string
+ tableName string
+ RawSQL string
+ RawParams []interface{}
+ UseCascade bool
+ UseAutoJoin bool
+ StoreEngine string
+ Charset string
+ UseCache bool
+ UseAutoTime bool
+ noAutoCondition bool
+ IsDistinct bool
+ IsForUpdate bool
+ TableAlias string
+ allUseBool bool
+ checkVersion bool
+ unscoped bool
+ mustColumnMap map[string]bool
+ nullableMap map[string]bool
+ incrColumns map[string]incrParam
+ decrColumns map[string]decrParam
+ exprColumns map[string]exprParam
+ cond builder.Cond
+}
+
+// Init reset all the statment's fields
+func (statement *Statement) Init() {
+ statement.RefTable = nil
+ statement.Start = 0
+ statement.LimitN = 0
+ statement.OrderStr = ""
+ statement.UseCascade = true
+ statement.JoinStr = ""
+ statement.joinArgs = make([]interface{}, 0)
+ statement.GroupByStr = ""
+ statement.HavingStr = ""
+ statement.ColumnStr = ""
+ statement.OmitStr = ""
+ statement.columnMap = make(map[string]bool)
+ statement.AltTableName = ""
+ statement.tableName = ""
+ statement.IdParam = nil
+ statement.RawSQL = ""
+ statement.RawParams = make([]interface{}, 0)
+ statement.UseCache = true
+ statement.UseAutoTime = true
+ statement.noAutoCondition = false
+ statement.IsDistinct = false
+ statement.IsForUpdate = false
+ statement.TableAlias = ""
+ statement.selectStr = ""
+ statement.allUseBool = false
+ statement.useAllCols = false
+ statement.mustColumnMap = make(map[string]bool)
+ statement.nullableMap = make(map[string]bool)
+ statement.checkVersion = true
+ statement.unscoped = false
+ statement.incrColumns = make(map[string]incrParam)
+ statement.decrColumns = make(map[string]decrParam)
+ statement.exprColumns = make(map[string]exprParam)
+ statement.cond = builder.NewCond()
+}
+
+// NoAutoCondition if you do not want convert bean's field as query condition, then use this function
+func (statement *Statement) NoAutoCondition(no ...bool) *Statement {
+ statement.noAutoCondition = true
+ if len(no) > 0 {
+ statement.noAutoCondition = no[0]
+ }
+ return statement
+}
+
+// Alias set the table alias
+func (statement *Statement) Alias(alias string) *Statement {
+ statement.TableAlias = alias
+ return statement
+}
+
+// Sql add the raw sql statement
+func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case (*builder.Builder):
+ var err error
+ statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL()
+ if err != nil {
+ statement.Engine.logger.Error(err)
+ }
+ case string:
+ statement.RawSQL = query.(string)
+ statement.RawParams = args
+ default:
+ statement.Engine.logger.Error("unsupported sql type")
+ }
+
+ return statement
+}
+
+// Where add Where statment
+func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement {
+ return statement.And(query, args...)
+}
+
+// And add Where & and statment
+func (statement *Statement) And(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case string:
+ cond := builder.Expr(query.(string), args...)
+ statement.cond = statement.cond.And(cond)
+ case builder.Cond:
+ cond := query.(builder.Cond)
+ statement.cond = statement.cond.And(cond)
+ for _, v := range args {
+ if vv, ok := v.(builder.Cond); ok {
+ statement.cond = statement.cond.And(vv)
+ }
+ }
+ default:
+ // TODO: not support condition type
+ }
+
+ return statement
+}
+
+// Or add Where & Or statment
+func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case string:
+ cond := builder.Expr(query.(string), args...)
+ statement.cond = statement.cond.Or(cond)
+ case builder.Cond:
+ cond := query.(builder.Cond)
+ statement.cond = statement.cond.Or(cond)
+ for _, v := range args {
+ if vv, ok := v.(builder.Cond); ok {
+ statement.cond = statement.cond.Or(vv)
+ }
+ }
+ default:
+ // TODO: not support condition type
+ }
+ return statement
+}
+
+// In generate "Where column IN (?) " statment
+func (statement *Statement) In(column string, args ...interface{}) *Statement {
+ if len(args) == 0 {
+ return statement
+ }
+
+ in := builder.In(column, args...)
+ statement.cond = statement.cond.And(in)
+ return statement
+}
+
+// NotIn generate "Where column NOT IN (?) " statment
+func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
+ if len(args) == 0 {
+ return statement
+ }
+
+ in := builder.NotIn(column, args...)
+ statement.cond = statement.cond.And(in)
+ return statement
+}
+
+func (statement *Statement) setRefValue(v reflect.Value) {
+ statement.RefTable = statement.Engine.autoMapType(reflect.Indirect(v))
+ statement.tableName = statement.Engine.tbName(v)
+}
+
+// Table tempororily set table name, the parameter could be a string or a pointer of struct
+func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
+ v := rValue(tableNameOrBean)
+ t := v.Type()
+ if t.Kind() == reflect.String {
+ statement.AltTableName = tableNameOrBean.(string)
+ } else if t.Kind() == reflect.Struct {
+ statement.RefTable = statement.Engine.autoMapType(v)
+ statement.AltTableName = statement.Engine.tbName(v)
+ }
+ return statement
+}
+
+// Auto generating update columnes and values according a struct
+func buildUpdates(engine *Engine, table *core.Table, bean interface{},
+ includeVersion bool, includeUpdated bool, includeNil bool,
+ includeAutoIncr bool, allUseBool bool, useAllCols bool,
+ mustColumnMap map[string]bool, nullableMap map[string]bool,
+ columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) {
+
+ var colNames = make([]string, 0)
+ var args = make([]interface{}, 0)
+ for _, col := range table.Columns() {
+ if !includeVersion && col.IsVersion {
+ continue
+ }
+ if col.IsCreated {
+ continue
+ }
+ if !includeUpdated && col.IsUpdated {
+ continue
+ }
+ if !includeAutoIncr && col.IsAutoIncrement {
+ continue
+ }
+ if col.IsDeleted && !unscoped {
+ continue
+ }
+ if use, ok := columnMap[col.Name]; ok && !use {
+ continue
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+
+ fieldValue := *fieldValuePtr
+ fieldType := reflect.TypeOf(fieldValue.Interface())
+
+ requiredField := useAllCols
+ includeNil := useAllCols
+ lColName := strings.ToLower(col.Name)
+
+ if b, ok := mustColumnMap[lColName]; ok {
+ if b {
+ requiredField = true
+ } else {
+ continue
+ }
+ }
+
+ // !evalphobia! set fieldValue as nil when column is nullable and zero-value
+ if b, ok := nullableMap[lColName]; ok {
+ if b && col.Nullable && isZero(fieldValue.Interface()) {
+ var nilValue *int
+ fieldValue = reflect.ValueOf(nilValue)
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ includeNil = true
+ }
+ }
+
+ var val interface{}
+
+ if fieldValue.CanAddr() {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ data, err := structConvert.ToDB()
+ if err != nil {
+ engine.logger.Error(err)
+ } else {
+ val = data
+ }
+ goto APPEND
+ }
+ }
+
+ if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ data, err := structConvert.ToDB()
+ if err != nil {
+ engine.logger.Error(err)
+ } else {
+ val = data
+ }
+ goto APPEND
+ }
+
+ if fieldType.Kind() == reflect.Ptr {
+ if fieldValue.IsNil() {
+ if includeNil {
+ args = append(args, nil)
+ colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
+ }
+ continue
+ } else if !fieldValue.IsValid() {
+ continue
+ } else {
+ // dereference ptr type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ requiredField = true
+ }
+ }
+
+ switch fieldType.Kind() {
+ case reflect.Bool:
+ if allUseBool || requiredField {
+ val = fieldValue.Interface()
+ } else {
+ // if a bool in a struct, it will not be as a condition because it default is false,
+ // please use Where() instead
+ continue
+ }
+ case reflect.String:
+ if !requiredField && fieldValue.String() == "" {
+ continue
+ }
+ // for MyString, should convert to string or panic
+ if fieldType.String() != reflect.String.String() {
+ val = fieldValue.String()
+ } else {
+ val = fieldValue.Interface()
+ }
+ case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
+ if !requiredField && fieldValue.Int() == 0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Float32, reflect.Float64:
+ if !requiredField && fieldValue.Float() == 0.0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
+ if !requiredField && fieldValue.Uint() == 0 {
+ continue
+ }
+ t := int64(fieldValue.Uint())
+ val = reflect.ValueOf(&t).Interface()
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
+ continue
+ }
+ val = engine.FormatTime(col.SQLType.Name, t)
+ } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok {
+ val, _ = nulType.Value()
+ } else {
+ if !col.SQLType.IsJson() {
+ engine.autoMapType(fieldValue)
+ if table, ok := engine.Tables[fieldValue.Type()]; ok {
+ if len(table.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
+ // fix non-int pk issues
+ if pkField.IsValid() && (!requiredField && !isZero(pkField.Interface())) {
+ val = pkField.Interface()
+ } else {
+ continue
+ }
+ } else {
+ //TODO: how to handler?
+ panic("not supported")
+ }
+ } else {
+ val = fieldValue.Interface()
+ }
+ } else {
+ // Blank struct could not be as update data
+ if requiredField || !isStructZero(fieldValue) {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface()))
+ }
+ if col.SQLType.IsText() {
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ }
+ }
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if !requiredField {
+ if fieldValue == reflect.Zero(fieldType) {
+ continue
+ }
+ if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+ continue
+ }
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
+ fieldType.Elem().Kind() == reflect.Uint8 {
+ if fieldValue.Len() > 0 {
+ val = fieldValue.Bytes()
+ } else {
+ continue
+ }
+ } else {
+ bytes, err = json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ default:
+ val = fieldValue.Interface()
+ }
+
+ APPEND:
+ args = append(args, val)
+ if col.IsPrimaryKey && engine.dialect.DBType() == "ql" {
+ continue
+ }
+ colNames = append(colNames, fmt.Sprintf("%v = ?", engine.Quote(col.Name)))
+ }
+
+ return colNames, args
+}
+
+func (statement *Statement) needTableName() bool {
+ return len(statement.JoinStr) > 0
+}
+
+func (statement *Statement) colName(col *core.Column, tableName string) string {
+ if statement.needTableName() {
+ var nm = tableName
+ if len(statement.TableAlias) > 0 {
+ nm = statement.TableAlias
+ }
+ return statement.Engine.Quote(nm) + "." + statement.Engine.Quote(col.Name)
+ }
+ return statement.Engine.Quote(col.Name)
+}
+
+func buildConds(engine *Engine, table *core.Table, bean interface{},
+ includeVersion bool, includeUpdated bool, includeNil bool,
+ includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
+ mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) {
+ var conds []builder.Cond
+ for _, col := range table.Columns() {
+ if !includeVersion && col.IsVersion {
+ continue
+ }
+ if !includeUpdated && col.IsUpdated {
+ continue
+ }
+ if !includeAutoIncr && col.IsAutoIncrement {
+ continue
+ }
+
+ if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
+ continue
+ }
+ if col.SQLType.IsJson() {
+ continue
+ }
+
+ var colName string
+ if addedTableName {
+ var nm = tableName
+ if len(aliasName) > 0 {
+ nm = aliasName
+ }
+ colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
+ } else {
+ colName = engine.Quote(col.Name)
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+
+ if col.IsDeleted && !unscoped { // tag "deleted" is enabled
+ conds = append(conds, builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"}))
+ }
+
+ fieldValue := *fieldValuePtr
+ if fieldValue.Interface() == nil {
+ continue
+ }
+
+ fieldType := reflect.TypeOf(fieldValue.Interface())
+ requiredField := useAllCols
+ if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
+ if b {
+ requiredField = true
+ } else {
+ continue
+ }
+ }
+
+ if fieldType.Kind() == reflect.Ptr {
+ if fieldValue.IsNil() {
+ if includeNil {
+ conds = append(conds, builder.Eq{colName: nil})
+ }
+ continue
+ } else if !fieldValue.IsValid() {
+ continue
+ } else {
+ // dereference ptr type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ requiredField = true
+ }
+ }
+
+ var val interface{}
+ switch fieldType.Kind() {
+ case reflect.Bool:
+ if allUseBool || requiredField {
+ val = fieldValue.Interface()
+ } else {
+ // if a bool in a struct, it will not be as a condition because it default is false,
+ // please use Where() instead
+ continue
+ }
+ case reflect.String:
+ if !requiredField && fieldValue.String() == "" {
+ continue
+ }
+ // for MyString, should convert to string or panic
+ if fieldType.String() != reflect.String.String() {
+ val = fieldValue.String()
+ } else {
+ val = fieldValue.Interface()
+ }
+ case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
+ if !requiredField && fieldValue.Int() == 0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Float32, reflect.Float64:
+ if !requiredField && fieldValue.Float() == 0.0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
+ if !requiredField && fieldValue.Uint() == 0 {
+ continue
+ }
+ t := int64(fieldValue.Uint())
+ val = reflect.ValueOf(&t).Interface()
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
+ continue
+ }
+ val = engine.FormatTime(col.SQLType.Name, t)
+ } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
+ continue
+ } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
+ val, _ = valNul.Value()
+ if val == nil {
+ continue
+ }
+ } else {
+ if col.SQLType.IsJson() {
+ if col.SQLType.IsText() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ bytes, err = json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ engine.autoMapType(fieldValue)
+ if table, ok := engine.Tables[fieldValue.Type()]; ok {
+ if len(table.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
+ // fix non-int pk issues
+ //if pkField.Int() != 0 {
+ if pkField.IsValid() && !isZero(pkField.Interface()) {
+ val = pkField.Interface()
+ } else {
+ continue
+ }
+ } else {
+ //TODO: how to handler?
+ panic(fmt.Sprintln("not supported", fieldValue.Interface(), "as", table.PrimaryKeys))
+ }
+ } else {
+ val = fieldValue.Interface()
+ }
+ }
+ }
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if fieldValue == reflect.Zero(fieldType) {
+ continue
+ }
+ if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+ continue
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
+ fieldType.Elem().Kind() == reflect.Uint8 {
+ if fieldValue.Len() > 0 {
+ val = fieldValue.Bytes()
+ } else {
+ continue
+ }
+ } else {
+ bytes, err = json.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ default:
+ val = fieldValue.Interface()
+ }
+
+ conds = append(conds, builder.Eq{colName: val})
+ }
+
+ return builder.And(conds...), nil
+}
+
+// TableName return current tableName
+func (statement *Statement) TableName() string {
+ if statement.AltTableName != "" {
+ return statement.AltTableName
+ }
+
+ return statement.tableName
+}
+
+// Id generate "where id = ? " statment or for composite key "where key1 = ? and key2 = ?"
+func (statement *Statement) Id(id interface{}) *Statement {
+ idValue := reflect.ValueOf(id)
+ idType := reflect.TypeOf(idValue.Interface())
+
+ switch idType {
+ case ptrPkType:
+ if pkPtr, ok := (id).(*core.PK); ok {
+ statement.IdParam = pkPtr
+ return statement
+ }
+ case pkType:
+ if pk, ok := (id).(core.PK); ok {
+ statement.IdParam = &pk
+ return statement
+ }
+ }
+
+ switch idType.Kind() {
+ case reflect.String:
+ statement.IdParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
+ return statement
+ }
+
+ statement.IdParam = &core.PK{id}
+ return statement
+}
+
+// Incr Generate "Update ... Set column = column + arg" statment
+func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
+ k := strings.ToLower(column)
+ if len(arg) > 0 {
+ statement.incrColumns[k] = incrParam{column, arg[0]}
+ } else {
+ statement.incrColumns[k] = incrParam{column, 1}
+ }
+ return statement
+}
+
+// Decr Generate "Update ... Set column = column - arg" statment
+func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
+ k := strings.ToLower(column)
+ if len(arg) > 0 {
+ statement.decrColumns[k] = decrParam{column, arg[0]}
+ } else {
+ statement.decrColumns[k] = decrParam{column, 1}
+ }
+ return statement
+}
+
+// SetExpr Generate "Update ... Set column = {expression}" statment
+func (statement *Statement) SetExpr(column string, expression string) *Statement {
+ k := strings.ToLower(column)
+ statement.exprColumns[k] = exprParam{column, expression}
+ return statement
+}
+
+// Generate "Update ... Set column = column + arg" statment
+func (statement *Statement) getInc() map[string]incrParam {
+ return statement.incrColumns
+}
+
+// Generate "Update ... Set column = column - arg" statment
+func (statement *Statement) getDec() map[string]decrParam {
+ return statement.decrColumns
+}
+
+// Generate "Update ... Set column = {expression}" statment
+func (statement *Statement) getExpr() map[string]exprParam {
+ return statement.exprColumns
+}
+
+func (statement *Statement) col2NewColsWithQuote(columns ...string) []string {
+ newColumns := make([]string, 0)
+ for _, col := range columns {
+ col = strings.Replace(col, "`", "", -1)
+ col = strings.Replace(col, statement.Engine.QuoteStr(), "", -1)
+ ccols := strings.Split(col, ",")
+ for _, c := range ccols {
+ fields := strings.Split(strings.TrimSpace(c), ".")
+ if len(fields) == 1 {
+ newColumns = append(newColumns, statement.Engine.quote(fields[0]))
+ } else if len(fields) == 2 {
+ newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+
+ statement.Engine.quote(fields[1]))
+ } else {
+ panic(errors.New("unwanted colnames"))
+ }
+ }
+ }
+ return newColumns
+}
+
+// Generate "Distince col1, col2 " statment
+func (statement *Statement) Distinct(columns ...string) *Statement {
+ statement.IsDistinct = true
+ statement.Cols(columns...)
+ return statement
+}
+
+// Generate "SELECT ... FOR UPDATE" statment
+func (statement *Statement) ForUpdate() *Statement {
+ statement.IsForUpdate = true
+ return statement
+}
+
+// Select replace select
+func (s *Statement) Select(str string) *Statement {
+ s.selectStr = str
+ return s
+}
+
+// Cols generate "col1, col2" statement
+func (statement *Statement) Cols(columns ...string) *Statement {
+ cols := col2NewCols(columns...)
+ for _, nc := range cols {
+ statement.columnMap[strings.ToLower(nc)] = true
+ }
+
+ newColumns := statement.col2NewColsWithQuote(columns...)
+ statement.ColumnStr = strings.Join(newColumns, ", ")
+ statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1)
+ return statement
+}
+
+// AllCols update use only: update all columns
+func (statement *Statement) AllCols() *Statement {
+ statement.useAllCols = true
+ return statement
+}
+
+// MustCols update use only: must update columns
+func (statement *Statement) MustCols(columns ...string) *Statement {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.mustColumnMap[strings.ToLower(nc)] = true
+ }
+ return statement
+}
+
+// UseBool indicates that use bool fields as update contents and query contiditions
+func (statement *Statement) UseBool(columns ...string) *Statement {
+ if len(columns) > 0 {
+ statement.MustCols(columns...)
+ } else {
+ statement.allUseBool = true
+ }
+ return statement
+}
+
+// Omit do not use the columns
+func (statement *Statement) Omit(columns ...string) {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.columnMap[strings.ToLower(nc)] = false
+ }
+ statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", ")))
+}
+
+// Nullable Update use only: update columns to null when value is nullable and zero-value
+func (statement *Statement) Nullable(columns ...string) {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.nullableMap[strings.ToLower(nc)] = true
+ }
+}
+
+// Top generate LIMIT limit statement
+func (statement *Statement) Top(limit int) *Statement {
+ statement.Limit(limit)
+ return statement
+}
+
+// Limit generate LIMIT start, limit statement
+func (statement *Statement) Limit(limit int, start ...int) *Statement {
+ statement.LimitN = limit
+ if len(start) > 0 {
+ statement.Start = start[0]
+ }
+ return statement
+}
+
+// OrderBy generate "Order By order" statement
+func (statement *Statement) OrderBy(order string) *Statement {
+ if len(statement.OrderStr) > 0 {
+ statement.OrderStr += ", "
+ }
+ statement.OrderStr += order
+ return statement
+}
+
+// Desc generate `ORDER BY xx DESC`
+func (statement *Statement) Desc(colNames ...string) *Statement {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, statement.OrderStr)
+ if len(statement.OrderStr) > 0 {
+ fmt.Fprint(&buf, ", ")
+ }
+ newColNames := statement.col2NewColsWithQuote(colNames...)
+ fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, "))
+ statement.OrderStr = buf.String()
+ return statement
+}
+
+// Asc provide asc order by query condition, the input parameters are columns.
+func (statement *Statement) Asc(colNames ...string) *Statement {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, statement.OrderStr)
+ if len(statement.OrderStr) > 0 {
+ fmt.Fprint(&buf, ", ")
+ }
+ newColNames := statement.col2NewColsWithQuote(colNames...)
+ fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, "))
+ statement.OrderStr = buf.String()
+ return statement
+}
+
+// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
+ var buf bytes.Buffer
+ if len(statement.JoinStr) > 0 {
+ fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
+ } else {
+ fmt.Fprintf(&buf, "%v JOIN ", joinOP)
+ }
+
+ switch tablename.(type) {
+ case []string:
+ t := tablename.([]string)
+ if len(t) > 1 {
+ fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1]))
+ } else if len(t) == 1 {
+ fmt.Fprintf(&buf, statement.Engine.Quote(t[0]))
+ }
+ case []interface{}:
+ t := tablename.([]interface{})
+ l := len(t)
+ var table string
+ if l > 0 {
+ f := t[0]
+ v := rValue(f)
+ t := v.Type()
+ if t.Kind() == reflect.String {
+ table = f.(string)
+ } else if t.Kind() == reflect.Struct {
+ table = statement.Engine.tbName(v)
+ }
+ }
+ if l > 1 {
+ fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(table),
+ statement.Engine.Quote(fmt.Sprintf("%v", t[1])))
+ } else if l == 1 {
+ fmt.Fprintf(&buf, statement.Engine.Quote(table))
+ }
+ default:
+ fmt.Fprintf(&buf, statement.Engine.Quote(fmt.Sprintf("%v", tablename)))
+ }
+
+ fmt.Fprintf(&buf, " ON %v", condition)
+ statement.JoinStr = buf.String()
+ statement.joinArgs = append(statement.joinArgs, args...)
+ return statement
+}
+
+// GroupBy generate "Group By keys" statement
+func (statement *Statement) GroupBy(keys string) *Statement {
+ statement.GroupByStr = keys
+ return statement
+}
+
+// Having generate "Having conditions" statement
+func (statement *Statement) Having(conditions string) *Statement {
+ statement.HavingStr = fmt.Sprintf("HAVING %v", conditions)
+ return statement
+}
+
+// Unscoped always disable struct tag "deleted"
+func (statement *Statement) Unscoped() *Statement {
+ statement.unscoped = true
+ return statement
+}
+
+func (statement *Statement) genColumnStr() string {
+ table := statement.RefTable
+ var colNames []string
+ for _, col := range table.Columns() {
+ if statement.OmitStr != "" {
+ if _, ok := statement.columnMap[strings.ToLower(col.Name)]; ok {
+ continue
+ }
+ }
+ if col.MapType == core.ONLYTODB {
+ continue
+ }
+
+ if statement.JoinStr != "" {
+ var name string
+ if statement.TableAlias != "" {
+ name = statement.Engine.Quote(statement.TableAlias)
+ } else {
+ name = statement.Engine.Quote(statement.TableName())
+ }
+ name += "." + statement.Engine.Quote(col.Name)
+ if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" {
+ colNames = append(colNames, "id() AS "+name)
+ } else {
+ colNames = append(colNames, name)
+ }
+ } else {
+ name := statement.Engine.Quote(col.Name)
+ if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" {
+ colNames = append(colNames, "id() AS "+name)
+ } else {
+ colNames = append(colNames, name)
+ }
+ }
+ }
+ return strings.Join(colNames, ", ")
+}
+
+func (statement *Statement) genCreateTableSQL() string {
+ return statement.Engine.dialect.CreateTableSql(statement.RefTable, statement.TableName(),
+ statement.StoreEngine, statement.Charset)
+}
+
+func (s *Statement) genIndexSQL() []string {
+ var sqls []string
+ tbName := s.TableName()
+ quote := s.Engine.Quote
+ for idxName, index := range s.RefTable.Indexes {
+ if index.Type == core.IndexType {
+ sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(tbName, idxName)),
+ quote(tbName), quote(strings.Join(index.Cols, quote(","))))
+ sqls = append(sqls, sql)
+ }
+ }
+ return sqls
+}
+
+func uniqueName(tableName, uqeName string) string {
+ return fmt.Sprintf("UQE_%v_%v", tableName, uqeName)
+}
+
+func (s *Statement) genUniqueSQL() []string {
+ var sqls []string
+ tbName := s.TableName()
+ for _, index := range s.RefTable.Indexes {
+ if index.Type == core.UniqueType {
+ sql := s.Engine.dialect.CreateIndexSql(tbName, index)
+ sqls = append(sqls, sql)
+ }
+ }
+ return sqls
+}
+
+func (s *Statement) genDelIndexSQL() []string {
+ var sqls []string
+ tbName := s.TableName()
+ for idxName, index := range s.RefTable.Indexes {
+ var rIdxName string
+ if index.Type == core.UniqueType {
+ rIdxName = uniqueName(tbName, idxName)
+ } else if index.Type == core.IndexType {
+ rIdxName = indexName(tbName, idxName)
+ }
+ sql := fmt.Sprintf("DROP INDEX %v", s.Engine.Quote(rIdxName))
+ if s.Engine.dialect.IndexOnTable() {
+ sql += fmt.Sprintf(" ON %v", s.Engine.Quote(s.TableName()))
+ }
+ sqls = append(sqls, sql)
+ }
+ return sqls
+}
+
+func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) {
+ quote := s.Engine.Quote
+ sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(s.TableName()),
+ col.String(s.Engine.dialect))
+ return sql, []interface{}{}
+}
+
+func (statement *Statement) buildConds(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) (builder.Cond, error) {
+ return buildConds(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols,
+ statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
+}
+
+func (statement *Statement) genConds(bean interface{}) (string, []interface{}, error) {
+ if !statement.noAutoCondition {
+ var addedTableName = (len(statement.JoinStr) > 0)
+ autoCond, err := statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName)
+ if err != nil {
+ return "", nil, err
+ }
+ statement.cond = statement.cond.And(autoCond)
+ }
+
+ statement.processIdParam()
+
+ return builder.ToSQL(statement.cond)
+}
+
+func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) {
+ statement.setRefValue(rValue(bean))
+
+ var columnStr = statement.ColumnStr
+ if len(statement.selectStr) > 0 {
+ columnStr = statement.selectStr
+ } else {
+ // TODO: always generate column names, not use * even if join
+ if len(statement.JoinStr) == 0 {
+ if len(columnStr) == 0 {
+ if len(statement.GroupByStr) > 0 {
+ columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
+ } else {
+ columnStr = statement.genColumnStr()
+ }
+ }
+ } else {
+ if len(columnStr) == 0 {
+ if len(statement.GroupByStr) > 0 {
+ columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
+ } else {
+ columnStr = "*"
+ }
+ }
+ }
+ }
+
+ condSQL, condArgs, _ := statement.genConds(bean)
+
+ return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...)
+}
+
+func (statement *Statement) genCountSQL(bean interface{}) (string, []interface{}) {
+ statement.setRefValue(rValue(bean))
+
+ condSQL, condArgs, _ := statement.genConds(bean)
+
+ var selectSql = statement.selectStr
+ if len(selectSql) <= 0 {
+ if statement.IsDistinct {
+ selectSql = fmt.Sprintf("count(DISTINCT %s)", statement.ColumnStr)
+ }
+ selectSql = "count(*)"
+ }
+ return statement.genSelectSQL(selectSql, condSQL), append(statement.joinArgs, condArgs...)
+}
+
+func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}) {
+ statement.setRefValue(rValue(bean))
+
+ var sumStrs = make([]string, 0, len(columns))
+ for _, colName := range columns {
+ sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName))
+ }
+
+ condSQL, condArgs, _ := statement.genConds(bean)
+
+ return statement.genSelectSQL(strings.Join(sumStrs, ", "), condSQL), append(statement.joinArgs, condArgs...)
+}
+
+func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
+ var distinct string
+ if statement.IsDistinct {
+ distinct = "DISTINCT "
+ }
+
+ var dialect = statement.Engine.Dialect()
+ var quote = statement.Engine.Quote
+ var top string
+ var mssqlCondi string
+
+ statement.processIdParam()
+
+ var buf bytes.Buffer
+ if len(condSQL) > 0 {
+ fmt.Fprintf(&buf, " WHERE %v", condSQL)
+ }
+ var whereStr = buf.String()
+
+ var fromStr = " FROM " + quote(statement.TableName())
+ if statement.TableAlias != "" {
+ if dialect.DBType() == core.ORACLE {
+ fromStr += " " + quote(statement.TableAlias)
+ } else {
+ fromStr += " AS " + quote(statement.TableAlias)
+ }
+ }
+ if statement.JoinStr != "" {
+ fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr)
+ }
+
+ if dialect.DBType() == core.MSSQL {
+ if statement.LimitN > 0 {
+ top = fmt.Sprintf(" TOP %d ", statement.LimitN)
+ }
+ if statement.Start > 0 {
+ var column string
+ if len(statement.RefTable.PKColumns()) == 0 {
+ for _, index := range statement.RefTable.Indexes {
+ if len(index.Cols) == 1 {
+ column = index.Cols[0]
+ break
+ }
+ }
+ if len(column) == 0 {
+ column = statement.RefTable.ColumnsSeq()[0]
+ }
+ } else {
+ column = statement.RefTable.PKColumns()[0].Name
+ }
+ if statement.needTableName() {
+ if len(statement.TableAlias) > 0 {
+ column = statement.TableAlias + "." + column
+ } else {
+ column = statement.TableName() + "." + column
+ }
+ }
+
+ var orderStr string
+ if len(statement.OrderStr) > 0 {
+ orderStr = " ORDER BY " + statement.OrderStr
+ }
+ var groupStr string
+ if len(statement.GroupByStr) > 0 {
+ groupStr = " GROUP BY " + statement.GroupByStr
+ }
+ mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))",
+ column, statement.Start, column, fromStr, whereStr, orderStr, groupStr)
+ }
+ }
+
+ // !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern
+ a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr, fromStr, whereStr)
+ if len(mssqlCondi) > 0 {
+ if len(whereStr) > 0 {
+ a += " AND " + mssqlCondi
+ } else {
+ a += " WHERE " + mssqlCondi
+ }
+ }
+
+ if statement.GroupByStr != "" {
+ a = fmt.Sprintf("%v GROUP BY %v", a, statement.GroupByStr)
+ }
+ if statement.HavingStr != "" {
+ a = fmt.Sprintf("%v %v", a, statement.HavingStr)
+ }
+ if statement.OrderStr != "" {
+ a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr)
+ }
+ if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE {
+ if statement.Start > 0 {
+ a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start)
+ } else if statement.LimitN > 0 {
+ a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN)
+ }
+ } else if dialect.DBType() == core.ORACLE {
+ if statement.Start != 0 || statement.LimitN != 0 {
+ a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start)
+ }
+ }
+ if statement.IsForUpdate {
+ a = dialect.ForUpdateSql(a)
+ }
+
+ return
+}
+
+func (statement *Statement) processIdParam() {
+ if statement.IdParam == nil {
+ return
+ }
+
+ for i, col := range statement.RefTable.PKColumns() {
+ var colName = statement.colName(col, statement.TableName())
+ if i < len(*(statement.IdParam)) {
+ statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.IdParam))[i]})
+ } else {
+ statement.cond = statement.cond.And(builder.Eq{colName: ""})
+ }
+ }
+}
+
+func (statement *Statement) joinColumns(cols []*core.Column, includeTableName bool) string {
+ var colnames = make([]string, len(cols))
+ for i, col := range cols {
+ if includeTableName {
+ colnames[i] = statement.Engine.Quote(statement.TableName()) +
+ "." + statement.Engine.Quote(col.Name)
+ } else {
+ colnames[i] = statement.Engine.Quote(col.Name)
+ }
+ }
+ return strings.Join(colnames, ", ")
+}
+
+func (statement *Statement) convertIDSQL(sqlStr string) string {
+ if statement.RefTable != nil {
+ cols := statement.RefTable.PKColumns()
+ if len(cols) == 0 {
+ return ""
+ }
+
+ colstrs := statement.joinColumns(cols, false)
+ sqls := splitNNoCase(sqlStr, " from ", 2)
+ if len(sqls) != 2 {
+ return ""
+ }
+
+ return fmt.Sprintf("SELECT %s FROM %v", colstrs, sqls[1])
+ }
+ return ""
+}
+
+func (statement *Statement) convertUpdateSQL(sqlStr string) (string, string) {
+ if statement.RefTable == nil || len(statement.RefTable.PrimaryKeys) != 1 {
+ return "", ""
+ }
+
+ colstrs := statement.joinColumns(statement.RefTable.PKColumns(), true)
+ sqls := splitNNoCase(sqlStr, "where", 2)
+ if len(sqls) != 2 {
+ if len(sqls) == 1 {
+ return sqls[0], fmt.Sprintf("SELECT %v FROM %v",
+ colstrs, statement.Engine.Quote(statement.TableName()))
+ }
+ return "", ""
+ }
+
+ var whereStr = sqls[1]
+
+ //TODO: for postgres only, if any other database?
+ var paraStr string
+ if statement.Engine.dialect.DBType() == core.POSTGRES {
+ paraStr = "$"
+ } else if statement.Engine.dialect.DBType() == core.MSSQL {
+ paraStr = ":"
+ }
+
+ if paraStr != "" {
+ if strings.Contains(sqls[1], paraStr) {
+ dollers := strings.Split(sqls[1], paraStr)
+ whereStr = dollers[0]
+ for i, c := range dollers[1:] {
+ ccs := strings.SplitN(c, " ", 2)
+ whereStr += fmt.Sprintf(paraStr+"%v %v", i+1, ccs[1])
+ }
+ }
+ }
+
+ return sqls[0], fmt.Sprintf("SELECT %v FROM %v WHERE %v",
+ colstrs, statement.Engine.Quote(statement.TableName()),
+ whereStr)
+}
diff --git a/vendor/github.com/go-xorm/xorm/syslogger.go b/vendor/github.com/go-xorm/xorm/syslogger.go
new file mode 100644
index 0000000000..799bcca3ca
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/syslogger.go
@@ -0,0 +1,77 @@
+// Copyright 2015 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.
+
+// +build !windows,!nacl,!plan9
+
+package xorm
+
+import (
+ "fmt"
+ "log/syslog"
+
+ "github.com/go-xorm/core"
+)
+
+var _ core.ILogger = &SyslogLogger{}
+
+// SyslogLogger will be depricated
+type SyslogLogger struct {
+ w *syslog.Writer
+ showSQL bool
+}
+
+func NewSyslogLogger(w *syslog.Writer) *SyslogLogger {
+ return &SyslogLogger{w: w}
+}
+
+func (s *SyslogLogger) Debug(v ...interface{}) {
+ s.w.Debug(fmt.Sprint(v...))
+}
+
+func (s *SyslogLogger) Debugf(format string, v ...interface{}) {
+ s.w.Debug(fmt.Sprintf(format, v...))
+}
+
+func (s *SyslogLogger) Error(v ...interface{}) {
+ s.w.Err(fmt.Sprint(v...))
+}
+
+func (s *SyslogLogger) Errorf(format string, v ...interface{}) {
+ s.w.Err(fmt.Sprintf(format, v...))
+}
+
+func (s *SyslogLogger) Info(v ...interface{}) {
+ s.w.Info(fmt.Sprint(v...))
+}
+
+func (s *SyslogLogger) Infof(format string, v ...interface{}) {
+ s.w.Info(fmt.Sprintf(format, v...))
+}
+
+func (s *SyslogLogger) Warn(v ...interface{}) {
+ s.w.Warning(fmt.Sprint(v...))
+}
+
+func (s *SyslogLogger) Warnf(format string, v ...interface{}) {
+ s.w.Warning(fmt.Sprintf(format, v...))
+}
+
+func (s *SyslogLogger) Level() core.LogLevel {
+ return core.LOG_UNKNOWN
+}
+
+// SetLevel always return error, as current log/syslog package doesn't allow to set priority level after syslog.Writer created
+func (s *SyslogLogger) SetLevel(l core.LogLevel) {}
+
+func (s *SyslogLogger) ShowSQL(show ...bool) {
+ if len(show) == 0 {
+ s.showSQL = true
+ return
+ }
+ s.showSQL = show[0]
+}
+
+func (s *SyslogLogger) IsShowSQL() bool {
+ return s.showSQL
+}
diff --git a/vendor/github.com/go-xorm/xorm/types.go b/vendor/github.com/go-xorm/xorm/types.go
new file mode 100644
index 0000000000..8bf85d7a28
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/types.go
@@ -0,0 +1,12 @@
+package xorm
+
+import (
+ "reflect"
+
+ "github.com/go-xorm/core"
+)
+
+var (
+ ptrPkType = reflect.TypeOf(&core.PK{})
+ pkType = reflect.TypeOf(core.PK{})
+)
diff --git a/vendor/github.com/go-xorm/xorm/xorm.go b/vendor/github.com/go-xorm/xorm/xorm.go
new file mode 100644
index 0000000000..6414d8a26b
--- /dev/null
+++ b/vendor/github.com/go-xorm/xorm/xorm.go
@@ -0,0 +1,104 @@
+// Copyright 2015 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 (
+ "fmt"
+ "os"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/go-xorm/core"
+)
+
+const (
+ // Version show the xorm's version
+ Version string = "0.6.0.1022"
+)
+
+func regDrvsNDialects() bool {
+ providedDrvsNDialects := map[string]struct {
+ dbType core.DbType
+ getDriver func() core.Driver
+ getDialect func() core.Dialect
+ }{
+ "mssql": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }},
+ "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access
+ "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }},
+ "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }},
+ "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }},
+ "pgx": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }},
+ "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }},
+ "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }},
+ "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }},
+ }
+
+ for driverName, v := range providedDrvsNDialects {
+ if driver := core.QueryDriver(driverName); driver == nil {
+ core.RegisterDriver(driverName, v.getDriver())
+ core.RegisterDialect(v.dbType, v.getDialect)
+ }
+ }
+ return true
+}
+
+func close(engine *Engine) {
+ engine.Close()
+}
+
+// NewEngine new a db manager according to the parameter. Currently support four
+// drivers
+func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
+ regDrvsNDialects()
+ driver := core.QueryDriver(driverName)
+ if driver == nil {
+ return nil, fmt.Errorf("Unsupported driver name: %v", driverName)
+ }
+
+ uri, err := driver.Parse(driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ dialect := core.QueryDialect(uri.DbType)
+ if dialect == nil {
+ return nil, fmt.Errorf("Unsupported dialect type: %v", uri.DbType)
+ }
+
+ db, err := core.Open(driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ err = dialect.Init(db, uri, driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ engine := &Engine{
+ db: db,
+ dialect: dialect,
+ Tables: make(map[reflect.Type]*core.Table),
+ mutex: &sync.RWMutex{},
+ TagIdentifier: "xorm",
+ TZLocation: time.Local,
+ }
+
+ logger := NewSimpleLogger(os.Stdout)
+ logger.SetLevel(core.LOG_INFO)
+ engine.SetLogger(logger)
+ engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
+
+ runtime.SetFinalizer(engine, close)
+
+ return engine, nil
+}
+
+// Clone clone an engine
+func (engine *Engine) Clone() (*Engine, error) {
+ return NewEngine(engine.DriverName(), engine.DataSourceName())
+}