github.com/go-courier/sqlx/v2@v2.23.13/connectors/mysql/schemas.go (about) 1 package mysql 2 3 import ( 4 "database/sql" 5 "strings" 6 7 "github.com/go-courier/sqlx/v2" 8 "github.com/go-courier/sqlx/v2/builder" 9 ) 10 11 func toInterfaces(list ...string) []interface{} { 12 s := make([]interface{}, len(list)) 13 for i, v := range list { 14 s[i] = v 15 } 16 return s 17 } 18 19 func dbFromInformationSchema(db sqlx.DBExecutor) (*sqlx.Database, error) { 20 d := db.D() 21 tableNames := d.Tables.TableNames() 22 23 database := sqlx.NewDatabase(d.Name) 24 25 tableColumnSchema := SchemaDatabase.T(&ColumnSchema{}) 26 columnSchemaList := make([]ColumnSchema, 0) 27 28 err := db.QueryExprAndScan( 29 builder.Select(tableColumnSchema.Columns.Clone()). 30 From(tableColumnSchema, 31 builder.Where( 32 builder.And( 33 tableColumnSchema.F("TABLE_SCHEMA").Eq(database.Name), 34 tableColumnSchema.F("TABLE_NAME").In(toInterfaces(tableNames...)...), 35 ), 36 ), 37 ), 38 &columnSchemaList, 39 ) 40 if err != nil { 41 return nil, err 42 } 43 44 for i := range columnSchemaList { 45 columnSchema := columnSchemaList[i] 46 table := database.Table(columnSchema.TABLE_NAME) 47 if table == nil { 48 table = builder.T(columnSchema.TABLE_NAME) 49 database.AddTable(table) 50 } 51 table.AddCol(colFromColumnSchema(&columnSchema)) 52 } 53 54 if tableColumnSchema.Columns.Len() != 0 { 55 tableIndexSchema := SchemaDatabase.T(&IndexSchema{}) 56 57 indexList := make([]IndexSchema, 0) 58 59 err = db.QueryExprAndScan( 60 builder.Select(tableIndexSchema.Columns.Clone()). 61 From( 62 tableIndexSchema, 63 builder.Where( 64 builder.And( 65 tableIndexSchema.F("TABLE_SCHEMA").Eq(database.Name), 66 tableIndexSchema.F("TABLE_NAME").In(toInterfaces(tableNames...)...), 67 ), 68 ), 69 builder.OrderBy( 70 builder.AscOrder(tableIndexSchema.F("INDEX_NAME")), 71 builder.AscOrder(tableIndexSchema.F("SEQ_IN_INDEX")), 72 ), 73 ), 74 &indexList, 75 ) 76 77 if err != nil { 78 return nil, err 79 } 80 81 for _, indexSchema := range indexList { 82 table := database.Table(indexSchema.TABLE_NAME) 83 84 if key := table.Keys.Key(indexSchema.INDEX_NAME); key != nil { 85 key.Def.ColNames = append(key.Def.ColNames, indexSchema.COLUMN_NAME) 86 } else { 87 key := &builder.Key{} 88 key.Name = strings.ToLower(indexSchema.INDEX_NAME) 89 key.Method = indexSchema.INDEX_TYPE 90 key.IsUnique = indexSchema.NON_UNIQUE == 0 91 key.Def.ColNames = []string{indexSchema.COLUMN_NAME} 92 93 table.AddKey(key) 94 } 95 } 96 } 97 98 return database, nil 99 } 100 101 var SchemaDatabase = sqlx.NewDatabase("INFORMATION_SCHEMA") 102 103 func init() { 104 SchemaDatabase.Register(&ColumnSchema{}) 105 SchemaDatabase.Register(&IndexSchema{}) 106 } 107 108 func colFromColumnSchema(columnSchema *ColumnSchema) *builder.Column { 109 col := builder.Col(columnSchema.COLUMN_NAME) 110 111 col.AutoIncrement = strings.Contains(columnSchema.EXTRA, "auto_increment") 112 113 defaultValue := columnSchema.COLUMN_DEFAULT 114 115 if defaultValue.Valid { 116 v := normalizeDefaultValue(defaultValue.String) 117 col.Default = &v 118 } 119 120 if strings.Contains(columnSchema.EXTRA, "on update ") { 121 v := strings.Split(columnSchema.EXTRA, "on update ")[1] 122 col.OnUpdate = &v 123 } 124 125 dataType := columnSchema.DATA_TYPE 126 127 if strings.HasSuffix(columnSchema.COLUMN_TYPE, "unsigned") { 128 dataType = dataType + " unsigned" 129 } 130 131 col.DataType = dataType 132 133 // numeric type 134 if columnSchema.NUMERIC_PRECISION > 0 { 135 col.Length = columnSchema.NUMERIC_PRECISION 136 col.Decimal = columnSchema.NUMERIC_SCALE 137 } else { 138 col.Length = columnSchema.CHARACTER_MAXIMUM_LENGTH 139 } 140 141 if columnSchema.IS_NULLABLE == "YES" { 142 col.Null = true 143 } 144 145 return col 146 } 147 148 // https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html 149 func normalizeDefaultValue(v string) string { 150 if len(v) == 0 { 151 return "''" 152 } 153 154 switch v { 155 case "CURRENT_TIMESTAMP", "CURRENT_DATE", "NULL": 156 return v 157 } 158 159 // functions 160 if strings.Contains(v, "(") && strings.Contains(v, ")") { 161 return v 162 } 163 164 return quoteWith(v, '\'', false, false) 165 } 166 167 type ColumnSchema struct { 168 TABLE_SCHEMA string `db:"TABLE_SCHEMA"` 169 TABLE_NAME string `db:"TABLE_NAME"` 170 COLUMN_NAME string `db:"COLUMN_NAME"` 171 DATA_TYPE string `db:"DATA_TYPE"` 172 COLUMN_TYPE string `db:"COLUMN_TYPE"` 173 EXTRA string `db:"EXTRA"` 174 IS_NULLABLE string `db:"IS_NULLABLE"` 175 COLUMN_DEFAULT sql.NullString `db:"COLUMN_DEFAULT"` 176 CHARACTER_MAXIMUM_LENGTH uint64 `db:"CHARACTER_MAXIMUM_LENGTH"` 177 NUMERIC_PRECISION uint64 `db:"NUMERIC_PRECISION"` 178 NUMERIC_SCALE uint64 `db:"NUMERIC_SCALE"` 179 } 180 181 func (ColumnSchema) TableName() string { 182 return "INFORMATION_SCHEMA.COLUMNS" 183 } 184 185 type IndexSchema struct { 186 TABLE_SCHEMA string `db:"TABLE_SCHEMA"` 187 TABLE_NAME string `db:"TABLE_NAME"` 188 NON_UNIQUE int32 `db:"NON_UNIQUE"` 189 INDEX_NAME string `db:"INDEX_NAME"` 190 SEQ_IN_INDEX int32 `db:"SEQ_IN_INDEX"` 191 COLUMN_NAME string `db:"COLUMN_NAME"` 192 SUB_PART string `db:"SUB_PART"` 193 INDEX_TYPE string `db:"INDEX_TYPE"` 194 } 195 196 func (IndexSchema) TableName() string { 197 return "INFORMATION_SCHEMA.STATISTICS" 198 }