diff --git a/crates/codegen/src/get_child_token_range.rs b/crates/codegen/src/get_child_token_range.rs new file mode 100644 index 00000000..fed68c2f --- /dev/null +++ b/crates/codegen/src/get_child_token_range.rs @@ -0,0 +1,354 @@ +use pg_query_proto_parser::{FieldType, Node, ProtoParser}; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote}; + +pub fn get_child_token_range_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let parser = ProtoParser::new("./libpg_query/protobuf/pg_query.proto"); + + let proto_file = parser.parse(); + + let node_identifiers = node_identifiers(&proto_file.nodes); + let node_handlers = node_handlers(&proto_file.nodes); + + quote! { + use log::{debug}; + use pg_query::{protobuf::ScanToken, protobuf::Token, NodeEnum, protobuf::SortByDir}; + use cstree::text::{TextRange, TextSize}; + + #[derive(Debug)] + struct TokenProperty { + value: Option, + token: Option, + } + + impl From for TokenProperty { + fn from(value: i32) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + impl From for TokenProperty { + fn from(value: u32) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + + impl From for TokenProperty { + fn from(value: i64) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + impl From for TokenProperty { + fn from(value: u64) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + impl From for TokenProperty { + fn from(value: f64) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + impl From for TokenProperty { + fn from(value: bool) -> TokenProperty { + TokenProperty { + value: Some(value.to_string()), + token: None, + } + } + } + + impl From for TokenProperty { + fn from(value: String) -> TokenProperty { + assert!(value.len() > 0, "String property value has length 0"); + TokenProperty { + value: Some(value.to_lowercase()), + token: None, + } + } + } + + + impl From<&pg_query::protobuf::Integer> for TokenProperty { + fn from(node: &pg_query::protobuf::Integer) -> TokenProperty { + TokenProperty { + value: Some(node.ival.to_string()), + token: Some(Token::Iconst) + } + } + } + + impl From<&pg_query::protobuf::Boolean> for TokenProperty { + fn from(node: &pg_query::protobuf::Boolean) -> TokenProperty { + TokenProperty { + value: Some(node.boolval.to_string()), + token: match node.boolval { + true => Some(Token::TrueP), + false => Some(Token::FalseP), + } + } + } + } + + impl From for TokenProperty { + fn from(token: Token) -> TokenProperty { + TokenProperty { + value: None, + token: Some(token), + } + } + } + + fn get_token_text(token: &ScanToken ,text: &str) -> String { + let start = usize::try_from(token.start).unwrap(); + let end = usize::try_from(token.end).unwrap(); + text.chars() + .skip(start) + .take(end - start) + .collect::() + .to_lowercase() + } + + + /// list of aliases from https://www.postgresql.org/docs/current/datatype.html + const ALIASES: [&[&str]; 2]= [ + &["integer", "int", "int4"], + &["real", "float4"], + ]; + + /// returns a list of aliases for a string. primarily used for data types. + fn aliases(text: &str) -> Vec<&str> { + for alias in ALIASES { + if alias.contains(&text) { + return alias.to_vec(); + } + } + return vec![text]; + } + + #[derive(Debug)] + pub enum ChildTokenRangeResult { + TooManyTokens, + NoTokens, + /// indices are the .start of all child tokens used to estimate the range + ChildTokenRange { used_token_indices: Vec, range: TextRange }, + } + + pub fn get_child_token_range(node: &NodeEnum, tokens: Vec<&ScanToken>, text: &str, nearest_parent_location: Option) -> ChildTokenRangeResult { + let mut child_tokens: Vec<&ScanToken> = Vec::new(); + + // if true, we found more than one valid token for at least one property of the node + let mut has_too_many_tokens: bool = false; + + let mut get_token = |property: TokenProperty| { + let possible_tokens = tokens + .iter() + .filter_map(|t| { + if property.token.is_some() { + // if a token is set, we can safely ignore all tokens that are not of the same type + if t.token() != property.token.unwrap() { + return None; + } + } + + // make a string comparison of the text of the token and the property value + if property.value.is_some() { + let mut token_text = get_token_text(t, text); + // if token is Sconst, remove leading and trailing quotes + if t.token() == Token::Sconst { + let string_delimiter: &[char; 2] = &['\'', '$']; + token_text = token_text.trim_start_matches(string_delimiter).trim_end_matches(string_delimiter).to_string(); + } + + if !aliases(property.value.as_ref().unwrap()).contains(&token_text.as_str()) { + return None; + } + } + + Some(t) + }) + .collect::>(); + + if possible_tokens.len() == 0 { + debug!( + "No matching token found for property {:#?} of node {:#?} in {:#?} with tokens {:#?}", + property, node, text, tokens + ); + return; + } + + if possible_tokens.len() == 1 { + debug!( + "Found token {:#?} for property {:#?} of node {:#?}", + possible_tokens[0], property, node + ); + child_tokens.push(possible_tokens[0]); + return; + } + + if nearest_parent_location.is_none() { + debug!("Found {:#?} for property {:#?} and no nearest_parent_location set", possible_tokens, property); + has_too_many_tokens = true; + return; + } + + let token = possible_tokens + .iter().map(|t| ((nearest_parent_location.unwrap() as i32 - t.start), t)) + .min_by_key(|(d, _)| d.to_owned()) + .map(|(_, t)| t); + + debug!("Selected {:#?} as token closest from parent {:#?} as location {:#?}", token.unwrap(), node, nearest_parent_location); + + child_tokens.push(token.unwrap()); + }; + + match node { + #(NodeEnum::#node_identifiers(n) => {#node_handlers}),*, + }; + + + if has_too_many_tokens == true { + ChildTokenRangeResult::TooManyTokens + } else if child_tokens.len() == 0 { + ChildTokenRangeResult::NoTokens + } else { + ChildTokenRangeResult::ChildTokenRange { + used_token_indices: child_tokens.iter().map(|t| t.start).collect(), + range: TextRange::new( + TextSize::from(child_tokens.iter().min_by_key(|t| t.start).unwrap().start as u32), + TextSize::from(child_tokens.iter().max_by_key(|t| t.end).unwrap().end as u32), + ) + } + } + } + } +} + +fn node_identifiers(nodes: &[Node]) -> Vec { + nodes + .iter() + .map(|node| format_ident!("{}", &node.name)) + .collect() +} + +fn node_handlers(nodes: &[Node]) -> Vec { + nodes + .iter() + .map(|node| { + let string_property_handlers = string_property_handlers(&node); + let custom_handlers = custom_handlers(&node); + quote! { + #custom_handlers + #(#string_property_handlers)* + } + }) + .collect() +} + +fn custom_handlers(node: &Node) -> TokenStream { + match node.name.as_str() { + "SelectStmt" => quote! { + get_token(TokenProperty::from(Token::Select)); + if n.distinct_clause.len() > 0 { + get_token(TokenProperty::from(Token::Distinct)); + } + }, + "Integer" => quote! { + get_token(TokenProperty::from(n)); + }, + "WindowDef" => quote! { + if n.partition_clause.len() > 0 { + get_token(TokenProperty::from(Token::Window)); + } else { + get_token(TokenProperty::from(Token::Over)); + } + }, + "Boolean" => quote! { + get_token(TokenProperty::from(n)); + }, + "AStar" => quote! { + get_token(TokenProperty::from(Token::Ascii42)); + }, + "FuncCall" => quote! { + if n.agg_filter.is_some() { + get_token(TokenProperty::from(Token::Filter)); + } + }, + "SqlvalueFunction" => quote! { + match n.op { + // 1 SvfopCurrentDate + // 2 SvfopCurrentTime + // 3 SvfopCurrentTimeN + // 4 SvfopCurrentTimestamp + // 5 SvfopCurrentTimestampN + // 6 SvfopLocaltime + // 7 SvfopLocaltimeN + // 8 SvfopLocaltimestamp + // 9 SvfopLocaltimestampN + // 10 SvfopCurrentRole + 10 => get_token(TokenProperty::from(Token::CurrentRole)), + // 11 SvfopCurrentUser + 11 => get_token(TokenProperty::from(Token::CurrentUser)), + // 12 SvfopUser + // 13 SvfopSessionUser + // 14 SvfopCurrentCatalog + // 15 SvfopCurrentSchema + _ => panic!("Unknown SqlvalueFunction {:#?}", n.op), + } + }, + "SortBy" => quote! { + get_token(TokenProperty::from(Token::Order)); + match n.sortby_dir { + 2 => get_token(TokenProperty::from(Token::Asc)), + 3 => get_token(TokenProperty::from(Token::Desc)), + _ => {} + } + }, + "AConst" => quote! { + if n.isnull { + get_token(TokenProperty::from(Token::NullP)); + } + }, + _ => quote! {}, + } +} + +fn string_property_handlers(node: &Node) -> Vec { + node.fields + .iter() + .filter_map(|field| { + if field.repeated { + return None; + } + let field_name = format_ident!("{}", field.name.as_str()); + match field.field_type { + // just handle string values for now + FieldType::String => Some(quote! { + // most string values are never None, but an empty string + if n.#field_name.len() > 0 { + get_token(TokenProperty::from(n.#field_name.to_owned())); + } + }), + _ => None, + } + }) + .collect() +} diff --git a/crates/codegen/src/get_location.rs b/crates/codegen/src/get_location.rs index 29c79088..7e8cf5b0 100644 --- a/crates/codegen/src/get_location.rs +++ b/crates/codegen/src/get_location.rs @@ -14,27 +14,36 @@ pub fn get_location_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenSt quote! { use pg_query::NodeEnum; - // Returns the location of a node - pub fn get_location(node: &NodeEnum) -> Option { + /// Returns the location of a node + pub fn get_location(node: &NodeEnum) -> Option { + let loc = get_location_internal(node); + if loc.is_some() { + u32::try_from(loc.unwrap()).ok() + } else { + None + } + } + + fn get_location_internal(node: &NodeEnum) -> Option { let location = match node { - // for some nodes, the location of the node itself is after their childrens location. + // for some nodes, the location of the node itself is after their children location. // we implement the logic for those nodes manually. // if you add one, make sure to add its name to `manual_node_names()`. NodeEnum::BoolExpr(n) => { let a = n.args.iter().min_by(|a, b| { - let loc_a = get_location(&a.node.as_ref().unwrap()); - let loc_b = get_location(&b.node.as_ref().unwrap()); + let loc_a = get_location_internal(&a.node.as_ref().unwrap()); + let loc_b = get_location_internal(&b.node.as_ref().unwrap()); loc_a.cmp(&loc_b) }); - get_location(&a.unwrap().node.as_ref().unwrap()) + get_location_internal(&a.unwrap().node.as_ref().unwrap()) }, - NodeEnum::AExpr(n) => get_location(&n.lexpr.as_ref().unwrap().node.as_ref().unwrap()), + NodeEnum::AExpr(n) => get_location_internal(&n.lexpr.as_ref().unwrap().node.as_ref().unwrap()), #(NodeEnum::#node_identifiers(n) => #location_idents),* }; if location.is_some() && location.unwrap() < 0 { None } else { - location + location } } } diff --git a/crates/codegen/src/get_children.rs b/crates/codegen/src/get_nodes.rs similarity index 89% rename from crates/codegen/src/get_children.rs rename to crates/codegen/src/get_nodes.rs index e92c5f6e..758bf8f7 100644 --- a/crates/codegen/src/get_children.rs +++ b/crates/codegen/src/get_nodes.rs @@ -2,7 +2,7 @@ use pg_query_proto_parser::{FieldType, Node, ProtoParser}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -pub fn get_children_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenStream { +pub fn get_nodes_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenStream { let parser = ProtoParser::new("./libpg_query/protobuf/pg_query.proto"); let proto_file = parser.parse(); @@ -16,7 +16,7 @@ pub fn get_children_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenSt use std::collections::VecDeque; #[derive(Debug, Clone)] - pub struct ChildrenNode { + pub struct Node { pub node: NodeEnum, pub depth: i32, pub path: String, @@ -24,8 +24,10 @@ pub fn get_children_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenSt /// Returns all children of the node, recursively /// location is resolved manually - pub fn get_children(node: &NodeEnum, text: String, current_depth: i32) -> Vec { - let mut nodes: Vec = vec![]; + pub fn get_nodes(node: &NodeEnum, text: String, current_depth: i32) -> Vec { + let mut nodes: Vec = vec![ + Node { node: node.to_owned(), depth: current_depth, path: "0".to_string() } + ]; // Node, depth, path let mut stack: VecDeque<(NodeEnum, i32, String)> = VecDeque::from(vec![(node.to_owned(), current_depth, "0".to_string())]); @@ -37,7 +39,7 @@ pub fn get_children_mod(_item: proc_macro2::TokenStream) -> proc_macro2::TokenSt let path = path.clone() + "." + child_ctr.to_string().as_str(); child_ctr = child_ctr + 1; stack.push_back((c.to_owned(), current_depth, path.clone())); - nodes.push(ChildrenNode { + nodes.push(Node { node: c, depth: current_depth, path: path.clone(), @@ -99,8 +101,9 @@ fn property_handlers(node: &Node) -> Vec { Some(quote! { n.#field_name .iter() - .for_each(|x| handle_child(x.node.as_ref().unwrap().to_owned())); - + .for_each(|x| if x.node.is_some() { + handle_child(x.node.as_ref().unwrap().to_owned()); + }); }) } else if field.field_type == FieldType::Node && field.is_one_of == false { if field.node_name == Some("Node".to_owned()) { diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index fba42ea7..b935182d 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1,14 +1,21 @@ -mod get_children; +mod get_child_token_range; mod get_location; +mod get_nodes; mod syntax_kind; -use get_children::get_children_mod; +use get_child_token_range::get_child_token_range_mod; use get_location::get_location_mod; +use get_nodes::get_nodes_mod; use syntax_kind::syntax_kind_mod; #[proc_macro] -pub fn get_children(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - get_children_mod(item.into()).into() +pub fn get_child_token_range(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + get_child_token_range_mod(item.into()).into() +} + +#[proc_macro] +pub fn get_nodes(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + get_nodes_mod(item.into()).into() } #[proc_macro] diff --git a/crates/parser/src/estimate_node_range.rs b/crates/parser/src/estimate_node_range.rs new file mode 100644 index 00000000..8c8e514b --- /dev/null +++ b/crates/parser/src/estimate_node_range.rs @@ -0,0 +1,276 @@ +use std::cmp::max; + +use crate::get_child_token_range_codegen::{get_child_token_range, ChildTokenRangeResult}; +use crate::get_location_codegen::get_location; +use crate::get_nodes_codegen::Node; +use cstree::text::{TextRange, TextSize}; +use pg_query::protobuf::ScanToken; + +#[derive(Debug, Clone)] +pub struct RangedNode { + pub inner: Node, + pub range: TextRange, +} + +/// Turns a `Vec` into a `Vec` by estimating their range. +pub fn estimate_node_range( + nodes: &mut Vec, + tokens: &Vec, + text: &str, +) -> Vec { + // ensure that all children of any given node are already processed before processing the node itself + nodes.sort_by(|a, b| b.path.cmp(&a.path)); + + // first get ranges only from child tokens + let mut used_tokens: Vec = Vec::new(); + let mut child_token_ranges: Vec> = Vec::new(); + let mut too_many_tokens_at: Vec = Vec::new(); + + nodes.iter().for_each(|n| { + match get_child_token_range( + &n.node, + tokens + .iter() + .filter(|t| !used_tokens.contains(&t.start)) + .collect(), + text, + None, + ) { + ChildTokenRangeResult::TooManyTokens => { + too_many_tokens_at.push(nodes.iter().position(|x| x.path == n.path).unwrap()); + child_token_ranges.push(None); + } + ChildTokenRangeResult::ChildTokenRange { + used_token_indices, + range, + } => { + used_tokens.extend(used_token_indices); + child_token_ranges.push(Some(range)); + } + ChildTokenRangeResult::NoTokens => { + child_token_ranges.push(None); + } + }; + }); + + // second iteration using the nearest parent from the first, or the location of the nearest + // parent node + for idx in too_many_tokens_at { + let nearest_parent_start = + get_nearest_parent_start(&nodes[idx], &nodes, &child_token_ranges); + let nearest_parent_location = get_nearest_parent_location(&nodes[idx], &nodes); + + match get_child_token_range( + &nodes[idx].node, + tokens + .iter() + .filter(|t| !used_tokens.contains(&t.start)) + .collect(), + text, + Some(max(nearest_parent_start, nearest_parent_location)), + ) { + ChildTokenRangeResult::ChildTokenRange { + used_token_indices, + range, + } => { + used_tokens.extend(used_token_indices); + child_token_ranges[idx] = Some(range) + } + _ => {} + }; + } + + let mut ranged_nodes: Vec = Vec::new(); + + nodes.iter().enumerate().for_each(|(idx, n)| { + let child_token_range = child_token_ranges[idx]; + + let child_node_ranges = ranged_nodes + .iter() + .filter(|x| x.inner.path.starts_with(n.path.as_str())) + .collect::>(); + + // get `from` location as the smaller value of the location of the node, the start of all children nodes, and the start of the first child token + let node_location = match get_location(&n.node) { + Some(l) => Some(TextSize::from(l)), + None => None, + }; + let start_of_all_children_ranges = if child_node_ranges.len() > 0 { + Some( + child_node_ranges + .iter() + .min_by_key(|n| n.range.start()) + .unwrap() + .range + .start(), + ) + } else { + None + }; + let start_of_first_child_token = match child_token_range { + Some(r) => Some(r.start()), + None => None, + }; + + let from_locations: [Option; 3] = [ + node_location, + start_of_all_children_ranges, + start_of_first_child_token, + ]; + let from = from_locations.iter().filter(|v| v.is_some()).min(); + + // For `to`, it’s the larger value of the end of the last direkt child token, and the end of all children ranges. + let end_of_all_children_ranges = if child_node_ranges.len() > 0 { + Some( + child_node_ranges + .iter() + .max_by_key(|n| n.range.end()) + .unwrap() + .range + .end(), + ) + } else { + None + }; + let end_of_last_child_token = match child_token_range { + Some(r) => Some(r.end()), + None => None, + }; + let to_locations: [Option; 2] = + [end_of_all_children_ranges, end_of_last_child_token]; + let to = to_locations.iter().filter(|v| v.is_some()).max(); + + if from.is_some() && to.is_some() { + // ignore nodes that have no range. They are not relevant for the cst. + ranged_nodes.push(RangedNode { + inner: n.to_owned(), + range: TextRange::new(from.unwrap().unwrap(), to.unwrap().unwrap()), + }); + } + }); + + // sort by start of range, and then by depth + ranged_nodes.sort_by_key(|i| (i.range.start(), i.inner.depth)); + + ranged_nodes +} + +fn get_nearest_parent_start( + node: &Node, + nodes: &Vec, + child_token_ranges: &Vec>, +) -> u32 { + let mut path_elements = node.path.split(".").collect::>(); + path_elements.pop(); + while path_elements.len() > 0 { + let parent_path = path_elements.join("."); + let parent_idx = nodes.iter().position(|c| c.path == parent_path); + if parent_idx.is_some() { + if child_token_ranges[parent_idx.unwrap()].is_some() { + return u32::from(child_token_ranges[parent_idx.unwrap()].unwrap().start()); + } + } + + path_elements.pop(); + } + + // fallback to 0 + 0 +} + +fn get_nearest_parent_location(n: &Node, children: &Vec) -> u32 { + // if location is set, return it + let location = get_location(&n.node); + if location.is_some() { + return location.unwrap(); + } + + // go up in the tree and check if location exists on any parent + let mut path_elements = n.path.split(".").collect::>(); + path_elements.pop(); + while path_elements.len() > 0 { + let parent_path = path_elements.join("."); + let node = children.iter().find(|c| c.path == parent_path); + if node.is_some() { + let location = get_location(&node.unwrap().node); + if location.is_some() { + return location.unwrap(); + } + } + + path_elements.pop(); + } + + // fallback to 0 + 0 +} + +#[cfg(test)] +mod tests { + use cstree::text::{TextRange, TextSize}; + use pg_query::NodeEnum; + + use crate::estimate_node_range::estimate_node_range; + use crate::get_nodes_codegen::get_nodes; + + #[test] + fn test_estimate_node_range() { + let input = "select null"; + + let pg_query_tokens = match pg_query::scan(input) { + Ok(scanned) => scanned.tokens, + Err(_) => Vec::new(), + }; + + let pg_query_root = match pg_query::parse(input) { + Ok(parsed) => Some( + parsed + .protobuf + .nodes() + .iter() + .find(|n| n.1 == 1) + .unwrap() + .0 + .to_enum(), + ), + Err(_) => None, + }; + + let mut nodes = get_nodes(&pg_query_root.unwrap(), input.to_string(), 1); + + let ranged_nodes = estimate_node_range(&mut nodes, &pg_query_tokens, &input); + + assert!(ranged_nodes + .iter() + .find( + |n| n.range == TextRange::new(TextSize::from(0), TextSize::from(11)) + && match &n.inner.node { + NodeEnum::SelectStmt(_) => true, + _ => false, + } + ) + .is_some()); + + assert!(ranged_nodes + .iter() + .find( + |n| n.range == TextRange::new(TextSize::from(7), TextSize::from(11)) + && match &n.inner.node { + NodeEnum::ResTarget(_) => true, + _ => false, + } + ) + .is_some()); + + assert!(ranged_nodes + .iter() + .find( + |n| n.range == TextRange::new(TextSize::from(7), TextSize::from(11)) + && match &n.inner.node { + NodeEnum::AConst(_) => true, + _ => false, + } + ) + .is_some()); + } +} diff --git a/crates/parser/src/get_child_token_range_codegen.rs b/crates/parser/src/get_child_token_range_codegen.rs new file mode 100644 index 00000000..9b90d602 --- /dev/null +++ b/crates/parser/src/get_child_token_range_codegen.rs @@ -0,0 +1,3 @@ +use codegen::get_child_token_range; + +get_child_token_range!(); diff --git a/crates/parser/src/get_location_codegen.rs b/crates/parser/src/get_location_codegen.rs new file mode 100644 index 00000000..fcc6685d --- /dev/null +++ b/crates/parser/src/get_location_codegen.rs @@ -0,0 +1,3 @@ +use codegen::get_location; + +get_location!(); diff --git a/crates/parser/src/get_nodes_codegen.rs b/crates/parser/src/get_nodes_codegen.rs new file mode 100644 index 00000000..3305baab --- /dev/null +++ b/crates/parser/src/get_nodes_codegen.rs @@ -0,0 +1,30 @@ +use codegen::get_nodes; + +get_nodes!(); + +#[cfg(test)] +mod tests { + use crate::get_nodes_codegen::get_nodes; + + #[test] + fn test_get_nodes() { + let input = "with c as (insert into contact (id) values ('id')) select * from c;"; + + let pg_query_root = match pg_query::parse(input) { + Ok(parsed) => Some( + parsed + .protobuf + .nodes() + .iter() + .find(|n| n.1 == 1) + .unwrap() + .0 + .to_enum(), + ), + Err(_) => None, + }; + + let nodes = get_nodes(&pg_query_root.unwrap(), input.to_string(), 1); + assert_eq!(nodes.len(), 14); + } +} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 90b2f9a2..2a24b634 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -3,19 +3,23 @@ //! This crate provides a parser for the Postgres SQL dialect. //! It is based in the pg_query.rs crate, which is a wrapper around the PostgreSQL query parser. //! The main `Parser` struct parses a source file and individual statements. -//! The `Parse` struct contains the resulting concrete syntax tree, syntax errors, and the abtract syntax tree, which is a list of pg_query statements and their positions. +//! The `Parse` result struct contains the resulting concrete syntax tree, syntax errors, and the abtract syntax tree, which is a list of pg_query statements and their positions. //! //! The idea is to offload the heavy lifting to the same parser that the PostgreSQL server uses, -//! and just fill in the gaps to be able to build both cst and ast from a a source file that +//! and just fill in the gaps to be able to build both cst and ast from a source file that //! potentially contains erroneous statements. //! //! The main drawbacks of the PostgreSQL query parser mitigated by this parser are: //! - it only parsed a full source text, and if there is any syntax error in a file, it will not parse anything and return an error. -//! - it does not parse whitespaces and newlines, so it is not possible to build a concrete syntax tree build a concrete syntax tree. +//! - it does not parse whitespaces and newlines, and it only returns ast nodes. The concrete syntax tree has to be reverse-engineered. //! -//! To see how these drawbacks are mitigated, see the `statement.rs` and the `source_file.rs` module. +//! To see how these drawbacks are mitigated, see the `statement_parser.rs` and the `source_parser.rs` module. mod ast_node; +mod estimate_node_range; +mod get_child_token_range_codegen; +mod get_location_codegen; +mod get_nodes_codegen; mod parser; mod sibling_token; mod source_parser; diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 80219f8d..06cd9d85 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -8,7 +8,7 @@ use crate::syntax_error::SyntaxError; use crate::syntax_kind_codegen::SyntaxKind; use crate::syntax_node::SyntaxNode; -/// Main parser that controls the cst building process, and collects errors and statements +/// Main parser that exposes the `cstree` api, and collects errors and statements #[derive(Debug)] pub struct Parser { /// The cst builder @@ -17,16 +17,9 @@ pub struct Parser { errors: Vec, /// The pg_query statements representing the abtract syntax tree stmts: Vec, - /// The current checkpoint depth, if any - checkpoint: Option, - /// Whether the parser is currently parsing a flat node - is_parsing_flat_node: bool, - /// Keeps track of currently open nodes - /// Latest opened is last - open_nodes: Vec<(SyntaxKind, i32)>, } -/// Result of parsing +/// Result of Building #[derive(Debug)] pub struct Parse { /// The concrete syntax tree @@ -43,77 +36,24 @@ impl Parser { inner: GreenNodeBuilder::new(), errors: Vec::new(), stmts: Vec::new(), - checkpoint: None, - is_parsing_flat_node: false, - open_nodes: Vec::new(), } } - /// close all nodes until the specified depth is reached - pub fn close_until_depth(&mut self, depth: i32) { - debug!("close until depth {}", depth); - if self.open_nodes.is_empty() || self.get_current_depth() < depth { - return; - } - loop { - if self.open_nodes.is_empty() || self.get_current_depth() < depth { - break; - } - self.finish_node(); - } - } - - fn get_current_depth(&self) -> i32 { - self.open_nodes[self.open_nodes.len() - 1].1 - } - - /// set a checkpoint at current depth - /// - /// if `is_parsing_flat_node` is true, all tokens parsed until this checkpoint is closed will be applied immediately - pub fn set_checkpoint(&mut self) { - assert!( - self.checkpoint.is_none(), - "Must close previouos checkpoint before setting new one" - ); - self.checkpoint = Some(self.get_current_depth()); - } - - /// close all nodes until checkpoint depth is reached - pub fn close_checkpoint(&mut self) { - if self.checkpoint.is_some() { - self.close_until_depth(self.checkpoint.unwrap()); - } - self.checkpoint = None; - self.is_parsing_flat_node = false; - } - - /// start a new node of `SyntaxKind` at `depth` - /// handles closing previous nodes if necessary - pub fn start_node_at(&mut self, kind: SyntaxKind, depth: i32) { - debug!("starting node at depth {} {:?}", depth, kind); - // close until target depth - self.close_until_depth(depth); - - self.open_nodes.push((kind, depth)); - debug!("start node {:?}", kind); + /// start a new node of `SyntaxKind` + pub fn start_node(&mut self, kind: SyntaxKind) { + debug!("start_node: {:?}", kind); self.inner.start_node(kind); } /// finish current node pub fn finish_node(&mut self) { debug!("finish_node"); - - let n = self.open_nodes.pop(); - if n.is_none() { - panic!("No node to finish"); - } - - debug!("finish node {:?}", n.unwrap().0); self.inner.finish_node(); } /// applies token pub fn token(&mut self, kind: SyntaxKind, text: &str) { + debug!("token: {:?} {:?}", kind, text); self.inner.token(kind, text); } diff --git a/crates/parser/src/sibling_token.rs b/crates/parser/src/sibling_token.rs index 6a42dd0d..67e2ed71 100644 --- a/crates/parser/src/sibling_token.rs +++ b/crates/parser/src/sibling_token.rs @@ -1,31 +1,49 @@ use crate::syntax_kind_codegen::SyntaxKind; +const SIBLINGS: [(SyntaxKind, SyntaxKind); 1] = [(SyntaxKind::Ascii40, SyntaxKind::Ascii41)]; + impl SyntaxKind { - pub fn is_opening_sibling(&self) -> bool { - match self { - SyntaxKind::Ascii40 => true, - SyntaxKind::Ascii91 => true, - SyntaxKind::Case => true, - _ => false, - } + pub fn is_closing_sibling(self) -> bool { + SIBLINGS.iter().any(|(_, close)| *close == self) + } + + pub fn is_opening_sibling(self) -> bool { + SIBLINGS.iter().any(|(open, _)| *open == self) + } + + pub fn get_closing_sibling(self) -> SyntaxKind { + SIBLINGS + .iter() + .find_map(|(open, close)| if *open == self { Some(*close) } else { None }) + .unwrap() + } + + pub fn get_opening_sibling(self) -> SyntaxKind { + SIBLINGS + .iter() + .find_map(|(open, close)| if *close == self { Some(*open) } else { None }) + .unwrap() } - pub fn is_closing_sibling(&self) -> bool { - match self { - SyntaxKind::Ascii41 => true, - SyntaxKind::Ascii93 => true, - SyntaxKind::EndP => true, - _ => false, - } +} + +#[cfg(test)] +mod tests { + use std::assert_eq; + + use super::*; + + #[test] + fn test_siblings() { + assert_eq!(SyntaxKind::Ascii40.is_opening_sibling(), true); + assert_eq!( + SyntaxKind::Ascii40.get_closing_sibling(), + SyntaxKind::Ascii41 + ); } - pub fn sibling(&self) -> Option { - match self { - SyntaxKind::Case => Some(SyntaxKind::EndP), - SyntaxKind::EndP => Some(SyntaxKind::Case), - SyntaxKind::Ascii40 => Some(SyntaxKind::Ascii41), - SyntaxKind::Ascii41 => Some(SyntaxKind::Ascii40), - SyntaxKind::Ascii91 => Some(SyntaxKind::Ascii93), - SyntaxKind::Ascii93 => Some(SyntaxKind::Ascii91), - _ => None, - } + + #[test] + #[should_panic] + fn test_mismatched_siblings() { + SyntaxKind::Ascii41.get_closing_sibling(); } } diff --git a/crates/parser/src/source_parser.rs b/crates/parser/src/source_parser.rs index b7a727ec..d59acc44 100644 --- a/crates/parser/src/source_parser.rs +++ b/crates/parser/src/source_parser.rs @@ -75,14 +75,15 @@ fn tokens(input: &str) -> Vec { } impl Parser { - /// Parse a source pub fn parse_source_at(&mut self, text: &str, at_offset: Option) { let offset = at_offset.unwrap_or(0); let tokens = tokens(&text); let mut tokens_iter = tokens.iter(); - self.start_node_at(SyntaxKind::SourceFile, 0); + // open root `SourceFile` node + self.start_node(SyntaxKind::SourceFile); + while let Some(token) = tokens_iter.next() { match token.kind { SourceFileToken::Comment => { @@ -92,13 +93,15 @@ impl Parser { self.token(SyntaxKind::Newline, token.text.as_str()); } SourceFileToken::Statement => { - self.parse_statement( + self.parse_statement_at( token.text.as_str(), Some(offset + u32::from(token.span.start())), ); } }; } + + // close root `SourceFile` node self.finish_node(); } } @@ -107,6 +110,10 @@ impl Parser { mod tests { use super::*; + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } + #[test] fn test_source_file_lexer() { let input = "select * from contact where id = '123';\n\n-- test comment\n\nselect wrong statement;\n\nselect id,username from contact\n\nselect id,name\nfrom contact -- test inline comment\nwhere id = '123';\n\n"; @@ -142,6 +149,8 @@ mod tests { #[test] fn test_source_file_parser() { + init(); + let input = "select id, name from users where id = '1224'; select select; @@ -163,6 +172,8 @@ select 1; #[test] fn test_lexer_with_nested_statements() { + init(); + let input = "select * from test; select 123; diff --git a/crates/parser/src/statement_parser.rs b/crates/parser/src/statement_parser.rs index d0f6a25c..25d35511 100644 --- a/crates/parser/src/statement_parser.rs +++ b/crates/parser/src/statement_parser.rs @@ -1,12 +1,15 @@ +use std::collections::VecDeque; + use cstree::text::{TextRange, TextSize}; -use logos::{Logos, Span}; +use log::debug; +use logos::Logos; -use crate::{parser::Parser, syntax_kind_codegen::SyntaxKind}; +use crate::{ + estimate_node_range::estimate_node_range, get_nodes_codegen::get_nodes, parser::Parser, + syntax_kind_codegen::SyntaxKind, +}; -/// A super simple lexer for sql statements. -/// -/// One weakness of pg_query.rs is that it does not parse whitespace or newlines. We use a very -/// simple lexer to fill the gaps. +/// Super simple lexer that only catches the tokens that libpg_query ignores. #[derive(Logos, Debug, PartialEq)] pub enum StatementToken { // comments and whitespaces @@ -21,8 +24,7 @@ pub enum StatementToken { } impl StatementToken { - /// Creates a `SyntaxKind` from a `StatementToken`. - /// can be generated. + /// Create a `SyntaxKind` from a `StatementToken`. pub fn syntax_kind(&self) -> SyntaxKind { match self { StatementToken::Whitespace => SyntaxKind::Whitespace, @@ -34,38 +36,35 @@ impl StatementToken { } impl Parser { - /// The main entry point for parsing a statement `text`. `at_offset` is the offset of the statement in the source file. + /// Parse a single statement passed in `text`. If `at_offset` is `Some`, the statement is assumed to be at that offset in the source file. /// - /// On a high level, the algorithm works as follows: - /// 1. Parse the statement with pg_query.rs. If the statement contains syntax errors, the parser will report the error and continue to work without information - /// about the nodes. The result will be a flat list of tokens under the generic `Stmt` node. - /// If successful, the first node in the ordered list will be the main node of the statement, - /// and serves as a root node. - /// 2. Scan the statements for tokens with pg_query.rs. This will never fail, even if the statement contains syntax errors. - /// 3. Parse the statement with the `StatementToken` lexer. The lexer only contains the tokens - /// that are not parsed by pg_query.rs, such as whitespace. - /// 4. Define a pointer that starts at 0 and move it along the statement. - /// - first, check if the current pointer is within a pg_query token. If so, consume the - /// token. - /// - if not, consume the next token from the `StatementToken` lexer. - /// 5. Close all open nodes for that statement. - pub fn parse_statement(&mut self, text: &str, at_offset: Option) { + /// On a high level, the parser works as follows: + /// - 1. Collect all information from pg_query.rs and `StatementToken` lexer + /// - 2. Derive as much information as possible from the collected information + /// - 3. Collect AST node and errors, if any + /// - 3. Walk the statement token by token, and reverse-engineer the concrete syntax tree + pub fn parse_statement_at(&mut self, text: &str, at_offset: Option) { + // 1. Collect as much information as possible from pg_query.rs and `StatementToken` lexer + + // offset of the statement in the source file. let offset = at_offset.unwrap_or(0); + + // range of the statement in the source file. let range = TextRange::new( TextSize::from(offset), TextSize::from(offset + text.len() as u32), ); - let mut pg_query_tokens = match pg_query::scan(text) { - Ok(scanned) => scanned.tokens.into_iter().peekable(), + // tokens from pg_query.rs + let pg_query_tokens = match pg_query::scan(text) { + Ok(scanned) => scanned.tokens, Err(e) => { self.error(e.to_string(), range); - Vec::new().into_iter().peekable() + Vec::new() } }; - // Get root node with depth 1 - // Since we are parsing only a single statement there can be only a single node at depth 1 + // root node of the statement, if no syntax errors let pg_query_root = match pg_query::parse(text) { Ok(parsed) => Some( parsed @@ -83,79 +82,164 @@ impl Parser { } }; - let mut lexer = StatementToken::lexer(&text); + debug!("pg_query_root: {:#?}", pg_query_root); + + // ranged nodes from pg_query.rs, including the root node + // the nodes are ordered by starting range, starting with the root node + let mut pg_query_nodes = match &pg_query_root { + Some(root) => estimate_node_range( + &mut get_nodes(root, text.to_string(), 1), + &pg_query_tokens, + &text, + ) + .into_iter() + .peekable(), + None => Vec::new().into_iter().peekable(), + }; + + let mut pg_query_tokens = pg_query_tokens.iter().peekable(); + + let mut statement_token_lexer = StatementToken::lexer(&text); + + // 2. Setup data structures required for the parsing algorithm - // parse root node if no syntax errors - if pg_query_root.is_some() { - let root_node = pg_query_root.unwrap(); - self.stmt(root_node.to_owned(), range); - self.start_node_at(SyntaxKind::new_from_pg_query_node(&root_node), 1); + // A buffer for tokens that are not applied immediately to the cst + let mut token_buffer: VecDeque<(SyntaxKind, String)> = VecDeque::new(); + // Keeps track of currently open nodes. Latest opened is last. + let mut open_nodes: Vec<(SyntaxKind, TextRange, i32)> = Vec::new(); + // List of (SyntaxKind, depth) to keep track of currently open sibling tokens and their depths. Latest opened is last. + let mut open_tokens: Vec<(SyntaxKind, i32)> = Vec::new(); + + // 3. Parse the statement + + // Handle root node + if pg_query_nodes.len() > 0 { + // if there are no syntax errors, use the pg_query node as the root node + let root_node = pg_query_nodes + .find(|n| n.inner.path == "0".to_string()) + .unwrap(); + // can only be at depth 1 + assert_eq!( + root_node.inner.depth, 1, + "Root node must be at depth 1, but is at depth {}", + root_node.inner.depth + ); + self.stmt(root_node.inner.node.to_owned(), range); + self.start_node(SyntaxKind::new_from_pg_query_node(&root_node.inner.node)); + open_nodes.push(( + SyntaxKind::new_from_pg_query_node(&root_node.inner.node), + range, + 1, + )); } else { // fallback to generic node as root - self.start_node_at(SyntaxKind::Stmt, 1); + self.start_node(SyntaxKind::Stmt); + open_nodes.push((SyntaxKind::Stmt, range, 1)); } - self.set_checkpoint(); // start at 0, and increment by the length of the token let mut pointer: i32 = 0; - #[derive(Debug)] - struct Token { - syntax_kind: SyntaxKind, - span: Span, - } - + // main loop that walks through the statement token by token while pointer < text.len() as i32 { // Check if the pointer is within a pg_query token let next_pg_query_token = pg_query_tokens.peek(); - let token = if next_pg_query_token.is_some() + + let token_length = if next_pg_query_token.is_some() && next_pg_query_token.unwrap().start <= pointer && pointer <= next_pg_query_token.unwrap().end { let token = pg_query_tokens.next().unwrap(); - Token { - syntax_kind: SyntaxKind::new_from_pg_query_token(&token), - span: Span { - start: token.start as usize, - end: token.end as usize, - }, + let token_syntax_kind = SyntaxKind::new_from_pg_query_token(token); + + let token_text = text + .chars() + .skip(token.start as usize) + .take((token.end as usize) - (token.start as usize)) + .collect::(); + + // a node can only start and end with a pg_query token, so we can handle them here + + // if closing token, close nodes until depth of opening token before applying it + let target_depth = if token_syntax_kind.is_closing_sibling() { + let opening_token = open_tokens.pop().unwrap(); + assert_eq!( + opening_token.0.get_closing_sibling(), + token_syntax_kind, + "Opening token {:?} does not match closing token {:?}", + opening_token.0, + token_syntax_kind + ); + Some(opening_token.1) + } else { + None + }; + + // before applying the token, close any node that ends before the token starts + while open_nodes.last().is_some() + && open_nodes.last().unwrap().1.end() <= TextSize::from(token.start as u32) + && (target_depth.is_none() + || open_nodes.last().unwrap().2 > target_depth.unwrap()) + { + self.finish_node(); + open_nodes.pop(); + } + + // drain token buffer + for (kind, text) in token_buffer.drain(0..token_buffer.len()) { + self.token(kind, text.as_str()); + } + + // consume all nodes that start before the token ends + while pg_query_nodes.peek().is_some() + && pg_query_nodes.peek().unwrap().range.start() + < TextSize::from(token.end as u32) + { + let node = pg_query_nodes.next().unwrap(); + self.start_node(SyntaxKind::new_from_pg_query_node(&node.inner.node)); + open_nodes.push(( + SyntaxKind::new_from_pg_query_node(&node.inner.node), + node.range, + node.inner.depth, + )); } + + // apply the token to the cst + self.token(token_syntax_kind, token_text.as_str()); + // save the token as an opening sibling token, if it is one + if token_syntax_kind.is_opening_sibling() { + open_tokens.push((token_syntax_kind, open_nodes.last().unwrap().2)); + } + + token_text.len() as i32 } else { // fallback to statement token // move statement token lexer to before pointer - while (lexer.span().end as i32) < pointer { - lexer.next(); + while (statement_token_lexer.span().end as i32) < pointer { + statement_token_lexer.next(); } - let token = lexer.next(); - if token.is_none() || (lexer.span().start as i32) != pointer { + let token = statement_token_lexer.next(); + if token.is_none() || (statement_token_lexer.span().start as i32) != pointer { // if the token is not at the pointer, we have a syntax error panic!( "Expected token for '{}' at offset {}", - lexer.slice(), - lexer.span().start + statement_token_lexer.slice(), + statement_token_lexer.span().start ); } - Token { - syntax_kind: token.unwrap().unwrap().syntax_kind(), - span: lexer.span(), - } + let token_text = statement_token_lexer.slice().to_string(); + token_buffer.push_back((token.unwrap().unwrap().syntax_kind(), token_text.clone())); + token_text.len() as i32 }; - self.token( - token.syntax_kind, - text.chars() - .skip(token.span.start) - .take(token.span.end - token.span.start) - .collect::() - .as_str(), - ); - - pointer = pointer + (token.span.end - token.span.start) as i32; + pointer = pointer + token_length; } - // close up nodes - self.close_checkpoint(); + while open_nodes.last().is_some() { + self.finish_node(); + open_nodes.pop(); + } } } @@ -165,27 +249,176 @@ mod tests { use super::*; + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } + + #[test] + fn test_statement() { + init(); + + let input = "select 1;"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_sibling_tokens() { + init(); + + let input = "SELECT city, count(*) FILTER (WHERE temp_lo < 45), max(temp_lo) FROM weather GROUP BY city;"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_opening_token() { + init(); + + let input = "INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + #[test] - fn test_invalid_statement() { - let input = "select select;"; + fn test_closing_token_at_last_position() { + init(); + + let input = "CREATE TABLE weather ( + city varchar(80) references cities(name), + temp_lo int +);"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_select_with_alias() { + init(); + + let input = "SELECT w1.temp_lo AS low, w1.temp_hi AS high FROM weather"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_select_distinct() { + init(); + + let input = "SELECT DISTINCT city + FROM weather + ORDER BY city;"; let mut parser = Parser::new(); - parser.parse_statement(input, None); + parser.parse_statement_at(input, None); let parsed = parser.finish(); assert_eq!(parsed.cst.text(), input); } #[test] - fn test_create_sql_function() { - let input = "CREATE FUNCTION dup(in int, out f1 int, out f2 text) - AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ - LANGUAGE SQL;"; + fn test_order_by() { + init(); + + let input = "SELECT sum(salary) OVER w, avg(salary) OVER w + FROM empsalary + WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);"; let mut parser = Parser::new(); - parser.parse_statement(input, None); + parser.parse_statement_at(input, None); let parsed = parser.finish(); assert_eq!(parsed.cst.text(), input); } + + #[test] + fn test_fn_call() { + init(); + + let input = + "SELECT count(*) FILTER (WHERE i < 5) AS filtered FROM generate_series(1,10) AS s(i);"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_window_call() { + init(); + + let input = + "SELECT sum(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_access_priv() { + init(); + + let input = "GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + dbg!(&parsed.cst); + + assert_eq!(parsed.cst.text(), input); + } + + #[test] + fn test_create_policy() { + init(); + + let input = "CREATE POLICY account_managers ON accounts TO managers USING (manager = current_user);"; + + let mut parser = Parser::new(); + parser.parse_statement_at(input, None); + let parsed = parser.finish(); + + dbg!(&parsed.cst); + + assert_eq!(parsed.cst.text(), input); + } + + // #[test] + // fn test_create_sql_function() { + // let input = "CREATE FUNCTION dup(in int, out f1 int, out f2 text) + // AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ + // LANGUAGE SQL;"; + // + // let mut parser = Parser::new(); + // parser.parse_statement(input, None); + // let parsed = parser.finish(); + // + // assert_eq!(parsed.cst.text(), input); + // } } diff --git a/crates/parser/src/syntax_error.rs b/crates/parser/src/syntax_error.rs index d8dee689..df6d670f 100644 --- a/crates/parser/src/syntax_error.rs +++ b/crates/parser/src/syntax_error.rs @@ -2,7 +2,7 @@ use std::fmt; use cstree::text::{TextRange, TextSize}; -/// Represents the result of unsuccessful tokenization, parsing +/// Represents the result of unsuccessful tokenization, parsing, /// or tree validation. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct SyntaxError(String, TextRange); diff --git a/crates/parser/tests/snapshots/statements/valid/0001.snap b/crates/parser/tests/snapshots/statements/valid/0001.snap index 1c2083ab..b1bcd7cd 100644 --- a/crates/parser/tests/snapshots/statements/valid/0001.snap +++ b/crates/parser/tests/snapshots/statements/valid/0001.snap @@ -2,46 +2,65 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT city, count(*) FILTER (WHERE temp_lo < 45), max(temp_lo)\n FROM weather\n GROUP BY city;\n" --- -SelectStmt@0..100 +SelectStmt@0..99 Select@0..6 "SELECT" Whitespace@6..7 " " - Ident@7..11 "city" + ResTarget@7..11 + ColumnRef@7..11 + String@7..11 + Ident@7..11 "city" Ascii44@11..12 "," Whitespace@12..13 " " - Ident@13..18 "count" - Ascii40@18..19 "(" - Ascii42@19..20 "*" - Ascii41@20..21 ")" - Whitespace@21..22 " " - Filter@22..28 "FILTER" - Whitespace@28..29 " " - Ascii40@29..30 "(" - Where@30..35 "WHERE" - Whitespace@35..36 " " - Ident@36..43 "temp_lo" - Whitespace@43..44 " " - Ascii60@44..45 "<" - Whitespace@45..46 " " - Iconst@46..48 "45" - Ascii41@48..49 ")" + ResTarget@13..49 + FuncCall@13..49 + String@13..18 + Ident@13..18 "count" + Ascii40@18..19 "(" + Ascii42@19..20 "*" + Ascii41@20..21 ")" + Whitespace@21..22 " " + Filter@22..28 "FILTER" + Whitespace@28..29 " " + Ascii40@29..30 "(" + Where@30..35 "WHERE" + Whitespace@35..36 " " + AExpr@36..48 + ColumnRef@36..43 + String@36..43 + Ident@36..43 "temp_lo" + Whitespace@43..44 " " + String@44..45 + Ascii60@44..45 "<" + Whitespace@45..46 " " + AConst@46..48 + Integer@46..48 + Iconst@46..48 "45" + Ascii41@48..49 ")" Ascii44@49..50 "," Whitespace@50..51 " " - Ident@51..54 "max" - Ascii40@54..55 "(" - Ident@55..62 "temp_lo" - Ascii41@62..63 ")" + ResTarget@51..63 + FuncCall@51..63 + String@51..54 + Ident@51..54 "max" + Ascii40@54..55 "(" + ColumnRef@55..62 + String@55..62 + Ident@55..62 "temp_lo" + Ascii41@62..63 ")" Newline@63..64 "\n" Whitespace@64..68 " " From@68..72 "FROM" Whitespace@72..73 " " - Ident@73..80 "weather" + RangeVar@73..80 + Ident@73..80 "weather" Newline@80..81 "\n" Whitespace@81..85 " " GroupP@85..90 "GROUP" Whitespace@90..91 " " By@91..93 "BY" Whitespace@93..94 " " - Ident@94..98 "city" + ColumnRef@94..98 + String@94..98 + Ident@94..98 "city" Ascii59@98..99 ";" - Newline@99..100 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0002.snap b/crates/parser/tests/snapshots/statements/valid/0002.snap index 45ebc465..ec7822f2 100644 --- a/crates/parser/tests/snapshots/statements/valid/0002.snap +++ b/crates/parser/tests/snapshots/statements/valid/0002.snap @@ -2,14 +2,14 @@ source: crates/parser/tests/statement_parser_test.rs description: "COPY weather FROM '/home/user/weather.txt';\n" --- -CopyStmt@0..44 +CopyStmt@0..43 Copy@0..4 "COPY" Whitespace@4..5 " " - Ident@5..12 "weather" + RangeVar@5..12 + Ident@5..12 "weather" Whitespace@12..13 " " From@13..17 "FROM" Whitespace@17..18 " " Sconst@18..42 "'/home/user/weather.txt'" Ascii59@42..43 ";" - Newline@43..44 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0003.snap b/crates/parser/tests/snapshots/statements/valid/0003.snap index 090b3a9a..8e53c49f 100644 --- a/crates/parser/tests/snapshots/statements/valid/0003.snap +++ b/crates/parser/tests/snapshots/statements/valid/0003.snap @@ -2,55 +2,75 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE weather (\n city varchar(80) references cities(name),\n temp_lo int,\n temp_hi int,\n prcp real,\n date date\n);\n" --- -CreateStmt@0..174 +CreateStmt@0..173 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..20 "weather" + RangeVar@13..20 + Ident@13..20 "weather" Whitespace@20..21 " " Ascii40@21..22 "(" Newline@22..23 "\n" Whitespace@23..31 " " - Ident@31..35 "city" - Whitespace@35..41 " " - Varchar@41..48 "varchar" - Ascii40@48..49 "(" - Iconst@49..51 "80" - Ascii41@51..52 ")" - Whitespace@52..53 " " - References@53..63 "references" - Whitespace@63..64 " " - Ident@64..70 "cities" - Ascii40@70..71 "(" - NameP@71..75 "name" - Ascii41@75..76 ")" + ColumnDef@31..76 + Ident@31..35 "city" + Whitespace@35..41 " " + TypeName@41..52 + String@41..48 + Varchar@41..48 "varchar" + Ascii40@48..49 "(" + AConst@49..51 + Integer@49..51 + Iconst@49..51 "80" + Ascii41@51..52 ")" + Whitespace@52..53 " " + Constraint@53..76 + References@53..63 "references" + Whitespace@63..64 " " + RangeVar@64..70 + Ident@64..70 "cities" + Ascii40@70..71 "(" + String@71..75 + NameP@71..75 "name" + Ascii41@75..76 ")" Ascii44@76..77 "," Newline@77..78 "\n" Whitespace@78..86 " " - Ident@86..93 "temp_lo" - Whitespace@93..96 " " - IntP@96..99 "int" + ColumnDef@86..99 + Ident@86..93 "temp_lo" + Whitespace@93..96 " " + TypeName@96..99 + String@96..99 + IntP@96..99 "int" Ascii44@99..100 "," Newline@100..101 "\n" Whitespace@101..109 " " - Ident@109..116 "temp_hi" - Whitespace@116..119 " " - IntP@119..122 "int" + ColumnDef@109..122 + Ident@109..116 "temp_hi" + Whitespace@116..119 " " + TypeName@119..122 + String@119..122 + IntP@119..122 "int" Ascii44@122..123 "," Newline@123..124 "\n" Whitespace@124..132 " " - Ident@132..136 "prcp" - Whitespace@136..142 " " - Real@142..146 "real" + ColumnDef@132..146 + Ident@132..136 "prcp" + Whitespace@136..142 " " + TypeName@142..146 + String@142..146 + Real@142..146 "real" Ascii44@146..147 "," Newline@147..148 "\n" Whitespace@148..156 " " - Ident@156..160 "date" - Whitespace@160..166 " " - Ident@166..170 "date" + ColumnDef@156..170 + Ident@156..160 "date" + Whitespace@160..166 " " + TypeName@166..170 + String@166..170 + Ident@166..170 "date" Newline@170..171 "\n" Ascii41@171..172 ")" Ascii59@172..173 ";" - Newline@173..174 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0004.snap b/crates/parser/tests/snapshots/statements/valid/0004.snap index d5e1524f..aa2ae312 100644 --- a/crates/parser/tests/snapshots/statements/valid/0004.snap +++ b/crates/parser/tests/snapshots/statements/valid/0004.snap @@ -2,51 +2,78 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE VIEW myview AS\n SELECT name, temp_lo, temp_hi, prcp, date, location\n FROM weather, cities\n WHERE city = name;\n" --- -ViewStmt@0..134 +ViewStmt@0..133 Create@0..6 "CREATE" Whitespace@6..7 " " View@7..11 "VIEW" Whitespace@11..12 " " - Ident@12..18 "myview" + RangeVar@12..18 + Ident@12..18 "myview" Whitespace@18..19 " " As@19..21 "AS" Newline@21..22 "\n" Whitespace@22..26 " " - Select@26..32 "SELECT" - Whitespace@32..33 " " - NameP@33..37 "name" - Ascii44@37..38 "," - Whitespace@38..39 " " - Ident@39..46 "temp_lo" - Ascii44@46..47 "," - Whitespace@47..48 " " - Ident@48..55 "temp_hi" - Ascii44@55..56 "," - Whitespace@56..57 " " - Ident@57..61 "prcp" - Ascii44@61..62 "," - Whitespace@62..63 " " - Ident@63..67 "date" - Ascii44@67..68 "," - Whitespace@68..69 " " - Location@69..77 "location" - Newline@77..78 "\n" - Whitespace@78..86 " " - From@86..90 "FROM" - Whitespace@90..91 " " - Ident@91..98 "weather" - Ascii44@98..99 "," - Whitespace@99..100 " " - Ident@100..106 "cities" - Newline@106..107 "\n" - Whitespace@107..115 " " - Where@115..120 "WHERE" - Whitespace@120..121 " " - Ident@121..125 "city" - Whitespace@125..126 " " - Ascii61@126..127 "=" - Whitespace@127..128 " " - NameP@128..132 "name" + SelectStmt@26..132 + Select@26..32 "SELECT" + Whitespace@32..33 " " + ResTarget@33..37 + ColumnRef@33..37 + String@33..37 + NameP@33..37 "name" + Ascii44@37..38 "," + Whitespace@38..39 " " + ResTarget@39..46 + ColumnRef@39..46 + String@39..46 + Ident@39..46 "temp_lo" + Ascii44@46..47 "," + Whitespace@47..48 " " + ResTarget@48..55 + ColumnRef@48..55 + String@48..55 + Ident@48..55 "temp_hi" + Ascii44@55..56 "," + Whitespace@56..57 " " + ResTarget@57..61 + ColumnRef@57..61 + String@57..61 + Ident@57..61 "prcp" + Ascii44@61..62 "," + Whitespace@62..63 " " + ResTarget@63..67 + ColumnRef@63..67 + String@63..67 + Ident@63..67 "date" + Ascii44@67..68 "," + Whitespace@68..69 " " + ResTarget@69..77 + ColumnRef@69..77 + String@69..77 + Location@69..77 "location" + Newline@77..78 "\n" + Whitespace@78..86 " " + From@86..90 "FROM" + Whitespace@90..91 " " + RangeVar@91..98 + Ident@91..98 "weather" + Ascii44@98..99 "," + Whitespace@99..100 " " + RangeVar@100..106 + Ident@100..106 "cities" + Newline@106..107 "\n" + Whitespace@107..115 " " + Where@115..120 "WHERE" + Whitespace@120..121 " " + AExpr@121..132 + ColumnRef@121..125 + String@121..125 + Ident@121..125 "city" + Whitespace@125..126 " " + String@126..127 + Ascii61@126..127 "=" + Whitespace@127..128 " " + ColumnRef@128..132 + String@128..132 + NameP@128..132 "name" Ascii59@132..133 ";" - Newline@133..134 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0005.snap b/crates/parser/tests/snapshots/statements/valid/0005.snap index d99908db..bb3a6ef7 100644 --- a/crates/parser/tests/snapshots/statements/valid/0005.snap +++ b/crates/parser/tests/snapshots/statements/valid/0005.snap @@ -2,20 +2,26 @@ source: crates/parser/tests/statement_parser_test.rs description: "DELETE FROM weather WHERE city = 'Hayward';\n" --- -DeleteStmt@0..44 +DeleteStmt@0..43 DeleteP@0..6 "DELETE" Whitespace@6..7 " " From@7..11 "FROM" Whitespace@11..12 " " - Ident@12..19 "weather" + RangeVar@12..19 + Ident@12..19 "weather" Whitespace@19..20 " " Where@20..25 "WHERE" Whitespace@25..26 " " - Ident@26..30 "city" - Whitespace@30..31 " " - Ascii61@31..32 "=" - Whitespace@32..33 " " - Sconst@33..42 "'Hayward'" + AExpr@26..42 + ColumnRef@26..30 + String@26..30 + Ident@26..30 "city" + Whitespace@30..31 " " + String@31..32 + Ascii61@31..32 "=" + Whitespace@32..33 " " + AConst@33..42 + String@33..42 + Sconst@33..42 "'Hayward'" Ascii59@42..43 ";" - Newline@43..44 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0006.snap b/crates/parser/tests/snapshots/statements/valid/0006.snap index 37cfd337..81fda79f 100644 --- a/crates/parser/tests/snapshots/statements/valid/0006.snap +++ b/crates/parser/tests/snapshots/statements/valid/0006.snap @@ -2,12 +2,13 @@ source: crates/parser/tests/statement_parser_test.rs description: "DROP TABLE tablename;\n" --- -DropStmt@0..22 +DropStmt@0..21 Drop@0..4 "DROP" Whitespace@4..5 " " Table@5..10 "TABLE" Whitespace@10..11 " " - Ident@11..20 "tablename" + List@11..20 + String@11..20 + Ident@11..20 "tablename" Ascii59@20..21 ";" - Newline@21..22 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0007.snap b/crates/parser/tests/snapshots/statements/valid/0007.snap index 61ccfb22..f4235d2c 100644 --- a/crates/parser/tests/snapshots/statements/valid/0007.snap +++ b/crates/parser/tests/snapshots/statements/valid/0007.snap @@ -2,35 +2,44 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE cities (\n name text,\n population real,\n elevation int -- (in ft)\n);\n\n" --- -CreateStmt@0..96 +CreateStmt@0..94 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..19 "cities" + RangeVar@13..19 + Ident@13..19 "cities" Whitespace@19..20 " " Ascii40@20..21 "(" Newline@21..22 "\n" Whitespace@22..24 " " - NameP@24..28 "name" - Whitespace@28..35 " " - TextP@35..39 "text" + ColumnDef@24..39 + NameP@24..28 "name" + Whitespace@28..35 " " + TypeName@35..39 + String@35..39 + TextP@35..39 "text" Ascii44@39..40 "," Newline@40..41 "\n" Whitespace@41..43 " " - Ident@43..53 "population" - Whitespace@53..54 " " - Real@54..58 "real" + ColumnDef@43..58 + Ident@43..53 "population" + Whitespace@53..54 " " + TypeName@54..58 + String@54..58 + Real@54..58 "real" Ascii44@58..59 "," Newline@59..60 "\n" Whitespace@60..62 " " - Ident@62..71 "elevation" - Whitespace@71..73 " " - IntP@73..76 "int" + ColumnDef@62..76 + Ident@62..71 "elevation" + Whitespace@71..73 " " + TypeName@73..76 + String@73..76 + IntP@73..76 "int" Whitespace@76..81 " " SqlComment@81..91 "-- (in ft)" Newline@91..92 "\n" Ascii41@92..93 ")" Ascii59@93..94 ";" - Newline@94..96 "\n\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0008.snap b/crates/parser/tests/snapshots/statements/valid/0008.snap index a33927f9..14a3c516 100644 --- a/crates/parser/tests/snapshots/statements/valid/0008.snap +++ b/crates/parser/tests/snapshots/statements/valid/0008.snap @@ -2,41 +2,55 @@ source: crates/parser/tests/statement_parser_test.rs description: "INSERT INTO weather (date, city, temp_hi, temp_lo)\n VALUES ('1994-11-29', 'Hayward', 54, 37);\n" --- -InsertStmt@0..97 +InsertStmt@0..96 Insert@0..6 "INSERT" Whitespace@6..7 " " Into@7..11 "INTO" Whitespace@11..12 " " - Ident@12..19 "weather" + RangeVar@12..19 + Ident@12..19 "weather" Whitespace@19..20 " " Ascii40@20..21 "(" - Ident@21..25 "date" + ResTarget@21..25 + Ident@21..25 "date" Ascii44@25..26 "," Whitespace@26..27 " " - Ident@27..31 "city" + ResTarget@27..31 + Ident@27..31 "city" Ascii44@31..32 "," Whitespace@32..33 " " - Ident@33..40 "temp_hi" + ResTarget@33..40 + Ident@33..40 "temp_hi" Ascii44@40..41 "," Whitespace@41..42 " " - Ident@42..49 "temp_lo" + ResTarget@42..49 + Ident@42..49 "temp_lo" Ascii41@49..50 ")" Newline@50..51 "\n" Whitespace@51..55 " " Values@55..61 "VALUES" Whitespace@61..62 " " Ascii40@62..63 "(" - Sconst@63..75 "'1994-11-29'" - Ascii44@75..76 "," - Whitespace@76..77 " " - Sconst@77..86 "'Hayward'" - Ascii44@86..87 "," - Whitespace@87..88 " " - Iconst@88..90 "54" - Ascii44@90..91 "," - Whitespace@91..92 " " - Iconst@92..94 "37" + SelectStmt@63..94 + List@63..94 + AConst@63..75 + String@63..75 + Sconst@63..75 "'1994-11-29'" + Ascii44@75..76 "," + Whitespace@76..77 " " + AConst@77..86 + String@77..86 + Sconst@77..86 "'Hayward'" + Ascii44@86..87 "," + Whitespace@87..88 " " + AConst@88..90 + Integer@88..90 + Iconst@88..90 "54" + Ascii44@90..91 "," + Whitespace@91..92 " " + AConst@92..94 + Integer@92..94 + Iconst@92..94 "37" Ascii41@94..95 ")" Ascii59@95..96 ";" - Newline@96..97 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0009.snap b/crates/parser/tests/snapshots/statements/valid/0009.snap index bf17d60a..fe1cba70 100644 --- a/crates/parser/tests/snapshots/statements/valid/0009.snap +++ b/crates/parser/tests/snapshots/statements/valid/0009.snap @@ -2,92 +2,137 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT w1.city, w1.temp_lo AS low, w1.temp_hi AS high,\n w2.city, w2.temp_lo AS low, w2.temp_hi AS high\n FROM weather w1 JOIN weather w2\n ON w1.temp_lo < w2.temp_lo AND w1.temp_hi > w2.temp_hi;\n" --- -SelectStmt@0..209 +SelectStmt@0..208 Select@0..6 "SELECT" Whitespace@6..7 " " - Ident@7..9 "w1" - Ascii46@9..10 "." - Ident@10..14 "city" + ResTarget@7..14 + ColumnRef@7..14 + String@7..9 + Ident@7..9 "w1" + Ascii46@9..10 "." + String@10..14 + Ident@10..14 "city" Ascii44@14..15 "," Whitespace@15..16 " " - Ident@16..18 "w1" - Ascii46@18..19 "." - Ident@19..26 "temp_lo" - Whitespace@26..27 " " - As@27..29 "AS" - Whitespace@29..30 " " - Ident@30..33 "low" + ResTarget@16..33 + ColumnRef@16..26 + String@16..18 + Ident@16..18 "w1" + Ascii46@18..19 "." + String@19..26 + Ident@19..26 "temp_lo" + Whitespace@26..27 " " + As@27..29 "AS" + Whitespace@29..30 " " + Ident@30..33 "low" Ascii44@33..34 "," Whitespace@34..35 " " - Ident@35..37 "w1" - Ascii46@37..38 "." - Ident@38..45 "temp_hi" - Whitespace@45..46 " " - As@46..48 "AS" - Whitespace@48..49 " " - Ident@49..53 "high" + ResTarget@35..53 + ColumnRef@35..45 + String@35..37 + Ident@35..37 "w1" + Ascii46@37..38 "." + String@38..45 + Ident@38..45 "temp_hi" + Whitespace@45..46 " " + As@46..48 "AS" + Whitespace@48..49 " " + Ident@49..53 "high" Ascii44@53..54 "," Newline@54..55 "\n" Whitespace@55..62 " " - Ident@62..64 "w2" - Ascii46@64..65 "." - Ident@65..69 "city" + ResTarget@62..69 + ColumnRef@62..69 + String@62..64 + Ident@62..64 "w2" + Ascii46@64..65 "." + String@65..69 + Ident@65..69 "city" Ascii44@69..70 "," Whitespace@70..71 " " - Ident@71..73 "w2" - Ascii46@73..74 "." - Ident@74..81 "temp_lo" - Whitespace@81..82 " " - As@82..84 "AS" - Whitespace@84..85 " " - Ident@85..88 "low" + ResTarget@71..88 + ColumnRef@71..81 + String@71..73 + Ident@71..73 "w2" + Ascii46@73..74 "." + String@74..81 + Ident@74..81 "temp_lo" + Whitespace@81..82 " " + As@82..84 "AS" + Whitespace@84..85 " " + Ident@85..88 "low" Ascii44@88..89 "," Whitespace@89..90 " " - Ident@90..92 "w2" - Ascii46@92..93 "." - Ident@93..100 "temp_hi" - Whitespace@100..101 " " - As@101..103 "AS" - Whitespace@103..104 " " - Ident@104..108 "high" + ResTarget@90..108 + ColumnRef@90..100 + String@90..92 + Ident@90..92 "w2" + Ascii46@92..93 "." + String@93..100 + Ident@93..100 "temp_hi" + Whitespace@100..101 " " + As@101..103 "AS" + Whitespace@103..104 " " + Ident@104..108 "high" Newline@108..109 "\n" Whitespace@109..113 " " From@113..117 "FROM" Whitespace@117..118 " " - Ident@118..125 "weather" - Whitespace@125..126 " " - Ident@126..128 "w1" - Whitespace@128..129 " " - Join@129..133 "JOIN" - Whitespace@133..134 " " - Ident@134..141 "weather" - Whitespace@141..142 " " - Ident@142..144 "w2" - Newline@144..145 "\n" - Whitespace@145..153 " " - On@153..155 "ON" - Whitespace@155..156 " " - Ident@156..158 "w1" - Ascii46@158..159 "." - Ident@159..166 "temp_lo" - Whitespace@166..167 " " - Ascii60@167..168 "<" - Whitespace@168..169 " " - Ident@169..171 "w2" - Ascii46@171..172 "." - Ident@172..179 "temp_lo" - Whitespace@179..180 " " - And@180..183 "AND" - Whitespace@183..184 " " - Ident@184..186 "w1" - Ascii46@186..187 "." - Ident@187..194 "temp_hi" - Whitespace@194..195 " " - Ascii62@195..196 ">" - Whitespace@196..197 " " - Ident@197..199 "w2" - Ascii46@199..200 "." - Ident@200..207 "temp_hi" + JoinExpr@118..207 + RangeVar@118..128 + Ident@118..125 "weather" + Whitespace@125..126 " " + Alias@126..128 + Ident@126..128 "w1" + Whitespace@128..129 " " + Join@129..133 "JOIN" + Whitespace@133..134 " " + RangeVar@134..144 + Ident@134..141 "weather" + Whitespace@141..142 " " + Alias@142..144 + Ident@142..144 "w2" + Newline@144..145 "\n" + Whitespace@145..153 " " + On@153..155 "ON" + Whitespace@155..156 " " + BoolExpr@156..207 + AExpr@156..179 + ColumnRef@156..166 + String@156..158 + Ident@156..158 "w1" + Ascii46@158..159 "." + String@159..166 + Ident@159..166 "temp_lo" + Whitespace@166..167 " " + String@167..168 + Ascii60@167..168 "<" + Whitespace@168..169 " " + ColumnRef@169..179 + String@169..171 + Ident@169..171 "w2" + Ascii46@171..172 "." + String@172..179 + Ident@172..179 "temp_lo" + Whitespace@179..180 " " + And@180..183 "AND" + Whitespace@183..184 " " + AExpr@184..207 + ColumnRef@184..194 + String@184..186 + Ident@184..186 "w1" + Ascii46@186..187 "." + String@187..194 + Ident@187..194 "temp_hi" + Whitespace@194..195 " " + String@195..196 + Ascii62@195..196 ">" + Whitespace@196..197 " " + ColumnRef@197..207 + String@197..199 + Ident@197..199 "w2" + Ascii46@199..200 "." + String@200..207 + Ident@200..207 "temp_hi" Ascii59@207..208 ";" - Newline@208..209 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0010.snap b/crates/parser/tests/snapshots/statements/valid/0010.snap index 402b5c07..bef5bc54 100644 --- a/crates/parser/tests/snapshots/statements/valid/0010.snap +++ b/crates/parser/tests/snapshots/statements/valid/0010.snap @@ -2,30 +2,42 @@ source: crates/parser/tests/statement_parser_test.rs description: "INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');\n" --- -InsertStmt@0..74 +InsertStmt@0..73 Insert@0..6 "INSERT" Whitespace@6..7 " " Into@7..11 "INTO" Whitespace@11..12 " " - Ident@12..19 "weather" + RangeVar@12..19 + Ident@12..19 "weather" Whitespace@19..20 " " Values@20..26 "VALUES" Whitespace@26..27 " " Ascii40@27..28 "(" - Sconst@28..43 "'San Francisco'" - Ascii44@43..44 "," - Whitespace@44..45 " " - Iconst@45..47 "46" - Ascii44@47..48 "," - Whitespace@48..49 " " - Iconst@49..51 "50" - Ascii44@51..52 "," - Whitespace@52..53 " " - Fconst@53..57 "0.25" - Ascii44@57..58 "," - Whitespace@58..59 " " - Sconst@59..71 "'1994-11-27'" + SelectStmt@28..71 + List@28..71 + AConst@28..43 + String@28..43 + Sconst@28..43 "'San Francisco'" + Ascii44@43..44 "," + Whitespace@44..45 " " + AConst@45..47 + Integer@45..47 + Iconst@45..47 "46" + Ascii44@47..48 "," + Whitespace@48..49 " " + AConst@49..51 + Integer@49..51 + Iconst@49..51 "50" + Ascii44@51..52 "," + Whitespace@52..53 " " + AConst@53..57 + Float@53..57 + Fconst@53..57 "0.25" + Ascii44@57..58 "," + Whitespace@58..59 " " + AConst@59..71 + String@59..71 + Sconst@59..71 "'1994-11-27'" Ascii41@71..72 ")" Ascii59@72..73 ";" - Newline@73..74 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0011.snap b/crates/parser/tests/snapshots/statements/valid/0011.snap index 48d7b674..2f2bb2d8 100644 --- a/crates/parser/tests/snapshots/statements/valid/0011.snap +++ b/crates/parser/tests/snapshots/statements/valid/0011.snap @@ -2,24 +2,30 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT DISTINCT city\n FROM weather\n ORDER BY city;\n" --- -SelectStmt@0..57 +SelectStmt@0..56 Select@0..6 "SELECT" Whitespace@6..7 " " Distinct@7..15 "DISTINCT" Whitespace@15..16 " " - Ident@16..20 "city" + ResTarget@16..20 + ColumnRef@16..20 + String@16..20 + Ident@16..20 "city" Newline@20..21 "\n" Whitespace@21..25 " " From@25..29 "FROM" Whitespace@29..30 " " - Ident@30..37 "weather" + RangeVar@30..37 + Ident@30..37 "weather" Newline@37..38 "\n" Whitespace@38..42 " " - Order@42..47 "ORDER" - Whitespace@47..48 " " - By@48..50 "BY" - Whitespace@50..51 " " - Ident@51..55 "city" + SortBy@42..55 + Order@42..47 "ORDER" + Whitespace@47..48 " " + By@48..50 "BY" + Whitespace@50..51 " " + ColumnRef@51..55 + String@51..55 + Ident@51..55 "city" Ascii59@55..56 ";" - Newline@56..57 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0012.snap b/crates/parser/tests/snapshots/statements/valid/0012.snap index f4d77ae0..ad8b591b 100644 --- a/crates/parser/tests/snapshots/statements/valid/0012.snap +++ b/crates/parser/tests/snapshots/statements/valid/0012.snap @@ -2,45 +2,54 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE measurement_y2008m01 PARTITION OF measurement\n FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')\n WITH (parallel_workers = 4)\n TABLESPACE fasttablespace;\n" --- -CreateStmt@0..175 +CreateStmt@0..174 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..33 "measurement_y2008m01" + RangeVar@13..33 + Ident@13..33 "measurement_y2008m01" Whitespace@33..34 " " Partition@34..43 "PARTITION" Whitespace@43..44 " " Of@44..46 "OF" Whitespace@46..47 " " - Ident@47..58 "measurement" + RangeVar@47..58 + Ident@47..58 "measurement" Newline@58..59 "\n" Whitespace@59..63 " " For@63..66 "FOR" Whitespace@66..67 " " Values@67..73 "VALUES" Whitespace@73..74 " " - From@74..78 "FROM" - Whitespace@78..79 " " - Ascii40@79..80 "(" - Sconst@80..92 "'2008-01-01'" - Ascii41@92..93 ")" - Whitespace@93..94 " " - To@94..96 "TO" - Whitespace@96..97 " " - Ascii40@97..98 "(" - Sconst@98..110 "'2008-02-01'" - Ascii41@110..111 ")" + PartitionBoundSpec@74..111 + From@74..78 "FROM" + Whitespace@78..79 " " + Ascii40@79..80 "(" + AConst@80..92 + String@80..92 + Sconst@80..92 "'2008-01-01'" + Ascii41@92..93 ")" + Whitespace@93..94 " " + To@94..96 "TO" + Whitespace@96..97 " " + Ascii40@97..98 "(" + AConst@98..110 + String@98..110 + Sconst@98..110 "'2008-02-01'" + Ascii41@110..111 ")" Newline@111..112 "\n" Whitespace@112..116 " " With@116..120 "WITH" Whitespace@120..121 " " Ascii40@121..122 "(" - Ident@122..138 "parallel_workers" - Whitespace@138..139 " " - Ascii61@139..140 "=" - Whitespace@140..141 " " - Iconst@141..142 "4" + DefElem@122..142 + Ident@122..138 "parallel_workers" + Whitespace@138..139 " " + Ascii61@139..140 "=" + Whitespace@140..141 " " + Integer@141..142 + Iconst@141..142 "4" Ascii41@142..143 ")" Newline@143..144 "\n" Whitespace@144..148 " " @@ -48,5 +57,4 @@ CreateStmt@0..175 Whitespace@158..159 " " Ident@159..173 "fasttablespace" Ascii59@173..174 ";" - Newline@174..175 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0013.snap b/crates/parser/tests/snapshots/statements/valid/0013.snap index 643f1e95..031b67df 100644 --- a/crates/parser/tests/snapshots/statements/valid/0013.snap +++ b/crates/parser/tests/snapshots/statements/valid/0013.snap @@ -2,43 +2,63 @@ source: crates/parser/tests/statement_parser_test.rs description: "UPDATE weather\n SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2\n WHERE date > '1994-11-28';\n" --- -UpdateStmt@0..100 +UpdateStmt@0..99 Update@0..6 "UPDATE" Whitespace@6..7 " " - Ident@7..14 "weather" + RangeVar@7..14 + Ident@7..14 "weather" Newline@14..15 "\n" Whitespace@15..19 " " Set@19..22 "SET" Whitespace@22..23 " " - Ident@23..30 "temp_hi" - Whitespace@30..31 " " - Ascii61@31..32 "=" - Whitespace@32..33 " " - Ident@33..40 "temp_hi" - Whitespace@40..41 " " - Ascii45@41..42 "-" - Whitespace@42..43 " " - Iconst@43..44 "2" + ResTarget@23..44 + Ident@23..30 "temp_hi" + Whitespace@30..31 " " + Ascii61@31..32 "=" + Whitespace@32..33 " " + AExpr@33..44 + ColumnRef@33..40 + String@33..40 + Ident@33..40 "temp_hi" + Whitespace@40..41 " " + String@41..42 + Ascii45@41..42 "-" + Whitespace@42..43 " " + AConst@43..44 + Integer@43..44 + Iconst@43..44 "2" Ascii44@44..45 "," Whitespace@45..47 " " - Ident@47..54 "temp_lo" - Whitespace@54..55 " " - Ascii61@55..56 "=" - Whitespace@56..57 " " - Ident@57..64 "temp_lo" - Whitespace@64..65 " " - Ascii45@65..66 "-" - Whitespace@66..67 " " - Iconst@67..68 "2" + ResTarget@47..68 + Ident@47..54 "temp_lo" + Whitespace@54..55 " " + Ascii61@55..56 "=" + Whitespace@56..57 " " + AExpr@57..68 + ColumnRef@57..64 + String@57..64 + Ident@57..64 "temp_lo" + Whitespace@64..65 " " + String@65..66 + Ascii45@65..66 "-" + Whitespace@66..67 " " + AConst@67..68 + Integer@67..68 + Iconst@67..68 "2" Newline@68..69 "\n" Whitespace@69..73 " " Where@73..78 "WHERE" Whitespace@78..79 " " - Ident@79..83 "date" - Whitespace@83..84 " " - Ascii62@84..85 ">" - Whitespace@85..86 " " - Sconst@86..98 "'1994-11-28'" + AExpr@79..98 + ColumnRef@79..83 + String@79..83 + Ident@79..83 "date" + Whitespace@83..84 " " + String@84..85 + Ascii62@84..85 ">" + Whitespace@85..86 " " + AConst@86..98 + String@86..98 + Sconst@86..98 "'1994-11-28'" Ascii59@98..99 ";" - Newline@99..100 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0014.snap b/crates/parser/tests/snapshots/statements/valid/0014.snap index 80ceaf6d..8d760544 100644 --- a/crates/parser/tests/snapshots/statements/valid/0014.snap +++ b/crates/parser/tests/snapshots/statements/valid/0014.snap @@ -2,55 +2,73 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT sum(salary) OVER w, avg(salary) OVER w\n FROM empsalary\n WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);\n" --- -SelectStmt@0..122 +SelectStmt@0..121 Select@0..6 "SELECT" Whitespace@6..7 " " - Ident@7..10 "sum" - Ascii40@10..11 "(" - Ident@11..17 "salary" - Ascii41@17..18 ")" - Whitespace@18..19 " " - Over@19..23 "OVER" - Whitespace@23..24 " " - Ident@24..25 "w" + ResTarget@7..25 + FuncCall@7..25 + String@7..10 + Ident@7..10 "sum" + Ascii40@10..11 "(" + ColumnRef@11..17 + String@11..17 + Ident@11..17 "salary" + Ascii41@17..18 ")" + Whitespace@18..19 " " + WindowDef@19..25 + Over@19..23 "OVER" + Whitespace@23..24 " " + Ident@24..25 "w" Ascii44@25..26 "," Whitespace@26..27 " " - Ident@27..30 "avg" - Ascii40@30..31 "(" - Ident@31..37 "salary" - Ascii41@37..38 ")" - Whitespace@38..39 " " - Over@39..43 "OVER" - Whitespace@43..44 " " - Ident@44..45 "w" + ResTarget@27..45 + FuncCall@27..45 + String@27..30 + Ident@27..30 "avg" + Ascii40@30..31 "(" + ColumnRef@31..37 + String@31..37 + Ident@31..37 "salary" + Ascii41@37..38 ")" + Whitespace@38..39 " " + WindowDef@39..45 + Over@39..43 "OVER" + Whitespace@43..44 " " + Ident@44..45 "w" Newline@45..46 "\n" Whitespace@46..48 " " From@48..52 "FROM" Whitespace@52..53 " " - Ident@53..62 "empsalary" + RangeVar@53..62 + Ident@53..62 "empsalary" Newline@62..63 "\n" Whitespace@63..65 " " - Window@65..71 "WINDOW" - Whitespace@71..72 " " - Ident@72..73 "w" - Whitespace@73..74 " " - As@74..76 "AS" - Whitespace@76..77 " " - Ascii40@77..78 "(" - Partition@78..87 "PARTITION" - Whitespace@87..88 " " - By@88..90 "BY" - Whitespace@90..91 " " - Ident@91..98 "depname" - Whitespace@98..99 " " - Order@99..104 "ORDER" - Whitespace@104..105 " " - By@105..107 "BY" - Whitespace@107..108 " " - Ident@108..114 "salary" - Whitespace@114..115 " " - Desc@115..119 "DESC" - Ascii41@119..120 ")" + WindowDef@65..120 + Window@65..71 "WINDOW" + Whitespace@71..72 " " + Ident@72..73 "w" + Whitespace@73..74 " " + As@74..76 "AS" + Whitespace@76..77 " " + Ascii40@77..78 "(" + Partition@78..87 "PARTITION" + Whitespace@87..88 " " + By@88..90 "BY" + Whitespace@90..91 " " + ColumnRef@91..98 + String@91..98 + Ident@91..98 "depname" + Whitespace@98..99 " " + SortBy@99..119 + Order@99..104 "ORDER" + Whitespace@104..105 " " + By@105..107 "BY" + Whitespace@107..108 " " + ColumnRef@108..114 + String@108..114 + Ident@108..114 "salary" + Whitespace@114..115 " " + Desc@115..119 "DESC" + Ascii41@119..120 ")" Ascii59@120..121 ";" - Newline@121..122 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0015.snap b/crates/parser/tests/snapshots/statements/valid/0015.snap index 0d9223c5..4f6bf7df 100644 --- a/crates/parser/tests/snapshots/statements/valid/0015.snap +++ b/crates/parser/tests/snapshots/statements/valid/0015.snap @@ -2,57 +2,78 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT\n count(*) AS unfiltered,\n count(*) FILTER (WHERE i < 5) AS filtered\nFROM generate_series(1,10) AS s(i);\n" --- -SelectStmt@0..117 +SelectStmt@0..116 Select@0..6 "SELECT" Newline@6..7 "\n" Whitespace@7..11 " " - Ident@11..16 "count" - Ascii40@16..17 "(" - Ascii42@17..18 "*" - Ascii41@18..19 ")" - Whitespace@19..20 " " - As@20..22 "AS" - Whitespace@22..23 " " - Ident@23..33 "unfiltered" + ResTarget@11..33 + FuncCall@11..16 + String@11..16 + Ident@11..16 "count" + Ascii40@16..17 "(" + Ascii42@17..18 "*" + Ascii41@18..19 ")" + Whitespace@19..20 " " + As@20..22 "AS" + Whitespace@22..23 " " + Ident@23..33 "unfiltered" Ascii44@33..34 "," Newline@34..35 "\n" Whitespace@35..39 " " - Ident@39..44 "count" - Ascii40@44..45 "(" - Ascii42@45..46 "*" - Ascii41@46..47 ")" - Whitespace@47..48 " " - Filter@48..54 "FILTER" - Whitespace@54..55 " " - Ascii40@55..56 "(" - Where@56..61 "WHERE" - Whitespace@61..62 " " - Ident@62..63 "i" - Whitespace@63..64 " " - Ascii60@64..65 "<" - Whitespace@65..66 " " - Iconst@66..67 "5" - Ascii41@67..68 ")" - Whitespace@68..69 " " - As@69..71 "AS" - Whitespace@71..72 " " - Ident@72..80 "filtered" + ResTarget@39..80 + FuncCall@39..68 + String@39..44 + Ident@39..44 "count" + Ascii40@44..45 "(" + Ascii42@45..46 "*" + Ascii41@46..47 ")" + Whitespace@47..48 " " + Filter@48..54 "FILTER" + Whitespace@54..55 " " + Ascii40@55..56 "(" + Where@56..61 "WHERE" + Whitespace@61..62 " " + AExpr@62..67 + ColumnRef@62..63 + String@62..63 + Ident@62..63 "i" + Whitespace@63..64 " " + String@64..65 + Ascii60@64..65 "<" + Whitespace@65..66 " " + AConst@66..67 + Integer@66..67 + Iconst@66..67 "5" + Ascii41@67..68 ")" + Whitespace@68..69 " " + As@69..71 "AS" + Whitespace@71..72 " " + Ident@72..80 "filtered" Newline@80..81 "\n" From@81..85 "FROM" Whitespace@85..86 " " - Ident@86..101 "generate_series" - Ascii40@101..102 "(" - Iconst@102..103 "1" - Ascii44@103..104 "," - Iconst@104..106 "10" - Ascii41@106..107 ")" - Whitespace@107..108 " " - As@108..110 "AS" - Whitespace@110..111 " " - Ident@111..112 "s" - Ascii40@112..113 "(" - Ident@113..114 "i" - Ascii41@114..115 ")" + RangeFunction@86..115 + List@86..107 + FuncCall@86..107 + String@86..101 + Ident@86..101 "generate_series" + Ascii40@101..102 "(" + AConst@102..103 + Integer@102..103 + Iconst@102..103 "1" + Ascii44@103..104 "," + AConst@104..106 + Integer@104..106 + Iconst@104..106 "10" + Ascii41@106..107 ")" + Whitespace@107..108 " " + As@108..110 "AS" + Whitespace@110..111 " " + Alias@111..115 + Ident@111..112 "s" + Ascii40@112..113 "(" + String@113..114 + Ident@113..114 "i" + Ascii41@114..115 ")" Ascii59@115..116 ";" - Newline@116..117 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0016.snap b/crates/parser/tests/snapshots/statements/valid/0016.snap index 6306aa10..a93b47ec 100644 --- a/crates/parser/tests/snapshots/statements/valid/0016.snap +++ b/crates/parser/tests/snapshots/statements/valid/0016.snap @@ -2,26 +2,36 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT * FROM tbl WHERE a COLLATE \"C\" > 'foo';\n" --- -SelectStmt@0..47 +SelectStmt@0..46 Select@0..6 "SELECT" Whitespace@6..7 " " - Ascii42@7..8 "*" + ResTarget@7..8 + ColumnRef@7..8 + AStar@7..8 + Ascii42@7..8 "*" Whitespace@8..9 " " From@9..13 "FROM" Whitespace@13..14 " " - Ident@14..17 "tbl" + RangeVar@14..17 + Ident@14..17 "tbl" Whitespace@17..18 " " Where@18..23 "WHERE" Whitespace@23..24 " " - Ident@24..25 "a" - Whitespace@25..26 " " - Collate@26..33 "COLLATE" - Whitespace@33..34 " " - Ident@34..37 "\"C\"" - Whitespace@37..38 " " - Ascii62@38..39 ">" - Whitespace@39..40 " " - Sconst@40..45 "'foo'" + AExpr@24..45 + CollateClause@24..25 + ColumnRef@24..25 + String@24..25 + Ident@24..25 "a" + Whitespace@25..26 " " + Collate@26..33 "COLLATE" + Whitespace@33..34 " " + Ident@34..37 "\"C\"" + Whitespace@37..38 " " + String@38..39 + Ascii62@38..39 ">" + Whitespace@39..40 " " + AConst@40..45 + String@40..45 + Sconst@40..45 "'foo'" Ascii59@45..46 ";" - Newline@46..47 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0017.snap b/crates/parser/tests/snapshots/statements/valid/0017.snap index 6c3f9d52..21dc6c7d 100644 --- a/crates/parser/tests/snapshots/statements/valid/0017.snap +++ b/crates/parser/tests/snapshots/statements/valid/0017.snap @@ -2,41 +2,61 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)\n FROM states;\n" --- -SelectStmt@0..93 +SelectStmt@0..92 Select@0..6 "SELECT" Whitespace@6..7 " " - NameP@7..11 "name" + ResTarget@7..11 + ColumnRef@7..11 + String@7..11 + NameP@7..11 "name" Ascii44@11..12 "," Whitespace@12..13 " " - Ascii40@13..14 "(" - Select@14..20 "SELECT" - Whitespace@20..21 " " - Ident@21..24 "max" - Ascii40@24..25 "(" - Ident@25..28 "pop" - Ascii41@28..29 ")" - Whitespace@29..30 " " - From@30..34 "FROM" - Whitespace@34..35 " " - Ident@35..41 "cities" - Whitespace@41..42 " " - Where@42..47 "WHERE" - Whitespace@47..48 " " - Ident@48..54 "cities" - Ascii46@54..55 "." - Ident@55..60 "state" - Whitespace@60..61 " " - Ascii61@61..62 "=" - Whitespace@62..63 " " - Ident@63..69 "states" - Ascii46@69..70 "." - NameP@70..74 "name" - Ascii41@74..75 ")" + ResTarget@13..75 + SubLink@13..75 + Ascii40@13..14 "(" + SelectStmt@14..74 + Select@14..20 "SELECT" + Whitespace@20..21 " " + ResTarget@21..29 + FuncCall@21..29 + String@21..24 + Ident@21..24 "max" + Ascii40@24..25 "(" + ColumnRef@25..28 + String@25..28 + Ident@25..28 "pop" + Ascii41@28..29 ")" + Whitespace@29..30 " " + From@30..34 "FROM" + Whitespace@34..35 " " + RangeVar@35..41 + Ident@35..41 "cities" + Whitespace@41..42 " " + Where@42..47 "WHERE" + Whitespace@47..48 " " + AExpr@48..74 + ColumnRef@48..60 + String@48..54 + Ident@48..54 "cities" + Ascii46@54..55 "." + String@55..60 + Ident@55..60 "state" + Whitespace@60..61 " " + String@61..62 + Ascii61@61..62 "=" + Whitespace@62..63 " " + ColumnRef@63..74 + String@63..69 + Ident@63..69 "states" + Ascii46@69..70 "." + String@70..74 + NameP@70..74 "name" + Ascii41@74..75 ")" Newline@75..76 "\n" Whitespace@76..80 " " From@80..84 "FROM" Whitespace@84..85 " " - Ident@85..91 "states" + RangeVar@85..91 + Ident@85..91 "states" Ascii59@91..92 ";" - Newline@92..93 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0018.snap b/crates/parser/tests/snapshots/statements/valid/0018.snap index a93ef99c..d8152852 100644 --- a/crates/parser/tests/snapshots/statements/valid/0018.snap +++ b/crates/parser/tests/snapshots/statements/valid/0018.snap @@ -2,21 +2,31 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT ARRAY[1,2,22.7]::integer[];\n" --- -SelectStmt@0..35 +SelectStmt@0..34 Select@0..6 "SELECT" Whitespace@6..7 " " - Array@7..12 "ARRAY" - Ascii91@12..13 "[" - Iconst@13..14 "1" - Ascii44@14..15 "," - Iconst@15..16 "2" - Ascii44@16..17 "," - Fconst@17..21 "22.7" - Ascii93@21..22 "]" - Typecast@22..24 "::" - Integer@24..31 "integer" + ResTarget@7..31 + TypeCast@7..31 + AArrayExpr@7..21 + Array@7..12 "ARRAY" + Ascii91@12..13 "[" + AConst@13..14 + Integer@13..14 + Iconst@13..14 "1" + Ascii44@14..15 "," + AConst@15..16 + Integer@15..16 + Iconst@15..16 "2" + Ascii44@16..17 "," + AConst@17..21 + Float@17..21 + Fconst@17..21 "22.7" + Ascii93@21..22 "]" + Typecast@22..24 "::" + TypeName@24..31 + String@24..31 + Integer@24..31 "integer" Ascii91@31..32 "[" Ascii93@32..33 "]" Ascii59@33..34 ";" - Newline@34..35 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0019.snap b/crates/parser/tests/snapshots/statements/valid/0019.snap index 97f59c8f..ac87dd53 100644 --- a/crates/parser/tests/snapshots/statements/valid/0019.snap +++ b/crates/parser/tests/snapshots/statements/valid/0019.snap @@ -2,33 +2,52 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT CASE WHEN min(employees) > 0\n THEN avg(expenses / employees)\n END\n FROM departments;\n" --- -SelectStmt@0..112 +SelectStmt@0..111 Select@0..6 "SELECT" Whitespace@6..7 " " - Case@7..11 "CASE" - Whitespace@11..12 " " - When@12..16 "WHEN" - Whitespace@16..17 " " - Ident@17..20 "min" - Ascii40@20..21 "(" - Ident@21..30 "employees" - Ascii41@30..31 ")" - Whitespace@31..32 " " - Ascii62@32..33 ">" - Whitespace@33..34 " " - Iconst@34..35 "0" - Newline@35..36 "\n" - Whitespace@36..48 " " - Then@48..52 "THEN" - Whitespace@52..53 " " - Ident@53..56 "avg" - Ascii40@56..57 "(" - Ident@57..65 "expenses" - Whitespace@65..66 " " - Ascii47@66..67 "/" - Whitespace@67..68 " " - Ident@68..77 "employees" - Ascii41@77..78 ")" + ResTarget@7..78 + CaseExpr@7..78 + Case@7..11 "CASE" + Whitespace@11..12 " " + CaseWhen@12..78 + When@12..16 "WHEN" + Whitespace@16..17 " " + AExpr@17..35 + FuncCall@17..31 + String@17..20 + Ident@17..20 "min" + Ascii40@20..21 "(" + ColumnRef@21..30 + String@21..30 + Ident@21..30 "employees" + Ascii41@30..31 ")" + Whitespace@31..32 " " + String@32..33 + Ascii62@32..33 ">" + Whitespace@33..34 " " + AConst@34..35 + Integer@34..35 + Iconst@34..35 "0" + Newline@35..36 "\n" + Whitespace@36..48 " " + Then@48..52 "THEN" + Whitespace@52..53 " " + FuncCall@53..78 + String@53..56 + Ident@53..56 "avg" + Ascii40@56..57 "(" + AExpr@57..77 + ColumnRef@57..65 + String@57..65 + Ident@57..65 "expenses" + Whitespace@65..66 " " + String@66..67 + Ascii47@66..67 "/" + Whitespace@67..68 " " + ColumnRef@68..77 + String@68..77 + Ident@68..77 "employees" + Ascii41@77..78 ")" Newline@78..79 "\n" Whitespace@79..86 " " EndP@86..89 "END" @@ -36,7 +55,7 @@ SelectStmt@0..112 Whitespace@90..94 " " From@94..98 "FROM" Whitespace@98..99 " " - Ident@99..110 "departments" + RangeVar@99..110 + Ident@99..110 "departments" Ascii59@110..111 ";" - Newline@111..112 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0020.snap b/crates/parser/tests/snapshots/statements/valid/0020.snap index f84f0ed9..6a404472 100644 --- a/crates/parser/tests/snapshots/statements/valid/0020.snap +++ b/crates/parser/tests/snapshots/statements/valid/0020.snap @@ -2,47 +2,66 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)\nRETURNS text\nAS\n$$\n SELECT CASE\n WHEN $3 THEN UPPER($1 || ' ' || $2)\n ELSE LOWER($1 || ' ' || $2)\n END;\n$$\nLANGUAGE SQL IMMUTABLE STRICT;\n" --- -CreateFunctionStmt@0..246 +CreateFunctionStmt@0..245 Create@0..6 "CREATE" Whitespace@6..7 " " Function@7..15 "FUNCTION" Whitespace@15..16 " " - Ident@16..37 "concat_lower_or_upper" + String@16..37 + Ident@16..37 "concat_lower_or_upper" Ascii40@37..38 "(" - Ident@38..39 "a" - Whitespace@39..40 " " - TextP@40..44 "text" + FunctionParameter@38..44 + Ident@38..39 "a" + Whitespace@39..40 " " + TypeName@40..44 + String@40..44 + TextP@40..44 "text" Ascii44@44..45 "," Whitespace@45..46 " " - Ident@46..47 "b" - Whitespace@47..48 " " - TextP@48..52 "text" + FunctionParameter@46..52 + Ident@46..47 "b" + Whitespace@47..48 " " + TypeName@48..52 + String@48..52 + TextP@48..52 "text" Ascii44@52..53 "," Whitespace@53..54 " " - Ident@54..63 "uppercase" - Whitespace@63..64 " " - BooleanP@64..71 "boolean" - Whitespace@71..72 " " - Default@72..79 "DEFAULT" - Whitespace@79..80 " " - FalseP@80..85 "false" + FunctionParameter@54..85 + Ident@54..63 "uppercase" + Whitespace@63..64 " " + BooleanP@64..71 "boolean" + Whitespace@71..72 " " + Default@72..79 "DEFAULT" + Whitespace@79..80 " " + AConst@80..85 + Boolean@80..85 + FalseP@80..85 "false" Ascii41@85..86 ")" Newline@86..87 "\n" Returns@87..94 "RETURNS" Whitespace@94..95 " " - TextP@95..99 "text" + TypeName@95..99 + String@95..99 + TextP@95..99 "text" Newline@99..100 "\n" - As@100..102 "AS" - Newline@102..103 "\n" - Sconst@103..214 "$$\n SELECT CASE\n ..." + DefElem@100..214 + As@100..102 "AS" + Newline@102..103 "\n" + List@103..214 + String@103..214 + Sconst@103..214 "$$\n SELECT CASE\n ..." Newline@214..215 "\n" - Language@215..223 "LANGUAGE" - Whitespace@223..224 " " - SqlP@224..227 "SQL" + DefElem@215..227 + Language@215..223 "LANGUAGE" + Whitespace@223..224 " " + String@224..227 + SqlP@224..227 "SQL" Whitespace@227..228 " " - Immutable@228..237 "IMMUTABLE" + DefElem@228..237 + String@228..237 + Immutable@228..237 "IMMUTABLE" Whitespace@237..238 " " - StrictP@238..244 "STRICT" + DefElem@238..244 + StrictP@238..244 "STRICT" Ascii59@244..245 ";" - Newline@245..246 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0021.snap b/crates/parser/tests/snapshots/statements/valid/0021.snap index ec8ea421..805cf921 100644 --- a/crates/parser/tests/snapshots/statements/valid/0021.snap +++ b/crates/parser/tests/snapshots/statements/valid/0021.snap @@ -2,31 +2,42 @@ source: crates/parser/tests/statement_parser_test.rs description: "SELECT concat_lower_or_upper(a => 'Hello', b => 'World', uppercase => true);\n" --- -SelectStmt@0..77 +SelectStmt@0..76 Select@0..6 "SELECT" Whitespace@6..7 " " - Ident@7..28 "concat_lower_or_upper" - Ascii40@28..29 "(" - Ident@29..30 "a" - Whitespace@30..31 " " - EqualsGreater@31..33 "=>" - Whitespace@33..34 " " - Sconst@34..41 "'Hello'" - Ascii44@41..42 "," - Whitespace@42..43 " " - Ident@43..44 "b" - Whitespace@44..45 " " - EqualsGreater@45..47 "=>" - Whitespace@47..48 " " - Sconst@48..55 "'World'" - Ascii44@55..56 "," - Whitespace@56..57 " " - Ident@57..66 "uppercase" - Whitespace@66..67 " " - EqualsGreater@67..69 "=>" - Whitespace@69..70 " " - TrueP@70..74 "true" - Ascii41@74..75 ")" + ResTarget@7..75 + FuncCall@7..75 + String@7..28 + Ident@7..28 "concat_lower_or_upper" + Ascii40@28..29 "(" + NamedArgExpr@29..41 + Ident@29..30 "a" + Whitespace@30..31 " " + EqualsGreater@31..33 "=>" + Whitespace@33..34 " " + AConst@34..41 + String@34..41 + Sconst@34..41 "'Hello'" + Ascii44@41..42 "," + Whitespace@42..43 " " + NamedArgExpr@43..55 + Ident@43..44 "b" + Whitespace@44..45 " " + EqualsGreater@45..47 "=>" + Whitespace@47..48 " " + AConst@48..55 + String@48..55 + Sconst@48..55 "'World'" + Ascii44@55..56 "," + Whitespace@56..57 " " + NamedArgExpr@57..74 + Ident@57..66 "uppercase" + Whitespace@66..67 " " + EqualsGreater@67..69 "=>" + Whitespace@69..70 " " + AConst@70..74 + Boolean@70..74 + TrueP@70..74 "true" + Ascii41@74..75 ")" Ascii59@75..76 ";" - Newline@76..77 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0022.snap b/crates/parser/tests/snapshots/statements/valid/0022.snap index b80610e9..78671f80 100644 --- a/crates/parser/tests/snapshots/statements/valid/0022.snap +++ b/crates/parser/tests/snapshots/statements/valid/0022.snap @@ -2,37 +2,49 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE products (\n product_no integer,\n name text,\n price numeric DEFAULT 9.99\n);\n" --- -CreateStmt@0..97 +CreateStmt@0..96 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..21 "products" + RangeVar@13..21 + Ident@13..21 "products" Whitespace@21..22 " " Ascii40@22..23 "(" Newline@23..24 "\n" Whitespace@24..28 " " - Ident@28..38 "product_no" - Whitespace@38..39 " " - Integer@39..46 "integer" + ColumnDef@28..46 + Ident@28..38 "product_no" + Whitespace@38..39 " " + TypeName@39..46 + String@39..46 + Integer@39..46 "integer" Ascii44@46..47 "," Newline@47..48 "\n" Whitespace@48..52 " " - NameP@52..56 "name" - Whitespace@56..57 " " - TextP@57..61 "text" + ColumnDef@52..61 + NameP@52..56 "name" + Whitespace@56..57 " " + TypeName@57..61 + String@57..61 + TextP@57..61 "text" Ascii44@61..62 "," Newline@62..63 "\n" Whitespace@63..67 " " - Ident@67..72 "price" - Whitespace@72..73 " " - Numeric@73..80 "numeric" - Whitespace@80..81 " " - Default@81..88 "DEFAULT" - Whitespace@88..89 " " - Fconst@89..93 "9.99" + ColumnDef@67..93 + Ident@67..72 "price" + Whitespace@72..73 " " + TypeName@73..80 + String@73..80 + Numeric@73..80 "numeric" + Whitespace@80..81 " " + Constraint@81..93 + Default@81..88 "DEFAULT" + Whitespace@88..89 " " + AConst@89..93 + Float@89..93 + Fconst@89..93 "9.99" Newline@93..94 "\n" Ascii41@94..95 ")" Ascii59@95..96 ";" - Newline@96..97 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0023.snap b/crates/parser/tests/snapshots/statements/valid/0023.snap index 2a8ccc2b..b9af2250 100644 --- a/crates/parser/tests/snapshots/statements/valid/0023.snap +++ b/crates/parser/tests/snapshots/statements/valid/0023.snap @@ -2,71 +2,104 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE products (\n product_no integer,\n name text,\n price numeric CHECK (price > 0),\n discounted_price numeric CHECK (discounted_price > 0),\n CHECK (price > discounted_price)\n);\n" --- -CreateStmt@0..199 +CreateStmt@0..198 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..21 "products" + RangeVar@13..21 + Ident@13..21 "products" Whitespace@21..22 " " Ascii40@22..23 "(" Newline@23..24 "\n" Whitespace@24..28 " " - Ident@28..38 "product_no" - Whitespace@38..39 " " - Integer@39..46 "integer" + ColumnDef@28..46 + Ident@28..38 "product_no" + Whitespace@38..39 " " + TypeName@39..46 + String@39..46 + Integer@39..46 "integer" Ascii44@46..47 "," Newline@47..48 "\n" Whitespace@48..52 " " - NameP@52..56 "name" - Whitespace@56..57 " " - TextP@57..61 "text" + ColumnDef@52..61 + NameP@52..56 "name" + Whitespace@56..57 " " + TypeName@57..61 + String@57..61 + TextP@57..61 "text" Ascii44@61..62 "," Newline@62..63 "\n" Whitespace@63..67 " " - Ident@67..72 "price" - Whitespace@72..73 " " - Numeric@73..80 "numeric" - Whitespace@80..81 " " - Check@81..86 "CHECK" - Whitespace@86..87 " " - Ascii40@87..88 "(" - Ident@88..93 "price" - Whitespace@93..94 " " - Ascii62@94..95 ">" - Whitespace@95..96 " " - Iconst@96..97 "0" - Ascii41@97..98 ")" + ColumnDef@67..98 + Ident@67..72 "price" + Whitespace@72..73 " " + TypeName@73..80 + String@73..80 + Numeric@73..80 "numeric" + Whitespace@80..81 " " + Constraint@81..98 + Check@81..86 "CHECK" + Whitespace@86..87 " " + Ascii40@87..88 "(" + AExpr@88..97 + ColumnRef@88..93 + String@88..93 + Ident@88..93 "price" + Whitespace@93..94 " " + String@94..95 + Ascii62@94..95 ">" + Whitespace@95..96 " " + AConst@96..97 + Integer@96..97 + Iconst@96..97 "0" + Ascii41@97..98 ")" Ascii44@98..99 "," Newline@99..100 "\n" Whitespace@100..104 " " - Ident@104..120 "discounted_price" - Whitespace@120..121 " " - Numeric@121..128 "numeric" - Whitespace@128..129 " " - Check@129..134 "CHECK" - Whitespace@134..135 " " - Ascii40@135..136 "(" - Ident@136..152 "discounted_price" - Whitespace@152..153 " " - Ascii62@153..154 ">" - Whitespace@154..155 " " - Iconst@155..156 "0" - Ascii41@156..157 ")" + ColumnDef@104..157 + Ident@104..120 "discounted_price" + Whitespace@120..121 " " + TypeName@121..128 + String@121..128 + Numeric@121..128 "numeric" + Whitespace@128..129 " " + Constraint@129..157 + Check@129..134 "CHECK" + Whitespace@134..135 " " + Ascii40@135..136 "(" + AExpr@136..156 + ColumnRef@136..152 + String@136..152 + Ident@136..152 "discounted_price" + Whitespace@152..153 " " + String@153..154 + Ascii62@153..154 ">" + Whitespace@154..155 " " + AConst@155..156 + Integer@155..156 + Iconst@155..156 "0" + Ascii41@156..157 ")" Ascii44@157..158 "," Newline@158..159 "\n" Whitespace@159..163 " " - Check@163..168 "CHECK" - Whitespace@168..169 " " - Ascii40@169..170 "(" - Ident@170..175 "price" - Whitespace@175..176 " " - Ascii62@176..177 ">" - Whitespace@177..178 " " - Ident@178..194 "discounted_price" - Ascii41@194..195 ")" + Constraint@163..195 + Check@163..168 "CHECK" + Whitespace@168..169 " " + Ascii40@169..170 "(" + AExpr@170..194 + ColumnRef@170..175 + String@170..175 + Ident@170..175 "price" + Whitespace@175..176 " " + String@176..177 + Ascii62@176..177 ">" + Whitespace@177..178 " " + ColumnRef@178..194 + String@178..194 + Ident@178..194 "discounted_price" + Ascii41@194..195 ")" Newline@195..196 "\n" Ascii41@196..197 ")" Ascii59@197..198 ";" - Newline@198..199 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0024.snap b/crates/parser/tests/snapshots/statements/valid/0024.snap index e4c63589..7e844839 100644 --- a/crates/parser/tests/snapshots/statements/valid/0024.snap +++ b/crates/parser/tests/snapshots/statements/valid/0024.snap @@ -2,54 +2,70 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE order_items (\n product_no integer REFERENCES products,\n order_id integer REFERENCES orders,\n quantity integer,\n PRIMARY KEY (product_no, order_id)\n);\n" --- -CreateStmt@0..175 +CreateStmt@0..174 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..24 "order_items" + RangeVar@13..24 + Ident@13..24 "order_items" Whitespace@24..25 " " Ascii40@25..26 "(" Newline@26..27 "\n" Whitespace@27..31 " " - Ident@31..41 "product_no" - Whitespace@41..42 " " - Integer@42..49 "integer" - Whitespace@49..50 " " - References@50..60 "REFERENCES" - Whitespace@60..61 " " - Ident@61..69 "products" + ColumnDef@31..69 + Ident@31..41 "product_no" + Whitespace@41..42 " " + TypeName@42..49 + String@42..49 + Integer@42..49 "integer" + Whitespace@49..50 " " + Constraint@50..69 + References@50..60 "REFERENCES" + Whitespace@60..61 " " + RangeVar@61..69 + Ident@61..69 "products" Ascii44@69..70 "," Newline@70..71 "\n" Whitespace@71..75 " " - Ident@75..83 "order_id" - Whitespace@83..84 " " - Integer@84..91 "integer" - Whitespace@91..92 " " - References@92..102 "REFERENCES" - Whitespace@102..103 " " - Ident@103..109 "orders" + ColumnDef@75..109 + Ident@75..83 "order_id" + Whitespace@83..84 " " + TypeName@84..91 + String@84..91 + Integer@84..91 "integer" + Whitespace@91..92 " " + Constraint@92..109 + References@92..102 "REFERENCES" + Whitespace@102..103 " " + RangeVar@103..109 + Ident@103..109 "orders" Ascii44@109..110 "," Newline@110..111 "\n" Whitespace@111..115 " " - Ident@115..123 "quantity" - Whitespace@123..124 " " - Integer@124..131 "integer" + ColumnDef@115..131 + Ident@115..123 "quantity" + Whitespace@123..124 " " + TypeName@124..131 + String@124..131 + Integer@124..131 "integer" Ascii44@131..132 "," Newline@132..133 "\n" Whitespace@133..137 " " - Primary@137..144 "PRIMARY" - Whitespace@144..145 " " - Key@145..148 "KEY" - Whitespace@148..149 " " - Ascii40@149..150 "(" - Ident@150..160 "product_no" - Ascii44@160..161 "," - Whitespace@161..162 " " - Ident@162..170 "order_id" - Ascii41@170..171 ")" + Constraint@137..171 + Primary@137..144 "PRIMARY" + Whitespace@144..145 " " + Key@145..148 "KEY" + Whitespace@148..149 " " + Ascii40@149..150 "(" + String@150..160 + Ident@150..160 "product_no" + Ascii44@160..161 "," + Whitespace@161..162 " " + String@162..170 + Ident@162..170 "order_id" + Ascii41@170..171 ")" Newline@171..172 "\n" Ascii41@172..173 ")" Ascii59@173..174 ";" - Newline@174..175 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0025.snap b/crates/parser/tests/snapshots/statements/valid/0025.snap index cc279acf..0b8b6f2a 100644 --- a/crates/parser/tests/snapshots/statements/valid/0025.snap +++ b/crates/parser/tests/snapshots/statements/valid/0025.snap @@ -2,24 +2,30 @@ source: crates/parser/tests/statement_parser_test.rs description: "ALTER TABLE products ADD CHECK (name <> '');\n" --- -AlterTableStmt@0..45 +AlterTableStmt@0..44 Alter@0..5 "ALTER" Whitespace@5..6 " " Table@6..11 "TABLE" Whitespace@11..12 " " - Ident@12..20 "products" + RangeVar@12..20 + Ident@12..20 "products" Whitespace@20..21 " " AddP@21..24 "ADD" Whitespace@24..25 " " - Check@25..30 "CHECK" - Whitespace@30..31 " " - Ascii40@31..32 "(" - NameP@32..36 "name" - Whitespace@36..37 " " - NotEquals@37..39 "<>" + AlterTableCmd@25..39 + Constraint@25..39 + Check@25..30 "CHECK" + Whitespace@30..31 " " + Ascii40@31..32 "(" + AExpr@32..39 + ColumnRef@32..36 + String@32..36 + NameP@32..36 "name" + Whitespace@36..37 " " + String@37..39 + NotEquals@37..39 "<>" Whitespace@39..40 " " Sconst@40..42 "''" Ascii41@42..43 ")" Ascii59@43..44 ";" - Newline@44..45 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0026.snap b/crates/parser/tests/snapshots/statements/valid/0026.snap index 730b2138..a575f9dc 100644 --- a/crates/parser/tests/snapshots/statements/valid/0026.snap +++ b/crates/parser/tests/snapshots/statements/valid/0026.snap @@ -2,27 +2,35 @@ source: crates/parser/tests/statement_parser_test.rs description: "ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);\n" --- -AlterTableStmt@0..60 +AlterTableStmt@0..59 Alter@0..5 "ALTER" Whitespace@5..6 " " Table@6..11 "TABLE" Whitespace@11..12 " " - Ident@12..20 "products" + RangeVar@12..20 + Ident@12..20 "products" Whitespace@20..21 " " Alter@21..26 "ALTER" Whitespace@26..27 " " Column@27..33 "COLUMN" Whitespace@33..34 " " - Ident@34..39 "price" - Whitespace@39..40 " " - TypeP@40..44 "TYPE" - Whitespace@44..45 " " - Numeric@45..52 "numeric" - Ascii40@52..53 "(" - Iconst@53..55 "10" - Ascii44@55..56 "," - Iconst@56..57 "2" - Ascii41@57..58 ")" + AlterTableCmd@34..58 + ColumnDef@34..58 + Ident@34..39 "price" + Whitespace@39..40 " " + TypeP@40..44 "TYPE" + Whitespace@44..45 " " + TypeName@45..58 + String@45..52 + Numeric@45..52 "numeric" + Ascii40@52..53 "(" + AConst@53..55 + Integer@53..55 + Iconst@53..55 "10" + Ascii44@55..56 "," + AConst@56..57 + Integer@56..57 + Iconst@56..57 "2" + Ascii41@57..58 ")" Ascii59@58..59 ";" - Newline@59..60 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0027.snap b/crates/parser/tests/snapshots/statements/valid/0027.snap index 2b88e432..d4354e7d 100644 --- a/crates/parser/tests/snapshots/statements/valid/0027.snap +++ b/crates/parser/tests/snapshots/statements/valid/0027.snap @@ -2,18 +2,20 @@ source: crates/parser/tests/statement_parser_test.rs description: "GRANT UPDATE ON accounts TO joe;\n" --- -GrantStmt@0..33 +GrantStmt@0..32 Grant@0..5 "GRANT" Whitespace@5..6 " " - Update@6..12 "UPDATE" + AccessPriv@6..12 + Update@6..12 "UPDATE" Whitespace@12..13 " " On@13..15 "ON" Whitespace@15..16 " " - Ident@16..24 "accounts" + RangeVar@16..24 + Ident@16..24 "accounts" Whitespace@24..25 " " To@25..27 "TO" Whitespace@27..28 " " - Ident@28..31 "joe" + RoleSpec@28..31 + Ident@28..31 "joe" Ascii59@31..32 ";" - Newline@32..33 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0028.snap b/crates/parser/tests/snapshots/statements/valid/0028.snap index c9ca0372..05a72aba 100644 --- a/crates/parser/tests/snapshots/statements/valid/0028.snap +++ b/crates/parser/tests/snapshots/statements/valid/0028.snap @@ -2,18 +2,18 @@ source: crates/parser/tests/statement_parser_test.rs description: "REVOKE ALL ON accounts FROM PUBLIC;\n" --- -GrantStmt@0..36 +GrantStmt@0..35 Revoke@0..6 "REVOKE" Whitespace@6..7 " " All@7..10 "ALL" Whitespace@10..11 " " On@11..13 "ON" Whitespace@13..14 " " - Ident@14..22 "accounts" + RangeVar@14..22 + Ident@14..22 "accounts" Whitespace@22..23 " " From@23..27 "FROM" Whitespace@27..28 " " Ident@28..34 "PUBLIC" Ascii59@34..35 ";" - Newline@35..36 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0029.snap b/crates/parser/tests/snapshots/statements/valid/0029.snap index ae280f52..4c2bcacc 100644 --- a/crates/parser/tests/snapshots/statements/valid/0029.snap +++ b/crates/parser/tests/snapshots/statements/valid/0029.snap @@ -2,29 +2,34 @@ source: crates/parser/tests/statement_parser_test.rs description: "GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;\n" --- -GrantStmt@0..60 +GrantStmt@0..59 Grant@0..5 "GRANT" Whitespace@5..6 " " - Select@6..12 "SELECT" - Whitespace@12..13 " " - Ascii40@13..14 "(" - Ident@14..18 "col1" - Ascii41@18..19 ")" + AccessPriv@6..19 + Select@6..12 "SELECT" + Whitespace@12..13 " " + Ascii40@13..14 "(" + String@14..18 + Ident@14..18 "col1" + Ascii41@18..19 ")" Ascii44@19..20 "," Whitespace@20..21 " " - Update@21..27 "UPDATE" - Whitespace@27..28 " " - Ascii40@28..29 "(" - Ident@29..33 "col1" - Ascii41@33..34 ")" + AccessPriv@21..34 + Update@21..27 "UPDATE" + Whitespace@27..28 " " + Ascii40@28..29 "(" + String@29..33 + Ident@29..33 "col1" + Ascii41@33..34 ")" Whitespace@34..35 " " On@35..37 "ON" Whitespace@37..38 " " - Ident@38..45 "mytable" + RangeVar@38..45 + Ident@38..45 "mytable" Whitespace@45..46 " " To@46..48 "TO" Whitespace@48..49 " " - Ident@49..58 "miriam_rw" + RoleSpec@49..58 + Ident@49..58 "miriam_rw" Ascii59@58..59 ";" - Newline@59..60 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0030.snap b/crates/parser/tests/snapshots/statements/valid/0030.snap index 68c40f34..e3f2bb1e 100644 --- a/crates/parser/tests/snapshots/statements/valid/0030.snap +++ b/crates/parser/tests/snapshots/statements/valid/0030.snap @@ -2,7 +2,7 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE POLICY account_managers ON accounts TO managers\n USING (manager = current_user);\n" --- -CreatePolicyStmt@0..91 +CreatePolicyStmt@0..90 Create@0..6 "CREATE" Whitespace@6..7 " " Policy@7..13 "POLICY" @@ -11,22 +11,28 @@ CreatePolicyStmt@0..91 Whitespace@30..31 " " On@31..33 "ON" Whitespace@33..34 " " - Ident@34..42 "accounts" + RangeVar@34..42 + Ident@34..42 "accounts" Whitespace@42..43 " " To@43..45 "TO" Whitespace@45..46 " " - Ident@46..54 "managers" + RoleSpec@46..54 + Ident@46..54 "managers" Newline@54..55 "\n" Whitespace@55..59 " " Using@59..64 "USING" Whitespace@64..65 " " Ascii40@65..66 "(" - Ident@66..73 "manager" - Whitespace@73..74 " " - Ascii61@74..75 "=" - Whitespace@75..76 " " - CurrentUser@76..88 "current_user" + AExpr@66..88 + ColumnRef@66..73 + String@66..73 + Ident@66..73 "manager" + Whitespace@73..74 " " + String@74..75 + Ascii61@74..75 "=" + Whitespace@75..76 " " + SqlvalueFunction@76..88 + CurrentUser@76..88 "current_user" Ascii41@88..89 ")" Ascii59@89..90 ";" - Newline@90..91 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0031.snap b/crates/parser/tests/snapshots/statements/valid/0031.snap index f3456e11..42afd22e 100644 --- a/crates/parser/tests/snapshots/statements/valid/0031.snap +++ b/crates/parser/tests/snapshots/statements/valid/0031.snap @@ -2,7 +2,7 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE POLICY user_mod ON passwd FOR UPDATE\n USING (current_user = user_name)\n WITH CHECK (\n current_user = user_name AND\n shell IN ('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/tcsh')\n );\n" --- -CreatePolicyStmt@0..204 +CreatePolicyStmt@0..203 Create@0..6 "CREATE" Whitespace@6..7 " " Policy@7..13 "POLICY" @@ -11,7 +11,8 @@ CreatePolicyStmt@0..204 Whitespace@22..23 " " On@23..25 "ON" Whitespace@25..26 " " - Ident@26..32 "passwd" + RangeVar@26..32 + Ident@26..32 "passwd" Whitespace@32..33 " " For@33..36 "FOR" Whitespace@36..37 " " @@ -21,48 +22,72 @@ CreatePolicyStmt@0..204 Using@46..51 "USING" Whitespace@51..52 " " Ascii40@52..53 "(" - CurrentUser@53..65 "current_user" - Whitespace@65..66 " " - Ascii61@66..67 "=" - Whitespace@67..68 " " - Ident@68..77 "user_name" - Ascii41@77..78 ")" - Newline@78..79 "\n" - Whitespace@79..81 " " - With@81..85 "WITH" - Whitespace@85..86 " " - Check@86..91 "CHECK" - Whitespace@91..92 " " - Ascii40@92..93 "(" - Newline@93..94 "\n" - Whitespace@94..98 " " - CurrentUser@98..110 "current_user" - Whitespace@110..111 " " - Ascii61@111..112 "=" - Whitespace@112..113 " " - Ident@113..122 "user_name" - Whitespace@122..123 " " - And@123..126 "AND" - Newline@126..127 "\n" - Whitespace@127..131 " " - Ident@131..136 "shell" - Whitespace@136..137 " " - InP@137..139 "IN" - Whitespace@139..140 " " - Ascii40@140..141 "(" - Sconst@141..152 "'/bin/bash'" - Ascii44@152..153 "," - Sconst@153..162 "'/bin/sh'" - Ascii44@162..163 "," - Sconst@163..174 "'/bin/dash'" - Ascii44@174..175 "," - Sconst@175..185 "'/bin/zsh'" - Ascii44@185..186 "," - Sconst@186..197 "'/bin/tcsh'" - Ascii41@197..198 ")" - Newline@198..199 "\n" - Whitespace@199..201 " " - Ascii41@201..202 ")" + AExpr@53..202 + SqlvalueFunction@53..65 + CurrentUser@53..65 "current_user" + Whitespace@65..66 " " + BoolExpr@66..202 + AExpr@66..202 + String@66..67 + Ascii61@66..67 "=" + Whitespace@67..68 " " + ColumnRef@68..77 + String@68..77 + Ident@68..77 "user_name" + Ascii41@77..78 ")" + Newline@78..79 "\n" + Whitespace@79..81 " " + With@81..85 "WITH" + Whitespace@85..86 " " + Check@86..91 "CHECK" + Whitespace@91..92 " " + Ascii40@92..93 "(" + Newline@93..94 "\n" + Whitespace@94..98 " " + SqlvalueFunction@98..110 + CurrentUser@98..110 "current_user" + Whitespace@110..111 " " + AExpr@111..202 + String@111..112 + Ascii61@111..112 "=" + Whitespace@112..113 " " + ColumnRef@113..122 + String@113..122 + Ident@113..122 "user_name" + Whitespace@122..123 " " + And@123..126 "AND" + Newline@126..127 "\n" + Whitespace@127..131 " " + ColumnRef@131..136 + String@131..136 + Ident@131..136 "shell" + Whitespace@136..137 " " + InP@137..139 "IN" + Whitespace@139..140 " " + Ascii40@140..141 "(" + List@141..197 + AConst@141..152 + String@141..152 + Sconst@141..152 "'/bin/bash'" + Ascii44@152..153 "," + AConst@153..162 + String@153..162 + Sconst@153..162 "'/bin/sh'" + Ascii44@162..163 "," + AConst@163..174 + String@163..174 + Sconst@163..174 "'/bin/dash'" + Ascii44@174..175 "," + AConst@175..185 + String@175..185 + Sconst@175..185 "'/bin/zsh'" + Ascii44@185..186 "," + AConst@186..197 + String@186..197 + Sconst@186..197 "'/bin/tcsh'" + Ascii41@197..198 ")" + Newline@198..199 "\n" + Whitespace@199..201 " " + Ascii41@201..202 ")" Ascii59@202..203 ";" - Newline@203..204 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0032.snap b/crates/parser/tests/snapshots/statements/valid/0032.snap index d82132b6..772fbfbc 100644 --- a/crates/parser/tests/snapshots/statements/valid/0032.snap +++ b/crates/parser/tests/snapshots/statements/valid/0032.snap @@ -2,16 +2,19 @@ source: crates/parser/tests/statement_parser_test.rs description: "SET search_path TO myschema,public;\n" --- -VariableSetStmt@0..36 +VariableSetStmt@0..35 Set@0..3 "SET" Whitespace@3..4 " " Ident@4..15 "search_path" Whitespace@15..16 " " To@16..18 "TO" Whitespace@18..19 " " - Ident@19..27 "myschema" + AConst@19..27 + String@19..27 + Ident@19..27 "myschema" Ascii44@27..28 "," - Ident@28..34 "public" + AConst@28..34 + String@28..34 + Ident@28..34 "public" Ascii59@34..35 ";" - Newline@35..36 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0033.snap b/crates/parser/tests/snapshots/statements/valid/0033.snap index dbef84a2..f871cd80 100644 --- a/crates/parser/tests/snapshots/statements/valid/0033.snap +++ b/crates/parser/tests/snapshots/statements/valid/0033.snap @@ -2,19 +2,23 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE TABLE measurement (\n city_id int not null,\n logdate date not null,\n peaktemp int,\n unitsales int\n) PARTITION BY RANGE (logdate);\n" --- -CreateStmt@0..177 +CreateStmt@0..176 Create@0..6 "CREATE" Whitespace@6..7 " " Table@7..12 "TABLE" Whitespace@12..13 " " - Ident@13..24 "measurement" + RangeVar@13..24 + Ident@13..24 "measurement" Whitespace@24..25 " " Ascii40@25..26 "(" Newline@26..27 "\n" Whitespace@27..31 " " - Ident@31..38 "city_id" - Whitespace@38..47 " " - IntP@47..50 "int" + ColumnDef@31..50 + Ident@31..38 "city_id" + Whitespace@38..47 " " + TypeName@47..50 + String@47..50 + IntP@47..50 "int" Whitespace@50..51 " " Not@51..54 "not" Whitespace@54..55 " " @@ -22,9 +26,12 @@ CreateStmt@0..177 Ascii44@59..60 "," Newline@60..61 "\n" Whitespace@61..65 " " - Ident@65..72 "logdate" - Whitespace@72..81 " " - Ident@81..85 "date" + ColumnDef@65..85 + Ident@65..72 "logdate" + Whitespace@72..81 " " + TypeName@81..85 + String@81..85 + Ident@81..85 "date" Whitespace@85..86 " " Not@86..89 "not" Whitespace@89..90 " " @@ -32,27 +39,34 @@ CreateStmt@0..177 Ascii44@94..95 "," Newline@95..96 "\n" Whitespace@96..100 " " - Ident@100..108 "peaktemp" - Whitespace@108..116 " " - IntP@116..119 "int" + ColumnDef@100..119 + Ident@100..108 "peaktemp" + Whitespace@108..116 " " + TypeName@116..119 + String@116..119 + IntP@116..119 "int" Ascii44@119..120 "," Newline@120..121 "\n" Whitespace@121..125 " " - Ident@125..134 "unitsales" - Whitespace@134..141 " " - IntP@141..144 "int" + ColumnDef@125..144 + Ident@125..134 "unitsales" + Whitespace@134..141 " " + TypeName@141..144 + String@141..144 + IntP@141..144 "int" Newline@144..145 "\n" Ascii41@145..146 ")" Whitespace@146..147 " " - Partition@147..156 "PARTITION" - Whitespace@156..157 " " - By@157..159 "BY" - Whitespace@159..160 " " - Range@160..165 "RANGE" - Whitespace@165..166 " " - Ascii40@166..167 "(" - Ident@167..174 "logdate" - Ascii41@174..175 ")" + PartitionSpec@147..175 + Partition@147..156 "PARTITION" + Whitespace@156..157 " " + By@157..159 "BY" + Whitespace@159..160 " " + Range@160..165 "RANGE" + Whitespace@165..166 " " + Ascii40@166..167 "(" + PartitionElem@167..174 + Ident@167..174 "logdate" + Ascii41@174..175 ")" Ascii59@175..176 ";" - Newline@176..177 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0034.snap b/crates/parser/tests/snapshots/statements/valid/0034.snap index efede445..356bacab 100644 --- a/crates/parser/tests/snapshots/statements/valid/0034.snap +++ b/crates/parser/tests/snapshots/statements/valid/0034.snap @@ -2,24 +2,36 @@ source: crates/parser/tests/statement_parser_test.rs description: "select *,some_col from contact where id = '123 4 5';\n" --- -SelectStmt@0..53 +SelectStmt@0..52 Select@0..6 "select" Whitespace@6..7 " " - Ascii42@7..8 "*" + ResTarget@7..8 + ColumnRef@7..8 + AStar@7..8 + Ascii42@7..8 "*" Ascii44@8..9 "," - Ident@9..17 "some_col" + ResTarget@9..17 + ColumnRef@9..17 + String@9..17 + Ident@9..17 "some_col" Whitespace@17..18 " " From@18..22 "from" Whitespace@22..23 " " - Ident@23..30 "contact" + RangeVar@23..30 + Ident@23..30 "contact" Whitespace@30..31 " " Where@31..36 "where" Whitespace@36..37 " " - Ident@37..39 "id" - Whitespace@39..40 " " - Ascii61@40..41 "=" - Whitespace@41..42 " " - Sconst@42..51 "'123 4 5'" + AExpr@37..51 + ColumnRef@37..39 + String@37..39 + Ident@37..39 "id" + Whitespace@39..40 " " + String@40..41 + Ascii61@40..41 "=" + Whitespace@41..42 " " + AConst@42..51 + String@42..51 + Sconst@42..51 "'123 4 5'" Ascii59@51..52 ";" - Newline@52..53 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0035.snap b/crates/parser/tests/snapshots/statements/valid/0035.snap index efede445..356bacab 100644 --- a/crates/parser/tests/snapshots/statements/valid/0035.snap +++ b/crates/parser/tests/snapshots/statements/valid/0035.snap @@ -2,24 +2,36 @@ source: crates/parser/tests/statement_parser_test.rs description: "select *,some_col from contact where id = '123 4 5';\n" --- -SelectStmt@0..53 +SelectStmt@0..52 Select@0..6 "select" Whitespace@6..7 " " - Ascii42@7..8 "*" + ResTarget@7..8 + ColumnRef@7..8 + AStar@7..8 + Ascii42@7..8 "*" Ascii44@8..9 "," - Ident@9..17 "some_col" + ResTarget@9..17 + ColumnRef@9..17 + String@9..17 + Ident@9..17 "some_col" Whitespace@17..18 " " From@18..22 "from" Whitespace@22..23 " " - Ident@23..30 "contact" + RangeVar@23..30 + Ident@23..30 "contact" Whitespace@30..31 " " Where@31..36 "where" Whitespace@36..37 " " - Ident@37..39 "id" - Whitespace@39..40 " " - Ascii61@40..41 "=" - Whitespace@41..42 " " - Sconst@42..51 "'123 4 5'" + AExpr@37..51 + ColumnRef@37..39 + String@37..39 + Ident@37..39 "id" + Whitespace@39..40 " " + String@40..41 + Ascii61@40..41 "=" + Whitespace@41..42 " " + AConst@42..51 + String@42..51 + Sconst@42..51 "'123 4 5'" Ascii59@51..52 ";" - Newline@52..53 "\n" diff --git a/crates/parser/tests/snapshots/statements/valid/0036.snap b/crates/parser/tests/snapshots/statements/valid/0036.snap index 56b13946..2f52a5d4 100644 --- a/crates/parser/tests/snapshots/statements/valid/0036.snap +++ b/crates/parser/tests/snapshots/statements/valid/0036.snap @@ -2,41 +2,55 @@ source: crates/parser/tests/statement_parser_test.rs description: "CREATE FUNCTION dup(in int, out f1 int, out f2 text)\n AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$\n LANGUAGE SQL;\n" --- -CreateFunctionStmt@0..126 +CreateFunctionStmt@0..125 Create@0..6 "CREATE" Whitespace@6..7 " " Function@7..15 "FUNCTION" Whitespace@15..16 " " - Ident@16..19 "dup" + String@16..19 + Ident@16..19 "dup" Ascii40@19..20 "(" InP@20..22 "in" Whitespace@22..23 " " - IntP@23..26 "int" + FunctionParameter@23..26 + TypeName@23..26 + String@23..26 + IntP@23..26 "int" Ascii44@26..27 "," Whitespace@27..28 " " OutP@28..31 "out" Whitespace@31..32 " " - Ident@32..34 "f1" - Whitespace@34..35 " " - IntP@35..38 "int" + FunctionParameter@32..38 + Ident@32..34 "f1" + Whitespace@34..35 " " + TypeName@35..38 + String@35..38 + IntP@35..38 "int" Ascii44@38..39 "," Whitespace@39..40 " " OutP@40..43 "out" Whitespace@43..44 " " - Ident@44..46 "f2" - Whitespace@46..47 " " - TextP@47..51 "text" + FunctionParameter@44..51 + Ident@44..46 "f2" + Whitespace@46..47 " " + TypeName@47..51 + String@47..51 + TextP@47..51 "text" Ascii41@51..52 ")" Newline@52..53 "\n" Whitespace@53..57 " " - As@57..59 "AS" - Whitespace@59..60 " " - Sconst@60..107 "$$ SELECT $1, CAST($1 ..." + DefElem@57..107 + As@57..59 "AS" + Whitespace@59..60 " " + List@60..107 + String@60..107 + Sconst@60..107 "$$ SELECT $1, CAST($1 ..." Newline@107..108 "\n" Whitespace@108..112 " " - Language@112..120 "LANGUAGE" - Whitespace@120..121 " " - SqlP@121..124 "SQL" + DefElem@112..124 + Language@112..120 "LANGUAGE" + Whitespace@120..121 " " + String@121..124 + SqlP@121..124 "SQL" Ascii59@124..125 ";" - Newline@125..126 "\n" diff --git a/crates/parser/tests/statement_parser_test.rs b/crates/parser/tests/statement_parser_test.rs index 4eef7303..7ffbaf56 100644 --- a/crates/parser/tests/statement_parser_test.rs +++ b/crates/parser/tests/statement_parser_test.rs @@ -1,6 +1,7 @@ use std::fs; mod common; use insta; +use log::debug; use parser::Parser; const VALID_STATEMENTS_PATH: &str = "tests/data/statements/valid/"; @@ -22,8 +23,10 @@ fn valid_statements() { let contents = fs::read_to_string(&path).unwrap(); + debug!("Parsing statement: {}", test_name); + let mut parser = Parser::new(); - parser.parse_statement(&contents, None); + parser.parse_statement_at(&contents, None); let parsed = parser.finish(); let mut settings = insta::Settings::clone_current();