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  }