mysql.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package mysql
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "math"
  7. "strings"
  8. _ "github.com/go-sql-driver/mysql"
  9. "gorm.io/gorm"
  10. "gorm.io/gorm/callbacks"
  11. "gorm.io/gorm/clause"
  12. "gorm.io/gorm/logger"
  13. "gorm.io/gorm/migrator"
  14. "gorm.io/gorm/schema"
  15. )
  16. type Config struct {
  17. DriverName string
  18. DSN string
  19. Conn gorm.ConnPool
  20. SkipInitializeWithVersion bool
  21. DefaultStringSize uint
  22. DisableDatetimePrecision bool
  23. DontSupportRenameIndex bool
  24. DontSupportRenameColumn bool
  25. }
  26. type Dialector struct {
  27. *Config
  28. }
  29. func Open(dsn string) gorm.Dialector {
  30. return &Dialector{Config: &Config{DSN: dsn}}
  31. }
  32. func New(config Config) gorm.Dialector {
  33. return &Dialector{Config: &config}
  34. }
  35. func (dialector Dialector) Name() string {
  36. return "mysql"
  37. }
  38. func (dialector Dialector) Initialize(db *gorm.DB) (err error) {
  39. ctx := context.Background()
  40. // register callbacks
  41. callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{})
  42. db.Callback().Update().Replace("gorm:update", Update)
  43. if dialector.DriverName == "" {
  44. dialector.DriverName = "mysql"
  45. }
  46. if dialector.Conn != nil {
  47. db.ConnPool = dialector.Conn
  48. } else {
  49. db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)
  50. if err != nil {
  51. return err
  52. }
  53. }
  54. if !dialector.Config.SkipInitializeWithVersion {
  55. var version string
  56. err = db.ConnPool.QueryRowContext(ctx, "SELECT VERSION()").Scan(&version)
  57. if err != nil {
  58. return err
  59. }
  60. if strings.Contains(version, "MariaDB") {
  61. dialector.Config.DontSupportRenameIndex = true
  62. dialector.Config.DontSupportRenameColumn = true
  63. } else if strings.HasPrefix(version, "5.6.") {
  64. dialector.Config.DontSupportRenameIndex = true
  65. dialector.Config.DontSupportRenameColumn = true
  66. } else if strings.HasPrefix(version, "5.7.") {
  67. dialector.Config.DontSupportRenameColumn = true
  68. } else if strings.HasPrefix(version, "5.") {
  69. dialector.Config.DisableDatetimePrecision = true
  70. dialector.Config.DontSupportRenameIndex = true
  71. dialector.Config.DontSupportRenameColumn = true
  72. }
  73. }
  74. for k, v := range dialector.ClauseBuilders() {
  75. db.ClauseBuilders[k] = v
  76. }
  77. return
  78. }
  79. func (dialector Dialector) ClauseBuilders() map[string]clause.ClauseBuilder {
  80. return map[string]clause.ClauseBuilder{
  81. "ON CONFLICT": func(c clause.Clause, builder clause.Builder) {
  82. if onConflict, ok := c.Expression.(clause.OnConflict); ok {
  83. builder.WriteString("ON DUPLICATE KEY UPDATE ")
  84. if len(onConflict.DoUpdates) == 0 {
  85. if s := builder.(*gorm.Statement).Schema; s != nil {
  86. var column clause.Column
  87. onConflict.DoNothing = false
  88. if s.PrioritizedPrimaryField != nil {
  89. column = clause.Column{Name: s.PrioritizedPrimaryField.DBName}
  90. } else if len(s.DBNames) > 0 {
  91. column = clause.Column{Name: s.DBNames[0]}
  92. }
  93. if column.Name != "" {
  94. onConflict.DoUpdates = []clause.Assignment{{Column: column, Value: column}}
  95. }
  96. }
  97. }
  98. for idx, assignment := range onConflict.DoUpdates {
  99. if idx > 0 {
  100. builder.WriteByte(',')
  101. }
  102. builder.WriteQuoted(assignment.Column)
  103. builder.WriteByte('=')
  104. if column, ok := assignment.Value.(clause.Column); ok && column.Table == "excluded" {
  105. column.Table = ""
  106. builder.WriteString("VALUES(")
  107. builder.WriteQuoted(column)
  108. builder.WriteByte(')')
  109. } else {
  110. builder.AddVar(builder, assignment.Value)
  111. }
  112. }
  113. } else {
  114. c.Build(builder)
  115. }
  116. },
  117. "VALUES": func(c clause.Clause, builder clause.Builder) {
  118. if values, ok := c.Expression.(clause.Values); ok && len(values.Columns) == 0 {
  119. builder.WriteString("VALUES()")
  120. return
  121. }
  122. c.Build(builder)
  123. },
  124. }
  125. }
  126. func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression {
  127. return clause.Expr{SQL: "DEFAULT"}
  128. }
  129. func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator {
  130. return Migrator{
  131. Migrator: migrator.Migrator{
  132. Config: migrator.Config{
  133. DB: db,
  134. Dialector: dialector,
  135. },
  136. },
  137. Dialector: dialector,
  138. }
  139. }
  140. func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
  141. writer.WriteByte('?')
  142. }
  143. func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
  144. writer.WriteByte('`')
  145. if strings.Contains(str, ".") {
  146. for idx, str := range strings.Split(str, ".") {
  147. if idx > 0 {
  148. writer.WriteString(".`")
  149. }
  150. writer.WriteString(str)
  151. writer.WriteByte('`')
  152. }
  153. } else {
  154. writer.WriteString(str)
  155. writer.WriteByte('`')
  156. }
  157. }
  158. func (dialector Dialector) Explain(sql string, vars ...interface{}) string {
  159. return logger.ExplainSQL(sql, nil, `'`, vars...)
  160. }
  161. func (dialector Dialector) DataTypeOf(field *schema.Field) string {
  162. switch field.DataType {
  163. case schema.Bool:
  164. return "boolean"
  165. case schema.Int, schema.Uint:
  166. sqlType := "bigint"
  167. switch {
  168. case field.Size <= 8:
  169. sqlType = "tinyint"
  170. case field.Size <= 16:
  171. sqlType = "smallint"
  172. case field.Size <= 24:
  173. sqlType = "mediumint"
  174. case field.Size <= 32:
  175. sqlType = "int"
  176. }
  177. if field.DataType == schema.Uint {
  178. sqlType += " unsigned"
  179. }
  180. if field.AutoIncrement {
  181. sqlType += " AUTO_INCREMENT"
  182. }
  183. return sqlType
  184. case schema.Float:
  185. if field.Precision > 0 {
  186. return fmt.Sprintf("decimal(%d, %d)", field.Precision, field.Scale)
  187. }
  188. if field.Size <= 32 {
  189. return "float"
  190. }
  191. return "double"
  192. case schema.String:
  193. size := field.Size
  194. defaultSize := dialector.DefaultStringSize
  195. if size == 0 {
  196. if defaultSize > 0 {
  197. size = int(defaultSize)
  198. } else {
  199. hasIndex := field.TagSettings["INDEX"] != "" || field.TagSettings["UNIQUE"] != ""
  200. // TEXT, GEOMETRY or JSON column can't have a default value
  201. if field.PrimaryKey || field.HasDefaultValue || hasIndex {
  202. size = 191 // utf8mb4
  203. }
  204. }
  205. }
  206. if size >= 65536 && size <= int(math.Pow(2, 24)) {
  207. return "mediumtext"
  208. } else if size > int(math.Pow(2, 24)) || size <= 0 {
  209. return "longtext"
  210. }
  211. return fmt.Sprintf("varchar(%d)", size)
  212. case schema.Time:
  213. precision := ""
  214. if !dialector.DisableDatetimePrecision {
  215. if field.Precision == 0 {
  216. field.Precision = 3
  217. }
  218. if field.Precision > 0 {
  219. precision = fmt.Sprintf("(%d)", field.Precision)
  220. }
  221. }
  222. if field.NotNull || field.PrimaryKey {
  223. return "datetime" + precision
  224. }
  225. return "datetime" + precision + " NULL"
  226. case schema.Bytes:
  227. if field.Size > 0 && field.Size < 65536 {
  228. return fmt.Sprintf("varbinary(%d)", field.Size)
  229. }
  230. if field.Size >= 65536 && field.Size <= int(math.Pow(2, 24)) {
  231. return "mediumblob"
  232. }
  233. return "longblob"
  234. }
  235. return string(field.DataType)
  236. }
  237. func (dialectopr Dialector) SavePoint(tx *gorm.DB, name string) error {
  238. tx.Exec("SAVEPOINT " + name)
  239. return nil
  240. }
  241. func (dialectopr Dialector) RollbackTo(tx *gorm.DB, name string) error {
  242. tx.Exec("ROLLBACK TO SAVEPOINT " + name)
  243. return nil
  244. }