Conflicts:
	session.go
This commit is contained in:
hzm 2016-01-04 02:31:21 +08:00
commit 7d79bd259b
10 changed files with 668 additions and 478 deletions

146
README.md
View File

@ -11,7 +11,7 @@ Xorm is a simple and powerful ORM for Go.
* Struct <-> Table Mapping Support * Struct <-> Table Mapping Support
* Chainable APIs * Chainable APIs
* Transaction Support * Transaction Support
* Both ORM and raw SQL operation Support * Both ORM and raw SQL operation Support
@ -72,7 +72,7 @@ Drivers for Go's sql package which currently support database/sql includes:
# Installation # Installation
If you have [gopm](https://github.com/gpmgo/gopm) installed, If you have [gopm](https://github.com/gpmgo/gopm) installed,
gopm get github.com/go-xorm/xorm gopm get github.com/go-xorm/xorm
@ -88,6 +88,148 @@ Or
* [GoWalker](http://gowalker.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 multipe 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 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 to use Cols, AllCols and etc.
```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 = ?
```
* Delete one or more records, Delete MUST has conditon
```Go
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
```
* Count records
```Go
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
```
# Cases # Cases
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)

View File

@ -20,7 +20,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件 * 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
* 支持级联加载Struct * 支持级联加载Struct
* 支持缓存 * 支持缓存
@ -58,7 +58,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* sql.NullString支持 * sql.NullString支持
* ForUpdate 支持 * ForUpdate 支持
* bug修正 * bug修正
* **v0.4.3** * **v0.4.3**
* Json 字段类型支持 * Json 字段类型支持
* oracle实验性支持 * oracle实验性支持
@ -68,10 +68,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 安装 ## 安装
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
gopm get github.com/go-xorm/xorm gopm get github.com/go-xorm/xorm
或者您也可以使用go工具进行安装 或者您也可以使用go工具进行安装
go get github.com/go-xorm/xorm go get github.com/go-xorm/xorm
@ -84,8 +84,149 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* [Godoc代码文档](http://godoc.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
```
# 案例
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)

View File

@ -1 +1 @@
xorm v0.4.4.1029 xorm v0.4.5.0102

View File

@ -320,6 +320,12 @@ func (engine *Engine) NoAutoTime() *Session {
return session.NoAutoTime() return session.NoAutoTime()
} }
func (engine *Engine) NoAutoCondition(no ...bool) *Session {
session := engine.NewSession()
session.IsAutoClose = true
return session.NoAutoCondition(no...)
}
// Retrieve all tables, columns, indexes' informations from database. // Retrieve all tables, columns, indexes' informations from database.
func (engine *Engine) DBMetas() ([]*core.Table, error) { func (engine *Engine) DBMetas() ([]*core.Table, error) {
tables, err := engine.dialect.GetTables() tables, err := engine.dialect.GetTables()
@ -377,13 +383,25 @@ func (engine *Engine) DumpAll(w io.Writer) error {
return err return err
} }
for _, table := range tables { _, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n",
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+"\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, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
if err != nil { if err != nil {
return err return err
} }
for _, index := range table.Indexes { for _, index := range table.Indexes {
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n") _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n")
if err != nil { if err != nil {
return err return err
} }
@ -443,7 +461,7 @@ func (engine *Engine) DumpAll(w io.Writer) error {
} }
} }
} }
_, err = io.WriteString(w, temp[2:]+");\n\n") _, err = io.WriteString(w, temp[2:]+");\n")
if err != nil { if err != nil {
return err return err
} }
@ -747,7 +765,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if ormTagStr != "" { if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)} IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)}
tags := strings.Split(ormTagStr, " ") tags := splitTag(ormTagStr)
if len(tags) > 0 { if len(tags) > 0 {
if tags[0] == "-" { if tags[0] == "-" {

View File

@ -15,6 +15,30 @@ import (
"github.com/go-xorm/core" "github.com/go-xorm/core"
) )
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 { func isZero(k interface{}) bool {
switch k.(type) { switch k.(type) {
case int: case int:
@ -45,12 +69,33 @@ func isZero(k interface{}) bool {
return k.(bool) == false return k.(bool) == false
case string: case string:
return k.(string) == "" return k.(string) == ""
case time.Time: case zeroable:
return k.(time.Time).IsZero() return k.(zeroable).IsZero()
} }
return false return false
} }
func int64ToInt(id int64, k reflect.Kind) interface{} {
var v interface{} = id
switch k {
case reflect.Int16:
v = int16(id)
case reflect.Int32:
v = int32(id)
case reflect.Int:
v = int(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 v
}
func isPKZero(pk core.PK) bool { func isPKZero(pk core.PK) bool {
for _, k := range pk { for _, k := range pk {
if isZero(k) { if isZero(k) {

22
helpers_test.go Normal file
View File

@ -0,0 +1,22 @@
package xorm
import "testing"
func TestSplitTag(t *testing.T) {
var cases = []struct {
tag string
tags []string
}{
{"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}},
{"TEXT", []string{"TEXT"}},
{"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}},
{"json binary", []string{"json", "binary"}},
}
for _, kase := range cases {
tags := splitTag(kase.tag)
if !sliceEq(tags, kase.tags) {
t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags)
}
}
}

View File

@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) {
return "", err return "", err
} }
if u.Scheme != "postgres" { if u.Scheme != "postgresql" && u.Scheme != "postgres" {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
} }
@ -103,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.POSTGRES} db := &core.Uri{DbType: core.POSTGRES}
o := make(values) o := make(values)
var err error var err error
if strings.HasPrefix(dataSourceName, "postgres://") { if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName) dataSourceName, err = parseURL(dataSourceName)
if err != nil { if err != nil {
return nil, err return nil, err

File diff suppressed because it is too large Load Diff

View File

@ -39,45 +39,46 @@ type exprParam struct {
// statement save all the sql info for executing SQL // statement save all the sql info for executing SQL
type Statement struct { type Statement struct {
RefTable *core.Table RefTable *core.Table
Engine *Engine Engine *Engine
Start int Start int
LimitN int LimitN int
WhereStr string WhereStr string
IdParam *core.PK IdParam *core.PK
Params []interface{} Params []interface{}
OrderStr string OrderStr string
JoinStr string JoinStr string
GroupByStr string GroupByStr string
HavingStr string HavingStr string
ColumnStr string ColumnStr string
selectStr string selectStr string
columnMap map[string]bool columnMap map[string]bool
useAllCols bool useAllCols bool
OmitStr string OmitStr string
ConditionStr string ConditionStr string
AltTableName string AltTableName string
RawSQL string RawSQL string
RawParams []interface{} RawParams []interface{}
UseCascade bool UseCascade bool
UseAutoJoin bool UseAutoJoin bool
StoreEngine string StoreEngine string
Charset string Charset string
BeanArgs []interface{} BeanArgs []interface{}
UseCache bool UseCache bool
UseAutoTime bool UseAutoTime bool
IsDistinct bool noAutoCondition bool
IsForUpdate bool IsDistinct bool
TableAlias string IsForUpdate bool
allUseBool bool TableAlias string
checkVersion bool allUseBool bool
unscoped bool checkVersion bool
mustColumnMap map[string]bool unscoped bool
nullableMap map[string]bool mustColumnMap map[string]bool
inColumns map[string]*inParam nullableMap map[string]bool
incrColumns map[string]incrParam inColumns map[string]*inParam
decrColumns map[string]decrParam incrColumns map[string]incrParam
exprColumns map[string]exprParam decrColumns map[string]decrParam
exprColumns map[string]exprParam
} }
// init // init
@ -103,6 +104,7 @@ func (statement *Statement) Init() {
statement.BeanArgs = make([]interface{}, 0) statement.BeanArgs = make([]interface{}, 0)
statement.UseCache = true statement.UseCache = true
statement.UseAutoTime = true statement.UseAutoTime = true
statement.noAutoCondition = false
statement.IsDistinct = false statement.IsDistinct = false
statement.IsForUpdate = false statement.IsForUpdate = false
statement.TableAlias = "" statement.TableAlias = ""
@ -119,6 +121,15 @@ func (statement *Statement) Init() {
statement.exprColumns = make(map[string]exprParam) statement.exprColumns = make(map[string]exprParam)
} }
// NoAutoCondition
func (statement *Statement) NoAutoCondition(no ...bool) *Statement {
statement.noAutoCondition = true
if len(no) > 0 {
statement.noAutoCondition = no[0]
}
return statement
}
// add the raw sql statement // add the raw sql statement
func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement {
statement.RawSQL = querystring statement.RawSQL = querystring
@ -182,7 +193,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return statement return statement
} }
// Auto generating conditions according a struct // Auto generating update columnes and values according a struct
func buildUpdates(engine *Engine, table *core.Table, bean interface{}, func buildUpdates(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool, includeAutoIncr bool, allUseBool bool, useAllCols bool,
@ -211,10 +222,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
continue continue
} }
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean) fieldValuePtr, err := col.ValueOf(bean)
if err != nil { if err != nil {
engine.LogError(err) engine.LogError(err)
@ -338,7 +345,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
if len(table.PrimaryKeys) == 1 { if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
// fix non-int pk issues // fix non-int pk issues
//if pkField.Int() != 0 {
if pkField.IsValid() && !isZero(pkField.Interface()) { if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface() val = pkField.Interface()
} else { } else {
@ -364,11 +370,13 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
} }
} }
case reflect.Array, reflect.Slice, reflect.Map: case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) { if !requiredField {
continue if fieldValue == reflect.Zero(fieldType) {
} continue
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { }
continue if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
} }
if col.SQLType.IsText() { if col.SQLType.IsText() {
@ -1122,12 +1130,14 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
var addedTableName = (len(statement.JoinStr) > 0) var addedTableName = (len(statement.JoinStr) > 0)
colNames, args := buildConditions(statement.Engine, table, bean, true, true, if !statement.noAutoCondition {
false, true, statement.allUseBool, statement.useAllCols, colNames, args := buildConditions(statement.Engine, table, bean, true, true,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) false, true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ")
statement.BeanArgs = args statement.BeanArgs = args
}
var columnStr string = statement.ColumnStr var columnStr string = statement.ColumnStr
if len(statement.selectStr) > 0 { if len(statement.selectStr) > 0 {
@ -1183,12 +1193,14 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
var addedTableName = (len(statement.JoinStr) > 0) var addedTableName = (len(statement.JoinStr) > 0)
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, if !statement.noAutoCondition {
true, statement.allUseBool, statement.useAllCols, colNames, args := buildConditions(statement.Engine, table, bean, true, true, false,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ")
statement.BeanArgs = args statement.BeanArgs = args
}
// count(index fieldname) > count(0) > count(*) // count(index fieldname) > count(0) > count(*)
var id string = "*" var id string = "*"

View File

@ -17,7 +17,7 @@ import (
) )
const ( const (
Version string = "0.4.4.1029" Version string = "0.4.5.0102"
) )
func regDrvsNDialects() bool { func regDrvsNDialects() bool {