Skip to content

Commit

Permalink
feat(sqlparser-rs#757): Add Location/Spans in to the AST/parse layer
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeswenson authored and lustefaniak committed Sep 25, 2023
1 parent 970f4e9 commit b4547ca
Show file tree
Hide file tree
Showing 17 changed files with 946 additions and 544 deletions.
3 changes: 2 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use sqlparser::ast::WithSpan;
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

Expand Down Expand Up @@ -754,7 +755,7 @@ impl fmt::Display for UserDefinedTypeRepresentation {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UserDefinedTypeCompositeAttributeDef {
pub name: Ident,
pub name: WithSpan<Ident>,
pub data_type: DataType,
pub collation: Option<ObjectName>,
}
Expand Down
140 changes: 104 additions & 36 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};

use core::borrow::Borrow;
use core::fmt::{self, Display};
use std::ops::Deref;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -50,6 +53,7 @@ pub use self::value::{
use crate::ast::helpers::stmt_data_loading::{
DataLoadingOptions, StageLoadSelectItem, StageParamsObject,
};
use crate::tokenizer::Span;
#[cfg(feature = "visitor")]
pub use visitor::*;

Expand Down Expand Up @@ -340,6 +344,78 @@ impl fmt::Display for JsonOperator {
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WithSpan<T>
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
inner: T,
span: Span,
}

impl<T> WithSpan<T>
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
pub fn new(inner: T, span: Span) -> Self {
Self { inner, span }
}

pub fn unwrap(self) -> T {
self.inner
}
}

pub trait SpanWrapped: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq {
fn spanning<U: Into<Span>>(self, span: U) -> WithSpan<Self> {
WithSpan::new(self, span.into())
}

fn empty_span(self) -> WithSpan<Self> {
self.spanning(Span::default())
}
}

impl<T> SpanWrapped for T
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
fn spanning<U: Into<Span>>(self, span: U) -> WithSpan<Self> {
WithSpan::new(self, span.into())
}
}

impl<T> Deref for WithSpan<T>
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
type Target = T;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<T> Borrow<T> for WithSpan<T>
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
fn borrow(&self) -> &T {
&self.inner
}
}

impl<T: fmt::Display> fmt::Display for WithSpan<T>
where
T: Clone + Eq + Ord + std::hash::Hash + PartialOrd + PartialEq,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}

/// An SQL expression of any type.
///
/// The parser does not distinguish between expressions of different types
Expand All @@ -354,7 +430,7 @@ impl fmt::Display for JsonOperator {
)]
pub enum Expr {
/// Identifier e.g. table name or column name
Identifier(Ident),
Identifier(WithSpan<Ident>),
/// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col`
CompoundIdentifier(Vec<Ident>),
/// JSON access (postgres) eg: data->'tags'
Expand Down Expand Up @@ -1025,7 +1101,7 @@ impl fmt::Display for Expr {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WindowType {
WindowSpec(WindowSpec),
NamedWindow(Ident),
NamedWindow(WithSpan<Ident>),
}

impl Display for WindowType {
Expand Down Expand Up @@ -4732,6 +4808,10 @@ impl fmt::Display for SearchModifier {
mod tests {
use super::*;

fn ident<T: Into<String>>(value: T) -> WithSpan<Ident> {
SpanWrapped::empty_span(Ident::new(value))
}

#[test]
fn test_window_frame_default() {
let window_frame = WindowFrame::default();
Expand All @@ -4742,84 +4822,72 @@ mod tests {
fn test_grouping_sets_display() {
// a and b in different group
let grouping_sets = Expr::GroupingSets(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
vec![Expr::Identifier(ident("a"))],
vec![Expr::Identifier(ident("b"))],
]);
assert_eq!("GROUPING SETS ((a), (b))", format!("{grouping_sets}"));

// a and b in the same group
let grouping_sets = Expr::GroupingSets(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
Expr::Identifier(ident("a")),
Expr::Identifier(ident("b")),
]]);
assert_eq!("GROUPING SETS ((a, b))", format!("{grouping_sets}"));

// (a, b) and (c, d) in different group
let grouping_sets = Expr::GroupingSets(vec![
vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
],
vec![
Expr::Identifier(Ident::new("c")),
Expr::Identifier(Ident::new("d")),
],
vec![Expr::Identifier(ident("a")), Expr::Identifier(ident("b"))],
vec![Expr::Identifier(ident("c")), Expr::Identifier(ident("d"))],
]);
assert_eq!("GROUPING SETS ((a, b), (c, d))", format!("{grouping_sets}"));
}

#[test]
fn test_rollup_display() {
let rollup = Expr::Rollup(vec![vec![Expr::Identifier(Ident::new("a"))]]);
let rollup = Expr::Rollup(vec![vec![Expr::Identifier(ident("a"))]]);
assert_eq!("ROLLUP (a)", format!("{rollup}"));

let rollup = Expr::Rollup(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
Expr::Identifier(ident("a")),
Expr::Identifier(ident("b")),
]]);
assert_eq!("ROLLUP ((a, b))", format!("{rollup}"));

let rollup = Expr::Rollup(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
vec![Expr::Identifier(ident("a"))],
vec![Expr::Identifier(ident("b"))],
]);
assert_eq!("ROLLUP (a, b)", format!("{rollup}"));

let rollup = Expr::Rollup(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![
Expr::Identifier(Ident::new("b")),
Expr::Identifier(Ident::new("c")),
],
vec![Expr::Identifier(Ident::new("d"))],
vec![Expr::Identifier(ident("a"))],
vec![Expr::Identifier(ident("b")), Expr::Identifier(ident("c"))],
vec![Expr::Identifier(ident("d"))],
]);
assert_eq!("ROLLUP (a, (b, c), d)", format!("{rollup}"));
}

