diff --git a/enginetest/enginetests.go b/enginetest/enginetests.go index 660c0779a9..fcd6e356ad 100644 --- a/enginetest/enginetests.go +++ b/enginetest/enginetests.go @@ -252,7 +252,7 @@ func TestInfoSchema(t *testing.T, h Harness) { ctx, err := p.AddProcess(ctx, "SELECT foo") require.NoError(t, err) - TestQueryWithContext(t, ctx, e, h, "SELECT * FROM information_schema.processlist", []sql.Row{{1, "root", "localhost", "NULL", "Query", 0, "processlist(processlist (0/? partitions))", "SELECT foo"}}, nil, nil) + TestQueryWithContext(t, ctx, e, h, "SELECT * FROM information_schema.processlist", []sql.Row{{uint64(1), "root", "localhost", "NULL", "Query", 0, "processlist(processlist (0/? partitions))", "SELECT foo"}}, nil, nil) }) for _, tt := range queries.SkippedInfoSchemaQueries { diff --git a/enginetest/queries/priv_auth_queries.go b/enginetest/queries/priv_auth_queries.go index f0bbdf9ea5..303e210e00 100644 --- a/enginetest/queries/priv_auth_queries.go +++ b/enginetest/queries/priv_auth_queries.go @@ -1403,7 +1403,47 @@ var UserPrivTests = []UserPrivilegeTest{ }, }, { - Name: "information_schema shows tables with privileges only", + Name: "information_schema.column_statistics shows columns with privileges only", + SetUpScript: []string{ + "CREATE TABLE two (i int primary key, j int)", + "INSERT INTO two VALUES (1, 4), (2, 5), (3, 6)", + "CREATE TABLE one (f float)", + "INSERT INTO one VALUES (1.25), (45.25), (7.5), (10.5)", + "ANALYZE TABLE one", + "ANALYZE TABLE two", + "CREATE USER tester@localhost;", + }, + Assertions: []UserPrivilegeTestAssertion{ + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.one TO tester@localhost;", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM information_schema.column_statistics where schema_name = 'mydb';", + Expected: []sql.Row{{"mydb", "one", "f", sql.JSONDocument{Val: map[string]interface{}{"buckets": []interface{}{[]interface{}{"1.25", "1.25", "0.25"}, []interface{}{"7.50", "7.50", "0.25"}, []interface{}{"10.50", "10.50", "0.25"}, []interface{}{"45.25", "45.25", "0.25"}}}}}}, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.two TO tester@localhost;", + Expected: []sql.Row{{sql.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM information_schema.column_statistics where schema_name = 'mydb';", + Expected: []sql.Row{{"mydb", "one", "f", sql.JSONDocument{Val: map[string]interface{}{"buckets": []interface{}{[]interface{}{"1.25", "1.25", "0.25"}, []interface{}{"7.50", "7.50", "0.25"}, []interface{}{"10.50", "10.50", "0.25"}, []interface{}{"45.25", "45.25", "0.25"}}}}}, + {"mydb", "two", "i", sql.JSONDocument{Val: map[string]interface{}{"buckets": []interface{}{[]interface{}{"1.00", "1.00", "0.33"}, []interface{}{"2.00", "2.00", "0.33"}, []interface{}{"3.00", "3.00", "0.33"}}}}}, + {"mydb", "two", "j", sql.JSONDocument{Val: map[string]interface{}{"buckets": []interface{}{[]interface{}{"4.00", "4.00", "0.33"}, []interface{}{"5.00", "5.00", "0.33"}, []interface{}{"6.00", "6.00", "0.33"}}}}}}, + }, + }, + }, + { + Name: "information_schema.statistics shows tables with privileges only", SetUpScript: []string{ "CREATE TABLE checks (a INTEGER PRIMARY KEY, b INTEGER, c VARCHAR(20))", "CREATE TABLE test (pk BIGINT PRIMARY KEY, c VARCHAR(20), p POINT default (POINT(1,1)))", diff --git a/enginetest/queries/queries.go b/enginetest/queries/queries.go index 843829fd41..419bbafe83 100644 --- a/enginetest/queries/queries.go +++ b/enginetest/queries/queries.go @@ -5137,7 +5137,7 @@ var QueryTests = []QueryTest{ { sql.Collation_binary.String(), "binary", - int64(sql.Collation_binary), + uint64(sql.Collation_binary), sql.Collation_binary.IsDefault(), sql.Collation_binary.IsCompiled(), sql.Collation_binary.SortLength(), @@ -5146,7 +5146,7 @@ var QueryTests = []QueryTest{ { sql.Collation_utf8_general_ci.String(), "utf8mb3", - int64(sql.Collation_utf8_general_ci), + uint64(sql.Collation_utf8_general_ci), sql.Collation_utf8_general_ci.IsDefault(), sql.Collation_utf8_general_ci.IsCompiled(), sql.Collation_utf8_general_ci.SortLength(), @@ -5155,7 +5155,7 @@ var QueryTests = []QueryTest{ { sql.Collation_utf8mb4_0900_ai_ci.String(), "utf8mb4", - int64(sql.Collation_utf8mb4_0900_ai_ci), + uint64(sql.Collation_utf8mb4_0900_ai_ci), sql.Collation_utf8mb4_0900_ai_ci.IsDefault(), sql.Collation_utf8mb4_0900_ai_ci.IsCompiled(), sql.Collation_utf8mb4_0900_ai_ci.SortLength(), @@ -5173,7 +5173,7 @@ var QueryTests = []QueryTest{ { sql.Collation_binary.String(), "binary", - int64(sql.Collation_binary), + uint64(sql.Collation_binary), sql.Collation_binary.IsDefault(), sql.Collation_binary.IsCompiled(), sql.Collation_binary.SortLength(), @@ -5191,7 +5191,7 @@ var QueryTests = []QueryTest{ { sql.Collation_utf8mb4_0900_ai_ci.String(), "utf8mb4", - int64(sql.Collation_utf8mb4_0900_ai_ci), + uint64(sql.Collation_utf8mb4_0900_ai_ci), sql.Collation_utf8mb4_0900_ai_ci.IsDefault(), sql.Collation_utf8mb4_0900_ai_ci.IsCompiled(), sql.Collation_utf8mb4_0900_ai_ci.SortLength(), @@ -8910,6 +8910,19 @@ var InfoSchemaQueries = []QueryTest{ {"mydb.fk_tbl", "pk", "pk,pk,pk"}, }, }, + { + Query: `SELECT count(*) FROM information_schema.COLLATIONS`, + Expected: []sql.Row{{286}}, + }, + { + Query: `SELECT * FROM information_schema.COLLATIONS ORDER BY collation_name LIMIT 4`, + Expected: []sql.Row{ + {"armscii8_bin", "armscii8", uint64(64), "", "Yes", uint32(1), "PAD SPACE"}, + {"armscii8_general_ci", "armscii8", uint64(32), "Yes", "Yes", uint32(1), "PAD SPACE"}, + {"ascii_bin", "ascii", uint64(65), "", "Yes", uint32(1), "PAD SPACE"}, + {"ascii_general_ci", "ascii", uint64(11), "Yes", "Yes", uint32(1), "PAD SPACE"}, + }, + }, { Query: `SELECT * FROM information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ORDER BY collation_name LIMIT 4 `, Expected: []sql.Row{ @@ -8919,6 +8932,12 @@ var InfoSchemaQueries = []QueryTest{ {"ascii_general_ci", "ascii"}, }, }, + { + Query: `SELECT * FROM information_schema.ENGINES ORDER BY engine`, + Expected: []sql.Row{ + {"InnoDB", "DEFAULT", "Supports transactions, row-level locking, and foreign keys", "YES", "YES", "YES"}, + }, + }, { Query: `SELECT * from information_schema.administrable_role_authorizations`, Expected: []sql.Row{}, @@ -9151,6 +9170,10 @@ FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'mydb' AND INDEX_NAME='P {"mydb", "mytable", "i", 1, "PRIMARY"}, }, }, + { + Query: "select * from information_schema.character_sets;", + Expected: []sql.Row{{"utf8mb4", "utf8mb4_0900_ai_ci", "UTF-8 Unicode", uint32(4)}}, + }, { Query: `show columns from fk_tbl from mydb`, Expected: []sql.Row{ diff --git a/sql/collations.go b/sql/collations.go index 51650008a2..97239eaaf3 100644 --- a/sql/collations.go +++ b/sql/collations.go @@ -252,7 +252,11 @@ const ( Collation_utf8mb4_0900_as_ci CollationID = 305 Collation_utf8mb4_0900_as_cs CollationID = 278 Collation_utf8mb4_0900_bin CollationID = 309 + Collation_utf8mb4_bg_0900_ai_ci CollationID = 318 + Collation_utf8mb4_bg_0900_as_cs CollationID = 319 Collation_utf8mb4_bin CollationID = 46 + Collation_utf8mb4_bs_0900_ai_ci CollationID = 316 + Collation_utf8mb4_bs_0900_as_cs CollationID = 317 Collation_utf8mb4_croatian_ci CollationID = 245 Collation_utf8mb4_cs_0900_ai_ci CollationID = 266 Collation_utf8mb4_cs_0900_as_cs CollationID = 289 @@ -274,6 +278,8 @@ const ( Collation_utf8mb4_et_0900_as_cs CollationID = 285 Collation_utf8mb4_general_ci CollationID = 45 Collation_utf8mb4_german2_ci CollationID = 244 + Collation_utf8mb4_gl_0900_ai_ci CollationID = 320 + Collation_utf8mb4_gl_0900_as_cs CollationID = 321 Collation_utf8mb4_hr_0900_ai_ci CollationID = 275 Collation_utf8mb4_hr_0900_as_cs CollationID = 298 Collation_utf8mb4_hu_0900_ai_ci CollationID = 274 @@ -292,6 +298,12 @@ const ( Collation_utf8mb4_lt_0900_as_cs CollationID = 291 Collation_utf8mb4_lv_0900_ai_ci CollationID = 258 Collation_utf8mb4_lv_0900_as_cs CollationID = 281 + Collation_utf8mb4_mn_cyrl_0900_ai_ci CollationID = 322 + Collation_utf8mb4_mn_cyrl_0900_as_cs CollationID = 323 + Collation_utf8mb4_nb_0900_ai_ci CollationID = 310 + Collation_utf8mb4_nb_0900_as_cs CollationID = 311 + Collation_utf8mb4_nn_0900_ai_ci CollationID = 312 + Collation_utf8mb4_nn_0900_as_cs CollationID = 313 Collation_utf8mb4_persian_ci CollationID = 240 Collation_utf8mb4_pl_0900_ai_ci CollationID = 261 Collation_utf8mb4_pl_0900_as_cs CollationID = 284 @@ -311,6 +323,8 @@ const ( Collation_utf8mb4_slovenian_ci CollationID = 228 Collation_utf8mb4_spanish2_ci CollationID = 238 Collation_utf8mb4_spanish_ci CollationID = 231 + Collation_utf8mb4_sr_latn_0900_ai_ci CollationID = 314 + Collation_utf8mb4_sr_latn_0900_as_cs CollationID = 315 Collation_utf8mb4_sv_0900_ai_ci CollationID = 264 Collation_utf8mb4_sv_0900_as_cs CollationID = 287 Collation_utf8mb4_swedish_ci CollationID = 232 @@ -365,7 +379,7 @@ const ( // efficiently passed around (since only an uint16 is needed), while still being able to quickly access all of their // properties (index lookups are significantly faster than map lookups). Not all IDs are used, which is why there are // gaps in the array. -var collationArray = [310]Collation{ +var collationArray = [324]Collation{ /*000*/ {Collation_Unspecified, "", CharacterSet_Unspecified, true, true, 0, "", nil}, /*001*/ {Collation_big5_chinese_ci, "big5_chinese_ci", CharacterSet_big5, true, true, 1, "PAD SPACE", nil}, /*002*/ {Collation_latin2_czech_cs, "latin2_czech_cs", CharacterSet_latin2, false, true, 4, "PAD SPACE", nil}, @@ -676,6 +690,20 @@ var collationArray = [310]Collation{ /*307*/ {Collation_utf8mb4_ru_0900_as_cs, "utf8mb4_ru_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, /*308*/ {Collation_utf8mb4_zh_0900_as_cs, "utf8mb4_zh_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, /*309*/ {Collation_utf8mb4_0900_bin, "utf8mb4_0900_bin", CharacterSet_utf8mb4, false, true, 1, "NO PAD", encodings.Utf8mb4_0900_bin_RuneWeight}, + /*310*/ {Collation_utf8mb4_nb_0900_ai_ci, "utf8mb4_nb_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*311*/ {Collation_utf8mb4_nb_0900_as_cs, "utf8mb4_nb_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*312*/ {Collation_utf8mb4_nn_0900_ai_ci, "utf8mb4_nn_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*313*/ {Collation_utf8mb4_nn_0900_as_cs, "utf8mb4_nn_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*314*/ {Collation_utf8mb4_sr_latn_0900_ai_ci, "utf8mb4_sr_latn_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*315*/ {Collation_utf8mb4_sr_latn_0900_as_cs, "utf8mb4_sr_latn_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*316*/ {Collation_utf8mb4_bs_0900_ai_ci, "utf8mb4_bs_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*317*/ {Collation_utf8mb4_bs_0900_as_cs, "utf8mb4_bs_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*318*/ {Collation_utf8mb4_bg_0900_ai_ci, "utf8mb4_bg_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*319*/ {Collation_utf8mb4_bg_0900_as_cs, "utf8mb4_bg_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*320*/ {Collation_utf8mb4_gl_0900_ai_ci, "utf8mb4_gl_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*321*/ {Collation_utf8mb4_gl_0900_as_cs, "utf8mb4_gl_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*322*/ {Collation_utf8mb4_mn_cyrl_0900_ai_ci, "utf8mb4_mn_cyrl_0900_ai_ci", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, + /*323*/ {Collation_utf8mb4_mn_cyrl_0900_as_cs, "utf8mb4_mn_cyrl_0900_as_cs", CharacterSet_utf8mb4, false, true, 0, "NO PAD", nil}, } func init() { @@ -795,8 +823,8 @@ func (c CollationID) IsCompiled() string { } // SortLength returns the sort length of the collation. -func (c CollationID) SortLength() int64 { - return int64(collationArray[c].SortLength) +func (c CollationID) SortLength() uint32 { + return uint32(collationArray[c].SortLength) } // PadAttribute returns a string representing the pad attribute of the collation. diff --git a/sql/information_schema/information_schema.go b/sql/information_schema/information_schema.go index f57fd4e557..11ca8271c5 100644 --- a/sql/information_schema/information_schema.go +++ b/sql/information_schema/information_schema.go @@ -857,7 +857,7 @@ func collationsRowIter(ctx *Context, c Catalog) (RowIter, error) { rows = append(rows, Row{ c.Name, c.CharacterSet.Name(), - int64(c.ID), + uint64(c.ID), c.ID.IsDefault(), c.ID.IsCompiled(), c.ID.SortLength(), @@ -874,10 +874,15 @@ func columnStatisticsRowIter(ctx *Context, c Catalog) (RowIter, error) { if err != nil { return nil, err } + + privSet, privSetCount := ctx.GetPrivilegeSet() for _, db := range c.AllDatabases(ctx) { - err := DBTableIter(ctx, db, func(t Table) (cont bool, err error) { + dbName := db.Name() + privSetDb := privSet.Database(dbName) - tableHist, err := statsTbl.Hist(ctx, db.Name(), t.Name()) + err := DBTableIter(ctx, db, func(t Table) (cont bool, err error) { + privSetTbl := privSetDb.Table(t.Name()) + tableHist, err := statsTbl.Hist(ctx, dbName, t.Name()) if err != nil { return true, nil } @@ -887,6 +892,10 @@ func columnStatisticsRowIter(ctx *Context, c Catalog) (RowIter, error) { } for _, col := range t.Schema() { + privSetCol := privSetTbl.Column(col.Name) + if privSetCount == 0 && privSetDb.Count() == 0 && privSetTbl.Count() == 0 && privSetCol.Count() == 0 { + continue + } if _, ok := col.Type.(StringType); ok { continue } @@ -901,19 +910,14 @@ func columnStatisticsRowIter(ctx *Context, c Catalog) (RowIter, error) { buckets[i] = []interface{}{fmt.Sprintf("%.2f", b.LowerBound), fmt.Sprintf("%.2f", b.UpperBound), fmt.Sprintf("%.2f", b.Frequency)} } + // TODO: missing other key/value pairs in the JSON + histogram := JSONDocument{Val: map[string]interface{}{"buckets": buckets}} + rows = append(rows, Row{ db.Name(), // table_schema t.Name(), // table_name col.Name, // column_name - //hist.Mean, // mean - //hist.Min, // min - //hist.Max, // max - //hist.Count, // count - //hist.NullCount, // null_count - //hist.DistinctCount, // distinct_count - //bucketStrings, // buckets - // TODO: missing other key/value pairs in the JSON - JSONDocument{Val: map[string]interface{}{"buckets": buckets}}, // histogram + histogram, // histogram }) } return true, nil @@ -1029,12 +1033,12 @@ func processListRowIter(ctx *Context, c Catalog) (RowIter, error) { } sort.Strings(status) rows[i] = Row{ - int64(proc.Connection), // id + uint64(proc.Connection), // id proc.User, // user ctx.Session.Client().Address, // host db, // db "Query", // command - int64(proc.Seconds()), // time + int32(proc.Seconds()), // time strings.Join(status, ", "), // state proc.Query, // info } diff --git a/sql/mysql_db/privileged_database_provider.go b/sql/mysql_db/privileged_database_provider.go index a960728454..5d015e2b37 100644 --- a/sql/mysql_db/privileged_database_provider.go +++ b/sql/mysql_db/privileged_database_provider.go @@ -157,7 +157,8 @@ func (pdb PrivilegedDatabase) GetTableNames(ctx *sql.Context) ([]string, error) privSet := pdb.grantTables.UserActivePrivilegeSet(ctx) dbSet := privSet.Database(pdb.db.Name()) // If there are no usable privileges for this database then no table is accessible. - if privSet.Count() == 0 && !dbSet.HasPrivileges() { + privSetCount := privSet.Count() + if privSetCount == 0 && !dbSet.HasPrivileges() { return nil, nil } @@ -165,7 +166,6 @@ func (pdb PrivilegedDatabase) GetTableNames(ctx *sql.Context) ([]string, error) if err != nil { return nil, err } - privSetCount := privSet.Count() dbSetCount := dbSet.Count() for _, tblName := range tblNames { // If the user has any global static privileges, database-level privileges, or table-relevant privileges then a