github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/delegate/show_table.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 "fmt" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/lex" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 20 ) 21 22 func (d *delegator) delegateShowCreate(n *tree.ShowCreate) (tree.Statement, error) { 23 sqltelemetry.IncrementShowCounter(sqltelemetry.Create) 24 25 const showCreateQuery = ` 26 WITH zone_configs AS ( 27 SELECT string_agg(raw_config_sql, e';\n') FROM crdb_internal.zones 28 WHERE database_name = %[1]s 29 AND table_name = %[2]s 30 AND raw_config_yaml IS NOT NULL 31 AND raw_config_sql IS NOT NULL 32 ) 33 SELECT 34 %[3]s AS table_name, 35 concat(create_statement, 36 CASE 37 WHEN NOT has_partitions 38 THEN NULL 39 WHEN (SELECT * FROM zone_configs) IS NULL 40 THEN e'\n-- Warning: Partitioned table with no zone configurations.' 41 ELSE concat(e';\n', (SELECT * FROM zone_configs)) 42 END 43 ) AS create_statement 44 FROM 45 %[4]s.crdb_internal.create_statements 46 WHERE 47 descriptor_id = %[6]d 48 ` 49 50 return d.showTableDetails(n.Name, showCreateQuery) 51 } 52 53 func (d *delegator) delegateShowIndexes(n *tree.ShowIndexes) (tree.Statement, error) { 54 sqltelemetry.IncrementShowCounter(sqltelemetry.Indexes) 55 getIndexesQuery := ` 56 SELECT 57 s.table_name, 58 s.index_name, 59 non_unique::BOOL, 60 seq_in_index, 61 column_name, 62 direction, 63 storing::BOOL, 64 implicit::BOOL` 65 66 if n.WithComment { 67 getIndexesQuery += `, 68 obj_description(pg_indexes.crdb_oid) AS comment` 69 } 70 71 getIndexesQuery += ` 72 FROM 73 %[4]s.information_schema.statistics AS s` 74 75 if n.WithComment { 76 getIndexesQuery += ` 77 LEFT JOIN pg_indexes ON 78 pg_indexes.tablename = s.table_name AND 79 pg_indexes.indexname = s.index_name 80 ` 81 } 82 83 getIndexesQuery += ` 84 WHERE 85 table_catalog=%[1]s 86 AND table_schema=%[5]s 87 AND table_name=%[2]s` 88 89 return d.showTableDetails(n.Table, getIndexesQuery) 90 } 91 92 func (d *delegator) delegateShowColumns(n *tree.ShowColumns) (tree.Statement, error) { 93 getColumnsQuery := ` 94 SELECT 95 column_name AS column_name, 96 crdb_sql_type AS data_type, 97 is_nullable::BOOL, 98 column_default, 99 generation_expression, 100 IF(inames[1] IS NULL, ARRAY[]:::STRING[], inames) AS indices, 101 is_hidden::BOOL` 102 103 if n.WithComment { 104 getColumnsQuery += `, 105 col_description(%[6]d, attnum) AS comment` 106 } 107 108 getColumnsQuery += ` 109 FROM 110 (SELECT column_name, crdb_sql_type, is_nullable, column_default, generation_expression, 111 ordinal_position, is_hidden, array_agg(index_name) AS inames 112 FROM 113 (SELECT column_name, crdb_sql_type, is_nullable, column_default, generation_expression, 114 ordinal_position, is_hidden 115 FROM %[4]s.information_schema.columns 116 WHERE (length(%[1]s)=0 OR table_catalog=%[1]s) AND table_schema=%[5]s AND table_name=%[2]s) 117 LEFT OUTER JOIN 118 (SELECT column_name, index_name 119 FROM %[4]s.information_schema.statistics 120 WHERE (length(%[1]s)=0 OR table_catalog=%[1]s) AND table_schema=%[5]s AND table_name=%[2]s) 121 USING(column_name) 122 GROUP BY column_name, crdb_sql_type, is_nullable, column_default, generation_expression, 123 ordinal_position, is_hidden 124 )` 125 126 if n.WithComment { 127 getColumnsQuery += ` 128 LEFT OUTER JOIN pg_attribute 129 ON column_name = pg_attribute.attname 130 AND attrelid = %[6]d` 131 } 132 133 getColumnsQuery += ` 134 ORDER BY ordinal_position` 135 136 return d.showTableDetails(n.Table, getColumnsQuery) 137 } 138 139 func (d *delegator) delegateShowConstraints(n *tree.ShowConstraints) (tree.Statement, error) { 140 sqltelemetry.IncrementShowCounter(sqltelemetry.Constraints) 141 const getConstraintsQuery = ` 142 SELECT 143 t.relname AS table_name, 144 c.conname AS constraint_name, 145 CASE c.contype 146 WHEN 'p' THEN 'PRIMARY KEY' 147 WHEN 'u' THEN 'UNIQUE' 148 WHEN 'c' THEN 'CHECK' 149 WHEN 'f' THEN 'FOREIGN KEY' 150 ELSE c.contype 151 END AS constraint_type, 152 c.condef AS details, 153 c.convalidated AS validated 154 FROM 155 %[4]s.pg_catalog.pg_class t, 156 %[4]s.pg_catalog.pg_namespace n, 157 %[4]s.pg_catalog.pg_constraint c 158 WHERE t.relname = %[2]s 159 AND n.nspname = %[5]s AND t.relnamespace = n.oid 160 AND t.oid = c.conrelid 161 ORDER BY 1, 2` 162 163 return d.showTableDetails(n.Table, getConstraintsQuery) 164 } 165 166 // showTableDetails returns the AST of a query which extracts information about 167 // the given table using the given query patterns in SQL. The query pattern must 168 // accept the following formatting parameters: 169 // %[1]s the database name as SQL string literal. 170 // %[2]s the unqualified table name as SQL string literal. 171 // %[3]s the given table name as SQL string literal. 172 // %[4]s the database name as SQL identifier. 173 // %[5]s the schema name as SQL string literal. 174 // %[6]s the table ID. 175 func (d *delegator) showTableDetails( 176 name *tree.UnresolvedObjectName, query string, 177 ) (tree.Statement, error) { 178 // We avoid the cache so that we can observe the details without 179 // taking a lease, like other SHOW commands. 180 flags := cat.Flags{AvoidDescriptorCaches: true, NoTableStats: true} 181 tn := name.ToTableName() 182 dataSource, resName, err := d.catalog.ResolveDataSource(d.ctx, flags, &tn) 183 if err != nil { 184 return nil, err 185 } 186 if err := d.catalog.CheckAnyPrivilege(d.ctx, dataSource); err != nil { 187 return nil, err 188 } 189 190 fullQuery := fmt.Sprintf(query, 191 lex.EscapeSQLString(resName.Catalog()), 192 lex.EscapeSQLString(resName.Table()), 193 lex.EscapeSQLString(resName.String()), 194 resName.CatalogName.String(), // note: CatalogName.String() != Catalog() 195 lex.EscapeSQLString(resName.Schema()), 196 dataSource.PostgresDescriptorID(), 197 ) 198 199 return parse(fullQuery) 200 }