vitess.io/vitess@v0.16.2/go/cmd/vtctldclient/command/schema.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 command 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "strings" 24 "time" 25 26 "github.com/spf13/cobra" 27 28 "vitess.io/vitess/go/cmd/vtctldclient/cli" 29 "vitess.io/vitess/go/protoutil" 30 "vitess.io/vitess/go/vt/logutil" 31 "vitess.io/vitess/go/vt/schema" 32 "vitess.io/vitess/go/vt/sqlparser" 33 "vitess.io/vitess/go/vt/topo/topoproto" 34 "vitess.io/vitess/go/vt/wrangler" 35 36 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 37 "vitess.io/vitess/go/vt/proto/vtrpc" 38 ) 39 40 var ( 41 // ApplySchema makes an ApplySchema gRPC call to a vtctld. 42 ApplySchema = &cobra.Command{ 43 Use: "ApplySchema [--allow-long-unavailability] [--ddl-strategy <strategy>] [--uuid <uuid> ...] [--migration-context <context>] [--wait-replicas-timeout <duration>] [--skip-preflight] [--caller-id <caller_id>] {--sql-file <file> | --sql <sql>} <keyspace>", 44 Short: "Applies the schema change to the specified keyspace on every primary, running in parallel on all shards. The changes are then propagated to replicas via replication.", 45 Long: `Applies the schema change to the specified keyspace on every primary, running in parallel on all shards. The changes are then propagated to replicas via replication. 46 47 If --allow-long-unavailability is set, schema changes affecting a large number of rows (and possibly incurring a longer period of unavailability) will not be rejected. 48 --ddl-strategy is used to instruct migrations via vreplication, gh-ost or pt-osc with optional parameters. 49 --migration-context allows the user to specify a custom migration context for online DDL migrations. 50 If --skip-preflight, SQL goes directly to shards without going through sanity checks. 51 52 The --uuid and --sql flags are repeatable, so they can be passed multiple times to build a list of values. 53 For --uuid, this is used like "--uuid $first_uuid --uuid $second_uuid". 54 For --sql, semi-colons and repeated values may be mixed, for example: 55 56 ApplySchema --sql "CREATE TABLE my_table; CREATE TABLE my_other_table" 57 ApplySchema --sql "CREATE TABLE my_table" --sql "CREATE TABLE my_other_table"`, 58 DisableFlagsInUseLine: true, 59 Args: cobra.ExactArgs(1), 60 RunE: commandApplySchema, 61 } 62 // GetSchema makes a GetSchema gRPC call to a vtctld. 63 GetSchema = &cobra.Command{ 64 Use: "GetSchema [--tables TABLES ...] [--exclude-tables EXCLUDE_TABLES ...] [{--table-names-only | --table-sizes-only}] [--include-views] alias", 65 Short: "Displays the full schema for a tablet, optionally restricted to the specified tables/views.", 66 DisableFlagsInUseLine: true, 67 Args: cobra.ExactArgs(1), 68 RunE: commandGetSchema, 69 } 70 // ReloadSchema makes a ReloadSchema gRPC call to a vtctld. 71 ReloadSchema = &cobra.Command{ 72 Use: "ReloadSchema <tablet_alias>", 73 Short: "Reloads the schema on a remote tablet.", 74 DisableFlagsInUseLine: true, 75 Args: cobra.ExactArgs(1), 76 RunE: commandReloadSchema, 77 } 78 // ReloadSchemaKeyspace makes a ReloadSchemaKeyspace gRPC call to a vtctld. 79 ReloadSchemaKeyspace = &cobra.Command{ 80 Use: "ReloadSchemaKeyspace [--concurrency=<concurrency>] [--include-primary] <keyspace>", 81 Short: "Reloads the schema on all tablets in a keyspace. This is done on a best-effort basis.", 82 DisableFlagsInUseLine: true, 83 Args: cobra.ExactArgs(1), 84 RunE: commandReloadSchemaKeyspace, 85 } 86 // ReloadSchemaShard makes a ReloadSchemaShard gRPC call to a vtctld. 87 ReloadSchemaShard = &cobra.Command{ 88 Use: "ReloadSchemaShard [--concurrency=10] [--include-primary] <keyspace/shard>", 89 Short: "Reloads the schema on all tablets in a shard. This is done on a best-effort basis.", 90 DisableFlagsInUseLine: true, 91 Args: cobra.ExactArgs(1), 92 RunE: commandReloadSchemaShard, 93 } 94 ) 95 96 var applySchemaOptions = struct { 97 AllowLongUnavailability bool 98 SQL []string 99 SQLFile string 100 DDLStrategy string 101 UUIDList []string 102 MigrationContext string 103 WaitReplicasTimeout time.Duration 104 SkipPreflight bool 105 CallerID string 106 }{} 107 108 func commandApplySchema(cmd *cobra.Command, args []string) error { 109 var allSQL string 110 if applySchemaOptions.SQLFile != "" { 111 if len(applySchemaOptions.SQL) != 0 { 112 return errors.New("Exactly one of --sql and --sql-file must be specified, not both.") // nolint 113 } 114 115 data, err := os.ReadFile(applySchemaOptions.SQLFile) 116 if err != nil { 117 return err 118 } 119 120 allSQL = string(data) 121 } else { 122 allSQL = strings.Join(applySchemaOptions.SQL, ";") 123 } 124 125 parts, err := sqlparser.SplitStatementToPieces(allSQL) 126 if err != nil { 127 return err 128 } 129 130 cli.FinishedParsing(cmd) 131 132 var cid *vtrpc.CallerID 133 if applySchemaOptions.CallerID != "" { 134 cid = &vtrpc.CallerID{Principal: applySchemaOptions.CallerID} 135 } 136 137 ks := cmd.Flags().Arg(0) 138 139 resp, err := client.ApplySchema(commandCtx, &vtctldatapb.ApplySchemaRequest{ 140 Keyspace: ks, 141 AllowLongUnavailability: applySchemaOptions.AllowLongUnavailability, 142 DdlStrategy: applySchemaOptions.DDLStrategy, 143 Sql: parts, 144 SkipPreflight: applySchemaOptions.SkipPreflight, 145 UuidList: applySchemaOptions.UUIDList, 146 MigrationContext: applySchemaOptions.MigrationContext, 147 WaitReplicasTimeout: protoutil.DurationToProto(applySchemaOptions.WaitReplicasTimeout), 148 CallerId: cid, 149 }) 150 if err != nil { 151 return err 152 } 153 154 fmt.Println(strings.Join(resp.UuidList, "\n")) 155 return nil 156 } 157 158 var getSchemaOptions = struct { 159 Tables []string 160 ExcludeTables []string 161 IncludeViews bool 162 TableNamesOnly bool 163 TableSizesOnly bool 164 TableSchemaOnly bool 165 }{} 166 167 func commandGetSchema(cmd *cobra.Command, args []string) error { 168 if getSchemaOptions.TableNamesOnly && getSchemaOptions.TableSizesOnly { 169 return errors.New("can only pass one of --table-names-only and --table-sizes-only") 170 } 171 172 alias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(0)) 173 if err != nil { 174 return err 175 } 176 177 cli.FinishedParsing(cmd) 178 179 resp, err := client.GetSchema(commandCtx, &vtctldatapb.GetSchemaRequest{ 180 TabletAlias: alias, 181 Tables: getSchemaOptions.Tables, 182 ExcludeTables: getSchemaOptions.ExcludeTables, 183 IncludeViews: getSchemaOptions.IncludeViews, 184 TableNamesOnly: getSchemaOptions.TableNamesOnly, 185 TableSizesOnly: getSchemaOptions.TableSizesOnly, 186 TableSchemaOnly: getSchemaOptions.TableSchemaOnly, 187 }) 188 if err != nil { 189 return err 190 } 191 192 if getSchemaOptions.TableNamesOnly { 193 names := make([]string, len(resp.Schema.TableDefinitions)) 194 195 for i, td := range resp.Schema.TableDefinitions { 196 names[i] = td.Name 197 } 198 199 fmt.Printf("%s\n", strings.Join(names, "\n")) 200 201 return nil 202 } 203 204 data, err := cli.MarshalJSON(resp.Schema) 205 if err != nil { 206 return err 207 } 208 209 fmt.Printf("%s\n", data) 210 211 return nil 212 } 213 214 func commandReloadSchema(cmd *cobra.Command, args []string) error { 215 tabletAlias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(0)) 216 if err != nil { 217 return err 218 } 219 220 cli.FinishedParsing(cmd) 221 222 _, err = client.ReloadSchema(commandCtx, &vtctldatapb.ReloadSchemaRequest{ 223 TabletAlias: tabletAlias, 224 }) 225 if err != nil { 226 return err 227 } 228 229 return nil 230 } 231 232 var reloadSchemaKeyspaceOptions = struct { 233 Concurrency uint32 234 IncludePrimary bool 235 }{ 236 Concurrency: 10, 237 } 238 239 func commandReloadSchemaKeyspace(cmd *cobra.Command, args []string) error { 240 cli.FinishedParsing(cmd) 241 242 logger := logutil.NewConsoleLogger() 243 resp, err := client.ReloadSchemaKeyspace(commandCtx, &vtctldatapb.ReloadSchemaKeyspaceRequest{ 244 Keyspace: cmd.Flags().Arg(0), 245 Concurrency: reloadSchemaKeyspaceOptions.Concurrency, 246 IncludePrimary: reloadSchemaKeyspaceOptions.IncludePrimary, 247 }) 248 if resp != nil { 249 for _, e := range resp.Events { 250 logutil.LogEvent(logger, e) 251 } 252 } 253 254 return err 255 } 256 257 var reloadSchemaShardOptions = struct { 258 Concurrency uint32 259 IncludePrimary bool 260 }{ 261 Concurrency: 10, 262 } 263 264 func commandReloadSchemaShard(cmd *cobra.Command, args []string) error { 265 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 266 if err != nil { 267 return err 268 } 269 270 cli.FinishedParsing(cmd) 271 272 logger := logutil.NewConsoleLogger() 273 resp, err := client.ReloadSchemaShard(commandCtx, &vtctldatapb.ReloadSchemaShardRequest{ 274 Keyspace: keyspace, 275 Shard: shard, 276 Concurrency: reloadSchemaShardOptions.Concurrency, 277 IncludePrimary: reloadSchemaShardOptions.IncludePrimary, 278 }) 279 if resp != nil { 280 for _, e := range resp.Events { 281 logutil.LogEvent(logger, e) 282 } 283 } 284 285 return err 286 } 287 288 func init() { 289 ApplySchema.Flags().BoolVar(&applySchemaOptions.AllowLongUnavailability, "allow-long-unavailability", false, "Allow large schema changes which incur a longer unavailability of the database.") 290 ApplySchema.Flags().StringVar(&applySchemaOptions.DDLStrategy, "ddl-strategy", string(schema.DDLStrategyDirect), "Online DDL strategy, compatible with @@ddl_strategy session variable (examples: 'gh-ost', 'pt-osc', 'gh-ost --max-load=Threads_running=100'.") 291 ApplySchema.Flags().StringSliceVar(&applySchemaOptions.UUIDList, "uuid", nil, "Optional, comma-delimited, repeatable, explicit UUIDs for migration. If given, must match number of DDL changes.") 292 ApplySchema.Flags().StringVar(&applySchemaOptions.MigrationContext, "migration-context", "", "For Online DDL, optionally supply a custom unique string used as context for the migration(s) in this command. By default a unique context is auto-generated by Vitess.") 293 ApplySchema.Flags().DurationVar(&applySchemaOptions.WaitReplicasTimeout, "wait-replicas-timeout", wrangler.DefaultWaitReplicasTimeout, "Amount of time to wait for replicas to receive the schema change via replication.") 294 ApplySchema.Flags().BoolVar(&applySchemaOptions.SkipPreflight, "skip-preflight", false, "Skip pre-apply schema checks, and directly forward schema change query to shards.") 295 ApplySchema.Flags().StringVar(&applySchemaOptions.CallerID, "caller-id", "", "Effective caller ID used for the operation and should map to an ACL name which grants this identity the necessary permissions to perform the operation (this is only necessary when strict table ACLs are used).") 296 ApplySchema.Flags().StringArrayVar(&applySchemaOptions.SQL, "sql", nil, "Semicolon-delimited, repeatable SQL commands to apply. Exactly one of --sql|--sql-file is required.") 297 ApplySchema.Flags().StringVar(&applySchemaOptions.SQLFile, "sql-file", "", "Path to a file containing semicolon-delimited SQL commands to apply. Exactly one of --sql|--sql-file is required.") 298 299 Root.AddCommand(ApplySchema) 300 301 GetSchema.Flags().StringSliceVar(&getSchemaOptions.Tables, "tables", nil, "List of tables to display the schema for. Each is either an exact match, or a regular expression of the form `/regexp/`.") 302 GetSchema.Flags().StringSliceVar(&getSchemaOptions.ExcludeTables, "exclude-tables", nil, "List of tables to exclude from the result. Each is either an exact match, or a regular expression of the form `/regexp/`.") 303 GetSchema.Flags().BoolVar(&getSchemaOptions.IncludeViews, "include-views", false, "Includes views in the output in addition to base tables.") 304 GetSchema.Flags().BoolVarP(&getSchemaOptions.TableNamesOnly, "table-names-only", "n", false, "Display only table names in the result.") 305 GetSchema.Flags().BoolVarP(&getSchemaOptions.TableSizesOnly, "table-sizes-only", "s", false, "Display only size information for matching tables. Ignored if --table-names-only is set.") 306 GetSchema.Flags().BoolVarP(&getSchemaOptions.TableSchemaOnly, "table-schema-only", "", false, "Skip introspecting columns and fields metadata.") 307 308 Root.AddCommand(GetSchema) 309 310 Root.AddCommand(ReloadSchema) 311 312 ReloadSchemaKeyspace.Flags().Uint32Var(&reloadSchemaKeyspaceOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") 313 ReloadSchemaKeyspace.Flags().BoolVar(&reloadSchemaKeyspaceOptions.IncludePrimary, "include-primary", false, "Also reload the primary tablets.") 314 Root.AddCommand(ReloadSchemaKeyspace) 315 316 ReloadSchemaShard.Flags().Uint32Var(&reloadSchemaShardOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") 317 ReloadSchemaShard.Flags().BoolVar(&reloadSchemaShardOptions.IncludePrimary, "include-primary", false, "Also reload the primary tablet.") 318 Root.AddCommand(ReloadSchemaShard) 319 }