github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/memristed/memex/expr_to_pb.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 memex 15 16 import ( 17 "github.com/gogo/protobuf/proto" 18 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 19 "github.com/whtcorpsinc/BerolinaSQL/terror" 20 "github.com/whtcorpsinc/errors" 21 "github.com/whtcorpsinc/failpoint" 22 "github.com/whtcorpsinc/fidelpb/go-fidelpb" 23 "github.com/whtcorpsinc/milevadb/ekv" 24 "github.com/whtcorpsinc/milevadb/soliton/chunk" 25 "github.com/whtcorpsinc/milevadb/soliton/codec" 26 "github.com/whtcorpsinc/milevadb/soliton/defCauslate" 27 "github.com/whtcorpsinc/milevadb/soliton/logutil" 28 "github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx" 29 "github.com/whtcorpsinc/milevadb/types" 30 "go.uber.org/zap" 31 ) 32 33 // ExpressionsToPBList converts memexs to fidelpb.Expr list for new plan. 34 func ExpressionsToPBList(sc *stmtctx.StatementContext, exprs []Expression, client ekv.Client) (pbExpr []*fidelpb.Expr, err error) { 35 pc := PbConverter{client: client, sc: sc} 36 for _, expr := range exprs { 37 v := pc.ExprToPB(expr) 38 if v == nil { 39 return nil, terror.ClassOptimizer.New(allegrosql.ErrInternal, allegrosql.MyALLEGROSQLErrName[allegrosql.ErrInternal]). 40 GenWithStack("memex %v cannot be pushed down", expr) 41 } 42 pbExpr = append(pbExpr, v) 43 } 44 return 45 } 46 47 // PbConverter supplys methods to convert MilevaDB memexs to FIDelpb. 48 type PbConverter struct { 49 client ekv.Client 50 sc *stmtctx.StatementContext 51 } 52 53 // NewPBConverter creates a PbConverter. 54 func NewPBConverter(client ekv.Client, sc *stmtctx.StatementContext) PbConverter { 55 return PbConverter{client: client, sc: sc} 56 } 57 58 // ExprToPB converts Expression to FIDelpb. 59 func (pc PbConverter) ExprToPB(expr Expression) *fidelpb.Expr { 60 switch x := expr.(type) { 61 case *Constant: 62 pbExpr := pc.conOrCorDefCausToPBExpr(expr) 63 if pbExpr == nil { 64 return nil 65 } 66 if !x.Value.IsNull() { 67 pbExpr.FieldType.Flag |= uint32(allegrosql.NotNullFlag) 68 } 69 return pbExpr 70 case *CorrelatedDeferredCauset: 71 return pc.conOrCorDefCausToPBExpr(expr) 72 case *DeferredCauset: 73 return pc.defCausumnToPBExpr(x) 74 case *ScalarFunction: 75 return pc.scalarFuncToPBExpr(x) 76 } 77 return nil 78 } 79 80 func (pc PbConverter) conOrCorDefCausToPBExpr(expr Expression) *fidelpb.Expr { 81 ft := expr.GetType() 82 d, err := expr.Eval(chunk.Event{}) 83 if err != nil { 84 logutil.BgLogger().Error("eval constant or correlated defCausumn", zap.String("memex", expr.ExplainInfo()), zap.Error(err)) 85 return nil 86 } 87 tp, val, ok := pc.encodeCauset(ft, d) 88 if !ok { 89 return nil 90 } 91 92 if !pc.client.IsRequestTypeSupported(ekv.ReqTypeSelect, int64(tp)) { 93 return nil 94 } 95 return &fidelpb.Expr{Tp: tp, Val: val, FieldType: ToPBFieldType(ft)} 96 } 97 98 func (pc *PbConverter) encodeCauset(ft *types.FieldType, d types.Causet) (fidelpb.ExprType, []byte, bool) { 99 var ( 100 tp fidelpb.ExprType 101 val []byte 102 ) 103 switch d.HoTT() { 104 case types.HoTTNull: 105 tp = fidelpb.ExprType_Null 106 case types.HoTTInt64: 107 tp = fidelpb.ExprType_Int64 108 val = codec.EncodeInt(nil, d.GetInt64()) 109 case types.HoTTUint64: 110 tp = fidelpb.ExprType_Uint64 111 val = codec.EncodeUint(nil, d.GetUint64()) 112 case types.HoTTString, types.HoTTBinaryLiteral: 113 tp = fidelpb.ExprType_String 114 val = d.GetBytes() 115 case types.HoTTBytes: 116 tp = fidelpb.ExprType_Bytes 117 val = d.GetBytes() 118 case types.HoTTFloat32: 119 tp = fidelpb.ExprType_Float32 120 val = codec.EncodeFloat(nil, d.GetFloat64()) 121 case types.HoTTFloat64: 122 tp = fidelpb.ExprType_Float64 123 val = codec.EncodeFloat(nil, d.GetFloat64()) 124 case types.HoTTMysqlDuration: 125 tp = fidelpb.ExprType_MysqlDuration 126 val = codec.EncodeInt(nil, int64(d.GetMysqlDuration().Duration)) 127 case types.HoTTMysqlDecimal: 128 tp = fidelpb.ExprType_MysqlDecimal 129 var err error 130 val, err = codec.EncodeDecimal(nil, d.GetMysqlDecimal(), d.Length(), d.Frac()) 131 if err != nil { 132 logutil.BgLogger().Error("encode decimal", zap.Error(err)) 133 return tp, nil, false 134 } 135 case types.HoTTMysqlTime: 136 if pc.client.IsRequestTypeSupported(ekv.ReqTypePosetDag, int64(fidelpb.ExprType_MysqlTime)) { 137 tp = fidelpb.ExprType_MysqlTime 138 val, err := codec.EncodeMyALLEGROSQLTime(pc.sc, d.GetMysqlTime(), ft.Tp, nil) 139 if err != nil { 140 logutil.BgLogger().Error("encode allegrosql time", zap.Error(err)) 141 return tp, nil, false 142 } 143 return tp, val, true 144 } 145 return tp, nil, false 146 default: 147 return tp, nil, false 148 } 149 return tp, val, true 150 } 151 152 // ToPBFieldType converts *types.FieldType to *fidelpb.FieldType. 153 func ToPBFieldType(ft *types.FieldType) *fidelpb.FieldType { 154 return &fidelpb.FieldType{ 155 Tp: int32(ft.Tp), 156 Flag: uint32(ft.Flag), 157 Flen: int32(ft.Flen), 158 Decimal: int32(ft.Decimal), 159 Charset: ft.Charset, 160 DefCauslate: defCauslationToProto(ft.DefCauslate), 161 } 162 } 163 164 // FieldTypeFromPB converts *fidelpb.FieldType to *types.FieldType. 165 func FieldTypeFromPB(ft *fidelpb.FieldType) *types.FieldType { 166 return &types.FieldType{ 167 Tp: byte(ft.Tp), 168 Flag: uint(ft.Flag), 169 Flen: int(ft.Flen), 170 Decimal: int(ft.Decimal), 171 Charset: ft.Charset, 172 DefCauslate: protoToDefCauslation(ft.DefCauslate), 173 } 174 } 175 176 func defCauslationToProto(c string) int32 { 177 if v, ok := allegrosql.DefCauslationNames[c]; ok { 178 return defCauslate.RewriteNewDefCauslationIDIfNeeded(int32(v)) 179 } 180 v := defCauslate.RewriteNewDefCauslationIDIfNeeded(int32(allegrosql.DefaultDefCauslationID)) 181 logutil.BgLogger().Warn( 182 "Unable to get defCauslation ID by name, use ID of the default defCauslation instead", 183 zap.String("name", c), 184 zap.Int32("default defCauslation ID", v), 185 zap.String("default defCauslation", allegrosql.DefaultDefCauslationName), 186 ) 187 return v 188 } 189 190 func protoToDefCauslation(c int32) string { 191 v, ok := allegrosql.DefCauslations[uint8(defCauslate.RestoreDefCauslationIDIfNeeded(c))] 192 if ok { 193 return v 194 } 195 logutil.BgLogger().Warn( 196 "Unable to get defCauslation name from ID, use name of the default defCauslation instead", 197 zap.Int32("id", c), 198 zap.Int("default defCauslation ID", allegrosql.DefaultDefCauslationID), 199 zap.String("default defCauslation", allegrosql.DefaultDefCauslationName), 200 ) 201 return allegrosql.DefaultDefCauslationName 202 } 203 204 func (pc PbConverter) defCausumnToPBExpr(defCausumn *DeferredCauset) *fidelpb.Expr { 205 if !pc.client.IsRequestTypeSupported(ekv.ReqTypeSelect, int64(fidelpb.ExprType_DeferredCausetRef)) { 206 return nil 207 } 208 switch defCausumn.GetType().Tp { 209 case allegrosql.TypeBit, allegrosql.TypeSet, allegrosql.TypeEnum, allegrosql.TypeGeometry, allegrosql.TypeUnspecified: 210 return nil 211 } 212 213 if pc.client.IsRequestTypeSupported(ekv.ReqTypePosetDag, ekv.ReqSubTypeBasic) { 214 return &fidelpb.Expr{ 215 Tp: fidelpb.ExprType_DeferredCausetRef, 216 Val: codec.EncodeInt(nil, int64(defCausumn.Index)), 217 FieldType: ToPBFieldType(defCausumn.RetType), 218 } 219 } 220 id := defCausumn.ID 221 // Zero DeferredCauset ID is not a defCausumn from causet, can not support for now. 222 if id == 0 || id == -1 { 223 return nil 224 } 225 226 return &fidelpb.Expr{ 227 Tp: fidelpb.ExprType_DeferredCausetRef, 228 Val: codec.EncodeInt(nil, id)} 229 } 230 231 func (pc PbConverter) scalarFuncToPBExpr(expr *ScalarFunction) *fidelpb.Expr { 232 // Check whether this function has ProtoBuf signature. 233 pbCode := expr.Function.PbCode() 234 if pbCode <= fidelpb.ScalarFuncSig_Unspecified { 235 failpoint.Inject("PanicIfPbCodeUnspecified", func() { 236 panic(errors.Errorf("unspecified PbCode: %T", expr.Function)) 237 }) 238 return nil 239 } 240 241 // Check whether this function can be pushed. 242 if !canFuncBePushed(expr, ekv.UnSpecified) { 243 return nil 244 } 245 246 // Check whether all of its parameters can be pushed. 247 children := make([]*fidelpb.Expr, 0, len(expr.GetArgs())) 248 for _, arg := range expr.GetArgs() { 249 pbArg := pc.ExprToPB(arg) 250 if pbArg == nil { 251 return nil 252 } 253 children = append(children, pbArg) 254 } 255 256 var encoded []byte 257 if spacetimedata := expr.Function.spacetimedata(); spacetimedata != nil { 258 var err error 259 encoded, err = proto.Marshal(spacetimedata) 260 if err != nil { 261 logutil.BgLogger().Error("encode spacetimedata", zap.Any("spacetimedata", spacetimedata), zap.Error(err)) 262 return nil 263 } 264 } 265 266 // put defCauslation information into the RetType enforcedly and push it down to EinsteinDB/MockEinsteinDB 267 tp := *expr.RetType 268 if defCauslate.NewDefCauslationEnabled() { 269 _, tp.DefCauslate = expr.CharsetAndDefCauslation(expr.GetCtx()) 270 } 271 272 // Construct memex ProtoBuf. 273 return &fidelpb.Expr{ 274 Tp: fidelpb.ExprType_ScalarFunc, 275 Val: encoded, 276 Sig: pbCode, 277 Children: children, 278 FieldType: ToPBFieldType(&tp), 279 } 280 } 281 282 // GroupByItemToPB converts group by items to pb. 283 func GroupByItemToPB(sc *stmtctx.StatementContext, client ekv.Client, expr Expression) *fidelpb.ByItem { 284 pc := PbConverter{client: client, sc: sc} 285 e := pc.ExprToPB(expr) 286 if e == nil { 287 return nil 288 } 289 return &fidelpb.ByItem{Expr: e} 290 } 291 292 // SortByItemToPB converts order by items to pb. 293 func SortByItemToPB(sc *stmtctx.StatementContext, client ekv.Client, expr Expression, desc bool) *fidelpb.ByItem { 294 pc := PbConverter{client: client, sc: sc} 295 e := pc.ExprToPB(expr) 296 if e == nil { 297 return nil 298 } 299 return &fidelpb.ByItem{Expr: e, Desc: desc} 300 }