github.com/kunlun-qilian/sqlx/v3@v3.0.0/connectors/mysql/schemas.go (about)

     1  package mysql
     2  
     3  import (
     4  	"database/sql"
     5  	"strings"
     6  
     7  	"github.com/kunlun-qilian/sqlx/v3"
     8  	"github.com/kunlun-qilian/sqlx/v3/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  }