add SELECT INTO statement variation (#447)

Signed-off-by: Maciej Obuchowski <obuchowski.maciej@gmail.com>
This commit is contained in:
Maciej Obuchowski 2022-03-30 22:01:55 +02:00 committed by GitHub
parent b68e9a3801
commit d38c30e9ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 2 deletions

View file

@ -36,8 +36,8 @@ pub use self::ddl::{
pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{ pub use self::query::{
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows, Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, TableAlias,
TableWithJoins, Top, Values, With, TableFactor, TableWithJoins, Top, Values, With,
}; };
pub use self::value::{DateTimeField, TrimWhereField, Value}; pub use self::value::{DateTimeField, TrimWhereField, Value};

View file

@ -136,6 +136,8 @@ pub struct Select {
pub top: Option<Top>, pub top: Option<Top>,
/// projection expressions /// projection expressions
pub projection: Vec<SelectItem>, pub projection: Vec<SelectItem>,
/// INTO
pub into: Option<SelectInto>,
/// FROM /// FROM
pub from: Vec<TableWithJoins>, pub from: Vec<TableWithJoins>,
/// LATERAL VIEWs /// LATERAL VIEWs
@ -161,6 +163,11 @@ impl fmt::Display for Select {
write!(f, " {}", top)?; write!(f, " {}", top)?;
} }
write!(f, " {}", display_comma_separated(&self.projection))?; write!(f, " {}", display_comma_separated(&self.projection))?;
if let Some(ref into) = self.into {
write!(f, " {}", into)?;
}
if !self.from.is_empty() { if !self.from.is_empty() {
write!(f, " FROM {}", display_comma_separated(&self.from))?; write!(f, " FROM {}", display_comma_separated(&self.from))?;
} }
@ -636,3 +643,20 @@ impl fmt::Display for Values {
Ok(()) Ok(())
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SelectInto {
pub temporary: bool,
pub unlogged: bool,
pub name: ObjectName,
}
impl fmt::Display for SelectInto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let temporary = if self.temporary { " TEMPORARY" } else { "" };
let unlogged = if self.unlogged { " UNLOGGED" } else { "" };
write!(f, "INTO{}{} {}", temporary, unlogged, self.name)
}
}

View file

@ -499,6 +499,7 @@ define_keywords!(
UNION, UNION,
UNIQUE, UNIQUE,
UNKNOWN, UNKNOWN,
UNLOGGED,
UNNEST, UNNEST,
UNSIGNED, UNSIGNED,
UPDATE, UPDATE,
@ -600,4 +601,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
Keyword::DISTRIBUTE, Keyword::DISTRIBUTE,
// Reserved only as a column alias in the `SELECT` clause // Reserved only as a column alias in the `SELECT` clause
Keyword::FROM, Keyword::FROM,
Keyword::INTO,
]; ];

View file

@ -2892,6 +2892,21 @@ impl<'a> Parser<'a> {
let projection = self.parse_comma_separated(Parser::parse_select_item)?; let projection = self.parse_comma_separated(Parser::parse_select_item)?;
let into = if self.parse_keyword(Keyword::INTO) {
let temporary = self
.parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
.is_some();
let unlogged = self.parse_keyword(Keyword::UNLOGGED);
let name = self.parse_object_name()?;
Some(SelectInto {
temporary,
unlogged,
name,
})
} else {
None
};
// Note that for keywords to be properly handled here, they need to be // Note that for keywords to be properly handled here, they need to be
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`, // added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
// otherwise they may be parsed as an alias as part of the `projection` // otherwise they may be parsed as an alias as part of the `projection`
@ -2973,6 +2988,7 @@ impl<'a> Parser<'a> {
distinct, distinct,
top, top,
projection, projection,
into,
from, from,
lateral_views, lateral_views,
selection, selection,

View file

@ -380,6 +380,32 @@ fn parse_select_all_distinct() {
); );
} }
#[test]
fn parse_select_into() {
let sql = "SELECT * INTO table0 FROM table1";
one_statement_parses_to(sql, "SELECT * INTO table0 FROM table1");
let select = verified_only_select(sql);
assert_eq!(
&SelectInto {
temporary: false,
unlogged: false,
name: ObjectName(vec![Ident::new("table0")])
},
only(&select.into)
);
let sql = "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1";
one_statement_parses_to(sql, "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1");
// Do not allow aliases here
let sql = "SELECT * INTO table0 asdf FROM table1";
let result = parse_sql_statements(sql);
assert_eq!(
ParserError::ParserError("Expected end of statement, found: asdf".to_string()),
result.unwrap_err()
)
}
#[test] #[test]
fn parse_select_wildcard() { fn parse_select_wildcard() {
let sql = "SELECT * FROM foo"; let sql = "SELECT * FROM foo";
@ -4235,6 +4261,7 @@ fn parse_merge() {
distinct: false, distinct: false,
top: None, top: None,
projection: vec![SelectItem::Wildcard], projection: vec![SelectItem::Wildcard],
into: None,
from: vec![TableWithJoins { from: vec![TableWithJoins {
relation: TableFactor::Table { relation: TableFactor::Table {
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),

View file

@ -305,6 +305,7 @@ fn parse_quote_identifiers_2() {
value: "quoted ` identifier".into(), value: "quoted ` identifier".into(),
quote_style: Some('`'), quote_style: Some('`'),
}))], }))],
into: None,
from: vec![], from: vec![],
lateral_views: vec![], lateral_views: vec![],
selection: None, selection: None,
@ -732,6 +733,7 @@ fn parse_substring_in_select() {
false false
)))) ))))
})], })],
into: None,
from: vec![TableWithJoins { from: vec![TableWithJoins {
relation: TableFactor::Table { relation: TableFactor::Table {
name: ObjectName(vec![Ident { name: ObjectName(vec![Ident {

View file

@ -53,6 +53,7 @@ fn parse_map_access_expr() {
distinct: false, distinct: false,
})], })],
})], })],
into: None,
from: vec![TableWithJoins { from: vec![TableWithJoins {
relation: Table { relation: Table {
name: ObjectName(vec![Ident::new("foos")]), name: ObjectName(vec![Ident::new("foos")]),