// Copyright 2018 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package chunk import ( "time" "unsafe" "github.com/hanchuanchuan/goInception/mysql" "github.com/hanchuanchuan/goInception/types" "github.com/hanchuanchuan/goInception/types/json" "github.com/hanchuanchuan/goInception/util/hack" ) // Row represents a row of data, can be used to assess values. type Row struct { c *Chunk idx int } // IsEmpty returns true if the Row is empty. func (r Row) IsEmpty() bool { return r == Row{} } // Idx returns the row index of Chunk. func (r Row) Idx() int { return r.idx } // Len returns the number of values in the row. func (r Row) Len() int { return r.c.NumCols() } // GetInt64 returns the int64 value with the colIdx. func (r Row) GetInt64(colIdx int) int64 { col := r.c.columns[colIdx] return *(*int64)(unsafe.Pointer(&col.data[r.idx*8])) } // GetUint64 returns the uint64 value with the colIdx. func (r Row) GetUint64(colIdx int) uint64 { col := r.c.columns[colIdx] return *(*uint64)(unsafe.Pointer(&col.data[r.idx*8])) } // GetFloat32 returns the float32 value with the colIdx. func (r Row) GetFloat32(colIdx int) float32 { col := r.c.columns[colIdx] return *(*float32)(unsafe.Pointer(&col.data[r.idx*4])) } // GetFloat64 returns the float64 value with the colIdx. func (r Row) GetFloat64(colIdx int) float64 { col := r.c.columns[colIdx] return *(*float64)(unsafe.Pointer(&col.data[r.idx*8])) } // GetString returns the string value with the colIdx. func (r Row) GetString(colIdx int) string { col := r.c.columns[colIdx] start, end := col.offsets[r.idx], col.offsets[r.idx+1] return hack.String(col.data[start:end]) } // GetBytes returns the bytes value with the colIdx. func (r Row) GetBytes(colIdx int) []byte { col := r.c.columns[colIdx] start, end := col.offsets[r.idx], col.offsets[r.idx+1] return col.data[start:end] } // GetTime returns the Time value with the colIdx. // TODO: use Time structure directly. func (r Row) GetTime(colIdx int) types.Time { col := r.c.columns[colIdx] return readTime(col.data[r.idx*16:]) } // GetDuration returns the Duration value with the colIdx. func (r Row) GetDuration(colIdx int, fillFsp int) types.Duration { col := r.c.columns[colIdx] dur := *(*int64)(unsafe.Pointer(&col.data[r.idx*8])) return types.Duration{Duration: time.Duration(dur), Fsp: fillFsp} } func (r Row) getNameValue(colIdx int) (string, uint64) { col := r.c.columns[colIdx] start, end := col.offsets[r.idx], col.offsets[r.idx+1] if start == end { return "", 0 } val := *(*uint64)(unsafe.Pointer(&col.data[start])) name := hack.String(col.data[start+8 : end]) return name, val } // GetEnum returns the Enum value with the colIdx. func (r Row) GetEnum(colIdx int) types.Enum { name, val := r.getNameValue(colIdx) return types.Enum{Name: name, Value: val} } // GetSet returns the Set value with the colIdx. func (r Row) GetSet(colIdx int) types.Set { name, val := r.getNameValue(colIdx) return types.Set{Name: name, Value: val} } // GetMyDecimal returns the MyDecimal value with the colIdx. func (r Row) GetMyDecimal(colIdx int) *types.MyDecimal { col := r.c.columns[colIdx] return (*types.MyDecimal)(unsafe.Pointer(&col.data[r.idx*types.MyDecimalStructSize])) } // GetJSON returns the JSON value with the colIdx. func (r Row) GetJSON(colIdx int) json.BinaryJSON { col := r.c.columns[colIdx] start, end := col.offsets[r.idx], col.offsets[r.idx+1] return json.BinaryJSON{TypeCode: col.data[start], Value: col.data[start+1 : end]} } // GetDatumRow converts chunk.Row to types.DatumRow. // Keep in mind that GetDatumRow has a reference to r.c, which is a chunk, // this function works only if the underlying chunk is valid or unchanged. func (r Row) GetDatumRow(fields []*types.FieldType) []types.Datum { datumRow := make([]types.Datum, 0, r.c.NumCols()) for colIdx := 0; colIdx < r.c.NumCols(); colIdx++ { datum := r.GetDatum(colIdx, fields[colIdx]) datumRow = append(datumRow, datum) } return datumRow } // GetDatum implements the chunk.Row interface. func (r Row) GetDatum(colIdx int, tp *types.FieldType) types.Datum { var d types.Datum switch tp.Tp { case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: if !r.IsNull(colIdx) { if mysql.HasUnsignedFlag(tp.Flag) { d.SetUint64(r.GetUint64(colIdx)) } else { d.SetInt64(r.GetInt64(colIdx)) } } case mysql.TypeYear: // FIXBUG: because insert type of TypeYear is definite int64, so we regardless of the unsigned flag. if !r.IsNull(colIdx) { d.SetInt64(r.GetInt64(colIdx)) } case mysql.TypeFloat: if !r.IsNull(colIdx) { d.SetFloat32(r.GetFloat32(colIdx)) } case mysql.TypeDouble: if !r.IsNull(colIdx) { d.SetFloat64(r.GetFloat64(colIdx)) } case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: if !r.IsNull(colIdx) { d.SetBytes(r.GetBytes(colIdx)) } case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: if !r.IsNull(colIdx) { d.SetMysqlTime(r.GetTime(colIdx)) } case mysql.TypeDuration: if !r.IsNull(colIdx) { duration := r.GetDuration(colIdx, tp.Decimal) d.SetMysqlDuration(duration) } case mysql.TypeNewDecimal: if !r.IsNull(colIdx) { d.SetMysqlDecimal(r.GetMyDecimal(colIdx)) d.SetLength(tp.Flen) // If tp.Decimal is unspecified(-1), we should set it to the real // fraction length of the decimal value, if not, the d.Frac will // be set to MAX_UINT16 which will cause unexpected BadNumber error // when encoding. if tp.Decimal == types.UnspecifiedLength { d.SetFrac(d.Frac()) } else { d.SetFrac(tp.Decimal) } } case mysql.TypeEnum: if !r.IsNull(colIdx) { d.SetMysqlEnum(r.GetEnum(colIdx)) } case mysql.TypeSet: if !r.IsNull(colIdx) { d.SetMysqlSet(r.GetSet(colIdx)) } case mysql.TypeBit: if !r.IsNull(colIdx) { d.SetMysqlBit(r.GetBytes(colIdx)) } case mysql.TypeJSON: if !r.IsNull(colIdx) { d.SetMysqlJSON(r.GetJSON(colIdx)) } } return d } // IsNull returns if the datum in the chunk.Row is null. func (r Row) IsNull(colIdx int) bool { return r.c.columns[colIdx].isNull(r.idx) }