Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support parsing ALTER EVENT statements #233

Merged
merged 5 commits into from May 8, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 81 additions & 15 deletions go/vt/sqlparser/ast.go
Expand Up @@ -1785,15 +1785,35 @@ type EventSpec struct {
Definer string
IfNotExists bool
OnSchedule *EventScheduleSpec
OnCompletionPreserve bool
OnCompletionPreserve EventOnCompletion
Status EventStatus
Comment *SQLVal
Body Statement

// used for ALTER EVENT statement
RenameName EventName
}

// ValidateAlterEvent checks that at least one event field is defined to alter.
func (es *EventSpec) ValidateAlterEvent() error {
if es.OnSchedule == nil && es.OnCompletionPreserve == EventOnCompletion_Undefined && es.RenameName.IsEmpty() && es.Status == EventStatus_Undefined && es.Comment == nil && es.Body == nil {
return fmt.Errorf("You have an error in your SQL syntax; At least one event field to alter needs to be defined")
}
return nil
}

type EventOnCompletion string

const (
EventOnCompletion_Undefined EventOnCompletion = ""
EventOnCompletion_Preserve EventOnCompletion = "on completion preserve"
EventOnCompletion_NotPreserve EventOnCompletion = "on completion not preserve"
)

type EventStatus string

const (
EventStatus_Undefined EventStatus = ""
EventStatus_Enable EventStatus = "enable"
EventStatus_Disable EventStatus = "disable"
EventStatus_DisableOnSlave EventStatus = "disable on slave"
Expand All @@ -1815,9 +1835,9 @@ type EventScheduleTimeSpec struct {
func (est *EventScheduleTimeSpec) Format(buf *TrackedBuffer) {
sb := strings.Builder{}

sb.WriteString(fmt.Sprintf("%s ", String(est.EventTimestamp)))
sb.WriteString(fmt.Sprintf("%s", String(est.EventTimestamp)))
for _, interval := range est.EventIntervals {
sb.WriteString(fmt.Sprintf("+ interval %v %s ", interval.Expr, interval.Unit))
sb.WriteString(fmt.Sprintf(" + interval %v %s", interval.Expr, interval.Unit))
}

buf.Myprintf("%s", sb.String())
Expand Down Expand Up @@ -2071,19 +2091,22 @@ func (node *DDL) Format(buf *TrackedBuffer) {
sb.WriteString(fmt.Sprintf("event%s %s ", notExists, event.EventName))

if event.OnSchedule.At != nil {
sb.WriteString(fmt.Sprintf("on schedule at %s", event.OnSchedule.At.String()))
sb.WriteString(fmt.Sprintf("on schedule at %s ", event.OnSchedule.At.String()))
} else {
sb.WriteString(fmt.Sprintf("on schedule every %s %s ", String(event.OnSchedule.EveryInterval.Expr), event.OnSchedule.EveryInterval.Unit))
if event.OnSchedule.Starts != nil {
sb.WriteString(fmt.Sprintf("starts %s", event.OnSchedule.Starts.String()))
sb.WriteString(fmt.Sprintf("starts %s ", event.OnSchedule.Starts.String()))
}
if event.OnSchedule.Ends != nil {
sb.WriteString(fmt.Sprintf("ends %s", event.OnSchedule.Ends.String()))
sb.WriteString(fmt.Sprintf("ends %s ", event.OnSchedule.Ends.String()))
}
}

if event.OnCompletionPreserve {
sb.WriteString("on completion preserve ")
if event.OnCompletionPreserve == EventOnCompletion_Preserve {
sb.WriteString(fmt.Sprintf("%s ", event.OnCompletionPreserve))
}
if event.Status != EventStatus_Undefined {
sb.WriteString(fmt.Sprintf("%s ", event.Status))
}
if event.Comment != nil {
sb.WriteString(fmt.Sprintf("comment %s ", event.Comment))
Expand Down Expand Up @@ -2149,12 +2172,55 @@ func (node *DDL) Format(buf *TrackedBuffer) {
buf.Myprintf(", %v to %v", node.FromTables[i], node.ToTables[i])
}
case AlterStr:
buf.Myprintf("%s table %v", node.Action, node.Table)
node.alterFormat(buf)
if node.EventSpec != nil {
event := node.EventSpec
sb := strings.Builder{}
sb.WriteString("alter")
if event.Definer != "" {
sb.WriteString(fmt.Sprintf(" definer = %s", event.Definer))
}

sb.WriteString(fmt.Sprintf(" event %s", event.EventName))

if event.OnSchedule != nil {
if event.OnSchedule.At != nil {
sb.WriteString(fmt.Sprintf(" on schedule at %s", event.OnSchedule.At.String()))
} else {
sb.WriteString(fmt.Sprintf(" on schedule every %s %s", String(event.OnSchedule.EveryInterval.Expr), event.OnSchedule.EveryInterval.Unit))
if event.OnSchedule.Starts != nil {
sb.WriteString(fmt.Sprintf(" starts %s", event.OnSchedule.Starts.String()))
}
if event.OnSchedule.Ends != nil {
sb.WriteString(fmt.Sprintf(" ends %s", event.OnSchedule.Ends.String()))
}
}
}

if event.OnCompletionPreserve != EventOnCompletion_Undefined {
sb.WriteString(fmt.Sprintf(" %s", event.OnCompletionPreserve))
}
if !event.RenameName.IsEmpty() {
sb.WriteString(fmt.Sprintf(" rename to %s", event.RenameName))
}
if event.Status != EventStatus_Undefined {
sb.WriteString(fmt.Sprintf(" %s", event.Status))
}
if event.Comment != nil {
sb.WriteString(fmt.Sprintf(" comment %s", event.Comment))
}
if event.Body != nil {
buf.Myprintf("%s do %v", sb.String(), event.Body)
} else {
buf.Myprintf("%s", sb.String())
}
} else {
buf.Myprintf("%s table %v", node.Action, node.Table)
node.alterFormat(buf)
}
case FlushStr:
buf.Myprintf("%s", node.Action)
case AddAutoIncStr:
buf.Myprintf("alter vschema on %v add auto_increment %v", node.Table, node.AutoIncSpec)
buf.Myprintf("alter schema on %v add auto_increment %v", node.Table, node.AutoIncSpec)
default:
buf.Myprintf("%s table %v", node.Action, node.Table)
}
Expand Down Expand Up @@ -3177,12 +3243,12 @@ func (node *Explain) Format(buf *TrackedBuffer) {
const (
CreateTriggerStr = "create trigger"
CreateProcedureStr = "create procedure"
CreateEventStr = "create event"
CreateTableStr = "create table"
CreateEventStr = "create event"
CreateTableStr = "create table"

ProcedureStatusStr = "procedure status"
FunctionStatusStr = "function status"
TableStatusStr = "table status"
FunctionStatusStr = "function status"
TableStatusStr = "table status"
)

// Show represents a show statement.
Expand Down
68 changes: 55 additions & 13 deletions go/vt/sqlparser/parse_test.go
Expand Up @@ -1736,7 +1736,7 @@ var (
}, {
input: "show triggers like 'pattern'",
}, {
input: "show TRIGGERS where v = 'x'",
input: "show TRIGGERS where v = 'x'",
output: "show triggers where v = 'x'",
}, {
input: "show variables",
Expand Down Expand Up @@ -2879,32 +2879,68 @@ var (
input: "CREATE DEFINER = `root`@`localhost` EVENT event1 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 3 WEEK + INTERVAL 2 DAY DO INSERT INTO mytable VALUES (NOW())",
output: "create definer = `root`@`localhost` event event1 on schedule at CURRENT_TIMESTAMP() + interval 3 WEEK + interval 2 DAY do insert into mytable values (NOW())",
}, {
input: "CREATE EVENT event1 ON SCHEDULE EVERY 1 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 3 HOUR ON COMPLETION PRESERVE COMMENT 'new event' DO INSERT INTO mytable VALUES (1)",
output: "create event event1 on schedule every 1 MINUTE ends CURRENT_TIMESTAMP() + interval 3 HOUR on completion preserve comment 'new event' do insert into mytable values (1)",
input: "CREATE EVENT event1 ON SCHEDULE EVERY 1 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 3 HOUR ON COMPLETION PRESERVE disable COMMENT 'new event' DO INSERT INTO mytable VALUES (1)",
output: "create event event1 on schedule every 1 MINUTE ends CURRENT_TIMESTAMP() + interval 3 HOUR on completion preserve disable comment 'new event' do insert into mytable values (1)",
}, {
input: "CREATE EVENT event1 ON SCHEDULE EVERY 1 MINUTE STARTS CURRENT_TIMESTAMP + INTERVAL 2 HOUR ENDS CURRENT_TIMESTAMP + INTERVAL 3 HOUR ON COMPLETION NOT PRESERVE DO INSERT INTO mytable VALUES (1)",
output: "create event event1 on schedule every 1 MINUTE starts CURRENT_TIMESTAMP() + interval 2 HOUR ends CURRENT_TIMESTAMP() + interval 3 HOUR do insert into mytable values (1)",
input: "CREATE EVENT event1 ON SCHEDULE EVERY 1 MINUTE STARTS CURRENT_TIMESTAMP + INTERVAL 2 HOUR ENDS CURRENT_TIMESTAMP + INTERVAL 3 HOUR ON COMPLETION NOT PRESERVE enable DO INSERT INTO mytable VALUES (1)",
output: "create event event1 on schedule every 1 MINUTE starts CURRENT_TIMESTAMP() + interval 2 HOUR ends CURRENT_TIMESTAMP() + interval 3 HOUR enable do insert into mytable values (1)",
}, {
input: "CREATE EVENT e_call_myproc ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY DO CALL myproc(5, 27)",
output: "create event e_call_myproc on schedule at CURRENT_TIMESTAMP() + interval 1 DAY do call myproc(5, 27)",
input: "CREATE EVENT e_call_myproc ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY disable on slave DO CALL myproc(5, 27)",
output: "create event e_call_myproc on schedule at CURRENT_TIMESTAMP() + interval 1 DAY disable on slave do call myproc(5, 27)",
}, {
input: "SHOW EVENTS",
input: "CREATE EVENT e_call_myproc ON SCHEDULE AT CURRENT_TIMESTAMP DO CALL myproc(5, 27)",
output: "create event e_call_myproc on schedule at CURRENT_TIMESTAMP() do call myproc(5, 27)",
}, {
input: "CREATE EVENT e_call_myproc ON SCHEDULE AT CURRENT_TIMESTAMP DISABLE DO CALL myproc(5, 27)",
output: "create event e_call_myproc on schedule at CURRENT_TIMESTAMP() disable do call myproc(5, 27)",
}, {
input: "SHOW EVENTS",
output: "show events",
}, {
input: "SHOW EVENTS FROM dbName",
input: "SHOW EVENTS FROM dbName",
output: "show events from dbName",
}, {
input: "SHOW EVENTS IN dbName",
input: "SHOW EVENTS IN dbName",
output: "show events from dbName",
}, {
input: "SHOW EVENTS IN dbName LIKE 'pattern'",
input: "SHOW EVENTS IN dbName LIKE 'pattern'",
output: "show events from dbName like 'pattern'",
}, {
input: "SHOW EVENTS FROM dbName WHERE status = 'enabled'",
input: "SHOW EVENTS FROM dbName WHERE status = 'enabled'",
output: "show events from dbName where `status` = 'enabled'",
}, {
input: "SHOW CREATE EVENT myevent",
input: "SHOW CREATE EVENT myevent",
output: "show create event myevent",
}, {
input: "ALTER EVENT myevent ON SCHEDULE AT CURRENT_TIMESTAMP;",
output: "alter event myevent on schedule at CURRENT_TIMESTAMP()",
}, {
input: "ALTER EVENT myevent ON COMPLETION NOT PRESERVE",
output: "alter event myevent on completion not preserve",
}, {
input: "ALTER EVENT myevent RENAME TO event1",
output: "alter event myevent rename to event1",
}, {
input: "ALTER EVENT mydb.myevent RENAME TO newdb.event1",
output: "alter event mydb.myevent rename to newdb.event1",
}, {
input: "ALTER EVENT myevent ENABLE",
output: "alter event myevent enable",
}, {
input: "ALTER EVENT myevent COMMENT 'add comment'",
output: "alter event myevent comment 'add comment'",
}, {
input: "ALTER EVENT myevent DO INSERT INTO mytable values (1)",
output: "alter event myevent do insert into mytable values (1)",
}, {
input: "ALTER EVENT myevent ON SCHEDULE EVERY 1 MINUTE STARTS CURRENT_TIMESTAMP DO INSERT INTO mytable values (1)",
output: "alter event myevent on schedule every 1 MINUTE starts CURRENT_TIMESTAMP() do insert into mytable values (1)",
}, {
input: "ALTER EVENT myevent RENAME TO new_event DISABLE COMMENT 'renaming and disabling the event'",
output: "alter event myevent rename to new_event disable comment 'renaming and disabling the event'",
}, {
input: "ALTER DEFINER = `newuser`@`localhost` EVENT myevent ON COMPLETION NOT PRESERVE;",
output: "alter definer = `newuser`@`localhost` event myevent on completion not preserve",
},
}
// Any tests that contain multiple statements within the body (such as BEGIN/END blocks) should go here.
Expand Down Expand Up @@ -6057,6 +6093,12 @@ var (
input: "CREATE PROCEDURE testproc() BEGIN while1: WHILE a > 7 DO BEGIN END; END WHILE while2; END",
output: "End-label while2 without match at position 85 near 'while2'",
excludeMulti: true,
}, {
input: "ALTER EVENT myevent",
output: "You have an error in your SQL syntax; At least one event field to alter needs to be defined at position 20 near 'myevent'",
}, {
input: "ALTER DEFINER = `newuser`@`localhost` EVENT myevent",
output: "You have an error in your SQL syntax; At least one event field to alter needs to be defined at position 52 near 'myevent'",
},
}
)
Expand Down