2016-11-04 01:16:01 +03:00
// 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 {
//var unique string
2016-11-11 19:40:21 +03:00
quote := db . Quote
idxName := index . Name
2016-11-04 01:16:01 +03:00
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 {
2017-01-23 14:11:57 +03:00
col . Name = strings . Trim ( strings . Trim ( field , "`[] " ) , ` " ` )
2016-11-04 01:16:01 +03:00
continue
} else if idx == 1 {
2016-11-11 19:40:21 +03:00
col . SQLType = core . SQLType { Name : field , DefaultLength : 0 , DefaultLength2 : 0 }
2016-11-04 01:16:01 +03:00
}
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 ( ) {
2017-01-03 11:20:28 +03:00
var tmpSQL sql . NullString
err = rows . Scan ( & tmpSQL )
2016-11-04 01:16:01 +03:00
if err != nil {
return nil , err
}
2017-01-03 11:20:28 +03:00
if ! tmpSQL . Valid {
2016-11-04 01:16:01 +03:00
continue
}
2017-01-03 11:20:28 +03:00
sql := tmpSQL . String
2016-11-04 01:16:01 +03:00
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 ) {
2017-01-23 12:11:18 +03:00
index . Name = indexName [ 5 + len ( tableName ) : ]
2016-11-04 01:16:01 +03:00
} 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 { } }
}