2024-04-30 23:26:32 +00:00
|
|
|
package logstorage
|
|
|
|
|
|
|
|
import (
|
2024-05-03 09:15:09 +00:00
|
|
|
"encoding/binary"
|
2024-04-30 23:26:32 +00:00
|
|
|
"math"
|
2024-05-04 22:28:01 +00:00
|
|
|
"slices"
|
2024-04-30 23:26:32 +00:00
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
2024-05-03 09:15:09 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
2024-04-30 23:26:32 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
2024-05-05 23:27:05 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fastnum"
|
2024-04-30 23:26:32 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
|
|
)
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// blockResult holds results for a single block of log entries.
|
|
|
|
//
|
|
|
|
// It is expected that its contents is accessed only from a single goroutine at a time.
|
2024-04-30 23:26:32 +00:00
|
|
|
type blockResult struct {
|
|
|
|
// buf holds all the bytes behind the requested column values in the block.
|
|
|
|
buf []byte
|
|
|
|
|
|
|
|
// values holds all the requested column values in the block.
|
|
|
|
valuesBuf []string
|
|
|
|
|
|
|
|
// streamID is streamID for the given blockResult.
|
|
|
|
streamID streamID
|
|
|
|
|
|
|
|
// timestamps contain timestamps for the selected log entries in the block.
|
|
|
|
timestamps []int64
|
|
|
|
|
|
|
|
// csOffset contains cs offset for the requested columns.
|
|
|
|
//
|
|
|
|
// columns with indexes below csOffset are ignored.
|
|
|
|
// This is needed for simplifying data transformations at pipe stages.
|
|
|
|
csOffset int
|
|
|
|
|
|
|
|
// cs contains requested columns.
|
|
|
|
cs []blockResultColumn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) reset() {
|
|
|
|
br.buf = br.buf[:0]
|
|
|
|
|
|
|
|
clear(br.valuesBuf)
|
|
|
|
br.valuesBuf = br.valuesBuf[:0]
|
|
|
|
|
|
|
|
br.streamID.reset()
|
|
|
|
|
|
|
|
br.timestamps = br.timestamps[:0]
|
|
|
|
|
|
|
|
br.csOffset = 0
|
|
|
|
|
2024-05-05 23:27:05 +00:00
|
|
|
clear(br.cs)
|
|
|
|
br.cs = br.cs[:0]
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 23:27:05 +00:00
|
|
|
// setResultColumns sets the given rcs as br columns.
|
|
|
|
//
|
|
|
|
// The returned result is valid only until rcs are modified.
|
|
|
|
func (br *blockResult) setResultColumns(rcs []resultColumn) {
|
|
|
|
br.reset()
|
|
|
|
if len(rcs) == 0 {
|
|
|
|
return
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
2024-05-05 23:27:05 +00:00
|
|
|
fastnum.AppendInt64Zeros(br.timestamps[:0], len(rcs[0].values))
|
2024-04-30 23:26:32 +00:00
|
|
|
|
2024-05-05 23:27:05 +00:00
|
|
|
cs := br.cs
|
|
|
|
for _, rc := range rcs {
|
|
|
|
cs = append(cs, blockResultColumn{
|
|
|
|
name: rc.name,
|
|
|
|
valueType: valueTypeString,
|
|
|
|
encodedValues: rc.values,
|
|
|
|
})
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
2024-05-05 23:27:05 +00:00
|
|
|
br.cs = cs
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) fetchAllColumns(bs *blockSearch, bm *bitmap) {
|
|
|
|
if !br.addStreamColumn(bs) {
|
|
|
|
// Skip the current block, since the associated stream tags are missing.
|
|
|
|
br.reset()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
br.addTimeColumn()
|
|
|
|
|
|
|
|
// Add _msg column
|
|
|
|
v := bs.csh.getConstColumnValue("_msg")
|
|
|
|
if v != "" {
|
|
|
|
br.addConstColumn("_msg", v)
|
|
|
|
} else if ch := bs.csh.getColumnHeader("_msg"); ch != nil {
|
|
|
|
br.addColumn(bs, ch, bm)
|
|
|
|
} else {
|
|
|
|
br.addConstColumn("_msg", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add other const columns
|
|
|
|
for _, cc := range bs.csh.constColumns {
|
|
|
|
if isMsgFieldName(cc.Name) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
br.addConstColumn(cc.Name, cc.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add other non-const columns
|
|
|
|
chs := bs.csh.columnHeaders
|
|
|
|
for i := range chs {
|
|
|
|
ch := &chs[i]
|
|
|
|
if isMsgFieldName(ch.name) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
br.addColumn(bs, ch, bm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) fetchRequestedColumns(bs *blockSearch, bm *bitmap) {
|
2024-05-05 01:11:34 +00:00
|
|
|
for _, columnName := range bs.bsw.so.neededColumnNames {
|
2024-04-30 23:26:32 +00:00
|
|
|
switch columnName {
|
|
|
|
case "_stream":
|
|
|
|
if !br.addStreamColumn(bs) {
|
|
|
|
// Skip the current block, since the associated stream tags are missing.
|
|
|
|
br.reset()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "_time":
|
|
|
|
br.addTimeColumn()
|
|
|
|
default:
|
|
|
|
v := bs.csh.getConstColumnValue(columnName)
|
|
|
|
if v != "" {
|
|
|
|
br.addConstColumn(columnName, v)
|
|
|
|
} else if ch := bs.csh.getColumnHeader(columnName); ch != nil {
|
|
|
|
br.addColumn(bs, ch, bm)
|
|
|
|
} else {
|
|
|
|
br.addConstColumn(columnName, "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) mustInit(bs *blockSearch, bm *bitmap) {
|
|
|
|
br.reset()
|
|
|
|
|
|
|
|
br.streamID = bs.bsw.bh.streamID
|
|
|
|
|
|
|
|
if bm.isZero() {
|
|
|
|
// Nothing to initialize for zero matching log entries in the block.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize timestamps, since they are required for all the further work with br.
|
|
|
|
|
|
|
|
srcTimestamps := bs.getTimestamps()
|
|
|
|
if bm.areAllBitsSet() {
|
|
|
|
// Fast path - all the rows in the block are selected, so copy all the timestamps without any filtering.
|
|
|
|
br.timestamps = append(br.timestamps[:0], srcTimestamps...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow path - copy only the needed timestamps to br according to filter results.
|
|
|
|
dstTimestamps := br.timestamps[:0]
|
|
|
|
bm.forEachSetBit(func(idx int) bool {
|
|
|
|
ts := srcTimestamps[idx]
|
|
|
|
dstTimestamps = append(dstTimestamps, ts)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
br.timestamps = dstTimestamps
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) addColumn(bs *blockSearch, ch *columnHeader, bm *bitmap) {
|
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
var dictValues []string
|
|
|
|
|
|
|
|
appendValue := func(v string) {
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = append(buf, v...)
|
|
|
|
s := bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ch.valueType {
|
|
|
|
case valueTypeString:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeDict:
|
|
|
|
dictValues = ch.valuesDict.values
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 1 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected dict value size for column %q; got %d bytes; want 1 byte", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
dictIdx := v[0]
|
|
|
|
if int(dictIdx) >= len(dictValues) {
|
|
|
|
logger.Panicf("FATAL: %s: too big dict index for column %q: %d; should be smaller than %d", bs.partPath(), ch.name, dictIdx, len(dictValues))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeUint8:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 1 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for uint8 column %q; got %d bytes; want 1 byte", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeUint16:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 2 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for uint16 column %q; got %d bytes; want 2 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeUint32:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 4 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for uint32 column %q; got %d bytes; want 4 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeUint64:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 8 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for uint64 column %q; got %d bytes; want 8 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeFloat64:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 8 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for float64 column %q; got %d bytes; want 8 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeIPv4:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 4 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for ipv4 column %q; got %d bytes; want 4 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
case valueTypeTimestampISO8601:
|
|
|
|
visitValues(bs, ch, bm, func(v string) bool {
|
|
|
|
if len(v) != 8 {
|
|
|
|
logger.Panicf("FATAL: %s: unexpected size for timestmap column %q; got %d bytes; want 8 bytes", bs.partPath(), ch.name, len(v))
|
|
|
|
}
|
|
|
|
appendValue(v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
default:
|
|
|
|
logger.Panicf("FATAL: %s: unknown valueType=%d for column %q", bs.partPath(), ch.valueType, ch.name)
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedValues := valuesBuf[valuesBufLen:]
|
|
|
|
|
|
|
|
valuesBufLen = len(valuesBuf)
|
|
|
|
for _, v := range dictValues {
|
|
|
|
appendValue(v)
|
|
|
|
}
|
|
|
|
dictValues = valuesBuf[valuesBufLen:]
|
|
|
|
|
2024-05-04 22:28:01 +00:00
|
|
|
name := getCanonicalColumnName(ch.name)
|
2024-04-30 23:26:32 +00:00
|
|
|
br.cs = append(br.cs, blockResultColumn{
|
|
|
|
name: name,
|
|
|
|
valueType: ch.valueType,
|
|
|
|
dictValues: dictValues,
|
|
|
|
encodedValues: encodedValues,
|
|
|
|
})
|
|
|
|
br.buf = buf
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) addTimeColumn() {
|
|
|
|
br.cs = append(br.cs, blockResultColumn{
|
|
|
|
name: "_time",
|
|
|
|
isTime: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) addStreamColumn(bs *blockSearch) bool {
|
|
|
|
bb := bbPool.Get()
|
|
|
|
defer bbPool.Put(bb)
|
|
|
|
|
|
|
|
bb.B = bs.bsw.p.pt.appendStreamTagsByStreamID(bb.B[:0], &br.streamID)
|
|
|
|
if len(bb.B) == 0 {
|
|
|
|
// Couldn't find stream tags by streamID. This may be the case when the corresponding log stream
|
|
|
|
// was recently registered and its tags aren't visible to search yet.
|
|
|
|
// The stream tags must become visible in a few seconds.
|
|
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6042
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
st := GetStreamTags()
|
|
|
|
mustUnmarshalStreamTags(st, bb.B)
|
|
|
|
bb.B = st.marshalString(bb.B[:0])
|
|
|
|
PutStreamTags(st)
|
|
|
|
|
|
|
|
s := bytesutil.ToUnsafeString(bb.B)
|
|
|
|
br.addConstColumn("_stream", s)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) addConstColumn(name, value string) {
|
|
|
|
buf := br.buf
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = append(buf, value...)
|
|
|
|
s := bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
|
|
|
|
br.cs = append(br.cs, blockResultColumn{
|
|
|
|
name: name,
|
|
|
|
isConst: true,
|
|
|
|
encodedValues: valuesBuf[valuesBufLen:],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedColumnValues(c *blockResultColumn, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
if c.isConst {
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedConstValues(c.encodedValues[0], bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
}
|
|
|
|
if c.isTime {
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedTimestampValues(bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch c.valueType {
|
|
|
|
case valueTypeString:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedStringValues(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeDict:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedDictValues(c.encodedValues, c.dictValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeUint8:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedUint8Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeUint16:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedUint16Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeUint32:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedUint32Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeUint64:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedUint64Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeFloat64:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedFloat64Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeIPv4:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedIPv4Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
case valueTypeTimestampISO8601:
|
2024-05-03 10:10:45 +00:00
|
|
|
return br.getBucketedTimestampISO8601Values(c.encodedValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
default:
|
|
|
|
logger.Panicf("BUG: unknown valueType=%d", c.valueType)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedConstValues(v string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
if v == "" {
|
|
|
|
// Fast path - return a slice of empty strings without constructing the slice.
|
|
|
|
return getEmptyStrings(len(br.timestamps))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slower path - construct slice of identical values with the len(br.timestamps)
|
|
|
|
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
v = br.getBucketedValue(v, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
for range br.timestamps {
|
|
|
|
valuesBuf = append(valuesBuf, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedTimestampValues(bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
timestamps := br.timestamps
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if bucketSize <= 1 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i := range timestamps {
|
|
|
|
if i > 0 && timestamps[i-1] == timestamps[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalTimestampRFC3339Nano(buf, timestamps[i])
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := int64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
var prevTimestamp int64
|
|
|
|
for i := range timestamps {
|
|
|
|
if i > 0 && timestamps[i-1] == timestamps[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
timestamp := timestamps[i]
|
2024-05-03 10:10:45 +00:00
|
|
|
timestamp -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
timestamp -= timestamp % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
timestamp += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && timestamp == prevTimestamp {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
prevTimestamp = timestamp
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalTimestampRFC3339Nano(buf, timestamp)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.buf = buf
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedStringValues(values []string, bucketSize, bucketOffset float64) []string {
|
|
|
|
if bucketSize <= 0 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
return values
|
|
|
|
}
|
|
|
|
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
for i := range values {
|
|
|
|
if i > 0 && values[i-1] == values[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
s = br.getBucketedValue(values[i], bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedDictValues(encodedValues, dictValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
dictValues = br.getBucketedStringValues(dictValues, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
for _, v := range encodedValues {
|
|
|
|
dictIdx := v[0]
|
|
|
|
valuesBuf = append(valuesBuf, dictValues[dictIdx])
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedUint8Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if (bucketSize <= 1 || bucketSize >= (1<<8)) && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
n := uint64(v[0])
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint64(int64(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint64
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
n := uint64(v[0])
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return br.valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedUint16Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if (bucketSize <= 1 || bucketSize >= (1<<16)) && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := uint64(encoding.UnmarshalUint16(b))
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint64(int64(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint64
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := uint64(encoding.UnmarshalUint16(b))
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return br.valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedUint32Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if (bucketSize <= 1 || bucketSize >= (1<<32)) && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := uint64(encoding.UnmarshalUint32(b))
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint64(int64(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint64
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := uint64(encoding.UnmarshalUint32(b))
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return br.valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedUint64Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if (bucketSize <= 1 || bucketSize >= (1<<64)) && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint64(int64(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint64
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalUint64(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return br.valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedFloat64Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if bucketSize <= 0 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
f := math.Float64frombits(n)
|
|
|
|
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalFloat64(buf, f)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_, e := decimal.FromFloat(bucketSize)
|
|
|
|
p10 := math.Pow10(int(-e))
|
|
|
|
bucketSizeP10 := int64(bucketSize * p10)
|
|
|
|
var fPrev float64
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
f := math.Float64frombits(n)
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
f -= bucketOffset
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// emulate f % bucketSize for float64 values
|
|
|
|
fP10 := int64(f * p10)
|
|
|
|
fP10 -= fP10 % bucketSizeP10
|
|
|
|
f = float64(fP10) / p10
|
|
|
|
|
2024-05-04 22:28:01 +00:00
|
|
|
f += bucketOffset
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && f == fPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
fPrev = f
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalFloat64(buf, f)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return br.valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedIPv4Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if bucketSize <= 1 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = toIPv4String(buf, v)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint32(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint32(int32(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint32
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := binary.BigEndian.Uint32(b)
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalIPv4(buf, n)
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
func (br *blockResult) getBucketedTimestampISO8601Values(encodedValues []string, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
buf := br.buf
|
|
|
|
valuesBuf := br.valuesBuf
|
|
|
|
valuesBufLen := len(valuesBuf)
|
|
|
|
|
|
|
|
var s string
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
if bucketSize <= 1 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalTimestampISO8601(buf, int64(n))
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bucketSizeInt := uint64(bucketSize)
|
2024-05-03 10:10:45 +00:00
|
|
|
bucketOffsetInt := uint64(int64(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
var nPrev uint64
|
|
|
|
bb := bbPool.Get()
|
|
|
|
for i, v := range encodedValues {
|
|
|
|
if i > 0 && encodedValues[i-1] == encodedValues[i] {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % bucketSizeInt
|
2024-05-04 22:28:01 +00:00
|
|
|
n += bucketOffsetInt
|
2024-05-03 09:15:09 +00:00
|
|
|
if i > 0 && n == nPrev {
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nPrev = n
|
|
|
|
bufLen := len(buf)
|
|
|
|
buf = marshalTimestampISO8601(buf, int64(n))
|
|
|
|
s = bytesutil.ToUnsafeString(buf[bufLen:])
|
|
|
|
valuesBuf = append(valuesBuf, s)
|
|
|
|
}
|
|
|
|
bbPool.Put(bb)
|
|
|
|
}
|
|
|
|
|
|
|
|
br.valuesBuf = valuesBuf
|
|
|
|
br.buf = buf
|
|
|
|
|
|
|
|
return valuesBuf[valuesBufLen:]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
// getBucketedValue returns bucketed s according to the given bucketSize and bucketOffset
|
|
|
|
func (br *blockResult) getBucketedValue(s string, bucketSize, bucketOffset float64) string {
|
|
|
|
if bucketSize <= 0 && bucketOffset == 0 {
|
2024-05-03 09:15:09 +00:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
if len(s) == 0 {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
c := s[0]
|
|
|
|
if (c < '0' || c > '9') && c != '-' {
|
|
|
|
// Fast path - the value cannot be bucketed, since it starts with unexpected chars.
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
if f, ok := tryParseFloat64(s); ok {
|
2024-05-03 10:10:45 +00:00
|
|
|
f -= bucketOffset
|
2024-05-04 22:28:01 +00:00
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// emulate f % bucketSize for float64 values
|
|
|
|
_, e := decimal.FromFloat(bucketSize)
|
|
|
|
p10 := math.Pow10(int(-e))
|
|
|
|
fP10 := int64(f * p10)
|
|
|
|
fP10 -= fP10 % int64(bucketSize*p10)
|
|
|
|
f = float64(fP10) / p10
|
|
|
|
|
2024-05-04 22:28:01 +00:00
|
|
|
f += bucketOffset
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
bufLen := len(br.buf)
|
|
|
|
br.buf = marshalFloat64(br.buf, f)
|
|
|
|
return bytesutil.ToUnsafeString(br.buf[bufLen:])
|
|
|
|
}
|
|
|
|
|
|
|
|
if nsecs, ok := tryParseTimestampISO8601(s); ok {
|
2024-05-03 10:10:45 +00:00
|
|
|
nsecs -= int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
nsecs -= nsecs % int64(bucketSize)
|
2024-05-04 22:28:01 +00:00
|
|
|
nsecs += int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
bufLen := len(br.buf)
|
|
|
|
br.buf = marshalTimestampISO8601(br.buf, nsecs)
|
|
|
|
return bytesutil.ToUnsafeString(br.buf[bufLen:])
|
|
|
|
}
|
|
|
|
|
|
|
|
if nsecs, ok := tryParseTimestampRFC3339Nano(s); ok {
|
2024-05-03 10:10:45 +00:00
|
|
|
nsecs -= int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
nsecs -= nsecs % int64(bucketSize)
|
2024-05-04 22:28:01 +00:00
|
|
|
nsecs += int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
bufLen := len(br.buf)
|
|
|
|
br.buf = marshalTimestampRFC3339Nano(br.buf, nsecs)
|
|
|
|
return bytesutil.ToUnsafeString(br.buf[bufLen:])
|
|
|
|
}
|
|
|
|
|
|
|
|
if n, ok := tryParseIPv4(s); ok {
|
2024-05-03 10:10:45 +00:00
|
|
|
n -= uint32(int32(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
n -= n % uint32(bucketSize)
|
2024-05-04 22:28:01 +00:00
|
|
|
n += uint32(int32(bucketOffset))
|
2024-05-03 09:15:09 +00:00
|
|
|
bufLen := len(br.buf)
|
|
|
|
br.buf = marshalIPv4(br.buf, n)
|
|
|
|
return bytesutil.ToUnsafeString(br.buf[bufLen:])
|
|
|
|
}
|
|
|
|
|
|
|
|
if nsecs, ok := tryParseDuration(s); ok {
|
2024-05-03 10:10:45 +00:00
|
|
|
nsecs -= int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
nsecs -= nsecs % int64(bucketSize)
|
2024-05-04 22:28:01 +00:00
|
|
|
nsecs += int64(bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
bufLen := len(br.buf)
|
|
|
|
br.buf = marshalDuration(br.buf, nsecs)
|
|
|
|
return bytesutil.ToUnsafeString(br.buf[bufLen:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't parse s, so return it as is.
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2024-04-30 23:26:32 +00:00
|
|
|
func (br *blockResult) addEmptyStringColumn(columnName string) {
|
|
|
|
br.cs = append(br.cs, blockResultColumn{
|
|
|
|
name: columnName,
|
|
|
|
valueType: valueTypeString,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-04 22:28:01 +00:00
|
|
|
// copyColumns copies columns from srcColumnNames to dstColumnNames.
|
|
|
|
func (br *blockResult) copyColumns(srcColumnNames, dstColumnNames []string) {
|
|
|
|
if len(srcColumnNames) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cs := br.cs
|
|
|
|
csOffset := len(cs)
|
|
|
|
for _, c := range br.getColumns() {
|
|
|
|
if idx := slices.Index(srcColumnNames, c.name); idx >= 0 {
|
|
|
|
c.name = dstColumnNames[idx]
|
|
|
|
cs = append(cs, c)
|
|
|
|
// continue is skipped intentionally in order to leave the original column in the columns list.
|
|
|
|
}
|
|
|
|
if !slices.Contains(dstColumnNames, c.name) {
|
|
|
|
cs = append(cs, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
br.csOffset = csOffset
|
|
|
|
br.cs = cs
|
2024-05-05 01:06:01 +00:00
|
|
|
|
|
|
|
for _, dstColumnName := range dstColumnNames {
|
|
|
|
br.createMissingColumnByName(dstColumnName)
|
|
|
|
}
|
2024-05-04 22:28:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// renameColumns renames columns from srcColumnNames to dstColumnNames.
|
|
|
|
func (br *blockResult) renameColumns(srcColumnNames, dstColumnNames []string) {
|
|
|
|
if len(srcColumnNames) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cs := br.cs
|
|
|
|
csOffset := len(cs)
|
|
|
|
for _, c := range br.getColumns() {
|
|
|
|
if idx := slices.Index(srcColumnNames, c.name); idx >= 0 {
|
|
|
|
c.name = dstColumnNames[idx]
|
|
|
|
cs = append(cs, c)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !slices.Contains(dstColumnNames, c.name) {
|
|
|
|
cs = append(cs, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
br.csOffset = csOffset
|
|
|
|
br.cs = cs
|
2024-05-05 01:06:01 +00:00
|
|
|
|
|
|
|
for _, dstColumnName := range dstColumnNames {
|
|
|
|
br.createMissingColumnByName(dstColumnName)
|
|
|
|
}
|
2024-05-04 22:28:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// deleteColumns deletes columns with the given columnNames.
|
|
|
|
func (br *blockResult) deleteColumns(columnNames []string) {
|
|
|
|
if len(columnNames) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cs := br.cs
|
|
|
|
csOffset := len(cs)
|
|
|
|
for _, c := range br.getColumns() {
|
|
|
|
if !slices.Contains(columnNames, c.name) {
|
|
|
|
cs = append(cs, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
br.csOffset = csOffset
|
|
|
|
br.cs = cs
|
|
|
|
}
|
|
|
|
|
|
|
|
// setColumns sets the resulting columns to the given columnNames.
|
|
|
|
func (br *blockResult) setColumns(columnNames []string) {
|
2024-04-30 23:26:32 +00:00
|
|
|
if br.areSameColumns(columnNames) {
|
|
|
|
// Fast path - nothing to change.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow path - construct the requested columns
|
|
|
|
cs := br.cs
|
|
|
|
csOffset := len(cs)
|
|
|
|
for _, columnName := range columnNames {
|
|
|
|
c := br.getColumnByName(columnName)
|
|
|
|
cs = append(cs, c)
|
|
|
|
}
|
|
|
|
br.csOffset = csOffset
|
|
|
|
br.cs = cs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) areSameColumns(columnNames []string) bool {
|
|
|
|
cs := br.getColumns()
|
|
|
|
if len(cs) != len(columnNames) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range cs {
|
|
|
|
if cs[i].name != columnNames[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) getColumnByName(columnName string) blockResultColumn {
|
|
|
|
cs := br.getColumns()
|
2024-05-05 01:06:01 +00:00
|
|
|
for i := range cs {
|
2024-04-30 23:26:32 +00:00
|
|
|
if cs[i].name == columnName {
|
|
|
|
return cs[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockResultColumn{
|
|
|
|
name: columnName,
|
|
|
|
isConst: true,
|
|
|
|
encodedValues: getEmptyStrings(1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 01:06:01 +00:00
|
|
|
func (br *blockResult) createMissingColumnByName(columnName string) {
|
|
|
|
cs := br.getColumns()
|
|
|
|
for i := range cs {
|
|
|
|
if cs[i].name == columnName {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
br.cs = append(br.cs, blockResultColumn{
|
|
|
|
name: columnName,
|
|
|
|
isConst: true,
|
|
|
|
encodedValues: getEmptyStrings(1),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-04-30 23:26:32 +00:00
|
|
|
func (br *blockResult) getColumns() []blockResultColumn {
|
|
|
|
return br.cs[br.csOffset:]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) skipRows(skipRows int) {
|
|
|
|
br.timestamps = append(br.timestamps[:0], br.timestamps[skipRows:]...)
|
|
|
|
cs := br.getColumns()
|
|
|
|
for i := range cs {
|
|
|
|
c := &cs[i]
|
|
|
|
if c.values != nil {
|
|
|
|
c.values = append(c.values[:0], c.values[skipRows:]...)
|
|
|
|
}
|
|
|
|
if c.isConst {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if c.encodedValues != nil {
|
|
|
|
c.encodedValues = append(c.encodedValues[:0], c.encodedValues[skipRows:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (br *blockResult) truncateRows(keepRows int) {
|
|
|
|
br.timestamps = br.timestamps[:keepRows]
|
|
|
|
cs := br.getColumns()
|
|
|
|
for i := range cs {
|
|
|
|
c := &cs[i]
|
|
|
|
if c.values != nil {
|
|
|
|
c.values = c.values[:keepRows]
|
|
|
|
}
|
|
|
|
if c.isConst {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if c.encodedValues != nil {
|
|
|
|
c.encodedValues = c.encodedValues[:keepRows]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 23:27:05 +00:00
|
|
|
// blockResultColumn represents named column from blockResult.
|
|
|
|
//
|
|
|
|
// blockResultColumn doesn't own any referred data - all the referred data must be owned by blockResult.
|
|
|
|
// This simplifies copying, resetting and re-using of the struct.
|
2024-04-30 23:26:32 +00:00
|
|
|
type blockResultColumn struct {
|
|
|
|
// name is column name.
|
|
|
|
name string
|
|
|
|
|
|
|
|
// isConst is set to true if the column is const.
|
|
|
|
//
|
|
|
|
// The column value is stored in encodedValues[0]
|
|
|
|
isConst bool
|
|
|
|
|
|
|
|
// isTime is set to true if the column contains _time values.
|
|
|
|
//
|
|
|
|
// The column values are stored in blockResult.timestamps
|
|
|
|
isTime bool
|
|
|
|
|
|
|
|
// valueType is the type of non-cost value
|
|
|
|
valueType valueType
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// dictValues contains dictionary values for valueTypeDict column
|
2024-04-30 23:26:32 +00:00
|
|
|
dictValues []string
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// encodedValues contains encoded values for non-const column
|
2024-04-30 23:26:32 +00:00
|
|
|
encodedValues []string
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// values contains decoded values after getValues() call
|
2024-04-30 23:26:32 +00:00
|
|
|
values []string
|
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// bucketedValues contains values after getBucketedValues() call
|
|
|
|
bucketedValues []string
|
|
|
|
|
|
|
|
// bucketSize contains bucketSize for bucketedValues
|
|
|
|
bucketSize float64
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
// bucketOffset contains bucketOffset for bucketedValues
|
|
|
|
bucketOffset float64
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// getValueAtRow returns value for the value at the given rowIdx.
|
|
|
|
//
|
|
|
|
// The returned value is valid until br.reset() is called.
|
|
|
|
func (c *blockResultColumn) getValueAtRow(br *blockResult, rowIdx int) string {
|
|
|
|
if c.isConst {
|
|
|
|
// Fast path for const column.
|
|
|
|
return c.encodedValues[0]
|
|
|
|
}
|
|
|
|
if c.values != nil {
|
|
|
|
// Fast path, which avoids call overhead for getValues().
|
|
|
|
return c.values[rowIdx]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow path - decode all the values and return the given value.
|
|
|
|
values := c.getValues(br)
|
|
|
|
return values[rowIdx]
|
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
// getValues returns values for the given column, bucketed according to bucketSize and bucketOffset.
|
2024-04-30 23:26:32 +00:00
|
|
|
//
|
|
|
|
// The returned values are valid until br.reset() is called.
|
2024-05-03 10:10:45 +00:00
|
|
|
func (c *blockResultColumn) getBucketedValues(br *blockResult, bucketSize, bucketOffset float64) []string {
|
2024-05-03 09:15:09 +00:00
|
|
|
if bucketSize <= 0 {
|
|
|
|
return c.getValues(br)
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
2024-05-03 10:10:45 +00:00
|
|
|
if values := c.bucketedValues; values != nil && c.bucketSize == bucketSize && c.bucketOffset == bucketOffset {
|
2024-05-03 09:15:09 +00:00
|
|
|
return values
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
c.bucketedValues = br.getBucketedColumnValues(c, bucketSize, bucketOffset)
|
2024-05-03 09:15:09 +00:00
|
|
|
c.bucketSize = bucketSize
|
2024-05-03 10:10:45 +00:00
|
|
|
c.bucketOffset = bucketOffset
|
2024-05-03 09:15:09 +00:00
|
|
|
return c.bucketedValues
|
|
|
|
}
|
2024-04-30 23:26:32 +00:00
|
|
|
|
2024-05-03 09:15:09 +00:00
|
|
|
// getValues returns values for the given column.
|
|
|
|
//
|
|
|
|
// The returned values are valid until br.reset() is called.
|
|
|
|
func (c *blockResultColumn) getValues(br *blockResult) []string {
|
|
|
|
if values := c.values; values != nil {
|
|
|
|
return values
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
2024-05-03 10:10:45 +00:00
|
|
|
c.values = br.getBucketedColumnValues(c, 0, 0)
|
2024-04-30 23:26:32 +00:00
|
|
|
return c.values
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *blockResultColumn) getFloatValueAtRow(rowIdx int) float64 {
|
|
|
|
if c.isConst {
|
|
|
|
v := c.encodedValues[0]
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
|
|
|
return nan
|
|
|
|
}
|
2024-04-30 23:26:32 +00:00
|
|
|
return f
|
|
|
|
}
|
|
|
|
if c.isTime {
|
2024-04-30 23:58:35 +00:00
|
|
|
return nan
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch c.valueType {
|
|
|
|
case valueTypeString:
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok := tryParseFloat64(c.encodedValues[rowIdx])
|
|
|
|
if !ok {
|
|
|
|
return nan
|
|
|
|
}
|
2024-04-30 23:26:32 +00:00
|
|
|
return f
|
|
|
|
case valueTypeDict:
|
|
|
|
dictIdx := c.encodedValues[rowIdx][0]
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok := tryParseFloat64(c.dictValues[dictIdx])
|
|
|
|
if !ok {
|
|
|
|
return nan
|
|
|
|
}
|
2024-04-30 23:26:32 +00:00
|
|
|
return f
|
|
|
|
case valueTypeUint8:
|
|
|
|
return float64(c.encodedValues[rowIdx][0])
|
|
|
|
case valueTypeUint16:
|
|
|
|
b := bytesutil.ToUnsafeBytes(c.encodedValues[rowIdx])
|
|
|
|
return float64(encoding.UnmarshalUint16(b))
|
|
|
|
case valueTypeUint32:
|
|
|
|
b := bytesutil.ToUnsafeBytes(c.encodedValues[rowIdx])
|
|
|
|
return float64(encoding.UnmarshalUint32(b))
|
|
|
|
case valueTypeUint64:
|
|
|
|
b := bytesutil.ToUnsafeBytes(c.encodedValues[rowIdx])
|
|
|
|
return float64(encoding.UnmarshalUint64(b))
|
|
|
|
case valueTypeFloat64:
|
|
|
|
b := bytesutil.ToUnsafeBytes(c.encodedValues[rowIdx])
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
return math.Float64frombits(n)
|
|
|
|
case valueTypeIPv4:
|
2024-04-30 23:58:35 +00:00
|
|
|
return nan
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeTimestampISO8601:
|
2024-04-30 23:58:35 +00:00
|
|
|
return nan
|
2024-04-30 23:26:32 +00:00
|
|
|
default:
|
|
|
|
logger.Panicf("BUG: unknown valueType=%d", c.valueType)
|
2024-04-30 23:58:35 +00:00
|
|
|
return nan
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *blockResultColumn) getMaxValue(br *blockResult) float64 {
|
|
|
|
if c.isConst {
|
|
|
|
v := c.encodedValues[0]
|
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
|
|
|
return nan
|
|
|
|
}
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
if c.isTime {
|
|
|
|
return nan
|
|
|
|
}
|
|
|
|
|
|
|
|
switch c.valueType {
|
|
|
|
case valueTypeString:
|
2024-05-01 08:31:46 +00:00
|
|
|
max := nan
|
2024-04-30 23:58:35 +00:00
|
|
|
f := float64(0)
|
|
|
|
ok := false
|
|
|
|
values := c.encodedValues
|
|
|
|
for i := range values {
|
|
|
|
if i == 0 || values[i-1] != values[i] {
|
|
|
|
f, ok = tryParseFloat64(values[i])
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
if ok && (f > max || math.IsNaN(max)) {
|
2024-04-30 23:58:35 +00:00
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
case valueTypeDict:
|
|
|
|
a := encoding.GetFloat64s(len(c.dictValues))
|
|
|
|
dictValuesFloat := a.A
|
|
|
|
for i, v := range c.dictValues {
|
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
|
|
|
f = nan
|
|
|
|
}
|
|
|
|
dictValuesFloat[i] = f
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
max := nan
|
2024-04-30 23:58:35 +00:00
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
dictIdx := v[0]
|
|
|
|
f := dictValuesFloat[dictIdx]
|
2024-05-01 08:31:46 +00:00
|
|
|
if f > max || math.IsNaN(max) {
|
2024-04-30 23:58:35 +00:00
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
encoding.PutFloat64s(a)
|
|
|
|
return max
|
|
|
|
case valueTypeUint8:
|
|
|
|
max := math.Inf(-1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
f := float64(v[0])
|
|
|
|
if f > max {
|
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
case valueTypeUint16:
|
|
|
|
max := math.Inf(-1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint16(b))
|
|
|
|
if f > max {
|
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
case valueTypeUint32:
|
|
|
|
max := math.Inf(-1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint32(b))
|
|
|
|
if f > max {
|
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
case valueTypeUint64:
|
|
|
|
max := math.Inf(-1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint64(b))
|
|
|
|
if f > max {
|
|
|
|
max = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
case valueTypeIPv4:
|
|
|
|
return nan
|
|
|
|
case valueTypeTimestampISO8601:
|
|
|
|
return nan
|
|
|
|
default:
|
|
|
|
logger.Panicf("BUG: unknown valueType=%d", c.valueType)
|
2024-05-01 00:08:37 +00:00
|
|
|
return nan
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *blockResultColumn) getMinValue(br *blockResult) float64 {
|
|
|
|
if c.isConst {
|
|
|
|
v := c.encodedValues[0]
|
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
|
|
|
return nan
|
|
|
|
}
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
if c.isTime {
|
|
|
|
return nan
|
|
|
|
}
|
|
|
|
|
|
|
|
switch c.valueType {
|
|
|
|
case valueTypeString:
|
2024-05-01 08:31:46 +00:00
|
|
|
min := nan
|
2024-05-01 00:08:37 +00:00
|
|
|
f := float64(0)
|
|
|
|
ok := false
|
|
|
|
values := c.encodedValues
|
|
|
|
for i := range values {
|
|
|
|
if i == 0 || values[i-1] != values[i] {
|
|
|
|
f, ok = tryParseFloat64(values[i])
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
if ok && (f < min || math.IsNaN(min)) {
|
2024-05-01 00:08:37 +00:00
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
case valueTypeDict:
|
|
|
|
a := encoding.GetFloat64s(len(c.dictValues))
|
|
|
|
dictValuesFloat := a.A
|
|
|
|
for i, v := range c.dictValues {
|
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
|
|
|
f = nan
|
|
|
|
}
|
|
|
|
dictValuesFloat[i] = f
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
min := nan
|
2024-05-01 00:08:37 +00:00
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
dictIdx := v[0]
|
|
|
|
f := dictValuesFloat[dictIdx]
|
2024-05-01 08:31:46 +00:00
|
|
|
if f < min || math.IsNaN(min) {
|
2024-05-01 00:08:37 +00:00
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
encoding.PutFloat64s(a)
|
|
|
|
return min
|
|
|
|
case valueTypeUint8:
|
|
|
|
min := math.Inf(1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
f := float64(v[0])
|
|
|
|
if f < min {
|
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
case valueTypeUint16:
|
|
|
|
min := math.Inf(1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint16(b))
|
|
|
|
if f < min {
|
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
case valueTypeUint32:
|
|
|
|
min := math.Inf(1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint32(b))
|
|
|
|
if f < min {
|
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
case valueTypeUint64:
|
|
|
|
min := math.Inf(1)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
f := float64(encoding.UnmarshalUint64(b))
|
|
|
|
if f < min {
|
|
|
|
min = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
case valueTypeIPv4:
|
|
|
|
return nan
|
|
|
|
case valueTypeTimestampISO8601:
|
|
|
|
return nan
|
|
|
|
default:
|
|
|
|
logger.Panicf("BUG: unknown valueType=%d", c.valueType)
|
2024-04-30 23:58:35 +00:00
|
|
|
return nan
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-01 08:31:46 +00:00
|
|
|
func (c *blockResultColumn) sumValues(br *blockResult) (float64, int) {
|
2024-04-30 23:26:32 +00:00
|
|
|
if c.isConst {
|
|
|
|
v := c.encodedValues[0]
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
2024-05-01 08:31:46 +00:00
|
|
|
return 0, 0
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return f * float64(len(br.timestamps)), len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
if c.isTime {
|
2024-05-01 08:31:46 +00:00
|
|
|
return 0, 0
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch c.valueType {
|
|
|
|
case valueTypeString:
|
|
|
|
sum := float64(0)
|
2024-05-01 08:31:46 +00:00
|
|
|
count := 0
|
2024-04-30 23:26:32 +00:00
|
|
|
f := float64(0)
|
2024-04-30 23:58:35 +00:00
|
|
|
ok := false
|
2024-04-30 23:26:32 +00:00
|
|
|
values := c.encodedValues
|
|
|
|
for i := range values {
|
|
|
|
if i == 0 || values[i-1] != values[i] {
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok = tryParseFloat64(values[i])
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
2024-04-30 23:58:35 +00:00
|
|
|
if ok {
|
2024-04-30 23:26:32 +00:00
|
|
|
sum += f
|
2024-05-01 08:31:46 +00:00
|
|
|
count++
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return sum, count
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeDict:
|
|
|
|
a := encoding.GetFloat64s(len(c.dictValues))
|
|
|
|
dictValuesFloat := a.A
|
|
|
|
for i, v := range c.dictValues {
|
2024-04-30 23:58:35 +00:00
|
|
|
f, ok := tryParseFloat64(v)
|
|
|
|
if !ok {
|
2024-05-01 08:31:46 +00:00
|
|
|
f = nan
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
dictValuesFloat[i] = f
|
|
|
|
}
|
|
|
|
sum := float64(0)
|
2024-05-01 08:31:46 +00:00
|
|
|
count := 0
|
2024-04-30 23:26:32 +00:00
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
dictIdx := v[0]
|
2024-05-01 08:31:46 +00:00
|
|
|
f := dictValuesFloat[dictIdx]
|
|
|
|
if !math.IsNaN(f) {
|
|
|
|
sum += f
|
|
|
|
count++
|
|
|
|
}
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
encoding.PutFloat64s(a)
|
2024-05-01 08:31:46 +00:00
|
|
|
return sum, count
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeUint8:
|
|
|
|
sum := uint64(0)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
sum += uint64(v[0])
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return float64(sum), len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeUint16:
|
|
|
|
sum := uint64(0)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
sum += uint64(encoding.UnmarshalUint16(b))
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return float64(sum), len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeUint32:
|
|
|
|
sum := uint64(0)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
sum += uint64(encoding.UnmarshalUint32(b))
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return float64(sum), len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeUint64:
|
|
|
|
sum := float64(0)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
sum += float64(encoding.UnmarshalUint64(b))
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return sum, len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeFloat64:
|
|
|
|
sum := float64(0)
|
|
|
|
for _, v := range c.encodedValues {
|
|
|
|
b := bytesutil.ToUnsafeBytes(v)
|
|
|
|
n := encoding.UnmarshalUint64(b)
|
|
|
|
f := math.Float64frombits(n)
|
|
|
|
if !math.IsNaN(f) {
|
|
|
|
sum += f
|
|
|
|
}
|
|
|
|
}
|
2024-05-01 08:31:46 +00:00
|
|
|
return sum, len(br.timestamps)
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeIPv4:
|
2024-05-01 08:31:46 +00:00
|
|
|
return 0, 0
|
2024-04-30 23:26:32 +00:00
|
|
|
case valueTypeTimestampISO8601:
|
2024-05-01 08:31:46 +00:00
|
|
|
return 0, 0
|
2024-04-30 23:26:32 +00:00
|
|
|
default:
|
|
|
|
logger.Panicf("BUG: unknown valueType=%d", c.valueType)
|
2024-05-01 08:31:46 +00:00
|
|
|
return 0, 0
|
2024-04-30 23:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-30 23:58:35 +00:00
|
|
|
|
2024-05-05 23:27:05 +00:00
|
|
|
// resultColumn represents a column with result values
|
|
|
|
type resultColumn struct {
|
|
|
|
// name is column name.
|
|
|
|
name string
|
|
|
|
|
|
|
|
// buf contains values data.
|
|
|
|
buf []byte
|
|
|
|
|
|
|
|
// values is the result values. They are backed by data inside buf.
|
|
|
|
values []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *resultColumn) reset() {
|
|
|
|
rc.name = ""
|
|
|
|
rc.buf = rc.buf[:0]
|
|
|
|
|
|
|
|
clear(rc.values)
|
|
|
|
rc.values = rc.values[:0]
|
|
|
|
}
|
|
|
|
|
|
|
|
// addValue adds the given values v to rc.
|
|
|
|
func (rc *resultColumn) addValue(v string) {
|
|
|
|
bufLen := len(rc.buf)
|
|
|
|
rc.buf = append(rc.buf, v...)
|
|
|
|
rc.values = append(rc.values, bytesutil.ToUnsafeString(rc.buf[bufLen:]))
|
|
|
|
}
|
|
|
|
|
2024-04-30 23:58:35 +00:00
|
|
|
var nan = math.NaN()
|