You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dialect.go 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright 2019 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package dialects
  5. import (
  6. "context"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "xorm.io/xorm/core"
  11. "xorm.io/xorm/schemas"
  12. )
  13. // URI represents an uri to visit database
  14. type URI struct {
  15. DBType schemas.DBType
  16. Proto string
  17. Host string
  18. Port string
  19. DBName string
  20. User string
  21. Passwd string
  22. Charset string
  23. Laddr string
  24. Raddr string
  25. Timeout time.Duration
  26. Schema string
  27. }
  28. // SetSchema set schema
  29. func (uri *URI) SetSchema(schema string) {
  30. // hack me
  31. if uri.DBType == schemas.POSTGRES {
  32. uri.Schema = strings.TrimSpace(schema)
  33. }
  34. }
  35. // Dialect represents a kind of database
  36. type Dialect interface {
  37. Init(*URI) error
  38. URI() *URI
  39. SQLType(*schemas.Column) string
  40. FormatBytes(b []byte) string
  41. IsReserved(string) bool
  42. Quoter() schemas.Quoter
  43. SetQuotePolicy(quotePolicy QuotePolicy)
  44. AutoIncrStr() string
  45. GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error)
  46. IndexCheckSQL(tableName, idxName string) (string, []interface{})
  47. CreateIndexSQL(tableName string, index *schemas.Index) string
  48. DropIndexSQL(tableName string, index *schemas.Index) string
  49. GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error)
  50. IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error)
  51. CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool)
  52. DropTableSQL(tableName string) (string, bool)
  53. GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error)
  54. IsColumnExist(queryer core.Queryer, ctx context.Context, tableName string, colName string) (bool, error)
  55. AddColumnSQL(tableName string, col *schemas.Column) string
  56. ModifyColumnSQL(tableName string, col *schemas.Column) string
  57. ForUpdateSQL(query string) string
  58. Filters() []Filter
  59. SetParams(params map[string]string)
  60. }
  61. // Base represents a basic dialect and all real dialects could embed this struct
  62. type Base struct {
  63. dialect Dialect
  64. uri *URI
  65. quoter schemas.Quoter
  66. }
  67. func (b *Base) Quoter() schemas.Quoter {
  68. return b.quoter
  69. }
  70. func (b *Base) Init(dialect Dialect, uri *URI) error {
  71. b.dialect, b.uri = dialect, uri
  72. return nil
  73. }
  74. func (b *Base) URI() *URI {
  75. return b.uri
  76. }
  77. func (b *Base) DBType() schemas.DBType {
  78. return b.uri.DBType
  79. }
  80. func (b *Base) FormatBytes(bs []byte) string {
  81. return fmt.Sprintf("0x%x", bs)
  82. }
  83. func (db *Base) DropTableSQL(tableName string) (string, bool) {
  84. quote := db.dialect.Quoter().Quote
  85. return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName)), true
  86. }
  87. func (db *Base) HasRecords(queryer core.Queryer, ctx context.Context, query string, args ...interface{}) (bool, error) {
  88. rows, err := queryer.QueryContext(ctx, query, args...)
  89. if err != nil {
  90. return false, err
  91. }
  92. defer rows.Close()
  93. if rows.Next() {
  94. return true, nil
  95. }
  96. return false, nil
  97. }
  98. func (db *Base) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) {
  99. quote := db.dialect.Quoter().Quote
  100. query := fmt.Sprintf(
  101. "SELECT %v FROM %v.%v WHERE %v = ? AND %v = ? AND %v = ?",
  102. quote("COLUMN_NAME"),
  103. quote("INFORMATION_SCHEMA"),
  104. quote("COLUMNS"),
  105. quote("TABLE_SCHEMA"),
  106. quote("TABLE_NAME"),
  107. quote("COLUMN_NAME"),
  108. )
  109. return db.HasRecords(queryer, ctx, query, db.uri.DBName, tableName, colName)
  110. }
  111. func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string {
  112. s, _ := ColumnString(db.dialect, col, true)
  113. return fmt.Sprintf("ALTER TABLE %v ADD %v", db.dialect.Quoter().Quote(tableName), s)
  114. }
  115. func (db *Base) CreateIndexSQL(tableName string, index *schemas.Index) string {
  116. quoter := db.dialect.Quoter()
  117. var unique string
  118. var idxName string
  119. if index.Type == schemas.UniqueType {
  120. unique = " UNIQUE"
  121. }
  122. idxName = index.XName(tableName)
  123. return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
  124. quoter.Quote(idxName), quoter.Quote(tableName),
  125. quoter.Join(index.Cols, ","))
  126. }
  127. func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string {
  128. quote := db.dialect.Quoter().Quote
  129. var name string
  130. if index.IsRegular {
  131. name = index.XName(tableName)
  132. } else {
  133. name = index.Name
  134. }
  135. return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
  136. }
  137. func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string {
  138. s, _ := ColumnString(db.dialect, col, false)
  139. return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, s)
  140. }
  141. func (b *Base) ForUpdateSQL(query string) string {
  142. return query + " FOR UPDATE"
  143. }
  144. func (b *Base) SetParams(params map[string]string) {
  145. }
  146. var (
  147. dialects = map[string]func() Dialect{}
  148. )
  149. // RegisterDialect register database dialect
  150. func RegisterDialect(dbName schemas.DBType, dialectFunc func() Dialect) {
  151. if dialectFunc == nil {
  152. panic("core: Register dialect is nil")
  153. }
  154. dialects[strings.ToLower(string(dbName))] = dialectFunc // !nashtsai! allow override dialect
  155. }
  156. // QueryDialect query if registered database dialect
  157. func QueryDialect(dbName schemas.DBType) Dialect {
  158. if d, ok := dialects[strings.ToLower(string(dbName))]; ok {
  159. return d()
  160. }
  161. return nil
  162. }
  163. func regDrvsNDialects() bool {
  164. providedDrvsNDialects := map[string]struct {
  165. dbType schemas.DBType
  166. getDriver func() Driver
  167. getDialect func() Dialect
  168. }{
  169. "mssql": {"mssql", func() Driver { return &odbcDriver{} }, func() Dialect { return &mssql{} }},
  170. "odbc": {"mssql", func() Driver { return &odbcDriver{} }, func() Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access
  171. "mysql": {"mysql", func() Driver { return &mysqlDriver{} }, func() Dialect { return &mysql{} }},
  172. "mymysql": {"mysql", func() Driver { return &mymysqlDriver{} }, func() Dialect { return &mysql{} }},
  173. "postgres": {"postgres", func() Driver { return &pqDriver{} }, func() Dialect { return &postgres{} }},
  174. "pgx": {"postgres", func() Driver { return &pqDriverPgx{} }, func() Dialect { return &postgres{} }},
  175. "sqlite3": {"sqlite3", func() Driver { return &sqlite3Driver{} }, func() Dialect { return &sqlite3{} }},
  176. "oci8": {"oracle", func() Driver { return &oci8Driver{} }, func() Dialect { return &oracle{} }},
  177. "goracle": {"oracle", func() Driver { return &goracleDriver{} }, func() Dialect { return &oracle{} }},
  178. }
  179. for driverName, v := range providedDrvsNDialects {
  180. if driver := QueryDriver(driverName); driver == nil {
  181. RegisterDriver(driverName, v.getDriver())
  182. RegisterDialect(v.dbType, v.getDialect)
  183. }
  184. }
  185. return true
  186. }
  187. func init() {
  188. regDrvsNDialects()
  189. }
  190. // ColumnString generate column description string according dialect
  191. func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey bool) (string, error) {
  192. bd := strings.Builder{}
  193. if err := dialect.Quoter().QuoteTo(&bd, col.Name); err != nil {
  194. return "", err
  195. }
  196. if err := bd.WriteByte(' '); err != nil {
  197. return "", err
  198. }
  199. if _, err := bd.WriteString(dialect.SQLType(col)); err != nil {
  200. return "", err
  201. }
  202. if err := bd.WriteByte(' '); err != nil {
  203. return "", err
  204. }
  205. if includePrimaryKey && col.IsPrimaryKey {
  206. if _, err := bd.WriteString("PRIMARY KEY "); err != nil {
  207. return "", err
  208. }
  209. if col.IsAutoIncrement {
  210. if _, err := bd.WriteString(dialect.AutoIncrStr()); err != nil {
  211. return "", err
  212. }
  213. if err := bd.WriteByte(' '); err != nil {
  214. return "", err
  215. }
  216. }
  217. }
  218. if col.Default != "" {
  219. if _, err := bd.WriteString("DEFAULT "); err != nil {
  220. return "", err
  221. }
  222. if _, err := bd.WriteString(col.Default); err != nil {
  223. return "", err
  224. }
  225. if err := bd.WriteByte(' '); err != nil {
  226. return "", err
  227. }
  228. }
  229. if col.Nullable {
  230. if _, err := bd.WriteString("NULL "); err != nil {
  231. return "", err
  232. }
  233. } else {
  234. if _, err := bd.WriteString("NOT NULL "); err != nil {
  235. return "", err
  236. }
  237. }
  238. return bd.String(), nil
  239. }