Repository: pingcap/tidb
File: expression/builtin_time.go (Go)

Code before:
	baseFunctionClass
}

func (c *readTSInFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
	if err := c.verifyArgs(args); err != nil {
		return nil, err
	}
	bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETDatetime, types.ETDatetime)
	if err != nil {
		return nil, err
	}
	sig := &builtinReadTSInSig{bf}
	return sig, nil
}

type builtinReadTSInSig struct {
	baseBuiltinFunc
}

func (b *builtinReadTSInSig) Clone() builtinFunc {
	newSig := &builtinTidbParseTsoSig{}
	newSig.cloneFrom(&b.baseBuiltinFunc)
	return newSig
}

func (b *builtinReadTSInSig) evalInt(row chunk.Row) (int64, bool, error) {
	leftTime, isNull, err := b.args[0].EvalTime(b.ctx, row)
	if isNull || err != nil {
		return 0, true, handleInvalidTimeError(b.ctx, err)
	}
	rightTime, isNull, err := b.args[1].EvalTime(b.ctx, row)
	if isNull || err != nil {
		return 0, true, handleInvalidTimeError(b.ctx, err)
	}
	if invalidLeftTime, invalidRightTime := leftTime.InvalidZero(), rightTime.InvalidZero(); invalidLeftTime || invalidRightTime {
		if invalidLeftTime {
			err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, leftTime.String()))
		}
		if invalidRightTime {
			err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, rightTime.String()))
		}
		return 0, true, err
	}
	minTime, err := leftTime.GoTime(getTimeZone(b.ctx))
	if err != nil {
		return 0, true, err
	}
	maxTime, err := rightTime.GoTime(getTimeZone(b.ctx))
	if err != nil {
		return 0, true, err
	}

Review comment (suggestion):
We want `read_ts_in` as a [**deterministic** function](https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html), which means it always produces the same result for the same input parameters in the statement. such as `now()` is deterministic, `sysdata()` is nondeterministic:
```
mysql> select now(6), now(6), now(6), sysdate(6), sysdate(6), sysdate(6);
+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
| now(6)                     | now(6)                     | now(6)                     | sysdate(6)                 | sysdate(6)                 | sysdate(6)                 |
+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
| 2021-04-21 15:44:57.807908 | 2021-04-21 15:44:57.807908 | 2021-04-21 15:44:57.807908 | 2021-04-21 15:44:57.808105 | 2021-04-21 15:44:57.808106 | 2021-04-21 15:44:57.808107 |
+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
1 row in set (0.00 sec)
```

Code after:
	baseFunctionClass
}

func (c *tidbBoundedStalenessFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
	if err := c.verifyArgs(args); err != nil {
		return nil, err
	}
	bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETDatetime, types.ETDatetime, types.ETDatetime)
	if err != nil {
		return nil, err
	}
	sig := &builtinTiDBBoundedStalenessSig{bf}
	return sig, nil
}

type builtinTiDBBoundedStalenessSig struct {
	baseBuiltinFunc
}

func (b *builtinTiDBBoundedStalenessSig) Clone() builtinFunc {
	newSig := &builtinTidbParseTsoSig{}
	newSig.cloneFrom(&b.baseBuiltinFunc)
	return newSig
}

func (b *builtinTiDBBoundedStalenessSig) evalTime(row chunk.Row) (types.Time, bool, error) {
	leftTime, isNull, err := b.args[0].EvalTime(b.ctx, row)
	if isNull || err != nil {
		return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
	}
	rightTime, isNull, err := b.args[1].EvalTime(b.ctx, row)
	if isNull || err != nil {
		return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err)
	}
	if invalidLeftTime, invalidRightTime := leftTime.InvalidZero(), rightTime.InvalidZero(); invalidLeftTime || invalidRightTime {
		if invalidLeftTime {
			err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, leftTime.String()))
		}
		if invalidRightTime {
			err = handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, rightTime.String()))
		}
		return types.ZeroTime, true, err
	}
	timeZone := getTimeZone(b.ctx)
	minTime, err := leftTime.GoTime(timeZone)
	if err != nil {
		return types.ZeroTime, true, err
	}
	maxTime, err := rightTime.GoTime(timeZone)
	if err != nil {
		return types.ZeroTime, true, err