vitess.io/vitess@v0.16.2/go/vt/schema/parser.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package schema 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 "strings" 24 25 "vitess.io/vitess/go/textutil" 26 "vitess.io/vitess/go/vt/sqlparser" 27 ) 28 29 // NormalizedDDLQuery contains a query which is online-ddl -normalized 30 type NormalizedDDLQuery struct { 31 SQL string 32 TableName sqlparser.TableName 33 } 34 35 var ( 36 // ALTER TABLE 37 alterTableBasicPattern = `(?s)(?i)\balter\s+table\s+` 38 alterTableExplicitSchemaTableRegexps = []*regexp.Regexp{ 39 // ALTER TABLE `scm`.`tbl` something 40 regexp.MustCompile(alterTableBasicPattern + "`" + `([^` + "`" + `]+)` + "`" + `[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`), 41 // ALTER TABLE `scm`.tbl something 42 regexp.MustCompile(alterTableBasicPattern + "`" + `([^` + "`" + `]+)` + "`" + `[.]([\S]+)\s+(.*$)`), 43 // ALTER TABLE scm.`tbl` something 44 regexp.MustCompile(alterTableBasicPattern + `([\S]+)[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`), 45 // ALTER TABLE scm.tbl something 46 regexp.MustCompile(alterTableBasicPattern + `([\S]+)[.]([\S]+)\s+(.*$)`), 47 } 48 alterTableExplicitTableRegexps = []*regexp.Regexp{ 49 // ALTER TABLE `tbl` something 50 regexp.MustCompile(alterTableBasicPattern + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`), 51 // ALTER TABLE tbl something 52 regexp.MustCompile(alterTableBasicPattern + `([\S]+)\s+(.*$)`), 53 } 54 revertStatementRegexp = regexp.MustCompile(`(?i)^revert\s+([\S]*)$`) 55 56 enumValuesRegexp = regexp.MustCompile("(?i)^enum[(](.*)[)]$") 57 setValuesRegexp = regexp.MustCompile("(?i)^set[(](.*)[)]$") 58 ) 59 60 // ParseAlterTableOptions parses a ALTER ... TABLE... statement into: 61 // - explicit schema and table, if available 62 // - alter options (anything that follows ALTER ... TABLE) 63 func ParseAlterTableOptions(alterStatement string) (explicitSchema, explicitTable, alterOptions string) { 64 alterOptions = strings.TrimSpace(alterStatement) 65 for _, alterTableRegexp := range alterTableExplicitSchemaTableRegexps { 66 if submatch := alterTableRegexp.FindStringSubmatch(alterOptions); len(submatch) > 0 { 67 explicitSchema = submatch[1] 68 explicitTable = submatch[2] 69 alterOptions = submatch[3] 70 return explicitSchema, explicitTable, alterOptions 71 } 72 } 73 for _, alterTableRegexp := range alterTableExplicitTableRegexps { 74 if submatch := alterTableRegexp.FindStringSubmatch(alterOptions); len(submatch) > 0 { 75 explicitTable = submatch[1] 76 alterOptions = submatch[2] 77 return explicitSchema, explicitTable, alterOptions 78 } 79 } 80 return explicitSchema, explicitTable, alterOptions 81 } 82 83 // legacyParseRevertUUID expects a query like "revert 4e5dcf80_354b_11eb_82cd_f875a4d24e90" and returns the UUID value. 84 func legacyParseRevertUUID(sql string) (uuid string, err error) { 85 submatch := revertStatementRegexp.FindStringSubmatch(sql) 86 if len(submatch) == 0 { 87 return "", fmt.Errorf("Not a Revert DDL: '%s'", sql) 88 } 89 uuid = submatch[1] 90 if !IsOnlineDDLUUID(uuid) { 91 return "", fmt.Errorf("Not an online DDL UUID: '%s'", uuid) 92 } 93 return uuid, nil 94 } 95 96 // ParseEnumValues parses the comma delimited part of an enum column definition 97 func ParseEnumValues(enumColumnType string) string { 98 if submatch := enumValuesRegexp.FindStringSubmatch(enumColumnType); len(submatch) > 0 { 99 return submatch[1] 100 } 101 return enumColumnType 102 } 103 104 // ParseSetValues parses the comma delimited part of a set column definition 105 func ParseSetValues(setColumnType string) string { 106 if submatch := setValuesRegexp.FindStringSubmatch(setColumnType); len(submatch) > 0 { 107 return submatch[1] 108 } 109 return setColumnType 110 } 111 112 // parseEnumOrSetTokens parses the comma delimited part of an enum/set column definition and 113 // returns the (unquoted) text values 114 // Expected input: `'x-small','small','medium','large','x-large'` 115 // Unexpected input: `enum('x-small','small','medium','large','x-large')` 116 func parseEnumOrSetTokens(enumOrSetValues string) (tokens []string) { 117 if submatch := enumValuesRegexp.FindStringSubmatch(enumOrSetValues); len(submatch) > 0 { 118 // input should not contain `enum(...)` column definition, just the comma delimited list 119 return tokens 120 } 121 if submatch := setValuesRegexp.FindStringSubmatch(enumOrSetValues); len(submatch) > 0 { 122 // input should not contain `enum(...)` column definition, just the comma delimited list 123 return tokens 124 } 125 tokens = textutil.SplitDelimitedList(enumOrSetValues) 126 for i := range tokens { 127 if strings.HasPrefix(tokens[i], `'`) && strings.HasSuffix(tokens[i], `'`) { 128 tokens[i] = strings.Trim(tokens[i], `'`) 129 } 130 } 131 return tokens 132 } 133 134 // ParseEnumOrSetTokensMap parses the comma delimited part of an enum column definition 135 // and returns a map where ["1"] is the first token, and ["<n>"] is th elast token 136 func ParseEnumOrSetTokensMap(enumOrSetValues string) map[string]string { 137 tokens := parseEnumOrSetTokens(enumOrSetValues) 138 tokensMap := map[string]string{} 139 for i, token := range tokens { 140 tokensMap[strconv.Itoa(i+1)] = token 141 } 142 return tokensMap 143 }