保留原始视图定义与编码,按依赖排序导出,并严格识别和重排官方最终视图块;增加运行上下文和权限检查。补充视图、清单、恢复失败保护测试,并修正旧事件、触发器和例程测试的权限前提。 隔离MySQL 5.7和8.0完整专项分别通过176和202个叶子用例,无失败或跳过;后端常规全量、静态检查、模块校验及Linux测试包交叉编译通过。测试产物和凭据未提交。本提交不代表生产部署验收完成。
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type mysqlViewToken struct {
|
|
text string
|
|
kind byte
|
|
}
|
|
|
|
func (t mysqlViewToken) keyword(value string) bool {
|
|
return t.kind == 'w' && strings.EqualFold(t.text, value)
|
|
}
|
|
|
|
func (t mysqlViewToken) identifier() bool { return t.kind == 'w' || t.kind == 'i' }
|
|
|
|
// 输入仅限 sql_mode 为空且 sql_quote_show_create=1 的服务端规范定义。
|
|
// 保留标识符原文;字符串和注释永远不能成为依赖名称。
|
|
func lexMySQLView(definition string) ([]mysqlViewToken, error) {
|
|
var tokens []mysqlViewToken
|
|
for i := 0; i < len(definition); {
|
|
c := definition[i]
|
|
if c <= ' ' {
|
|
i++
|
|
continue
|
|
}
|
|
if c == '#' || (strings.HasPrefix(definition[i:], "--") && i+2 < len(definition) && definition[i+2] <= ' ') {
|
|
for i < len(definition) && definition[i] != '\n' {
|
|
i++
|
|
}
|
|
continue
|
|
}
|
|
if strings.HasPrefix(definition[i:], "/*") {
|
|
end := strings.Index(definition[i+2:], "*/")
|
|
if end < 0 || strings.HasPrefix(definition[i:], "/*!") {
|
|
return nil, fmt.Errorf("MySQL 视图规范定义含未闭合或可执行注释")
|
|
}
|
|
i += end + 4
|
|
continue
|
|
}
|
|
if c == '`' || c == '\'' || c == '"' {
|
|
quote := c
|
|
i++
|
|
var value strings.Builder
|
|
closed := false
|
|
for i < len(definition) {
|
|
c = definition[i]
|
|
i++
|
|
if c == quote {
|
|
if i < len(definition) && definition[i] == quote {
|
|
value.WriteByte(c)
|
|
i++
|
|
continue
|
|
}
|
|
closed = true
|
|
break
|
|
}
|
|
if c == '\\' && quote != '`' && i < len(definition) {
|
|
value.WriteByte(definition[i])
|
|
i++
|
|
} else {
|
|
value.WriteByte(c)
|
|
}
|
|
}
|
|
if !closed {
|
|
return nil, fmt.Errorf("MySQL 视图规范定义含未闭合引用")
|
|
}
|
|
kind := byte('s')
|
|
if quote == '`' {
|
|
kind = 'i'
|
|
}
|
|
tokens = append(tokens, mysqlViewToken{text: value.String(), kind: kind})
|
|
continue
|
|
}
|
|
if mysqlViewWordByte(c) {
|
|
start := i
|
|
for i < len(definition) && mysqlViewWordByte(definition[i]) {
|
|
i++
|
|
}
|
|
tokens = append(tokens, mysqlViewToken{text: definition[start:i], kind: 'w'})
|
|
continue
|
|
}
|
|
tokens = append(tokens, mysqlViewToken{text: string(c), kind: 'p'})
|
|
i++
|
|
}
|
|
return tokens, nil
|
|
}
|
|
|
|
func mysqlViewWordByte(c byte) bool {
|
|
return c >= 128 || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '$'
|
|
}
|
|
|
|
func mysqlViewGroupEnd(tokens []mysqlViewToken, start int) (int, error) {
|
|
depth := 0
|
|
for i := start; i < len(tokens); i++ {
|
|
if tokens[i].kind != 'p' {
|
|
continue
|
|
}
|
|
switch tokens[i].text {
|
|
case "(":
|
|
depth++
|
|
case ")":
|
|
depth--
|
|
if depth == 0 {
|
|
return i, nil
|
|
}
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("MySQL 视图规范定义含未闭合括号")
|
|
}
|