Skip to content

Commit 7661812

Browse files
craig[bot]fqaziyuzefovich
committed
150350: catalog: allow offline descriptors for type hydrations r=fqazi a=fqazi Previously, when a function referenced an implicit table record type, the entire schema would become inaccessible if the referenced table was executing an IMPORT. This would happen because looking up a schema involves hydrating all type references, which include implicit table record types. To address this, this patch allows for offline descriptors during the type hydration logic, since this scenario is possible during an import. Additionally, a test-only assertion is added to confirm this. Fixes: #149988 Release note (bug fix): Fixed a bug where the entire schema would become inaccessible if a table was referenced as an implicit record type by a UDF while the table was undergoing an IMPORT. 150388: util/parquet,sql: clean up DOidWrapper handling a bit r=yuzefovich a=yuzefovich This commit removes redundant branch for `DOidWrapper` in `encodeArrayElement` (we already do the unwrapping at the top of the function, so this was dead code) as well as replaces the custom unwrap function in `parquet` package with the exported one. It also replaces some other unwrappers with that exported function. Epic: None Release note: None Co-authored-by: Faizan Qazi <[email protected]> Co-authored-by: Yahor Yuzefovich <[email protected]>
3 parents 54118cd + f4603e5 + 07cf6b2 commit 7661812

File tree

11 files changed

+130
-40
lines changed

11 files changed

+130
-40
lines changed