#[test]
fn test_cube_display() {
let cube = Expr::Cube(vec![vec![Expr::Identifier(Ident::new("a"))]]);
let cube = Expr::Cube(vec![vec![Expr::Identifier(ident("a"))]]);
assert_eq!("CUBE (a)", format!("{cube}"));

let cube = Expr::Cube(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
Expr::Identifier(ident("a")),
Expr::Identifier(ident("b")),
]]);
assert_eq!("CUBE ((a, b))", format!("{cube}"));

let cube = Expr::Cube(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
vec![Expr::Identifier(ident("a"))],
vec![Expr::Identifier(ident("b"))],
]);
assert_eq!("CUBE (a, b)", format!("{cube}"));

let cube = Expr::Cube(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![
Expr::Identifier(Ident::new("b")),
Expr::Identifier(Ident::new("c")),
],
vec![Expr::Identifier(Ident::new("d"))],
vec![Expr::Identifier(ident("a"))],
vec![Expr::Identifier(ident("b")), Expr::Identifier(ident("c"))],
vec![Expr::Identifier(ident("d"))],
]);
assert_eq!("CUBE (a, (b, c), d)", format!("{cube}"));
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ impl fmt::Display for LateralView {
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct NamedWindowDefinition(pub Ident, pub WindowSpec);
pub struct NamedWindowDefinition(pub WithSpan<Ident>, pub WindowSpec);

impl fmt::Display for NamedWindowDefinition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down
12 changes: 6 additions & 6 deletions src/parser/alter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ impl<'a> Parser<'a> {
}

fn parse_mssql_alter_role(&mut self) -> Result<Statement, ParserError> {
let role_name = self.parse_identifier()?;
let role_name = self.parse_identifier()?.unwrap();

let operation = if self.parse_keywords(&[Keyword::ADD, Keyword::MEMBER]) {
let member_name = self.parse_identifier()?;
let member_name = self.parse_identifier()?.unwrap();
AlterRoleOperation::AddMember { member_name }
} else if self.parse_keywords(&[Keyword::DROP, Keyword::MEMBER]) {
let member_name = self.parse_identifier()?;
let member_name = self.parse_identifier()?.unwrap();
AlterRoleOperation::DropMember { member_name }
} else if self.parse_keywords(&[Keyword::WITH, Keyword::NAME]) {
if self.consume_token(&Token::Eq) {
let role_name = self.parse_identifier()?;
let role_name = self.parse_identifier()?.unwrap();
AlterRoleOperation::RenameRole { role_name }
} else {
return self.expected("= after WITH NAME ", self.peek_token());
Expand All @@ -63,7 +63,7 @@ impl<'a> Parser<'a> {
}

fn parse_pg_alter_role(&mut self) -> Result<Statement, ParserError> {
let role_name = self.parse_identifier()?;
let role_name = self.parse_identifier()?.unwrap();

// [ IN DATABASE _`database_name`_ ]
let in_database = if self.parse_keywords(&[Keyword::IN, Keyword::DATABASE]) {
Expand All @@ -74,7 +74,7 @@ impl<'a> Parser<'a> {

let operation = if self.parse_keyword(Keyword::RENAME) {
if self.parse_keyword(Keyword::TO) {
let role_name = self.parse_identifier()?;
let role_name = self.parse_identifier()?.unwrap();
AlterRoleOperation::RenameRole { role_name }
} else {
return self.expected("TO after RENAME", self.peek_token());
Expand Down

0 comments on commit b4547ca

Please sign in to comment.