| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 | 
							- // 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 dialects
 
- import (
 
- 	"context"
 
- 	"database/sql"
 
- 	"errors"
 
- 	"fmt"
 
- 	"regexp"
 
- 	"strings"
 
- 	"xorm.io/xorm/core"
 
- 	"xorm.io/xorm/schemas"
 
- )
 
- 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,
 
- 	}
 
- 	sqlite3Quoter = schemas.Quoter{
 
- 		Prefix:     '`',
 
- 		Suffix:     '`',
 
- 		IsReserved: schemas.AlwaysReserve,
 
- 	}
 
- )
 
- type sqlite3 struct {
 
- 	Base
 
- }
 
- func (db *sqlite3) Init(uri *URI) error {
 
- 	db.quoter = sqlite3Quoter
 
- 	return db.Base.Init(db, uri)
 
- }
 
- func (db *sqlite3) SetQuotePolicy(quotePolicy QuotePolicy) {
 
- 	switch quotePolicy {
 
- 	case QuotePolicyNone:
 
- 		var q = sqlite3Quoter
 
- 		q.IsReserved = schemas.AlwaysNoReserve
 
- 		db.quoter = q
 
- 	case QuotePolicyReserved:
 
- 		var q = sqlite3Quoter
 
- 		q.IsReserved = db.IsReserved
 
- 		db.quoter = q
 
- 	case QuotePolicyAlways:
 
- 		fallthrough
 
- 	default:
 
- 		db.quoter = sqlite3Quoter
 
- 	}
 
- }
 
- func (db *sqlite3) SQLType(c *schemas.Column) string {
 
- 	switch t := c.SQLType.Name; t {
 
- 	case schemas.Bool:
 
- 		if c.Default == "true" {
 
- 			c.Default = "1"
 
- 		} else if c.Default == "false" {
 
- 			c.Default = "0"
 
- 		}
 
- 		return schemas.Integer
 
- 	case schemas.Date, schemas.DateTime, schemas.TimeStamp, schemas.Time:
 
- 		return schemas.DateTime
 
- 	case schemas.TimeStampz:
 
- 		return schemas.Text
 
- 	case schemas.Char, schemas.Varchar, schemas.NVarchar, schemas.TinyText,
 
- 		schemas.Text, schemas.MediumText, schemas.LongText, schemas.Json:
 
- 		return schemas.Text
 
- 	case schemas.Bit, schemas.TinyInt, schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.BigInt:
 
- 		return schemas.Integer
 
- 	case schemas.Float, schemas.Double, schemas.Real:
 
- 		return schemas.Real
 
- 	case schemas.Decimal, schemas.Numeric:
 
- 		return schemas.Numeric
 
- 	case schemas.TinyBlob, schemas.Blob, schemas.MediumBlob, schemas.LongBlob, schemas.Bytea, schemas.Binary, schemas.VarBinary:
 
- 		return schemas.Blob
 
- 	case schemas.Serial, schemas.BigSerial:
 
- 		c.IsPrimaryKey = true
 
- 		c.IsAutoIncrement = true
 
- 		c.Nullable = false
 
- 		return schemas.Integer
 
- 	default:
 
- 		return t
 
- 	}
 
- }
 
- func (db *sqlite3) FormatBytes(bs []byte) string {
 
- 	return fmt.Sprintf("X'%x'", bs)
 
- }
 
- func (db *sqlite3) IsReserved(name string) bool {
 
- 	_, ok := sqlite3ReservedWords[strings.ToUpper(name)]
 
- 	return ok
 
- }
 
- func (db *sqlite3) AutoIncrStr() string {
 
- 	return "AUTOINCREMENT"
 
- }
 
- 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) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) {
 
- 	return db.HasRecords(queryer, ctx, "SELECT name FROM sqlite_master WHERE type='table' and name = ?", tableName)
 
- }
 
- func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string {
 
- 	// var unique string
 
- 	idxName := index.Name
 
- 	if !strings.HasPrefix(idxName, "UQE_") &&
 
- 		!strings.HasPrefix(idxName, "IDX_") {
 
- 		if index.Type == schemas.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", db.Quoter().Quote(idxName))
 
- }
 
- func (db *sqlite3) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
 
- 	var sql string
 
- 	sql = "CREATE TABLE IF NOT EXISTS "
 
- 	if tableName == "" {
 
- 		tableName = table.Name
 
- 	}
 
- 	quoter := db.Quoter()
 
- 	sql += quoter.Quote(tableName)
 
- 	sql += " ("
 
- 	if len(table.ColumnsSeq()) > 0 {
 
- 		pkList := table.PrimaryKeys
 
- 		for _, colName := range table.ColumnsSeq() {
 
- 			col := table.GetColumn(colName)
 
- 			s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
 
- 			sql += s
 
- 			sql = strings.TrimSpace(sql)
 
- 			sql += ", "
 
- 		}
 
- 		if len(pkList) > 1 {
 
- 			sql += "PRIMARY KEY ( "
 
- 			sql += quoter.Join(pkList, ",")
 
- 			sql += " ), "
 
- 		}
 
- 		sql = sql[:len(sql)-2]
 
- 	}
 
- 	sql += ")"
 
- 	return []string{sql}, true
 
- }
 
- func (db *sqlite3) ForUpdateSQL(query string) string {
 
- 	return query
 
- }
 
