Example #1
0
func (sei *session) handleSet(stmt *sqlparser.Set) error {
	// get first arg
	expr := stmt.Exprs[0]
	name := string(expr.Name.Name)
	switch name {
	case "dbnum":
		buf := sqlparser.NewTrackedBuffer(nil)
		expr.Expr.Format(buf)
		_, ok := expr.Expr.(sqlparser.NumVal)
		if ok {
			i, err := strconv.Atoi(buf.String())
			if err != nil {
				return sei.writeError(mysql.NewDefaultError(mysql.ER_WRONG_VALUE_FOR_VAR, buf.String(), name))
			}
			sei.dbnum = i
			glog.Infof("set dbnum=%v", sei.dbnum)
			return sei.writeOK(nil)
		}
		return sei.writeError(mysql.NewDefaultError(mysql.ER_WRONG_VALUE_FOR_VAR, buf.String(), name))
	case "partitionkey":
		buf := sqlparser.NewTrackedBuffer(nil)
		expr.Expr.Format(buf)
		_, ok := expr.Expr.(sqlparser.StrVal)
		if ok {
			sei.partitionKey = buf.String()
			glog.Infof("set partitionKey=%v", sei.partitionKey)
			return sei.writeOK(nil)
		}
		return sei.writeError(mysql.NewDefaultError(mysql.ER_WRONG_VALUE_FOR_VAR, buf.String(), name))
	default:
		return sei.writeError(mysql.NewDefaultError(mysql.ER_UNKNOWN_SYSTEM_VARIABLE, name))
	}
}
Example #2
0
func (sei *session) handleExplain(stmt *sqlparser.Explain) error {
	if sei.db == "" {
		return sei.writeError(mysql.NewDefaultError(mysql.ER_NO_DB_ERROR))
	}
	// build plan
	plan, err := sei.buildPlan(stmt.SQL)
	if err != nil {
		return sei.writeError(err)
	}
	// build result
	names := []string{"ID", "Backends", "SQLs"}
	var r *mysql.Resultset
	if len(plan.SQLs) == 0 {
		r, _ = sei.buildEmptySet(names, []interface{}{""})
	} else {
		var values [][]interface{}
		for id, sql := range plan.SQLs {
			values = append(values, []interface{}{
				id,
				sql.Backend.Name,
				strings.Join(sql.SQL, ";"),
			})
		}
		r, _ = sei.buildResultset(nil, names, values)
	}
	return sei.writeResultset(sei.status, r)
}
Example #3
0
func (sei *session) handleFieldList(data []byte) error {
	index := bytes.IndexByte(data, 0x00)
	table := string(data[0:index])
	wildcard := string(data[index+1:])

	if sei.db == "" {
		return mysql.NewDefaultError(mysql.ER_NO_DB_ERROR)
	}

	tableInfo, err := sei.getMeta().GetTable(sei.user, sei.db, table)
	if err != nil {
		return err
	}

	co, err := sei.getBpool().GetConn(tableInfo.Backends[0])
	if err != nil {
		return err
	}
	defer sei.getBpool().PushConn(tableInfo.Backends[0], co, err)
	fs, err := co.FieldList(table, wildcard)
	if err != nil {
		return err
	}
	return sei.writeFieldList(sei.status, fs)
}
Example #4
0
func (sei *session) useDB(db string) error {
	isExist, err := sei.server.info.IsDBExist(sei.user, db)
	if err != nil {
		return err
	}
	if isExist {
		sei.db = db
		return sei.writeOK(nil)
	}
	return sei.writeError(mysql.NewDefaultError(mysql.ER_BAD_DB_ERROR, db))
}
Example #5
0
func (sei *session) readHandshakeResponse() error {
	data, err := sei.pkg.ReadPacket()
	if err != nil {
		return err
	}

	pos := 0

	// capability
	sei.capability = binary.LittleEndian.Uint32(data[:4])
	pos += 4

	// skip max packet size
	pos += 4

	// charset, skip, if you want to use another charset, use set names
	// sei.collation = CollationId(data[pos])
	pos++

	// skip reserved 23[00]
	pos += 23

	// user name
	sei.user = string(data[pos : pos+bytes.IndexByte(data[pos:], 0)])
	pos += len(sei.user) + 1

	// auth length and auth
	authLen := int(data[pos])
	pos++
	auth := data[pos : pos+authLen]

	pos += authLen

	if sei.capability&mysql.CLIENT_CONNECT_WITH_DB > 0 {
		if len(data[pos:]) == 0 {
			return nil
		}

		sei.db = string(data[pos : pos+bytes.IndexByte(data[pos:], 0)])
		pos += len(sei.db) + 1
		glog.Info("DB is :", sei.db)
	}

	// user and password check.
	if !sei.server.info.CheckUser(sei.user, auth, sei.salt, sei.db) {
		glog.Infof("User(%v) password or user name error", sei.user)
		return mysql.NewDefaultError(mysql.ER_ACCESS_DENIED_ERROR, sei.user, "", "YES")
	}

	return nil
}
Example #6
0
func (sei *session) handleQuery(data []byte) error {
	sql := strings.TrimRight(string(data), ";")
	stmt, err := sqlparser.Parse(sql)
	if err != nil {
		glog.Infof("parse sql(%s) error:%v", sql, err)
		return sei.writeError(mysql.NewDefaultError(mysql.ER_SYNTAX_ERROR))
	}
	switch v := stmt.(type) {
	case *sqlparser.Explain:
		return sei.handleExplain(v)

	case *sqlparser.Select:
		return sei.handleSelect(v)

	case *sqlparser.Insert:
		return sei.handleInsert(v)

	case *sqlparser.Update:
		return sei.handleUpdate(v)

	case *sqlparser.Delete:
		return sei.handleDelete(v)

	case *sqlparser.Set:
		// only support like `SET autocommit=1`
		return sei.handleSet(v)

	case *sqlparser.DDL:
		return sei.handleDDL(sql)

	case *sqlparser.Show:
		r, err := sei.handleShow(v)
		if err != nil {
			glog.Infof("handle show stmt has error:%v", err)
			sei.writeError(err)
			// not throw the error
			return nil
		}
		return sei.writeResultset(sei.status, r)

	case *sqlparser.UseDB:
		return sei.handleUseDB(v)

	default:
		return fmt.Errorf("statement %T not support now", stmt)
	}
	return nil
}
Example #7
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)
}
Example #8
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)
}
Example #9
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
}
Example #10
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)
	}
}
Example #11
0
func (d *Manage) doTask(t *Task) *Result {
	// TODO: impl
	r := new(Result)
	plan := t.Plan
	// get status
	status, err := d.GetTaskStatus(plan.UserName, plan.ID)
	if err != nil {
		r.err = err
		return r
	}
	// prepare handle task
	switch t.Type {
	case CreateDB:
		// check the db
		isExist, err := d.info.IsDBExist(plan.UserName, plan.DBName)
		if err != nil {
			r.err = err
			return r
		}
		if isExist {
			glog.Infof("database(%v) is already exist in user(%v)", plan.DBName, plan.UserName)
			r.err = mysql.NewDefaultError(mysql.ER_DB_CREATE_EXISTS, plan.DBName)
			return r
		}
	case CreateTable:
		// check table
		isExist, err := d.info.IsTableExist(plan.UserName, plan.DBName, plan.Table.Name)
		if err != nil {
			r.err = err
			return r
		}
		if isExist {
			glog.Infof("table(%v) is already exist in user(%v) database(%v)", plan.Table, plan.UserName, plan.DBName)
			r.err = mysql.NewDefaultError(mysql.ER_DB_CREATE_EXISTS, plan.DBName)
			return r
		}

	default:
		r.err = fmt.Errorf("not support task's type:%v", t.Type)
	}
	// execute task
	for _, sp := range plan.SubPlans {
		d.updateTaskStatus(plan.UserName, plan.ID, sp.SubDatabase.Name, Doing, "", status, sp)
		if err := d.executeSubPlan(sp, t); err != nil {
			d.updateTaskStatus(plan.UserName, plan.ID, sp.SubDatabase.Name, Fail, err.Error(), status, sp)
			r.err = err
			break
		} else {
			r.affectedRows++
			d.updateTaskStatus(plan.UserName, plan.ID, sp.SubDatabase.Name, Done, "", status, sp)
		}
		// record
		plan.FinishNodes = append(plan.FinishNodes, sp.SubDatabase)
		if len(plan.SubPlans) > 1 {
			plan.SubPlans = plan.SubPlans[1:]
			data, _ := json.Marshal(t)
			d.info.UpdateTask(t.Plan.UserName, t.Seq, string(data))
		} else {
			// register result
			if err = d.registerTaskResult(t); err != nil {
				r.err = err
				break
			}
		}
	}
	return r
}