mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Table time travel clause support, add visit_table_factor
to Visitor (#951)
This commit is contained in:
parent
9500649c35
commit
1ea8858575
15 changed files with 225 additions and 12 deletions
|
@ -40,7 +40,8 @@ pub use self::query::{
|
|||
JoinConstraint, JoinOperator, LateralView, LockClause, LockType, NamedWindowDefinition,
|
||||
NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement,
|
||||
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table,
|
||||
TableAlias, TableFactor, TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
|
||||
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, Values, WildcardAdditionalOptions,
|
||||
With,
|
||||
};
|
||||
pub use self::value::{
|
||||
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,
|
||||
|
|
|
@ -646,6 +646,7 @@ impl fmt::Display for TableWithJoins {
|
|||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
#[cfg_attr(feature = "visitor", visit(with = "visit_table_factor"))]
|
||||
pub enum TableFactor {
|
||||
Table {
|
||||
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
|
||||
|
@ -661,6 +662,9 @@ pub enum TableFactor {
|
|||
args: Option<Vec<FunctionArg>>,
|
||||
/// MSSQL-specific `WITH (...)` hints such as NOLOCK.
|
||||
with_hints: Vec<Expr>,
|
||||
/// Optional version qualifier to facilitate table time-travel, as
|
||||
/// supported by BigQuery and MSSQL.
|
||||
version: Option<TableVersion>,
|
||||
},
|
||||
Derived {
|
||||
lateral: bool,
|
||||
|
@ -720,6 +724,7 @@ impl fmt::Display for TableFactor {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
write!(f, "{name}")?;
|
||||
if let Some(args) = args {
|
||||
|
@ -731,6 +736,9 @@ impl fmt::Display for TableFactor {
|
|||
if !with_hints.is_empty() {
|
||||
write!(f, " WITH ({})", display_comma_separated(with_hints))?;
|
||||
}
|
||||
if let Some(version) = version {
|
||||
write!(f, "{version}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TableFactor::Derived {
|
||||
|
@ -835,6 +843,22 @@ impl fmt::Display for TableAlias {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum TableVersion {
|
||||
ForSystemTimeAsOf(Expr),
|
||||
}
|
||||
|
||||
impl Display for TableVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TableVersion::ForSystemTimeAsOf(e) => write!(f, " FOR SYSTEM_TIME AS OF {e}")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
//! Recursive visitors for ast Nodes. See [`Visitor`] for more details.
|
||||
|
||||
use crate::ast::{Expr, ObjectName, Statement};
|
||||
use crate::ast::{Expr, ObjectName, Statement, TableFactor};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A type that can be visited by a [`Visitor`]. See [`Visitor`] for
|
||||
|
@ -115,8 +115,8 @@ visit_noop!(bigdecimal::BigDecimal);
|
|||
|
||||
/// A visitor that can be used to walk an AST tree.
|
||||
///
|
||||
/// `previst_` methods are invoked before visiting all children of the
|
||||
/// node and `postvisit_` methods are invoked after visiting all
|
||||
/// `pre_visit_` methods are invoked before visiting all children of the
|
||||
/// node and `post_visit_` methods are invoked after visiting all
|
||||
/// children of the node.
|
||||
///
|
||||
/// # See also
|
||||
|
@ -139,7 +139,7 @@ visit_noop!(bigdecimal::BigDecimal);
|
|||
/// }
|
||||
///
|
||||
/// // Visit relations and exprs before children are visited (depth first walk)
|
||||
/// // Note you can also visit statements and visit exprs after children have been visitoed
|
||||
/// // Note you can also visit statements and visit exprs after children have been visited
|
||||
/// impl Visitor for V {
|
||||
/// type Break = ();
|
||||
///
|
||||
|
@ -189,6 +189,16 @@ pub trait Visitor {
|
|||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any table factors that appear in the AST before visiting children
|
||||
fn pre_visit_table_factor(&mut self, _table_factor: &TableFactor) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any table factors that appear in the AST after visiting children
|
||||
fn post_visit_table_factor(&mut self, _table_factor: &TableFactor) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any expressions that appear in the AST before visiting children
|
||||
fn pre_visit_expr(&mut self, _expr: &Expr) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
|
@ -212,8 +222,8 @@ pub trait Visitor {
|
|||
|
||||
/// A visitor that can be used to mutate an AST tree.
|
||||
///
|
||||
/// `previst_` methods are invoked before visiting all children of the
|
||||
/// node and `postvisit_` methods are invoked after visiting all
|
||||
/// `pre_visit_` methods are invoked before visiting all children of the
|
||||
/// node and `post_visit_` methods are invoked after visiting all
|
||||
/// children of the node.
|
||||
///
|
||||
/// # See also
|
||||
|
@ -267,6 +277,22 @@ pub trait VisitorMut {
|
|||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any table factors that appear in the AST before visiting children
|
||||
fn pre_visit_table_factor(
|
||||
&mut self,
|
||||
_table_factor: &mut TableFactor,
|
||||
) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any table factors that appear in the AST after visiting children
|
||||
fn post_visit_table_factor(
|
||||
&mut self,
|
||||
_table_factor: &mut TableFactor,
|
||||
) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Invoked for any expressions that appear in the AST before visiting children
|
||||
fn pre_visit_expr(&mut self, _expr: &mut Expr) -> ControlFlow<Self::Break> {
|
||||
ControlFlow::Continue(())
|
||||
|
@ -609,6 +635,24 @@ mod tests {
|
|||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn pre_visit_table_factor(
|
||||
&mut self,
|
||||
table_factor: &TableFactor,
|
||||
) -> ControlFlow<Self::Break> {
|
||||
self.visited
|
||||
.push(format!("PRE: TABLE FACTOR: {table_factor}"));
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn post_visit_table_factor(
|
||||
&mut self,
|
||||
table_factor: &TableFactor,
|
||||
) -> ControlFlow<Self::Break> {
|
||||
self.visited
|
||||
.push(format!("POST: TABLE FACTOR: {table_factor}"));
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn pre_visit_expr(&mut self, expr: &Expr) -> ControlFlow<Self::Break> {
|
||||
self.visited.push(format!("PRE: EXPR: {expr}"));
|
||||
ControlFlow::Continue(())
|
||||
|
@ -647,22 +691,28 @@ mod tests {
|
|||
fn test_sql() {
|
||||
let tests = vec![
|
||||
(
|
||||
"SELECT * from table_name",
|
||||
"SELECT * from table_name as my_table",
|
||||
vec![
|
||||
"PRE: STATEMENT: SELECT * FROM table_name",
|
||||
"PRE: STATEMENT: SELECT * FROM table_name AS my_table",
|
||||
"PRE: TABLE FACTOR: table_name AS my_table",
|
||||
"PRE: RELATION: table_name",
|
||||
"POST: RELATION: table_name",
|
||||
"POST: STATEMENT: SELECT * FROM table_name",
|
||||
"POST: TABLE FACTOR: table_name AS my_table",
|
||||
"POST: STATEMENT: SELECT * FROM table_name AS my_table",
|
||||
],
|
||||
),
|
||||
(
|
||||
"SELECT * from t1 join t2 on t1.id = t2.t1_id",
|
||||
vec![
|
||||
"PRE: STATEMENT: SELECT * FROM t1 JOIN t2 ON t1.id = t2.t1_id",
|
||||
"PRE: TABLE FACTOR: t1",
|
||||
"PRE: RELATION: t1",
|
||||
"POST: RELATION: t1",
|
||||
"POST: TABLE FACTOR: t1",
|
||||
"PRE: TABLE FACTOR: t2",
|
||||
"PRE: RELATION: t2",
|
||||
"POST: RELATION: t2",
|
||||
"POST: TABLE FACTOR: t2",
|
||||
"PRE: EXPR: t1.id = t2.t1_id",
|
||||
"PRE: EXPR: t1.id",
|
||||
"POST: EXPR: t1.id",
|
||||
|
@ -676,13 +726,17 @@ mod tests {
|
|||
"SELECT * from t1 where EXISTS(SELECT column from t2)",
|
||||
vec![
|
||||
"PRE: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2)",
|
||||
"PRE: TABLE FACTOR: t1",
|
||||
"PRE: RELATION: t1",
|
||||
"POST: RELATION: t1",
|
||||
"POST: TABLE FACTOR: t1",
|
||||
"PRE: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"PRE: EXPR: column",
|
||||
"POST: EXPR: column",
|
||||
"PRE: TABLE FACTOR: t2",
|
||||
"PRE: RELATION: t2",
|
||||
"POST: RELATION: t2",
|
||||
"POST: TABLE FACTOR: t2",
|
||||
"POST: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"POST: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2)",
|
||||
],
|
||||
|
@ -691,13 +745,17 @@ mod tests {
|
|||
"SELECT * from t1 where EXISTS(SELECT column from t2)",
|
||||
vec![
|
||||
"PRE: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2)",
|
||||
"PRE: TABLE FACTOR: t1",
|
||||
"PRE: RELATION: t1",
|
||||
"POST: RELATION: t1",
|
||||
"POST: TABLE FACTOR: t1",
|
||||
"PRE: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"PRE: EXPR: column",
|
||||
"POST: EXPR: column",
|
||||
"PRE: TABLE FACTOR: t2",
|
||||
"PRE: RELATION: t2",
|
||||
"POST: RELATION: t2",
|
||||
"POST: TABLE FACTOR: t2",
|
||||
"POST: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"POST: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2)",
|
||||
],
|
||||
|
@ -706,16 +764,22 @@ mod tests {
|
|||
"SELECT * from t1 where EXISTS(SELECT column from t2) UNION SELECT * from t3",
|
||||
vec![
|
||||
"PRE: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2) UNION SELECT * FROM t3",
|
||||
"PRE: TABLE FACTOR: t1",
|
||||
"PRE: RELATION: t1",
|
||||
"POST: RELATION: t1",
|
||||
"POST: TABLE FACTOR: t1",
|
||||
"PRE: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"PRE: EXPR: column",
|
||||
"POST: EXPR: column",
|
||||
"PRE: TABLE FACTOR: t2",
|
||||
"PRE: RELATION: t2",
|
||||
"POST: RELATION: t2",
|
||||
"POST: TABLE FACTOR: t2",
|
||||
"POST: EXPR: EXISTS (SELECT column FROM t2)",
|
||||
"PRE: TABLE FACTOR: t3",
|
||||
"PRE: RELATION: t3",
|
||||
"POST: RELATION: t3",
|
||||
"POST: TABLE FACTOR: t3",
|
||||
"POST: STATEMENT: SELECT * FROM t1 WHERE EXISTS (SELECT column FROM t2) UNION SELECT * FROM t3",
|
||||
],
|
||||
),
|
||||
|
|
|
@ -6210,6 +6210,9 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
let name = self.parse_object_name()?;
|
||||
|
||||
// Parse potential version qualifier
|
||||
let version = self.parse_table_version()?;
|
||||
|
||||
// Postgres, MSSQL: table-valued functions:
|
||||
let args = if self.consume_token(&Token::LParen) {
|
||||
Some(self.parse_optional_args()?)
|
||||
|
@ -6240,10 +6243,25 @@ impl<'a> Parser<'a> {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a given table version specifier.
|
||||
///
|
||||
/// For now it only supports timestamp versioning for BigQuery and MSSQL dialects.
|
||||
pub fn parse_table_version(&mut self) -> Result<Option<TableVersion>, ParserError> {
|
||||
if dialect_of!(self is BigQueryDialect | MsSqlDialect)
|
||||
&& self.parse_keywords(&[Keyword::FOR, Keyword::SYSTEM_TIME, Keyword::AS, Keyword::OF])
|
||||
{
|
||||
let expr = self.parse_expr()?;
|
||||
Ok(Some(TableVersion::ForSystemTimeAsOf(expr)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_derived_table_factor(
|
||||
&mut self,
|
||||
lateral: IsLateral,
|
||||
|
|
|
@ -221,6 +221,7 @@ pub fn table(name: impl Into<String>) -> TableFactor {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ fn parse_table_identifiers() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
},]
|
||||
|
@ -143,6 +144,31 @@ fn parse_table_identifiers() {
|
|||
test_table_ident("abc5.GROUP", vec![Ident::new("abc5"), Ident::new("GROUP")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_time_travel() {
|
||||
let version = "2023-08-18 23:08:18".to_string();
|
||||
let sql = format!("SELECT 1 FROM t1 FOR SYSTEM_TIME AS OF '{version}'");
|
||||
let select = bigquery().verified_only_select(&sql);
|
||||
assert_eq!(
|
||||
select.from,
|
||||
vec![TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName(vec![Ident::new("t1")]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: Some(TableVersion::ForSystemTimeAsOf(Expr::Value(
|
||||
Value::SingleQuotedString(version)
|
||||
))),
|
||||
},
|
||||
joins: vec![]
|
||||
},]
|
||||
);
|
||||
|
||||
let sql = "SELECT 1 FROM t1 FOR SYSTEM TIME AS OF 'some_timestamp'".to_string();
|
||||
assert!(bigquery().parse_sql_statements(&sql).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_join_constraint_unnest_alias() {
|
||||
assert_eq!(
|
||||
|
|
|
@ -62,6 +62,7 @@ fn parse_map_access_expr() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
|
@ -169,11 +170,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
|
|
@ -214,6 +214,7 @@ fn parse_update_set_from() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
},
|
||||
|
@ -240,6 +241,7 @@ fn parse_update_set_from() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -303,6 +305,7 @@ fn parse_update_with_table_alias() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
},
|
||||
|
@ -365,6 +368,7 @@ fn parse_select_with_table_alias() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}]
|
||||
|
@ -395,6 +399,7 @@ fn parse_delete_statement() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].relation
|
||||
);
|
||||
|
@ -422,6 +427,7 @@ fn parse_delete_statement_for_multi_tables() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].relation
|
||||
);
|
||||
|
@ -431,6 +437,7 @@ fn parse_delete_statement_for_multi_tables() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].joins[0].relation
|
||||
);
|
||||
|
@ -454,6 +461,7 @@ fn parse_delete_statement_for_multi_tables_with_using() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].relation
|
||||
);
|
||||
|
@ -463,6 +471,7 @@ fn parse_delete_statement_for_multi_tables_with_using() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[1].relation
|
||||
);
|
||||
|
@ -472,6 +481,7 @@ fn parse_delete_statement_for_multi_tables_with_using() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
using[0].relation
|
||||
);
|
||||
|
@ -481,6 +491,7 @@ fn parse_delete_statement_for_multi_tables_with_using() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
using[0].joins[0].relation
|
||||
);
|
||||
|
@ -508,6 +519,7 @@ fn parse_where_delete_statement() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].relation,
|
||||
);
|
||||
|
@ -549,6 +561,7 @@ fn parse_where_delete_with_alias_statement() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
from[0].relation,
|
||||
);
|
||||
|
@ -562,6 +575,7 @@ fn parse_where_delete_with_alias_statement() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}]),
|
||||
|
@ -3564,6 +3578,7 @@ fn test_parse_named_window() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -3902,6 +3917,7 @@ fn parse_interval_and_or_xor() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -4506,6 +4522,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
},
|
||||
|
@ -4515,6 +4532,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
},
|
||||
|
@ -4532,6 +4550,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
|
@ -4539,6 +4558,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
|
||||
}],
|
||||
|
@ -4549,6 +4569,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
|
@ -4556,6 +4577,7 @@ fn parse_implicit_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
|
||||
}],
|
||||
|
@ -4576,6 +4598,7 @@ fn parse_cross_join() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: JoinOperator::CrossJoin,
|
||||
},
|
||||
|
@ -4596,6 +4619,7 @@ fn parse_joins_on() {
|
|||
alias,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: f(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::Identifier("c1".into())),
|
||||
|
@ -4665,6 +4689,7 @@ fn parse_joins_using() {
|
|||
alias,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
||||
}
|
||||
|
@ -4726,6 +4751,7 @@ fn parse_natural_join() {
|
|||
alias,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: f(JoinConstraint::Natural),
|
||||
}
|
||||
|
@ -4990,6 +5016,7 @@ fn parse_derived_tables() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
|
||||
}],
|
||||
|
@ -6317,6 +6344,7 @@ fn parse_merge() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(table, table_no_into);
|
||||
|
@ -6340,6 +6368,7 @@ fn parse_merge() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
|
|
@ -155,6 +155,7 @@ fn test_select_union_by_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -187,6 +188,7 @@ fn test_select_union_by_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -228,6 +230,7 @@ fn test_select_union_by_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
@ -260,6 +263,7 @@ fn test_select_union_by_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}],
|
||||
|
|
|
@ -322,11 +322,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
|
|
@ -42,6 +42,31 @@ fn parse_mssql_identifiers() {
|
|||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_time_travel() {
|
||||
let version = "2023-08-18 23:08:18".to_string();
|
||||
let sql = format!("SELECT 1 FROM t1 FOR SYSTEM_TIME AS OF '{version}'");
|
||||
let select = ms().verified_only_select(&sql);
|
||||
assert_eq!(
|
||||
select.from,
|
||||
vec![TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName(vec![Ident::new("t1")]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: Some(TableVersion::ForSystemTimeAsOf(Expr::Value(
|
||||
Value::SingleQuotedString(version)
|
||||
))),
|
||||
},
|
||||
joins: vec![]
|
||||
},]
|
||||
);
|
||||
|
||||
let sql = "SELECT 1 FROM t1 FOR SYSTEM TIME AS OF 'some_timestamp'".to_string();
|
||||
assert!(ms().parse_sql_statements(&sql).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mssql_single_quoted_aliases() {
|
||||
let _ = ms_and_generic().one_statement_parses_to("SELECT foo 'alias'", "SELECT foo AS 'alias'");
|
||||
|
@ -283,11 +308,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
@ -485,7 +512,8 @@ fn parse_substring_in_select() {
|
|||
}]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![]
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
|
|
|
@ -1093,6 +1093,7 @@ fn parse_select_with_numeric_prefix_column_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
|
@ -1141,6 +1142,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
|
@ -1200,6 +1202,7 @@ fn parse_update_with_joins() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
|
@ -1210,6 +1213,7 @@ fn parse_update_with_joins() {
|
|||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
|
@ -1324,7 +1328,8 @@ fn parse_substring_in_select() {
|
|||
}]),
|
||||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![]
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![]
|
||||
}],
|
||||
|
|
|
@ -2855,11 +2855,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ fn test_square_brackets_over_db_schema_table_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}
|
||||
|
@ -89,6 +90,7 @@ fn test_double_quotes_over_db_schema_table_name() {
|
|||
alias: None,
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
},
|
||||
joins: vec![],
|
||||
}
|
||||
|
@ -108,11 +110,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
|
|
@ -223,11 +223,13 @@ fn parse_delimited_identifiers() {
|
|||
alias,
|
||||
args,
|
||||
with_hints,
|
||||
version,
|
||||
} => {
|
||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||
assert!(args.is_none());
|
||||
assert!(with_hints.is_empty());
|
||||
assert!(version.is_none());
|
||||
}
|
||||
_ => panic!("Expecting TableFactor::Table"),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue