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 }