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
36 changes: 34 additions & 2 deletions enginetest/queries/priv_auth_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ var UserPrivTests = []UserPrivilegeTest{
"CREATE USER 'replica-admin'@localhost;",
"CREATE USER 'replica-client'@localhost;",
"CREATE USER 'replica-reload'@localhost;",
// REPLICATION_SLAVE_ADMIN allows: start replica,
// REPLICATION_SLAVE_ADMIN allows: start replica, stop replica, change replication source, change replication filter
"GRANT REPLICATION_SLAVE_ADMIN ON *.* TO 'replica-admin'@localhost;",
// REPLICATION CLIENT allows: show replica status
// REPLICATION CLIENT allows: show replica status, show binary logs, show binary log status
"GRANT REPLICATION CLIENT ON *.* to 'replica-client'@localhost;",
// RELOAD allows: reset replica
"GRANT RELOAD ON *.* TO 'replica-reload'@localhost;",
Expand Down Expand Up @@ -251,6 +251,38 @@ var UserPrivTests = []UserPrivilegeTest{
Expected: []sql.Row{},
},

// SHOW BINARY LOG STATUS
{
User: "user",
Host: "localhost",
Query: "SHOW BINARY LOG STATUS;",
ExpectedErr: sql.ErrPrivilegeCheckFailed,
},
{
User: "replica-admin",
Host: "localhost",
Query: "SHOW BINARY LOG STATUS;",
ExpectedErr: sql.ErrPrivilegeCheckFailed,
},
{
User: "replica-client",
Host: "localhost",
Query: "SHOW BINARY LOG STATUS;",
Expected: []sql.Row{},
},
{
User: "replica-reload",
Host: "localhost",
Query: "SHOW BINARY LOG STATUS;",
ExpectedErr: sql.ErrPrivilegeCheckFailed,
},
{
User: "root",
Host: "localhost",
Query: "SHOW BINARY LOG STATUS;",
Expected: []sql.Row{},
},

// CHANGE REPLICATION SOURCE
{
User: "user",
Expand Down
22 changes: 19 additions & 3 deletions sql/binlogreplication/binlog_replication.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,25 @@ type BinlogPrimaryController interface {
ListBinaryLogs(ctx *sql.Context) error

// GetBinaryLogStatus is called when the SHOW BINARY LOG STATUS statement is executed. The integrator should return
// the current status of the binary log. Note that this function will be expanded
// with an additional response parameter once it is wired up to the SQL engine.
GetBinaryLogStatus(ctx *sql.Context) error
// the current status of all available (i.e. non-purged) binary logs.
GetBinaryLogStatus(ctx *sql.Context) ([]BinaryLogStatus, error)
}

// BinaryLogStatus holds the data for one row of results from the `SHOW BINARY LOG STATUS` statement (or the deprecated
// `SHOW MASTER LOGS` statement). Integrators should return one instance for each binary log file that is being tracked
// by the server.
// https://dev.mysql.com/doc/refman/8.3/en/show-binary-log-status.html
type BinaryLogStatus struct {
// The filename of the binary log file.
File string
// The latest byte position in the binary log file.
Position uint
// Names of the databases whose changes are being tracked in this binary log.
DoDbs string
// Names of the databases whose changes are NOT being included in this binary log.
IgnoreDbs string
// The set of GTIDs that have been executed on this server.
ExecutedGtids string
}

// ReplicaStatus stores the status of a single binlog replica and is returned by `SHOW REPLICA STATUS`.
Expand Down
12 changes: 11 additions & 1 deletion sql/plan/replication_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,21 @@ const DynamicPrivilege_ReplicationSlaveAdmin = "replication_slave_admin"
type BinlogReplicaControllerCommand interface {
sql.Node

// WithBinlogReplicaController returns a new instance of this BinlogReplicaController, with the binlog replica
// WithBinlogReplicaController returns a new instance of this BinlogReplicaControllerCommand, with the binlog replica
// controller configured.
WithBinlogReplicaController(controller binlogreplication.BinlogReplicaController) sql.Node
}

// BinlogPrimaryControllerCommand represents a SQL statement that requires a BinlogPrimaryController
// (e.g. SHOW BINARY LOG STATUS, SHOW REPLICAS).
type BinlogPrimaryControllerCommand interface {
sql.Node

// WithBinlogPrimaryController returns a new instance of this BinlogPrimaryControllerCommand, with the binlog
// primary controller configured.
WithBinlogPrimaryController(controller binlogreplication.BinlogPrimaryController) sql.Node
}

// ChangeReplicationSource is the plan node for the "CHANGE REPLICATION SOURCE TO" statement.
// https://dev.mysql.com/doc/refman/8.0/en/change-replication-source-to.html
type ChangeReplicationSource struct {
Expand Down
88 changes: 88 additions & 0 deletions sql/plan/show_binlog_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2024 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 plan

import (
"github.com/dolthub/vitess/go/sqltypes"

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/binlogreplication"
"github.com/dolthub/go-mysql-server/sql/types"
)

// ShowBinlogStatus is the plan node for the "SHOW BINARY LOG STATUS" statement.
// https://dev.mysql.com/doc/refman/8.3/en/show-binary-log-status.html
type ShowBinlogStatus struct {
PrimaryController binlogreplication.BinlogPrimaryController
}

var _ sql.Node = (*ShowBinlogStatus)(nil)
var _ sql.CollationCoercible = (*ShowBinlogStatus)(nil)
var _ BinlogPrimaryControllerCommand = (*ShowBinlogStatus)(nil)

func NewShowBinlogStatus() *ShowBinlogStatus {
return &ShowBinlogStatus{}
}

// WithBinlogPrimaryController implements the BinlogPrimaryControllerCommand interface.
func (s *ShowBinlogStatus) WithBinlogPrimaryController(controller binlogreplication.BinlogPrimaryController) sql.Node {
nc := *s
nc.PrimaryController = controller
return &nc
}

func (s *ShowBinlogStatus) Resolved() bool {
return true
}

func (s *ShowBinlogStatus) String() string {
return "SHOW BINARY LOG STATUS"
}

func (s *ShowBinlogStatus) Schema() sql.Schema {
return sql.Schema{
{Name: "File", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 255), Default: nil, Nullable: false},
{Name: "Position", Type: types.Int64, Default: nil, Nullable: false},
{Name: "Binlog_Do_DB", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 255), Default: nil, Nullable: false},
{Name: "Binlog_Ignore_DB", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 255), Default: nil, Nullable: false},
{Name: "Executed_Gtid_Set", Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 255), Default: nil, Nullable: false},
}
}

