github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/zone_config.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sql 12 13 import ( 14 "context" 15 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/config" 18 "github.com/cockroachdb/cockroach/pkg/config/zonepb" 19 "github.com/cockroachdb/cockroach/pkg/keys" 20 "github.com/cockroachdb/cockroach/pkg/kv" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 23 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 24 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 25 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 26 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 27 "github.com/cockroachdb/errors" 28 ) 29 30 func init() { 31 // TODO(marc): we use a hook to avoid a dependency on the sql package. We 32 // should probably move keys/protos elsewhere. 33 config.ZoneConfigHook = ZoneConfigHook 34 } 35 36 var errNoZoneConfigApplies = errors.New("no zone config applies") 37 38 // TODO(nvanbenschoten): determine how zone configurations fit into a 39 // multi-tenant cluster. Does each tenant have its own Zones table? Does KV have 40 // to make sure to look at the correct Zones according to the tenant prefix of 41 // its key range? See #48375. 42 var zoneConfigCodec = keys.TODOSQLCodec 43 44 // getZoneConfig recursively looks up entries in system.zones until an 45 // entry that applies to the object with the specified id is 46 // found. Returns the ID of the matching zone, its zone config, and an 47 // optional placeholder ID and config if the looked-for ID was a table 48 // with a zone config specifying only indexes and/or partitions. 49 // 50 // This function must be kept in sync with ascendZoneSpecifier. 51 // 52 // if getInheritedDefault is true, the direct zone configuration, if it exists, is 53 // ignored, and the default that would apply if it did not exist is returned instead. 54 func getZoneConfig( 55 id uint32, getKey func(roachpb.Key) (*roachpb.Value, error), getInheritedDefault bool, 56 ) (uint32, *zonepb.ZoneConfig, uint32, *zonepb.ZoneConfig, error) { 57 var placeholder *zonepb.ZoneConfig 58 var placeholderID uint32 59 if !getInheritedDefault { 60 // Look in the zones table. 61 if zoneVal, err := getKey(config.MakeZoneKey(id)); err != nil { 62 return 0, nil, 0, nil, err 63 } else if zoneVal != nil { 64 // We found a matching entry. 65 var zone zonepb.ZoneConfig 66 if err := zoneVal.GetProto(&zone); err != nil { 67 return 0, nil, 0, nil, err 68 } 69 // If the zone isn't a subzone placeholder, we're done. 70 if !zone.IsSubzonePlaceholder() { 71 return id, &zone, 0, nil, nil 72 } 73 // If the zone is just a placeholder for subzones, keep recursing 74 // up the hierarchy. 75 placeholder = &zone 76 placeholderID = id 77 } 78 } 79 80 // No zone config for this ID. We need to figure out if it's a table, so we 81 // look up its descriptor. 82 if descVal, err := getKey(sqlbase.MakeDescMetadataKey(zoneConfigCodec, sqlbase.ID(id))); err != nil { 83 return 0, nil, 0, nil, err 84 } else if descVal != nil { 85 var desc sqlbase.Descriptor 86 if err := descVal.GetProto(&desc); err != nil { 87 return 0, nil, 0, nil, err 88 } 89 if tableDesc := desc.Table(descVal.Timestamp); tableDesc != nil { 90 // This is a table descriptor. Look up its parent database zone config. 91 dbID, zone, _, _, err := getZoneConfig(uint32(tableDesc.ParentID), getKey, false /* getInheritedDefault */) 92 if err != nil { 93 return 0, nil, 0, nil, err 94 } 95 return dbID, zone, placeholderID, placeholder, nil 96 } 97 } 98 99 // Retrieve the default zone config, but only as long as that wasn't the ID 100 // we were trying to retrieve (avoid infinite recursion). 101 if id != keys.RootNamespaceID { 102 rootID, zone, _, _, err := getZoneConfig(keys.RootNamespaceID, getKey, false /* getInheritedDefault */) 103 if err != nil { 104 return 0, nil, 0, nil, err 105 } 106 return rootID, zone, placeholderID, placeholder, nil 107 } 108 109 // No descriptor or not a table. 110 return 0, nil, 0, nil, errNoZoneConfigApplies 111 } 112 113 // completeZoneConfig takes a zone config pointer and fills in the 114 // missing fields by following the chain of inheritance. 115 // In the worst case, will have to inherit from the default zone config. 116 // NOTE: This will not work for subzones. To complete subzones, find a complete 117 // parent zone (index or table) and apply InheritFromParent to it. 118 func completeZoneConfig( 119 cfg *zonepb.ZoneConfig, id uint32, getKey func(roachpb.Key) (*roachpb.Value, error), 120 ) error { 121 if cfg.IsComplete() { 122 return nil 123 } 124 // Check to see if its a table. If so, inherit from the database. 125 // For all other cases, inherit from the default. 126 if descVal, err := getKey(sqlbase.MakeDescMetadataKey(zoneConfigCodec, sqlbase.ID(id))); err != nil { 127 return err 128 } else if descVal != nil { 129 var desc sqlbase.Descriptor 130 if err := descVal.GetProto(&desc); err != nil { 131 return err 132 } 133 if tableDesc := desc.Table(descVal.Timestamp); tableDesc != nil { 134 _, dbzone, _, _, err := getZoneConfig(uint32(tableDesc.ParentID), getKey, false /* getInheritedDefault */) 135 if err != nil { 136 return err 137 } 138 cfg.InheritFromParent(dbzone) 139 } 140 } 141 142 // Check if zone is complete. If not, inherit from the default zone config 143 if cfg.IsComplete() { 144 return nil 145 } 146 _, defaultZone, _, _, err := getZoneConfig(keys.RootNamespaceID, getKey, false /* getInheritedDefault */) 147 if err != nil { 148 return err 149 } 150 cfg.InheritFromParent(defaultZone) 151 return nil 152 } 153 154 // ZoneConfigHook returns the zone config for the object with id using the 155 // cached system config. If keySuffix is within a subzone, the subzone's config 156 // is returned instead. The bool is set to true when the value returned is 157 // cached. 158 func ZoneConfigHook( 159 cfg *config.SystemConfig, id uint32, 160 ) (*zonepb.ZoneConfig, *zonepb.ZoneConfig, bool, error) { 161 getKey := func(key roachpb.Key) (*roachpb.Value, error) { 162 return cfg.GetValue(key), nil 163 } 164 zoneID, zone, _, placeholder, err := getZoneConfig( 165 id, getKey, false /* getInheritedDefault */) 166 if errors.Is(err, errNoZoneConfigApplies) { 167 return nil, nil, true, nil 168 } else if err != nil { 169 return nil, nil, false, err 170 } 171 if err = completeZoneConfig(zone, zoneID, getKey); err != nil { 172 return nil, nil, false, err 173 } 174 return zone, placeholder, true, nil 175 } 176 177 // GetZoneConfigInTxn looks up the zone and subzone for the specified 178 // object ID, index, and partition. 179 func GetZoneConfigInTxn( 180 ctx context.Context, 181 txn *kv.Txn, 182 id uint32, 183 index *sqlbase.IndexDescriptor, 184 partition string, 185 getInheritedDefault bool, 186 ) (uint32, *zonepb.ZoneConfig, *zonepb.Subzone, error) { 187 getKey := func(key roachpb.Key) (*roachpb.Value, error) { 188 kv, err := txn.Get(ctx, key) 189 if err != nil { 190 return nil, err 191 } 192 return kv.Value, nil 193 } 194 zoneID, zone, placeholderID, placeholder, err := getZoneConfig( 195 id, getKey, getInheritedDefault) 196 if err != nil { 197 return 0, nil, nil, err 198 } 199 if err = completeZoneConfig(zone, zoneID, getKey); err != nil { 200 return 0, nil, nil, err 201 } 202 var subzone *zonepb.Subzone 203 if index != nil { 204 if placeholder != nil { 205 if subzone = placeholder.GetSubzone(uint32(index.ID), partition); subzone != nil { 206 if indexSubzone := placeholder.GetSubzone(uint32(index.ID), ""); indexSubzone != nil { 207 subzone.Config.InheritFromParent(&indexSubzone.Config) 208 } 209 subzone.Config.InheritFromParent(zone) 210 return placeholderID, placeholder, subzone, nil 211 } 212 } else { 213 if subzone = zone.GetSubzone(uint32(index.ID), partition); subzone != nil { 214 if indexSubzone := zone.GetSubzone(uint32(index.ID), ""); indexSubzone != nil { 215 subzone.Config.InheritFromParent(&indexSubzone.Config) 216 } 217 subzone.Config.InheritFromParent(zone) 218 } 219 } 220 } 221 return zoneID, zone, subzone, nil 222 } 223 224 func zoneSpecifierNotFoundError(zs tree.ZoneSpecifier) error { 225 if zs.NamedZone != "" { 226 return pgerror.Newf( 227 pgcode.InvalidCatalogName, "zone %q does not exist", zs.NamedZone) 228 } else if zs.Database != "" { 229 return sqlbase.NewUndefinedDatabaseError(string(zs.Database)) 230 } else { 231 return sqlbase.NewUndefinedRelationError(&zs.TableOrIndex) 232 } 233 } 234 235 // resolveTableForZone ensures that the table part of the zone 236 // specifier is resolved (or resolvable) and, if the zone specifier 237 // points to an index, that the index name is expanded to a valid 238 // table. 239 // Returns res = nil if the zone specifier is not for a table or index. 240 func (p *planner) resolveTableForZone( 241 ctx context.Context, zs *tree.ZoneSpecifier, 242 ) (res *TableDescriptor, err error) { 243 if zs.TargetsIndex() { 244 var mutRes *MutableTableDescriptor 245 _, mutRes, err = expandMutableIndexName(ctx, p, &zs.TableOrIndex, true /* requireTable */) 246 if mutRes != nil { 247 res = mutRes.TableDesc() 248 } 249 } else if zs.TargetsTable() { 250 var immutRes *ImmutableTableDescriptor 251 p.runWithOptions(resolveFlags{skipCache: true}, func() { 252 flags := tree.ObjectLookupFlagsWithRequired() 253 flags.IncludeOffline = true 254 immutRes, err = resolver.ResolveExistingTableObject(ctx, p, &zs.TableOrIndex.Table, flags, resolver.ResolveAnyDescType) 255 }) 256 if err != nil { 257 return nil, err 258 } else if immutRes != nil { 259 res = immutRes.TableDesc() 260 } 261 } 262 return res, err 263 } 264 265 // resolveZone resolves a zone specifier to a zone ID. If the zone 266 // specifier points to a table, index or partition, the table part 267 // must be properly normalized already. It is the caller's 268 // responsibility to do this using e.g .resolveTableForZone(). 269 func resolveZone(ctx context.Context, txn *kv.Txn, zs *tree.ZoneSpecifier) (sqlbase.ID, error) { 270 errMissingKey := errors.New("missing key") 271 id, err := zonepb.ResolveZoneSpecifier(zs, 272 func(parentID uint32, name string) (uint32, error) { 273 found, id, err := sqlbase.LookupPublicTableID(ctx, txn, zoneConfigCodec, sqlbase.ID(parentID), name) 274 if err != nil { 275 return 0, err 276 } 277 if !found { 278 return 0, errMissingKey 279 } 280 return uint32(id), nil 281 }, 282 ) 283 if err != nil { 284 if errors.Is(err, errMissingKey) { 285 return 0, zoneSpecifierNotFoundError(*zs) 286 } 287 return 0, err 288 } 289 return sqlbase.ID(id), nil 290 } 291 292 func resolveSubzone( 293 zs *tree.ZoneSpecifier, table *sqlbase.TableDescriptor, 294 ) (*sqlbase.IndexDescriptor, string, error) { 295 if !zs.TargetsTable() || zs.TableOrIndex.Index == "" && zs.Partition == "" { 296 return nil, "", nil 297 } 298 299 indexName := string(zs.TableOrIndex.Index) 300 var index *sqlbase.IndexDescriptor 301 if indexName == "" { 302 index = &table.PrimaryIndex 303 indexName = index.Name 304 } else { 305 var err error 306 index, _, err = table.FindIndexByName(indexName) 307 if err != nil { 308 return nil, "", err 309 } 310 } 311 312 partitionName := string(zs.Partition) 313 if partitionName != "" { 314 if partitioning := index.FindPartitionByName(partitionName); partitioning == nil { 315 return nil, "", fmt.Errorf("partition %q does not exist on index %q", partitionName, indexName) 316 } 317 } 318 319 return index, partitionName, nil 320 } 321 322 func deleteRemovedPartitionZoneConfigs( 323 ctx context.Context, 324 txn *kv.Txn, 325 tableDesc *sqlbase.TableDescriptor, 326 idxDesc *sqlbase.IndexDescriptor, 327 oldPartDesc *sqlbase.PartitioningDescriptor, 328 newPartDesc *sqlbase.PartitioningDescriptor, 329 execCfg *ExecutorConfig, 330 ) error { 331 newNames := map[string]struct{}{} 332 for _, n := range newPartDesc.PartitionNames() { 333 newNames[n] = struct{}{} 334 } 335 removedNames := []string{} 336 for _, n := range oldPartDesc.PartitionNames() { 337 if _, exists := newNames[n]; !exists { 338 removedNames = append(removedNames, n) 339 } 340 } 341 if len(removedNames) == 0 { 342 return nil 343 } 344 zone, err := getZoneConfigRaw(ctx, txn, tableDesc.ID) 345 if err != nil { 346 return err 347 } else if zone == nil { 348 zone = zonepb.NewZoneConfig() 349 } 350 for _, n := range removedNames { 351 zone.DeleteSubzone(uint32(idxDesc.ID), n) 352 } 353 hasNewSubzones := false 354 _, err = writeZoneConfig(ctx, txn, tableDesc.ID, tableDesc, zone, execCfg, hasNewSubzones) 355 return err 356 }