2016-06-17 06:03:18 +00:00
|
|
|
/*
|
2022-07-18 16:37:18 +00:00
|
|
|
Copyright 2022 GitHub Inc.
|
2016-06-17 06:03:18 +00:00
|
|
|
See https://github.com/github/gh-ost/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
package sql
|
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
2016-11-29 14:44:38 +00:00
|
|
|
"strings"
|
2016-06-17 06:03:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-07-22 09:33:02 +00:00
|
|
|
sanitizeQuotesRegexp = regexp.MustCompile("('[^']*')")
|
|
|
|
renameColumnRegexp = regexp.MustCompile(`(?i)\bchange\s+(column\s+|)([\S]+)\s+([\S]+)\s+`)
|
|
|
|
dropColumnRegexp = regexp.MustCompile(`(?i)\bdrop\s+(column\s+|)([\S]+)$`)
|
|
|
|
renameTableRegexp = regexp.MustCompile(`(?i)\brename\s+(to|as)\s+`)
|
2021-05-14 13:32:56 +00:00
|
|
|
autoIncrementRegexp = regexp.MustCompile(`(?i)\bauto_increment[\s]*=[\s]*([0-9]+)`)
|
2020-07-22 09:33:02 +00:00
|
|
|
alterTableExplicitSchemaTableRegexps = []*regexp.Regexp{
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE `scm`.`tbl` something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE `scm`.tbl something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `[.]([\S]+)\s+(.*$)`),
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE scm.`tbl` something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE scm.tbl something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)[.]([\S]+)\s+(.*$)`),
|
|
|
|
}
|
|
|
|
alterTableExplicitTableRegexps = []*regexp.Regexp{
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE `tbl` something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
2020-07-23 11:04:14 +00:00
|
|
|
// ALTER TABLE tbl something
|
2020-07-22 09:33:02 +00:00
|
|
|
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)\s+(.*$)`),
|
|
|
|
}
|
2021-06-10 15:17:49 +00:00
|
|
|
enumValuesRegexp = regexp.MustCompile("^enum[(](.*)[)]$")
|
2016-06-17 06:03:18 +00:00
|
|
|
)
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
type AlterTableParser struct {
|
2021-05-14 13:32:56 +00:00
|
|
|
columnRenameMap map[string]string
|
|
|
|
droppedColumns map[string]bool
|
|
|
|
isRenameTable bool
|
|
|
|
isAutoIncrementDefined bool
|
2020-07-22 09:33:02 +00:00
|
|
|
|
2020-07-23 08:38:05 +00:00
|
|
|
alterStatementOptions string
|
|
|
|
alterTokens []string
|
2020-07-22 09:33:02 +00:00
|
|
|
|
|
|
|
explicitSchema string
|
|
|
|
explicitTable string
|
2016-06-17 06:03:18 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func NewAlterTableParser() *AlterTableParser {
|
|
|
|
return &AlterTableParser{
|
2016-06-17 06:03:18 +00:00
|
|
|
columnRenameMap: make(map[string]string),
|
2017-04-23 05:23:56 +00:00
|
|
|
droppedColumns: make(map[string]bool),
|
2016-06-17 06:03:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func NewParserFromAlterStatement(alterStatement string) *AlterTableParser {
|
|
|
|
parser := NewAlterTableParser()
|
|
|
|
parser.ParseAlterStatement(alterStatement)
|
|
|
|
return parser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *AlterTableParser) tokenizeAlterStatement(alterStatement string) (tokens []string, err error) {
|
2016-11-29 14:44:38 +00:00
|
|
|
terminatingQuote := rune(0)
|
|
|
|
f := func(c rune) bool {
|
|
|
|
switch {
|
|
|
|
case c == terminatingQuote:
|
|
|
|
terminatingQuote = rune(0)
|
|
|
|
return false
|
|
|
|
case terminatingQuote != rune(0):
|
|
|
|
return false
|
|
|
|
case c == '\'':
|
|
|
|
terminatingQuote = c
|
|
|
|
return false
|
|
|
|
case c == '(':
|
|
|
|
terminatingQuote = ')'
|
|
|
|
return false
|
|
|
|
default:
|
|
|
|
return c == ','
|
2016-06-18 19:12:07 +00:00
|
|
|
}
|
2016-11-29 14:44:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tokens = strings.FieldsFunc(alterStatement, f)
|
|
|
|
for i := range tokens {
|
|
|
|
tokens[i] = strings.TrimSpace(tokens[i])
|
|
|
|
}
|
|
|
|
return tokens, nil
|
|
|
|
}
|
2016-06-17 06:03:18 +00:00
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) sanitizeQuotesFromAlterStatement(alterStatement string) (strippedStatement string) {
|
2016-11-29 14:44:38 +00:00
|
|
|
strippedStatement = alterStatement
|
2016-11-29 14:47:39 +00:00
|
|
|
strippedStatement = sanitizeQuotesRegexp.ReplaceAllString(strippedStatement, "''")
|
2016-11-29 14:44:38 +00:00
|
|
|
return strippedStatement
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) parseAlterToken(alterToken string) (err error) {
|
2017-04-23 05:23:56 +00:00
|
|
|
{
|
|
|
|
// rename
|
2016-11-29 14:44:38 +00:00
|
|
|
allStringSubmatch := renameColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
|
|
|
for _, submatch := range allStringSubmatch {
|
|
|
|
if unquoted, err := strconv.Unquote(submatch[2]); err == nil {
|
|
|
|
submatch[2] = unquoted
|
|
|
|
}
|
|
|
|
if unquoted, err := strconv.Unquote(submatch[3]); err == nil {
|
|
|
|
submatch[3] = unquoted
|
|
|
|
}
|
|
|
|
this.columnRenameMap[submatch[2]] = submatch[3]
|
|
|
|
}
|
2016-06-17 06:03:18 +00:00
|
|
|
}
|
2017-04-23 05:23:56 +00:00
|
|
|
{
|
|
|
|
// drop
|
|
|
|
allStringSubmatch := dropColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
|
|
|
for _, submatch := range allStringSubmatch {
|
|
|
|
if unquoted, err := strconv.Unquote(submatch[2]); err == nil {
|
|
|
|
submatch[2] = unquoted
|
|
|
|
}
|
|
|
|
this.droppedColumns[submatch[2]] = true
|
|
|
|
}
|
|
|
|
}
|
2018-05-06 08:19:03 +00:00
|
|
|
{
|
|
|
|
// rename table
|
|
|
|
if renameTableRegexp.MatchString(alterToken) {
|
|
|
|
this.isRenameTable = true
|
|
|
|
}
|
|
|
|
}
|
2021-05-14 13:32:56 +00:00
|
|
|
{
|
|
|
|
// auto_increment
|
|
|
|
if autoIncrementRegexp.MatchString(alterToken) {
|
|
|
|
this.isAutoIncrementDefined = true
|
|
|
|
}
|
|
|
|
}
|
2017-04-23 05:23:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) ParseAlterStatement(alterStatement string) (err error) {
|
2020-07-23 08:38:05 +00:00
|
|
|
this.alterStatementOptions = alterStatement
|
2020-07-22 09:33:02 +00:00
|
|
|
for _, alterTableRegexp := range alterTableExplicitSchemaTableRegexps {
|
2020-07-23 08:38:05 +00:00
|
|
|
if submatch := alterTableRegexp.FindStringSubmatch(this.alterStatementOptions); len(submatch) > 0 {
|
2020-07-22 09:33:02 +00:00
|
|
|
this.explicitSchema = submatch[1]
|
|
|
|
this.explicitTable = submatch[2]
|
2020-07-23 08:38:05 +00:00
|
|
|
this.alterStatementOptions = submatch[3]
|
2020-07-22 09:33:02 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, alterTableRegexp := range alterTableExplicitTableRegexps {
|
2020-07-23 08:38:05 +00:00
|
|
|
if submatch := alterTableRegexp.FindStringSubmatch(this.alterStatementOptions); len(submatch) > 0 {
|
2020-07-22 09:33:02 +00:00
|
|
|
this.explicitTable = submatch[1]
|
2020-07-23 08:38:05 +00:00
|
|
|
this.alterStatementOptions = submatch[2]
|
2020-07-22 09:33:02 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-07-23 08:38:05 +00:00
|
|
|
alterTokens, _ := this.tokenizeAlterStatement(this.alterStatementOptions)
|
2017-04-23 05:23:56 +00:00
|
|
|
for _, alterToken := range alterTokens {
|
|
|
|
alterToken = this.sanitizeQuotesFromAlterStatement(alterToken)
|
|
|
|
this.parseAlterToken(alterToken)
|
2020-07-22 09:33:02 +00:00
|
|
|
this.alterTokens = append(this.alterTokens, alterToken)
|
2017-04-23 05:23:56 +00:00
|
|
|
}
|
2016-06-17 06:03:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) GetNonTrivialRenames() map[string]string {
|
2016-06-17 06:03:18 +00:00
|
|
|
result := make(map[string]string)
|
|
|
|
for column, renamed := range this.columnRenameMap {
|
|
|
|
if column != renamed {
|
|
|
|
result[column] = renamed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) HasNonTrivialRenames() bool {
|
2016-06-17 06:03:18 +00:00
|
|
|
return len(this.GetNonTrivialRenames()) > 0
|
|
|
|
}
|
2017-04-23 05:38:35 +00:00
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) DroppedColumnsMap() map[string]bool {
|
2017-04-23 05:38:35 +00:00
|
|
|
return this.droppedColumns
|
|
|
|
}
|
2018-05-06 08:19:03 +00:00
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) IsRenameTable() bool {
|
2018-05-06 08:19:03 +00:00
|
|
|
return this.isRenameTable
|
|
|
|
}
|
2021-05-14 13:32:56 +00:00
|
|
|
|
|
|
|
func (this *AlterTableParser) IsAutoIncrementDefined() bool {
|
|
|
|
return this.isAutoIncrementDefined
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:33:02 +00:00
|
|
|
func (this *AlterTableParser) GetExplicitSchema() string {
|
|
|
|
return this.explicitSchema
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *AlterTableParser) HasExplicitSchema() bool {
|
|
|
|
return this.GetExplicitSchema() != ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *AlterTableParser) GetExplicitTable() string {
|
|
|
|
return this.explicitTable
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *AlterTableParser) HasExplicitTable() bool {
|
|
|
|
return this.GetExplicitTable() != ""
|
|
|
|
}
|
2020-07-23 08:38:05 +00:00
|
|
|
|
|
|
|
func (this *AlterTableParser) GetAlterStatementOptions() string {
|
|
|
|
return this.alterStatementOptions
|
|
|
|
}
|
2021-06-10 15:17:49 +00:00
|
|
|
|
|
|
|
func ParseEnumValues(enumColumnType string) string {
|
|
|
|
if submatch := enumValuesRegexp.FindStringSubmatch(enumColumnType); len(submatch) > 0 {
|
|
|
|
return submatch[1]
|
|
|
|
}
|
|
|
|
return enumColumnType
|
|
|
|
}
|