From 871d1b347ab7444b4ccd7378999868300b3ef89e Mon Sep 17 00:00:00 2001 From: Rebecca Taft Date: Tue, 2 Jun 2020 18:19:28 -0500 Subject: [PATCH] sql: add support for ANALYZE This commit adds support for `ANALYZE ` by adding the statement as syntactic sugar for the equivalent command `CREATE STATISTICS "" FROM `. This improves compatibility with Postgres, and is needed to run the PostGIS tutorial as written. Note that this commit does not add support for `ANALYZE` without a table name. We can add support for that and other variants later if needed, but it is not necessary for the PostGIS tutorial. Fixes #49214 Release note (sql change): Added support for `ANALYZE `, which causes the database to collect statistics on the given table for use by the optimizer. The functionality of this command is equivalent to the existing command `CREATE STATISTICS "" FROM `, but it increases compatibility with Postgres by using the same syntax that Postgres uses. --- docs/generated/sql/bnf/stmt_block.bnf | 8 +++++ pkg/sql/create_stats.go | 8 +++++ .../testdata/logic_test/distsql_stats | 27 +++++++++++++++ pkg/sql/opaque.go | 3 ++ pkg/sql/parser/sql.y | 34 +++++++++++++++++-- pkg/sql/plan_opt.go | 1 + pkg/sql/sem/tree/analyze.go | 22 ++++++++++++ pkg/sql/sem/tree/stmt.go | 7 ++++ 8 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 pkg/sql/sem/tree/analyze.go diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 6afd89faaa38..7efea121f108 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -4,6 +4,7 @@ stmt_block ::= stmt ::= 'HELPTOKEN' | preparable_stmt + | analyze_stmt | copy_from_stmt | comment_stmt | execute_stmt @@ -42,6 +43,10 @@ preparable_stmt ::= | update_stmt | upsert_stmt +analyze_stmt ::= + 'ANALYZE' analyze_target + | 'ANALYSE' analyze_target + copy_from_stmt ::= 'COPY' table_name opt_column_list 'FROM' 'STDIN' opt_with_options @@ -206,6 +211,9 @@ update_stmt ::= upsert_stmt ::= opt_with_clause 'UPSERT' 'INTO' insert_target insert_rest returning_clause +analyze_target ::= + table_name + table_name ::= db_object_name diff --git a/pkg/sql/create_stats.go b/pkg/sql/create_stats.go index 4e1a9fd17535..b16a1ea6f340 100644 --- a/pkg/sql/create_stats.go +++ b/pkg/sql/create_stats.go @@ -54,6 +54,14 @@ func (p *planner) CreateStatistics(ctx context.Context, n *tree.CreateStats) (pl }, nil } +// Analyze is syntactic sugar for CreateStatistics. +func (p *planner) Analyze(ctx context.Context, n *tree.Analyze) (planNode, error) { + return &createStatsNode{ + CreateStats: tree.CreateStats{Table: n.Table}, + p: p, + }, nil +} + // createStatsNode is a planNode implemented in terms of a function. The // startJob function starts a Job during Start, and the remainder of the // CREATE STATISTICS planning and execution is performed within the jobs diff --git a/pkg/sql/logictest/testdata/logic_test/distsql_stats b/pkg/sql/logictest/testdata/logic_test/distsql_stats index 666cd790ffe0..b652da3b64ad 100644 --- a/pkg/sql/logictest/testdata/logic_test/distsql_stats +++ b/pkg/sql/logictest/testdata/logic_test/distsql_stats @@ -107,9 +107,36 @@ NULL {b} 256 4 0 true let $json_stats SHOW STATISTICS USING JSON FOR TABLE data +# ANALYZE is syntactic sugar for CREATE STATISTICS with default columns. +statement ok +ANALYZE data + +query TTIIIB colnames +SELECT + statistics_name, + column_names, + row_count, + distinct_count, + null_count, + histogram_id IS NOT NULL AS has_histogram +FROM + [SHOW STATISTICS FOR TABLE data]; +---- +statistics_name column_names row_count distinct_count null_count has_histogram +NULL {a} 256 4 0 true +NULL {a,b} 256 16 0 false +NULL {a,b,c} 256 64 0 false +NULL {a,b,c,d} 256 256 0 false +NULL {c} 256 4 0 true +NULL {c,d} 256 16 0 false +NULL {b} 256 4 0 false +NULL {d} 256 4 0 false +NULL {e} 256 2 0 true + statement ok DELETE FROM system.table_statistics +# Restore the old stats. statement ok ALTER TABLE data INJECT STATISTICS '$json_stats' diff --git a/pkg/sql/opaque.go b/pkg/sql/opaque.go index cdd8ceaedd54..fc0c5fa339eb 100644 --- a/pkg/sql/opaque.go +++ b/pkg/sql/opaque.go @@ -57,6 +57,8 @@ func buildOpaque( plan, err = p.AlterRole(ctx, n) case *tree.AlterSequence: plan, err = p.AlterSequence(ctx, n) + case *tree.Analyze: + plan, err = p.Analyze(ctx, n) case *tree.CommentOnColumn: plan, err = p.CommentOnColumn(ctx, n) case *tree.CommentOnDatabase: @@ -169,6 +171,7 @@ func init() { &tree.AlterType{}, &tree.AlterSequence{}, &tree.AlterRole{}, + &tree.Analyze{}, &tree.CommentOnColumn{}, &tree.CommentOnDatabase{}, &tree.CommentOnIndex{}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 13ecd5664f0a..794127d0f877 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -762,6 +762,7 @@ func (u *sqlSymUnion) alterTypeAddValuePlacement() *tree.AlterTypeAddValuePlacem %type drop_view_stmt %type drop_sequence_stmt +%type analyze_stmt %type explain_stmt %type prepare_stmt %type preparable_stmt @@ -887,7 +888,7 @@ func (u *sqlSymUnion) alterTypeAddValuePlacement() *tree.AlterTypeAddValuePlacem %type schema_name %type <*tree.UnresolvedName> table_pattern complex_table_pattern %type <*tree.UnresolvedName> column_path prefixed_column_path column_path_with_star -%type insert_target create_stats_target +%type insert_target create_stats_target analyze_target %type <*tree.TableIndexName> table_index_name %type table_index_name_list @@ -1169,7 +1170,8 @@ stmt_block: stmt: HELPTOKEN { return helpWith(sqllex, "") } -| preparable_stmt // help texts in sub-rule +| preparable_stmt // help texts in sub-rule +| analyze_stmt // EXTEND WITH HELP: ANALYZE | copy_from_stmt | comment_stmt | execute_stmt // EXTEND WITH HELP: EXECUTE @@ -2844,6 +2846,34 @@ table_name_list: $$.val = append($1.tableNames(), name) } +// %Help: ANALYZE - collect table statistics +// %Category: Misc +// %Text: +// ANALYZE +// +// %SeeAlso: CREATE STATISTICS +analyze_stmt: + ANALYZE analyze_target + { + $$.val = &tree.Analyze{ + Table: $2.tblExpr(), + } + } +| ANALYZE error // SHOW HELP: ANALYZE +| ANALYSE analyze_target + { + $$.val = &tree.Analyze{ + Table: $2.tblExpr(), + } + } +| ANALYSE error // SHOW HELP: ANALYZE + +analyze_target: + table_name + { + $$.val = $1.unresolvedObjectName() + } + // %Help: EXPLAIN - show the logical plan of a query // %Category: Misc // %Text: diff --git a/pkg/sql/plan_opt.go b/pkg/sql/plan_opt.go index 977b7d766915..6404567dece5 100644 --- a/pkg/sql/plan_opt.go +++ b/pkg/sql/plan_opt.go @@ -50,6 +50,7 @@ func (p *planner) prepareUsingOptimizer(ctx context.Context) (planFlags, error) switch stmt.AST.(type) { case *tree.AlterIndex, *tree.AlterTable, *tree.AlterSequence, + *tree.Analyze, *tree.BeginTransaction, *tree.CommentOnColumn, *tree.CommentOnDatabase, *tree.CommentOnIndex, *tree.CommentOnTable, *tree.CommitTransaction, diff --git a/pkg/sql/sem/tree/analyze.go b/pkg/sql/sem/tree/analyze.go new file mode 100644 index 000000000000..7bc71a17caa9 --- /dev/null +++ b/pkg/sql/sem/tree/analyze.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package tree + +// Analyze represents an ANALYZE statement. +type Analyze struct { + Table TableExpr +} + +// Format implements the NodeFormatter interface. +func (node *Analyze) Format(ctx *FmtCtx) { + ctx.WriteString("ANALYZE ") + ctx.FormatNode(node.Table) +} diff --git a/pkg/sql/sem/tree/stmt.go b/pkg/sql/sem/tree/stmt.go index 48a856c71713..25953c70bd65 100644 --- a/pkg/sql/sem/tree/stmt.go +++ b/pkg/sql/sem/tree/stmt.go @@ -212,6 +212,12 @@ func (*AlterRole) cclOnlyStatement() {} func (*AlterRole) hiddenFromShowQueries() {} +// StatementType implements the Statement interface. +func (*Analyze) StatementType() StatementType { return DDL } + +// StatementTag returns a short string identifying the type of statement. +func (*Analyze) StatementTag() string { return "ANALYZE" } + // StatementType implements the Statement interface. func (*Backup) StatementType() StatementType { return Rows } @@ -919,6 +925,7 @@ func (n *AlterTableSetNotNull) String() string { return AsString(n) } func (n *AlterType) String() string { return AsString(n) } func (n *AlterRole) String() string { return AsString(n) } func (n *AlterSequence) String() string { return AsString(n) } +func (n *Analyze) String() string { return AsString(n) } func (n *Backup) String() string { return AsString(n) } func (n *BeginTransaction) String() string { return AsString(n) } func (n *ControlJobs) String() string { return AsString(n) }