github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/aql_postprocessor.go (about) 1 // Copyright (c) 2017-2018 Uber Technologies, 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package query 16 17 // #include "time_series_aggregate.h" 18 import "C" 19 20 import ( 21 memCom "github.com/uber/aresdb/memstore/common" 22 "github.com/uber/aresdb/memutils" 23 queryCom "github.com/uber/aresdb/query/common" 24 "github.com/uber/aresdb/query/expr" 25 "github.com/uber/aresdb/utils" 26 "unsafe" 27 ) 28 29 // Postprocess converts the internal dimension and measure vector in binary 30 // format to AQLQueryResult nested result format. It also translates enum 31 // values back to their string representations. 32 func (qc *AQLQueryContext) Postprocess() queryCom.AQLQueryResult { 33 oopkContext := qc.OOPK 34 if oopkContext.IsHLL() { 35 result, err := queryCom.NewTimeSeriesHLLResult(qc.HLLQueryResult, queryCom.HLLDataHeader) 36 if err != nil { 37 // should never be here except bug 38 qc.Error = utils.StackError(err, "failed to read hll result") 39 return nil 40 } 41 return queryCom.ComputeHLLResult(result) 42 } 43 44 result := make(queryCom.AQLQueryResult) 45 dimValues := make([]*string, len(oopkContext.Dimensions)) 46 dataTypes := make([]memCom.DataType, len(oopkContext.Dimensions)) 47 reverseDicts := make(map[int][]string) 48 dimOffsets := make(map[int][2]int) 49 50 for dimIndex, dimExpr := range oopkContext.Dimensions { 51 dimVectorIndex := oopkContext.DimensionVectorIndex[dimIndex] 52 valueOffset, nullOffset := queryCom.GetDimensionStartOffsets(oopkContext.NumDimsPerDimWidth, dimVectorIndex, oopkContext.ResultSize) 53 dimOffsets[dimIndex] = [2]int{valueOffset, nullOffset} 54 dataTypes[dimIndex], reverseDicts[dimIndex] = getDimensionDataType(dimExpr), qc.getEnumReverseDict(dimIndex, dimExpr) 55 } 56 57 var fromOffset, toOffset int 58 if qc.fromTime != nil && qc.toTime != nil { 59 _, fromOffset = qc.fromTime.Time.Zone() 60 _, toOffset = qc.toTime.Time.Zone() 61 } 62 // caches time formatted time dimension values 63 dimensionValueCache := make([]map[queryCom.TimeDimensionMeta]map[int64]string, len(oopkContext.Dimensions)) 64 for i := 0; i < oopkContext.ResultSize; i++ { 65 for dimIndex := range oopkContext.Dimensions { 66 offsets := dimOffsets[dimIndex] 67 valueOffset, nullOffset := offsets[0], offsets[1] 68 valuePtr, nullPtr := utils.MemAccess(oopkContext.dimensionVectorH, valueOffset), utils.MemAccess(oopkContext.dimensionVectorH, nullOffset) 69 70 if qc.Query.Dimensions[dimIndex].isTimeDimension() && dimensionValueCache[dimIndex] == nil { 71 dimensionValueCache[dimIndex] = make(map[queryCom.TimeDimensionMeta]map[int64]string) 72 } 73 74 var timeDimensionMeta *queryCom.TimeDimensionMeta 75 76 if qc.Query.Dimensions[dimIndex].isTimeDimension() { 77 timeDimensionMeta = &queryCom.TimeDimensionMeta{ 78 TimeBucketizer: qc.Query.Dimensions[dimIndex].TimeBucketizer, 79 TimeUnit: qc.Query.Dimensions[dimIndex].TimeUnit, 80 IsTimezoneTable: qc.timezoneTable.tableColumn != "", 81 TimeZone: qc.fixedTimezone, 82 DSTSwitchTs: qc.dstswitch, 83 FromOffset: fromOffset, 84 ToOffset: toOffset, 85 } 86 } 87 88 dimValues[dimIndex] = queryCom.ReadDimension( 89 valuePtr, nullPtr, i, dataTypes[dimIndex], reverseDicts[dimIndex], 90 timeDimensionMeta, dimensionValueCache[dimIndex]) 91 } 92 93 if qc.isNonAggregationQuery { 94 result.Append(dimValues) 95 } else { 96 measureBytes := oopkContext.MeasureBytes 97 98 // For avg aggregation function, we only need to read first 4 bytes which is the average. 99 if qc.OOPK.AggregateType == C.AGGR_AVG_FLOAT { 100 measureBytes = 4 101 } 102 103 measureValue := readMeasure( 104 utils.MemAccess(oopkContext.measureVectorH, i*oopkContext.MeasureBytes), oopkContext.Measure, 105 measureBytes) 106 107 result.Set(dimValues, measureValue) 108 } 109 } 110 111 if qc.isNonAggregationQuery { 112 headers := make([]string, len(qc.Query.Dimensions)) 113 for i, dim := range qc.Query.Dimensions { 114 headers[i] = dim.Expr 115 } 116 result.SetHeaders(headers) 117 } 118 return result 119 } 120 121 // PostprocessAsHLLData serializes the query result into HLLData format. It will also release the device memory after 122 // serialization. 123 func (qc *AQLQueryContext) PostprocessAsHLLData() ([]byte, error) { 124 oopkContext := qc.OOPK 125 if oopkContext.ResultSize == 0 { 126 return []byte{}, nil 127 } 128 129 dataTypes := make([]memCom.DataType, len(oopkContext.Dimensions)) 130 reverseDicts := make(map[int][]string) 131 132 var timeDimensions []int 133 for dimIndex, ast := range oopkContext.Dimensions { 134 dataTypes[dimIndex], reverseDicts[dimIndex] = getDimensionDataType(ast), qc.getEnumReverseDict(dimIndex, ast) 135 if qc.Query.Dimensions[dimIndex].isTimeDimension() { 136 timeDimensions = append(timeDimensions, dimIndex) 137 } 138 } 139 140 return qc.SerializeHLL(dataTypes, reverseDicts, timeDimensions) 141 } 142 143 // getEnumReverseDict returns the enum reverse dict of a ast node if it's a VarRef node, otherwise it will return 144 // a nil slice. 145 func (qc *AQLQueryContext) getEnumReverseDict(dimIndex int, expression expr.Expr) []string { 146 varRef, ok := expression.(*expr.VarRef) 147 if ok && (varRef.DataType == memCom.SmallEnum || varRef.DataType == memCom.BigEnum) { 148 return varRef.EnumReverseDict 149 } 150 151 // return validShapeUUIDs as the reverse enum dict if dimIndex match geo dimension 152 if qc.OOPK.geoIntersection != nil && qc.OOPK.geoIntersection.dimIndex == dimIndex { 153 return qc.OOPK.geoIntersection.validShapeUUIDs 154 } 155 156 return nil 157 } 158 159 // ReleaseHostResultsBuffers deletes the result buffer from host memory after postprocessing 160 func (qc *AQLQueryContext) ReleaseHostResultsBuffers() { 161 ctx := &qc.OOPK 162 memutils.HostFree(ctx.dimensionVectorH) 163 ctx.dimensionVectorH = nil 164 if ctx.measureVectorH != nil { 165 memutils.HostFree(ctx.measureVectorH) 166 ctx.measureVectorH = nil 167 } 168 169 // hllVectorD and hllDimRegIDCountD used for hll query only 170 deviceFreeAndSetNil(&ctx.hllVectorD) 171 deviceFreeAndSetNil(&ctx.hllDimRegIDCountD) 172 173 // set geoIntersection to nil 174 qc.OOPK.geoIntersection = nil 175 } 176 177 func readMeasure(measureRow unsafe.Pointer, ast expr.Expr, measureBytes int) *float64 { 178 // TODO: consider converting non-zero identity values to nil. 179 var result float64 180 if measureBytes == 4 { 181 switch ast.Type() { 182 case expr.Unsigned: 183 result = float64(*(*uint32)(measureRow)) 184 case expr.Signed, expr.Boolean: 185 result = float64(*(*int32)(measureRow)) 186 case expr.Float: 187 result = float64(*(*float32)(measureRow)) 188 default: 189 // Should never happen 190 return nil 191 } 192 } else if measureBytes == 8 { 193 switch ast.Type() { 194 case expr.Unsigned: 195 result = float64(*(*uint64)(measureRow)) 196 case expr.Signed, expr.Boolean: 197 result = float64(*(*int64)(measureRow)) 198 case expr.Float: 199 result = *(*float64)(measureRow) 200 default: 201 // Should never happen. 202 return nil 203 } 204 } else { // should never happen 205 return nil 206 } 207 208 return &result 209 }