github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/txn.go (about)

     1  // Copyright 2021 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  	"errors"
    20  	"sync"
    21  
    22  	"github.com/google/uuid"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/clusterservice"
    25  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    26  	"github.com/matrixorigin/matrixone/pkg/logutil"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    28  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine/memoryengine"
    30  
    31  	"go.uber.org/zap"
    32  
    33  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    34  	moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime"
    35  	"github.com/matrixorigin/matrixone/pkg/defines"
    36  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    37  	"github.com/matrixorigin/matrixone/pkg/txn/storage/memorystorage"
    38  	"github.com/matrixorigin/matrixone/pkg/util/metric"
    39  	"github.com/matrixorigin/matrixone/pkg/util/trace"
    40  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    41  )
    42  
    43  var (
    44  	dumpUUID = uuid.UUID{}
    45  )
    46  
    47  // get errors during the transaction. rollback the transaction
    48  func rollbackTxnFunc(ses FeSession, execErr error, execCtx *ExecCtx) error {
    49  	incStatementErrorsCounter(execCtx.tenant, execCtx.stmt)
    50  	/*
    51  		Cases    | set Autocommit = 1/0 | BEGIN statement |
    52  		---------------------------------------------------
    53  		Case1      1                       Yes
    54  		Case2      1                       No
    55  		Case3      0                       Yes
    56  		Case4      0                       No
    57  		---------------------------------------------------
    58  		update error message in Case1,Case3,Case4.
    59  	*/
    60  	if ses.GetTxnHandler().InMultiStmtTransactionMode() && ses.GetTxnHandler().InActiveTxn() {
    61  		ses.cleanCache()
    62  	}
    63  	logError(ses, ses.GetDebugString(), execErr.Error())
    64  	execCtx.txnOpt.byRollback = execCtx.txnOpt.byRollback || isErrorRollbackWholeTxn(execErr)
    65  	txnErr := ses.GetTxnHandler().Rollback(execCtx)
    66  	if txnErr != nil {
    67  		logStatementStatus(execCtx.reqCtx, ses, execCtx.stmt, fail, txnErr)
    68  		return txnErr
    69  	}
    70  	logStatementStatus(execCtx.reqCtx, ses, execCtx.stmt, fail, execErr)
    71  	return execErr
    72  }
    73  
    74  // execution succeeds during the transaction. commit the transaction
    75  func commitTxnFunc(ses FeSession,
    76  	execCtx *ExecCtx) (retErr error) {
    77  	// Call a defer function -- if TxnCommitSingleStatement paniced, we
    78  	// want to catch it and convert it to an error.
    79  	defer func() {
    80  		if r := recover(); r != nil {
    81  			retErr = moerr.ConvertPanicError(execCtx.reqCtx, r)
    82  		}
    83  	}()
    84  
    85  	//load data handle txn failure internally
    86  	retErr = ses.GetTxnHandler().Commit(execCtx)
    87  	if retErr != nil {
    88  		logStatementStatus(execCtx.reqCtx, ses, execCtx.stmt, fail, retErr)
    89  	}
    90  	return
    91  }
    92  
    93  // finish the transaction
    94  func finishTxnFunc(ses FeSession, execErr error, execCtx *ExecCtx) (err error) {
    95  	// First recover all panics.   If paniced, we will abort.
    96  	if r := recover(); r != nil {
    97  		recoverErr := moerr.ConvertPanicError(execCtx.reqCtx, r)
    98  		logError(ses, ses.GetDebugString(), "recover from panic", zap.Error(recoverErr), zap.Error(execErr))
    99  	}
   100  
   101  	if execCtx.txnOpt.byCommit {
   102  		//commit the txn by the COMMIT statement
   103  		err = ses.GetTxnHandler().Commit(execCtx)
   104  		if err != nil {
   105  			logStatementStatus(execCtx.reqCtx, ses, execCtx.stmt, fail, err)
   106  		}
   107  	} else if execCtx.txnOpt.byRollback {
   108  		//roll back the txn by the ROLLBACK statement
   109  		err = ses.GetTxnHandler().Rollback(execCtx)
   110  		if err != nil {
   111  			logStatementStatus(execCtx.reqCtx, ses, execCtx.stmt, fail, err)
   112  			return err
   113  		}
   114  	} else {
   115  		if execErr == nil {
   116  			err = commitTxnFunc(ses, execCtx)
   117  			if err == nil {
   118  				return err
   119  			}
   120  			// if commitTxnFunc failed, we will roll back the transaction.
   121  			execErr = err
   122  		}
   123  
   124  		return rollbackTxnFunc(ses, execErr, execCtx)
   125  	}
   126  	return
   127  }
   128  
   129  type FeTxnOption struct {
   130  	//byBegin denotes the txn started by the BEGIN stmt
   131  	byBegin bool
   132  	//autoCommit the variable AUTOCOMMIT is enabled
   133  	autoCommit bool
   134  	//byCommit denotes the txn committed by the COMMIT
   135  	byCommit bool
   136  	//byRollback denotes the txn rolled back by the ROLLBACK.
   137  	//or error types that need to roll back the whole txn.
   138  	byRollback bool
   139  }
   140  
   141  const (
   142  	defaultServerStatus uint32 = uint32(SERVER_STATUS_AUTOCOMMIT)
   143  	defaultOptionBits   uint32 = OPTION_AUTOCOMMIT
   144  )
   145  
   146  type TxnHandler struct {
   147  	mu sync.Mutex
   148  
   149  	storage       engine.Engine
   150  	tempStorage   *memorystorage.Storage
   151  	tempTnService *metadata.TNService
   152  	tempEngine    *memoryengine.Engine
   153  	txnOp         TxnOperator
   154  
   155  	//connCtx is the ancestor of the txnCtx.
   156  	//it is initialized at the TxnHandler object created and
   157  	//exists always.
   158  	//it starts from the routineCtx.
   159  	connCtx context.Context
   160  
   161  	// it is for the transaction and different from the requestCtx.
   162  	// it is created before the transaction is started and
   163  	// is not released after the transaction is commit or rollback.
   164  	// the lifetime of txnCtx is longer than the requestCtx and
   165  	// the same as the connCtx.
   166  	// it inherits the connCtx.
   167  	// it can not be canceled at the KillQuery
   168  	txnCtx       context.Context
   169  	txnCtxCancel context.CancelFunc
   170  
   171  	shareTxn bool
   172  
   173  	//the server status
   174  	serverStatus uint32
   175  
   176  	//the option bits
   177  	optionBits uint32
   178  }
   179  
   180  func InitTxnHandler(storage engine.Engine, connCtx context.Context, txnOp TxnOperator) *TxnHandler {
   181  	ret := &TxnHandler{
   182  		storage:      &engine.EntireEngine{Engine: storage},
   183  		connCtx:      connCtx,
   184  		txnOp:        txnOp,
   185  		shareTxn:     txnOp != nil,
   186  		serverStatus: defaultServerStatus,
   187  		optionBits:   defaultOptionBits,
   188  	}
   189  	ret.txnCtx, ret.txnCtxCancel = context.WithCancel(connCtx)
   190  	return ret
   191  }
   192  
   193  func (th *TxnHandler) Close() {
   194  	th.mu.Lock()
   195  	defer th.mu.Unlock()
   196  	th.storage = nil
   197  	th.tempStorage = nil
   198  	th.tempTnService = nil
   199  	th.tempEngine = nil
   200  	th.txnOp = nil
   201  	th.connCtx = nil
   202  	if th.txnCtxCancel != nil {
   203  		th.txnCtxCancel()
   204  	}
   205  	th.txnCtx = nil
   206  }
   207  
   208  func (th *TxnHandler) GetConnCtx() context.Context {
   209  	th.mu.Lock()
   210  	defer th.mu.Unlock()
   211  	return th.connCtx
   212  }
   213  
   214  func (th *TxnHandler) GetTxnCtx() context.Context {
   215  	th.mu.Lock()
   216  	defer th.mu.Unlock()
   217  	return th.txnCtx
   218  }
   219  
   220  // invalidateTxnUnsafe releases the txnOp and clears the server status bit SERVER_STATUS_IN_TRANS
   221  func (th *TxnHandler) invalidateTxnUnsafe() {
   222  	th.txnOp = nil
   223  	resetBits(&th.serverStatus, defaultServerStatus)
   224  	resetBits(&th.optionBits, defaultOptionBits)
   225  }
   226  
   227  func (th *TxnHandler) InActiveTxn() bool {
   228  	th.mu.Lock()
   229  	defer th.mu.Unlock()
   230  	return th.inActiveTxnUnsafe()
   231  }
   232  
   233  // inActiveTxnUnsafe can not be used outside the TxnHandler.
   234  // refresh server status also
   235  func (th *TxnHandler) inActiveTxnUnsafe() bool {
   236  	if th.txnOp != nil && th.txnCtx == nil {
   237  		panic("txnOp != nil and txnCtx == nil")
   238  	}
   239  	return th.txnOp != nil && th.txnCtx != nil
   240  }
   241  
   242  // Create starts a new txn.
   243  // option bits decide the actual behaviour
   244  func (th *TxnHandler) Create(execCtx *ExecCtx) error {
   245  	var err error
   246  	th.mu.Lock()
   247  	defer th.mu.Unlock()
   248  
   249  	// check BEGIN stmt
   250  	if execCtx.txnOpt.byBegin || !th.inActiveTxnUnsafe() {
   251  		//commit existed txn anyway
   252  		err = th.createUnsafe(execCtx)
   253  		if err != nil {
   254  			return err
   255  		}
   256  		resetBits(&th.serverStatus, defaultServerStatus)
   257  		resetBits(&th.optionBits, defaultOptionBits)
   258  		setBits(&th.serverStatus, uint32(SERVER_STATUS_IN_TRANS))
   259  
   260  		if execCtx.txnOpt.byBegin {
   261  			setBits(&th.optionBits, OPTION_BEGIN)
   262  		} else {
   263  			clearBits(&th.optionBits, OPTION_BEGIN)
   264  		}
   265  
   266  		if execCtx.txnOpt.autoCommit {
   267  			clearBits(&th.optionBits, OPTION_NOT_AUTOCOMMIT)
   268  			setBits(&th.serverStatus, uint32(SERVER_STATUS_AUTOCOMMIT))
   269  		} else {
   270  			setBits(&th.optionBits, OPTION_NOT_AUTOCOMMIT)
   271  			clearBits(&th.serverStatus, uint32(SERVER_STATUS_AUTOCOMMIT))
   272  		}
   273  	}
   274  	return nil
   275  }
   276  
   277  // starts a new txn.
   278  // if there is a txn existed, commit it before creating a new one.
   279  func (th *TxnHandler) createUnsafe(execCtx *ExecCtx) error {
   280  	var err error
   281  	defer th.inActiveTxnUnsafe()
   282  	if th.shareTxn {
   283  		return moerr.NewInternalError(execCtx.reqCtx, "NewTxn: the share txn is not allowed to create new txn")
   284  	}
   285  
   286  	//in active txn
   287  	//commit existed txn first
   288  	err = th.commitUnsafe(execCtx)
   289  	if err != nil {
   290  		/*
   291  			fix issue 6024.
   292  			When we get a w-w conflict during commit the txn,
   293  			we convert the error into a readable error.
   294  		*/
   295  		if moerr.IsMoErrCode(err, moerr.ErrTxnWWConflict) {
   296  			return moerr.NewInternalError(execCtx.reqCtx, writeWriteConflictsErrorInfo())
   297  		}
   298  		return err
   299  	}
   300  
   301  	defer func() {
   302  		if err != nil {
   303  			tenant := execCtx.tenant
   304  			incTransactionErrorsCounter(tenant, metric.SQLTypeBegin)
   305  		}
   306  	}()
   307  	err = th.createTxnOpUnsafe(execCtx)
   308  	if err != nil {
   309  		return err
   310  	}
   311  	if th.txnCtx == nil {
   312  		panic("context should not be nil")
   313  	}
   314  	var accId uint32
   315  	accId, err = defines.GetAccountId(execCtx.reqCtx)
   316  	if err != nil {
   317  		return err
   318  	}
   319  	tempCtx := defines.AttachAccountId(th.txnCtx, accId)
   320  	err = th.storage.New(tempCtx, th.txnOp)
   321  	if err != nil {
   322  		execCtx.ses.SetTxnId(dumpUUID[:])
   323  	} else {
   324  		execCtx.ses.SetTxnId(th.txnOp.Txn().ID)
   325  	}
   326  	return err
   327  }
   328  
   329  // createTxnOpUnsafe creates a new txn operator using TxnClient. Should not be called outside txn
   330  func (th *TxnHandler) createTxnOpUnsafe(execCtx *ExecCtx) error {
   331  	var err error
   332  	if getGlobalPu().TxnClient == nil {
   333  		panic("must set txn client")
   334  	}
   335  
   336  	if th.shareTxn {
   337  		return moerr.NewInternalError(execCtx.reqCtx, "NewTxnOperator: the share txn is not allowed to create new txn")
   338  	}
   339  
   340  	var opts []client.TxnOption
   341  	rt := moruntime.ProcessLevelRuntime()
   342  	if rt != nil {
   343  		if v, ok := rt.GetGlobalVariables(moruntime.TxnOptions); ok {
   344  			opts = v.([]client.TxnOption)
   345  		}
   346  	}
   347  	if th.txnCtx == nil {
   348  		panic("context should not be nil")
   349  	}
   350  
   351  	accountID := uint32(0)
   352  	userName := ""
   353  	connectionID := uint32(0)
   354  	if execCtx.proto != nil {
   355  		connectionID = execCtx.proto.ConnectionID()
   356  	}
   357  	if execCtx.ses.GetTenantInfo() != nil {
   358  		accountID = execCtx.ses.GetTenantInfo().TenantID
   359  		userName = execCtx.ses.GetTenantInfo().User
   360  	}
   361  	sessionInfo := execCtx.ses.GetDebugString()
   362  	opts = append(opts,
   363  		client.WithTxnCreateBy(
   364  			accountID,
   365  			userName,
   366  			execCtx.ses.GetUUIDString(),
   367  			connectionID),
   368  		client.WithSessionInfo(sessionInfo))
   369  
   370  	if execCtx.ses.GetFromRealUser() {
   371  		opts = append(opts,
   372  			client.WithUserTxn())
   373  	}
   374  
   375  	if execCtx.ses.IsBackgroundSession() ||
   376  		execCtx.ses.DisableTrace() {
   377  		opts = append(opts, client.WithDisableTrace(true))
   378  	} else {
   379  		varVal, err := execCtx.ses.GetSessionVar(execCtx.reqCtx, "disable_txn_trace")
   380  		if err != nil {
   381  			return err
   382  		}
   383  		if gsv, ok := GSysVariables.GetDefinitionOfSysVar("disable_txn_trace"); ok {
   384  			if svbt, ok2 := gsv.GetType().(SystemVariableBoolType); ok2 {
   385  				if svbt.IsTrue(varVal) {
   386  					opts = append(opts, client.WithDisableTrace(true))
   387  				}
   388  			}
   389  		}
   390  	}
   391  
   392  	th.txnOp, err = getGlobalPu().TxnClient.New(
   393  		th.txnCtx,
   394  		execCtx.ses.getLastCommitTS(),
   395  		opts...)
   396  	if err != nil {
   397  		return err
   398  	}
   399  	if th.txnOp == nil {
   400  		return moerr.NewInternalError(execCtx.reqCtx, "NewTxnOperator: txnClient new a null txn")
   401  	}
   402  	return err
   403  }
   404  
   405  func (th *TxnHandler) GetTxn() TxnOperator {
   406  	th.mu.Lock()
   407  	defer th.mu.Unlock()
   408  	return th.txnOp
   409  }
   410  
   411  // Commit commits the txn.
   412  // option bits decide the actual commit behaviour
   413  func (th *TxnHandler) Commit(execCtx *ExecCtx) error {
   414  	var err error
   415  	th.mu.Lock()
   416  	defer th.mu.Unlock()
   417  	/*
   418  		Commit Rules:
   419  		1, if it is in single-statement mode:
   420  			it commits.
   421  		2, if it is in multi-statement mode:
   422  			if the statement is the one can be executed in the active transaction,
   423  				the transaction need to be committed at the end of the statement.
   424  	*/
   425  	if !bitsIsSet(th.optionBits, OPTION_BEGIN|OPTION_NOT_AUTOCOMMIT) ||
   426  		th.inActiveTxnUnsafe() && NeedToBeCommittedInActiveTransaction(execCtx.stmt) ||
   427  		execCtx.txnOpt.byCommit {
   428  		err = th.commitUnsafe(execCtx)
   429  		if err != nil {
   430  			return err
   431  		}
   432  	}
   433  	//do nothing
   434  	return nil
   435  }
   436  
   437  func (th *TxnHandler) commitUnsafe(execCtx *ExecCtx) error {
   438  	_, span := trace.Start(execCtx.reqCtx, "TxnHandler.CommitTxn",
   439  		trace.WithKind(trace.SpanKindStatement))
   440  	defer span.End(trace.WithStatementExtra(execCtx.ses.GetTxnId(), execCtx.ses.GetStmtId(), execCtx.ses.GetSqlOfStmt()))
   441  	var err error
   442  	defer th.inActiveTxnUnsafe()
   443  	if !th.inActiveTxnUnsafe() || th.shareTxn {
   444  		return nil
   445  	}
   446  	sessionInfo := execCtx.ses.GetDebugString()
   447  	if th.txnOp == nil {
   448  		th.invalidateTxnUnsafe()
   449  	}
   450  	if th.txnCtx == nil {
   451  		panic("context should not be nil")
   452  	}
   453  	if th.hasTempEngineUnsafe() && th.tempStorage != nil {
   454  		if th.txnCtx.Value(defines.TemporaryTN{}) == nil {
   455  			th.txnCtx = context.WithValue(th.txnCtx, defines.TemporaryTN{}, th.tempStorage)
   456  		}
   457  	}
   458  	storage := th.storage
   459  	ctx2, cancel := context.WithTimeout(
   460  		th.txnCtx,
   461  		storage.Hints().CommitOrRollbackTimeout,
   462  	)
   463  	defer cancel()
   464  	val, e := execCtx.ses.GetSessionVar(execCtx.reqCtx, "mo_pk_check_by_dn")
   465  	if e != nil {
   466  		return e
   467  	}
   468  	if val != nil {
   469  		ctx2 = context.WithValue(ctx2, defines.PkCheckByTN{}, val.(int8))
   470  	}
   471  	defer func() {
   472  		// metric count
   473  		tenant := execCtx.ses.GetTenantName()
   474  		incTransactionCounter(tenant)
   475  		if err != nil {
   476  			incTransactionErrorsCounter(tenant, metric.SQLTypeCommit)
   477  		}
   478  	}()
   479  
   480  	if logutil.GetSkip1Logger().Core().Enabled(zap.DebugLevel) {
   481  		txnId := th.txnOp.Txn().DebugString()
   482  		logDebugf(sessionInfo, "CommitTxn txnId:%s", txnId)
   483  		defer func() {
   484  			logDebugf(sessionInfo, "CommitTxn exit txnId:%s", txnId)
   485  		}()
   486  	}
   487  	if th.txnOp != nil {
   488  		commitTs := th.txnOp.Txn().CommitTS
   489  		execCtx.ses.SetTxnId(th.txnOp.Txn().ID)
   490  		err = th.txnOp.Commit(ctx2)
   491  		if err != nil {
   492  			th.invalidateTxnUnsafe()
   493  		}
   494  		execCtx.ses.updateLastCommitTS(commitTs)
   495  	}
   496  	th.invalidateTxnUnsafe()
   497  	execCtx.ses.SetTxnId(dumpUUID[:])
   498  	return err
   499  }
   500  
   501  // Rollback rolls back the txn
   502  // the option bits decide the actual behavior
   503  func (th *TxnHandler) Rollback(execCtx *ExecCtx) error {
   504  	var err error
   505  	th.mu.Lock()
   506  	defer th.mu.Unlock()
   507  	/*
   508  			Rollback Rules:
   509  			1, if it is in single-statement mode (Case2):
   510  				it rollbacks.
   511  			2, if it is in multi-statement mode (Case1,Case3,Case4):
   512  		        the transaction need to be rollback at the end of the statement.
   513  				(every error will abort the transaction.)
   514  	*/
   515  	if !bitsIsSet(th.optionBits, OPTION_BEGIN|OPTION_NOT_AUTOCOMMIT) ||
   516  		th.inActiveTxnUnsafe() && NeedToBeCommittedInActiveTransaction(execCtx.stmt) ||
   517  		execCtx.txnOpt.byRollback {
   518  		//Case1.1: autocommit && not_begin
   519  		//Case1.2: (not_autocommit || begin) && activeTxn && needToBeCommitted
   520  		//Case1.3: the error that should rollback the whole txn
   521  		err = th.rollbackUnsafe(execCtx)
   522  	} else {
   523  		//Case2: not ( autocommit && !begin ) && not ( activeTxn && needToBeCommitted )
   524  		//<==>  ( not_autocommit || begin ) && not ( activeTxn && needToBeCommitted )
   525  		//just rollback statement
   526  
   527  		//non derived statement
   528  		if th.txnOp != nil && !execCtx.ses.IsDerivedStmt() {
   529  			err = th.txnOp.GetWorkspace().RollbackLastStatement(th.txnCtx)
   530  			if err != nil {
   531  				err4 := th.rollbackUnsafe(execCtx)
   532  				return errors.Join(err, err4)
   533  			}
   534  		}
   535  	}
   536  	return err
   537  }
   538  
   539  func (th *TxnHandler) rollbackUnsafe(execCtx *ExecCtx) error {
   540  	_, span := trace.Start(execCtx.reqCtx, "TxnHandler.RollbackTxn",
   541  		trace.WithKind(trace.SpanKindStatement))
   542  	defer span.End(trace.WithStatementExtra(execCtx.ses.GetTxnId(), execCtx.ses.GetStmtId(), execCtx.ses.GetSqlOfStmt()))
   543  	var err error
   544  	defer th.inActiveTxnUnsafe()
   545  	if !th.inActiveTxnUnsafe() || th.shareTxn {
   546  		return nil
   547  	}
   548  
   549  	sessionInfo := execCtx.ses.GetDebugString()
   550  
   551  	if th.txnOp == nil {
   552  		th.invalidateTxnUnsafe()
   553  	}
   554  	if th.txnCtx == nil {
   555  		panic("context should not be nil")
   556  	}
   557  	if th.hasTempEngineUnsafe() && th.tempStorage != nil {
   558  		if th.txnCtx.Value(defines.TemporaryTN{}) == nil {
   559  			th.txnCtx = context.WithValue(th.txnCtx, defines.TemporaryTN{}, th.tempStorage)
   560  		}
   561  	}
   562  	ctx2, cancel := context.WithTimeout(
   563  		th.txnCtx,
   564  		th.storage.Hints().CommitOrRollbackTimeout,
   565  	)
   566  	defer cancel()
   567  	defer func() {
   568  		// metric count
   569  		tenant := execCtx.ses.GetTenantName()
   570  		incTransactionCounter(tenant)
   571  		incTransactionErrorsCounter(tenant, metric.SQLTypeOther) // exec rollback cnt
   572  		if err != nil {
   573  			incTransactionErrorsCounter(tenant, metric.SQLTypeRollback)
   574  		}
   575  	}()
   576  	if logutil.GetSkip1Logger().Core().Enabled(zap.DebugLevel) {
   577  		txnId := th.txnOp.Txn().DebugString()
   578  		logDebugf(sessionInfo, "RollbackTxn txnId:%s", txnId)
   579  		defer func() {
   580  			logDebugf(sessionInfo, "RollbackTxn exit txnId:%s", txnId)
   581  		}()
   582  	}
   583  	if th.txnOp != nil {
   584  		execCtx.ses.SetTxnId(th.txnOp.Txn().ID)
   585  		err = th.txnOp.Rollback(ctx2)
   586  		if err != nil {
   587  			th.invalidateTxnUnsafe()
   588  		}
   589  	}
   590  	th.invalidateTxnUnsafe()
   591  	execCtx.ses.SetTxnId(dumpUUID[:])
   592  	return err
   593  }
   594  
   595  /*
   596  SetAutocommit sets the value of the system variable 'autocommit'.
   597  
   598  It commits the active transaction if the old value is false and the new value is true.
   599  */
   600  func (th *TxnHandler) SetAutocommit(execCtx *ExecCtx, old, on bool) error {
   601  	th.mu.Lock()
   602  	defer th.mu.Unlock()
   603  	//on -> on : do nothing
   604  	//off -> on : commit active txn
   605  	//	if commit failed, clean OPTION_AUTOCOMMIT
   606  	//	if commit succeeds, clean OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT
   607  	//		and set SERVER_STATUS_AUTOCOMMIT
   608  	//on -> off :
   609  	//	clean OPTION_AUTOCOMMIT
   610  	//	clean SERVER_STATUS_AUTOCOMMIT
   611  	//	set OPTION_NOT_AUTOCOMMIT
   612  	//off -> off : do nothing
   613  	if !old && on { //off -> on
   614  		//activating autocommit
   615  		err := th.commitUnsafe(execCtx)
   616  		if err != nil {
   617  			clearBits(&th.optionBits, OPTION_AUTOCOMMIT)
   618  			return err
   619  		}
   620  		clearBits(&th.optionBits, OPTION_BEGIN|OPTION_NOT_AUTOCOMMIT)
   621  		setBits(&th.serverStatus, uint32(SERVER_STATUS_AUTOCOMMIT))
   622  	} else if old && !on { //on -> off
   623  		clearBits(&th.optionBits, OPTION_AUTOCOMMIT)
   624  		clearBits(&th.serverStatus, uint32(SERVER_STATUS_AUTOCOMMIT))
   625  		setBits(&th.optionBits, OPTION_NOT_AUTOCOMMIT)
   626  	}
   627  	return nil
   628  }
   629  
   630  func (th *TxnHandler) setAutocommitOn() {
   631  	th.mu.Lock()
   632  	defer th.mu.Unlock()
   633  	clearBits(&th.optionBits, OPTION_BEGIN|OPTION_NOT_AUTOCOMMIT)
   634  	setBits(&th.optionBits, OPTION_AUTOCOMMIT)
   635  	setBits(&th.serverStatus, uint32(SERVER_STATUS_AUTOCOMMIT))
   636  }
   637  
   638  func (th *TxnHandler) IsShareTxn() bool {
   639  	th.mu.Lock()
   640  	defer th.mu.Unlock()
   641  	return th.shareTxn
   642  }
   643  
   644  func (th *TxnHandler) SetOptionBits(bits uint32) {
   645  	th.mu.Lock()
   646  	defer th.mu.Unlock()
   647  	setBits(&th.optionBits, bits)
   648  }
   649  
   650  func (th *TxnHandler) GetOptionBits() uint32 {
   651  	th.mu.Lock()
   652  	defer th.mu.Unlock()
   653  	return th.optionBits
   654  }
   655  
   656  func (th *TxnHandler) SetServerStatus(status uint16) {
   657  	th.mu.Lock()
   658  	defer th.mu.Unlock()
   659  	setBits(&th.serverStatus, uint32(status))
   660  }
   661  
   662  func (th *TxnHandler) GetServerStatus() uint16 {
   663  	th.mu.Lock()
   664  	defer th.mu.Unlock()
   665  	return uint16(th.serverStatus)
   666  }
   667  
   668  func (th *TxnHandler) InMultiStmtTransactionMode() bool {
   669  	th.mu.Lock()
   670  	defer th.mu.Unlock()
   671  	return bitsIsSet(th.optionBits, OPTION_NOT_AUTOCOMMIT|OPTION_BEGIN)
   672  }
   673  
   674  func (th *TxnHandler) GetStorage() engine.Engine {
   675  	th.mu.Lock()
   676  	defer th.mu.Unlock()
   677  	return th.storage
   678  }
   679  
   680  func (th *TxnHandler) HasTempEngine() bool {
   681  	th.mu.Lock()
   682  	defer th.mu.Unlock()
   683  	return th.hasTempEngineUnsafe()
   684  }
   685  
   686  func (th *TxnHandler) hasTempEngineUnsafe() bool {
   687  	if entireEng, ok := th.storage.(*engine.EntireEngine); ok {
   688  		return entireEng.TempEngine != nil
   689  	}
   690  	return false
   691  }
   692  
   693  func (th *TxnHandler) OptionBitsIsSet(bit uint32) bool {
   694  	th.mu.Lock()
   695  	defer th.mu.Unlock()
   696  	return bitsIsSet(th.optionBits, bit)
   697  }
   698  
   699  func (th *TxnHandler) CreateTempStorage(ck clock.Clock) error {
   700  	th.mu.Lock()
   701  	defer th.mu.Unlock()
   702  	return th.createTempStorageUnsafe(ck)
   703  }
   704  
   705  func (th *TxnHandler) GetTempStorage() *memorystorage.Storage {
   706  	th.mu.Lock()
   707  	defer th.mu.Unlock()
   708  	if th.tempStorage == nil {
   709  		panic("temp table storage is not initialized")
   710  	}
   711  	return th.tempStorage
   712  }
   713  
   714  func (th *TxnHandler) GetTempTNService() *metadata.TNService {
   715  	th.mu.Lock()
   716  	defer th.mu.Unlock()
   717  	return th.tempTnService
   718  }
   719  
   720  func (th *TxnHandler) createTempStorageUnsafe(ck clock.Clock) error {
   721  	// Without concurrency, there is no potential for data competition
   722  	// Arbitrary value is OK since it's single sharded. Let's use 0xbeef
   723  	// suggested by @reusee
   724  	shards := []metadata.TNShard{
   725  		{
   726  			ReplicaID:     0xbeef,
   727  			TNShardRecord: metadata.TNShardRecord{ShardID: 0xbeef},
   728  		},
   729  	}
   730  	// Arbitrary value is OK, for more information about TEMPORARY_TABLE_DN_ADDR, please refer to the comment in defines/const.go
   731  	tnAddr := defines.TEMPORARY_TABLE_TN_ADDR
   732  	uid, err := uuid.NewV7()
   733  	if err != nil {
   734  		return err
   735  	}
   736  	th.tempTnService = &metadata.TNService{
   737  		ServiceID:         uid.String(),
   738  		TxnServiceAddress: tnAddr,
   739  		Shards:            shards,
   740  	}
   741  
   742  	ms, err := memorystorage.NewMemoryStorage(
   743  		mpool.MustNewZeroNoFixed(),
   744  		ck,
   745  		memoryengine.RandomIDGenerator,
   746  	)
   747  	if err != nil {
   748  		return err
   749  	}
   750  	th.tempStorage = ms
   751  	return nil
   752  }
   753  
   754  func (th *TxnHandler) CreateTempEngine() {
   755  	th.mu.Lock()
   756  	defer th.mu.Unlock()
   757  
   758  	th.tempEngine = memoryengine.New(
   759  		context.TODO(), //!!!NOTE: memoryengine.New will neglect this context.
   760  		memoryengine.NewDefaultShardPolicy(
   761  			mpool.MustNewZeroNoFixed(),
   762  		),
   763  		memoryengine.RandomIDGenerator,
   764  		clusterservice.NewMOCluster(
   765  			nil,
   766  			0,
   767  			clusterservice.WithDisableRefresh(),
   768  			clusterservice.WithServices(nil, []metadata.TNService{
   769  				*th.tempTnService,
   770  			})),
   771  	)
   772  	updateTempEngine(th.storage, th.tempEngine)
   773  }
   774  
   775  func (th *TxnHandler) GetTempEngine() *memoryengine.Engine {
   776  	th.mu.Lock()
   777  	defer th.mu.Unlock()
   778  	return th.tempEngine
   779  }