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  }