func (s *ShowBinlogStatus) Children() []sql.Node {
return nil
}

func (s *ShowBinlogStatus) IsReadOnly() bool {
return true
}

func (s *ShowBinlogStatus) WithChildren(children ...sql.Node) (sql.Node, error) {
if len(children) != 0 {
return nil, sql.ErrInvalidChildrenNumber.New(s, len(children), 0)
}

newNode := *s
return &newNode, nil
}

func (s *ShowBinlogStatus) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_ReplicationClient))
}

// CollationCoercibility implements the interface sql.CollationCoercible.
func (*ShowBinlogStatus) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
return sql.Collation_binary, 7
}
2 changes: 1 addition & 1 deletion sql/planbuilder/priv.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ func (b *Builder) buildFlush(inScope *scope, f *ast.Flush) (outScope *scope) {
case "privileges":
node, _ := plan.NewFlushPrivileges(writesToBinlog).WithDatabase(b.resolveDb("mysql"))
outScope.node = node
case "binary logs", "engine logs":
case "binary logs", "engine logs", "table", "tables":
node := plan.Nothing{}
outScope.node = node
case "error logs", "relay logs", "general logs", "slow logs", "status":
Expand Down
7 changes: 7 additions & 0 deletions sql/planbuilder/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ func (b *Builder) buildShow(inScope *scope, s *ast.Show) (outScope *scope) {
return b.buildShowStatus(inScope, s)
case ast.KeywordString(ast.PLUGINS):
return b.buildShowPlugins(inScope, s)
case "binary log status":
outScope = inScope.push()
showRep := plan.NewShowBinlogStatus()
if binCat, ok := b.cat.(binlogreplication.BinlogPrimaryCatalog); ok && binCat.HasBinlogPrimaryController() {
showRep.PrimaryController = binCat.GetBinlogPrimaryController()
}
outScope.node = showRep
case "replica status":
outScope = inScope.push()
showRep := plan.NewShowReplicaStatus()
Expand Down
2 changes: 2 additions & 0 deletions sql/rowexec/node_builder.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func (b *BaseBuilder) buildNodeExecNoAnalyze(ctx *sql.Context, n sql.Node, row s
return b.buildDropHistogram(ctx, n, row)
case *plan.QueryProcess:
return b.buildQueryProcess(ctx, n, row)
case *plan.ShowBinlogStatus:
return b.buildShowBinlogStatus(ctx, n, row)
case *plan.ShowReplicaStatus:
return b.buildShowReplicaStatus(ctx, n, row)
case *plan.UpdateSource:
Expand Down
26 changes: 26 additions & 0 deletions sql/rowexec/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,32 @@ func (b *BaseBuilder) buildShowCreateTable(ctx *sql.Context, n *plan.ShowCreateT
}, nil
}

func (b *BaseBuilder) buildShowBinlogStatus(ctx *sql.Context, n *plan.ShowBinlogStatus, row sql.Row) (sql.RowIter, error) {
if n.PrimaryController == nil {
return sql.RowsToRowIter(), nil
}

statusResults, err := n.PrimaryController.GetBinaryLogStatus(ctx)
if err != nil {
return nil, err
}
if statusResults == nil {
return sql.RowsToRowIter(), nil
}

for _, status := range statusResults {
row = sql.Row{
status.File, // File
status.Position, // Position
status.DoDbs, // Binlog_Do_DB
status.IgnoreDbs, // Binlog_Ignore_DB
status.ExecutedGtids, // Executed_Gtid_Set
}
}

return sql.RowsToRowIter(row), nil
}

func (b *BaseBuilder) buildShowReplicaStatus(ctx *sql.Context, n *plan.ShowReplicaStatus, row sql.Row) (sql.RowIter, error) {
if n.ReplicaController == nil {
return sql.RowsToRowIter(), nil
Expand Down