func (c *Conn) newEmptyResultset(stmt *sqlparser.Select) *mysql.Resultset { r := &mysql.Resultset{} r.Fields = make([]*mysql.Field, len(stmt.SelectExprs)) for i, expr := range stmt.SelectExprs { r.Fields[i] = &mysql.Field{} switch e := expr.(type) { case *sqlparser.StarExpr: r.Fields[i].Name = []byte("*") case *sqlparser.NonStarExpr: if e.As != nil { r.Fields[i].Name = e.As r.Fields[i].OrgName = hack.Slice(nstring(e.Expr, c.alloc)) } else { r.Fields[i].Name = hack.Slice(nstring(e.Expr, c.alloc)) } default: r.Fields[i].Name = hack.Slice(nstring(e, c.alloc)) } } r.Values = make([]mysql.RowValue, 0) r.RowDatas = make([]mysql.RowData, 0) return r }
func (c *Conn) handleExec(stmt sqlparser.Statement, sql string, args []interface{}, skipCache bool) error { if !skipCache { // handle cache plan, ti, err := c.getPlanAndTableInfo(stmt) if err != nil { return errors.Trace(err) } if ti == nil { return errors.Errorf("sql: %s not support", sql) } c.server.IncCounter(plan.PlanId.String()) if ti.CacheType != schema.CACHE_NONE { if len(ti.PKColumns) != len(plan.PKValues) { return errors.Errorf("updated/delete/replace without primary key not allowed %+v", plan.PKValues) } if len(plan.PKValues) == 0 { return errors.Errorf("pk not exist, sql: %s", sql) } log.Debugf("%s %+v, %+v", sql, plan, plan.PKValues) pks := pkValuesToStrings(ti.PKColumns, plan.PKValues) ti.Lock.Lock(hack.Slice(pks[0])) defer ti.Lock.Unlock(hack.Slice(pks[0])) invalidCache(ti, pks) } } bindVars := makeBindVars(args) conns, err := c.getShardConns(false, stmt, bindVars) if err != nil { return errors.Trace(err) } else if len(conns) == 0 { //todo:handle error return errors.Errorf("not server found %s", sql) } var rs []*mysql.Result rs, err = c.executeInShard(conns, sql, args) c.closeShardConns(conns) if err == nil { err = c.mergeExecResult(rs) } return errors.Trace(err) }
func (c *Conn) fillCacheAndReturnResults(plan *planbuilder.ExecPlan, ti *tabletserver.TableInfo, keys []string) error { rowsql, err := generateSelectSql(ti, plan) log.Info(rowsql) ti.Lock.Lock(hack.Slice(keys[0])) defer ti.Lock.Unlock(hack.Slice(keys[0])) conns, err := c.getShardConns(true, nil, nil) if err != nil { return errors.Trace(err) } else if len(conns) == 0 { return errors.Errorf("not enough connection for %s", rowsql) } rs, err := c.executeInShard(conns, rowsql, nil) defer c.closeShardConns(conns) if err != nil { return errors.Trace(err) } //todo:fix hard code result := rs[0] if len(result.Values) == 0 { log.Debug("empty set") return c.writeResultset(result.Status, result.Resultset) } //log.Debugf("%+v", result.Values[0]) retValues := applyFilter(plan.ColumnNumbers, result.Values[0]) //log.Debug(len(retValues), len(keys)) r, err := c.buildResultset(getFieldNames(plan, ti), []mysql.RowValue{retValues}) if err != nil { log.Error(err) return errors.Trace(err) } //just do simple cache now if len(result.Values) == 1 && len(keys) == 1 && ti.CacheType != schema.CACHE_NONE { pks := pkValuesToStrings(ti.PKColumns, plan.PKValues) log.Debug("fill cache", pks) c.server.IncCounter("fill") ti.Cache.Set(pks[0], result.RowDatas[0], 0) } return c.writeResultset(c.status, r) }
//compare value using asc func cmpValue(v1 interface{}, v2 interface{}) int { if v1 == nil && v2 == nil { return 0 } else if v1 == nil { return -1 } else if v2 == nil { return 1 } switch v := v1.(type) { case string: s := v2.(string) return bytes.Compare(hack.Slice(v), hack.Slice(s)) case []byte: s := v2.([]byte) return bytes.Compare(v, s) case int64: s := v2.(int64) if v < s { return -1 } else if v > s { return 1 } else { return 0 } case uint64: s := v2.(uint64) if v < s { return -1 } else if v > s { return 1 } else { return 0 } case float64: s := v2.(float64) if v < s { return -1 } else if v > s { return 1 } else { return 0 } default: //can not go here panic(fmt.Sprintf("invalid type %T", v)) } }
func Raw(t byte, val Value, isUnsigned bool) []byte { if val == nil { return nil } var ret []byte switch t { case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, MYSQL_TYPE_LONGLONG, MYSQL_TYPE_YEAR: if isUnsigned { ret = []byte(strconv.FormatUint(val.(uint64), 10)) } else { ret = []byte(strconv.FormatInt(val.(int64), 10)) } case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE: ret = []byte(strconv.FormatFloat(val.(float64), 'f', 16, 64)) case MYSQL_TYPE_VARCHAR: str, ok := val.(string) if ok { ret = hack.Slice(str) break } fallthrough default: var ok bool ret, ok = val.([]byte) if !ok { log.Errorf("%v, %+v, %T", t, val, val) } } return ret }
func HashValue(value interface{}) uint64 { switch val := value.(type) { case int: return uint64(val) case uint64: return uint64(val) case int64: return uint64(val) case string: return uint64(crc32.ChecksumIEEE(hack.Slice(val))) case []byte: return uint64(crc32.ChecksumIEEE(val)) } panic(NewKeyError("Unexpected key variable type %T", value)) }
func (c *Conn) buildResultset(nameTypes []schema.TableColumn, values []mysql.RowValue) (*mysql.Resultset, error) { r := &mysql.Resultset{Fields: make([]*mysql.Field, len(nameTypes))} var b []byte var err error for i, vs := range values { if len(vs) != len(r.Fields) { return nil, errors.Errorf("row %d has %d column not equal %d", i, len(vs), len(r.Fields)) } var row []byte for j, value := range vs { field := &mysql.Field{} if i == 0 { r.Fields[j] = field //log.Warningf("%+v", nameTypes[i]) field.Name = hack.Slice(nameTypes[j].Name) if err = formatField(field, value); err != nil { return nil, errors.Trace(err) } field.Type = nameTypes[j].SqlType field.Charset = uint16(mysql.CollationNames[nameTypes[j].Collation]) field.IsUnsigned = nameTypes[j].IsUnsigned } if value == nil { row = append(row, "\xfb"...) } else { b = mysql.Raw(byte(field.Type), value, field.IsUnsigned) row = append(row, mysql.PutLengthEncodedString(b, c.alloc)...) } } r.RowDatas = append(r.RowDatas, row) } return r, nil }