Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/generated/sql/bnf/create_table_as_stmt.bnf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
create_table_as_stmt ::=
'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' column_name create_as_col_qual_list ( ( ',' column_name create_as_col_qual_list | ',' family_def | ',' create_as_constraint_def ) )* ')' opt_with_storage_parameter_list 'AS' select_stmt 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' table_name opt_with_storage_parameter_list 'AS' select_stmt 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' column_name create_as_col_qual_list ( ( ',' column_name create_as_col_qual_list | ',' family_def | ',' create_as_constraint_def ) )* ')' opt_with_storage_parameter_list 'AS' select_stmt 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name opt_with_storage_parameter_list 'AS' select_stmt 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' column_name create_as_col_qual_list ( ( ',' column_name create_as_col_qual_list | ',' family_def | ',' create_as_constraint_def ) )* ')' opt_with_storage_parameter_list 'AS' select_stmt opt_create_as_data 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' table_name opt_with_storage_parameter_list 'AS' select_stmt opt_create_as_data 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' column_name create_as_col_qual_list ( ( ',' column_name create_as_col_qual_list | ',' family_def | ',' create_as_constraint_def ) )* ')' opt_with_storage_parameter_list 'AS' select_stmt opt_create_as_data 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name opt_with_storage_parameter_list 'AS' select_stmt opt_create_as_data 'ON' 'COMMIT' 'PRESERVE' 'ROWS'
7 changes: 5 additions & 2 deletions docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -2028,8 +2028,8 @@ create_table_stmt ::=
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' opt_table_elem_list ')' opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality

create_table_as_stmt ::=
'CREATE' opt_persistence_temp_table 'TABLE' table_name create_as_opt_col_list opt_table_with 'AS' select_stmt opt_create_table_on_commit
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name create_as_opt_col_list opt_table_with 'AS' select_stmt opt_create_table_on_commit
'CREATE' opt_persistence_temp_table 'TABLE' table_name create_as_opt_col_list opt_table_with 'AS' select_stmt opt_create_as_data opt_create_table_on_commit
| 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name create_as_opt_col_list opt_table_with 'AS' select_stmt opt_create_as_data opt_create_table_on_commit

create_type_stmt ::=
'CREATE' 'TYPE' type_name 'AS' 'ENUM' '(' opt_enum_val_list ')'
Expand Down Expand Up @@ -2949,6 +2949,9 @@ create_as_opt_col_list ::=
'(' create_as_table_defs ')'
|

opt_create_as_data ::=
'WITH' 'NO' 'DATA'

opt_enum_val_list ::=
enum_val_list
|
Expand Down
9 changes: 5 additions & 4 deletions pkg/sql/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,9 @@ func (n *createTableNode) startExec(params runParams) error {
}