- func (db *sqlite3) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) {
 
- 	query := "SELECT * FROM " + tableName + " LIMIT 0"
 
- 	rows, err := queryer.QueryContext(ctx, query)
 
- 	if err != nil {
 
- 		return false, err
 
- 	}
 
- 	defer rows.Close()
 
- 	cols, err := rows.Columns()
 
- 	if err != nil {
 
- 		return false, err
 
- 	}
 
- 	for _, col := range cols {
 
- 		if strings.EqualFold(col, colName) {
 
- 			return true, nil
 
- 		}
 
- 	}
 
- 	return false, nil
 
- }
 
- // splitColStr splits a sqlite col strings as fields
 
- func splitColStr(colStr string) []string {
 
- 	colStr = strings.TrimSpace(colStr)
 
- 	var results = make([]string, 0, 10)
 
- 	var lastIdx int
 
- 	var hasC, hasQuote bool
 
- 	for i, c := range colStr {
 
- 		if c == ' ' && !hasQuote {
 
- 			if hasC {
 
- 				results = append(results, colStr[lastIdx:i])
 
- 				hasC = false
 
- 			}
 
- 		} else {
 
- 			if c == '\'' {
 
- 				hasQuote = !hasQuote
 
- 			}
 
- 			if !hasC {
 
- 				lastIdx = i
 
- 			}
 
- 			hasC = true
 
- 			if i == len(colStr)-1 {
 
- 				results = append(results, colStr[lastIdx:i+1])
 
- 			}
 
- 		}
 
- 	}
 
- 	return results
 
- }
 
- func parseString(colStr string) (*schemas.Column, error) {
 
- 	fields := splitColStr(colStr)
 
- 	col := new(schemas.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(strings.Trim(field, "`[] "), `"`)
 
- 			continue
 
- 		} else if idx == 1 {
 
- 			col.SQLType = schemas.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0}
 
- 			continue
 
- 		}
 
- 		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
 
- 		}
 
- 	}
 
- 	return col, nil
 
- }
 
- func (db *sqlite3) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) {
 
- 	args := []interface{}{tableName}
 
- 	s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
 
- 	rows, err := queryer.QueryContext(ctx, 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]*schemas.Column)
 
- 	colSeq := make([]string, 0)
 
- 	for _, colStr := range colCreates {
 
- 		reg = regexp.MustCompile(`,\s`)
 
- 		colStr = reg.ReplaceAllString(colStr, ",")
 
- 		if strings.HasPrefix(strings.TrimSpace(colStr), "PRIMARY KEY") {
 
- 			parts := strings.Split(strings.TrimSpace(colStr), "(")
 
- 			if len(parts) == 2 {
 
- 				pkCols := strings.Split(strings.TrimRight(strings.TrimSpace(parts[1]), ")"), ",")
 
- 				for _, pk := range pkCols {
 
- 					if col, ok := cols[strings.Trim(strings.TrimSpace(pk), "`")]; ok {
 
- 						col.IsPrimaryKey = true
 
- 					}
 
- 				}
 
- 			}
 
- 			continue
 
- 		}
 
- 		col, err := parseString(colStr)
 
- 		if err != nil {
 
- 			return colSeq, cols, err
 
- 		}
 
- 		cols[col.Name] = col
 
- 		colSeq = append(colSeq, col.Name)
 
- 	}
 
- 	return colSeq, cols, nil
 
- }
 
- func (db *sqlite3) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) {
 
- 	args := []interface{}{}
 
- 	s := "SELECT name FROM sqlite_master WHERE type='table'"
 
- 	rows, err := queryer.QueryContext(ctx, s, args...)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	defer rows.Close()
 
- 	tables := make([]*schemas.Table, 0)
 
- 	for rows.Next() {
 
- 		table := schemas.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(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) {
 
- 	args := []interface{}{tableName}
 
- 	s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
 
- 	rows, err := queryer.QueryContext(ctx, s, args...)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	defer rows.Close()
 
- 	indexes := make(map[string]*schemas.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(schemas.Index)
 
- 		nNStart := strings.Index(sql, "INDEX")
 
- 		nNEnd := strings.Index(sql, "ON")
 
- 		if nNStart == -1 || nNEnd == -1 {
 
- 			continue
 
- 		}
 
- 		indexName := strings.Trim(strings.TrimSpace(sql[nNStart+6:nNEnd]), "`[]'\"")
 
- 		var isRegular bool
 
- 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 
- 			index.Name = indexName[5+len(tableName):]
 
- 			isRegular = true
 
- 		} else {
 
- 			index.Name = indexName
 
- 		}
 
- 		if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
 
- 			index.Type = schemas.UniqueType
 
- 		} else {
 
- 			index.Type = schemas.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, "` []"))
 
- 		}
 
- 		index.IsRegular = isRegular
 
- 		indexes[index.Name] = index
 
- 	}
 
- 	return indexes, nil
 
- }
 
- func (db *sqlite3) Filters() []Filter {
 
- 	return []Filter{}
 
- }
 
- type sqlite3Driver struct {
 
- }
 
- func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*URI, error) {
 
- 	if strings.Contains(dataSourceName, "?") {
 
- 		dataSourceName = dataSourceName[:strings.Index(dataSourceName, "?")]
 
- 	}
 
- 	return &URI{DBType: schemas.SQLITE, DBName: dataSourceName}, nil
 
- }
 
 
  |