diff --git a/pkg/sql/logictest/testdata/logic_test/distsql_stats b/pkg/sql/logictest/testdata/logic_test/distsql_stats index abb9280d6827..33164526b6ac 100644 --- a/pkg/sql/logictest/testdata/logic_test/distsql_stats +++ b/pkg/sql/logictest/testdata/logic_test/distsql_stats @@ -1542,3 +1542,91 @@ SELECT jsonb_pretty(COALESCE(json_agg(stat), '[]')) "row_count": 1 } ] + +# Regression test for #76573 +statement ok +CREATE TABLE t1 (a INT, b INT, c INT) + +statement ok +ANALYZE t1 + +statement ok +CREATE STATISTICS t1_ab ON a,b FROM t1 + +statement ok +CREATE STATISTICS t1_ac ON a,c FROM t1 + +statement ok +CREATE STATISTICS t1_bc ON b,c FROM t1 + +statement ok +ALTER TABLE t1 drop column c + +statement ok +show statistics for table t1 + +query TTIII colnames +SELECT + statistics_name, + column_names, + row_count, + distinct_count, + null_count +FROM + [SHOW STATISTICS FOR TABLE t1] +ORDER BY statistics_name, column_names::STRING +---- +statistics_name column_names row_count distinct_count null_count +NULL {a} 0 0 0 +NULL {b} 0 0 0 +NULL {rowid} 0 0 0 +t1_ab {a,b} 0 0 0 + +query T +SELECT jsonb_pretty(COALESCE(json_agg(stat), '[]')) + FROM ( +SELECT json_array_elements(statistics) - 'created_at' - 'histo_col_type' - 'histo_version' AS stat +FROM [SHOW STATISTICS USING JSON FOR TABLE t1] +) +---- +[ + { + "avg_size": 0, + "columns": [ + "rowid" + ], + "distinct_count": 0, + "null_count": 0, + "row_count": 0 + }, + { + "avg_size": 0, + "columns": [ + "a" + ], + "distinct_count": 0, + "null_count": 0, + "row_count": 0 + }, + { + "avg_size": 0, + "columns": [ + "b" + ], + "distinct_count": 0, + "null_count": 0, + "row_count": 0 + }, + { + "avg_size": 0, + "columns": [ + "a", + "b" + ], + "distinct_count": 0, + "name": "t1_ab", + "null_count": 0, + "row_count": 0 + } +] + diff --git a/pkg/sql/show_stats.go b/pkg/sql/show_stats.go index 17fcfd4cfd18..60e73223d5d9 100644 --- a/pkg/sql/show_stats.go +++ b/pkg/sql/show_stats.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" "github.com/cockroachdb/cockroach/pkg/sql/stats" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util/errorutil" @@ -152,27 +153,37 @@ func (p *planner) ShowTableStats(ctx context.Context, n *tree.ShowTableStats) (p v := p.newContainerValuesNode(columns, 0) if n.UsingJSON { - result := make([]stats.JSONStatistic, len(rows)) - for i, r := range rows { - result[i].CreatedAt = tree.AsStringWithFlags(r[createdAtIdx], tree.FmtBareStrings) - result[i].RowCount = (uint64)(*r[rowCountIdx].(*tree.DInt)) - result[i].DistinctCount = (uint64)(*r[distinctCountIdx].(*tree.DInt)) - result[i].NullCount = (uint64)(*r[nullCountIdx].(*tree.DInt)) + result := make([]stats.JSONStatistic, 0, len(rows)) + for _, r := range rows { + var statsRow stats.JSONStatistic + colIDs := r[columnIDsIdx].(*tree.DArray).Array + statsRow.Columns = make([]string, len(colIDs)) + ignoreStatsRowWithDroppedColumn := false + for j, d := range colIDs { + statsRow.Columns[j], err = statColumnString(desc, d) + if err != nil && sqlerrors.IsUndefinedColumnError(err) { + ignoreStatsRowWithDroppedColumn = true + break + } + } + if ignoreStatsRowWithDroppedColumn { + continue + } + statsRow.CreatedAt = tree.AsStringWithFlags(r[createdAtIdx], tree.FmtBareStrings) + statsRow.RowCount = (uint64)(*r[rowCountIdx].(*tree.DInt)) + statsRow.DistinctCount = (uint64)(*r[distinctCountIdx].(*tree.DInt)) + statsRow.NullCount = (uint64)(*r[nullCountIdx].(*tree.DInt)) if avgSizeColVerActive { - result[i].AvgSize = (uint64)(*r[avgSizeIdx].(*tree.DInt)) + statsRow.AvgSize = (uint64)(*r[avgSizeIdx].(*tree.DInt)) } if r[nameIdx] != tree.DNull { - result[i].Name = string(*r[nameIdx].(*tree.DString)) + statsRow.Name = string(*r[nameIdx].(*tree.DString)) } - colIDs := r[columnIDsIdx].(*tree.DArray).Array - result[i].Columns = make([]string, len(colIDs)) - for j, d := range colIDs { - result[i].Columns[j] = statColumnString(desc, d) - } - if err := result[i].DecodeAndSetHistogram(ctx, &p.semaCtx, r[histIdx]); err != nil { + if err := statsRow.DecodeAndSetHistogram(ctx, &p.semaCtx, r[histIdx]); err != nil { v.Close(ctx) return nil, err } + result = append(result, statsRow) } encoded, err := encjson.Marshal(result) if err != nil { @@ -200,8 +211,18 @@ func (p *planner) ShowTableStats(ctx context.Context, n *tree.ShowTableStats) (p colIDs := r[columnIDsIdx].(*tree.DArray).Array colNames := tree.NewDArray(types.String) colNames.Array = make(tree.Datums, len(colIDs)) + ignoreStatsRowWithDroppedColumn := false + var colName string for i, d := range colIDs { - colNames.Array[i] = tree.NewDString(statColumnString(desc, d)) + colName, err = statColumnString(desc, d) + if err != nil && sqlerrors.IsUndefinedColumnError(err) { + ignoreStatsRowWithDroppedColumn = true + break + } + colNames.Array[i] = tree.NewDString(colName) + } + if ignoreStatsRowWithDroppedColumn { + continue } histogramID := tree.DNull @@ -243,12 +264,12 @@ func (p *planner) ShowTableStats(ctx context.Context, n *tree.ShowTableStats) (p }, nil } -func statColumnString(desc catalog.TableDescriptor, colID tree.Datum) string { +func statColumnString(desc catalog.TableDescriptor, colID tree.Datum) (colName string, err error) { id := descpb.ColumnID(*colID.(*tree.DInt)) colDesc, err := desc.FindColumnWithID(id) if err != nil { // This can happen if a column was removed. - return "" + return "", err } - return colDesc.GetName() + return colDesc.GetName(), nil }