Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions enginetest/queries/priv_auth_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ type ServerAuthenticationTestAssertion struct {
// UserPrivTests test the user and privilege systems. These tests always have the root account available, and the root
// account is used with any queries in the SetUpScript.
var UserPrivTests = []UserPrivilegeTest{
// https://github.com/dolthub/dolt/issues/10083
{
Name: "Selecting a view only needs SELECT grants",
SetUpScript: []string{
"CREATE TABLE perms_t (id INT PRIMARY KEY, val VARCHAR(10));",
"INSERT INTO perms_t VALUES (1, 'a'), (2, 'b');",
"CREATE VIEW perms_v AS SELECT id, val FROM perms_t ORDER BY id;",
"CREATE USER 'view_reader'@'localhost' IDENTIFIED BY 'pw';",
"GRANT SELECT ON perms_t TO 'view_reader'@'localhost';",
"GRANT SELECT ON perms_v TO 'view_reader'@'localhost';",
},
Assertions: []UserPrivilegeTestAssertion{
{
User: "view_reader",
Host: "localhost",
Query: "SELECT * FROM perms_v",
Expected: []sql.Row{
{int64(1), "a"},
{int64(2), "b"},
},
},
},
},
{
Name: "Create user limits",
Assertions: []UserPrivilegeTestAssertion{
Expand Down
1 change: 0 additions & 1 deletion sql/base_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ func (s *BaseSession) Client() Client { return s.client }
// SetClient implements the Session interface.
func (s *BaseSession) SetClient(c Client) {
s.client = c
return
}

// GetAllSessionVariables implements the Session interface.
Expand Down
51 changes: 51 additions & 0 deletions sql/planbuilder/definer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2025 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package planbuilder

import (
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/mysql_db"
)

// mockDefiner temporarily impersonates the definer during binding. It clones the current authorization state
// (when available), adds the requested global privileges (e.g. CREATE VIEW), and updates both the session privilege
// cache and the cached AuthorizationQueryState. Callers must defer the returned restore function.
func (b *Builder) mockDefiner(privileges ...sql.PrivilegeType) func() {
if b == nil || b.ctx == nil || b.ctx.Session == nil {
return func() {}
}

var privilegeSet mysql_db.PrivilegeSet
if state, ok := b.authQueryState.(defaultAuthorizationQueryState); ok && state.enabled {
privilegeSet = state.privSet.Copy()
} else {
privilegeSet = mysql_db.NewPrivilegeSet()
}
privilegeSet.AddGlobalStatic(privileges...)

initialAuthQueryState := b.authQueryState
if state, ok := b.authQueryState.(defaultAuthorizationQueryState); ok {
state.privSet = privilegeSet
b.authQueryState = state
}

initialPrivilegeSet, initialCounter := b.ctx.Session.GetPrivilegeSet()
b.ctx.SetPrivilegeSet(privilegeSet, initialCounter)

return func() {
b.authQueryState = initialAuthQueryState
b.ctx.SetPrivilegeSet(initialPrivilegeSet, initialCounter)
}
}
3 changes: 3 additions & 0 deletions sql/planbuilder/from.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,9 @@ func (b *Builder) resolveView(name string, database sql.Database, asOf interface
if err != nil {
b.handleErr(err)
}
// TODO: Once view definers are persisted, load the real definer client
restoreInvoker := b.mockDefiner(sql.PrivilegeType_CreateView)
defer restoreInvoker()
node, _, err := b.bindOnlyWithDatabase(database, stmt, viewDef.CreateViewStatement)
if err != nil {
// TODO: Need to account for non-existing functions or
Expand Down