github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/catalog/catalogkv/physical_accessor.go (about)

     1  // Copyright 2020 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 catalogkv
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/keys"
    18  	"github.com/cockroachdb/cockroach/pkg/kv"
    19  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/catalog"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    24  	"github.com/cockroachdb/cockroach/pkg/util/log"
    25  )
    26  
    27  // UncachedPhysicalAccessor implements direct access to sql object descriptors
    28  // stored in system tables without any kind of caching.
    29  type UncachedPhysicalAccessor struct {
    30  	// Used to avoid allocations.
    31  	tn tree.TableName
    32  }
    33  
    34  var _ catalog.Accessor = UncachedPhysicalAccessor{}
    35  
    36  // GetDatabaseDesc implements the Accessor interface.
    37  func (a UncachedPhysicalAccessor) GetDatabaseDesc(
    38  	ctx context.Context,
    39  	txn *kv.Txn,
    40  	codec keys.SQLCodec,
    41  	name string,
    42  	flags tree.DatabaseLookupFlags,
    43  ) (desc *sqlbase.DatabaseDescriptor, err error) {
    44  	if name == sqlbase.SystemDB.Name {
    45  		// We can't return a direct reference to SystemDB, because the
    46  		// caller expects a private object that can be modified in-place.
    47  		sysDB := sqlbase.MakeSystemDatabaseDesc()
    48  		return &sysDB, nil
    49  	}
    50  
    51  	found, descID, err := sqlbase.LookupDatabaseID(ctx, txn, codec, name)
    52  	if err != nil {
    53  		return nil, err
    54  	} else if !found {
    55  		if flags.Required {
    56  			return nil, sqlbase.NewUndefinedDatabaseError(name)
    57  		}
    58  		return nil, nil
    59  	}
    60  
    61  	return GetDatabaseDescByID(ctx, txn, codec, descID)
    62  }
    63  
    64  // IsValidSchema implements the Accessor interface.
    65  func (a UncachedPhysicalAccessor) IsValidSchema(
    66  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, dbID sqlbase.ID, scName string,
    67  ) (bool, sqlbase.ID, error) {
    68  	return ResolveSchemaID(ctx, txn, codec, dbID, scName)
    69  }
    70  
    71  // GetObjectNames implements the Accessor interface.
    72  func (a UncachedPhysicalAccessor) GetObjectNames(
    73  	ctx context.Context,
    74  	txn *kv.Txn,
    75  	codec keys.SQLCodec,
    76  	dbDesc *sqlbase.DatabaseDescriptor,
    77  	scName string,
    78  	flags tree.DatabaseListFlags,
    79  ) (tree.TableNames, error) {
    80  	ok, schemaID, err := a.IsValidSchema(ctx, txn, codec, dbDesc.ID, scName)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	if !ok {
    85  		if flags.Required {
    86  			tn := tree.MakeTableNameWithSchema(tree.Name(dbDesc.Name), tree.Name(scName), "")
    87  			return nil, sqlbase.NewUnsupportedSchemaUsageError(tree.ErrString(&tn.ObjectNamePrefix))
    88  		}
    89  		return nil, nil
    90  	}
    91  
    92  	log.Eventf(ctx, "fetching list of objects for %q", dbDesc.Name)
    93  	prefix := sqlbase.NewTableKey(dbDesc.ID, schemaID, "").Key(codec)
    94  	sr, err := txn.Scan(ctx, prefix, prefix.PrefixEnd(), 0)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	// We scan both the deprecated and new system.namespace table to get the
    99  	// complete list of tables. Duplicate entries may be present in both the tables,
   100  	// so we filter those out. If a duplicate entry is present, it doesn't matter
   101  	// which table it is read from -- system.namespace entries are never modified,
   102  	// they are only added/deleted. Entries are written to only one table, so
   103  	// duplicate entries must have been copied over during migration. Thus, it
   104  	// doesn't matter which table (newer/deprecated) the value is read from.
   105  	//
   106  	// It may seem counter-intuitive to read both tables if we have found data in
   107  	// the newer version. The migration copied all entries from the deprecated
   108  	// system.namespace and all new entries after the cluster version bump are added
   109  	// to the new system.namespace. Why do we do this then?
   110  	// This is to account the scenario where a table was created before
   111  	// the cluster version was bumped, but after the older system.namespace was
   112  	// copied into the newer system.namespace. Objects created in this window
   113  	// will only be present in the older system.namespace. To account for this
   114  	// scenario, we must do this filtering logic.
   115  	// TODO(solon): This complexity can be removed in  20.2.
   116  	dprefix := sqlbase.NewDeprecatedTableKey(dbDesc.ID, "").Key(codec)
   117  	dsr, err := txn.Scan(ctx, dprefix, dprefix.PrefixEnd(), 0)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	alreadySeen := make(map[string]bool)
   123  	var tableNames tree.TableNames
   124  
   125  	for _, row := range sr {
   126  		_, tableName, err := encoding.DecodeUnsafeStringAscending(bytes.TrimPrefix(
   127  			row.Key, prefix), nil)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  		alreadySeen[tableName] = true
   132  		tn := tree.MakeTableNameWithSchema(tree.Name(dbDesc.Name), tree.Name(scName), tree.Name(tableName))
   133  		tn.ExplicitCatalog = flags.ExplicitPrefix
   134  		tn.ExplicitSchema = flags.ExplicitPrefix
   135  		tableNames = append(tableNames, tn)
   136  	}
   137  
   138  	for _, row := range dsr {
   139  		// Decode using the deprecated key prefix.
   140  		_, tableName, err := encoding.DecodeUnsafeStringAscending(
   141  			bytes.TrimPrefix(row.Key, dprefix), nil)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  		if alreadySeen[tableName] {
   146  			continue
   147  		}
   148  		tn := tree.MakeTableNameWithSchema(tree.Name(dbDesc.Name), tree.Name(scName), tree.Name(tableName))
   149  		tn.ExplicitCatalog = flags.ExplicitPrefix
   150  		tn.ExplicitSchema = flags.ExplicitPrefix
   151  		tableNames = append(tableNames, tn)
   152  	}
   153  	return tableNames, nil
   154  }
   155  
   156  // GetObjectDesc implements the Accessor interface.
   157  func (a UncachedPhysicalAccessor) GetObjectDesc(
   158  	ctx context.Context,
   159  	txn *kv.Txn,
   160  	settings *cluster.Settings,
   161  	codec keys.SQLCodec,
   162  	db, schema, object string,
   163  	flags tree.ObjectLookupFlags,
   164  ) (catalog.Descriptor, error) {
   165  	// Look up the database ID.
   166  	dbID, err := GetDatabaseID(ctx, txn, codec, db, flags.Required)
   167  	if err != nil || dbID == sqlbase.InvalidID {
   168  		// dbID can still be invalid if required is false and the database is not found.
   169  		return nil, err
   170  	}
   171  
   172  	ok, schemaID, err := a.IsValidSchema(ctx, txn, codec, dbID, schema)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	if !ok {
   177  		if flags.Required {
   178  			a.tn = tree.MakeTableNameWithSchema(tree.Name(db), tree.Name(schema), tree.Name(object))
   179  			return nil, sqlbase.NewUnsupportedSchemaUsageError(tree.ErrString(&a.tn))
   180  		}
   181  		return nil, nil
   182  	}
   183  
   184  	// Try to use the system name resolution bypass. This avoids a hotspot.
   185  	// Note: we can only bypass name to ID resolution. The desc
   186  	// lookup below must still go through KV because system descriptors
   187  	// can be modified on a running cluster.
   188  	descID := sqlbase.LookupSystemTableDescriptorID(ctx, settings, codec, dbID, object)
   189  	if descID == sqlbase.InvalidID {
   190  		var found bool
   191  		found, descID, err = sqlbase.LookupObjectID(ctx, txn, codec, dbID, schemaID, object)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		if !found {
   196  			// KV name resolution failed.
   197  			if flags.Required {
   198  				a.tn = tree.MakeTableNameWithSchema(tree.Name(db), tree.Name(schema), tree.Name(object))
   199  				return nil, sqlbase.NewUndefinedObjectError(&a.tn, flags.DesiredObjectKind)
   200  			}
   201  			return nil, nil
   202  		}
   203  	}
   204  
   205  	// Look up the object using the discovered database descriptor.
   206  	rawDesc, err := GetDescriptorByID(ctx, txn, codec, descID)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	switch desc := rawDesc.(type) {
   211  	case *sqlbase.TableDescriptor:
   212  		// We have a descriptor, allow it to be in the PUBLIC or ADD state. Possibly
   213  		// OFFLINE if the relevant flag is set.
   214  		acceptableStates := map[sqlbase.TableDescriptor_State]bool{
   215  			sqlbase.TableDescriptor_ADD:     true,
   216  			sqlbase.TableDescriptor_PUBLIC:  true,
   217  			sqlbase.TableDescriptor_OFFLINE: flags.IncludeOffline,
   218  		}
   219  		if acceptableStates[desc.State] {
   220  			// Immediately after a RENAME an old name still points to the
   221  			// descriptor during the drain phase for the name. Do not
   222  			// return a descriptor during draining.
   223  			//
   224  			// The second or condition ensures that clusters < 20.1 access the
   225  			// system.namespace_deprecated table when selecting from system.namespace.
   226  			// As this table can not be renamed by users, it is okay that the first
   227  			// check fails.
   228  			if desc.Name == object ||
   229  				object == sqlbase.NamespaceTableName && db == sqlbase.SystemDB.Name {
   230  				if flags.RequireMutable {
   231  					return sqlbase.NewMutableExistingTableDescriptor(*desc), nil
   232  				}
   233  				return sqlbase.NewImmutableTableDescriptor(*desc), nil
   234  			}
   235  		}
   236  		return nil, nil
   237  	case *sqlbase.TypeDescriptor:
   238  		if flags.RequireMutable {
   239  			return sqlbase.NewMutableExistingTypeDescriptor(*desc), nil
   240  		}
   241  		return sqlbase.NewImmutableTypeDescriptor(*desc), nil
   242  	default:
   243  		return nil, nil
   244  	}
   245  }