// If we have a single statement txn we want to run CTAS async, and
// consequently ensure it gets queued as a SchemaChange.
if params.extendedEvalCtx.TxnIsSingleStmt {
// consequently ensure it gets queued as a SchemaChange. WITH NO DATA
// has no data to copy, so the descriptor can go PUBLIC immediately.
if params.extendedEvalCtx.TxnIsSingleStmt && !n.n.WithNoData {
desc.State = descpb.DescriptorState_ADD
}
} else {
Expand Down Expand Up @@ -538,8 +539,8 @@ func (n *createTableNode) startExec(params runParams) error {
}

// If we are in a multi-statement txn or the source has placeholders, we
// execute the CTAS query synchronously.
if n.n.As() && !params.extendedEvalCtx.TxnIsSingleStmt {
// execute the CTAS query synchronously. WITH NO DATA skips the row fill.
if n.n.As() && !params.extendedEvalCtx.TxnIsSingleStmt && !n.n.WithNoData {
err = func() error {
// The data fill portion of CREATE AS must operate on a read snapshot,
// so that it doesn't end up observing its own writes.
Expand Down
61 changes: 61 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/create_as
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,64 @@ forks 30 1
spoons 10 1

subtest end

# Test CREATE TABLE ... AS ... WITH NO DATA (#171333). The destination table
# inherits the SELECT's column types but is left empty.
subtest with_no_data

statement ok
CREATE TABLE wnd_src (a INT, b STRING); INSERT INTO wnd_src VALUES (1, 'x'), (2, 'y')

statement ok
CREATE TABLE wnd_empty AS SELECT * FROM wnd_src WITH NO DATA

query IT rowsort
SELECT a, b FROM wnd_empty
----

query TT
SELECT column_name, data_type FROM [SHOW COLUMNS FROM wnd_empty] WHERE column_name IN ('a', 'b') ORDER BY column_name
----
a INT8
b STRING

# Explicit WITH DATA still copies rows.
statement ok
CREATE TABLE wnd_full AS SELECT * FROM wnd_src WITH DATA

query IT rowsort
SELECT a, b FROM wnd_full
----
1 x
2 y

# Default (no clause) still copies rows.
statement ok
CREATE TABLE wnd_default AS SELECT * FROM wnd_src

query IT rowsort
SELECT a, b FROM wnd_default
----
1 x
2 y

# IF NOT EXISTS path accepts WITH NO DATA.
statement ok
CREATE TABLE IF NOT EXISTS wnd_ifne AS SELECT * FROM wnd_src WITH NO DATA

query IT rowsort
SELECT a, b FROM wnd_ifne
----

# Temporary table path accepts WITH NO DATA.
statement ok
SET experimental_enable_temp_tables = 'on'

statement ok
CREATE TEMP TABLE wnd_temp AS SELECT * FROM wnd_src WITH NO DATA

query IT rowsort
SELECT a, b FROM wnd_temp
----

subtest end
2 changes: 0 additions & 2 deletions pkg/sql/parser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,6 @@ func TestUnimplementedSyntax(t *testing.T) {

{`CREATE TABLE a(b INT8) WITH OIDS`, 0, `create table with oids`, ``},

{`CREATE TABLE a AS SELECT b WITH NO DATA`, 0, `create table as with no data`, ``},

{`CREATE TABLE a(b INT8 REFERENCES c(x) MATCH PARTIAL`, 20305, `match partial`, ``},
{`CREATE TABLE a(b INT8, FOREIGN KEY (b) REFERENCES c(x) MATCH PARTIAL)`, 20305, `match partial`, ``},

Expand Down
10 changes: 6 additions & 4 deletions pkg/sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -1621,7 +1621,7 @@ func (u *sqlSymUnion) filterType() tree.FilterType {
%type <[]tree.RangePartition> range_partitions
%type <empty> opt_all_clause
%type <empty> opt_privileges_clause
%type <bool> distinct_clause opt_with_data
%type <bool> distinct_clause opt_with_data opt_create_as_data
%type <tree.DistinctOn> distinct_on_clause
%type <tree.NameList> opt_column_list insert_column_list opt_stats_columns query_stats_cols
// Note that "no index" variants exist to disable custom ORDER BY <index> syntax
Expand Down Expand Up @@ -11850,6 +11850,7 @@ create_table_as_stmt:
IfNotExists: false,
Defs: $5.tblDefs(),
AsSource: $8.slct(),
WithNoData: $9.bool(),
StorageParams: $6.storageParams(),
OnCommit: $10.createTableOnCommitSetting(),
Persistence: $2.persistence(),
Expand All @@ -11863,16 +11864,17 @@ create_table_as_stmt:
IfNotExists: true,
Defs: $8.tblDefs(),
AsSource: $11.slct(),
WithNoData: $12.bool(),
StorageParams: $9.storageParams(),
OnCommit: $13.createTableOnCommitSetting(),
Persistence: $2.persistence(),
}
}

opt_create_as_data:
/* EMPTY */ { /* no error */ }
| WITH DATA { /* SKIP DOC */ /* This is the default */ }
| WITH NO DATA { return unimplemented(sqllex, "create table as with no data") }
/* EMPTY */ { $$.val = false }
| WITH DATA { /* SKIP DOC */ $$.val = false }
| WITH NO DATA { $$.val = true }

/*
* Redundancy here is needed to avoid shift/reduce conflicts,
Expand Down
24 changes: 24 additions & 0 deletions pkg/sql/parser/testdata/create_table
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,30 @@ CREATE TABLE IF NOT EXISTS a AS SELECT (*) FROM b -- fully parenthesized
CREATE TABLE IF NOT EXISTS a AS SELECT * FROM b -- literals removed
CREATE TABLE IF NOT EXISTS _ AS SELECT * FROM _ -- identifiers removed

parse
CREATE TABLE a AS SELECT * FROM b WITH NO DATA
----
CREATE TABLE a AS SELECT * FROM b WITH NO DATA
CREATE TABLE a AS SELECT (*) FROM b WITH NO DATA -- fully parenthesized
CREATE TABLE a AS SELECT * FROM b WITH NO DATA -- literals removed
CREATE TABLE _ AS SELECT * FROM _ WITH NO DATA -- identifiers removed

parse
CREATE TABLE a AS SELECT * FROM b WITH DATA
----
CREATE TABLE a AS SELECT * FROM b -- normalized!
CREATE TABLE a AS SELECT (*) FROM b -- fully parenthesized
CREATE TABLE a AS SELECT * FROM b -- literals removed
CREATE TABLE _ AS SELECT * FROM _ -- identifiers removed

parse
CREATE TABLE IF NOT EXISTS a AS SELECT * FROM b WITH NO DATA
----
CREATE TABLE IF NOT EXISTS a AS SELECT * FROM b WITH NO DATA
CREATE TABLE IF NOT EXISTS a AS SELECT (*) FROM b WITH NO DATA -- fully parenthesized
CREATE TABLE IF NOT EXISTS a AS SELECT * FROM b WITH NO DATA -- literals removed
CREATE TABLE IF NOT EXISTS _ AS SELECT * FROM _ WITH NO DATA -- identifiers removed

parse
CREATE TABLE a AS SELECT * FROM b ORDER BY c
----
Expand Down
10 changes: 7 additions & 3 deletions pkg/sql/sem/tree/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -1550,9 +1550,10 @@ type CreateTable struct {
// In CREATE...AS queries, Defs represents a list of ColumnTableDefs, one for
// each column, and a ConstraintTableDef for each constraint on a subset of
// these columns.
Defs TableDefs
AsSource *Select
Locality *Locality
Defs TableDefs
AsSource *Select
WithNoData bool
Locality *Locality
}

// As returns true if this table represents a CREATE TABLE ... AS statement,
Expand Down Expand Up @@ -1609,6 +1610,9 @@ func (node *CreateTable) FormatBody(ctx *FmtCtx) {
}
ctx.WriteString(" AS ")
ctx.FormatNode(node.AsSource)
if node.WithNoData {
ctx.WriteString(" WITH NO DATA")
}
} else {
ctx.WriteString(" (")
ctx.FormatNode(&node.Defs)
Expand Down
3 changes: 3 additions & 0 deletions pkg/sql/sem/tree/pretty.go
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,9 @@ func (node *CreateTable) Doc(p *PrettyCfg) pretty.Doc {
clauses := make([]pretty.Doc, 0, 4)
if node.As() {
clauses = append(clauses, p.Doc(node.AsSource))
if node.WithNoData {
clauses = append(clauses, pretty.Keyword("WITH NO DATA"))
}
}
if node.PartitionByTable != nil {
clauses = append(clauses, p.Doc(node.PartitionByTable))
Expand Down
Loading