Skip to content

Commit bbece90

Browse files
byte-sourcereriffyio
authored andcommitted
feat: support INSERT INTO [TABLE] FUNCTION of Clickhouse (apache#1633)
Co-authored-by: Kermit <[email protected]> Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent ef32785 commit bbece90

11 files changed

+147
-53
lines changed

src/ast/dml.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use super::{
3636
FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
3737
InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens,
3838
OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, SqliteOnConflict, TableEngine,
39-
TableWithJoins, Tag, WrappedCollection,
39+
TableObject, TableWithJoins, Tag, WrappedCollection,
4040
};
4141

4242
/// CREATE INDEX statement.
@@ -470,8 +470,7 @@ pub struct Insert {
470470
/// INTO - optional keyword
471471
pub into: bool,
472472
/// TABLE
473-
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
474-
pub table_name: ObjectName,
473+
pub table: TableObject,
475474
/// table_name as foo (for PostgreSQL)
476475
pub table_alias: Option<Ident>,
477476
/// COLUMNS
@@ -488,7 +487,7 @@ pub struct Insert {
488487
/// Columns defined after PARTITION
489488
pub after_columns: Vec<Ident>,
490489
/// whether the insert has the table keyword (Hive)
491-
pub table: bool,
490+
pub has_table_keyword: bool,
492491
pub on: Option<OnInsert>,
493492
/// RETURNING
494493
pub returning: Option<Vec<SelectItem>>,
@@ -503,9 +502,9 @@ pub struct Insert {
503502
impl Display for Insert {
504503
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505504
let table_name = if let Some(alias) = &self.table_alias {
506-
format!("{0} AS {alias}", self.table_name)
505+
format!("{0} AS {alias}", self.table)
507506
} else {
508-
self.table_name.to_string()
507+
self.table.to_string()
509508
};
510509

511510
if let Some(on_conflict) = self.or {
@@ -531,7 +530,7 @@ impl Display for Insert {
531530
ignore = if self.ignore { " IGNORE" } else { "" },
532531
over = if self.overwrite { " OVERWRITE" } else { "" },
533532
int = if self.into { " INTO" } else { "" },
534-
tbl = if self.table { " TABLE" } else { "" },
533+
tbl = if self.has_table_keyword { " TABLE" } else { "" },
535534
)?;
536535
}
537536
if !self.columns.is_empty() {

src/ast/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -7925,6 +7925,36 @@ impl fmt::Display for RenameTable {
79257925
}
79267926
}
79277927

7928+
/// Represents the referenced table in an `INSERT INTO` statement
7929+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7930+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7931+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7932+
pub enum TableObject {
7933+
/// Table specified by name.
7934+
/// Example:
7935+
/// ```sql
7936+
/// INSERT INTO my_table
7937+
/// ```
7938+
TableName(#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] ObjectName),
7939+
7940+
/// Table specified as a function.
7941+
/// Example:
7942+
/// ```sql
7943+
/// INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table)
7944+
/// ```
7945+
/// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/table-functions)
7946+
TableFunction(Function),
7947+
}
7948+
7949+
impl fmt::Display for TableObject {
7950+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7951+
match self {
7952+
Self::TableName(table_name) => write!(f, "{table_name}"),
7953+
Self::TableFunction(func) => write!(f, "FUNCTION {}", func),
7954+
}
7955+
}
7956+
}
7957+
79287958
#[cfg(test)]
79297959
mod tests {
79307960
use super::*;

src/ast/spans.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ use super::{
3232
OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction,
3333
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
3434
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
35-
TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use,
36-
Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
35+
TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins,
36+
UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WildcardAdditionalOptions, With,
37+
WithFill,
3738
};
3839

3940
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -1141,14 +1142,14 @@ impl Spanned for Insert {
11411142
or: _, // enum, sqlite specific
11421143
ignore: _, // bool
11431144
into: _, // bool
1144-
table_name,
1145+
table,
11451146
table_alias,
11461147
columns,
11471148
overwrite: _, // bool
11481149
source,
11491150
partitioned,
11501151
after_columns,
1151-
table: _, // bool
1152+
has_table_keyword: _, // bool
11521153
on,
11531154
returning,
11541155
replace_into: _, // bool
@@ -1158,7 +1159,7 @@ impl Spanned for Insert {
11581159
} = self;
11591160

11601161
union_spans(
1161-
core::iter::once(table_name.span())
1162+
core::iter::once(table.span())
11621163
.chain(table_alias.as_ref().map(|i| i.span))
11631164
.chain(columns.iter().map(|i| i.span))
11641165
.chain(source.as_ref().map(|q| q.span()))
@@ -2126,6 +2127,17 @@ impl Spanned for UpdateTableFromKind {
21262127
}
21272128
}
21282129

2130+
impl Spanned for TableObject {
2131+
fn span(&self) -> Span {
2132+
match self {
2133+
TableObject::TableName(ObjectName(segments)) => {
2134+
union_spans(segments.iter().map(|i| i.span))
2135+
}
2136+
TableObject::TableFunction(func) => func.span(),
2137+
}
2138+
}
2139+
}
2140+
21292141
#[cfg(test)]
21302142
pub mod tests {
21312143
use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect};

src/dialect/clickhouse.rs

+4
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@ impl Dialect for ClickHouseDialect {
5050
fn supports_limit_comma(&self) -> bool {
5151
true
5252
}
53+
54+
fn supports_insert_table_function(&self) -> bool {
55+
true
56+
}
5357
}

src/dialect/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,11 @@ pub trait Dialect: Debug + Any {
792792
fn supports_insert_set(&self) -> bool {
793793
false
794794
}
795+
796+
/// Does the dialect support table function in insertion?
797+
fn supports_insert_table_function(&self) -> bool {
798+
false
799+
}
795800
}
796801

797802
/// This represents the operators for which precedence must be defined

src/parser/mod.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -8947,6 +8947,18 @@ impl<'a> Parser<'a> {
89478947
}
89488948
}
89498949

8950+
/// Parse a table object for insetion
8951+
/// e.g. `some_database.some_table` or `FUNCTION some_table_func(...)`
8952+
pub fn parse_table_object(&mut self) -> Result<TableObject, ParserError> {
8953+
if self.dialect.supports_insert_table_function() && self.parse_keyword(Keyword::FUNCTION) {
8954+
let fn_name = self.parse_object_name(false)?;
8955+
self.parse_function_call(fn_name)
8956+
.map(TableObject::TableFunction)
8957+
} else {
8958+
self.parse_object_name(false).map(TableObject::TableName)
8959+
}
8960+
}
8961+
89508962
/// Parse a possibly qualified, possibly quoted identifier, optionally allowing for wildcards,
89518963
/// e.g. *, *.*, `foo`.*, or "foo"."bar"
89528964
fn parse_object_name_with_wildcards(
@@ -12020,7 +12032,7 @@ impl<'a> Parser<'a> {
1202012032
} else {
1202112033
// Hive lets you put table here regardless
1202212034
let table = self.parse_keyword(Keyword::TABLE);
12023-
let table_name = self.parse_object_name(false)?;
12035+
let table_object = self.parse_table_object()?;
1202412036

1202512037
let table_alias =
1202612038
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) {
@@ -12128,7 +12140,7 @@ impl<'a> Parser<'a> {
1212812140

1212912141
Ok(Statement::Insert(Insert {
1213012142
or,
12131-
table_name,
12143+
table: table_object,
1213212144
table_alias,
1213312145
ignore,
1213412146
into,
@@ -12138,7 +12150,7 @@ impl<'a> Parser<'a> {
1213812150
after_columns,
1213912151
source,
1214012152
assignments,
12141-
table,
12153+
has_table_keyword: table,
1214212154
on,
1214312155
returning,
1214412156
replace_into,

src/test_utils.rs

-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ impl TestedDialects {
154154
pub fn one_statement_parses_to(&self, sql: &str, canonical: &str) -> Statement {
155155
let mut statements = self.parse_sql_statements(sql).expect(sql);
156156
assert_eq!(statements.len(), 1);
157-
158157
if !canonical.is_empty() && sql != canonical {
159158
assert_eq!(self.parse_sql_statements(canonical).unwrap(), statements);
160159
}

tests/sqlparser_clickhouse.rs

+6
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ fn parse_create_table() {
222222
);
223223
}
224224

225+
#[test]
226+
fn parse_insert_into_function() {
227+
clickhouse().verified_stmt(r#"INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table) VALUES (100, 'inserted via remote()')"#);
228+
clickhouse().verified_stmt(r#"INSERT INTO FUNCTION remote('localhost', default.simple_table) VALUES (100, 'inserted via remote()')"#);
229+
}
230+
225231
#[test]
226232
fn parse_alter_table_attach_and_detach_partition() {
227233
for operation in &["ATTACH", "DETACH"] {

tests/sqlparser_common.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ fn parse_insert_values() {
9696
) {
9797
match verified_stmt(sql) {
9898
Statement::Insert(Insert {
99-
table_name,
99+
table: table_name,
100100
columns,
101101
source: Some(source),
102102
..
@@ -149,7 +149,7 @@ fn parse_insert_default_values() {
149149
partitioned,
150150
returning,
151151
source,
152-
table_name,
152+
table: table_name,
153153
..
154154
}) => {
155155
assert_eq!(columns, vec![]);
@@ -158,7 +158,10 @@ fn parse_insert_default_values() {
158158
assert_eq!(partitioned, None);
159159
assert_eq!(returning, None);
160160
assert_eq!(source, None);
161-
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
161+
assert_eq!(
162+
table_name,
163+
TableObject::TableName(ObjectName(vec!["test_table".into()]))
164+
);
162165
}
163166
_ => unreachable!(),
164167
}
@@ -174,7 +177,7 @@ fn parse_insert_default_values() {
174177
partitioned,
175178
returning,
176179
source,
177-
table_name,
180+
table: table_name,
178181
..
179182
}) => {
180183
assert_eq!(after_columns, vec![]);
@@ -183,7 +186,10 @@ fn parse_insert_default_values() {
183186
assert_eq!(partitioned, None);
184187
assert!(returning.is_some());
185188
assert_eq!(source, None);
186-
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
189+
assert_eq!(
190+
table_name,
191+
TableObject::TableName(ObjectName(vec!["test_table".into()]))
192+
);
187193
}
188194
_ => unreachable!(),
189195
}
@@ -199,7 +205,7 @@ fn parse_insert_default_values() {
199205
partitioned,
200206
returning,
201207
source,
202-
table_name,
208+
table: table_name,
203209
..
204210
}) => {
205211
assert_eq!(after_columns, vec![]);
@@ -208,7 +214,10 @@ fn parse_insert_default_values() {
208214
assert_eq!(partitioned, None);
209215
assert_eq!(returning, None);
210216
assert_eq!(source, None);
211-
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
217+
assert_eq!(
218+
table_name,
219+
TableObject::TableName(ObjectName(vec!["test_table".into()]))
220+
);
212221
}
213222
_ => unreachable!(),
214223
}

0 commit comments

Comments
 (0)