Beispiel #1
0
func (sei *session) handleSimpleSelectFunc(stmt *sqlparser.Select, f *sqlparser.FuncExpr) error {
	expr := stmt.SelectExprs[0].(*sqlparser.NonStarExpr)
	switch f.Name {
	case "database":
		if f.Exprs != nil {
			return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, "not support stmt"))
		}
		// get now database
		var name []string
		if expr.As == "" {
			name = []string{"DATABASE()"}
		} else {
			name = []string{string(expr.As)}
		}
		var values [][]interface{}
		if sei.db == "" {
			values = [][]interface{}{
				[]interface{}{"NULL"},
			}
		} else {
			values = [][]interface{}{
				[]interface{}{sei.db},
			}
		}
		r, err := sei.buildResultset(nil, name, values)
		if err != nil {
			return err
		}
		return sei.writeResultset(sei.status, r)
	}
	return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, "not support stmt"))
}
Beispiel #2
0
func (sei *session) dispatch(data []byte) error {
	cmd := data[0]
	glog.Infof("cmd type: %v", cmd)
	data = data[1:]

	switch cmd {
	case mysql.COM_QUIT:
		return sei.Close()

	case mysql.COM_QUERY:
		return sei.handleQuery(data)

	case mysql.COM_PING:
		return sei.writeOK(nil)

	case mysql.COM_INIT_DB:
		return sei.useDB(string(data))

	case mysql.COM_FIELD_LIST:
		return sei.handleFieldList(data)

	case mysql.COM_STMT_PREPARE:
	case mysql.COM_STMT_EXECUTE:
	case mysql.COM_STMT_CLOSE:
	case mysql.COM_STMT_SEND_LONG_DATA:
	case mysql.COM_STMT_RESET:
	case mysql.COM_SET_OPTION:
	default:
		msg := fmt.Sprintf("command %d not supported", cmd)
		glog.Errorf("session(%v) dispatch %s", sei.id, msg)
		return mysql.NewError(mysql.ER_UNKNOWN_ERROR, msg)
	}
	return nil
}
Beispiel #3
0
func (sei *session) handleSimpleSelect(stmt *sqlparser.Select) error {
	switch expr := stmt.SelectExprs[0].(type) {
	case *sqlparser.NonStarExpr:
		switch f := expr.Expr.(type) {
		case *sqlparser.FuncExpr:
			return sei.handleSimpleSelectFunc(stmt, f)
		}
	case *sqlparser.StarExpr:

	}
	return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, "not support stmt"))
}
Beispiel #4
0
func (sei *session) handleSelect(stmt *sqlparser.Select) error {
	if stmt.From == nil {
		return sei.handleSimpleSelect(stmt)
	}
	if len(stmt.From) > 1 {
		return sei.writeError(mysql.NewDefaultError(mysql.ER_SYNTAX_ERROR))
	}

	plan, err := sei.buildPlan(stmt)
	if err != nil {
		return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error()))
	}
	rs, err := sei.executePlan(plan)
	if err != nil {
		return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error()))
	}

	offset, count := plan.getOffetCount()
	// merge select rs
	return sei.mergeSelectResult(rs, stmt, offset, count)
}
Beispiel #5
0
func (sei *session) handleNormalExecute(stmt sqlparser.Statement) error {
	if sei.db == "" {
		return sei.writeError(mysql.NewDefaultError(mysql.ER_NO_DB_ERROR))
	}

	plan, err := sei.buildPlan(stmt)
	if err != nil {
		return sei.writeError(err)
	}
	rs, err := sei.executePlan(plan)
	if err != nil {
		return sei.writeError(mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error()))
	}
	// merge results
	return sei.mergeExecResult(rs)
}
Beispiel #6
0
func (sei *session) writeError(e error) error {
	var m *mysql.SqlError
	var ok bool
	if m, ok = e.(*mysql.SqlError); !ok {
		m = mysql.NewError(mysql.ER_UNKNOWN_ERROR, e.Error())
	}

	data := make([]byte, 4, 16+len(m.Message))

	data = append(data, mysql.ERR_HEADER)
	data = append(data, byte(m.Code), byte(m.Code>>8))

	if sei.capability&mysql.CLIENT_PROTOCOL_41 > 0 {
		data = append(data, '#')
		data = append(data, m.State...)
	}

	data = append(data, m.Message...)

	return sei.pkg.WritePacket(data)
}
Beispiel #7
0
func (sei *session) buildInsertPlan(stmt *sqlparser.Insert) (*Plan, error) {
	plan := &Plan{}

	isNoCol := false
	isNeedInitCol := true
	colLen := len(stmt.Columns)

	// get table
	table, err := sei.getMeta().GetTable(sei.user, sei.db, string(stmt.Table.Name))
	if err != nil {
		return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error())
	}
	plan.Table = table
	if colLen == 0 {
		isNoCol = true
	}
	// get sqls
	values, ok := stmt.Rows.(sqlparser.Values)
	if !ok {
		return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unsupport")
	}

	for row, value := range values {
		tuple, ok := value.(sqlparser.ValTuple)
		if !ok {
			return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unsupport")
		}
		if (isNoCol && len(tuple) != table.Table.ColsLen) ||
			!isNoCol && len(tuple) != colLen {
			return nil, mysql.NewDefaultError(mysql.ER_WRONG_VALUE_COUNT_ON_ROW, row+1)
		}
		// rewrite auto key
		if isNoCol {
			// rewrite by index
			for _, autokey := range table.Table.AutoKeys {
				id, err := table.GetKey(autokey.Name, config.Config.Proxy.AutoKeyInterval, sei.server.info)
				if err != nil {
					glog.Warningf("Get autokey(%v) get error(%v)", autokey.Name, err)
					return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error())
				}
				if autokey.Type == meta.TypeKeyInt {
					tuple[autokey.Index] = sqlparser.NumVal(id)
				} else {
					tuple[autokey.Index] = sqlparser.StrVal(id)
				}
			}
		} else {
			for _, autokey := range table.Table.AutoKeys {
				isExist := false
				id, err := table.GetKey(autokey.Name, config.Config.Proxy.AutoKeyInterval, sei.server.info)
				if err != nil {
					glog.Warningf("Get autokey(%v) get error(%v)", autokey.Name, err)
					return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, err.Error())
				}
				for n, column := range stmt.Columns {
					col, ok := column.(*sqlparser.NonStarExpr)
					if !ok {
						return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unsupport")
					}
					colName, ok := col.Expr.(*sqlparser.ColName)
					if !ok {
						return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unsupport")
					}
					if autokey.Name == string(colName.Name) && n < len(tuple) {
						isExist = true
						if autokey.Type == meta.TypeKeyInt {
							tuple[n] = sqlparser.NumVal(id)
						} else {
							tuple[n] = sqlparser.StrVal(id)
						}
					}
				}
				if !isExist {
					if isNeedInitCol {
						stmt.Columns = append(stmt.Columns, &sqlparser.NonStarExpr{
							Expr: &sqlparser.ColName{
								Name: sqlparser.SQLName(autokey.Name),
							},
						})
					}
					if autokey.Type == meta.TypeKeyInt {
						tuple = append(tuple, sqlparser.NumVal(id))
					} else {
						tuple = append(tuple, sqlparser.StrVal(id))
					}
				}
			}
		}
		values[row] = tuple
		isNeedInitCol = false
	}
	stmt.Rows = values

	sqls := make(map[int]sqlparser.InsertRows)
	// split stmt
	for row, value := range values {
		tuple := value.(sqlparser.ValTuple)
		var value string
		if isNoCol {
			// get hash key
			switch v := tuple[table.Table.PartitionKey.Index].(type) {
			case sqlparser.NumVal:
				value = string(v)
			case sqlparser.StrVal:
				value = string(v)
			default:
				return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unknow partition key type")
			}
		} else {
			for col, column := range stmt.Columns {
				name := string(column.(*sqlparser.NonStarExpr).Expr.(*sqlparser.ColName).Name)
				if name == table.Table.PartitionKey.Name {
					switch v := tuple[col].(type) {
					case sqlparser.NumVal:
						value = string(v)
					case sqlparser.StrVal:
						value = string(v)
					default:
						return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unknow partition key type")
					}
				}
			}
		}
		if value == "" {
			glog.Warning("Get partition key error value is null")
			return nil, mysql.NewError(mysql.ER_UNKNOWN_ERROR, "unknow partition key type")
		}
		index := FindForKey(value, len(table.Backends))
		s, ok := sqls[index]
		if !ok {
			sqls[index] = sqlparser.Values{values[row]}
		} else {
			sqls[index] = append(s.(sqlparser.Values), values[row])
		}
	}

	var lastIDs []uint64
	if len(table.Table.AutoIns) > 0 {
		index := 0
		if isNoCol {
			// range column
			index = table.Table.AutoIns[0].Index
		} else {
			// get index
			index = sort.Search(len(stmt.Columns), func(i int) bool {
				return string(stmt.Columns[i].(*sqlparser.NonStarExpr).Expr.(*sqlparser.ColName).Name) ==
					table.Table.AutoIns[0].Name
			})
		}
		lastIDs = make([]uint64, len(sqls))
		n := 0
		for _, sql := range sqls {
			switch v := sql.(sqlparser.Values)[0].(sqlparser.ValTuple)[index].(type) {
			case sqlparser.NumVal:
				glog.Info(n, len(lastIDs))
				lastIDs[n], _ = strconv.ParseUint(string(v), 10, 64)
			case sqlparser.StrVal:
				lastIDs[n], _ = strconv.ParseUint(string(v), 10, 64)
			}
			n++
		}
	}

	for k, v := range sqls {
		stmt.Rows = v
		plan.generateOneSQL(stmt, k)
	}
	return plan, nil
}
Beispiel #8
0
func (sei *session) handleShow(stmt *sqlparser.Show) (*mysql.Resultset, error) {
	var (
		r      = new(mysql.Resultset)
		name   []string
		values [][]interface{}
		err    error
	)

	switch strings.ToLower(stmt.Key) {
	case "databases":
		name = append(name, "Database")
		// get databases/tables from etcd
		var dbs []string
		dbs, err = sei.server.info.ShowDatabases(sei.user)
		if err != nil {
			return nil, err
		}
		if len(dbs) > 0 {
			values = make([][]interface{}, 0, len(dbs))
		} else {
			glog.Info("empty databases")
			r, err = sei.buildEmptySet(name, []interface{}{""})
			break
		}
		for _, db := range dbs {
			values = append(values, []interface{}{db})
		}
		r, err = sei.buildResultset(nil, name, values)
	case "tables":
		if sei.db == "" {
			return nil, mysql.NewError(mysql.ER_NO_DB_ERROR, mysql.MySQLErrName[mysql.ER_NO_DB_ERROR])
		}
		// get talbbes
		name = append(name, fmt.Sprintf("Tables_in_%s", sei.db))
		// get databases from etcd
		var dbs []string
		dbs, err = sei.server.info.ShowTables(sei.user, sei.db)
		if err != nil {
			return nil, err
		}
		if len(dbs) > 0 {
			values = make([][]interface{}, 0, len(dbs))
		} else {
			glog.Info("empty tables")
			r, err = sei.buildEmptySet(name, []interface{}{""})
			break
		}
		for _, db := range dbs {
			values = append(values, []interface{}{db})
		}
		r, err = sei.buildResultset(nil, name, values)

	case "variables":

	case "ddl_task":
		// get tasks
		// TODO

	}
	return r, err
}
Beispiel #9
0
func (sei *session) handleDDL(sql string) error {
	stmt, err := parser.ParseOneStmt(sql, "", "")
	if err != nil {
		glog.Infof("parse ddl sql(%s) error:%v", sql, err)
		return sei.writeError(mysql.NewDefaultError(mysql.ER_SYNTAX_ERROR))
	}
	switch v := stmt.(type) {
	case *ast.CreateDatabaseStmt:
		dbname := v.Name
		id, rows, err := sei.ddlManage().CreateDatabase(sei.user, dbname, sei.dbnum)
		glog.Infof("DDL plan id(%v)", id)
		if err != nil {
			glog.Infof("CREATE DATABASE has an error(%v)", err)
			err = sei.writeError(err)
		} else {
			// one time only creata a db
			r := &mysql.Result{
				AffectedRows: rows,
			}
			err = sei.writeOK(r)
		}
		return err

	case *ast.CreateTableStmt:
		if sei.db == "" {
			return sei.writeError(mysql.NewDefaultError(mysql.ER_NO_DB_ERROR))
		}
		table := &meta.Table{
			Scheme: "hash",
			Name:   v.Table.Name.String(),
			PartitionKey: &meta.Key{
				Name: sei.partitionKey,
			},
			ColsLen: len(v.Cols),
		}

		existMap := make(map[string]bool)
		// get constraints
		for _, constraint := range v.Constraints {
			if constraint.Tp == ast.ConstraintPrimaryKey ||
				constraint.Tp == ast.ConstraintUniq {
				if len(constraint.Keys) > 1 {
					err := mysql.NewError(mysql.ER_SYNTAX_ERROR,
						"not support constraint keys' length > 1")
					return sei.writeError(err)
				}
				// get type
				name := constraint.Keys[0].Column.Name.String()
				index, typ := sei.getFieldType(v.Cols, name)
				if typ == meta.TypeKeyUnknow {
					err := mysql.NewError(mysql.ER_SYNTAX_ERROR,
						"unsupport key's type ")
					return sei.writeError(err)
				}

				if constraint.Tp == ast.ConstraintPrimaryKey && sei.partitionKey == "" {
					// set primary key for partition key
					table.PartitionKey.Name = name
					table.PartitionKey.Type = typ
					table.PartitionKey.Index = index
				}

				table.AutoKeys = append(table.AutoKeys, &meta.Key{
					Name:  name,
					Type:  typ,
					Index: index,
				})
				existMap[name] = true
			}
		}
		// check auto increment
		for _, col := range v.Cols {
			for n, option := range col.Options {
				t := sei.getOneFeildType(col)
				if t == meta.TypeKeyUnknow {
					err := mysql.NewError(mysql.ER_SYNTAX_ERROR,
						"unsupport key's type ")
					return sei.writeError(err)
				}
				switch option.Tp {
				case ast.ColumnOptionAutoIncrement, ast.ColumnOptionPrimaryKey, ast.ColumnOptionUniq:
					if ast.ColumnOptionPrimaryKey == option.Tp && table.PartitionKey.Name == "" {
						table.PartitionKey.Name = col.Name.Name.String()
						table.PartitionKey.Type = t
						table.PartitionKey.Index = n
					}
					// check if exist not append
					if existMap[col.Name.Name.String()] {
						continue
					}
					table.AutoKeys = append(table.AutoKeys, &meta.Key{
						Name:  col.Name.Name.String(),
						Type:  t,
						Index: n,
					})
					existMap[col.Name.Name.String()] = true
				}
				if option.Tp == ast.ColumnOptionAutoIncrement {
					glog.Infof("record auto increment option index(%v)", n)
					// record
					table.AutoIns = append(table.AutoIns, &meta.Key{
						Name:  col.Name.Name.String(),
						Type:  t,
						Index: n,
					})
				}
			}
		}
		// check partition key
		if table.PartitionKey.Name == "" {
			err := mysql.NewError(mysql.ER_SYNTAX_ERROR,
				"partitionKey is null")
			return sei.writeError(err)
		}
		data, _ := json.MarshalIndent(table, "", "\t")
		glog.Info("table is\n", string(data))
		id, rows, err := sei.ddlManage().CreateTable(sei.user, sei.db, sql, table)
		glog.Infof("DDL plan id(%v)", id)
		if err != nil {
			glog.Infof("CREATE TABLE has an error(%v)", err)
			err = sei.writeError(err)
		} else {
			// one time only creata a db
			r := &mysql.Result{
				AffectedRows: rows,
			}
			err = sei.writeOK(r)
		}
		return err

	default:
		return fmt.Errorf("create statement %T not support now", stmt)
	}
}