github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/backupccl/restore_schema_change_creation.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package backupccl 10 11 import ( 12 "context" 13 "fmt" 14 "strings" 15 16 "github.com/cockroachdb/cockroach/pkg/jobs" 17 "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" 18 "github.com/cockroachdb/cockroach/pkg/keys" 19 "github.com/cockroachdb/cockroach/pkg/kv" 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/util/log" 23 "github.com/cockroachdb/errors" 24 ) 25 26 // jobDescriptionFromMutationID returns a string description of a mutation with 27 // a particular ID on a given tableDesc, as well as the number of mutations 28 // associated with it. This is only used to reconstruct a job based off a 29 // mutation, namely during RESTORE. 30 // N.B.: This is only to get an indication of what the schema change was trying 31 // to do and is not meant to be the exact same description/SQL that was used in 32 // the original job. 33 func jobDescriptionFromMutationID( 34 tableDesc *sqlbase.TableDescriptor, id sqlbase.MutationID, 35 ) (string, int, error) { 36 var jobDescBuilder strings.Builder 37 mutationCount := 0 38 for _, m := range tableDesc.Mutations { 39 if m.MutationID == id { 40 mutationCount++ 41 // This is one of the mutations that we're looking for. 42 // Note that for primary key swaps, we want the last mutation in this list. 43 isPrimaryKeySwap := false 44 switch m.Descriptor_.(type) { 45 case *sqlbase.DescriptorMutation_PrimaryKeySwap: 46 isPrimaryKeySwap = true 47 } 48 49 if isPrimaryKeySwap { 50 // Primary key swaps have multiple mutations with the same ID, but we 51 // can derive the description from just the PrimaryKeySwap mutation 52 // which appears after the other mutations. 53 jobDescBuilder.Reset() 54 } else if jobDescBuilder.Len() != 0 { 55 jobDescBuilder.WriteString("; ") 56 } 57 58 if m.Rollback { 59 jobDescBuilder.WriteString("rollback for ") 60 } 61 62 jobDescBuilder.WriteString(fmt.Sprintf("schema change on %s ", tableDesc.Name)) 63 64 if !isPrimaryKeySwap { 65 switch m.Direction { 66 case sqlbase.DescriptorMutation_ADD: 67 jobDescBuilder.WriteString("adding ") 68 case sqlbase.DescriptorMutation_DROP: 69 jobDescBuilder.WriteString("dropping ") 70 default: 71 return "", 0, errors.Newf("unsupported mutation %+v, while restoring table %+v", m, tableDesc) 72 } 73 } 74 75 switch t := m.Descriptor_.(type) { 76 case *sqlbase.DescriptorMutation_Column: 77 jobDescBuilder.WriteString("column ") 78 jobDescBuilder.WriteString(t.Column.Name) 79 if m.Direction == sqlbase.DescriptorMutation_ADD { 80 jobDescBuilder.WriteString(" " + t.Column.Type.String()) 81 } 82 case *sqlbase.DescriptorMutation_Index: 83 jobDescBuilder.WriteString("index ") 84 jobDescBuilder.WriteString(t.Index.Name + " for " + tableDesc.Name + " (") 85 jobDescBuilder.WriteString(strings.Join(t.Index.ColumnNames, ", ")) 86 jobDescBuilder.WriteString(")") 87 case *sqlbase.DescriptorMutation_Constraint: 88 jobDescBuilder.WriteString("constraint ") 89 jobDescBuilder.WriteString(t.Constraint.Name) 90 case *sqlbase.DescriptorMutation_PrimaryKeySwap: 91 jobDescBuilder.WriteString("changing primary key to (") 92 newIndexID := t.PrimaryKeySwap.NewPrimaryIndexId 93 // Find the ADD INDEX mutation with the same mutation ID that is adding 94 // the new index. 95 for _, otherMut := range tableDesc.Mutations { 96 if indexMut, ok := otherMut.Descriptor_.(*sqlbase.DescriptorMutation_Index); ok && 97 indexMut.Index.ID == newIndexID && 98 otherMut.MutationID == m.MutationID && 99 m.Direction == sqlbase.DescriptorMutation_ADD { 100 jobDescBuilder.WriteString(strings.Join(indexMut.Index.ColumnNames, ", ")) 101 } 102 } 103 jobDescBuilder.WriteString(")") 104 default: 105 return "", 0, errors.Newf("unsupported mutation %+v, while restoring table %+v", m, tableDesc) 106 } 107 } 108 } 109 110 jobDesc := jobDescBuilder.String() 111 if mutationCount == 0 { 112 return "", 0, errors.Newf("could not find mutation %d on table %s (%d) while restoring", id, tableDesc.Name, tableDesc.ID) 113 } 114 return jobDesc, mutationCount, nil 115 } 116 117 // createSchemaChangeJobsFromMutations creates and runs jobs for any mutations 118 // on the table descriptor. It also updates tableDesc's MutationJobs to 119 // reference the new jobs. This is only used to reconstruct a job based off a 120 // mutation, namely during RESTORE. 121 func createSchemaChangeJobsFromMutations( 122 ctx context.Context, 123 jr *jobs.Registry, 124 codec keys.SQLCodec, 125 txn *kv.Txn, 126 username string, 127 tableDesc *sqlbase.TableDescriptor, 128 ) ([]*jobs.StartableJob, error) { 129 mutationJobs := make([]sqlbase.TableDescriptor_MutationJob, 0, len(tableDesc.MutationJobs)) 130 newJobs := make([]*jobs.StartableJob, 0, len(tableDesc.MutationJobs)) 131 for _, mj := range tableDesc.MutationJobs { 132 mutationID := mj.MutationID 133 jobDesc, mutationCount, err := jobDescriptionFromMutationID(tableDesc, mj.MutationID) 134 if err != nil { 135 return nil, err 136 } 137 spanList := make([]jobspb.ResumeSpanList, mutationCount) 138 for i := range spanList { 139 spanList[i] = jobspb.ResumeSpanList{ResumeSpans: []roachpb.Span{tableDesc.PrimaryIndexSpan(codec)}} 140 } 141 jobRecord := jobs.Record{ 142 // We indicate that this schema change was triggered by a RESTORE since 143 // the job description may not have all the information to fully describe 144 // the schema change. 145 Description: "RESTORING: " + jobDesc, 146 Username: username, 147 DescriptorIDs: sqlbase.IDs{tableDesc.GetID()}, 148 Details: jobspb.SchemaChangeDetails{ 149 TableID: tableDesc.ID, 150 MutationID: mutationID, 151 ResumeSpanList: spanList, 152 FormatVersion: jobspb.JobResumerFormatVersion, 153 }, 154 Progress: jobspb.SchemaChangeProgress{}, 155 } 156 newJob, err := jr.CreateStartableJobWithTxn(ctx, jobRecord, txn, nil) 157 if err != nil { 158 return nil, err 159 } 160 newMutationJob := sqlbase.TableDescriptor_MutationJob{ 161 MutationID: mutationID, 162 JobID: *newJob.ID(), 163 } 164 mutationJobs = append(mutationJobs, newMutationJob) 165 newJobs = append(newJobs, newJob) 166 167 log.Infof(ctx, "queued new schema change job %d for table %d, mutation %d", 168 newJob.ID(), tableDesc.ID, mutationID) 169 } 170 tableDesc.MutationJobs = mutationJobs 171 return newJobs, nil 172 }