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 }