github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/delegate/show_grants.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package delegate 12 13 import ( 14 "bytes" 15 "fmt" 16 "strings" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/lex" 19 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 ) 22 23 // delegateShowGrants implements SHOW GRANTS which returns grant details for the 24 // specified objects and users. 25 // Privileges: None. 26 // Notes: postgres does not have a SHOW GRANTS statement. 27 // mysql only returns the user's privileges. 28 func (d *delegator) delegateShowGrants(n *tree.ShowGrants) (tree.Statement, error) { 29 var params []string 30 31 const dbPrivQuery = ` 32 SELECT table_catalog AS database_name, 33 table_schema AS schema_name, 34 grantee, 35 privilege_type 36 FROM "".information_schema.schema_privileges` 37 const tablePrivQuery = ` 38 SELECT table_catalog AS database_name, 39 table_schema AS schema_name, 40 table_name, 41 grantee, 42 privilege_type 43 FROM "".information_schema.table_privileges` 44 45 var source bytes.Buffer 46 var cond bytes.Buffer 47 var orderBy string 48 49 if n.Targets != nil && n.Targets.Databases != nil { 50 // Get grants of database from information_schema.schema_privileges 51 // if the type of target is database. 52 dbNames := n.Targets.Databases.ToStrings() 53 54 for _, db := range dbNames { 55 name := cat.SchemaName{ 56 CatalogName: tree.Name(db), 57 SchemaName: tree.Name(tree.PublicSchema), 58 ExplicitCatalog: true, 59 ExplicitSchema: true, 60 } 61 _, _, err := d.catalog.ResolveSchema(d.ctx, cat.Flags{AvoidDescriptorCaches: true}, &name) 62 if err != nil { 63 return nil, err 64 } 65 params = append(params, lex.EscapeSQLString(db)) 66 } 67 68 fmt.Fprint(&source, dbPrivQuery) 69 orderBy = "1,2,3,4" 70 if len(params) == 0 { 71 // There are no rows, but we can't simply return emptyNode{} because 72 // the result columns must still be defined. 73 cond.WriteString(`WHERE false`) 74 } else { 75 fmt.Fprintf(&cond, `WHERE database_name IN (%s)`, strings.Join(params, ",")) 76 } 77 } else { 78 fmt.Fprint(&source, tablePrivQuery) 79 orderBy = "1,2,3,4,5" 80 81 if n.Targets != nil { 82 // Get grants of table from information_schema.table_privileges 83 // if the type of target is table. 84 var allTables tree.TableNames 85 86 for _, tableTarget := range n.Targets.Tables { 87 tableGlob, err := tableTarget.NormalizeTablePattern() 88 if err != nil { 89 return nil, err 90 } 91 // We avoid the cache so that we can observe the grants taking 92 // a lease, like other SHOW commands. 93 tables, err := cat.ExpandDataSourceGlob( 94 d.ctx, d.catalog, cat.Flags{AvoidDescriptorCaches: true}, tableGlob, 95 ) 96 if err != nil { 97 return nil, err 98 } 99 allTables = append(allTables, tables...) 100 } 101 102 for i := range allTables { 103 params = append(params, fmt.Sprintf("(%s,%s,%s)", 104 lex.EscapeSQLString(allTables[i].Catalog()), 105 lex.EscapeSQLString(allTables[i].Schema()), 106 lex.EscapeSQLString(allTables[i].Table()))) 107 } 108 109 if len(params) == 0 { 110 // The glob pattern has expanded to zero matching tables. 111 // There are no rows, but we can't simply return emptyNode{} because 112 // the result columns must still be defined. 113 cond.WriteString(`WHERE false`) 114 } else { 115 fmt.Fprintf(&cond, `WHERE (database_name, schema_name, table_name) IN (%s)`, strings.Join(params, ",")) 116 } 117 } else { 118 // No target: only look at tables and schemas in the current database. 119 source.WriteString(` UNION ALL ` + 120 `SELECT database_name, schema_name, NULL::STRING AS table_name, grantee, privilege_type FROM (`) 121 source.WriteString(dbPrivQuery) 122 source.WriteByte(')') 123 // If the current database is set, restrict the command to it. 124 if currDB := d.evalCtx.SessionData.Database; currDB != "" { 125 fmt.Fprintf(&cond, ` WHERE database_name = %s`, lex.EscapeSQLString(currDB)) 126 } else { 127 cond.WriteString(`WHERE true`) 128 } 129 } 130 } 131 132 if n.Grantees != nil { 133 params = params[:0] 134 for _, grantee := range n.Grantees.ToStrings() { 135 params = append(params, lex.EscapeSQLString(grantee)) 136 } 137 fmt.Fprintf(&cond, ` AND grantee IN (%s)`, strings.Join(params, ",")) 138 } 139 query := fmt.Sprintf("SELECT * FROM (%s) %s ORDER BY %s", source.String(), cond.String(), orderBy) 140 return parse(query) 141 }