diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 276189f95..49c82857f 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -33,7 +33,7 @@ pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
Cte, ExcludeSelectItem, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType,
Offset, OffsetRows, OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator,
- SetQuantifier, TableAlias, TableFactor, TableWithJoins, Top, Values, With,
+ SetQuantifier, Table, TableAlias, TableFactor, TableWithJoins, Top, Values, With,
};
pub use self::value::{escape_quoted_string, DateTimeField, TrimWhereField, Value};
diff --git a/src/ast/query.rs b/src/ast/query.rs
index 172ba0f91..83cd5aded 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -84,7 +84,7 @@ pub enum SetExpr {
},
Values(Values),
Insert(Statement),
- // TODO: ANSI SQL supports `TABLE` here.
+ Table(Box
),
}
impl fmt::Display for SetExpr {
@@ -94,6 +94,7 @@ impl fmt::Display for SetExpr {
SetExpr::Query(q) => write!(f, "({})", q),
SetExpr::Values(v) => write!(f, "{}", v),
SetExpr::Insert(v) => write!(f, "{}", v),
+ SetExpr::Table(t) => write!(f, "{}", t),
SetExpr::SetOperation {
left,
right,
@@ -152,6 +153,31 @@ impl fmt::Display for SetQuantifier {
}
}
}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+/// A [`TABLE` command]( https://www.postgresql.org/docs/current/sql-select.html#SQL-TABLE)
+pub struct Table {
+ pub table_name: Option,
+ pub schema_name: Option,
+}
+
+impl fmt::Display for Table {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(ref schema_name) = self.schema_name {
+ write!(
+ f,
+ "TABLE {}.{}",
+ schema_name,
+ self.table_name.as_ref().unwrap(),
+ )?;
+ } else {
+ write!(f, "TABLE {}", self.table_name.as_ref().unwrap(),)?;
+ }
+ Ok(())
+ }
+}
+
/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
/// appear either as the only body item of a `Query`, or as an operand
/// to a set operation like `UNION`.
diff --git a/src/parser.rs b/src/parser.rs
index 260dbfbeb..b23cc297d 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -4368,6 +4368,14 @@ impl<'a> Parser<'a> {
SetExpr::Query(Box::new(subquery))
} else if self.parse_keyword(Keyword::VALUES) {
SetExpr::Values(self.parse_values()?)
+ } else if self.parse_keyword(Keyword::TABLE) {
+ let token1 = self.peek_token();
+ let token2 = self.peek_nth_token(1);
+ let token3 = self.peek_nth_token(2);
+ self.next_token();
+ self.next_token();
+ self.next_token();
+ SetExpr::Table(Box::new(self.parse_as_table(token1, token2, token3)?))
} else {
return self.expected(
"SELECT, VALUES, or a subquery in the query body",
@@ -4566,6 +4574,52 @@ impl<'a> Parser<'a> {
})
}
+ /// Parse `CREATE TABLE x AS TABLE y`
+ pub fn parse_as_table(
+ &self,
+ token1: Token,
+ token2: Token,
+ token3: Token,
+ ) -> Result {
+ let table_name;
+ let schema_name;
+ if token2 == Token::Period {
+ match token1 {
+ Token::Word(w) => {
+ schema_name = w.value;
+ }
+ _ => {
+ return self.expected("Schema name", token1);
+ }
+ }
+ match token3 {
+ Token::Word(w) => {
+ table_name = w.value;
+ }
+ _ => {
+ return self.expected("Table name", token3);
+ }
+ }
+ Ok(Table {
+ table_name: Some(table_name),
+ schema_name: Some(schema_name),
+ })
+ } else {
+ match token1 {
+ Token::Word(w) => {
+ table_name = w.value;
+ }
+ _ => {
+ return self.expected("Table name", token1);
+ }
+ }
+ Ok(Table {
+ table_name: Some(table_name),
+ schema_name: None,
+ })
+ }
+ }
+
pub fn parse_set(&mut self) -> Result {
let modifier =
self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]);
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index e9b73b93d..7a8f4a01b 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -2270,6 +2270,55 @@ fn parse_create_table_as() {
}
}
+#[test]
+fn parse_create_table_as_table() {
+ let sql1 = "CREATE TABLE new_table AS TABLE old_table";
+
+ let expected_query1 = Box::new(Query {
+ with: None,
+ body: Box::new(SetExpr::Table(Box::new(Table {
+ table_name: Some("old_table".to_string()),
+ schema_name: None,
+ }))),
+ order_by: vec![],
+ limit: None,
+ offset: None,
+ fetch: None,
+ lock: None,
+ });
+
+ match verified_stmt(sql1) {
+ Statement::CreateTable { query, name, .. } => {
+ assert_eq!(name, ObjectName(vec![Ident::new("new_table")]));
+ assert_eq!(query.unwrap(), expected_query1);
+ }
+ _ => unreachable!(),
+ }
+
+ let sql2 = "CREATE TABLE new_table AS TABLE schema_name.old_table";
+
+ let expected_query2 = Box::new(Query {
+ with: None,
+ body: Box::new(SetExpr::Table(Box::new(Table {
+ table_name: Some("old_table".to_string()),
+ schema_name: Some("schema_name".to_string()),
+ }))),
+ order_by: vec![],
+ limit: None,
+ offset: None,
+ fetch: None,
+ lock: None,
+ });
+
+ match verified_stmt(sql2) {
+ Statement::CreateTable { query, name, .. } => {
+ assert_eq!(name, ObjectName(vec![Ident::new("new_table")]));
+ assert_eq!(query.unwrap(), expected_query2);
+ }
+ _ => unreachable!(),
+ }
+}
+
#[test]
fn parse_create_table_on_cluster() {
// Using single-quote literal to define current cluster