github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqlparse/tidbparser/dependency/util/chunk/chunk.go (about) 1 // Copyright 2017 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package chunk 15 16 import ( 17 "encoding/binary" 18 "unsafe" 19 20 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/mysql" 21 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/types" 22 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/types/json" 23 "github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/util/hack" 24 ) 25 26 var _ types.Row = Row{} 27 28 // Chunk stores multiple rows of data in Apache Arrow format. 29 // See https://arrow.apache.org/docs/memory_layout.html 30 // Values are appended in compact format and can be directly accessed without decoding. 31 // When the chunk is done processing, we can reuse the allocated memory by resetting it. 32 type Chunk struct { 33 columns []*column 34 // numVirtualRows indicates the number of virtual rows, witch have zero columns. 35 // It is used only when this Chunk doesn't hold any data, i.e. "len(columns)==0". 36 numVirtualRows int 37 } 38 39 // Capacity constants. 40 const ( 41 InitialCapacity = 32 42 ) 43 44 // NewChunk creates a new chunk with field types. 45 func NewChunk(fields []*types.FieldType) *Chunk { 46 return NewChunkWithCapacity(fields, InitialCapacity) 47 } 48 49 // NewChunkWithCapacity creates a new chunk with field types and capacity. 50 func NewChunkWithCapacity(fields []*types.FieldType, cap int) *Chunk { 51 chk := new(Chunk) 52 chk.columns = make([]*column, 0, len(fields)) 53 chk.numVirtualRows = 0 54 for _, f := range fields { 55 chk.addColumnByFieldType(f, cap) 56 } 57 return chk 58 } 59 60 // MemoryUsage returns the total memory usage of a Chunk in B. 61 // We ignore the size of column.length and column.nullCount 62 // since they have little effect of the total memory usage. 63 func (c *Chunk) MemoryUsage() (sum int64) { 64 for _, col := range c.columns { 65 curColMemUsage := int64(unsafe.Sizeof(*col)) + int64(cap(col.nullBitmap)) + int64(cap(col.offsets)*4) + int64(cap(col.data)) + int64(cap(col.elemBuf)) 66 sum += curColMemUsage 67 } 68 return 69 } 70 71 // addFixedLenColumn adds a fixed length column with elemLen and initial data capacity. 72 func (c *Chunk) addFixedLenColumn(elemLen, initCap int) { 73 c.columns = append(c.columns, &column{ 74 elemBuf: make([]byte, elemLen), 75 data: make([]byte, 0, initCap*elemLen), 76 nullBitmap: make([]byte, 0, initCap>>3), 77 }) 78 } 79 80 // addVarLenColumn adds a variable length column with initial data capacity. 81 func (c *Chunk) addVarLenColumn(initCap int) { 82 c.columns = append(c.columns, &column{ 83 offsets: make([]int32, 1, initCap+1), 84 data: make([]byte, 0, initCap*4), 85 nullBitmap: make([]byte, 0, initCap>>3), 86 }) 87 } 88 89 // addColumnByFieldType adds a column by field type. 90 func (c *Chunk) addColumnByFieldType(fieldTp *types.FieldType, initCap int) { 91 switch fieldTp.Tp { 92 case mysql.TypeFloat: 93 c.addFixedLenColumn(4, initCap) 94 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, 95 mysql.TypeDouble, mysql.TypeYear: 96 c.addFixedLenColumn(8, initCap) 97 case mysql.TypeDuration: 98 c.addFixedLenColumn(16, initCap) 99 case mysql.TypeNewDecimal: 100 c.addFixedLenColumn(types.MyDecimalStructSize, initCap) 101 case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: 102 c.addFixedLenColumn(16, initCap) 103 default: 104 c.addVarLenColumn(initCap) 105 } 106 } 107 108 // MakeRef makes column in "dstColIdx" reference to column in "srcColIdx". 109 func (c *Chunk) MakeRef(srcColIdx, dstColIdx int) { 110 c.columns[dstColIdx] = c.columns[srcColIdx] 111 } 112 113 // SwapColumn swaps column "c.columns[colIdx]" with column "other.columns[otherIdx]". 114 func (c *Chunk) SwapColumn(colIdx int, other *Chunk, otherIdx int) { 115 c.columns[colIdx], other.columns[otherIdx] = other.columns[otherIdx], c.columns[colIdx] 116 } 117 118 // SwapColumns swaps columns with another Chunk. 119 func (c *Chunk) SwapColumns(other *Chunk) { 120 c.columns, other.columns = other.columns, c.columns 121 c.numVirtualRows, other.numVirtualRows = other.numVirtualRows, c.numVirtualRows 122 } 123 124 // SetNumVirtualRows sets the virtual row number for a Chunk. 125 // It should only be used when there exists no column in the Chunk. 126 func (c *Chunk) SetNumVirtualRows(numVirtualRows int) { 127 c.numVirtualRows = numVirtualRows 128 } 129 130 // Reset resets the chunk, so the memory it allocated can be reused. 131 // Make sure all the data in the chunk is not used anymore before you reuse this chunk. 132 func (c *Chunk) Reset() { 133 for _, c := range c.columns { 134 c.reset() 135 } 136 c.numVirtualRows = 0 137 } 138 139 // NumCols returns the number of columns in the chunk. 140 func (c *Chunk) NumCols() int { 141 return len(c.columns) 142 } 143 144 // NumRows returns the number of rows in the chunk. 145 func (c *Chunk) NumRows() int { 146 if c.NumCols() == 0 { 147 return c.numVirtualRows 148 } 149 return c.columns[0].length 150 } 151 152 // GetRow gets the Row in the chunk with the row index. 153 func (c *Chunk) GetRow(idx int) Row { 154 return Row{c: c, idx: idx} 155 } 156 157 // AppendRow appends a row to the chunk. 158 func (c *Chunk) AppendRow(row Row) { 159 c.AppendPartialRow(0, row) 160 c.numVirtualRows++ 161 } 162 163 // AppendPartialRow appends a row to the chunk. 164 func (c *Chunk) AppendPartialRow(colIdx int, row Row) { 165 for i, rowCol := range row.c.columns { 166 chkCol := c.columns[colIdx+i] 167 chkCol.appendNullBitmap(!rowCol.isNull(row.idx)) 168 if rowCol.isFixed() { 169 elemLen := len(rowCol.elemBuf) 170 offset := row.idx * elemLen 171 chkCol.data = append(chkCol.data, rowCol.data[offset:offset+elemLen]...) 172 } else { 173 start, end := rowCol.offsets[row.idx], rowCol.offsets[row.idx+1] 174 chkCol.data = append(chkCol.data, rowCol.data[start:end]...) 175 chkCol.offsets = append(chkCol.offsets, int32(len(chkCol.data))) 176 } 177 chkCol.length++ 178 } 179 } 180 181 // Append appends rows in [begin, end) in another Chunk to a Chunk. 182 func (c *Chunk) Append(other *Chunk, begin, end int) { 183 for colID, src := range other.columns { 184 dst := c.columns[colID] 185 if src.isFixed() { 186 elemLen := len(src.elemBuf) 187 dst.data = append(dst.data, src.data[begin*elemLen:end*elemLen]...) 188 } else { 189 beginOffset, endOffset := src.offsets[begin], src.offsets[end] 190 dst.data = append(dst.data, src.data[beginOffset:endOffset]...) 191 for i := begin; i < end; i++ { 192 dst.offsets = append(dst.offsets, dst.offsets[len(dst.offsets)-1]+src.offsets[i+1]-src.offsets[i]) 193 } 194 } 195 for i := begin; i < end; i++ { 196 dst.appendNullBitmap(!src.isNull(i)) 197 dst.length++ 198 } 199 } 200 c.numVirtualRows += end - begin 201 } 202 203 // TruncateTo truncates rows from tail to head in a Chunk to "numRows" rows. 204 func (c *Chunk) TruncateTo(numRows int) { 205 for _, col := range c.columns { 206 if col.isFixed() { 207 elemLen := len(col.elemBuf) 208 col.data = col.data[:numRows*elemLen] 209 } else { 210 col.data = col.data[:col.offsets[numRows]] 211 col.offsets = col.offsets[:numRows+1] 212 } 213 for i := numRows; i < col.length; i++ { 214 if col.isNull(i) { 215 col.nullCount-- 216 } 217 } 218 col.length = numRows 219 col.nullBitmap = col.nullBitmap[:(col.length>>3)+1] 220 } 221 c.numVirtualRows = numRows 222 } 223 224 // AppendNull appends a null value to the chunk. 225 func (c *Chunk) AppendNull(colIdx int) { 226 c.columns[colIdx].appendNull() 227 } 228 229 // AppendInt64 appends a int64 value to the chunk. 230 func (c *Chunk) AppendInt64(colIdx int, i int64) { 231 c.columns[colIdx].appendInt64(i) 232 } 233 234 // AppendUint64 appends a uint64 value to the chunk. 235 func (c *Chunk) AppendUint64(colIdx int, u uint64) { 236 c.columns[colIdx].appendUint64(u) 237 } 238 239 // AppendFloat32 appends a float32 value to the chunk. 240 func (c *Chunk) AppendFloat32(colIdx int, f float32) { 241 c.columns[colIdx].appendFloat32(f) 242 } 243 244 // AppendFloat64 appends a float64 value to the chunk. 245 func (c *Chunk) AppendFloat64(colIdx int, f float64) { 246 c.columns[colIdx].appendFloat64(f) 247 } 248 249 // AppendString appends a string value to the chunk. 250 func (c *Chunk) AppendString(colIdx int, str string) { 251 c.columns[colIdx].appendString(str) 252 } 253 254 // AppendBytes appends a bytes value to the chunk. 255 func (c *Chunk) AppendBytes(colIdx int, b []byte) { 256 c.columns[colIdx].appendBytes(b) 257 } 258 259 // AppendTime appends a Time value to the chunk. 260 // TODO: change the time structure so it can be directly written to memory. 261 func (c *Chunk) AppendTime(colIdx int, t types.Time) { 262 c.columns[colIdx].appendTime(t) 263 } 264 265 // AppendDuration appends a Duration value to the chunk. 266 func (c *Chunk) AppendDuration(colIdx int, dur types.Duration) { 267 c.columns[colIdx].appendDuration(dur) 268 } 269 270 // AppendMyDecimal appends a MyDecimal value to the chunk. 271 func (c *Chunk) AppendMyDecimal(colIdx int, dec *types.MyDecimal) { 272 c.columns[colIdx].appendMyDecimal(dec) 273 } 274 275 // AppendEnum appends an Enum value to the chunk. 276 func (c *Chunk) AppendEnum(colIdx int, enum types.Enum) { 277 c.columns[colIdx].appendNameValue(enum.Name, enum.Value) 278 } 279 280 // AppendSet appends a Set value to the chunk. 281 func (c *Chunk) AppendSet(colIdx int, set types.Set) { 282 c.columns[colIdx].appendNameValue(set.Name, set.Value) 283 } 284 285 // AppendJSON appends a JSON value to the chunk. 286 func (c *Chunk) AppendJSON(colIdx int, j json.BinaryJSON) { 287 c.columns[colIdx].appendJSON(j) 288 } 289 290 // AppendDatum appends a datum into the chunk. 291 func (c *Chunk) AppendDatum(colIdx int, d *types.Datum) { 292 switch d.Kind() { 293 case types.KindNull: 294 c.AppendNull(colIdx) 295 case types.KindInt64: 296 c.AppendInt64(colIdx, d.GetInt64()) 297 case types.KindUint64: 298 c.AppendUint64(colIdx, d.GetUint64()) 299 case types.KindFloat32: 300 c.AppendFloat32(colIdx, d.GetFloat32()) 301 case types.KindFloat64: 302 c.AppendFloat64(colIdx, d.GetFloat64()) 303 case types.KindString, types.KindBytes, types.KindBinaryLiteral, types.KindRaw, types.KindMysqlBit: 304 c.AppendBytes(colIdx, d.GetBytes()) 305 case types.KindMysqlDecimal: 306 c.AppendMyDecimal(colIdx, d.GetMysqlDecimal()) 307 case types.KindMysqlDuration: 308 c.AppendDuration(colIdx, d.GetMysqlDuration()) 309 case types.KindMysqlEnum: 310 c.AppendEnum(colIdx, d.GetMysqlEnum()) 311 case types.KindMysqlSet: 312 c.AppendSet(colIdx, d.GetMysqlSet()) 313 case types.KindMysqlTime: 314 c.AppendTime(colIdx, d.GetMysqlTime()) 315 case types.KindMysqlJSON: 316 c.AppendJSON(colIdx, d.GetMysqlJSON()) 317 } 318 } 319 320 type column struct { 321 length int 322 nullCount int 323 nullBitmap []byte 324 offsets []int32 325 data []byte 326 elemBuf []byte 327 } 328 329 func (c *column) isFixed() bool { 330 return c.elemBuf != nil 331 } 332 333 func (c *column) reset() { 334 c.length = 0 335 c.nullCount = 0 336 c.nullBitmap = c.nullBitmap[:0] 337 if len(c.offsets) > 0 { 338 // The first offset is always 0, it makes slicing the data easier, we need to keep it. 339 c.offsets = c.offsets[:1] 340 } 341 c.data = c.data[:0] 342 } 343 344 func (c *column) isNull(rowIdx int) bool { 345 nullByte := c.nullBitmap[rowIdx/8] 346 return nullByte&(1<<(uint(rowIdx)&7)) == 0 347 } 348 349 func (c *column) appendNullBitmap(on bool) { 350 idx := c.length >> 3 351 if idx >= len(c.nullBitmap) { 352 c.nullBitmap = append(c.nullBitmap, 0) 353 } 354 if on { 355 pos := uint(c.length) & 7 356 c.nullBitmap[idx] |= byte(1 << pos) 357 } else { 358 c.nullCount++ 359 } 360 } 361 362 func (c *column) appendNull() { 363 c.appendNullBitmap(false) 364 if c.isFixed() { 365 c.data = append(c.data, c.elemBuf...) 366 } else { 367 c.offsets = append(c.offsets, c.offsets[c.length]) 368 } 369 c.length++ 370 } 371 372 func (c *column) finishAppendFixed() { 373 c.data = append(c.data, c.elemBuf...) 374 c.appendNullBitmap(true) 375 c.length++ 376 } 377 378 func (c *column) appendInt64(i int64) { 379 *(*int64)(unsafe.Pointer(&c.elemBuf[0])) = i 380 c.finishAppendFixed() 381 } 382 383 func (c *column) appendUint64(u uint64) { 384 *(*uint64)(unsafe.Pointer(&c.elemBuf[0])) = u 385 c.finishAppendFixed() 386 } 387 388 func (c *column) appendFloat32(f float32) { 389 *(*float32)(unsafe.Pointer(&c.elemBuf[0])) = f 390 c.finishAppendFixed() 391 } 392 393 func (c *column) appendFloat64(f float64) { 394 *(*float64)(unsafe.Pointer(&c.elemBuf[0])) = f 395 c.finishAppendFixed() 396 } 397 398 func (c *column) finishAppendVar() { 399 c.appendNullBitmap(true) 400 c.offsets = append(c.offsets, int32(len(c.data))) 401 c.length++ 402 } 403 404 func (c *column) appendString(str string) { 405 c.data = append(c.data, str...) 406 c.finishAppendVar() 407 } 408 409 func (c *column) appendBytes(b []byte) { 410 c.data = append(c.data, b...) 411 c.finishAppendVar() 412 } 413 414 func (c *column) appendTime(t types.Time) { 415 writeTime(c.elemBuf, t) 416 c.finishAppendFixed() 417 } 418 419 func writeTime(buf []byte, t types.Time) { 420 binary.BigEndian.PutUint16(buf, uint16(t.Time.Year())) 421 buf[2] = uint8(t.Time.Month()) 422 buf[3] = uint8(t.Time.Day()) 423 buf[4] = uint8(t.Time.Hour()) 424 buf[5] = uint8(t.Time.Minute()) 425 buf[6] = uint8(t.Time.Second()) 426 binary.BigEndian.PutUint32(buf[8:], uint32(t.Time.Microsecond())) 427 buf[12] = t.Type 428 buf[13] = uint8(t.Fsp) 429 } 430 431 func (c *column) appendDuration(dur types.Duration) { 432 *(*types.Duration)(unsafe.Pointer(&c.elemBuf[0])) = dur 433 c.finishAppendFixed() 434 } 435 436 func (c *column) appendMyDecimal(dec *types.MyDecimal) { 437 *(*types.MyDecimal)(unsafe.Pointer(&c.elemBuf[0])) = *dec 438 c.finishAppendFixed() 439 } 440 441 func (c *column) appendNameValue(name string, val uint64) { 442 var buf [8]byte 443 *(*uint64)(unsafe.Pointer(&buf[0])) = val 444 c.data = append(c.data, buf[:]...) 445 c.data = append(c.data, name...) 446 c.finishAppendVar() 447 } 448 449 func (c *column) appendJSON(j json.BinaryJSON) { 450 c.data = append(c.data, j.TypeCode) 451 c.data = append(c.data, j.Value...) 452 c.finishAppendVar() 453 } 454 455 // Row represents a row of data, can be used to assess values. 456 type Row struct { 457 c *Chunk 458 idx int 459 } 460 461 // Idx returns the row index of Chunk. 462 func (r Row) Idx() int { 463 return r.idx 464 } 465 466 // Len returns the number of values in the row. 467 func (r Row) Len() int { 468 return r.c.NumCols() 469 } 470 471 // GetInt64 returns the int64 value with the colIdx. 472 func (r Row) GetInt64(colIdx int) int64 { 473 col := r.c.columns[colIdx] 474 return *(*int64)(unsafe.Pointer(&col.data[r.idx*8])) 475 } 476 477 // GetUint64 returns the uint64 value with the colIdx. 478 func (r Row) GetUint64(colIdx int) uint64 { 479 col := r.c.columns[colIdx] 480 return *(*uint64)(unsafe.Pointer(&col.data[r.idx*8])) 481 } 482 483 // GetFloat32 returns the float64 value with the colIdx. 484 func (r Row) GetFloat32(colIdx int) float32 { 485 col := r.c.columns[colIdx] 486 return *(*float32)(unsafe.Pointer(&col.data[r.idx*4])) 487 } 488 489 // GetFloat64 returns the float64 value with the colIdx. 490 func (r Row) GetFloat64(colIdx int) float64 { 491 col := r.c.columns[colIdx] 492 return *(*float64)(unsafe.Pointer(&col.data[r.idx*8])) 493 } 494 495 // GetString returns the string value with the colIdx. 496 func (r Row) GetString(colIdx int) string { 497 col := r.c.columns[colIdx] 498 start, end := col.offsets[r.idx], col.offsets[r.idx+1] 499 return hack.String(col.data[start:end]) 500 } 501 502 // GetBytes returns the bytes value with the colIdx. 503 func (r Row) GetBytes(colIdx int) []byte { 504 col := r.c.columns[colIdx] 505 start, end := col.offsets[r.idx], col.offsets[r.idx+1] 506 return col.data[start:end] 507 } 508 509 // GetTime returns the Time value with the colIdx. 510 // TODO: use Time structure directly. 511 func (r Row) GetTime(colIdx int) types.Time { 512 col := r.c.columns[colIdx] 513 return readTime(col.data[r.idx*16:]) 514 } 515 516 func readTime(buf []byte) types.Time { 517 year := int(binary.BigEndian.Uint16(buf)) 518 month := int(buf[2]) 519 day := int(buf[3]) 520 hour := int(buf[4]) 521 minute := int(buf[5]) 522 second := int(buf[6]) 523 microseconds := int(binary.BigEndian.Uint32(buf[8:])) 524 tp := buf[12] 525 fsp := int(buf[13]) 526 return types.Time{ 527 Time: types.FromDate(year, month, day, hour, minute, second, microseconds), 528 Type: tp, 529 Fsp: fsp, 530 } 531 } 532 533 // GetDuration returns the Duration value with the colIdx. 534 func (r Row) GetDuration(colIdx int) types.Duration { 535 col := r.c.columns[colIdx] 536 return *(*types.Duration)(unsafe.Pointer(&col.data[r.idx*16])) 537 } 538 539 func (r Row) getNameValue(colIdx int) (string, uint64) { 540 col := r.c.columns[colIdx] 541 start, end := col.offsets[r.idx], col.offsets[r.idx+1] 542 if start == end { 543 return "", 0 544 } 545 val := *(*uint64)(unsafe.Pointer(&col.data[start])) 546 name := hack.String(col.data[start+8 : end]) 547 return name, val 548 } 549 550 // GetEnum returns the Enum value with the colIdx. 551 func (r Row) GetEnum(colIdx int) types.Enum { 552 name, val := r.getNameValue(colIdx) 553 return types.Enum{Name: name, Value: val} 554 } 555 556 // GetSet returns the Set value with the colIdx. 557 func (r Row) GetSet(colIdx int) types.Set { 558 name, val := r.getNameValue(colIdx) 559 return types.Set{Name: name, Value: val} 560 } 561 562 // GetMyDecimal returns the MyDecimal value with the colIdx. 563 func (r Row) GetMyDecimal(colIdx int) *types.MyDecimal { 564 col := r.c.columns[colIdx] 565 return (*types.MyDecimal)(unsafe.Pointer(&col.data[r.idx*types.MyDecimalStructSize])) 566 } 567 568 // GetJSON returns the JSON value with the colIdx. 569 func (r Row) GetJSON(colIdx int) json.BinaryJSON { 570 col := r.c.columns[colIdx] 571 start, end := col.offsets[r.idx], col.offsets[r.idx+1] 572 return json.BinaryJSON{TypeCode: col.data[start], Value: col.data[start+1 : end]} 573 } 574 575 // GetDatumRow converts chunk.Row to types.DatumRow. 576 // Keep in mind that GetDatumRow has a reference to r.c, which is a chunk, 577 // this function works only if the underlying chunk is valid or unchanged. 578 func (r Row) GetDatumRow(fields []*types.FieldType) types.DatumRow { 579 datumRow := make(types.DatumRow, 0, r.c.NumCols()) 580 for colIdx := 0; colIdx < r.c.NumCols(); colIdx++ { 581 datum := r.GetDatum(colIdx, fields[colIdx]) 582 datumRow = append(datumRow, datum) 583 } 584 return datumRow 585 } 586 587 // GetDatum implements the types.Row interface. 588 func (r Row) GetDatum(colIdx int, tp *types.FieldType) types.Datum { 589 var d types.Datum 590 switch tp.Tp { 591 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: 592 if !r.IsNull(colIdx) { 593 if mysql.HasUnsignedFlag(tp.Flag) { 594 d.SetUint64(r.GetUint64(colIdx)) 595 } else { 596 d.SetInt64(r.GetInt64(colIdx)) 597 } 598 } 599 case mysql.TypeFloat: 600 if !r.IsNull(colIdx) { 601 d.SetFloat32(r.GetFloat32(colIdx)) 602 } 603 case mysql.TypeDouble: 604 if !r.IsNull(colIdx) { 605 d.SetFloat64(r.GetFloat64(colIdx)) 606 } 607 case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, 608 mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: 609 if !r.IsNull(colIdx) { 610 d.SetBytes(r.GetBytes(colIdx)) 611 } 612 case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: 613 if !r.IsNull(colIdx) { 614 d.SetMysqlTime(r.GetTime(colIdx)) 615 } 616 case mysql.TypeDuration: 617 if !r.IsNull(colIdx) { 618 d.SetMysqlDuration(r.GetDuration(colIdx)) 619 } 620 case mysql.TypeNewDecimal: 621 if !r.IsNull(colIdx) { 622 d.SetMysqlDecimal(r.GetMyDecimal(colIdx)) 623 d.SetLength(tp.Flen) 624 d.SetFrac(tp.Decimal) 625 } 626 case mysql.TypeEnum: 627 if !r.IsNull(colIdx) { 628 d.SetMysqlEnum(r.GetEnum(colIdx)) 629 } 630 case mysql.TypeSet: 631 if !r.IsNull(colIdx) { 632 d.SetMysqlSet(r.GetSet(colIdx)) 633 } 634 case mysql.TypeBit: 635 if !r.IsNull(colIdx) { 636 d.SetMysqlBit(r.GetBytes(colIdx)) 637 } 638 case mysql.TypeJSON: 639 if !r.IsNull(colIdx) { 640 d.SetMysqlJSON(r.GetJSON(colIdx)) 641 } 642 } 643 return d 644 } 645 646 // IsNull implements the types.Row interface. 647 func (r Row) IsNull(colIdx int) bool { 648 return r.c.columns[colIdx].isNull(r.idx) 649 }