// The return type of a CASE expression is the compatible aggregated type of all return values, // but also depends on the context in which it is used. // If used in a string context, the result is returned as a string. // If used in a numeric context, the result is returned as a decimal, real, or integer value. func (v *typeInferrer) handleCaseExpr(x *ast.CaseExpr) { var currType *types.FieldType for _, w := range x.WhenClauses { t := w.Result.GetType() if currType == nil { currType = t continue } mtp := types.MergeFieldType(currType.Tp, t.Tp) if mtp == t.Tp && mtp != currType.Tp { currType.Charset = t.Charset currType.Collate = t.Collate } currType.Tp = mtp } if x.ElseClause != nil { t := x.ElseClause.GetType() if currType == nil { currType = t } else { mtp := types.MergeFieldType(currType.Tp, t.Tp) if mtp == t.Tp && mtp != currType.Tp { currType.Charset = t.Charset currType.Collate = t.Collate } currType.Tp = mtp } } x.SetType(currType) // TODO: We need a better way to set charset/collation x.Type.Charset, x.Type.Collate = types.DefaultCharsetForType(x.Type.Tp) }
// AddCastToString adds a cast function to string type if the expr charset is not UTF8. func (v *typeInferrer) addCastToString(expr ast.ExprNode) ast.ExprNode { if !mysql.IsUTF8Charset(expr.GetType().Charset) { castTp := types.NewFieldType(mysql.TypeString) castTp.Charset, castTp.Collate = types.DefaultCharsetForType(mysql.TypeString) if val, ok := expr.(*ast.ValueExpr); ok { newVal, err := val.Datum.ConvertTo(v.sc, castTp) if err != nil { v.err = errors.Trace(err) } expr.SetDatum(newVal) } else { castFunc := &ast.FuncCastExpr{ Expr: expr, Tp: castTp, FunctionType: ast.CastFunction, } expr = castFunc } expr.SetType(castTp) } return expr }
func (v *typeInferrer) Leave(in ast.Node) (out ast.Node, ok bool) { switch x := in.(type) { case *ast.AggregateFuncExpr: v.aggregateFunc(x) case *ast.BetweenExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.BinaryOperationExpr: v.binaryOperation(x) case *ast.CaseExpr: v.handleCaseExpr(x) case *ast.ColumnNameExpr: x.SetType(&x.Refer.Column.FieldType) case *ast.CompareSubqueryExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.ExistsSubqueryExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.FuncCallExpr: v.handleFuncCallExpr(x) case *ast.FuncCastExpr: x.SetType(x.Tp) if len(x.Type.Charset) == 0 { x.Type.Charset, x.Type.Collate = types.DefaultCharsetForType(x.Type.Tp) } case *ast.IsNullExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.IsTruthExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.ParamMarkerExpr: x.SetType(types.DefaultTypeForValue(x.GetValue())) case *ast.ParenthesesExpr: x.SetType(x.Expr.GetType()) case *ast.PatternInExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.PatternLikeExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.PatternRegexpExpr: x.SetType(types.NewFieldType(mysql.TypeLonglong)) x.Type.Charset = charset.CharsetBin x.Type.Collate = charset.CollationBin case *ast.SelectStmt: v.selectStmt(x) case *ast.UnaryOperationExpr: v.unaryOperation(x) case *ast.ValueExpr: v.handleValueExpr(x) case *ast.VariableExpr: x.SetType(types.NewFieldType(mysql.TypeVarString)) x.Type.Charset = v.defaultCharset cln, err := charset.GetDefaultCollation(v.defaultCharset) if err != nil { v.err = err } x.Type.Collate = cln // TODO: handle all expression types. } return in, true }
func (nr *nameResolver) fillShowFields(s *ast.ShowStmt) { if s.DBName == "" { if s.Table != nil && s.Table.Schema.L != "" { s.DBName = s.Table.Schema.O } else { s.DBName = nr.DefaultSchema.O } } else if s.Table != nil && s.Table.Schema.L == "" { s.Table.Schema = model.NewCIStr(s.DBName) } var fields []*ast.ResultField var ( names []string ftypes []byte ) switch s.Tp { case ast.ShowEngines: names = []string{"Engine", "Support", "Comment", "Transactions", "XA", "Savepoints"} case ast.ShowDatabases: names = []string{"Database"} case ast.ShowTables: names = []string{fmt.Sprintf("Tables_in_%s", s.DBName)} if s.Full { names = append(names, "Table_type") } case ast.ShowTableStatus: names = []string{"Name", "Engine", "Version", "Row_format", "Rows", "Avg_row_length", "Data_length", "Max_data_length", "Index_length", "Data_free", "Auto_increment", "Create_time", "Update_time", "Check_time", "Collation", "Checksum", "Create_options", "Comment"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case ast.ShowColumns: names = table.ColDescFieldNames(s.Full) case ast.ShowWarnings: names = []string{"Level", "Code", "Message"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeLong, mysql.TypeVarchar} case ast.ShowCharset: names = []string{"Charset", "Description", "Default collation", "Maxlen"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong} case ast.ShowVariables: names = []string{"Variable_name", "Value"} case ast.ShowStatus: names = []string{"Variable_name", "Value"} case ast.ShowCollation: names = []string{"Collation", "Charset", "Id", "Default", "Compiled", "Sortlen"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong} case ast.ShowCreateTable: names = []string{"Table", "Create Table"} case ast.ShowGrants: names = []string{fmt.Sprintf("Grants for %s", s.User)} case ast.ShowTriggers: names = []string{"Trigger", "Event", "Table", "Statement", "Timing", "Created", "sql_mode", "Definer", "character_set_client", "collation_connection", "Database Collation"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case ast.ShowProcedureStatus: names = []string{} ftypes = []byte{} case ast.ShowIndex: names = []string{"Table", "Non_unique", "Key_name", "Seq_in_index", "Column_name", "Collation", "Cardinality", "Sub_part", "Packed", "Null", "Index_type", "Comment", "Index_comment"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} } for i, name := range names { f := &ast.ResultField{ ColumnAsName: model.NewCIStr(name), Column: &model.ColumnInfo{}, // Empty column info. Table: &model.TableInfo{}, // Empty table info. } if ftypes == nil || ftypes[i] == 0 { // use varchar as the default return column type f.Column.Tp = mysql.TypeVarchar } else { f.Column.Tp = ftypes[i] } f.Column.Charset, f.Column.Collate = types.DefaultCharsetForType(f.Column.Tp) f.Expr = &ast.ValueExpr{} f.Expr.SetType(&f.Column.FieldType) fields = append(fields, f) } if s.Pattern != nil && s.Pattern.Expr == nil { rf := fields[0] s.Pattern.Expr = &ast.ColumnNameExpr{ Name: &ast.ColumnName{Name: rf.ColumnAsName}, } ast.SetFlag(s.Pattern) } s.SetResultFields(fields) nr.currentContext().fieldList = fields }
func newStringType() types.FieldType { ft := types.NewFieldType(mysql.TypeVarchar) ft.Charset, ft.Collate = types.DefaultCharsetForType(mysql.TypeVarchar) return *ft }
// GetFields implements plan.Plan GetFields interface. func (s *ShowPlan) GetFields() []*field.ResultField { var ( names []string ftypes []byte ) switch s.Target { case stmt.ShowEngines: names = []string{"Engine", "Support", "Comment", "Transactions", "XA", "Savepoints"} case stmt.ShowDatabases: names = []string{"Database"} case stmt.ShowTables: names = []string{fmt.Sprintf("Tables_in_%s", s.DBName)} if s.Full { names = append(names, "Table_type") } case stmt.ShowTableStatus: names = []string{"Name", "Engine", "Version", "Row_format", "Rows", "Avg_row_length", "Data_length", "Max_data_length", "Index_length", "Data_free", "Auto_increment", "Create_time", "Update_time", "Check_time", "Collation", "Checksum", "Create_options", "Comment"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case stmt.ShowColumns: names = column.ColDescFieldNames(s.Full) case stmt.ShowWarnings: names = []string{"Level", "Code", "Message"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeLong, mysql.TypeVarchar} case stmt.ShowCharset: names = []string{"Charset", "Description", "Default collation", "Maxlen"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong} case stmt.ShowVariables: names = []string{"Variable_name", "Value"} case stmt.ShowStatus: names = []string{"Variable_name", "Value"} case stmt.ShowCollation: names = []string{"Collation", "Charset", "Id", "Default", "Compiled", "Sortlen"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong} case stmt.ShowCreateTable: names = []string{"Table", "Create Table"} case stmt.ShowGrants: names = []string{fmt.Sprintf("Grants for %s", s.User)} case stmt.ShowTriggers: names = []string{"Trigger", "Event", "Table", "Statement", "Timing", "Created", "sql_mode", "Definer", "character_set_client", "collation_connection", "Database Collation"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case stmt.ShowProcedureStatus: names = []string{} ftypes = []byte{} case stmt.ShowIndex: names = []string{"Table", "Non_unique", "Key_name", "Seq_in_index", "Column_name", "Collation", "Cardinality", "Sub_part", "Packed", "Null", "Index_type", "Comment", "Index_comment"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} } fields := make([]*field.ResultField, 0, len(names)) for i, name := range names { f := &field.ResultField{Name: name} if ftypes == nil || ftypes[i] == 0 { // use varchar as the default return column type f.Col.Tp = mysql.TypeVarchar } else { f.Col.Tp = ftypes[i] } f.Col.Charset, f.Col.Collate = types.DefaultCharsetForType(f.Col.Tp) fields = append(fields, f) } return fields }