Skip to content

Commit d3c27b3

Browse files
committed
Add COPY .. TO .. syntax support
1 parent b578c58 commit d3c27b3

File tree

5 files changed

+412
-41
lines changed

5 files changed

+412
-41
lines changed

datafusion/core/src/execution/context.rs

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ use crate::logical_expr::{
6969
LogicalPlanBuilder, SetVariable, TableSource, TableType, UNNAMED_TABLE,
7070
};
7171
use crate::optimizer::OptimizerRule;
72-
use datafusion_sql::{planner::ParserOptions, ResolvedTableReference, TableReference};
72+
use datafusion_sql::{
73+
parser::{CopyToSource, CopyToStatement},
74+
planner::ParserOptions,
75+
ResolvedTableReference, TableReference,
76+
};
7377

7478
use crate::physical_optimizer::coalesce_batches::CoalesceBatches;
7579
use crate::physical_optimizer::repartition::Repartition;
@@ -1649,45 +1653,58 @@ impl SessionState {
16491653
// table providers for all relations referenced in this query
16501654
let mut relations = hashbrown::HashSet::with_capacity(10);
16511655

1652-
match statement {
1653-
DFStatement::Statement(s) => {
1654-
struct RelationVisitor<'a>(&'a mut hashbrown::HashSet<ObjectName>);
1656+
struct RelationVisitor<'a>(&'a mut hashbrown::HashSet<ObjectName>);
1657+
1658+
impl<'a> RelationVisitor<'a> {
1659+
/// Record that `relation` was used in this statement
1660+
fn insert(&mut self, relation: &ObjectName) {
1661+
self.0.get_or_insert_with(relation, |_| relation.clone());
1662+
}
1663+
}
16551664

1656-
impl<'a> Visitor for RelationVisitor<'a> {
1657-
type Break = ();
1665+
impl<'a> Visitor for RelationVisitor<'a> {
1666+
type Break = ();
16581667

1659-
fn pre_visit_relation(
1660-
&mut self,
1661-
relation: &ObjectName,
1662-
) -> ControlFlow<()> {
1663-
self.0.get_or_insert_with(relation, |_| relation.clone());
1664-
ControlFlow::Continue(())
1665-
}
1668+
fn pre_visit_relation(&mut self, relation: &ObjectName) -> ControlFlow<()> {
1669+
self.insert(relation);
1670+
ControlFlow::Continue(())
1671+
}
16661672

1667-
fn pre_visit_statement(
1668-
&mut self,
1669-
statement: &Statement,
1670-
) -> ControlFlow<()> {
1671-
if let Statement::ShowCreate {
1672-
obj_type: ShowCreateObject::Table | ShowCreateObject::View,
1673-
obj_name,
1674-
} = statement
1675-
{
1676-
self.0.get_or_insert_with(obj_name, |_| obj_name.clone());
1677-
}
1678-
ControlFlow::Continue(())
1679-
}
1673+
fn pre_visit_statement(&mut self, statement: &Statement) -> ControlFlow<()> {
1674+
if let Statement::ShowCreate {
1675+
obj_type: ShowCreateObject::Table | ShowCreateObject::View,
1676+
obj_name,
1677+
} = statement
1678+
{
1679+
self.insert(obj_name)
16801680
}
1681-
let mut visitor = RelationVisitor(&mut relations);
1681+
ControlFlow::Continue(())
1682+
}
1683+
}
1684+
1685+
let mut visitor = RelationVisitor(&mut relations);
1686+
match statement {
1687+
DFStatement::Statement(s) => {
16821688
let _ = s.as_ref().visit(&mut visitor);
16831689
}
16841690
DFStatement::CreateExternalTable(table) => {
1685-
relations.insert(ObjectName(vec![Ident::from(table.name.as_str())]));
1686-
}
1687-
DFStatement::DescribeTableStmt(table) => {
1688-
relations
1689-
.get_or_insert_with(&table.table_name, |_| table.table_name.clone());
1691+
visitor
1692+
.0
1693+
.insert(ObjectName(vec![Ident::from(table.name.as_str())]));
16901694
}
1695+
DFStatement::DescribeTableStmt(table) => visitor.insert(&table.table_name),
1696+
DFStatement::CopyTo(CopyToStatement {
1697+
source,
1698+
target: _,
1699+
options: _,
1700+
}) => match source {
1701+
CopyToSource::Relation(table_name) => {
1702+
visitor.insert(table_name);
1703+
}
1704+
CopyToSource::Query(query) => {
1705+
query.visit(&mut visitor);
1706+
}
1707+
},
16911708
}
16921709

16931710
// Always include information_schema if available
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# tests for copy command
19+
20+
statement ok
21+
create table source_table(col1 integer, col2 varchar) as values (1, 'Foo'), (2, 'Bar');
22+
23+
# Copy from table
24+
statement error DataFusion error: This feature is not implemented: `COPY \.\. TO \.\.` statement is yet supported
25+
COPY source_table to '/tmp/table.parquet';
26+
27+
# Copy from table with options
28+
statement error DataFusion error: This feature is not implemented: `COPY \.\. TO \.\.` statement is yet supported
29+
COPY source_table to '/tmp/table.parquet' (row_group_size 55);
30+
31+
# Copy from table with options (and trailing comma)
32+
statement error DataFusion error: This feature is not implemented: `COPY \.\. TO \.\.` statement is yet supported
33+
COPY source_table to '/tmp/table.parquet' (row_group_size 55, row_group_limit_bytes 9,);
34+
35+
36+
# Error cases:
37+
38+
# Incomplete statement
39+
statement error DataFusion error: SQL error: ParserError\("Expected \), found: EOF"\)
40+
COPY (select col2, sum(col1) from source_table
41+
42+
# Copy from table with non literal
43+
statement error DataFusion error: SQL error: ParserError\("Expected ',' or '\)' after option definition, found: \+"\)
44+
COPY source_table to '/tmp/table.parquet' (row_group_size 55 + 102);

0 commit comments

Comments
 (0)