Skip to content

Commit 6fe6d0c

Browse files
authored
Merge pull request #1961 from dolthub/aaron/readonly-engine-updates
engine: Make ReadOnly a toggleable atomic.Bool.
2 parents d10c5b0 + f237179 commit 6fe6d0c

File tree

3 files changed

+67
-18
lines changed

3 files changed

+67
-18
lines changed

engine.go

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strconv"
2121
"strings"
2222
"sync"
23+
"sync/atomic"
2324

2425
"github.com/dolthub/vitess/go/sqltypes"
2526
querypb "github.com/dolthub/vitess/go/vt/proto/query"
@@ -136,7 +137,7 @@ type Engine struct {
136137
ProcessList sql.ProcessList
137138
MemoryManager *sql.MemoryManager
138139
BackgroundThreads *sql.BackgroundThreads
139-
IsReadOnly bool
140+
ReadOnly atomic.Bool
140141
IsServerLocked bool
141142
PreparedDataCache *PreparedDataCache
142143
mu *sync.Mutex
@@ -168,17 +169,18 @@ func New(a *analyzer.Analyzer, cfg *Config) *Engine {
168169
})
169170
a.Catalog.RegisterFunction(emptyCtx, function.GetLockingFuncs(ls)...)
170171

171-
return &Engine{
172+
ret := &Engine{
172173
Analyzer: a,
173174
MemoryManager: sql.NewMemoryManager(sql.ProcessMemory),
174175
ProcessList: NewProcessList(),
175176
LS: ls,
176177
BackgroundThreads: sql.NewBackgroundThreads(),
177-
IsReadOnly: cfg.IsReadOnly,
178178
IsServerLocked: cfg.IsServerLocked,
179179
PreparedDataCache: NewPreparedDataCache(),
180180
mu: &sync.Mutex{},
181181
}
182+
ret.ReadOnly.Store(cfg.IsReadOnly)
183+
return ret
182184
}
183185

184186
// NewDefault creates a new default Engine.
@@ -635,23 +637,19 @@ func (e *Engine) WithBackgroundThreads(b *sql.BackgroundThreads) *Engine {
635637
return e
636638
}
637639

640+
func (e *Engine) IsReadOnly() bool {
641+
return e.ReadOnly.Load()
642+
}
643+
638644
// readOnlyCheck checks to see if the query is valid with the modification setting of the engine.
639645
func (e *Engine) readOnlyCheck(node sql.Node) error {
640-
if plan.IsDDLNode(node) {
641-
if e.IsReadOnly {
642-
return sql.ErrReadOnly.New()
643-
} else if e.IsServerLocked {
644-
return sql.ErrDatabaseWriteLocked.New()
645-
}
646+
// Note: We only compute plan.IsReadOnly if the server is in one of
647+
// these two modes, since otherwise it is simply wasted work.
648+
if e.IsReadOnly() && !plan.IsReadOnly(node) {
649+
return sql.ErrReadOnly.New()
646650
}
647-
switch node.(type) {
648-
case
649-
*plan.DeleteFrom, *plan.InsertInto, *plan.Update, *plan.LockTables, *plan.UnlockTables:
650-
if e.IsReadOnly {
651-
return sql.ErrReadOnly.New()
652-
} else if e.IsServerLocked {
653-
return sql.ErrDatabaseWriteLocked.New()
654-
}
651+
if e.IsServerLocked && !plan.IsReadOnly(node) {
652+
return sql.ErrDatabaseWriteLocked.New()
655653
}
656654
return nil
657655
}

enginetest/enginetests.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ func TestOrderByGroupBy(t *testing.T, harness Harness) {
623623
func TestReadOnly(t *testing.T, harness Harness) {
624624
harness.Setup(setup.Mytable...)
625625
e := mustNewEngine(t, harness)
626-
e.IsReadOnly = true
626+
e.ReadOnly.Store(true)
627627
defer e.Close()
628628

629629
RunQuery(t, e, harness, `SELECT i FROM mytable`)
@@ -634,6 +634,11 @@ func TestReadOnly(t *testing.T, harness Harness) {
634634
`INSERT INTO mytable (i, s) VALUES(42, 'yolo')`,
635635
`CREATE VIEW myview3 AS SELECT i FROM mytable`,
636636
`DROP VIEW myview`,
637+
`DROP DATABASE mydb`,
638+
`CREATE DATABASE newdb`,
639+
`CREATE USER tester@localhost`,
640+
`CREATE ROLE test_role`,
641+
`GRANT SUPER ON * TO 'root'@'localhost'`,
637642
}
638643

639644
for _, query := range writingQueries {

sql/plan/process.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,49 @@ func IsShowNode(node sql.Node) bool {
478478
func IsNoRowNode(node sql.Node) bool {
479479
return IsDDLNode(node) || IsShowNode(node)
480480
}
481+
482+
func IsReadOnly(node sql.Node) bool {
483+
isExplain := false
484+
transform.Inspect(node, func(n sql.Node) bool {
485+
switch n.(type) {
486+
case *DescribeQuery:
487+
isExplain = true
488+
return false
489+
}
490+
return true
491+
})
492+
if isExplain {
493+
return true
494+
}
495+
496+
if IsDDLNode(node) {
497+
return false
498+
}
499+
500+
switch node.(type) {
501+
case *DeleteFrom, *InsertInto, *Update, *LockTables, *UnlockTables:
502+
return false
503+
}
504+
505+
isPrivNodeP := func(n sql.Node) bool {
506+
switch node.(type) {
507+
case *CreateUser, *DropUser, *RenameUser, *CreateRole, *DropRole, *Grant, *GrantRole, *GrantProxy, *Revoke, *RevokeRole, *RevokeAll, *RevokeProxy:
508+
return true
509+
}
510+
return false
511+
}
512+
isPrivNode := false
513+
transform.Inspect(node, func(n sql.Node) bool {
514+
if isPrivNodeP(n) {
515+
isPrivNode = true
516+
return false
517+
}
518+
return true
519+
})
520+
521+
if isPrivNode {
522+
return false
523+
}
524+
525+
return true
526+
}

0 commit comments

Comments
 (0)