github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/show_create_procedure.go (about) 1 // Copyright 2020-2021 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 plan 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/types" 23 ) 24 25 type ShowCreateProcedure struct { 26 db sql.Database 27 ProcedureName string 28 ExternalStoredProcedure *sql.ExternalStoredProcedureDetails 29 } 30 31 var _ sql.Databaser = (*ShowCreateProcedure)(nil) 32 var _ sql.Node = (*ShowCreateProcedure)(nil) 33 var _ sql.CollationCoercible = (*ShowCreateProcedure)(nil) 34 35 var showCreateProcedureSchema = sql.Schema{ 36 &sql.Column{Name: "Procedure", Type: types.LongText, Nullable: false}, 37 &sql.Column{Name: "sql_mode", Type: types.LongText, Nullable: false}, 38 &sql.Column{Name: "Create Procedure", Type: types.LongText, Nullable: false}, 39 &sql.Column{Name: "character_set_client", Type: types.LongText, Nullable: false}, 40 &sql.Column{Name: "collation_connection", Type: types.LongText, Nullable: false}, 41 &sql.Column{Name: "Database Collation", Type: types.LongText, Nullable: false}, 42 } 43 44 // NewShowCreateProcedure creates a new ShowCreateProcedure node for SHOW CREATE PROCEDURE statements. 45 func NewShowCreateProcedure(db sql.Database, procedure string) *ShowCreateProcedure { 46 return &ShowCreateProcedure{ 47 db: db, 48 ProcedureName: strings.ToLower(procedure), 49 } 50 } 51 52 // String implements the sql.Node interface. 53 func (s *ShowCreateProcedure) String() string { 54 return fmt.Sprintf("SHOW CREATE PROCEDURE %s", s.ProcedureName) 55 } 56 57 // Resolved implements the sql.Node interface. 58 func (s *ShowCreateProcedure) Resolved() bool { 59 _, ok := s.db.(sql.UnresolvedDatabase) 60 return !ok 61 } 62 63 func (s *ShowCreateProcedure) IsReadOnly() bool { 64 return true 65 } 66 67 // Children implements the sql.Node interface. 68 func (s *ShowCreateProcedure) Children() []sql.Node { 69 return nil 70 } 71 72 // Schema implements the sql.Node interface. 73 func (s *ShowCreateProcedure) Schema() sql.Schema { 74 return showCreateProcedureSchema 75 } 76 77 // RowIter implements the sql.Node interface. 78 func (s *ShowCreateProcedure) RowIter(ctx *sql.Context, _ sql.Row) (sql.RowIter, error) { 79 characterSetClient, err := ctx.GetSessionVariable(ctx, "character_set_client") 80 if err != nil { 81 return nil, err 82 } 83 collationConnection, err := ctx.GetSessionVariable(ctx, "collation_connection") 84 if err != nil { 85 return nil, err 86 } 87 collationServer, err := ctx.GetSessionVariable(ctx, "collation_server") 88 if err != nil { 89 return nil, err 90 } 91 92 if s.ExternalStoredProcedure != nil { 93 // If an external stored procedure has been plugged in by the analyzer, use that 94 fakeCreateProcedureStmt := s.ExternalStoredProcedure.FakeCreateProcedureStmt() 95 return sql.RowsToRowIter(sql.Row{ 96 s.ExternalStoredProcedure.Name, // Procedure 97 "", // sql_mode 98 fakeCreateProcedureStmt, // Create Procedure 99 characterSetClient, // character_set_client 100 collationConnection, // collation_connection 101 collationServer, // Database Collation 102 }), nil 103 } else { 104 // Otherwise, search the StoredProcedureDatabase for a user-created stored procedure 105 procedureDb, ok := s.db.(sql.StoredProcedureDatabase) 106 if !ok { 107 return nil, sql.ErrStoredProceduresNotSupported.New(s.db.Name()) 108 } 109 procedures, err := procedureDb.GetStoredProcedures(ctx) 110 if err != nil { 111 return nil, err 112 } 113 for _, procedure := range procedures { 114 if strings.ToLower(procedure.Name) == s.ProcedureName { 115 return sql.RowsToRowIter(sql.Row{ 116 procedure.Name, // Procedure 117 "", // sql_mode 118 procedure.CreateStatement, // Create Procedure 119 characterSetClient, // character_set_client 120 collationConnection, // collation_connection 121 collationServer, // Database Collation 122 }), nil 123 } 124 } 125 return nil, sql.ErrStoredProcedureDoesNotExist.New(s.ProcedureName) 126 } 127 } 128 129 // WithChildren implements the sql.Node interface. 130 func (s *ShowCreateProcedure) WithChildren(children ...sql.Node) (sql.Node, error) { 131 return NillaryWithChildren(s, children...) 132 } 133 134 // CheckPrivileges implements the interface sql.Node. 135 func (s *ShowCreateProcedure) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 136 // TODO: set definer 137 // TODO: dynamic privilege SHOW ROUTINE 138 // According to: https://dev.mysql.com/doc/refman/8.0/en/show-create-procedure.html 139 // Must have Global SELECT, SHOW_ROUTINE, CREATE_ROUTINE, ALTER_ROUTINE, or EXECUTE privileges. 140 141 dbSubject := sql.PrivilegeCheckSubject{ 142 Database: s.db.Name(), 143 } 144 145 return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(sql.PrivilegeCheckSubject{}, sql.PrivilegeType_Select)) || 146 opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(dbSubject, sql.PrivilegeType_CreateRoutine)) || 147 opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(dbSubject, sql.PrivilegeType_AlterRoutine)) || 148 opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(dbSubject, sql.PrivilegeType_Execute)) 149 } 150 151 // CollationCoercibility implements the interface sql.CollationCoercible. 152 func (*ShowCreateProcedure) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 153 return sql.Collation_binary, 7 154 } 155 156 // Database implements the sql.Databaser interface. 157 func (s *ShowCreateProcedure) Database() sql.Database { 158 return s.db 159 } 160 161 // WithDatabase implements the sql.Databaser interface. 162 func (s *ShowCreateProcedure) WithDatabase(db sql.Database) (sql.Node, error) { 163 ns := *s 164 ns.db = db 165 return &ns, nil 166 } 167 168 // WithExternalStoredProcedure returns a new ShowCreateProcedure node with the specified external stored procedure set 169 // as the procedure to be shown. 170 func (s *ShowCreateProcedure) WithExternalStoredProcedure(procedure sql.ExternalStoredProcedureDetails) sql.Node { 171 ns := *s 172 ns.ExternalStoredProcedure = &procedure 173 return &ns 174 }