Scert/backend/internal/cli/mysql_view_tokens.go
xuans 777c7169b6 完善MySQL视图备份恢复及完整清单权限校验
保留原始视图定义与编码,按依赖排序导出,并严格识别和重排官方最终视图块;增加运行上下文和权限检查。补充视图、清单、恢复失败保护测试,并修正旧事件、触发器和例程测试的权限前提。

隔离MySQL 5.7和8.0完整专项分别通过176和202个叶子用例,无失败或跳过;后端常规全量、静态检查、模块校验及Linux测试包交叉编译通过。测试产物和凭据未提交。本提交不代表生产部署验收完成。
2026-09-28 00:35:26 +08:00

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 视图规范定义含未闭合括号")
}