func (isp *InfoSchemaPlan) doColumns(schemas []*model.DBInfo, iterFunc plan.RowIterFunc) error { for _, schema := range schemas { for _, table := range schema.Tables { for i, col := range table.Columns { colLen := col.Flen if colLen == types.UnspecifiedLength { colLen = mysql.GetDefaultFieldLength(col.Tp) } decimal := col.Decimal if decimal == types.UnspecifiedLength { decimal = 0 } dataType := types.TypeToStr(col.Tp, col.Charset == charset.CharsetBin) columnType := fmt.Sprintf("%s(%d)", dataType, colLen) columnDesc := column.NewColDesc(&column.Col{ColumnInfo: *col}) var columnDefault interface{} if columnDesc.DefaultValue != nil { columnDefault = fmt.Sprintf("%v", columnDesc.DefaultValue) } record := []interface{}{ catalogVal, // TABLE_CATALOG schema.Name.O, // TABLE_SCHEMA table.Name.O, // TABLE_NAME col.Name.O, // COLUMN_NAME i + 1, // ORIGINAL_POSITION columnDefault, // COLUMN_DEFAULT columnDesc.Null, // IS_NULLABLE types.TypeToStr(col.Tp, col.Charset == charset.CharsetBin), // DATA_TYPE colLen, // CHARACTER_MAXIMUM_LENGTH colLen, // CHARACTOR_OCTET_LENGTH decimal, // NUMERIC_PRECISION 0, // NUMERIC_SCALE 0, // DATETIME_PRECISION col.Charset, // CHARACTER_SET_NAME col.Collate, // COLLATION_NAME columnType, // COLUMN_TYPE columnDesc.Key, // COLUMN_KEY columnDesc.Extra, // EXTRA "select,insert,update,references", // PRIVILEGES "", // COLUMN_COMMENT } if more, err := iterFunc(0, record); !more || err != nil { return err } } } } return nil }
func (isp *InfoSchemaPlan) fetchColumns(schemas []*model.DBInfo) { for _, schema := range schemas { for _, table := range schema.Tables { for i, col := range table.Columns { colLen := col.Flen if colLen == types.UnspecifiedLength { colLen = mysql.GetDefaultFieldLength(col.Tp) } decimal := col.Decimal if decimal == types.UnspecifiedLength { decimal = 0 } columnType := col.FieldType.CompactStr() columnDesc := column.NewColDesc(&column.Col{ColumnInfo: *col}) var columnDefault interface{} if columnDesc.DefaultValue != nil { columnDefault = fmt.Sprintf("%v", columnDesc.DefaultValue) } record := []interface{}{ catalogVal, // TABLE_CATALOG schema.Name.O, // TABLE_SCHEMA table.Name.O, // TABLE_NAME col.Name.O, // COLUMN_NAME i + 1, // ORIGINAL_POSITION columnDefault, // COLUMN_DEFAULT columnDesc.Null, // IS_NULLABLE types.TypeToStr(col.Tp, col.Charset), // DATA_TYPE colLen, // CHARACTER_MAXIMUM_LENGTH colLen, // CHARACTOR_OCTET_LENGTH decimal, // NUMERIC_PRECISION 0, // NUMERIC_SCALE 0, // DATETIME_PRECISION col.Charset, // CHARACTER_SET_NAME col.Collate, // COLLATION_NAME columnType, // COLUMN_TYPE columnDesc.Key, // COLUMN_KEY columnDesc.Extra, // EXTRA "select,insert,update,references", // PRIVILEGES "", // COLUMN_COMMENT } isp.rows = append(isp.rows, &plan.Row{Data: record}) } } } }
// Set ResultField info according to values // This is used for inferring calculated fields type/Flen/charset // For example "select count(*) from t;" will return a ResultField with type TypeLonglong, charset binary and Flen 21. func setResultFieldInfo(fields []*field.ResultField, values []interface{}) error { if len(fields) != len(values) { return errors.Errorf("Fields and Values length unmatch %d VS %d", len(fields), len(values)) } for i, rf := range fields { if rf.Col.Tp == 0 { // 0 == TypeDecimal, Tp maybe uninitialized rf.Col.Charset = charset.CharsetBin rf.Col.Collate = charset.CharsetBin c := values[i] switch v := c.(type) { case int8, int16, int, int32, int64: rf.Col.Tp = mysql.TypeLonglong case uint8, uint16, uint, uint32, uint64: rf.Col.Tp = mysql.TypeLonglong rf.Col.Flag |= mysql.UnsignedFlag case float32, float64: rf.Col.Tp = mysql.TypeFloat case string: rf.Col.Tp = mysql.TypeVarchar rf.Col.Flen = len(v) rf.Col.Charset = mysql.DefaultCharset rf.Col.Collate = mysql.DefaultCollationName case []byte: rf.Col.Tp = mysql.TypeBlob case mysql.Time: rf.Col.Tp = v.Type case mysql.Duration: rf.Col.Tp = mysql.TypeDuration case mysql.Decimal: rf.Col.Tp = mysql.TypeDecimal default: return errors.Errorf("Unknown type %T", c) } if rf.Col.Flen == 0 { rf.Col.Flen = mysql.GetDefaultFieldLength(rf.Col.Tp) } // TODO: set flags } } return nil }
// ColumnDefToCol converts converts ColumnDef to Col and TableConstraints. func ColumnDefToCol(offset int, colDef *ColumnDef) (*column.Col, []*TableConstraint, error) { constraints := []*TableConstraint{} col := &column.Col{ ColumnInfo: model.ColumnInfo{ Offset: offset, Name: model.NewCIStr(colDef.Name), FieldType: *colDef.Tp, }, } // Check and set TimestampFlag and OnUpdateNowFlag. if col.Tp == mysql.TypeTimestamp { col.Flag |= mysql.TimestampFlag col.Flag |= mysql.OnUpdateNowFlag col.Flag |= mysql.NotNullFlag } // If flen is not assigned, assigned it by type. if col.Flen == 0 { col.Flen = mysql.GetDefaultFieldLength(col.Tp) } setOnUpdateNow := false hasDefaultValue := false if colDef.Constraints != nil { keys := []*IndexColName{ { colDef.Name, colDef.Tp.Flen, }, } for _, v := range colDef.Constraints { switch v.Tp { case ConstrNotNull: col.Flag |= mysql.NotNullFlag case ConstrNull: col.Flag &= ^uint(mysql.NotNullFlag) removeOnUpdateNowFlag(col) case ConstrAutoIncrement: col.Flag |= mysql.AutoIncrementFlag case ConstrPrimaryKey: constraint := &TableConstraint{Tp: ConstrPrimaryKey, Keys: keys} constraints = append(constraints, constraint) col.Flag |= mysql.PriKeyFlag case ConstrUniq: constraint := &TableConstraint{Tp: ConstrUniq, ConstrName: colDef.Name, Keys: keys} constraints = append(constraints, constraint) col.Flag |= mysql.UniqueKeyFlag case ConstrIndex: constraint := &TableConstraint{Tp: ConstrIndex, ConstrName: colDef.Name, Keys: keys} constraints = append(constraints, constraint) case ConstrUniqIndex: constraint := &TableConstraint{Tp: ConstrUniqIndex, ConstrName: colDef.Name, Keys: keys} constraints = append(constraints, constraint) col.Flag |= mysql.UniqueKeyFlag case ConstrKey: constraint := &TableConstraint{Tp: ConstrKey, ConstrName: colDef.Name, Keys: keys} constraints = append(constraints, constraint) case ConstrUniqKey: constraint := &TableConstraint{Tp: ConstrUniqKey, ConstrName: colDef.Name, Keys: keys} constraints = append(constraints, constraint) col.Flag |= mysql.UniqueKeyFlag case ConstrDefaultValue: value, err := getDefaultValue(v, colDef.Tp.Tp, colDef.Tp.Decimal) if err != nil { return nil, nil, errors.Errorf("invalid default value - %s", errors.Trace(err)) } col.DefaultValue = value hasDefaultValue = true removeOnUpdateNowFlag(col) case ConstrOnUpdate: if !expressions.IsCurrentTimeExpr(v.Evalue) { return nil, nil, errors.Errorf("invalid ON UPDATE for - %s", col.Name) } col.Flag |= mysql.OnUpdateNowFlag setOnUpdateNow = true case ConstrFulltext: // Do nothing. } } } setTimestampDefaultValue(col, hasDefaultValue, setOnUpdateNow) // Set `NoDefaultValueFlag` if this field doesn't have a default value and // it is `not null` and not an `AUTO_INCREMENT` field or `TIMESTAMP` field. setNoDefaultValueFlag(col, hasDefaultValue) err := checkDefaultValue(col, hasDefaultValue) if err != nil { return nil, nil, errors.Trace(err) } if col.Charset == charset.CharsetBin { col.Flag |= mysql.BinaryFlag } return col, constraints, nil }