github.com/matrixorigin/matrixone@v0.7.0/pkg/frontend/internal_executor.go (about)

     1  // Copyright 2022 Matrix Origin
     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 frontend
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  
    21  	"github.com/fagongzi/goetty/v2"
    22  	"go.uber.org/zap"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    25  	"github.com/matrixorigin/matrixone/pkg/config"
    26  	"github.com/matrixorigin/matrixone/pkg/defines"
    27  	"github.com/matrixorigin/matrixone/pkg/logutil"
    28  	ie "github.com/matrixorigin/matrixone/pkg/util/internalExecutor"
    29  	"github.com/matrixorigin/matrixone/pkg/util/trace"
    30  )
    31  
    32  const DefaultTenantMoAdmin = "sys:internal:moadmin"
    33  
    34  func applyOverride(sess *Session, opts ie.SessionOverrideOptions) {
    35  	if opts.Database != nil {
    36  		sess.SetDatabaseName(*opts.Database)
    37  	}
    38  
    39  	if opts.Username != nil {
    40  		sess.GetMysqlProtocol().SetUserName(*opts.Username)
    41  	}
    42  
    43  	if opts.IsInternal != nil {
    44  		sess.isInternal = *opts.IsInternal
    45  	}
    46  }
    47  
    48  type internalMiniExec interface {
    49  	doComQuery(requestCtx context.Context, sql string) error
    50  	SetSession(*Session)
    51  }
    52  
    53  type internalExecutor struct {
    54  	sync.Mutex
    55  	proto          *internalProtocol
    56  	executor       internalMiniExec // MySqlCmdExecutor struct impls miniExec
    57  	pu             *config.ParameterUnit
    58  	baseSessOpts   ie.SessionOverrideOptions
    59  	autoIncrCaches defines.AutoIncrCaches
    60  }
    61  
    62  func NewInternalExecutor(pu *config.ParameterUnit, autoIncrCaches defines.AutoIncrCaches) *internalExecutor {
    63  	return newIe(pu, NewMysqlCmdExecutor(), autoIncrCaches)
    64  }
    65  
    66  func newIe(pu *config.ParameterUnit, inner internalMiniExec, autoIncrCaches defines.AutoIncrCaches) *internalExecutor {
    67  	proto := &internalProtocol{result: &internalExecResult{}}
    68  	ret := &internalExecutor{
    69  		proto:          proto,
    70  		executor:       inner,
    71  		pu:             pu,
    72  		baseSessOpts:   ie.NewOptsBuilder().Finish(),
    73  		autoIncrCaches: autoIncrCaches,
    74  	}
    75  	return ret
    76  }
    77  
    78  type internalExecResult struct {
    79  	affectedRows uint64
    80  	resultSet    *MysqlResultSet
    81  	dropped      uint64
    82  	err          error
    83  }
    84  
    85  func (res *internalExecResult) Error() error {
    86  	return res.err
    87  }
    88  
    89  func (res *internalExecResult) ColumnCount() uint64 {
    90  	return res.resultSet.GetColumnCount()
    91  }
    92  
    93  func (res *internalExecResult) Column(ctx context.Context, i uint64) (name string, typ uint8, signed bool, err error) {
    94  	col, err := res.resultSet.GetColumn(ctx, i)
    95  	if err == nil {
    96  		name = col.Name()
    97  		typ = uint8(col.ColumnType())
    98  		signed = col.IsSigned()
    99  	}
   100  	return
   101  }
   102  
   103  func (res *internalExecResult) RowCount() uint64 {
   104  	return res.resultSet.GetRowCount()
   105  }
   106  
   107  func (res *internalExecResult) Row(ctx context.Context, i uint64) ([]interface{}, error) {
   108  	return res.resultSet.GetRow(ctx, i)
   109  }
   110  
   111  func (res *internalExecResult) Value(ctx context.Context, ridx uint64, cidx uint64) (interface{}, error) {
   112  	return res.resultSet.GetValue(ctx, ridx, cidx)
   113  }
   114  
   115  func (res *internalExecResult) ValueByName(ctx context.Context, ridx uint64, col string) (interface{}, error) {
   116  	return res.resultSet.GetValueByName(ctx, ridx, col)
   117  }
   118  
   119  func (res *internalExecResult) StringValueByName(ctx context.Context, ridx uint64, col string) (string, error) {
   120  	if cidx, err := res.resultSet.columnName2Index(ctx, col); err != nil {
   121  		return "", err
   122  	} else {
   123  		return res.resultSet.GetString(ctx, ridx, cidx)
   124  	}
   125  }
   126  
   127  func (res *internalExecResult) Float64ValueByName(ctx context.Context, ridx uint64, col string) (float64, error) {
   128  	if cidx, err := res.resultSet.columnName2Index(ctx, col); err != nil {
   129  		return 0.0, err
   130  	} else {
   131  		return res.resultSet.GetFloat64(ctx, ridx, cidx)
   132  	}
   133  }
   134  
   135  func (ie *internalExecutor) Exec(ctx context.Context, sql string, opts ie.SessionOverrideOptions) (err error) {
   136  	ie.Lock()
   137  	defer ie.Unlock()
   138  	sess := ie.newCmdSession(ctx, opts)
   139  	defer sess.Dispose()
   140  	ie.executor.SetSession(sess)
   141  	ie.proto.stashResult = false
   142  	return ie.executor.doComQuery(ctx, sql)
   143  }
   144  
   145  func (ie *internalExecutor) Query(ctx context.Context, sql string, opts ie.SessionOverrideOptions) ie.InternalExecResult {
   146  	ie.Lock()
   147  	defer ie.Unlock()
   148  	sess := ie.newCmdSession(ctx, opts)
   149  	defer sess.Dispose()
   150  	ie.executor.SetSession(sess)
   151  	ie.proto.stashResult = true
   152  	logutil.Info("internalExecutor new session", trace.ContextField(ctx), zap.String("session uuid", sess.uuid.String()))
   153  	err := ie.executor.doComQuery(ctx, sql)
   154  	res := ie.proto.swapOutResult()
   155  	res.err = err
   156  	return res
   157  }
   158  
   159  func (ie *internalExecutor) newCmdSession(ctx context.Context, opts ie.SessionOverrideOptions) *Session {
   160  	// Use the Mid configuration for session. We can make Mid a configuration
   161  	// param, or, compute from GuestMmuLimitation.   Lazy.
   162  	//
   163  	// XXX MPOOL
   164  	// Cannot use Mid.   Turns out we create a Session for *EVERY QUERY*
   165  	// If we preallocate anything, we will explode.
   166  	//
   167  	// Session does not have a close call.   We need a Close() call in the Exec/Query method above.
   168  	//
   169  	mp, err := mpool.NewMPool("internal_exec_cmd_session", ie.pu.SV.GuestMmuLimitation, mpool.NoFixed)
   170  	if err != nil {
   171  		logutil.Fatalf("internalExecutor cannot create mpool in newCmdSession")
   172  		panic(err)
   173  	}
   174  	sess := NewSession(ie.proto, mp, ie.pu, GSysVariables, true)
   175  	sess.SetRequestContext(ctx)
   176  
   177  	// Set AutoIncrCache for this session.
   178  	sess.SetAutoIncrCaches(ie.autoIncrCaches)
   179  
   180  	t, _ := GetTenantInfo(ctx, DefaultTenantMoAdmin)
   181  	sess.SetTenantInfo(t)
   182  	applyOverride(sess, ie.baseSessOpts)
   183  	applyOverride(sess, opts)
   184  	return sess
   185  }
   186  
   187  func (ie *internalExecutor) ApplySessionOverride(opts ie.SessionOverrideOptions) {
   188  	ie.baseSessOpts = opts
   189  }
   190  
   191  // func showCaller() {
   192  // 	pc, _, _, _ := runtime.Caller(1)
   193  // 	callFunc := runtime.FuncForPC(pc)
   194  // 	logutil.Infof("[Metric] called: %s", callFunc.Name())
   195  // }
   196  
   197  var _ MysqlProtocol = &internalProtocol{}
   198  
   199  type internalProtocol struct {
   200  	sync.Mutex
   201  	stashResult bool
   202  	result      *internalExecResult
   203  	database    string
   204  	username    string
   205  }
   206  
   207  func (ip *internalProtocol) GetCapability() uint32 {
   208  	return DefaultCapability
   209  }
   210  
   211  func (ip *internalProtocol) IsTlsEstablished() bool {
   212  	return true
   213  }
   214  
   215  func (ip *internalProtocol) SetTlsEstablished() {
   216  }
   217  
   218  func (ip *internalProtocol) HandleHandshake(ctx context.Context, payload []byte) (bool, error) {
   219  	return false, nil
   220  }
   221  
   222  func (ip *internalProtocol) GetTcpConnection() goetty.IOSession {
   223  	return nil
   224  }
   225  
   226  func (ip *internalProtocol) GetConciseProfile() string {
   227  	return "internal protocol"
   228  }
   229  
   230  func (ip *internalProtocol) GetSequenceId() uint8 {
   231  	return 0
   232  }
   233  
   234  func (ip *internalProtocol) SetSequenceID(value uint8) {
   235  }
   236  
   237  func (ip *internalProtocol) makeProfile(profileTyp profileType) {
   238  
   239  }
   240  
   241  func (ip *internalProtocol) getProfile(profileTyp profileType) string {
   242  	return ""
   243  }
   244  
   245  func (ip *internalProtocol) IsEstablished() bool {
   246  	return true
   247  }
   248  
   249  func (ip *internalProtocol) ParseExecuteData(ctx context.Context, stmt *PrepareStmt, data []byte, pos int) (names []string, vars []any, err error) {
   250  	return nil, nil, nil
   251  }
   252  
   253  func (ip *internalProtocol) SendPrepareResponse(ctx context.Context, stmt *PrepareStmt) error {
   254  	return nil
   255  }
   256  
   257  func (ip *internalProtocol) SetEstablished() {}
   258  
   259  func (ip *internalProtocol) GetRequest(payload []byte) *Request {
   260  	panic("not impl")
   261  }
   262  
   263  // ConnectionID the identity of the client
   264  func (ip *internalProtocol) ConnectionID() uint32 {
   265  	return 74751101
   266  }
   267  
   268  // Peer gets the address [Host:Port] of the client
   269  func (ip *internalProtocol) Peer() (string, string, string, string) {
   270  	panic("not impl")
   271  }
   272  
   273  func (ip *internalProtocol) GetDatabaseName() string {
   274  	return ip.database
   275  }
   276  
   277  func (ip *internalProtocol) SetDatabaseName(database string) {
   278  	ip.database = database
   279  }
   280  
   281  func (ip *internalProtocol) GetUserName() string {
   282  	return ip.username
   283  }
   284  
   285  func (ip *internalProtocol) SetUserName(username string) {
   286  	ip.username = username
   287  }
   288  
   289  func (ip *internalProtocol) Quit() {}
   290  
   291  func (ip *internalProtocol) sendRows(mrs *MysqlResultSet, cnt uint64) error {
   292  	if ip.stashResult {
   293  		res := ip.result.resultSet
   294  		if res == nil {
   295  			res = &MysqlResultSet{}
   296  			ip.result.resultSet = res
   297  		}
   298  
   299  		if res.GetRowCount() > 100 {
   300  			ip.result.dropped += cnt
   301  			return nil
   302  		}
   303  
   304  		if res.GetColumnCount() == 0 {
   305  			for _, col := range mrs.Columns {
   306  				res.AddColumn(col)
   307  			}
   308  		}
   309  		colCnt := res.GetColumnCount()
   310  		for i := uint64(0); i < cnt; i++ {
   311  			row := make([]any, colCnt)
   312  			copy(row, mrs.Data[i])
   313  			res.Data = append(res.Data, row)
   314  		}
   315  	}
   316  
   317  	ip.result.affectedRows += cnt
   318  	return nil
   319  }
   320  
   321  func (ip *internalProtocol) swapOutResult() *internalExecResult {
   322  	ret := ip.result
   323  	if ret.resultSet == nil {
   324  		ret.resultSet = &MysqlResultSet{}
   325  	}
   326  	ip.result = &internalExecResult{}
   327  	return ret
   328  }
   329  
   330  // the server send group row of the result set as an independent packet thread safe
   331  func (ip *internalProtocol) SendResultSetTextBatchRow(mrs *MysqlResultSet, cnt uint64) error {
   332  	ip.Lock()
   333  	defer ip.Unlock()
   334  	return ip.sendRows(mrs, cnt)
   335  }
   336  
   337  func (ip *internalProtocol) SendResultSetTextBatchRowSpeedup(mrs *MysqlResultSet, cnt uint64) error {
   338  	ip.Lock()
   339  	defer ip.Unlock()
   340  	return ip.sendRows(mrs, cnt)
   341  }
   342  
   343  // SendColumnDefinitionPacket the server send the column definition to the client
   344  func (ip *internalProtocol) SendColumnDefinitionPacket(ctx context.Context, column Column, cmd int) error {
   345  	return nil
   346  }
   347  
   348  // SendColumnCountPacket makes the column count packet
   349  func (ip *internalProtocol) SendColumnCountPacket(count uint64) error {
   350  	return nil
   351  }
   352  
   353  // SendResponse sends a response to the client for the application request
   354  func (ip *internalProtocol) SendResponse(ctx context.Context, resp *Response) error {
   355  	ip.Lock()
   356  	defer ip.Unlock()
   357  	ip.ResetStatistics()
   358  	if resp.category == ResultResponse {
   359  		if mer := resp.data.(*MysqlExecutionResult); mer != nil && mer.Mrs() != nil {
   360  			ip.sendRows(mer.Mrs(), mer.mrs.GetRowCount())
   361  		}
   362  	} else {
   363  		// OkResponse. this is NOT ErrorResponse because error will be returned by doComQuery
   364  		ip.result.affectedRows = resp.affectedRows
   365  	}
   366  	return nil
   367  }
   368  
   369  // SendEOFPacketIf ends the sending of columns definations
   370  func (ip *internalProtocol) SendEOFPacketIf(warnings uint16, status uint16) error {
   371  	return nil
   372  }
   373  
   374  // sendOKPacket sends OK packet to the client, used in the end of sql like use <database>
   375  func (ip *internalProtocol) sendOKPacket(affectedRows uint64, lastInsertId uint64, status uint16, warnings uint16, message string) error {
   376  	ip.result.affectedRows = affectedRows
   377  	return nil
   378  }
   379  
   380  // sendEOFOrOkPacket sends the OK or EOF packet thread safe, and ends the sending of result set
   381  func (ip *internalProtocol) sendEOFOrOkPacket(warnings uint16, status uint16) error {
   382  	return nil
   383  }
   384  
   385  func (ip *internalProtocol) ResetStatistics() {
   386  	ip.result.affectedRows = 0
   387  	ip.result.dropped = 0
   388  	ip.result.err = nil
   389  	ip.result.resultSet = nil
   390  }
   391  
   392  func (ip *internalProtocol) GetStats() string { return "internal unknown stats" }
   393  
   394  func (ip *internalProtocol) sendLocalInfileRequest(filename string) error {
   395  	return nil
   396  }