ql support

This commit is contained in:
Lunny Xiao 2014-03-28 15:03:35 +08:00
parent 182428e13d
commit ff3a06b3dc
4 changed files with 377 additions and 0 deletions

View File

@ -23,6 +23,7 @@ const (
MSSQL = "mssql"
ORACLE_OCI = "oci8"
QL = "ql"
)
// a dialect is a driver's wrapper

232
ql.go Normal file
View File

@ -0,0 +1,232 @@
package xorm
import (
"database/sql"
"strings"
)
type ql struct {
base
}
type qlParser struct {
}
func (p *qlParser) parse(driverName, dataSourceName string) (*uri, error) {
return &uri{dbType: QL, dbName: dataSourceName}, nil
}
func (db *ql) Init(drivername, dataSourceName string) error {
return db.base.init(&qlParser{}, drivername, dataSourceName)
}
func (db *ql) SqlType(c *Column) string {
switch t := c.SQLType.Name; t {
case Date, DateTime, TimeStamp, Time:
return Numeric
case TimeStampz:
return Text
case Char, Varchar, TinyText, Text, MediumText, LongText:
return Text
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool:
return Integer
case Float, Double, Real:
return Real
case Decimal, Numeric:
return Numeric
case TinyBlob, Blob, MediumBlob, LongBlob, Bytea, Binary, VarBinary:
return Blob
case Serial, BigSerial:
c.IsPrimaryKey = true
c.IsAutoIncrement = true
c.Nullable = false
return Integer
default:
return t
}
}
func (db *ql) SupportInsertMany() bool {
return true
}
func (db *ql) QuoteStr() string {
return ""
}
func (db *ql) AutoIncrStr() string {
return "AUTOINCREMENT"
}
func (db *ql) SupportEngine() bool {
return false
}
func (db *ql) SupportCharset() bool {
return false
}
func (db *ql) IndexOnTable() bool {
return false
}
func (db *ql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{idxName}
return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
}
func (db *ql) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{tableName}
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
}
func (db *ql) 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 *ql) GetColumns(tableName string) ([]string, map[string]*Column, error) {
args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil {
return nil, nil, err
}
defer cnn.Close()
res, err := query(cnn, s, args...)
if err != nil {
return nil, nil, err
}
var sql string
for _, record := range res {
for name, content := range record {
if name == "sql" {
sql = string(content)
}
}
}
nStart := strings.Index(sql, "(")
nEnd := strings.Index(sql, ")")
colCreates := strings.Split(sql[nStart+1:nEnd], ",")
cols := make(map[string]*Column)
colSeq := make([]string, 0)
for _, colStr := range colCreates {
fields := strings.Fields(strings.TrimSpace(colStr))
col := new(Column)
col.Indexes = make(map[string]bool)
col.Nullable = true
for idx, field := range fields {
if idx == 0 {
col.Name = strings.Trim(field, "`[] ")
continue
} else if idx == 1 {
col.SQLType = 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
}
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *ql) GetTables() ([]*Table, error) {
args := []interface{}{}
s := "SELECT name FROM sqlite_master WHERE type='table'"
cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil {
return nil, err
}
defer cnn.Close()
res, err := query(cnn, s, args...)
if err != nil {
return nil, err
}
tables := make([]*Table, 0)
for _, record := range res {
table := new(Table)
for name, content := range record {
switch name {
case "name":
table.Name = string(content)
}
}
if table.Name == "sqlite_sequence" {
continue
}
tables = append(tables, table)
}
return tables, nil
}
func (db *ql) GetIndexes(tableName string) (map[string]*Index, error) {
args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil {
return nil, err
}
defer cnn.Close()
res, err := query(cnn, s, args...)
if err != nil {
return nil, err
}
indexes := make(map[string]*Index, 0)
for _, record := range res {
index := new(Index)
sql := string(record["sql"])
if sql == "" {
continue
}
nNStart := strings.Index(sql, "INDEX")
nNEnd := strings.Index(sql, "ON")
if nNStart == -1 || nNEnd == -1 {
continue
}
indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
//fmt.Println(indexName)
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 = UniqueType
} else {
index.Type = 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
}

140
ql_test.go Normal file
View File

@ -0,0 +1,140 @@
package xorm
import (
"database/sql"
"os"
"testing"
_ "github.com/mattn/ql-driver"
)
func newQlEngine() (*Engine, error) {
os.Remove("./ql.db")
return NewEngine("ql", "./ql.db")
}
func newQlDriverDB() (*sql.DB, error) {
os.Remove("./ql.db")
return sql.Open("ql", "./ql.db")
}
func TestQl(t *testing.T) {
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
engine.ShowSQL = showTestSql
engine.ShowErr = showTestSql
engine.ShowWarn = showTestSql
engine.ShowDebug = showTestSql
testAll(engine, t)
testAll2(engine, t)
testAll3(engine, t)
}
func TestQlWithCache(t *testing.T) {
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000))
engine.ShowSQL = showTestSql
engine.ShowErr = showTestSql
engine.ShowWarn = showTestSql
engine.ShowDebug = showTestSql
testAll(engine, t)
testAll2(engine, t)
}
const (
createTableQl = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, `title` TEXT NULL, `age` TEXT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL);"
dropTableQl = "DROP TABLE IF EXISTS `big_struct`;"
)
func BenchmarkQlDriverInsert(t *testing.B) {
doBenchDriver(newQlDriverDB, createTableQl, dropTableQl,
doBenchDriverInsert, t)
}
func BenchmarkQlDriverFind(t *testing.B) {
doBenchDriver(newQlDriverDB, createTableQl, dropTableQl,
doBenchDriverFind, t)
}
func BenchmarkQlNoCacheInsert(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
//engine.ShowSQL = true
doBenchInsert(engine, t)
}
func BenchmarkQlNoCacheFind(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
//engine.ShowSQL = true
doBenchFind(engine, t)
}
func BenchmarkQlNoCacheFindPtr(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
//engine.ShowSQL = true
doBenchFindPtr(engine, t)
}
func BenchmarkQlCacheInsert(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000))
doBenchInsert(engine, t)
}
func BenchmarkQlCacheFind(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000))
doBenchFind(engine, t)
}
func BenchmarkQlCacheFindPtr(t *testing.B) {
t.StopTimer()
engine, err := newQlEngine()
defer engine.Close()
if err != nil {
t.Error(err)
return
}
engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000))
doBenchFindPtr(engine, t)
}

View File

@ -40,6 +40,10 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
} else if driverName == ORACLE_OCI {
engine.dialect = &oracle{}
engine.Filters = append(engine.Filters, &QuoteFilter{})
} else if driverName == QL {
engine.dialect = &ql{}
engine.Filters = append(engine.Filters, &PgSeqFilter{})
engine.Filters = append(engine.Filters, &QuoteFilter{})
} else {
return nil, errors.New(fmt.Sprintf("Unsupported driver name: %v", driverName))
}