github.com/dolthub/go-mysql-server@v0.18.0/eventscheduler/event_scheduler.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package eventscheduler 16 17 import ( 18 "errors" 19 "fmt" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/analyzer" 23 ) 24 25 // ErrEventSchedulerDisabled is returned when user tries to set `event_scheduler_notifier` global system variable to ON or OFF 26 // when the server started with either `--event-scheduler=DISABLED` or `--skip-grant-tables` configuration. Should have ERROR 1290 code. 27 var ErrEventSchedulerDisabled = errors.New("The server is running with the --event-scheduler=DISABLED or --skip-grant-tables option and the event scheduler cannot be enabled") 28 29 // SchedulerStatus can be one of 'ON', 'OFF' or 'DISABLED' 30 // If --event-scheduler configuration variable is set to 'DISABLED' 31 // at the start of the server, it cannot be updated during runtime. 32 // If not defined, it defaults to 'ON', and it can be updated to 'OFF' 33 // during runtime. 34 // If --skip-grant-tables configuration flag is used, it defaults 35 // to 'DISABLED'. 36 type SchedulerStatus string 37 38 const ( 39 SchedulerOn SchedulerStatus = "ON" 40 SchedulerOff SchedulerStatus = "OFF" 41 SchedulerDisabled SchedulerStatus = "DISABLED" 42 ) 43 44 var _ sql.EventScheduler = (*EventScheduler)(nil) 45 46 // EventScheduler is responsible for SQL events execution. 47 type EventScheduler struct { 48 status SchedulerStatus 49 executor *eventExecutor 50 ctxGetterFunc func() (*sql.Context, func() error, error) 51 } 52 53 // InitEventScheduler is called at the start of the server. This function returns EventScheduler object 54 // creating eventExecutor with empty events list. The enabled events will be loaded into the eventExecutor 55 // if the EventScheduler status is 'ON'. The runQueryFunc is used to run an event definition during 56 // event execution. If the |period| parameter is 1 or greater, then that value will be used as the period 57 // (in seconds) at which the event scheduler will wake up and see if events need to be executed. 58 func InitEventScheduler( 59 a *analyzer.Analyzer, 60 bgt *sql.BackgroundThreads, 61 getSqlCtxFunc func() (*sql.Context, func() error, error), 62 status SchedulerStatus, 63 runQueryFunc func(ctx *sql.Context, dbName, query, username, address string) error, 64 period int, 65 ) (*EventScheduler, error) { 66 var es = &EventScheduler{ 67 status: status, 68 executor: newEventExecutor(bgt, getSqlCtxFunc, runQueryFunc, period), 69 ctxGetterFunc: getSqlCtxFunc, 70 } 71 72 // If the EventSchedulerStatus is ON, then load enabled 73 // events and start executing events on schedule. 74 if es.status == SchedulerOn { 75 ctx, commit, err := getSqlCtxFunc() 76 if err != nil { 77 return nil, err 78 } 79 err = es.loadEventsAndStartEventExecutor(ctx, a) 80 if err != nil { 81 return nil, err 82 } 83 err = commit() 84 if err != nil { 85 return nil, err 86 } 87 } 88 89 return es, nil 90 } 91 92 // Close closes the EventScheduler. 93 func (es *EventScheduler) Close() { 94 es.status = SchedulerOff 95 es.executor.shutdown() 96 } 97 98 // TurnOnEventScheduler is called when user sets --event-scheduler system variable to ON or 1. 99 // This function requires valid analyzer and sql context to evaluate all events in all databases 100 // to load enabled events to the EventScheduler. 101 func (es *EventScheduler) TurnOnEventScheduler(a *analyzer.Analyzer) error { 102 if es.status == SchedulerDisabled { 103 return ErrEventSchedulerDisabled 104 } else if es.status == SchedulerOn { 105 return nil 106 } 107 108 es.status = SchedulerOn 109 110 ctx, commit, err := es.ctxGetterFunc() 111 if err != nil { 112 return err 113 } 114 err = es.loadEventsAndStartEventExecutor(ctx, a) 115 if err != nil { 116 return err 117 } 118 return commit() 119 } 120 121 // TurnOffEventScheduler is called when user sets --event-scheduler system variable to OFF or 0. 122 func (es *EventScheduler) TurnOffEventScheduler() error { 123 if es.status == SchedulerDisabled { 124 return ErrEventSchedulerDisabled 125 } else if es.status == SchedulerOff { 126 return nil 127 } 128 129 es.status = SchedulerOff 130 es.executor.shutdown() 131 132 return nil 133 } 134 135 // loadEventsAndStartEventExecutor evaluates all events in all databases and evaluates the enabled events 136 // with valid schedule to load into the eventExecutor. Then, it starts the eventExecutor. 137 func (es *EventScheduler) loadEventsAndStartEventExecutor(ctx *sql.Context, a *analyzer.Analyzer) error { 138 es.executor.catalog = a.Catalog 139 es.executor.loadAllEvents(ctx) 140 go es.executor.start() 141 return nil 142 } 143 144 // AddEvent implements sql.EventScheduler interface. 145 // This function is called when there is an event created at runtime. 146 func (es *EventScheduler) AddEvent(ctx *sql.Context, edb sql.EventDatabase, details sql.EventDefinition) { 147 if es.status == SchedulerDisabled || es.status == SchedulerOff { 148 return 149 } 150 es.executor.addEvent(ctx, edb, details) 151 } 152 153 // UpdateEvent implements sql.EventScheduler interface. 154 // This function is called when there is an event altered at runtime. 155 func (es *EventScheduler) UpdateEvent(ctx *sql.Context, edb sql.EventDatabase, orgEventName string, details sql.EventDefinition) { 156 if es.status == SchedulerDisabled || es.status == SchedulerOff { 157 return 158 } 159 es.executor.updateEvent(ctx, edb, orgEventName, details) 160 } 161 162 // RemoveEvent implements sql.EventScheduler interface. 163 // This function is called when there is an event dropped at runtime. This function 164 // removes the given event if it exists in the enabled events list of the EventScheduler. 165 func (es *EventScheduler) RemoveEvent(dbName, eventName string) { 166 if es.status == SchedulerDisabled || es.status == SchedulerOff { 167 return 168 } 169 es.executor.removeEvent(fmt.Sprintf("%s.%s", dbName, eventName)) 170 } 171 172 // RemoveSchemaEvents implements sql.EventScheduler interface. 173 // This function is called when there is a database dropped at runtime. This function 174 // removes all events of given database that exist in the enabled events list of the EventScheduler. 175 func (es *EventScheduler) RemoveSchemaEvents(dbName string) { 176 if es.status == SchedulerDisabled || es.status == SchedulerOff { 177 return 178 } 179 es.executor.removeSchemaEvents(dbName) 180 }