Skip to content

Commit d38c30e

Browse files
authored
add SELECT INTO statement variation (#447)
Signed-off-by: Maciej Obuchowski <[email protected]>
1 parent b68e9a3 commit d38c30e

File tree

7 files changed

+74
-2
lines changed

7 files changed

+74
-2
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub use self::ddl::{
3636
pub use self::operator::{BinaryOperator, UnaryOperator};
3737
pub use self::query::{
3838
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
39-
OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor,
40-
TableWithJoins, Top, Values, With,
39+
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, TableAlias,
40+
TableFactor, TableWithJoins, Top, Values, With,
4141
};
4242
pub use self::value::{DateTimeField, TrimWhereField, Value};
4343

src/ast/query.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub struct Select {
136136
pub top: Option<Top>,
137137
/// projection expressions
138138
pub projection: Vec<SelectItem>,
139+
/// INTO
140+
pub into: Option<SelectInto>,
139141
/// FROM
140142
pub from: Vec<TableWithJoins>,
141143
/// LATERAL VIEWs
@@ -161,6 +163,11 @@ impl fmt::Display for Select {
161163
write!(f, " {}", top)?;
162164
}
163165
write!(f, " {}", display_comma_separated(&self.projection))?;
166+
167+
if let Some(ref into) = self.into {
168+
write!(f, " {}", into)?;
169+
}
170+
164171
if !self.from.is_empty() {
165172
write!(f, " FROM {}", display_comma_separated(&self.from))?;
166173
}
@@ -636,3 +643,20 @@ impl fmt::Display for Values {
636643
Ok(())
637644
}
638645
}
646+
647+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
648+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
649+
pub struct SelectInto {
650+
pub temporary: bool,
651+
pub unlogged: bool,
652+
pub name: ObjectName,
653+
}
654+
655+
impl fmt::Display for SelectInto {
656+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657+
let temporary = if self.temporary { " TEMPORARY" } else { "" };
658+
let unlogged = if self.unlogged { " UNLOGGED" } else { "" };
659+
660+
write!(f, "INTO{}{} {}", temporary, unlogged, self.name)
661+
}
662+
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ define_keywords!(
499499
UNION,
500500
UNIQUE,
501501
UNKNOWN,
502+
UNLOGGED,
502503
UNNEST,
503504
UNSIGNED,
504505
UPDATE,
@@ -600,4 +601,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
600601
Keyword::DISTRIBUTE,
601602
// Reserved only as a column alias in the `SELECT` clause
602603
Keyword::FROM,
604+
Keyword::INTO,
603605
];

src/parser.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,21 @@ impl<'a> Parser<'a> {
28922892

28932893
let projection = self.parse_comma_separated(Parser::parse_select_item)?;
28942894

2895+
let into = if self.parse_keyword(Keyword::INTO) {
2896+
let temporary = self
2897+
.parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
2898+
.is_some();
2899+
let unlogged = self.parse_keyword(Keyword::UNLOGGED);
2900+
let name = self.parse_object_name()?;
2901+
Some(SelectInto {
2902+
temporary,
2903+
unlogged,
2904+
name,
2905+
})
2906+
} else {
2907+
None
2908+
};
2909+
28952910
// Note that for keywords to be properly handled here, they need to be
28962911
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
28972912
// otherwise they may be parsed as an alias as part of the `projection`
@@ -2973,6 +2988,7 @@ impl<'a> Parser<'a> {
29732988
distinct,
29742989
top,
29752990
projection,
2991+
into,
29762992
from,
29772993
lateral_views,
29782994
selection,

tests/sqlparser_common.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,32 @@ fn parse_select_all_distinct() {
380380
);
381381
}
382382

383+
#[test]
384+
fn parse_select_into() {
385+
let sql = "SELECT * INTO table0 FROM table1";
386+
one_statement_parses_to(sql, "SELECT * INTO table0 FROM table1");
387+
let select = verified_only_select(sql);
388+
assert_eq!(
389+
&SelectInto {
390+
temporary: false,
391+
unlogged: false,
392+
name: ObjectName(vec![Ident::new("table0")])
393+
},
394+
only(&select.into)
395+
);
396+
397+
let sql = "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1";
398+
one_statement_parses_to(sql, "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1");
399+
400+
// Do not allow aliases here
401+
let sql = "SELECT * INTO table0 asdf FROM table1";
402+
let result = parse_sql_statements(sql);
403+
assert_eq!(
404+
ParserError::ParserError("Expected end of statement, found: asdf".to_string()),
405+
result.unwrap_err()
406+
)
407+
}
408+
383409
#[test]
384410
fn parse_select_wildcard() {
385411
let sql = "SELECT * FROM foo";
@@ -4235,6 +4261,7 @@ fn parse_merge() {
42354261
distinct: false,
42364262
top: None,
42374263
projection: vec![SelectItem::Wildcard],
4264+
into: None,
42384265
from: vec![TableWithJoins {
42394266
relation: TableFactor::Table {
42404267
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),

tests/sqlparser_mysql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ fn parse_quote_identifiers_2() {
305305
value: "quoted ` identifier".into(),
306306
quote_style: Some('`'),
307307
}))],
308+
into: None,
308309
from: vec![],
309310
lateral_views: vec![],
310311
selection: None,
@@ -732,6 +733,7 @@ fn parse_substring_in_select() {
732733
false
733734
))))
734735
})],
736+
into: None,
735737
from: vec![TableWithJoins {
736738
relation: TableFactor::Table {
737739
name: ObjectName(vec![Ident {

tests/sqpparser_clickhouse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn parse_map_access_expr() {
5353
distinct: false,
5454
})],
5555
})],
56+
into: None,
5657
from: vec![TableWithJoins {
5758
relation: Table {
5859
name: ObjectName(vec![Ident::new("foos")]),

0 commit comments

Comments
 (0)