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  }