vitess.io/vitess@v0.16.2/go/vt/schema/ddl_strategy.go (about)

     1  /*
     2  Copyright 2021 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  
    23  	"github.com/google/shlex"
    24  )
    25  
    26  var (
    27  	strategyParserRegexp = regexp.MustCompile(`^([\S]+)\s+(.*)$`)
    28  )
    29  
    30  const (
    31  	declarativeFlag        = "declarative"
    32  	skipTopoFlag           = "skip-topo" // legacy. Kept for backwards compatibility, but unused
    33  	singletonFlag          = "singleton"
    34  	singletonContextFlag   = "singleton-context"
    35  	allowZeroInDateFlag    = "allow-zero-in-date"
    36  	postponeLaunchFlag     = "postpone-launch"
    37  	postponeCompletionFlag = "postpone-completion"
    38  	inOrderCompletionFlag  = "in-order-completion"
    39  	allowConcurrentFlag    = "allow-concurrent"
    40  	preferInstantDDL       = "prefer-instant-ddl"
    41  	fastRangeRotationFlag  = "fast-range-rotation"
    42  	vreplicationTestSuite  = "vreplication-test-suite"
    43  	allowForeignKeysFlag   = "unsafe-allow-foreign-keys"
    44  )
    45  
    46  // DDLStrategy suggests how an ALTER TABLE should run (e.g. "direct", "online", "gh-ost" or "pt-osc")
    47  type DDLStrategy string
    48  
    49  const (
    50  	// DDLStrategyDirect means not an online-ddl migration; unmanaged. Just a normal MySQL `ALTER TABLE`
    51  	DDLStrategyDirect DDLStrategy = "direct"
    52  	// DDLStrategyVitess requests vreplication to run the migration; new name for DDLStrategyOnline
    53  	DDLStrategyVitess DDLStrategy = "vitess"
    54  	// DDLStrategyOnline requests vreplication to run the migration
    55  	DDLStrategyOnline DDLStrategy = "online"
    56  	// DDLStrategyGhost requests gh-ost to run the migration
    57  	DDLStrategyGhost DDLStrategy = "gh-ost"
    58  	// DDLStrategyPTOSC requests pt-online-schema-change to run the migration
    59  	DDLStrategyPTOSC DDLStrategy = "pt-osc"
    60  	// DDLStrategyMySQL is a managed migration (queued and executed by the scheduler) but runs through a MySQL `ALTER TABLE`
    61  	DDLStrategyMySQL DDLStrategy = "mysql"
    62  )
    63  
    64  // IsDirect returns true if this strategy is a direct strategy
    65  // A strategy is direct if it's not explciitly one of the online DDL strategies
    66  func (s DDLStrategy) IsDirect() bool {
    67  	switch s {
    68  	case DDLStrategyVitess, DDLStrategyOnline, DDLStrategyGhost, DDLStrategyPTOSC, DDLStrategyMySQL:
    69  		return false
    70  	}
    71  	return true
    72  }
    73  
    74  // DDLStrategySetting is a formal breakdown of the @@ddl_strategy variable, into strategy and options
    75  type DDLStrategySetting struct {
    76  	Strategy DDLStrategy `json:"strategy,omitempty"`
    77  	Options  string      `json:"options,omitempty"`
    78  }
    79  
    80  // NewDDLStrategySetting instantiates a new setting
    81  func NewDDLStrategySetting(strategy DDLStrategy, options string) *DDLStrategySetting {
    82  	return &DDLStrategySetting{
    83  		Strategy: strategy,
    84  		Options:  options,
    85  	}
    86  }
    87  
    88  // ParseDDLStrategy parses and validates the value of @@ddl_strategy or -ddl_strategy variables
    89  func ParseDDLStrategy(strategyVariable string) (*DDLStrategySetting, error) {
    90  	setting := &DDLStrategySetting{}
    91  	strategyName := strategyVariable
    92  	if submatch := strategyParserRegexp.FindStringSubmatch(strategyVariable); len(submatch) > 0 {
    93  		strategyName = submatch[1]
    94  		setting.Options = submatch[2]
    95  	}
    96  
    97  	switch strategy := DDLStrategy(strategyName); strategy {
    98  	case "": // backward compatiblity and to handle unspecified values
    99  		setting.Strategy = DDLStrategyDirect
   100  	case DDLStrategyVitess, DDLStrategyOnline, DDLStrategyGhost, DDLStrategyPTOSC, DDLStrategyMySQL, DDLStrategyDirect:
   101  		setting.Strategy = strategy
   102  	default:
   103  		return nil, fmt.Errorf("Unknown online DDL strategy: '%v'", strategy)
   104  	}
   105  	return setting, nil
   106  }
   107  
   108  // isFlag return true when the given string is a CLI flag of the given name
   109  func isFlag(s string, name string) bool {
   110  	if s == fmt.Sprintf("-%s", name) {
   111  		return true
   112  	}
   113  	if s == fmt.Sprintf("--%s", name) {
   114  		return true
   115  	}
   116  	return false
   117  }
   118  
   119  // hasFlag returns true when Options include named flag
   120  func (setting *DDLStrategySetting) hasFlag(name string) bool {
   121  	opts, _ := shlex.Split(setting.Options)
   122  	for _, opt := range opts {
   123  		if isFlag(opt, name) {
   124  			return true
   125  		}
   126  	}
   127  	return false
   128  }
   129  
   130  // IsDeclarative checks if strategy options include --declarative
   131  func (setting *DDLStrategySetting) IsDeclarative() bool {
   132  	return setting.hasFlag(declarativeFlag)
   133  }
   134  
   135  // IsSingleton checks if strategy options include --singleton
   136  func (setting *DDLStrategySetting) IsSingleton() bool {
   137  	return setting.hasFlag(singletonFlag)
   138  }
   139  
   140  // IsSingletonContext checks if strategy options include --singleton-context
   141  func (setting *DDLStrategySetting) IsSingletonContext() bool {
   142  	return setting.hasFlag(singletonContextFlag)
   143  }
   144  
   145  // IsAllowZeroInDateFlag checks if strategy options include --allow-zero-in-date
   146  func (setting *DDLStrategySetting) IsAllowZeroInDateFlag() bool {
   147  	return setting.hasFlag(allowZeroInDateFlag)
   148  }
   149  
   150  // IsPostponeLaunch checks if strategy options include --postpone-launch
   151  func (setting *DDLStrategySetting) IsPostponeLaunch() bool {
   152  	return setting.hasFlag(postponeLaunchFlag)
   153  }
   154  
   155  // IsPostponeCompletion checks if strategy options include --postpone-completion
   156  func (setting *DDLStrategySetting) IsPostponeCompletion() bool {
   157  	return setting.hasFlag(postponeCompletionFlag)
   158  }
   159  
   160  // IsInOrderCompletion checks if strategy options include --in-order-completion
   161  func (setting *DDLStrategySetting) IsInOrderCompletion() bool {
   162  	return setting.hasFlag(inOrderCompletionFlag)
   163  }
   164  
   165  // IsAllowConcurrent checks if strategy options include --allow-concurrent
   166  func (setting *DDLStrategySetting) IsAllowConcurrent() bool {
   167  	return setting.hasFlag(allowConcurrentFlag)
   168  }
   169  
   170  // IsPreferInstantDDL checks if strategy options include --prefer-instant-ddl
   171  func (setting *DDLStrategySetting) IsPreferInstantDDL() bool {
   172  	return setting.hasFlag(preferInstantDDL)
   173  }
   174  
   175  // IsFastRangeRotationFlag checks if strategy options include --fast-range-rotation
   176  func (setting *DDLStrategySetting) IsFastRangeRotationFlag() bool {
   177  	return setting.hasFlag(fastRangeRotationFlag)
   178  }
   179  
   180  // IsVreplicationTestSuite checks if strategy options include --vreplicatoin-test-suite
   181  func (setting *DDLStrategySetting) IsVreplicationTestSuite() bool {
   182  	return setting.hasFlag(vreplicationTestSuite)
   183  }
   184  
   185  // IsAllowForeignKeysFlag checks if strategy options include --unsafe-allow-foreign-keys
   186  func (setting *DDLStrategySetting) IsAllowForeignKeysFlag() bool {
   187  	return setting.hasFlag(allowForeignKeysFlag)
   188  }
   189  
   190  // RuntimeOptions returns the options used as runtime flags for given strategy, removing any internal hint options
   191  func (setting *DDLStrategySetting) RuntimeOptions() []string {
   192  	opts, _ := shlex.Split(setting.Options)
   193  	validOpts := []string{}
   194  	for _, opt := range opts {
   195  		switch {
   196  		case isFlag(opt, declarativeFlag):
   197  		case isFlag(opt, skipTopoFlag):
   198  		case isFlag(opt, singletonFlag):
   199  		case isFlag(opt, singletonContextFlag):
   200  		case isFlag(opt, allowZeroInDateFlag):
   201  		case isFlag(opt, postponeLaunchFlag):
   202  		case isFlag(opt, postponeCompletionFlag):
   203  		case isFlag(opt, inOrderCompletionFlag):
   204  		case isFlag(opt, allowConcurrentFlag):
   205  		case isFlag(opt, preferInstantDDL):
   206  		case isFlag(opt, fastRangeRotationFlag):
   207  		case isFlag(opt, vreplicationTestSuite):
   208  		case isFlag(opt, allowForeignKeysFlag):
   209  		default:
   210  			validOpts = append(validOpts, opt)
   211  		}
   212  	}
   213  	return validOpts
   214  }
   215  
   216  // ToString returns a simple string representation of this instance
   217  func (setting *DDLStrategySetting) ToString() string {
   218  	return fmt.Sprintf("DDLStrategySetting: strategy=%v, options=%s", setting.Strategy, setting.Options)
   219  }