migrator.go 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. package migrator
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "fmt"
  7. "reflect"
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "gorm.io/gorm"
  13. "gorm.io/gorm/clause"
  14. "gorm.io/gorm/logger"
  15. "gorm.io/gorm/schema"
  16. )
  17. // This regular expression seeks to find a sequence of digits (\d+) among zero or more non-digit characters (\D*),
  18. // with a possible trailing non-digit character (\D?).
  19. // For example, values that can pass this regular expression are:
  20. // - "123"
  21. // - "abc456"
  22. // -"%$#@789"
  23. var regFullDataType = regexp.MustCompile(`\D*(\d+)\D?`)
  24. // TODO:? Create const vars for raw sql queries ?
  25. var _ gorm.Migrator = (*Migrator)(nil)
  26. // Migrator m struct
  27. type Migrator struct {
  28. Config
  29. }
  30. // Config schema config
  31. type Config struct {
  32. CreateIndexAfterCreateTable bool
  33. DB *gorm.DB
  34. gorm.Dialector
  35. }
  36. type printSQLLogger struct {
  37. logger.Interface
  38. }
  39. func (l *printSQLLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
  40. sql, _ := fc()
  41. fmt.Println(sql + ";")
  42. l.Interface.Trace(ctx, begin, fc, err)
  43. }
  44. // GormDataTypeInterface gorm data type interface
  45. type GormDataTypeInterface interface {
  46. GormDBDataType(*gorm.DB, *schema.Field) string
  47. }
  48. // RunWithValue run migration with statement value
  49. func (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statement) error) error {
  50. stmt := &gorm.Statement{DB: m.DB}
  51. if m.DB.Statement != nil {
  52. stmt.Table = m.DB.Statement.Table
  53. stmt.TableExpr = m.DB.Statement.TableExpr
  54. }
  55. if table, ok := value.(string); ok {
  56. stmt.Table = table
  57. } else if err := stmt.ParseWithSpecialTableName(value, stmt.Table); err != nil {
  58. return err
  59. }
  60. return fc(stmt)
  61. }
  62. // DataTypeOf return field's db data type
  63. func (m Migrator) DataTypeOf(field *schema.Field) string {
  64. fieldValue := reflect.New(field.IndirectFieldType)
  65. if dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok {
  66. if dataType := dataTyper.GormDBDataType(m.DB, field); dataType != "" {
  67. return dataType
  68. }
  69. }
  70. return m.Dialector.DataTypeOf(field)
  71. }
  72. // FullDataTypeOf returns field's db full data type
  73. func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
  74. expr.SQL = m.DataTypeOf(field)
  75. if field.NotNull {
  76. expr.SQL += " NOT NULL"
  77. }
  78. if field.HasDefaultValue && (field.DefaultValueInterface != nil || field.DefaultValue != "") {
  79. if field.DefaultValueInterface != nil {
  80. defaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValueInterface}}
  81. m.Dialector.BindVarTo(defaultStmt, defaultStmt, field.DefaultValueInterface)
  82. expr.SQL += " DEFAULT " + m.Dialector.Explain(defaultStmt.SQL.String(), field.DefaultValueInterface)
  83. } else if field.DefaultValue != "(-)" {
  84. expr.SQL += " DEFAULT " + field.DefaultValue
  85. }
  86. }
  87. return
  88. }
  89. func (m Migrator) GetQueryAndExecTx() (queryTx, execTx *gorm.DB) {
  90. queryTx = m.DB.Session(&gorm.Session{})
  91. execTx = queryTx
  92. if m.DB.DryRun {
  93. queryTx.DryRun = false
  94. execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
  95. }
  96. return queryTx, execTx
  97. }
  98. // AutoMigrate auto migrate values
  99. func (m Migrator) AutoMigrate(values ...interface{}) error {
  100. for _, value := range m.ReorderModels(values, true) {
  101. queryTx, execTx := m.GetQueryAndExecTx()
  102. if !queryTx.Migrator().HasTable(value) {
  103. if err := execTx.Migrator().CreateTable(value); err != nil {
  104. return err
  105. }
  106. } else {
  107. if err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
  108. if stmt.Schema == nil {
  109. return errors.New("failed to get schema")
  110. }
  111. columnTypes, err := queryTx.Migrator().ColumnTypes(value)
  112. if err != nil {
  113. return err
  114. }
  115. var (
  116. parseIndexes = stmt.Schema.ParseIndexes()
  117. parseCheckConstraints = stmt.Schema.ParseCheckConstraints()
  118. )
  119. for _, dbName := range stmt.Schema.DBNames {
  120. var foundColumn gorm.ColumnType
  121. for _, columnType := range columnTypes {
  122. if columnType.Name() == dbName {
  123. foundColumn = columnType
  124. break
  125. }
  126. }
  127. if foundColumn == nil {
  128. // not found, add column
  129. if err = execTx.Migrator().AddColumn(value, dbName); err != nil {
  130. return err
  131. }
  132. } else {
  133. // found, smartly migrate
  134. field := stmt.Schema.FieldsByDBName[dbName]
  135. if err = execTx.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
  136. return err
  137. }
  138. }
  139. }
  140. if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {
  141. for _, rel := range stmt.Schema.Relationships.Relations {
  142. if rel.Field.IgnoreMigration {
  143. continue
  144. }
  145. if constraint := rel.ParseConstraint(); constraint != nil &&
  146. constraint.Schema == stmt.Schema && !queryTx.Migrator().HasConstraint(value, constraint.Name) {
  147. if err := execTx.Migrator().CreateConstraint(value, constraint.Name); err != nil {
  148. return err
  149. }
  150. }
  151. }
  152. }
  153. for _, chk := range parseCheckConstraints {
  154. if !queryTx.Migrator().HasConstraint(value, chk.Name) {
  155. if err := execTx.Migrator().CreateConstraint(value, chk.Name); err != nil {
  156. return err
  157. }
  158. }
  159. }
  160. for _, idx := range parseIndexes {
  161. if !queryTx.Migrator().HasIndex(value, idx.Name) {
  162. if err := execTx.Migrator().CreateIndex(value, idx.Name); err != nil {
  163. return err
  164. }
  165. }
  166. }
  167. return nil
  168. }); err != nil {
  169. return err
  170. }
  171. }
  172. }
  173. return nil
  174. }
  175. // GetTables returns tables
  176. func (m Migrator) GetTables() (tableList []string, err error) {
  177. err = m.DB.Raw("SELECT TABLE_NAME FROM information_schema.tables where TABLE_SCHEMA=?", m.CurrentDatabase()).
  178. Scan(&tableList).Error
  179. return
  180. }
  181. // CreateTable create table in database for values
  182. func (m Migrator) CreateTable(values ...interface{}) error {
  183. for _, value := range m.ReorderModels(values, false) {
  184. tx := m.DB.Session(&gorm.Session{})
  185. if err := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
  186. if stmt.Schema == nil {
  187. return errors.New("failed to get schema")
  188. }
  189. var (
  190. createTableSQL = "CREATE TABLE ? ("
  191. values = []interface{}{m.CurrentTable(stmt)}
  192. hasPrimaryKeyInDataType bool
  193. )
  194. for _, dbName := range stmt.Schema.DBNames {
  195. field := stmt.Schema.FieldsByDBName[dbName]
  196. if !field.IgnoreMigration {
  197. createTableSQL += "? ?"
  198. hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(m.DataTypeOf(field)), "PRIMARY KEY")
  199. values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))
  200. createTableSQL += ","
  201. }
  202. }
  203. if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 {
  204. createTableSQL += "PRIMARY KEY ?,"
  205. primaryKeys := make([]interface{}, 0, len(stmt.Schema.PrimaryFields))
  206. for _, field := range stmt.Schema.PrimaryFields {
  207. primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
  208. }
  209. values = append(values, primaryKeys)
  210. }
  211. for _, idx := range stmt.Schema.ParseIndexes() {
  212. if m.CreateIndexAfterCreateTable {
  213. defer func(value interface{}, name string) {
  214. if err == nil {
  215. err = tx.Migrator().CreateIndex(value, name)
  216. }
  217. }(value, idx.Name)
  218. } else {
  219. if idx.Class != "" {
  220. createTableSQL += idx.Class + " "
  221. }
  222. createTableSQL += "INDEX ? ?"
  223. if idx.Comment != "" {
  224. createTableSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment)
  225. }
  226. if idx.Option != "" {
  227. createTableSQL += " " + idx.Option
  228. }
  229. createTableSQL += ","
  230. values = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
  231. }
  232. }
  233. if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {
  234. for _, rel := range stmt.Schema.Relationships.Relations {
  235. if rel.Field.IgnoreMigration {
  236. continue
  237. }
  238. if constraint := rel.ParseConstraint(); constraint != nil {
  239. if constraint.Schema == stmt.Schema {
  240. sql, vars := constraint.Build()
  241. createTableSQL += sql + ","
  242. values = append(values, vars...)
  243. }
  244. }
  245. }
  246. }
  247. for _, uni := range stmt.Schema.ParseUniqueConstraints() {
  248. createTableSQL += "CONSTRAINT ? UNIQUE (?),"
  249. values = append(values, clause.Column{Name: uni.Name}, clause.Expr{SQL: stmt.Quote(uni.Field.DBName)})
  250. }
  251. for _, chk := range stmt.Schema.ParseCheckConstraints() {
  252. createTableSQL += "CONSTRAINT ? CHECK (?),"
  253. values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
  254. }
  255. createTableSQL = strings.TrimSuffix(createTableSQL, ",")
  256. createTableSQL += ")"
  257. if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
  258. createTableSQL += fmt.Sprint(tableOption)
  259. }
  260. err = tx.Exec(createTableSQL, values...).Error
  261. return err
  262. }); err != nil {
  263. return err
  264. }
  265. }
  266. return nil
  267. }
  268. // DropTable drop table for values
  269. func (m Migrator) DropTable(values ...interface{}) error {
  270. values = m.ReorderModels(values, false)
  271. for i := len(values) - 1; i >= 0; i-- {
  272. tx := m.DB.Session(&gorm.Session{})
  273. if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
  274. return tx.Exec("DROP TABLE IF EXISTS ?", m.CurrentTable(stmt)).Error
  275. }); err != nil {
  276. return err
  277. }
  278. }
  279. return nil
  280. }
  281. // HasTable returns table exists or not for value, value could be a struct or string
  282. func (m Migrator) HasTable(value interface{}) bool {
  283. var count int64
  284. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  285. currentDatabase := m.DB.Migrator().CurrentDatabase()
  286. return m.DB.Raw("SELECT count(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ? AND table_type = ?", currentDatabase, stmt.Table, "BASE TABLE").Row().Scan(&count)
  287. })
  288. return count > 0
  289. }
  290. // RenameTable rename table from oldName to newName
  291. func (m Migrator) RenameTable(oldName, newName interface{}) error {
  292. var oldTable, newTable interface{}
  293. if v, ok := oldName.(string); ok {
  294. oldTable = clause.Table{Name: v}
  295. } else {
  296. stmt := &gorm.Statement{DB: m.DB}
  297. if err := stmt.Parse(oldName); err == nil {
  298. oldTable = m.CurrentTable(stmt)
  299. } else {
  300. return err
  301. }
  302. }
  303. if v, ok := newName.(string); ok {
  304. newTable = clause.Table{Name: v}
  305. } else {
  306. stmt := &gorm.Statement{DB: m.DB}
  307. if err := stmt.Parse(newName); err == nil {
  308. newTable = m.CurrentTable(stmt)
  309. } else {
  310. return err
  311. }
  312. }
  313. return m.DB.Exec("ALTER TABLE ? RENAME TO ?", oldTable, newTable).Error
  314. }
  315. // AddColumn create `name` column for value
  316. func (m Migrator) AddColumn(value interface{}, name string) error {
  317. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  318. // avoid using the same name field
  319. if stmt.Schema == nil {
  320. return errors.New("failed to get schema")
  321. }
  322. f := stmt.Schema.LookUpField(name)
  323. if f == nil {
  324. return fmt.Errorf("failed to look up field with name: %s", name)
  325. }
  326. if !f.IgnoreMigration {
  327. return m.DB.Exec(
  328. "ALTER TABLE ? ADD ? ?",
  329. m.CurrentTable(stmt), clause.Column{Name: f.DBName}, m.DB.Migrator().FullDataTypeOf(f),
  330. ).Error
  331. }
  332. return nil
  333. })
  334. }
  335. // DropColumn drop value's `name` column
  336. func (m Migrator) DropColumn(value interface{}, name string) error {
  337. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  338. if stmt.Schema != nil {
  339. if field := stmt.Schema.LookUpField(name); field != nil {
  340. name = field.DBName
  341. }
  342. }
  343. return m.DB.Exec(
  344. "ALTER TABLE ? DROP COLUMN ?", m.CurrentTable(stmt), clause.Column{Name: name},
  345. ).Error
  346. })
  347. }
  348. // AlterColumn alter value's `field` column' type based on schema definition
  349. func (m Migrator) AlterColumn(value interface{}, field string) error {
  350. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  351. if stmt.Schema != nil {
  352. if field := stmt.Schema.LookUpField(field); field != nil {
  353. fileType := m.FullDataTypeOf(field)
  354. return m.DB.Exec(
  355. "ALTER TABLE ? ALTER COLUMN ? TYPE ?",
  356. m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
  357. ).Error
  358. }
  359. }
  360. return fmt.Errorf("failed to look up field with name: %s", field)
  361. })
  362. }
  363. // HasColumn check has column `field` for value or not
  364. func (m Migrator) HasColumn(value interface{}, field string) bool {
  365. var count int64
  366. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  367. currentDatabase := m.DB.Migrator().CurrentDatabase()
  368. name := field
  369. if stmt.Schema != nil {
  370. if field := stmt.Schema.LookUpField(field); field != nil {
  371. name = field.DBName
  372. }
  373. }
  374. return m.DB.Raw(
  375. "SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?",
  376. currentDatabase, stmt.Table, name,
  377. ).Row().Scan(&count)
  378. })
  379. return count > 0
  380. }
  381. // RenameColumn rename value's field name from oldName to newName
  382. func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
  383. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  384. if stmt.Schema != nil {
  385. if field := stmt.Schema.LookUpField(oldName); field != nil {
  386. oldName = field.DBName
  387. }
  388. if field := stmt.Schema.LookUpField(newName); field != nil {
  389. newName = field.DBName
  390. }
  391. }
  392. return m.DB.Exec(
  393. "ALTER TABLE ? RENAME COLUMN ? TO ?",
  394. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  395. ).Error
  396. })
  397. }
  398. // MigrateColumn migrate column
  399. func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
  400. if field.IgnoreMigration {
  401. return nil
  402. }
  403. // found, smart migrate
  404. fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))
  405. realDataType := strings.ToLower(columnType.DatabaseTypeName())
  406. var (
  407. alterColumn bool
  408. isSameType = fullDataType == realDataType
  409. )
  410. if !field.PrimaryKey {
  411. // check type
  412. if !strings.HasPrefix(fullDataType, realDataType) {
  413. // check type aliases
  414. aliases := m.DB.Migrator().GetTypeAliases(realDataType)
  415. for _, alias := range aliases {
  416. if strings.HasPrefix(fullDataType, alias) {
  417. isSameType = true
  418. break
  419. }
  420. }
  421. if !isSameType {
  422. alterColumn = true
  423. }
  424. }
  425. }
  426. if !isSameType {
  427. // check size
  428. if length, ok := columnType.Length(); length != int64(field.Size) {
  429. if length > 0 && field.Size > 0 {
  430. alterColumn = true
  431. } else {
  432. // has size in data type and not equal
  433. // Since the following code is frequently called in the for loop, reg optimization is needed here
  434. matches2 := regFullDataType.FindAllStringSubmatch(fullDataType, -1)
  435. if !field.PrimaryKey &&
  436. (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length) && ok) {
  437. alterColumn = true
  438. }
  439. }
  440. }
  441. // check precision
  442. if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
  443. if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
  444. alterColumn = true
  445. }
  446. }
  447. }
  448. // check nullable
  449. if nullable, ok := columnType.Nullable(); ok && nullable == field.NotNull {
  450. // not primary key & current database is non-nullable(to be nullable)
  451. if !field.PrimaryKey && !nullable {
  452. alterColumn = true
  453. }
  454. }
  455. // check default value
  456. if !field.PrimaryKey {
  457. currentDefaultNotNull := field.HasDefaultValue && (field.DefaultValueInterface != nil || !strings.EqualFold(field.DefaultValue, "NULL"))
  458. dv, dvNotNull := columnType.DefaultValue()
  459. if dvNotNull && !currentDefaultNotNull {
  460. // default value -> null
  461. alterColumn = true
  462. } else if !dvNotNull && currentDefaultNotNull {
  463. // null -> default value
  464. alterColumn = true
  465. } else if currentDefaultNotNull || dvNotNull {
  466. switch field.GORMDataType {
  467. case schema.Time:
  468. if !strings.EqualFold(strings.TrimSuffix(dv, "()"), strings.TrimSuffix(field.DefaultValue, "()")) {
  469. alterColumn = true
  470. }
  471. case schema.Bool:
  472. v1, _ := strconv.ParseBool(dv)
  473. v2, _ := strconv.ParseBool(field.DefaultValue)
  474. alterColumn = v1 != v2
  475. default:
  476. alterColumn = dv != field.DefaultValue
  477. }
  478. }
  479. }
  480. // check comment
  481. if comment, ok := columnType.Comment(); ok && comment != field.Comment {
  482. // not primary key
  483. if !field.PrimaryKey {
  484. alterColumn = true
  485. }
  486. }
  487. if alterColumn {
  488. if err := m.DB.Migrator().AlterColumn(value, field.DBName); err != nil {
  489. return err
  490. }
  491. }
  492. if err := m.DB.Migrator().MigrateColumnUnique(value, field, columnType); err != nil {
  493. return err
  494. }
  495. return nil
  496. }
  497. func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
  498. unique, ok := columnType.Unique()
  499. if !ok || field.PrimaryKey {
  500. return nil // skip primary key
  501. }
  502. // By default, ColumnType's Unique is not affected by UniqueIndex, so we don't care about UniqueIndex.
  503. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  504. // We're currently only receiving boolean values on `Unique` tag,
  505. // so the UniqueConstraint name is fixed
  506. constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
  507. if unique && !field.Unique {
  508. return m.DB.Migrator().DropConstraint(value, constraint)
  509. }
  510. if !unique && field.Unique {
  511. return m.DB.Migrator().CreateConstraint(value, constraint)
  512. }
  513. return nil
  514. })
  515. }
  516. // ColumnTypes return columnTypes []gorm.ColumnType and execErr error
  517. func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
  518. columnTypes := make([]gorm.ColumnType, 0)
  519. execErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
  520. rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
  521. if err != nil {
  522. return err
  523. }
  524. defer func() {
  525. err = rows.Close()
  526. }()
  527. var rawColumnTypes []*sql.ColumnType
  528. rawColumnTypes, err = rows.ColumnTypes()
  529. if err != nil {
  530. return err
  531. }
  532. for _, c := range rawColumnTypes {
  533. columnTypes = append(columnTypes, ColumnType{SQLColumnType: c})
  534. }
  535. return
  536. })
  537. return columnTypes, execErr
  538. }
  539. // CreateView create view from Query in gorm.ViewOption.
  540. // Query in gorm.ViewOption is a [subquery]
  541. //
  542. // // CREATE VIEW `user_view` AS SELECT * FROM `users` WHERE age > 20
  543. // q := DB.Model(&User{}).Where("age > ?", 20)
  544. // DB.Debug().Migrator().CreateView("user_view", gorm.ViewOption{Query: q})
  545. //
  546. // // CREATE OR REPLACE VIEW `users_view` AS SELECT * FROM `users` WITH CHECK OPTION
  547. // q := DB.Model(&User{})
  548. // DB.Debug().Migrator().CreateView("user_view", gorm.ViewOption{Query: q, Replace: true, CheckOption: "WITH CHECK OPTION"})
  549. //
  550. // [subquery]: https://gorm.io/docs/advanced_query.html#SubQuery
  551. func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
  552. if option.Query == nil {
  553. return gorm.ErrSubQueryRequired
  554. }
  555. sql := new(strings.Builder)
  556. sql.WriteString("CREATE ")
  557. if option.Replace {
  558. sql.WriteString("OR REPLACE ")
  559. }
  560. sql.WriteString("VIEW ")
  561. m.QuoteTo(sql, name)
  562. sql.WriteString(" AS ")
  563. m.DB.Statement.AddVar(sql, option.Query)
  564. if option.CheckOption != "" {
  565. sql.WriteString(" ")
  566. sql.WriteString(option.CheckOption)
  567. }
  568. return m.DB.Exec(m.Explain(sql.String(), m.DB.Statement.Vars...)).Error
  569. }
  570. // DropView drop view
  571. func (m Migrator) DropView(name string) error {
  572. return m.DB.Exec("DROP VIEW IF EXISTS ?", clause.Table{Name: name}).Error
  573. }
  574. // GuessConstraintAndTable guess statement's constraint and it's table based on name
  575. //
  576. // Deprecated: use GuessConstraintInterfaceAndTable instead.
  577. func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (*schema.Constraint, *schema.CheckConstraint, string) {
  578. constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
  579. switch c := constraint.(type) {
  580. case *schema.Constraint:
  581. return c, nil, table
  582. case *schema.CheckConstraint:
  583. return nil, c, table
  584. default:
  585. return nil, nil, table
  586. }
  587. }
  588. // GuessConstraintInterfaceAndTable guess statement's constraint and it's table based on name
  589. // nolint:cyclop
  590. func (m Migrator) GuessConstraintInterfaceAndTable(stmt *gorm.Statement, name string) (_ schema.ConstraintInterface, table string) {
  591. if stmt.Schema == nil {
  592. return nil, stmt.Table
  593. }
  594. checkConstraints := stmt.Schema.ParseCheckConstraints()
  595. if chk, ok := checkConstraints[name]; ok {
  596. return &chk, stmt.Table
  597. }
  598. uniqueConstraints := stmt.Schema.ParseUniqueConstraints()
  599. if uni, ok := uniqueConstraints[name]; ok {
  600. return &uni, stmt.Table
  601. }
  602. getTable := func(rel *schema.Relationship) string {
  603. switch rel.Type {
  604. case schema.HasOne, schema.HasMany:
  605. return rel.FieldSchema.Table
  606. case schema.Many2Many:
  607. return rel.JoinTable.Table
  608. }
  609. return stmt.Table
  610. }
  611. for _, rel := range stmt.Schema.Relationships.Relations {
  612. if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
  613. return constraint, getTable(rel)
  614. }
  615. }
  616. if field := stmt.Schema.LookUpField(name); field != nil {
  617. for k := range checkConstraints {
  618. if checkConstraints[k].Field == field {
  619. v := checkConstraints[k]
  620. return &v, stmt.Table
  621. }
  622. }
  623. for k := range uniqueConstraints {
  624. if uniqueConstraints[k].Field == field {
  625. v := uniqueConstraints[k]
  626. return &v, stmt.Table
  627. }
  628. }
  629. for _, rel := range stmt.Schema.Relationships.Relations {
  630. if constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {
  631. return constraint, getTable(rel)
  632. }
  633. }
  634. }
  635. return nil, stmt.Schema.Table
  636. }
  637. // CreateConstraint create constraint
  638. func (m Migrator) CreateConstraint(value interface{}, name string) error {
  639. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  640. constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
  641. if constraint != nil {
  642. vars := []interface{}{clause.Table{Name: table}}
  643. if stmt.TableExpr != nil {
  644. vars[0] = stmt.TableExpr
  645. }
  646. sql, values := constraint.Build()
  647. return m.DB.Exec("ALTER TABLE ? ADD "+sql, append(vars, values...)...).Error
  648. }
  649. return nil
  650. })
  651. }
  652. // DropConstraint drop constraint
  653. func (m Migrator) DropConstraint(value interface{}, name string) error {
  654. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  655. constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
  656. if constraint != nil {
  657. name = constraint.GetName()
  658. }
  659. return m.DB.Exec("ALTER TABLE ? DROP CONSTRAINT ?", clause.Table{Name: table}, clause.Column{Name: name}).Error
  660. })
  661. }
  662. // HasConstraint check has constraint or not
  663. func (m Migrator) HasConstraint(value interface{}, name string) bool {
  664. var count int64
  665. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  666. currentDatabase := m.DB.Migrator().CurrentDatabase()
  667. constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
  668. if constraint != nil {
  669. name = constraint.GetName()
  670. }
  671. return m.DB.Raw(
  672. "SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
  673. currentDatabase, table, name,
  674. ).Row().Scan(&count)
  675. })
  676. return count > 0
  677. }
  678. // BuildIndexOptions build index options
  679. func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
  680. for _, opt := range opts {
  681. str := stmt.Quote(opt.DBName)
  682. if opt.Expression != "" {
  683. str = opt.Expression
  684. } else if opt.Length > 0 {
  685. str += fmt.Sprintf("(%d)", opt.Length)
  686. }
  687. if opt.Collate != "" {
  688. str += " COLLATE " + opt.Collate
  689. }
  690. if opt.Sort != "" {
  691. str += " " + opt.Sort
  692. }
  693. results = append(results, clause.Expr{SQL: str})
  694. }
  695. return
  696. }
  697. // BuildIndexOptionsInterface build index options interface
  698. type BuildIndexOptionsInterface interface {
  699. BuildIndexOptions([]schema.IndexOption, *gorm.Statement) []interface{}
  700. }
  701. // CreateIndex create index `name`
  702. func (m Migrator) CreateIndex(value interface{}, name string) error {
  703. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  704. if stmt.Schema == nil {
  705. return errors.New("failed to get schema")
  706. }
  707. if idx := stmt.Schema.LookIndex(name); idx != nil {
  708. opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)
  709. values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
  710. createIndexSQL := "CREATE "
  711. if idx.Class != "" {
  712. createIndexSQL += idx.Class + " "
  713. }
  714. createIndexSQL += "INDEX ? ON ??"
  715. if idx.Type != "" {
  716. createIndexSQL += " USING " + idx.Type
  717. }
  718. if idx.Comment != "" {
  719. createIndexSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment)
  720. }
  721. if idx.Option != "" {
  722. createIndexSQL += " " + idx.Option
  723. }
  724. return m.DB.Exec(createIndexSQL, values...).Error
  725. }
  726. return fmt.Errorf("failed to create index with name %s", name)
  727. })
  728. }
  729. // DropIndex drop index `name`
  730. func (m Migrator) DropIndex(value interface{}, name string) error {
  731. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  732. if stmt.Schema != nil {
  733. if idx := stmt.Schema.LookIndex(name); idx != nil {
  734. name = idx.Name
  735. }
  736. }
  737. return m.DB.Exec("DROP INDEX ? ON ?", clause.Column{Name: name}, m.CurrentTable(stmt)).Error
  738. })
  739. }
  740. // HasIndex check has index `name` or not
  741. func (m Migrator) HasIndex(value interface{}, name string) bool {
  742. var count int64
  743. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  744. currentDatabase := m.DB.Migrator().CurrentDatabase()
  745. if stmt.Schema != nil {
  746. if idx := stmt.Schema.LookIndex(name); idx != nil {
  747. name = idx.Name
  748. }
  749. }
  750. return m.DB.Raw(
  751. "SELECT count(*) FROM information_schema.statistics WHERE table_schema = ? AND table_name = ? AND index_name = ?",
  752. currentDatabase, stmt.Table, name,
  753. ).Row().Scan(&count)
  754. })
  755. return count > 0
  756. }
  757. // RenameIndex rename index from oldName to newName
  758. func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
  759. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  760. return m.DB.Exec(
  761. "ALTER TABLE ? RENAME INDEX ? TO ?",
  762. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  763. ).Error
  764. })
  765. }
  766. // CurrentDatabase returns current database name
  767. func (m Migrator) CurrentDatabase() (name string) {
  768. m.DB.Raw("SELECT DATABASE()").Row().Scan(&name)
  769. return
  770. }
  771. // ReorderModels reorder models according to constraint dependencies
  772. func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []interface{}) {
  773. type Dependency struct {
  774. *gorm.Statement
  775. Depends []*schema.Schema
  776. }
  777. var (
  778. modelNames, orderedModelNames []string
  779. orderedModelNamesMap = map[string]bool{}
  780. parsedSchemas = map[*schema.Schema]bool{}
  781. valuesMap = map[string]Dependency{}
  782. insertIntoOrderedList func(name string)
  783. parseDependence func(value interface{}, addToList bool)
  784. )
  785. parseDependence = func(value interface{}, addToList bool) {
  786. dep := Dependency{
  787. Statement: &gorm.Statement{DB: m.DB, Dest: value},
  788. }
  789. beDependedOn := map[*schema.Schema]bool{}
  790. // support for special table name
  791. if err := dep.ParseWithSpecialTableName(value, m.DB.Statement.Table); err != nil {
  792. m.DB.Logger.Error(context.Background(), "failed to parse value %#v, got error %v", value, err)
  793. }
  794. if _, ok := parsedSchemas[dep.Statement.Schema]; ok {
  795. return
  796. }
  797. parsedSchemas[dep.Statement.Schema] = true
  798. if !m.DB.IgnoreRelationshipsWhenMigrating {
  799. for _, rel := range dep.Schema.Relationships.Relations {
  800. if rel.Field.IgnoreMigration {
  801. continue
  802. }
  803. if c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema {
  804. dep.Depends = append(dep.Depends, c.ReferenceSchema)
  805. }
  806. if rel.Type == schema.HasOne || rel.Type == schema.HasMany {
  807. beDependedOn[rel.FieldSchema] = true
  808. }
  809. if rel.JoinTable != nil {
  810. // append join value
  811. defer func(rel *schema.Relationship, joinValue interface{}) {
  812. if !beDependedOn[rel.FieldSchema] {
  813. dep.Depends = append(dep.Depends, rel.FieldSchema)
  814. } else {
  815. fieldValue := reflect.New(rel.FieldSchema.ModelType).Interface()
  816. parseDependence(fieldValue, autoAdd)
  817. }
  818. parseDependence(joinValue, autoAdd)
  819. }(rel, reflect.New(rel.JoinTable.ModelType).Interface())
  820. }
  821. }
  822. }
  823. valuesMap[dep.Schema.Table] = dep
  824. if addToList {
  825. modelNames = append(modelNames, dep.Schema.Table)
  826. }
  827. }
  828. insertIntoOrderedList = func(name string) {
  829. if _, ok := orderedModelNamesMap[name]; ok {
  830. return // avoid loop
  831. }
  832. orderedModelNamesMap[name] = true
  833. if autoAdd {
  834. dep := valuesMap[name]
  835. for _, d := range dep.Depends {
  836. if _, ok := valuesMap[d.Table]; ok {
  837. insertIntoOrderedList(d.Table)
  838. } else {
  839. parseDependence(reflect.New(d.ModelType).Interface(), autoAdd)
  840. insertIntoOrderedList(d.Table)
  841. }
  842. }
  843. }
  844. orderedModelNames = append(orderedModelNames, name)
  845. }
  846. for _, value := range values {
  847. if v, ok := value.(string); ok {
  848. results = append(results, v)
  849. } else {
  850. parseDependence(value, true)
  851. }
  852. }
  853. for _, name := range modelNames {
  854. insertIntoOrderedList(name)
  855. }
  856. for _, name := range orderedModelNames {
  857. results = append(results, valuesMap[name].Statement.Dest)
  858. }
  859. return
  860. }
  861. // CurrentTable returns current statement's table expression
  862. func (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {
  863. if stmt.TableExpr != nil {
  864. return *stmt.TableExpr
  865. }
  866. return clause.Table{Name: stmt.Table}
  867. }
  868. // GetIndexes return Indexes []gorm.Index and execErr error
  869. func (m Migrator) GetIndexes(dst interface{}) ([]gorm.Index, error) {
  870. return nil, errors.New("not support")
  871. }
  872. // GetTypeAliases return database type aliases
  873. func (m Migrator) GetTypeAliases(databaseTypeName string) []string {
  874. return nil
  875. }
  876. // TableType return tableType gorm.TableType and execErr error
  877. func (m Migrator) TableType(dst interface{}) (gorm.TableType, error) {
  878. return nil, errors.New("not support")
  879. }