sql/schemachanger: implement DROP OWNED BY#82936
sql/schemachanger: implement DROP OWNED BY#82936craig[bot] merged 1 commit intocockroachdb:masterfrom
Conversation
Xiang-Gu
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @jasonmchan and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 44 at r1 (raw file):
var toCheckBackrefs []descpb.ID // Lookup all objects in the current database.
curious: can a user own objects across databases? If so, what's the expected behavior if we DRO OWNED BY him?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 56 at r1 (raw file):
}) objects = append(objects, sp.SchemaID) })
Is this block used to retrieve all object IDs in db?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 61 at r1 (raw file):
for _, id := range objects { elts := b.QueryByID(id) _, _, owner := scpb.FindOwner(elts)
is it possible that elts does not have a Owner element?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 88 at r1 (raw file):
for _, role := range normalizedRoles { if e.UserName == role.Normalized() && e.UserName != b.SessionData().User().Normalized() { dropElement(b, e)
you probably need to change this to simply b.Drop(e) with #82907 coming out soon.
pkg/sql/logictest/testdata/logic_test/drop_owned_by line 83 at r1 (raw file):
SHOW TABLES FROM public ----
-
when
testuserdrops all objects he owned, why areuandv1also dropped (which are owned bytestuser2)? -
in addition to dropping the objects, we also revoke the privileges from users. Do we tests this behavior in the logic test?
pkg/sql/logictest/testdata/logic_test/drop_owned_by line 212 at r1 (raw file):
statement error pq: cannot drop objects owned by role "admin" because they are required by the database system DROP OWNED BY admin
Can we add some more comments on the test? It's a bit hard to follow and remember what's been dropped and what's been created by which user.
You can break up the tests into subtests and each subtest focus on a pattern/theme/case of tests.
Xiang-Gu
left a comment
There was a problem hiding this comment.
Good job! Keep up the good work!
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @jasonmchan and @Xiang-Gu)
eb377e6 to
37304b7
Compare
jasonmchan
left a comment
There was a problem hiding this comment.
TFTR!
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @Xiang-Gu)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 44 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
curious: can a user own objects across databases? If so, what's the expected behavior if we
DRO OWNED BY him?
Yes. DROP OWNED BY drops objects in the current database (see Postgres docs)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 56 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
Is this block used to retrieve all object IDs in
db?
Yes, would it be clearer if I renamed objects to objectIDs?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 61 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
is it possible that
eltsdoes not have a Owner element?
I've been working under this assumption, but maybe someone else can confirm. My understanding is that all objects have owners, which is enforced when we decompose descriptors into elements.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 88 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
you probably need to change this to simply
b.Drop(e)with #82907 coming out soon.
Thanks for letting me know! I didn't pay close attention to the difference 😅
pkg/sql/logictest/testdata/logic_test/drop_owned_by line 83 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
when
testuserdrops all objects he owned, why areuandv1also dropped (which are owned bytestuser2)?in addition to dropping the objects, we also revoke the privileges from users. Do we tests this behavior in the logic test?
- The previous statement
DROP OWNED BY testuser2drops them. The intention here was to testCASCADE; I've added a TODO comment to indicate this. - Yes
pkg/sql/logictest/testdata/logic_test/drop_owned_by line 212 at r1 (raw file):
Previously, Xiang-Gu (Xiang Gu) wrote…
Can we add some more comments on the test? It's a bit hard to follow and remember what's been dropped and what's been created by which user.
You can break up the tests into subtests and each subtest focus on a pattern/theme/case of tests.
I've attempted to improve the readability of the tests. Hopefully they're clearer now!
37304b7 to
f93df50
Compare
chengxiong-ruan
left a comment
There was a problem hiding this comment.
Nice work :)
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @chengxiong-ruan, @jasonmchan, and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/dependencies.go line 172 at r2 (raw file):
// MemberOfWithAdminOption looks up the roles that 'member' is a member of. MemberOfWithAdminOption(ctx context.Context, member username.SQLUsername) (map[username.SQLUsername]bool, error)
the go convention is to use map[username.SQLUsername]struct{} here to avoid the confusion of true and false boolean values :)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/dependencies.go line 177 at r2 (raw file):
// CheckMemberOf returns true iff the current user has membership in the // specified role. IsMemberOf(member username.SQLUsername) bool
IsMemberOf -> CurrentUserIsMemberOf?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 88 at r1 (raw file):
Previously, jasonmchan (Jason Chan) wrote…
Thanks for letting me know! I didn't pay close attention to the difference 😅
ha, nice
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 27 at r2 (raw file):
// DropOwnedBy implements DROP OWNED BY. func DropOwnedBy(b BuildCtx, n *tree.DropOwnedBy) { normalizedRoles, err := decodeusername.FromRoleSpecList(
what does normalized mean here?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 36 at r2 (raw file):
if role.IsAdminRole() || role.IsRootUser() || role.IsNodeUser() { panic(pgerror.Newf(pgcode.DependentObjectsStillExist, "cannot drop objects owned by role %q because they are required by the database system", role))
Is it legit to drop non-system objects though?
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 39 at r2 (raw file):
} if role != b.SessionData().User() && !b.IsMemberOf(role) { panic(pgerror.New(pgcode.InsufficientPrivilege, "permission denied to drop objects"))
Is it legit for admin/root perform this operation if they are not part the input roles?
pkg/sql/logictest/testdata/logic_test/schema_change_feature_flags line 249 at r2 (raw file):
REASSIGN OWNED BY root TO testuser # Test DROP OWNED BY.
Could you add DROP OWNED BY a special case here as well so that it still use its SchemaFeatureName which is DROP OWNED BY?:
We'd need to refactor this better somehow. I haven't thought about it yet :(
jasonmchan
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @chengxiong-ruan and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/dependencies.go line 172 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
the go convention is to use
map[username.SQLUsername]struct{}here to avoid the confusion oftrueandfalseboolean values :)
My comment was misleading, the boolean value actually indicates whether the member is an admin of the role. I've updated the comment. Leaving the signature as is to maintain parity with the AuthorizationAccessor in authorization.go.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/dependencies.go line 177 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
IsMemberOf->CurrentUserIsMemberOf?
Done.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 27 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
what does
normalizedmean here?
It's referring to the username being normalized.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 36 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
Is it legit to drop non-system objects though?
Postgres won't drop non-system objects for these roles. I'm not sure if there's a case where these roles don't own any system objects and so the statement would succeed, but I think that erroring here is the safe option.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 39 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
Is it legit for admin/root perform this operation if they are not part the input
roles?
Yes. The check for this is in the CurrentUserIsMemberOf function, although maybe it would be clearer if the check were elsewhere. Here's a logictest for this scenario.
pkg/sql/logictest/testdata/logic_test/schema_change_feature_flags line 249 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
Could you add
?DROP OWNED BYa special case here as well so that it still use itsSchemaFeatureNamewhich isDROP OWNED BY?:
We'd need to refactor this better somehow. I haven't thought about it yet :(
Done.
f93df50 to
44f2326
Compare
chengxiong-ruan
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @chengxiong-ruan, @jasonmchan, and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/dependencies.go line 172 at r2 (raw file):
Previously, jasonmchan (Jason Chan) wrote…
My comment was misleading, the boolean value actually indicates whether the member is an admin of the role. I've updated the comment. Leaving the signature as is to maintain parity with the
AuthorizationAccessorinauthorization.go.
ah, interesting. thanks for explaining that.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 36 at r2 (raw file):
Previously, jasonmchan (Jason Chan) wrote…
Postgres won't drop non-system objects for these roles. I'm not sure if there's a case where these roles don't own any system objects and so the statement would succeed, but I think that erroring here is the safe option.
yeah, that make sense, thanks.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 39 at r2 (raw file):
Previously, jasonmchan (Jason Chan) wrote…
Yes. The check for this is in the
CurrentUserIsMemberOffunction, although maybe it would be clearer if the check were elsewhere. Here's a logictest for this scenario.
Hmm. wait.. I thought CurrentUserIsMemberOf only checks if current is a member of role, but don't really care about if it's an admin or not?
Oh.. does MemberOfWithAdminOption actually also return admin even the role is not an admin? heh...
pkg/sql/schemachanger/scplan/testdata/drop_owned_by line 1 at r4 (raw file):
setup
Sorry, I should have noticed this earlier but just realized that we need a builder test similar to these as well
44f2326 to
3dac516
Compare
jasonmchan
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @chengxiong-ruan, @jasonmchan, and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 39 at r2 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
Hmm. wait.. I thought
CurrentUserIsMemberOfonly checks if current is a member ofrole, but don't really care about if it's an admin or not?
Oh.. doesMemberOfWithAdminOptionactually also returnadmineven theroleis not an admin? heh...
It's actually because I put a separate check here for the admin case. I renamed the function to CurrentUserHasAdminOrIsMemberOf, is this better?
pkg/sql/schemachanger/scplan/testdata/drop_owned_by line 1 at r4 (raw file):
Previously, chengxiong-ruan (Chengxiong Ruan) wrote…
Sorry, I should have noticed this earlier but just realized that we need a builder test similar to these as well
Done.
chengxiong-ruan
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 1 of 0 LGTMs obtained (waiting on @chengxiong-ruan, @jasonmchan, and @Xiang-Gu)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/drop_owned_by.go line 39 at r2 (raw file):
Previously, jasonmchan (Jason Chan) wrote…
It's actually because I put a separate check here for the admin case. I renamed the function to
CurrentUserHasAdminOrIsMemberOf, is this better?
Thanks that's much better!
|
bors r+ |
| DROP OWNED BY root | ||
|
|
||
| statement error pq: cannot drop objects owned by role "admin" because they are required by the database system | ||
| DROP OWNED BY admin |
There was a problem hiding this comment.
I would like to see more test cases involving:
- databases
- schemas
- tables, views and types referencing each other but owned by different roles.
The latter case is arguably more interesting for CASCADE but still.
There was a problem hiding this comment.
Since you're not implementing CASCADE you should probably have a case that checks for a nice error message (search for statement error unimplemented: for examples). Also add a case in the unimplemented_drop data-driven test suite in the scbuild package.
There was a problem hiding this comment.
I changed the unimplemented error from the one in scerror to the one in unimplemented, since it seems like the former causes us to try to fall back to the old schema changer, which ends up throwing a less descriptive error message for DROP OWNED BY. I added the logictest check but not the unimplemented_drop one--is this the right call?
There was a problem hiding this comment.
Yeah, I hear you. I'm good with this approach since we're going to implement it anyway. But I'm wondering if you could just set new schema changer's mode to unsafe_always since you disable the legacy schema changer configuration in this logic test :) also cc @postamar if you have strong opinion on the options here.
There was a problem hiding this comment.
I like your idea @chengxiong-ruan. The purpose of these tests is to check which schema changes aren't implemented by the declarative schema changer. It would be fine to ignore the legacy schema changer, in my opinion.
I don't have strong opinions either. @jasonmchan please do what you think is best and go ahead and bors.
| mutationOp | ||
| DescID descpb.ID | ||
| User string | ||
| } |
There was a problem hiding this comment.
User privileges are implicitly removed when dropping something, so you can remove this op altogether.
There was a problem hiding this comment.
I believe DROP OWNED BY should revoke user privileges for the role(s) on objects that aren't owned, so we need this op to drop those privileges without dropping the entire objects.
There was a problem hiding this comment.
Oh, right. In that case, you should update the rules to avoid emitting this op for dropped objects. Look for op_rules.go.
There was a problem hiding this comment.
Good catch. @jasonmchan see rules in pkg/sql/schemachanger/scplan/internal/rules/op_drop.go
There was a problem hiding this comment.
I think this rule should handle this, since user privileges are part of the descriptor.
There was a problem hiding this comment.
Amazing. Good find.
|
bors r- Sorry for the late review! Please address my comments and feel free to re-bors. |
|
Canceled. |
3dac516 to
c9edde5
Compare
|
@jasonmchan feel free to go ahead and bors this again. |
|
bors r+ |
|
Build failed (retrying...): |
|
Build failed (retrying...): |
|
I think this has some merge skew on generated output. bors r- |
|
Canceled. |
Previously, we did not support the DROP OWNED BY statement (cockroachdb#55381). This commit adds partial support for DROP OWNED BY in the declarative schema changer. Followup work is needed to support the CASCADE modifier. Release note (sql change): Support `DROP OWNED BY`.
c9edde5 to
421856b
Compare
|
Merge conflict should be fixed! bors r+ |
|
Build succeeded: |
|
By all means! |
Previously, we did not support the DROP OWNED BY statement (#55381).
This commit adds partial support for DROP OWNED BY in the declarative
schema changer. Followup work is needed to support the CASCADE modifier.
Release note (sql change): Support
DROP OWNED BY.