pkg/sql/catalog/descs/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ go_library(
6565
"//pkg/sql/sqlerrors",
6666
"//pkg/sql/sqlliveness",
6767
"//pkg/sql/types",
68+
"//pkg/util/buildutil",
6869
"//pkg/util/hlc",
6970
"//pkg/util/intsets",
7071
"//pkg/util/iterutil",

pkg/sql/catalog/descs/descriptor.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,17 @@ func getDescriptorsByID(
215215
if err := tc.finalizeDescriptors(ctx, txn, flags, descs, vls); err != nil {
216216
return err
217217
}
218-
// Hydration is skipped if "SkipHydration" flag is true.
219-
if err := tc.hydrateDescriptors(ctx, txn, flags, descs); err != nil {
220-
return err
221-
}
218+
// Apply any filters on descriptors before hydrating, since if a descriptor
219+
// is offline / dropped, we are doing needless work.
222220
for _, desc := range descs {
223221
if err := filterDescriptor(desc, flags); err != nil {
224222
return err
225223
}
226224
}
225+
// Hydration is skipped if "SkipHydration" flag is true.
226+
if err := tc.hydrateDescriptors(ctx, txn, flags, descs); err != nil {
227+
return err
228+
}
227229
return nil
228230
}
229231

pkg/sql/catalog/descs/hydrate.go

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import (
1414
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
1515
"github.com/cockroachdb/cockroach/pkg/sql/catalog/nstree"
1616
"github.com/cockroachdb/cockroach/pkg/sql/catalog/schemadesc"
17+
"github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc"
1718
"github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc"
1819
"github.com/cockroachdb/cockroach/pkg/sql/sem/catconstants"
1920
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
2021
"github.com/cockroachdb/cockroach/pkg/sql/sqlerrors"
22+
"github.com/cockroachdb/cockroach/pkg/util/buildutil"
2123
"github.com/cockroachdb/cockroach/pkg/util/intsets"
2224
"github.com/cockroachdb/cockroach/pkg/util/tracing"
2325
"github.com/cockroachdb/errors"
@@ -67,7 +69,7 @@ func (tc *Collection) hydrateDescriptors(
6769

6870
// hydrate mutable hydratable descriptors of the slice in-place.
6971
if !hydratableMutableIndexes.Empty() {
70-
typeFn := makeMutableTypeLookupFunc(tc, txn, descs)
72+
typeFn := makeMutableTypeLookupFunc(tc, txn, flags, descs)
7173
for _, i := range hydratableMutableIndexes.Ordered() {
7274
if err := hydrate(ctx, descs[i], typeFn); err != nil {
7375
return err
@@ -108,7 +110,7 @@ func (tc *Collection) hydrateDescriptors(
108110
}
109111

110112
func makeMutableTypeLookupFunc(
111-
tc *Collection, txn *kv.Txn, descs []catalog.Descriptor,
113+
tc *Collection, txn *kv.Txn, flags getterFlags, descs []catalog.Descriptor,
112114
) typedesc.TypeLookupFunc {
113115
var mc nstree.MutableCatalog
114116
for _, desc := range descs {
@@ -131,7 +133,7 @@ func makeMutableTypeLookupFunc(
131133
if id == catconstants.PublicSchemaID {
132134
return schemadesc.GetPublicSchema(), nil
133135
}
134-
flags := getterFlags{
136+
f := getterFlags{
135137
contextFlags: contextFlags{
136138
isMutable: true,
137139
},
@@ -140,9 +142,35 @@ func makeMutableTypeLookupFunc(
140142
withoutLeased: true,
141143
withoutHydration: skipHydration,
142144
},
145+
descFilters: descFilters{
146+
// For hydration, we will allow offline descriptors for lookups
147+
// of type descriptors. A descriptor can be offline either due to:
148+
// 1) IMPORT in which case we are looking at an implicit record type
149+
// 2) RESTORE which will always pick a new object to restore into, so
150+
// only the restore code paths can enter here, which will include
151+
// offline descriptors explicitly.
152+
withoutOffline: false,
153+
},
154+
}
155+
g := ByIDGetter(makeGetterBase(txn, tc, f))
156+
desc, err := g.Desc(ctx, id)
157+
if err != nil {
158+
return nil, err
143159
}
144-
g := ByIDGetter(makeGetterBase(txn, tc, flags))
145-
return g.Desc(ctx, id)
160+
// Sanity: If offline descriptors were asked to be ignored, then we should
161+
// only observe ones caused by IMPORT.
162+
if flags.descFilters.withoutOffline &&
163+
(desc.GetOfflineReason() != "" && desc.GetOfflineReason() != tabledesc.OfflineReasonImporting) {
164+
if buildutil.CrdbTestBuild {
165+
return nil, errors.AssertionFailedf("unexpected offline descriptor %s(%d): %s",
166+
desc.GetName(),
167+
desc.GetID(),
168+
desc.GetOfflineReason())
169+
}
170+
// For release builds surface an offline descriptor error.
171+
return nil, catalog.FilterOfflineDescriptor(desc)
172+
}
173+
return desc, nil
146174
}
147175
return makeTypeLookupFuncForHydration(mc, mutableLookupFunc)
148176
}
@@ -169,11 +197,34 @@ func makeImmutableTypeLookupFunc(
169197
},
170198
descFilters: descFilters{
171199
withoutDropped: true,
172-
withoutOffline: flags.descFilters.withoutOffline,
200+
// For hydration, we will allow offline descriptors for lookups
201+
// of type descriptors. A descriptor can be offline either due to:
202+
// 1) IMPORT in which case we are looking at an implicit record type
203+
// 2) RESTORE which will always pick a new object to restore into, so
204+
// only the restore code paths can enter here, which will include
205+
// offline descriptors explicitly.
206+
withoutOffline: false,
173207
},
174208
}
175209
g := ByIDGetter(makeGetterBase(txn, tc, f))
176-
return g.Desc(ctx, id)
210+
desc, err := g.Desc(ctx, id)
211+
if err != nil {
212+
return nil, err
213+
}
214+
// Sanity: If offline descriptors were asked to be ignored, then we should
215+
// only observe ones caused by IMPORT.
216+
if flags.descFilters.withoutOffline &&
217+
(desc.GetOfflineReason() != "" && desc.GetOfflineReason() != tabledesc.OfflineReasonImporting) {
218+
if buildutil.CrdbTestBuild {
219+
return nil, errors.AssertionFailedf("unexpected offline descriptor %s(%d): %s",
220+
desc.GetName(),
221+
desc.GetID(),
222+
desc.GetOfflineReason())
223+
}
224+
// For release builds surface an offline descriptor error.
225+
return nil, catalog.FilterOfflineDescriptor(desc)
226+
}
227+
return desc, nil
177228
}
178229
return makeTypeLookupFuncForHydration(mc, immutableLookupFunc)
179230
}

pkg/sql/catalog/resolver/resolver_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,64 @@ CREATE INDEX baz_idx ON baz (s);
764764
require.NoError(t, err)
765765
}
766766

767+
// TestOfflineImplicitTableRecord confirms that offline implicit record references
768+
// do not impact unrelated objects.
769+
func TestOfflineImplicitTableRecord(t *testing.T) {
770+
defer leaktest.AfterTest(t)()
771+
defer log.Scope(t).Close(t)
772+
773+
ctx := context.Background()
774+
srv, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{})
775+
defer srv.Stopper().Stop(ctx)
776+
s := srv.ApplicationLayer()
777+
tDB := sqlutils.MakeSQLRunner(sqlDB)
778+
779+
tDB.Exec(t, `
780+
CREATE DATABASE db1;
781+
USE db1;
782+
CREATE TABLE a (k INT PRIMARY KEY);
783+
784+
CREATE TABLE b (
785+
k INT PRIMARY KEY,
786+
a INT,
787+
b INT
788+
);
789+
790+
-- This function will have an implicit record reference to b.
791+
CREATE FUNCTION f(x INT) RETURNS b LANGUAGE SQL AS $$
792+
SELECT k, a, b FROM b WHERE k = x
793+
$$;
794+
`)
795+
// Mark the function as offline.
796+
var tblID descpb.ID
797+
err := sqltestutils.TestingDescsTxn(ctx, s, func(ctx context.Context, txn isql.Txn, col *descs.Collection) error {
798+
dbID, err := col.LookupDatabaseID(ctx, txn.KV(), "db1")
799+
if err != nil {
800+
return err
801+
}
802+
schemaID, err := col.LookupSchemaID(ctx, txn.KV(), dbID, "public")
803+
if err != nil {
804+
return err
805+
}
806+
tblID, err = col.LookupObjectID(ctx, txn.KV(), dbID, schemaID, "b")
807+
if err != nil {
808+
return err
809+
}
810+
mutTbl, err := col.MutableByID(txn.KV()).Table(ctx, tblID)
811+
if err != nil {
812+
return err
813+
}
814+
mutTbl.SetOffline(tabledesc.OfflineReasonImporting)
815+
return col.WriteDesc(ctx, false /*kvTrace*/, mutTbl, txn.KV())
816+
})
817+
require.NoError(t, err)
818+
// Confirm that we can still resolve things under the schema.
819+
tDB.Exec(t, "SELECT * FROM db1.public.a")
820+
// Confirm resolving the actual table hits an error.
821+
tDB.ExpectErr(t, "relation \"b\" is offline: importing", "SELECT * FROM db1.public.b")
822+
tDB.ExpectErr(t, "relation \"b\" is offline: importing", "SELECT * FROM f(0)")
823+
}
824+
767825
func newTableIndexName(db, sc, tbl, idx string) *tree.TableIndexName {
768826
name := &tree.TableIndexName{
769827
Index: tree.UnrestrictedName(idx),

pkg/sql/colconv/datum_to_vec.eg.go

Lines changed: 1 addition & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/colexec/execgen/cmd/execgen/rowtovec_gen.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ type familyWidthPair struct {
6969

7070
var rowToVecPreludeTmpls = map[familyWidthPair]string{
7171
{types.StringFamily, anyWidth}: `// Handle other STRING-related OID types, like oid.T_name.
72-
wrapper, ok := %[1]s.(*tree.DOidWrapper)
73-
if ok {
74-
%[1]s = wrapper.Wrapped
75-
}`,
72+
%[1]s = tree.UnwrapDOidWrapper(%[1]s)`,
7673
}
7774

7875
// rowToVecConversionTmpls maps the type families to the corresponding

pkg/sql/colexec/rowtovec.eg.go

Lines changed: 1 addition & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/opt/norm/scalar_funcs.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,7 @@ func (c *CustomFuncs) ConvertConstArrayToTuple(arr *memo.ConstExpr) opt.ScalarEx
245245
// collated string constant with the given locale.
246246
func (c *CustomFuncs) CastToCollatedString(str opt.ScalarExpr, locale string) opt.ScalarExpr {
247247
datum := str.(*memo.ConstExpr).Value
248-
if wrap, ok := datum.(*tree.DOidWrapper); ok {
249-
datum = wrap.Wrapped
250-
}
248+
datum = tree.UnwrapDOidWrapper(datum)
251249

252250
// buildCollated is a recursive helper function to handle casting arrays.
253251
var buildCollated func(datum tree.Datum) tree.Datum

pkg/sql/rowenc/valueside/array.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,6 @@ func encodeArrayElement(b []byte, d tree.Datum) ([]byte, error) {
336336
return encoding.EncodeUntaggedIntValue(b, int64(t.Oid)), nil
337337
case *tree.DCollatedString:
338338
return encoding.EncodeUntaggedBytesValue(b, []byte(t.Contents)), nil
339-
case *tree.DOidWrapper:
340-
return encodeArrayElement(b, t.Wrapped)
341339
case *tree.DEnum:
342340
return encoding.EncodeUntaggedBytesValue(b, t.PhysicalRep), nil
343341
case *tree.DJSON:

pkg/sql/stats/automatic_stats_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ func TestAnalyzeSystemTables(t *testing.T) {
903903
return descpb.ID(tableID)
904904
}
905905
for _, row := range rows {
906-
tableName := string(*row[0].(*tree.DOidWrapper).Wrapped.(*tree.DString))
906+
tableName := string(*tree.UnwrapDOidWrapper(row[0]).(*tree.DString))
907907
if DisallowedOnSystemTable(getTableID(tableName)) {
908908
continue
909909
}

0 commit comments

Comments
 (0)