func (v *typeInferrer) aggregateFunc(x *ast.AggregateFuncExpr) { name := strings.ToLower(x.F) switch name { case ast.AggFuncCount: ft := types.NewFieldType(mysql.TypeLonglong) ft.Flen = 21 ft.Charset = charset.CharsetBin ft.Collate = charset.CollationBin x.SetType(ft) case ast.AggFuncMax, ast.AggFuncMin: x.SetType(x.Args[0].GetType()) case ast.AggFuncSum, ast.AggFuncAvg: ft := types.NewFieldType(mysql.TypeNewDecimal) ft.Charset = charset.CharsetBin ft.Collate = charset.CollationBin x.SetType(ft) case ast.AggFuncGroupConcat: ft := types.NewFieldType(mysql.TypeVarString) ft.Charset = v.defaultCharset cln, err := charset.GetDefaultCollation(v.defaultCharset) if err != nil { v.err = err } ft.Collate = cln x.SetType(ft) } }
func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { var ( tp *types.FieldType chs = charset.CharsetBin ) switch x.FnName.L { case "abs", "ifnull", "nullif": tp = x.Args[0].GetType() case "pow", "power", "rand": tp = types.NewFieldType(mysql.TypeDouble) case "curdate", "current_date", "date": tp = types.NewFieldType(mysql.TypeDate) case "curtime", "current_time": tp = types.NewFieldType(mysql.TypeDuration) tp.Decimal = v.getFsp(x) case "current_timestamp": tp = types.NewFieldType(mysql.TypeDatetime) case "microsecond", "second", "minute", "hour", "day", "week", "month", "year", "dayofweek", "dayofmonth", "dayofyear", "weekday", "weekofyear", "yearweek", "found_rows", "length": tp = types.NewFieldType(mysql.TypeLonglong) case "now", "sysdate": tp = types.NewFieldType(mysql.TypeDatetime) tp.Decimal = v.getFsp(x) case "dayname", "version", "database", "user", "current_user", "concat", "concat_ws", "left", "lower", "repeat", "replace", "upper": tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset case "connection_id": tp = types.NewFieldType(mysql.TypeLonglong) tp.Flag |= mysql.UnsignedFlag case "if": // TODO: fix this // See: https://dev.mysql.com/doc/refman/5.5/en/control-flow-functions.html#function_if // The default return type of IF() (which may matter when it is stored into a temporary table) is calculated as follows. // Expression Return Value // expr2 or expr3 returns a string string // expr2 or expr3 returns a floating-point value floating-point // expr2 or expr3 returns an integer integer tp = x.Args[1].GetType() default: tp = types.NewFieldType(mysql.TypeUnspecified) } // If charset is unspecified. if len(tp.Charset) == 0 { tp.Charset = chs cln := charset.CollationBin if chs != charset.CharsetBin { var err error cln, err = charset.GetDefaultCollation(chs) if err != nil { v.err = err } } tp.Collate = cln } x.SetType(tp) }
func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { var ( tp *types.FieldType chs = charset.CharsetBin ) switch x.FnName.L { case "abs", "ifnull", "nullif": tp = x.Args[0].GetType() case "pow", "power", "rand": tp = types.NewFieldType(mysql.TypeDouble) case "curdate", "current_date", "date": tp = types.NewFieldType(mysql.TypeDate) case "curtime", "current_time": tp = types.NewFieldType(mysql.TypeDuration) tp.Decimal = v.getFsp(x) case "current_timestamp": tp = types.NewFieldType(mysql.TypeDatetime) case "microsecond", "second", "minute", "hour", "day", "week", "month", "year", "dayofweek", "dayofmonth", "dayofyear", "weekday", "weekofyear", "yearweek", "found_rows", "length": tp = types.NewFieldType(mysql.TypeLonglong) case "now", "sysdate": tp = types.NewFieldType(mysql.TypeDatetime) tp.Decimal = v.getFsp(x) case "dayname", "version", "database", "user", "current_user", "concat", "concat_ws", "left", "lower", "repeat", "replace", "upper": tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset case "connection_id": tp = types.NewFieldType(mysql.TypeLonglong) tp.Flag |= mysql.UnsignedFlag case "if": tp = x.Args[1].GetType() default: tp = types.NewFieldType(mysql.TypeUnspecified) } // If charset is unspecified. if len(tp.Charset) == 0 { tp.Charset = chs cln := charset.CollationBin if chs != charset.CharsetBin { var err error cln, err = charset.GetDefaultCollation(chs) if err != nil { v.err = err } } tp.Collate = cln } x.SetType(tp) }
func (e *SetExecutor) setCharset(cs, co string) error { var err error if len(co) == 0 { co, err = charset.GetDefaultCollation(cs) if err != nil { return errors.Trace(err) } } sessionVars := e.ctx.GetSessionVars() for _, v := range variable.SetNamesVariables { sessionVars.Systems[v] = cs } sessionVars.Systems[variable.CollationConnection] = co return nil }
// Exec implements the stmt.Statement Exec interface. // SET NAMES sets the three session system variables character_set_client, character_set_connection, // and character_set_results to the given character set. Setting character_set_connection to charset_name // also sets collation_connection to the default collation for charset_name. // The optional COLLATE clause may be used to specify a collation explicitly. func (s *SetCharsetStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { log.Debug("Set charset to ", s.Charset) collation := s.Collate if len(collation) == 0 { collation, err = charset.GetDefaultCollation(s.Charset) if err != nil { return nil, errors.Trace(err) } } sessionVars := variable.GetSessionVars(ctx) for _, v := range variable.SetNamesVariables { sessionVars.Systems[v] = s.Charset } sessionVars.Systems[variable.CollationConnection] = collation return nil, nil }
func (e *SimpleExec) executeSetCharset(s *ast.SetCharsetStmt) error { collation := s.Collate if len(collation) == 0 { var err error collation, err = charset.GetDefaultCollation(s.Charset) if err != nil { return errors.Trace(err) } } sessionVars := variable.GetSessionVars(e.ctx) for _, v := range variable.SetNamesVariables { sessionVars.Systems[v] = s.Charset } sessionVars.Systems[variable.CollationConnection] = collation return nil }
func (e *SimpleExec) setCharset(cs, co string) error { var err error if len(co) == 0 { co, err = charset.GetDefaultCollation(cs) if err != nil { return errors.Trace(err) } } sessionVars := variable.GetSessionVars(e.ctx) for _, v := range variable.SetNamesVariables { err = sessionVars.SetSystemVar(v, types.NewStringDatum(cs)) if err != nil { return errors.Trace(err) } } err = sessionVars.SetSystemVar(variable.CollationConnection, types.NewStringDatum(co)) if err != nil { return errors.Trace(err) } return nil }
func (e *SimpleExec) executeSetCharset(s *ast.SetCharsetStmt) error { collation := s.Collate var err error if len(collation) == 0 { collation, err = charset.GetDefaultCollation(s.Charset) if err != nil { return errors.Trace(err) } } sessionVars := variable.GetSessionVars(e.ctx) for _, v := range variable.SetNamesVariables { err = sessionVars.SetSystemVar(v, types.NewStringDatum(s.Charset)) if err != nil { return errors.Trace(err) } } err = sessionVars.SetSystemVar(variable.CollationConnection, types.NewStringDatum(collation)) if err != nil { return errors.Trace(err) } return nil }
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 (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { var ( tp *types.FieldType chs = charset.CharsetBin ) switch x.FnName.L { case "abs", "ifnull", "nullif": tp = x.Args[0].GetType() // TODO: We should cover all types. if x.FnName.L == "abs" && tp.Tp == mysql.TypeDatetime { tp = types.NewFieldType(mysql.TypeDouble) } case "greatest": for _, arg := range x.Args { InferType(v.sc, arg) } if len(x.Args) > 0 { tp = x.Args[0].GetType() for i := 1; i < len(x.Args); i++ { mergeArithType(tp.Tp, x.Args[i].GetType().Tp) } } case "ceil", "ceiling": t := x.Args[0].GetType().Tp if t == mysql.TypeNull || t == mysql.TypeFloat || t == mysql.TypeDouble || t == mysql.TypeVarchar || t == mysql.TypeTinyBlob || t == mysql.TypeMediumBlob || t == mysql.TypeLongBlob || t == mysql.TypeBlob || t == mysql.TypeVarString || t == mysql.TypeString { tp = types.NewFieldType(mysql.TypeDouble) } else { tp = types.NewFieldType(mysql.TypeLonglong) } case "ln", "log", "log2", "log10": tp = types.NewFieldType(mysql.TypeDouble) case "pow", "power", "rand": tp = types.NewFieldType(mysql.TypeDouble) case "curdate", "current_date", "date": tp = types.NewFieldType(mysql.TypeDate) case "curtime", "current_time", "timediff": tp = types.NewFieldType(mysql.TypeDuration) tp.Decimal = v.getFsp(x) case "current_timestamp", "date_arith": tp = types.NewFieldType(mysql.TypeDatetime) case "microsecond", "second", "minute", "hour", "day", "week", "month", "year", "dayofweek", "dayofmonth", "dayofyear", "weekday", "weekofyear", "yearweek", "found_rows", "length", "extract", "locate": tp = types.NewFieldType(mysql.TypeLonglong) case "now", "sysdate": tp = types.NewFieldType(mysql.TypeDatetime) tp.Decimal = v.getFsp(x) case "from_unixtime": if len(x.Args) == 1 { tp = types.NewFieldType(mysql.TypeDatetime) } else { tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset } case "str_to_date": tp = types.NewFieldType(mysql.TypeDatetime) case "dayname", "version", "database", "user", "current_user", "schema", "concat", "concat_ws", "left", "lcase", "lower", "repeat", "replace", "ucase", "upper", "convert", "substring", "substring_index", "trim", "ltrim", "rtrim", "reverse", "hex", "unhex", "date_format": tp = types.NewFieldType(mysql.TypeVarString) chs = v.defaultCharset case "strcmp", "isnull": tp = types.NewFieldType(mysql.TypeLonglong) case "connection_id": tp = types.NewFieldType(mysql.TypeLonglong) tp.Flag |= mysql.UnsignedFlag case "if": // TODO: fix this // See https://dev.mysql.com/doc/refman/5.5/en/control-flow-functions.html#function_if // The default return type of IF() (which may matter when it is stored into a temporary table) is calculated as follows. // Expression Return Value // expr2 or expr3 returns a string string // expr2 or expr3 returns a floating-point value floating-point // expr2 or expr3 returns an integer integer tp = x.Args[1].GetType() case "get_lock", "release_lock": tp = types.NewFieldType(mysql.TypeLonglong) default: tp = types.NewFieldType(mysql.TypeUnspecified) } // If charset is unspecified. if len(tp.Charset) == 0 { tp.Charset = chs cln := charset.CollationBin if chs != charset.CharsetBin { var err error cln, err = charset.GetDefaultCollation(chs) if err != nil { v.err = err } } tp.Collate = cln } x.SetType(tp) }