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

     1  // Copyright 2019 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 sqlbase
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/clusterversion"
    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/util/log"
    21  )
    22  
    23  // This file abstracts all accesses to system.namespace. Entries in
    24  // system.namespace are never modified. We only write new entries or delete
    25  // existing entries.
    26  //
    27  // As of 20.1, the older system.namespace table is marked deprecated. It is
    28  // replaced by a new system.namespace table that has an additional parentSchemaID
    29  // column, which allows support for additional physical schemas. The new
    30  // namespace table is also created outside the system config range, so it is no
    31  // longer gossiped.
    32  //
    33  // To ensure accesses are seamless across mixed version clusters, >= 20.1 clusters,
    34  // and during the upgrade process, the following functions should be used
    35  // for adding/removing entries.
    36  // TODO(solon): The fallback semantics will no longer be required in 20.2.
    37  // This code should be cleaned up then, to only access the new system.namespace
    38  // table.
    39  
    40  // Deleting entries from system.namespace:
    41  // Entries are deleted from both the deprecated and newer system.namespace, if
    42  // they exist in them.
    43  // Entries may be in one/both of the tables.
    44  // - In a mixed version (19.2/20.1) cluster, the entry only exists in the older
    45  // system.namespace.
    46  // - In a 20.1 cluster, if the entry was created before upgrade, the entry exists
    47  // in both the tables.
    48  // - In a 20.1 cluster, if the entry was created after upgrade, it exists only
    49  // in the newer system.namespace.
    50  //
    51  // Adding entries to system.namespace:
    52  // Entries are added to either the new system.namespace or the deprecated
    53  // system.namespace, depending on the cluster version. Methods supplied by
    54  // this file only abstract key construction based on the cluster settings.
    55  // It is not safe to construct keys and do removals/lookups using them, as
    56  // this can cause issues in mixed version clusters. Please use the provided
    57  // removal/lookup methods for those cases.
    58  
    59  // RemoveObjectNamespaceEntry removes entries from both the deprecated and
    60  // new system.namespace table (if one exists).
    61  func RemoveObjectNamespaceEntry(
    62  	ctx context.Context,
    63  	txn *kv.Txn,
    64  	codec keys.SQLCodec,
    65  	parentID ID,
    66  	parentSchemaID ID,
    67  	name string,
    68  	KVTrace bool,
    69  ) error {
    70  	b := txn.NewBatch()
    71  	var toDelete []DescriptorKey
    72  	// The (parentID, name) mapping could be in either the new system.namespace
    73  	// or the deprecated version. Thus we try to remove the mapping from both.
    74  	if parentID == keys.RootNamespaceID {
    75  		toDelete = append(toDelete, NewDatabaseKey(name))
    76  		// TODO(solon): This can be completely removed in 20.2.
    77  		toDelete = append(toDelete, NewDeprecatedDatabaseKey(name))
    78  	} else if parentSchemaID == keys.RootNamespaceID {
    79  		// Schemas were introduced in 20.1.
    80  		toDelete = append(toDelete, NewSchemaKey(parentID, name))
    81  	} else {
    82  		toDelete = append(toDelete, NewTableKey(parentID, parentSchemaID, name))
    83  		// TODO(solon): This can be completely removed in 20.2.
    84  		toDelete = append(toDelete, NewDeprecatedTableKey(parentID, name))
    85  	}
    86  	for _, delKey := range toDelete {
    87  		if KVTrace {
    88  			log.VEventf(ctx, 2, "Del %s", delKey)
    89  		}
    90  		b.Del(delKey.Key(codec))
    91  	}
    92  	return txn.Run(ctx, b)
    93  }
    94  
    95  // RemovePublicTableNamespaceEntry is a wrapper around RemoveObjectNamespaceEntry
    96  // for public tables.
    97  func RemovePublicTableNamespaceEntry(
    98  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, parentID ID, name string,
    99  ) error {
   100  	return RemoveObjectNamespaceEntry(ctx, txn, codec, parentID, keys.PublicSchemaID, name, false /* KVTrace */)
   101  }
   102  
   103  // RemoveSchemaNamespaceEntry is a wrapper around RemoveObjectNamespaceEntry
   104  // for schemas.
   105  func RemoveSchemaNamespaceEntry(
   106  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, parentID ID, name string,
   107  ) error {
   108  	return RemoveObjectNamespaceEntry(ctx, txn, codec, parentID, keys.RootNamespaceID, name, false /* KVTrace */)
   109  }
   110  
   111  // RemoveDatabaseNamespaceEntry is a wrapper around RemoveObjectNamespaceEntry
   112  // for databases.
   113  func RemoveDatabaseNamespaceEntry(
   114  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, name string, KVTrace bool,
   115  ) error {
   116  	return RemoveObjectNamespaceEntry(ctx, txn, codec, keys.RootNamespaceID, keys.RootNamespaceID, name, KVTrace)
   117  }
   118  
   119  // MakeObjectNameKey returns a key in the system.namespace table for
   120  // a given parentID and name, based on the cluster version.
   121  // - If cluster version >= 20.1, the key is in the new system.namespace table.
   122  // - If cluster version < 20.1, the key is in the deprecated system.namespace table.
   123  // - The parentSchemaID field is ignored in < 20.1 clusters.
   124  func MakeObjectNameKey(
   125  	ctx context.Context, settings *cluster.Settings, parentID ID, parentSchemaID ID, name string,
   126  ) DescriptorKey {
   127  	// TODO(solon): This if condition can be removed in 20.2
   128  	if !settings.Version.IsActive(ctx, clusterversion.VersionNamespaceTableWithSchemas) {
   129  		return NewDeprecatedTableKey(parentID, name)
   130  	}
   131  	var key DescriptorKey
   132  	if parentID == keys.RootNamespaceID {
   133  		key = NewDatabaseKey(name)
   134  	} else if parentSchemaID == keys.RootNamespaceID {
   135  		key = NewSchemaKey(parentID, name)
   136  	} else {
   137  		key = NewTableKey(parentID, parentSchemaID, name)
   138  	}
   139  	return key
   140  }
   141  
   142  // MakePublicTableNameKey is a wrapper around MakeObjectNameKey for public tables.
   143  func MakePublicTableNameKey(
   144  	ctx context.Context, settings *cluster.Settings, parentID ID, name string,
   145  ) DescriptorKey {
   146  	return MakeObjectNameKey(ctx, settings, parentID, keys.PublicSchemaID, name)
   147  }
   148  
   149  // MakeDatabaseNameKey is a wrapper around MakeObjectNameKey for databases.
   150  func MakeDatabaseNameKey(
   151  	ctx context.Context, settings *cluster.Settings, name string,
   152  ) DescriptorKey {
   153  	return MakeObjectNameKey(ctx, settings, keys.RootNamespaceID, keys.RootNamespaceID, name)
   154  }
   155  
   156  // LookupObjectID returns the ObjectID for the given
   157  // (parentID, parentSchemaID, name) supplied. If cluster version < 20.1,
   158  // the parentSchemaID is ignored.
   159  func LookupObjectID(
   160  	ctx context.Context,
   161  	txn *kv.Txn,
   162  	codec keys.SQLCodec,
   163  	parentID ID,
   164  	parentSchemaID ID,
   165  	name string,
   166  ) (bool, ID, error) {
   167  	var key DescriptorKey
   168  	if parentID == keys.RootNamespaceID {
   169  		key = NewDatabaseKey(name)
   170  	} else if parentSchemaID == keys.RootNamespaceID {
   171  		key = NewSchemaKey(parentID, name)
   172  	} else {
   173  		key = NewTableKey(parentID, parentSchemaID, name)
   174  	}
   175  	log.Eventf(ctx, "looking up descriptor ID for name key %q", key.Key(codec))
   176  	res, err := txn.Get(ctx, key.Key(codec))
   177  	if err != nil {
   178  		return false, InvalidID, err
   179  	}
   180  	if res.Exists() {
   181  		return true, ID(res.ValueInt()), nil
   182  	}
   183  	// If the key wasn't found in the new system.namespace table, it may still
   184  	// exist in the deprecated system.namespace in the case of mixed version clusters.
   185  	// TODO(solon): This can be removed in 20.2.
   186  
   187  	// This fallback logic is only required if the table is under the public schema
   188  	// or we are resolving a database.
   189  	// Without this check, we can run into the following problem:
   190  	// - Persistent table `t` was created before the cluster upgrade, so it is
   191  	// present in both the old & new system.namespace table.
   192  	// - A session creates a temporary table `u`, which means the session has a
   193  	// valid temporary schema.
   194  	// - If this session explicitly accesses `pg_temp.t`, it should fail -- but
   195  	// without this check, `pg_temp.t` will return the permanent table instead.
   196  	if parentSchemaID != keys.PublicSchemaID && parentSchemaID != keys.RootNamespaceID {
   197  		return false, InvalidID, nil
   198  	}
   199  
   200  	var dKey DescriptorKey
   201  	if parentID == keys.RootNamespaceID {
   202  		dKey = NewDeprecatedDatabaseKey(name)
   203  	} else {
   204  		dKey = NewDeprecatedTableKey(parentID, name)
   205  	}
   206  	log.Eventf(ctx, "looking up descriptor ID for name key %q", dKey.Key(codec))
   207  	res, err = txn.Get(ctx, dKey.Key(codec))
   208  	if err != nil {
   209  		return false, InvalidID, err
   210  	}
   211  	if res.Exists() {
   212  		return true, ID(res.ValueInt()), nil
   213  	}
   214  	return false, InvalidID, nil
   215  }
   216  
   217  // LookupPublicTableID is a wrapper around LookupObjectID for public tables.
   218  func LookupPublicTableID(
   219  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, parentID ID, name string,
   220  ) (bool, ID, error) {
   221  	return LookupObjectID(ctx, txn, codec, parentID, keys.PublicSchemaID, name)
   222  }
   223  
   224  // LookupDatabaseID is  a wrapper around LookupObjectID for databases.
   225  func LookupDatabaseID(
   226  	ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, name string,
   227  ) (bool, ID, error) {
   228  	return LookupObjectID(ctx, txn, codec, keys.RootNamespaceID, keys.RootNamespaceID, name)
   229  }