| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| //! SQL Parser | |
| #[cfg(not(feature = "std"))] | |
| use alloc::{ | |
| boxed::Box, | |
| collections::{BTreeMap, BTreeSet}, | |
| format, | |
| string::{String, ToString}, | |
| vec, | |
| vec::Vec, | |
| }; | |
| use core::{ | |
| fmt::{self, Display}, | |
| str::FromStr, | |
| }; | |
| #[cfg(feature = "std")] | |
| use std::collections::{BTreeMap, BTreeSet}; | |
| use helpers::attached_token::AttachedToken; | |
| use log::debug; | |
| use recursion::RecursionCounter; | |
| use IsLateral::*; | |
| use IsOptional::*; | |
| use crate::ast::*; | |
| use crate::ast::{ | |
| comments, | |
| helpers::{ | |
| key_value_options::{ | |
| KeyValueOption, KeyValueOptionKind, KeyValueOptions, KeyValueOptionsDelimiter, | |
| }, | |
| stmt_create_table::{CreateTableBuilder, CreateTableConfiguration}, | |
| }, | |
| }; | |
| use crate::dialect::*; | |
| use crate::keywords::{Keyword, ALL_KEYWORDS}; | |
| use crate::tokenizer::*; | |
| use sqlparser::parser::ParserState::ColumnDefinition; | |
| /// Errors produced by the SQL parser. | |
| #[derive(Debug, Clone, PartialEq, Eq)] | |
| pub enum ParserError { | |
| /// Error originating from the tokenizer with a message. | |
| TokenizerError(String), | |
| /// Generic parser error with a message. | |
| ParserError(String), | |
| /// Raised when a recursion depth limit is exceeded. | |
| RecursionLimitExceeded, | |
| } | |
| // Use `Parser::expected` instead, if possible | |
| macro_rules! parser_err { | |
| ($MSG:expr, $loc:expr) => { | |
| Err(ParserError::ParserError(format!("{}{}", $MSG, $loc))) | |
| }; | |
| } | |
| mod alter; | |
| mod merge; | |
| /// Implementation of [`RecursionCounter`]. | |
| /// | |
| /// Explicitly requires only `alloc` and `core`, so recursion is limited even | |
| /// when the "std" feature is disabled. | |
| mod recursion { | |
| use alloc::rc::Rc; | |
| use core::cell::Cell; | |
| use super::ParserError; | |
| /// Tracks remaining recursion depth. This value is decremented on | |
| /// each call to [`RecursionCounter::try_decrease()`], when it reaches 0 an error will | |
| /// be returned. | |
| /// | |
| /// Note: Uses an [`alloc::rc::Rc`] and [`core::cell::Cell`] in order to satisfy the Rust | |
| /// borrow checker so the automatic [`DepthGuard`] decrement a | |
| /// reference to the counter. | |
| /// | |
| /// Note: when "recursive-protection" feature is enabled, this crate uses additional stack overflow protection | |
| /// for some of its recursive methods. See [`recursive::recursive`] for more information. | |
| pub(crate) struct RecursionCounter { | |
| remaining_depth: Rc<Cell<usize>>, | |
| } | |
| impl RecursionCounter { | |
| /// Creates a [`RecursionCounter`] with the specified maximum | |
| /// depth | |
| pub fn new(remaining_depth: usize) -> Self { | |
| Self { | |
| remaining_depth: Rc::new(remaining_depth.into()), | |
| } | |
| } | |
| /// Decreases the remaining depth by 1. | |
| /// | |
| /// Returns [`Err`] if the remaining depth falls to 0. | |
| /// | |
| /// Returns a [`DepthGuard`] which will adds 1 to the | |
| /// remaining depth upon drop; | |
| pub fn try_decrease(&self) -> Result<DepthGuard, ParserError> { | |
| let old_value = self.remaining_depth.get(); | |
| // ran out of space | |
| if old_value == 0 { | |
| Err(ParserError::RecursionLimitExceeded) | |
| } else { | |
| self.remaining_depth.set(old_value - 1); | |
| Ok(DepthGuard::new(Rc::clone(&self.remaining_depth))) | |
| } | |
| } | |
| } | |
| /// Guard that increases the remaining depth by 1 on drop | |
| pub struct DepthGuard { | |
| remaining_depth: Rc<Cell<usize>>, | |
| } | |
| impl DepthGuard { | |
| fn new(remaining_depth: Rc<Cell<usize>>) -> Self { | |
| Self { remaining_depth } | |
| } | |
| } | |
| impl Drop for DepthGuard { | |
| fn drop(&mut self) { | |
| let old_value = self.remaining_depth.get(); | |
| self.remaining_depth.set(old_value.saturating_add(1)); | |
| } | |
| } | |
| } | |
| #[derive(PartialEq, Eq)] | |
| /// Indicates whether a parser element is optional or mandatory. | |
| pub enum IsOptional { | |
| /// The element is optional. | |
| Optional, | |
| /// The element is mandatory. | |
| Mandatory, | |
| } | |
| /// Indicates if a table expression is lateral. | |
| pub enum IsLateral { | |
| /// The expression is lateral. | |
| Lateral, | |
| /// The expression is not lateral. | |
| NotLateral, | |
| } | |
| /// Represents a wildcard expression used in SELECT lists. | |
| pub enum WildcardExpr { | |
| /// A specific expression used instead of a wildcard. | |
| Expr(Expr), | |
| /// A qualified wildcard like `table.*`. | |
| QualifiedWildcard(ObjectName), | |
| /// An unqualified `*` wildcard. | |
| Wildcard, | |
| } | |
| impl From<TokenizerError> for ParserError { | |
| fn from(e: TokenizerError) -> Self { | |
| ParserError::TokenizerError(e.to_string()) | |
| } | |
| } | |
| impl fmt::Display for ParserError { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!( | |
| f, | |
| "sql parser error: {}", | |
| match self { | |
| ParserError::TokenizerError(s) => s, | |
| ParserError::ParserError(s) => s, | |
| ParserError::RecursionLimitExceeded => "recursion limit exceeded", | |
| } | |
| ) | |
| } | |
| } | |
| impl core::error::Error for ParserError {} | |
| // By default, allow expressions up to this deep before erroring | |
| const DEFAULT_REMAINING_DEPTH: usize = 50; | |
| // A constant EOF token that can be referenced. | |
| const EOF_TOKEN: TokenWithSpan = TokenWithSpan { | |
| token: Token::EOF, | |
| span: Span { | |
| start: Location { line: 0, column: 0 }, | |
| end: Location { line: 0, column: 0 }, | |
| }, | |
| }; | |
| /// Composite types declarations using angle brackets syntax can be arbitrary | |
| /// nested such that the following declaration is possible: | |
| /// `ARRAY<ARRAY<INT>>` | |
| /// But the tokenizer recognizes the `>>` as a ShiftRight token. | |
| /// We work around that limitation when parsing a data type by accepting | |
| /// either a `>` or `>>` token in such cases, remembering which variant we | |
| /// matched. | |
| /// In the latter case having matched a `>>`, the parent type will not look to | |
| /// match its closing `>` as a result since that will have taken place at the | |
| /// child type. | |
| /// | |
| /// See [Parser::parse_data_type] for details | |
| struct MatchedTrailingBracket(bool); | |
| impl From<bool> for MatchedTrailingBracket { | |
| fn from(value: bool) -> Self { | |
| Self(value) | |
| } | |
| } | |
| /// Options that control how the [`Parser`] parses SQL text | |
| #[derive(Debug, Clone, PartialEq, Eq)] | |
| pub struct ParserOptions { | |
| /// Allow trailing commas in lists (e.g. `a, b,`). | |
| pub trailing_commas: bool, | |
| /// Controls how literal values are unescaped. See | |
| /// [`Tokenizer::with_unescape`] for more details. | |
| pub unescape: bool, | |
| /// Controls if the parser expects a semi-colon token | |
| /// between statements. Default is `true`. | |
| pub require_semicolon_stmt_delimiter: bool, | |
| } | |
| impl Default for ParserOptions { | |
| fn default() -> Self { | |
| Self { | |
| trailing_commas: false, | |
| unescape: true, | |
| require_semicolon_stmt_delimiter: true, | |
| } | |
| } | |
| } | |
| impl ParserOptions { | |
| /// Create a new [`ParserOptions`] | |
| pub fn new() -> Self { | |
| Default::default() | |
| } | |
| /// Set if trailing commas are allowed. | |
| /// | |
| /// If this option is `false` (the default), the following SQL will | |
| /// not parse. If the option is `true`, the SQL will parse. | |
| /// | |
| /// ```sql | |
| /// SELECT | |
| /// foo, | |
| /// bar, | |
| /// FROM baz | |
| /// ``` | |
| pub fn with_trailing_commas(mut self, trailing_commas: bool) -> Self { | |
| self.trailing_commas = trailing_commas; | |
| self | |
| } | |
| /// Set if literal values are unescaped. Defaults to true. See | |
| /// [`Tokenizer::with_unescape`] for more details. | |
| pub fn with_unescape(mut self, unescape: bool) -> Self { | |
| self.unescape = unescape; | |
| self | |
| } | |
| } | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | |
| enum ParserState { | |
| /// The default state of the parser. | |
| Normal, | |
| /// The state when parsing a CONNECT BY expression. This allows parsing | |
| /// PRIOR expressions while still allowing prior as an identifier name | |
| /// in other contexts. | |
| ConnectBy, | |
| /// The state when parsing column definitions. This state prohibits | |
| /// NOT NULL as an alias for IS NOT NULL. For example: | |
| /// ```sql | |
| /// CREATE TABLE foo (abc BIGINT NOT NULL); | |
| /// ``` | |
| ColumnDefinition, | |
| } | |
| /// A SQL Parser | |
| /// | |
| /// This struct is the main entry point for parsing SQL queries. | |
| /// | |
| /// # Functionality: | |
| /// * Parsing SQL: see examples on [`Parser::new`] and [`Parser::parse_sql`] | |
| /// * Controlling recursion: See [`Parser::with_recursion_limit`] | |
| /// * Controlling parser options: See [`Parser::with_options`] | |
| /// * Providing your own tokens: See [`Parser::with_tokens`] | |
| /// | |
| /// # Internals | |
| /// | |
| /// The parser uses a [`Tokenizer`] to tokenize the input SQL string into a | |
| /// `Vec` of [`TokenWithSpan`]s and maintains an `index` to the current token | |
| /// being processed. The token vec may contain multiple SQL statements. | |
| /// | |
| /// * The "current" token is the token at `index - 1` | |
| /// * The "next" token is the token at `index` | |
| /// * The "previous" token is the token at `index - 2` | |
| /// | |
| /// If `index` is equal to the length of the token stream, the 'next' token is | |
| /// [`Token::EOF`]. | |
| /// | |
| /// For example, the SQL string "SELECT * FROM foo" will be tokenized into | |
| /// following tokens: | |
| /// ```text | |
| /// [ | |
| /// "SELECT", // token index 0 | |
| /// " ", // whitespace | |
| /// "*", | |
| /// " ", | |
| /// "FROM", | |
| /// " ", | |
| /// "foo" | |
| /// ] | |
| /// ``` | |
| /// | |
| /// | |
| pub struct Parser<'a> { | |
| /// The tokens | |
| tokens: Vec<TokenWithSpan>, | |
| /// The index of the first unprocessed token in [`Parser::tokens`]. | |
| index: usize, | |
| /// The current state of the parser. | |
| state: ParserState, | |
| /// The SQL dialect to use. | |
| dialect: &'a dyn Dialect, | |
| /// Additional options that allow you to mix & match behavior | |
| /// otherwise constrained to certain dialects (e.g. trailing | |
| /// commas) and/or format of parse (e.g. unescaping). | |
| options: ParserOptions, | |
| /// Ensures the stack does not overflow by limiting recursion depth. | |
| recursion_counter: RecursionCounter, | |
| /// Cached failures from `parse_prefix` calls that returned `Err`. See | |
| /// [`Parser::parse_prefix`] for the 2^N patterns this guards. | |
| failed_prefix_positions: BTreeMap<usize, ExprPrefixError>, | |
| /// Cached failures from the speculative reserved-word prefix arm. See | |
| /// [`Parser::parse_prefix`] for the 2^N patterns this guards. | |
| failed_reserved_word_prefix_positions: BTreeMap<usize, ExprPrefixError>, | |
| /// Cached failures from the speculative derived-table arm of | |
| /// `parse_table_factor`. See [`Parser::parse_table_factor`] for the 2^N | |
| /// pattern this guards. | |
| failed_derived_table_factor_positions: BTreeSet<usize>, | |
| /// Cached failures of the speculative `POSITION(<expr> IN <expr>)` arm. | |
| /// See [`Parser::parse_position_expr`] for the 2^N pattern this guards. | |
| /// Keyed by state too, since `PRIOR` parses differently in `CONNECT BY`. | |
| failed_position_syntax_positions: BTreeSet<(usize, ParserState)>, | |
| /// Cached failures from the speculative subquery arm of [`Parser::parse_in`], | |
| /// which `IN` lists nested in a leading subquery would otherwise retry at | |
| /// every level, taking 2^N time. Maps the arm's start to where a parsed | |
| /// query lacked its `)`. | |
| failed_in_subquery_positions: BTreeMap<usize, Option<usize>>, | |
| } | |
| /// Copy marker for a [`ParserError`] cached by the `parse_prefix` failure | |
| /// memoization, so the caches hold no strings. | |
| #[derive(Debug, Clone, Copy)] | |
| enum ExprPrefixError { | |
| RecursionLimitExceeded, | |
| Err, | |
| } | |
| impl From<&ParserError> for ExprPrefixError { | |
| fn from(e: &ParserError) -> Self { | |
| match e { | |
| ParserError::RecursionLimitExceeded => Self::RecursionLimitExceeded, | |
| _ => Self::Err, | |
| } | |
| } | |
| } | |
| impl<'a> Parser<'a> { | |
| /// Create a parser for a [`Dialect`] | |
| /// | |
| /// See also [`Parser::parse_sql`] | |
| /// | |
| /// Example: | |
| /// ``` | |
| /// # use sqlparser::{parser::{Parser, ParserError}, dialect::GenericDialect}; | |
| /// # fn main() -> Result<(), ParserError> { | |
| /// let dialect = GenericDialect{}; | |
| /// let statements = Parser::new(&dialect) | |
| /// .try_with_sql("SELECT * FROM foo")? | |
| /// .parse_statements()?; | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| pub fn new(dialect: &'a dyn Dialect) -> Self { | |
| Self { | |
| tokens: vec![], | |
| index: 0, | |
| state: ParserState::Normal, | |
| dialect, | |
| recursion_counter: RecursionCounter::new(DEFAULT_REMAINING_DEPTH), | |
| options: ParserOptions::new().with_trailing_commas(dialect.supports_trailing_commas()), | |
| failed_prefix_positions: BTreeMap::new(), | |
| failed_reserved_word_prefix_positions: BTreeMap::new(), | |
| failed_derived_table_factor_positions: BTreeSet::new(), | |
| failed_position_syntax_positions: BTreeSet::new(), | |
| failed_in_subquery_positions: BTreeMap::new(), | |
| } | |
| } | |
| /// Specify the maximum recursion limit while parsing. | |
| /// | |
| /// [`Parser`] prevents stack overflows by returning | |
| /// [`ParserError::RecursionLimitExceeded`] if the parser exceeds | |
| /// this depth while processing the query. | |
| /// | |
| /// Example: | |
| /// ``` | |
| /// # use sqlparser::{parser::{Parser, ParserError}, dialect::GenericDialect}; | |
| /// # fn main() -> Result<(), ParserError> { | |
| /// let dialect = GenericDialect{}; | |
| /// let result = Parser::new(&dialect) | |
| /// .with_recursion_limit(1) | |
| /// .try_with_sql("SELECT * FROM foo WHERE (a OR (b OR (c OR d)))")? | |
| /// .parse_statements(); | |
| /// assert_eq!(result, Err(ParserError::RecursionLimitExceeded)); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| /// | |
| /// Note: Versions prior to `0.63.0` did not enforce any limit in builds | |
| /// without the "std" feature. | |
| /// | |
| /// Note: when "recursive-protection" feature is enabled, this crate uses | |
| /// additional stack overflow protection for some of its recursive methods. | |
| /// See [`recursive::recursive`] for more information. | |
| pub fn with_recursion_limit(mut self, recursion_limit: usize) -> Self { | |
| self.recursion_counter = RecursionCounter::new(recursion_limit); | |
| self | |
| } | |
| /// Specify additional parser options | |
| /// | |
| /// [`Parser`] supports additional options ([`ParserOptions`]) | |
| /// that allow you to mix & match behavior otherwise constrained | |
| /// to certain dialects (e.g. trailing commas). | |
| /// | |
| /// Example: | |
| /// ``` | |
| /// # use sqlparser::{parser::{Parser, ParserError, ParserOptions}, dialect::GenericDialect}; | |
| /// # fn main() -> Result<(), ParserError> { | |
| /// let dialect = GenericDialect{}; | |
| /// let options = ParserOptions::new() | |
| /// .with_trailing_commas(true) | |
| /// .with_unescape(false); | |
| /// let result = Parser::new(&dialect) | |
| /// .with_options(options) | |
| /// .try_with_sql("SELECT a, b, COUNT(*), FROM foo GROUP BY a, b,")? | |
| /// .parse_statements(); | |
| /// assert!(matches!(result, Ok(_))); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| pub fn with_options(mut self, options: ParserOptions) -> Self { | |
| self.options = options; | |
| self | |
| } | |
| /// Reset this parser to parse the specified token stream | |
| pub fn with_tokens_with_locations(mut self, tokens: Vec<TokenWithSpan>) -> Self { | |
| self.tokens = tokens; | |
| self.index = 0; | |
| self.failed_prefix_positions.clear(); | |
| self.failed_reserved_word_prefix_positions.clear(); | |
| self.failed_derived_table_factor_positions.clear(); | |
| self.failed_position_syntax_positions.clear(); | |
| self.failed_in_subquery_positions.clear(); | |
| self | |
| } | |
| /// Reset this parser state to parse the specified tokens | |
| pub fn with_tokens(self, tokens: Vec<Token>) -> Self { | |
| // Put in dummy locations | |
| let tokens_with_locations: Vec<TokenWithSpan> = tokens | |
| .into_iter() | |
| .map(|token| TokenWithSpan { | |
| token, | |
| span: Span::empty(), | |
| }) | |
| .collect(); | |
| self.with_tokens_with_locations(tokens_with_locations) | |
| } | |
| /// Tokenize the sql string and sets this [`Parser`]'s state to | |
| /// parse the resulting tokens | |
| /// | |
| /// Returns an error if there was an error tokenizing the SQL string. | |
| /// | |
| /// See example on [`Parser::new()`] for an example | |
| pub fn try_with_sql(self, sql: &str) -> Result<Self, ParserError> { | |
| debug!("Parsing sql '{sql}'..."); | |
| let tokens = Tokenizer::new(self.dialect, sql) | |
| .with_unescape(self.options.unescape) | |
| .tokenize_with_location()?; | |
| Ok(self.with_tokens_with_locations(tokens)) | |
| } | |
| /// Parse potentially multiple statements | |
| /// | |
| /// Example | |
| /// ``` | |
| /// # use sqlparser::{parser::{Parser, ParserError}, dialect::GenericDialect}; | |
| /// # fn main() -> Result<(), ParserError> { | |
| /// let dialect = GenericDialect{}; | |
| /// let statements = Parser::new(&dialect) | |
| /// // Parse a SQL string with 2 separate statements | |
| /// .try_with_sql("SELECT * FROM foo; SELECT * FROM bar;")? | |
| /// .parse_statements()?; | |
| /// assert_eq!(statements.len(), 2); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| pub fn parse_statements(&mut self) -> Result<Vec<Statement>, ParserError> { | |
| let mut stmts = Vec::new(); | |
| let mut expecting_statement_delimiter = false; | |
| loop { | |
| // ignore empty statements (between successive statement delimiters) | |
| while self.consume_token(&Token::SemiColon) { | |
| expecting_statement_delimiter = false; | |
| } | |
| if !self.options.require_semicolon_stmt_delimiter { | |
| expecting_statement_delimiter = false; | |
| } | |
| match &self.peek_token_ref().token { | |
| Token::EOF => break, | |
| // end of statement | |
| Token::Word(word) | |
| if expecting_statement_delimiter && word.keyword == Keyword::END => | |
| { | |
| break; | |
| } | |
| _ => {} | |
| } | |
| if expecting_statement_delimiter { | |
| return self.expected_ref("end of statement", self.peek_token_ref()); | |
| } | |
| let statement = self.parse_statement()?; | |
| stmts.push(statement); | |
| expecting_statement_delimiter = true; | |
| } | |
| Ok(stmts) | |
| } | |
| /// Convenience method to parse a string with one or more SQL | |
| /// statements into produce an Abstract Syntax Tree (AST). | |
| /// | |
| /// Example | |
| /// ``` | |
| /// # use sqlparser::{parser::{Parser, ParserError}, dialect::GenericDialect}; | |
| /// # fn main() -> Result<(), ParserError> { | |
| /// let dialect = GenericDialect{}; | |
| /// let statements = Parser::parse_sql( | |
| /// &dialect, "SELECT * FROM foo" | |
| /// )?; | |
| /// assert_eq!(statements.len(), 1); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| pub fn parse_sql(dialect: &dyn Dialect, sql: &str) -> Result<Vec<Statement>, ParserError> { | |
| Parser::new(dialect).try_with_sql(sql)?.parse_statements() | |
| } | |
| /// Parses the given `sql` into an Abstract Syntax Tree (AST), returning | |
| /// also encountered source code comments. | |
| /// | |
| /// See [Parser::parse_sql]. | |
| pub fn parse_sql_with_comments( | |
| dialect: &'a dyn Dialect, | |
| sql: &str, | |
| ) -> Result<(Vec<Statement>, comments::Comments), ParserError> { | |
| let mut p = Parser::new(dialect).try_with_sql(sql)?; | |
| p.parse_statements().map(|stmts| (stmts, p.into_comments())) | |
| } | |
| /// Consumes this parser returning comments from the parsed token stream. | |
| pub fn into_comments(self) -> comments::Comments { | |
| let mut comments = comments::Comments::default(); | |
| for t in self.tokens.into_iter() { | |
| match t.token { | |
| Token::Whitespace(Whitespace::SingleLineComment { comment, prefix }) => { | |
| comments.offer(comments::CommentWithSpan { | |
| comment: comments::Comment::SingleLine { | |
| content: comment, | |
| prefix, | |
| }, | |
| span: t.span, | |
| }); | |
| } | |
| Token::Whitespace(Whitespace::MultiLineComment(comment)) => { | |
| comments.offer(comments::CommentWithSpan { | |
| comment: comments::Comment::MultiLine(comment), | |
| span: t.span, | |
| }); | |
| } | |
| _ => {} | |
| } | |
| } | |
| comments | |
| } | |
| /// Parse a single top-level statement (such as SELECT, INSERT, CREATE, etc.), | |
| /// stopping before the statement separator, if any. | |
| pub fn parse_statement(&mut self) -> Result<Statement, ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| // allow the dialect to override statement parsing | |
| if let Some(statement) = self.dialect.parse_statement(self) { | |
| return statement; | |
| } | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::KILL => self.parse_kill(), | |
| Keyword::FLUSH => self.parse_flush(), | |
| Keyword::DESC => self.parse_explain(DescribeAlias::Desc), | |
| Keyword::DESCRIBE => self.parse_explain(DescribeAlias::Describe), | |
| Keyword::EXPLAIN => self.parse_explain(DescribeAlias::Explain), | |
| Keyword::ANALYZE => self.parse_analyze().map(Into::into), | |
| Keyword::CASE => { | |
| self.prev_token(); | |
| self.parse_case_stmt().map(Into::into) | |
| } | |
| Keyword::IF => { | |
| self.prev_token(); | |
| self.parse_if_stmt().map(Into::into) | |
| } | |
| Keyword::WHILE => { | |
| self.prev_token(); | |
| self.parse_while().map(Into::into) | |
| } | |
| Keyword::RAISE => { | |
| self.prev_token(); | |
| self.parse_raise_stmt().map(Into::into) | |
| } | |
| Keyword::SELECT | Keyword::WITH | Keyword::VALUES | Keyword::FROM => { | |
| self.prev_token(); | |
| self.parse_query().map(Into::into) | |
| } | |
| Keyword::TRUNCATE => self.parse_truncate().map(Into::into), | |
| Keyword::ATTACH => { | |
| if dialect_of!(self is DuckDbDialect) { | |
| self.parse_attach_duckdb_database() | |
| } else { | |
| self.parse_attach_database() | |
| } | |
| } | |
| Keyword::DETACH if self.dialect.supports_detach() => { | |
| self.parse_detach_duckdb_database() | |
| } | |
| Keyword::MSCK => self.parse_msck().map(Into::into), | |
| Keyword::CREATE => self.parse_create(), | |
| Keyword::CACHE => self.parse_cache_table(), | |
| Keyword::DROP => self.parse_drop(), | |
| Keyword::DISCARD => self.parse_discard(), | |
| Keyword::DECLARE => self.parse_declare(), | |
| Keyword::FETCH => self.parse_fetch_statement(), | |
| Keyword::DELETE => self.parse_delete(next_token), | |
| Keyword::INSERT => self.parse_insert(next_token), | |
| Keyword::REPLACE => self.parse_replace(next_token), | |
| Keyword::UNCACHE => self.parse_uncache_table(), | |
| Keyword::UPDATE => self.parse_update(next_token), | |
| Keyword::ALTER => self.parse_alter(), | |
| Keyword::CALL => self.parse_call(), | |
| Keyword::COPY => self.parse_copy(), | |
| Keyword::OPEN => { | |
| self.prev_token(); | |
| self.parse_open() | |
| } | |
| Keyword::CLOSE => self.parse_close(), | |
| Keyword::SET => self.parse_set(), | |
| Keyword::SHOW => self.parse_show(), | |
| Keyword::USE => self.parse_use(), | |
| Keyword::GRANT => self.parse_grant().map(Into::into), | |
| Keyword::DENY => { | |
| self.prev_token(); | |
| self.parse_deny() | |
| } | |
| Keyword::REVOKE => self.parse_revoke().map(Into::into), | |
| Keyword::START => self.parse_start_transaction(), | |
| Keyword::BEGIN => self.parse_begin(), | |
| Keyword::END => self.parse_end(), | |
| Keyword::SAVEPOINT => self.parse_savepoint(), | |
| Keyword::RELEASE => self.parse_release(), | |
| Keyword::COMMIT => self.parse_commit(), | |
| Keyword::RAISERROR => Ok(self.parse_raiserror()?), | |
| Keyword::THROW => { | |
| self.prev_token(); | |
| self.parse_throw().map(Into::into) | |
| } | |
| Keyword::ROLLBACK => self.parse_rollback(), | |
| Keyword::ABORT => self.parse_abort(), | |
| Keyword::ASSERT => self.parse_assert(), | |
| // `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific | |
| // syntaxes. They are used for Postgres prepared statement. | |
| Keyword::DEALLOCATE => self.parse_deallocate(), | |
| Keyword::EXECUTE | Keyword::EXEC => self.parse_execute(), | |
| Keyword::PREPARE => self.parse_prepare(), | |
| Keyword::MERGE => self.parse_merge(next_token).map(Into::into), | |
| // `LISTEN`, `UNLISTEN` and `NOTIFY` are Postgres-specific | |
| // syntaxes. They are used for Postgres statement. | |
| Keyword::LISTEN if self.dialect.supports_listen_notify() => self.parse_listen(), | |
| Keyword::UNLISTEN if self.dialect.supports_listen_notify() => self.parse_unlisten(), | |
| Keyword::NOTIFY if self.dialect.supports_listen_notify() => self.parse_notify(), | |
| // `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html | |
| Keyword::PRAGMA => self.parse_pragma(), | |
| Keyword::UNLOAD => { | |
| self.prev_token(); | |
| self.parse_unload() | |
| } | |
| Keyword::RENAME => self.parse_rename(), | |
| // `INSTALL` is duckdb specific https://duckdb.org/docs/extensions/overview | |
| Keyword::INSTALL if self.dialect.supports_install() => self.parse_install(), | |
| Keyword::LOAD => self.parse_load(), | |
| Keyword::LOCK => { | |
| self.prev_token(); | |
| self.parse_lock_statement().map(Into::into) | |
| } | |
| Keyword::OPTIMIZE if self.dialect.supports_optimize_table() => { | |
| self.parse_optimize_table() | |
| } | |
| // `COMMENT` is snowflake specific https://docs.snowflake.com/en/sql-reference/sql/comment | |
| Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(), | |
| Keyword::PRINT => self.parse_print(), | |
| // `WAITFOR` is MSSQL specific https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql | |
| Keyword::WAITFOR => self.parse_waitfor(), | |
| Keyword::RETURN => self.parse_return(), | |
| Keyword::EXPORT => { | |
| self.prev_token(); | |
| self.parse_export_data() | |
| } | |
| Keyword::VACUUM => { | |
| self.prev_token(); | |
| self.parse_vacuum() | |
| } | |
| Keyword::RESET => self.parse_reset().map(Into::into), | |
| _ => self.expected("an SQL statement", next_token), | |
| }, | |
| Token::LParen => { | |
| self.prev_token(); | |
| self.parse_query().map(Into::into) | |
| } | |
| _ => self.expected("an SQL statement", next_token), | |
| } | |
| } | |
| /// Parse a `CASE` statement. | |
| /// | |
| /// See [Statement::Case] | |
| pub fn parse_case_stmt(&mut self) -> Result<CaseStatement, ParserError> { | |
| let case_token = self.expect_keyword(Keyword::CASE)?; | |
| let match_expr = if self.peek_keyword(Keyword::WHEN) { | |
| None | |
| } else { | |
| Some(self.parse_expr()?) | |
| }; | |
| self.expect_keyword_is(Keyword::WHEN)?; | |
| let when_blocks = self.parse_keyword_separated(Keyword::WHEN, |parser| { | |
| parser.parse_conditional_statement_block(&[Keyword::WHEN, Keyword::ELSE, Keyword::END]) | |
| })?; | |
| let else_block = if self.parse_keyword(Keyword::ELSE) { | |
| Some(self.parse_conditional_statement_block(&[Keyword::END])?) | |
| } else { | |
| None | |
| }; | |
| let mut end_case_token = self.expect_keyword(Keyword::END)?; | |
| if self.peek_keyword(Keyword::CASE) { | |
| end_case_token = self.expect_keyword(Keyword::CASE)?; | |
| } | |
| Ok(CaseStatement { | |
| case_token: AttachedToken(case_token), | |
| match_expr, | |
| when_blocks, | |
| else_block, | |
| end_case_token: AttachedToken(end_case_token), | |
| }) | |
| } | |
| /// Parse an `IF` statement. | |
| /// | |
| /// See [Statement::If] | |
| pub fn parse_if_stmt(&mut self) -> Result<IfStatement, ParserError> { | |
| self.expect_keyword_is(Keyword::IF)?; | |
| let if_block = self.parse_conditional_statement_block(&[ | |
| Keyword::ELSE, | |
| Keyword::ELSEIF, | |
| Keyword::END, | |
| ])?; | |
| let elseif_blocks = if self.parse_keyword(Keyword::ELSEIF) { | |
| self.parse_keyword_separated(Keyword::ELSEIF, |parser| { | |
| parser.parse_conditional_statement_block(&[ | |
| Keyword::ELSEIF, | |
| Keyword::ELSE, | |
| Keyword::END, | |
| ]) | |
| })? | |
| } else { | |
| vec![] | |
| }; | |
| let else_block = if self.parse_keyword(Keyword::ELSE) { | |
| Some(self.parse_conditional_statement_block(&[Keyword::END])?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::END)?; | |
| let end_token = self.expect_keyword(Keyword::IF)?; | |
| Ok(IfStatement { | |
| if_block, | |
| elseif_blocks, | |
| else_block, | |
| end_token: Some(AttachedToken(end_token)), | |
| }) | |
| } | |
| /// Parse a `WHILE` statement. | |
| /// | |
| /// See [Statement::While] | |
| fn parse_while(&mut self) -> Result<WhileStatement, ParserError> { | |
| self.expect_keyword_is(Keyword::WHILE)?; | |
| let while_block = self.parse_conditional_statement_block(&[Keyword::END])?; | |
| Ok(WhileStatement { while_block }) | |
| } | |
| /// Parses an expression and associated list of statements | |
| /// belonging to a conditional statement like `IF` or `WHEN` or `WHILE`. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// IF condition THEN statement1; statement2; | |
| /// ``` | |
| fn parse_conditional_statement_block( | |
| &mut self, | |
| terminal_keywords: &[Keyword], | |
| ) -> Result<ConditionalStatementBlock, ParserError> { | |
| let start_token = self.get_current_token().clone(); // self.expect_keyword(keyword)?; | |
| let mut then_token = None; | |
| let condition = match &start_token.token { | |
| Token::Word(w) if w.keyword == Keyword::ELSE => None, | |
| Token::Word(w) if w.keyword == Keyword::WHILE => { | |
| let expr = self.parse_expr()?; | |
| Some(expr) | |
| } | |
| _ => { | |
| let expr = self.parse_expr()?; | |
| then_token = Some(AttachedToken(self.expect_keyword(Keyword::THEN)?)); | |
| Some(expr) | |
| } | |
| }; | |
| let conditional_statements = self.parse_conditional_statements(terminal_keywords)?; | |
| Ok(ConditionalStatementBlock { | |
| start_token: AttachedToken(start_token), | |
| condition, | |
| then_token, | |
| conditional_statements, | |
| }) | |
| } | |
| /// Parse a BEGIN/END block or a sequence of statements | |
| /// This could be inside of a conditional (IF, CASE, WHILE etc.) or an object body defined optionally BEGIN/END and one or more statements. | |
| pub(crate) fn parse_conditional_statements( | |
| &mut self, | |
| terminal_keywords: &[Keyword], | |
| ) -> Result<ConditionalStatements, ParserError> { | |
| let conditional_statements = if self.peek_keyword(Keyword::BEGIN) { | |
| let begin_token = self.expect_keyword(Keyword::BEGIN)?; | |
| let statements = self.parse_statement_list(terminal_keywords)?; | |
| let end_token = self.expect_keyword(Keyword::END)?; | |
| ConditionalStatements::BeginEnd(BeginEndStatements { | |
| begin_token: AttachedToken(begin_token), | |
| statements, | |
| end_token: AttachedToken(end_token), | |
| }) | |
| } else { | |
| ConditionalStatements::Sequence { | |
| statements: self.parse_statement_list(terminal_keywords)?, | |
| } | |
| }; | |
| Ok(conditional_statements) | |
| } | |
| /// Parse a `RAISE` statement. | |
| /// | |
| /// See [Statement::Raise] | |
| pub fn parse_raise_stmt(&mut self) -> Result<RaiseStatement, ParserError> { | |
| self.expect_keyword_is(Keyword::RAISE)?; | |
| let value = if self.parse_keywords(&[Keyword::USING, Keyword::MESSAGE]) { | |
| self.expect_token(&Token::Eq)?; | |
| Some(RaiseStatementValue::UsingMessage(self.parse_expr()?)) | |
| } else { | |
| self.maybe_parse(|parser| parser.parse_expr().map(RaiseStatementValue::Expr))? | |
| }; | |
| Ok(RaiseStatement { value }) | |
| } | |
| /// Parse a COMMENT statement. | |
| /// | |
| /// See [Statement::Comment] | |
| pub fn parse_comment(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| self.expect_keyword_is(Keyword::ON)?; | |
| let token = self.next_token(); | |
| let (object_type, object_name) = match token.token { | |
| Token::Word(w) if w.keyword == Keyword::COLLATION => { | |
| (CommentObject::Collation, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::COLUMN => { | |
| (CommentObject::Column, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::DATABASE => { | |
| (CommentObject::Database, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::DOMAIN => { | |
| (CommentObject::Domain, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::EXTENSION => { | |
| (CommentObject::Extension, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::FUNCTION => { | |
| (CommentObject::Function, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::INDEX => { | |
| (CommentObject::Index, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::MATERIALIZED => { | |
| self.expect_keyword_is(Keyword::VIEW)?; | |
| ( | |
| CommentObject::MaterializedView, | |
| self.parse_object_name(false)?, | |
| ) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::PROCEDURE => { | |
| (CommentObject::Procedure, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::ROLE => { | |
| (CommentObject::Role, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::SCHEMA => { | |
| (CommentObject::Schema, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::SEQUENCE => { | |
| (CommentObject::Sequence, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::TABLE => { | |
| (CommentObject::Table, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::TYPE => { | |
| (CommentObject::Type, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::USER => { | |
| (CommentObject::User, self.parse_object_name(false)?) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::VIEW => { | |
| (CommentObject::View, self.parse_object_name(false)?) | |
| } | |
| _ => self.expected("comment object_type", token)?, | |
| }; | |
| self.expect_keyword_is(Keyword::IS)?; | |
| let comment = if self.parse_keyword(Keyword::NULL) { | |
| None | |
| } else { | |
| Some(self.parse_literal_string()?) | |
| }; | |
| Ok(Statement::Comment { | |
| object_type, | |
| object_name, | |
| comment, | |
| if_exists, | |
| }) | |
| } | |
| /// Parse `FLUSH` statement. | |
| pub fn parse_flush(&mut self) -> Result<Statement, ParserError> { | |
| let mut channel = None; | |
| let mut tables: Vec<ObjectName> = vec![]; | |
| let mut read_lock = false; | |
| let mut export = false; | |
| if !dialect_of!(self is MySqlDialect | GenericDialect) { | |
| return parser_err!( | |
| "Unsupported statement FLUSH", | |
| self.peek_token_ref().span.start | |
| ); | |
| } | |
| let location = if self.parse_keyword(Keyword::NO_WRITE_TO_BINLOG) { | |
| Some(FlushLocation::NoWriteToBinlog) | |
| } else if self.parse_keyword(Keyword::LOCAL) { | |
| Some(FlushLocation::Local) | |
| } else { | |
| None | |
| }; | |
| let object_type = if self.parse_keywords(&[Keyword::BINARY, Keyword::LOGS]) { | |
| FlushType::BinaryLogs | |
| } else if self.parse_keywords(&[Keyword::ENGINE, Keyword::LOGS]) { | |
| FlushType::EngineLogs | |
| } else if self.parse_keywords(&[Keyword::ERROR, Keyword::LOGS]) { | |
| FlushType::ErrorLogs | |
| } else if self.parse_keywords(&[Keyword::GENERAL, Keyword::LOGS]) { | |
| FlushType::GeneralLogs | |
| } else if self.parse_keywords(&[Keyword::HOSTS]) { | |
| FlushType::Hosts | |
| } else if self.parse_keyword(Keyword::PRIVILEGES) { | |
| FlushType::Privileges | |
| } else if self.parse_keyword(Keyword::OPTIMIZER_COSTS) { | |
| FlushType::OptimizerCosts | |
| } else if self.parse_keywords(&[Keyword::RELAY, Keyword::LOGS]) { | |
| if self.parse_keywords(&[Keyword::FOR, Keyword::CHANNEL]) { | |
| channel = Some(self.parse_object_name(false)?.to_string()); | |
| } | |
| FlushType::RelayLogs | |
| } else if self.parse_keywords(&[Keyword::SLOW, Keyword::LOGS]) { | |
| FlushType::SlowLogs | |
| } else if self.parse_keyword(Keyword::STATUS) { | |
| FlushType::Status | |
| } else if self.parse_keyword(Keyword::USER_RESOURCES) { | |
| FlushType::UserResources | |
| } else if self.parse_keywords(&[Keyword::LOGS]) { | |
| FlushType::Logs | |
| } else if self.parse_keywords(&[Keyword::TABLES]) { | |
| loop { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::WITH => { | |
| read_lock = self.parse_keywords(&[Keyword::READ, Keyword::LOCK]); | |
| } | |
| Keyword::FOR => { | |
| export = self.parse_keyword(Keyword::EXPORT); | |
| } | |
| Keyword::NoKeyword => { | |
| self.prev_token(); | |
| tables = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| } | |
| _ => {} | |
| }, | |
| _ => { | |
| break; | |
| } | |
| } | |
| } | |
| FlushType::Tables | |
| } else { | |
| return self.expected_ref( | |
| "BINARY LOGS, ENGINE LOGS, ERROR LOGS, GENERAL LOGS, HOSTS, LOGS, PRIVILEGES, OPTIMIZER_COSTS,\ | |
| RELAY LOGS [FOR CHANNEL channel], SLOW LOGS, STATUS, USER_RESOURCES", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(Statement::Flush { | |
| object_type, | |
| location, | |
| channel, | |
| read_lock, | |
| export, | |
| tables, | |
| }) | |
| } | |
| /// Parse `MSCK` statement. | |
| pub fn parse_msck(&mut self) -> Result<Msck, ParserError> { | |
| let repair = self.parse_keyword(Keyword::REPAIR); | |
| self.expect_keyword_is(Keyword::TABLE)?; | |
| let table_name = self.parse_object_name(false)?; | |
| let partition_action = self | |
| .maybe_parse(|parser| { | |
| let pa = match parser.parse_one_of_keywords(&[ | |
| Keyword::ADD, | |
| Keyword::DROP, | |
| Keyword::SYNC, | |
| ]) { | |
| Some(Keyword::ADD) => Some(AddDropSync::ADD), | |
| Some(Keyword::DROP) => Some(AddDropSync::DROP), | |
| Some(Keyword::SYNC) => Some(AddDropSync::SYNC), | |
| _ => None, | |
| }; | |
| parser.expect_keyword_is(Keyword::PARTITIONS)?; | |
| Ok(pa) | |
| })? | |
| .unwrap_or_default(); | |
| Ok(Msck { | |
| repair, | |
| table_name, | |
| partition_action, | |
| }) | |
| } | |
| /// Parse `TRUNCATE` statement. | |
| pub fn parse_truncate(&mut self) -> Result<Truncate, ParserError> { | |
| let table = self.parse_keyword(Keyword::TABLE); | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let table_names = self.parse_comma_separated(|p| { | |
| let only = p.parse_keyword(Keyword::ONLY); | |
| let name = p.parse_object_name(false)?; | |
| let has_asterisk = p.consume_token(&Token::Mul); | |
| Ok(TruncateTableTarget { | |
| name, | |
| only, | |
| has_asterisk, | |
| }) | |
| })?; | |
| let mut partitions = None; | |
| if self.parse_keyword(Keyword::PARTITION) { | |
| self.expect_token(&Token::LParen)?; | |
| partitions = Some(self.parse_comma_separated(Parser::parse_expr)?); | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| let mut identity = None; | |
| let mut cascade = None; | |
| if dialect_of!(self is PostgreSqlDialect | GenericDialect) { | |
| identity = if self.parse_keywords(&[Keyword::RESTART, Keyword::IDENTITY]) { | |
| Some(TruncateIdentityOption::Restart) | |
| } else if self.parse_keywords(&[Keyword::CONTINUE, Keyword::IDENTITY]) { | |
| Some(TruncateIdentityOption::Continue) | |
| } else { | |
| None | |
| }; | |
| cascade = self.parse_cascade_option(); | |
| }; | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| Ok(Truncate { | |
| table_names, | |
| partitions, | |
| table, | |
| if_exists, | |
| identity, | |
| cascade, | |
| on_cluster, | |
| }) | |
| } | |
| fn parse_cascade_option(&mut self) -> Option<CascadeOption> { | |
| if self.parse_keyword(Keyword::CASCADE) { | |
| Some(CascadeOption::Cascade) | |
| } else if self.parse_keyword(Keyword::RESTRICT) { | |
| Some(CascadeOption::Restrict) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parse options for `ATTACH DUCKDB DATABASE` statement. | |
| pub fn parse_attach_duckdb_database_options( | |
| &mut self, | |
| ) -> Result<Vec<AttachDuckDBDatabaseOption>, ParserError> { | |
| if !self.consume_token(&Token::LParen) { | |
| return Ok(vec![]); | |
| } | |
| let mut options = vec![]; | |
| loop { | |
| if self.parse_keyword(Keyword::READ_ONLY) { | |
| let boolean = if self.parse_keyword(Keyword::TRUE) { | |
| Some(true) | |
| } else if self.parse_keyword(Keyword::FALSE) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| options.push(AttachDuckDBDatabaseOption::ReadOnly(boolean)); | |
| } else if self.parse_keyword(Keyword::TYPE) { | |
| let ident = self.parse_identifier()?; | |
| options.push(AttachDuckDBDatabaseOption::Type(ident)); | |
| } else { | |
| return self | |
| .expected_ref("expected one of: ), READ_ONLY, TYPE", self.peek_token_ref()); | |
| }; | |
| if self.consume_token(&Token::RParen) { | |
| return Ok(options); | |
| } else if self.consume_token(&Token::Comma) { | |
| continue; | |
| } else { | |
| return self.expected_ref("expected one of: ')', ','", self.peek_token_ref()); | |
| } | |
| } | |
| } | |
| /// Parse `ATTACH DUCKDB DATABASE` statement. | |
| pub fn parse_attach_duckdb_database(&mut self) -> Result<Statement, ParserError> { | |
| let database = self.parse_keyword(Keyword::DATABASE); | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let database_path = self.parse_identifier()?; | |
| let database_alias = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let attach_options = self.parse_attach_duckdb_database_options()?; | |
| Ok(Statement::AttachDuckDBDatabase { | |
| if_not_exists, | |
| database, | |
| database_path, | |
| database_alias, | |
| attach_options, | |
| }) | |
| } | |
| /// Parse `DETACH DUCKDB DATABASE` statement. | |
| pub fn parse_detach_duckdb_database(&mut self) -> Result<Statement, ParserError> { | |
| let database = self.parse_keyword(Keyword::DATABASE); | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let database_alias = self.parse_identifier()?; | |
| Ok(Statement::DetachDuckDBDatabase { | |
| if_exists, | |
| database, | |
| database_alias, | |
| }) | |
| } | |
| /// Parse `ATTACH DATABASE` statement. | |
| pub fn parse_attach_database(&mut self) -> Result<Statement, ParserError> { | |
| let database = self.parse_keyword(Keyword::DATABASE); | |
| let database_file_name = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let schema_name = self.parse_identifier()?; | |
| Ok(Statement::AttachDatabase { | |
| database, | |
| schema_name, | |
| database_file_name, | |
| }) | |
| } | |
| /// Parse `ANALYZE` statement. | |
| pub fn parse_analyze(&mut self) -> Result<Analyze, ParserError> { | |
| let has_table_keyword = self.parse_keyword(Keyword::TABLE); | |
| let table_name = self.maybe_parse(|parser| parser.parse_object_name(false))?; | |
| let mut for_columns = false; | |
| let mut cache_metadata = false; | |
| let mut noscan = false; | |
| let mut partitions = None; | |
| let mut compute_statistics = false; | |
| let mut columns = vec![]; | |
| // PostgreSQL syntax: ANALYZE t (col1, col2) | |
| if table_name.is_some() && self.consume_token(&Token::LParen) { | |
| columns = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| loop { | |
| match self.parse_one_of_keywords(&[ | |
| Keyword::PARTITION, | |
| Keyword::FOR, | |
| Keyword::CACHE, | |
| Keyword::NOSCAN, | |
| Keyword::COMPUTE, | |
| ]) { | |
| Some(Keyword::PARTITION) => { | |
| self.expect_token(&Token::LParen)?; | |
| partitions = Some(self.parse_comma_separated(Parser::parse_expr)?); | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| Some(Keyword::NOSCAN) => noscan = true, | |
| Some(Keyword::FOR) => { | |
| self.expect_keyword_is(Keyword::COLUMNS)?; | |
| columns = self | |
| .maybe_parse(|parser| { | |
| parser.parse_comma_separated(|p| p.parse_identifier()) | |
| })? | |
| .unwrap_or_default(); | |
| for_columns = true | |
| } | |
| Some(Keyword::CACHE) => { | |
| self.expect_keyword_is(Keyword::METADATA)?; | |
| cache_metadata = true | |
| } | |
| Some(Keyword::COMPUTE) => { | |
| self.expect_keyword_is(Keyword::STATISTICS)?; | |
| compute_statistics = true | |
| } | |
| _ => break, | |
| } | |
| } | |
| Ok(Analyze { | |
| has_table_keyword, | |
| table_name, | |
| for_columns, | |
| columns, | |
| partitions, | |
| cache_metadata, | |
| noscan, | |
| compute_statistics, | |
| }) | |
| } | |
| /// Parse a new expression including wildcard & qualified wildcard. | |
| pub fn parse_wildcard_expr(&mut self) -> Result<Expr, ParserError> { | |
| let index = self.index; | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| t @ (Token::Word(_) | Token::SingleQuotedString(_)) | |
| if self.peek_token_ref().token == Token::Period => | |
| { | |
| let mut id_parts: Vec<Ident> = vec![match t { | |
| Token::Word(w) => w.into_ident(next_token.span), | |
| Token::SingleQuotedString(s) => Ident::with_quote('\'', s), | |
| _ => { | |
| return Err(ParserError::ParserError( | |
| "Internal parser error: unexpected token type".to_string(), | |
| )) | |
| } | |
| }]; | |
| while self.consume_token(&Token::Period) { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => id_parts.push(w.into_ident(next_token.span)), | |
| Token::SingleQuotedString(s) => { | |
| // SQLite has single-quoted identifiers | |
| id_parts.push(Ident::with_quote('\'', s)) | |
| } | |
| Token::Placeholder(s) => { | |
| // Snowflake uses $1, $2, etc. for positional column references | |
| // in staged data queries like: SELECT t.$1 FROM @stage t | |
| id_parts.push(Ident::new(s)) | |
| } | |
| Token::Mul => { | |
| return Ok(Expr::QualifiedWildcard( | |
| ObjectName::from(id_parts), | |
| AttachedToken(next_token), | |
| )); | |
| } | |
| // A field index such as `t.1` is parsed as an expression. | |
| Token::Number(..) if self.dialect.supports_tuple_element_access() => break, | |
| _ => { | |
| return self.expected("an identifier or a '*' after '.'", next_token); | |
| } | |
| } | |
| } | |
| } | |
| Token::Mul => { | |
| return Ok(Expr::Wildcard(AttachedToken(next_token))); | |
| } | |
| // Handle parenthesized wildcard: (*) | |
| Token::LParen => { | |
| let [maybe_mul, maybe_rparen] = self.peek_tokens_ref(); | |
| if maybe_mul.token == Token::Mul && maybe_rparen.token == Token::RParen { | |
| let mul_token = self.next_token(); // consume Mul | |
| self.next_token(); // consume RParen | |
| return Ok(Expr::Wildcard(AttachedToken(mul_token))); | |
| } | |
| } | |
| _ => (), | |
| }; | |
| self.index = index; | |
| self.parse_expr() | |
| } | |
| /// Parse a new expression. | |
| pub fn parse_expr(&mut self) -> Result<Expr, ParserError> { | |
| self.parse_subexpr(self.dialect.prec_unknown()) | |
| } | |
| /// Parse expression with optional alias and order by. | |
| pub fn parse_expr_with_alias_and_order_by( | |
| &mut self, | |
| ) -> Result<ExprWithAliasAndOrderBy, ParserError> { | |
| let expr = self.parse_expr()?; | |
| fn validator(explicit: bool, kw: &Keyword, _parser: &mut Parser) -> bool { | |
| explicit || !&[Keyword::ASC, Keyword::DESC, Keyword::GROUP].contains(kw) | |
| } | |
| let alias = self.parse_optional_alias_inner(None, validator)?; | |
| let order_by = OrderByOptions { | |
| sort: self.parse_optional_order_by_sort(), | |
| nulls_first: None, | |
| }; | |
| Ok(ExprWithAliasAndOrderBy { | |
| expr: ExprWithAlias { expr, alias }, | |
| order_by, | |
| }) | |
| } | |
| /// Parse tokens until the precedence changes. | |
| #[cfg_attr(feature = "recursive-protection", recursive::recursive)] | |
| pub fn parse_subexpr(&mut self, precedence: u8) -> Result<Expr, ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| debug!("parsing expr"); | |
| let mut expr = self.parse_prefix()?; | |
| expr = self.parse_compound_expr(expr, vec![])?; | |
| // Parse an optional collation cast operator following `expr`. | |
| // | |
| // For example (MSSQL): t1.a COLLATE Latin1_General_CI_AS | |
| if !self.in_column_definition_state() && self.parse_keyword(Keyword::COLLATE) { | |
| expr = Expr::Collate { | |
| expr: Box::new(expr), | |
| collation: self.parse_object_name(false)?, | |
| }; | |
| } | |
| debug!("prefix: {expr:?}"); | |
| loop { | |
| let next_precedence = self.get_next_precedence()?; | |
| debug!("next precedence: {next_precedence:?}"); | |
| if precedence >= next_precedence { | |
| break; | |
| } | |
| // The period operator is handled exclusively by the | |
| // compound field access parsing. | |
| if Token::Period == self.peek_token_ref().token { | |
| break; | |
| } | |
| expr = self.parse_infix(expr, next_precedence)?; | |
| } | |
| Ok(expr) | |
| } | |
| /// Parse `ASSERT` statement. | |
| pub fn parse_assert(&mut self) -> Result<Statement, ParserError> { | |
| let condition = self.parse_expr()?; | |
| let message = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::Assert { condition, message }) | |
| } | |
| /// Parse `SAVEPOINT` statement. | |
| pub fn parse_savepoint(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_identifier()?; | |
| Ok(Statement::Savepoint { name }) | |
| } | |
| /// Parse `RELEASE` statement. | |
| pub fn parse_release(&mut self) -> Result<Statement, ParserError> { | |
| let _ = self.parse_keyword(Keyword::SAVEPOINT); | |
| let name = self.parse_identifier()?; | |
| Ok(Statement::ReleaseSavepoint { name }) | |
| } | |
| /// Parse `LISTEN` statement. | |
| pub fn parse_listen(&mut self) -> Result<Statement, ParserError> { | |
| let channel = self.parse_identifier()?; | |
| Ok(Statement::LISTEN { channel }) | |
| } | |
| /// Parse `UNLISTEN` statement. | |
| pub fn parse_unlisten(&mut self) -> Result<Statement, ParserError> { | |
| let channel = if self.consume_token(&Token::Mul) { | |
| Ident::new(Expr::Wildcard(AttachedToken::empty()).to_string()) | |
| } else { | |
| match self.parse_identifier() { | |
| Ok(expr) => expr, | |
| _ => { | |
| self.prev_token(); | |
| return self.expected_ref("wildcard or identifier", self.peek_token_ref()); | |
| } | |
| } | |
| }; | |
| Ok(Statement::UNLISTEN { channel }) | |
| } | |
| /// Parse `NOTIFY` statement. | |
| pub fn parse_notify(&mut self) -> Result<Statement, ParserError> { | |
| let channel = self.parse_identifier()?; | |
| let payload = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::NOTIFY { channel, payload }) | |
| } | |
| /// Parses a `RENAME TABLE` statement. See [Statement::RenameTable] | |
| pub fn parse_rename(&mut self) -> Result<Statement, ParserError> { | |
| if self.peek_keyword(Keyword::TABLE) { | |
| self.expect_keyword(Keyword::TABLE)?; | |
| let rename_tables = self.parse_comma_separated(|parser| { | |
| let old_name = parser.parse_object_name(false)?; | |
| parser.expect_keyword(Keyword::TO)?; | |
| let new_name = parser.parse_object_name(false)?; | |
| Ok(RenameTable { old_name, new_name }) | |
| })?; | |
| Ok(rename_tables.into()) | |
| } else { | |
| self.expected_ref("KEYWORD `TABLE` after RENAME", self.peek_token_ref()) | |
| } | |
| } | |
| /// Tries to parse an expression by matching the specified word to known keywords that have a special meaning in the dialect. | |
| /// Returns `None if no match is found. | |
| fn parse_expr_prefix_by_reserved_word( | |
| &mut self, | |
| w: &Word, | |
| w_span: Span, | |
| ) -> Result<Option<Expr>, ParserError> { | |
| match w.keyword { | |
| Keyword::TRUE | Keyword::FALSE if self.dialect.supports_boolean_literals() => { | |
| self.prev_token(); | |
| Ok(Some(Expr::Value(self.parse_value()?))) | |
| } | |
| Keyword::NULL => { | |
| self.prev_token(); | |
| Ok(Some(Expr::Value(self.parse_value()?))) | |
| } | |
| Keyword::CURRENT_CATALOG | |
| | Keyword::CURRENT_USER | |
| | Keyword::SESSION_USER | |
| | Keyword::USER | |
| if dialect_of!(self is PostgreSqlDialect | GenericDialect) => | |
| { | |
| Ok(Some(Expr::Function(Function { | |
| name: ObjectName::from(vec![w.to_ident(w_span)]), | |
| uses_odbc_syntax: false, | |
| parameters: FunctionArguments::None, | |
| args: FunctionArguments::None, | |
| null_treatment: None, | |
| filter: None, | |
| over: None, | |
| within_group: vec![], | |
| }))) | |
| } | |
| Keyword::CURRENT_TIMESTAMP | |
| | Keyword::CURRENT_TIME | |
| | Keyword::CURRENT_DATE | |
| | Keyword::LOCALTIME | |
| | Keyword::LOCALTIMESTAMP => { | |
| Ok(Some(self.parse_time_functions(ObjectName::from(vec![w.to_ident(w_span)]))?)) | |
| } | |
| Keyword::CASE => Ok(Some(self.parse_case_expr()?)), | |
| Keyword::CONVERT => Ok(Some(self.parse_convert_expr(false)?)), | |
| Keyword::TRY_CONVERT if self.dialect.supports_try_convert() => Ok(Some(self.parse_convert_expr(true)?)), | |
| Keyword::CAST => Ok(Some(self.parse_cast_expr(CastKind::Cast)?)), | |
| Keyword::TRY_CAST => Ok(Some(self.parse_cast_expr(CastKind::TryCast)?)), | |
| Keyword::SAFE_CAST => Ok(Some(self.parse_cast_expr(CastKind::SafeCast)?)), | |
| Keyword::EXISTS | |
| // Support parsing Databricks has a function named `exists`. | |
| if !dialect_of!(self is DatabricksDialect) | |
| || matches!( | |
| self.peek_nth_token_ref(1).token, | |
| Token::Word(Word { | |
| keyword: Keyword::SELECT | Keyword::WITH, | |
| .. | |
| }) | |
| ) => | |
| { | |
| Ok(Some(self.parse_exists_expr(false)?)) | |
| } | |
| Keyword::EXTRACT => Ok(Some(self.parse_extract_expr()?)), | |
| Keyword::CEIL => Ok(Some(self.parse_ceil_floor_expr(true)?)), | |
| Keyword::FLOOR => Ok(Some(self.parse_ceil_floor_expr(false)?)), | |
| Keyword::POSITION if self.peek_token_ref().token == Token::LParen => { | |
| Ok(Some(self.parse_position_expr(w.to_ident(w_span))?)) | |
| } | |
| Keyword::SUBSTR | Keyword::SUBSTRING => { | |
| self.prev_token(); | |
| Ok(Some(self.parse_substring()?)) | |
| } | |
| Keyword::OVERLAY => Ok(Some(self.parse_overlay_expr()?)), | |
| Keyword::TRIM => Ok(Some(self.parse_trim_expr()?)), | |
| Keyword::INTERVAL => Ok(Some(self.parse_interval()?)), | |
| // Treat ARRAY[1,2,3] as an array [1,2,3], otherwise try as subquery or a function call | |
| Keyword::ARRAY if *self.peek_token_ref() == Token::LBracket => { | |
| self.expect_token(&Token::LBracket)?; | |
| Ok(Some(self.parse_array_expr(true)?)) | |
| } | |
| Keyword::ARRAY | |
| if self.peek_token_ref().token == Token::LParen | |
| && !dialect_of!(self is ClickHouseDialect | DatabricksDialect) => | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let query = self.parse_query()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(Expr::Function(Function { | |
| name: ObjectName::from(vec![w.to_ident(w_span)]), | |
| uses_odbc_syntax: false, | |
| parameters: FunctionArguments::None, | |
| args: FunctionArguments::Subquery(query), | |
| filter: None, | |
| null_treatment: None, | |
| over: None, | |
| within_group: vec![], | |
| }))) | |
| } | |
| Keyword::NOT => Ok(Some(self.parse_not()?)), | |
| Keyword::MATCH if self.dialect.supports_match_against() => { | |
| Ok(Some(self.parse_match_against()?)) | |
| } | |
| Keyword::STRUCT if self.dialect.supports_struct_literal() => { | |
| let struct_expr = self.parse_struct_literal()?; | |
| Ok(Some(struct_expr)) | |
| } | |
| Keyword::PRIOR if matches!(self.state, ParserState::ConnectBy) => { | |
| let expr = self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?; | |
| Ok(Some(Expr::Prior(Box::new(expr)))) | |
| } | |
| Keyword::MAP if *self.peek_token_ref() == Token::LBrace && self.dialect.support_map_literal_syntax() => { | |
| Ok(Some(self.parse_duckdb_map_literal()?)) | |
| } | |
| Keyword::APPROXIMATE | |
| if self.dialect.supports_approximate_percentile_disc() | |
| && self.peek_keyword(Keyword::PERCENTILE_DISC) => | |
| { | |
| self.maybe_parse(|parser| { | |
| let function_name = parser.parse_object_name(false)?; | |
| parser.parse_function(function_name).map(|function| Expr::Prefixed { | |
| prefix: w.to_ident(w_span), | |
| value: Box::new(function), | |
| }) | |
| }) | |
| } | |
| Keyword::LAMBDA if self.dialect.supports_lambda_keyword_syntax() => { | |
| Ok(Some(self.parse_lambda_expr()?)) | |
| } | |
| _ if self.dialect.supports_geometric_types() => match w.keyword { | |
| Keyword::CIRCLE => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Circle)?)), | |
| Keyword::BOX => Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricBox)?)), | |
| Keyword::PATH => Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricPath)?)), | |
| Keyword::LINE => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Line)?)), | |
| Keyword::LSEG => Ok(Some(self.parse_geometric_type(GeometricTypeKind::LineSegment)?)), | |
| Keyword::POINT => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Point)?)), | |
| Keyword::POLYGON => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Polygon)?)), | |
| _ => Ok(None), | |
| }, | |
| _ => Ok(None), | |
| } | |
| } | |
| /// Tries to parse an expression by a word that is not known to have a special meaning in the dialect. | |
| fn parse_expr_prefix_by_unreserved_word( | |
| &mut self, | |
| w: &Word, | |
| w_span: Span, | |
| ) -> Result<Expr, ParserError> { | |
| let is_outer_join = self.peek_outer_join_operator(); | |
| match &self.peek_token_ref().token { | |
| Token::LParen if !is_outer_join => { | |
| let id_parts = vec![w.to_ident(w_span)]; | |
| self.parse_function(ObjectName::from(id_parts)) | |
| } | |
| // string introducer https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html | |
| Token::SingleQuotedString(_) | |
| | Token::DoubleQuotedString(_) | |
| | Token::HexStringLiteral(_) | |
| if w.value.starts_with('_') => | |
| { | |
| Ok(Expr::Prefixed { | |
| prefix: w.to_ident(w_span), | |
| value: self.parse_introduced_string_expr()?.into(), | |
| }) | |
| } | |
| // string introducer https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html | |
| Token::SingleQuotedString(_) | |
| | Token::DoubleQuotedString(_) | |
| | Token::HexStringLiteral(_) | |
| if w.value.starts_with('_') => | |
| { | |
| Ok(Expr::Prefixed { | |
| prefix: w.to_ident(w_span), | |
| value: self.parse_introduced_string_expr()?.into(), | |
| }) | |
| } | |
| // An unreserved word (likely an identifier) is followed by an arrow, | |
| // which indicates a lambda function with a single, untyped parameter. | |
| // For example: `a -> a * 2`. | |
| Token::Arrow if self.dialect.supports_lambda_functions() => { | |
| self.expect_token(&Token::Arrow)?; | |
| Ok(Expr::Lambda(LambdaFunction { | |
| params: OneOrManyWithParens::One(LambdaFunctionParameter { | |
| name: w.to_ident(w_span), | |
| data_type: None, | |
| }), | |
| body: Box::new(self.parse_expr()?), | |
| syntax: LambdaSyntax::Arrow, | |
| })) | |
| } | |
| // An unreserved word (likely an identifier) that is followed by another word (likley a data type) | |
| // which is then followed by an arrow, which indicates a lambda function with a single, typed parameter. | |
| // For example: `a INT -> a * 2`. | |
| Token::Word(_) | |
| if self.dialect.supports_lambda_functions() | |
| && self.peek_nth_token_ref(1).token == Token::Arrow => | |
| { | |
| let data_type = self.parse_data_type()?; | |
| self.expect_token(&Token::Arrow)?; | |
| Ok(Expr::Lambda(LambdaFunction { | |
| params: OneOrManyWithParens::One(LambdaFunctionParameter { | |
| name: w.to_ident(w_span), | |
| data_type: Some(data_type), | |
| }), | |
| body: Box::new(self.parse_expr()?), | |
| syntax: LambdaSyntax::Arrow, | |
| })) | |
| } | |
| _ => Ok(Expr::Identifier(w.to_ident(w_span))), | |
| } | |
| } | |
| /// Returns true if the given [ObjectName] is a single unquoted | |
| /// identifier matching `expected` (case-insensitive). | |
| fn is_simple_unquoted_object_name(name: &ObjectName, expected: &str) -> bool { | |
| if let [ObjectNamePart::Identifier(ident)] = name.0.as_slice() { | |
| ident.quote_style.is_none() && ident.value.eq_ignore_ascii_case(expected) | |
| } else { | |
| false | |
| } | |
| } | |
| /// Parse an expression prefix. | |
| pub fn parse_prefix(&mut self) -> Result<Expr, ParserError> { | |
| // allow the dialect to override prefix parsing | |
| if let Some(prefix) = self.dialect.parse_prefix(self) { | |
| return prefix; | |
| } | |
| // Memoize parse_prefix failures to break 2^N speculation when both | |
| // prefix arms fail at every level (e.g. `IF(current_time(...x`). | |
| // The per-arm cache in `parse_prefix_inner` complements this for | |
| // chains where the reserved arm fails but the unreserved fallback | |
| // succeeds (e.g. `case-case-...c`). | |
| let start_index = self.index; | |
| if let Some(&cached) = self.failed_prefix_positions.get(&start_index) { | |
| return self.cached_prefix_error(cached, self.peek_token_ref()); | |
| } | |
| let result = self.parse_prefix_inner(); | |
| if let Err(ref e) = result { | |
| self.failed_prefix_positions.insert(start_index, e.into()); | |
| } | |
| result | |
| } | |
| /// Rebuild the error for a cached prefix failure at the `found` token. | |
| fn cached_prefix_error<T>( | |
| &self, | |
| cached: ExprPrefixError, | |
| found: &TokenWithSpan, | |
| ) -> Result<T, ParserError> { | |
| match cached { | |
| ExprPrefixError::RecursionLimitExceeded => Err(ParserError::RecursionLimitExceeded), | |
| ExprPrefixError::Err => self.expected_ref("an expression", found), | |
| } | |
| } | |
| fn parse_prefix_inner(&mut self) -> Result<Expr, ParserError> { | |
| // PostgreSQL allows any string literal to be preceded by a type name, indicating that the | |
| // string literal represents a literal of that type. Some examples: | |
| // | |
| // DATE '2020-05-20' | |
| // TIMESTAMP WITH TIME ZONE '2020-05-20 7:43:54' | |
| // BOOL 'true' | |
| // | |
| // The first two are standard SQL, while the latter is a PostgreSQL extension. Complicating | |
| // matters is the fact that INTERVAL string literals may optionally be followed by special | |
| // keywords, e.g.: | |
| // | |
| // INTERVAL '7' DAY | |
| // | |
| // Note also that naively `SELECT date` looks like a syntax error because the `date` type | |
| // name is not followed by a string literal, but in fact in PostgreSQL it is a valid | |
| // expression that should parse as the column name "date". | |
| let loc = self.peek_token_ref().span.start; | |
| let opt_expr = self.maybe_parse(|parser| { | |
| match parser.parse_data_type()? { | |
| DataType::Interval { .. } => parser.parse_interval(), | |
| // PostgreSQL allows almost any identifier to be used as custom data type name, | |
| // and we support that in `parse_data_type()`. But unlike Postgres we don't | |
| // have a list of globally reserved keywords (since they vary across dialects), | |
| // so given `NOT 'a' LIKE 'b'`, we'd accept `NOT` as a possible custom data type | |
| // name, resulting in `NOT 'a'` being recognized as a `TypedString` instead of | |
| // an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the | |
| // `type 'string'` syntax for the custom data types at all ... | |
| // | |
| // ... with the exception of `xml '...'` on dialects that support XML | |
| // expressions, which is a valid PostgreSQL typed string literal. | |
| DataType::Custom(ref name, ref modifiers) | |
| if modifiers.is_empty() | |
| && Self::is_simple_unquoted_object_name(name, "xml") | |
| && parser.dialect.supports_xml_expressions() => | |
| { | |
| Ok(Expr::TypedString(TypedString { | |
| data_type: DataType::Custom(name.clone(), modifiers.clone()), | |
| value: parser.parse_value()?, | |
| uses_odbc_syntax: false, | |
| })) | |
| } | |
| DataType::Custom(..) => parser_err!("dummy", loc), | |
| // MySQL supports using the `BINARY` keyword as a cast to binary type. | |
| DataType::Binary(..) if self.dialect.supports_binary_kw_as_cast() => { | |
| Ok(Expr::Cast { | |
| kind: CastKind::Cast, | |
| expr: Box::new(parser.parse_expr()?), | |
| data_type: DataType::Binary(None), | |
| format: None, | |
| }) | |
| } | |
| data_type => Ok(Expr::TypedString(TypedString { | |
| data_type, | |
| value: parser.parse_value()?, | |
| uses_odbc_syntax: false, | |
| })), | |
| } | |
| })?; | |
| if let Some(expr) = opt_expr { | |
| return Ok(expr); | |
| } | |
| // Cache some dialect properties to avoid lifetime issues with the | |
| // next_token reference. | |
| let dialect = self.dialect; | |
| self.advance_token(); | |
| let next_token_index = self.get_current_index(); | |
| let next_token = self.get_current_token(); | |
| let span = next_token.span; | |
| let expr = match &next_token.token { | |
| Token::Word(w) => { | |
| // The word we consumed may fall into one of two cases: it has a special meaning, or not. | |
| // For example, in Snowflake, the word `interval` may have two meanings depending on the context: | |
| // `SELECT CURRENT_DATE() + INTERVAL '1 DAY', MAX(interval) FROM tbl;` | |
| // ^^^^^^^^^^^^^^^^ ^^^^^^^^ | |
| // interval expression identifier | |
| // | |
| // We first try to parse the word and following tokens as a special expression, and if that fails, | |
| // we rollback and try to parse it as an identifier. | |
| let w = w.clone(); | |
| // Memoize failed speculative reserved-word parses. When | |
| // the reserved arm (CASE, CURRENT_TIME, etc.) does | |
| // exponential work but the unreserved fallback ultimately | |
| // succeeds, the overall `parse_prefix` returns `Ok` and the | |
| // outer cache never fires. Chains like `case-case-...c` | |
| // need this per-arm cache to break the doubling. | |
| let try_parse_result = if let Some(&cached) = self | |
| .failed_reserved_word_prefix_positions | |
| .get(&next_token_index) | |
| { | |
| self.cached_prefix_error(cached, self.get_current_token()) | |
| } else { | |
| self.try_parse(|parser| parser.parse_expr_prefix_by_reserved_word(&w, span)) | |
| }; | |
| match try_parse_result { | |
| // This word indicated an expression prefix and parsing was successful | |
| Ok(Some(expr)) => Ok(expr), | |
| // No expression prefix associated with this word | |
| Ok(None) => Ok(self.parse_expr_prefix_by_unreserved_word(&w, span)?), | |
| // If parsing of the word as a special expression failed, we are facing two options: | |
| // 1. The statement is malformed, e.g. `SELECT INTERVAL '1 DAI` (`DAI` instead of `DAY`) | |
| // 2. The word is used as an identifier, e.g. `SELECT MAX(interval) FROM tbl` | |
| // We first try to parse the word as an identifier and if that fails | |
| // we rollback and return the parsing error we got from trying to parse a | |
| // special expression (to maintain backwards compatibility of parsing errors). | |
| Err(e) => { | |
| self.failed_reserved_word_prefix_positions | |
| .insert(next_token_index, (&e).into()); | |
| if !self.dialect.is_reserved_for_identifier(w.keyword) { | |
| if let Ok(Some(expr)) = self.maybe_parse(|parser| { | |
| parser.parse_expr_prefix_by_unreserved_word(&w, span) | |
| }) { | |
| return Ok(expr); | |
| } | |
| } | |
| return Err(e); | |
| } | |
| } | |
| } // End of Token::Word | |
| // array `[1, 2, 3]` | |
| Token::LBracket => self.parse_array_expr(false), | |
| tok @ Token::Minus | tok @ Token::Plus => { | |
| let op = if *tok == Token::Plus { | |
| UnaryOperator::Plus | |
| } else { | |
| UnaryOperator::Minus | |
| }; | |
| Ok(Expr::UnaryOp { | |
| op, | |
| expr: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::MulDivModOp))?, | |
| ), | |
| }) | |
| } | |
| Token::ExclamationMark if dialect.supports_bang_not_operator() => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::BangNot, | |
| expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?), | |
| }), | |
| tok @ Token::DoubleExclamationMark | |
| | tok @ Token::PGSquareRoot | |
| | tok @ Token::PGCubeRoot | |
| | tok @ Token::AtSign | |
| if dialect_is!(dialect is PostgreSqlDialect) => | |
| { | |
| let op = match tok { | |
| Token::DoubleExclamationMark => UnaryOperator::PGPrefixFactorial, | |
| Token::PGSquareRoot => UnaryOperator::PGSquareRoot, | |
| Token::PGCubeRoot => UnaryOperator::PGCubeRoot, | |
| Token::AtSign => UnaryOperator::PGAbs, | |
| _ => { | |
| return Err(ParserError::ParserError( | |
| "Internal parser error: unexpected unary operator token".to_string(), | |
| )) | |
| } | |
| }; | |
| Ok(Expr::UnaryOp { | |
| op, | |
| expr: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?, | |
| ), | |
| }) | |
| } | |
| Token::Tilde => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::BitwiseNot, | |
| expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?), | |
| }), | |
| tok @ Token::Sharp | |
| | tok @ Token::AtDashAt | |
| | tok @ Token::AtAt | |
| | tok @ Token::QuestionMarkDash | |
| | tok @ Token::QuestionPipe | |
| if self.dialect.supports_geometric_types() => | |
| { | |
| let op = match tok { | |
| Token::Sharp => UnaryOperator::Hash, | |
| Token::AtDashAt => UnaryOperator::AtDashAt, | |
| Token::AtAt => UnaryOperator::DoubleAt, | |
| Token::QuestionMarkDash => UnaryOperator::QuestionDash, | |
| Token::QuestionPipe => UnaryOperator::QuestionPipe, | |
| _ => { | |
| return Err(ParserError::ParserError(format!( | |
| "Unexpected token in unary operator parsing: {tok:?}" | |
| ))) | |
| } | |
| }; | |
| Ok(Expr::UnaryOp { | |
| op, | |
| expr: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?, | |
| ), | |
| }) | |
| } | |
| Token::EscapedStringLiteral(_) if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => | |
| { | |
| self.prev_token(); | |
| Ok(Expr::Value(self.parse_value()?)) | |
| } | |
| Token::UnicodeStringLiteral(_) => { | |
| self.prev_token(); | |
| Ok(Expr::Value(self.parse_value()?)) | |
| } | |
| Token::Number(_, _) | |
| | Token::SingleQuotedString(_) | |
| | Token::DoubleQuotedString(_) | |
| | Token::TripleSingleQuotedString(_) | |
| | Token::TripleDoubleQuotedString(_) | |
| | Token::DollarQuotedString(_) | |
| | Token::SingleQuotedByteStringLiteral(_) | |
| | Token::DoubleQuotedByteStringLiteral(_) | |
| | Token::TripleSingleQuotedByteStringLiteral(_) | |
| | Token::TripleDoubleQuotedByteStringLiteral(_) | |
| | Token::SingleQuotedRawStringLiteral(_) | |
| | Token::DoubleQuotedRawStringLiteral(_) | |
| | Token::TripleSingleQuotedRawStringLiteral(_) | |
| | Token::TripleDoubleQuotedRawStringLiteral(_) | |
| | Token::NationalStringLiteral(_) | |
| | Token::QuoteDelimitedStringLiteral(_) | |
| | Token::NationalQuoteDelimitedStringLiteral(_) | |
| | Token::HexStringLiteral(_) => { | |
| self.prev_token(); | |
| Ok(Expr::Value(self.parse_value()?)) | |
| } | |
| Token::LParen => { | |
| let expr = | |
| if let Some(expr) = self.try_parse_expr_sub_query()? { | |
| expr | |
| } else if let Some(lambda) = self.try_parse_lambda()? { | |
| return Ok(lambda); | |
| } else { | |
| // Parentheses in expressions switch to "normal" parsing state. | |
| // This matters for dialects (SQLite, DuckDB) where `NOT NULL` can | |
| // be an alias for `IS NOT NULL`. In column definitions like: | |
| // | |
| // CREATE TABLE t (c INT DEFAULT (42 NOT NULL) NOT NULL) | |
| // | |
| // The `(42 NOT NULL)` is an expression with parens, so it parses | |
| // as `IsNotNull(42)`. The trailing `NOT NULL` is outside those | |
| // expression parens (the outer parens are CREATE TABLE syntax), | |
| // so it remains a column constraint. | |
| let exprs = self.with_state(ParserState::Normal, |p| { | |
| p.parse_comma_separated(Parser::parse_expr) | |
| })?; | |
| match exprs.len() { | |
| 0 => return Err(ParserError::ParserError( | |
| "Internal parser error: parse_comma_separated returned empty list" | |
| .to_string(), | |
| )), | |
| 1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())), | |
| _ => Expr::Tuple(exprs), | |
| } | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(expr) | |
| } | |
| Token::Placeholder(_) | Token::Colon | Token::AtSign => { | |
| self.prev_token(); | |
| Ok(Expr::Value(self.parse_value()?)) | |
| } | |
| Token::LBrace => { | |
| self.prev_token(); | |
| self.parse_lbrace_expr() | |
| } | |
| _ => self.expected_at("an expression", next_token_index), | |
| }?; | |
| Ok(expr) | |
| } | |
| fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> Result<Expr, ParserError> { | |
| Ok(Expr::TypedString(TypedString { | |
| data_type: DataType::GeometricType(kind), | |
| value: self.parse_value()?, | |
| uses_odbc_syntax: false, | |
| })) | |
| } | |
| /// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`. | |
| /// If all the fields are `Expr::Identifier`s, return an [Expr::CompoundIdentifier] instead. | |
| /// If only the root exists, return the root. | |
| /// Parses compound expressions which may be delimited by period | |
| /// or bracket notation. | |
| /// For example: `a.b.c`, `a.b[1]`. | |
| pub fn parse_compound_expr( | |
| &mut self, | |
| root: Expr, | |
| mut chain: Vec<AccessExpr>, | |
| ) -> Result<Expr, ParserError> { | |
| let mut ending_wildcard: Option<TokenWithSpan> = None; | |
| loop { | |
| if self.consume_token(&Token::Period) { | |
| let next_token = self.peek_token_ref(); | |
| match &next_token.token { | |
| Token::Mul => { | |
| // Postgres explicitly allows funcnm(tablenm.*) and the | |
| // function array_agg traverses this control flow | |
| if dialect_of!(self is PostgreSqlDialect) { | |
| ending_wildcard = Some(self.next_token()); | |
| } else { | |
| // Put back the consumed `.` tokens before exiting. | |
| // If this expression is being parsed in the | |
| // context of a projection, then the `.*` could imply | |
| // a wildcard expansion. For example: | |
| // `SELECT STRUCT('foo').* FROM T` | |
| self.prev_token(); // . | |
| } | |
| break; | |
| } | |
| Token::SingleQuotedString(s) => { | |
| let expr = | |
| Expr::Identifier(Ident::with_quote_and_span('\'', next_token.span, s)); | |
| chain.push(AccessExpr::Dot(expr)); | |
| self.advance_token(); // The consumed string | |
| } | |
| Token::Placeholder(s) => { | |
| // Snowflake uses $1, $2, etc. for positional column references | |
| // in staged data queries like: SELECT t.$1 FROM @stage t | |
| let expr = Expr::Identifier(Ident::with_span(next_token.span, s)); | |
| chain.push(AccessExpr::Dot(expr)); | |
| self.advance_token(); // The consumed placeholder | |
| } | |
| // Parse a single field component, restricted to expression types valid | |
| // after `.` (so e.g. `T.interval` is a compound identifier, not an | |
| // interval expression). Using `parse_prefix` here rather than | |
| // `parse_subexpr` avoids 2^N work on inputs like `IF a.b.c...x.#`: | |
| // the outer loop already consumes successive `.field` segments, so a | |
| // recursive `parse_subexpr` would re-walk the rest of the chain at | |
| // every dot. | |
| _ => { | |
| // For a plain `Word` field (not followed by `(`), skip the | |
| // speculative `parse_prefix`. The only result the validator | |
| // below would accept is `Identifier`, which `parse_identifier` | |
| // in the None branch produces directly. This avoids 2^N work | |
| // on chains like `.not-b.not-b...` where `parse_prefix` would | |
| // descend into `parse_not` and re-walk the remaining chain at | |
| // every segment. | |
| let word_field_no_lparen = | |
| matches!(self.peek_token_ref().token, Token::Word(_)) | |
| && self.peek_nth_token_ref(1).token != Token::LParen; | |
| let expr = if word_field_no_lparen { | |
| None | |
| } else { | |
| self.maybe_parse(|parser| { | |
| let expr = parser.parse_prefix()?; | |
| match &expr { | |
| Expr::CompoundFieldAccess { .. } | |
| | Expr::CompoundIdentifier(_) | |
| | Expr::Identifier(_) | |
| | Expr::Value(_) | |
| | Expr::Function(_) => Ok(expr), | |
| _ => parser.expected_ref( | |
| "an identifier or value", | |
| parser.peek_token_ref(), | |
| ), | |
| } | |
| })? | |
| }; | |
| match expr { | |
| // If we get back a compound field access or identifier, | |
| // we flatten the nested expression. | |
| // For example if the current root is `foo` | |
| // and we get back a compound identifier expression `bar.baz` | |
| // The full expression should be `foo.bar.baz` (i.e. | |
| // a root with an access chain with 2 entries) and not | |
| // `foo.(bar.baz)` (i.e. a root with an access chain with | |
| // 1 entry`). | |
| Some(Expr::CompoundFieldAccess { root, access_chain }) => { | |
| chain.push(AccessExpr::Dot(*root)); | |
| chain.extend(access_chain); | |
| } | |
| Some(Expr::CompoundIdentifier(parts)) => chain.extend( | |
| parts.into_iter().map(Expr::Identifier).map(AccessExpr::Dot), | |
| ), | |
| Some(expr) => { | |
| chain.push(AccessExpr::Dot(expr)); | |
| } | |
| // If the expression is not a valid suffix, fall back to | |
| // parsing as an identifier. This handles cases like `T.interval` | |
| // where `interval` is a keyword but should be treated as an identifier. | |
| None => { | |
| chain.push(AccessExpr::Dot(Expr::Identifier( | |
| self.parse_identifier()?, | |
| ))); | |
| } | |
| } | |
| } | |
| } | |
| } else if !self.dialect.supports_partiql() | |
| && self.peek_token_ref().token == Token::LBracket | |
| { | |
| self.parse_multi_dim_subscript(&mut chain)?; | |
| } else { | |
| break; | |
| } | |
| } | |
| let tok_index = self.get_current_index(); | |
| if let Some(wildcard_token) = ending_wildcard { | |
| if !Self::is_all_ident(&root, &chain) { | |
| return self | |
| .expected_ref("an identifier or a '*' after '.'", self.peek_token_ref()); | |
| }; | |
| Ok(Expr::QualifiedWildcard( | |
| ObjectName::from(Self::exprs_to_idents(root, chain)?), | |
| AttachedToken(wildcard_token), | |
| )) | |
| } else if self.maybe_parse_outer_join_operator() { | |
| if !Self::is_all_ident(&root, &chain) { | |
| return self.expected_at("column identifier before (+)", tok_index); | |
| }; | |
| let expr = if chain.is_empty() { | |
| root | |
| } else { | |
| Expr::CompoundIdentifier(Self::exprs_to_idents(root, chain)?) | |
| }; | |
| Ok(Expr::OuterJoin(expr.into())) | |
| } else { | |
| Self::build_compound_expr(root, chain) | |
| } | |
| } | |
| /// Combines a root expression and access chain to form | |
| /// a compound expression. Which may be a [Expr::CompoundFieldAccess] | |
| /// or other special cased expressions like [Expr::CompoundIdentifier], | |
| /// [Expr::OuterJoin]. | |
| fn build_compound_expr( | |
| root: Expr, | |
| mut access_chain: Vec<AccessExpr>, | |
| ) -> Result<Expr, ParserError> { | |
| if access_chain.is_empty() { | |
| return Ok(root); | |
| } | |
| if Self::is_all_ident(&root, &access_chain) { | |
| return Ok(Expr::CompoundIdentifier(Self::exprs_to_idents( | |
| root, | |
| access_chain, | |
| )?)); | |
| } | |
| // Flatten qualified function calls. | |
| // For example, the expression `a.b.c.foo(1,2,3)` should | |
| // represent a function called `a.b.c.foo`, rather than | |
| // a composite expression. | |
| if matches!(root, Expr::Identifier(_)) | |
| && matches!( | |
| access_chain.last(), | |
| Some(AccessExpr::Dot(Expr::Function(_))) | |
| ) | |
| && access_chain | |
| .iter() | |
| .rev() | |
| .skip(1) // All except the Function | |
| .all(|access| matches!(access, AccessExpr::Dot(Expr::Identifier(_)))) | |
| { | |
| let Some(AccessExpr::Dot(Expr::Function(mut func))) = access_chain.pop() else { | |
| return parser_err!("expected function expression", root.span().start); | |
| }; | |
| let compound_func_name = [root] | |
| .into_iter() | |
| .chain(access_chain.into_iter().flat_map(|access| match access { | |
| AccessExpr::Dot(expr) => Some(expr), | |
| _ => None, | |
| })) | |
| .flat_map(|expr| match expr { | |
| Expr::Identifier(ident) => Some(ident), | |
| _ => None, | |
| }) | |
| .map(ObjectNamePart::Identifier) | |
| .chain(func.name.0) | |
| .collect::<Vec<_>>(); | |
| func.name = ObjectName(compound_func_name); | |
| return Ok(Expr::Function(func)); | |
| } | |
| // Flatten qualified outer join expressions. | |
| // For example, the expression `T.foo(+)` should | |
| // represent an outer join on the column name `T.foo` | |
| // rather than a composite expression. | |
| if access_chain.len() == 1 | |
| && matches!( | |
| access_chain.last(), | |
| Some(AccessExpr::Dot(Expr::OuterJoin(_))) | |
| ) | |
| { | |
| let Some(AccessExpr::Dot(Expr::OuterJoin(inner_expr))) = access_chain.pop() else { | |
| return parser_err!("expected (+) expression", root.span().start); | |
| }; | |
| if !Self::is_all_ident(&root, &[]) { | |
| return parser_err!("column identifier before (+)", root.span().start); | |
| }; | |
| let token_start = root.span().start; | |
| let mut idents = Self::exprs_to_idents(root, vec![])?; | |
| match *inner_expr { | |
| Expr::CompoundIdentifier(suffix) => idents.extend(suffix), | |
| Expr::Identifier(suffix) => idents.push(suffix), | |
| _ => { | |
| return parser_err!("column identifier before (+)", token_start); | |
| } | |
| } | |
| return Ok(Expr::OuterJoin(Expr::CompoundIdentifier(idents).into())); | |
| } | |
| Ok(Expr::CompoundFieldAccess { | |
| root: Box::new(root), | |
| access_chain, | |
| }) | |
| } | |
| fn keyword_to_modifier(k: Keyword) -> Option<ContextModifier> { | |
| match k { | |
| Keyword::LOCAL => Some(ContextModifier::Local), | |
| Keyword::GLOBAL => Some(ContextModifier::Global), | |
| Keyword::SESSION => Some(ContextModifier::Session), | |
| _ => None, | |
| } | |
| } | |
| /// Check if the root is an identifier and all fields are identifiers. | |
| fn is_all_ident(root: &Expr, fields: &[AccessExpr]) -> bool { | |
| if !matches!(root, Expr::Identifier(_)) { | |
| return false; | |
| } | |
| fields | |
| .iter() | |
| .all(|x| matches!(x, AccessExpr::Dot(Expr::Identifier(_)))) | |
| } | |
| /// Convert a root and a list of fields to a list of identifiers. | |
| fn exprs_to_idents(root: Expr, fields: Vec<AccessExpr>) -> Result<Vec<Ident>, ParserError> { | |
| let mut idents = vec![]; | |
| if let Expr::Identifier(root) = root { | |
| idents.push(root); | |
| for x in fields { | |
| if let AccessExpr::Dot(Expr::Identifier(ident)) = x { | |
| idents.push(ident); | |
| } else { | |
| return parser_err!( | |
| format!("Expected identifier, found: {}", x), | |
| x.span().start | |
| ); | |
| } | |
| } | |
| Ok(idents) | |
| } else { | |
| parser_err!( | |
| format!("Expected identifier, found: {}", root), | |
| root.span().start | |
| ) | |
| } | |
| } | |
| /// Returns true if the next tokens indicate the outer join operator `(+)`. | |
| fn peek_outer_join_operator(&mut self) -> bool { | |
| if !self.dialect.supports_outer_join_operator() { | |
| return false; | |
| } | |
| let [maybe_lparen, maybe_plus, maybe_rparen] = self.peek_tokens_ref(); | |
| Token::LParen == maybe_lparen.token | |
| && Token::Plus == maybe_plus.token | |
| && Token::RParen == maybe_rparen.token | |
| } | |
| /// If the next tokens indicates the outer join operator `(+)`, consume | |
| /// the tokens and return true. | |
| fn maybe_parse_outer_join_operator(&mut self) -> bool { | |
| self.dialect.supports_outer_join_operator() | |
| && self.consume_tokens(&[Token::LParen, Token::Plus, Token::RParen]) | |
| } | |
| /// Parse utility options in the form of `(option1, option2 arg2, option3 arg3, ...)` | |
| pub fn parse_utility_options(&mut self) -> Result<Vec<UtilityOption>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated(Self::parse_utility_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(options) | |
| } | |
| fn parse_utility_option(&mut self) -> Result<UtilityOption, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let next_token = self.peek_token_ref(); | |
| if next_token == &Token::Comma || next_token == &Token::RParen { | |
| return Ok(UtilityOption { name, arg: None }); | |
| } | |
| let arg = self.parse_expr()?; | |
| Ok(UtilityOption { | |
| name, | |
| arg: Some(arg), | |
| }) | |
| } | |
| fn try_parse_expr_sub_query(&mut self) -> Result<Option<Expr>, ParserError> { | |
| if !self.peek_sub_query() { | |
| return Ok(None); | |
| } | |
| Ok(Some(Expr::Subquery(self.parse_query()?))) | |
| } | |
| fn try_parse_lambda(&mut self) -> Result<Option<Expr>, ParserError> { | |
| if !self.dialect.supports_lambda_functions() { | |
| return Ok(None); | |
| } | |
| self.maybe_parse(|p| { | |
| let params = p.parse_comma_separated(|p| p.parse_lambda_function_parameter())?; | |
| p.expect_token(&Token::RParen)?; | |
| p.expect_token(&Token::Arrow)?; | |
| let expr = p.parse_expr()?; | |
| Ok(Expr::Lambda(LambdaFunction { | |
| params: OneOrManyWithParens::Many(params), | |
| body: Box::new(expr), | |
| syntax: LambdaSyntax::Arrow, | |
| })) | |
| }) | |
| } | |
| /// Parses a lambda expression following the `LAMBDA` keyword syntax. | |
| /// | |
| /// Syntax: `LAMBDA <params> : <expr>` | |
| /// | |
| /// Examples: | |
| /// - `LAMBDA x : x + 1` | |
| /// - `LAMBDA x, i : x > i` | |
| /// | |
| /// See <https://duckdb.org/docs/stable/sql/functions/lambda> | |
| fn parse_lambda_expr(&mut self) -> Result<Expr, ParserError> { | |
| // Parse the parameters: either a single identifier or comma-separated identifiers | |
| let params = self.parse_lambda_function_parameters()?; | |
| // Expect the colon separator | |
| self.expect_token(&Token::Colon)?; | |
| // Parse the body expression | |
| let body = self.parse_expr()?; | |
| Ok(Expr::Lambda(LambdaFunction { | |
| params, | |
| body: Box::new(body), | |
| syntax: LambdaSyntax::LambdaKeyword, | |
| })) | |
| } | |
| /// Parses the parameters of a lambda function with optional typing. | |
| fn parse_lambda_function_parameters( | |
| &mut self, | |
| ) -> Result<OneOrManyWithParens<LambdaFunctionParameter>, ParserError> { | |
| // Parse the parameters: either a single identifier or comma-separated identifiers | |
| let params = if self.consume_token(&Token::LParen) { | |
| // Parenthesized parameters: (x, y) | |
| let params = self.parse_comma_separated(|p| p.parse_lambda_function_parameter())?; | |
| self.expect_token(&Token::RParen)?; | |
| OneOrManyWithParens::Many(params) | |
| } else { | |
| // Unparenthesized parameters: x or x, y | |
| let params = self.parse_comma_separated(|p| p.parse_lambda_function_parameter())?; | |
| if params.len() == 1 { | |
| OneOrManyWithParens::One(params.into_iter().next().unwrap()) | |
| } else { | |
| OneOrManyWithParens::Many(params) | |
| } | |
| }; | |
| Ok(params) | |
| } | |
| /// Parses a single parameter of a lambda function, with optional typing. | |
| fn parse_lambda_function_parameter(&mut self) -> Result<LambdaFunctionParameter, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let data_type = match &self.peek_token_ref().token { | |
| Token::Word(_) => self.maybe_parse(|p| p.parse_data_type())?, | |
| _ => None, | |
| }; | |
| Ok(LambdaFunctionParameter { name, data_type }) | |
| } | |
| /// Tries to parse the body of an [ODBC escaping sequence] | |
| /// i.e. without the enclosing braces | |
| /// Currently implemented: | |
| /// Scalar Function Calls | |
| /// Date, Time, and Timestamp Literals | |
| /// See <https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/escape-sequences-in-odbc?view=sql-server-2017> | |
| fn maybe_parse_odbc_body(&mut self) -> Result<Option<Expr>, ParserError> { | |
| // Attempt 1: Try to parse it as a function. | |
| if let Some(expr) = self.maybe_parse_odbc_fn_body()? { | |
| return Ok(Some(expr)); | |
| } | |
| // Attempt 2: Try to parse it as a Date, Time or Timestamp Literal | |
| self.maybe_parse_odbc_body_datetime() | |
| } | |
| /// Tries to parse the body of an [ODBC Date, Time, and Timestamp Literals] call. | |
| /// | |
| /// ```sql | |
| /// {d '2025-07-17'} | |
| /// {t '14:12:01'} | |
| /// {ts '2025-07-17 14:12:01'} | |
| /// ``` | |
| /// | |
| /// [ODBC Date, Time, and Timestamp Literals]: | |
| /// https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals?view=sql-server-2017 | |
| fn maybe_parse_odbc_body_datetime(&mut self) -> Result<Option<Expr>, ParserError> { | |
| self.maybe_parse(|p| { | |
| let token = p.next_token().clone(); | |
| let word_string = token.token.to_string(); | |
| let data_type = match word_string.as_str() { | |
| "t" => DataType::Time(None, TimezoneInfo::None), | |
| "d" => DataType::Date, | |
| "ts" => DataType::Timestamp(None, TimezoneInfo::None), | |
| _ => return p.expected("ODBC datetime keyword (t, d, or ts)", token), | |
| }; | |
| let value = p.parse_value()?; | |
| Ok(Expr::TypedString(TypedString { | |
| data_type, | |
| value, | |
| uses_odbc_syntax: true, | |
| })) | |
| }) | |
| } | |
| /// Tries to parse the body of an [ODBC function] call. | |
| /// i.e. without the enclosing braces | |
| /// | |
| /// ```sql | |
| /// fn myfunc(1,2,3) | |
| /// ``` | |
| /// | |
| /// [ODBC function]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/scalar-function-calls?view=sql-server-2017 | |
| fn maybe_parse_odbc_fn_body(&mut self) -> Result<Option<Expr>, ParserError> { | |
| self.maybe_parse(|p| { | |
| p.expect_keyword(Keyword::FN)?; | |
| let fn_name = p.parse_object_name(false)?; | |
| let mut fn_call = p.parse_function_call(fn_name)?; | |
| fn_call.uses_odbc_syntax = true; | |
| Ok(Expr::Function(fn_call)) | |
| }) | |
| } | |
| /// Parse the argument list of `XMLPARSE({ DOCUMENT | CONTENT } value)`, | |
| /// including the closing parenthesis. The mode word becomes the name of the | |
| /// single argument, with no operator between it and the value. | |
| fn parse_xmlparse_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> { | |
| let arg = FunctionArg::Named { | |
| name: self.parse_identifier()?, | |
| arg: FunctionArgExpr::Expr(self.parse_expr()?), | |
| operator: FunctionArgOperator::Space, | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(FunctionArgumentList { | |
| duplicate_treatment: None, | |
| args: vec![arg], | |
| clauses: vec![], | |
| }) | |
| } | |
| /// Parse a function call expression named by `name` and return it as an `Expr`. | |
| pub fn parse_function(&mut self, name: ObjectName) -> Result<Expr, ParserError> { | |
| self.parse_function_call(name).map(Expr::Function) | |
| } | |
| fn parse_function_call(&mut self, name: ObjectName) -> Result<Function, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| // Snowflake permits a subquery to be passed as an argument without | |
| // an enclosing set of parens if it's the only argument. | |
| if self.dialect.supports_subquery_as_function_arg() && self.peek_sub_query() { | |
| let subquery = self.parse_query()?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Function { | |
| name, | |
| uses_odbc_syntax: false, | |
| parameters: FunctionArguments::None, | |
| args: FunctionArguments::Subquery(subquery), | |
| filter: None, | |
| null_treatment: None, | |
| over: None, | |
| within_group: vec![], | |
| }); | |
| } | |
| let mut args = if self.dialect.supports_xml_expressions() | |
| && Self::is_simple_unquoted_object_name(&name, "xmlparse") | |
| { | |
| self.parse_xmlparse_argument_list()? | |
| } else { | |
| self.parse_function_argument_list()? | |
| }; | |
| let mut parameters = FunctionArguments::None; | |
| // ClickHouse aggregations support parametric functions like `HISTOGRAM(0.5, 0.6)(x, y)` | |
| // which (0.5, 0.6) is a parameter to the function. | |
| if dialect_of!(self is ClickHouseDialect | GenericDialect) | |
| && self.consume_token(&Token::LParen) | |
| { | |
| parameters = FunctionArguments::List(args); | |
| args = self.parse_function_argument_list()?; | |
| } | |
| let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) { | |
| self.expect_token(&Token::LParen)?; | |
| self.expect_keywords(&[Keyword::ORDER, Keyword::BY])?; | |
| let order_by = self.parse_comma_separated(Parser::parse_order_by_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| order_by | |
| } else { | |
| vec![] | |
| }; | |
| let filter = if self.dialect.supports_filter_during_aggregation() | |
| && self.parse_keyword(Keyword::FILTER) | |
| && self.consume_token(&Token::LParen) | |
| && self.parse_keyword(Keyword::WHERE) | |
| { | |
| let filter = Some(Box::new(self.parse_expr()?)); | |
| self.expect_token(&Token::RParen)?; | |
| filter | |
| } else { | |
| None | |
| }; | |
| // Syntax for null treatment shows up either in the args list | |
| // or after the function call, but not both. | |
| let null_treatment = if args | |
| .clauses | |
| .iter() | |
| .all(|clause| !matches!(clause, FunctionArgumentClause::IgnoreOrRespectNulls(_))) | |
| { | |
| self.parse_null_treatment()? | |
| } else { | |
| None | |
| }; | |
| let over = if self.parse_keyword(Keyword::OVER) { | |
| if self.consume_token(&Token::LParen) { | |
| let window_spec = self.parse_window_spec()?; | |
| Some(WindowType::WindowSpec(window_spec)) | |
| } else { | |
| Some(WindowType::NamedWindow(self.parse_identifier()?)) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(Function { | |
| name, | |
| uses_odbc_syntax: false, | |
| parameters, | |
| args: FunctionArguments::List(args), | |
| null_treatment, | |
| filter, | |
| over, | |
| within_group, | |
| }) | |
| } | |
| /// Optionally parses a null treatment clause. | |
| fn parse_null_treatment(&mut self) -> Result<Option<NullTreatment>, ParserError> { | |
| match self.parse_one_of_keywords(&[Keyword::RESPECT, Keyword::IGNORE]) { | |
| Some(keyword) => { | |
| self.expect_keyword_is(Keyword::NULLS)?; | |
| Ok(match keyword { | |
| Keyword::RESPECT => Some(NullTreatment::RespectNulls), | |
| Keyword::IGNORE => Some(NullTreatment::IgnoreNulls), | |
| _ => None, | |
| }) | |
| } | |
| None => Ok(None), | |
| } | |
| } | |
| /// Parse time-related function `name` possibly followed by `(...)` arguments. | |
| pub fn parse_time_functions(&mut self, name: ObjectName) -> Result<Expr, ParserError> { | |
| let args = if self.consume_token(&Token::LParen) { | |
| FunctionArguments::List(self.parse_function_argument_list()?) | |
| } else { | |
| FunctionArguments::None | |
| }; | |
| Ok(Expr::Function(Function { | |
| name, | |
| uses_odbc_syntax: false, | |
| parameters: FunctionArguments::None, | |
| args, | |
| filter: None, | |
| over: None, | |
| null_treatment: None, | |
| within_group: vec![], | |
| })) | |
| } | |
| /// Parse window frame `UNITS` clause: `ROWS`, `RANGE`, or `GROUPS`. | |
| pub fn parse_window_frame_units(&mut self) -> Result<WindowFrameUnits, ParserError> { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::ROWS => Ok(WindowFrameUnits::Rows), | |
| Keyword::RANGE => Ok(WindowFrameUnits::Range), | |
| Keyword::GROUPS => Ok(WindowFrameUnits::Groups), | |
| _ => self.expected("ROWS, RANGE, GROUPS", next_token)?, | |
| }, | |
| _ => self.expected("ROWS, RANGE, GROUPS", next_token), | |
| } | |
| } | |
| /// Parse a `WINDOW` frame definition (units and bounds). | |
| pub fn parse_window_frame(&mut self) -> Result<WindowFrame, ParserError> { | |
| let units = self.parse_window_frame_units()?; | |
| let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN) { | |
| let start_bound = self.parse_window_frame_bound()?; | |
| self.expect_keyword_is(Keyword::AND)?; | |
| let end_bound = Some(self.parse_window_frame_bound()?); | |
| (start_bound, end_bound) | |
| } else { | |
| (self.parse_window_frame_bound()?, None) | |
| }; | |
| Ok(WindowFrame { | |
| units, | |
| start_bound, | |
| end_bound, | |
| }) | |
| } | |
| /// Parse a window frame bound: `CURRENT ROW` or `<n> PRECEDING|FOLLOWING`. | |
| pub fn parse_window_frame_bound(&mut self) -> Result<WindowFrameBound, ParserError> { | |
| if self.parse_keywords(&[Keyword::CURRENT, Keyword::ROW]) { | |
| Ok(WindowFrameBound::CurrentRow) | |
| } else { | |
| let rows = if self.parse_keyword(Keyword::UNBOUNDED) { | |
| None | |
| } else { | |
| Some(Box::new(match &self.peek_token_ref().token { | |
| Token::SingleQuotedString(_) => self.parse_interval()?, | |
| _ => self.parse_expr()?, | |
| })) | |
| }; | |
| if self.parse_keyword(Keyword::PRECEDING) { | |
| Ok(WindowFrameBound::Preceding(rows)) | |
| } else if self.parse_keyword(Keyword::FOLLOWING) { | |
| Ok(WindowFrameBound::Following(rows)) | |
| } else { | |
| self.expected_ref("PRECEDING or FOLLOWING", self.peek_token_ref()) | |
| } | |
| } | |
| } | |
| /// Parse a group by expr. Group by expr can be one of group sets, roll up, cube, or simple expr. | |
| fn parse_group_by_expr(&mut self) -> Result<Expr, ParserError> { | |
| if self.dialect.supports_group_by_expr() { | |
| if self.parse_keywords(&[Keyword::GROUPING, Keyword::SETS]) { | |
| self.expect_token(&Token::LParen)?; | |
| let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::GroupingSets(result)) | |
| } else if self.parse_keyword(Keyword::CUBE) { | |
| self.expect_token(&Token::LParen)?; | |
| let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Cube(result)) | |
| } else if self.parse_keyword(Keyword::ROLLUP) { | |
| self.expect_token(&Token::LParen)?; | |
| let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Rollup(result)) | |
| } else if self.consume_tokens(&[Token::LParen, Token::RParen]) { | |
| // PostgreSQL allow to use empty tuple as a group by expression, | |
| // e.g. `GROUP BY (), name`. Please refer to GROUP BY Clause section in | |
| // [PostgreSQL](https://www.postgresql.org/docs/16/sql-select.html) | |
| Ok(Expr::Tuple(vec![])) | |
| } else { | |
| self.parse_expr() | |
| } | |
| } else { | |
| // TODO parse rollup for other dialects | |
| self.parse_expr() | |
| } | |
| } | |
| /// Parse a tuple with `(` and `)`. | |
| /// If `lift_singleton` is true, then a singleton tuple is lifted to a tuple of length 1, otherwise it will fail. | |
| /// If `allow_empty` is true, then an empty tuple is allowed. | |
| fn parse_tuple( | |
| &mut self, | |
| lift_singleton: bool, | |
| allow_empty: bool, | |
| ) -> Result<Vec<Expr>, ParserError> { | |
| if lift_singleton { | |
| if self.consume_token(&Token::LParen) { | |
| let result = if allow_empty && self.consume_token(&Token::RParen) { | |
| vec![] | |
| } else { | |
| let result = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| result | |
| }; | |
| Ok(result) | |
| } else { | |
| Ok(vec![self.parse_expr()?]) | |
| } | |
| } else { | |
| self.expect_token(&Token::LParen)?; | |
| let result = if allow_empty && self.consume_token(&Token::RParen) { | |
| vec![] | |
| } else { | |
| let result = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| result | |
| }; | |
| Ok(result) | |
| } | |
| } | |
| /// Parse a `CASE` expression and return an [`Expr::Case`]. | |
| pub fn parse_case_expr(&mut self) -> Result<Expr, ParserError> { | |
| let case_token = AttachedToken(self.get_current_token().clone()); | |
| let mut operand = None; | |
| if !self.parse_keyword(Keyword::WHEN) { | |
| operand = Some(Box::new(self.parse_expr()?)); | |
| self.expect_keyword_is(Keyword::WHEN)?; | |
| } | |
| let mut conditions = vec![]; | |
| loop { | |
| let condition = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::THEN)?; | |
| let result = self.parse_expr()?; | |
| conditions.push(CaseWhen { condition, result }); | |
| if !self.parse_keyword(Keyword::WHEN) { | |
| break; | |
| } | |
| } | |
| let else_result = if self.parse_keyword(Keyword::ELSE) { | |
| Some(Box::new(self.parse_expr()?)) | |
| } else { | |
| None | |
| }; | |
| let end_token = AttachedToken(self.expect_keyword(Keyword::END)?); | |
| Ok(Expr::Case { | |
| case_token, | |
| end_token, | |
| operand, | |
| conditions, | |
| else_result, | |
| }) | |
| } | |
| /// Parse an optional `FORMAT` clause for `CAST` expressions. | |
| pub fn parse_optional_cast_format(&mut self) -> Result<Option<CastFormat>, ParserError> { | |
| if self.parse_keyword(Keyword::FORMAT) { | |
| let value = self.parse_value()?; | |
| match self.parse_optional_time_zone()? { | |
| Some(tz) => Ok(Some(CastFormat::ValueAtTimeZone(value, tz))), | |
| None => Ok(Some(CastFormat::Value(value))), | |
| } | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse an optional `AT TIME ZONE` clause. | |
| pub fn parse_optional_time_zone(&mut self) -> Result<Option<ValueWithSpan>, ParserError> { | |
| if self.parse_keywords(&[Keyword::AT, Keyword::TIME, Keyword::ZONE]) { | |
| self.parse_value().map(Some) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// mssql-like convert function | |
| fn parse_mssql_convert(&mut self, is_try: bool) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let data_type = self.parse_data_type()?; | |
| self.expect_token(&Token::Comma)?; | |
| let expr = self.parse_expr()?; | |
| let styles = if self.consume_token(&Token::Comma) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| Default::default() | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Convert { | |
| is_try, | |
| expr: Box::new(expr), | |
| data_type: Some(data_type), | |
| charset: None, | |
| target_before_value: true, | |
| styles, | |
| }) | |
| } | |
| /// Parse a SQL CONVERT function: | |
| /// - `CONVERT('héhé' USING utf8mb4)` (MySQL) | |
| /// - `CONVERT('héhé', CHAR CHARACTER SET utf8mb4)` (MySQL) | |
| /// - `CONVERT(DECIMAL(10, 5), 42)` (MSSQL) - the type comes first | |
| pub fn parse_convert_expr(&mut self, is_try: bool) -> Result<Expr, ParserError> { | |
| if self.dialect.convert_type_before_value() { | |
| return self.parse_mssql_convert(is_try); | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| if self.parse_keyword(Keyword::USING) { | |
| let charset = self.parse_object_name(false)?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Expr::Convert { | |
| is_try, | |
| expr: Box::new(expr), | |
| data_type: None, | |
| charset: Some(charset), | |
| target_before_value: false, | |
| styles: vec![], | |
| }); | |
| } | |
| self.expect_token(&Token::Comma)?; | |
| let data_type = self.parse_data_type()?; | |
| let charset = if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Convert { | |
| is_try, | |
| expr: Box::new(expr), | |
| data_type: Some(data_type), | |
| charset, | |
| target_before_value: false, | |
| styles: vec![], | |
| }) | |
| } | |
| /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` | |
| pub fn parse_cast_expr(&mut self, kind: CastKind) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let data_type = if self.dialect.supports_cast_empty_data_type_to_unspecified() | |
| && self.peek_token_ref().token == Token::RParen | |
| { | |
| DataType::Unspecified | |
| } else { | |
| self.parse_data_type()? | |
| }; | |
| let format = self.parse_optional_cast_format()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Cast { | |
| kind, | |
| expr: Box::new(expr), | |
| data_type, | |
| format, | |
| }) | |
| } | |
| /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. | |
| pub fn parse_exists_expr(&mut self, negated: bool) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let exists_node = Expr::Exists { | |
| negated, | |
| subquery: self.parse_query()?, | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(exists_node) | |
| } | |
| /// Parse a SQL `EXTRACT` expression e.g. `EXTRACT(YEAR FROM date)`. | |
| pub fn parse_extract_expr(&mut self) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let field = self.parse_date_time_field()?; | |
| let syntax = if self.parse_keyword(Keyword::FROM) { | |
| ExtractSyntax::From | |
| } else if self.dialect.supports_extract_comma_syntax() && self.consume_token(&Token::Comma) | |
| { | |
| ExtractSyntax::Comma | |
| } else { | |
| return Err(ParserError::ParserError( | |
| "Expected 'FROM' or ','".to_string(), | |
| )); | |
| }; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Extract { | |
| field, | |
| expr: Box::new(expr), | |
| syntax, | |
| }) | |
| } | |
| /// Parse a `CEIL` or `FLOOR` expression. | |
| pub fn parse_ceil_floor_expr(&mut self, is_ceil: bool) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| // Parse `CEIL/FLOOR(expr)` | |
| let field = if self.parse_keyword(Keyword::TO) { | |
| // Parse `CEIL/FLOOR(expr TO DateTimeField)` | |
| CeilFloorKind::DateTimeField(self.parse_date_time_field()?) | |
| } else if self.consume_token(&Token::Comma) { | |
| // Parse `CEIL/FLOOR(expr, scale)` | |
| let v = self.parse_value()?; | |
| if matches!(v.value, Value::Number(_, _)) { | |
| CeilFloorKind::Scale(v) | |
| } else { | |
| return Err(ParserError::ParserError( | |
| "Scale field can only be of number type".to_string(), | |
| )); | |
| } | |
| } else { | |
| CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| if is_ceil { | |
| Ok(Expr::Ceil { | |
| expr: Box::new(expr), | |
| field, | |
| }) | |
| } else { | |
| Ok(Expr::Floor { | |
| expr: Box::new(expr), | |
| field, | |
| }) | |
| } | |
| } | |
| /// Parse a `POSITION` expression. | |
| pub fn parse_position_expr(&mut self, ident: Ident) -> Result<Expr, ParserError> { | |
| let between_prec = self.dialect.prec_value(Precedence::Between); | |
| // Memoize a failed `IN` form: the function-call fallback re-parses the | |
| // same argument, so nested `POSITION(POSITION(...))` would take 2^N. | |
| let memo_key = (self.index, self.state); | |
| if self.failed_position_syntax_positions.contains(&memo_key) { | |
| return self.parse_function(ObjectName::from(vec![ident])); | |
| } | |
| let position_expr = self.maybe_parse(|p| { | |
| // PARSE SELECT POSITION('@' in field) | |
| p.expect_token(&Token::LParen)?; | |
| // Parse the subexpr till the IN keyword | |
| let expr = p.parse_subexpr(between_prec)?; | |
| p.expect_keyword_is(Keyword::IN)?; | |
| let from = p.parse_expr()?; | |
| p.expect_token(&Token::RParen)?; | |
| Ok(Expr::Position { | |
| expr: Box::new(expr), | |
| r#in: Box::new(from), | |
| }) | |
| })?; | |
| match position_expr { | |
| Some(expr) => Ok(expr), | |
| // Snowflake supports `position` as an ordinary function call | |
| // without the special `IN` syntax. | |
| None => { | |
| self.failed_position_syntax_positions.insert(memo_key); | |
| self.parse_function(ObjectName::from(vec![ident])) | |
| } | |
| } | |
| } | |
| /// Parse `SUBSTRING`/`SUBSTR` expressions: `SUBSTRING(expr FROM start FOR length)` or `SUBSTR(expr, start, length)`. | |
| pub fn parse_substring(&mut self) -> Result<Expr, ParserError> { | |
| let shorthand = match self.expect_one_of_keywords(&[Keyword::SUBSTR, Keyword::SUBSTRING])? { | |
| Keyword::SUBSTR => true, | |
| Keyword::SUBSTRING => false, | |
| _ => { | |
| self.prev_token(); | |
| return self.expected_ref("SUBSTR or SUBSTRING", self.peek_token_ref()); | |
| } | |
| }; | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| let mut from_expr = None; | |
| let special = self.consume_token(&Token::Comma); | |
| if special || self.parse_keyword(Keyword::FROM) { | |
| from_expr = Some(self.parse_expr()?); | |
| } | |
| let mut to_expr = None; | |
| if self.parse_keyword(Keyword::FOR) || self.consume_token(&Token::Comma) { | |
| to_expr = Some(self.parse_expr()?); | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Substring { | |
| expr: Box::new(expr), | |
| substring_from: from_expr.map(Box::new), | |
| substring_for: to_expr.map(Box::new), | |
| special, | |
| shorthand, | |
| }) | |
| } | |
| /// Parse an OVERLAY expression. | |
| /// | |
| /// See [Expr::Overlay] | |
| pub fn parse_overlay_expr(&mut self) -> Result<Expr, ParserError> { | |
| // PARSE OVERLAY (EXPR PLACING EXPR FROM 1 [FOR 3]) | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::PLACING)?; | |
| let what_expr = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::FROM)?; | |
| let from_expr = self.parse_expr()?; | |
| let mut for_expr = None; | |
| if self.parse_keyword(Keyword::FOR) { | |
| for_expr = Some(self.parse_expr()?); | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Overlay { | |
| expr: Box::new(expr), | |
| overlay_what: Box::new(what_expr), | |
| overlay_from: Box::new(from_expr), | |
| overlay_for: for_expr.map(Box::new), | |
| }) | |
| } | |
| /// ```sql | |
| /// TRIM ([WHERE] ['text' FROM] 'text') | |
| /// TRIM ('text') | |
| /// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic | |
| /// ``` | |
| pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let mut trim_where = None; | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING].contains(&word.keyword) { | |
| trim_where = Some(self.parse_trim_where()?); | |
| } | |
| } | |
| if self.dialect.supports_comma_separated_trim() && self.parse_keyword(Keyword::FROM) { | |
| let expr = self.parse_expr()?; | |
| let trim_characters = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_comma_separated(Parser::parse_expr)?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Expr::Trim { | |
| expr: Box::new(expr), | |
| trim_where, | |
| trim_what: None, | |
| trim_characters, | |
| }); | |
| } | |
| let expr = self.parse_expr()?; | |
| if self.parse_keyword(Keyword::FROM) { | |
| let trim_what = Box::new(expr); | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Trim { | |
| expr: Box::new(expr), | |
| trim_where, | |
| trim_what: Some(trim_what), | |
| trim_characters: None, | |
| }) | |
| } else if self.dialect.supports_comma_separated_trim() && self.consume_token(&Token::Comma) | |
| { | |
| let characters = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Trim { | |
| expr: Box::new(expr), | |
| trim_where, | |
| trim_what: None, | |
| trim_characters: Some(characters), | |
| }) | |
| } else { | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Trim { | |
| expr: Box::new(expr), | |
| trim_where, | |
| trim_what: None, | |
| trim_characters: None, | |
| }) | |
| } | |
| } | |
| /// Parse the `WHERE` field for a `TRIM` expression. | |
| /// | |
| /// See [TrimWhereField] | |
| pub fn parse_trim_where(&mut self) -> Result<TrimWhereField, ParserError> { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::BOTH => Ok(TrimWhereField::Both), | |
| Keyword::LEADING => Ok(TrimWhereField::Leading), | |
| Keyword::TRAILING => Ok(TrimWhereField::Trailing), | |
| _ => self.expected("trim_where field", next_token)?, | |
| }, | |
| _ => self.expected("trim_where field", next_token), | |
| } | |
| } | |
| /// Parses an array expression `[ex1, ex2, ..]` | |
| /// if `named` is `true`, came from an expression like `ARRAY[ex1, ex2]` | |
| pub fn parse_array_expr(&mut self, named: bool) -> Result<Expr, ParserError> { | |
| let exprs = self.parse_comma_separated0(Parser::parse_expr, Token::RBracket)?; | |
| self.expect_token(&Token::RBracket)?; | |
| Ok(Expr::Array(Array { elem: exprs, named })) | |
| } | |
| /// Parse the `ON OVERFLOW` clause for `LISTAGG`. | |
| /// | |
| /// See [`ListAggOnOverflow`] | |
| pub fn parse_listagg_on_overflow(&mut self) -> Result<Option<ListAggOnOverflow>, ParserError> { | |
| if self.parse_keywords(&[Keyword::ON, Keyword::OVERFLOW]) { | |
| if self.parse_keyword(Keyword::ERROR) { | |
| Ok(Some(ListAggOnOverflow::Error)) | |
| } else { | |
| self.expect_keyword_is(Keyword::TRUNCATE)?; | |
| let filler = match &self.peek_token_ref().token { | |
| Token::Word(w) | |
| if w.keyword == Keyword::WITH || w.keyword == Keyword::WITHOUT => | |
| { | |
| None | |
| } | |
| Token::SingleQuotedString(_) | |
| | Token::EscapedStringLiteral(_) | |
| | Token::UnicodeStringLiteral(_) | |
| | Token::NationalStringLiteral(_) | |
| | Token::QuoteDelimitedStringLiteral(_) | |
| | Token::NationalQuoteDelimitedStringLiteral(_) | |
| | Token::HexStringLiteral(_) => Some(Box::new(self.parse_expr()?)), | |
| _ => self.expected_ref( | |
| "either filler, WITH, or WITHOUT in LISTAGG", | |
| self.peek_token_ref(), | |
| )?, | |
| }; | |
| let with_count = self.parse_keyword(Keyword::WITH); | |
| if !with_count && !self.parse_keyword(Keyword::WITHOUT) { | |
| self.expected_ref("either WITH or WITHOUT in LISTAGG", self.peek_token_ref())?; | |
| } | |
| self.expect_keyword_is(Keyword::COUNT)?; | |
| Ok(Some(ListAggOnOverflow::Truncate { filler, with_count })) | |
| } | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a date/time field for `EXTRACT`, interval qualifiers, and ceil/floor operations. | |
| /// | |
| /// `EXTRACT` supports a wider set of date/time fields than interval qualifiers, | |
| /// so this function may need to be split in two. | |
| /// | |
| /// See [`DateTimeField`] | |
| pub fn parse_date_time_field(&mut self) -> Result<DateTimeField, ParserError> { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::YEAR => Ok(DateTimeField::Year), | |
| Keyword::YEARS => Ok(DateTimeField::Years), | |
| Keyword::MONTH => Ok(DateTimeField::Month), | |
| Keyword::MONTHS => Ok(DateTimeField::Months), | |
| Keyword::WEEK => { | |
| let week_day = if dialect_of!(self is BigQueryDialect | GenericDialect) | |
| && self.consume_token(&Token::LParen) | |
| { | |
| let week_day = self.parse_identifier()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(week_day) | |
| } else { | |
| None | |
| }; | |
| Ok(DateTimeField::Week(week_day)) | |
| } | |
| Keyword::WEEKS => Ok(DateTimeField::Weeks), | |
| Keyword::DAY => Ok(DateTimeField::Day), | |
| Keyword::DAYOFWEEK => Ok(DateTimeField::DayOfWeek), | |
| Keyword::DAYOFYEAR => Ok(DateTimeField::DayOfYear), | |
| Keyword::DAYS => Ok(DateTimeField::Days), | |
| Keyword::DATE => Ok(DateTimeField::Date), | |
| Keyword::DATETIME => Ok(DateTimeField::Datetime), | |
| Keyword::HOUR => Ok(DateTimeField::Hour), | |
| Keyword::HOURS => Ok(DateTimeField::Hours), | |
| Keyword::MINUTE => Ok(DateTimeField::Minute), | |
| Keyword::MINUTES => Ok(DateTimeField::Minutes), | |
| Keyword::SECOND => Ok(DateTimeField::Second), | |
| Keyword::SECONDS => Ok(DateTimeField::Seconds), | |
| Keyword::CENTURY => Ok(DateTimeField::Century), | |
| Keyword::DECADE => Ok(DateTimeField::Decade), | |
| Keyword::DOY => Ok(DateTimeField::Doy), | |
| Keyword::DOW => Ok(DateTimeField::Dow), | |
| Keyword::EPOCH => Ok(DateTimeField::Epoch), | |
| Keyword::ISODOW => Ok(DateTimeField::Isodow), | |
| Keyword::ISOYEAR => Ok(DateTimeField::Isoyear), | |
| Keyword::ISOWEEK => Ok(DateTimeField::IsoWeek), | |
| Keyword::JULIAN => Ok(DateTimeField::Julian), | |
| Keyword::MICROSECOND => Ok(DateTimeField::Microsecond), | |
| Keyword::MICROSECONDS => Ok(DateTimeField::Microseconds), | |
| Keyword::MILLENIUM => Ok(DateTimeField::Millenium), | |
| Keyword::MILLENNIUM => Ok(DateTimeField::Millennium), | |
| Keyword::MILLISECOND => Ok(DateTimeField::Millisecond), | |
| Keyword::MILLISECONDS => Ok(DateTimeField::Milliseconds), | |
| Keyword::NANOSECOND => Ok(DateTimeField::Nanosecond), | |
| Keyword::NANOSECONDS => Ok(DateTimeField::Nanoseconds), | |
| Keyword::QUARTER => Ok(DateTimeField::Quarter), | |
| Keyword::TIME => Ok(DateTimeField::Time), | |
| Keyword::TIMEZONE => Ok(DateTimeField::Timezone), | |
| Keyword::TIMEZONE_ABBR => Ok(DateTimeField::TimezoneAbbr), | |
| Keyword::TIMEZONE_HOUR => Ok(DateTimeField::TimezoneHour), | |
| Keyword::TIMEZONE_MINUTE => Ok(DateTimeField::TimezoneMinute), | |
| Keyword::TIMEZONE_REGION => Ok(DateTimeField::TimezoneRegion), | |
| _ if self.dialect.allow_extract_custom() => { | |
| self.prev_token(); | |
| let custom = self.parse_identifier()?; | |
| Ok(DateTimeField::Custom(custom)) | |
| } | |
| _ => self.expected("date/time field", next_token), | |
| }, | |
| Token::SingleQuotedString(_) if self.dialect.allow_extract_single_quotes() => { | |
| self.prev_token(); | |
| let custom = self.parse_identifier()?; | |
| Ok(DateTimeField::Custom(custom)) | |
| } | |
| _ => self.expected("date/time field", next_token), | |
| } | |
| } | |
| /// Parse a `NOT` expression. | |
| /// | |
| /// Represented in the AST as `Expr::UnaryOp` with `UnaryOperator::Not`. | |
| pub fn parse_not(&mut self) -> Result<Expr, ParserError> { | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::EXISTS => { | |
| let negated = true; | |
| let _ = self.parse_keyword(Keyword::EXISTS); | |
| self.parse_exists_expr(negated) | |
| } | |
| _ => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::Not, | |
| expr: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?, | |
| ), | |
| }), | |
| }, | |
| _ => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::Not, | |
| expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?), | |
| }), | |
| } | |
| } | |
| /// Parse expression types that start with a left brace '{'. | |
| /// Examples: | |
| /// ```sql | |
| /// -- Dictionary expr. | |
| /// {'key1': 'value1', 'key2': 'value2'} | |
| /// | |
| /// -- Function call using the ODBC syntax. | |
| /// { fn CONCAT('foo', 'bar') } | |
| /// ``` | |
| fn parse_lbrace_expr(&mut self) -> Result<Expr, ParserError> { | |
| let token = self.expect_token(&Token::LBrace)?; | |
| if let Some(fn_expr) = self.maybe_parse_odbc_body()? { | |
| self.expect_token(&Token::RBrace)?; | |
| return Ok(fn_expr); | |
| } | |
| if self.dialect.supports_dictionary_syntax() { | |
| self.prev_token(); // Put back the '{' | |
| return self.parse_dictionary(); | |
| } | |
| self.expected("an expression", token) | |
| } | |
| /// Parses fulltext expressions [`sqlparser::ast::Expr::MatchAgainst`] | |
| /// | |
| /// # Errors | |
| /// This method will raise an error if the column list is empty or with invalid identifiers, | |
| /// the match expression is not a literal string, or if the search modifier is not valid. | |
| pub fn parse_match_against(&mut self) -> Result<Expr, ParserError> { | |
| let columns = self.parse_parenthesized_qualified_column_list(Mandatory, false)?; | |
| self.expect_keyword_is(Keyword::AGAINST)?; | |
| self.expect_token(&Token::LParen)?; | |
| // MySQL is too permissive about the value, IMO we can't validate it perfectly on syntax level. | |
| let match_value = self.parse_value()?; | |
| let in_natural_language_mode_keywords = &[ | |
| Keyword::IN, | |
| Keyword::NATURAL, | |
| Keyword::LANGUAGE, | |
| Keyword::MODE, | |
| ]; | |
| let with_query_expansion_keywords = &[Keyword::WITH, Keyword::QUERY, Keyword::EXPANSION]; | |
| let in_boolean_mode_keywords = &[Keyword::IN, Keyword::BOOLEAN, Keyword::MODE]; | |
| let opt_search_modifier = if self.parse_keywords(in_natural_language_mode_keywords) { | |
| if self.parse_keywords(with_query_expansion_keywords) { | |
| Some(SearchModifier::InNaturalLanguageModeWithQueryExpansion) | |
| } else { | |
| Some(SearchModifier::InNaturalLanguageMode) | |
| } | |
| } else if self.parse_keywords(in_boolean_mode_keywords) { | |
| Some(SearchModifier::InBooleanMode) | |
| } else if self.parse_keywords(with_query_expansion_keywords) { | |
| Some(SearchModifier::WithQueryExpansion) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::MatchAgainst { | |
| columns, | |
| match_value, | |
| opt_search_modifier, | |
| }) | |
| } | |
| /// Parse an `INTERVAL` expression. | |
| /// | |
| /// Some syntactically valid intervals: | |
| /// | |
| /// ```sql | |
| /// 1. INTERVAL '1' DAY | |
| /// 2. INTERVAL '1-1' YEAR TO MONTH | |
| /// 3. INTERVAL '1' SECOND | |
| /// 4. INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5) | |
| /// 5. INTERVAL '1.1' SECOND (2, 2) | |
| /// 6. INTERVAL '1:1' HOUR (5) TO MINUTE (5) | |
| /// 7. (MySql & BigQuery only): INTERVAL 1 DAY | |
| /// ``` | |
| /// | |
| /// Note that we do not currently attempt to parse the quoted value. | |
| #[cfg_attr(feature = "recursive-protection", recursive::recursive)] | |
| pub fn parse_interval(&mut self) -> Result<Expr, ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| // The SQL standard allows an optional sign before the value string, but | |
| // it is not clear if any implementations support that syntax, so we | |
| // don't currently try to parse it. (The sign can instead be included | |
| // inside the value string.) | |
| // to match the different flavours of INTERVAL syntax, we only allow expressions | |
| // if the dialect requires an interval qualifier, | |
| // see https://github.com/sqlparser-rs/sqlparser-rs/pull/1398 for more details | |
| let value = if self.dialect.require_interval_qualifier() { | |
| // parse a whole expression so `INTERVAL 1 + 1 DAY` is valid | |
| self.parse_expr()? | |
| } else { | |
| // parse a prefix expression so `INTERVAL 1 DAY` is valid, but `INTERVAL 1 + 1 DAY` is not | |
| // this also means that `INTERVAL '5 days' > INTERVAL '1 day'` treated properly | |
| self.parse_prefix()? | |
| }; | |
| // Following the string literal is a qualifier which indicates the units | |
| // of the duration specified in the string literal. | |
| // | |
| // Note that PostgreSQL allows omitting the qualifier, so we provide | |
| // this more general implementation. | |
| let leading_field = if self.next_token_is_temporal_unit() { | |
| Some(self.parse_date_time_field()?) | |
| } else if self.dialect.require_interval_qualifier() { | |
| return parser_err!( | |
| "INTERVAL requires a unit after the literal value", | |
| self.peek_token_ref().span.start | |
| ); | |
| } else { | |
| None | |
| }; | |
| let (leading_precision, last_field, fsec_precision) = | |
| if leading_field == Some(DateTimeField::Second) { | |
| // SQL mandates special syntax for `SECOND TO SECOND` literals. | |
| // Instead of | |
| // `SECOND [(<leading precision>)] TO SECOND[(<fractional seconds precision>)]` | |
| // one must use the special format: | |
| // `SECOND [( <leading precision> [ , <fractional seconds precision>] )]` | |
| let last_field = None; | |
| let (leading_precision, fsec_precision) = self.parse_optional_precision_scale()?; | |
| (leading_precision, last_field, fsec_precision) | |
| } else { | |
| let leading_precision = self.parse_optional_precision()?; | |
| if self.parse_keyword(Keyword::TO) { | |
| let last_field = Some(self.parse_date_time_field()?); | |
| let fsec_precision = if last_field == Some(DateTimeField::Second) { | |
| self.parse_optional_precision()? | |
| } else { | |
| None | |
| }; | |
| (leading_precision, last_field, fsec_precision) | |
| } else { | |
| (leading_precision, None, None) | |
| } | |
| }; | |
| Ok(Expr::Interval(Interval { | |
| value: Box::new(value), | |
| leading_field, | |
| leading_precision, | |
| last_field, | |
| fractional_seconds_precision: fsec_precision, | |
| })) | |
| } | |
| /// Peek at the next token and determine if it is a temporal unit | |
| /// like `second`. | |
| pub fn next_token_is_temporal_unit(&mut self) -> bool { | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| matches!( | |
| word.keyword, | |
| Keyword::YEAR | |
| | Keyword::YEARS | |
| | Keyword::MONTH | |
| | Keyword::MONTHS | |
| | Keyword::WEEK | |
| | Keyword::WEEKS | |
| | Keyword::DAY | |
| | Keyword::DAYS | |
| | Keyword::HOUR | |
| | Keyword::HOURS | |
| | Keyword::MINUTE | |
| | Keyword::MINUTES | |
| | Keyword::SECOND | |
| | Keyword::SECONDS | |
| | Keyword::CENTURY | |
| | Keyword::DECADE | |
| | Keyword::DOW | |
| | Keyword::DOY | |
| | Keyword::EPOCH | |
| | Keyword::ISODOW | |
| | Keyword::ISOYEAR | |
| | Keyword::JULIAN | |
| | Keyword::MICROSECOND | |
| | Keyword::MICROSECONDS | |
| | Keyword::MILLENIUM | |
| | Keyword::MILLENNIUM | |
| | Keyword::MILLISECOND | |
| | Keyword::MILLISECONDS | |
| | Keyword::NANOSECOND | |
| | Keyword::NANOSECONDS | |
| | Keyword::QUARTER | |
| | Keyword::TIMEZONE | |
| | Keyword::TIMEZONE_HOUR | |
| | Keyword::TIMEZONE_MINUTE | |
| ) | |
| } else { | |
| false | |
| } | |
| } | |
| /// Syntax | |
| /// ```sql | |
| /// -- typed | |
| /// STRUCT<[field_name] field_type, ...>( expr1 [, ... ]) | |
| /// -- typeless | |
| /// STRUCT( expr1 [AS field_name] [, ... ]) | |
| /// ``` | |
| fn parse_struct_literal(&mut self) -> Result<Expr, ParserError> { | |
| // Parse the fields definition if exist `<[field_name] field_type, ...>` | |
| self.prev_token(); | |
| let (fields, trailing_bracket) = | |
| self.parse_struct_type_def(Self::parse_struct_field_def)?; | |
| if trailing_bracket.0 { | |
| return parser_err!( | |
| "unmatched > in STRUCT literal", | |
| self.peek_token_ref().span.start | |
| ); | |
| } | |
| // Parse the struct values `(expr1 [, ... ])` | |
| self.expect_token(&Token::LParen)?; | |
| let values = self | |
| .parse_comma_separated(|parser| parser.parse_struct_field_expr(!fields.is_empty()))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::Struct { values, fields }) | |
| } | |
| /// Parse an expression value for a struct literal | |
| /// Syntax | |
| /// ```sql | |
| /// expr [AS name] | |
| /// ``` | |
| /// | |
| /// For biquery [1], Parameter typed_syntax is set to true if the expression | |
| /// is to be parsed as a field expression declared using typed | |
| /// struct syntax [2], and false if using typeless struct syntax [3]. | |
| /// | |
| /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#constructing_a_struct | |
| /// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#typed_struct_syntax | |
| /// [3]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#typeless_struct_syntax | |
| fn parse_struct_field_expr(&mut self, typed_syntax: bool) -> Result<Expr, ParserError> { | |
| let expr = self.parse_expr()?; | |
| if self.parse_keyword(Keyword::AS) { | |
| if typed_syntax { | |
| return parser_err!("Typed syntax does not allow AS", { | |
| self.prev_token(); | |
| self.peek_token_ref().span.start | |
| }); | |
| } | |
| let field_name = self.parse_identifier()?; | |
| Ok(Expr::Named { | |
| expr: expr.into(), | |
| name: field_name, | |
| }) | |
| } else { | |
| Ok(expr) | |
| } | |
| } | |
| /// Parse a Struct type definition as a sequence of field-value pairs. | |
| /// The syntax of the Struct elem differs by dialect so it is customised | |
| /// by the `elem_parser` argument. | |
| /// | |
| /// Syntax | |
| /// ```sql | |
| /// Hive: | |
| /// STRUCT<field_name: field_type> | |
| /// | |
| /// BigQuery: | |
| /// STRUCT<[field_name] field_type> | |
| /// ``` | |
| fn parse_struct_type_def<F>( | |
| &mut self, | |
| mut elem_parser: F, | |
| ) -> Result<(Vec<StructField>, MatchedTrailingBracket), ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<(StructField, MatchedTrailingBracket), ParserError>, | |
| { | |
| self.expect_keyword_is(Keyword::STRUCT)?; | |
| // Nothing to do if we have no type information. | |
| if self.peek_token_ref().token != Token::Lt { | |
| return Ok((Default::default(), false.into())); | |
| } | |
| self.next_token(); | |
| let mut field_defs = vec![]; | |
| let trailing_bracket = loop { | |
| let (def, trailing_bracket) = elem_parser(self)?; | |
| field_defs.push(def); | |
| // The struct field definition is finished if it occurs `>>` or comma. | |
| if trailing_bracket.0 || !self.consume_token(&Token::Comma) { | |
| break trailing_bracket; | |
| } | |
| }; | |
| Ok(( | |
| field_defs, | |
| self.expect_closing_angle_bracket(trailing_bracket)?, | |
| )) | |
| } | |
| /// Duckdb Struct Data Type <https://duckdb.org/docs/sql/data_types/struct.html#retrieving-from-structs> | |
| fn parse_duckdb_struct_type_def(&mut self) -> Result<Vec<StructField>, ParserError> { | |
| self.expect_keyword_is(Keyword::STRUCT)?; | |
| self.expect_token(&Token::LParen)?; | |
| let struct_body = self.parse_comma_separated(|parser| { | |
| let field_name = parser.parse_identifier()?; | |
| let field_type = parser.parse_data_type()?; | |
| Ok(StructField { | |
| field_name: Some(field_name), | |
| field_type, | |
| options: None, | |
| }) | |
| }); | |
| self.expect_token(&Token::RParen)?; | |
| struct_body | |
| } | |
| /// Parse a field definition in a [struct] or [tuple]. | |
| /// Syntax: | |
| /// | |
| /// ```sql | |
| /// [field_name] field_type | |
| /// field_name: field_type | |
| /// ``` | |
| /// | |
| /// [struct]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#declaring_a_struct_type | |
| /// [tuple]: https://clickhouse.com/docs/en/sql-reference/data-types/tuple | |
| /// [databricks]: https://docs.databricks.com/en/sql/language-manual/data-types/struct-type.html | |
| fn parse_struct_field_def( | |
| &mut self, | |
| ) -> Result<(StructField, MatchedTrailingBracket), ParserError> { | |
| // Look beyond the next item to infer whether both field name | |
| // and type are specified. | |
| let is_named_field = matches!( | |
| (self.peek_nth_token(0).token, self.peek_nth_token(1).token), | |
| (Token::Word(_), Token::Word(_)) | (Token::Word(_), Token::Colon) | |
| ); | |
| let field_name = if is_named_field { | |
| let name = self.parse_identifier()?; | |
| let _ = self.consume_token(&Token::Colon); | |
| Some(name) | |
| } else { | |
| None | |
| }; | |
| let (field_type, trailing_bracket) = self.parse_data_type_with_optional_collation()?; | |
| let options = self.maybe_parse_options(Keyword::OPTIONS)?; | |
| Ok(( | |
| StructField { | |
| field_name, | |
| field_type, | |
| options, | |
| }, | |
| trailing_bracket, | |
| )) | |
| } | |
| /// DuckDB specific: Parse a Union type definition as a sequence of field-value pairs. | |
| /// | |
| /// Syntax: | |
| /// | |
| /// ```sql | |
| /// UNION(field_name field_type[,...]) | |
| /// ``` | |
| /// | |
| /// [1]: https://duckdb.org/docs/sql/data_types/union.html | |
| fn parse_union_type_def(&mut self) -> Result<Vec<UnionField>, ParserError> { | |
| self.expect_keyword_is(Keyword::UNION)?; | |
| self.expect_token(&Token::LParen)?; | |
| let fields = self.parse_comma_separated(|p| { | |
| Ok(UnionField { | |
| field_name: p.parse_identifier()?, | |
| field_type: p.parse_data_type()?, | |
| }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(fields) | |
| } | |
| /// DuckDB and ClickHouse specific: Parse a duckdb [dictionary] or a clickhouse [map] setting | |
| /// | |
| /// Syntax: | |
| /// | |
| /// ```sql | |
| /// {'field_name': expr1[, ... ]} | |
| /// ``` | |
| /// | |
| /// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs | |
| /// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters | |
| fn parse_dictionary(&mut self) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LBrace)?; | |
| let fields = self.parse_comma_separated0(Self::parse_dictionary_field, Token::RBrace)?; | |
| self.expect_token(&Token::RBrace)?; | |
| Ok(Expr::Dictionary(fields)) | |
| } | |
| /// Parse a field for a duckdb [dictionary] or a clickhouse [map] setting | |
| /// | |
| /// Syntax | |
| /// | |
| /// ```sql | |
| /// 'name': expr | |
| /// ``` | |
| /// | |
| /// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs | |
| /// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters | |
| fn parse_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> { | |
| let key = self.parse_identifier()?; | |
| self.expect_token(&Token::Colon)?; | |
| let expr = self.parse_expr()?; | |
| Ok(DictionaryField { | |
| key, | |
| value: Box::new(expr), | |
| }) | |
| } | |
| /// DuckDB specific: Parse a duckdb [map] | |
| /// | |
| /// Syntax: | |
| /// | |
| /// ```sql | |
| /// Map {key1: value1[, ... ]} | |
| /// ``` | |
| /// | |
| /// [map]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps | |
| fn parse_duckdb_map_literal(&mut self) -> Result<Expr, ParserError> { | |
| self.expect_token(&Token::LBrace)?; | |
| let fields = self.parse_comma_separated0(Self::parse_duckdb_map_field, Token::RBrace)?; | |
| self.expect_token(&Token::RBrace)?; | |
| Ok(Expr::Map(Map { entries: fields })) | |
| } | |
| /// Parse a field for a duckdb [map] | |
| /// | |
| /// Syntax | |
| /// | |
| /// ```sql | |
| /// key: value | |
| /// ``` | |
| /// | |
| /// [map]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps | |
| fn parse_duckdb_map_field(&mut self) -> Result<MapEntry, ParserError> { | |
| // Stop before `:` so it can act as a key/value separator | |
| let key = self.parse_subexpr(self.dialect.prec_value(Precedence::Colon))?; | |
| self.expect_token(&Token::Colon)?; | |
| let value = self.parse_expr()?; | |
| Ok(MapEntry { | |
| key: Box::new(key), | |
| value: Box::new(value), | |
| }) | |
| } | |
| /// Parse a parenthesized MAP type (ClickHouse or Snowflake). | |
| /// | |
| /// Syntax | |
| /// | |
| /// ```sql | |
| /// Map(key_data_type, value_data_type) | |
| /// ``` | |
| /// | |
| /// [map]: https://clickhouse.com/docs/en/sql-reference/data-types/map | |
| fn parse_parenthesized_map_type_def( | |
| &mut self, | |
| ) -> Result<(DataType, DataType, bool), ParserError> { | |
| self.expect_keyword_is(Keyword::MAP)?; | |
| self.expect_token(&Token::LParen)?; | |
| let key_data_type = self.parse_data_type()?; | |
| self.expect_token(&Token::Comma)?; | |
| let value_data_type = self.parse_data_type()?; | |
| let value_not_null = self.dialect.supports_map_value_not_null() | |
| && self.parse_keywords(&[Keyword::NOT, Keyword::NULL]); | |
| self.expect_token(&Token::RParen)?; | |
| Ok((key_data_type, value_data_type, value_not_null)) | |
| } | |
| /// Parse clickhouse [tuple] | |
| /// | |
| /// Syntax | |
| /// | |
| /// ```sql | |
| /// Tuple([field_name] field_type, ...) | |
| /// ``` | |
| /// | |
| /// [tuple]: https://clickhouse.com/docs/en/sql-reference/data-types/tuple | |
| fn parse_click_house_tuple_def(&mut self) -> Result<Vec<StructField>, ParserError> { | |
| self.expect_keyword_is(Keyword::TUPLE)?; | |
| self.expect_token(&Token::LParen)?; | |
| let mut field_defs = vec![]; | |
| loop { | |
| let (def, _) = self.parse_struct_field_def()?; | |
| field_defs.push(def); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(field_defs) | |
| } | |
| /// For nested types that use the angle bracket syntax, this matches either | |
| /// `>`, `>>` or nothing depending on which variant is expected (specified by the previously | |
| /// matched `trailing_bracket` argument). It returns whether there is a trailing | |
| /// left to be matched - (i.e. if '>>' was matched). | |
| fn expect_closing_angle_bracket( | |
| &mut self, | |
| trailing_bracket: MatchedTrailingBracket, | |
| ) -> Result<MatchedTrailingBracket, ParserError> { | |
| let trailing_bracket = if !trailing_bracket.0 { | |
| match &self.peek_token_ref().token { | |
| Token::Gt => { | |
| self.next_token(); | |
| false.into() | |
| } | |
| Token::ShiftRight => { | |
| self.next_token(); | |
| true.into() | |
| } | |
| _ => return self.expected_ref(">", self.peek_token_ref()), | |
| } | |
| } else { | |
| false.into() | |
| }; | |
| Ok(trailing_bracket) | |
| } | |
| /// Parse an operator following an expression | |
| pub fn parse_infix(&mut self, expr: Expr, precedence: u8) -> Result<Expr, ParserError> { | |
| // allow the dialect to override infix parsing | |
| if let Some(infix) = self.dialect.parse_infix(self, &expr, precedence) { | |
| return infix; | |
| } | |
| let dialect = self.dialect; | |
| self.advance_token(); | |
| let tok = self.get_current_token(); | |
| debug!("infix: {tok:?}"); | |
| let tok_index = self.get_current_index(); | |
| let span = tok.span; | |
| let regular_binary_operator = match &tok.token { | |
| Token::Spaceship => Some(BinaryOperator::Spaceship), | |
| Token::DoubleEq => Some(BinaryOperator::Eq), | |
| Token::Assignment => Some(BinaryOperator::Assignment), | |
| Token::Eq => Some(BinaryOperator::Eq), | |
| Token::Neq => Some(BinaryOperator::NotEq), | |
| Token::Gt => Some(BinaryOperator::Gt), | |
| Token::GtEq => Some(BinaryOperator::GtEq), | |
| Token::Lt => Some(BinaryOperator::Lt), | |
| Token::LtEq => Some(BinaryOperator::LtEq), | |
| Token::Plus => Some(BinaryOperator::Plus), | |
| Token::Minus => Some(BinaryOperator::Minus), | |
| Token::Mul => Some(BinaryOperator::Multiply), | |
| Token::Mod => Some(BinaryOperator::Modulo), | |
| Token::StringConcat => Some(BinaryOperator::StringConcat), | |
| Token::Pipe => Some(BinaryOperator::BitwiseOr), | |
| Token::Caret => { | |
| // In PostgreSQL, ^ stands for the exponentiation operation, | |
| // and # stands for XOR. See https://www.postgresql.org/docs/current/functions-math.html | |
| if dialect_is!(dialect is PostgreSqlDialect) { | |
| Some(BinaryOperator::PGExp) | |
| } else { | |
| Some(BinaryOperator::BitwiseXor) | |
| } | |
| } | |
| Token::Ampersand => Some(BinaryOperator::BitwiseAnd), | |
| Token::Div => Some(BinaryOperator::Divide), | |
| Token::DuckIntDiv if dialect_is!(dialect is DuckDbDialect | GenericDialect) => { | |
| Some(BinaryOperator::DuckIntegerDivide) | |
| } | |
| Token::ShiftLeft if dialect.supports_bitwise_shift_operators() => { | |
| Some(BinaryOperator::PGBitwiseShiftLeft) | |
| } | |
| Token::ShiftRight if dialect.supports_bitwise_shift_operators() => { | |
| Some(BinaryOperator::PGBitwiseShiftRight) | |
| } | |
| Token::Sharp if dialect_is!(dialect is PostgreSqlDialect | RedshiftSqlDialect) => { | |
| Some(BinaryOperator::PGBitwiseXor) | |
| } | |
| Token::Overlap if dialect_is!(dialect is PostgreSqlDialect | RedshiftSqlDialect) => { | |
| Some(BinaryOperator::PGOverlap) | |
| } | |
| Token::Overlap if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => { | |
| Some(BinaryOperator::PGOverlap) | |
| } | |
| Token::Overlap if dialect.supports_double_ampersand_operator() => { | |
| Some(BinaryOperator::And) | |
| } | |
| Token::CaretAt if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => { | |
| Some(BinaryOperator::PGStartsWith) | |
| } | |
| Token::Tilde => Some(BinaryOperator::PGRegexMatch), | |
| Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch), | |
| Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch), | |
| Token::ExclamationMarkTildeAsterisk => Some(BinaryOperator::PGRegexNotIMatch), | |
| Token::DoubleTilde => Some(BinaryOperator::PGLikeMatch), | |
| Token::DoubleTildeAsterisk => Some(BinaryOperator::PGILikeMatch), | |
| Token::ExclamationMarkDoubleTilde => Some(BinaryOperator::PGNotLikeMatch), | |
| Token::ExclamationMarkDoubleTildeAsterisk => Some(BinaryOperator::PGNotILikeMatch), | |
| Token::Arrow => Some(BinaryOperator::Arrow), | |
| Token::LongArrow => Some(BinaryOperator::LongArrow), | |
| Token::HashArrow => Some(BinaryOperator::HashArrow), | |
| Token::HashLongArrow => Some(BinaryOperator::HashLongArrow), | |
| Token::AtArrow => Some(BinaryOperator::AtArrow), | |
| Token::ArrowAt => Some(BinaryOperator::ArrowAt), | |
| Token::HashMinus => Some(BinaryOperator::HashMinus), | |
| Token::AtQuestion => Some(BinaryOperator::AtQuestion), | |
| Token::AtAt => Some(BinaryOperator::AtAt), | |
| Token::Question => Some(BinaryOperator::Question), | |
| Token::QuestionAnd => Some(BinaryOperator::QuestionAnd), | |
| Token::QuestionPipe => Some(BinaryOperator::QuestionPipe), | |
| Token::CustomBinaryOperator(s) => Some(BinaryOperator::Custom(s.clone())), | |
| Token::DoubleSharp if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::DoubleHash) | |
| } | |
| Token::AmpersandLeftAngleBracket if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::AndLt) | |
| } | |
| Token::AmpersandRightAngleBracket if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::AndGt) | |
| } | |
| Token::QuestionMarkDash if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::QuestionDash) | |
| } | |
| Token::AmpersandLeftAngleBracketVerticalBar | |
| if self.dialect.supports_geometric_types() => | |
| { | |
| Some(BinaryOperator::AndLtPipe) | |
| } | |
| Token::VerticalBarAmpersandRightAngleBracket | |
| if self.dialect.supports_geometric_types() => | |
| { | |
| Some(BinaryOperator::PipeAndGt) | |
| } | |
| Token::TwoWayArrow if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::LtDashGt) | |
| } | |
| Token::LeftAngleBracketCaret if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::LtCaret) | |
| } | |
| Token::RightAngleBracketCaret if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::GtCaret) | |
| } | |
| Token::QuestionMarkSharp if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::QuestionHash) | |
| } | |
| Token::QuestionMarkDoubleVerticalBar if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::QuestionDoublePipe) | |
| } | |
| Token::QuestionMarkDashVerticalBar if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::QuestionDashPipe) | |
| } | |
| Token::TildeEqual if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::TildeEq) | |
| } | |
| Token::ShiftLeftVerticalBar if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::LtLtPipe) | |
| } | |
| Token::VerticalBarShiftRight if self.dialect.supports_geometric_types() => { | |
| Some(BinaryOperator::PipeGtGt) | |
| } | |
| Token::AtSign if self.dialect.supports_geometric_types() => Some(BinaryOperator::At), | |
| Token::Word(w) => match w.keyword { | |
| Keyword::AND => Some(BinaryOperator::And), | |
| Keyword::OR => Some(BinaryOperator::Or), | |
| Keyword::XOR => Some(BinaryOperator::Xor), | |
| Keyword::OVERLAPS => Some(BinaryOperator::Overlaps), | |
| Keyword::OPERATOR if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => { | |
| // there are special rules for operator names in | |
| // postgres so we can not use 'parse_object' | |
| // or similar. | |
| // See https://www.postgresql.org/docs/current/sql-createoperator.html | |
| Some(BinaryOperator::PGCustomBinaryOperator( | |
| self.parse_pg_operator_ident_parts()?, | |
| )) | |
| } | |
| _ => None, | |
| }, | |
| _ => None, | |
| }; | |
| let tok = self.token_at(tok_index); | |
| if let Some(op) = regular_binary_operator { | |
| if let Some(keyword) = | |
| self.parse_one_of_keywords(&[Keyword::ANY, Keyword::ALL, Keyword::SOME]) | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let right = if self.peek_sub_query() { | |
| // We have a subquery ahead (SELECT\WITH ...) need to rewind and | |
| // use the parenthesis for parsing the subquery as an expression. | |
| self.prev_token(); // LParen | |
| self.parse_subexpr(precedence)? | |
| } else { | |
| // Non-subquery expression | |
| let right = self.parse_subexpr(precedence)?; | |
| self.expect_token(&Token::RParen)?; | |
| right | |
| }; | |
| if !matches!( | |
| op, | |
| BinaryOperator::Gt | |
| | BinaryOperator::Lt | |
| | BinaryOperator::GtEq | |
| | BinaryOperator::LtEq | |
| | BinaryOperator::Eq | |
| | BinaryOperator::NotEq | |
| | BinaryOperator::PGRegexMatch | |
| | BinaryOperator::PGRegexIMatch | |
| | BinaryOperator::PGRegexNotMatch | |
| | BinaryOperator::PGRegexNotIMatch | |
| | BinaryOperator::PGLikeMatch | |
| | BinaryOperator::PGILikeMatch | |
| | BinaryOperator::PGNotLikeMatch | |
| | BinaryOperator::PGNotILikeMatch | |
| ) { | |
| return parser_err!( | |
| format!( | |
| "Expected one of [=, >, <, =>, =<, !=, ~, ~*, !~, !~*, ~~, ~~*, !~~, !~~*] as comparison operator, found: {op}" | |
| ), | |
| span.start | |
| ); | |
| }; | |
| Ok(match keyword { | |
| Keyword::ALL => Expr::AllOp { | |
| left: Box::new(expr), | |
| compare_op: op, | |
| right: Box::new(right), | |
| }, | |
| Keyword::ANY | Keyword::SOME => Expr::AnyOp { | |
| left: Box::new(expr), | |
| compare_op: op, | |
| right: Box::new(right), | |
| is_some: keyword == Keyword::SOME, | |
| }, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: expected any of {{ALL, ANY, SOME}}, got {unexpected_keyword:?}"), | |
| )), | |
| }) | |
| } else { | |
| Ok(Expr::BinaryOp { | |
| left: Box::new(expr), | |
| op, | |
| right: Box::new(self.parse_subexpr(precedence)?), | |
| }) | |
| } | |
| } else if let Token::Word(w) = &tok.token { | |
| match w.keyword { | |
| Keyword::IS => { | |
| if self.parse_keyword(Keyword::NULL) { | |
| Ok(Expr::IsNull(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { | |
| Ok(Expr::IsNotNull(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::TRUE]) { | |
| Ok(Expr::IsTrue(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::TRUE]) { | |
| Ok(Expr::IsNotTrue(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::FALSE]) { | |
| Ok(Expr::IsFalse(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::FALSE]) { | |
| Ok(Expr::IsNotFalse(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::UNKNOWN]) { | |
| Ok(Expr::IsUnknown(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::UNKNOWN]) { | |
| Ok(Expr::IsNotUnknown(Box::new(expr))) | |
| } else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::FROM]) { | |
| // The right operand binds no more loosely than `IS` | |
| // itself, so that e.g. `a IS DISTINCT FROM b AND c` | |
| // parses as `(a IS DISTINCT FROM b) AND c`. | |
| let expr2 = self.parse_subexpr(precedence)?; | |
| Ok(Expr::IsDistinctFrom(Box::new(expr), Box::new(expr2))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::DISTINCT, Keyword::FROM]) | |
| { | |
| let expr2 = self.parse_subexpr(precedence)?; | |
| Ok(Expr::IsNotDistinctFrom(Box::new(expr), Box::new(expr2))) | |
| } else if self.parse_keyword(Keyword::JSON) { | |
| self.parse_is_json_predicate(expr, false) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::JSON]) { | |
| self.parse_is_json_predicate(expr, true) | |
| } else if let Ok(is_normalized) = self.parse_unicode_is_normalized(expr) { | |
| Ok(is_normalized) | |
| } else { | |
| self.expected_ref( | |
| "[NOT] NULL | TRUE | FALSE | DISTINCT | [NOT] JSON [VALUE | SCALAR | ARRAY | OBJECT] [WITH | WITHOUT UNIQUE [KEYS]] | [form] NORMALIZED FROM after IS", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| Keyword::AT => { | |
| self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; | |
| Ok(Expr::AtTimeZone { | |
| timestamp: Box::new(expr), | |
| time_zone: Box::new(self.parse_subexpr(precedence)?), | |
| }) | |
| } | |
| Keyword::GLOBAL if self.dialect.supports_global_in() => { | |
| let negated = self.parse_keyword(Keyword::NOT); | |
| self.expect_keyword_is(Keyword::IN)?; | |
| self.parse_in(expr, negated, true) | |
| } | |
| Keyword::NOT | |
| | Keyword::IN | |
| | Keyword::BETWEEN | |
| | Keyword::LIKE | |
| | Keyword::ILIKE | |
| | Keyword::SIMILAR | |
| | Keyword::REGEXP | |
| | Keyword::RLIKE => { | |
| self.prev_token(); | |
| let negated = self.parse_keyword(Keyword::NOT); | |
| let regexp = self.parse_keyword(Keyword::REGEXP); | |
| let rlike = self.parse_keyword(Keyword::RLIKE); | |
| let null = if !self.in_column_definition_state() { | |
| self.parse_keyword(Keyword::NULL) | |
| } else { | |
| false | |
| }; | |
| if regexp || rlike { | |
| Ok(Expr::RLike { | |
| negated, | |
| expr: Box::new(expr), | |
| pattern: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?, | |
| ), | |
| regexp, | |
| }) | |
| } else if negated && null { | |
| Ok(Expr::IsNotNull(Box::new(expr))) | |
| } else if self.parse_keyword(Keyword::IN) { | |
| self.parse_in(expr, negated, false) | |
| } else if self.parse_keyword(Keyword::BETWEEN) { | |
| self.parse_between(expr, negated) | |
| } else if self.parse_keyword(Keyword::LIKE) { | |
| Ok(Expr::Like { | |
| negated, | |
| any: self.parse_keyword(Keyword::ANY), | |
| expr: Box::new(expr), | |
| pattern: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?, | |
| ), | |
| escape_char: self.parse_escape_char()?, | |
| }) | |
| } else if self.parse_keyword(Keyword::ILIKE) { | |
| Ok(Expr::ILike { | |
| negated, | |
| any: self.parse_keyword(Keyword::ANY), | |
| expr: Box::new(expr), | |
| pattern: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?, | |
| ), | |
| escape_char: self.parse_escape_char()?, | |
| }) | |
| } else if self.parse_keywords(&[Keyword::SIMILAR, Keyword::TO]) { | |
| Ok(Expr::SimilarTo { | |
| negated, | |
| expr: Box::new(expr), | |
| pattern: Box::new( | |
| self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?, | |
| ), | |
| escape_char: self.parse_escape_char()?, | |
| }) | |
| } else { | |
| self.expected_ref("IN or BETWEEN after NOT", self.peek_token_ref()) | |
| } | |
| } | |
| Keyword::NOTNULL if dialect.supports_notnull_operator() => { | |
| Ok(Expr::IsNotNull(Box::new(expr))) | |
| } | |
| Keyword::MEMBER => { | |
| if self.parse_keyword(Keyword::OF) { | |
| self.expect_token(&Token::LParen)?; | |
| let array = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Expr::MemberOf(MemberOf { | |
| value: Box::new(expr), | |
| array: Box::new(array), | |
| })) | |
| } else { | |
| self.expected_ref("OF after MEMBER", self.peek_token_ref()) | |
| } | |
| } | |
| // Reached when the dialect assigns `COLLATE` a lower precedence than `::`, e.g. | |
| // Postgres's `expr::type COLLATE collation`. | |
| // See <https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-PRECEDENCE> | |
| Keyword::COLLATE => Ok(Expr::Collate { | |
| expr: Box::new(expr), | |
| collation: self.parse_object_name(false)?, | |
| }), | |
| // Can only happen if `get_next_precedence` got out of sync with this function | |
| _ => parser_err!( | |
| format!("No infix parser for token {:?}", tok.token), | |
| tok.span.start | |
| ), | |
| } | |
| } else if Token::DoubleColon == *tok { | |
| Ok(Expr::Cast { | |
| kind: CastKind::DoubleColon, | |
| expr: Box::new(expr), | |
| data_type: self.parse_data_type()?, | |
| format: None, | |
| }) | |
| } else if Token::ExclamationMark == *tok && self.dialect.supports_factorial_operator() { | |
| Ok(Expr::UnaryOp { | |
| op: UnaryOperator::PGPostfixFactorial, | |
| expr: Box::new(expr), | |
| }) | |
| } else if Token::LBracket == *tok && self.dialect.supports_partiql() | |
| || (Token::Colon == *tok) | |
| { | |
| self.prev_token(); | |
| self.parse_json_access(expr) | |
| } else { | |
| // Can only happen if `get_next_precedence` got out of sync with this function | |
| parser_err!( | |
| format!("No infix parser for token {:?}", tok.token), | |
| tok.span.start | |
| ) | |
| } | |
| } | |
| /// Parse the `ESCAPE CHAR` portion of `LIKE`, `ILIKE`, and `SIMILAR TO` | |
| pub fn parse_escape_char(&mut self) -> Result<Option<Box<Expr>>, ParserError> { | |
| if self.parse_keyword(Keyword::ESCAPE) { | |
| Ok(Some(Box::new(self.parse_subexpr( | |
| self.dialect.prec_value(Precedence::Like), | |
| )?))) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parses an array subscript like | |
| /// * `[:]` | |
| /// * `[l]` | |
| /// * `[l:]` | |
| /// * `[:u]` | |
| /// * `[l:u]` | |
| /// * `[l:u:s]` | |
| /// | |
| /// Parser is right after `[` | |
| fn parse_subscript_inner(&mut self) -> Result<Subscript, ParserError> { | |
| // at either `<lower>:(rest)` or `:(rest)]` | |
| let lower_bound = if self.consume_token(&Token::Colon) { | |
| None | |
| } else { | |
| // parse expr until we hit a colon (or any token with lower precedence) | |
| Some(self.parse_subexpr(self.dialect.prec_value(Precedence::Colon))?) | |
| }; | |
| // check for end | |
| if self.consume_token(&Token::RBracket) { | |
| if let Some(lower_bound) = lower_bound { | |
| return Ok(Subscript::Index { index: lower_bound }); | |
| }; | |
| return Ok(Subscript::Slice { | |
| lower_bound, | |
| upper_bound: None, | |
| stride: None, | |
| }); | |
| } | |
| // consume the `:` | |
| if lower_bound.is_some() { | |
| self.expect_token(&Token::Colon)?; | |
| } | |
| // we are now at either `]`, `<upper>(rest)]` | |
| let upper_bound = if self.consume_token(&Token::RBracket) { | |
| return Ok(Subscript::Slice { | |
| lower_bound, | |
| upper_bound: None, | |
| stride: None, | |
| }); | |
| } else { | |
| // parse expr until we hit a colon (or any token with lower precedence) | |
| Some(self.parse_subexpr(self.dialect.prec_value(Precedence::Colon))?) | |
| }; | |
| // check for end | |
| if self.consume_token(&Token::RBracket) { | |
| return Ok(Subscript::Slice { | |
| lower_bound, | |
| upper_bound, | |
| stride: None, | |
| }); | |
| } | |
| // we are now at `:]` or `:stride]` | |
| self.expect_token(&Token::Colon)?; | |
| let stride = if self.consume_token(&Token::RBracket) { | |
| None | |
| } else { | |
| Some(self.parse_expr()?) | |
| }; | |
| if stride.is_some() { | |
| self.expect_token(&Token::RBracket)?; | |
| } | |
| Ok(Subscript::Slice { | |
| lower_bound, | |
| upper_bound, | |
| stride, | |
| }) | |
| } | |
| /// Parse a multi-dimension array accessing like `[1:3][1][1]` | |
| pub fn parse_multi_dim_subscript( | |
| &mut self, | |
| chain: &mut Vec<AccessExpr>, | |
| ) -> Result<(), ParserError> { | |
| while self.consume_token(&Token::LBracket) { | |
| self.parse_subscript(chain)?; | |
| } | |
| Ok(()) | |
| } | |
| /// Parses an array subscript like `[1:3]` | |
| /// | |
| /// Parser is right after `[` | |
| fn parse_subscript(&mut self, chain: &mut Vec<AccessExpr>) -> Result<(), ParserError> { | |
| let subscript = self.parse_subscript_inner()?; | |
| chain.push(AccessExpr::Subscript(subscript)); | |
| Ok(()) | |
| } | |
| fn parse_json_path_object_key(&mut self) -> Result<JsonPathElem, ParserError> { | |
| let token = self.next_token(); | |
| match token.token { | |
| Token::Word(Word { | |
| value, | |
| // path segments in SF dot notation can be unquoted or double-quoted; | |
| // Databricks also supports backtick-quoted identifiers | |
| quote_style: quote_style @ (Some('"') | Some('`') | None), | |
| // some experimentation suggests that snowflake permits | |
| // any keyword here unquoted. | |
| keyword: _, | |
| }) => Ok(JsonPathElem::Dot { | |
| key: value, | |
| quoted: quote_style.is_some(), | |
| }), | |
| // This token should never be generated on snowflake or generic | |
| // dialects, but we handle it just in case this is used on future | |
| // dialects. | |
| Token::DoubleQuotedString(key) => Ok(JsonPathElem::Dot { key, quoted: true }), | |
| _ => self.expected("variant object key name", token), | |
| } | |
| } | |
| fn parse_json_access(&mut self, expr: Expr) -> Result<Expr, ParserError> { | |
| let path = self.parse_json_path()?; | |
| Ok(Expr::JsonAccess { | |
| value: Box::new(expr), | |
| path, | |
| }) | |
| } | |
| fn parse_json_path(&mut self) -> Result<JsonPath, ParserError> { | |
| let mut path = Vec::new(); | |
| loop { | |
| match self.next_token().token { | |
| Token::Colon if path.is_empty() && self.peek_token_ref() == &Token::LBracket => { | |
| self.next_token(); | |
| let key = self.parse_wildcard_expr()?; | |
| self.expect_token(&Token::RBracket)?; | |
| path.push(JsonPathElem::ColonBracket { key }); | |
| } | |
| Token::Colon if path.is_empty() => { | |
| path.push(self.parse_json_path_object_key()?); | |
| } | |
| Token::Period if !path.is_empty() => { | |
| path.push(self.parse_json_path_object_key()?); | |
| } | |
| Token::LBracket => { | |
| let key = self.parse_wildcard_expr()?; | |
| self.expect_token(&Token::RBracket)?; | |
| path.push(JsonPathElem::Bracket { key }); | |
| } | |
| _ => { | |
| self.prev_token(); | |
| break; | |
| } | |
| }; | |
| } | |
| debug_assert!(!path.is_empty()); | |
| Ok(JsonPath { path }) | |
| } | |
| /// Parses the parens following the `[ GLOBAL ] [ NOT ] IN` operator. | |
| pub fn parse_in( | |
| &mut self, | |
| expr: Expr, | |
| negated: bool, | |
| global: bool, | |
| ) -> Result<Expr, ParserError> { | |
| // BigQuery allows `IN UNNEST(array_expression)` | |
| // https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators | |
| if !global && self.parse_keyword(Keyword::UNNEST) { | |
| self.expect_token(&Token::LParen)?; | |
| let array_expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Expr::InUnnest { | |
| expr: Box::new(expr), | |
| array_expr: Box::new(array_expr), | |
| negated, | |
| }); | |
| } | |
| if self.dialect.supports_in_unparenthesized_expr() | |
| && self.peek_token_ref().token != Token::LParen | |
| { | |
| return Ok(Expr::InList { | |
| expr: Box::new(expr), | |
| list: vec![self.parse_subexpr(self.dialect.prec_value(Precedence::Eq))?], | |
| negated, | |
| global, | |
| }); | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| // A leading query is the whole subquery only when `)` follows it, | |
| // otherwise it is the first item of a list, as in `IN ((SELECT 1), 2)`. | |
| let start = self.index; | |
| // Where a parsed query lacked its `)`. Reported if the list fails no | |
| // further in, since that error would sit inside the query. | |
| let missing_rparen = match self.failed_in_subquery_positions.get(&start) { | |
| Some(&cached) => cached, | |
| None => { | |
| let mut missing_rparen = None; | |
| if let Some(subquery) = self.maybe_parse(|p| p.parse_query())? { | |
| if self.consume_token(&Token::RParen) { | |
| return Ok(Expr::InSubquery { | |
| expr: Box::new(expr), | |
| subquery, | |
| negated, | |
| global, | |
| }); | |
| } | |
| missing_rparen = Some(self.index); | |
| self.index = start; | |
| } | |
| self.failed_in_subquery_positions | |
| .insert(start, missing_rparen); | |
| missing_rparen | |
| } | |
| }; | |
| let list = if self.dialect.supports_in_empty_list() { | |
| self.parse_comma_separated0(Parser::parse_expr, Token::RParen) | |
| } else { | |
| self.parse_comma_separated(Parser::parse_expr) | |
| }; | |
| match list.and_then(|list| self.expect_token(&Token::RParen).map(|_| list)) { | |
| Ok(list) => Ok(Expr::InList { | |
| expr: Box::new(expr), | |
| list, | |
| negated, | |
| global, | |
| }), | |
| Err(ParserError::RecursionLimitExceeded) => Err(ParserError::RecursionLimitExceeded), | |
| Err(e) => match missing_rparen { | |
| Some(index) if self.index <= index => { | |
| self.index = index; | |
| self.expected_ref(")", self.peek_token_ref()) | |
| } | |
| _ => Err(e), | |
| }, | |
| } | |
| } | |
| /// Parses `BETWEEN <low> AND <high>`, assuming the `BETWEEN` keyword was already consumed. | |
| pub fn parse_between(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> { | |
| // Stop parsing subexpressions for <low> and <high> on tokens with | |
| // precedence lower than that of `BETWEEN`, such as `AND`, `IS`, etc. | |
| let low = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?; | |
| self.expect_keyword_is(Keyword::AND)?; | |
| let high = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?; | |
| Ok(Expr::Between { | |
| expr: Box::new(expr), | |
| negated, | |
| low: Box::new(low), | |
| high: Box::new(high), | |
| }) | |
| } | |
| /// Parse a PostgreSQL casting style which is in the form of `expr::datatype`. | |
| pub fn parse_pg_cast(&mut self, expr: Expr) -> Result<Expr, ParserError> { | |
| Ok(Expr::Cast { | |
| kind: CastKind::DoubleColon, | |
| expr: Box::new(expr), | |
| data_type: self.parse_data_type()?, | |
| format: None, | |
| }) | |
| } | |
| /// Get the precedence of the next token | |
| pub fn get_next_precedence(&self) -> Result<u8, ParserError> { | |
| self.dialect.get_next_precedence_default(self) | |
| } | |
| /// Return the token at the given location, or EOF if the index is beyond | |
| /// the length of the current set of tokens. | |
| pub fn token_at(&self, index: usize) -> &TokenWithSpan { | |
| self.tokens.get(index).unwrap_or(&EOF_TOKEN) | |
| } | |
| /// Return the first non-whitespace token that has not yet been processed | |
| /// or Token::EOF | |
| /// | |
| /// See [`Self::peek_token_ref`] to avoid the copy. | |
| pub fn peek_token(&self) -> TokenWithSpan { | |
| self.peek_nth_token(0) | |
| } | |
| /// Return a reference to the first non-whitespace token that has not yet | |
| /// been processed or Token::EOF | |
| pub fn peek_token_ref(&self) -> &TokenWithSpan { | |
| self.peek_nth_token_ref(0) | |
| } | |
| /// Returns the `N` next non-whitespace tokens that have not yet been | |
| /// processed. | |
| /// | |
| /// Example: | |
| /// ```rust | |
| /// # use sqlparser::dialect::GenericDialect; | |
| /// # use sqlparser::parser::Parser; | |
| /// # use sqlparser::keywords::Keyword; | |
| /// # use sqlparser::tokenizer::{Token, Word}; | |
| /// let dialect = GenericDialect {}; | |
| /// let mut parser = Parser::new(&dialect).try_with_sql("ORDER BY foo, bar").unwrap(); | |
| /// | |
| /// // Note that Rust infers the number of tokens to peek based on the | |
| /// // length of the slice pattern! | |
| /// assert!(matches!( | |
| /// parser.peek_tokens(), | |
| /// [ | |
| /// Token::Word(Word { keyword: Keyword::ORDER, .. }), | |
| /// Token::Word(Word { keyword: Keyword::BY, .. }), | |
| /// ] | |
| /// )); | |
| /// ``` | |
| pub fn peek_tokens<const N: usize>(&self) -> [Token; N] { | |
| self.peek_tokens_with_location() | |
| .map(|with_loc| with_loc.token) | |
| } | |
| /// Returns the `N` next non-whitespace tokens with locations that have not | |
| /// yet been processed. | |
| /// | |
| /// See [`Self::peek_token`] for an example. | |
| pub fn peek_tokens_with_location<const N: usize>(&self) -> [TokenWithSpan; N] { | |
| let mut index = self.index; | |
| core::array::from_fn(|_| loop { | |
| let token = self.tokens.get(index); | |
| index += 1; | |
| if let Some(TokenWithSpan { | |
| token: Token::Whitespace(_), | |
| span: _, | |
| }) = token | |
| { | |
| continue; | |
| } | |
| break token.cloned().unwrap_or(TokenWithSpan { | |
| token: Token::EOF, | |
| span: Span::empty(), | |
| }); | |
| }) | |
| } | |
| /// Returns references to the `N` next non-whitespace tokens | |
| /// that have not yet been processed. | |
| /// | |
| /// See [`Self::peek_tokens`] for an example. | |
| pub fn peek_tokens_ref<const N: usize>(&self) -> [&TokenWithSpan; N] { | |
| let mut index = self.index; | |
| core::array::from_fn(|_| loop { | |
| let token = self.tokens.get(index); | |
| index += 1; | |
| if let Some(TokenWithSpan { | |
| token: Token::Whitespace(_), | |
| span: _, | |
| }) = token | |
| { | |
| continue; | |
| } | |
| break token.unwrap_or(&EOF_TOKEN); | |
| }) | |
| } | |
| /// Return nth non-whitespace token that has not yet been processed | |
| pub fn peek_nth_token(&self, n: usize) -> TokenWithSpan { | |
| self.peek_nth_token_ref(n).clone() | |
| } | |
| /// Return nth non-whitespace token that has not yet been processed | |
| pub fn peek_nth_token_ref(&self, mut n: usize) -> &TokenWithSpan { | |
| let mut index = self.index; | |
| loop { | |
| index += 1; | |
| match self.tokens.get(index - 1) { | |
| Some(TokenWithSpan { | |
| token: Token::Whitespace(_), | |
| span: _, | |
| }) => continue, | |
| non_whitespace => { | |
| if n == 0 { | |
| return non_whitespace.unwrap_or(&EOF_TOKEN); | |
| } | |
| n -= 1; | |
| } | |
| } | |
| } | |
| } | |
| /// Return the first token, possibly whitespace, that has not yet been processed | |
| /// (or None if reached end-of-file). | |
| pub fn peek_token_no_skip(&self) -> TokenWithSpan { | |
| self.peek_nth_token_no_skip(0) | |
| } | |
| /// Return nth token, possibly whitespace, that has not yet been processed. | |
| pub fn peek_nth_token_no_skip(&self, n: usize) -> TokenWithSpan { | |
| self.tokens | |
| .get(self.index + n) | |
| .cloned() | |
| .unwrap_or(TokenWithSpan { | |
| token: Token::EOF, | |
| span: Span::empty(), | |
| }) | |
| } | |
| /// Return nth token, possibly whitespace, that has not yet been processed. | |
| fn peek_nth_token_no_skip_ref(&self, n: usize) -> &TokenWithSpan { | |
| self.tokens.get(self.index + n).unwrap_or(&EOF_TOKEN) | |
| } | |
| /// Return true if the next tokens exactly `expected` | |
| /// | |
| /// Does not advance the current token. | |
| fn peek_keywords(&mut self, expected: &[Keyword]) -> bool { | |
| let index = self.index; | |
| let matched = self.parse_keywords(expected); | |
| self.index = index; | |
| matched | |
| } | |
| /// Advances to the next non-whitespace token and returns a copy. | |
| /// | |
| /// Please use [`Self::advance_token`] and [`Self::get_current_token`] to | |
| /// avoid the copy. | |
| pub fn next_token(&mut self) -> TokenWithSpan { | |
| self.advance_token(); | |
| self.get_current_token().clone() | |
| } | |
| /// Returns the index of the current token | |
| /// | |
| /// This can be used with APIs that expect an index, such as | |
| /// [`Self::token_at`] | |
| pub fn get_current_index(&self) -> usize { | |
| self.index.saturating_sub(1) | |
| } | |
| /// Return the next unprocessed token, possibly whitespace. | |
| pub fn next_token_no_skip(&mut self) -> Option<&TokenWithSpan> { | |
| self.index += 1; | |
| self.tokens.get(self.index - 1) | |
| } | |
| /// Advances the current token to the next non-whitespace token | |
| /// | |
| /// See [`Self::get_current_token`] to get the current token after advancing | |
| pub fn advance_token(&mut self) { | |
| loop { | |
| self.index += 1; | |
| match self.tokens.get(self.index - 1) { | |
| Some(TokenWithSpan { | |
| token: Token::Whitespace(_), | |
| span: _, | |
| }) => continue, | |
| _ => break, | |
| } | |
| } | |
| } | |
| /// Returns a reference to the current token | |
| /// | |
| /// Does not advance the current token. | |
| pub fn get_current_token(&self) -> &TokenWithSpan { | |
| self.token_at(self.index.saturating_sub(1)) | |
| } | |
| /// Returns a reference to the previous token | |
| /// | |
| /// Does not advance the current token. | |
| pub fn get_previous_token(&self) -> &TokenWithSpan { | |
| self.token_at(self.index.saturating_sub(2)) | |
| } | |
| /// Returns a reference to the next token | |
| /// | |
| /// Does not advance the current token. | |
| pub fn get_next_token(&self) -> &TokenWithSpan { | |
| self.token_at(self.index) | |
| } | |
| /// Seek back the last one non-whitespace token. | |
| /// | |
| /// Must be called after `next_token()`, otherwise might panic. OK to call | |
| /// after `next_token()` indicates an EOF. | |
| /// | |
| // TODO rename to backup_token and deprecate prev_token? | |
| pub fn prev_token(&mut self) { | |
| loop { | |
| assert!(self.index > 0); | |
| self.index -= 1; | |
| if let Some(TokenWithSpan { | |
| token: Token::Whitespace(_), | |
| span: _, | |
| }) = self.tokens.get(self.index) | |
| { | |
| continue; | |
| } | |
| return; | |
| } | |
| } | |
| /// Report `found` was encountered instead of `expected` | |
| pub fn expected<T>(&self, expected: &str, found: TokenWithSpan) -> Result<T, ParserError> { | |
| parser_err!( | |
| format!("Expected: {expected}, found: {found}"), | |
| found.span.start | |
| ) | |
| } | |
| /// report `found` was encountered instead of `expected` | |
| pub fn expected_ref<T>(&self, expected: &str, found: &TokenWithSpan) -> Result<T, ParserError> { | |
| parser_err!( | |
| format!("Expected: {expected}, found: {found}"), | |
| found.span.start | |
| ) | |
| } | |
| /// Report that the token at `index` was found instead of `expected`. | |
| pub fn expected_at<T>(&self, expected: &str, index: usize) -> Result<T, ParserError> { | |
| let found = self.tokens.get(index).unwrap_or(&EOF_TOKEN); | |
| parser_err!( | |
| format!("Expected: {expected}, found: {found}"), | |
| found.span.start | |
| ) | |
| } | |
| /// If the current token is the `expected` keyword, consume it and returns | |
| /// true. Otherwise, no tokens are consumed and returns false. | |
| #[must_use] | |
| pub fn parse_keyword(&mut self, expected: Keyword) -> bool { | |
| if self.peek_keyword(expected) { | |
| self.advance_token(); | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| #[must_use] | |
| /// Check if the current token is the expected keyword without consuming it. | |
| /// | |
| /// Returns true if the current token matches the expected keyword. | |
| pub fn peek_keyword(&self, expected: Keyword) -> bool { | |
| matches!(&self.peek_token_ref().token, Token::Word(w) if expected == w.keyword) | |
| } | |
| /// If the current token is the `expected` keyword followed by | |
| /// specified tokens, consume them and returns true. | |
| /// Otherwise, no tokens are consumed and returns false. | |
| /// | |
| /// Note that if the length of `tokens` is too long, this function will | |
| /// not be efficient as it does a loop on the tokens with `peek_nth_token` | |
| /// each time. | |
| pub fn parse_keyword_with_tokens(&mut self, expected: Keyword, tokens: &[Token]) -> bool { | |
| self.keyword_with_tokens(expected, tokens, true) | |
| } | |
| /// Peeks to see if the current token is the `expected` keyword followed by specified tokens | |
| /// without consuming them. | |
| /// | |
| /// See [Self::parse_keyword_with_tokens] for details. | |
| pub(crate) fn peek_keyword_with_tokens(&mut self, expected: Keyword, tokens: &[Token]) -> bool { | |
| self.keyword_with_tokens(expected, tokens, false) | |
| } | |
| fn keyword_with_tokens(&mut self, expected: Keyword, tokens: &[Token], consume: bool) -> bool { | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) if expected == w.keyword => { | |
| for (idx, token) in tokens.iter().enumerate() { | |
| if self.peek_nth_token_ref(idx + 1).token != *token { | |
| return false; | |
| } | |
| } | |
| if consume { | |
| for _ in 0..(tokens.len() + 1) { | |
| self.advance_token(); | |
| } | |
| } | |
| true | |
| } | |
| _ => false, | |
| } | |
| } | |
| /// If the current and subsequent tokens exactly match the `keywords` | |
| /// sequence, consume them and returns true. Otherwise, no tokens are | |
| /// consumed and returns false | |
| #[must_use] | |
| pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool { | |
| self.parse_keywords_indexed(keywords).is_some() | |
| } | |
| /// Just like [Self::parse_keywords], but - upon success - returns the | |
| /// token index of the first keyword. | |
| #[must_use] | |
| fn parse_keywords_indexed(&mut self, keywords: &[Keyword]) -> Option<usize> { | |
| let start_index = self.index; | |
| let mut first_keyword_index = None; | |
| for &keyword in keywords { | |
| if !self.parse_keyword(keyword) { | |
| self.index = start_index; | |
| return None; | |
| } | |
| if first_keyword_index.is_none() { | |
| first_keyword_index = Some(self.index.saturating_sub(1)); | |
| } | |
| } | |
| first_keyword_index | |
| } | |
| /// If the current token is one of the given `keywords`, returns the keyword | |
| /// that matches, without consuming the token. Otherwise, returns [`None`]. | |
| #[must_use] | |
| pub fn peek_one_of_keywords(&self, keywords: &[Keyword]) -> Option<Keyword> { | |
| for keyword in keywords { | |
| if self.peek_keyword(*keyword) { | |
| return Some(*keyword); | |
| } | |
| } | |
| None | |
| } | |
| /// If the current token is one of the given `keywords`, consume the token | |
| /// and return the keyword that matches. Otherwise, no tokens are consumed | |
| /// and returns [`None`]. | |
| #[must_use] | |
| pub fn parse_one_of_keywords(&mut self, keywords: &[Keyword]) -> Option<Keyword> { | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) => { | |
| keywords | |
| .iter() | |
| .find(|keyword| **keyword == w.keyword) | |
| .map(|keyword| { | |
| self.advance_token(); | |
| *keyword | |
| }) | |
| } | |
| _ => None, | |
| } | |
| } | |
| /// If the current token is one of the expected keywords, consume the token | |
| /// and return the keyword that matches. Otherwise, return an error. | |
| pub fn expect_one_of_keywords(&mut self, keywords: &[Keyword]) -> Result<Keyword, ParserError> { | |
| if let Some(keyword) = self.parse_one_of_keywords(keywords) { | |
| Ok(keyword) | |
| } else { | |
| let keywords: Vec<String> = keywords.iter().map(|x| format!("{x:?}")).collect(); | |
| self.expected_ref( | |
| &format!("one of {}", keywords.join(" or ")), | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| /// If the current token is the `expected` keyword, consume the token. | |
| /// Otherwise, return an error. | |
| /// | |
| // todo deprecate in favor of expected_keyword_is | |
| pub fn expect_keyword(&mut self, expected: Keyword) -> Result<TokenWithSpan, ParserError> { | |
| if self.parse_keyword(expected) { | |
| Ok(self.get_current_token().clone()) | |
| } else { | |
| self.expected_ref(format!("{:?}", expected).as_str(), self.peek_token_ref()) | |
| } | |
| } | |
| /// If the current token is the `expected` keyword, consume the token. | |
| /// Otherwise, return an error. | |
| /// | |
| /// This differs from expect_keyword only in that the matched keyword | |
| /// token is not returned. | |
| pub fn expect_keyword_is(&mut self, expected: Keyword) -> Result<(), ParserError> { | |
| if self.parse_keyword(expected) { | |
| Ok(()) | |
| } else { | |
| self.expected_ref(format!("{:?}", expected).as_str(), self.peek_token_ref()) | |
| } | |
| } | |
| /// If the current and subsequent tokens exactly match the `keywords` | |
| /// sequence, consume them and returns Ok. Otherwise, return an Error. | |
| pub fn expect_keywords(&mut self, expected: &[Keyword]) -> Result<(), ParserError> { | |
| for &kw in expected { | |
| self.expect_keyword_is(kw)?; | |
| } | |
| Ok(()) | |
| } | |
| /// Consume the next token if it matches the expected token, otherwise return false | |
| /// | |
| /// See [Self::advance_token] to consume the token unconditionally | |
| #[must_use] | |
| pub fn consume_token(&mut self, expected: &Token) -> bool { | |
| if self.peek_token_ref() == expected { | |
| self.advance_token(); | |
| true | |
| } else { | |
| false | |
| } | |
| } | |
| /// If the current and subsequent tokens exactly match the `tokens` | |
| /// sequence, consume them and returns true. Otherwise, no tokens are | |
| /// consumed and returns false | |
| #[must_use] | |
| pub fn consume_tokens(&mut self, tokens: &[Token]) -> bool { | |
| let index = self.index; | |
| for token in tokens { | |
| if !self.consume_token(token) { | |
| self.index = index; | |
| return false; | |
| } | |
| } | |
| true | |
| } | |
| /// Bail out if the current token is not an expected keyword, or consume it if it is | |
| pub fn expect_token(&mut self, expected: &Token) -> Result<TokenWithSpan, ParserError> { | |
| if self.peek_token_ref() == expected { | |
| Ok(self.next_token()) | |
| } else { | |
| self.expected_ref(&expected.to_string(), self.peek_token_ref()) | |
| } | |
| } | |
| fn parse<T: FromStr>(s: String, loc: Location) -> Result<T, ParserError> | |
| where | |
| <T as FromStr>::Err: Display, | |
| { | |
| s.parse::<T>().map_err(|e| { | |
| ParserError::ParserError(format!( | |
| "Could not parse '{s}' as {}: {e}{loc}", | |
| core::any::type_name::<T>() | |
| )) | |
| }) | |
| } | |
| /// Parse a comma-separated list of 1+ SelectItem | |
| pub fn parse_projection(&mut self) -> Result<Vec<SelectItem>, ParserError> { | |
| // BigQuery and Snowflake allow trailing commas, but only in project lists | |
| // e.g. `SELECT 1, 2, FROM t` | |
| // https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#trailing_commas | |
| // https://docs.snowflake.com/en/release-notes/2024/8_11#select-supports-trailing-commas | |
| let trailing_commas = | |
| self.options.trailing_commas | self.dialect.supports_projection_trailing_commas(); | |
| self.parse_comma_separated_with_trailing_commas( | |
| |p| p.parse_select_item(), | |
| trailing_commas, | |
| Self::is_reserved_for_column_alias, | |
| ) | |
| } | |
| /// Parse a list of actions for `GRANT` statements. | |
| pub fn parse_actions_list(&mut self) -> Result<Vec<Action>, ParserError> { | |
| let mut values = vec![]; | |
| loop { | |
| values.push(self.parse_grant_permission()?); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } else if self.options.trailing_commas { | |
| match &self.peek_token_ref().token { | |
| Token::Word(kw) if kw.keyword == Keyword::ON => { | |
| break; | |
| } | |
| Token::RParen | |
| | Token::SemiColon | |
| | Token::EOF | |
| | Token::RBracket | |
| | Token::RBrace => break, | |
| _ => continue, | |
| } | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Parse a list of [TableWithJoins] | |
| fn parse_table_with_joins(&mut self) -> Result<Vec<TableWithJoins>, ParserError> { | |
| let trailing_commas = self.dialect.supports_from_trailing_commas(); | |
| self.parse_comma_separated_with_trailing_commas( | |
| Parser::parse_table_and_joins, | |
| trailing_commas, | |
| |kw, parser| !self.dialect.is_table_factor(kw, parser), | |
| ) | |
| } | |
| /// Parse the comma of a comma-separated syntax element. | |
| /// `R` is a predicate that should return true if the next | |
| /// keyword is a reserved keyword. | |
| /// Allows for control over trailing commas | |
| /// | |
| /// Returns true if there is a next element | |
| fn is_parse_comma_separated_end_with_trailing_commas<R>( | |
| &mut self, | |
| trailing_commas: bool, | |
| is_reserved_keyword: &R, | |
| ) -> bool | |
| where | |
| R: Fn(&Keyword, &mut Parser) -> bool, | |
| { | |
| if !self.consume_token(&Token::Comma) { | |
| true | |
| } else if trailing_commas { | |
| let token = self.next_token().token; | |
| let is_end = match token { | |
| Token::Word(ref kw) if is_reserved_keyword(&kw.keyword, self) => true, | |
| Token::RParen | Token::SemiColon | Token::EOF | Token::RBracket | Token::RBrace => { | |
| true | |
| } | |
| _ => false, | |
| }; | |
| self.prev_token(); | |
| is_end | |
| } else { | |
| false | |
| } | |
| } | |
| /// Parse the comma of a comma-separated syntax element. | |
| /// Returns true if there is a next element | |
| fn is_parse_comma_separated_end(&mut self) -> bool { | |
| self.is_parse_comma_separated_end_with_trailing_commas( | |
| self.options.trailing_commas, | |
| &Self::is_reserved_for_column_alias, | |
| ) | |
| } | |
| /// Parse a comma-separated list of 1+ items accepted by `F` | |
| pub fn parse_comma_separated<T, F>(&mut self, f: F) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| { | |
| self.parse_comma_separated_with_trailing_commas( | |
| f, | |
| self.options.trailing_commas, | |
| Self::is_reserved_for_column_alias, | |
| ) | |
| } | |
| /// Parse a comma-separated list of 1+ items accepted by `F`. | |
| /// `R` is a predicate that should return true if the next | |
| /// keyword is a reserved keyword. | |
| /// Allows for control over trailing commas. | |
| fn parse_comma_separated_with_trailing_commas<T, F, R>( | |
| &mut self, | |
| mut f: F, | |
| trailing_commas: bool, | |
| is_reserved_keyword: R, | |
| ) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| R: Fn(&Keyword, &mut Parser) -> bool, | |
| { | |
| let mut values = vec![]; | |
| loop { | |
| values.push(f(self)?); | |
| if self.is_parse_comma_separated_end_with_trailing_commas( | |
| trailing_commas, | |
| &is_reserved_keyword, | |
| ) { | |
| break; | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Parse a period-separated list of 1+ items accepted by `F` | |
| fn parse_period_separated<T, F>(&mut self, mut f: F) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| { | |
| let mut values = vec![]; | |
| loop { | |
| values.push(f(self)?); | |
| if !self.consume_token(&Token::Period) { | |
| break; | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Parse a keyword-separated list of 1+ items accepted by `F` | |
| pub fn parse_keyword_separated<T, F>( | |
| &mut self, | |
| keyword: Keyword, | |
| mut f: F, | |
| ) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| { | |
| let mut values = vec![]; | |
| loop { | |
| values.push(f(self)?); | |
| if !self.parse_keyword(keyword) { | |
| break; | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Parse an expression enclosed in parentheses. | |
| pub fn parse_parenthesized<T, F>(&mut self, mut f: F) -> Result<T, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let res = f(self)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(res) | |
| } | |
| /// Parse a comma-separated list of 0+ items accepted by `F` | |
| /// * `end_token` - expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) | |
| pub fn parse_comma_separated0<T, F>( | |
| &mut self, | |
| f: F, | |
| end_token: Token, | |
| ) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>, | |
| { | |
| if self.peek_token_ref().token == end_token { | |
| return Ok(vec![]); | |
| } | |
| if self.options.trailing_commas && self.peek_tokens() == [Token::Comma, end_token] { | |
| let _ = self.consume_token(&Token::Comma); | |
| return Ok(vec![]); | |
| } | |
| self.parse_comma_separated(f) | |
| } | |
| /// Parses 0 or more statements, each followed by a semicolon. | |
| /// If the next token is any of `terminal_keywords` then no more | |
| /// statements will be parsed. | |
| pub(crate) fn parse_statement_list( | |
| &mut self, | |
| terminal_keywords: &[Keyword], | |
| ) -> Result<Vec<Statement>, ParserError> { | |
| let mut values = vec![]; | |
| loop { | |
| match &self.peek_nth_token_ref(0).token { | |
| Token::EOF => break, | |
| Token::Word(w) | |
| if w.quote_style.is_none() && terminal_keywords.contains(&w.keyword) => | |
| { | |
| break; | |
| } | |
| _ => {} | |
| } | |
| values.push(self.parse_statement()?); | |
| self.expect_token(&Token::SemiColon)?; | |
| } | |
| Ok(values) | |
| } | |
| /// Default implementation of a predicate that returns true if | |
| /// the specified keyword is reserved for column alias. | |
| /// See [Dialect::is_column_alias] | |
| fn is_reserved_for_column_alias(kw: &Keyword, parser: &mut Parser) -> bool { | |
| !parser.dialect.is_column_alias(kw, parser) | |
| } | |
| /// Run a parser method `f`, reverting back to the current position if unsuccessful. | |
| /// Returns `ParserError::RecursionLimitExceeded` if `f` returns a `RecursionLimitExceeded`. | |
| /// Returns `Ok(None)` if `f` returns any other error. | |
| pub fn maybe_parse<T, F>(&mut self, f: F) -> Result<Option<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser) -> Result<T, ParserError>, | |
| { | |
| match self.try_parse(f) { | |
| Ok(t) => Ok(Some(t)), | |
| Err(ParserError::RecursionLimitExceeded) => Err(ParserError::RecursionLimitExceeded), | |
| _ => Ok(None), | |
| } | |
| } | |
| /// Run a parser method `f`, reverting back to the current position if unsuccessful. | |
| pub fn try_parse<T, F>(&mut self, mut f: F) -> Result<T, ParserError> | |
| where | |
| F: FnMut(&mut Parser) -> Result<T, ParserError>, | |
| { | |
| let index = self.index; | |
| match f(self) { | |
| Ok(t) => Ok(t), | |
| Err(e) => { | |
| // Unwind stack if limit exceeded | |
| self.index = index; | |
| Err(e) | |
| } | |
| } | |
| } | |
| /// Parse either `ALL`, `DISTINCT` or `DISTINCT ON (...)`. Returns [`None`] if `ALL` is parsed | |
| /// and results in a [`ParserError`] if both `ALL` and `DISTINCT` are found. | |
| pub fn parse_all_or_distinct(&mut self) -> Result<Option<Distinct>, ParserError> { | |
| let loc = self.peek_token_ref().span.start; | |
| let distinct = match self.parse_one_of_keywords(&[Keyword::ALL, Keyword::DISTINCT]) { | |
| Some(Keyword::ALL) => { | |
| if self.peek_keyword(Keyword::DISTINCT) { | |
| return parser_err!("Cannot specify ALL then DISTINCT".to_string(), loc); | |
| } | |
| Some(Distinct::All) | |
| } | |
| Some(Keyword::DISTINCT) => { | |
| if self.peek_keyword(Keyword::ALL) { | |
| return parser_err!("Cannot specify DISTINCT then ALL".to_string(), loc); | |
| } | |
| Some(Distinct::Distinct) | |
| } | |
| None => return Ok(None), | |
| _ => return parser_err!("ALL or DISTINCT", loc), | |
| }; | |
| let Some(Distinct::Distinct) = distinct else { | |
| return Ok(distinct); | |
| }; | |
| if !self.parse_keyword(Keyword::ON) { | |
| return Ok(Some(Distinct::Distinct)); | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| let col_names = if self.consume_token(&Token::RParen) { | |
| self.prev_token(); | |
| Vec::new() | |
| } else { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(Distinct::On(col_names))) | |
| } | |
| /// Parse a SQL CREATE statement | |
| pub fn parse_create(&mut self) -> Result<Statement, ParserError> { | |
| let modifier_loc = self.peek_token_ref().span.start; | |
| let or_replace = self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]); | |
| let or_alter = self.parse_keywords(&[Keyword::OR, Keyword::ALTER]); | |
| let multiset = self.maybe_parse_multiset(); | |
| let local = self.parse_one_of_keywords(&[Keyword::LOCAL]).is_some(); | |
| let global = self.parse_one_of_keywords(&[Keyword::GLOBAL]).is_some(); | |
| let transient = self.parse_one_of_keywords(&[Keyword::TRANSIENT]).is_some(); | |
| let global: Option<bool> = if global { | |
| Some(true) | |
| } else if local { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| let temporary = self | |
| .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) | |
| .is_some(); | |
| let volatile = self.parse_keyword(Keyword::VOLATILE); | |
| let unlogged = self.peek_keywords(&[Keyword::UNLOGGED, Keyword::TABLE]); | |
| if unlogged { | |
| self.expect_keyword(Keyword::UNLOGGED)?; | |
| } | |
| let persistent = dialect_of!(self is DuckDbDialect) | |
| && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some(); | |
| let create_view_params = self.parse_create_view_params()?; | |
| if self.peek_keywords(&[Keyword::SNAPSHOT, Keyword::TABLE]) { | |
| self.parse_create_snapshot_table().map(Into::into) | |
| } else if self.peek_keywords(&[Keyword::TEXT, Keyword::SEARCH]) { | |
| self.parse_create_text_search().map(Into::into) | |
| } else if self.parse_keyword(Keyword::TABLE) { | |
| self.parse_create_table( | |
| or_replace, temporary, unlogged, global, transient, volatile, multiset, | |
| ) | |
| .map(Into::into) | |
| } else if self.peek_keyword(Keyword::MATERIALIZED) | |
| || self.peek_keyword(Keyword::VIEW) | |
| || self.peek_keywords(&[Keyword::SECURE, Keyword::MATERIALIZED, Keyword::VIEW]) | |
| || self.peek_keywords(&[Keyword::SECURE, Keyword::VIEW]) | |
| { | |
| self.parse_create_view(or_alter, or_replace, temporary, create_view_params) | |
| .map(Into::into) | |
| } else if self.parse_keyword(Keyword::POLICY) { | |
| self.parse_create_policy().map(Into::into) | |
| } else if self.parse_keyword(Keyword::EXTERNAL) { | |
| self.parse_create_external_table(or_replace).map(Into::into) | |
| } else if self.parse_keyword(Keyword::FUNCTION) { | |
| self.parse_create_function(or_alter, or_replace, temporary) | |
| } else if self.parse_keyword(Keyword::DOMAIN) { | |
| self.parse_create_domain().map(Into::into) | |
| } else if self.parse_keyword(Keyword::TRIGGER) { | |
| self.parse_create_trigger(temporary, or_alter, or_replace, false) | |
| .map(Into::into) | |
| } else if self.parse_keywords(&[Keyword::CONSTRAINT, Keyword::TRIGGER]) { | |
| self.parse_create_trigger(temporary, or_alter, or_replace, true) | |
| .map(Into::into) | |
| } else if self.parse_keyword(Keyword::MACRO) { | |
| self.parse_create_macro(or_replace, temporary) | |
| } else if self.parse_keyword(Keyword::SECRET) { | |
| self.parse_create_secret(or_replace, temporary, persistent) | |
| } else if self.parse_keyword(Keyword::USER) { | |
| self.parse_create_user(or_replace).map(Into::into) | |
| } else if self.parse_keyword(Keyword::SCHEMA) { | |
| self.parse_create_schema(or_replace) | |
| } else if self.parse_keyword(Keyword::WAREHOUSE) { | |
| self.parse_create_warehouse(or_replace).map(Into::into) | |
| } else if or_replace { | |
| self.expected_ref( | |
| "[EXTERNAL] TABLE or [MATERIALIZED] VIEW or FUNCTION or SCHEMA or WAREHOUSE after CREATE OR REPLACE", | |
| self.peek_token_ref(), | |
| ) | |
| } else if self.parse_keyword(Keyword::EXTENSION) { | |
| self.parse_create_extension().map(Into::into) | |
| } else if self.parse_keyword(Keyword::INDEX) { | |
| self.parse_create_index(false).map(Into::into) | |
| } else if self.parse_keywords(&[Keyword::UNIQUE, Keyword::INDEX]) { | |
| self.parse_create_index(true).map(Into::into) | |
| } else if self.parse_keyword(Keyword::VIRTUAL) { | |
| self.parse_create_virtual_table() | |
| } else if self.parse_keyword(Keyword::DATABASE) { | |
| self.parse_create_database() | |
| } else if self.parse_keyword(Keyword::ROLE) { | |
| self.parse_create_role().map(Into::into) | |
| } else if self.parse_keyword(Keyword::SEQUENCE) { | |
| self.parse_create_sequence(temporary) | |
| } else if self.parse_keyword(Keyword::COLLATION) { | |
| self.parse_create_collation().map(Into::into) | |
| } else if self.parse_keyword(Keyword::TYPE) { | |
| self.parse_create_type() | |
| } else if self.parse_keyword(Keyword::PROCEDURE) | |
| || self.dialect.supports_create_proc_syntax() && self.parse_keyword(Keyword::PROC) | |
| { | |
| self.parse_create_procedure(or_alter) | |
| } else if self.parse_keyword(Keyword::CONNECTOR) { | |
| self.parse_create_connector().map(Into::into) | |
| } else if self.parse_keyword(Keyword::OPERATOR) { | |
| // Check if this is CREATE OPERATOR FAMILY or CREATE OPERATOR CLASS | |
| if self.parse_keyword(Keyword::FAMILY) { | |
| self.parse_create_operator_family().map(Into::into) | |
| } else if self.parse_keyword(Keyword::CLASS) { | |
| self.parse_create_operator_class().map(Into::into) | |
| } else { | |
| self.parse_create_operator().map(Into::into) | |
| } | |
| } else if self.parse_keyword(Keyword::SERVER) { | |
| self.parse_pg_create_server() | |
| } else if self.parse_keywords(&[Keyword::FOREIGN, Keyword::TABLE]) { | |
| // `or_replace` cannot reach here today, since the arm above catches it. | |
| // It stays so that reordering the arms cannot make it fall through. | |
| if or_replace | |
| || or_alter | |
| || temporary | |
| || global.is_some() | |
| || transient | |
| || volatile | |
| || multiset.is_some() | |
| || persistent | |
| || create_view_params.is_some() | |
| { | |
| return parser_err!( | |
| "CREATE FOREIGN TABLE does not accept this modifier", | |
| modifier_loc | |
| ); | |
| } | |
| self.parse_create_foreign_table().map(Into::into) | |
| } else { | |
| self.expected_ref("an object type after CREATE", self.peek_token_ref()) | |
| } | |
| } | |
| fn parse_text_search_object_type(&mut self) -> Result<TextSearchObjectType, ParserError> { | |
| match self.expect_one_of_keywords(&[ | |
| Keyword::DICTIONARY, | |
| Keyword::CONFIGURATION, | |
| Keyword::TEMPLATE, | |
| Keyword::PARSER, | |
| ])? { | |
| Keyword::DICTIONARY => Ok(TextSearchObjectType::Dictionary), | |
| Keyword::CONFIGURATION => Ok(TextSearchObjectType::Configuration), | |
| Keyword::TEMPLATE => Ok(TextSearchObjectType::Template), | |
| Keyword::PARSER => Ok(TextSearchObjectType::Parser), | |
| unexpected_keyword => Err(ParserError::ParserError(format!( | |
| "Internal parser error: expected any of {{DICTIONARY, CONFIGURATION, TEMPLATE, PARSER}}, got {unexpected_keyword:?}" | |
| ))), | |
| } | |
| } | |
| /// Parse a `CREATE TEXT SEARCH ...` statement. | |
| pub fn parse_create_text_search(&mut self) -> Result<CreateTextSearch, ParserError> { | |
| self.expect_keywords(&[Keyword::TEXT, Keyword::SEARCH])?; | |
| let object_type = self.parse_text_search_object_type()?; | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated(Parser::parse_sql_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(CreateTextSearch { | |
| object_type, | |
| name, | |
| options, | |
| }) | |
| } | |
| fn parse_alter_text_search_option(&mut self) -> Result<AlterTextSearchOption, ParserError> { | |
| let key = self.parse_identifier()?; | |
| let value = if self.consume_token(&Token::Eq) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(AlterTextSearchOption { key, value }) | |
| } | |
| /// Parse an `ALTER TEXT SEARCH ...` statement. | |
| pub fn parse_alter_text_search(&mut self) -> Result<AlterTextSearch, ParserError> { | |
| self.expect_keywords(&[Keyword::TEXT, Keyword::SEARCH])?; | |
| let object_type = self.parse_text_search_object_type()?; | |
| let name = self.parse_object_name(false)?; | |
| let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| AlterTextSearchOperation::RenameTo { | |
| new_name: self.parse_identifier()?, | |
| } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| AlterTextSearchOperation::OwnerTo(self.parse_owner()?) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| AlterTextSearchOperation::SetSchema { | |
| schema_name: self.parse_object_name(false)?, | |
| } | |
| } else if self.consume_token(&Token::LParen) { | |
| let options = self.parse_comma_separated(Parser::parse_alter_text_search_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| AlterTextSearchOperation::SetOptions { options } | |
| } else { | |
| let expected = "RENAME TO, OWNER TO, SET SCHEMA, or (...) after ALTER TEXT SEARCH"; | |
| return self.expected_ref(expected, self.peek_token_ref()); | |
| }; | |
| Ok(AlterTextSearch { | |
| object_type, | |
| name, | |
| operation, | |
| }) | |
| } | |
| fn parse_create_user(&mut self, or_replace: bool) -> Result<CreateUser, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let options = self | |
| .parse_key_value_options(false, &[Keyword::WITH, Keyword::TAG])? | |
| .options; | |
| let with_tags = self.parse_keyword(Keyword::WITH); | |
| let tags = if self.parse_keyword(Keyword::TAG) { | |
| self.parse_key_value_options(true, &[])?.options | |
| } else { | |
| vec![] | |
| }; | |
| Ok(CreateUser { | |
| or_replace, | |
| if_not_exists, | |
| name, | |
| options: KeyValueOptions { | |
| options, | |
| delimiter: KeyValueOptionsDelimiter::Space, | |
| }, | |
| with_tags, | |
| tags: KeyValueOptions { | |
| options: tags, | |
| delimiter: KeyValueOptionsDelimiter::Comma, | |
| }, | |
| }) | |
| } | |
| /// Parse a `CREATE WAREHOUSE` statement. | |
| /// | |
| /// See <https://docs.snowflake.com/en/sql-reference/sql/create-warehouse> | |
| fn parse_create_warehouse(&mut self, or_replace: bool) -> Result<CreateWarehouse, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| let _ = self.parse_keyword(Keyword::WITH); | |
| let options = self.parse_key_value_options(false, &[])?; | |
| Ok(CreateWarehouse { | |
| or_replace, | |
| if_not_exists, | |
| name, | |
| options, | |
| }) | |
| } | |
| /// See [DuckDB Docs](https://duckdb.org/docs/sql/statements/create_secret.html) for more details. | |
| pub fn parse_create_secret( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| persistent: bool, | |
| ) -> Result<Statement, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let mut storage_specifier = None; | |
| let mut name = None; | |
| if self.peek_token_ref().token != Token::LParen { | |
| if self.parse_keyword(Keyword::IN) { | |
| storage_specifier = self.parse_identifier().ok() | |
| } else { | |
| name = self.parse_identifier().ok(); | |
| } | |
| // Storage specifier may follow the name | |
| if storage_specifier.is_none() | |
| && self.peek_token_ref().token != Token::LParen | |
| && self.parse_keyword(Keyword::IN) | |
| { | |
| storage_specifier = self.parse_identifier().ok(); | |
| } | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| self.expect_keyword_is(Keyword::TYPE)?; | |
| let secret_type = self.parse_identifier()?; | |
| let mut options = Vec::new(); | |
| if self.consume_token(&Token::Comma) { | |
| options.append(&mut self.parse_comma_separated(|p| { | |
| let key = p.parse_identifier()?; | |
| let value = p.parse_identifier()?; | |
| Ok(SecretOption { key, value }) | |
| })?); | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| let temp = match (temporary, persistent) { | |
| (true, false) => Some(true), | |
| (false, true) => Some(false), | |
| (false, false) => None, | |
| _ => self.expected_ref("TEMPORARY or PERSISTENT", self.peek_token_ref())?, | |
| }; | |
| Ok(Statement::CreateSecret { | |
| or_replace, | |
| temporary: temp, | |
| if_not_exists, | |
| name, | |
| storage_specifier, | |
| secret_type, | |
| options, | |
| }) | |
| } | |
| /// Parse a CACHE TABLE statement | |
| pub fn parse_cache_table(&mut self) -> Result<Statement, ParserError> { | |
| let (mut table_flag, mut options, mut has_as, mut query) = (None, vec![], false, None); | |
| if self.parse_keyword(Keyword::TABLE) { | |
| let table_name = self.parse_object_name(false)?; | |
| if self.peek_token_ref().token != Token::EOF { | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| if word.keyword == Keyword::OPTIONS { | |
| options = self.parse_options(Keyword::OPTIONS)? | |
| } | |
| }; | |
| if self.peek_token_ref().token != Token::EOF { | |
| let (a, q) = self.parse_as_query()?; | |
| has_as = a; | |
| query = Some(q); | |
| } | |
| Ok(Statement::Cache { | |
| table_flag, | |
| table_name, | |
| has_as, | |
| options, | |
| query, | |
| }) | |
| } else { | |
| Ok(Statement::Cache { | |
| table_flag, | |
| table_name, | |
| has_as, | |
| options, | |
| query, | |
| }) | |
| } | |
| } else { | |
| table_flag = Some(self.parse_object_name(false)?); | |
| if self.parse_keyword(Keyword::TABLE) { | |
| let table_name = self.parse_object_name(false)?; | |
| if self.peek_token_ref().token != Token::EOF { | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| if word.keyword == Keyword::OPTIONS { | |
| options = self.parse_options(Keyword::OPTIONS)? | |
| } | |
| }; | |
| if self.peek_token_ref().token != Token::EOF { | |
| let (a, q) = self.parse_as_query()?; | |
| has_as = a; | |
| query = Some(q); | |
| } | |
| Ok(Statement::Cache { | |
| table_flag, | |
| table_name, | |
| has_as, | |
| options, | |
| query, | |
| }) | |
| } else { | |
| Ok(Statement::Cache { | |
| table_flag, | |
| table_name, | |
| has_as, | |
| options, | |
| query, | |
| }) | |
| } | |
| } else { | |
| if self.peek_token_ref().token == Token::EOF { | |
| self.prev_token(); | |
| } | |
| self.expected_ref("a `TABLE` keyword", self.peek_token_ref()) | |
| } | |
| } | |
| } | |
| /// Parse 'AS' before as query,such as `WITH XXX AS SELECT XXX` oer `CACHE TABLE AS SELECT XXX` | |
| pub fn parse_as_query(&mut self) -> Result<(bool, Box<Query>), ParserError> { | |
| match &self.peek_token_ref().token { | |
| Token::Word(word) => match word.keyword { | |
| Keyword::AS => { | |
| self.next_token(); | |
| Ok((true, self.parse_query()?)) | |
| } | |
| _ => Ok((false, self.parse_query()?)), | |
| }, | |
| _ => self.expected_ref("a QUERY statement", self.peek_token_ref()), | |
| } | |
| } | |
| /// Parse a UNCACHE TABLE statement | |
| pub fn parse_uncache_table(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword_is(Keyword::TABLE)?; | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let table_name = self.parse_object_name(false)?; | |
| Ok(Statement::UNCache { | |
| table_name, | |
| if_exists, | |
| }) | |
| } | |
| /// SQLite-specific `CREATE VIRTUAL TABLE` | |
| pub fn parse_create_virtual_table(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword_is(Keyword::TABLE)?; | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let table_name = self.parse_object_name(false)?; | |
| self.expect_keyword_is(Keyword::USING)?; | |
| let module_name = self.parse_identifier()?; | |
| // SQLite docs note that module "arguments syntax is sufficiently | |
| // general that the arguments can be made to appear as column | |
| // definitions in a traditional CREATE TABLE statement", but | |
| // we don't implement that. | |
| let module_args = self.parse_parenthesized_column_list(Optional, false)?; | |
| Ok(Statement::CreateVirtualTable { | |
| name: table_name, | |
| if_not_exists, | |
| module_name, | |
| module_args, | |
| }) | |
| } | |
| /// Parse a `CREATE SCHEMA` statement. | |
| pub fn parse_create_schema(&mut self, or_replace: bool) -> Result<Statement, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let schema_name = self.parse_schema_name()?; | |
| let default_collate_spec = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::COLLATE]) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let with = if self.peek_keyword(Keyword::WITH) { | |
| Some(self.parse_options(Keyword::WITH)?) | |
| } else { | |
| None | |
| }; | |
| let options = if self.peek_keyword(Keyword::OPTIONS) { | |
| Some(self.parse_options(Keyword::OPTIONS)?) | |
| } else { | |
| None | |
| }; | |
| let clone = if self.parse_keyword(Keyword::CLONE) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::CreateSchema { | |
| schema_name, | |
| or_replace, | |
| if_not_exists, | |
| with, | |
| options, | |
| default_collate_spec, | |
| clone, | |
| }) | |
| } | |
| fn parse_schema_name(&mut self) -> Result<SchemaName, ParserError> { | |
| if self.parse_keyword(Keyword::AUTHORIZATION) { | |
| Ok(SchemaName::UnnamedAuthorization(self.parse_identifier()?)) | |
| } else { | |
| let name = self.parse_object_name(false)?; | |
| if self.parse_keyword(Keyword::AUTHORIZATION) { | |
| Ok(SchemaName::NamedAuthorization( | |
| name, | |
| self.parse_identifier()?, | |
| )) | |
| } else { | |
| Ok(SchemaName::Simple(name)) | |
| } | |
| } | |
| } | |
| /// Parse a `CREATE DATABASE` statement. | |
| pub fn parse_create_database(&mut self) -> Result<Statement, ParserError> { | |
| let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let db_name = self.parse_object_name(false)?; | |
| let mut location = None; | |
| let mut managed_location = None; | |
| loop { | |
| match self.parse_one_of_keywords(&[Keyword::LOCATION, Keyword::MANAGEDLOCATION]) { | |
| Some(Keyword::LOCATION) => location = Some(self.parse_literal_string()?), | |
| Some(Keyword::MANAGEDLOCATION) => { | |
| managed_location = Some(self.parse_literal_string()?) | |
| } | |
| _ => break, | |
| } | |
| } | |
| let clone = if self.parse_keyword(Keyword::CLONE) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| // Parse MySQL-style [DEFAULT] CHARACTER SET and [DEFAULT] COLLATE options | |
| // | |
| // Note: The docs only mention `CHARACTER SET`, but `CHARSET` is also supported. | |
| // Furthermore, MySQL will only accept one character set, raising an error if there is more | |
| // than one, but will accept multiple collations and use the last one. | |
| // | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-database.html> | |
| let mut default_charset = None; | |
| let mut default_collation = None; | |
| loop { | |
| let has_default = self.parse_keyword(Keyword::DEFAULT); | |
| if default_charset.is_none() && self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) | |
| || self.parse_keyword(Keyword::CHARSET) | |
| { | |
| let _ = self.consume_token(&Token::Eq); | |
| default_charset = Some(self.parse_identifier()?.value); | |
| } else if self.parse_keyword(Keyword::COLLATE) { | |
| let _ = self.consume_token(&Token::Eq); | |
| default_collation = Some(self.parse_identifier()?.value); | |
| } else if has_default { | |
| // DEFAULT keyword not followed by CHARACTER SET, CHARSET, or COLLATE | |
| self.prev_token(); | |
| break; | |
| } else { | |
| break; | |
| } | |
| } | |
| Ok(Statement::CreateDatabase { | |
| db_name, | |
| if_not_exists: ine, | |
| location, | |
| managed_location, | |
| or_replace: false, | |
| transient: false, | |
| clone, | |
| data_retention_time_in_days: None, | |
| max_data_extension_time_in_days: None, | |
| external_volume: None, | |
| catalog: None, | |
| replace_invalid_characters: None, | |
| default_ddl_collation: None, | |
| storage_serialization_policy: None, | |
| comment: None, | |
| default_charset, | |
| default_collation, | |
| catalog_sync: None, | |
| catalog_sync_namespace_mode: None, | |
| catalog_sync_namespace_flatten_delimiter: None, | |
| with_tags: None, | |
| with_contacts: None, | |
| }) | |
| } | |
| /// Parse an optional `USING` clause for `CREATE FUNCTION`. | |
| pub fn parse_optional_create_function_using( | |
| &mut self, | |
| ) -> Result<Option<CreateFunctionUsing>, ParserError> { | |
| if !self.parse_keyword(Keyword::USING) { | |
| return Ok(None); | |
| }; | |
| let keyword = | |
| self.expect_one_of_keywords(&[Keyword::JAR, Keyword::FILE, Keyword::ARCHIVE])?; | |
| let uri = self.parse_literal_string()?; | |
| match keyword { | |
| Keyword::JAR => Ok(Some(CreateFunctionUsing::Jar(uri))), | |
| Keyword::FILE => Ok(Some(CreateFunctionUsing::File(uri))), | |
| Keyword::ARCHIVE => Ok(Some(CreateFunctionUsing::Archive(uri))), | |
| _ => self.expected( | |
| "JAR, FILE or ARCHIVE, got {:?}", | |
| TokenWithSpan::wrap(Token::make_keyword(format!("{keyword:?}").as_str())), | |
| ), | |
| } | |
| } | |
| /// Parse a `CREATE FUNCTION` statement. | |
| pub fn parse_create_function( | |
| &mut self, | |
| or_alter: bool, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<Statement, ParserError> { | |
| if dialect_of!(self is HiveDialect) { | |
| self.parse_hive_create_function(or_replace, temporary) | |
| .map(Into::into) | |
| } else if dialect_of!(self is PostgreSqlDialect | GenericDialect) { | |
| self.parse_postgres_create_function(or_replace, temporary) | |
| .map(Into::into) | |
| } else if dialect_of!(self is DuckDbDialect) { | |
| self.parse_create_macro(or_replace, temporary) | |
| } else if dialect_of!(self is BigQueryDialect) { | |
| self.parse_bigquery_create_function(or_replace, temporary) | |
| .map(Into::into) | |
| } else if dialect_of!(self is MsSqlDialect) { | |
| self.parse_mssql_create_function(or_alter, or_replace, temporary) | |
| .map(Into::into) | |
| } else { | |
| self.prev_token(); | |
| self.expected_ref("an object type after CREATE", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse `CREATE FUNCTION` for [PostgreSQL] | |
| /// | |
| /// [PostgreSQL]: https://www.postgresql.org/docs/15/sql-createfunction.html | |
| fn parse_postgres_create_function( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<CreateFunction, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::LParen)?; | |
| let args = if Token::RParen != self.peek_token_ref().token { | |
| self.parse_comma_separated(Parser::parse_function_arg)? | |
| } else { | |
| vec![] | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| let return_type = if self.parse_keyword(Keyword::RETURNS) { | |
| Some(self.parse_function_return_type()?) | |
| } else { | |
| None | |
| }; | |
| #[derive(Default)] | |
| struct Body { | |
| language: Option<Ident>, | |
| behavior: Option<FunctionBehavior>, | |
| function_body: Option<CreateFunctionBody>, | |
| called_on_null: Option<FunctionCalledOnNull>, | |
| parallel: Option<FunctionParallel>, | |
| security: Option<FunctionSecurity>, | |
| } | |
| let mut body = Body::default(); | |
| let mut set_params: Vec<FunctionDefinitionSetParam> = Vec::new(); | |
| loop { | |
| fn ensure_not_set<T>(field: &Option<T>, name: &str) -> Result<(), ParserError> { | |
| if field.is_some() { | |
| return Err(ParserError::ParserError(format!( | |
| "{name} specified more than once", | |
| ))); | |
| } | |
| Ok(()) | |
| } | |
| if self.parse_keyword(Keyword::AS) { | |
| ensure_not_set(&body.function_body, "AS")?; | |
| body.function_body = Some(self.parse_create_function_body_string()?); | |
| } else if self.parse_keyword(Keyword::LANGUAGE) { | |
| ensure_not_set(&body.language, "LANGUAGE")?; | |
| body.language = Some(self.parse_identifier()?); | |
| } else if self.parse_keyword(Keyword::IMMUTABLE) { | |
| ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; | |
| body.behavior = Some(FunctionBehavior::Immutable); | |
| } else if self.parse_keyword(Keyword::STABLE) { | |
| ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; | |
| body.behavior = Some(FunctionBehavior::Stable); | |
| } else if self.parse_keyword(Keyword::VOLATILE) { | |
| ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; | |
| body.behavior = Some(FunctionBehavior::Volatile); | |
| } else if self.parse_keywords(&[ | |
| Keyword::CALLED, | |
| Keyword::ON, | |
| Keyword::NULL, | |
| Keyword::INPUT, | |
| ]) { | |
| ensure_not_set( | |
| &body.called_on_null, | |
| "CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT", | |
| )?; | |
| body.called_on_null = Some(FunctionCalledOnNull::CalledOnNullInput); | |
| } else if self.parse_keywords(&[ | |
| Keyword::RETURNS, | |
| Keyword::NULL, | |
| Keyword::ON, | |
| Keyword::NULL, | |
| Keyword::INPUT, | |
| ]) { | |
| ensure_not_set( | |
| &body.called_on_null, | |
| "CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT", | |
| )?; | |
| body.called_on_null = Some(FunctionCalledOnNull::ReturnsNullOnNullInput); | |
| } else if self.parse_keyword(Keyword::STRICT) { | |
| ensure_not_set( | |
| &body.called_on_null, | |
| "CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT", | |
| )?; | |
| body.called_on_null = Some(FunctionCalledOnNull::Strict); | |
| } else if self.parse_keyword(Keyword::PARALLEL) { | |
| ensure_not_set(&body.parallel, "PARALLEL { UNSAFE | RESTRICTED | SAFE }")?; | |
| if self.parse_keyword(Keyword::UNSAFE) { | |
| body.parallel = Some(FunctionParallel::Unsafe); | |
| } else if self.parse_keyword(Keyword::RESTRICTED) { | |
| body.parallel = Some(FunctionParallel::Restricted); | |
| } else if self.parse_keyword(Keyword::SAFE) { | |
| body.parallel = Some(FunctionParallel::Safe); | |
| } else { | |
| return self | |
| .expected_ref("one of UNSAFE | RESTRICTED | SAFE", self.peek_token_ref()); | |
| } | |
| } else if self.parse_keyword(Keyword::SECURITY) { | |
| ensure_not_set(&body.security, "SECURITY { DEFINER | INVOKER }")?; | |
| if self.parse_keyword(Keyword::DEFINER) { | |
| body.security = Some(FunctionSecurity::Definer); | |
| } else if self.parse_keyword(Keyword::INVOKER) { | |
| body.security = Some(FunctionSecurity::Invoker); | |
| } else { | |
| return self.expected_ref("DEFINER or INVOKER", self.peek_token_ref()); | |
| } | |
| } else if self.parse_keyword(Keyword::SET) { | |
| let name = self.parse_object_name(false)?; | |
| let value = if self.parse_keywords(&[Keyword::FROM, Keyword::CURRENT]) { | |
| FunctionSetValue::FromCurrent | |
| } else { | |
| if !self.consume_token(&Token::Eq) && !self.parse_keyword(Keyword::TO) { | |
| return self.expected_ref("= or TO", self.peek_token_ref()); | |
| } | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| FunctionSetValue::Default | |
| } else { | |
| let values = self.parse_comma_separated(Parser::parse_expr)?; | |
| FunctionSetValue::Values(values) | |
| } | |
| }; | |
| set_params.push(FunctionDefinitionSetParam { name, value }); | |
| } else if self.parse_keyword(Keyword::RETURN) { | |
| ensure_not_set(&body.function_body, "RETURN")?; | |
| body.function_body = Some(CreateFunctionBody::Return(self.parse_expr()?)); | |
| } else { | |
| break; | |
| } | |
| } | |
| Ok(CreateFunction { | |
| or_alter: false, | |
| or_replace, | |
| temporary, | |
| name, | |
| args: Some(args), | |
| return_type, | |
| behavior: body.behavior, | |
| called_on_null: body.called_on_null, | |
| parallel: body.parallel, | |
| security: body.security, | |
| set_params, | |
| language: body.language, | |
| function_body: body.function_body, | |
| if_not_exists: false, | |
| using: None, | |
| determinism_specifier: None, | |
| options: None, | |
| remote_connection: None, | |
| }) | |
| } | |
| /// Parse `CREATE FUNCTION` for [Hive] | |
| /// | |
| /// [Hive]: https://cwiki.apache.org/confluence/display/hive/languagemanual+ddl#LanguageManualDDL-Create/Drop/ReloadFunction | |
| fn parse_hive_create_function( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<CreateFunction, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let body = self.parse_create_function_body_string()?; | |
| let using = self.parse_optional_create_function_using()?; | |
| Ok(CreateFunction { | |
| or_alter: false, | |
| or_replace, | |
| temporary, | |
| name, | |
| function_body: Some(body), | |
| using, | |
| if_not_exists: false, | |
| args: None, | |
| return_type: None, | |
| behavior: None, | |
| called_on_null: None, | |
| parallel: None, | |
| security: None, | |
| set_params: vec![], | |
| language: None, | |
| determinism_specifier: None, | |
| options: None, | |
| remote_connection: None, | |
| }) | |
| } | |
| /// Parse `CREATE FUNCTION` for [BigQuery] | |
| /// | |
| /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement | |
| fn parse_bigquery_create_function( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<CreateFunction, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let (name, args) = self.parse_create_function_name_and_params()?; | |
| let return_type = if self.parse_keyword(Keyword::RETURNS) { | |
| Some(self.parse_function_return_type()?) | |
| } else { | |
| None | |
| }; | |
| let determinism_specifier = if self.parse_keyword(Keyword::DETERMINISTIC) { | |
| Some(FunctionDeterminismSpecifier::Deterministic) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC]) { | |
| Some(FunctionDeterminismSpecifier::NotDeterministic) | |
| } else { | |
| None | |
| }; | |
| let language = if self.parse_keyword(Keyword::LANGUAGE) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let remote_connection = | |
| if self.parse_keywords(&[Keyword::REMOTE, Keyword::WITH, Keyword::CONNECTION]) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| // `OPTIONS` may come before of after the function body but | |
| // may be specified at most once. | |
| let mut options = self.maybe_parse_options(Keyword::OPTIONS)?; | |
| let function_body = if remote_connection.is_none() { | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let expr = self.parse_expr()?; | |
| if options.is_none() { | |
| options = self.maybe_parse_options(Keyword::OPTIONS)?; | |
| Some(CreateFunctionBody::AsBeforeOptions { | |
| body: expr, | |
| link_symbol: None, | |
| }) | |
| } else { | |
| Some(CreateFunctionBody::AsAfterOptions(expr)) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(CreateFunction { | |
| or_alter: false, | |
| or_replace, | |
| temporary, | |
| if_not_exists, | |
| name, | |
| args: Some(args), | |
| return_type, | |
| function_body, | |
| language, | |
| determinism_specifier, | |
| options, | |
| remote_connection, | |
| using: None, | |
| behavior: None, | |
| called_on_null: None, | |
| parallel: None, | |
| security: None, | |
| set_params: vec![], | |
| }) | |
| } | |
| /// Parse `CREATE FUNCTION` for [MsSql] | |
| /// | |
| /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql | |
| fn parse_mssql_create_function( | |
| &mut self, | |
| or_alter: bool, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<CreateFunction, ParserError> { | |
| let (name, args) = self.parse_create_function_name_and_params()?; | |
| self.expect_keyword(Keyword::RETURNS)?; | |
| let return_table = self.maybe_parse(|p| { | |
| let return_table_name = p.parse_identifier()?; | |
| p.expect_keyword_is(Keyword::TABLE)?; | |
| p.prev_token(); | |
| let table_column_defs = match p.parse_data_type()? { | |
| DataType::Table(Some(table_column_defs)) if !table_column_defs.is_empty() => { | |
| table_column_defs | |
| } | |
| _ => parser_err!( | |
| "Expected table column definitions after TABLE keyword", | |
| p.peek_token_ref().span.start | |
| )?, | |
| }; | |
| Ok(DataType::NamedTable { | |
| name: ObjectName(vec![ObjectNamePart::Identifier(return_table_name)]), | |
| columns: table_column_defs, | |
| }) | |
| })?; | |
| let data_type = match return_table { | |
| Some(table_type) => table_type, | |
| None => self.parse_data_type()?, | |
| }; | |
| let return_type = Some(FunctionReturnType::DataType(data_type)); | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let function_body = if self.peek_keyword(Keyword::BEGIN) { | |
| let begin_token = self.expect_keyword(Keyword::BEGIN)?; | |
| let statements = self.parse_statement_list(&[Keyword::END])?; | |
| let end_token = self.expect_keyword(Keyword::END)?; | |
| Some(CreateFunctionBody::AsBeginEnd(BeginEndStatements { | |
| begin_token: AttachedToken(begin_token), | |
| statements, | |
| end_token: AttachedToken(end_token), | |
| })) | |
| } else if self.parse_keyword(Keyword::RETURN) { | |
| if self.peek_token_ref().token == Token::LParen { | |
| Some(CreateFunctionBody::AsReturnExpr(self.parse_expr()?)) | |
| } else if self.peek_keyword(Keyword::SELECT) { | |
| let select = self.parse_select()?; | |
| Some(CreateFunctionBody::AsReturnSelect(select)) | |
| } else { | |
| parser_err!( | |
| "Expected a subquery (or bare SELECT statement) after RETURN", | |
| self.peek_token_ref().span.start | |
| )? | |
| } | |
| } else { | |
| parser_err!("Unparsable function body", self.peek_token_ref().span.start)? | |
| }; | |
| Ok(CreateFunction { | |
| or_alter, | |
| or_replace, | |
| temporary, | |
| if_not_exists: false, | |
| name, | |
| args: Some(args), | |
| return_type, | |
| function_body, | |
| language: None, | |
| determinism_specifier: None, | |
| options: None, | |
| remote_connection: None, | |
| using: None, | |
| behavior: None, | |
| called_on_null: None, | |
| parallel: None, | |
| security: None, | |
| set_params: vec![], | |
| }) | |
| } | |
| fn parse_function_return_type(&mut self) -> Result<FunctionReturnType, ParserError> { | |
| if self.parse_keyword(Keyword::SETOF) { | |
| Ok(FunctionReturnType::SetOf(self.parse_data_type()?)) | |
| } else { | |
| Ok(FunctionReturnType::DataType(self.parse_data_type()?)) | |
| } | |
| } | |
| fn parse_create_function_name_and_params( | |
| &mut self, | |
| ) -> Result<(ObjectName, Vec<OperateFunctionArg>), ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let parse_function_param = | |
| |parser: &mut Parser| -> Result<OperateFunctionArg, ParserError> { | |
| let name = parser.parse_identifier()?; | |
| let data_type = parser.parse_data_type()?; | |
| let default_expr = if parser.consume_token(&Token::Eq) { | |
| Some(parser.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(OperateFunctionArg { | |
| mode: None, | |
| name: Some(name), | |
| data_type, | |
| default_expr, | |
| }) | |
| }; | |
| self.expect_token(&Token::LParen)?; | |
| let args = self.parse_comma_separated0(parse_function_param, Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok((name, args)) | |
| } | |
| fn parse_function_arg(&mut self) -> Result<OperateFunctionArg, ParserError> { | |
| let mode = if self.parse_keyword(Keyword::IN) { | |
| Some(ArgMode::In) | |
| } else if self.parse_keyword(Keyword::OUT) { | |
| Some(ArgMode::Out) | |
| } else if self.parse_keyword(Keyword::INOUT) { | |
| Some(ArgMode::InOut) | |
| } else if self.parse_keyword(Keyword::VARIADIC) { | |
| Some(ArgMode::Variadic) | |
| } else { | |
| None | |
| }; | |
| // parse: [ argname ] argtype | |
| let mut name = None; | |
| let mut data_type = self.parse_data_type()?; | |
| // To check whether the first token is a name or a type, we need to | |
| // peek the next token, which if it is another type keyword, then the | |
| // first token is a name and not a type in itself. | |
| let data_type_idx = self.get_current_index(); | |
| // DEFAULT will be parsed as `DataType::Custom`, which is undesirable in this context | |
| fn parse_data_type_no_default(parser: &mut Parser) -> Result<DataType, ParserError> { | |
| if parser.peek_keyword(Keyword::DEFAULT) { | |
| // This dummy error is ignored in `maybe_parse` | |
| parser_err!( | |
| "The DEFAULT keyword is not a type", | |
| parser.peek_token_ref().span.start | |
| ) | |
| } else { | |
| parser.parse_data_type() | |
| } | |
| } | |
| if let Some(next_data_type) = self.maybe_parse(parse_data_type_no_default)? { | |
| let token = self.token_at(data_type_idx).clone(); | |
| // We ensure that the token is a `Word` token, and not other special tokens. | |
| match token.token { | |
| Token::Word(word) => name = Some(word.into_ident(token.span)), | |
| _ => return self.expected("a name or type", token), | |
| } | |
| data_type = next_data_type; | |
| } | |
| let default_expr = if self.parse_keyword(Keyword::DEFAULT) || self.consume_token(&Token::Eq) | |
| { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(OperateFunctionArg { | |
| mode, | |
| name, | |
| data_type, | |
| default_expr, | |
| }) | |
| } | |
| fn parse_aggregate_function_arg(&mut self) -> Result<OperateFunctionArg, ParserError> { | |
| let mode = if self.parse_keyword(Keyword::IN) { | |
| Some(ArgMode::In) | |
| } else { | |
| if self | |
| .peek_one_of_keywords(&[Keyword::OUT, Keyword::INOUT, Keyword::VARIADIC]) | |
| .is_some() | |
| { | |
| return self.expected_ref( | |
| "IN or argument type in aggregate signature", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| None | |
| }; | |
| // Parse: [ argname ] argtype, but do not consume ORDER from | |
| // `... argtype ORDER BY ...` as a type-name disambiguator. | |
| let mut name = None; | |
| let mut data_type = self.parse_data_type()?; | |
| let data_type_idx = self.get_current_index(); | |
| fn parse_data_type_for_aggregate_arg(parser: &mut Parser) -> Result<DataType, ParserError> { | |
| if parser.peek_keyword(Keyword::DEFAULT) | |
| || parser.peek_keyword(Keyword::ORDER) | |
| || parser.peek_token_ref().token == Token::Comma | |
| || parser.peek_token_ref().token == Token::RParen | |
| { | |
| // Dummy error ignored by maybe_parse | |
| parser_err!( | |
| "The current token cannot start an aggregate argument type", | |
| parser.peek_token_ref().span.start | |
| ) | |
| } else { | |
| parser.parse_data_type() | |
| } | |
| } | |
| if let Some(next_data_type) = self.maybe_parse(parse_data_type_for_aggregate_arg)? { | |
| let token = self.token_at(data_type_idx).clone(); | |
| match token.token { | |
| Token::Word(word) => name = Some(word.into_ident(token.span)), | |
| _ => return self.expected("a name or type", token), | |
| } | |
| data_type = next_data_type; | |
| } | |
| if self.peek_keyword(Keyword::DEFAULT) || self.peek_token_ref().token == Token::Eq { | |
| return self.expected_ref( | |
| "',' or ')' or ORDER BY after aggregate argument type", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| Ok(OperateFunctionArg { | |
| mode, | |
| name, | |
| data_type, | |
| default_expr: None, | |
| }) | |
| } | |
| /// Parse statements of the DropTrigger type such as: | |
| /// | |
| /// ```sql | |
| /// DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] | |
| /// ``` | |
| pub fn parse_drop_trigger(&mut self) -> Result<DropTrigger, ParserError> { | |
| if !dialect_of!(self is PostgreSqlDialect | SQLiteDialect | GenericDialect | MySqlDialect | MsSqlDialect) | |
| { | |
| self.prev_token(); | |
| return self.expected_ref("an object type after DROP", self.peek_token_ref()); | |
| } | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let trigger_name = self.parse_object_name(false)?; | |
| let table_name = if self.parse_keyword(Keyword::ON) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| let option = match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) { | |
| Some(Keyword::CASCADE) => Some(ReferentialAction::Cascade), | |
| Some(Keyword::RESTRICT) => Some(ReferentialAction::Restrict), | |
| Some(unexpected_keyword) => return Err(ParserError::ParserError( | |
| format!("Internal parser error: expected any of {{CASCADE, RESTRICT}}, got {unexpected_keyword:?}"), | |
| )), | |
| None => None, | |
| }; | |
| Ok(DropTrigger { | |
| if_exists, | |
| trigger_name, | |
| table_name, | |
| option, | |
| }) | |
| } | |
| /// Parse a `CREATE TRIGGER` statement. | |
| pub fn parse_create_trigger( | |
| &mut self, | |
| temporary: bool, | |
| or_alter: bool, | |
| or_replace: bool, | |
| is_constraint: bool, | |
| ) -> Result<CreateTrigger, ParserError> { | |
| if !dialect_of!(self is PostgreSqlDialect | SQLiteDialect | GenericDialect | MySqlDialect | MsSqlDialect) | |
| { | |
| self.prev_token(); | |
| return self.expected_ref("an object type after CREATE", self.peek_token_ref()); | |
| } | |
| let name = self.parse_object_name(false)?; | |
| let period = self.maybe_parse(|parser| parser.parse_trigger_period())?; | |
| let events = self.parse_keyword_separated(Keyword::OR, Parser::parse_trigger_event)?; | |
| self.expect_keyword_is(Keyword::ON)?; | |
| let table_name = self.parse_object_name(false)?; | |
| let referenced_table_name = if self.parse_keyword(Keyword::FROM) { | |
| self.parse_object_name(true).ok() | |
| } else { | |
| None | |
| }; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| let mut referencing = vec![]; | |
| if self.parse_keyword(Keyword::REFERENCING) { | |
| while let Some(refer) = self.parse_trigger_referencing()? { | |
| referencing.push(refer); | |
| } | |
| } | |
| let trigger_object = if self.parse_keyword(Keyword::FOR) { | |
| let include_each = self.parse_keyword(Keyword::EACH); | |
| let trigger_object = | |
| match self.expect_one_of_keywords(&[Keyword::ROW, Keyword::STATEMENT])? { | |
| Keyword::ROW => TriggerObject::Row, | |
| Keyword::STATEMENT => TriggerObject::Statement, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in ROW/STATEMENT"), | |
| )), | |
| }; | |
| Some(if include_each { | |
| TriggerObjectKind::ForEach(trigger_object) | |
| } else { | |
| TriggerObjectKind::For(trigger_object) | |
| }) | |
| } else { | |
| let _ = self.parse_keyword(Keyword::FOR); | |
| None | |
| }; | |
| let condition = self | |
| .parse_keyword(Keyword::WHEN) | |
| .then(|| self.parse_expr()) | |
| .transpose()?; | |
| let mut exec_body = None; | |
| let mut statements = None; | |
| if self.parse_keyword(Keyword::EXECUTE) { | |
| exec_body = Some(self.parse_trigger_exec_body()?); | |
| } else { | |
| statements = Some(self.parse_conditional_statements(&[Keyword::END])?); | |
| } | |
| Ok(CreateTrigger { | |
| or_alter, | |
| temporary, | |
| or_replace, | |
| is_constraint, | |
| name, | |
| period, | |
| period_before_table: true, | |
| events, | |
| table_name, | |
| referenced_table_name, | |
| referencing, | |
| trigger_object, | |
| condition, | |
| exec_body, | |
| statements_as: false, | |
| statements, | |
| characteristics, | |
| }) | |
| } | |
| /// Parse the period part of a trigger (`BEFORE`, `AFTER`, etc.). | |
| pub fn parse_trigger_period(&mut self) -> Result<TriggerPeriod, ParserError> { | |
| Ok( | |
| match self.expect_one_of_keywords(&[ | |
| Keyword::FOR, | |
| Keyword::BEFORE, | |
| Keyword::AFTER, | |
| Keyword::INSTEAD, | |
| ])? { | |
| Keyword::FOR => TriggerPeriod::For, | |
| Keyword::BEFORE => TriggerPeriod::Before, | |
| Keyword::AFTER => TriggerPeriod::After, | |
| Keyword::INSTEAD => self | |
| .expect_keyword_is(Keyword::OF) | |
| .map(|_| TriggerPeriod::InsteadOf)?, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger period"), | |
| )), | |
| }, | |
| ) | |
| } | |
| /// Parse the event part of a trigger (`INSERT`, `UPDATE`, etc.). | |
| pub fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParserError> { | |
| Ok( | |
| match self.expect_one_of_keywords(&[ | |
| Keyword::INSERT, | |
| Keyword::UPDATE, | |
| Keyword::DELETE, | |
| Keyword::TRUNCATE, | |
| ])? { | |
| Keyword::INSERT => TriggerEvent::Insert, | |
| Keyword::UPDATE => { | |
| if self.parse_keyword(Keyword::OF) { | |
| let cols = self.parse_comma_separated(Parser::parse_identifier)?; | |
| TriggerEvent::Update(cols) | |
| } else { | |
| TriggerEvent::Update(vec![]) | |
| } | |
| } | |
| Keyword::DELETE => TriggerEvent::Delete, | |
| Keyword::TRUNCATE => TriggerEvent::Truncate, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger event"), | |
| )), | |
| }, | |
| ) | |
| } | |
| /// Parse the `REFERENCING` clause of a trigger. | |
| pub fn parse_trigger_referencing(&mut self) -> Result<Option<TriggerReferencing>, ParserError> { | |
| let refer_type = match self.parse_one_of_keywords(&[Keyword::OLD, Keyword::NEW]) { | |
| Some(Keyword::OLD) if self.parse_keyword(Keyword::TABLE) => { | |
| TriggerReferencingType::OldTable | |
| } | |
| Some(Keyword::NEW) if self.parse_keyword(Keyword::TABLE) => { | |
| TriggerReferencingType::NewTable | |
| } | |
| _ => { | |
| return Ok(None); | |
| } | |
| }; | |
| let is_as = self.parse_keyword(Keyword::AS); | |
| let transition_relation_name = self.parse_object_name(false)?; | |
| Ok(Some(TriggerReferencing { | |
| refer_type, | |
| is_as, | |
| transition_relation_name, | |
| })) | |
| } | |
| /// Parse the execution body of a trigger (`FUNCTION` or `PROCEDURE`). | |
| pub fn parse_trigger_exec_body(&mut self) -> Result<TriggerExecBody, ParserError> { | |
| Ok(TriggerExecBody { | |
| exec_type: match self | |
| .expect_one_of_keywords(&[Keyword::FUNCTION, Keyword::PROCEDURE])? | |
| { | |
| Keyword::FUNCTION => TriggerExecBodyType::Function, | |
| Keyword::PROCEDURE => TriggerExecBodyType::Procedure, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in trigger exec body"), | |
| )), | |
| }, | |
| func_desc: self.parse_function_desc()?, | |
| }) | |
| } | |
| /// Parse a `CREATE MACRO` statement. | |
| pub fn parse_create_macro( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| ) -> Result<Statement, ParserError> { | |
| if dialect_of!(self is DuckDbDialect | GenericDialect) { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::LParen)?; | |
| let args = if self.consume_token(&Token::RParen) { | |
| self.prev_token(); | |
| None | |
| } else { | |
| Some(self.parse_comma_separated(Parser::parse_macro_arg)?) | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| Ok(Statement::CreateMacro { | |
| or_replace, | |
| temporary, | |
| name, | |
| args, | |
| definition: if self.parse_keyword(Keyword::TABLE) { | |
| MacroDefinition::Table(self.parse_query()?) | |
| } else { | |
| MacroDefinition::Expr(self.parse_expr()?) | |
| }, | |
| }) | |
| } else { | |
| self.prev_token(); | |
| self.expected_ref("an object type after CREATE", self.peek_token_ref()) | |
| } | |
| } | |
| fn parse_macro_arg(&mut self) -> Result<MacroArg, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let default_expr = | |
| if self.consume_token(&Token::Assignment) || self.consume_token(&Token::RArrow) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(MacroArg { name, default_expr }) | |
| } | |
| /// Parse a `CREATE EXTERNAL TABLE` statement. | |
| pub fn parse_create_external_table( | |
| &mut self, | |
| or_replace: bool, | |
| ) -> Result<CreateTable, ParserError> { | |
| self.expect_keyword_is(Keyword::TABLE)?; | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let table_name = self.parse_object_name(false)?; | |
| let (columns, constraints) = self.parse_columns()?; | |
| let hive_distribution = self.parse_hive_distribution()?; | |
| let hive_formats = self.parse_hive_formats()?; | |
| let file_format = if let Some(ref hf) = hive_formats { | |
| if let Some(ref ff) = hf.storage { | |
| match ff { | |
| HiveIOFormat::FileFormat { format } => Some(*format), | |
| _ => None, | |
| } | |
| } else { | |
| None | |
| } | |
| } else { | |
| None | |
| }; | |
| let location = hive_formats.as_ref().and_then(|hf| hf.location.clone()); | |
| let with_connection = if self.parse_keywords(&[Keyword::WITH, Keyword::CONNECTION]) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; | |
| let table_options = if !table_properties.is_empty() { | |
| CreateTableOptions::TableProperties(table_properties) | |
| } else if let Some(options) = self.maybe_parse_options(Keyword::OPTIONS)? { | |
| CreateTableOptions::Options(options) | |
| } else { | |
| CreateTableOptions::None | |
| }; | |
| Ok(CreateTableBuilder::new(table_name) | |
| .columns(columns) | |
| .constraints(constraints) | |
| .hive_distribution(hive_distribution) | |
| .hive_formats(hive_formats) | |
| .table_options(table_options) | |
| .with_connection(with_connection) | |
| .or_replace(or_replace) | |
| .if_not_exists(if_not_exists) | |
| .external(true) | |
| .file_format(file_format) | |
| .location(location) | |
| .build()) | |
| } | |
| /// Parse `CREATE SNAPSHOT TABLE` statement. | |
| /// | |
| /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_snapshot_table_statement> | |
| pub fn parse_create_snapshot_table(&mut self) -> Result<CreateTable, ParserError> { | |
| self.expect_keywords(&[Keyword::SNAPSHOT, Keyword::TABLE])?; | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let table_name = self.parse_object_name(true)?; | |
| self.expect_keyword_is(Keyword::CLONE)?; | |
| let clone = Some(self.parse_object_name(true)?); | |
| let version = | |
| if self.parse_keywords(&[Keyword::FOR, Keyword::SYSTEM_TIME, Keyword::AS, Keyword::OF]) | |
| { | |
| Some(TableVersion::ForSystemTimeAsOf(self.parse_expr()?)) | |
| } else { | |
| None | |
| }; | |
| let table_options = if let Some(options) = self.maybe_parse_options(Keyword::OPTIONS)? { | |
| CreateTableOptions::Options(options) | |
| } else { | |
| CreateTableOptions::None | |
| }; | |
| Ok(CreateTableBuilder::new(table_name) | |
| .snapshot(true) | |
| .if_not_exists(if_not_exists) | |
| .clone_clause(clone) | |
| .version(version) | |
| .table_options(table_options) | |
| .build()) | |
| } | |
| /// Parse a file format for external tables. | |
| pub fn parse_file_format(&mut self) -> Result<FileFormat, ParserError> { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::AVRO => Ok(FileFormat::AVRO), | |
| Keyword::JSONFILE => Ok(FileFormat::JSONFILE), | |
| Keyword::ORC => Ok(FileFormat::ORC), | |
| Keyword::PARQUET => Ok(FileFormat::PARQUET), | |
| Keyword::RCFILE => Ok(FileFormat::RCFILE), | |
| Keyword::SEQUENCEFILE => Ok(FileFormat::SEQUENCEFILE), | |
| Keyword::TEXTFILE => Ok(FileFormat::TEXTFILE), | |
| _ => self.expected("fileformat", next_token), | |
| }, | |
| _ => self.expected("fileformat", next_token), | |
| } | |
| } | |
| fn parse_analyze_format_kind(&mut self) -> Result<AnalyzeFormatKind, ParserError> { | |
| if self.consume_token(&Token::Eq) { | |
| Ok(AnalyzeFormatKind::Assignment(self.parse_analyze_format()?)) | |
| } else { | |
| Ok(AnalyzeFormatKind::Keyword(self.parse_analyze_format()?)) | |
| } | |
| } | |
| /// Parse an `ANALYZE FORMAT`. | |
| pub fn parse_analyze_format(&mut self) -> Result<AnalyzeFormat, ParserError> { | |
| let next_token = self.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::TEXT => Ok(AnalyzeFormat::TEXT), | |
| Keyword::GRAPHVIZ => Ok(AnalyzeFormat::GRAPHVIZ), | |
| Keyword::JSON => Ok(AnalyzeFormat::JSON), | |
| Keyword::TREE => Ok(AnalyzeFormat::TREE), | |
| _ => self.expected("fileformat", next_token), | |
| }, | |
| _ => self.expected("fileformat", next_token), | |
| } | |
| } | |
| /// Parse a `CREATE VIEW` statement. | |
| pub fn parse_create_view( | |
| &mut self, | |
| or_alter: bool, | |
| or_replace: bool, | |
| temporary: bool, | |
| create_view_params: Option<CreateViewParams>, | |
| ) -> Result<CreateView, ParserError> { | |
| let secure = self.parse_keyword(Keyword::SECURE); | |
| let materialized = self.parse_keyword(Keyword::MATERIALIZED); | |
| self.expect_keyword_is(Keyword::VIEW)?; | |
| let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); | |
| // Tries to parse IF NOT EXISTS either before name or after name | |
| // Name before IF NOT EXISTS is supported by snowflake but undocumented | |
| let if_not_exists_first = | |
| self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_object_name(allow_unquoted_hyphen)?; | |
| let name_before_not_exists = !if_not_exists_first | |
| && self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let if_not_exists = if_not_exists_first || name_before_not_exists; | |
| let mut copy_grants = self.parse_keywords(&[Keyword::COPY, Keyword::GRANTS]); | |
| // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). | |
| // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. | |
| let columns = self.parse_view_columns()?; | |
| // Snowflake also documents `COPY GRANTS` *after* the column list; accept | |
| // either position, but not both. | |
| // <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax> | |
| if !copy_grants { | |
| copy_grants = self.parse_keywords(&[Keyword::COPY, Keyword::GRANTS]); | |
| } | |
| let mut options = CreateTableOptions::None; | |
| let with_options = self.parse_options(Keyword::WITH)?; | |
| if !with_options.is_empty() { | |
| options = CreateTableOptions::With(with_options); | |
| } | |
| let cluster_by = if self.parse_keyword(Keyword::CLUSTER) { | |
| self.expect_keyword_is(Keyword::BY)?; | |
| self.parse_parenthesized_column_list(Optional, false)? | |
| } else { | |
| vec![] | |
| }; | |
| if dialect_of!(self is BigQueryDialect | GenericDialect) { | |
| if let Some(opts) = self.maybe_parse_options(Keyword::OPTIONS)? { | |
| if !opts.is_empty() { | |
| options = CreateTableOptions::Options(opts); | |
| } | |
| }; | |
| } | |
| let to = if dialect_of!(self is ClickHouseDialect | GenericDialect) | |
| && self.parse_keyword(Keyword::TO) | |
| { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| let comment = if self.dialect.supports_create_view_comment_syntax() | |
| && self.parse_keyword(Keyword::COMMENT) | |
| { | |
| self.expect_token(&Token::Eq)?; | |
| Some(self.parse_comment_value()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let query = self.parse_query()?; | |
| // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. | |
| let with_no_schema_binding = dialect_of!(self is RedshiftSqlDialect | GenericDialect) | |
| && self.parse_keywords(&[ | |
| Keyword::WITH, | |
| Keyword::NO, | |
| Keyword::SCHEMA, | |
| Keyword::BINDING, | |
| ]); | |
| Ok(CreateView { | |
| or_alter, | |
| name, | |
| columns, | |
| query, | |
| materialized, | |
| secure, | |
| or_replace, | |
| options, | |
| cluster_by, | |
| comment, | |
| with_no_schema_binding, | |
| if_not_exists, | |
| temporary, | |
| copy_grants, | |
| to, | |
| params: create_view_params, | |
| name_before_not_exists, | |
| }) | |
| } | |
| /// Parse optional parameters for the `CREATE VIEW` statement supported by [MySQL]. | |
| /// | |
| /// [MySQL]: https://dev.mysql.com/doc/refman/9.1/en/create-view.html | |
| fn parse_create_view_params(&mut self) -> Result<Option<CreateViewParams>, ParserError> { | |
| let algorithm = if self.parse_keyword(Keyword::ALGORITHM) { | |
| self.expect_token(&Token::Eq)?; | |
| Some( | |
| match self.expect_one_of_keywords(&[ | |
| Keyword::UNDEFINED, | |
| Keyword::MERGE, | |
| Keyword::TEMPTABLE, | |
| ])? { | |
| Keyword::UNDEFINED => CreateViewAlgorithm::Undefined, | |
| Keyword::MERGE => CreateViewAlgorithm::Merge, | |
| Keyword::TEMPTABLE => CreateViewAlgorithm::TempTable, | |
| _ => { | |
| self.prev_token(); | |
| let found = self.next_token(); | |
| return self | |
| .expected("UNDEFINED or MERGE or TEMPTABLE after ALGORITHM =", found); | |
| } | |
| }, | |
| ) | |
| } else { | |
| None | |
| }; | |
| let definer = if self.parse_keyword(Keyword::DEFINER) { | |
| self.expect_token(&Token::Eq)?; | |
| Some(self.parse_grantee_name()?) | |
| } else { | |
| None | |
| }; | |
| let security = if self.parse_keywords(&[Keyword::SQL, Keyword::SECURITY]) { | |
| Some( | |
| match self.expect_one_of_keywords(&[Keyword::DEFINER, Keyword::INVOKER])? { | |
| Keyword::DEFINER => CreateViewSecurity::Definer, | |
| Keyword::INVOKER => CreateViewSecurity::Invoker, | |
| _ => { | |
| self.prev_token(); | |
| let found = self.next_token(); | |
| return self.expected("DEFINER or INVOKER after SQL SECURITY", found); | |
| } | |
| }, | |
| ) | |
| } else { | |
| None | |
| }; | |
| if algorithm.is_some() || definer.is_some() || security.is_some() { | |
| Ok(Some(CreateViewParams { | |
| algorithm, | |
| definer, | |
| security, | |
| })) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a `CREATE ROLE` statement. | |
| pub fn parse_create_role(&mut self) -> Result<CreateRole, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] | |
| let optional_keywords = if dialect_of!(self is MsSqlDialect) { | |
| vec![Keyword::AUTHORIZATION] | |
| } else if dialect_of!(self is PostgreSqlDialect) { | |
| vec![ | |
| Keyword::LOGIN, | |
| Keyword::NOLOGIN, | |
| Keyword::INHERIT, | |
| Keyword::NOINHERIT, | |
| Keyword::BYPASSRLS, | |
| Keyword::NOBYPASSRLS, | |
| Keyword::PASSWORD, | |
| Keyword::CREATEDB, | |
| Keyword::NOCREATEDB, | |
| Keyword::CREATEROLE, | |
| Keyword::NOCREATEROLE, | |
| Keyword::SUPERUSER, | |
| Keyword::NOSUPERUSER, | |
| Keyword::REPLICATION, | |
| Keyword::NOREPLICATION, | |
| Keyword::CONNECTION, | |
| Keyword::VALID, | |
| Keyword::IN, | |
| Keyword::ROLE, | |
| Keyword::ADMIN, | |
| Keyword::USER, | |
| ] | |
| } else { | |
| vec![] | |
| }; | |
| // MSSQL | |
| let mut authorization_owner = None; | |
| // Postgres | |
| let mut login = None; | |
| let mut inherit = None; | |
| let mut bypassrls = None; | |
| let mut password = None; | |
| let mut create_db = None; | |
| let mut create_role = None; | |
| let mut superuser = None; | |
| let mut replication = None; | |
| let mut connection_limit = None; | |
| let mut valid_until = None; | |
| let mut in_role = vec![]; | |
| let mut in_group = vec![]; | |
| let mut role = vec![]; | |
| let mut user = vec![]; | |
| let mut admin = vec![]; | |
| while let Some(keyword) = self.parse_one_of_keywords(&optional_keywords) { | |
| let loc = self | |
| .tokens | |
| .get(self.index - 1) | |
| .map_or(Location { line: 0, column: 0 }, |t| t.span.start); | |
| match keyword { | |
| Keyword::AUTHORIZATION => { | |
| if authorization_owner.is_some() { | |
| parser_err!("Found multiple AUTHORIZATION", loc) | |
| } else { | |
| authorization_owner = Some(self.parse_object_name(false)?); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::LOGIN | Keyword::NOLOGIN => { | |
| if login.is_some() { | |
| parser_err!("Found multiple LOGIN or NOLOGIN", loc) | |
| } else { | |
| login = Some(keyword == Keyword::LOGIN); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::INHERIT | Keyword::NOINHERIT => { | |
| if inherit.is_some() { | |
| parser_err!("Found multiple INHERIT or NOINHERIT", loc) | |
| } else { | |
| inherit = Some(keyword == Keyword::INHERIT); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::BYPASSRLS | Keyword::NOBYPASSRLS => { | |
| if bypassrls.is_some() { | |
| parser_err!("Found multiple BYPASSRLS or NOBYPASSRLS", loc) | |
| } else { | |
| bypassrls = Some(keyword == Keyword::BYPASSRLS); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::CREATEDB | Keyword::NOCREATEDB => { | |
| if create_db.is_some() { | |
| parser_err!("Found multiple CREATEDB or NOCREATEDB", loc) | |
| } else { | |
| create_db = Some(keyword == Keyword::CREATEDB); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::CREATEROLE | Keyword::NOCREATEROLE => { | |
| if create_role.is_some() { | |
| parser_err!("Found multiple CREATEROLE or NOCREATEROLE", loc) | |
| } else { | |
| create_role = Some(keyword == Keyword::CREATEROLE); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::SUPERUSER | Keyword::NOSUPERUSER => { | |
| if superuser.is_some() { | |
| parser_err!("Found multiple SUPERUSER or NOSUPERUSER", loc) | |
| } else { | |
| superuser = Some(keyword == Keyword::SUPERUSER); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::REPLICATION | Keyword::NOREPLICATION => { | |
| if replication.is_some() { | |
| parser_err!("Found multiple REPLICATION or NOREPLICATION", loc) | |
| } else { | |
| replication = Some(keyword == Keyword::REPLICATION); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::PASSWORD => { | |
| if password.is_some() { | |
| parser_err!("Found multiple PASSWORD", loc) | |
| } else { | |
| password = if self.parse_keyword(Keyword::NULL) { | |
| Some(Password::NullPassword) | |
| } else { | |
| Some(Password::Password(Expr::Value(self.parse_value()?))) | |
| }; | |
| Ok(()) | |
| } | |
| } | |
| Keyword::CONNECTION => { | |
| self.expect_keyword_is(Keyword::LIMIT)?; | |
| if connection_limit.is_some() { | |
| parser_err!("Found multiple CONNECTION LIMIT", loc) | |
| } else { | |
| connection_limit = Some(Expr::Value(self.parse_number_value()?)); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::VALID => { | |
| self.expect_keyword_is(Keyword::UNTIL)?; | |
| if valid_until.is_some() { | |
| parser_err!("Found multiple VALID UNTIL", loc) | |
| } else { | |
| valid_until = Some(Expr::Value(self.parse_value()?)); | |
| Ok(()) | |
| } | |
| } | |
| Keyword::IN => { | |
| if self.parse_keyword(Keyword::ROLE) { | |
| if !in_role.is_empty() { | |
| parser_err!("Found multiple IN ROLE", loc) | |
| } else { | |
| in_role = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| Ok(()) | |
| } | |
| } else if self.parse_keyword(Keyword::GROUP) { | |
| if !in_group.is_empty() { | |
| parser_err!("Found multiple IN GROUP", loc) | |
| } else { | |
| in_group = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| Ok(()) | |
| } | |
| } else { | |
| self.expected_ref("ROLE or GROUP after IN", self.peek_token_ref()) | |
| } | |
| } | |
| Keyword::ROLE => { | |
| if !role.is_empty() { | |
| parser_err!("Found multiple ROLE", loc) | |
| } else { | |
| role = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| Ok(()) | |
| } | |
| } | |
| Keyword::USER => { | |
| if !user.is_empty() { | |
| parser_err!("Found multiple USER", loc) | |
| } else { | |
| user = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| Ok(()) | |
| } | |
| } | |
| Keyword::ADMIN => { | |
| if !admin.is_empty() { | |
| parser_err!("Found multiple ADMIN", loc) | |
| } else { | |
| admin = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| Ok(()) | |
| } | |
| } | |
| _ => break, | |
| }? | |
| } | |
| Ok(CreateRole { | |
| names, | |
| if_not_exists, | |
| login, | |
| inherit, | |
| bypassrls, | |
| password, | |
| create_db, | |
| create_role, | |
| replication, | |
| superuser, | |
| connection_limit, | |
| valid_until, | |
| in_role, | |
| in_group, | |
| role, | |
| user, | |
| admin, | |
| authorization_owner, | |
| }) | |
| } | |
| /// Parse an `OWNER` clause. | |
| pub fn parse_owner(&mut self) -> Result<Owner, ParserError> { | |
| let owner = match self.parse_one_of_keywords(&[Keyword::CURRENT_USER, Keyword::CURRENT_ROLE, Keyword::SESSION_USER]) { | |
| Some(Keyword::CURRENT_USER) => Owner::CurrentUser, | |
| Some(Keyword::CURRENT_ROLE) => Owner::CurrentRole, | |
| Some(Keyword::SESSION_USER) => Owner::SessionUser, | |
| Some(unexpected_keyword) => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in owner"), | |
| )), | |
| None => { | |
| match self.parse_identifier() { | |
| Ok(ident) => Owner::Ident(ident), | |
| Err(e) => { | |
| return Err(ParserError::ParserError(format!("Expected: CURRENT_USER, CURRENT_ROLE, SESSION_USER or identifier after OWNER TO. {e}"))) | |
| } | |
| } | |
| } | |
| }; | |
| Ok(owner) | |
| } | |
| /// Parses a [Statement::CreateDomain] statement. | |
| fn parse_create_domain(&mut self) -> Result<CreateDomain, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let data_type = self.parse_data_type()?; | |
| let collation = if self.parse_keyword(Keyword::COLLATE) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let default = if self.parse_keyword(Keyword::DEFAULT) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let mut constraints = Vec::new(); | |
| while let Some(constraint) = self.parse_optional_table_constraint()? { | |
| constraints.push(constraint); | |
| } | |
| Ok(CreateDomain { | |
| name, | |
| data_type, | |
| collation, | |
| default, | |
| constraints, | |
| }) | |
| } | |
| /// ```sql | |
| /// CREATE POLICY name ON table_name [ AS { PERMISSIVE | RESTRICTIVE } ] | |
| /// [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ] | |
| /// [ TO { role_name | PUBLIC | CURRENT_USER | CURRENT_ROLE | SESSION_USER } [, ...] ] | |
| /// [ USING ( using_expression ) ] | |
| /// [ WITH CHECK ( with_check_expression ) ] | |
| /// ``` | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createpolicy.html) | |
| pub fn parse_create_policy(&mut self) -> Result<CreatePolicy, ParserError> { | |
| let name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::ON)?; | |
| let table_name = self.parse_object_name(false)?; | |
| let policy_type = if self.parse_keyword(Keyword::AS) { | |
| let keyword = | |
| self.expect_one_of_keywords(&[Keyword::PERMISSIVE, Keyword::RESTRICTIVE])?; | |
| Some(match keyword { | |
| Keyword::PERMISSIVE => CreatePolicyType::Permissive, | |
| Keyword::RESTRICTIVE => CreatePolicyType::Restrictive, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in policy type"), | |
| )), | |
| }) | |
| } else { | |
| None | |
| }; | |
| let command = if self.parse_keyword(Keyword::FOR) { | |
| let keyword = self.expect_one_of_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::SELECT, | |
| Keyword::INSERT, | |
| Keyword::UPDATE, | |
| Keyword::DELETE, | |
| ])?; | |
| Some(match keyword { | |
| Keyword::ALL => CreatePolicyCommand::All, | |
| Keyword::SELECT => CreatePolicyCommand::Select, | |
| Keyword::INSERT => CreatePolicyCommand::Insert, | |
| Keyword::UPDATE => CreatePolicyCommand::Update, | |
| Keyword::DELETE => CreatePolicyCommand::Delete, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in policy command"), | |
| )), | |
| }) | |
| } else { | |
| None | |
| }; | |
| let to = if self.parse_keyword(Keyword::TO) { | |
| Some(self.parse_comma_separated(|p| p.parse_owner())?) | |
| } else { | |
| None | |
| }; | |
| let using = if self.parse_keyword(Keyword::USING) { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(expr) | |
| } else { | |
| None | |
| }; | |
| let with_check = if self.parse_keywords(&[Keyword::WITH, Keyword::CHECK]) { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(expr) | |
| } else { | |
| None | |
| }; | |
| Ok(CreatePolicy { | |
| name, | |
| table_name, | |
| policy_type, | |
| command, | |
| to, | |
| using, | |
| with_check, | |
| }) | |
| } | |
| /// ```sql | |
| /// CREATE CONNECTOR [IF NOT EXISTS] connector_name | |
| /// [TYPE datasource_type] | |
| /// [URL datasource_url] | |
| /// [COMMENT connector_comment] | |
| /// [WITH DCPROPERTIES(property_name=property_value, ...)] | |
| /// ``` | |
| /// | |
| /// [Hive Documentation](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector) | |
| pub fn parse_create_connector(&mut self) -> Result<CreateConnector, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let connector_type = if self.parse_keyword(Keyword::TYPE) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| let url = if self.parse_keyword(Keyword::URL) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| let comment = self.parse_optional_inline_comment()?; | |
| let with_dcproperties = | |
| match self.parse_options_with_keywords(&[Keyword::WITH, Keyword::DCPROPERTIES])? { | |
| properties if !properties.is_empty() => Some(properties), | |
| _ => None, | |
| }; | |
| Ok(CreateConnector { | |
| name, | |
| if_not_exists, | |
| connector_type, | |
| url, | |
| comment, | |
| with_dcproperties, | |
| }) | |
| } | |
| /// Parse an operator name, which can contain special characters like +, -, <, >, = | |
| /// that are tokenized as operator tokens rather than identifiers. | |
| /// This is used for PostgreSQL CREATE OPERATOR statements. | |
| /// | |
| /// Examples: `+`, `myschema.+`, `pg_catalog.<=` | |
| fn parse_operator_name(&mut self) -> Result<ObjectName, ParserError> { | |
| let mut parts = vec![]; | |
| loop { | |
| parts.push(ObjectNamePart::Identifier(Ident::new( | |
| self.next_token().to_string(), | |
| ))); | |
| if !self.consume_token(&Token::Period) { | |
| break; | |
| } | |
| } | |
| Ok(ObjectName(parts)) | |
| } | |
| /// Parse a [Statement::CreateOperator] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createoperator.html) | |
| pub fn parse_create_operator(&mut self) -> Result<CreateOperator, ParserError> { | |
| let name = self.parse_operator_name()?; | |
| self.expect_token(&Token::LParen)?; | |
| let mut function: Option<ObjectName> = None; | |
| let mut is_procedure = false; | |
| let mut left_arg: Option<DataType> = None; | |
| let mut right_arg: Option<DataType> = None; | |
| let mut options: Vec<OperatorOption> = Vec::new(); | |
| loop { | |
| let keyword = self.expect_one_of_keywords(&[ | |
| Keyword::FUNCTION, | |
| Keyword::PROCEDURE, | |
| Keyword::LEFTARG, | |
| Keyword::RIGHTARG, | |
| Keyword::COMMUTATOR, | |
| Keyword::NEGATOR, | |
| Keyword::RESTRICT, | |
| Keyword::JOIN, | |
| Keyword::HASHES, | |
| Keyword::MERGES, | |
| ])?; | |
| match keyword { | |
| Keyword::HASHES if !options.iter().any(|o| matches!(o, OperatorOption::Hashes)) => { | |
| options.push(OperatorOption::Hashes); | |
| } | |
| Keyword::MERGES if !options.iter().any(|o| matches!(o, OperatorOption::Merges)) => { | |
| options.push(OperatorOption::Merges); | |
| } | |
| Keyword::FUNCTION | Keyword::PROCEDURE if function.is_none() => { | |
| self.expect_token(&Token::Eq)?; | |
| function = Some(self.parse_object_name(false)?); | |
| is_procedure = keyword == Keyword::PROCEDURE; | |
| } | |
| Keyword::LEFTARG if left_arg.is_none() => { | |
| self.expect_token(&Token::Eq)?; | |
| left_arg = Some(self.parse_data_type()?); | |
| } | |
| Keyword::RIGHTARG if right_arg.is_none() => { | |
| self.expect_token(&Token::Eq)?; | |
| right_arg = Some(self.parse_data_type()?); | |
| } | |
| Keyword::COMMUTATOR | |
| if !options | |
| .iter() | |
| .any(|o| matches!(o, OperatorOption::Commutator(_))) => | |
| { | |
| self.expect_token(&Token::Eq)?; | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| self.expect_token(&Token::LParen)?; | |
| let op = self.parse_operator_name()?; | |
| self.expect_token(&Token::RParen)?; | |
| options.push(OperatorOption::Commutator(op)); | |
| } else { | |
| options.push(OperatorOption::Commutator(self.parse_operator_name()?)); | |
| } | |
| } | |
| Keyword::NEGATOR | |
| if !options | |
| .iter() | |
| .any(|o| matches!(o, OperatorOption::Negator(_))) => | |
| { | |
| self.expect_token(&Token::Eq)?; | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| self.expect_token(&Token::LParen)?; | |
| let op = self.parse_operator_name()?; | |
| self.expect_token(&Token::RParen)?; | |
| options.push(OperatorOption::Negator(op)); | |
| } else { | |
| options.push(OperatorOption::Negator(self.parse_operator_name()?)); | |
| } | |
| } | |
| Keyword::RESTRICT | |
| if !options | |
| .iter() | |
| .any(|o| matches!(o, OperatorOption::Restrict(_))) => | |
| { | |
| self.expect_token(&Token::Eq)?; | |
| options.push(OperatorOption::Restrict(Some( | |
| self.parse_object_name(false)?, | |
| ))); | |
| } | |
| Keyword::JOIN if !options.iter().any(|o| matches!(o, OperatorOption::Join(_))) => { | |
| self.expect_token(&Token::Eq)?; | |
| options.push(OperatorOption::Join(Some(self.parse_object_name(false)?))); | |
| } | |
| _ => { | |
| return Err(ParserError::ParserError(format!( | |
| "Duplicate or unexpected keyword {:?} in CREATE OPERATOR", | |
| keyword | |
| ))) | |
| } | |
| } | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| // Expect closing parenthesis | |
| self.expect_token(&Token::RParen)?; | |
| // FUNCTION is required | |
| let function = function.ok_or_else(|| { | |
| ParserError::ParserError("CREATE OPERATOR requires FUNCTION parameter".to_string()) | |
| })?; | |
| Ok(CreateOperator { | |
| name, | |
| function, | |
| is_procedure, | |
| left_arg, | |
| right_arg, | |
| options, | |
| }) | |
| } | |
| /// Parse a [Statement::CreateOperatorFamily] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createopfamily.html) | |
| pub fn parse_create_operator_family(&mut self) -> Result<CreateOperatorFamily, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| Ok(CreateOperatorFamily { name, using }) | |
| } | |
| /// Parse a [Statement::CreateOperatorClass] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createopclass.html) | |
| pub fn parse_create_operator_class(&mut self) -> Result<CreateOperatorClass, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let default = self.parse_keyword(Keyword::DEFAULT); | |
| self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?; | |
| let for_type = self.parse_data_type()?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| let family = if self.parse_keyword(Keyword::FAMILY) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword(Keyword::AS)?; | |
| let mut items = vec![]; | |
| loop { | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| let strategy_number = self.parse_literal_uint()?; | |
| let operator_name = self.parse_operator_name()?; | |
| // Optional operator argument types | |
| let op_types = if self.consume_token(&Token::LParen) { | |
| let left = self.parse_data_type()?; | |
| self.expect_token(&Token::Comma)?; | |
| let right = self.parse_data_type()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(OperatorArgTypes { left, right }) | |
| } else { | |
| None | |
| }; | |
| // Optional purpose | |
| let purpose = if self.parse_keyword(Keyword::FOR) { | |
| if self.parse_keyword(Keyword::SEARCH) { | |
| Some(OperatorPurpose::ForSearch) | |
| } else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| let sort_family = self.parse_object_name(false)?; | |
| Some(OperatorPurpose::ForOrderBy { sort_family }) | |
| } else { | |
| return self | |
| .expected_ref("SEARCH or ORDER BY after FOR", self.peek_token_ref()); | |
| } | |
| } else { | |
| None | |
| }; | |
| items.push(OperatorClassItem::Operator { | |
| strategy_number, | |
| operator_name, | |
| op_types, | |
| purpose, | |
| }); | |
| } else if self.parse_keyword(Keyword::FUNCTION) { | |
| let support_number = self.parse_literal_uint()?; | |
| // Optional operator types | |
| let op_types = if self.consume_token(&Token::LParen) | |
| && self.peek_token_ref().token != Token::RParen | |
| { | |
| let mut types = vec![]; | |
| loop { | |
| types.push(self.parse_data_type()?); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Some(types) | |
| } else { | |
| None | |
| }; | |
| let function_name = self.parse_object_name(false)?; | |
| // Function argument types | |
| let argument_types = if self.consume_token(&Token::LParen) { | |
| let mut types = vec![]; | |
| loop { | |
| if self.peek_token_ref().token == Token::RParen { | |
| break; | |
| } | |
| types.push(self.parse_data_type()?); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| types | |
| } else { | |
| vec![] | |
| }; | |
| items.push(OperatorClassItem::Function { | |
| support_number, | |
| op_types, | |
| function_name, | |
| argument_types, | |
| }); | |
| } else if self.parse_keyword(Keyword::STORAGE) { | |
| let storage_type = self.parse_data_type()?; | |
| items.push(OperatorClassItem::Storage { storage_type }); | |
| } else { | |
| break; | |
| } | |
| // Check for comma separator | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| Ok(CreateOperatorClass { | |
| name, | |
| default, | |
| for_type, | |
| using, | |
| family, | |
| items, | |
| }) | |
| } | |
| /// Parse a `DROP` statement. | |
| pub fn parse_drop(&mut self) -> Result<Statement, ParserError> { | |
| // MySQL dialect supports `TEMPORARY` | |
| let temporary = dialect_of!(self is MySqlDialect | GenericDialect | DuckDbDialect) | |
| && self.parse_keyword(Keyword::TEMPORARY); | |
| let persistent = dialect_of!(self is DuckDbDialect) | |
| && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some(); | |
| let object_type = if self.parse_keyword(Keyword::TABLE) { | |
| ObjectType::Table | |
| } else if self.parse_keyword(Keyword::COLLATION) { | |
| ObjectType::Collation | |
| } else if self.parse_keyword(Keyword::VIEW) { | |
| ObjectType::View | |
| } else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEW]) { | |
| ObjectType::MaterializedView | |
| } else if self.parse_keyword(Keyword::INDEX) { | |
| ObjectType::Index | |
| } else if self.parse_keyword(Keyword::ROLE) { | |
| ObjectType::Role | |
| } else if self.parse_keyword(Keyword::SCHEMA) { | |
| ObjectType::Schema | |
| } else if self.parse_keyword(Keyword::DATABASE) { | |
| ObjectType::Database | |
| } else if self.parse_keyword(Keyword::SEQUENCE) { | |
| ObjectType::Sequence | |
| } else if self.parse_keyword(Keyword::STAGE) { | |
| ObjectType::Stage | |
| } else if self.parse_keyword(Keyword::TYPE) { | |
| ObjectType::Type | |
| } else if self.parse_keyword(Keyword::USER) { | |
| ObjectType::User | |
| } else if self.parse_keyword(Keyword::STREAM) { | |
| ObjectType::Stream | |
| } else if self.parse_keyword(Keyword::WAREHOUSE) { | |
| ObjectType::Warehouse | |
| } else if self.parse_keyword(Keyword::FUNCTION) { | |
| return self.parse_drop_function().map(Into::into); | |
| } else if self.parse_keyword(Keyword::POLICY) { | |
| return self.parse_drop_policy().map(Into::into); | |
| } else if self.parse_keyword(Keyword::CONNECTOR) { | |
| return self.parse_drop_connector(); | |
| } else if self.parse_keyword(Keyword::DOMAIN) { | |
| return self.parse_drop_domain().map(Into::into); | |
| } else if self.parse_keyword(Keyword::PROCEDURE) { | |
| return self.parse_drop_procedure(); | |
| } else if self.parse_keyword(Keyword::SECRET) { | |
| return self.parse_drop_secret(temporary, persistent); | |
| } else if self.parse_keyword(Keyword::TRIGGER) { | |
| return self.parse_drop_trigger().map(Into::into); | |
| } else if self.parse_keyword(Keyword::EXTENSION) { | |
| return self.parse_drop_extension(); | |
| } else if self.parse_keyword(Keyword::OPERATOR) { | |
| // Check if this is DROP OPERATOR FAMILY or DROP OPERATOR CLASS | |
| return if self.parse_keyword(Keyword::FAMILY) { | |
| self.parse_drop_operator_family() | |
| } else if self.parse_keyword(Keyword::CLASS) { | |
| self.parse_drop_operator_class() | |
| } else { | |
| self.parse_drop_operator() | |
| }; | |
| } else { | |
| return self.expected_ref( | |
| "COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| // Many dialects support the non-standard `IF EXISTS` clause and allow | |
| // specifying multiple objects to delete in a single statement | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| let loc = self.peek_token_ref().span.start; | |
| let cascade = self.parse_keyword(Keyword::CASCADE); | |
| let restrict = self.parse_keyword(Keyword::RESTRICT); | |
| let purge = self.parse_keyword(Keyword::PURGE); | |
| if cascade && restrict { | |
| return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP", loc); | |
| } | |
| if object_type == ObjectType::Role && (cascade || restrict || purge) { | |
| return parser_err!( | |
| "Cannot specify CASCADE, RESTRICT, or PURGE in DROP ROLE", | |
| loc | |
| ); | |
| } | |
| let table = if self.parse_keyword(Keyword::ON) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::Drop { | |
| object_type, | |
| if_exists, | |
| names, | |
| cascade, | |
| restrict, | |
| purge, | |
| temporary, | |
| table, | |
| }) | |
| } | |
| fn parse_optional_drop_behavior(&mut self) -> Option<DropBehavior> { | |
| match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) { | |
| Some(Keyword::CASCADE) => Some(DropBehavior::Cascade), | |
| Some(Keyword::RESTRICT) => Some(DropBehavior::Restrict), | |
| _ => None, | |
| } | |
| } | |
| /// ```sql | |
| /// DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] | |
| /// [ CASCADE | RESTRICT ] | |
| /// ``` | |
| fn parse_drop_function(&mut self) -> Result<DropFunction, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let func_desc = self.parse_comma_separated(Parser::parse_function_desc)?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(DropFunction { | |
| if_exists, | |
| func_desc, | |
| drop_behavior, | |
| }) | |
| } | |
| /// ```sql | |
| /// DROP POLICY [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] | |
| /// ``` | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-droppolicy.html) | |
| fn parse_drop_policy(&mut self) -> Result<DropPolicy, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::ON)?; | |
| let table_name = self.parse_object_name(false)?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(DropPolicy { | |
| if_exists, | |
| name, | |
| table_name, | |
| drop_behavior, | |
| }) | |
| } | |
| /// ```sql | |
| /// DROP CONNECTOR [IF EXISTS] name | |
| /// ``` | |
| /// | |
| /// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-DropConnector) | |
| fn parse_drop_connector(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| Ok(Statement::DropConnector { if_exists, name }) | |
| } | |
| /// ```sql | |
| /// DROP DOMAIN [ IF EXISTS ] name [ CASCADE | RESTRICT ] | |
| /// ``` | |
| fn parse_drop_domain(&mut self) -> Result<DropDomain, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(DropDomain { | |
| if_exists, | |
| name, | |
| drop_behavior, | |
| }) | |
| } | |
| /// ```sql | |
| /// DROP PROCEDURE [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] | |
| /// [ CASCADE | RESTRICT ] | |
| /// ``` | |
| fn parse_drop_procedure(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let proc_desc = self.parse_comma_separated(Parser::parse_function_desc)?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(Statement::DropProcedure { | |
| if_exists, | |
| proc_desc, | |
| drop_behavior, | |
| }) | |
| } | |
| fn parse_function_desc(&mut self) -> Result<FunctionDesc, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let args = if self.consume_token(&Token::LParen) { | |
| if self.consume_token(&Token::RParen) { | |
| Some(vec![]) | |
| } else { | |
| let args = self.parse_comma_separated(Parser::parse_function_arg)?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(args) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(FunctionDesc { name, args }) | |
| } | |
| /// See [DuckDB Docs](https://duckdb.org/docs/sql/statements/create_secret.html) for more details. | |
| fn parse_drop_secret( | |
| &mut self, | |
| temporary: bool, | |
| persistent: bool, | |
| ) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let storage_specifier = if self.parse_keyword(Keyword::FROM) { | |
| self.parse_identifier().ok() | |
| } else { | |
| None | |
| }; | |
| let temp = match (temporary, persistent) { | |
| (true, false) => Some(true), | |
| (false, true) => Some(false), | |
| (false, false) => None, | |
| _ => self.expected_ref("TEMPORARY or PERSISTENT", self.peek_token_ref())?, | |
| }; | |
| Ok(Statement::DropSecret { | |
| if_exists, | |
| temporary: temp, | |
| name, | |
| storage_specifier, | |
| }) | |
| } | |
| /// Parse a `DECLARE` statement. | |
| /// | |
| /// ```sql | |
| /// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ] | |
| /// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query | |
| /// ``` | |
| /// | |
| /// The syntax can vary significantly between warehouses. See the grammar | |
| /// on the warehouse specific function in such cases. | |
| pub fn parse_declare(&mut self) -> Result<Statement, ParserError> { | |
| if dialect_of!(self is BigQueryDialect) { | |
| return self.parse_big_query_declare(); | |
| } | |
| if dialect_of!(self is SnowflakeDialect) { | |
| return self.parse_snowflake_declare(); | |
| } | |
| if dialect_of!(self is MsSqlDialect) { | |
| return self.parse_mssql_declare(); | |
| } | |
| let name = self.parse_identifier()?; | |
| let binary = Some(self.parse_keyword(Keyword::BINARY)); | |
| let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) { | |
| Some(true) | |
| } else if self.parse_keyword(Keyword::ASENSITIVE) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| let scroll = if self.parse_keyword(Keyword::SCROLL) { | |
| Some(true) | |
| } else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::CURSOR)?; | |
| let declare_type = Some(DeclareType::Cursor); | |
| let hold = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) { | |
| Some(keyword) => { | |
| self.expect_keyword_is(Keyword::HOLD)?; | |
| match keyword { | |
| Keyword::WITH => Some(true), | |
| Keyword::WITHOUT => Some(false), | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in cursor hold"), | |
| )), | |
| } | |
| } | |
| None => None, | |
| }; | |
| self.expect_keyword_is(Keyword::FOR)?; | |
| let query = Some(self.parse_query()?); | |
| Ok(Statement::Declare { | |
| stmts: vec![Declare { | |
| names: vec![name], | |
| data_type: None, | |
| assignment: None, | |
| declare_type, | |
| binary, | |
| sensitive, | |
| scroll, | |
| hold, | |
| for_query: query, | |
| }], | |
| }) | |
| } | |
| /// Parse a [BigQuery] `DECLARE` statement. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| /// DECLARE variable_name[, ...] [{ <variable_type> | <DEFAULT expression> }]; | |
| /// ``` | |
| /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#declare | |
| pub fn parse_big_query_declare(&mut self) -> Result<Statement, ParserError> { | |
| let names = self.parse_comma_separated(Parser::parse_identifier)?; | |
| let data_type = match &self.peek_token_ref().token { | |
| Token::Word(w) if w.keyword == Keyword::DEFAULT => None, | |
| _ => Some(self.parse_data_type()?), | |
| }; | |
| let expr = if data_type.is_some() { | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| } | |
| } else { | |
| // If no variable type - default expression must be specified, per BQ docs. | |
| // i.e `DECLARE foo;` is invalid. | |
| self.expect_keyword_is(Keyword::DEFAULT)?; | |
| Some(self.parse_expr()?) | |
| }; | |
| Ok(Statement::Declare { | |
| stmts: vec![Declare { | |
| names, | |
| data_type, | |
| assignment: expr.map(|expr| DeclareAssignment::Default(Box::new(expr))), | |
| declare_type: None, | |
| binary: None, | |
| sensitive: None, | |
| scroll: None, | |
| hold: None, | |
| for_query: None, | |
| }], | |
| }) | |
| } | |
| /// Parse a [Snowflake] `DECLARE` statement. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| /// DECLARE | |
| /// [{ <variable_declaration> | |
| /// | <cursor_declaration> | |
| /// | <resultset_declaration> | |
| /// | <exception_declaration> }; ... ] | |
| /// | |
| /// <variable_declaration> | |
| /// <variable_name> [<type>] [ { DEFAULT | := } <expression>] | |
| /// | |
| /// <cursor_declaration> | |
| /// <cursor_name> CURSOR FOR <query> | |
| /// | |
| /// <resultset_declaration> | |
| /// <resultset_name> RESULTSET [ { DEFAULT | := } ( <query> ) ] ; | |
| /// | |
| /// <exception_declaration> | |
| /// <exception_name> EXCEPTION [ ( <exception_number> , '<exception_message>' ) ] ; | |
| /// ``` | |
| /// | |
| /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare | |
| pub fn parse_snowflake_declare(&mut self) -> Result<Statement, ParserError> { | |
| let mut stmts = vec![]; | |
| loop { | |
| let name = self.parse_identifier()?; | |
| let (declare_type, for_query, assigned_expr, data_type) = | |
| if self.parse_keyword(Keyword::CURSOR) { | |
| self.expect_keyword_is(Keyword::FOR)?; | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) if w.keyword == Keyword::SELECT => ( | |
| Some(DeclareType::Cursor), | |
| Some(self.parse_query()?), | |
| None, | |
| None, | |
| ), | |
| _ => ( | |
| Some(DeclareType::Cursor), | |
| None, | |
| Some(DeclareAssignment::For(Box::new(self.parse_expr()?))), | |
| None, | |
| ), | |
| } | |
| } else if self.parse_keyword(Keyword::RESULTSET) { | |
| let assigned_expr = if self.peek_token_ref().token != Token::SemiColon { | |
| self.parse_snowflake_variable_declaration_expression()? | |
| } else { | |
| // Nothing more to do. The statement has no further parameters. | |
| None | |
| }; | |
| (Some(DeclareType::ResultSet), None, assigned_expr, None) | |
| } else if self.parse_keyword(Keyword::EXCEPTION) { | |
| let assigned_expr = if self.peek_token_ref().token == Token::LParen { | |
| Some(DeclareAssignment::Expr(Box::new(self.parse_expr()?))) | |
| } else { | |
| // Nothing more to do. The statement has no further parameters. | |
| None | |
| }; | |
| (Some(DeclareType::Exception), None, assigned_expr, None) | |
| } else { | |
| // Without an explicit keyword, the only valid option is variable declaration. | |
| let (assigned_expr, data_type) = if let Some(assigned_expr) = | |
| self.parse_snowflake_variable_declaration_expression()? | |
| { | |
| (Some(assigned_expr), None) | |
| } else if let Token::Word(_) = &self.peek_token_ref().token { | |
| let data_type = self.parse_data_type()?; | |
| ( | |
| self.parse_snowflake_variable_declaration_expression()?, | |
| Some(data_type), | |
| ) | |
| } else { | |
| (None, None) | |
| }; | |
| (None, None, assigned_expr, data_type) | |
| }; | |
| let stmt = Declare { | |
| names: vec![name], | |
| data_type, | |
| assignment: assigned_expr, | |
| declare_type, | |
| binary: None, | |
| sensitive: None, | |
| scroll: None, | |
| hold: None, | |
| for_query, | |
| }; | |
| stmts.push(stmt); | |
| if self.consume_token(&Token::SemiColon) { | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) | |
| if ALL_KEYWORDS | |
| .binary_search(&w.value.to_uppercase().as_str()) | |
| .is_err() => | |
| { | |
| // Not a keyword - start of a new declaration. | |
| continue; | |
| } | |
| _ => { | |
| // Put back the semicolon, this is the end of the DECLARE statement. | |
| self.prev_token(); | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| Ok(Statement::Declare { stmts }) | |
| } | |
| /// Parse a [MsSql] `DECLARE` statement. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| /// DECLARE | |
| // { | |
| // { @local_variable [AS] data_type [ = value ] } | |
| // | { @cursor_variable_name CURSOR [ FOR ] } | |
| // } [ ,...n ] | |
| /// ``` | |
| /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-local-variable-transact-sql?view=sql-server-ver16 | |
| pub fn parse_mssql_declare(&mut self) -> Result<Statement, ParserError> { | |
| let stmts = self.parse_comma_separated(Parser::parse_mssql_declare_stmt)?; | |
| Ok(Statement::Declare { stmts }) | |
| } | |
| /// Parse the body of a [MsSql] `DECLARE`statement. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| // { | |
| // { @local_variable [AS] data_type [ = value ] } | |
| // | { @cursor_variable_name CURSOR [ FOR ]} | |
| // } [ ,...n ] | |
| /// ``` | |
| /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-local-variable-transact-sql?view=sql-server-ver16 | |
| pub fn parse_mssql_declare_stmt(&mut self) -> Result<Declare, ParserError> { | |
| let name = { | |
| let ident = self.parse_identifier()?; | |
| if !ident.value.starts_with('@') | |
| && !matches!( | |
| &self.peek_token_ref().token, | |
| Token::Word(w) if w.keyword == Keyword::CURSOR | |
| ) | |
| { | |
| Err(ParserError::TokenizerError( | |
| "Invalid MsSql variable declaration.".to_string(), | |
| )) | |
| } else { | |
| Ok(ident) | |
| } | |
| }?; | |
| let (declare_type, data_type) = match &self.peek_token_ref().token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::CURSOR => { | |
| self.next_token(); | |
| (Some(DeclareType::Cursor), None) | |
| } | |
| Keyword::AS => { | |
| self.next_token(); | |
| (None, Some(self.parse_data_type()?)) | |
| } | |
| _ => (None, Some(self.parse_data_type()?)), | |
| }, | |
| _ => (None, Some(self.parse_data_type()?)), | |
| }; | |
| let (for_query, assignment) = if self.peek_keyword(Keyword::FOR) { | |
| self.next_token(); | |
| let query = Some(self.parse_query()?); | |
| (query, None) | |
| } else { | |
| let assignment = self.parse_mssql_variable_declaration_expression()?; | |
| (None, assignment) | |
| }; | |
| Ok(Declare { | |
| names: vec![name], | |
| data_type, | |
| assignment, | |
| declare_type, | |
| binary: None, | |
| sensitive: None, | |
| scroll: None, | |
| hold: None, | |
| for_query, | |
| }) | |
| } | |
| /// Parses the assigned expression in a variable declaration. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| /// [ { DEFAULT | := } <expression>] | |
| /// ``` | |
| /// <https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare#variable-declaration-syntax> | |
| pub fn parse_snowflake_variable_declaration_expression( | |
| &mut self, | |
| ) -> Result<Option<DeclareAssignment>, ParserError> { | |
| Ok(match &self.peek_token_ref().token { | |
| Token::Word(w) if w.keyword == Keyword::DEFAULT => { | |
| self.next_token(); // Skip `DEFAULT` | |
| Some(DeclareAssignment::Default(Box::new(self.parse_expr()?))) | |
| } | |
| Token::Assignment => { | |
| self.next_token(); // Skip `:=` | |
| Some(DeclareAssignment::DuckAssignment(Box::new( | |
| self.parse_expr()?, | |
| ))) | |
| } | |
| _ => None, | |
| }) | |
| } | |
| /// Parses the assigned expression in a variable declaration. | |
| /// | |
| /// Syntax: | |
| /// ```text | |
| /// [ = <expression>] | |
| /// ``` | |
| pub fn parse_mssql_variable_declaration_expression( | |
| &mut self, | |
| ) -> Result<Option<DeclareAssignment>, ParserError> { | |
| Ok(match &self.peek_token_ref().token { | |
| Token::Eq => { | |
| self.next_token(); // Skip `=` | |
| Some(DeclareAssignment::MsSqlAssignment(Box::new( | |
| self.parse_expr()?, | |
| ))) | |
| } | |
| _ => None, | |
| }) | |
| } | |
| /// Parse `FETCH [direction] { FROM | IN } cursor INTO target;` statement. | |
| pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> { | |
| let direction = if self.parse_keyword(Keyword::NEXT) { | |
| FetchDirection::Next | |
| } else if self.parse_keyword(Keyword::PRIOR) { | |
| FetchDirection::Prior | |
| } else if self.parse_keyword(Keyword::FIRST) { | |
| FetchDirection::First | |
| } else if self.parse_keyword(Keyword::LAST) { | |
| FetchDirection::Last | |
| } else if self.parse_keyword(Keyword::ABSOLUTE) { | |
| FetchDirection::Absolute { | |
| limit: self.parse_number_value()?, | |
| } | |
| } else if self.parse_keyword(Keyword::RELATIVE) { | |
| FetchDirection::Relative { | |
| limit: self.parse_number_value()?, | |
| } | |
| } else if self.parse_keyword(Keyword::FORWARD) { | |
| if self.parse_keyword(Keyword::ALL) { | |
| FetchDirection::ForwardAll | |
| } else { | |
| FetchDirection::Forward { | |
| // TODO: Support optional | |
| limit: Some(self.parse_number_value()?), | |
| } | |
| } | |
| } else if self.parse_keyword(Keyword::BACKWARD) { | |
| if self.parse_keyword(Keyword::ALL) { | |
| FetchDirection::BackwardAll | |
| } else { | |
| FetchDirection::Backward { | |
| // TODO: Support optional | |
| limit: Some(self.parse_number_value()?), | |
| } | |
| } | |
| } else if self.parse_keyword(Keyword::ALL) { | |
| FetchDirection::All | |
| } else { | |
| FetchDirection::Count { | |
| limit: self.parse_number_value()?, | |
| } | |
| }; | |
| let position = if self.peek_keyword(Keyword::FROM) { | |
| self.expect_keyword(Keyword::FROM)?; | |
| FetchPosition::From | |
| } else if self.peek_keyword(Keyword::IN) { | |
| self.expect_keyword(Keyword::IN)?; | |
| FetchPosition::In | |
| } else { | |
| return parser_err!("Expected FROM or IN", self.peek_token_ref().span.start); | |
| }; | |
| let name = self.parse_identifier()?; | |
| let into = if self.parse_keyword(Keyword::INTO) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::Fetch { | |
| name, | |
| direction, | |
| position, | |
| into, | |
| }) | |
| } | |
| /// Parse a `DISCARD` statement. | |
| pub fn parse_discard(&mut self) -> Result<Statement, ParserError> { | |
| let object_type = if self.parse_keyword(Keyword::ALL) { | |
| DiscardObject::ALL | |
| } else if self.parse_keyword(Keyword::PLANS) { | |
| DiscardObject::PLANS | |
| } else if self.parse_keyword(Keyword::SEQUENCES) { | |
| DiscardObject::SEQUENCES | |
| } else if self.parse_keyword(Keyword::TEMP) || self.parse_keyword(Keyword::TEMPORARY) { | |
| DiscardObject::TEMP | |
| } else { | |
| return self.expected_ref( | |
| "ALL, PLANS, SEQUENCES, TEMP or TEMPORARY after DISCARD", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(Statement::Discard { object_type }) | |
| } | |
| /// Parse a `CREATE INDEX` statement. | |
| pub fn parse_create_index(&mut self, unique: bool) -> Result<CreateIndex, ParserError> { | |
| let concurrently = self.parse_keyword(Keyword::CONCURRENTLY); | |
| let r#async = self.parse_keyword(Keyword::ASYNC); | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let mut using = None; | |
| let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON) { | |
| let index_name = self.parse_object_name(false)?; | |
| // MySQL allows `USING index_type` either before or after `ON table_name` | |
| using = self.parse_optional_using_then_index_type()?; | |
| self.expect_keyword_is(Keyword::ON)?; | |
| Some(index_name) | |
| } else { | |
| None | |
| }; | |
| let table_name = self.parse_object_name(false)?; | |
| // MySQL allows having two `USING` clauses. | |
| // In that case, the second clause overwrites the first. | |
| using = self.parse_optional_using_then_index_type()?.or(using); | |
| let columns = self.parse_parenthesized_index_column_list()?; | |
| let include = if self.parse_keyword(Keyword::INCLUDE) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| self.expect_token(&Token::RParen)?; | |
| columns | |
| } else { | |
| vec![] | |
| }; | |
| let nulls_distinct = if self.parse_keyword(Keyword::NULLS) { | |
| let not = self.parse_keyword(Keyword::NOT); | |
| self.expect_keyword_is(Keyword::DISTINCT)?; | |
| Some(!not) | |
| } else { | |
| None | |
| }; | |
| let with = if self.dialect.supports_create_index_with_clause() | |
| && self.parse_keyword(Keyword::WITH) | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let with_params = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| with_params | |
| } else { | |
| Vec::new() | |
| }; | |
| let predicate = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| // MySQL options (including the modern style of `USING` after the column list instead of | |
| // before, which is deprecated) shouldn't conflict with other preceding options (e.g. `WITH | |
| // PARSER` won't be caught by the above `WITH` clause parsing because MySQL doesn't set that | |
| // support flag). This is probably invalid syntax for other dialects, but it is simpler to | |
| // parse it anyway (as we do inside `ALTER TABLE` and `CREATE TABLE` parsing). | |
| let index_options = self.parse_index_options()?; | |
| // MySQL allows `ALGORITHM` and `LOCK` options. Unlike in `ALTER TABLE`, they need not be comma separated. | |
| let mut alter_options = Vec::new(); | |
| while self | |
| .peek_one_of_keywords(&[Keyword::ALGORITHM, Keyword::LOCK]) | |
| .is_some() | |
| { | |
| alter_options.push(self.parse_alter_table_operation()?) | |
| } | |
| Ok(CreateIndex { | |
| name: index_name, | |
| table_name, | |
| using, | |
| columns, | |
| unique, | |
| concurrently, | |
| r#async, | |
| if_not_exists, | |
| include, | |
| nulls_distinct, | |
| with, | |
| predicate, | |
| index_options, | |
| alter_options, | |
| }) | |
| } | |
| /// Parse a `CREATE EXTENSION` statement. | |
| pub fn parse_create_extension(&mut self) -> Result<CreateExtension, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let (schema, version, cascade) = if self.parse_keyword(Keyword::WITH) { | |
| let schema = if self.parse_keyword(Keyword::SCHEMA) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let version = if self.parse_keyword(Keyword::VERSION) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let cascade = self.parse_keyword(Keyword::CASCADE); | |
| (schema, version, cascade) | |
| } else { | |
| (None, None, false) | |
| }; | |
| Ok(CreateExtension { | |
| name, | |
| if_not_exists, | |
| schema, | |
| version, | |
| cascade, | |
| }) | |
| } | |
| /// Parse a PostgreSQL-specific [Statement::CreateCollation] statement. | |
| pub fn parse_create_collation(&mut self) -> Result<CreateCollation, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| let definition = if self.parse_keyword(Keyword::FROM) { | |
| CreateCollationDefinition::From(self.parse_object_name(false)?) | |
| } else if self.consume_token(&Token::LParen) { | |
| let options = self.parse_comma_separated(Parser::parse_sql_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| CreateCollationDefinition::Options(options) | |
| } else { | |
| return self.expected_ref( | |
| "FROM or parenthesized option list after CREATE COLLATION name", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(CreateCollation { | |
| if_not_exists, | |
| name, | |
| definition, | |
| }) | |
| } | |
| /// Parse a PostgreSQL-specific [Statement::DropExtension] statement. | |
| pub fn parse_drop_extension(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let names = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| let cascade_or_restrict = | |
| self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]); | |
| Ok(Statement::DropExtension(DropExtension { | |
| names, | |
| if_exists, | |
| cascade_or_restrict: cascade_or_restrict | |
| .map(|k| match k { | |
| Keyword::CASCADE => Ok(ReferentialAction::Cascade), | |
| Keyword::RESTRICT => Ok(ReferentialAction::Restrict), | |
| _ => self.expected_ref("CASCADE or RESTRICT", self.peek_token_ref()), | |
| }) | |
| .transpose()?, | |
| })) | |
| } | |
| /// Parse a[Statement::DropOperator] statement. | |
| /// | |
| pub fn parse_drop_operator(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let operators = self.parse_comma_separated(|p| p.parse_drop_operator_signature())?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(Statement::DropOperator(DropOperator { | |
| if_exists, | |
| operators, | |
| drop_behavior, | |
| })) | |
| } | |
| /// Parse an operator signature for a [Statement::DropOperator] | |
| /// Format: `name ( { left_type | NONE } , right_type )` | |
| fn parse_drop_operator_signature(&mut self) -> Result<DropOperatorSignature, ParserError> { | |
| let name = self.parse_operator_name()?; | |
| self.expect_token(&Token::LParen)?; | |
| // Parse left operand type (or NONE for prefix operators) | |
| let left_type = if self.parse_keyword(Keyword::NONE) { | |
| None | |
| } else { | |
| Some(self.parse_data_type()?) | |
| }; | |
| self.expect_token(&Token::Comma)?; | |
| // Parse right operand type (always required) | |
| let right_type = self.parse_data_type()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(DropOperatorSignature { | |
| name, | |
| left_type, | |
| right_type, | |
| }) | |
| } | |
| /// Parse a [Statement::DropOperatorFamily] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-dropopfamily.html) | |
| pub fn parse_drop_operator_family(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(Statement::DropOperatorFamily(DropOperatorFamily { | |
| if_exists, | |
| names, | |
| using, | |
| drop_behavior, | |
| })) | |
| } | |
| /// Parse a [Statement::DropOperatorClass] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-dropopclass.html) | |
| pub fn parse_drop_operator_class(&mut self) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| Ok(Statement::DropOperatorClass(DropOperatorClass { | |
| if_exists, | |
| names, | |
| using, | |
| drop_behavior, | |
| })) | |
| } | |
| /// Parse Hive distribution style. | |
| /// | |
| /// TODO: Support parsing for `SKEWED` distribution style. | |
| pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> { | |
| if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = | |
| self.parse_comma_separated(|parser| parser.parse_column_def_inner(true))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(HiveDistributionStyle::PARTITIONED { columns }) | |
| } else { | |
| Ok(HiveDistributionStyle::NONE) | |
| } | |
| } | |
| /// Parse Redshift `DISTSTYLE { AUTO | EVEN | KEY | ALL }`. | |
| /// | |
| /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html> | |
| fn parse_dist_style(&mut self) -> Result<DistStyle, ParserError> { | |
| let token = self.next_token(); | |
| match &token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::AUTO => Ok(DistStyle::Auto), | |
| Keyword::EVEN => Ok(DistStyle::Even), | |
| Keyword::KEY => Ok(DistStyle::Key), | |
| Keyword::ALL => Ok(DistStyle::All), | |
| _ => self.expected("AUTO, EVEN, KEY, or ALL", token), | |
| }, | |
| _ => self.expected("AUTO, EVEN, KEY, or ALL", token), | |
| } | |
| } | |
| /// Parse Hive formats. | |
| pub fn parse_hive_formats(&mut self) -> Result<Option<HiveFormat>, ParserError> { | |
| let mut hive_format: Option<HiveFormat> = None; | |
| loop { | |
| match self.parse_one_of_keywords(&[ | |
| Keyword::ROW, | |
| Keyword::STORED, | |
| Keyword::LOCATION, | |
| Keyword::WITH, | |
| Keyword::USING, | |
| ]) { | |
| Some(Keyword::ROW) => { | |
| hive_format | |
| .get_or_insert_with(HiveFormat::default) | |
| .row_format = Some(self.parse_row_format()?); | |
| } | |
| Some(Keyword::STORED) => { | |
| self.expect_keyword_is(Keyword::AS)?; | |
| if self.parse_keyword(Keyword::INPUTFORMAT) { | |
| let input_format = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::OUTPUTFORMAT)?; | |
| let output_format = self.parse_expr()?; | |
| hive_format.get_or_insert_with(HiveFormat::default).storage = | |
| Some(HiveIOFormat::IOF { | |
| input_format, | |
| output_format, | |
| }); | |
| } else { | |
| let format = self.parse_file_format()?; | |
| hive_format.get_or_insert_with(HiveFormat::default).storage = | |
| Some(HiveIOFormat::FileFormat { format }); | |
| } | |
| } | |
| Some(Keyword::LOCATION) => { | |
| hive_format.get_or_insert_with(HiveFormat::default).location = | |
| Some(self.parse_literal_string()?); | |
| } | |
| Some(Keyword::WITH) => { | |
| self.prev_token(); | |
| let properties = self | |
| .parse_options_with_keywords(&[Keyword::WITH, Keyword::SERDEPROPERTIES])?; | |
| if !properties.is_empty() { | |
| hive_format | |
| .get_or_insert_with(HiveFormat::default) | |
| .serde_properties = Some(properties); | |
| } else { | |
| break; | |
| } | |
| } | |
| Some(Keyword::USING) if self.dialect.supports_create_table_using() => { | |
| let format = self.parse_identifier()?; | |
| hive_format.get_or_insert_with(HiveFormat::default).storage = | |
| Some(HiveIOFormat::Using { format }); | |
| } | |
| Some(Keyword::USING) => { | |
| // USING is not a table format keyword in this dialect; put it back | |
| self.prev_token(); | |
| break; | |
| } | |
| None => break, | |
| _ => break, | |
| } | |
| } | |
| Ok(hive_format) | |
| } | |
| /// Parse Hive row format. | |
| pub fn parse_row_format(&mut self) -> Result<HiveRowFormat, ParserError> { | |
| self.expect_keyword_is(Keyword::FORMAT)?; | |
| match self.parse_one_of_keywords(&[Keyword::SERDE, Keyword::DELIMITED]) { | |
| Some(Keyword::SERDE) => { | |
| let class = self.parse_literal_string()?; | |
| Ok(HiveRowFormat::SERDE { class }) | |
| } | |
| _ => { | |
| let mut row_delimiters = vec![]; | |
| loop { | |
| match self.parse_one_of_keywords(&[ | |
| Keyword::FIELDS, | |
| Keyword::COLLECTION, | |
| Keyword::MAP, | |
| Keyword::LINES, | |
| Keyword::NULL, | |
| ]) { | |
| Some(Keyword::FIELDS) | |
| if self.parse_keywords(&[Keyword::TERMINATED, Keyword::BY]) => | |
| { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::FieldsTerminatedBy, | |
| char: self.parse_identifier()?, | |
| }); | |
| if self.parse_keywords(&[Keyword::ESCAPED, Keyword::BY]) { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::FieldsEscapedBy, | |
| char: self.parse_identifier()?, | |
| }); | |
| } | |
| } | |
| Some(Keyword::COLLECTION) | |
| if self.parse_keywords(&[ | |
| Keyword::ITEMS, | |
| Keyword::TERMINATED, | |
| Keyword::BY, | |
| ]) => | |
| { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::CollectionItemsTerminatedBy, | |
| char: self.parse_identifier()?, | |
| }); | |
| } | |
| Some(Keyword::MAP) | |
| if self.parse_keywords(&[ | |
| Keyword::KEYS, | |
| Keyword::TERMINATED, | |
| Keyword::BY, | |
| ]) => | |
| { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::MapKeysTerminatedBy, | |
| char: self.parse_identifier()?, | |
| }); | |
| } | |
| Some(Keyword::LINES) | |
| if self.parse_keywords(&[Keyword::TERMINATED, Keyword::BY]) => | |
| { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::LinesTerminatedBy, | |
| char: self.parse_identifier()?, | |
| }); | |
| } | |
| Some(Keyword::NULL) | |
| if self.parse_keywords(&[Keyword::DEFINED, Keyword::AS]) => | |
| { | |
| row_delimiters.push(HiveRowDelimiter { | |
| delimiter: HiveDelimiter::NullDefinedAs, | |
| char: self.parse_identifier()?, | |
| }); | |
| } | |
| Some(Keyword::NULL) => break, | |
| _ => { | |
| break; | |
| } | |
| } | |
| } | |
| Ok(HiveRowFormat::DELIMITED { | |
| delimiters: row_delimiters, | |
| }) | |
| } | |
| } | |
| } | |
| fn parse_optional_on_cluster(&mut self) -> Result<Option<Ident>, ParserError> { | |
| if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) { | |
| Ok(Some(self.parse_identifier()?)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse `CREATE TABLE` statement. | |
| #[expect( | |
| clippy::too_many_arguments, | |
| reason = "each flag is a `CREATE` modifier the caller parsed before `TABLE`" | |
| )] | |
| pub fn parse_create_table( | |
| &mut self, | |
| or_replace: bool, | |
| temporary: bool, | |
| unlogged: bool, | |
| global: Option<bool>, | |
| transient: bool, | |
| volatile: bool, | |
| multiset: Option<bool>, | |
| ) -> Result<CreateTable, ParserError> { | |
| let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let table_name = self.parse_object_name(allow_unquoted_hyphen)?; | |
| let fallback = if self.dialect.supports_leading_comma_before_table_options() | |
| && self.consume_token(&Token::Comma) | |
| { | |
| let fallback = self.maybe_parse_fallback()?; | |
| if fallback.is_none() { | |
| self.prev_token(); // Put back comma. | |
| } | |
| fallback | |
| } else { | |
| None | |
| }; | |
| // PostgreSQL PARTITION OF for child partition tables | |
| // Note: This is a PostgreSQL-specific feature, but the dialect check was intentionally | |
| // removed to allow GenericDialect and other dialects to parse this syntax. This enables | |
| // multi-dialect SQL tools to work with PostgreSQL-specific DDL statements. | |
| // | |
| // PARTITION OF can be combined with other table definition clauses in the AST, | |
| // though PostgreSQL itself prohibits PARTITION OF with AS SELECT or LIKE clauses. | |
| // The parser accepts these combinations for flexibility; semantic validation | |
| // is left to downstream tools. | |
| // Child partitions can have their own constraints and indexes. | |
| let partition_of = if self.parse_keywords(&[Keyword::PARTITION, Keyword::OF]) { | |
| Some(self.parse_object_name(allow_unquoted_hyphen)?) | |
| } else { | |
| None | |
| }; | |
| // Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| let like = self.maybe_parse_create_table_like(allow_unquoted_hyphen)?; | |
| let clone = if self.parse_keyword(Keyword::CLONE) { | |
| self.parse_object_name(allow_unquoted_hyphen).ok() | |
| } else { | |
| None | |
| }; | |
| // parse optional column list (schema) | |
| let (columns, constraints) = self.parse_columns()?; | |
| let comment_after_column_def = | |
| if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::SingleQuotedString(str) => Some(CommentDef::WithoutEq(str)), | |
| _ => self.expected("comment", next_token)?, | |
| } | |
| } else { | |
| None | |
| }; | |
| // PostgreSQL PARTITION OF: partition bound specification | |
| let for_values = if partition_of.is_some() { | |
| if self.peek_keyword(Keyword::FOR) || self.peek_keyword(Keyword::DEFAULT) { | |
| Some(self.parse_partition_for_values()?) | |
| } else { | |
| return self.expected_ref( | |
| "FOR VALUES or DEFAULT after PARTITION OF", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } else { | |
| None | |
| }; | |
| let sqlite_table_options = self.parse_sqlite_table_options()?; | |
| let hive_distribution = self.parse_hive_distribution()?; | |
| let clustered_by = self.parse_optional_clustered_by()?; | |
| let hive_formats = self.parse_hive_formats()?; | |
| let create_table_config = self.parse_optional_create_table_config()?; | |
| // ClickHouse supports `PRIMARY KEY`, before `ORDER BY` | |
| // https://clickhouse.com/docs/en/sql-reference/statements/create/table#primary-key | |
| let primary_key = if dialect_of!(self is ClickHouseDialect | GenericDialect) | |
| && self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) | |
| { | |
| Some(Box::new(self.parse_expr()?)) | |
| } else { | |
| None | |
| }; | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| Some(self.parse_sorting_key()?) | |
| } else { | |
| None | |
| }; | |
| // ClickHouse allows PARTITION BY after ORDER BY | |
| // https://clickhouse.com/docs/en/sql-reference/statements/create/table#partition-by | |
| let partition_by = if create_table_config.partition_by.is_none() | |
| && self.dialect.supports_partition_by_after_order_by() | |
| && self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) | |
| { | |
| Some(Box::new(self.parse_expr()?)) | |
| } else { | |
| create_table_config.partition_by | |
| }; | |
| let on_commit = if self.parse_keywords(&[Keyword::ON, Keyword::COMMIT]) { | |
| Some(self.parse_create_table_on_commit()?) | |
| } else { | |
| None | |
| }; | |
| // Redshift: BACKUP YES|NO | |
| let backup = if self.parse_keyword(Keyword::BACKUP) { | |
| let keyword = self.expect_one_of_keywords(&[Keyword::YES, Keyword::NO])?; | |
| Some(keyword == Keyword::YES) | |
| } else { | |
| None | |
| }; | |
| // Redshift: DISTSTYLE, DISTKEY, SORTKEY | |
| let diststyle = if self.parse_keyword(Keyword::DISTSTYLE) { | |
| Some(self.parse_dist_style()?) | |
| } else { | |
| None | |
| }; | |
| let distkey = if self.parse_keyword(Keyword::DISTKEY) { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(expr) | |
| } else { | |
| None | |
| }; | |
| let sortkey = if self.parse_keyword(Keyword::SORTKEY) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| p.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(columns) | |
| } else { | |
| None | |
| }; | |
| // Parse optional `AS ( query )` | |
| let query = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_query()?) | |
| } else if self.dialect.supports_create_table_select() && self.parse_keyword(Keyword::SELECT) | |
| { | |
| // rewind the SELECT keyword | |
| self.prev_token(); | |
| Some(self.parse_query()?) | |
| } else { | |
| None | |
| }; | |
| // `WITH DATA` clause only applies if there is a query body. | |
| let with_data = if query.is_some() { | |
| self.maybe_parse_with_data()? | |
| } else { | |
| None | |
| }; | |
| Ok(CreateTableBuilder::new(table_name) | |
| .temporary(temporary) | |
| .unlogged(unlogged) | |
| .columns(columns) | |
| .constraints(constraints) | |
| .or_replace(or_replace) | |
| .if_not_exists(if_not_exists) | |
| .transient(transient) | |
| .volatile(volatile) | |
| .multiset(multiset) | |
| .fallback(fallback) | |
| .hive_distribution(hive_distribution) | |
| .hive_formats(hive_formats) | |
| .global(global) | |
| .query(query) | |
| .sqlite_table_options(sqlite_table_options) | |
| .like(like) | |
| .clone_clause(clone) | |
| .comment_after_column_def(comment_after_column_def) | |
| .order_by(order_by) | |
| .on_commit(on_commit) | |
| .on_cluster(on_cluster) | |
| .clustered_by(clustered_by) | |
| .partition_by(partition_by) | |
| .cluster_by(create_table_config.cluster_by) | |
| .inherits(create_table_config.inherits) | |
| .partition_of(partition_of) | |
| .for_values(for_values) | |
| .table_options(create_table_config.table_options) | |
| .primary_key(primary_key) | |
| .with_data(with_data) | |
| .backup(backup) | |
| .diststyle(diststyle) | |
| .distkey(distkey) | |
| .sortkey(sortkey) | |
| .build()) | |
| } | |
| /// Parse SQLite's `table-options` list, empty when absent, and the | |
| /// optional comma before it. | |
| /// | |
| /// See <https://www.sqlite.org/lang_createtable.html>. | |
| fn parse_sqlite_table_options(&mut self) -> Result<SqliteTableOptions, ParserError> { | |
| let leading_comma = self.consume_token(&Token::Comma); | |
| let Some(first) = self.maybe_parse_sqlite_table_option()? else { | |
| if leading_comma { | |
| self.prev_token(); | |
| } | |
| return Ok(SqliteTableOptions::default()); | |
| }; | |
| let mut options = Vec::with_capacity(2); | |
| options.push(first); | |
| while self.consume_token(&Token::Comma) { | |
| match self.maybe_parse_sqlite_table_option()? { | |
| Some(option) => options.push(option), | |
| None => return self.expected_ref("WITHOUT ROWID or STRICT", self.peek_token_ref()), | |
| } | |
| } | |
| Ok(SqliteTableOptions { | |
| leading_comma, | |
| options, | |
| }) | |
| } | |
| fn maybe_parse_sqlite_table_option( | |
| &mut self, | |
| ) -> Result<Option<SqliteTableOption>, ParserError> { | |
| if self.parse_keyword(Keyword::WITHOUT) { | |
| self.expect_keyword_is(Keyword::ROWID)?; | |
| Ok(Some(SqliteTableOption::WithoutRowid)) | |
| } else if self.parse_keyword(Keyword::STRICT) { | |
| Ok(Some(SqliteTableOption::Strict)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse `MULTISET` table-kind prefix on `CREATE TABLE`. | |
| fn maybe_parse_multiset(&mut self) -> Option<bool> { | |
| match self.parse_one_of_keywords(&[Keyword::SET, Keyword::MULTISET]) { | |
| Some(Keyword::MULTISET) => Some(true), | |
| Some(Keyword::SET) => Some(false), | |
| _ => None, | |
| } | |
| } | |
| /// Parse `FALLBACK` option on a `CREATE TABLE` statement, | |
| fn maybe_parse_fallback(&mut self) -> Result<Option<bool>, ParserError> { | |
| if self.parse_keywords(&[Keyword::NO, Keyword::FALLBACK]) { | |
| Ok(Some(false)) | |
| } else if self.parse_keyword(Keyword::FALLBACK) { | |
| Ok(Some(true)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse [`WithData`] clause on `CREATE TABLE ... AS` statement. | |
| fn maybe_parse_with_data(&mut self) -> Result<Option<WithData>, ParserError> { | |
| let data = if self.parse_keywords(&[Keyword::WITH, Keyword::DATA]) { | |
| true | |
| } else if self.parse_keywords(&[Keyword::WITH, Keyword::NO, Keyword::DATA]) { | |
| false | |
| } else { | |
| return Ok(None); | |
| }; | |
| let statistics = if self.parse_keywords(&[Keyword::AND, Keyword::STATISTICS]) { | |
| Some(true) | |
| } else if self.parse_keywords(&[Keyword::AND, Keyword::NO, Keyword::STATISTICS]) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| Ok(Some(WithData { data, statistics })) | |
| } | |
| fn maybe_parse_create_table_like( | |
| &mut self, | |
| allow_unquoted_hyphen: bool, | |
| ) -> Result<Option<CreateTableLikeKind>, ParserError> { | |
| let like = if self.dialect.supports_create_table_like_parenthesized() | |
| && self.consume_token(&Token::LParen) | |
| { | |
| if self.parse_keyword(Keyword::LIKE) { | |
| let name = self.parse_object_name(allow_unquoted_hyphen)?; | |
| let defaults = if self.parse_keywords(&[Keyword::INCLUDING, Keyword::DEFAULTS]) { | |
| Some(CreateTableLikeDefaults::Including) | |
| } else if self.parse_keywords(&[Keyword::EXCLUDING, Keyword::DEFAULTS]) { | |
| Some(CreateTableLikeDefaults::Excluding) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Some(CreateTableLikeKind::Parenthesized(CreateTableLike { | |
| name, | |
| defaults, | |
| })) | |
| } else { | |
| // Rollback the '(' it's probably the columns list | |
| self.prev_token(); | |
| None | |
| } | |
| } else if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { | |
| let name = self.parse_object_name(allow_unquoted_hyphen)?; | |
| Some(CreateTableLikeKind::Plain(CreateTableLike { | |
| name, | |
| defaults: None, | |
| })) | |
| } else { | |
| None | |
| }; | |
| Ok(like) | |
| } | |
| pub(crate) fn parse_create_table_on_commit(&mut self) -> Result<OnCommit, ParserError> { | |
| if self.parse_keywords(&[Keyword::DELETE, Keyword::ROWS]) { | |
| Ok(OnCommit::DeleteRows) | |
| } else if self.parse_keywords(&[Keyword::PRESERVE, Keyword::ROWS]) { | |
| Ok(OnCommit::PreserveRows) | |
| } else if self.parse_keywords(&[Keyword::DROP]) { | |
| Ok(OnCommit::Drop) | |
| } else { | |
| parser_err!( | |
| "Expecting DELETE ROWS, PRESERVE ROWS or DROP", | |
| self.peek_token_ref() | |
| ) | |
| } | |
| } | |
| /// Parse [ForValues] of a `PARTITION OF` clause. | |
| /// | |
| /// Parses: `FOR VALUES partition_bound_spec | DEFAULT` | |
| /// | |
| /// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtable.html) | |
| fn parse_partition_for_values(&mut self) -> Result<ForValues, ParserError> { | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| return Ok(ForValues::Default); | |
| } | |
| self.expect_keywords(&[Keyword::FOR, Keyword::VALUES])?; | |
| if self.parse_keyword(Keyword::IN) { | |
| // FOR VALUES IN (expr, ...) | |
| self.expect_token(&Token::LParen)?; | |
| if self.peek_token_ref().token == Token::RParen { | |
| return self.expected_ref("at least one value", self.peek_token_ref()); | |
| } | |
| let values = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(ForValues::In(values)) | |
| } else if self.parse_keyword(Keyword::FROM) { | |
| // FOR VALUES FROM (...) TO (...) | |
| self.expect_token(&Token::LParen)?; | |
| if self.peek_token_ref().token == Token::RParen { | |
| return self.expected_ref("at least one value", self.peek_token_ref()); | |
| } | |
| let from = self.parse_comma_separated(Parser::parse_partition_bound_value)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_keyword(Keyword::TO)?; | |
| self.expect_token(&Token::LParen)?; | |
| if self.peek_token_ref().token == Token::RParen { | |
| return self.expected_ref("at least one value", self.peek_token_ref()); | |
| } | |
| let to = self.parse_comma_separated(Parser::parse_partition_bound_value)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(ForValues::From { from, to }) | |
| } else if self.parse_keyword(Keyword::WITH) { | |
| // FOR VALUES WITH (MODULUS n, REMAINDER r) | |
| self.expect_token(&Token::LParen)?; | |
| self.expect_keyword(Keyword::MODULUS)?; | |
| let modulus = self.parse_literal_uint()?; | |
| self.expect_token(&Token::Comma)?; | |
| self.expect_keyword(Keyword::REMAINDER)?; | |
| let remainder = self.parse_literal_uint()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(ForValues::With { modulus, remainder }) | |
| } else { | |
| self.expected_ref("IN, FROM, or WITH after FOR VALUES", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse a single partition bound value (MINVALUE, MAXVALUE, or expression). | |
| fn parse_partition_bound_value(&mut self) -> Result<PartitionBoundValue, ParserError> { | |
| if self.parse_keyword(Keyword::MINVALUE) { | |
| Ok(PartitionBoundValue::MinValue) | |
| } else if self.parse_keyword(Keyword::MAXVALUE) { | |
| Ok(PartitionBoundValue::MaxValue) | |
| } else { | |
| Ok(PartitionBoundValue::Expr(self.parse_expr()?)) | |
| } | |
| } | |
| /// Parse configuration like inheritance, partitioning, clustering information during the table creation. | |
| /// | |
| /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_2) | |
| /// [PostgreSQL](https://www.postgresql.org/docs/current/ddl-partitioning.html) | |
| /// [MySql](https://dev.mysql.com/doc/refman/8.4/en/create-table.html) | |
| fn parse_optional_create_table_config( | |
| &mut self, | |
| ) -> Result<CreateTableConfiguration, ParserError> { | |
| let mut table_options = CreateTableOptions::None; | |
| let inherits = if self.parse_keyword(Keyword::INHERITS) { | |
| Some(self.parse_parenthesized_qualified_column_list(IsOptional::Mandatory, false)?) | |
| } else { | |
| None | |
| }; | |
| // PostgreSQL supports `WITH ( options )`, before `AS` | |
| let with_options = self.parse_options(Keyword::WITH)?; | |
| if !with_options.is_empty() { | |
| table_options = CreateTableOptions::With(with_options) | |
| } | |
| let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; | |
| if !table_properties.is_empty() { | |
| table_options = CreateTableOptions::TableProperties(table_properties); | |
| } | |
| let partition_by = if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | GenericDialect) | |
| && self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) | |
| { | |
| Some(Box::new(self.parse_expr()?)) | |
| } else { | |
| None | |
| }; | |
| let mut cluster_by = None; | |
| if dialect_of!(self is BigQueryDialect | GenericDialect) { | |
| if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { | |
| cluster_by = Some(WrappedCollection::NoWrapping( | |
| self.parse_comma_separated(|p| p.parse_expr())?, | |
| )); | |
| }; | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| if word.keyword == Keyword::OPTIONS { | |
| table_options = | |
| CreateTableOptions::Options(self.parse_options(Keyword::OPTIONS)?) | |
| } | |
| }; | |
| } | |
| if !dialect_of!(self is HiveDialect) && table_options == CreateTableOptions::None { | |
| let plain_options = self.parse_plain_options()?; | |
| if !plain_options.is_empty() { | |
| table_options = CreateTableOptions::Plain(plain_options) | |
| } | |
| }; | |
| Ok(CreateTableConfiguration { | |
| partition_by, | |
| cluster_by, | |
| inherits, | |
| table_options, | |
| }) | |
| } | |
| fn parse_plain_option(&mut self) -> Result<Option<SqlOption>, ParserError> { | |
| // Single parameter option | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| if self.parse_keywords(&[Keyword::START, Keyword::TRANSACTION]) { | |
| return Ok(Some(SqlOption::Ident(Ident::new("START TRANSACTION")))); | |
| } | |
| // Custom option | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| if self.parse_keywords(&[Keyword::COMMENT]) { | |
| let has_eq = self.consume_token(&Token::Eq); | |
| let value = self.next_token(); | |
| let comment = match (has_eq, value.token) { | |
| (true, Token::SingleQuotedString(s)) => { | |
| Ok(Some(SqlOption::Comment(CommentDef::WithEq(s)))) | |
| } | |
| (false, Token::SingleQuotedString(s)) => { | |
| Ok(Some(SqlOption::Comment(CommentDef::WithoutEq(s)))) | |
| } | |
| (_, token) => { | |
| self.expected("Token::SingleQuotedString", TokenWithSpan::wrap(token)) | |
| } | |
| }; | |
| return comment; | |
| } | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| // <https://clickhouse.com/docs/sql-reference/statements/create/table> | |
| if self.parse_keywords(&[Keyword::ENGINE]) { | |
| let _ = self.consume_token(&Token::Eq); | |
| let value = self.next_token(); | |
| let engine = match value.token { | |
| Token::Word(w) => { | |
| let parameters = if self.peek_token_ref().token == Token::LParen { | |
| self.parse_parenthesized_identifiers()? | |
| } else { | |
| vec![] | |
| }; | |
| Ok(Some(SqlOption::NamedParenthesizedList( | |
| NamedParenthesizedList { | |
| key: Ident::new("ENGINE"), | |
| name: Some(w.into_ident(value.span)), | |
| values: parameters, | |
| }, | |
| ))) | |
| } | |
| _ => { | |
| return self.expected("Token::Word", value)?; | |
| } | |
| }; | |
| return engine; | |
| } | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| if self.parse_keywords(&[Keyword::TABLESPACE]) { | |
| let _ = self.consume_token(&Token::Eq); | |
| let value = self.next_token(); | |
| let tablespace = match value.token { | |
| Token::Word(Word { value: name, .. }) | Token::SingleQuotedString(name) => { | |
| let storage = match self.parse_keyword(Keyword::STORAGE) { | |
| true => { | |
| let _ = self.consume_token(&Token::Eq); | |
| let storage_token = self.next_token(); | |
| match &storage_token.token { | |
| Token::Word(w) => match w.value.to_uppercase().as_str() { | |
| "DISK" => Some(StorageType::Disk), | |
| "MEMORY" => Some(StorageType::Memory), | |
| _ => self | |
| .expected("Storage type (DISK or MEMORY)", storage_token)?, | |
| }, | |
| _ => self.expected("Token::Word", storage_token)?, | |
| } | |
| } | |
| false => None, | |
| }; | |
| Ok(Some(SqlOption::TableSpace(TablespaceOption { | |
| name, | |
| storage, | |
| }))) | |
| } | |
| _ => { | |
| return self.expected("Token::Word", value)?; | |
| } | |
| }; | |
| return tablespace; | |
| } | |
| // <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| if self.parse_keyword(Keyword::UNION) { | |
| let _ = self.consume_token(&Token::Eq); | |
| let value = self.next_token(); | |
| match value.token { | |
| Token::LParen => { | |
| let tables: Vec<Ident> = | |
| self.parse_comma_separated0(Parser::parse_identifier, Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Some(SqlOption::NamedParenthesizedList( | |
| NamedParenthesizedList { | |
| key: Ident::new("UNION"), | |
| name: None, | |
| values: tables, | |
| }, | |
| ))); | |
| } | |
| _ => { | |
| return self.expected("Token::LParen", value)?; | |
| } | |
| } | |
| } | |
| // Key/Value parameter option | |
| let key = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) { | |
| Ident::new("DEFAULT CHARSET") | |
| } else if self.parse_keyword(Keyword::CHARSET) { | |
| Ident::new("CHARSET") | |
| } else if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARACTER, Keyword::SET]) { | |
| Ident::new("DEFAULT CHARACTER SET") | |
| } else if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { | |
| Ident::new("CHARACTER SET") | |
| } else if self.parse_keywords(&[Keyword::DEFAULT, Keyword::COLLATE]) { | |
| Ident::new("DEFAULT COLLATE") | |
| } else if self.parse_keyword(Keyword::COLLATE) { | |
| Ident::new("COLLATE") | |
| } else if self.parse_keywords(&[Keyword::DATA, Keyword::DIRECTORY]) { | |
| Ident::new("DATA DIRECTORY") | |
| } else if self.parse_keywords(&[Keyword::INDEX, Keyword::DIRECTORY]) { | |
| Ident::new("INDEX DIRECTORY") | |
| } else if self.parse_keyword(Keyword::KEY_BLOCK_SIZE) { | |
| Ident::new("KEY_BLOCK_SIZE") | |
| } else if self.parse_keyword(Keyword::ROW_FORMAT) { | |
| Ident::new("ROW_FORMAT") | |
| } else if self.parse_keyword(Keyword::PACK_KEYS) { | |
| Ident::new("PACK_KEYS") | |
| } else if self.parse_keyword(Keyword::STATS_AUTO_RECALC) { | |
| Ident::new("STATS_AUTO_RECALC") | |
| } else if self.parse_keyword(Keyword::STATS_PERSISTENT) { | |
| Ident::new("STATS_PERSISTENT") | |
| } else if self.parse_keyword(Keyword::STATS_SAMPLE_PAGES) { | |
| Ident::new("STATS_SAMPLE_PAGES") | |
| } else if self.parse_keyword(Keyword::DELAY_KEY_WRITE) { | |
| Ident::new("DELAY_KEY_WRITE") | |
| } else if self.parse_keyword(Keyword::COMPRESSION) { | |
| Ident::new("COMPRESSION") | |
| } else if self.parse_keyword(Keyword::ENCRYPTION) { | |
| Ident::new("ENCRYPTION") | |
| } else if self.parse_keyword(Keyword::MAX_ROWS) { | |
| Ident::new("MAX_ROWS") | |
| } else if self.parse_keyword(Keyword::MIN_ROWS) { | |
| Ident::new("MIN_ROWS") | |
| } else if self.parse_keyword(Keyword::AUTOEXTEND_SIZE) { | |
| Ident::new("AUTOEXTEND_SIZE") | |
| } else if self.parse_keyword(Keyword::AVG_ROW_LENGTH) { | |
| Ident::new("AVG_ROW_LENGTH") | |
| } else if self.parse_keyword(Keyword::CHECKSUM) { | |
| Ident::new("CHECKSUM") | |
| } else if self.parse_keyword(Keyword::CONNECTION) { | |
| Ident::new("CONNECTION") | |
| } else if self.parse_keyword(Keyword::ENGINE_ATTRIBUTE) { | |
| Ident::new("ENGINE_ATTRIBUTE") | |
| } else if self.parse_keyword(Keyword::PASSWORD) { | |
| Ident::new("PASSWORD") | |
| } else if self.parse_keyword(Keyword::SECONDARY_ENGINE_ATTRIBUTE) { | |
| Ident::new("SECONDARY_ENGINE_ATTRIBUTE") | |
| } else if self.parse_keyword(Keyword::INSERT_METHOD) { | |
| Ident::new("INSERT_METHOD") | |
| } else if self.parse_keyword(Keyword::AUTO_INCREMENT) { | |
| Ident::new("AUTO_INCREMENT") | |
| } else { | |
| return Ok(None); | |
| }; | |
| let _ = self.consume_token(&Token::Eq); | |
| let value = match self | |
| .maybe_parse(|parser| parser.parse_value())? | |
| .map(Expr::Value) | |
| { | |
| Some(expr) => expr, | |
| None => Expr::Identifier(self.parse_identifier()?), | |
| }; | |
| Ok(Some(SqlOption::KeyValue { key, value })) | |
| } | |
| /// Parse plain options. | |
| pub fn parse_plain_options(&mut self) -> Result<Vec<SqlOption>, ParserError> { | |
| let mut options = Vec::new(); | |
| while let Some(option) = self.parse_plain_option()? { | |
| options.push(option); | |
| // Some dialects support comma-separated options; it shouldn't introduce ambiguity to | |
| // consume it for all dialects. | |
| let _ = self.consume_token(&Token::Comma); | |
| } | |
| Ok(options) | |
| } | |
| /// Parse optional inline comment. | |
| pub fn parse_optional_inline_comment(&mut self) -> Result<Option<CommentDef>, ParserError> { | |
| let comment = if self.parse_keyword(Keyword::COMMENT) { | |
| let has_eq = self.consume_token(&Token::Eq); | |
| let comment = self.parse_comment_value()?; | |
| Some(if has_eq { | |
| CommentDef::WithEq(comment) | |
| } else { | |
| CommentDef::WithoutEq(comment) | |
| }) | |
| } else { | |
| None | |
| }; | |
| Ok(comment) | |
| } | |
| /// Parse comment value. | |
| pub fn parse_comment_value(&mut self) -> Result<String, ParserError> { | |
| let next_token = self.next_token(); | |
| let value = match next_token.token { | |
| Token::SingleQuotedString(str) => str, | |
| Token::DollarQuotedString(str) => str.value, | |
| _ => self.expected("string literal", next_token)?, | |
| }; | |
| Ok(value) | |
| } | |
| /// Parse optional procedure parameters. | |
| pub fn parse_optional_procedure_parameters( | |
| &mut self, | |
| ) -> Result<Option<Vec<ProcedureParam>>, ParserError> { | |
| let mut params = vec![]; | |
| if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) { | |
| return Ok(Some(params)); | |
| } | |
| loop { | |
| if let Token::Word(_) = &self.peek_token_ref().token { | |
| params.push(self.parse_procedure_param()?) | |
| } | |
| let comma = self.consume_token(&Token::Comma); | |
| if self.consume_token(&Token::RParen) { | |
| // allow a trailing comma, even though it's not in standard | |
| break; | |
| } else if !comma { | |
| return self.expected_ref( | |
| "',' or ')' after parameter definition", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } | |
| Ok(Some(params)) | |
| } | |
| /// Parse columns and constraints. | |
| pub fn parse_columns(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> { | |
| let mut columns = vec![]; | |
| let mut constraints = vec![]; | |
| if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) { | |
| return Ok((columns, constraints)); | |
| } | |
| loop { | |
| if let Some(constraint) = self.parse_optional_table_constraint()? { | |
| constraints.push(constraint); | |
| } else if let Token::Word(_) = &self.peek_token_ref().token { | |
| columns.push(self.parse_column_def()?); | |
| } else if matches!(&self.peek_token_ref().token, Token::SingleQuotedString(_)) | |
| && self.dialect.supports_string_literal_column_names() | |
| { | |
| columns.push(self.parse_column_def()?); | |
| } else { | |
| return self.expected_ref( | |
| "column name or constraint definition", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| let comma = self.consume_token(&Token::Comma); | |
| let rparen = self.peek_token_ref().token == Token::RParen; | |
| if !comma && !rparen { | |
| return self | |
| .expected_ref("',' or ')' after column definition", self.peek_token_ref()); | |
| }; | |
| if rparen | |
| && (!comma | |
| || self.dialect.supports_column_definition_trailing_commas() | |
| || self.options.trailing_commas) | |
| { | |
| let _ = self.consume_token(&Token::RParen); | |
| break; | |
| } | |
| } | |
| Ok((columns, constraints)) | |
| } | |
| /// Parse procedure parameter. | |
| pub fn parse_procedure_param(&mut self) -> Result<ProcedureParam, ParserError> { | |
| let mode = if self.parse_keyword(Keyword::IN) { | |
| Some(ArgMode::In) | |
| } else if self.parse_keyword(Keyword::OUT) { | |
| Some(ArgMode::Out) | |
| } else if self.parse_keyword(Keyword::INOUT) { | |
| Some(ArgMode::InOut) | |
| } else { | |
| None | |
| }; | |
| let name = self.parse_identifier()?; | |
| let data_type = self.parse_data_type()?; | |
| let default = if self.consume_token(&Token::Eq) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(ProcedureParam { | |
| name, | |
| data_type, | |
| mode, | |
| default, | |
| }) | |
| } | |
| /// Parse column definition. | |
| pub fn parse_column_def(&mut self) -> Result<ColumnDef, ParserError> { | |
| self.parse_column_def_inner(false) | |
| } | |
| fn parse_column_def_inner( | |
| &mut self, | |
| optional_data_type: bool, | |
| ) -> Result<ColumnDef, ParserError> { | |
| let col_name = self.parse_identifier()?; | |
| let data_type = if self.is_column_type_sqlite_unspecified() { | |
| DataType::Unspecified | |
| } else if optional_data_type { | |
| self.maybe_parse(|parser| parser.parse_data_type())? | |
| .unwrap_or(DataType::Unspecified) | |
| } else { | |
| self.parse_data_type()? | |
| }; | |
| let mut options = vec![]; | |
| loop { | |
| if self.parse_keyword(Keyword::CONSTRAINT) { | |
| let name = Some(self.parse_identifier()?); | |
| if let Some(option) = self.parse_optional_column_option()? { | |
| options.push(ColumnOptionDef { name, option }); | |
| } else { | |
| return self.expected_ref( | |
| "constraint details after CONSTRAINT <name>", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } else if let Some(option) = self.parse_optional_column_option()? { | |
| options.push(ColumnOptionDef { name: None, option }); | |
| } else { | |
| break; | |
| }; | |
| } | |
| Ok(ColumnDef { | |
| name: col_name, | |
| data_type, | |
| options, | |
| }) | |
| } | |
| fn is_column_type_sqlite_unspecified(&mut self) -> bool { | |
| if dialect_of!(self is SQLiteDialect) { | |
| match &self.peek_token_ref().token { | |
| Token::Word(word) => matches!( | |
| word.keyword, | |
| Keyword::CONSTRAINT | |
| | Keyword::PRIMARY | |
| | Keyword::NOT | |
| | Keyword::UNIQUE | |
| | Keyword::CHECK | |
| | Keyword::DEFAULT | |
| | Keyword::COLLATE | |
| | Keyword::REFERENCES | |
| | Keyword::GENERATED | |
| | Keyword::AS | |
| ), | |
| _ => true, // e.g. comma immediately after column name | |
| } | |
| } else { | |
| false | |
| } | |
| } | |
| /// Parse optional column option. | |
| pub fn parse_optional_column_option(&mut self) -> Result<Option<ColumnOption>, ParserError> { | |
| if let Some(option) = self.dialect.parse_column_option(self)? { | |
| return option; | |
| } | |
| self.with_state( | |
| ColumnDefinition, | |
| |parser| -> Result<Option<ColumnOption>, ParserError> { | |
| parser.parse_optional_column_option_inner() | |
| }, | |
| ) | |
| } | |
| fn parse_optional_column_option_inner(&mut self) -> Result<Option<ColumnOption>, ParserError> { | |
| if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { | |
| Ok(Some(ColumnOption::CharacterSet( | |
| self.parse_object_name(false)?, | |
| ))) | |
| } else if self.parse_keywords(&[Keyword::COLLATE]) { | |
| Ok(Some(ColumnOption::Collation( | |
| self.parse_object_name(false)?, | |
| ))) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { | |
| Ok(Some(ColumnOption::NotNull)) | |
| } else if self.parse_keywords(&[Keyword::COMMENT]) { | |
| Ok(Some(ColumnOption::Comment(self.parse_comment_value()?))) | |
| } else if self.parse_keyword(Keyword::NULL) { | |
| Ok(Some(ColumnOption::Null)) | |
| } else if self.parse_keyword(Keyword::DEFAULT) { | |
| Ok(Some(ColumnOption::Default(self.parse_expr()?))) | |
| } else if dialect_of!(self is ClickHouseDialect| GenericDialect) | |
| && self.parse_keyword(Keyword::MATERIALIZED) | |
| { | |
| Ok(Some(ColumnOption::Materialized(self.parse_expr()?))) | |
| } else if dialect_of!(self is ClickHouseDialect| GenericDialect) | |
| && self.parse_keyword(Keyword::ALIAS) | |
| { | |
| Ok(Some(ColumnOption::Alias(self.parse_expr()?))) | |
| } else if dialect_of!(self is ClickHouseDialect| GenericDialect) | |
| && self.parse_keyword(Keyword::EPHEMERAL) | |
| { | |
| // The expression is optional for the EPHEMERAL syntax, so we need to check | |
| // if the column definition has remaining tokens before parsing the expression. | |
| if matches!(self.peek_token_ref().token, Token::Comma | Token::RParen) { | |
| Ok(Some(ColumnOption::Ephemeral(None))) | |
| } else { | |
| Ok(Some(ColumnOption::Ephemeral(Some(self.parse_expr()?)))) | |
| } | |
| } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) { | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| PrimaryKeyConstraint { | |
| name: None, | |
| index_name: None, | |
| index_type: None, | |
| columns: vec![], | |
| include: vec![], | |
| index_options: vec![], | |
| characteristics, | |
| } | |
| .into(), | |
| )) | |
| } else if self.parse_keyword(Keyword::UNIQUE) { | |
| let index_type_display = | |
| if self.dialect.supports_key_column_option() && self.parse_keyword(Keyword::KEY) { | |
| KeyOrIndexDisplay::Key | |
| } else { | |
| KeyOrIndexDisplay::None | |
| }; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| UniqueConstraint { | |
| name: None, | |
| index_name: None, | |
| index_type_display, | |
| index_type: None, | |
| columns: vec![], | |
| include: vec![], | |
| index_options: vec![], | |
| characteristics, | |
| nulls_distinct: NullsDistinctOption::None, | |
| } | |
| .into(), | |
| )) | |
| } else if self.dialect.supports_key_column_option() && self.parse_keyword(Keyword::KEY) { | |
| // In MySQL, `KEY` in a column definition is shorthand for `PRIMARY KEY`. | |
| // See: https://dev.mysql.com/doc/refman/8.4/en/create-table.html | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| PrimaryKeyConstraint { | |
| name: None, | |
| index_name: None, | |
| index_type: None, | |
| columns: vec![], | |
| include: vec![], | |
| index_options: vec![], | |
| characteristics, | |
| } | |
| .into(), | |
| )) | |
| } else if self.parse_keyword(Keyword::REFERENCES) { | |
| let foreign_table = self.parse_object_name(false)?; | |
| // PostgreSQL allows omitting the column list and | |
| // uses the primary key column of the foreign table by default | |
| let referred_columns = self.parse_parenthesized_column_list(Optional, false)?; | |
| let mut match_kind = None; | |
| let mut on_delete = None; | |
| let mut on_update = None; | |
| loop { | |
| if match_kind.is_none() && self.parse_keyword(Keyword::MATCH) { | |
| match_kind = Some(self.parse_match_kind()?); | |
| } else if on_delete.is_none() | |
| && self.parse_keywords(&[Keyword::ON, Keyword::DELETE]) | |
| { | |
| on_delete = Some(self.parse_referential_action()?); | |
| } else if on_update.is_none() | |
| && self.parse_keywords(&[Keyword::ON, Keyword::UPDATE]) | |
| { | |
| on_update = Some(self.parse_referential_action()?); | |
| } else { | |
| break; | |
| } | |
| } | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| ForeignKeyConstraint { | |
| name: None, // Column-level constraints don't have names | |
| index_name: None, // Not applicable for column-level constraints | |
| columns: vec![], // Not applicable for column-level constraints | |
| foreign_table, | |
| referred_columns, | |
| on_delete, | |
| on_update, | |
| match_kind, | |
| characteristics, | |
| } | |
| .into(), | |
| )) | |
| } else if self.parse_keyword(Keyword::CHECK) { | |
| self.expect_token(&Token::LParen)?; | |
| // since `CHECK` requires parentheses, we can parse the inner expression in ParserState::Normal | |
| let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]); | |
| let enforced = if self.parse_keyword(Keyword::ENFORCED) { | |
| Some(true) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::ENFORCED]) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| Ok(Some( | |
| CheckConstraint { | |
| name: None, // Column-level check constraints don't have names | |
| expr: Box::new(expr), | |
| no_inherit, | |
| enforced, | |
| } | |
| .into(), | |
| )) | |
| } else if self.parse_keyword(Keyword::AUTO_INCREMENT) | |
| && dialect_of!(self is MySqlDialect | GenericDialect) | |
| { | |
| // Support AUTO_INCREMENT for MySQL | |
| Ok(Some(ColumnOption::DialectSpecific(vec![ | |
| Token::make_keyword("AUTO_INCREMENT"), | |
| ]))) | |
| } else if self.parse_keyword(Keyword::AUTOINCREMENT) | |
| && dialect_of!(self is SQLiteDialect | GenericDialect) | |
| { | |
| // Support AUTOINCREMENT for SQLite | |
| Ok(Some(ColumnOption::DialectSpecific(vec![ | |
| Token::make_keyword("AUTOINCREMENT"), | |
| ]))) | |
| } else if self.parse_keyword(Keyword::ASC) | |
| && self.dialect.supports_asc_desc_in_column_definition() | |
| { | |
| // Support ASC for SQLite | |
| Ok(Some(ColumnOption::DialectSpecific(vec![ | |
| Token::make_keyword("ASC"), | |
| ]))) | |
| } else if self.parse_keyword(Keyword::DESC) | |
| && self.dialect.supports_asc_desc_in_column_definition() | |
| { | |
| // Support DESC for SQLite | |
| Ok(Some(ColumnOption::DialectSpecific(vec![ | |
| Token::make_keyword("DESC"), | |
| ]))) | |
| } else if self.parse_keywords(&[Keyword::ON, Keyword::UPDATE]) | |
| && dialect_of!(self is MySqlDialect | GenericDialect) | |
| { | |
| let expr = self.parse_expr()?; | |
| Ok(Some(ColumnOption::OnUpdate(expr))) | |
| } else if self.parse_keyword(Keyword::GENERATED) { | |
| self.parse_optional_column_option_generated() | |
| } else if dialect_of!(self is BigQueryDialect | GenericDialect) | |
| && self.parse_keyword(Keyword::OPTIONS) | |
| { | |
| self.prev_token(); | |
| Ok(Some(ColumnOption::Options( | |
| self.parse_options(Keyword::OPTIONS)?, | |
| ))) | |
| } else if self.parse_keyword(Keyword::AS) | |
| && dialect_of!(self is MySqlDialect | SQLiteDialect | DuckDbDialect | GenericDialect) | |
| { | |
| self.parse_optional_column_option_as() | |
| } else if self.parse_keyword(Keyword::SRID) | |
| && dialect_of!(self is MySqlDialect | GenericDialect) | |
| { | |
| Ok(Some(ColumnOption::Srid(Box::new(self.parse_expr()?)))) | |
| } else if self.parse_keyword(Keyword::IDENTITY) | |
| && dialect_of!(self is MsSqlDialect | GenericDialect) | |
| { | |
| let parameters = if self.consume_token(&Token::LParen) { | |
| let seed = self.parse_number()?; | |
| self.expect_token(&Token::Comma)?; | |
| let increment = self.parse_number()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(IdentityPropertyFormatKind::FunctionCall( | |
| IdentityParameters { seed, increment }, | |
| )) | |
| } else { | |
| None | |
| }; | |
| Ok(Some(ColumnOption::Identity( | |
| IdentityPropertyKind::Identity(IdentityProperty { | |
| parameters, | |
| order: None, | |
| }), | |
| ))) | |
| } else if dialect_of!(self is SQLiteDialect | GenericDialect) | |
| && self.parse_keywords(&[Keyword::ON, Keyword::CONFLICT]) | |
| { | |
| // Support ON CONFLICT for SQLite | |
| Ok(Some(ColumnOption::OnConflict( | |
| self.expect_one_of_keywords(&[ | |
| Keyword::ROLLBACK, | |
| Keyword::ABORT, | |
| Keyword::FAIL, | |
| Keyword::IGNORE, | |
| Keyword::REPLACE, | |
| ])?, | |
| ))) | |
| } else if self.parse_keyword(Keyword::INVISIBLE) { | |
| Ok(Some(ColumnOption::Invisible)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| pub(crate) fn parse_tag(&mut self) -> Result<Tag, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::Eq)?; | |
| let value = self.parse_literal_string()?; | |
| Ok(Tag::new(name, value)) | |
| } | |
| fn parse_optional_column_option_generated( | |
| &mut self, | |
| ) -> Result<Option<ColumnOption>, ParserError> { | |
| if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS, Keyword::IDENTITY]) { | |
| let mut sequence_options = vec![]; | |
| if self.expect_token(&Token::LParen).is_ok() { | |
| sequence_options = self.parse_create_sequence_options()?; | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| Ok(Some(ColumnOption::Generated { | |
| generated_as: GeneratedAs::Always, | |
| sequence_options: Some(sequence_options), | |
| generation_expr: None, | |
| generation_expr_mode: None, | |
| generated_keyword: true, | |
| })) | |
| } else if self.parse_keywords(&[ | |
| Keyword::BY, | |
| Keyword::DEFAULT, | |
| Keyword::AS, | |
| Keyword::IDENTITY, | |
| ]) { | |
| let mut sequence_options = vec![]; | |
| if self.expect_token(&Token::LParen).is_ok() { | |
| sequence_options = self.parse_create_sequence_options()?; | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| Ok(Some(ColumnOption::Generated { | |
| generated_as: GeneratedAs::ByDefault, | |
| sequence_options: Some(sequence_options), | |
| generation_expr: None, | |
| generation_expr_mode: None, | |
| generated_keyword: true, | |
| })) | |
| } else if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS]) { | |
| if self.expect_token(&Token::LParen).is_ok() { | |
| let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| let (gen_as, expr_mode) = if self.parse_keywords(&[Keyword::STORED]) { | |
| Ok(( | |
| GeneratedAs::ExpStored, | |
| Some(GeneratedExpressionMode::Stored), | |
| )) | |
| } else if dialect_of!(self is PostgreSqlDialect) { | |
| // Postgres' AS IDENTITY branches are above, this one needs STORED | |
| self.expected_ref("STORED", self.peek_token_ref()) | |
| } else if self.parse_keywords(&[Keyword::VIRTUAL]) { | |
| Ok((GeneratedAs::Always, Some(GeneratedExpressionMode::Virtual))) | |
| } else { | |
| Ok((GeneratedAs::Always, None)) | |
| }?; | |
| Ok(Some(ColumnOption::Generated { | |
| generated_as: gen_as, | |
| sequence_options: None, | |
| generation_expr: Some(expr), | |
| generation_expr_mode: expr_mode, | |
| generated_keyword: true, | |
| })) | |
| } else { | |
| Ok(None) | |
| } | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn parse_optional_column_option_as(&mut self) -> Result<Option<ColumnOption>, ParserError> { | |
| // Some DBs allow 'AS (expr)', shorthand for GENERATED ALWAYS AS | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| let (gen_as, expr_mode) = if self.parse_keywords(&[Keyword::STORED]) { | |
| ( | |
| GeneratedAs::ExpStored, | |
| Some(GeneratedExpressionMode::Stored), | |
| ) | |
| } else if self.parse_keywords(&[Keyword::VIRTUAL]) { | |
| (GeneratedAs::Always, Some(GeneratedExpressionMode::Virtual)) | |
| } else { | |
| (GeneratedAs::Always, None) | |
| }; | |
| Ok(Some(ColumnOption::Generated { | |
| generated_as: gen_as, | |
| sequence_options: None, | |
| generation_expr: Some(expr), | |
| generation_expr_mode: expr_mode, | |
| generated_keyword: false, | |
| })) | |
| } | |
| /// Parse optional `CLUSTERED BY` clause for Hive/Generic dialects. | |
| pub fn parse_optional_clustered_by(&mut self) -> Result<Option<ClusteredBy>, ParserError> { | |
| let clustered_by = if dialect_of!(self is HiveDialect|GenericDialect) | |
| && self.parse_keywords(&[Keyword::CLUSTERED, Keyword::BY]) | |
| { | |
| let columns = self.parse_parenthesized_column_list(Mandatory, false)?; | |
| let sorted_by = if self.parse_keywords(&[Keyword::SORTED, Keyword::BY]) { | |
| self.expect_token(&Token::LParen)?; | |
| let sorted_by_columns = self.parse_comma_separated(|p| p.parse_order_by_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(sorted_by_columns) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::INTO)?; | |
| let num_buckets = self.parse_number_value()?.value; | |
| self.expect_keyword_is(Keyword::BUCKETS)?; | |
| Some(ClusteredBy { | |
| columns, | |
| sorted_by, | |
| num_buckets, | |
| }) | |
| } else { | |
| None | |
| }; | |
| Ok(clustered_by) | |
| } | |
| /// Parse a referential action used in foreign key clauses. | |
| /// | |
| /// Recognized forms: `RESTRICT`, `CASCADE`, `SET NULL`, `NO ACTION`, `SET DEFAULT`. | |
| pub fn parse_referential_action(&mut self) -> Result<ReferentialAction, ParserError> { | |
| if self.parse_keyword(Keyword::RESTRICT) { | |
| Ok(ReferentialAction::Restrict) | |
| } else if self.parse_keyword(Keyword::CASCADE) { | |
| Ok(ReferentialAction::Cascade) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::NULL]) { | |
| Ok(ReferentialAction::SetNull) | |
| } else if self.parse_keywords(&[Keyword::NO, Keyword::ACTION]) { | |
| Ok(ReferentialAction::NoAction) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) { | |
| Ok(ReferentialAction::SetDefault) | |
| } else { | |
| self.expected_ref( | |
| "one of RESTRICT, CASCADE, SET NULL, NO ACTION or SET DEFAULT", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| /// Parse a `MATCH` kind for constraint references: `FULL`, `PARTIAL`, or `SIMPLE`. | |
| pub fn parse_match_kind(&mut self) -> Result<ConstraintReferenceMatchKind, ParserError> { | |
| if self.parse_keyword(Keyword::FULL) { | |
| Ok(ConstraintReferenceMatchKind::Full) | |
| } else if self.parse_keyword(Keyword::PARTIAL) { | |
| Ok(ConstraintReferenceMatchKind::Partial) | |
| } else if self.parse_keyword(Keyword::SIMPLE) { | |
| Ok(ConstraintReferenceMatchKind::Simple) | |
| } else { | |
| self.expected_ref("one of FULL, PARTIAL or SIMPLE", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse `index_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]` | |
| /// after `{ PRIMARY KEY | UNIQUE } USING INDEX`. | |
| fn parse_constraint_using_index( | |
| &mut self, | |
| name: Option<Ident>, | |
| ) -> Result<ConstraintUsingIndex, ParserError> { | |
| let index_name = self.parse_identifier()?; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(ConstraintUsingIndex { | |
| name, | |
| index_name, | |
| characteristics, | |
| }) | |
| } | |
| /// Parse optional constraint characteristics such as `DEFERRABLE`, `INITIALLY` and `ENFORCED`. | |
| pub fn parse_constraint_characteristics( | |
| &mut self, | |
| ) -> Result<Option<ConstraintCharacteristics>, ParserError> { | |
| let mut cc = ConstraintCharacteristics::default(); | |
| loop { | |
| if cc.deferrable.is_none() && self.parse_keywords(&[Keyword::NOT, Keyword::DEFERRABLE]) | |
| { | |
| cc.deferrable = Some(false); | |
| } else if cc.deferrable.is_none() && self.parse_keyword(Keyword::DEFERRABLE) { | |
| cc.deferrable = Some(true); | |
| } else if cc.initially.is_none() && self.parse_keyword(Keyword::INITIALLY) { | |
| if self.parse_keyword(Keyword::DEFERRED) { | |
| cc.initially = Some(DeferrableInitial::Deferred); | |
| } else if self.parse_keyword(Keyword::IMMEDIATE) { | |
| cc.initially = Some(DeferrableInitial::Immediate); | |
| } else { | |
| self.expected_ref("one of DEFERRED or IMMEDIATE", self.peek_token_ref())?; | |
| } | |
| } else if cc.enforced.is_none() && self.parse_keyword(Keyword::ENFORCED) { | |
| cc.enforced = Some(true); | |
| } else if cc.enforced.is_none() | |
| && self.parse_keywords(&[Keyword::NOT, Keyword::ENFORCED]) | |
| { | |
| cc.enforced = Some(false); | |
| } else { | |
| break; | |
| } | |
| } | |
| if cc.deferrable.is_some() || cc.initially.is_some() || cc.enforced.is_some() { | |
| Ok(Some(cc)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse an optional table constraint (e.g. `PRIMARY KEY`, `UNIQUE`, `FOREIGN KEY`, `CHECK`). | |
| pub fn parse_optional_table_constraint( | |
| &mut self, | |
| ) -> Result<Option<TableConstraint>, ParserError> { | |
| let name = if self.parse_keyword(Keyword::CONSTRAINT) { | |
| if self.dialect.supports_constraint_keyword_without_name() | |
| && self | |
| .peek_one_of_keywords(&[ | |
| Keyword::CHECK, | |
| Keyword::PRIMARY, | |
| Keyword::UNIQUE, | |
| Keyword::FOREIGN, | |
| ]) | |
| .is_some() | |
| { | |
| None | |
| } else { | |
| Some(self.parse_identifier()?) | |
| } | |
| } else { | |
| None | |
| }; | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) if w.keyword == Keyword::UNIQUE => { | |
| // PostgreSQL: UNIQUE USING INDEX index_name | |
| // https://www.postgresql.org/docs/current/sql-altertable.html | |
| if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) { | |
| return Ok(Some(TableConstraint::UniqueUsingIndex( | |
| self.parse_constraint_using_index(name)?, | |
| ))); | |
| } | |
| let index_type_display = self.parse_index_type_display(); | |
| if !dialect_of!(self is GenericDialect | MySqlDialect) | |
| && !index_type_display.is_none() | |
| { | |
| return self.expected_ref( | |
| "`index_name` or `(column_name [, ...])`", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| let nulls_distinct = self.parse_optional_nulls_distinct()?; | |
| // optional index name | |
| let index_name = self.parse_optional_ident()?; | |
| let index_type = self.parse_optional_using_then_index_type()?; | |
| let columns = self.parse_parenthesized_index_column_list()?; | |
| let include = self.parse_optional_include_columns()?; | |
| let index_options = self.parse_index_options()?; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| UniqueConstraint { | |
| name, | |
| index_name, | |
| index_type_display, | |
| index_type, | |
| columns, | |
| include, | |
| index_options, | |
| characteristics, | |
| nulls_distinct, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::PRIMARY => { | |
| // after `PRIMARY` always stay `KEY` | |
| self.expect_keyword_is(Keyword::KEY)?; | |
| // PostgreSQL: PRIMARY KEY USING INDEX index_name | |
| // https://www.postgresql.org/docs/current/sql-altertable.html | |
| if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) { | |
| return Ok(Some(TableConstraint::PrimaryKeyUsingIndex( | |
| self.parse_constraint_using_index(name)?, | |
| ))); | |
| } | |
| // optional index name | |
| let index_name = self.parse_optional_ident()?; | |
| let index_type = self.parse_optional_using_then_index_type()?; | |
| let columns = self.parse_parenthesized_index_column_list()?; | |
| let include = self.parse_optional_include_columns()?; | |
| let index_options = self.parse_index_options()?; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| PrimaryKeyConstraint { | |
| name, | |
| index_name, | |
| index_type, | |
| columns, | |
| include, | |
| index_options, | |
| characteristics, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::FOREIGN => { | |
| self.expect_keyword_is(Keyword::KEY)?; | |
| let index_name = self.parse_optional_ident()?; | |
| let columns = self.parse_parenthesized_column_list(Mandatory, false)?; | |
| self.expect_keyword_is(Keyword::REFERENCES)?; | |
| let foreign_table = self.parse_object_name(false)?; | |
| let referred_columns = self.parse_parenthesized_column_list(Optional, false)?; | |
| let mut match_kind = None; | |
| let mut on_delete = None; | |
| let mut on_update = None; | |
| loop { | |
| if match_kind.is_none() && self.parse_keyword(Keyword::MATCH) { | |
| match_kind = Some(self.parse_match_kind()?); | |
| } else if on_delete.is_none() | |
| && self.parse_keywords(&[Keyword::ON, Keyword::DELETE]) | |
| { | |
| on_delete = Some(self.parse_referential_action()?); | |
| } else if on_update.is_none() | |
| && self.parse_keywords(&[Keyword::ON, Keyword::UPDATE]) | |
| { | |
| on_update = Some(self.parse_referential_action()?); | |
| } else { | |
| break; | |
| } | |
| } | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(Some( | |
| ForeignKeyConstraint { | |
| name, | |
| index_name, | |
| columns, | |
| foreign_table, | |
| referred_columns, | |
| on_delete, | |
| on_update, | |
| match_kind, | |
| characteristics, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::CHECK => { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = Box::new(self.parse_expr()?); | |
| self.expect_token(&Token::RParen)?; | |
| let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]); | |
| let enforced = if self.parse_keyword(Keyword::ENFORCED) { | |
| Some(true) | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::ENFORCED]) { | |
| Some(false) | |
| } else { | |
| None | |
| }; | |
| Ok(Some( | |
| CheckConstraint { | |
| name, | |
| expr, | |
| no_inherit, | |
| enforced, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) | |
| if (w.keyword == Keyword::INDEX || w.keyword == Keyword::KEY) | |
| && dialect_of!(self is GenericDialect | MySqlDialect) | |
| && name.is_none() => | |
| { | |
| let display_as_key = w.keyword == Keyword::KEY; | |
| let name = match &self.peek_token_ref().token { | |
| Token::Word(word) if word.keyword == Keyword::USING => None, | |
| _ => self.parse_optional_ident()?, | |
| }; | |
| let index_type = self.parse_optional_using_then_index_type()?; | |
| let columns = self.parse_parenthesized_index_column_list()?; | |
| let index_options = self.parse_index_options()?; | |
| Ok(Some( | |
| IndexConstraint { | |
| display_as_key, | |
| name, | |
| index_type, | |
| columns, | |
| index_options, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) | |
| if (w.keyword == Keyword::FULLTEXT || w.keyword == Keyword::SPATIAL) | |
| && dialect_of!(self is GenericDialect | MySqlDialect) => | |
| { | |
| if let Some(name) = name { | |
| return self.expected( | |
| "FULLTEXT or SPATIAL option without constraint name", | |
| TokenWithSpan { | |
| token: Token::make_keyword(&name.to_string()), | |
| span: next_token.span, | |
| }, | |
| ); | |
| } | |
| let fulltext = w.keyword == Keyword::FULLTEXT; | |
| let index_type_display = self.parse_index_type_display(); | |
| let opt_index_name = self.parse_optional_ident()?; | |
| let columns = self.parse_parenthesized_index_column_list()?; | |
| Ok(Some( | |
| FullTextOrSpatialConstraint { | |
| fulltext, | |
| index_type_display, | |
| opt_index_name, | |
| columns, | |
| } | |
| .into(), | |
| )) | |
| } | |
| Token::Word(w) | |
| if w.keyword == Keyword::EXCLUDE && self.dialect.supports_exclude_constraint() => | |
| { | |
| // `EXCLUDE` is a non-reserved keyword in PostgreSQL, so it is a | |
| // valid column name. Only treat it as an exclusion constraint | |
| // when it begins one (named, or followed by `USING` / `(`); | |
| // otherwise backtrack and let it parse as a column. | |
| if name.is_some() | |
| || self.peek_keyword(Keyword::USING) | |
| || self.peek_token_ref().token == Token::LParen | |
| { | |
| Ok(Some(self.parse_exclude_constraint(name)?.into())) | |
| } else { | |
| self.prev_token(); | |
| Ok(None) | |
| } | |
| } | |
| _ => { | |
| if name.is_some() { | |
| self.expected("PRIMARY, UNIQUE, FOREIGN, CHECK, or EXCLUDE", next_token) | |
| } else { | |
| self.prev_token(); | |
| Ok(None) | |
| } | |
| } | |
| } | |
| } | |
| // The leading `EXCLUDE` keyword is already consumed by the caller. | |
| fn parse_exclude_constraint( | |
| &mut self, | |
| name: Option<Ident>, | |
| ) -> Result<ExcludeConstraint, ParserError> { | |
| let index_method = if self.parse_keyword(Keyword::USING) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::LParen)?; | |
| let elements = self.parse_comma_separated(|p| p.parse_exclude_constraint_element())?; | |
| self.expect_token(&Token::RParen)?; | |
| let include = if self.parse_keyword(Keyword::INCLUDE) { | |
| self.expect_token(&Token::LParen)?; | |
| let cols = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| self.expect_token(&Token::RParen)?; | |
| cols | |
| } else { | |
| vec![] | |
| }; | |
| let where_clause = if self.parse_keyword(Keyword::WHERE) { | |
| self.expect_token(&Token::LParen)?; | |
| let predicate = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(Box::new(predicate)) | |
| } else { | |
| None | |
| }; | |
| let characteristics = self.parse_constraint_characteristics()?; | |
| Ok(ExcludeConstraint { | |
| name, | |
| index_method, | |
| elements, | |
| include, | |
| where_clause, | |
| characteristics, | |
| }) | |
| } | |
| fn parse_exclude_constraint_element( | |
| &mut self, | |
| ) -> Result<ExcludeConstraintElement, ParserError> { | |
| let column = self.parse_create_index_expr()?; | |
| self.expect_keyword_is(Keyword::WITH)?; | |
| let operator = self.parse_exclude_constraint_operator()?; | |
| Ok(ExcludeConstraintElement { column, operator }) | |
| } | |
| /// Parse the operator that follows `WITH` in an `EXCLUDE` element. | |
| fn parse_exclude_constraint_operator( | |
| &mut self, | |
| ) -> Result<ExcludeConstraintOperator, ParserError> { | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| return Ok(ExcludeConstraintOperator::PGOperator( | |
| self.parse_pg_operator_ident_parts()?, | |
| )); | |
| } | |
| let operator_token = self.next_token(); | |
| Ok(ExcludeConstraintOperator::Token( | |
| operator_token.token.to_string(), | |
| )) | |
| } | |
| /// Parse the body of a Postgres `OPERATOR(schema.op)` form: the | |
| /// parenthesized `.`-separated path of name parts after the `OPERATOR` | |
| /// keyword. Shared between binary expression parsing and exclusion | |
| /// constraint parsing. | |
| fn parse_pg_operator_ident_parts(&mut self) -> Result<Vec<String>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| if self.peek_token_ref().token == Token::RParen { | |
| let token = self.next_token(); | |
| return self.expected("operator name", token); | |
| } | |
| let mut idents = vec![]; | |
| loop { | |
| self.advance_token(); | |
| idents.push(self.get_current_token().to_string()); | |
| if !self.consume_token(&Token::Period) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(idents) | |
| } | |
| fn parse_optional_nulls_distinct(&mut self) -> Result<NullsDistinctOption, ParserError> { | |
| Ok(if self.parse_keyword(Keyword::NULLS) { | |
| let not = self.parse_keyword(Keyword::NOT); | |
| self.expect_keyword_is(Keyword::DISTINCT)?; | |
| if not { | |
| NullsDistinctOption::NotDistinct | |
| } else { | |
| NullsDistinctOption::Distinct | |
| } | |
| } else { | |
| NullsDistinctOption::None | |
| }) | |
| } | |
| /// Optionally parse a parenthesized list of `SqlOption`s introduced by `keyword`. | |
| pub fn maybe_parse_options( | |
| &mut self, | |
| keyword: Keyword, | |
| ) -> Result<Option<Vec<SqlOption>>, ParserError> { | |
| if let Token::Word(word) = &self.peek_token_ref().token { | |
| if word.keyword == keyword { | |
| return Ok(Some(self.parse_options(keyword)?)); | |
| } | |
| }; | |
| Ok(None) | |
| } | |
| /// Parse a parenthesized list of `SqlOption`s following `keyword`, or return an empty vec. | |
| pub fn parse_options(&mut self, keyword: Keyword) -> Result<Vec<SqlOption>, ParserError> { | |
| if self.parse_keyword(keyword) { | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated0(Parser::parse_sql_option, Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(options) | |
| } else { | |
| Ok(vec![]) | |
| } | |
| } | |
| /// Parse options introduced by one of `keywords` followed by a parenthesized list. | |
| pub fn parse_options_with_keywords( | |
| &mut self, | |
| keywords: &[Keyword], | |
| ) -> Result<Vec<SqlOption>, ParserError> { | |
| if self.parse_keywords(keywords) { | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated(Parser::parse_sql_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(options) | |
| } else { | |
| Ok(vec![]) | |
| } | |
| } | |
| /// Parse an index type token (e.g. `BTREE`, `HASH`, or a custom identifier). | |
| pub fn parse_index_type(&mut self) -> Result<IndexType, ParserError> { | |
| Ok(if self.parse_keyword(Keyword::BTREE) { | |
| IndexType::BTree | |
| } else if self.parse_keyword(Keyword::HASH) { | |
| IndexType::Hash | |
| } else if self.parse_keyword(Keyword::GIN) { | |
| IndexType::GIN | |
| } else if self.parse_keyword(Keyword::GIST) { | |
| IndexType::GiST | |
| } else if self.parse_keyword(Keyword::SPGIST) { | |
| IndexType::SPGiST | |
| } else if self.parse_keyword(Keyword::BRIN) { | |
| IndexType::BRIN | |
| } else if self.parse_keyword(Keyword::BLOOM) { | |
| IndexType::Bloom | |
| } else { | |
| IndexType::Custom(self.parse_identifier()?) | |
| }) | |
| } | |
| /// Optionally parse the `USING` keyword, followed by an [IndexType] | |
| /// Example: | |
| /// ```sql | |
| //// USING BTREE (name, age DESC) | |
| /// ``` | |
| /// Optionally parse `USING <index_type>` and return the parsed `IndexType` if present. | |
| pub fn parse_optional_using_then_index_type( | |
| &mut self, | |
| ) -> Result<Option<IndexType>, ParserError> { | |
| if self.parse_keyword(Keyword::USING) { | |
| Ok(Some(self.parse_index_type()?)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse `[ident]`, mostly `ident` is name, like: | |
| /// `window_name`, `index_name`, ... | |
| /// Parse an optional identifier, returning `Some(Ident)` if present. | |
| pub fn parse_optional_ident(&mut self) -> Result<Option<Ident>, ParserError> { | |
| self.maybe_parse(|parser| parser.parse_identifier()) | |
| } | |
| #[must_use] | |
| /// Parse optional `KEY` or `INDEX` display tokens used in index/constraint declarations. | |
| pub fn parse_index_type_display(&mut self) -> KeyOrIndexDisplay { | |
| if self.parse_keyword(Keyword::KEY) { | |
| KeyOrIndexDisplay::Key | |
| } else if self.parse_keyword(Keyword::INDEX) { | |
| KeyOrIndexDisplay::Index | |
| } else { | |
| KeyOrIndexDisplay::None | |
| } | |
| } | |
| /// Parse an optional index option such as `USING <type>` or `COMMENT <string>`. | |
| pub fn parse_optional_index_option(&mut self) -> Result<Option<IndexOption>, ParserError> { | |
| if let Some(index_type) = self.parse_optional_using_then_index_type()? { | |
| Ok(Some(IndexOption::Using(index_type))) | |
| } else if self.parse_keyword(Keyword::COMMENT) { | |
| let s = self.parse_literal_string()?; | |
| Ok(Some(IndexOption::Comment(s))) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse zero or more index options and return them as a vector. | |
| pub fn parse_index_options(&mut self) -> Result<Vec<IndexOption>, ParserError> { | |
| let mut options = Vec::new(); | |
| loop { | |
| match self.parse_optional_index_option()? { | |
| Some(index_option) => options.push(index_option), | |
| None => return Ok(options), | |
| } | |
| } | |
| } | |
| /// Parse an optional `INCLUDE (col, ...)` clause on a table constraint. | |
| pub fn parse_optional_include_columns(&mut self) -> Result<Vec<Ident>, ParserError> { | |
| if self.parse_keyword(Keyword::INCLUDE) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(columns) | |
| } else { | |
| Ok(vec![]) | |
| } | |
| } | |
| /// Parse a single `SqlOption` used by various dialect-specific DDL statements. | |
| pub fn parse_sql_option(&mut self) -> Result<SqlOption, ParserError> { | |
| let is_mssql = dialect_of!(self is MsSqlDialect|GenericDialect); | |
| match &self.peek_token_ref().token { | |
| Token::Word(w) if w.keyword == Keyword::HEAP && is_mssql => { | |
| Ok(SqlOption::Ident(self.parse_identifier()?)) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::PARTITION && is_mssql => { | |
| self.parse_option_partition() | |
| } | |
| Token::Word(w) if w.keyword == Keyword::CLUSTERED && is_mssql => { | |
| self.parse_option_clustered() | |
| } | |
| _ => { | |
| let name = self.parse_identifier()?; | |
| self.expect_token(&Token::Eq)?; | |
| let value = self.parse_expr()?; | |
| Ok(SqlOption::KeyValue { key: name, value }) | |
| } | |
| } | |
| } | |
| /// Parse a `CLUSTERED` table option (MSSQL-specific syntaxes supported). | |
| pub fn parse_option_clustered(&mut self) -> Result<SqlOption, ParserError> { | |
| if self.parse_keywords(&[ | |
| Keyword::CLUSTERED, | |
| Keyword::COLUMNSTORE, | |
| Keyword::INDEX, | |
| Keyword::ORDER, | |
| ]) { | |
| Ok(SqlOption::Clustered( | |
| TableOptionsClustered::ColumnstoreIndexOrder( | |
| self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?, | |
| ), | |
| )) | |
| } else if self.parse_keywords(&[Keyword::CLUSTERED, Keyword::COLUMNSTORE, Keyword::INDEX]) { | |
| Ok(SqlOption::Clustered( | |
| TableOptionsClustered::ColumnstoreIndex, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::CLUSTERED, Keyword::INDEX]) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| { | |
| let name = p.parse_identifier()?; | |
| let asc = p.parse_asc_desc(); | |
| Ok(ClusteredIndex { name, asc }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(SqlOption::Clustered(TableOptionsClustered::Index(columns))) | |
| } else { | |
| Err(ParserError::ParserError( | |
| "invalid CLUSTERED sequence".to_string(), | |
| )) | |
| } | |
| } | |
| /// Parse a `PARTITION(...) FOR VALUES(...)` table option. | |
| pub fn parse_option_partition(&mut self) -> Result<SqlOption, ParserError> { | |
| self.expect_keyword_is(Keyword::PARTITION)?; | |
| self.expect_token(&Token::LParen)?; | |
| let column_name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::RANGE)?; | |
| let range_direction = if self.parse_keyword(Keyword::LEFT) { | |
| Some(PartitionRangeDirection::Left) | |
| } else if self.parse_keyword(Keyword::RIGHT) { | |
| Some(PartitionRangeDirection::Right) | |
| } else { | |
| None | |
| }; | |
| self.expect_keywords(&[Keyword::FOR, Keyword::VALUES])?; | |
| self.expect_token(&Token::LParen)?; | |
| let for_values = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(SqlOption::Partition { | |
| column_name, | |
| range_direction, | |
| for_values, | |
| }) | |
| } | |
| /// Parse a parenthesized list of partition expressions and return a `Partition` value. | |
| pub fn parse_partition(&mut self) -> Result<Partition, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let partitions = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Partition::Partitions(partitions)) | |
| } | |
| /// Parse a parenthesized `SELECT` projection used for projection-based operations. | |
| pub fn parse_projection_select(&mut self) -> Result<ProjectionSelect, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| self.expect_keyword_is(Keyword::SELECT)?; | |
| let projection = self.parse_projection()?; | |
| let group_by = self.parse_optional_group_by()?; | |
| let order_by = self.parse_optional_order_by()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(ProjectionSelect { | |
| projection, | |
| group_by, | |
| order_by, | |
| }) | |
| } | |
| /// Parse `ALTER TABLE ... ADD PROJECTION ...` operation. | |
| pub fn parse_alter_table_add_projection(&mut self) -> Result<AlterTableOperation, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let query = self.parse_projection_select()?; | |
| Ok(AlterTableOperation::AddProjection { | |
| if_not_exists, | |
| name, | |
| select: query, | |
| }) | |
| } | |
| /// Parse Redshift `ALTER SORTKEY (column_list)`. | |
| /// | |
| /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_ALTER_TABLE.html> | |
| fn parse_alter_sort_key(&mut self) -> Result<AlterTableOperation, ParserError> { | |
| self.expect_keyword_is(Keyword::ALTER)?; | |
| self.expect_keyword_is(Keyword::SORTKEY)?; | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| p.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(AlterTableOperation::AlterSortKey { columns }) | |
| } | |
| /// Parse the expression(s) following ClickHouse `ORDER BY`: either a single | |
| /// expression or a possibly empty parenthesized list. | |
| fn parse_sorting_key(&mut self) -> Result<OneOrManyWithParens<Expr>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let columns = if self.peek_token_ref().token != Token::RParen { | |
| self.parse_comma_separated(|p| p.parse_expr())? | |
| } else { | |
| vec![] | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(OneOrManyWithParens::Many(columns)) | |
| } else { | |
| Ok(OneOrManyWithParens::One(self.parse_expr()?)) | |
| } | |
| } | |
| /// Parse a single `ALTER TABLE` operation and return an `AlterTableOperation`. | |
| pub fn parse_alter_table_operation(&mut self) -> Result<AlterTableOperation, ParserError> { | |
| let operation = if self.parse_keyword(Keyword::ADD) { | |
| if let Some(constraint) = self.parse_optional_table_constraint()? { | |
| let not_valid = self.parse_keywords(&[Keyword::NOT, Keyword::VALID]); | |
| AlterTableOperation::AddConstraint { | |
| constraint, | |
| not_valid, | |
| } | |
| } else if dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| && self.parse_keyword(Keyword::PROJECTION) | |
| { | |
| return self.parse_alter_table_add_projection(); | |
| } else { | |
| let if_not_exists = | |
| self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let mut new_partitions = vec![]; | |
| loop { | |
| if self.parse_keyword(Keyword::PARTITION) { | |
| new_partitions.push(self.parse_partition()?); | |
| } else { | |
| break; | |
| } | |
| } | |
| if !new_partitions.is_empty() { | |
| AlterTableOperation::AddPartitions { | |
| if_not_exists, | |
| new_partitions, | |
| } | |
| } else { | |
| let column_keyword = self.parse_keyword(Keyword::COLUMN); | |
| let if_not_exists = if dialect_of!(self is PostgreSqlDialect | BigQueryDialect | DuckDbDialect | GenericDialect) | |
| { | |
| self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]) | |
| || if_not_exists | |
| } else { | |
| false | |
| }; | |
| let column_def = self.parse_column_def()?; | |
| let column_position = self.parse_column_position()?; | |
| AlterTableOperation::AddColumn { | |
| column_keyword, | |
| if_not_exists, | |
| column_def, | |
| column_position, | |
| } | |
| } | |
| } | |
| } else if self.parse_keyword(Keyword::RENAME) { | |
| if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) { | |
| let old_name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::TO)?; | |
| let new_name = self.parse_identifier()?; | |
| AlterTableOperation::RenameConstraint { old_name, new_name } | |
| } else if self.parse_keyword(Keyword::TO) { | |
| let table_name = self.parse_object_name(false)?; | |
| AlterTableOperation::RenameTable { | |
| table_name: RenameTableNameKind::To(table_name), | |
| } | |
| } else if self.parse_keyword(Keyword::AS) { | |
| let table_name = self.parse_object_name(false)?; | |
| AlterTableOperation::RenameTable { | |
| table_name: RenameTableNameKind::As(table_name), | |
| } | |
| } else { | |
| let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] | |
| let old_column_name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::TO)?; | |
| let new_column_name = self.parse_identifier()?; | |
| AlterTableOperation::RenameColumn { | |
| old_column_name, | |
| new_column_name, | |
| } | |
| } | |
| } else if self.parse_keyword(Keyword::DISABLE) { | |
| if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { | |
| AlterTableOperation::DisableRowLevelSecurity {} | |
| } else if self.parse_keyword(Keyword::RULE) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::DisableRule { name } | |
| } else if self.parse_keyword(Keyword::TRIGGER) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::DisableTrigger { name } | |
| } else { | |
| return self.expected_ref( | |
| "ROW LEVEL SECURITY, RULE, or TRIGGER after DISABLE", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } else if self.parse_keyword(Keyword::ENABLE) { | |
| if self.parse_keywords(&[Keyword::ALWAYS, Keyword::RULE]) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableAlwaysRule { name } | |
| } else if self.parse_keywords(&[Keyword::ALWAYS, Keyword::TRIGGER]) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableAlwaysTrigger { name } | |
| } else if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { | |
| AlterTableOperation::EnableRowLevelSecurity {} | |
| } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::RULE]) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableReplicaRule { name } | |
| } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::TRIGGER]) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableReplicaTrigger { name } | |
| } else if self.parse_keyword(Keyword::RULE) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableRule { name } | |
| } else if self.parse_keyword(Keyword::TRIGGER) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::EnableTrigger { name } | |
| } else { | |
| return self.expected_ref( | |
| "ALWAYS, REPLICA, ROW LEVEL SECURITY, RULE, or TRIGGER after ENABLE", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } else if self.parse_keywords(&[ | |
| Keyword::FORCE, | |
| Keyword::ROW, | |
| Keyword::LEVEL, | |
| Keyword::SECURITY, | |
| ]) { | |
| AlterTableOperation::ForceRowLevelSecurity | |
| } else if self.parse_keywords(&[ | |
| Keyword::NO, | |
| Keyword::FORCE, | |
| Keyword::ROW, | |
| Keyword::LEVEL, | |
| Keyword::SECURITY, | |
| ]) { | |
| AlterTableOperation::NoForceRowLevelSecurity | |
| } else if self.parse_keywords(&[Keyword::CLEAR, Keyword::PROJECTION]) | |
| && dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let partition = if self.parse_keywords(&[Keyword::IN, Keyword::PARTITION]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| AlterTableOperation::ClearProjection { | |
| if_exists, | |
| name, | |
| partition, | |
| } | |
| } else if self.parse_keywords(&[Keyword::MATERIALIZE, Keyword::PROJECTION]) | |
| && dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let partition = if self.parse_keywords(&[Keyword::IN, Keyword::PARTITION]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| AlterTableOperation::MaterializeProjection { | |
| if_exists, | |
| name, | |
| partition, | |
| } | |
| } else if self.parse_keyword(Keyword::DROP) { | |
| if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { | |
| self.expect_token(&Token::LParen)?; | |
| let partitions = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| AlterTableOperation::DropPartitions { | |
| partitions, | |
| if_exists: true, | |
| } | |
| } else if self.parse_keyword(Keyword::PARTITION) { | |
| self.expect_token(&Token::LParen)?; | |
| let partitions = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| AlterTableOperation::DropPartitions { | |
| partitions, | |
| if_exists: false, | |
| } | |
| } else if self.parse_keyword(Keyword::CONSTRAINT) { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| AlterTableOperation::DropConstraint { | |
| if_exists, | |
| name, | |
| drop_behavior, | |
| } | |
| } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) { | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| AlterTableOperation::DropPrimaryKey { drop_behavior } | |
| } else if self.parse_keywords(&[Keyword::FOREIGN, Keyword::KEY]) { | |
| let name = self.parse_identifier()?; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| AlterTableOperation::DropForeignKey { | |
| name, | |
| drop_behavior, | |
| } | |
| } else if self.parse_keyword(Keyword::INDEX) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::DropIndex { name } | |
| } else if self.parse_keyword(Keyword::PROJECTION) | |
| && dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::DropProjection { if_exists, name } | |
| } else if self.parse_keywords(&[Keyword::CLUSTERING, Keyword::KEY]) { | |
| AlterTableOperation::DropClusteringKey | |
| } else { | |
| let has_column_keyword = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let column_names = if self.dialect.supports_comma_separated_drop_column_list() { | |
| self.parse_comma_separated(Parser::parse_identifier)? | |
| } else { | |
| vec![self.parse_identifier()?] | |
| }; | |
| let drop_behavior = self.parse_optional_drop_behavior(); | |
| AlterTableOperation::DropColumn { | |
| has_column_keyword, | |
| column_names, | |
| if_exists, | |
| drop_behavior, | |
| } | |
| } | |
| } else if self.parse_keyword(Keyword::PARTITION) { | |
| self.expect_token(&Token::LParen)?; | |
| let before = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_keyword_is(Keyword::RENAME)?; | |
| self.expect_keywords(&[Keyword::TO, Keyword::PARTITION])?; | |
| self.expect_token(&Token::LParen)?; | |
| let renames = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| AlterTableOperation::RenamePartitions { | |
| old_partitions: before, | |
| new_partitions: renames, | |
| } | |
| } else if self.parse_keyword(Keyword::CHANGE) { | |
| let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] | |
| let old_name = self.parse_identifier()?; | |
| let new_name = self.parse_identifier()?; | |
| let data_type = self.parse_data_type()?; | |
| let mut options = vec![]; | |
| while let Some(option) = self.parse_optional_column_option()? { | |
| options.push(option); | |
| } | |
| let column_position = self.parse_column_position()?; | |
| AlterTableOperation::ChangeColumn { | |
| old_name, | |
| new_name, | |
| data_type, | |
| options, | |
| column_position, | |
| } | |
| } else if self.parse_keywords(&[Keyword::MODIFY, Keyword::ORDER, Keyword::BY]) { | |
| AlterTableOperation::ModifyOrderBy { | |
| order_by: self.parse_sorting_key()?, | |
| } | |
| } else if self.parse_keyword(Keyword::MODIFY) { | |
| let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] | |
| let col_name = self.parse_identifier()?; | |
| let data_type = self.parse_data_type()?; | |
| let mut options = vec![]; | |
| while let Some(option) = self.parse_optional_column_option()? { | |
| options.push(option); | |
| } | |
| let column_position = self.parse_column_position()?; | |
| AlterTableOperation::ModifyColumn { | |
| col_name, | |
| data_type, | |
| options, | |
| column_position, | |
| } | |
| } else if self.parse_keyword(Keyword::ALTER) { | |
| if self.peek_keyword(Keyword::SORTKEY) { | |
| self.prev_token(); | |
| return self.parse_alter_sort_key(); | |
| } | |
| let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] | |
| let column_name = self.parse_identifier()?; | |
| let is_postgresql = dialect_of!(self is PostgreSqlDialect); | |
| let op: AlterColumnOperation = if self.parse_keywords(&[ | |
| Keyword::SET, | |
| Keyword::NOT, | |
| Keyword::NULL, | |
| ]) { | |
| AlterColumnOperation::SetNotNull {} | |
| } else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) { | |
| AlterColumnOperation::DropNotNull {} | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) { | |
| AlterColumnOperation::SetDefault { | |
| value: self.parse_expr()?, | |
| } | |
| } else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) { | |
| AlterColumnOperation::DropDefault {} | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE]) { | |
| self.parse_set_data_type(true)? | |
| } else if self.parse_keyword(Keyword::TYPE) { | |
| self.parse_set_data_type(false)? | |
| } else if self.parse_keywords(&[Keyword::ADD, Keyword::GENERATED]) { | |
| let generated_as = if self.parse_keyword(Keyword::ALWAYS) { | |
| Some(GeneratedAs::Always) | |
| } else if self.parse_keywords(&[Keyword::BY, Keyword::DEFAULT]) { | |
| Some(GeneratedAs::ByDefault) | |
| } else { | |
| None | |
| }; | |
| self.expect_keywords(&[Keyword::AS, Keyword::IDENTITY])?; | |
| let mut sequence_options: Option<Vec<SequenceOptions>> = None; | |
| if self.peek_token_ref().token == Token::LParen { | |
| self.expect_token(&Token::LParen)?; | |
| sequence_options = Some(self.parse_create_sequence_options()?); | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| AlterColumnOperation::AddGenerated { | |
| generated_as, | |
| sequence_options, | |
| } | |
| } else { | |
| let message = if is_postgresql { | |
| "SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE, or ADD GENERATED after ALTER COLUMN" | |
| } else { | |
| "SET/DROP NOT NULL, SET DEFAULT, or SET DATA TYPE after ALTER COLUMN" | |
| }; | |
| return self.expected_ref(message, self.peek_token_ref()); | |
| }; | |
| AlterTableOperation::AlterColumn { column_name, op } | |
| } else if self.parse_keyword(Keyword::SWAP) { | |
| self.expect_keyword_is(Keyword::WITH)?; | |
| let table_name = self.parse_object_name(false)?; | |
| AlterTableOperation::SwapWith { table_name } | |
| } else if dialect_of!(self is PostgreSqlDialect | GenericDialect) | |
| && self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) | |
| { | |
| let new_owner = self.parse_owner()?; | |
| AlterTableOperation::OwnerTo { new_owner } | |
| } else if dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| && self.parse_keyword(Keyword::ATTACH) | |
| { | |
| AlterTableOperation::AttachPartition { | |
| partition: self.parse_part_or_partition()?, | |
| } | |
| } else if dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| && self.parse_keyword(Keyword::DETACH) | |
| { | |
| AlterTableOperation::DetachPartition { | |
| partition: self.parse_part_or_partition()?, | |
| } | |
| } else if dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| && self.parse_keyword(Keyword::FREEZE) | |
| { | |
| let partition = self.parse_part_or_partition()?; | |
| let with_name = if self.parse_keyword(Keyword::WITH) { | |
| self.expect_keyword_is(Keyword::NAME)?; | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| AlterTableOperation::FreezePartition { | |
| partition, | |
| with_name, | |
| } | |
| } else if dialect_of!(self is ClickHouseDialect|GenericDialect) | |
| && self.parse_keyword(Keyword::UNFREEZE) | |
| { | |
| let partition = self.parse_part_or_partition()?; | |
| let with_name = if self.parse_keyword(Keyword::WITH) { | |
| self.expect_keyword_is(Keyword::NAME)?; | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| AlterTableOperation::UnfreezePartition { | |
| partition, | |
| with_name, | |
| } | |
| } else if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { | |
| self.expect_token(&Token::LParen)?; | |
| let exprs = self.parse_comma_separated(|parser| parser.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| AlterTableOperation::ClusterBy { exprs } | |
| } else if self.parse_keywords(&[Keyword::SUSPEND, Keyword::RECLUSTER]) { | |
| AlterTableOperation::SuspendRecluster | |
| } else if self.parse_keywords(&[Keyword::RESUME, Keyword::RECLUSTER]) { | |
| AlterTableOperation::ResumeRecluster | |
| } else if self.parse_keyword(Keyword::LOCK) { | |
| let equals = self.consume_token(&Token::Eq); | |
| let lock = match self.parse_one_of_keywords(&[ | |
| Keyword::DEFAULT, | |
| Keyword::EXCLUSIVE, | |
| Keyword::NONE, | |
| Keyword::SHARED, | |
| ]) { | |
| Some(Keyword::DEFAULT) => AlterTableLock::Default, | |
| Some(Keyword::EXCLUSIVE) => AlterTableLock::Exclusive, | |
| Some(Keyword::NONE) => AlterTableLock::None, | |
| Some(Keyword::SHARED) => AlterTableLock::Shared, | |
| _ => self.expected_ref( | |
| "DEFAULT, EXCLUSIVE, NONE or SHARED after LOCK [=]", | |
| self.peek_token_ref(), | |
| )?, | |
| }; | |
| AlterTableOperation::Lock { equals, lock } | |
| } else if self.parse_keyword(Keyword::ALGORITHM) { | |
| let equals = self.consume_token(&Token::Eq); | |
| let algorithm = match self.parse_one_of_keywords(&[ | |
| Keyword::DEFAULT, | |
| Keyword::INSTANT, | |
| Keyword::INPLACE, | |
| Keyword::COPY, | |
| ]) { | |
| Some(Keyword::DEFAULT) => AlterTableAlgorithm::Default, | |
| Some(Keyword::INSTANT) => AlterTableAlgorithm::Instant, | |
| Some(Keyword::INPLACE) => AlterTableAlgorithm::Inplace, | |
| Some(Keyword::COPY) => AlterTableAlgorithm::Copy, | |
| _ => self.expected_ref( | |
| "DEFAULT, INSTANT, INPLACE, or COPY after ALGORITHM [=]", | |
| self.peek_token_ref(), | |
| )?, | |
| }; | |
| AlterTableOperation::Algorithm { equals, algorithm } | |
| } else if self.parse_keyword(Keyword::AUTO_INCREMENT) { | |
| let equals = self.consume_token(&Token::Eq); | |
| let value = self.parse_number_value()?; | |
| AlterTableOperation::AutoIncrement { equals, value } | |
| } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::IDENTITY]) { | |
| let identity = if self.parse_keyword(Keyword::NOTHING) { | |
| ReplicaIdentity::Nothing | |
| } else if self.parse_keyword(Keyword::FULL) { | |
| ReplicaIdentity::Full | |
| } else if self.parse_keyword(Keyword::DEFAULT) { | |
| ReplicaIdentity::Default | |
| } else if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) { | |
| ReplicaIdentity::Index(self.parse_identifier()?) | |
| } else { | |
| return self.expected_ref( | |
| "NOTHING, FULL, DEFAULT, or USING INDEX index_name after REPLICA IDENTITY", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| AlterTableOperation::ReplicaIdentity { identity } | |
| } else if self.parse_keywords(&[Keyword::VALIDATE, Keyword::CONSTRAINT]) { | |
| let name = self.parse_identifier()?; | |
| AlterTableOperation::ValidateConstraint { name } | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::LOGGED]) { | |
| AlterTableOperation::SetLogged | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::UNLOGGED]) { | |
| AlterTableOperation::SetUnlogged | |
| } else { | |
| let mut options = | |
| self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?; | |
| if !options.is_empty() { | |
| AlterTableOperation::SetTblProperties { | |
| table_properties: options, | |
| } | |
| } else { | |
| options = self.parse_options(Keyword::SET)?; | |
| if !options.is_empty() { | |
| AlterTableOperation::SetOptionsParens { options } | |
| } else { | |
| return self.expected_ref( | |
| "ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, SET, or SET TBLPROPERTIES after ALTER TABLE", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } | |
| }; | |
| Ok(operation) | |
| } | |
| fn parse_set_data_type(&mut self, had_set: bool) -> Result<AlterColumnOperation, ParserError> { | |
| let data_type = self.parse_data_type()?; | |
| let using = if self.dialect.supports_alter_column_type_using() | |
| && self.parse_keyword(Keyword::USING) | |
| { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(AlterColumnOperation::SetDataType { | |
| data_type, | |
| using, | |
| had_set, | |
| }) | |
| } | |
| fn parse_part_or_partition(&mut self) -> Result<Partition, ParserError> { | |
| let keyword = self.expect_one_of_keywords(&[Keyword::PART, Keyword::PARTITION])?; | |
| match keyword { | |
| Keyword::PART => Ok(Partition::Part(self.parse_expr()?)), | |
| Keyword::PARTITION => Ok(Partition::Expr(self.parse_expr()?)), | |
| // unreachable because expect_one_of_keywords used above | |
| unexpected_keyword => Err(ParserError::ParserError( | |
| format!("Internal parser error: expected any of {{PART, PARTITION}}, got {unexpected_keyword:?}"), | |
| )), | |
| } | |
| } | |
| /// Parse an `ALTER <object>` statement and dispatch to the appropriate alter handler. | |
| pub fn parse_alter(&mut self) -> Result<Statement, ParserError> { | |
| if self.peek_keywords(&[Keyword::TEXT, Keyword::SEARCH]) { | |
| return self.parse_alter_text_search().map(Into::into); | |
| } | |
| let object_type = self.expect_one_of_keywords(&[ | |
| Keyword::VIEW, | |
| Keyword::TYPE, | |
| Keyword::COLLATION, | |
| Keyword::TABLE, | |
| Keyword::INDEX, | |
| Keyword::FUNCTION, | |
| Keyword::AGGREGATE, | |
| Keyword::ROLE, | |
| Keyword::POLICY, | |
| Keyword::CONNECTOR, | |
| Keyword::ICEBERG, | |
| Keyword::SCHEMA, | |
| Keyword::USER, | |
| Keyword::OPERATOR, | |
| ])?; | |
| match object_type { | |
| Keyword::SCHEMA => { | |
| self.prev_token(); | |
| self.prev_token(); | |
| self.parse_alter_schema() | |
| } | |
| Keyword::VIEW => self.parse_alter_view(), | |
| Keyword::TYPE => self.parse_alter_type(), | |
| Keyword::COLLATION => self.parse_alter_collation().map(Into::into), | |
| Keyword::TABLE => self.parse_alter_table(false), | |
| Keyword::ICEBERG => { | |
| self.expect_keyword(Keyword::TABLE)?; | |
| self.parse_alter_table(true) | |
| } | |
| Keyword::INDEX => { | |
| let index_name = self.parse_object_name(false)?; | |
| let operation = if self.parse_keyword(Keyword::RENAME) { | |
| if self.parse_keyword(Keyword::TO) { | |
| let index_name = self.parse_object_name(false)?; | |
| AlterIndexOperation::RenameIndex { index_name } | |
| } else { | |
| return self.expected_ref("TO after RENAME", self.peek_token_ref()); | |
| } | |
| } else { | |
| return self.expected_ref("RENAME after ALTER INDEX", self.peek_token_ref()); | |
| }; | |
| Ok(Statement::AlterIndex { | |
| name: index_name, | |
| operation, | |
| }) | |
| } | |
| Keyword::FUNCTION => self.parse_alter_function(AlterFunctionKind::Function), | |
| Keyword::AGGREGATE => self.parse_alter_function(AlterFunctionKind::Aggregate), | |
| Keyword::OPERATOR => { | |
| if self.parse_keyword(Keyword::FAMILY) { | |
| self.parse_alter_operator_family().map(Into::into) | |
| } else if self.parse_keyword(Keyword::CLASS) { | |
| self.parse_alter_operator_class().map(Into::into) | |
| } else { | |
| self.parse_alter_operator().map(Into::into) | |
| } | |
| } | |
| Keyword::ROLE => self.parse_alter_role(), | |
| Keyword::POLICY => self.parse_alter_policy().map(Into::into), | |
| Keyword::CONNECTOR => self.parse_alter_connector(), | |
| Keyword::USER if self.dialect.supports_alter_user_as_alter_role() => { | |
| self.parse_alter_role() | |
| } | |
| Keyword::USER => self.parse_alter_user().map(Into::into), | |
| // unreachable because expect_one_of_keywords used above | |
| unexpected_keyword => Err(ParserError::ParserError( | |
| format!("Internal parser error: expected any of {{TEXT SEARCH, VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"), | |
| )), | |
| } | |
| } | |
| fn parse_alter_aggregate_signature( | |
| &mut self, | |
| ) -> Result<(FunctionDesc, bool, Option<Vec<OperateFunctionArg>>), ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::LParen)?; | |
| if self.consume_token(&Token::Mul) { | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(( | |
| FunctionDesc { | |
| name, | |
| args: Some(vec![]), | |
| }, | |
| true, | |
| None, | |
| )); | |
| } | |
| let args = | |
| if self.peek_keyword(Keyword::ORDER) || self.peek_token_ref().token == Token::RParen { | |
| vec![] | |
| } else { | |
| self.parse_comma_separated(Parser::parse_aggregate_function_arg)? | |
| }; | |
| let aggregate_order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| Some(self.parse_comma_separated(Parser::parse_aggregate_function_arg)?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(( | |
| FunctionDesc { | |
| name, | |
| args: Some(args), | |
| }, | |
| false, | |
| aggregate_order_by, | |
| )) | |
| } | |
| fn parse_alter_function_action(&mut self) -> Result<Option<AlterFunctionAction>, ParserError> { | |
| let action = if self.parse_keywords(&[ | |
| Keyword::CALLED, | |
| Keyword::ON, | |
| Keyword::NULL, | |
| Keyword::INPUT, | |
| ]) { | |
| Some(AlterFunctionAction::CalledOnNull( | |
| FunctionCalledOnNull::CalledOnNullInput, | |
| )) | |
| } else if self.parse_keywords(&[ | |
| Keyword::RETURNS, | |
| Keyword::NULL, | |
| Keyword::ON, | |
| Keyword::NULL, | |
| Keyword::INPUT, | |
| ]) { | |
| Some(AlterFunctionAction::CalledOnNull( | |
| FunctionCalledOnNull::ReturnsNullOnNullInput, | |
| )) | |
| } else if self.parse_keyword(Keyword::STRICT) { | |
| Some(AlterFunctionAction::CalledOnNull( | |
| FunctionCalledOnNull::Strict, | |
| )) | |
| } else if self.parse_keyword(Keyword::IMMUTABLE) { | |
| Some(AlterFunctionAction::Behavior(FunctionBehavior::Immutable)) | |
| } else if self.parse_keyword(Keyword::STABLE) { | |
| Some(AlterFunctionAction::Behavior(FunctionBehavior::Stable)) | |
| } else if self.parse_keyword(Keyword::VOLATILE) { | |
| Some(AlterFunctionAction::Behavior(FunctionBehavior::Volatile)) | |
| } else if self.parse_keyword(Keyword::NOT) { | |
| self.expect_keyword(Keyword::LEAKPROOF)?; | |
| Some(AlterFunctionAction::Leakproof(false)) | |
| } else if self.parse_keyword(Keyword::LEAKPROOF) { | |
| Some(AlterFunctionAction::Leakproof(true)) | |
| } else if self.parse_keyword(Keyword::EXTERNAL) { | |
| self.expect_keyword(Keyword::SECURITY)?; | |
| let security = if self.parse_keyword(Keyword::DEFINER) { | |
| FunctionSecurity::Definer | |
| } else if self.parse_keyword(Keyword::INVOKER) { | |
| FunctionSecurity::Invoker | |
| } else { | |
| return self.expected_ref("DEFINER or INVOKER", self.peek_token_ref()); | |
| }; | |
| Some(AlterFunctionAction::Security { | |
| external: true, | |
| security, | |
| }) | |
| } else if self.parse_keyword(Keyword::SECURITY) { | |
| let security = if self.parse_keyword(Keyword::DEFINER) { | |
| FunctionSecurity::Definer | |
| } else if self.parse_keyword(Keyword::INVOKER) { | |
| FunctionSecurity::Invoker | |
| } else { | |
| return self.expected_ref("DEFINER or INVOKER", self.peek_token_ref()); | |
| }; | |
| Some(AlterFunctionAction::Security { | |
| external: false, | |
| security, | |
| }) | |
| } else if self.parse_keyword(Keyword::PARALLEL) { | |
| let parallel = if self.parse_keyword(Keyword::UNSAFE) { | |
| FunctionParallel::Unsafe | |
| } else if self.parse_keyword(Keyword::RESTRICTED) { | |
| FunctionParallel::Restricted | |
| } else if self.parse_keyword(Keyword::SAFE) { | |
| FunctionParallel::Safe | |
| } else { | |
| return self | |
| .expected_ref("one of UNSAFE | RESTRICTED | SAFE", self.peek_token_ref()); | |
| }; | |
| Some(AlterFunctionAction::Parallel(parallel)) | |
| } else if self.parse_keyword(Keyword::COST) { | |
| Some(AlterFunctionAction::Cost(self.parse_number()?)) | |
| } else if self.parse_keyword(Keyword::ROWS) { | |
| Some(AlterFunctionAction::Rows(self.parse_number()?)) | |
| } else if self.parse_keyword(Keyword::SUPPORT) { | |
| Some(AlterFunctionAction::Support(self.parse_object_name(false)?)) | |
| } else if self.parse_keyword(Keyword::SET) { | |
| let name = self.parse_object_name(false)?; | |
| let value = if self.parse_keywords(&[Keyword::FROM, Keyword::CURRENT]) { | |
| FunctionSetValue::FromCurrent | |
| } else { | |
| if !self.consume_token(&Token::Eq) && !self.parse_keyword(Keyword::TO) { | |
| return self.expected_ref("= or TO", self.peek_token_ref()); | |
| } | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| FunctionSetValue::Default | |
| } else { | |
| FunctionSetValue::Values(self.parse_comma_separated(Parser::parse_expr)?) | |
| } | |
| }; | |
| Some(AlterFunctionAction::Set(FunctionDefinitionSetParam { | |
| name, | |
| value, | |
| })) | |
| } else if self.parse_keyword(Keyword::RESET) { | |
| let reset_config = if self.parse_keyword(Keyword::ALL) { | |
| ResetConfig::ALL | |
| } else { | |
| ResetConfig::ConfigName(self.parse_object_name(false)?) | |
| }; | |
| Some(AlterFunctionAction::Reset(reset_config)) | |
| } else { | |
| None | |
| }; | |
| Ok(action) | |
| } | |
| fn parse_alter_function_actions( | |
| &mut self, | |
| ) -> Result<(Vec<AlterFunctionAction>, bool), ParserError> { | |
| let mut actions = vec![]; | |
| while let Some(action) = self.parse_alter_function_action()? { | |
| actions.push(action); | |
| } | |
| if actions.is_empty() { | |
| return self.expected_ref("at least one ALTER FUNCTION action", self.peek_token_ref()); | |
| } | |
| let restrict = self.parse_keyword(Keyword::RESTRICT); | |
| Ok((actions, restrict)) | |
| } | |
| /// Parse an `ALTER FUNCTION` or `ALTER AGGREGATE` statement. | |
| pub fn parse_alter_function( | |
| &mut self, | |
| kind: AlterFunctionKind, | |
| ) -> Result<Statement, ParserError> { | |
| let (function, aggregate_star, aggregate_order_by) = match kind { | |
| AlterFunctionKind::Function => (self.parse_function_desc()?, false, None), | |
| AlterFunctionKind::Aggregate => self.parse_alter_aggregate_signature()?, | |
| }; | |
| let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| let new_name = self.parse_identifier()?; | |
| AlterFunctionOperation::RenameTo { new_name } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| AlterFunctionOperation::OwnerTo(self.parse_owner()?) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| AlterFunctionOperation::SetSchema { | |
| schema_name: self.parse_object_name(false)?, | |
| } | |
| } else if matches!(kind, AlterFunctionKind::Function) && self.parse_keyword(Keyword::NO) { | |
| if !self.parse_keyword(Keyword::DEPENDS) { | |
| return self.expected_ref("DEPENDS after NO", self.peek_token_ref()); | |
| } | |
| self.expect_keywords(&[Keyword::ON, Keyword::EXTENSION])?; | |
| AlterFunctionOperation::DependsOnExtension { | |
| no: true, | |
| extension_name: self.parse_object_name(false)?, | |
| } | |
| } else if matches!(kind, AlterFunctionKind::Function) | |
| && self.parse_keyword(Keyword::DEPENDS) | |
| { | |
| self.expect_keywords(&[Keyword::ON, Keyword::EXTENSION])?; | |
| AlterFunctionOperation::DependsOnExtension { | |
| no: false, | |
| extension_name: self.parse_object_name(false)?, | |
| } | |
| } else if matches!(kind, AlterFunctionKind::Function) { | |
| let (actions, restrict) = self.parse_alter_function_actions()?; | |
| AlterFunctionOperation::Actions { actions, restrict } | |
| } else { | |
| return self.expected_ref( | |
| "RENAME TO, OWNER TO, or SET SCHEMA after ALTER AGGREGATE", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(Statement::AlterFunction(AlterFunction { | |
| kind, | |
| function, | |
| aggregate_order_by, | |
| aggregate_star, | |
| operation, | |
| })) | |
| } | |
| /// Parse a [Statement::AlterTable] | |
| pub fn parse_alter_table(&mut self, iceberg: bool) -> Result<Statement, ParserError> { | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] | |
| let table_name = self.parse_object_name(false)?; | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; | |
| let mut location = None; | |
| if self.parse_keyword(Keyword::LOCATION) { | |
| location = Some(HiveSetLocation { | |
| has_set: false, | |
| location: self.parse_identifier()?, | |
| }); | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::LOCATION]) { | |
| location = Some(HiveSetLocation { | |
| has_set: true, | |
| location: self.parse_identifier()?, | |
| }); | |
| } | |
| let end_token = if self.peek_token_ref().token == Token::SemiColon { | |
| self.peek_token_ref().clone() | |
| } else { | |
| self.get_current_token().clone() | |
| }; | |
| Ok(AlterTable { | |
| name: table_name, | |
| if_exists, | |
| only, | |
| operations, | |
| location, | |
| on_cluster, | |
| table_type: if iceberg { | |
| Some(AlterTableType::Iceberg) | |
| } else { | |
| None | |
| }, | |
| end_token: AttachedToken(end_token), | |
| } | |
| .into()) | |
| } | |
| /// Parse an `ALTER VIEW` statement. | |
| pub fn parse_alter_view(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let columns = self.parse_parenthesized_column_list(Optional, false)?; | |
| let with_options = self.parse_options(Keyword::WITH)?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let query = self.parse_query()?; | |
| Ok(Statement::AlterView { | |
| name, | |
| columns, | |
| query, | |
| with_options, | |
| }) | |
| } | |
| /// Parse a [Statement::AlterType] | |
| pub fn parse_alter_type(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| let new_name = self.parse_identifier()?; | |
| Ok(Statement::AlterType(AlterType { | |
| name, | |
| operation: AlterTypeOperation::Rename(AlterTypeRename { new_name }), | |
| })) | |
| } else if self.parse_keywords(&[Keyword::ADD, Keyword::VALUE]) { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let new_enum_value = self.parse_identifier()?; | |
| let position = if self.parse_keyword(Keyword::BEFORE) { | |
| Some(AlterTypeAddValuePosition::Before(self.parse_identifier()?)) | |
| } else if self.parse_keyword(Keyword::AFTER) { | |
| Some(AlterTypeAddValuePosition::After(self.parse_identifier()?)) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::AlterType(AlterType { | |
| name, | |
| operation: AlterTypeOperation::AddValue(AlterTypeAddValue { | |
| if_not_exists, | |
| value: new_enum_value, | |
| position, | |
| }), | |
| })) | |
| } else if self.parse_keywords(&[Keyword::RENAME, Keyword::VALUE]) { | |
| let existing_enum_value = self.parse_identifier()?; | |
| self.expect_keyword(Keyword::TO)?; | |
| let new_enum_value = self.parse_identifier()?; | |
| Ok(Statement::AlterType(AlterType { | |
| name, | |
| operation: AlterTypeOperation::RenameValue(AlterTypeRenameValue { | |
| from: existing_enum_value, | |
| to: new_enum_value, | |
| }), | |
| })) | |
| } else { | |
| self.expected_ref( | |
| "{RENAME TO | { RENAME | ADD } VALUE}", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| /// Parse a [Statement::AlterCollation]. | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-altercollation.html) | |
| pub fn parse_alter_collation(&mut self) -> Result<AlterCollation, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| AlterCollationOperation::RenameTo { | |
| new_name: self.parse_identifier()?, | |
| } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| AlterCollationOperation::OwnerTo(self.parse_owner()?) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| AlterCollationOperation::SetSchema { | |
| schema_name: self.parse_object_name(false)?, | |
| } | |
| } else if self.parse_keywords(&[Keyword::REFRESH, Keyword::VERSION]) { | |
| AlterCollationOperation::RefreshVersion | |
| } else { | |
| return self.expected_ref( | |
| "RENAME TO, OWNER TO, SET SCHEMA, or REFRESH VERSION after ALTER COLLATION", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(AlterCollation { name, operation }) | |
| } | |
| /// Parse a [Statement::AlterOperator] | |
| /// | |
| /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-alteroperator.html) | |
| pub fn parse_alter_operator(&mut self) -> Result<AlterOperator, ParserError> { | |
| let name = self.parse_operator_name()?; | |
| // Parse (left_type, right_type) | |
| self.expect_token(&Token::LParen)?; | |
| let left_type = if self.parse_keyword(Keyword::NONE) { | |
| None | |
| } else { | |
| Some(self.parse_data_type()?) | |
| }; | |
| self.expect_token(&Token::Comma)?; | |
| let right_type = self.parse_data_type()?; | |
| self.expect_token(&Token::RParen)?; | |
| // Parse the operation | |
| let operation = if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| let owner = if self.parse_keyword(Keyword::CURRENT_ROLE) { | |
| Owner::CurrentRole | |
| } else if self.parse_keyword(Keyword::CURRENT_USER) { | |
| Owner::CurrentUser | |
| } else if self.parse_keyword(Keyword::SESSION_USER) { | |
| Owner::SessionUser | |
| } else { | |
| Owner::Ident(self.parse_identifier()?) | |
| }; | |
| AlterOperatorOperation::OwnerTo(owner) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| let schema_name = self.parse_object_name(false)?; | |
| AlterOperatorOperation::SetSchema { schema_name } | |
| } else if self.parse_keyword(Keyword::SET) { | |
| self.expect_token(&Token::LParen)?; | |
| let mut options = Vec::new(); | |
| loop { | |
| let keyword = self.expect_one_of_keywords(&[ | |
| Keyword::RESTRICT, | |
| Keyword::JOIN, | |
| Keyword::COMMUTATOR, | |
| Keyword::NEGATOR, | |
| Keyword::HASHES, | |
| Keyword::MERGES, | |
| ])?; | |
| match keyword { | |
| Keyword::RESTRICT => { | |
| self.expect_token(&Token::Eq)?; | |
| let proc_name = if self.parse_keyword(Keyword::NONE) { | |
| None | |
| } else { | |
| Some(self.parse_object_name(false)?) | |
| }; | |
| options.push(OperatorOption::Restrict(proc_name)); | |
| } | |
| Keyword::JOIN => { | |
| self.expect_token(&Token::Eq)?; | |
| let proc_name = if self.parse_keyword(Keyword::NONE) { | |
| None | |
| } else { | |
| Some(self.parse_object_name(false)?) | |
| }; | |
| options.push(OperatorOption::Join(proc_name)); | |
| } | |
| Keyword::COMMUTATOR => { | |
| self.expect_token(&Token::Eq)?; | |
| let op_name = self.parse_operator_name()?; | |
| options.push(OperatorOption::Commutator(op_name)); | |
| } | |
| Keyword::NEGATOR => { | |
| self.expect_token(&Token::Eq)?; | |
| let op_name = self.parse_operator_name()?; | |
| options.push(OperatorOption::Negator(op_name)); | |
| } | |
| Keyword::HASHES => { | |
| options.push(OperatorOption::Hashes); | |
| } | |
| Keyword::MERGES => { | |
| options.push(OperatorOption::Merges); | |
| } | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in operator option"), | |
| )), | |
| } | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| AlterOperatorOperation::Set { options } | |
| } else { | |
| return self.expected_ref( | |
| "OWNER TO, SET SCHEMA, or SET after ALTER OPERATOR", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(AlterOperator { | |
| name, | |
| left_type, | |
| right_type, | |
| operation, | |
| }) | |
| } | |
| /// Parse an operator item for ALTER OPERATOR FAMILY ADD operations | |
| fn parse_operator_family_add_operator(&mut self) -> Result<OperatorFamilyItem, ParserError> { | |
| let strategy_number = self.parse_literal_uint()?; | |
| let operator_name = self.parse_operator_name()?; | |
| // Operator argument types (required for ALTER OPERATOR FAMILY) | |
| self.expect_token(&Token::LParen)?; | |
| let op_types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| // Optional purpose | |
| let purpose = if self.parse_keyword(Keyword::FOR) { | |
| if self.parse_keyword(Keyword::SEARCH) { | |
| Some(OperatorPurpose::ForSearch) | |
| } else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| let sort_family = self.parse_object_name(false)?; | |
| Some(OperatorPurpose::ForOrderBy { sort_family }) | |
| } else { | |
| return self.expected_ref("SEARCH or ORDER BY after FOR", self.peek_token_ref()); | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(OperatorFamilyItem::Operator { | |
| strategy_number, | |
| operator_name, | |
| op_types, | |
| purpose, | |
| }) | |
| } | |
| /// Parse a function item for ALTER OPERATOR FAMILY ADD operations | |
| fn parse_operator_family_add_function(&mut self) -> Result<OperatorFamilyItem, ParserError> { | |
| let support_number = self.parse_literal_uint()?; | |
| // Optional operator types | |
| let op_types = | |
| if self.consume_token(&Token::LParen) && self.peek_token_ref().token != Token::RParen { | |
| let types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(types) | |
| } else { | |
| None | |
| }; | |
| let function_name = self.parse_object_name(false)?; | |
| // Function argument types | |
| let argument_types = if self.consume_token(&Token::LParen) { | |
| if self.peek_token_ref().token == Token::RParen { | |
| self.expect_token(&Token::RParen)?; | |
| vec![] | |
| } else { | |
| let types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| types | |
| } | |
| } else { | |
| vec![] | |
| }; | |
| Ok(OperatorFamilyItem::Function { | |
| support_number, | |
| op_types, | |
| function_name, | |
| argument_types, | |
| }) | |
| } | |
| /// Parse an operator item for ALTER OPERATOR FAMILY DROP operations | |
| fn parse_operator_family_drop_operator( | |
| &mut self, | |
| ) -> Result<OperatorFamilyDropItem, ParserError> { | |
| let strategy_number = self.parse_literal_uint()?; | |
| // Operator argument types (required for DROP) | |
| self.expect_token(&Token::LParen)?; | |
| let op_types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(OperatorFamilyDropItem::Operator { | |
| strategy_number, | |
| op_types, | |
| }) | |
| } | |
| /// Parse a function item for ALTER OPERATOR FAMILY DROP operations | |
| fn parse_operator_family_drop_function( | |
| &mut self, | |
| ) -> Result<OperatorFamilyDropItem, ParserError> { | |
| let support_number = self.parse_literal_uint()?; | |
| // Operator types (required for DROP) | |
| self.expect_token(&Token::LParen)?; | |
| let op_types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(OperatorFamilyDropItem::Function { | |
| support_number, | |
| op_types, | |
| }) | |
| } | |
| /// Parse an operator family item for ADD operations (dispatches to operator or function parsing) | |
| fn parse_operator_family_add_item(&mut self) -> Result<OperatorFamilyItem, ParserError> { | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| self.parse_operator_family_add_operator() | |
| } else if self.parse_keyword(Keyword::FUNCTION) { | |
| self.parse_operator_family_add_function() | |
| } else { | |
| self.expected_ref("OPERATOR or FUNCTION", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse an operator family item for DROP operations (dispatches to operator or function parsing) | |
| fn parse_operator_family_drop_item(&mut self) -> Result<OperatorFamilyDropItem, ParserError> { | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| self.parse_operator_family_drop_operator() | |
| } else if self.parse_keyword(Keyword::FUNCTION) { | |
| self.parse_operator_family_drop_function() | |
| } else { | |
| self.expected_ref("OPERATOR or FUNCTION", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse a [Statement::AlterOperatorFamily] | |
| /// See <https://www.postgresql.org/docs/current/sql-alteropfamily.html> | |
| pub fn parse_alter_operator_family(&mut self) -> Result<AlterOperatorFamily, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| let operation = if self.parse_keyword(Keyword::ADD) { | |
| let items = self.parse_comma_separated(Parser::parse_operator_family_add_item)?; | |
| AlterOperatorFamilyOperation::Add { items } | |
| } else if self.parse_keyword(Keyword::DROP) { | |
| let items = self.parse_comma_separated(Parser::parse_operator_family_drop_item)?; | |
| AlterOperatorFamilyOperation::Drop { items } | |
| } else if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| let new_name = self.parse_object_name(false)?; | |
| AlterOperatorFamilyOperation::RenameTo { new_name } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| let owner = self.parse_owner()?; | |
| AlterOperatorFamilyOperation::OwnerTo(owner) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| let schema_name = self.parse_object_name(false)?; | |
| AlterOperatorFamilyOperation::SetSchema { schema_name } | |
| } else { | |
| return self.expected_ref( | |
| "ADD, DROP, RENAME TO, OWNER TO, or SET SCHEMA after ALTER OPERATOR FAMILY", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(AlterOperatorFamily { | |
| name, | |
| using, | |
| operation, | |
| }) | |
| } | |
| /// Parse an `ALTER OPERATOR CLASS` statement. | |
| /// | |
| /// Handles operations like `RENAME TO`, `OWNER TO`, and `SET SCHEMA`. | |
| pub fn parse_alter_operator_class(&mut self) -> Result<AlterOperatorClass, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_keyword(Keyword::USING)?; | |
| let using = self.parse_identifier()?; | |
| let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| let new_name = self.parse_object_name(false)?; | |
| AlterOperatorClassOperation::RenameTo { new_name } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| let owner = self.parse_owner()?; | |
| AlterOperatorClassOperation::OwnerTo(owner) | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) { | |
| let schema_name = self.parse_object_name(false)?; | |
| AlterOperatorClassOperation::SetSchema { schema_name } | |
| } else { | |
| return self.expected_ref( | |
| "RENAME TO, OWNER TO, or SET SCHEMA after ALTER OPERATOR CLASS", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Ok(AlterOperatorClass { | |
| name, | |
| using, | |
| operation, | |
| }) | |
| } | |
| /// Parse an `ALTER SCHEMA` statement. | |
| /// | |
| /// Supports operations such as setting options, renaming, adding/dropping replicas, and changing owner. | |
| pub fn parse_alter_schema(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keywords(&[Keyword::ALTER, Keyword::SCHEMA])?; | |
| let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| let operation = if self.parse_keywords(&[Keyword::SET, Keyword::OPTIONS]) { | |
| self.prev_token(); | |
| let options = self.parse_options(Keyword::OPTIONS)?; | |
| AlterSchemaOperation::SetOptionsParens { options } | |
| } else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT, Keyword::COLLATE]) { | |
| let collate = self.parse_expr()?; | |
| AlterSchemaOperation::SetDefaultCollate { collate } | |
| } else if self.parse_keywords(&[Keyword::ADD, Keyword::REPLICA]) { | |
| let replica = self.parse_identifier()?; | |
| let options = if self.peek_keyword(Keyword::OPTIONS) { | |
| Some(self.parse_options(Keyword::OPTIONS)?) | |
| } else { | |
| None | |
| }; | |
| AlterSchemaOperation::AddReplica { replica, options } | |
| } else if self.parse_keywords(&[Keyword::DROP, Keyword::REPLICA]) { | |
| let replica = self.parse_identifier()?; | |
| AlterSchemaOperation::DropReplica { replica } | |
| } else if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) { | |
| let new_name = self.parse_object_name(false)?; | |
| AlterSchemaOperation::Rename { name: new_name } | |
| } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) { | |
| let owner = self.parse_owner()?; | |
| AlterSchemaOperation::OwnerTo { owner } | |
| } else { | |
| return self.expected_ref("ALTER SCHEMA operation", self.peek_token_ref()); | |
| }; | |
| Ok(Statement::AlterSchema(AlterSchema { | |
| name, | |
| if_exists, | |
| operations: vec![operation], | |
| })) | |
| } | |
| /// Parse a `CALL procedure_name(arg1, arg2, ...)` | |
| /// or `CALL procedure_name` statement | |
| pub fn parse_call(&mut self) -> Result<Statement, ParserError> { | |
| let object_name = self.parse_object_name(false)?; | |
| if self.peek_token_ref().token == Token::LParen { | |
| match self.parse_function(object_name)? { | |
| Expr::Function(f) => Ok(Statement::Call(f)), | |
| other => parser_err!( | |
| format!("Expected a simple procedure call but found: {other}"), | |
| self.peek_token_ref().span.start | |
| ), | |
| } | |
| } else { | |
| Ok(Statement::Call(Function { | |
| name: object_name, | |
| uses_odbc_syntax: false, | |
| parameters: FunctionArguments::None, | |
| args: FunctionArguments::None, | |
| over: None, | |
| filter: None, | |
| null_treatment: None, | |
| within_group: vec![], | |
| })) | |
| } | |
| } | |
| /// Parse a copy statement | |
| pub fn parse_copy(&mut self) -> Result<Statement, ParserError> { | |
| let source; | |
| if self.consume_token(&Token::LParen) { | |
| source = CopySource::Query(self.parse_query()?); | |
| self.expect_token(&Token::RParen)?; | |
| } else { | |
| let table_name = self.parse_object_name(false)?; | |
| let columns = self.parse_parenthesized_column_list(Optional, false)?; | |
| source = CopySource::Table { | |
| table_name, | |
| columns, | |
| }; | |
| } | |
| let to = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::TO]) { | |
| Some(Keyword::FROM) => false, | |
| Some(Keyword::TO) => true, | |
| _ => self.expected_ref("FROM or TO", self.peek_token_ref())?, | |
| }; | |
| if !to { | |
| // Use a separate if statement to prevent Rust compiler from complaining about | |
| // "if statement in this position is unstable: https://github.com/rust-lang/rust/issues/53667" | |
| if let CopySource::Query(_) = source { | |
| return Err(ParserError::ParserError( | |
| "COPY ... FROM does not support query as a source".to_string(), | |
| )); | |
| } | |
| } | |
| let target = if self.parse_keyword(Keyword::STDIN) { | |
| CopyTarget::Stdin | |
| } else if self.parse_keyword(Keyword::STDOUT) { | |
| CopyTarget::Stdout | |
| } else if self.parse_keyword(Keyword::PROGRAM) { | |
| CopyTarget::Program { | |
| command: self.parse_literal_string()?, | |
| } | |
| } else { | |
| CopyTarget::File { | |
| filename: self.parse_literal_string()?, | |
| } | |
| }; | |
| let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] | |
| let mut options = vec![]; | |
| if self.consume_token(&Token::LParen) { | |
| options = self.parse_comma_separated(Parser::parse_copy_option)?; | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| let mut legacy_options = vec![]; | |
| while let Some(opt) = self.maybe_parse(|parser| parser.parse_copy_legacy_option())? { | |
| legacy_options.push(opt); | |
| } | |
| let values = | |
| if matches!(target, CopyTarget::Stdin) && self.peek_token_ref().token != Token::EOF { | |
| self.expect_token(&Token::SemiColon)?; | |
| self.parse_tsv() | |
| } else { | |
| vec![] | |
| }; | |
| Ok(Statement::Copy { | |
| source, | |
| to, | |
| target, | |
| options, | |
| legacy_options, | |
| values, | |
| }) | |
| } | |
| /// Parse [Statement::Open] | |
| fn parse_open(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword(Keyword::OPEN)?; | |
| Ok(Statement::Open(OpenStatement { | |
| cursor_name: self.parse_identifier()?, | |
| })) | |
| } | |
| /// Parse a `CLOSE` cursor statement. | |
| pub fn parse_close(&mut self) -> Result<Statement, ParserError> { | |
| let cursor = if self.parse_keyword(Keyword::ALL) { | |
| CloseCursor::All | |
| } else { | |
| let name = self.parse_identifier()?; | |
| CloseCursor::Specific { name } | |
| }; | |
| Ok(Statement::Close { cursor }) | |
| } | |
| fn parse_copy_option(&mut self) -> Result<CopyOption, ParserError> { | |
| let ret = match self.parse_one_of_keywords(&[ | |
| Keyword::FORMAT, | |
| Keyword::FREEZE, | |
| Keyword::DELIMITER, | |
| Keyword::NULL, | |
| Keyword::HEADER, | |
| Keyword::QUOTE, | |
| Keyword::ESCAPE, | |
| Keyword::FORCE_QUOTE, | |
| Keyword::FORCE_NOT_NULL, | |
| Keyword::FORCE_NULL, | |
| Keyword::ENCODING, | |
| ]) { | |
| Some(Keyword::FORMAT) => CopyOption::Format(self.parse_identifier()?), | |
| Some(Keyword::FREEZE) => CopyOption::Freeze(!matches!( | |
| self.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]), | |
| Some(Keyword::FALSE) | |
| )), | |
| Some(Keyword::DELIMITER) => CopyOption::Delimiter(self.parse_literal_char()?), | |
| Some(Keyword::NULL) => CopyOption::Null(self.parse_literal_string()?), | |
| Some(Keyword::HEADER) => CopyOption::Header(!matches!( | |
| self.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]), | |
| Some(Keyword::FALSE) | |
| )), | |
| Some(Keyword::QUOTE) => CopyOption::Quote(self.parse_literal_char()?), | |
| Some(Keyword::ESCAPE) => CopyOption::Escape(self.parse_literal_char()?), | |
| Some(Keyword::FORCE_QUOTE) => { | |
| CopyOption::ForceQuote(self.parse_parenthesized_column_list(Mandatory, false)?) | |
| } | |
| Some(Keyword::FORCE_NOT_NULL) => { | |
| CopyOption::ForceNotNull(self.parse_parenthesized_column_list(Mandatory, false)?) | |
| } | |
| Some(Keyword::FORCE_NULL) => { | |
| CopyOption::ForceNull(self.parse_parenthesized_column_list(Mandatory, false)?) | |
| } | |
| Some(Keyword::ENCODING) => CopyOption::Encoding(self.parse_literal_string()?), | |
| _ => self.expected_ref("option", self.peek_token_ref())?, | |
| }; | |
| Ok(ret) | |
| } | |
| fn parse_copy_legacy_option(&mut self) -> Result<CopyLegacyOption, ParserError> { | |
| // FORMAT \[ AS \] is optional | |
| if self.parse_keyword(Keyword::FORMAT) { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| } | |
| let ret = match self.parse_one_of_keywords(&[ | |
| Keyword::ACCEPTANYDATE, | |
| Keyword::ACCEPTINVCHARS, | |
| Keyword::ADDQUOTES, | |
| Keyword::ALLOWOVERWRITE, | |
| Keyword::BINARY, | |
| Keyword::BLANKSASNULL, | |
| Keyword::BZIP2, | |
| Keyword::CLEANPATH, | |
| Keyword::COMPUPDATE, | |
| Keyword::CREDENTIALS, | |
| Keyword::CSV, | |
| Keyword::DATEFORMAT, | |
| Keyword::DELIMITER, | |
| Keyword::EMPTYASNULL, | |
| Keyword::ENCRYPTED, | |
| Keyword::ESCAPE, | |
| Keyword::EXTENSION, | |
| Keyword::FIXEDWIDTH, | |
| Keyword::GZIP, | |
| Keyword::HEADER, | |
| Keyword::IAM_ROLE, | |
| Keyword::IGNOREHEADER, | |
| Keyword::JSON, | |
| Keyword::MANIFEST, | |
| Keyword::MAXFILESIZE, | |
| Keyword::NULL, | |
| Keyword::PARALLEL, | |
| Keyword::PARQUET, | |
| Keyword::PARTITION, | |
| Keyword::REGION, | |
| Keyword::REMOVEQUOTES, | |
| Keyword::ROWGROUPSIZE, | |
| Keyword::STATUPDATE, | |
| Keyword::TIMEFORMAT, | |
| Keyword::TRUNCATECOLUMNS, | |
| Keyword::ZSTD, | |
| ]) { | |
| Some(Keyword::ACCEPTANYDATE) => CopyLegacyOption::AcceptAnyDate, | |
| Some(Keyword::ACCEPTINVCHARS) => { | |
| let _ = self.parse_keyword(Keyword::AS); // [ AS ] | |
| let ch = if matches!(self.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| CopyLegacyOption::AcceptInvChars(ch) | |
| } | |
| Some(Keyword::ADDQUOTES) => CopyLegacyOption::AddQuotes, | |
| Some(Keyword::ALLOWOVERWRITE) => CopyLegacyOption::AllowOverwrite, | |
| Some(Keyword::BINARY) => CopyLegacyOption::Binary, | |
| Some(Keyword::BLANKSASNULL) => CopyLegacyOption::BlankAsNull, | |
| Some(Keyword::BZIP2) => CopyLegacyOption::Bzip2, | |
| Some(Keyword::CLEANPATH) => CopyLegacyOption::CleanPath, | |
| Some(Keyword::COMPUPDATE) => { | |
| let preset = self.parse_keyword(Keyword::PRESET); | |
| let enabled = match self.parse_one_of_keywords(&[ | |
| Keyword::TRUE, | |
| Keyword::FALSE, | |
| Keyword::ON, | |
| Keyword::OFF, | |
| ]) { | |
| Some(Keyword::TRUE) | Some(Keyword::ON) => Some(true), | |
| Some(Keyword::FALSE) | Some(Keyword::OFF) => Some(false), | |
| _ => None, | |
| }; | |
| CopyLegacyOption::CompUpdate { preset, enabled } | |
| } | |
| Some(Keyword::CREDENTIALS) => { | |
| CopyLegacyOption::Credentials(self.parse_literal_string()?) | |
| } | |
| Some(Keyword::CSV) => CopyLegacyOption::Csv({ | |
| let mut opts = vec![]; | |
| while let Some(opt) = | |
| self.maybe_parse(|parser| parser.parse_copy_legacy_csv_option())? | |
| { | |
| opts.push(opt); | |
| } | |
| opts | |
| }), | |
| Some(Keyword::DATEFORMAT) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let fmt = if matches!(self.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| CopyLegacyOption::DateFormat(fmt) | |
| } | |
| Some(Keyword::DELIMITER) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| CopyLegacyOption::Delimiter(self.parse_literal_char()?) | |
| } | |
| Some(Keyword::EMPTYASNULL) => CopyLegacyOption::EmptyAsNull, | |
| Some(Keyword::ENCRYPTED) => { | |
| let auto = self.parse_keyword(Keyword::AUTO); | |
| CopyLegacyOption::Encrypted { auto } | |
| } | |
| Some(Keyword::ESCAPE) => CopyLegacyOption::Escape, | |
| Some(Keyword::EXTENSION) => { | |
| let ext = self.parse_literal_string()?; | |
| CopyLegacyOption::Extension(ext) | |
| } | |
| Some(Keyword::FIXEDWIDTH) => { | |
| let spec = self.parse_literal_string()?; | |
| CopyLegacyOption::FixedWidth(spec) | |
| } | |
| Some(Keyword::GZIP) => CopyLegacyOption::Gzip, | |
| Some(Keyword::HEADER) => CopyLegacyOption::Header, | |
| Some(Keyword::IAM_ROLE) => CopyLegacyOption::IamRole(self.parse_iam_role_kind()?), | |
| Some(Keyword::IGNOREHEADER) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let num_rows = self.parse_literal_uint()?; | |
| CopyLegacyOption::IgnoreHeader(num_rows) | |
| } | |
| Some(Keyword::JSON) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let fmt = if matches!(self.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| CopyLegacyOption::Json(fmt) | |
| } | |
| Some(Keyword::MANIFEST) => { | |
| let verbose = self.parse_keyword(Keyword::VERBOSE); | |
| CopyLegacyOption::Manifest { verbose } | |
| } | |
| Some(Keyword::MAXFILESIZE) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let size = self.parse_number_value()?; | |
| let unit = match self.parse_one_of_keywords(&[Keyword::MB, Keyword::GB]) { | |
| Some(Keyword::MB) => Some(FileSizeUnit::MB), | |
| Some(Keyword::GB) => Some(FileSizeUnit::GB), | |
| _ => None, | |
| }; | |
| CopyLegacyOption::MaxFileSize(FileSize { size, unit }) | |
| } | |
| Some(Keyword::NULL) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| CopyLegacyOption::Null(self.parse_literal_string()?) | |
| } | |
| Some(Keyword::PARALLEL) => { | |
| let enabled = match self.parse_one_of_keywords(&[ | |
| Keyword::TRUE, | |
| Keyword::FALSE, | |
| Keyword::ON, | |
| Keyword::OFF, | |
| ]) { | |
| Some(Keyword::TRUE) | Some(Keyword::ON) => Some(true), | |
| Some(Keyword::FALSE) | Some(Keyword::OFF) => Some(false), | |
| _ => None, | |
| }; | |
| CopyLegacyOption::Parallel(enabled) | |
| } | |
| Some(Keyword::PARQUET) => CopyLegacyOption::Parquet, | |
| Some(Keyword::PARTITION) => { | |
| self.expect_keyword(Keyword::BY)?; | |
| let columns = self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?; | |
| let include = self.parse_keyword(Keyword::INCLUDE); | |
| CopyLegacyOption::PartitionBy(UnloadPartitionBy { columns, include }) | |
| } | |
| Some(Keyword::REGION) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let region = self.parse_literal_string()?; | |
| CopyLegacyOption::Region(region) | |
| } | |
| Some(Keyword::REMOVEQUOTES) => CopyLegacyOption::RemoveQuotes, | |
| Some(Keyword::ROWGROUPSIZE) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let file_size = self.parse_file_size()?; | |
| CopyLegacyOption::RowGroupSize(file_size) | |
| } | |
| Some(Keyword::STATUPDATE) => { | |
| let enabled = match self.parse_one_of_keywords(&[ | |
| Keyword::TRUE, | |
| Keyword::FALSE, | |
| Keyword::ON, | |
| Keyword::OFF, | |
| ]) { | |
| Some(Keyword::TRUE) | Some(Keyword::ON) => Some(true), | |
| Some(Keyword::FALSE) | Some(Keyword::OFF) => Some(false), | |
| _ => None, | |
| }; | |
| CopyLegacyOption::StatUpdate(enabled) | |
| } | |
| Some(Keyword::TIMEFORMAT) => { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| let fmt = if matches!(self.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| CopyLegacyOption::TimeFormat(fmt) | |
| } | |
| Some(Keyword::TRUNCATECOLUMNS) => CopyLegacyOption::TruncateColumns, | |
| Some(Keyword::ZSTD) => CopyLegacyOption::Zstd, | |
| _ => self.expected_ref("option", self.peek_token_ref())?, | |
| }; | |
| Ok(ret) | |
| } | |
| fn parse_file_size(&mut self) -> Result<FileSize, ParserError> { | |
| let size = self.parse_number_value()?; | |
| let unit = self.maybe_parse_file_size_unit(); | |
| Ok(FileSize { size, unit }) | |
| } | |
| fn maybe_parse_file_size_unit(&mut self) -> Option<FileSizeUnit> { | |
| match self.parse_one_of_keywords(&[Keyword::MB, Keyword::GB]) { | |
| Some(Keyword::MB) => Some(FileSizeUnit::MB), | |
| Some(Keyword::GB) => Some(FileSizeUnit::GB), | |
| _ => None, | |
| } | |
| } | |
| fn parse_iam_role_kind(&mut self) -> Result<IamRoleKind, ParserError> { | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| Ok(IamRoleKind::Default) | |
| } else { | |
| let arn = self.parse_literal_string()?; | |
| Ok(IamRoleKind::Arn(arn)) | |
| } | |
| } | |
| fn parse_copy_legacy_csv_option(&mut self) -> Result<CopyLegacyCsvOption, ParserError> { | |
| let ret = match self.parse_one_of_keywords(&[ | |
| Keyword::HEADER, | |
| Keyword::QUOTE, | |
| Keyword::ESCAPE, | |
| Keyword::FORCE, | |
| ]) { | |
| Some(Keyword::HEADER) => CopyLegacyCsvOption::Header, | |
| Some(Keyword::QUOTE) => { | |
| let _ = self.parse_keyword(Keyword::AS); // [ AS ] | |
| CopyLegacyCsvOption::Quote(self.parse_literal_char()?) | |
| } | |
| Some(Keyword::ESCAPE) => { | |
| let _ = self.parse_keyword(Keyword::AS); // [ AS ] | |
| CopyLegacyCsvOption::Escape(self.parse_literal_char()?) | |
| } | |
| Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) => { | |
| CopyLegacyCsvOption::ForceNotNull( | |
| self.parse_comma_separated(|p| p.parse_identifier())?, | |
| ) | |
| } | |
| Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::QUOTE]) => { | |
| CopyLegacyCsvOption::ForceQuote( | |
| self.parse_comma_separated(|p| p.parse_identifier())?, | |
| ) | |
| } | |
| _ => self.expected_ref("csv option", self.peek_token_ref())?, | |
| }; | |
| Ok(ret) | |
| } | |
| fn parse_literal_char(&mut self) -> Result<char, ParserError> { | |
| let s = self.parse_literal_string()?; | |
| if s.len() != 1 { | |
| let loc = self | |
| .tokens | |
| .get(self.index - 1) | |
| .map_or(Location { line: 0, column: 0 }, |t| t.span.start); | |
| return parser_err!(format!("Expect a char, found {s:?}"), loc); | |
| } | |
| Ok(s.chars().next().unwrap()) | |
| } | |
| /// Parse a tab separated values in | |
| /// COPY payload | |
| pub fn parse_tsv(&mut self) -> Vec<Option<String>> { | |
| self.parse_tab_value() | |
| } | |
| /// Parse a single tab-separated value row used by `COPY` payload parsing. | |
| pub fn parse_tab_value(&mut self) -> Vec<Option<String>> { | |
| let mut values = vec![]; | |
| let mut content = String::new(); | |
| while let Some(t) = self.next_token_no_skip().map(|t| &t.token) { | |
| match t { | |
| Token::Whitespace(Whitespace::Tab) => { | |
| values.push(Some(core::mem::take(&mut content))); | |
| } | |
| Token::Whitespace(Whitespace::Newline) => { | |
| values.push(Some(core::mem::take(&mut content))); | |
| } | |
| Token::Backslash => { | |
| if self.consume_token(&Token::Period) { | |
| return values; | |
| } | |
| if let Token::Word(w) = self.next_token().token { | |
| if w.value == "N" { | |
| values.push(None); | |
| } | |
| } | |
| } | |
| _ => { | |
| content.push_str(&t.to_string()); | |
| } | |
| } | |
| } | |
| values | |
| } | |
| /// Parse a literal value (numbers, strings, date/time, booleans) | |
| pub fn parse_value(&mut self) -> Result<ValueWithSpan, ParserError> { | |
| let next_token = self.next_token(); | |
| let span = next_token.span; | |
| let ok_value = |value: Value| Ok(value.with_span(span)); | |
| match next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::TRUE if self.dialect.supports_boolean_literals() => { | |
| ok_value(Value::Boolean(true)) | |
| } | |
| Keyword::FALSE if self.dialect.supports_boolean_literals() => { | |
| ok_value(Value::Boolean(false)) | |
| } | |
| Keyword::NULL => ok_value(Value::Null), | |
| Keyword::NoKeyword if w.quote_style.is_some() => match w.quote_style { | |
| Some('"') => ok_value(Value::DoubleQuotedString(w.value)), | |
| Some('\'') => ok_value(Value::SingleQuotedString(w.value)), | |
| _ => self.expected( | |
| "A value?", | |
| TokenWithSpan { | |
| token: Token::Word(w), | |
| span, | |
| }, | |
| )?, | |
| }, | |
| _ => self.expected( | |
| "a concrete value", | |
| TokenWithSpan { | |
| token: Token::Word(w), | |
| span, | |
| }, | |
| ), | |
| }, | |
| // The call to n.parse() returns a bigdecimal when the | |
| // bigdecimal feature is enabled, and is otherwise a no-op | |
| // (i.e., it returns the input string). | |
| Token::Number(n, l) => ok_value(Value::Number(Self::parse(n, span.start)?, l)), | |
| Token::SingleQuotedString(ref s) => ok_value(Value::SingleQuotedString( | |
| self.maybe_concat_string_literal(s.to_string()), | |
| )), | |
| Token::DoubleQuotedString(ref s) => ok_value(Value::DoubleQuotedString( | |
| self.maybe_concat_string_literal(s.to_string()), | |
| )), | |
| Token::TripleSingleQuotedString(ref s) => { | |
| ok_value(Value::TripleSingleQuotedString(s.to_string())) | |
| } | |
| Token::TripleDoubleQuotedString(ref s) => { | |
| ok_value(Value::TripleDoubleQuotedString(s.to_string())) | |
| } | |
| Token::DollarQuotedString(ref s) => ok_value(Value::DollarQuotedString(s.clone())), | |
| Token::SingleQuotedByteStringLiteral(ref s) => { | |
| ok_value(Value::SingleQuotedByteStringLiteral(s.clone())) | |
| } | |
| Token::DoubleQuotedByteStringLiteral(ref s) => { | |
| ok_value(Value::DoubleQuotedByteStringLiteral(s.clone())) | |
| } | |
| Token::TripleSingleQuotedByteStringLiteral(ref s) => { | |
| ok_value(Value::TripleSingleQuotedByteStringLiteral(s.clone())) | |
| } | |
| Token::TripleDoubleQuotedByteStringLiteral(ref s) => { | |
| ok_value(Value::TripleDoubleQuotedByteStringLiteral(s.clone())) | |
| } | |
| Token::SingleQuotedRawStringLiteral(ref s) => { | |
| ok_value(Value::SingleQuotedRawStringLiteral(s.clone())) | |
| } | |
| Token::DoubleQuotedRawStringLiteral(ref s) => { | |
| ok_value(Value::DoubleQuotedRawStringLiteral(s.clone())) | |
| } | |
| Token::TripleSingleQuotedRawStringLiteral(ref s) => { | |
| ok_value(Value::TripleSingleQuotedRawStringLiteral(s.clone())) | |
| } | |
| Token::TripleDoubleQuotedRawStringLiteral(ref s) => { | |
| ok_value(Value::TripleDoubleQuotedRawStringLiteral(s.clone())) | |
| } | |
| Token::NationalStringLiteral(ref s) => { | |
| ok_value(Value::NationalStringLiteral(s.to_string())) | |
| } | |
| Token::QuoteDelimitedStringLiteral(v) => { | |
| ok_value(Value::QuoteDelimitedStringLiteral(v)) | |
| } | |
| Token::NationalQuoteDelimitedStringLiteral(v) => { | |
| ok_value(Value::NationalQuoteDelimitedStringLiteral(v)) | |
| } | |
| Token::EscapedStringLiteral(ref s) => { | |
| ok_value(Value::EscapedStringLiteral(s.to_string())) | |
| } | |
| Token::UnicodeStringLiteral(ref s) => { | |
| ok_value(Value::UnicodeStringLiteral(s.to_string())) | |
| } | |
| Token::HexStringLiteral(ref s) => ok_value(Value::HexStringLiteral(s.to_string())), | |
| Token::Placeholder(ref s) => ok_value(Value::Placeholder(s.to_string())), | |
| tok @ Token::Colon | tok @ Token::AtSign => { | |
| // 1. Not calling self.parse_identifier(false)? | |
| // because only in placeholder we want to check | |
| // numbers as idfentifies. This because snowflake | |
| // allows numbers as placeholders | |
| // 2. Not calling self.next_token() to enforce `tok` | |
| // be followed immediately by a word/number, ie. | |
| // without any whitespace in between | |
| let next_token = self.next_token_no_skip().unwrap_or(&EOF_TOKEN).clone(); | |
| let ident = match next_token.token { | |
| Token::Word(w) if w.quote_style.is_none() => Ok(w.into_ident(next_token.span)), | |
| Token::Number(w, false) => Ok(Ident::with_span(next_token.span, w)), | |
| _ => self.expected("placeholder", next_token), | |
| }?; | |
| Ok(Value::Placeholder(format!("{tok}{}", ident.value)) | |
| .with_span(Span::new(span.start, ident.span.end))) | |
| } | |
| unexpected => self.expected( | |
| "a value", | |
| TokenWithSpan { | |
| token: unexpected, | |
| span, | |
| }, | |
| ), | |
| } | |
| } | |
| fn maybe_concat_string_literal(&mut self, mut str: String) -> String { | |
| if self.dialect.supports_string_literal_concatenation() { | |
| while let Token::SingleQuotedString(ref s) | Token::DoubleQuotedString(ref s) = | |
| self.peek_token_ref().token | |
| { | |
| str.push_str(s); | |
| self.advance_token(); | |
| } | |
| } else if self | |
| .dialect | |
| .supports_string_literal_concatenation_with_newline() | |
| { | |
| // We are iterating over tokens including whitespaces, to identify | |
| // string literals separated by newlines so we can concatenate them. | |
| let mut after_newline = false; | |
| loop { | |
| match self.peek_token_no_skip().token { | |
| Token::Whitespace(Whitespace::Newline) => { | |
| after_newline = true; | |
| self.next_token_no_skip(); | |
| } | |
| Token::Whitespace(_) => { | |
| self.next_token_no_skip(); | |
| } | |
| Token::SingleQuotedString(ref s) | Token::DoubleQuotedString(ref s) | |
| if after_newline => | |
| { | |
| str.push_str(s.clone().as_str()); | |
| self.next_token_no_skip(); | |
| after_newline = false; | |
| } | |
| _ => break, | |
| } | |
| } | |
| } | |
| str | |
| } | |
| /// Parse an unsigned numeric literal | |
| pub fn parse_number_value(&mut self) -> Result<ValueWithSpan, ParserError> { | |
| let value_wrapper = self.parse_value()?; | |
| match &value_wrapper.value { | |
| Value::Number(_, _) => Ok(value_wrapper), | |
| Value::Placeholder(_) => Ok(value_wrapper), | |
| _ => { | |
| self.prev_token(); | |
| self.expected_ref("literal number", self.peek_token_ref()) | |
| } | |
| } | |
| } | |
| /// Parse a numeric literal as an expression. Returns a [`Expr::UnaryOp`] if the number is signed, | |
| /// otherwise returns a [`Expr::Value`] | |
| pub fn parse_number(&mut self) -> Result<Expr, ParserError> { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Plus => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::Plus, | |
| expr: Box::new(Expr::Value(self.parse_number_value()?)), | |
| }), | |
| Token::Minus => Ok(Expr::UnaryOp { | |
| op: UnaryOperator::Minus, | |
| expr: Box::new(Expr::Value(self.parse_number_value()?)), | |
| }), | |
| _ => { | |
| self.prev_token(); | |
| Ok(Expr::Value(self.parse_number_value()?)) | |
| } | |
| } | |
| } | |
| fn parse_introduced_string_expr(&mut self) -> Result<Expr, ParserError> { | |
| let next_token = self.next_token(); | |
| let span = next_token.span; | |
| match next_token.token { | |
| Token::SingleQuotedString(ref s) => Ok(Expr::Value( | |
| Value::SingleQuotedString(s.to_string()).with_span(span), | |
| )), | |
| Token::DoubleQuotedString(ref s) => Ok(Expr::Value( | |
| Value::DoubleQuotedString(s.to_string()).with_span(span), | |
| )), | |
| Token::HexStringLiteral(ref s) => Ok(Expr::Value( | |
| Value::HexStringLiteral(s.to_string()).with_span(span), | |
| )), | |
| unexpected => self.expected( | |
| "a string value", | |
| TokenWithSpan { | |
| token: unexpected, | |
| span, | |
| }, | |
| ), | |
| } | |
| } | |
| /// Parse an unsigned literal integer/long | |
| pub fn parse_literal_uint(&mut self) -> Result<u64, ParserError> { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Number(s, _) => Self::parse::<u64>(s, next_token.span.start), | |
| _ => self.expected("literal int", next_token), | |
| } | |
| } | |
| /// Parse the body of a `CREATE FUNCTION` specified as a string. | |
| /// e.g. `CREATE FUNCTION ... AS $$ body $$`. | |
| fn parse_create_function_body_string(&mut self) -> Result<CreateFunctionBody, ParserError> { | |
| let parse_string_expr = |parser: &mut Parser| -> Result<Expr, ParserError> { | |
| let peek_token = parser.peek_token(); | |
| let span = peek_token.span; | |
| match peek_token.token { | |
| Token::DollarQuotedString(s) if dialect_of!(parser is PostgreSqlDialect | GenericDialect) => | |
| { | |
| parser.next_token(); | |
| Ok(Expr::Value(Value::DollarQuotedString(s).with_span(span))) | |
| } | |
| _ => Ok(Expr::Value( | |
| Value::SingleQuotedString(parser.parse_literal_string()?).with_span(span), | |
| )), | |
| } | |
| }; | |
| Ok(CreateFunctionBody::AsBeforeOptions { | |
| body: parse_string_expr(self)?, | |
| link_symbol: if self.consume_token(&Token::Comma) { | |
| Some(parse_string_expr(self)?) | |
| } else { | |
| None | |
| }, | |
| }) | |
| } | |
| /// Parse a literal string | |
| pub fn parse_literal_string(&mut self) -> Result<String, ParserError> { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(Word { | |
| value, | |
| keyword: Keyword::NoKeyword, | |
| .. | |
| }) => Ok(value), | |
| Token::SingleQuotedString(s) => Ok(s), | |
| Token::DoubleQuotedString(s) => Ok(s), | |
| Token::EscapedStringLiteral(s) if dialect_of!(self is PostgreSqlDialect | GenericDialect) => { | |
| Ok(s) | |
| } | |
| Token::UnicodeStringLiteral(s) => Ok(s), | |
| _ => self.expected("literal string", next_token), | |
| } | |
| } | |
| /// Parse a boolean string | |
| pub(crate) fn parse_boolean_string(&mut self) -> Result<bool, ParserError> { | |
| match self.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]) { | |
| Some(Keyword::TRUE) => Ok(true), | |
| Some(Keyword::FALSE) => Ok(false), | |
| _ => self.expected_ref("TRUE or FALSE", self.peek_token_ref()), | |
| } | |
| } | |
| /// Parse the `IS [NOT] JSON` predicate after `JSON` (and optional `NOT`) was consumed. | |
| fn parse_is_json_predicate(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> { | |
| let kind = match self.parse_one_of_keywords(&[ | |
| Keyword::VALUE, | |
| Keyword::SCALAR, | |
| Keyword::ARRAY, | |
| Keyword::OBJECT, | |
| ]) { | |
| Some(Keyword::VALUE) => Some(JsonPredicateType::Value), | |
| Some(Keyword::SCALAR) => Some(JsonPredicateType::Scalar), | |
| Some(Keyword::ARRAY) => Some(JsonPredicateType::Array), | |
| Some(Keyword::OBJECT) => Some(JsonPredicateType::Object), | |
| _ => None, | |
| }; | |
| let unique_keys = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) { | |
| Some(Keyword::WITH) => { | |
| self.expect_keyword_is(Keyword::UNIQUE)?; | |
| let _ = self.parse_keyword(Keyword::KEYS); | |
| Some(JsonKeyUniqueness::WithUniqueKeys) | |
| } | |
| Some(Keyword::WITHOUT) => { | |
| self.expect_keyword_is(Keyword::UNIQUE)?; | |
| let _ = self.parse_keyword(Keyword::KEYS); | |
| Some(JsonKeyUniqueness::WithoutUniqueKeys) | |
| } | |
| _ => None, | |
| }; | |
| Ok(Expr::IsJson { | |
| expr: Box::new(expr), | |
| kind, | |
| unique_keys, | |
| negated, | |
| }) | |
| } | |
| /// Parse a literal unicode normalization clause | |
| pub fn parse_unicode_is_normalized(&mut self, expr: Expr) -> Result<Expr, ParserError> { | |
| let neg = self.parse_keyword(Keyword::NOT); | |
| let normalized_form = self.maybe_parse(|parser| { | |
| match parser.parse_one_of_keywords(&[ | |
| Keyword::NFC, | |
| Keyword::NFD, | |
| Keyword::NFKC, | |
| Keyword::NFKD, | |
| ]) { | |
| Some(Keyword::NFC) => Ok(NormalizationForm::NFC), | |
| Some(Keyword::NFD) => Ok(NormalizationForm::NFD), | |
| Some(Keyword::NFKC) => Ok(NormalizationForm::NFKC), | |
| Some(Keyword::NFKD) => Ok(NormalizationForm::NFKD), | |
| _ => parser.expected_ref("unicode normalization form", parser.peek_token_ref()), | |
| } | |
| })?; | |
| if self.parse_keyword(Keyword::NORMALIZED) { | |
| return Ok(Expr::IsNormalized { | |
| expr: Box::new(expr), | |
| form: normalized_form, | |
| negated: neg, | |
| }); | |
| } | |
| self.expected_ref("unicode normalization form", self.peek_token_ref()) | |
| } | |
| /// Parse parenthesized enum members, used with `ENUM(...)` type definitions. | |
| pub fn parse_enum_values(&mut self) -> Result<Vec<EnumMember>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let values = self.parse_comma_separated(|parser| { | |
| let name = parser.parse_literal_string()?; | |
| let e = if parser.consume_token(&Token::Eq) { | |
| let value = parser.parse_number()?; | |
| EnumMember::NamedValue(name, value) | |
| } else { | |
| EnumMember::Name(name) | |
| }; | |
| Ok(e) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(values) | |
| } | |
| /// Parse a SQL datatype (in the context of a CREATE TABLE statement for example) | |
| pub fn parse_data_type(&mut self) -> Result<DataType, ParserError> { | |
| let (ty, trailing_bracket) = self.parse_data_type_helper()?; | |
| if trailing_bracket.0 { | |
| return parser_err!( | |
| format!("unmatched > after parsing data type {ty}"), | |
| self.peek_token_ref() | |
| ); | |
| } | |
| Ok(ty) | |
| } | |
| #[cfg_attr(feature = "recursive-protection", recursive::recursive)] | |
| fn parse_data_type_helper( | |
| &mut self, | |
| ) -> Result<(DataType, MatchedTrailingBracket), ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| let dialect = self.dialect; | |
| self.advance_token(); | |
| let next_token = self.get_current_token(); | |
| let next_token_index = self.get_current_index(); | |
| let mut trailing_bracket: MatchedTrailingBracket = false.into(); | |
| let mut data = match &next_token.token { | |
| Token::Word(w) => match w.keyword { | |
| Keyword::BOOLEAN => Ok(DataType::Boolean), | |
| Keyword::BOOL => Ok(DataType::Bool), | |
| Keyword::FLOAT => { | |
| let precision = self.parse_exact_number_optional_precision_scale()?; | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::FloatUnsigned(precision)) | |
| } else { | |
| Ok(DataType::Float(precision)) | |
| } | |
| } | |
| Keyword::REAL => { | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::RealUnsigned) | |
| } else { | |
| Ok(DataType::Real) | |
| } | |
| } | |
| Keyword::FLOAT4 => Ok(DataType::Float4), | |
| Keyword::FLOAT32 => Ok(DataType::Float32), | |
| Keyword::FLOAT64 => Ok(DataType::Float64), | |
| Keyword::FLOAT8 => Ok(DataType::Float8), | |
| Keyword::DOUBLE => { | |
| if self.parse_keyword(Keyword::PRECISION) { | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::DoublePrecisionUnsigned) | |
| } else { | |
| Ok(DataType::DoublePrecision) | |
| } | |
| } else { | |
| let precision = self.parse_exact_number_optional_precision_scale()?; | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::DoubleUnsigned(precision)) | |
| } else { | |
| Ok(DataType::Double(precision)) | |
| } | |
| } | |
| } | |
| Keyword::TINYINT => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::TinyIntUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::TinyInt(optional_precision?)) | |
| } | |
| } | |
| Keyword::INT2 => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::Int2Unsigned(optional_precision?)) | |
| } else { | |
| Ok(DataType::Int2(optional_precision?)) | |
| } | |
| } | |
| Keyword::SMALLINT => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::SmallIntUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::SmallInt(optional_precision?)) | |
| } | |
| } | |
| Keyword::MEDIUMINT => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::MediumIntUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::MediumInt(optional_precision?)) | |
| } | |
| } | |
| Keyword::INT => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::IntUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::Int(optional_precision?)) | |
| } | |
| } | |
| Keyword::INT4 => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::Int4Unsigned(optional_precision?)) | |
| } else { | |
| Ok(DataType::Int4(optional_precision?)) | |
| } | |
| } | |
| Keyword::INT8 => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::Int8Unsigned(optional_precision?)) | |
| } else { | |
| Ok(DataType::Int8(optional_precision?)) | |
| } | |
| } | |
| Keyword::INT16 => Ok(DataType::Int16), | |
| Keyword::INT32 => Ok(DataType::Int32), | |
| Keyword::INT64 => Ok(DataType::Int64), | |
| Keyword::INT128 => Ok(DataType::Int128), | |
| Keyword::INT256 => Ok(DataType::Int256), | |
| Keyword::INTEGER => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::IntegerUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::Integer(optional_precision?)) | |
| } | |
| } | |
| Keyword::BIGINT => { | |
| let optional_precision = self.parse_optional_precision(); | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::BigIntUnsigned(optional_precision?)) | |
| } else { | |
| if dialect.supports_data_type_signed_suffix() { | |
| let _ = self.parse_keyword(Keyword::SIGNED); | |
| } | |
| Ok(DataType::BigInt(optional_precision?)) | |
| } | |
| } | |
| Keyword::HUGEINT => Ok(DataType::HugeInt), | |
| Keyword::UBIGINT => Ok(DataType::UBigInt), | |
| Keyword::UHUGEINT => Ok(DataType::UHugeInt), | |
| Keyword::USMALLINT => Ok(DataType::USmallInt), | |
| Keyword::UTINYINT => Ok(DataType::UTinyInt), | |
| Keyword::UINT8 => Ok(DataType::UInt8), | |
| Keyword::UINT16 => Ok(DataType::UInt16), | |
| Keyword::UINT32 => Ok(DataType::UInt32), | |
| Keyword::UINT64 => Ok(DataType::UInt64), | |
| Keyword::UINT128 => Ok(DataType::UInt128), | |
| Keyword::UINT256 => Ok(DataType::UInt256), | |
| Keyword::VARCHAR => Ok(DataType::Varchar(self.parse_optional_character_length()?)), | |
| Keyword::NVARCHAR => { | |
| Ok(DataType::Nvarchar(self.parse_optional_character_length()?)) | |
| } | |
| Keyword::CHARACTER => { | |
| if self.parse_keyword(Keyword::VARYING) { | |
| Ok(DataType::CharacterVarying( | |
| self.parse_optional_character_length()?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::LARGE, Keyword::OBJECT]) { | |
| Ok(DataType::CharacterLargeObject( | |
| self.parse_optional_precision()?, | |
| )) | |
| } else { | |
| Ok(DataType::Character(self.parse_optional_character_length()?)) | |
| } | |
| } | |
| Keyword::CHAR => { | |
| if self.parse_keyword(Keyword::VARYING) { | |
| Ok(DataType::CharVarying( | |
| self.parse_optional_character_length()?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::LARGE, Keyword::OBJECT]) { | |
| Ok(DataType::CharLargeObject(self.parse_optional_precision()?)) | |
| } else { | |
| Ok(DataType::Char(self.parse_optional_character_length()?)) | |
| } | |
| } | |
| Keyword::CLOB => Ok(DataType::Clob(self.parse_optional_precision()?)), | |
| Keyword::BINARY => Ok(DataType::Binary(self.parse_optional_precision()?)), | |
| Keyword::VARBINARY => Ok(DataType::Varbinary(self.parse_optional_binary_length()?)), | |
| Keyword::BLOB => Ok(DataType::Blob(self.parse_optional_precision()?)), | |
| Keyword::TINYBLOB => Ok(DataType::TinyBlob), | |
| Keyword::MEDIUMBLOB => Ok(DataType::MediumBlob), | |
| Keyword::LONGBLOB => Ok(DataType::LongBlob), | |
| Keyword::LONG if self.dialect.supports_long_type_as_bigint() => { | |
| Ok(DataType::BigInt(None)) | |
| } | |
| Keyword::BYTES => Ok(DataType::Bytes(self.parse_optional_precision()?)), | |
| Keyword::BIT => { | |
| if self.parse_keyword(Keyword::VARYING) { | |
| Ok(DataType::BitVarying(self.parse_optional_precision()?)) | |
| } else { | |
| Ok(DataType::Bit(self.parse_optional_precision()?)) | |
| } | |
| } | |
| Keyword::VARBIT => Ok(DataType::VarBit(self.parse_optional_precision()?)), | |
| Keyword::UUID => Ok(DataType::Uuid), | |
| Keyword::DATE => Ok(DataType::Date), | |
| Keyword::DATE32 => Ok(DataType::Date32), | |
| Keyword::DATETIME => Ok(DataType::Datetime(self.parse_optional_precision()?)), | |
| Keyword::DATETIME64 => { | |
| self.prev_token(); | |
| let (precision, time_zone) = self.parse_datetime_64()?; | |
| Ok(DataType::Datetime64(precision, time_zone)) | |
| } | |
| Keyword::TIMESTAMP => { | |
| let precision = self.parse_optional_precision()?; | |
| let tz = if self.parse_keyword(Keyword::WITH) { | |
| self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; | |
| TimezoneInfo::WithTimeZone | |
| } else if self.parse_keyword(Keyword::WITHOUT) { | |
| self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; | |
| TimezoneInfo::WithoutTimeZone | |
| } else { | |
| TimezoneInfo::None | |
| }; | |
| Ok(DataType::Timestamp(precision, tz)) | |
| } | |
| Keyword::TIMESTAMPTZ => Ok(DataType::Timestamp( | |
| self.parse_optional_precision()?, | |
| TimezoneInfo::Tz, | |
| )), | |
| Keyword::TIMESTAMP_NTZ => { | |
| Ok(DataType::TimestampNtz(self.parse_optional_precision()?)) | |
| } | |
| Keyword::TIME => { | |
| let precision = self.parse_optional_precision()?; | |
| let tz = if self.parse_keyword(Keyword::WITH) { | |
| self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; | |
| TimezoneInfo::WithTimeZone | |
| } else if self.parse_keyword(Keyword::WITHOUT) { | |
| self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; | |
| TimezoneInfo::WithoutTimeZone | |
| } else { | |
| TimezoneInfo::None | |
| }; | |
| Ok(DataType::Time(precision, tz)) | |
| } | |
| Keyword::TIMETZ => Ok(DataType::Time( | |
| self.parse_optional_precision()?, | |
| TimezoneInfo::Tz, | |
| )), | |
| Keyword::INTERVAL => { | |
| if self.dialect.supports_interval_options() { | |
| let fields = self.maybe_parse_optional_interval_fields()?; | |
| let precision = self.parse_optional_precision()?; | |
| Ok(DataType::Interval { fields, precision }) | |
| } else { | |
| Ok(DataType::Interval { | |
| fields: None, | |
| precision: None, | |
| }) | |
| } | |
| } | |
| Keyword::JSON => Ok(DataType::JSON), | |
| Keyword::JSONB => Ok(DataType::JSONB), | |
| Keyword::REGCLASS => Ok(DataType::Regclass), | |
| Keyword::STRING => Ok(DataType::String(self.parse_optional_precision()?)), | |
| Keyword::FIXEDSTRING => { | |
| self.expect_token(&Token::LParen)?; | |
| let character_length = self.parse_literal_uint()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(DataType::FixedString(character_length)) | |
| } | |
| Keyword::TEXT => { | |
| if let Some(modifiers) = self.parse_optional_type_modifiers()? { | |
| Ok(DataType::Custom( | |
| ObjectName::from(vec![Ident::new("TEXT")]), | |
| modifiers, | |
| )) | |
| } else { | |
| Ok(DataType::Text) | |
| } | |
| } | |
| Keyword::TINYTEXT => Ok(DataType::TinyText), | |
| Keyword::MEDIUMTEXT => Ok(DataType::MediumText), | |
| Keyword::LONGTEXT => Ok(DataType::LongText), | |
| Keyword::BYTEA => Ok(DataType::Bytea), | |
| Keyword::NUMERIC => Ok(DataType::Numeric( | |
| self.parse_exact_number_optional_precision_scale()?, | |
| )), | |
| Keyword::DECIMAL => { | |
| let precision = self.parse_exact_number_optional_precision_scale()?; | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::DecimalUnsigned(precision)) | |
| } else { | |
| Ok(DataType::Decimal(precision)) | |
| } | |
| } | |
| Keyword::DEC => { | |
| let precision = self.parse_exact_number_optional_precision_scale()?; | |
| if self.parse_keyword(Keyword::UNSIGNED) { | |
| Ok(DataType::DecUnsigned(precision)) | |
| } else { | |
| Ok(DataType::Dec(precision)) | |
| } | |
| } | |
| Keyword::BIGNUMERIC => Ok(DataType::BigNumeric( | |
| self.parse_exact_number_optional_precision_scale()?, | |
| )), | |
| Keyword::BIGDECIMAL => Ok(DataType::BigDecimal( | |
| self.parse_exact_number_optional_precision_scale()?, | |
| )), | |
| Keyword::ENUM => Ok(DataType::Enum(self.parse_enum_values()?, None)), | |
| Keyword::ENUM8 => Ok(DataType::Enum(self.parse_enum_values()?, Some(8))), | |
| Keyword::ENUM16 => Ok(DataType::Enum(self.parse_enum_values()?, Some(16))), | |
| Keyword::SET => Ok(DataType::Set(self.parse_string_values()?)), | |
| Keyword::ARRAY => { | |
| if self.dialect.supports_array_typedef_with_parentheses() { | |
| if self.peek_token_ref().token == Token::LParen { | |
| self.expect_token(&Token::LParen)?; | |
| let internal_type = self.parse_data_type()?; | |
| let not_null = self.dialect.supports_array_element_not_null() | |
| && self.parse_keywords(&[Keyword::NOT, Keyword::NULL]); | |
| self.expect_token(&Token::RParen)?; | |
| if not_null { | |
| Ok(DataType::Array(ArrayElemTypeDef::ParenthesisNotNull( | |
| Box::new(internal_type), | |
| ))) | |
| } else { | |
| Ok(DataType::Array(ArrayElemTypeDef::Parenthesis(Box::new( | |
| internal_type, | |
| )))) | |
| } | |
| } else if self.dialect.supports_array_typedef_without_element_type() { | |
| Ok(DataType::Array(ArrayElemTypeDef::None)) | |
| } else { | |
| self.expected("(", self.peek_token()) | |
| } | |
| } else if self.dialect.supports_array_typedef_without_element_type() { | |
| Ok(DataType::Array(ArrayElemTypeDef::None)) | |
| } else { | |
| self.expect_token(&Token::Lt)?; | |
| let (inside_type, _trailing_bracket) = | |
| self.parse_data_type_with_optional_collation()?; | |
| trailing_bracket = self.expect_closing_angle_bracket(_trailing_bracket)?; | |
| Ok(DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( | |
| inside_type, | |
| )))) | |
| } | |
| } | |
| Keyword::OBJECT if self.peek_token_ref().token == Token::LParen => { | |
| if let Some(fields) = | |
| self.maybe_parse(|parser| parser.parse_structured_object_type_def())? | |
| { | |
| Ok(DataType::Object(fields)) | |
| } else { | |
| self.prev_token(); | |
| let type_name = self.parse_object_name(false)?; | |
| let modifiers = self.parse_optional_type_modifiers()?.unwrap_or_default(); | |
| Ok(DataType::Custom(type_name, modifiers)) | |
| } | |
| } | |
| Keyword::STRUCT if dialect_is!(dialect is DuckDbDialect) => { | |
| self.prev_token(); | |
| let field_defs = self.parse_duckdb_struct_type_def()?; | |
| Ok(DataType::Struct(field_defs, StructBracketKind::Parentheses)) | |
| } | |
| Keyword::STRUCT if self.dialect.supports_struct_literal() => { | |
| self.prev_token(); | |
| let (field_defs, _trailing_bracket) = | |
| self.parse_struct_type_def(Self::parse_struct_field_def)?; | |
| trailing_bracket = _trailing_bracket; | |
| Ok(DataType::Struct( | |
| field_defs, | |
| StructBracketKind::AngleBrackets, | |
| )) | |
| } | |
| Keyword::UNION if dialect_is!(dialect is DuckDbDialect | GenericDialect) => { | |
| self.prev_token(); | |
| let fields = self.parse_union_type_def()?; | |
| Ok(DataType::Union(fields)) | |
| } | |
| Keyword::NULLABLE if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { | |
| Ok(self.parse_sub_type(DataType::Nullable)?) | |
| } | |
| Keyword::LOWCARDINALITY if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { | |
| Ok(self.parse_sub_type(DataType::LowCardinality)?) | |
| } | |
| Keyword::MAP if self.dialect.supports_map_literal_with_angle_brackets() => { | |
| self.expect_token(&Token::Lt)?; | |
| let (key_data_type, key_trailing_bracket) = | |
| self.parse_data_type_with_optional_collation()?; | |
| if key_trailing_bracket.0 { | |
| return parser_err!( | |
| format!("unmatched > after parsing data type {key_data_type}"), | |
| self.peek_token_ref() | |
| ); | |
| } | |
| self.expect_token(&Token::Comma)?; | |
| let (value_data_type, _trailing_bracket) = | |
| self.parse_data_type_with_optional_collation()?; | |
| trailing_bracket = self.expect_closing_angle_bracket(_trailing_bracket)?; | |
| Ok(DataType::Map( | |
| Box::new(key_data_type), | |
| Box::new(value_data_type), | |
| MapBracketKind::AngleBrackets, | |
| )) | |
| } | |
| Keyword::MAP if self.dialect.supports_map_typedef_with_parentheses() => { | |
| self.prev_token(); | |
| let (key_data_type, value_data_type, value_not_null) = | |
| self.parse_parenthesized_map_type_def()?; | |
| let bracket = if value_not_null { | |
| MapBracketKind::ParenthesesNotNull | |
| } else { | |
| MapBracketKind::Parentheses | |
| }; | |
| Ok(DataType::Map( | |
| Box::new(key_data_type), | |
| Box::new(value_data_type), | |
| bracket, | |
| )) | |
| } | |
| Keyword::NESTED if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { | |
| self.expect_token(&Token::LParen)?; | |
| let field_defs = self.parse_comma_separated(Parser::parse_column_def)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(DataType::Nested(field_defs)) | |
| } | |
| Keyword::TUPLE if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { | |
| self.prev_token(); | |
| let field_defs = self.parse_click_house_tuple_def()?; | |
| Ok(DataType::Tuple(field_defs)) | |
| } | |
| Keyword::TRIGGER => Ok(DataType::Trigger), | |
| Keyword::ANY if self.peek_keyword(Keyword::TYPE) => { | |
| let _ = self.parse_keyword(Keyword::TYPE); | |
| Ok(DataType::AnyType) | |
| } | |
| Keyword::TABLE => { | |
| // an LParen after the TABLE keyword indicates that table columns are being defined | |
| // whereas no LParen indicates an anonymous table expression will be returned | |
| if self.peek_token_ref().token == Token::LParen { | |
| let columns = self.parse_returns_table_columns()?; | |
| Ok(DataType::Table(Some(columns))) | |
| } else { | |
| Ok(DataType::Table(None)) | |
| } | |
| } | |
| Keyword::SIGNED => { | |
| if self.parse_keyword(Keyword::INTEGER) { | |
| Ok(DataType::SignedInteger) | |
| } else { | |
| Ok(DataType::Signed) | |
| } | |
| } | |
| Keyword::UNSIGNED => { | |
| if self.parse_keyword(Keyword::INTEGER) { | |
| Ok(DataType::UnsignedInteger) | |
| } else { | |
| Ok(DataType::Unsigned) | |
| } | |
| } | |
| Keyword::TSVECTOR if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => { | |
| Ok(DataType::TsVector) | |
| } | |
| Keyword::TSQUERY if dialect_is!(dialect is PostgreSqlDialect | GenericDialect) => { | |
| Ok(DataType::TsQuery) | |
| } | |
| _ => { | |
| self.prev_token(); | |
| let type_name = self.parse_object_name(false)?; | |
| if let Some(modifiers) = self.parse_optional_type_modifiers()? { | |
| Ok(DataType::Custom(type_name, modifiers)) | |
| } else { | |
| Ok(DataType::Custom(type_name, vec![])) | |
| } | |
| } | |
| }, | |
| _ => self.expected_at("a data type name", next_token_index), | |
| }?; | |
| // The `>>` that closed this type also closed the enclosing one, so any suffix belongs to that. | |
| if trailing_bracket.0 { | |
| return Ok((data, trailing_bracket)); | |
| } | |
| if self.is_zerofill_allowed(next_token_index) && self.parse_keyword(Keyword::ZEROFILL) { | |
| data = DataType::Zerofill(Box::new(data)); | |
| } | |
| if self.dialect.supports_array_typedef_with_brackets() { | |
| while self.consume_token(&Token::LBracket) { | |
| // Parse optional array data type size | |
| let size = self.maybe_parse(|p| p.parse_literal_uint())?; | |
| self.expect_token(&Token::RBracket)?; | |
| data = DataType::Array(ArrayElemTypeDef::SquareBracket(Box::new(data), size)) | |
| } | |
| } | |
| // Type-qualified array, e.g. `INT ARRAY` or `INT ARRAY[3]`. One-dimensional | |
| // with a single optional size. The `ARRAY` keyword is unambiguous everywhere. | |
| if self.parse_keyword(Keyword::ARRAY) { | |
| let size = if self.consume_token(&Token::LBracket) { | |
| let size = self.maybe_parse(|p| p.parse_literal_uint())?; | |
| self.expect_token(&Token::RBracket)?; | |
| size | |
| } else { | |
| None | |
| }; | |
| data = DataType::Array(ArrayElemTypeDef::Qualified(Box::new(data), size)); | |
| } | |
| Ok((data, trailing_bracket)) | |
| } | |
| fn parse_data_type_with_optional_collation( | |
| &mut self, | |
| ) -> Result<(DataType, MatchedTrailingBracket), ParserError> { | |
| let (mut data_type, trailing_bracket) = self.parse_data_type_helper()?; | |
| if !trailing_bracket.0 | |
| && self.dialect.supports_data_type_collation() | |
| && self.parse_keyword(Keyword::COLLATE) | |
| { | |
| data_type = DataType::Collate(Box::new(data_type), self.parse_object_name(false)?); | |
| } | |
| Ok((data_type, trailing_bracket)) | |
| } | |
| fn is_zerofill_allowed(&self, token_index: usize) -> bool { | |
| if !self.dialect.supports_data_type_zerofill_suffix() { | |
| return false; | |
| } | |
| match &self.token_at(token_index).token { | |
| Token::Word(w) => matches!( | |
| w.keyword, | |
| Keyword::TINYINT | |
| | Keyword::SMALLINT | |
| | Keyword::MEDIUMINT | |
| | Keyword::INT | |
| | Keyword::INTEGER | |
| | Keyword::BIGINT | |
| | Keyword::INT2 | |
| | Keyword::INT4 | |
| | Keyword::INT8 | |
| | Keyword::DECIMAL | |
| | Keyword::DEC | |
| | Keyword::NUMERIC | |
| | Keyword::FLOAT | |
| | Keyword::FLOAT4 | |
| | Keyword::FLOAT8 | |
| | Keyword::DOUBLE | |
| | Keyword::REAL | |
| ), | |
| _ => false, | |
| } | |
| } | |
| fn parse_returns_table_column(&mut self) -> Result<ColumnDef, ParserError> { | |
| self.parse_column_def() | |
| } | |
| fn parse_returns_table_columns(&mut self) -> Result<Vec<ColumnDef>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(Parser::parse_returns_table_column)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(columns) | |
| } | |
| /// Parse a parenthesized, comma-separated list of single-quoted strings. | |
| pub fn parse_string_values(&mut self) -> Result<Vec<String>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let mut values = Vec::new(); | |
| loop { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::SingleQuotedString(value) => values.push(value), | |
| _ => self.expected("a string", next_token)?, | |
| } | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Comma => (), | |
| Token::RParen => break, | |
| _ => self.expected(", or }", next_token)?, | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Strictly parse `identifier AS identifier` | |
| pub fn parse_identifier_with_alias(&mut self) -> Result<IdentWithAlias, ParserError> { | |
| let ident = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let alias = self.parse_identifier()?; | |
| Ok(IdentWithAlias { ident, alias }) | |
| } | |
| /// Parse `identifier [AS] identifier` where the AS keyword is optional | |
| fn parse_identifier_with_optional_alias(&mut self) -> Result<IdentWithAlias, ParserError> { | |
| let ident = self.parse_identifier()?; | |
| let _after_as = self.parse_keyword(Keyword::AS); | |
| let alias = self.parse_identifier()?; | |
| Ok(IdentWithAlias { ident, alias }) | |
| } | |
| /// Parse comma-separated list of parenthesized queries for pipe operators | |
| fn parse_pipe_operator_queries(&mut self) -> Result<Vec<Query>, ParserError> { | |
| self.parse_comma_separated(|parser| { | |
| parser.expect_token(&Token::LParen)?; | |
| let query = parser.parse_query()?; | |
| parser.expect_token(&Token::RParen)?; | |
| Ok(*query) | |
| }) | |
| } | |
| /// Parse set quantifier for pipe operators that require DISTINCT. E.g. INTERSECT and EXCEPT | |
| fn parse_distinct_required_set_quantifier( | |
| &mut self, | |
| operator_name: &str, | |
| ) -> Result<SetQuantifier, ParserError> { | |
| let quantifier = self.parse_set_quantifier(&Some(SetOperator::Intersect)); | |
| match quantifier { | |
| SetQuantifier::Distinct | SetQuantifier::DistinctByName => Ok(quantifier), | |
| _ => Err(ParserError::ParserError(format!( | |
| "{operator_name} pipe operator requires DISTINCT modifier", | |
| ))), | |
| } | |
| } | |
| /// Parse optional identifier alias (with or without AS keyword) | |
| fn parse_identifier_optional_alias(&mut self) -> Result<Option<Ident>, ParserError> { | |
| if self.parse_keyword(Keyword::AS) { | |
| Ok(Some(self.parse_identifier()?)) | |
| } else { | |
| // Check if the next token is an identifier (implicit alias) | |
| self.maybe_parse(|parser| parser.parse_identifier()) | |
| } | |
| } | |
| /// Optionally parses an alias for a select list item | |
| fn maybe_parse_select_item_alias(&mut self) -> Result<Option<Ident>, ParserError> { | |
| fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool { | |
| parser.dialect.is_select_item_alias(explicit, kw, parser) | |
| } | |
| self.parse_optional_alias_inner(None, validator) | |
| } | |
| /// Optionally parses an alias for a table like in `... FROM generate_series(1, 10) AS t (col)`. | |
| /// In this case, the alias is allowed to optionally name the columns in the table, in | |
| /// addition to the table itself. | |
| pub fn maybe_parse_table_alias(&mut self) -> Result<Option<TableAlias>, ParserError> { | |
| fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool { | |
| parser.dialect.is_table_factor_alias(explicit, kw, parser) | |
| } | |
| let explicit = self.peek_keyword(Keyword::AS); | |
| match self.parse_optional_alias_inner(None, validator)? { | |
| Some(name) => { | |
| let columns = self.parse_table_alias_column_defs()?; | |
| let at = if self.dialect.supports_partiql() && self.parse_keyword(Keyword::AT) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Some(TableAlias { | |
| explicit, | |
| name, | |
| columns, | |
| at, | |
| })) | |
| } | |
| None => Ok(None), | |
| } | |
| } | |
| fn parse_table_index_hints(&mut self) -> Result<Vec<TableIndexHints>, ParserError> { | |
| let mut hints = vec![]; | |
| while let Some(hint_type) = | |
| self.parse_one_of_keywords(&[Keyword::USE, Keyword::IGNORE, Keyword::FORCE]) | |
| { | |
| let hint_type = match hint_type { | |
| Keyword::USE => TableIndexHintType::Use, | |
| Keyword::IGNORE => TableIndexHintType::Ignore, | |
| Keyword::FORCE => TableIndexHintType::Force, | |
| _ => { | |
| return self.expected_ref( | |
| "expected to match USE/IGNORE/FORCE keyword", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| }; | |
| let index_type = match self.parse_one_of_keywords(&[Keyword::INDEX, Keyword::KEY]) { | |
| Some(Keyword::INDEX) => TableIndexType::Index, | |
| Some(Keyword::KEY) => TableIndexType::Key, | |
| _ => { | |
| return self | |
| .expected_ref("expected to match INDEX/KEY keyword", self.peek_token_ref()) | |
| } | |
| }; | |
| let for_clause = if self.parse_keyword(Keyword::FOR) { | |
| let clause = if self.parse_keyword(Keyword::JOIN) { | |
| TableIndexHintForClause::Join | |
| } else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| TableIndexHintForClause::OrderBy | |
| } else if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) { | |
| TableIndexHintForClause::GroupBy | |
| } else { | |
| return self.expected_ref( | |
| "expected to match FOR/ORDER BY/GROUP BY table hint in for clause", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| Some(clause) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::LParen)?; | |
| let index_names = if self.peek_token_ref().token != Token::RParen { | |
| self.parse_comma_separated(Parser::parse_identifier)? | |
| } else { | |
| vec![] | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| hints.push(TableIndexHints { | |
| hint_type, | |
| index_type, | |
| for_clause, | |
| index_names, | |
| }); | |
| } | |
| Ok(hints) | |
| } | |
| /// Wrapper for parse_optional_alias_inner, left for backwards-compatibility | |
| /// but new flows should use the context-specific methods such as `maybe_parse_select_item_alias` | |
| /// and `maybe_parse_table_alias`. | |
| pub fn parse_optional_alias( | |
| &mut self, | |
| reserved_kwds: &[Keyword], | |
| ) -> Result<Option<Ident>, ParserError> { | |
| fn validator(_explicit: bool, _kw: &Keyword, _parser: &mut Parser) -> bool { | |
| false | |
| } | |
| self.parse_optional_alias_inner(Some(reserved_kwds), validator) | |
| } | |
| /// Parses an optional alias after a SQL element such as a select list item | |
| /// or a table name. | |
| /// | |
| /// This method accepts an optional list of reserved keywords or a function | |
| /// to call to validate if a keyword should be parsed as an alias, to allow | |
| /// callers to customize the parsing logic based on their context. | |
| fn parse_optional_alias_inner<F>( | |
| &mut self, | |
| reserved_kwds: Option<&[Keyword]>, | |
| validator: F, | |
| ) -> Result<Option<Ident>, ParserError> | |
| where | |
| F: Fn(bool, &Keyword, &mut Parser) -> bool, | |
| { | |
| let after_as = self.parse_keyword(Keyword::AS); | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| // Accepts a keyword as an alias if the AS keyword explicitly indicate an alias or if the | |
| // caller provided a list of reserved keywords and the keyword is not on that list. | |
| Token::Word(w) | |
| if reserved_kwds.is_some() | |
| && (after_as || reserved_kwds.is_some_and(|x| !x.contains(&w.keyword))) => | |
| { | |
| Ok(Some(w.into_ident(next_token.span))) | |
| } | |
| // Accepts a keyword as alias based on the caller's context, such as to what SQL element | |
| // this word is a potential alias of using the validator call-back. This allows for | |
| // dialect-specific logic. | |
| Token::Word(w) if validator(after_as, &w.keyword, self) => { | |
| Ok(Some(w.into_ident(next_token.span))) | |
| } | |
| Token::SingleQuotedString(s) if self.dialect.supports_string_literal_alias() => { | |
| Ok(Some(Ident::with_quote('\'', s))) | |
| } | |
| Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))), | |
| _ => { | |
| if after_as { | |
| return self.expected("an identifier after AS", next_token); | |
| } | |
| self.prev_token(); | |
| Ok(None) // no alias found | |
| } | |
| } | |
| } | |
| /// Parse an optional `GROUP BY` clause, returning `Some(GroupByExpr)` when present. | |
| pub fn parse_optional_group_by(&mut self) -> Result<Option<GroupByExpr>, ParserError> { | |
| if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) { | |
| let expressions = if self.parse_keyword(Keyword::ALL) { | |
| None | |
| } else { | |
| Some(self.parse_comma_separated(Parser::parse_group_by_expr)?) | |
| }; | |
| let mut modifiers = vec![]; | |
| if self.dialect.supports_group_by_with_modifier() { | |
| loop { | |
| if !self.parse_keyword(Keyword::WITH) { | |
| break; | |
| } | |
| let keyword = self.expect_one_of_keywords(&[ | |
| Keyword::ROLLUP, | |
| Keyword::CUBE, | |
| Keyword::TOTALS, | |
| ])?; | |
| modifiers.push(match keyword { | |
| Keyword::ROLLUP => GroupByWithModifier::Rollup, | |
| Keyword::CUBE => GroupByWithModifier::Cube, | |
| Keyword::TOTALS => GroupByWithModifier::Totals, | |
| _ => { | |
| return parser_err!( | |
| "BUG: expected to match GroupBy modifier keyword", | |
| self.peek_token_ref().span.start | |
| ) | |
| } | |
| }); | |
| } | |
| } | |
| if self.parse_keywords(&[Keyword::GROUPING, Keyword::SETS]) { | |
| self.expect_token(&Token::LParen)?; | |
| let result = self.parse_comma_separated(|p| { | |
| if p.peek_token_ref().token == Token::LParen { | |
| p.parse_tuple(true, true) | |
| } else { | |
| Ok(vec![p.parse_expr()?]) | |
| } | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| modifiers.push(GroupByWithModifier::GroupingSets(Expr::GroupingSets( | |
| result, | |
| ))); | |
| }; | |
| let group_by = match expressions { | |
| None => GroupByExpr::All(modifiers), | |
| Some(exprs) => GroupByExpr::Expressions(exprs, modifiers), | |
| }; | |
| Ok(Some(group_by)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse an optional `ORDER BY` clause, returning `Some(OrderBy)` when present. | |
| pub fn parse_optional_order_by(&mut self) -> Result<Option<OrderBy>, ParserError> { | |
| if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| let order_by = | |
| if self.dialect.supports_order_by_all() && self.parse_keyword(Keyword::ALL) { | |
| let order_by_options = self.parse_order_by_options()?; | |
| OrderBy { | |
| kind: OrderByKind::All(order_by_options), | |
| interpolate: None, | |
| } | |
| } else { | |
| let exprs = self.parse_comma_separated(Parser::parse_order_by_expr)?; | |
| let interpolate = if self.dialect.supports_interpolate() { | |
| self.parse_interpolations()? | |
| } else { | |
| None | |
| }; | |
| OrderBy { | |
| kind: OrderByKind::Expressions(exprs), | |
| interpolate, | |
| } | |
| }; | |
| Ok(Some(order_by)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn parse_optional_limit_clause(&mut self) -> Result<Option<LimitClause>, ParserError> { | |
| let mut offset = if self.parse_keyword(Keyword::OFFSET) { | |
| Some(self.parse_offset()?) | |
| } else { | |
| None | |
| }; | |
| let (limit, limit_by) = if self.parse_keyword(Keyword::LIMIT) { | |
| let expr = self.parse_limit()?; | |
| if self.dialect.supports_limit_comma() | |
| && offset.is_none() | |
| && expr.is_some() // ALL not supported with comma | |
| && self.consume_token(&Token::Comma) | |
| { | |
| let offset = expr.ok_or_else(|| { | |
| ParserError::ParserError( | |
| "Missing offset for LIMIT <offset>, <limit>".to_string(), | |
| ) | |
| })?; | |
| return Ok(Some(LimitClause::OffsetCommaLimit { | |
| offset, | |
| limit: self.parse_expr()?, | |
| })); | |
| } | |
| let limit_by = if self.dialect.supports_limit_by() && self.parse_keyword(Keyword::BY) { | |
| Some(self.parse_comma_separated(Parser::parse_expr)?) | |
| } else { | |
| None | |
| }; | |
| (Some(expr), limit_by) | |
| } else { | |
| (None, None) | |
| }; | |
| if offset.is_none() && limit.is_some() && self.parse_keyword(Keyword::OFFSET) { | |
| offset = Some(self.parse_offset()?); | |
| } | |
| if offset.is_some() || (limit.is_some() && limit != Some(None)) || limit_by.is_some() { | |
| Ok(Some(LimitClause::LimitOffset { | |
| limit: limit.unwrap_or_default(), | |
| offset, | |
| limit_by: limit_by.unwrap_or_default(), | |
| })) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a table object for insertion | |
| /// e.g. `some_database.some_table` or `FUNCTION some_table_func(...)` | |
| pub fn parse_table_object(&mut self) -> Result<TableObject, ParserError> { | |
| if self.dialect.supports_insert_table_function() && self.parse_keyword(Keyword::FUNCTION) { | |
| let fn_name = self.parse_object_name(false)?; | |
| self.parse_function_call(fn_name) | |
| .map(TableObject::TableFunction) | |
| } else if self.dialect.supports_insert_table_query() && self.peek_subquery_or_cte_start() { | |
| self.parse_parenthesized(|p| p.parse_query()) | |
| .map(TableObject::TableQuery) | |
| } else { | |
| self.parse_object_name(false).map(TableObject::TableName) | |
| } | |
| } | |
| /// Parse a possibly qualified, possibly quoted identifier, e.g. | |
| /// `foo` or `myschema."table" | |
| /// | |
| /// The `in_table_clause` parameter indicates whether the object name is a table in a FROM, JOIN, | |
| /// or similar table clause. Currently, this is used only to support unquoted hyphenated identifiers | |
| /// in this context on BigQuery. | |
| pub fn parse_object_name(&mut self, in_table_clause: bool) -> Result<ObjectName, ParserError> { | |
| self.parse_object_name_inner(in_table_clause, false) | |
| } | |
| /// Parse a possibly qualified, possibly quoted identifier, e.g. | |
| /// `foo` or `myschema."table" | |
| /// | |
| /// The `in_table_clause` parameter indicates whether the object name is a table in a FROM, JOIN, | |
| /// or similar table clause. Currently, this is used only to support unquoted hyphenated identifiers | |
| /// in this context on BigQuery. | |
| /// | |
| /// The `allow_wildcards` parameter indicates whether to allow for wildcards in the object name | |
| /// e.g. *, *.*, `foo`.*, or "foo"."bar" | |
| fn parse_object_name_inner( | |
| &mut self, | |
| in_table_clause: bool, | |
| allow_wildcards: bool, | |
| ) -> Result<ObjectName, ParserError> { | |
| let mut parts = vec![]; | |
| if dialect_of!(self is BigQueryDialect) && in_table_clause { | |
| loop { | |
| let (ident, end_with_period) = self.parse_unquoted_hyphenated_identifier()?; | |
| parts.push(ObjectNamePart::Identifier(ident)); | |
| if !self.consume_token(&Token::Period) && !end_with_period { | |
| break; | |
| } | |
| } | |
| } else { | |
| loop { | |
| if allow_wildcards && self.peek_token_ref().token == Token::Mul { | |
| let span = self.next_token().span; | |
| parts.push(ObjectNamePart::Identifier(Ident { | |
| value: Token::Mul.to_string(), | |
| quote_style: None, | |
| span, | |
| })); | |
| } else if dialect_of!(self is BigQueryDialect) && in_table_clause { | |
| let (ident, end_with_period) = self.parse_unquoted_hyphenated_identifier()?; | |
| parts.push(ObjectNamePart::Identifier(ident)); | |
| if !self.consume_token(&Token::Period) && !end_with_period { | |
| break; | |
| } | |
| } else if self.dialect.supports_object_name_double_dot_notation() | |
| && parts.len() == 1 | |
| && matches!(self.peek_token_ref().token, Token::Period) | |
| { | |
| // Empty string here means default schema | |
| parts.push(ObjectNamePart::Identifier(Ident::new(""))); | |
| } else { | |
| let ident = self.parse_identifier()?; | |
| let part = if self | |
| .dialect | |
| .is_identifier_generating_function_name(&ident, &parts) | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let args: Vec<FunctionArg> = | |
| self.parse_comma_separated0(Self::parse_function_args, Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| ObjectNamePart::Function(ObjectNamePartFunction { name: ident, args }) | |
| } else { | |
| ObjectNamePart::Identifier(ident) | |
| }; | |
| parts.push(part); | |
| } | |
| if !self.consume_token(&Token::Period) { | |
| break; | |
| } | |
| } | |
| } | |
| // BigQuery accepts any number of quoted identifiers of a table name. | |
| // https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_identifiers | |
| if dialect_of!(self is BigQueryDialect) | |
| && parts.iter().any(|part| { | |
| part.as_ident() | |
| .is_some_and(|ident| ident.value.contains('.')) | |
| }) | |
| { | |
| parts = parts | |
| .into_iter() | |
| .flat_map(|part| match part.as_ident() { | |
| Some(ident) => ident | |
| .value | |
| .split('.') | |
| .map(|value| { | |
| ObjectNamePart::Identifier(Ident { | |
| value: value.into(), | |
| quote_style: ident.quote_style, | |
| span: ident.span, | |
| }) | |
| }) | |
| .collect::<Vec<_>>(), | |
| None => vec![part], | |
| }) | |
| .collect() | |
| } | |
| Ok(ObjectName(parts)) | |
| } | |
| /// Parse identifiers | |
| pub fn parse_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> { | |
| let mut idents = vec![]; | |
| loop { | |
| let token = self.peek_token_ref(); | |
| match &token.token { | |
| Token::Word(w) => { | |
| idents.push(w.to_ident(token.span)); | |
| } | |
| Token::EOF | Token::Eq | Token::SemiColon | Token::VerticalBarRightAngleBracket => { | |
| break | |
| } | |
| _ => {} | |
| } | |
| self.advance_token(); | |
| } | |
| Ok(idents) | |
| } | |
| /// Parse identifiers of form ident1[.identN]* | |
| /// | |
| /// Similar in functionality to [parse_identifiers], with difference | |
| /// being this function is much more strict about parsing a valid multipart identifier, not | |
| /// allowing extraneous tokens to be parsed, otherwise it fails. | |
| /// | |
| /// For example: | |
| /// | |
| /// ```rust | |
| /// use sqlparser::ast::Ident; | |
| /// use sqlparser::dialect::GenericDialect; | |
| /// use sqlparser::parser::Parser; | |
| /// | |
| /// let dialect = GenericDialect {}; | |
| /// let expected = vec![Ident::new("one"), Ident::new("two")]; | |
| /// | |
| /// // expected usage | |
| /// let sql = "one.two"; | |
| /// let mut parser = Parser::new(&dialect).try_with_sql(sql).unwrap(); | |
| /// let actual = parser.parse_multipart_identifier().unwrap(); | |
| /// assert_eq!(&actual, &expected); | |
| /// | |
| /// // parse_identifiers is more loose on what it allows, parsing successfully | |
| /// let sql = "one + two"; | |
| /// let mut parser = Parser::new(&dialect).try_with_sql(sql).unwrap(); | |
| /// let actual = parser.parse_identifiers().unwrap(); | |
| /// assert_eq!(&actual, &expected); | |
| /// | |
| /// // expected to strictly fail due to + separator | |
| /// let sql = "one + two"; | |
| /// let mut parser = Parser::new(&dialect).try_with_sql(sql).unwrap(); | |
| /// let actual = parser.parse_multipart_identifier().unwrap_err(); | |
| /// assert_eq!( | |
| /// actual.to_string(), | |
| /// "sql parser error: Unexpected token in identifier: +" | |
| /// ); | |
| /// ``` | |
| /// | |
| /// [parse_identifiers]: Parser::parse_identifiers | |
| pub fn parse_multipart_identifier(&mut self) -> Result<Vec<Ident>, ParserError> { | |
| let mut idents = vec![]; | |
| // expecting at least one word for identifier | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => idents.push(w.into_ident(next_token.span)), | |
| Token::EOF => { | |
| return Err(ParserError::ParserError( | |
| "Empty input when parsing identifier".to_string(), | |
| ))? | |
| } | |
| token => { | |
| return Err(ParserError::ParserError(format!( | |
| "Unexpected token in identifier: {token}" | |
| )))? | |
| } | |
| }; | |
| // parse optional next parts if exist | |
| loop { | |
| match self.next_token().token { | |
| // ensure that optional period is succeeded by another identifier | |
| Token::Period => { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => idents.push(w.into_ident(next_token.span)), | |
| Token::EOF => { | |
| return Err(ParserError::ParserError( | |
| "Trailing period in identifier".to_string(), | |
| ))? | |
| } | |
| token => { | |
| return Err(ParserError::ParserError(format!( | |
| "Unexpected token following period in identifier: {token}" | |
| )))? | |
| } | |
| } | |
| } | |
| Token::EOF => break, | |
| token => { | |
| Err(ParserError::ParserError(format!( | |
| "Unexpected token in identifier: {token}" | |
| )))?; | |
| } | |
| } | |
| } | |
| Ok(idents) | |
| } | |
| /// Parse a simple one-word identifier (possibly quoted, possibly a keyword) | |
| pub fn parse_identifier(&mut self) -> Result<Ident, ParserError> { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => Ok(w.into_ident(next_token.span)), | |
| Token::SingleQuotedString(s) => { | |
| Ok(Ident::with_quote_and_span('\'', next_token.span, s)) | |
| } | |
| Token::DoubleQuotedString(s) => { | |
| Ok(Ident::with_quote_and_span('\"', next_token.span, s)) | |
| } | |
| _ => self.expected("identifier", next_token), | |
| } | |
| } | |
| /// On BigQuery, hyphens are permitted in unquoted identifiers inside of a FROM or | |
| /// TABLE clause. | |
| /// | |
| /// The first segment must be an ordinary unquoted identifier, e.g. it must not start | |
| /// with a digit. Subsequent segments are either must either be valid identifiers or | |
| /// integers, e.g. foo-123 is allowed, but foo-123a is not. | |
| /// | |
| /// [BigQuery-lexical](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical) | |
| /// | |
| /// Return a tuple of the identifier and a boolean indicating it ends with a period. | |
| fn parse_unquoted_hyphenated_identifier(&mut self) -> Result<(Ident, bool), ParserError> { | |
| match self.peek_token().token { | |
| Token::Word(w) => { | |
| let quote_style_is_none = w.quote_style.is_none(); | |
| let mut requires_whitespace = false; | |
| let mut ident = w.into_ident(self.next_token().span); | |
| if quote_style_is_none { | |
| while matches!(self.peek_token_no_skip().token, Token::Minus) { | |
| self.next_token(); | |
| ident.value.push('-'); | |
| let token = self | |
| .next_token_no_skip() | |
| .cloned() | |
| .unwrap_or(TokenWithSpan::wrap(Token::EOF)); | |
| requires_whitespace = match token.token { | |
| Token::Word(next_word) if next_word.quote_style.is_none() => { | |
| ident.value.push_str(&next_word.value); | |
| false | |
| } | |
| Token::Number(s, false) => { | |
| // A number token can represent a decimal value ending with a period, e.g., `Number('123.')`. | |
| // However, for an [ObjectName], it is part of a hyphenated identifier, e.g., `foo-123.bar`. | |
| // | |
| // If a number token is followed by a period, it is part of an [ObjectName]. | |
| // Return the identifier with `true` if the number token is followed by a period, indicating that | |
| // parsing should continue for the next part of the hyphenated identifier. | |
| if s.ends_with('.') { | |
| let Some(s) = s.split('.').next().filter(|s| { | |
| !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()) | |
| }) else { | |
| return self.expected( | |
| "continuation of hyphenated identifier", | |
| TokenWithSpan::new(Token::Number(s, false), token.span), | |
| ); | |
| }; | |
| ident.value.push_str(s); | |
| return Ok((ident, true)); | |
| } else { | |
| ident.value.push_str(&s); | |
| } | |
| // If next token is period, then it is part of an ObjectName and we don't expect whitespace | |
| // after the number. | |
| !matches!(self.peek_token_ref().token, Token::Period) | |
| } | |
| _ => { | |
| return self | |
| .expected("continuation of hyphenated identifier", token); | |
| } | |
| } | |
| } | |
| // If the last segment was a number, we must check that it's followed by whitespace, | |
| // otherwise foo-123a will be parsed as `foo-123` with the alias `a`. | |
| if requires_whitespace { | |
| let token = self.next_token(); | |
| if !matches!(token.token, Token::EOF | Token::Whitespace(_)) { | |
| return self | |
| .expected("whitespace following hyphenated identifier", token); | |
| } | |
| } | |
| } | |
| Ok((ident, false)) | |
| } | |
| _ => Ok((self.parse_identifier()?, false)), | |
| } | |
| } | |
| /// Parses a parenthesized, comma-separated list of column definitions within a view. | |
| fn parse_view_columns(&mut self) -> Result<Vec<ViewColumnDef>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| if self.peek_token_ref().token == Token::RParen { | |
| self.next_token(); | |
| Ok(vec![]) | |
| } else { | |
| let cols = self.parse_comma_separated_with_trailing_commas( | |
| Parser::parse_view_column, | |
| self.dialect.supports_column_definition_trailing_commas(), | |
| Self::is_reserved_for_column_alias, | |
| )?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(cols) | |
| } | |
| } else { | |
| Ok(vec![]) | |
| } | |
| } | |
| /// Parses a column definition within a view. | |
| fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let options = self.parse_view_column_options()?; | |
| let data_type = if dialect_of!(self is ClickHouseDialect) { | |
| Some(self.parse_data_type()?) | |
| } else { | |
| None | |
| }; | |
| Ok(ViewColumnDef { | |
| name, | |
| data_type, | |
| options, | |
| }) | |
| } | |
| fn parse_view_column_options(&mut self) -> Result<Option<ColumnOptions>, ParserError> { | |
| let mut options = Vec::new(); | |
| loop { | |
| let option = self.parse_optional_column_option()?; | |
| if let Some(option) = option { | |
| options.push(option); | |
| } else { | |
| break; | |
| } | |
| } | |
| if options.is_empty() { | |
| Ok(None) | |
| } else if self.dialect.supports_space_separated_column_options() { | |
| Ok(Some(ColumnOptions::SpaceSeparated(options))) | |
| } else { | |
| Ok(Some(ColumnOptions::CommaSeparated(options))) | |
| } | |
| } | |
| /// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers. | |
| /// For example: `(col1, "col 2", ...)` | |
| pub fn parse_parenthesized_column_list( | |
| &mut self, | |
| optional: IsOptional, | |
| allow_empty: bool, | |
| ) -> Result<Vec<Ident>, ParserError> { | |
| self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| p.parse_identifier()) | |
| } | |
| /// Parse a parenthesized list of compound identifiers as expressions. | |
| pub fn parse_parenthesized_compound_identifier_list( | |
| &mut self, | |
| optional: IsOptional, | |
| allow_empty: bool, | |
| ) -> Result<Vec<Expr>, ParserError> { | |
| self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| { | |
| Ok(Expr::CompoundIdentifier( | |
| p.parse_period_separated(|p| p.parse_identifier())?, | |
| )) | |
| }) | |
| } | |
| /// Parses a parenthesized comma-separated list of index columns, which can be arbitrary | |
| /// expressions with ordering information (and an opclass in some dialects). | |
| fn parse_parenthesized_index_column_list(&mut self) -> Result<Vec<IndexColumn>, ParserError> { | |
| self.parse_parenthesized_column_list_inner(Mandatory, false, |p| { | |
| p.parse_create_index_expr() | |
| }) | |
| } | |
| /// Parses a parenthesized comma-separated list of qualified, possibly quoted identifiers. | |
| /// For example: `(db1.sc1.tbl1.col1, db1.sc1.tbl1."col 2", ...)` | |
| pub fn parse_parenthesized_qualified_column_list( | |
| &mut self, | |
| optional: IsOptional, | |
| allow_empty: bool, | |
| ) -> Result<Vec<ObjectName>, ParserError> { | |
| self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| { | |
| p.parse_object_name(true) | |
| }) | |
| } | |
| /// Parses a parenthesized comma-separated list of columns using | |
| /// the provided function to parse each element. | |
| fn parse_parenthesized_column_list_inner<F, T>( | |
| &mut self, | |
| optional: IsOptional, | |
| allow_empty: bool, | |
| mut f: F, | |
| ) -> Result<Vec<T>, ParserError> | |
| where | |
| F: FnMut(&mut Parser) -> Result<T, ParserError>, | |
| { | |
| if self.consume_token(&Token::LParen) { | |
| if allow_empty && self.peek_token_ref().token == Token::RParen { | |
| self.next_token(); | |
| Ok(vec![]) | |
| } else { | |
| let cols = self.parse_comma_separated(|p| f(p))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(cols) | |
| } | |
| } else if optional == Optional { | |
| Ok(vec![]) | |
| } else { | |
| self.expected_ref("a list of columns in parentheses", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parses a parenthesized comma-separated list of table alias column definitions. | |
| fn parse_table_alias_column_defs(&mut self) -> Result<Vec<TableAliasColumnDef>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let cols = self.parse_comma_separated(|p| { | |
| let name = p.parse_identifier()?; | |
| let data_type = p.maybe_parse(|p| p.parse_data_type())?; | |
| Ok(TableAliasColumnDef { name, data_type }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(cols) | |
| } else { | |
| Ok(vec![]) | |
| } | |
| } | |
| /// Parse an unsigned precision value enclosed in parentheses, e.g. `(10)`. | |
| pub fn parse_precision(&mut self) -> Result<u64, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let n = self.parse_literal_uint()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(n) | |
| } | |
| /// Parse an optional precision `(n)` and return it as `Some(n)` when present. | |
| pub fn parse_optional_precision(&mut self) -> Result<Option<u64>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let n = self.parse_literal_uint()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(n)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn maybe_parse_optional_interval_fields( | |
| &mut self, | |
| ) -> Result<Option<IntervalFields>, ParserError> { | |
| match self.parse_one_of_keywords(&[ | |
| // Can be followed by `TO` option | |
| Keyword::YEAR, | |
| Keyword::DAY, | |
| Keyword::HOUR, | |
| Keyword::MINUTE, | |
| // No `TO` option | |
| Keyword::MONTH, | |
| Keyword::SECOND, | |
| ]) { | |
| Some(Keyword::YEAR) => { | |
| if self.peek_keyword(Keyword::TO) { | |
| self.expect_keyword(Keyword::TO)?; | |
| self.expect_keyword(Keyword::MONTH)?; | |
| Ok(Some(IntervalFields::YearToMonth)) | |
| } else { | |
| Ok(Some(IntervalFields::Year)) | |
| } | |
| } | |
| Some(Keyword::DAY) => { | |
| if self.peek_keyword(Keyword::TO) { | |
| self.expect_keyword(Keyword::TO)?; | |
| match self.expect_one_of_keywords(&[ | |
| Keyword::HOUR, | |
| Keyword::MINUTE, | |
| Keyword::SECOND, | |
| ])? { | |
| Keyword::HOUR => Ok(Some(IntervalFields::DayToHour)), | |
| Keyword::MINUTE => Ok(Some(IntervalFields::DayToMinute)), | |
| Keyword::SECOND => Ok(Some(IntervalFields::DayToSecond)), | |
| _ => { | |
| self.prev_token(); | |
| self.expected_ref("HOUR, MINUTE, or SECOND", self.peek_token_ref()) | |
| } | |
| } | |
| } else { | |
| Ok(Some(IntervalFields::Day)) | |
| } | |
| } | |
| Some(Keyword::HOUR) => { | |
| if self.peek_keyword(Keyword::TO) { | |
| self.expect_keyword(Keyword::TO)?; | |
| match self.expect_one_of_keywords(&[Keyword::MINUTE, Keyword::SECOND])? { | |
| Keyword::MINUTE => Ok(Some(IntervalFields::HourToMinute)), | |
| Keyword::SECOND => Ok(Some(IntervalFields::HourToSecond)), | |
| _ => { | |
| self.prev_token(); | |
| self.expected_ref("MINUTE or SECOND", self.peek_token_ref()) | |
| } | |
| } | |
| } else { | |
| Ok(Some(IntervalFields::Hour)) | |
| } | |
| } | |
| Some(Keyword::MINUTE) => { | |
| if self.peek_keyword(Keyword::TO) { | |
| self.expect_keyword(Keyword::TO)?; | |
| self.expect_keyword(Keyword::SECOND)?; | |
| Ok(Some(IntervalFields::MinuteToSecond)) | |
| } else { | |
| Ok(Some(IntervalFields::Minute)) | |
| } | |
| } | |
| Some(Keyword::MONTH) => Ok(Some(IntervalFields::Month)), | |
| Some(Keyword::SECOND) => Ok(Some(IntervalFields::Second)), | |
| Some(_) => { | |
| self.prev_token(); | |
| self.expected_ref( | |
| "YEAR, MONTH, DAY, HOUR, MINUTE, or SECOND", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| None => Ok(None), | |
| } | |
| } | |
| /// Parse datetime64 [1] | |
| /// Syntax | |
| /// ```sql | |
| /// DateTime64(precision[, timezone]) | |
| /// ``` | |
| /// | |
| /// [1]: https://clickhouse.com/docs/en/sql-reference/data-types/datetime64 | |
| pub fn parse_datetime_64(&mut self) -> Result<(u64, Option<String>), ParserError> { | |
| self.expect_keyword_is(Keyword::DATETIME64)?; | |
| self.expect_token(&Token::LParen)?; | |
| let precision = self.parse_literal_uint()?; | |
| let time_zone = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok((precision, time_zone)) | |
| } | |
| /// Parse an optional character length specification `(n | MAX [CHARACTERS|OCTETS])`. | |
| pub fn parse_optional_character_length( | |
| &mut self, | |
| ) -> Result<Option<CharacterLength>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let character_length = self.parse_character_length()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(character_length)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse an optional binary length specification like `(n)`. | |
| pub fn parse_optional_binary_length(&mut self) -> Result<Option<BinaryLength>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let binary_length = self.parse_binary_length()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(binary_length)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a character length, handling `MAX` or integer lengths with optional units. | |
| pub fn parse_character_length(&mut self) -> Result<CharacterLength, ParserError> { | |
| if self.parse_keyword(Keyword::MAX) { | |
| return Ok(CharacterLength::Max); | |
| } | |
| let length = self.parse_literal_uint()?; | |
| let unit = if self.parse_keyword(Keyword::CHARACTERS) { | |
| Some(CharLengthUnits::Characters) | |
| } else if self.parse_keyword(Keyword::OCTETS) { | |
| Some(CharLengthUnits::Octets) | |
| } else { | |
| None | |
| }; | |
| Ok(CharacterLength::IntegerLength { length, unit }) | |
| } | |
| /// Parse a binary length specification, returning `BinaryLength`. | |
| pub fn parse_binary_length(&mut self) -> Result<BinaryLength, ParserError> { | |
| if self.parse_keyword(Keyword::MAX) { | |
| return Ok(BinaryLength::Max); | |
| } | |
| let length = self.parse_literal_uint()?; | |
| Ok(BinaryLength::IntegerLength { length }) | |
| } | |
| /// Parse an optional `(precision[, scale])` and return `(Option<precision>, Option<scale>)`. | |
| pub fn parse_optional_precision_scale( | |
| &mut self, | |
| ) -> Result<(Option<u64>, Option<u64>), ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let n = self.parse_literal_uint()?; | |
| let scale = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_literal_uint()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok((Some(n), scale)) | |
| } else { | |
| Ok((None, None)) | |
| } | |
| } | |
| /// Parse exact-number precision/scale info like `(precision[, scale])` for decimal types. | |
| pub fn parse_exact_number_optional_precision_scale( | |
| &mut self, | |
| ) -> Result<ExactNumberInfo, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let precision = self.parse_literal_uint()?; | |
| let scale = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_signed_integer()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| match scale { | |
| None => Ok(ExactNumberInfo::Precision(precision)), | |
| Some(scale) => Ok(ExactNumberInfo::PrecisionAndScale(precision, scale)), | |
| } | |
| } else { | |
| Ok(ExactNumberInfo::None) | |
| } | |
| } | |
| /// Parse an optionally signed integer literal. | |
| fn parse_signed_integer(&mut self) -> Result<i64, ParserError> { | |
| let is_negative = self.consume_token(&Token::Minus); | |
| if !is_negative { | |
| let _ = self.consume_token(&Token::Plus); | |
| } | |
| let current_token = self.peek_token_ref(); | |
| match ¤t_token.token { | |
| Token::Number(s, _) => { | |
| let s = s.clone(); | |
| let span_start = current_token.span.start; | |
| self.advance_token(); | |
| let value = Self::parse::<i64>(s, span_start)?; | |
| Ok(if is_negative { -value } else { value }) | |
| } | |
| _ => self.expected_ref("number", current_token), | |
| } | |
| } | |
| /// Parse optional type modifiers appearing in parentheses e.g. `(UNSIGNED, ZEROFILL)`. | |
| pub fn parse_optional_type_modifiers(&mut self) -> Result<Option<Vec<String>>, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let mut modifiers = Vec::new(); | |
| loop { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => modifiers.push(w.to_string()), | |
| Token::Number(n, _) => modifiers.push(n), | |
| Token::SingleQuotedString(s) => modifiers.push(s), | |
| Token::Comma => { | |
| continue; | |
| } | |
| Token::RParen => { | |
| break; | |
| } | |
| _ => self.expected("type modifiers", next_token)?, | |
| } | |
| } | |
| Ok(Some(modifiers)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn parse_structured_object_type_def(&mut self) -> Result<Vec<ColumnDef>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| if self.consume_token(&Token::RParen) { | |
| return Ok(vec![]); | |
| } | |
| let fields = self.parse_comma_separated(|parser| { | |
| if matches!(parser.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| return parser.expected("an object field identifier", parser.peek_token()); | |
| } | |
| let name = parser.parse_identifier()?; | |
| let data_type = parser.parse_data_type()?; | |
| let options = if parser.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { | |
| vec![ColumnOptionDef { | |
| name: None, | |
| option: ColumnOption::NotNull, | |
| }] | |
| } else { | |
| vec![] | |
| }; | |
| Ok(ColumnDef { | |
| name, | |
| data_type, | |
| options, | |
| }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(fields) | |
| } | |
| /// Parse a parenthesized sub data type | |
| fn parse_sub_type<F>(&mut self, parent_type: F) -> Result<DataType, ParserError> | |
| where | |
| F: FnOnce(Box<DataType>) -> DataType, | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let inside_type = self.parse_data_type()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(parent_type(inside_type.into())) | |
| } | |
| /// Parse a DELETE statement, returning a `Box`ed SetExpr | |
| /// | |
| /// This is used to reduce the size of the stack frames in debug builds | |
| fn parse_delete_setexpr_boxed( | |
| &mut self, | |
| delete_token: TokenWithSpan, | |
| ) -> Result<Box<SetExpr>, ParserError> { | |
| Ok(Box::new(SetExpr::Delete(self.parse_delete(delete_token)?))) | |
| } | |
| /// Parse a `DELETE` statement and return `Statement::Delete`. | |
| pub fn parse_delete(&mut self, delete_token: TokenWithSpan) -> Result<Statement, ParserError> { | |
| let optimizer_hints = self.maybe_parse_optimizer_hints()?; | |
| let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM) { | |
| // `FROM` keyword is optional in BigQuery SQL. | |
| // https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement | |
| if dialect_of!(self is BigQueryDialect | OracleDialect | GenericDialect) { | |
| (vec![], false) | |
| } else if self.dialect.supports_delete_multiple_tables() { | |
| let tables = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| self.expect_keyword_is(Keyword::FROM)?; | |
| (tables, true) | |
| } else { | |
| return self.expected("FROM", self.peek_token()); | |
| } | |
| } else { | |
| (vec![], true) | |
| }; | |
| let from = self.parse_comma_separated(Parser::parse_table_and_joins)?; | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| let output = self.maybe_parse_output_clause()?; | |
| let using = if self.parse_keyword(Keyword::USING) { | |
| Some(self.parse_comma_separated(Parser::parse_table_and_joins)?) | |
| } else { | |
| None | |
| }; | |
| let selection = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let returning = if self.parse_keyword(Keyword::RETURNING) { | |
| Some(self.parse_comma_separated(Parser::parse_select_item)?) | |
| } else { | |
| None | |
| }; | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let limit = if self.parse_keyword(Keyword::LIMIT) { | |
| self.parse_limit()? | |
| } else { | |
| None | |
| }; | |
| let settings = self.parse_settings()?; | |
| Ok(Statement::Delete(Delete { | |
| delete_token: delete_token.into(), | |
| optimizer_hints, | |
| tables, | |
| from: if with_from_keyword { | |
| FromTable::WithFromKeyword(from) | |
| } else { | |
| FromTable::WithoutKeyword(from) | |
| }, | |
| on_cluster, | |
| using, | |
| selection, | |
| returning, | |
| output, | |
| order_by, | |
| limit, | |
| settings, | |
| })) | |
| } | |
| /// Parse a `KILL` statement, optionally specifying `CONNECTION`, `QUERY`, or `MUTATION`. | |
| /// KILL [CONNECTION | QUERY | MUTATION] processlist_id | |
| pub fn parse_kill(&mut self) -> Result<Statement, ParserError> { | |
| let modifier_keyword = | |
| self.parse_one_of_keywords(&[Keyword::CONNECTION, Keyword::QUERY, Keyword::MUTATION]); | |
| let id = self.parse_literal_uint()?; | |
| let modifier = match modifier_keyword { | |
| Some(Keyword::CONNECTION) => Some(KillType::Connection), | |
| Some(Keyword::QUERY) => Some(KillType::Query), | |
| Some(Keyword::MUTATION) => { | |
| if dialect_of!(self is ClickHouseDialect | GenericDialect) { | |
| Some(KillType::Mutation) | |
| } else { | |
| self.expected_ref( | |
| "Unsupported type for KILL, allowed: CONNECTION | QUERY", | |
| self.peek_token_ref(), | |
| )? | |
| } | |
| } | |
| _ => None, | |
| }; | |
| Ok(Statement::Kill { modifier, id }) | |
| } | |
| /// Parse an `EXPLAIN` statement, handling dialect-specific options and modifiers. | |
| pub fn parse_explain( | |
| &mut self, | |
| describe_alias: DescribeAlias, | |
| ) -> Result<Statement, ParserError> { | |
| let mut analyze = false; | |
| let mut verbose = false; | |
| let mut query_plan = false; | |
| let mut estimate = false; | |
| let mut format = None; | |
| let mut options = None; | |
| // Note: DuckDB is compatible with PostgreSQL syntax for this statement, | |
| // although not all features may be implemented. | |
| if describe_alias == DescribeAlias::Explain | |
| && self.dialect.supports_explain_with_utility_options() | |
| && self.peek_token_ref().token == Token::LParen | |
| { | |
| options = Some(self.parse_utility_options()?) | |
| } else if self.parse_keywords(&[Keyword::QUERY, Keyword::PLAN]) { | |
| query_plan = true; | |
| } else if self.parse_keyword(Keyword::ESTIMATE) { | |
| estimate = true; | |
| } else { | |
| analyze = self.parse_keyword(Keyword::ANALYZE); | |
| verbose = self.parse_keyword(Keyword::VERBOSE); | |
| if self.parse_keyword(Keyword::FORMAT) { | |
| format = Some(self.parse_analyze_format_kind()?); | |
| } | |
| } | |
| match self.maybe_parse(|parser| parser.parse_statement())? { | |
| Some(Statement::Explain { .. }) | Some(Statement::ExplainTable { .. }) => Err( | |
| ParserError::ParserError("Explain must be root of the plan".to_string()), | |
| ), | |
| Some(statement) => Ok(Statement::Explain { | |
| describe_alias, | |
| analyze, | |
| verbose, | |
| query_plan, | |
| estimate, | |
| statement: Box::new(statement), | |
| format, | |
| options, | |
| }), | |
| _ => { | |
| let hive_format = | |
| match self.parse_one_of_keywords(&[Keyword::EXTENDED, Keyword::FORMATTED]) { | |
| Some(Keyword::EXTENDED) => Some(HiveDescribeFormat::Extended), | |
| Some(Keyword::FORMATTED) => Some(HiveDescribeFormat::Formatted), | |
| _ => None, | |
| }; | |
| let has_table_keyword = if self.dialect.describe_requires_table_keyword() { | |
| // only allow to use TABLE keyword for DESC|DESCRIBE statement | |
| self.parse_keyword(Keyword::TABLE) | |
| } else { | |
| false | |
| }; | |
| let table_name = self.parse_object_name(false)?; | |
| Ok(Statement::ExplainTable { | |
| describe_alias, | |
| hive_format, | |
| has_table_keyword, | |
| table_name, | |
| }) | |
| } | |
| } | |
| } | |
| /// Parse a query expression, i.e. a `SELECT` statement optionally | |
| /// preceded with some `WITH` CTE declarations and optionally followed | |
| /// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't | |
| /// expect the initial keyword to be already consumed | |
| #[cfg_attr(feature = "recursive-protection", recursive::recursive)] | |
| pub fn parse_query(&mut self) -> Result<Box<Query>, ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| let with = if self.parse_keyword(Keyword::WITH) { | |
| let with_token = self.get_current_token(); | |
| Some(With { | |
| with_token: with_token.clone().into(), | |
| recursive: self.parse_keyword(Keyword::RECURSIVE), | |
| cte_tables: self.parse_comma_separated(Parser::parse_cte)?, | |
| }) | |
| } else { | |
| None | |
| }; | |
| if self.parse_keyword(Keyword::INSERT) { | |
| Ok(Query { | |
| with, | |
| body: self.parse_insert_setexpr_boxed(self.get_current_token().clone())?, | |
| order_by: None, | |
| limit_clause: None, | |
| fetch: None, | |
| locks: vec![], | |
| for_clause: None, | |
| settings: None, | |
| format_clause: None, | |
| pipe_operators: vec![], | |
| } | |
| .into()) | |
| } else if self.parse_keyword(Keyword::UPDATE) { | |
| Ok(Query { | |
| with, | |
| body: self.parse_update_setexpr_boxed(self.get_current_token().clone())?, | |
| order_by: None, | |
| limit_clause: None, | |
| fetch: None, | |
| locks: vec![], | |
| for_clause: None, | |
| settings: None, | |
| format_clause: None, | |
| pipe_operators: vec![], | |
| } | |
| .into()) | |
| } else if self.parse_keyword(Keyword::DELETE) { | |
| Ok(Query { | |
| with, | |
| body: self.parse_delete_setexpr_boxed(self.get_current_token().clone())?, | |
| limit_clause: None, | |
| order_by: None, | |
| fetch: None, | |
| locks: vec![], | |
| for_clause: None, | |
| settings: None, | |
| format_clause: None, | |
| pipe_operators: vec![], | |
| } | |
| .into()) | |
| } else if self.parse_keyword(Keyword::MERGE) { | |
| Ok(Query { | |
| with, | |
| body: self.parse_merge_setexpr_boxed(self.get_current_token().clone())?, | |
| limit_clause: None, | |
| order_by: None, | |
| fetch: None, | |
| locks: vec![], | |
| for_clause: None, | |
| settings: None, | |
| format_clause: None, | |
| pipe_operators: vec![], | |
| } | |
| .into()) | |
| } else { | |
| let body = self.parse_query_body(self.dialect.prec_unknown())?; | |
| let order_by = self.parse_optional_order_by()?; | |
| let mut limit_clause = self.parse_optional_limit_clause()?; | |
| let settings = self.parse_settings()?; | |
| let fetch = if self.parse_keyword(Keyword::FETCH) { | |
| Some(self.parse_fetch()?) | |
| } else { | |
| None | |
| }; | |
| let mut for_clause = None; | |
| let mut locks = Vec::new(); | |
| while self.parse_keyword(Keyword::FOR) { | |
| if let Some(parsed_for_clause) = self.parse_for_clause()? { | |
| for_clause = Some(parsed_for_clause); | |
| break; | |
| } else { | |
| locks.push(self.parse_lock()?); | |
| } | |
| } | |
| // Some databases (e.g. PostgreSQL) accept `LIMIT`/`OFFSET` after the | |
| // row-locking clause (`... FOR UPDATE SKIP LOCKED LIMIT 5`) as well | |
| // as before it. The locking clause above is parsed for every | |
| // dialect, so accept a trailing limit here too rather than gating it | |
| // behind a dialect flag. | |
| if limit_clause.is_none() { | |
| limit_clause = self.parse_optional_limit_clause()?; | |
| } | |
| let format_clause = | |
| if self.dialect.supports_select_format() && self.parse_keyword(Keyword::FORMAT) { | |
| if self.parse_keyword(Keyword::NULL) { | |
| Some(FormatClause::Null) | |
| } else { | |
| let ident = self.parse_identifier()?; | |
| Some(FormatClause::Identifier(ident)) | |
| } | |
| } else { | |
| None | |
| }; | |
| let pipe_operators = if self.dialect.supports_pipe_operator() { | |
| self.parse_pipe_operators()? | |
| } else { | |
| Vec::new() | |
| }; | |
| Ok(Query { | |
| with, | |
| body, | |
| order_by, | |
| limit_clause, | |
| fetch, | |
| locks, | |
| for_clause, | |
| settings, | |
| format_clause, | |
| pipe_operators, | |
| } | |
| .into()) | |
| } | |
| } | |
| fn parse_pipe_operators(&mut self) -> Result<Vec<PipeOperator>, ParserError> { | |
| let mut pipe_operators = Vec::new(); | |
| while self.consume_token(&Token::VerticalBarRightAngleBracket) { | |
| let kw = self.expect_one_of_keywords(&[ | |
| Keyword::SELECT, | |
| Keyword::EXTEND, | |
| Keyword::SET, | |
| Keyword::DROP, | |
| Keyword::AS, | |
| Keyword::WHERE, | |
| Keyword::LIMIT, | |
| Keyword::AGGREGATE, | |
| Keyword::ORDER, | |
| Keyword::TABLESAMPLE, | |
| Keyword::RENAME, | |
| Keyword::UNION, | |
| Keyword::INTERSECT, | |
| Keyword::EXCEPT, | |
| Keyword::CALL, | |
| Keyword::PIVOT, | |
| Keyword::UNPIVOT, | |
| Keyword::JOIN, | |
| Keyword::INNER, | |
| Keyword::LEFT, | |
| Keyword::RIGHT, | |
| Keyword::FULL, | |
| Keyword::CROSS, | |
| ])?; | |
| match kw { | |
| Keyword::SELECT => { | |
| let exprs = self.parse_comma_separated(Parser::parse_select_item)?; | |
| pipe_operators.push(PipeOperator::Select { exprs }) | |
| } | |
| Keyword::EXTEND => { | |
| let exprs = self.parse_comma_separated(Parser::parse_select_item)?; | |
| pipe_operators.push(PipeOperator::Extend { exprs }) | |
| } | |
| Keyword::SET => { | |
| let assignments = self.parse_comma_separated(Parser::parse_assignment)?; | |
| pipe_operators.push(PipeOperator::Set { assignments }) | |
| } | |
| Keyword::DROP => { | |
| let columns = self.parse_identifiers()?; | |
| pipe_operators.push(PipeOperator::Drop { columns }) | |
| } | |
| Keyword::AS => { | |
| let alias = self.parse_identifier()?; | |
| pipe_operators.push(PipeOperator::As { alias }) | |
| } | |
| Keyword::WHERE => { | |
| let expr = self.parse_expr()?; | |
| pipe_operators.push(PipeOperator::Where { expr }) | |
| } | |
| Keyword::LIMIT => { | |
| let expr = self.parse_expr()?; | |
| let offset = if self.parse_keyword(Keyword::OFFSET) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| pipe_operators.push(PipeOperator::Limit { expr, offset }) | |
| } | |
| Keyword::AGGREGATE => { | |
| let full_table_exprs = if self.peek_keyword(Keyword::GROUP) { | |
| vec![] | |
| } else { | |
| self.parse_comma_separated(|parser| { | |
| parser.parse_expr_with_alias_and_order_by() | |
| })? | |
| }; | |
| let group_by_expr = if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) { | |
| self.parse_comma_separated(|parser| { | |
| parser.parse_expr_with_alias_and_order_by() | |
| })? | |
| } else { | |
| vec![] | |
| }; | |
| pipe_operators.push(PipeOperator::Aggregate { | |
| full_table_exprs, | |
| group_by_expr, | |
| }) | |
| } | |
| Keyword::ORDER => { | |
| self.expect_one_of_keywords(&[Keyword::BY])?; | |
| let exprs = self.parse_comma_separated(Parser::parse_order_by_expr)?; | |
| pipe_operators.push(PipeOperator::OrderBy { exprs }) | |
| } | |
| Keyword::TABLESAMPLE => { | |
| let sample = self.parse_table_sample(TableSampleModifier::TableSample)?; | |
| pipe_operators.push(PipeOperator::TableSample { sample }); | |
| } | |
| Keyword::RENAME => { | |
| let mappings = | |
| self.parse_comma_separated(Parser::parse_identifier_with_optional_alias)?; | |
| pipe_operators.push(PipeOperator::Rename { mappings }); | |
| } | |
| Keyword::UNION => { | |
| let set_quantifier = self.parse_set_quantifier(&Some(SetOperator::Union)); | |
| let queries = self.parse_pipe_operator_queries()?; | |
| pipe_operators.push(PipeOperator::Union { | |
| set_quantifier, | |
| queries, | |
| }); | |
| } | |
| Keyword::INTERSECT => { | |
| let set_quantifier = | |
| self.parse_distinct_required_set_quantifier("INTERSECT")?; | |
| let queries = self.parse_pipe_operator_queries()?; | |
| pipe_operators.push(PipeOperator::Intersect { | |
| set_quantifier, | |
| queries, | |
| }); | |
| } | |
| Keyword::EXCEPT => { | |
| let set_quantifier = self.parse_distinct_required_set_quantifier("EXCEPT")?; | |
| let queries = self.parse_pipe_operator_queries()?; | |
| pipe_operators.push(PipeOperator::Except { | |
| set_quantifier, | |
| queries, | |
| }); | |
| } | |
| Keyword::CALL => { | |
| let function_name = self.parse_object_name(false)?; | |
| let function_expr = self.parse_function(function_name)?; | |
| if let Expr::Function(function) = function_expr { | |
| let alias = self.parse_identifier_optional_alias()?; | |
| pipe_operators.push(PipeOperator::Call { function, alias }); | |
| } else { | |
| return Err(ParserError::ParserError( | |
| "Expected function call after CALL".to_string(), | |
| )); | |
| } | |
| } | |
| Keyword::PIVOT => { | |
| self.expect_token(&Token::LParen)?; | |
| let aggregate_functions = | |
| self.parse_comma_separated(Self::parse_pivot_aggregate_function)?; | |
| self.expect_keyword_is(Keyword::FOR)?; | |
| let value_column = self.parse_period_separated(|p| p.parse_identifier())?; | |
| self.expect_keyword_is(Keyword::IN)?; | |
| self.expect_token(&Token::LParen)?; | |
| let value_source = if self.parse_keyword(Keyword::ANY) { | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| PivotValueSource::Any(order_by) | |
| } else if self.peek_sub_query() { | |
| PivotValueSource::Subquery(self.parse_query()?) | |
| } else { | |
| PivotValueSource::List( | |
| self.parse_comma_separated(Self::parse_expr_with_alias)?, | |
| ) | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.parse_identifier_optional_alias()?; | |
| pipe_operators.push(PipeOperator::Pivot { | |
| aggregate_functions, | |
| value_column, | |
| value_source, | |
| alias, | |
| }); | |
| } | |
| Keyword::UNPIVOT => { | |
| self.expect_token(&Token::LParen)?; | |
| let value_column = self.parse_identifier()?; | |
| self.expect_keyword(Keyword::FOR)?; | |
| let name_column = self.parse_identifier()?; | |
| self.expect_keyword(Keyword::IN)?; | |
| self.expect_token(&Token::LParen)?; | |
| let unpivot_columns = self.parse_comma_separated(Parser::parse_identifier)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.parse_identifier_optional_alias()?; | |
| pipe_operators.push(PipeOperator::Unpivot { | |
| value_column, | |
| name_column, | |
| unpivot_columns, | |
| alias, | |
| }); | |
| } | |
| Keyword::JOIN | |
| | Keyword::INNER | |
| | Keyword::LEFT | |
| | Keyword::RIGHT | |
| | Keyword::FULL | |
| | Keyword::CROSS => { | |
| self.prev_token(); | |
| let mut joins = self.parse_joins()?; | |
| if joins.len() != 1 { | |
| return Err(ParserError::ParserError( | |
| "Join pipe operator must have a single join".to_string(), | |
| )); | |
| } | |
| let join = joins.swap_remove(0); | |
| pipe_operators.push(PipeOperator::Join(join)) | |
| } | |
| unhandled => { | |
| return Err(ParserError::ParserError(format!( | |
| "`expect_one_of_keywords` further up allowed unhandled keyword: {unhandled:?}" | |
| ))) | |
| } | |
| } | |
| } | |
| Ok(pipe_operators) | |
| } | |
| fn parse_settings(&mut self) -> Result<Option<Vec<Setting>>, ParserError> { | |
| let settings = if self.dialect.supports_settings() && self.parse_keyword(Keyword::SETTINGS) | |
| { | |
| let key_values = self.parse_comma_separated(|p| { | |
| let key = p.parse_identifier()?; | |
| p.expect_token(&Token::Eq)?; | |
| let value = p.parse_expr()?; | |
| Ok(Setting { key, value }) | |
| })?; | |
| Some(key_values) | |
| } else { | |
| None | |
| }; | |
| Ok(settings) | |
| } | |
| /// Parse a mssql `FOR [XML | JSON | BROWSE]` clause | |
| pub fn parse_for_clause(&mut self) -> Result<Option<ForClause>, ParserError> { | |
| if self.parse_keyword(Keyword::XML) { | |
| Ok(Some(self.parse_for_xml()?)) | |
| } else if self.parse_keyword(Keyword::JSON) { | |
| Ok(Some(self.parse_for_json()?)) | |
| } else if self.parse_keyword(Keyword::BROWSE) { | |
| Ok(Some(ForClause::Browse)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a mssql `FOR XML` clause | |
| pub fn parse_for_xml(&mut self) -> Result<ForClause, ParserError> { | |
| let for_xml = if self.parse_keyword(Keyword::RAW) { | |
| let mut element_name = None; | |
| if self.peek_token_ref().token == Token::LParen { | |
| self.expect_token(&Token::LParen)?; | |
| element_name = Some(self.parse_literal_string()?); | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| ForXml::Raw(element_name) | |
| } else if self.parse_keyword(Keyword::AUTO) { | |
| ForXml::Auto | |
| } else if self.parse_keyword(Keyword::EXPLICIT) { | |
| ForXml::Explicit | |
| } else if self.parse_keyword(Keyword::PATH) { | |
| let mut element_name = None; | |
| if self.peek_token_ref().token == Token::LParen { | |
| self.expect_token(&Token::LParen)?; | |
| element_name = Some(self.parse_literal_string()?); | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| ForXml::Path(element_name) | |
| } else { | |
| return Err(ParserError::ParserError( | |
| "Expected FOR XML [RAW | AUTO | EXPLICIT | PATH ]".to_string(), | |
| )); | |
| }; | |
| let mut elements = false; | |
| let mut binary_base64 = false; | |
| let mut root = None; | |
| let mut r#type = false; | |
| while self.peek_token_ref().token == Token::Comma { | |
| self.next_token(); | |
| if self.parse_keyword(Keyword::ELEMENTS) { | |
| elements = true; | |
| } else if self.parse_keyword(Keyword::BINARY) { | |
| self.expect_keyword_is(Keyword::BASE64)?; | |
| binary_base64 = true; | |
| } else if self.parse_keyword(Keyword::ROOT) { | |
| self.expect_token(&Token::LParen)?; | |
| root = Some(self.parse_literal_string()?); | |
| self.expect_token(&Token::RParen)?; | |
| } else if self.parse_keyword(Keyword::TYPE) { | |
| r#type = true; | |
| } | |
| } | |
| Ok(ForClause::Xml { | |
| for_xml, | |
| elements, | |
| binary_base64, | |
| root, | |
| r#type, | |
| }) | |
| } | |
| /// Parse a mssql `FOR JSON` clause | |
| pub fn parse_for_json(&mut self) -> Result<ForClause, ParserError> { | |
| let for_json = if self.parse_keyword(Keyword::AUTO) { | |
| ForJson::Auto | |
| } else if self.parse_keyword(Keyword::PATH) { | |
| ForJson::Path | |
| } else { | |
| return Err(ParserError::ParserError( | |
| "Expected FOR JSON [AUTO | PATH ]".to_string(), | |
| )); | |
| }; | |
| let mut root = None; | |
| let mut include_null_values = false; | |
| let mut without_array_wrapper = false; | |
| while self.peek_token_ref().token == Token::Comma { | |
| self.next_token(); | |
| if self.parse_keyword(Keyword::ROOT) { | |
| self.expect_token(&Token::LParen)?; | |
| root = Some(self.parse_literal_string()?); | |
| self.expect_token(&Token::RParen)?; | |
| } else if self.parse_keyword(Keyword::INCLUDE_NULL_VALUES) { | |
| include_null_values = true; | |
| } else if self.parse_keyword(Keyword::WITHOUT_ARRAY_WRAPPER) { | |
| without_array_wrapper = true; | |
| } | |
| } | |
| Ok(ForClause::Json { | |
| for_json, | |
| root, | |
| include_null_values, | |
| without_array_wrapper, | |
| }) | |
| } | |
| /// Parse a CTE (`alias [( col1, col2, ... )] [AS] (subquery)`) | |
| pub fn parse_cte(&mut self) -> Result<Cte, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let as_optional = self.dialect.supports_cte_without_as(); | |
| // If AS is optional, first try to parse `name (query)` directly | |
| if as_optional && !self.peek_keyword(Keyword::AS) { | |
| if let Some((query, closing_paren_token)) = self.maybe_parse(|p| { | |
| p.expect_token(&Token::LParen)?; | |
| let query = p.parse_query()?; | |
| let closing_paren_token = p.expect_token(&Token::RParen)?; | |
| Ok((query, closing_paren_token)) | |
| })? { | |
| let mut cte = Cte { | |
| alias: TableAlias { | |
| explicit: false, | |
| name, | |
| columns: vec![], | |
| at: None, | |
| }, | |
| query, | |
| from: None, | |
| materialized: None, | |
| closing_paren_token: closing_paren_token.into(), | |
| }; | |
| if self.parse_keyword(Keyword::FROM) { | |
| cte.from = Some(self.parse_identifier()?); | |
| } | |
| return Ok(cte); | |
| } | |
| } | |
| // Determine column definitions and consume AS | |
| let columns = if self.parse_keyword(Keyword::AS) { | |
| vec![] | |
| } else { | |
| let columns = self.parse_table_alias_column_defs()?; | |
| if as_optional { | |
| let _ = self.parse_keyword(Keyword::AS); | |
| } else { | |
| self.expect_keyword_is(Keyword::AS)?; | |
| } | |
| columns | |
| }; | |
| let mut is_materialized = None; | |
| if self.dialect.supports_cte_materialization() { | |
| if self.parse_keyword(Keyword::MATERIALIZED) { | |
| is_materialized = Some(CteAsMaterialized::Materialized); | |
| } else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) { | |
| is_materialized = Some(CteAsMaterialized::NotMaterialized); | |
| } | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| let query = self.parse_query()?; | |
| let closing_paren_token = self.expect_token(&Token::RParen)?; | |
| let mut cte = Cte { | |
| alias: TableAlias { | |
| explicit: false, | |
| name, | |
| columns, | |
| at: None, | |
| }, | |
| query, | |
| from: None, | |
| materialized: is_materialized, | |
| closing_paren_token: closing_paren_token.into(), | |
| }; | |
| if self.dialect.supports_from_first_insert() && self.parse_keyword(Keyword::FROM) { | |
| cte.from = Some(self.parse_identifier()?); | |
| } | |
| Ok(cte) | |
| } | |
| /// Parse a "query body", which is an expression with roughly the | |
| /// following grammar: | |
| /// ```sql | |
| /// query_body ::= restricted_select | '(' subquery ')' | set_operation | |
| /// restricted_select ::= 'SELECT' [expr_list] [ from ] [ where ] [ groupby_having ] | |
| /// subquery ::= query_body [ order_by_limit ] | |
| /// set_operation ::= query_body { 'UNION' | 'EXCEPT' | 'INTERSECT' } [ 'ALL' ] query_body | |
| /// ``` | |
| pub fn parse_query_body(&mut self, precedence: u8) -> Result<Box<SetExpr>, ParserError> { | |
| // We parse the expression using a Pratt parser, as in `parse_expr()`. | |
| // Start by parsing a restricted SELECT or a `(subquery)`: | |
| let expr = if self.peek_keyword(Keyword::SELECT) | |
| || (self.peek_keyword(Keyword::FROM) && self.dialect.supports_from_first_select()) | |
| { | |
| SetExpr::Select(self.parse_select().map(Box::new)?) | |
| } else if self.consume_token(&Token::LParen) { | |
| // CTEs are not allowed here, but the parser currently accepts them | |
| let subquery = self.parse_query()?; | |
| self.expect_token(&Token::RParen)?; | |
| SetExpr::Query(subquery) | |
| } else if self.parse_keyword(Keyword::VALUES) { | |
| let is_mysql = dialect_of!(self is MySqlDialect); | |
| SetExpr::Values(self.parse_values(is_mysql, false)?) | |
| } else if self.parse_keyword(Keyword::VALUE) { | |
| let is_mysql = dialect_of!(self is MySqlDialect); | |
| SetExpr::Values(self.parse_values(is_mysql, true)?) | |
| } else if self.parse_keyword(Keyword::TABLE) { | |
| SetExpr::Table(Box::new(self.parse_as_table()?)) | |
| } else { | |
| return self.expected_ref( | |
| "SELECT, VALUES, or a subquery in the query body", | |
| self.peek_token_ref(), | |
| ); | |
| }; | |
| self.parse_remaining_set_exprs(expr, precedence) | |
| } | |
| /// Parse any extra set expressions that may be present in a query body | |
| /// | |
| /// (this is its own function to reduce required stack size in debug builds) | |
| fn parse_remaining_set_exprs( | |
| &mut self, | |
| mut expr: SetExpr, | |
| precedence: u8, | |
| ) -> Result<Box<SetExpr>, ParserError> { | |
| loop { | |
| // The query can be optionally followed by a set operator: | |
| let op = self.parse_set_operator(&self.peek_token().token); | |
| let next_precedence = match op { | |
| // UNION and EXCEPT have the same binding power and evaluate left-to-right | |
| Some(SetOperator::Union) | Some(SetOperator::Except) | Some(SetOperator::Minus) => { | |
| 10 | |
| } | |
| // INTERSECT has higher precedence than UNION/EXCEPT | |
| Some(SetOperator::Intersect) => 20, | |
| // Unexpected token or EOF => stop parsing the query body | |
| None => break, | |
| }; | |
| if precedence >= next_precedence { | |
| break; | |
| } | |
| self.next_token(); // skip past the set operator | |
| let set_quantifier = self.parse_set_quantifier(&op); | |
| expr = SetExpr::SetOperation { | |
| left: Box::new(expr), | |
| op: op.unwrap(), | |
| set_quantifier, | |
| right: self.parse_query_body(next_precedence)?, | |
| }; | |
| } | |
| Ok(expr.into()) | |
| } | |
| /// Parse a set operator token into its `SetOperator` variant. | |
| pub fn parse_set_operator(&mut self, token: &Token) -> Option<SetOperator> { | |
| match token { | |
| Token::Word(w) if w.keyword == Keyword::UNION => Some(SetOperator::Union), | |
| Token::Word(w) if w.keyword == Keyword::EXCEPT => Some(SetOperator::Except), | |
| Token::Word(w) if w.keyword == Keyword::INTERSECT => Some(SetOperator::Intersect), | |
| Token::Word(w) if w.keyword == Keyword::MINUS => Some(SetOperator::Minus), | |
| _ => None, | |
| } | |
| } | |
| /// Parse a set quantifier (e.g., `ALL`, `DISTINCT BY NAME`) for the given set operator. | |
| pub fn parse_set_quantifier(&mut self, op: &Option<SetOperator>) -> SetQuantifier { | |
| match op { | |
| Some( | |
| SetOperator::Except | |
| | SetOperator::Intersect | |
| | SetOperator::Union | |
| | SetOperator::Minus, | |
| ) => { | |
| if self.parse_keywords(&[Keyword::DISTINCT, Keyword::BY, Keyword::NAME]) { | |
| SetQuantifier::DistinctByName | |
| } else if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { | |
| SetQuantifier::ByName | |
| } else if self.parse_keyword(Keyword::ALL) { | |
| if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { | |
| SetQuantifier::AllByName | |
| } else { | |
| SetQuantifier::All | |
| } | |
| } else if self.parse_keyword(Keyword::DISTINCT) { | |
| SetQuantifier::Distinct | |
| } else { | |
| SetQuantifier::None | |
| } | |
| } | |
| _ => SetQuantifier::None, | |
| } | |
| } | |
| /// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`) | |
| pub fn parse_select(&mut self) -> Result<Select, ParserError> { | |
| let mut from_first = None; | |
| if self.dialect.supports_from_first_select() && self.peek_keyword(Keyword::FROM) { | |
| let from_token = self.expect_keyword(Keyword::FROM)?; | |
| let from = self.parse_table_with_joins()?; | |
| if !self.peek_keyword(Keyword::SELECT) { | |
| return Ok(Select { | |
| select_token: AttachedToken(from_token), | |
| optimizer_hints: vec![], | |
| distinct: None, | |
| select_modifiers: None, | |
| top: None, | |
| top_before_distinct: false, | |
| projection: vec![], | |
| exclude: None, | |
| into: None, | |
| from, | |
| lateral_views: vec![], | |
| prewhere: None, | |
| selection: None, | |
| group_by: GroupByExpr::Expressions(vec![], vec![]), | |
| cluster_by: vec![], | |
| distribute_by: vec![], | |
| sort_by: vec![], | |
| having: None, | |
| named_window: vec![], | |
| window_before_qualify: false, | |
| qualify: None, | |
| value_table_mode: None, | |
| connect_by: vec![], | |
| flavor: SelectFlavor::FromFirstNoSelect, | |
| }); | |
| } | |
| from_first = Some(from); | |
| } | |
| let select_token = self.expect_keyword(Keyword::SELECT)?; | |
| let optimizer_hints = self.maybe_parse_optimizer_hints()?; | |
| let value_table_mode = self.parse_value_table_mode()?; | |
| let (select_modifiers, distinct_select_modifier) = | |
| if self.dialect.supports_select_modifiers() { | |
| self.parse_select_modifiers()? | |
| } else { | |
| (None, None) | |
| }; | |
| let mut top_before_distinct = false; | |
| let mut top = None; | |
| if self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) { | |
| top = Some(self.parse_top()?); | |
| top_before_distinct = true; | |
| } | |
| let distinct = if distinct_select_modifier.is_some() { | |
| distinct_select_modifier | |
| } else { | |
| self.parse_all_or_distinct()? | |
| }; | |
| if !self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) { | |
| top = Some(self.parse_top()?); | |
| } | |
| let projection = | |
| if self.dialect.supports_empty_projections() && self.peek_keyword(Keyword::FROM) { | |
| vec![] | |
| } else { | |
| self.parse_projection()? | |
| }; | |
| let exclude = if self.dialect.supports_select_exclude() { | |
| self.parse_optional_select_item_exclude()? | |
| } else { | |
| None | |
| }; | |
| let into = if self.parse_keyword(Keyword::INTO) { | |
| Some(self.parse_select_into()?) | |
| } else { | |
| None | |
| }; | |
| // Note that for keywords to be properly handled here, they need to be | |
| // added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`, | |
| // otherwise they may be parsed as an alias as part of the `projection` | |
| // or `from`. | |
| let (from, from_first) = if let Some(from) = from_first.take() { | |
| (from, true) | |
| } else if self.parse_keyword(Keyword::FROM) { | |
| (self.parse_table_with_joins()?, false) | |
| } else { | |
| (vec![], false) | |
| }; | |
| let mut lateral_views = vec![]; | |
| loop { | |
| if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) { | |
| let outer = self.parse_keyword(Keyword::OUTER); | |
| let lateral_view = self.parse_expr()?; | |
| let lateral_view_name = self.parse_object_name(false)?; | |
| let lateral_col_alias = self | |
| .parse_comma_separated(|parser| { | |
| parser.parse_optional_alias(&[ | |
| Keyword::WHERE, | |
| Keyword::GROUP, | |
| Keyword::CLUSTER, | |
| Keyword::HAVING, | |
| Keyword::LATERAL, | |
| ]) // This couldn't possibly be a bad idea | |
| })? | |
| .into_iter() | |
| .flatten() | |
| .collect(); | |
| lateral_views.push(LateralView { | |
| lateral_view, | |
| lateral_view_name, | |
| lateral_col_alias, | |
| outer, | |
| }); | |
| } else { | |
| break; | |
| } | |
| } | |
| let prewhere = if self.dialect.supports_prewhere() && self.parse_keyword(Keyword::PREWHERE) | |
| { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let selection = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let connect_by = self.maybe_parse_connect_by()?; | |
| let group_by = self | |
| .parse_optional_group_by()? | |
| .unwrap_or_else(|| GroupByExpr::Expressions(vec![], vec![])); | |
| let cluster_by = if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let distribute_by = if self.parse_keywords(&[Keyword::DISTRIBUTE, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let sort_by = if self.parse_keywords(&[Keyword::SORT, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let having = if self.parse_keyword(Keyword::HAVING) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| // Accept QUALIFY and WINDOW in any order and flag accordingly. | |
| let (named_windows, qualify, window_before_qualify) = if self.parse_keyword(Keyword::WINDOW) | |
| { | |
| let named_windows = self.parse_comma_separated(Parser::parse_named_window)?; | |
| if self.parse_keyword(Keyword::QUALIFY) { | |
| (named_windows, Some(self.parse_expr()?), true) | |
| } else { | |
| (named_windows, None, true) | |
| } | |
| } else if self.parse_keyword(Keyword::QUALIFY) { | |
| let qualify = Some(self.parse_expr()?); | |
| if self.parse_keyword(Keyword::WINDOW) { | |
| ( | |
| self.parse_comma_separated(Parser::parse_named_window)?, | |
| qualify, | |
| false, | |
| ) | |
| } else { | |
| (Default::default(), qualify, false) | |
| } | |
| } else { | |
| Default::default() | |
| }; | |
| Ok(Select { | |
| select_token: AttachedToken(select_token), | |
| optimizer_hints, | |
| distinct, | |
| select_modifiers, | |
| top, | |
| top_before_distinct, | |
| projection, | |
| exclude, | |
| into, | |
| from, | |
| lateral_views, | |
| prewhere, | |
| selection, | |
| group_by, | |
| cluster_by, | |
| distribute_by, | |
| sort_by, | |
| having, | |
| named_window: named_windows, | |
| window_before_qualify, | |
| qualify, | |
| value_table_mode, | |
| connect_by, | |
| flavor: if from_first { | |
| SelectFlavor::FromFirst | |
| } else { | |
| SelectFlavor::Standard | |
| }, | |
| }) | |
| } | |
| /// Parses optimizer hints at the current token position. | |
| /// | |
| /// Collects all `/*prefix+...*/` and `--prefix+...` patterns. | |
| /// The `prefix` is any run of ASCII alphanumeric characters between the | |
| /// comment marker and `+` (e.g. `""` for `/*+...*/`, `"abc"` for `/*abc+...*/`). | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html#optimizer-hints-overview) | |
| /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E) | |
| fn maybe_parse_optimizer_hints(&mut self) -> Result<Vec<OptimizerHint>, ParserError> { | |
| let supports_hints = self.dialect.supports_comment_optimizer_hint(); | |
| if !supports_hints { | |
| return Ok(vec![]); | |
| } | |
| let mut hints = vec![]; | |
| loop { | |
| let t = self.peek_nth_token_no_skip_ref(0); | |
| let Token::Whitespace(ws) = &t.token else { | |
| break; | |
| }; | |
| match ws { | |
| Whitespace::SingleLineComment { comment, prefix } => { | |
| if let Some((hint_prefix, text)) = Self::extract_hint_prefix_and_text(comment) { | |
| hints.push(OptimizerHint { | |
| prefix: hint_prefix, | |
| text, | |
| style: OptimizerHintStyle::SingleLine { | |
| prefix: prefix.clone(), | |
| }, | |
| }); | |
| } | |
| self.next_token_no_skip(); | |
| } | |
| Whitespace::MultiLineComment(comment) => { | |
| if let Some((hint_prefix, text)) = Self::extract_hint_prefix_and_text(comment) { | |
| hints.push(OptimizerHint { | |
| prefix: hint_prefix, | |
| text, | |
| style: OptimizerHintStyle::MultiLine, | |
| }); | |
| } | |
| self.next_token_no_skip(); | |
| } | |
| Whitespace::Space | Whitespace::Tab | Whitespace::Newline => { | |
| self.next_token_no_skip(); | |
| } | |
| } | |
| } | |
| Ok(hints) | |
| } | |
| /// Checks if a comment's content starts with `[ASCII-alphanumeric]*+` | |
| /// and returns `(prefix, text_after_plus)` if so. | |
| fn extract_hint_prefix_and_text(comment: &str) -> Option<(String, String)> { | |
| let (before_plus, text) = comment.split_once('+')?; | |
| if before_plus.chars().all(|c| c.is_ascii_alphanumeric()) { | |
| Some((before_plus.to_string(), text.to_string())) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parses MySQL SELECT modifiers and DISTINCT/ALL in any order. | |
| /// | |
| /// Manual testing shows odifiers can appear in any order, and modifiers other than DISTINCT/ALL | |
| /// can be repeated. | |
| /// | |
| /// <https://dev.mysql.com/doc/refman/8.4/en/select.html> | |
| fn parse_select_modifiers( | |
| &mut self, | |
| ) -> Result<(Option<SelectModifiers>, Option<Distinct>), ParserError> { | |
| let mut modifiers = SelectModifiers::default(); | |
| let mut distinct = None; | |
| let keywords = &[ | |
| Keyword::ALL, | |
| Keyword::DISTINCT, | |
| Keyword::DISTINCTROW, | |
| Keyword::HIGH_PRIORITY, | |
| Keyword::STRAIGHT_JOIN, | |
| Keyword::SQL_SMALL_RESULT, | |
| Keyword::SQL_BIG_RESULT, | |
| Keyword::SQL_BUFFER_RESULT, | |
| Keyword::SQL_NO_CACHE, | |
| Keyword::SQL_CALC_FOUND_ROWS, | |
| ]; | |
| while let Some(keyword) = self.parse_one_of_keywords(keywords) { | |
| match keyword { | |
| Keyword::ALL | Keyword::DISTINCT if distinct.is_none() => { | |
| self.prev_token(); | |
| distinct = self.parse_all_or_distinct()?; | |
| } | |
| // DISTINCTROW is a MySQL-specific legacy (but not deprecated) alias for DISTINCT | |
| Keyword::DISTINCTROW if distinct.is_none() => { | |
| distinct = Some(Distinct::Distinct); | |
| } | |
| Keyword::HIGH_PRIORITY => modifiers.high_priority = true, | |
| Keyword::STRAIGHT_JOIN => modifiers.straight_join = true, | |
| Keyword::SQL_SMALL_RESULT => modifiers.sql_small_result = true, | |
| Keyword::SQL_BIG_RESULT => modifiers.sql_big_result = true, | |
| Keyword::SQL_BUFFER_RESULT => modifiers.sql_buffer_result = true, | |
| Keyword::SQL_NO_CACHE => modifiers.sql_no_cache = true, | |
| Keyword::SQL_CALC_FOUND_ROWS => modifiers.sql_calc_found_rows = true, | |
| _ => { | |
| self.prev_token(); | |
| return self.expected_ref( | |
| "HIGH_PRIORITY, STRAIGHT_JOIN, or other MySQL select modifier", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| } | |
| } | |
| // Avoid polluting the AST with `Some(SelectModifiers::default())` empty value unless there | |
| // actually were some modifiers set. | |
| let select_modifiers = if modifiers.is_any_set() { | |
| Some(modifiers) | |
| } else { | |
| None | |
| }; | |
| Ok((select_modifiers, distinct)) | |
| } | |
| fn parse_value_table_mode(&mut self) -> Result<Option<ValueTableMode>, ParserError> { | |
| if !dialect_of!(self is BigQueryDialect) { | |
| return Ok(None); | |
| } | |
| let mode = if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::VALUE]) { | |
| Some(ValueTableMode::DistinctAsValue) | |
| } else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::STRUCT]) { | |
| Some(ValueTableMode::DistinctAsStruct) | |
| } else if self.parse_keywords(&[Keyword::AS, Keyword::VALUE]) | |
| || self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::VALUE]) | |
| { | |
| Some(ValueTableMode::AsValue) | |
| } else if self.parse_keywords(&[Keyword::AS, Keyword::STRUCT]) | |
| || self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::STRUCT]) | |
| { | |
| Some(ValueTableMode::AsStruct) | |
| } else if self.parse_keyword(Keyword::AS) { | |
| self.expected_ref("VALUE or STRUCT", self.peek_token_ref())? | |
| } else { | |
| None | |
| }; | |
| Ok(mode) | |
| } | |
| /// Invoke `f` after first setting the parser's `ParserState` to `state`. | |
| /// | |
| /// Upon return, restores the parser's state to what it started at. | |
| fn with_state<T, F>(&mut self, state: ParserState, mut f: F) -> Result<T, ParserError> | |
| where | |
| F: FnMut(&mut Parser) -> Result<T, ParserError>, | |
| { | |
| let current_state = self.state; | |
| self.state = state; | |
| let res = f(self); | |
| self.state = current_state; | |
| res | |
| } | |
| /// Parse a `CONNECT BY` clause (Oracle-style hierarchical query support). | |
| pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> { | |
| let mut clauses = Vec::with_capacity(2); | |
| loop { | |
| if let Some(idx) = self.parse_keywords_indexed(&[Keyword::START, Keyword::WITH]) { | |
| clauses.push(ConnectByKind::StartWith { | |
| start_token: self.token_at(idx).clone().into(), | |
| condition: self.parse_expr()?.into(), | |
| }); | |
| } else if let Some(idx) = self.parse_keywords_indexed(&[Keyword::CONNECT, Keyword::BY]) | |
| { | |
| clauses.push(ConnectByKind::ConnectBy { | |
| connect_token: self.token_at(idx).clone().into(), | |
| nocycle: self.parse_keyword(Keyword::NOCYCLE), | |
| relationships: self.with_state(ParserState::ConnectBy, |parser| { | |
| parser.parse_comma_separated(Parser::parse_expr) | |
| })?, | |
| }); | |
| } else { | |
| break; | |
| } | |
| } | |
| Ok(clauses) | |
| } | |
| /// Parse `CREATE TABLE x AS TABLE y` | |
| pub fn parse_as_table(&mut self) -> Result<Table, ParserError> { | |
| let first_name = self.parse_identifier()?; | |
| if self.consume_token(&Token::Period) { | |
| let second_name = self.parse_identifier()?; | |
| Ok(Table { | |
| table_name: Some(second_name), | |
| schema_name: Some(first_name), | |
| }) | |
| } else { | |
| Ok(Table { | |
| table_name: Some(first_name), | |
| schema_name: None, | |
| }) | |
| } | |
| } | |
| /// Parse a `SET ROLE` statement. Expects SET to be consumed already. | |
| fn parse_set_role( | |
| &mut self, | |
| modifier: Option<ContextModifier>, | |
| ) -> Result<Statement, ParserError> { | |
| self.expect_keyword_is(Keyword::ROLE)?; | |
| let role_name = if self.parse_keyword(Keyword::NONE) { | |
| None | |
| } else { | |
| Some(self.parse_identifier()?) | |
| }; | |
| Ok(Statement::Set(Set::SetRole { | |
| context_modifier: modifier, | |
| role_name, | |
| })) | |
| } | |
| fn parse_set_values( | |
| &mut self, | |
| parenthesized_assignment: bool, | |
| ) -> Result<Vec<Expr>, ParserError> { | |
| let mut values = vec![]; | |
| if parenthesized_assignment { | |
| self.expect_token(&Token::LParen)?; | |
| } | |
| loop { | |
| let value = if let Some(expr) = self.try_parse_expr_sub_query()? { | |
| expr | |
| } else if let Ok(expr) = self.parse_expr() { | |
| expr | |
| } else { | |
| self.expected_ref("variable value", self.peek_token_ref())? | |
| }; | |
| values.push(value); | |
| if self.consume_token(&Token::Comma) { | |
| continue; | |
| } | |
| if parenthesized_assignment { | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| return Ok(values); | |
| } | |
| } | |
| fn parse_context_modifier(&mut self) -> Option<ContextModifier> { | |
| let modifier = | |
| self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::GLOBAL])?; | |
| Self::keyword_to_modifier(modifier) | |
| } | |
| /// Parse a single SET statement assignment `var = expr`. | |
| fn parse_set_assignment(&mut self) -> Result<SetAssignment, ParserError> { | |
| let scope = self.parse_context_modifier(); | |
| let name = if self.dialect.supports_parenthesized_set_variables() | |
| && self.consume_token(&Token::LParen) | |
| { | |
| // Parenthesized assignments are handled in the `parse_set` function after | |
| // trying to parse list of assignments using this function. | |
| // If a dialect supports both, and we find a LParen, we early exit from this function. | |
| self.expected_ref("Unparenthesized assignment", self.peek_token_ref())? | |
| } else { | |
| self.parse_object_name(false)? | |
| }; | |
| if !(self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO)) { | |
| return self.expected_ref("assignment operator", self.peek_token_ref()); | |
| } | |
| let value = self.parse_expr()?; | |
| Ok(SetAssignment { scope, name, value }) | |
| } | |
| fn parse_set(&mut self) -> Result<Statement, ParserError> { | |
| let hivevar = self.parse_keyword(Keyword::HIVEVAR); | |
| // Modifier is either HIVEVAR: or a ContextModifier (LOCAL, SESSION, etc), not both | |
| let scope = if !hivevar { | |
| self.parse_context_modifier() | |
| } else { | |
| None | |
| }; | |
| if hivevar { | |
| self.expect_token(&Token::Colon)?; | |
| } | |
| if let Some(set_role_stmt) = self.maybe_parse(|parser| parser.parse_set_role(scope))? { | |
| return Ok(set_role_stmt); | |
| } | |
| // Handle special cases first | |
| if self.parse_keywords(&[Keyword::TIME, Keyword::ZONE]) | |
| || self.parse_keyword(Keyword::TIMEZONE) | |
| { | |
| if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { | |
| return Ok(Set::SingleAssignment { | |
| scope, | |
| hivevar, | |
| variable: ObjectName::from(vec!["TIMEZONE".into()]), | |
| values: self.parse_set_values(false)?, | |
| } | |
| .into()); | |
| } else { | |
| // A shorthand alias for SET TIME ZONE that doesn't require | |
| // the assignment operator. It's originally PostgreSQL specific, | |
| // but we allow it for all the dialects | |
| return Ok(Set::SetTimeZone { | |
| local: scope == Some(ContextModifier::Local), | |
| value: self.parse_expr()?, | |
| } | |
| .into()); | |
| } | |
| } else if self.dialect.supports_set_names() && self.parse_keyword(Keyword::NAMES) { | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| return Ok(Set::SetNamesDefault {}.into()); | |
| } | |
| let charset_name = self.parse_identifier()?; | |
| let collation_name = if self.parse_one_of_keywords(&[Keyword::COLLATE]).is_some() { | |
| Some(self.parse_literal_string()?) | |
| } else { | |
| None | |
| }; | |
| return Ok(Set::SetNames { | |
| charset_name, | |
| collation_name, | |
| } | |
| .into()); | |
| } else if self.parse_keyword(Keyword::CHARACTERISTICS) { | |
| self.expect_keywords(&[Keyword::AS, Keyword::TRANSACTION])?; | |
| return Ok(Set::SetTransaction { | |
| modes: self.parse_transaction_modes()?, | |
| snapshot: None, | |
| session: true, | |
| } | |
| .into()); | |
| } else if self.parse_keyword(Keyword::TRANSACTION) { | |
| if self.parse_keyword(Keyword::SNAPSHOT) { | |
| let snapshot_id = self.parse_value()?; | |
| return Ok(Set::SetTransaction { | |
| modes: vec![], | |
| snapshot: Some(snapshot_id), | |
| session: false, | |
| } | |
| .into()); | |
| } | |
| return Ok(Set::SetTransaction { | |
| modes: self.parse_transaction_modes()?, | |
| snapshot: None, | |
| session: false, | |
| } | |
| .into()); | |
| } else if self.parse_keyword(Keyword::AUTHORIZATION) { | |
| let scope = match scope { | |
| Some(s) => s, | |
| None => { | |
| return self.expected_at( | |
| "SESSION, LOCAL, or other scope modifier before AUTHORIZATION", | |
| self.get_current_index(), | |
| ) | |
| } | |
| }; | |
| let auth_value = if self.parse_keyword(Keyword::DEFAULT) { | |
| SetSessionAuthorizationParamKind::Default | |
| } else { | |
| let value = self.parse_identifier()?; | |
| SetSessionAuthorizationParamKind::User(value) | |
| }; | |
| return Ok(Set::SetSessionAuthorization(SetSessionAuthorizationParam { | |
| scope, | |
| kind: auth_value, | |
| }) | |
| .into()); | |
| } | |
| if self.dialect.supports_comma_separated_set_assignments() { | |
| if scope.is_some() { | |
| self.prev_token(); | |
| } | |
| if let Some(assignments) = self | |
| .maybe_parse(|parser| parser.parse_comma_separated(Parser::parse_set_assignment))? | |
| { | |
| return if assignments.len() > 1 { | |
| Ok(Set::MultipleAssignments { assignments }.into()) | |
| } else { | |
| let SetAssignment { scope, name, value } = | |
| assignments.into_iter().next().ok_or_else(|| { | |
| ParserError::ParserError("Expected at least one assignment".to_string()) | |
| })?; | |
| Ok(Set::SingleAssignment { | |
| scope, | |
| hivevar, | |
| variable: name, | |
| values: vec![value], | |
| } | |
| .into()) | |
| }; | |
| } | |
| } | |
| let variables = if self.dialect.supports_parenthesized_set_variables() | |
| && self.consume_token(&Token::LParen) | |
| { | |
| let vars = OneOrManyWithParens::Many( | |
| self.parse_comma_separated(|parser: &mut Parser<'a>| parser.parse_identifier())? | |
| .into_iter() | |
| .map(|ident| ObjectName::from(vec![ident])) | |
| .collect(), | |
| ); | |
| self.expect_token(&Token::RParen)?; | |
| vars | |
| } else { | |
| OneOrManyWithParens::One(self.parse_object_name(false)?) | |
| }; | |
| if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { | |
| let stmt = match variables { | |
| OneOrManyWithParens::One(var) => Set::SingleAssignment { | |
| scope, | |
| hivevar, | |
| variable: var, | |
| values: self.parse_set_values(false)?, | |
| }, | |
| OneOrManyWithParens::Many(vars) => Set::ParenthesizedAssignments { | |
| variables: vars, | |
| values: self.parse_set_values(true)?, | |
| }, | |
| }; | |
| return Ok(stmt.into()); | |
| } | |
| if self.dialect.supports_set_stmt_without_operator() { | |
| self.prev_token(); | |
| return self.parse_set_session_params(); | |
| }; | |
| self.expected_ref("equals sign or TO", self.peek_token_ref()) | |
| } | |
| /// Parse session parameter assignments after `SET` when no `=` or `TO` is present. | |
| pub fn parse_set_session_params(&mut self) -> Result<Statement, ParserError> { | |
| if self.parse_keyword(Keyword::STATISTICS) { | |
| let topic = match self.parse_one_of_keywords(&[ | |
| Keyword::IO, | |
| Keyword::PROFILE, | |
| Keyword::TIME, | |
| Keyword::XML, | |
| ]) { | |
| Some(Keyword::IO) => SessionParamStatsTopic::IO, | |
| Some(Keyword::PROFILE) => SessionParamStatsTopic::Profile, | |
| Some(Keyword::TIME) => SessionParamStatsTopic::Time, | |
| Some(Keyword::XML) => SessionParamStatsTopic::Xml, | |
| _ => return self.expected_ref("IO, PROFILE, TIME or XML", self.peek_token_ref()), | |
| }; | |
| let value = self.parse_session_param_value()?; | |
| Ok( | |
| Set::SetSessionParam(SetSessionParamKind::Statistics(SetSessionParamStatistics { | |
| topic, | |
| value, | |
| })) | |
| .into(), | |
| ) | |
| } else if self.parse_keyword(Keyword::IDENTITY_INSERT) { | |
| let obj = self.parse_object_name(false)?; | |
| let value = self.parse_session_param_value()?; | |
| Ok(Set::SetSessionParam(SetSessionParamKind::IdentityInsert( | |
| SetSessionParamIdentityInsert { obj, value }, | |
| )) | |
| .into()) | |
| } else if self.parse_keyword(Keyword::OFFSETS) { | |
| let keywords = self.parse_comma_separated(|parser| { | |
| let next_token = parser.next_token(); | |
| match &next_token.token { | |
| Token::Word(w) => Ok(w.to_string()), | |
| _ => parser.expected("SQL keyword", next_token), | |
| } | |
| })?; | |
| let value = self.parse_session_param_value()?; | |
| Ok( | |
| Set::SetSessionParam(SetSessionParamKind::Offsets(SetSessionParamOffsets { | |
| keywords, | |
| value, | |
| })) | |
| .into(), | |
| ) | |
| } else { | |
| let names = self.parse_comma_separated(|parser| { | |
| let next_token = parser.next_token(); | |
| match next_token.token { | |
| Token::Word(w) => Ok(w.to_string()), | |
| _ => parser.expected("Session param name", next_token), | |
| } | |
| })?; | |
| let value = self.parse_expr()?.to_string(); | |
| Ok( | |
| Set::SetSessionParam(SetSessionParamKind::Generic(SetSessionParamGeneric { | |
| names, | |
| value, | |
| })) | |
| .into(), | |
| ) | |
| } | |
| } | |
| fn parse_session_param_value(&mut self) -> Result<SessionParamValue, ParserError> { | |
| if self.parse_keyword(Keyword::ON) { | |
| Ok(SessionParamValue::On) | |
| } else if self.parse_keyword(Keyword::OFF) { | |
| Ok(SessionParamValue::Off) | |
| } else { | |
| self.expected_ref("ON or OFF", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse a `SHOW` statement and dispatch to specific SHOW handlers. | |
| pub fn parse_show(&mut self) -> Result<Statement, ParserError> { | |
| let terse = self.parse_keyword(Keyword::TERSE); | |
| let extended = self.parse_keyword(Keyword::EXTENDED); | |
| let full = self.parse_keyword(Keyword::FULL); | |
| let session = self.parse_keyword(Keyword::SESSION); | |
| let global = self.parse_keyword(Keyword::GLOBAL); | |
| let external = self.parse_keyword(Keyword::EXTERNAL); | |
| if self | |
| .parse_one_of_keywords(&[Keyword::COLUMNS, Keyword::FIELDS]) | |
| .is_some() | |
| { | |
| Ok(self.parse_show_columns(extended, full)?) | |
| } else if self.parse_keyword(Keyword::TABLES) { | |
| Ok(self.parse_show_tables(terse, extended, full, external)?) | |
| } else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEWS]) { | |
| Ok(self.parse_show_views(terse, true)?) | |
| } else if self.parse_keyword(Keyword::VIEWS) { | |
| Ok(self.parse_show_views(terse, false)?) | |
| } else if self.parse_keyword(Keyword::FUNCTIONS) { | |
| Ok(self.parse_show_functions()?) | |
| } else if self.parse_keyword(Keyword::PROCESSLIST) { | |
| Ok(Statement::ShowProcessList { full }) | |
| } else if extended || full { | |
| Err(ParserError::ParserError( | |
| "EXTENDED/FULL are not supported with this type of SHOW query".to_string(), | |
| )) | |
| } else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() { | |
| Ok(self.parse_show_create()?) | |
| } else if self.parse_keyword(Keyword::COLLATION) { | |
| Ok(self.parse_show_collation()?) | |
| } else if self.parse_keyword(Keyword::VARIABLES) | |
| && dialect_of!(self is MySqlDialect | GenericDialect) | |
| { | |
| Ok(Statement::ShowVariables { | |
| filter: self.parse_show_statement_filter()?, | |
| session, | |
| global, | |
| }) | |
| } else if self.parse_keyword(Keyword::STATUS) | |
| && dialect_of!(self is MySqlDialect | GenericDialect) | |
| { | |
| Ok(Statement::ShowStatus { | |
| filter: self.parse_show_statement_filter()?, | |
| session, | |
| global, | |
| }) | |
| } else if self.parse_keyword(Keyword::CATALOGS) { | |
| self.parse_show_catalogs(terse) | |
| } else if self.parse_keyword(Keyword::DATABASES) { | |
| self.parse_show_databases(terse) | |
| } else if self.parse_keyword(Keyword::SCHEMAS) { | |
| self.parse_show_schemas(terse) | |
| } else if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { | |
| self.parse_show_charset(false) | |
| } else if self.parse_keyword(Keyword::CHARSET) { | |
| self.parse_show_charset(true) | |
| } else { | |
| Ok(Statement::ShowVariable { | |
| variable: self.parse_identifiers()?, | |
| }) | |
| } | |
| } | |
| fn parse_show_charset(&mut self, is_shorthand: bool) -> Result<Statement, ParserError> { | |
| // parse one of keywords | |
| Ok(Statement::ShowCharset(ShowCharset { | |
| is_shorthand, | |
| filter: self.parse_show_statement_filter()?, | |
| })) | |
| } | |
| fn parse_show_catalogs(&mut self, terse: bool) -> Result<Statement, ParserError> { | |
| let history = self.parse_keyword(Keyword::HISTORY); | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowCatalogs { | |
| terse, | |
| history, | |
| show_options, | |
| }) | |
| } | |
| fn parse_show_databases(&mut self, terse: bool) -> Result<Statement, ParserError> { | |
| let history = self.parse_keyword(Keyword::HISTORY); | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowDatabases { | |
| terse, | |
| history, | |
| show_options, | |
| }) | |
| } | |
| fn parse_show_schemas(&mut self, terse: bool) -> Result<Statement, ParserError> { | |
| let history = self.parse_keyword(Keyword::HISTORY); | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowSchemas { | |
| terse, | |
| history, | |
| show_options, | |
| }) | |
| } | |
| /// Parse `SHOW CREATE <object>` returning the corresponding `ShowCreate` statement. | |
| pub fn parse_show_create(&mut self) -> Result<Statement, ParserError> { | |
| let obj_type = match self.expect_one_of_keywords(&[ | |
| Keyword::TABLE, | |
| Keyword::TRIGGER, | |
| Keyword::FUNCTION, | |
| Keyword::PROCEDURE, | |
| Keyword::EVENT, | |
| Keyword::VIEW, | |
| ])? { | |
| Keyword::TABLE => Ok(ShowCreateObject::Table), | |
| Keyword::TRIGGER => Ok(ShowCreateObject::Trigger), | |
| Keyword::FUNCTION => Ok(ShowCreateObject::Function), | |
| Keyword::PROCEDURE => Ok(ShowCreateObject::Procedure), | |
| Keyword::EVENT => Ok(ShowCreateObject::Event), | |
| Keyword::VIEW => Ok(ShowCreateObject::View), | |
| keyword => Err(ParserError::ParserError(format!( | |
| "Unable to map keyword to ShowCreateObject: {keyword:?}" | |
| ))), | |
| }?; | |
| let obj_name = self.parse_object_name(false)?; | |
| Ok(Statement::ShowCreate { obj_type, obj_name }) | |
| } | |
| /// Parse `SHOW COLUMNS`/`SHOW FIELDS` and return a `ShowColumns` statement. | |
| pub fn parse_show_columns( | |
| &mut self, | |
| extended: bool, | |
| full: bool, | |
| ) -> Result<Statement, ParserError> { | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowColumns { | |
| extended, | |
| full, | |
| show_options, | |
| }) | |
| } | |
| fn parse_show_tables( | |
| &mut self, | |
| terse: bool, | |
| extended: bool, | |
| full: bool, | |
| external: bool, | |
| ) -> Result<Statement, ParserError> { | |
| let history = !external && self.parse_keyword(Keyword::HISTORY); | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowTables { | |
| terse, | |
| history, | |
| extended, | |
| full, | |
| external, | |
| show_options, | |
| }) | |
| } | |
| fn parse_show_views( | |
| &mut self, | |
| terse: bool, | |
| materialized: bool, | |
| ) -> Result<Statement, ParserError> { | |
| let show_options = self.parse_show_stmt_options()?; | |
| Ok(Statement::ShowViews { | |
| materialized, | |
| terse, | |
| show_options, | |
| }) | |
| } | |
| /// Parse `SHOW FUNCTIONS` and optional filter. | |
| pub fn parse_show_functions(&mut self) -> Result<Statement, ParserError> { | |
| let filter = self.parse_show_statement_filter()?; | |
| Ok(Statement::ShowFunctions { filter }) | |
| } | |
| /// Parse `SHOW COLLATION` and optional filter. | |
| pub fn parse_show_collation(&mut self) -> Result<Statement, ParserError> { | |
| let filter = self.parse_show_statement_filter()?; | |
| Ok(Statement::ShowCollation { filter }) | |
| } | |
| /// Parse an optional filter used by `SHOW` statements (LIKE, ILIKE, WHERE, or literal). | |
| pub fn parse_show_statement_filter( | |
| &mut self, | |
| ) -> Result<Option<ShowStatementFilter>, ParserError> { | |
| if self.parse_keyword(Keyword::LIKE) { | |
| Ok(Some(ShowStatementFilter::Like( | |
| self.parse_literal_string()?, | |
| ))) | |
| } else if self.parse_keyword(Keyword::ILIKE) { | |
| Ok(Some(ShowStatementFilter::ILike( | |
| self.parse_literal_string()?, | |
| ))) | |
| } else if self.parse_keyword(Keyword::WHERE) { | |
| Ok(Some(ShowStatementFilter::Where(self.parse_expr()?))) | |
| } else { | |
| self.maybe_parse(|parser| -> Result<String, ParserError> { | |
| parser.parse_literal_string() | |
| })? | |
| .map_or(Ok(None), |filter| { | |
| Ok(Some(ShowStatementFilter::NoKeyword(filter))) | |
| }) | |
| } | |
| } | |
| /// Parse a `USE` statement (database/catalog/schema/warehouse/role selection). | |
| pub fn parse_use(&mut self) -> Result<Statement, ParserError> { | |
| // Determine which keywords are recognized by the current dialect | |
| let parsed_keyword = if dialect_of!(self is HiveDialect) { | |
| // HiveDialect accepts USE DEFAULT; statement without any db specified | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| return Ok(Statement::Use(Use::Default)); | |
| } | |
| None // HiveDialect doesn't expect any other specific keyword after `USE` | |
| } else if dialect_of!(self is DatabricksDialect) { | |
| self.parse_one_of_keywords(&[Keyword::CATALOG, Keyword::DATABASE, Keyword::SCHEMA]) | |
| } else if dialect_of!(self is SnowflakeDialect) { | |
| self.parse_one_of_keywords(&[ | |
| Keyword::DATABASE, | |
| Keyword::SCHEMA, | |
| Keyword::WAREHOUSE, | |
| Keyword::ROLE, | |
| Keyword::SECONDARY, | |
| ]) | |
| } else { | |
| None // No specific keywords for other dialects, including GenericDialect | |
| }; | |
| let result = if matches!(parsed_keyword, Some(Keyword::SECONDARY)) { | |
| self.parse_secondary_roles()? | |
| } else { | |
| let obj_name = self.parse_object_name(false)?; | |
| match parsed_keyword { | |
| Some(Keyword::CATALOG) => Use::Catalog(obj_name), | |
| Some(Keyword::DATABASE) => Use::Database(obj_name), | |
| Some(Keyword::SCHEMA) => Use::Schema(obj_name), | |
| Some(Keyword::WAREHOUSE) => Use::Warehouse(obj_name), | |
| Some(Keyword::ROLE) => Use::Role(obj_name), | |
| _ => Use::Object(obj_name), | |
| } | |
| }; | |
| Ok(Statement::Use(result)) | |
| } | |
| fn parse_secondary_roles(&mut self) -> Result<Use, ParserError> { | |
| self.expect_one_of_keywords(&[Keyword::ROLES, Keyword::ROLE])?; | |
| if self.parse_keyword(Keyword::NONE) { | |
| Ok(Use::SecondaryRoles(SecondaryRoles::None)) | |
| } else if self.parse_keyword(Keyword::ALL) { | |
| Ok(Use::SecondaryRoles(SecondaryRoles::All)) | |
| } else { | |
| let roles = self.parse_comma_separated(|parser| parser.parse_identifier())?; | |
| Ok(Use::SecondaryRoles(SecondaryRoles::List(roles))) | |
| } | |
| } | |
| /// Parse a table factor followed by any join clauses, returning `TableWithJoins`. | |
| pub fn parse_table_and_joins(&mut self) -> Result<TableWithJoins, ParserError> { | |
| let relation = self.parse_table_factor()?; | |
| // Note that for keywords to be properly handled here, they need to be | |
| // added to `RESERVED_FOR_TABLE_ALIAS`, otherwise they may be parsed as | |
| // a table alias. | |
| let joins = self.parse_joins()?; | |
| Ok(TableWithJoins { relation, joins }) | |
| } | |
| fn parse_joins(&mut self) -> Result<Vec<Join>, ParserError> { | |
| let mut joins = vec![]; | |
| loop { | |
| let global = self.parse_keyword(Keyword::GLOBAL); | |
| let join = if self.parse_keyword(Keyword::CROSS) { | |
| let join_operator = if self.parse_keyword(Keyword::JOIN) { | |
| JoinOperator::CrossJoin(JoinConstraint::None) | |
| } else if self.parse_keyword(Keyword::APPLY) { | |
| // MSSQL extension, similar to CROSS JOIN LATERAL | |
| JoinOperator::CrossApply | |
| } else { | |
| return self.expected_ref("JOIN or APPLY after CROSS", self.peek_token_ref()); | |
| }; | |
| let relation = self.parse_table_factor()?; | |
| let join_operator = if matches!(join_operator, JoinOperator::CrossJoin(_)) | |
| && self.dialect.supports_cross_join_constraint() | |
| { | |
| let constraint = self.parse_join_constraint(false)?; | |
| JoinOperator::CrossJoin(constraint) | |
| } else { | |
| join_operator | |
| }; | |
| Join { | |
| relation, | |
| global, | |
| join_operator, | |
| } | |
| } else if self.parse_keyword(Keyword::OUTER) { | |
| // MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1 | |
| self.expect_keyword_is(Keyword::APPLY)?; | |
| Join { | |
| relation: self.parse_table_factor()?, | |
| global, | |
| join_operator: JoinOperator::OuterApply, | |
| } | |
| } else if self.parse_keyword(Keyword::ASOF) { | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| let relation = self.parse_table_factor()?; | |
| self.expect_keyword_is(Keyword::MATCH_CONDITION)?; | |
| let match_condition = self.parse_parenthesized(Self::parse_expr)?; | |
| Join { | |
| relation, | |
| global, | |
| join_operator: JoinOperator::AsOf { | |
| match_condition, | |
| constraint: self.parse_join_constraint(false)?, | |
| }, | |
| } | |
| } else if self.dialect.supports_array_join_syntax() | |
| && self.parse_keywords(&[Keyword::INNER, Keyword::ARRAY, Keyword::JOIN]) | |
| { | |
| // ClickHouse: INNER ARRAY JOIN | |
| Join { | |
| relation: self.parse_table_factor()?, | |
| global, | |
| join_operator: JoinOperator::InnerArrayJoin, | |
| } | |
| } else if self.dialect.supports_array_join_syntax() | |
| && self.parse_keywords(&[Keyword::LEFT, Keyword::ARRAY, Keyword::JOIN]) | |
| { | |
| // ClickHouse: LEFT ARRAY JOIN | |
| Join { | |
| relation: self.parse_table_factor()?, | |
| global, | |
| join_operator: JoinOperator::LeftArrayJoin, | |
| } | |
| } else if self.dialect.supports_array_join_syntax() | |
| && self.parse_keywords(&[Keyword::ARRAY, Keyword::JOIN]) | |
| { | |
| // ClickHouse: ARRAY JOIN | |
| Join { | |
| relation: self.parse_table_factor()?, | |
| global, | |
| join_operator: JoinOperator::ArrayJoin, | |
| } | |
| } else { | |
| let natural = self.parse_keyword(Keyword::NATURAL); | |
| let peek_keyword = if let Token::Word(w) = &self.peek_token_ref().token { | |
| w.keyword | |
| } else { | |
| Keyword::NoKeyword | |
| }; | |
| let join_operator_type = match peek_keyword { | |
| Keyword::INNER | Keyword::JOIN => { | |
| let inner = self.parse_keyword(Keyword::INNER); // [ INNER ] | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| if inner { | |
| JoinOperator::Inner | |
| } else { | |
| JoinOperator::Join | |
| } | |
| } | |
| kw @ Keyword::LEFT | kw @ Keyword::RIGHT => { | |
| let _ = self.next_token(); // consume LEFT/RIGHT | |
| let is_left = kw == Keyword::LEFT; | |
| let join_type = self.parse_one_of_keywords(&[ | |
| Keyword::OUTER, | |
| Keyword::SEMI, | |
| Keyword::ANTI, | |
| Keyword::JOIN, | |
| ]); | |
| match join_type { | |
| Some(Keyword::OUTER) => { | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| if is_left { | |
| JoinOperator::LeftOuter | |
| } else { | |
| JoinOperator::RightOuter | |
| } | |
| } | |
| Some(Keyword::SEMI) => { | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| if is_left { | |
| JoinOperator::LeftSemi | |
| } else { | |
| JoinOperator::RightSemi | |
| } | |
| } | |
| Some(Keyword::ANTI) => { | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| if is_left { | |
| JoinOperator::LeftAnti | |
| } else { | |
| JoinOperator::RightAnti | |
| } | |
| } | |
| Some(Keyword::JOIN) => { | |
| if is_left { | |
| JoinOperator::Left | |
| } else { | |
| JoinOperator::Right | |
| } | |
| } | |
| _ => { | |
| return Err(ParserError::ParserError(format!( | |
| "expected OUTER, SEMI, ANTI or JOIN after {kw:?}" | |
| ))) | |
| } | |
| } | |
| } | |
| Keyword::ANTI => { | |
| let _ = self.next_token(); // consume ANTI | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| JoinOperator::Anti | |
| } | |
| Keyword::SEMI => { | |
| let _ = self.next_token(); // consume SEMI | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| JoinOperator::Semi | |
| } | |
| Keyword::FULL => { | |
| let _ = self.next_token(); // consume FULL | |
| let _ = self.parse_keyword(Keyword::OUTER); // [ OUTER ] | |
| self.expect_keyword_is(Keyword::JOIN)?; | |
| JoinOperator::FullOuter | |
| } | |
| Keyword::OUTER => { | |
| return self.expected_ref("LEFT, RIGHT, or FULL", self.peek_token_ref()); | |
| } | |
| Keyword::STRAIGHT_JOIN => { | |
| let _ = self.next_token(); // consume STRAIGHT_JOIN | |
| JoinOperator::StraightJoin | |
| } | |
| _ if natural => { | |
| return self | |
| .expected_ref("a join type after NATURAL", self.peek_token_ref()); | |
| } | |
| _ => break, | |
| }; | |
| let mut relation = self.parse_table_factor()?; | |
| if !self | |
| .dialect | |
| .supports_left_associative_joins_without_parens() | |
| && !natural | |
| && self.peek_parens_less_nested_join() | |
| { | |
| let joins = self.parse_joins()?; | |
| relation = TableFactor::NestedJoin { | |
| table_with_joins: Box::new(TableWithJoins { relation, joins }), | |
| alias: None, | |
| }; | |
| } | |
| let join_constraint = self.parse_join_constraint(natural)?; | |
| Join { | |
| relation, | |
| global, | |
| join_operator: join_operator_type(join_constraint), | |
| } | |
| }; | |
| joins.push(join); | |
| } | |
| Ok(joins) | |
| } | |
| fn peek_parens_less_nested_join(&self) -> bool { | |
| matches!( | |
| self.peek_token_ref().token, | |
| Token::Word(Word { | |
| keyword: Keyword::JOIN | |
| | Keyword::INNER | |
| | Keyword::LEFT | |
| | Keyword::RIGHT | |
| | Keyword::FULL, | |
| .. | |
| }) | |
| ) | |
| } | |
| /// A table name or a parenthesized subquery, followed by optional `[AS] alias` | |
| #[cfg_attr(feature = "recursive-protection", recursive::recursive)] | |
| pub fn parse_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| let _guard = self.recursion_counter.try_decrease()?; | |
| if self.parse_keyword(Keyword::LATERAL) { | |
| // LATERAL must always be followed by a subquery or table function. | |
| if self.consume_token(&Token::LParen) { | |
| self.parse_derived_table_factor(Lateral) | |
| } else { | |
| let name = self.parse_object_name(false)?; | |
| self.expect_token(&Token::LParen)?; | |
| let args = self.parse_optional_args()?; | |
| let with_ordinality = self.parse_keywords(&[Keyword::WITH, Keyword::ORDINALITY]); | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::Function { | |
| lateral: true, | |
| name, | |
| args, | |
| with_ordinality, | |
| alias, | |
| }) | |
| } | |
| } else if self.parse_keyword(Keyword::TABLE) { | |
| // parse table function (SELECT * FROM TABLE (<expr>) [ AS <alias> ]) | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::TableFunction { expr, alias }) | |
| } else if self.consume_token(&Token::LParen) { | |
| // A left paren introduces either a derived table (i.e., a subquery) | |
| // or a nested join. It's nearly impossible to determine ahead of | |
| // time which it is... so we just try to parse both. | |
| // | |
| // Here's an example that demonstrates the complexity: | |
| // /-------------------------------------------------------\ | |
| // | /-----------------------------------\ | | |
| // SELECT * FROM ( ( ( (SELECT 1) UNION (SELECT 2) ) AS t1 NATURAL JOIN t2 ) ) | |
| // ^ ^ ^ ^ | |
| // | | | | | |
| // | | | | | |
| // | | | (4) belongs to a SetExpr::Query inside the subquery | |
| // | | (3) starts a derived table (subquery) | |
| // | (2) starts a nested join | |
| // (1) an additional set of parens around a nested join | |
| // | |
| // If the recently consumed '(' starts a derived table, the call to | |
| // `parse_derived_table_factor` below will return success after parsing the | |
| // subquery, followed by the closing ')', and the alias of the derived table. | |
| // In the example above this is case (3). | |
| // | |
| // Memoize failures to break the 2^N work on inputs like | |
| // `FROM ((((...`, where the nested-join fallback recurses back into | |
| // `parse_table_factor` and re-attempts the same speculative parse. | |
| let derived_pos = self.index; | |
| let derived = if self | |
| .failed_derived_table_factor_positions | |
| .contains(&derived_pos) | |
| { | |
| None | |
| } else { | |
| match self.maybe_parse(|parser| parser.parse_derived_table_factor(NotLateral))? { | |
| Some(t) => Some(t), | |
| None => { | |
| self.failed_derived_table_factor_positions | |
| .insert(derived_pos); | |
| None | |
| } | |
| } | |
| }; | |
| if let Some(mut table) = derived { | |
| while let Some(kw) = self.parse_one_of_keywords(&[Keyword::PIVOT, Keyword::UNPIVOT]) | |
| { | |
| table = match kw { | |
| Keyword::PIVOT => self.parse_pivot_table_factor(table)?, | |
| Keyword::UNPIVOT => self.parse_unpivot_table_factor(table)?, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in pivot/unpivot"), | |
| )), | |
| } | |
| } | |
| return Ok(table); | |
| } | |
| // A parsing error from `parse_derived_table_factor` indicates that the '(' we've | |
| // recently consumed does not start a derived table (cases 1, 2, or 4). | |
| // `maybe_parse` will ignore such an error and rewind to be after the opening '('. | |
| // Inside the parentheses we expect to find an (A) table factor | |
| // followed by some joins or (B) another level of nesting. | |
| let mut table_and_joins = self.parse_table_and_joins()?; | |
| if !table_and_joins.joins.is_empty() { | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::NestedJoin { | |
| table_with_joins: Box::new(table_and_joins), | |
| alias, | |
| }) // (A) | |
| } else if let TableFactor::NestedJoin { | |
| table_with_joins: _, | |
| alias: _, | |
| } = &table_and_joins.relation | |
| { | |
| // (B): `table_and_joins` (what we found inside the parentheses) | |
| // is a nested join `(foo JOIN bar)`, not followed by other joins. | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::NestedJoin { | |
| table_with_joins: Box::new(table_and_joins), | |
| alias, | |
| }) | |
| } else if self.dialect.supports_parens_around_table_factor() { | |
| // Dialect-specific behavior: Snowflake diverges from the | |
| // standard and from most of the other implementations by | |
| // allowing extra parentheses not only around a join (B), but | |
| // around lone table names (e.g. `FROM (mytable [AS alias])`) | |
| // and around derived tables (e.g. `FROM ((SELECT ...) | |
| // [AS alias])`) as well. | |
| self.expect_token(&Token::RParen)?; | |
| if let Some(outer_alias) = self.maybe_parse_table_alias()? { | |
| // Snowflake also allows specifying an alias *after* parens | |
| // e.g. `FROM (mytable) AS alias` | |
| match &mut table_and_joins.relation { | |
| TableFactor::Derived { alias, .. } | |
| | TableFactor::Table { alias, .. } | |
| | TableFactor::Function { alias, .. } | |
| | TableFactor::UNNEST { alias, .. } | |
| | TableFactor::JsonTable { alias, .. } | |
| | TableFactor::XmlTable { alias, .. } | |
| | TableFactor::OpenJsonTable { alias, .. } | |
| | TableFactor::TableFunction { alias, .. } | |
| | TableFactor::Pivot { alias, .. } | |
| | TableFactor::Unpivot { alias, .. } | |
| | TableFactor::MatchRecognize { alias, .. } | |
| | TableFactor::SemanticView { alias, .. } | |
| | TableFactor::NestedJoin { alias, .. } => { | |
| // but not `FROM (mytable AS alias1) AS alias2`. | |
| if let Some(inner_alias) = alias { | |
| return Err(ParserError::ParserError(format!( | |
| "duplicate alias {inner_alias}" | |
| ))); | |
| } | |
| // Act as if the alias was specified normally next | |
| // to the table name: `(mytable) AS alias` -> | |
| // `(mytable AS alias)` | |
| alias.replace(outer_alias); | |
| } | |
| TableFactor::UnpivotExpr { .. } => { | |
| return Err(ParserError::ParserError( | |
| "alias after parenthesized UNPIVOT expression is not supported" | |
| .to_string(), | |
| )) | |
| } | |
| }; | |
| } | |
| // Do not store the extra set of parens in the AST | |
| Ok(table_and_joins.relation) | |
| } else { | |
| // The SQL spec prohibits derived tables and bare tables from | |
| // appearing alone in parentheses (e.g. `FROM (mytable)`) | |
| self.expected_ref("joined table", self.peek_token_ref()) | |
| } | |
| } else if self.dialect.supports_values_as_table_factor() | |
| && matches!( | |
| self.peek_tokens(), | |
| [ | |
| Token::Word(Word { | |
| keyword: Keyword::VALUES, | |
| .. | |
| }), | |
| Token::LParen | |
| ] | |
| ) | |
| { | |
| self.expect_keyword_is(Keyword::VALUES)?; | |
| // Snowflake and Databricks allow syntax like below: | |
| // SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2) | |
| // where there are no parentheses around the VALUES clause. | |
| let values = SetExpr::Values(self.parse_values(false, false)?); | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::Derived { | |
| lateral: false, | |
| subquery: Box::new(Query { | |
| with: None, | |
| body: Box::new(values), | |
| order_by: None, | |
| limit_clause: None, | |
| fetch: None, | |
| locks: vec![], | |
| for_clause: None, | |
| settings: None, | |
| format_clause: None, | |
| pipe_operators: vec![], | |
| }), | |
| alias, | |
| sample: None, | |
| }) | |
| } else if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | GenericDialect) | |
| && self.parse_keyword(Keyword::UNNEST) | |
| { | |
| self.expect_token(&Token::LParen)?; | |
| let array_exprs = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| let with_ordinality = self.parse_keywords(&[Keyword::WITH, Keyword::ORDINALITY]); | |
| let alias = match self.maybe_parse_table_alias() { | |
| Ok(Some(alias)) => Some(alias), | |
| Ok(None) => None, | |
| Err(e) => return Err(e), | |
| }; | |
| let with_offset = match self.expect_keywords(&[Keyword::WITH, Keyword::OFFSET]) { | |
| Ok(()) => true, | |
| Err(_) => false, | |
| }; | |
| let with_offset_alias = if with_offset { | |
| match self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS) { | |
| Ok(Some(alias)) => Some(alias), | |
| Ok(None) => None, | |
| Err(e) => return Err(e), | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(TableFactor::UNNEST { | |
| alias, | |
| array_exprs, | |
| with_offset, | |
| with_offset_alias, | |
| with_ordinality, | |
| }) | |
| } else if self.dialect.supports_unpivot_expr() && self.peek_keyword(Keyword::UNPIVOT) { | |
| self.parse_unpivot_expr_table_factor() | |
| } else if self.parse_keyword_with_tokens(Keyword::JSON_TABLE, &[Token::LParen]) { | |
| let json_expr = self.parse_expr()?; | |
| self.expect_token(&Token::Comma)?; | |
| let json_path = self.parse_value()?; | |
| self.expect_keyword_is(Keyword::COLUMNS)?; | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(Parser::parse_json_table_column_def)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::JsonTable { | |
| json_expr, | |
| json_path, | |
| columns, | |
| alias, | |
| }) | |
| } else if self.parse_keyword_with_tokens(Keyword::OPENJSON, &[Token::LParen]) { | |
| self.prev_token(); | |
| self.parse_open_json_table_factor() | |
| } else if self.parse_keyword_with_tokens(Keyword::XMLTABLE, &[Token::LParen]) { | |
| self.prev_token(); | |
| self.parse_xml_table_factor() | |
| } else if self.dialect.supports_semantic_view_table_factor() | |
| && self.peek_keyword_with_tokens(Keyword::SEMANTIC_VIEW, &[Token::LParen]) | |
| { | |
| self.parse_semantic_view_table_factor() | |
| } else if self.dialect.supports_stages() && self.peek_token_ref().token == Token::AtSign { | |
| // Stage reference: @mystage or @namespace.stage (e.g. Snowflake) | |
| self.parse_snowflake_stage_table_factor() | |
| } else { | |
| let name = self.parse_object_name(true)?; | |
| let json_path = match &self.peek_token_ref().token { | |
| Token::LBracket if self.dialect.supports_partiql() => Some(self.parse_json_path()?), | |
| _ => None, | |
| }; | |
| let partitions: Vec<Ident> = if self.dialect.supports_table_partitions() | |
| && self.parse_keyword(Keyword::PARTITION) | |
| { | |
| self.parse_parenthesized_identifiers()? | |
| } else { | |
| vec![] | |
| }; | |
| // Parse potential version qualifier | |
| let version = self.maybe_parse_table_version()?; | |
| // Postgres, MSSQL, ClickHouse: table-valued functions: | |
| let args = if self.consume_token(&Token::LParen) { | |
| Some(self.parse_table_function_args()?) | |
| } else { | |
| None | |
| }; | |
| let with_ordinality = self.parse_keywords(&[Keyword::WITH, Keyword::ORDINALITY]); | |
| let mut sample = None; | |
| if self.dialect.supports_table_sample_before_alias() { | |
| if let Some(parsed_sample) = self.maybe_parse_table_sample()? { | |
| sample = Some(TableSampleKind::BeforeTableAlias(parsed_sample)); | |
| } | |
| } | |
| let alias = self.maybe_parse_table_alias()?; | |
| // MYSQL-specific table hints: | |
| let index_hints = if self.dialect.supports_table_hints() { | |
| self.maybe_parse(|p| p.parse_table_index_hints())? | |
| .unwrap_or(vec![]) | |
| } else { | |
| vec![] | |
| }; | |
| // MSSQL-specific table hints: | |
| let mut with_hints = vec![]; | |
| if self.parse_keyword(Keyword::WITH) { | |
| if self.consume_token(&Token::LParen) { | |
| with_hints = self.parse_comma_separated(Parser::parse_expr)?; | |
| self.expect_token(&Token::RParen)?; | |
| } else { | |
| // rewind, as WITH may belong to the next statement's CTE | |
| self.prev_token(); | |
| } | |
| }; | |
| if !self.dialect.supports_table_sample_before_alias() { | |
| if let Some(parsed_sample) = self.maybe_parse_table_sample()? { | |
| sample = Some(TableSampleKind::AfterTableAlias(parsed_sample)); | |
| } | |
| } | |
| let mut table = TableFactor::Table { | |
| name, | |
| alias, | |
| args, | |
| with_hints, | |
| version, | |
| partitions, | |
| with_ordinality, | |
| json_path, | |
| sample, | |
| index_hints, | |
| }; | |
| while let Some(kw) = self.parse_one_of_keywords(&[Keyword::PIVOT, Keyword::UNPIVOT]) { | |
| table = match kw { | |
| Keyword::PIVOT => self.parse_pivot_table_factor(table)?, | |
| Keyword::UNPIVOT => self.parse_unpivot_table_factor(table)?, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in pivot/unpivot"), | |
| )), | |
| } | |
| } | |
| if self.dialect.supports_match_recognize() | |
| && self.parse_keyword(Keyword::MATCH_RECOGNIZE) | |
| { | |
| table = self.parse_match_recognize(table)?; | |
| } | |
| Ok(table) | |
| } | |
| } | |
| /// Parse a Snowflake stage reference as a table factor. | |
| /// Handles syntax like: `@mystage1 (file_format => 'myformat', pattern => '...')` | |
| /// | |
| /// See: <https://docs.snowflake.com/en/user-guide/querying-stage> | |
| fn parse_snowflake_stage_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| // Parse the stage name starting with @ | |
| let name = crate::dialect::parse_snowflake_stage_name(self)?; | |
| // Parse optional stage options like (file_format => 'myformat', pattern => '...') | |
| let args = if self.consume_token(&Token::LParen) { | |
| Some(self.parse_table_function_args()?) | |
| } else { | |
| None | |
| }; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::Table { | |
| name, | |
| alias, | |
| args, | |
| with_hints: vec![], | |
| version: None, | |
| partitions: vec![], | |
| with_ordinality: false, | |
| json_path: None, | |
| sample: None, | |
| index_hints: vec![], | |
| }) | |
| } | |
| fn maybe_parse_table_sample(&mut self) -> Result<Option<Box<TableSample>>, ParserError> { | |
| let modifier = if self.parse_keyword(Keyword::TABLESAMPLE) { | |
| TableSampleModifier::TableSample | |
| } else if self.parse_keyword(Keyword::SAMPLE) { | |
| TableSampleModifier::Sample | |
| } else { | |
| return Ok(None); | |
| }; | |
| self.parse_table_sample(modifier).map(Some) | |
| } | |
| fn parse_table_sample( | |
| &mut self, | |
| modifier: TableSampleModifier, | |
| ) -> Result<Box<TableSample>, ParserError> { | |
| let name = match self.parse_one_of_keywords(&[ | |
| Keyword::BERNOULLI, | |
| Keyword::ROW, | |
| Keyword::SYSTEM, | |
| Keyword::BLOCK, | |
| ]) { | |
| Some(Keyword::BERNOULLI) => Some(TableSampleMethod::Bernoulli), | |
| Some(Keyword::ROW) => Some(TableSampleMethod::Row), | |
| Some(Keyword::SYSTEM) => Some(TableSampleMethod::System), | |
| Some(Keyword::BLOCK) => Some(TableSampleMethod::Block), | |
| _ => None, | |
| }; | |
| let parenthesized = self.consume_token(&Token::LParen); | |
| let (quantity, bucket) = if parenthesized && self.parse_keyword(Keyword::BUCKET) { | |
| let selected_bucket = self.parse_number_value()?; | |
| self.expect_keywords(&[Keyword::OUT, Keyword::OF])?; | |
| let total = self.parse_number_value()?; | |
| let on = if self.parse_keyword(Keyword::ON) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| ( | |
| None, | |
| Some(TableSampleBucket { | |
| bucket: selected_bucket, | |
| total, | |
| on, | |
| }), | |
| ) | |
| } else { | |
| let value = match self.maybe_parse(|p| p.parse_expr())? { | |
| Some(num) => num, | |
| None => { | |
| let next_token = self.next_token(); | |
| if let Token::Word(w) = next_token.token { | |
| Expr::Value(Value::Placeholder(w.value).with_span(next_token.span)) | |
| } else { | |
| return parser_err!( | |
| "Expecting number or byte length e.g. 100M", | |
| self.peek_token_ref().span.start | |
| ); | |
| } | |
| } | |
| }; | |
| let unit = if self.parse_keyword(Keyword::ROWS) { | |
| Some(TableSampleUnit::Rows) | |
| } else if self.parse_keyword(Keyword::PERCENT) { | |
| Some(TableSampleUnit::Percent) | |
| } else { | |
| None | |
| }; | |
| ( | |
| Some(TableSampleQuantity { | |
| parenthesized, | |
| value, | |
| unit, | |
| }), | |
| None, | |
| ) | |
| }; | |
| if parenthesized { | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| let seed = if self.parse_keyword(Keyword::REPEATABLE) { | |
| Some(self.parse_table_sample_seed(TableSampleSeedModifier::Repeatable)?) | |
| } else if self.parse_keyword(Keyword::SEED) { | |
| Some(self.parse_table_sample_seed(TableSampleSeedModifier::Seed)?) | |
| } else { | |
| None | |
| }; | |
| let offset = if self.parse_keyword(Keyword::OFFSET) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Box::new(TableSample { | |
| modifier, | |
| name, | |
| quantity, | |
| seed, | |
| bucket, | |
| offset, | |
| })) | |
| } | |
| fn parse_table_sample_seed( | |
| &mut self, | |
| modifier: TableSampleSeedModifier, | |
| ) -> Result<TableSampleSeed, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let value = self.parse_number_value()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(TableSampleSeed { modifier, value }) | |
| } | |
| /// Parses `OPENJSON( jsonExpression [ , path ] ) [ <with_clause> ]` clause, | |
| /// assuming the `OPENJSON` keyword was already consumed. | |
| fn parse_open_json_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let json_expr = self.parse_expr()?; | |
| let json_path = if self.consume_token(&Token::Comma) { | |
| Some(self.parse_value()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| let columns = if self.parse_keyword(Keyword::WITH) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(Parser::parse_openjson_table_column_def)?; | |
| self.expect_token(&Token::RParen)?; | |
| columns | |
| } else { | |
| Vec::new() | |
| }; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::OpenJsonTable { | |
| json_expr, | |
| json_path, | |
| columns, | |
| alias, | |
| }) | |
| } | |
| fn parse_xml_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let namespaces = if self.parse_keyword(Keyword::XMLNAMESPACES) { | |
| self.expect_token(&Token::LParen)?; | |
| let namespaces = self.parse_comma_separated(Parser::parse_xml_namespace_definition)?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_token(&Token::Comma)?; | |
| namespaces | |
| } else { | |
| vec![] | |
| }; | |
| let row_expression = self.parse_expr()?; | |
| let passing = self.parse_xml_passing_clause()?; | |
| self.expect_keyword_is(Keyword::COLUMNS)?; | |
| let columns = self.parse_comma_separated(Parser::parse_xml_table_column)?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::XmlTable { | |
| namespaces, | |
| row_expression, | |
| passing, | |
| columns, | |
| alias, | |
| }) | |
| } | |
| fn parse_xml_namespace_definition(&mut self) -> Result<XmlNamespaceDefinition, ParserError> { | |
| let uri = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let name = self.parse_identifier()?; | |
| Ok(XmlNamespaceDefinition { uri, name }) | |
| } | |
| fn parse_xml_table_column(&mut self) -> Result<XmlTableColumn, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let option = if self.parse_keyword(Keyword::FOR) { | |
| self.expect_keyword(Keyword::ORDINALITY)?; | |
| XmlTableColumnOption::ForOrdinality | |
| } else { | |
| let r#type = self.parse_data_type()?; | |
| let mut path = None; | |
| let mut default = None; | |
| if self.parse_keyword(Keyword::PATH) { | |
| path = Some(self.parse_expr()?); | |
| } | |
| if self.parse_keyword(Keyword::DEFAULT) { | |
| default = Some(self.parse_expr()?); | |
| } | |
| let not_null = self.parse_keywords(&[Keyword::NOT, Keyword::NULL]); | |
| if !not_null { | |
| // NULL is the default but can be specified explicitly | |
| let _ = self.parse_keyword(Keyword::NULL); | |
| } | |
| XmlTableColumnOption::NamedInfo { | |
| r#type, | |
| path, | |
| default, | |
| nullable: !not_null, | |
| } | |
| }; | |
| Ok(XmlTableColumn { name, option }) | |
| } | |
| fn parse_xml_passing_clause(&mut self) -> Result<XmlPassingClause, ParserError> { | |
| let mut arguments = vec![]; | |
| if self.parse_keyword(Keyword::PASSING) { | |
| loop { | |
| let by_value = | |
| self.parse_keyword(Keyword::BY) && self.expect_keyword(Keyword::VALUE).is_ok(); | |
| let expr = self.parse_expr()?; | |
| let alias = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| arguments.push(XmlPassingArgument { | |
| expr, | |
| alias, | |
| by_value, | |
| }); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| } | |
| Ok(XmlPassingClause { arguments }) | |
| } | |
| /// Parse a [TableFactor::SemanticView] | |
| fn parse_semantic_view_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| self.expect_keyword(Keyword::SEMANTIC_VIEW)?; | |
| self.expect_token(&Token::LParen)?; | |
| let name = self.parse_object_name(true)?; | |
| // Parse DIMENSIONS, METRICS, FACTS and WHERE clauses in flexible order | |
| let mut dimensions = Vec::new(); | |
| let mut metrics = Vec::new(); | |
| let mut facts = Vec::new(); | |
| let mut where_clause = None; | |
| while self.peek_token_ref().token != Token::RParen { | |
| if self.parse_keyword(Keyword::DIMENSIONS) { | |
| if !dimensions.is_empty() { | |
| return Err(ParserError::ParserError( | |
| "DIMENSIONS clause can only be specified once".to_string(), | |
| )); | |
| } | |
| dimensions = self.parse_comma_separated(Parser::parse_wildcard_expr)?; | |
| } else if self.parse_keyword(Keyword::METRICS) { | |
| if !metrics.is_empty() { | |
| return Err(ParserError::ParserError( | |
| "METRICS clause can only be specified once".to_string(), | |
| )); | |
| } | |
| metrics = self.parse_comma_separated(Parser::parse_wildcard_expr)?; | |
| } else if self.parse_keyword(Keyword::FACTS) { | |
| if !facts.is_empty() { | |
| return Err(ParserError::ParserError( | |
| "FACTS clause can only be specified once".to_string(), | |
| )); | |
| } | |
| facts = self.parse_comma_separated(Parser::parse_wildcard_expr)?; | |
| } else if self.parse_keyword(Keyword::WHERE) { | |
| if where_clause.is_some() { | |
| return Err(ParserError::ParserError( | |
| "WHERE clause can only be specified once".to_string(), | |
| )); | |
| } | |
| where_clause = Some(self.parse_expr()?); | |
| } else { | |
| let tok = self.peek_token_ref(); | |
| parser_err!( | |
| format!( | |
| "Expected one of DIMENSIONS, METRICS, FACTS or WHERE, got {}", | |
| tok.token | |
| ), | |
| tok.span.start | |
| )?; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::SemanticView { | |
| name, | |
| dimensions, | |
| metrics, | |
| facts, | |
| where_clause, | |
| alias, | |
| }) | |
| } | |
| fn parse_match_recognize(&mut self, table: TableFactor) -> Result<TableFactor, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let partition_by = if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let measures = if self.parse_keyword(Keyword::MEASURES) { | |
| self.parse_comma_separated(|p| { | |
| let expr = p.parse_expr()?; | |
| let _ = p.parse_keyword(Keyword::AS); | |
| let alias = p.parse_identifier()?; | |
| Ok(Measure { expr, alias }) | |
| })? | |
| } else { | |
| vec![] | |
| }; | |
| let rows_per_match = | |
| if self.parse_keywords(&[Keyword::ONE, Keyword::ROW, Keyword::PER, Keyword::MATCH]) { | |
| Some(RowsPerMatch::OneRow) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::ROWS, | |
| Keyword::PER, | |
| Keyword::MATCH, | |
| ]) { | |
| Some(RowsPerMatch::AllRows( | |
| if self.parse_keywords(&[Keyword::SHOW, Keyword::EMPTY, Keyword::MATCHES]) { | |
| Some(EmptyMatchesMode::Show) | |
| } else if self.parse_keywords(&[ | |
| Keyword::OMIT, | |
| Keyword::EMPTY, | |
| Keyword::MATCHES, | |
| ]) { | |
| Some(EmptyMatchesMode::Omit) | |
| } else if self.parse_keywords(&[ | |
| Keyword::WITH, | |
| Keyword::UNMATCHED, | |
| Keyword::ROWS, | |
| ]) { | |
| Some(EmptyMatchesMode::WithUnmatched) | |
| } else { | |
| None | |
| }, | |
| )) | |
| } else { | |
| None | |
| }; | |
| let after_match_skip = | |
| if self.parse_keywords(&[Keyword::AFTER, Keyword::MATCH, Keyword::SKIP]) { | |
| if self.parse_keywords(&[Keyword::PAST, Keyword::LAST, Keyword::ROW]) { | |
| Some(AfterMatchSkip::PastLastRow) | |
| } else if self.parse_keywords(&[Keyword::TO, Keyword::NEXT, Keyword::ROW]) { | |
| Some(AfterMatchSkip::ToNextRow) | |
| } else if self.parse_keywords(&[Keyword::TO, Keyword::FIRST]) { | |
| Some(AfterMatchSkip::ToFirst(self.parse_identifier()?)) | |
| } else if self.parse_keywords(&[Keyword::TO, Keyword::LAST]) { | |
| Some(AfterMatchSkip::ToLast(self.parse_identifier()?)) | |
| } else { | |
| let found = self.next_token(); | |
| return self.expected("after match skip option", found); | |
| } | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::PATTERN)?; | |
| let pattern = self.parse_parenthesized(Self::parse_pattern)?; | |
| self.expect_keyword_is(Keyword::DEFINE)?; | |
| let symbols = self.parse_comma_separated(|p| { | |
| let symbol = p.parse_identifier()?; | |
| p.expect_keyword_is(Keyword::AS)?; | |
| let definition = p.parse_expr()?; | |
| Ok(SymbolDefinition { symbol, definition }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::MatchRecognize { | |
| table: Box::new(table), | |
| partition_by, | |
| order_by, | |
| measures, | |
| rows_per_match, | |
| after_match_skip, | |
| pattern, | |
| symbols, | |
| alias, | |
| }) | |
| } | |
| fn parse_base_pattern(&mut self) -> Result<MatchRecognizePattern, ParserError> { | |
| match self.next_token().token { | |
| Token::Caret => Ok(MatchRecognizePattern::Symbol(MatchRecognizeSymbol::Start)), | |
| Token::Placeholder(s) if s == "$" => { | |
| Ok(MatchRecognizePattern::Symbol(MatchRecognizeSymbol::End)) | |
| } | |
| Token::LBrace => { | |
| self.expect_token(&Token::Minus)?; | |
| let symbol = self.parse_identifier().map(MatchRecognizeSymbol::Named)?; | |
| self.expect_token(&Token::Minus)?; | |
| self.expect_token(&Token::RBrace)?; | |
| Ok(MatchRecognizePattern::Exclude(symbol)) | |
| } | |
| Token::Word(Word { | |
| value, | |
| quote_style: None, | |
| .. | |
| }) if value == "PERMUTE" => { | |
| self.expect_token(&Token::LParen)?; | |
| let symbols = self.parse_comma_separated(|p| { | |
| p.parse_identifier().map(MatchRecognizeSymbol::Named) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(MatchRecognizePattern::Permute(symbols)) | |
| } | |
| Token::LParen => { | |
| let pattern = self.parse_pattern()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(MatchRecognizePattern::Group(Box::new(pattern))) | |
| } | |
| _ => { | |
| self.prev_token(); | |
| self.parse_identifier() | |
| .map(MatchRecognizeSymbol::Named) | |
| .map(MatchRecognizePattern::Symbol) | |
| } | |
| } | |
| } | |
| fn parse_repetition_pattern(&mut self) -> Result<MatchRecognizePattern, ParserError> { | |
| let pattern = self.parse_base_pattern()?; | |
| let token = self.next_token(); | |
| let quantifier = match token.token { | |
| Token::Mul => RepetitionQuantifier::ZeroOrMore, | |
| Token::Plus => RepetitionQuantifier::OneOrMore, | |
| Token::Placeholder(s) if s == "?" => RepetitionQuantifier::AtMostOne, | |
| Token::LBrace => { | |
| // quantifier is a range like {n} or {n,} or {,m} or {n,m} | |
| let token = self.next_token(); | |
| match token.token { | |
| Token::Comma => { | |
| let next_token = self.next_token(); | |
| let Token::Number(n, _) = next_token.token else { | |
| return self.expected("literal number", next_token); | |
| }; | |
| self.expect_token(&Token::RBrace)?; | |
| RepetitionQuantifier::AtMost(Self::parse(n, token.span.start)?) | |
| } | |
| Token::Number(n, _) if self.consume_token(&Token::Comma) => { | |
| let next_token = self.next_token(); | |
| match next_token.token { | |
| Token::Number(m, _) => { | |
| self.expect_token(&Token::RBrace)?; | |
| RepetitionQuantifier::Range( | |
| Self::parse(n, token.span.start)?, | |
| Self::parse(m, token.span.start)?, | |
| ) | |
| } | |
| Token::RBrace => { | |
| RepetitionQuantifier::AtLeast(Self::parse(n, token.span.start)?) | |
| } | |
| _ => return self.expected("} or upper bound", next_token), | |
| } | |
| } | |
| Token::Number(n, _) => { | |
| self.expect_token(&Token::RBrace)?; | |
| RepetitionQuantifier::Exactly(Self::parse(n, token.span.start)?) | |
| } | |
| _ => return self.expected("quantifier range", token), | |
| } | |
| } | |
| _ => { | |
| self.prev_token(); | |
| return Ok(pattern); | |
| } | |
| }; | |
| let quantifier = if self.consume_token(&Token::Placeholder("?".into())) { | |
| RepetitionQuantifier::Reluctant(Box::new(quantifier)) | |
| } else { | |
| quantifier | |
| }; | |
| Ok(MatchRecognizePattern::Repetition( | |
| Box::new(pattern), | |
| quantifier, | |
| )) | |
| } | |
| fn parse_concat_pattern(&mut self) -> Result<MatchRecognizePattern, ParserError> { | |
| let mut patterns = vec![self.parse_repetition_pattern()?]; | |
| while !matches!(self.peek_token_ref().token, Token::RParen | Token::Pipe) { | |
| patterns.push(self.parse_repetition_pattern()?); | |
| } | |
| match <[MatchRecognizePattern; 1]>::try_from(patterns) { | |
| Ok([pattern]) => Ok(pattern), | |
| Err(patterns) => Ok(MatchRecognizePattern::Concat(patterns)), | |
| } | |
| } | |
| fn parse_pattern(&mut self) -> Result<MatchRecognizePattern, ParserError> { | |
| let pattern = self.parse_concat_pattern()?; | |
| if self.consume_token(&Token::Pipe) { | |
| match self.parse_pattern()? { | |
| // flatten nested alternations | |
| MatchRecognizePattern::Alternation(mut patterns) => { | |
| patterns.insert(0, pattern); | |
| Ok(MatchRecognizePattern::Alternation(patterns)) | |
| } | |
| next => Ok(MatchRecognizePattern::Alternation(vec![pattern, next])), | |
| } | |
| } else { | |
| Ok(pattern) | |
| } | |
| } | |
| /// Parses a the timestamp version specifier (i.e. query historical data) | |
| pub fn maybe_parse_table_version(&mut self) -> Result<Option<TableVersion>, ParserError> { | |
| if self.dialect.supports_table_versioning() { | |
| if self.parse_keywords(&[Keyword::FOR, Keyword::SYSTEM_TIME, Keyword::AS, Keyword::OF]) | |
| { | |
| let expr = self.parse_expr()?; | |
| return Ok(Some(TableVersion::ForSystemTimeAsOf(expr))); | |
| } else if self.peek_keyword(Keyword::CHANGES) { | |
| return self.parse_table_version_changes().map(Some); | |
| } else if self.peek_keyword(Keyword::AT) || self.peek_keyword(Keyword::BEFORE) { | |
| let func_name = self.parse_object_name(true)?; | |
| let func = self.parse_function(func_name)?; | |
| return Ok(Some(TableVersion::Function(func))); | |
| } else if self.parse_keywords(&[Keyword::TIMESTAMP, Keyword::AS, Keyword::OF]) { | |
| let expr = self.parse_expr()?; | |
| return Ok(Some(TableVersion::TimestampAsOf(expr))); | |
| } else if self.parse_keywords(&[Keyword::VERSION, Keyword::AS, Keyword::OF]) { | |
| let expr = Expr::Value(self.parse_number_value()?); | |
| return Ok(Some(TableVersion::VersionAsOf(expr))); | |
| } | |
| } | |
| Ok(None) | |
| } | |
| /// Parses the Snowflake `CHANGES` clause for change tracking queries. | |
| /// | |
| /// Syntax: | |
| /// ```sql | |
| /// CHANGES (INFORMATION => DEFAULT) | |
| /// AT (TIMESTAMP => <expr>) | |
| /// [END (TIMESTAMP => <expr>)] | |
| /// ``` | |
| /// | |
| /// <https://docs.snowflake.com/en/sql-reference/constructs/changes> | |
| fn parse_table_version_changes(&mut self) -> Result<TableVersion, ParserError> { | |
| let changes_name = self.parse_object_name(true)?; | |
| let changes = self.parse_function(changes_name)?; | |
| let at_name = self.parse_object_name(true)?; | |
| let at = self.parse_function(at_name)?; | |
| let end = if self.peek_keyword(Keyword::END) { | |
| let end_name = self.parse_object_name(true)?; | |
| Some(self.parse_function(end_name)?) | |
| } else { | |
| None | |
| }; | |
| Ok(TableVersion::Changes { changes, at, end }) | |
| } | |
| /// Parses MySQL's JSON_TABLE column definition. | |
| /// For example: `id INT EXISTS PATH '$' DEFAULT '0' ON EMPTY ERROR ON ERROR` | |
| pub fn parse_json_table_column_def(&mut self) -> Result<JsonTableColumn, ParserError> { | |
| if self.parse_keyword(Keyword::NESTED) { | |
| let _has_path_keyword = self.parse_keyword(Keyword::PATH); | |
| let path = self.parse_value()?; | |
| self.expect_keyword_is(Keyword::COLUMNS)?; | |
| let columns = self.parse_parenthesized(|p| { | |
| p.parse_comma_separated(Self::parse_json_table_column_def) | |
| })?; | |
| return Ok(JsonTableColumn::Nested(JsonTableNestedColumn { | |
| path, | |
| columns, | |
| })); | |
| } | |
| let name = self.parse_identifier()?; | |
| if self.parse_keyword(Keyword::FOR) { | |
| self.expect_keyword_is(Keyword::ORDINALITY)?; | |
| return Ok(JsonTableColumn::ForOrdinality(name)); | |
| } | |
| let r#type = self.parse_data_type()?; | |
| let exists = self.parse_keyword(Keyword::EXISTS); | |
| self.expect_keyword_is(Keyword::PATH)?; | |
| let path = self.parse_value()?; | |
| let mut on_empty = None; | |
| let mut on_error = None; | |
| while let Some(error_handling) = self.parse_json_table_column_error_handling()? { | |
| if self.parse_keyword(Keyword::EMPTY) { | |
| on_empty = Some(error_handling); | |
| } else { | |
| self.expect_keyword_is(Keyword::ERROR)?; | |
| on_error = Some(error_handling); | |
| } | |
| } | |
| Ok(JsonTableColumn::Named(JsonTableNamedColumn { | |
| name, | |
| r#type, | |
| path, | |
| exists, | |
| on_empty, | |
| on_error, | |
| })) | |
| } | |
| /// Parses MSSQL's `OPENJSON WITH` column definition. | |
| /// | |
| /// ```sql | |
| /// colName type [ column_path ] [ AS JSON ] | |
| /// ``` | |
| /// | |
| /// Reference: <https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16#syntax> | |
| pub fn parse_openjson_table_column_def(&mut self) -> Result<OpenJsonTableColumn, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let r#type = self.parse_data_type()?; | |
| let path = if let Token::SingleQuotedString(path) = self.peek_token().token { | |
| self.next_token(); | |
| Some(path) | |
| } else { | |
| None | |
| }; | |
| let as_json = self.parse_keyword(Keyword::AS); | |
| if as_json { | |
| self.expect_keyword_is(Keyword::JSON)?; | |
| } | |
| Ok(OpenJsonTableColumn { | |
| name, | |
| r#type, | |
| path, | |
| as_json, | |
| }) | |
| } | |
| fn parse_json_table_column_error_handling( | |
| &mut self, | |
| ) -> Result<Option<JsonTableColumnErrorHandling>, ParserError> { | |
| let res = if self.parse_keyword(Keyword::NULL) { | |
| JsonTableColumnErrorHandling::Null | |
| } else if self.parse_keyword(Keyword::ERROR) { | |
| JsonTableColumnErrorHandling::Error | |
| } else if self.parse_keyword(Keyword::DEFAULT) { | |
| JsonTableColumnErrorHandling::Default(self.parse_value()?) | |
| } else { | |
| return Ok(None); | |
| }; | |
| self.expect_keyword_is(Keyword::ON)?; | |
| Ok(Some(res)) | |
| } | |
| /// Parse a derived table factor (a parenthesized subquery), handling optional LATERAL. | |
| pub fn parse_derived_table_factor( | |
| &mut self, | |
| lateral: IsLateral, | |
| ) -> Result<TableFactor, ParserError> { | |
| let subquery = self.parse_query()?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| // Parse optional SAMPLE clause after alias | |
| let sample = self | |
| .maybe_parse_table_sample()? | |
| .map(TableSampleKind::AfterTableAlias); | |
| Ok(TableFactor::Derived { | |
| lateral: match lateral { | |
| Lateral => true, | |
| NotLateral => false, | |
| }, | |
| subquery, | |
| alias, | |
| sample, | |
| }) | |
| } | |
| /// Parses an expression with an optional alias | |
| /// | |
| /// Examples: | |
| /// | |
| /// ```sql | |
| /// SUM(price) AS total_price | |
| /// ``` | |
| /// ```sql | |
| /// SUM(price) | |
| /// ``` | |
| /// | |
| /// Example | |
| /// ``` | |
| /// # use sqlparser::parser::{Parser, ParserError}; | |
| /// # use sqlparser::dialect::GenericDialect; | |
| /// # fn main() ->Result<(), ParserError> { | |
| /// let sql = r#"SUM("a") as "b""#; | |
| /// let mut parser = Parser::new(&GenericDialect).try_with_sql(sql)?; | |
| /// let expr_with_alias = parser.parse_expr_with_alias()?; | |
| /// assert_eq!(Some("b".to_string()), expr_with_alias.alias.map(|x|x.value)); | |
| /// # Ok(()) | |
| /// # } | |
| pub fn parse_expr_with_alias(&mut self) -> Result<ExprWithAlias, ParserError> { | |
| let expr = self.parse_expr()?; | |
| let alias = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| Ok(ExprWithAlias { expr, alias }) | |
| } | |
| /// Parse an expression followed by an optional alias; Unlike | |
| /// [Self::parse_expr_with_alias] the "AS" keyword between the expression | |
| /// and the alias is optional. | |
| fn parse_expr_with_alias_optional_as_keyword(&mut self) -> Result<ExprWithAlias, ParserError> { | |
| let expr = self.parse_expr()?; | |
| let alias = self.parse_identifier_optional_alias()?; | |
| Ok(ExprWithAlias { expr, alias }) | |
| } | |
| /// Parses a plain function call with an optional alias for the `PIVOT` clause | |
| fn parse_pivot_aggregate_function(&mut self) -> Result<ExprWithAlias, ParserError> { | |
| let function_name = match self.next_token().token { | |
| Token::Word(w) => Ok(w.value), | |
| _ => self.expected_ref("a function identifier", self.peek_token_ref()), | |
| }?; | |
| let expr = self.parse_function(ObjectName::from(vec![Ident::new(function_name)]))?; | |
| let alias = { | |
| fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool { | |
| // ~ for a PIVOT aggregate function the alias must not be a "FOR"; in any dialect | |
| kw != &Keyword::FOR && parser.dialect.is_select_item_alias(explicit, kw, parser) | |
| } | |
| self.parse_optional_alias_inner(None, validator)? | |
| }; | |
| Ok(ExprWithAlias { expr, alias }) | |
| } | |
| /// Parse a PIVOT table factor (ClickHouse/Oracle style pivot), returning a TableFactor. | |
| pub fn parse_pivot_table_factor( | |
| &mut self, | |
| table: TableFactor, | |
| ) -> Result<TableFactor, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let aggregate_functions = | |
| self.parse_comma_separated(Self::parse_pivot_aggregate_function)?; | |
| self.expect_keyword_is(Keyword::FOR)?; | |
| let value_column = if self.peek_token_ref().token == Token::LParen { | |
| self.parse_parenthesized_column_list_inner(Mandatory, false, |p| { | |
| p.parse_subexpr(self.dialect.prec_value(Precedence::Between)) | |
| })? | |
| } else { | |
| vec![self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?] | |
| }; | |
| self.expect_keyword_is(Keyword::IN)?; | |
| self.expect_token(&Token::LParen)?; | |
| let value_source = if self.parse_keyword(Keyword::ANY) { | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| PivotValueSource::Any(order_by) | |
| } else if self.peek_sub_query() { | |
| PivotValueSource::Subquery(self.parse_query()?) | |
| } else { | |
| PivotValueSource::List( | |
| self.parse_comma_separated(Self::parse_expr_with_alias_optional_as_keyword)?, | |
| ) | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| let default_on_null = | |
| if self.parse_keywords(&[Keyword::DEFAULT, Keyword::ON, Keyword::NULL]) { | |
| self.expect_token(&Token::LParen)?; | |
| let expr = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(expr) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::Pivot { | |
| table: Box::new(table), | |
| aggregate_functions, | |
| value_column, | |
| value_source, | |
| default_on_null, | |
| alias, | |
| }) | |
| } | |
| /// Parse an UNPIVOT table factor, returning a TableFactor. | |
| pub fn parse_unpivot_table_factor( | |
| &mut self, | |
| table: TableFactor, | |
| ) -> Result<TableFactor, ParserError> { | |
| let null_inclusion = if self.parse_keyword(Keyword::INCLUDE) { | |
| self.expect_keyword_is(Keyword::NULLS)?; | |
| Some(NullInclusion::IncludeNulls) | |
| } else if self.parse_keyword(Keyword::EXCLUDE) { | |
| self.expect_keyword_is(Keyword::NULLS)?; | |
| Some(NullInclusion::ExcludeNulls) | |
| } else { | |
| None | |
| }; | |
| self.expect_token(&Token::LParen)?; | |
| let value = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::FOR)?; | |
| let name = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::IN)?; | |
| let columns = self.parse_parenthesized_column_list_inner(Mandatory, false, |p| { | |
| p.parse_expr_with_alias() | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| let alias = self.maybe_parse_table_alias()?; | |
| Ok(TableFactor::Unpivot { | |
| table: Box::new(table), | |
| value, | |
| null_inclusion, | |
| name, | |
| columns, | |
| alias, | |
| }) | |
| } | |
| /// Parse an object UNPIVOT table factor in FROM clause. | |
| /// | |
| /// Syntax: | |
| /// `UNPIVOT expression AS value_alias [AT attribute_alias]` | |
| pub fn parse_unpivot_expr_table_factor(&mut self) -> Result<TableFactor, ParserError> { | |
| self.expect_keyword_is(Keyword::UNPIVOT)?; | |
| let expression = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let value_alias = self.parse_identifier()?; | |
| let attribute_alias = if self.parse_keyword(Keyword::AT) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| Ok(TableFactor::UnpivotExpr { | |
| expression, | |
| value_alias, | |
| attribute_alias, | |
| }) | |
| } | |
| /// Parse a JOIN constraint (`NATURAL`, `ON <expr>`, `USING (...)`, or no constraint). | |
| pub fn parse_join_constraint(&mut self, natural: bool) -> Result<JoinConstraint, ParserError> { | |
| if natural { | |
| Ok(JoinConstraint::Natural) | |
| } else if self.parse_keyword(Keyword::ON) { | |
| let constraint = self.parse_expr()?; | |
| Ok(JoinConstraint::On(constraint)) | |
| } else if self.parse_keyword(Keyword::USING) { | |
| let columns = self.parse_parenthesized_qualified_column_list(Mandatory, false)?; | |
| if self.parse_keyword(Keyword::AS) { | |
| Ok(JoinConstraint::UsingAlias( | |
| columns, | |
| self.parse_identifier()?, | |
| )) | |
| } else { | |
| Ok(JoinConstraint::Using(columns)) | |
| } | |
| } else { | |
| Ok(JoinConstraint::None) | |
| //self.expected_ref("ON, or USING after JOIN", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse a GRANT statement. | |
| pub fn parse_grant(&mut self) -> Result<Grant, ParserError> { | |
| let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?; | |
| self.expect_keyword_is(Keyword::TO)?; | |
| let grantees = self.parse_grantees()?; | |
| let with_grant_option = | |
| self.parse_keywords(&[Keyword::WITH, Keyword::GRANT, Keyword::OPTION]); | |
| let current_grants = | |
| if self.parse_keywords(&[Keyword::COPY, Keyword::CURRENT, Keyword::GRANTS]) { | |
| Some(CurrentGrantsKind::CopyCurrentGrants) | |
| } else if self.parse_keywords(&[Keyword::REVOKE, Keyword::CURRENT, Keyword::GRANTS]) { | |
| Some(CurrentGrantsKind::RevokeCurrentGrants) | |
| } else { | |
| None | |
| }; | |
| let as_grantor = if self.parse_keywords(&[Keyword::AS]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let granted_by = if self.parse_keywords(&[Keyword::GRANTED, Keyword::BY]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Grant { | |
| privileges, | |
| objects, | |
| grantees, | |
| with_grant_option, | |
| as_grantor, | |
| granted_by, | |
| current_grants, | |
| }) | |
| } | |
| fn parse_grantees(&mut self) -> Result<Vec<Grantee>, ParserError> { | |
| let mut values = vec![]; | |
| let mut grantee_type = GranteesType::None; | |
| loop { | |
| let new_grantee_type = if self.parse_keyword(Keyword::ROLE) { | |
| GranteesType::Role | |
| } else if self.parse_keyword(Keyword::USER) { | |
| GranteesType::User | |
| } else if self.parse_keyword(Keyword::SHARE) { | |
| GranteesType::Share | |
| } else if self.parse_keyword(Keyword::GROUP) { | |
| GranteesType::Group | |
| } else if self.parse_keyword(Keyword::PUBLIC) { | |
| GranteesType::Public | |
| } else if self.parse_keywords(&[Keyword::DATABASE, Keyword::ROLE]) { | |
| GranteesType::DatabaseRole | |
| } else if self.parse_keywords(&[Keyword::APPLICATION, Keyword::ROLE]) { | |
| GranteesType::ApplicationRole | |
| } else if self.parse_keyword(Keyword::APPLICATION) { | |
| GranteesType::Application | |
| } else { | |
| grantee_type.clone() // keep from previous iteraton, if not specified | |
| }; | |
| if self | |
| .dialect | |
| .get_reserved_grantees_types() | |
| .contains(&new_grantee_type) | |
| { | |
| self.prev_token(); | |
| } else { | |
| grantee_type = new_grantee_type; | |
| } | |
| let grantee = if grantee_type == GranteesType::Public { | |
| Grantee { | |
| grantee_type: grantee_type.clone(), | |
| name: None, | |
| } | |
| } else { | |
| let mut name = self.parse_grantee_name()?; | |
| if self.consume_token(&Token::Colon) { | |
| // Redshift supports namespace prefix for external users and groups: | |
| // <Namespace>:<GroupName> or <Namespace>:<UserName> | |
| // https://docs.aws.amazon.com/redshift/latest/mgmt/redshift-iam-access-control-native-idp.html | |
| let ident = self.parse_identifier()?; | |
| if let GranteeName::ObjectName(namespace) = name { | |
| name = GranteeName::ObjectName(ObjectName::from(vec![Ident::new( | |
| format!("{namespace}:{ident}"), | |
| )])); | |
| }; | |
| } | |
| Grantee { | |
| grantee_type: grantee_type.clone(), | |
| name: Some(name), | |
| } | |
| }; | |
| values.push(grantee); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| Ok(values) | |
| } | |
| /// Parse privileges and optional target objects for GRANT/DENY/REVOKE statements. | |
| pub fn parse_grant_deny_revoke_privileges_objects( | |
| &mut self, | |
| ) -> Result<(Privileges, Option<GrantObjects>), ParserError> { | |
| let privileges = if self.parse_keyword(Keyword::ALL) { | |
| Privileges::All { | |
| with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES), | |
| } | |
| } else { | |
| let actions = self.parse_actions_list()?; | |
| Privileges::Actions(actions) | |
| }; | |
| let objects = if self.parse_keyword(Keyword::ON) { | |
| if self.parse_keywords(&[Keyword::ALL, Keyword::TABLES, Keyword::IN, Keyword::SCHEMA]) { | |
| Some(GrantObjects::AllTablesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::EXTERNAL, | |
| Keyword::TABLES, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::AllExternalTablesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::VIEWS, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::AllViewsInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::MATERIALIZED, | |
| Keyword::VIEWS, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::AllMaterializedViewsInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::FUNCTIONS, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::AllFunctionsInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::SCHEMAS, | |
| Keyword::IN, | |
| Keyword::DATABASE, | |
| ]) { | |
| Some(GrantObjects::FutureSchemasInDatabase { | |
| databases: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::TABLES, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::FutureTablesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::EXTERNAL, | |
| Keyword::TABLES, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::FutureExternalTablesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::VIEWS, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::FutureViewsInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::MATERIALIZED, | |
| Keyword::VIEWS, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::FutureMaterializedViewsInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::ALL, | |
| Keyword::SEQUENCES, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::AllSequencesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[ | |
| Keyword::FUTURE, | |
| Keyword::SEQUENCES, | |
| Keyword::IN, | |
| Keyword::SCHEMA, | |
| ]) { | |
| Some(GrantObjects::FutureSequencesInSchema { | |
| schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| }) | |
| } else if self.parse_keywords(&[Keyword::RESOURCE, Keyword::MONITOR]) { | |
| Some(GrantObjects::ResourceMonitors( | |
| self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::COMPUTE, Keyword::POOL]) { | |
| Some(GrantObjects::ComputePools( | |
| self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::FAILOVER, Keyword::GROUP]) { | |
| Some(GrantObjects::FailoverGroup( | |
| self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::REPLICATION, Keyword::GROUP]) { | |
| Some(GrantObjects::ReplicationGroup( | |
| self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| )) | |
| } else if self.parse_keywords(&[Keyword::EXTERNAL, Keyword::VOLUME]) { | |
| Some(GrantObjects::ExternalVolumes( | |
| self.parse_comma_separated(|p| p.parse_object_name(false))?, | |
| )) | |
| } else { | |
| let object_type = self.parse_one_of_keywords(&[ | |
| Keyword::SEQUENCE, | |
| Keyword::DATABASE, | |
| Keyword::SCHEMA, | |
| Keyword::TABLE, | |
| Keyword::VIEW, | |
| Keyword::WAREHOUSE, | |
| Keyword::INTEGRATION, | |
| Keyword::VIEW, | |
| Keyword::WAREHOUSE, | |
| Keyword::INTEGRATION, | |
| Keyword::USER, | |
| Keyword::CONNECTION, | |
| Keyword::PROCEDURE, | |
| Keyword::FUNCTION, | |
| ]); | |
| let objects = | |
| self.parse_comma_separated(|p| p.parse_object_name_inner(false, true)); | |
| match object_type { | |
| Some(Keyword::DATABASE) => Some(GrantObjects::Databases(objects?)), | |
| Some(Keyword::SCHEMA) => Some(GrantObjects::Schemas(objects?)), | |
| Some(Keyword::SEQUENCE) => Some(GrantObjects::Sequences(objects?)), | |
| Some(Keyword::WAREHOUSE) => Some(GrantObjects::Warehouses(objects?)), | |
| Some(Keyword::INTEGRATION) => Some(GrantObjects::Integrations(objects?)), | |
| Some(Keyword::VIEW) => Some(GrantObjects::Views(objects?)), | |
| Some(Keyword::USER) => Some(GrantObjects::Users(objects?)), | |
| Some(Keyword::CONNECTION) => Some(GrantObjects::Connections(objects?)), | |
| kw @ (Some(Keyword::PROCEDURE) | Some(Keyword::FUNCTION)) => { | |
| if let Some(name) = objects?.first() { | |
| self.parse_grant_procedure_or_function(name, &kw)? | |
| } else { | |
| self.expected_ref("procedure or function name", self.peek_token_ref())? | |
| } | |
| } | |
| Some(Keyword::TABLE) | None => Some(GrantObjects::Tables(objects?)), | |
| Some(unexpected_keyword) => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in grant objects"), | |
| )), | |
| } | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok((privileges, objects)) | |
| } | |
| fn parse_grant_procedure_or_function( | |
| &mut self, | |
| name: &ObjectName, | |
| kw: &Option<Keyword>, | |
| ) -> Result<Option<GrantObjects>, ParserError> { | |
| let arg_types = if self.consume_token(&Token::LParen) { | |
| let list = self.parse_comma_separated0(Self::parse_data_type, Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| list | |
| } else { | |
| vec![] | |
| }; | |
| match kw { | |
| Some(Keyword::PROCEDURE) => Ok(Some(GrantObjects::Procedure { | |
| name: name.clone(), | |
| arg_types, | |
| })), | |
| Some(Keyword::FUNCTION) => Ok(Some(GrantObjects::Function { | |
| name: name.clone(), | |
| arg_types, | |
| })), | |
| _ => self.expected_ref("procedure or function keywords", self.peek_token_ref())?, | |
| } | |
| } | |
| /// Parse a single grantable permission/action (used within GRANT statements). | |
| pub fn parse_grant_permission(&mut self) -> Result<Action, ParserError> { | |
| fn parse_columns(parser: &mut Parser) -> Result<Option<Vec<Ident>>, ParserError> { | |
| let columns = parser.parse_parenthesized_column_list(Optional, false)?; | |
| if columns.is_empty() { | |
| Ok(None) | |
| } else { | |
| Ok(Some(columns)) | |
| } | |
| } | |
| // Multi-word privileges | |
| if self.parse_keywords(&[Keyword::IMPORTED, Keyword::PRIVILEGES]) { | |
| Ok(Action::ImportedPrivileges) | |
| } else if self.parse_keywords(&[Keyword::ADD, Keyword::SEARCH, Keyword::OPTIMIZATION]) { | |
| Ok(Action::AddSearchOptimization) | |
| } else if self.parse_keywords(&[Keyword::ATTACH, Keyword::LISTING]) { | |
| Ok(Action::AttachListing) | |
| } else if self.parse_keywords(&[Keyword::ATTACH, Keyword::POLICY]) { | |
| Ok(Action::AttachPolicy) | |
| } else if self.parse_keywords(&[Keyword::BIND, Keyword::SERVICE, Keyword::ENDPOINT]) { | |
| Ok(Action::BindServiceEndpoint) | |
| } else if self.parse_keywords(&[Keyword::DATABASE, Keyword::ROLE]) { | |
| let role = self.parse_object_name(false)?; | |
| Ok(Action::DatabaseRole { role }) | |
| } else if self.parse_keywords(&[Keyword::EVOLVE, Keyword::SCHEMA]) { | |
| Ok(Action::EvolveSchema) | |
| } else if self.parse_keywords(&[Keyword::IMPORT, Keyword::SHARE]) { | |
| Ok(Action::ImportShare) | |
| } else if self.parse_keywords(&[Keyword::MANAGE, Keyword::VERSIONS]) { | |
| Ok(Action::ManageVersions) | |
| } else if self.parse_keywords(&[Keyword::MANAGE, Keyword::RELEASES]) { | |
| Ok(Action::ManageReleases) | |
| } else if self.parse_keywords(&[Keyword::OVERRIDE, Keyword::SHARE, Keyword::RESTRICTIONS]) { | |
| Ok(Action::OverrideShareRestrictions) | |
| } else if self.parse_keywords(&[ | |
| Keyword::PURCHASE, | |
| Keyword::DATA, | |
| Keyword::EXCHANGE, | |
| Keyword::LISTING, | |
| ]) { | |
| Ok(Action::PurchaseDataExchangeListing) | |
| } else if self.parse_keywords(&[Keyword::RESOLVE, Keyword::ALL]) { | |
| Ok(Action::ResolveAll) | |
| } else if self.parse_keywords(&[Keyword::READ, Keyword::SESSION]) { | |
| Ok(Action::ReadSession) | |
| // Single-word privileges | |
| } else if self.parse_keyword(Keyword::APPLY) { | |
| let apply_type = self.parse_action_apply_type()?; | |
| Ok(Action::Apply { apply_type }) | |
| } else if self.parse_keyword(Keyword::APPLYBUDGET) { | |
| Ok(Action::ApplyBudget) | |
| } else if self.parse_keyword(Keyword::AUDIT) { | |
| Ok(Action::Audit) | |
| } else if self.parse_keyword(Keyword::CONNECT) { | |
| Ok(Action::Connect) | |
| } else if self.parse_keyword(Keyword::CREATE) { | |
| let obj_type = self.maybe_parse_action_create_object_type(); | |
| Ok(Action::Create { obj_type }) | |
| } else if self.parse_keyword(Keyword::DELETE) { | |
| Ok(Action::Delete) | |
| } else if self.parse_keyword(Keyword::EXEC) { | |
| let obj_type = self.maybe_parse_action_execute_obj_type(); | |
| Ok(Action::Exec { obj_type }) | |
| } else if self.parse_keyword(Keyword::EXECUTE) { | |
| let obj_type = self.maybe_parse_action_execute_obj_type(); | |
| Ok(Action::Execute { obj_type }) | |
| } else if self.parse_keyword(Keyword::FAILOVER) { | |
| Ok(Action::Failover) | |
| } else if self.parse_keyword(Keyword::INSERT) { | |
| Ok(Action::Insert { | |
| columns: parse_columns(self)?, | |
| }) | |
| } else if self.parse_keyword(Keyword::MANAGE) { | |
| let manage_type = self.parse_action_manage_type()?; | |
| Ok(Action::Manage { manage_type }) | |
| } else if self.parse_keyword(Keyword::MODIFY) { | |
| let modify_type = self.parse_action_modify_type(); | |
| Ok(Action::Modify { modify_type }) | |
| } else if self.parse_keyword(Keyword::MONITOR) { | |
| let monitor_type = self.parse_action_monitor_type(); | |
| Ok(Action::Monitor { monitor_type }) | |
| } else if self.parse_keyword(Keyword::OPERATE) { | |
| Ok(Action::Operate) | |
| } else if self.parse_keyword(Keyword::REFERENCES) { | |
| Ok(Action::References { | |
| columns: parse_columns(self)?, | |
| }) | |
| } else if self.parse_keyword(Keyword::READ) { | |
| Ok(Action::Read) | |
| } else if self.parse_keyword(Keyword::REPLICATE) { | |
| Ok(Action::Replicate) | |
| } else if self.parse_keyword(Keyword::ROLE) { | |
| let role = self.parse_object_name(false)?; | |
| Ok(Action::Role { role }) | |
| } else if self.parse_keyword(Keyword::SELECT) { | |
| Ok(Action::Select { | |
| columns: parse_columns(self)?, | |
| }) | |
| } else if self.parse_keyword(Keyword::TEMPORARY) { | |
| Ok(Action::Temporary) | |
| } else if self.parse_keyword(Keyword::TRIGGER) { | |
| Ok(Action::Trigger) | |
| } else if self.parse_keyword(Keyword::TRUNCATE) { | |
| Ok(Action::Truncate) | |
| } else if self.parse_keyword(Keyword::UPDATE) { | |
| Ok(Action::Update { | |
| columns: parse_columns(self)?, | |
| }) | |
| } else if self.parse_keyword(Keyword::USAGE) { | |
| Ok(Action::Usage) | |
| } else if self.parse_keyword(Keyword::OWNERSHIP) { | |
| Ok(Action::Ownership) | |
| } else if self.parse_keyword(Keyword::DROP) { | |
| Ok(Action::Drop) | |
| } else { | |
| self.expected_ref("a privilege keyword", self.peek_token_ref())? | |
| } | |
| } | |
| fn maybe_parse_action_create_object_type(&mut self) -> Option<ActionCreateObjectType> { | |
| // Multi-word object types | |
| if self.parse_keywords(&[Keyword::APPLICATION, Keyword::PACKAGE]) { | |
| Some(ActionCreateObjectType::ApplicationPackage) | |
| } else if self.parse_keywords(&[Keyword::COMPUTE, Keyword::POOL]) { | |
| Some(ActionCreateObjectType::ComputePool) | |
| } else if self.parse_keywords(&[Keyword::DATA, Keyword::EXCHANGE, Keyword::LISTING]) { | |
| Some(ActionCreateObjectType::DataExchangeListing) | |
| } else if self.parse_keywords(&[Keyword::EXTERNAL, Keyword::VOLUME]) { | |
| Some(ActionCreateObjectType::ExternalVolume) | |
| } else if self.parse_keywords(&[Keyword::FAILOVER, Keyword::GROUP]) { | |
| Some(ActionCreateObjectType::FailoverGroup) | |
| } else if self.parse_keywords(&[Keyword::NETWORK, Keyword::POLICY]) { | |
| Some(ActionCreateObjectType::NetworkPolicy) | |
| } else if self.parse_keywords(&[Keyword::ORGANIZATION, Keyword::LISTING]) { | |
| Some(ActionCreateObjectType::OrganiationListing) | |
| } else if self.parse_keywords(&[Keyword::REPLICATION, Keyword::GROUP]) { | |
| Some(ActionCreateObjectType::ReplicationGroup) | |
| } | |
| // Single-word object types | |
| else if self.parse_keyword(Keyword::ACCOUNT) { | |
| Some(ActionCreateObjectType::Account) | |
| } else if self.parse_keyword(Keyword::APPLICATION) { | |
| Some(ActionCreateObjectType::Application) | |
| } else if self.parse_keyword(Keyword::DATABASE) { | |
| Some(ActionCreateObjectType::Database) | |
| } else if self.parse_keyword(Keyword::INTEGRATION) { | |
| Some(ActionCreateObjectType::Integration) | |
| } else if self.parse_keyword(Keyword::ROLE) { | |
| Some(ActionCreateObjectType::Role) | |
| } else if self.parse_keyword(Keyword::SCHEMA) { | |
| Some(ActionCreateObjectType::Schema) | |
| } else if self.parse_keyword(Keyword::SHARE) { | |
| Some(ActionCreateObjectType::Share) | |
| } else if self.parse_keyword(Keyword::USER) { | |
| Some(ActionCreateObjectType::User) | |
| } else if self.parse_keyword(Keyword::WAREHOUSE) { | |
| Some(ActionCreateObjectType::Warehouse) | |
| } else { | |
| None | |
| } | |
| } | |
| fn parse_action_apply_type(&mut self) -> Result<ActionApplyType, ParserError> { | |
| if self.parse_keywords(&[Keyword::AGGREGATION, Keyword::POLICY]) { | |
| Ok(ActionApplyType::AggregationPolicy) | |
| } else if self.parse_keywords(&[Keyword::AUTHENTICATION, Keyword::POLICY]) { | |
| Ok(ActionApplyType::AuthenticationPolicy) | |
| } else if self.parse_keywords(&[Keyword::JOIN, Keyword::POLICY]) { | |
| Ok(ActionApplyType::JoinPolicy) | |
| } else if self.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) { | |
| Ok(ActionApplyType::MaskingPolicy) | |
| } else if self.parse_keywords(&[Keyword::PACKAGES, Keyword::POLICY]) { | |
| Ok(ActionApplyType::PackagesPolicy) | |
| } else if self.parse_keywords(&[Keyword::PASSWORD, Keyword::POLICY]) { | |
| Ok(ActionApplyType::PasswordPolicy) | |
| } else if self.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) { | |
| Ok(ActionApplyType::ProjectionPolicy) | |
| } else if self.parse_keywords(&[Keyword::ROW, Keyword::ACCESS, Keyword::POLICY]) { | |
| Ok(ActionApplyType::RowAccessPolicy) | |
| } else if self.parse_keywords(&[Keyword::SESSION, Keyword::POLICY]) { | |
| Ok(ActionApplyType::SessionPolicy) | |
| } else if self.parse_keyword(Keyword::TAG) { | |
| Ok(ActionApplyType::Tag) | |
| } else { | |
| self.expected_ref("GRANT APPLY type", self.peek_token_ref()) | |
| } | |
| } | |
| fn maybe_parse_action_execute_obj_type(&mut self) -> Option<ActionExecuteObjectType> { | |
| if self.parse_keywords(&[Keyword::DATA, Keyword::METRIC, Keyword::FUNCTION]) { | |
| Some(ActionExecuteObjectType::DataMetricFunction) | |
| } else if self.parse_keywords(&[Keyword::MANAGED, Keyword::ALERT]) { | |
| Some(ActionExecuteObjectType::ManagedAlert) | |
| } else if self.parse_keywords(&[Keyword::MANAGED, Keyword::TASK]) { | |
| Some(ActionExecuteObjectType::ManagedTask) | |
| } else if self.parse_keyword(Keyword::ALERT) { | |
| Some(ActionExecuteObjectType::Alert) | |
| } else if self.parse_keyword(Keyword::TASK) { | |
| Some(ActionExecuteObjectType::Task) | |
| } else { | |
| None | |
| } | |
| } | |
| fn parse_action_manage_type(&mut self) -> Result<ActionManageType, ParserError> { | |
| if self.parse_keywords(&[Keyword::ACCOUNT, Keyword::SUPPORT, Keyword::CASES]) { | |
| Ok(ActionManageType::AccountSupportCases) | |
| } else if self.parse_keywords(&[Keyword::EVENT, Keyword::SHARING]) { | |
| Ok(ActionManageType::EventSharing) | |
| } else if self.parse_keywords(&[Keyword::LISTING, Keyword::AUTO, Keyword::FULFILLMENT]) { | |
| Ok(ActionManageType::ListingAutoFulfillment) | |
| } else if self.parse_keywords(&[Keyword::ORGANIZATION, Keyword::SUPPORT, Keyword::CASES]) { | |
| Ok(ActionManageType::OrganizationSupportCases) | |
| } else if self.parse_keywords(&[Keyword::USER, Keyword::SUPPORT, Keyword::CASES]) { | |
| Ok(ActionManageType::UserSupportCases) | |
| } else if self.parse_keyword(Keyword::GRANTS) { | |
| Ok(ActionManageType::Grants) | |
| } else if self.parse_keyword(Keyword::WAREHOUSES) { | |
| Ok(ActionManageType::Warehouses) | |
| } else { | |
| self.expected_ref("GRANT MANAGE type", self.peek_token_ref()) | |
| } | |
| } | |
| fn parse_action_modify_type(&mut self) -> Option<ActionModifyType> { | |
| if self.parse_keywords(&[Keyword::LOG, Keyword::LEVEL]) { | |
| Some(ActionModifyType::LogLevel) | |
| } else if self.parse_keywords(&[Keyword::TRACE, Keyword::LEVEL]) { | |
| Some(ActionModifyType::TraceLevel) | |
| } else if self.parse_keywords(&[Keyword::SESSION, Keyword::LOG, Keyword::LEVEL]) { | |
| Some(ActionModifyType::SessionLogLevel) | |
| } else if self.parse_keywords(&[Keyword::SESSION, Keyword::TRACE, Keyword::LEVEL]) { | |
| Some(ActionModifyType::SessionTraceLevel) | |
| } else { | |
| None | |
| } | |
| } | |
| fn parse_action_monitor_type(&mut self) -> Option<ActionMonitorType> { | |
| if self.parse_keyword(Keyword::EXECUTION) { | |
| Some(ActionMonitorType::Execution) | |
| } else if self.parse_keyword(Keyword::SECURITY) { | |
| Some(ActionMonitorType::Security) | |
| } else if self.parse_keyword(Keyword::USAGE) { | |
| Some(ActionMonitorType::Usage) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parse a grantee name, possibly with a host qualifier (user@host). | |
| pub fn parse_grantee_name(&mut self) -> Result<GranteeName, ParserError> { | |
| let mut name = self.parse_object_name(false)?; | |
| if self.dialect.supports_user_host_grantee() | |
| && name.0.len() == 1 | |
| && name.0[0].as_ident().is_some() | |
| && self.consume_token(&Token::AtSign) | |
| { | |
| let user = name.0.pop().unwrap().as_ident().unwrap().clone(); | |
| let host = self.parse_identifier()?; | |
| Ok(GranteeName::UserHost { user, host }) | |
| } else { | |
| Ok(GranteeName::ObjectName(name)) | |
| } | |
| } | |
| /// Parse [`Statement::Deny`] | |
| pub fn parse_deny(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword(Keyword::DENY)?; | |
| let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?; | |
| let objects = match objects { | |
| Some(o) => o, | |
| None => { | |
| return parser_err!( | |
| "DENY statements must specify an object", | |
| self.peek_token_ref().span.start | |
| ) | |
| } | |
| }; | |
| self.expect_keyword_is(Keyword::TO)?; | |
| let grantees = self.parse_grantees()?; | |
| let cascade = self.parse_cascade_option(); | |
| let granted_by = if self.parse_keywords(&[Keyword::AS]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::Deny(DenyStatement { | |
| privileges, | |
| objects, | |
| grantees, | |
| cascade, | |
| granted_by, | |
| })) | |
| } | |
| /// Parse a REVOKE statement | |
| pub fn parse_revoke(&mut self) -> Result<Revoke, ParserError> { | |
| let grant_option_for = | |
| self.parse_keywords(&[Keyword::GRANT, Keyword::OPTION, Keyword::FOR]); | |
| let (privileges, objects) = self.parse_grant_deny_revoke_privileges_objects()?; | |
| self.expect_keyword_is(Keyword::FROM)?; | |
| let grantees = self.parse_grantees()?; | |
| let granted_by = if self.parse_keywords(&[Keyword::GRANTED, Keyword::BY]) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let cascade = self.parse_cascade_option(); | |
| Ok(Revoke { | |
| grant_option_for, | |
| privileges, | |
| objects, | |
| grantees, | |
| granted_by, | |
| cascade, | |
| }) | |
| } | |
| /// Parse an REPLACE statement | |
| pub fn parse_replace( | |
| &mut self, | |
| replace_token: TokenWithSpan, | |
| ) -> Result<Statement, ParserError> { | |
| if !dialect_of!(self is MySqlDialect | GenericDialect) { | |
| return parser_err!( | |
| "Unsupported statement REPLACE", | |
| self.peek_token_ref().span.start | |
| ); | |
| } | |
| let mut insert = self.parse_insert(replace_token)?; | |
| if let Statement::Insert(Insert { replace_into, .. }) = &mut insert { | |
| *replace_into = true; | |
| } | |
| Ok(insert) | |
| } | |
| /// Parse an INSERT statement, returning a `Box`ed SetExpr | |
| /// | |
| /// This is used to reduce the size of the stack frames in debug builds | |
| fn parse_insert_setexpr_boxed( | |
| &mut self, | |
| insert_token: TokenWithSpan, | |
| ) -> Result<Box<SetExpr>, ParserError> { | |
| Ok(Box::new(SetExpr::Insert(self.parse_insert(insert_token)?))) | |
| } | |
| /// Parse an INSERT statement | |
| pub fn parse_insert(&mut self, insert_token: TokenWithSpan) -> Result<Statement, ParserError> { | |
| let optimizer_hints = self.maybe_parse_optimizer_hints()?; | |
| let or = self.parse_conflict_clause(); | |
| let priority = if !dialect_of!(self is MySqlDialect | GenericDialect) { | |
| None | |
| } else if self.parse_keyword(Keyword::LOW_PRIORITY) { | |
| Some(MysqlInsertPriority::LowPriority) | |
| } else if self.parse_keyword(Keyword::DELAYED) { | |
| Some(MysqlInsertPriority::Delayed) | |
| } else if self.parse_keyword(Keyword::HIGH_PRIORITY) { | |
| Some(MysqlInsertPriority::HighPriority) | |
| } else { | |
| None | |
| }; | |
| let ignore = dialect_of!(self is MySqlDialect | GenericDialect) | |
| && self.parse_keyword(Keyword::IGNORE); | |
| let replace_into = false; | |
| let overwrite = self.parse_keyword(Keyword::OVERWRITE); | |
| let into = if self.parse_keyword(Keyword::INTO) { | |
| true | |
| } else if self.dialect.supports_insert_without_into() { | |
| false | |
| } else { | |
| return self.expected("INTO", self.peek_token()); | |
| }; | |
| let local = self.parse_keyword(Keyword::LOCAL); | |
| if self.parse_keyword(Keyword::DIRECTORY) { | |
| let path = self.parse_literal_string()?; | |
| let file_format = if self.parse_keywords(&[Keyword::STORED, Keyword::AS]) { | |
| Some(self.parse_file_format()?) | |
| } else { | |
| None | |
| }; | |
| let source = self.parse_query()?; | |
| Ok(Statement::Directory { | |
| local, | |
| path, | |
| overwrite, | |
| file_format, | |
| source, | |
| }) | |
| } else { | |
| // Hive lets you put table here regardless | |
| let table = self.parse_keyword(Keyword::TABLE); | |
| let table_object = self.parse_table_object()?; | |
| // `BY NAME` and `OVERRIDING { SYSTEM | USER } VALUE` are INSERT clauses, not table | |
| // aliases. | |
| let table_alias = if self.dialect.supports_insert_table_alias() | |
| && !self.peek_keywords(&[Keyword::BY, Keyword::NAME]) | |
| && !self.peek_keywords(&[Keyword::OVERRIDING, Keyword::SYSTEM]) | |
| && !self.peek_keywords(&[Keyword::OVERRIDING, Keyword::USER]) | |
| && !self.peek_sub_query() | |
| && self | |
| .peek_one_of_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) | |
| .is_none() | |
| { | |
| if self.parse_keyword(Keyword::AS) { | |
| Some(TableAliasWithoutColumns { | |
| explicit: true, | |
| alias: self.parse_identifier()?, | |
| }) | |
| } else { | |
| self.maybe_parse(|parser| parser.parse_identifier())? | |
| .map(|alias| TableAliasWithoutColumns { | |
| explicit: false, | |
| alias, | |
| }) | |
| } | |
| } else { | |
| None | |
| }; | |
| let is_mysql = dialect_of!(self is MySqlDialect); | |
| let mut by_name = false; | |
| let mut overriding = None; | |
| let (columns, partitioned, after_columns, output, source, assignments) = if self | |
| .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) | |
| { | |
| (vec![], None, vec![], None, None, vec![]) | |
| } else { | |
| let (columns, partitioned, after_columns) = if !self.peek_subquery_start() { | |
| let columns = | |
| self.parse_parenthesized_qualified_column_list(Optional, is_mysql)?; | |
| let partitioned = self.parse_insert_partition()?; | |
| by_name = self.parse_keywords(&[Keyword::BY, Keyword::NAME]); | |
| // Hive allows you to specify columns after partitions as well if you want. | |
| let after_columns = if dialect_of!(self is HiveDialect) { | |
| self.parse_parenthesized_column_list(Optional, false)? | |
| } else { | |
| vec![] | |
| }; | |
| (columns, partitioned, after_columns) | |
| } else { | |
| Default::default() | |
| }; | |
| overriding = self.parse_insert_overriding()?; | |
| let output = self.maybe_parse_output_clause()?; | |
| let (source, assignments) = if self.peek_keyword(Keyword::FORMAT) | |
| || self.peek_keyword(Keyword::SETTINGS) | |
| { | |
| (None, vec![]) | |
| } else if self.dialect.supports_insert_set() && self.parse_keyword(Keyword::SET) { | |
| (None, self.parse_comma_separated(Parser::parse_assignment)?) | |
| } else { | |
| (Some(self.parse_query()?), vec![]) | |
| }; | |
| ( | |
| columns, | |
| partitioned, | |
| after_columns, | |
| output, | |
| source, | |
| assignments, | |
| ) | |
| }; | |
| let (format_clause, settings) = if self.dialect.supports_insert_format() { | |
| // Settings always comes before `FORMAT` for ClickHouse: | |
| // <https://clickhouse.com/docs/en/sql-reference/statements/insert-into> | |
| let settings = self.parse_settings()?; | |
| let format = if self.parse_keyword(Keyword::FORMAT) { | |
| Some(self.parse_input_format_clause()?) | |
| } else { | |
| None | |
| }; | |
| (format, settings) | |
| } else { | |
| Default::default() | |
| }; | |
| let insert_alias = if dialect_of!(self is MySqlDialect | GenericDialect) | |
| && self.parse_keyword(Keyword::AS) | |
| { | |
| let row_alias = self.parse_object_name(false)?; | |
| let col_aliases = Some(self.parse_parenthesized_column_list(Optional, false)?); | |
| Some(InsertAliases { | |
| row_alias, | |
| col_aliases, | |
| }) | |
| } else { | |
| None | |
| }; | |
| let on = if self.parse_keyword(Keyword::ON) { | |
| if self.parse_keyword(Keyword::CONFLICT) { | |
| let conflict_target = | |
| if self.parse_keywords(&[Keyword::ON, Keyword::CONSTRAINT]) { | |
| Some(ConflictTarget::OnConstraint(self.parse_object_name(false)?)) | |
| } else if self.peek_token_ref().token == Token::LParen { | |
| Some(ConflictTarget::Columns( | |
| self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?, | |
| )) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::DO)?; | |
| let action = if self.parse_keyword(Keyword::NOTHING) { | |
| OnConflictAction::DoNothing | |
| } else { | |
| self.expect_keyword_is(Keyword::UPDATE)?; | |
| self.expect_keyword_is(Keyword::SET)?; | |
| let assignments = self.parse_comma_separated(Parser::parse_assignment)?; | |
| let selection = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| OnConflictAction::DoUpdate(DoUpdate { | |
| assignments, | |
| selection, | |
| }) | |
| }; | |
| Some(OnInsert::OnConflict(OnConflict { | |
| conflict_target, | |
| action, | |
| })) | |
| } else { | |
| self.expect_keyword_is(Keyword::DUPLICATE)?; | |
| self.expect_keyword_is(Keyword::KEY)?; | |
| self.expect_keyword_is(Keyword::UPDATE)?; | |
| let l = self.parse_comma_separated(Parser::parse_assignment)?; | |
| Some(OnInsert::DuplicateKeyUpdate(l)) | |
| } | |
| } else { | |
| None | |
| }; | |
| let returning = if self.parse_keyword(Keyword::RETURNING) { | |
| Some(self.parse_comma_separated(Parser::parse_select_item)?) | |
| } else { | |
| None | |
| }; | |
| Ok(Insert { | |
| insert_token: insert_token.into(), | |
| optimizer_hints, | |
| or, | |
| table: table_object, | |
| table_alias, | |
| ignore, | |
| into, | |
| overwrite, | |
| by_name, | |
| partitioned, | |
| columns, | |
| after_columns, | |
| overriding, | |
| source, | |
| assignments, | |
| has_table_keyword: table, | |
| on, | |
| returning, | |
| output, | |
| replace_into, | |
| priority, | |
| insert_alias, | |
| settings, | |
| format_clause, | |
| multi_table_insert_type: None, | |
| multi_table_into_clauses: vec![], | |
| multi_table_when_clauses: vec![], | |
| multi_table_else_clause: None, | |
| } | |
| .into()) | |
| } | |
| } | |
| /// Parses input format clause used for ClickHouse. | |
| /// | |
| /// <https://clickhouse.com/docs/en/interfaces/formats> | |
| pub fn parse_input_format_clause(&mut self) -> Result<InputFormatClause, ParserError> { | |
| let ident = self.parse_identifier()?; | |
| let values = self | |
| .maybe_parse(|p| p.parse_comma_separated(|p| p.parse_expr()))? | |
| .unwrap_or_default(); | |
| Ok(InputFormatClause { ident, values }) | |
| } | |
| /// Returns true if the immediate tokens look like the | |
| /// beginning of a subquery. `(SELECT ...` or `((SELECT ...` etc. | |
| fn peek_subquery_start(&mut self) -> bool { | |
| // Handle (SELECT, ((SELECT, (((SELECT, etc. | |
| // This makes INSERT consistent with other contexts where nested | |
| // parentheses around subqueries are handled by recursive descent. | |
| let mut i = 0; | |
| loop { | |
| match &self.peek_nth_token_ref(i).token { | |
| Token::LParen => i += 1, | |
| Token::Word(w) if w.keyword == Keyword::SELECT => return i > 0, | |
| _ => return false, | |
| } | |
| } | |
| } | |
| /// Returns true if the immediate tokens look like the | |
| /// beginning of a subquery possibly preceded by CTEs; | |
| /// i.e. `(WITH ...` or `(SELECT ...`. | |
| fn peek_subquery_or_cte_start(&mut self) -> bool { | |
| matches!( | |
| self.peek_tokens_ref(), | |
| [ | |
| TokenWithSpan { | |
| token: Token::LParen, | |
| .. | |
| }, | |
| TokenWithSpan { | |
| token: Token::Word(Word { | |
| keyword: Keyword::SELECT | Keyword::WITH, | |
| .. | |
| }), | |
| .. | |
| }, | |
| ] | |
| ) | |
| } | |
| fn parse_conflict_clause(&mut self) -> Option<SqliteOnConflict> { | |
| if self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]) { | |
| Some(SqliteOnConflict::Replace) | |
| } else if self.parse_keywords(&[Keyword::OR, Keyword::ROLLBACK]) { | |
| Some(SqliteOnConflict::Rollback) | |
| } else if self.parse_keywords(&[Keyword::OR, Keyword::ABORT]) { | |
| Some(SqliteOnConflict::Abort) | |
| } else if self.parse_keywords(&[Keyword::OR, Keyword::FAIL]) { | |
| Some(SqliteOnConflict::Fail) | |
| } else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) { | |
| Some(SqliteOnConflict::Ignore) | |
| } else if self.parse_keyword(Keyword::REPLACE) { | |
| Some(SqliteOnConflict::Replace) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parse PostgreSQL's `OVERRIDING { SYSTEM | USER } VALUE` in an `INSERT`, if present. | |
| fn parse_insert_overriding(&mut self) -> Result<Option<InsertOverriding>, ParserError> { | |
| if !self.parse_keyword(Keyword::OVERRIDING) { | |
| return Ok(None); | |
| } | |
| let overriding = if self.parse_keyword(Keyword::SYSTEM) { | |
| InsertOverriding::System | |
| } else if self.parse_keyword(Keyword::USER) { | |
| InsertOverriding::User | |
| } else { | |
| return self.expected_ref("SYSTEM or USER", self.peek_token_ref()); | |
| }; | |
| self.expect_keyword_is(Keyword::VALUE)?; | |
| Ok(Some(overriding)) | |
| } | |
| /// Parse an optional `PARTITION (...)` clause for INSERT statements. | |
| pub fn parse_insert_partition(&mut self) -> Result<Option<Vec<Expr>>, ParserError> { | |
| if self.parse_keyword(Keyword::PARTITION) { | |
| self.expect_token(&Token::LParen)?; | |
| let partition_cols = Some(self.parse_comma_separated(Parser::parse_expr)?); | |
| self.expect_token(&Token::RParen)?; | |
| Ok(partition_cols) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse optional Hive `INPUTFORMAT ... SERDE ...` clause used by LOAD DATA. | |
| pub fn parse_load_data_table_format( | |
| &mut self, | |
| ) -> Result<Option<HiveLoadDataFormat>, ParserError> { | |
| if self.parse_keyword(Keyword::INPUTFORMAT) { | |
| let input_format = self.parse_expr()?; | |
| self.expect_keyword_is(Keyword::SERDE)?; | |
| let serde = self.parse_expr()?; | |
| Ok(Some(HiveLoadDataFormat { | |
| input_format, | |
| serde, | |
| })) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse an UPDATE statement, returning a `Box`ed SetExpr | |
| /// | |
| /// This is used to reduce the size of the stack frames in debug builds | |
| fn parse_update_setexpr_boxed( | |
| &mut self, | |
| update_token: TokenWithSpan, | |
| ) -> Result<Box<SetExpr>, ParserError> { | |
| Ok(Box::new(SetExpr::Update(self.parse_update(update_token)?))) | |
| } | |
| /// Parse an `UPDATE` statement and return `Statement::Update`. | |
| pub fn parse_update(&mut self, update_token: TokenWithSpan) -> Result<Statement, ParserError> { | |
| let optimizer_hints = self.maybe_parse_optimizer_hints()?; | |
| let or = self.parse_conflict_clause(); | |
| let table = self.parse_table_and_joins()?; | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| let from_before_set = if self.parse_keyword(Keyword::FROM) { | |
| Some(UpdateTableFromKind::BeforeSet( | |
| self.parse_table_with_joins()?, | |
| )) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword(Keyword::SET)?; | |
| let assignments = self.parse_comma_separated(Parser::parse_assignment)?; | |
| let output = self.maybe_parse_output_clause()?; | |
| let from = if from_before_set.is_none() && self.parse_keyword(Keyword::FROM) { | |
| Some(UpdateTableFromKind::AfterSet( | |
| self.parse_table_with_joins()?, | |
| )) | |
| } else { | |
| from_before_set | |
| }; | |
| let selection = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let returning = if self.parse_keyword(Keyword::RETURNING) { | |
| Some(self.parse_comma_separated(Parser::parse_select_item)?) | |
| } else { | |
| None | |
| }; | |
| let order_by = if self.dialect.supports_update_order_by() | |
| && self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) | |
| { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let limit = if self.parse_keyword(Keyword::LIMIT) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let settings = self.parse_settings()?; | |
| Ok(Update { | |
| update_token: update_token.into(), | |
| optimizer_hints, | |
| table, | |
| on_cluster, | |
| assignments, | |
| from, | |
| selection, | |
| returning, | |
| output, | |
| or, | |
| order_by, | |
| limit, | |
| settings, | |
| } | |
| .into()) | |
| } | |
| /// Parse a `var = expr` assignment, used in an UPDATE statement | |
| pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> { | |
| let target = self.parse_assignment_target()?; | |
| if self.dialect.supports_double_eq_assignment() { | |
| if !self.consume_token(&Token::Eq) && !self.consume_token(&Token::DoubleEq) { | |
| return self.expected("= or == (for assignment)", self.peek_token()); | |
| } | |
| } else { | |
| self.expect_token(&Token::Eq)?; | |
| } | |
| let value = self.parse_expr()?; | |
| Ok(Assignment { target, value }) | |
| } | |
| /// Parse the left-hand side of an assignment, used in an UPDATE statement | |
| pub fn parse_assignment_target(&mut self) -> Result<AssignmentTarget, ParserError> { | |
| if self.consume_token(&Token::LParen) { | |
| let columns = self.parse_comma_separated(|p| p.parse_object_name(false))?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(AssignmentTarget::Tuple(columns)) | |
| } else { | |
| let column = self.parse_object_name(false)?; | |
| Ok(AssignmentTarget::ColumnName(column)) | |
| } | |
| } | |
| /// Parse a single function argument, handling named and unnamed variants. | |
| pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> { | |
| // Parse the argument expression once, then check for a named-arg | |
| // operator. Parsing it speculatively and re-parsing on the unnamed | |
| // path is O(2^depth) on nested calls like `CAST(CASE (CAST(CASE (…`. | |
| if self.dialect.supports_named_fn_args_with_expr_name() { | |
| let expr = self.parse_wildcard_expr()?; | |
| // A wildcard is never a named-arg name; only the unnamed form applies. | |
| if !matches!(expr, Expr::Wildcard(_) | Expr::QualifiedWildcard(..)) { | |
| if let Some(operator) = | |
| self.maybe_parse(|p| p.parse_function_named_arg_operator())? | |
| { | |
| let arg = self.parse_wildcard_expr()?.into(); | |
| return Ok(FunctionArg::ExprNamed { | |
| name: expr, | |
| arg, | |
| operator, | |
| }); | |
| } | |
| } | |
| let arg_expr = self.function_arg_expr_from_wildcard(expr)?; | |
| return Ok(FunctionArg::Unnamed( | |
| self.maybe_parse_aliased_function_arg(arg_expr)?, | |
| )); | |
| } | |
| let arg = self.maybe_parse(|p| { | |
| let name = p.parse_identifier()?; | |
| let operator = p.parse_function_named_arg_operator()?; | |
| let arg = p.parse_wildcard_expr()?.into(); | |
| Ok(FunctionArg::Named { | |
| name, | |
| arg, | |
| operator, | |
| }) | |
| })?; | |
| if let Some(arg) = arg { | |
| return Ok(arg); | |
| } | |
| let wildcard_expr = self.parse_wildcard_expr()?; | |
| let arg_expr = self.function_arg_expr_from_wildcard(wildcard_expr)?; | |
| Ok(FunctionArg::Unnamed( | |
| self.maybe_parse_aliased_function_arg(arg_expr)?, | |
| )) | |
| } | |
| /// Wrap an already-parsed expression as a function argument, parsing any | |
| /// trailing wildcard options (e.g. Snowflake's `HASH(* EXCLUDE(col))`). | |
| fn function_arg_expr_from_wildcard( | |
| &mut self, | |
| wildcard_expr: Expr, | |
| ) -> Result<FunctionArgExpr, ParserError> { | |
| Ok(match wildcard_expr { | |
| Expr::Wildcard(ref token) if self.dialect.supports_select_wildcard_exclude() => { | |
| // Parse the options the same way SELECT items do. | |
| let opts = self.parse_wildcard_additional_options(token.0.clone())?; | |
| if opts.opt_exclude.is_some() | |
| || opts.opt_except.is_some() | |
| || opts.opt_replace.is_some() | |
| || opts.opt_rename.is_some() | |
| || opts.opt_ilike.is_some() | |
| { | |
| FunctionArgExpr::WildcardWithOptions(opts) | |
| } else { | |
| wildcard_expr.into() | |
| } | |
| } | |
| other => other.into(), | |
| }) | |
| } | |
| /// Parse an optional `AS <alias>` on an unnamed function argument | |
| /// (e.g. `XMLFOREST(a AS x)` in PostgreSQL). | |
| fn maybe_parse_aliased_function_arg( | |
| &mut self, | |
| arg_expr: FunctionArgExpr, | |
| ) -> Result<FunctionArgExpr, ParserError> { | |
| Ok(match arg_expr { | |
| FunctionArgExpr::Expr(expr) | |
| if self.dialect.supports_aliased_function_args() | |
| && self.parse_keyword(Keyword::AS) => | |
| { | |
| FunctionArgExpr::Expr(Expr::Named { | |
| expr: expr.into(), | |
| name: self.parse_identifier()?, | |
| }) | |
| } | |
| other => other, | |
| }) | |
| } | |
| fn parse_function_named_arg_operator(&mut self) -> Result<FunctionArgOperator, ParserError> { | |
| if self.parse_keyword(Keyword::VALUE) { | |
| return Ok(FunctionArgOperator::Value); | |
| } | |
| let tok = self.next_token(); | |
| match tok.token { | |
| Token::RArrow if self.dialect.supports_named_fn_args_with_rarrow_operator() => { | |
| Ok(FunctionArgOperator::RightArrow) | |
| } | |
| Token::Eq if self.dialect.supports_named_fn_args_with_eq_operator() => { | |
| Ok(FunctionArgOperator::Equals) | |
| } | |
| Token::Assignment | |
| if self | |
| .dialect | |
| .supports_named_fn_args_with_assignment_operator() => | |
| { | |
| Ok(FunctionArgOperator::Assignment) | |
| } | |
| Token::Colon if self.dialect.supports_named_fn_args_with_colon_operator() => { | |
| Ok(FunctionArgOperator::Colon) | |
| } | |
| _ => { | |
| self.prev_token(); | |
| self.expected("argument operator", tok) | |
| } | |
| } | |
| } | |
| /// Parse an optional, comma-separated list of function arguments (consumes closing paren). | |
| pub fn parse_optional_args(&mut self) -> Result<Vec<FunctionArg>, ParserError> { | |
| if self.consume_token(&Token::RParen) { | |
| Ok(vec![]) | |
| } else { | |
| let args = self.parse_comma_separated(Parser::parse_function_args)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(args) | |
| } | |
| } | |
| fn parse_table_function_args(&mut self) -> Result<TableFunctionArgs, ParserError> { | |
| if self.consume_token(&Token::RParen) { | |
| return Ok(TableFunctionArgs { | |
| args: vec![], | |
| settings: None, | |
| }); | |
| } | |
| let mut args = vec![]; | |
| let settings = loop { | |
| if let Some(settings) = self.parse_settings()? { | |
| break Some(settings); | |
| } | |
| args.push(self.parse_function_args()?); | |
| if self.is_parse_comma_separated_end() { | |
| break None; | |
| } | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(TableFunctionArgs { args, settings }) | |
| } | |
| /// Parses a potentially empty list of arguments to a function | |
| /// (including the closing parenthesis). | |
| /// | |
| /// Examples: | |
| /// ```sql | |
| /// FIRST_VALUE(x ORDER BY 1,2,3); | |
| /// FIRST_VALUE(x IGNORE NULL); | |
| /// ``` | |
| fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> { | |
| let mut clauses = vec![]; | |
| // Handle clauses that may exist with an empty argument list | |
| if let Some(null_clause) = self.parse_json_null_clause() { | |
| clauses.push(FunctionArgumentClause::JsonNullClause(null_clause)); | |
| } | |
| if let Some(json_returning_clause) = self.maybe_parse_json_returning_clause()? { | |
| clauses.push(FunctionArgumentClause::JsonReturningClause( | |
| json_returning_clause, | |
| )); | |
| } | |
| if self.consume_token(&Token::RParen) { | |
| return Ok(FunctionArgumentList { | |
| duplicate_treatment: None, | |
| args: vec![], | |
| clauses, | |
| }); | |
| } | |
| let duplicate_treatment = self.parse_duplicate_treatment()?; | |
| let args = self.parse_comma_separated(Parser::parse_function_args)?; | |
| if self.parse_keyword(Keyword::WHERE) { | |
| clauses.push(FunctionArgumentClause::Where(self.parse_expr()?)); | |
| } | |
| if self.dialect.supports_window_function_null_treatment_arg() { | |
| if let Some(null_treatment) = self.parse_null_treatment()? { | |
| clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment)); | |
| } | |
| } | |
| if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| clauses.push(FunctionArgumentClause::OrderBy( | |
| self.parse_comma_separated(Parser::parse_order_by_expr)?, | |
| )); | |
| } | |
| if self.parse_keyword(Keyword::LIMIT) { | |
| clauses.push(FunctionArgumentClause::Limit(self.parse_expr()?)); | |
| } | |
| if dialect_of!(self is GenericDialect | BigQueryDialect) | |
| && self.parse_keyword(Keyword::HAVING) | |
| { | |
| let kind = match self.expect_one_of_keywords(&[Keyword::MIN, Keyword::MAX])? { | |
| Keyword::MIN => HavingBoundKind::Min, | |
| Keyword::MAX => HavingBoundKind::Max, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in having bound"), | |
| )), | |
| }; | |
| clauses.push(FunctionArgumentClause::Having(HavingBound( | |
| kind, | |
| self.parse_expr()?, | |
| ))) | |
| } | |
| if dialect_of!(self is GenericDialect | MySqlDialect) | |
| && self.parse_keyword(Keyword::SEPARATOR) | |
| { | |
| clauses.push(FunctionArgumentClause::Separator(self.parse_value()?)); | |
| } | |
| if let Some(on_overflow) = self.parse_listagg_on_overflow()? { | |
| clauses.push(FunctionArgumentClause::OnOverflow(on_overflow)); | |
| } | |
| if let Some(null_clause) = self.parse_json_null_clause() { | |
| clauses.push(FunctionArgumentClause::JsonNullClause(null_clause)); | |
| } | |
| if let Some(json_returning_clause) = self.maybe_parse_json_returning_clause()? { | |
| clauses.push(FunctionArgumentClause::JsonReturningClause( | |
| json_returning_clause, | |
| )); | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(FunctionArgumentList { | |
| duplicate_treatment, | |
| args, | |
| clauses, | |
| }) | |
| } | |
| fn parse_json_null_clause(&mut self) -> Option<JsonNullClause> { | |
| if self.parse_keywords(&[Keyword::ABSENT, Keyword::ON, Keyword::NULL]) { | |
| Some(JsonNullClause::AbsentOnNull) | |
| } else if self.parse_keywords(&[Keyword::NULL, Keyword::ON, Keyword::NULL]) { | |
| Some(JsonNullClause::NullOnNull) | |
| } else { | |
| None | |
| } | |
| } | |
| fn maybe_parse_json_returning_clause( | |
| &mut self, | |
| ) -> Result<Option<JsonReturningClause>, ParserError> { | |
| if self.parse_keyword(Keyword::RETURNING) { | |
| let data_type = self.parse_data_type()?; | |
| Ok(Some(JsonReturningClause { data_type })) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn parse_duplicate_treatment(&mut self) -> Result<Option<DuplicateTreatment>, ParserError> { | |
| let loc = self.peek_token_ref().span.start; | |
| match ( | |
| self.parse_keyword(Keyword::ALL), | |
| self.parse_keyword(Keyword::DISTINCT), | |
| ) { | |
| (true, false) => Ok(Some(DuplicateTreatment::All)), | |
| (false, true) => Ok(Some(DuplicateTreatment::Distinct)), | |
| (false, false) => Ok(None), | |
| (true, true) => parser_err!("Cannot specify both ALL and DISTINCT".to_string(), loc), | |
| } | |
| } | |
| /// Parse a comma-delimited list of projections after SELECT | |
| pub fn parse_select_item(&mut self) -> Result<SelectItem, ParserError> { | |
| let prefix = self | |
| .parse_one_of_keywords( | |
| self.dialect | |
| .get_reserved_keywords_for_select_item_operator(), | |
| ) | |
| .map(|keyword| Ident::new(format!("{keyword:?}"))); | |
| match self.parse_wildcard_expr()? { | |
| Expr::QualifiedWildcard(prefix, token) => Ok(SelectItem::QualifiedWildcard( | |
| SelectItemQualifiedWildcardKind::ObjectName(prefix), | |
| self.parse_wildcard_additional_options(token.0)?, | |
| )), | |
| Expr::Wildcard(token) => Ok(SelectItem::Wildcard( | |
| self.parse_wildcard_additional_options(token.0)?, | |
| )), | |
| Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none() => { | |
| parser_err!( | |
| format!("Expected an expression, found: {}", v), | |
| self.peek_token_ref().span.start | |
| ) | |
| } | |
| Expr::BinaryOp { | |
| left, | |
| op: BinaryOperator::Eq, | |
| right, | |
| } if self.dialect.supports_eq_alias_assignment() | |
| && matches!(left.as_ref(), Expr::Identifier(_)) => | |
| { | |
| if matches!(right.as_ref(), Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none()) | |
| { | |
| return parser_err!( | |
| format!("Expected an expression, found: {right}"), | |
| self.peek_token_ref().span.start | |
| ); | |
| } | |
| let Expr::Identifier(alias) = *left else { | |
| return parser_err!( | |
| "BUG: expected identifier expression as alias", | |
| self.peek_token_ref().span.start | |
| ); | |
| }; | |
| Ok(SelectItem::ExprWithAlias { | |
| expr: *right, | |
| alias, | |
| }) | |
| } | |
| expr if self.dialect.supports_select_expr_star() | |
| && self.consume_tokens(&[Token::Period, Token::Mul]) => | |
| { | |
| let wildcard_token = self.get_previous_token().clone(); | |
| Ok(SelectItem::QualifiedWildcard( | |
| SelectItemQualifiedWildcardKind::Expr(expr), | |
| self.parse_wildcard_additional_options(wildcard_token)?, | |
| )) | |
| } | |
| expr if self.dialect.supports_select_item_multi_column_alias() | |
| && self.peek_keyword(Keyword::AS) | |
| && self.peek_nth_token(1).token == Token::LParen => | |
| { | |
| self.expect_keyword(Keyword::AS)?; | |
| self.expect_token(&Token::LParen)?; | |
| let aliases = self.parse_comma_separated(|p| p.parse_identifier())?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(SelectItem::ExprWithAliases { | |
| expr: maybe_prefixed_expr(expr, prefix), | |
| aliases, | |
| }) | |
| } | |
| expr => self | |
| .maybe_parse_select_item_alias() | |
| .map(|alias| match alias { | |
| Some(alias) => SelectItem::ExprWithAlias { | |
| expr: maybe_prefixed_expr(expr, prefix), | |
| alias, | |
| }, | |
| None => SelectItem::UnnamedExpr(maybe_prefixed_expr(expr, prefix)), | |
| }), | |
| } | |
| } | |
| /// Parse an [`WildcardAdditionalOptions`] information for wildcard select items. | |
| /// | |
| /// If it is not possible to parse it, will return an option. | |
| pub fn parse_wildcard_additional_options( | |
| &mut self, | |
| wildcard_token: TokenWithSpan, | |
| ) -> Result<WildcardAdditionalOptions, ParserError> { | |
| let opt_ilike = if self.dialect.supports_select_wildcard_ilike() { | |
| self.parse_optional_select_item_ilike()? | |
| } else { | |
| None | |
| }; | |
| let opt_exclude = if opt_ilike.is_none() && self.dialect.supports_select_wildcard_exclude() | |
| { | |
| self.parse_optional_select_item_exclude()? | |
| } else { | |
| None | |
| }; | |
| let opt_except = if self.dialect.supports_select_wildcard_except() { | |
| self.parse_optional_select_item_except()? | |
| } else { | |
| None | |
| }; | |
| let opt_replace = if self.dialect.supports_select_wildcard_replace() { | |
| self.parse_optional_select_item_replace()? | |
| } else { | |
| None | |
| }; | |
| let opt_rename = if self.dialect.supports_select_wildcard_rename() { | |
| self.parse_optional_select_item_rename()? | |
| } else { | |
| None | |
| }; | |
| let opt_alias = if self.dialect.supports_select_wildcard_with_alias() { | |
| self.maybe_parse_select_item_alias()? | |
| } else { | |
| None | |
| }; | |
| Ok(WildcardAdditionalOptions { | |
| wildcard_token: wildcard_token.into(), | |
| opt_ilike, | |
| opt_exclude, | |
| opt_except, | |
| opt_rename, | |
| opt_replace, | |
| opt_alias, | |
| }) | |
| } | |
| /// Parse an [`Ilike`](IlikeSelectItem) information for wildcard select items. | |
| /// | |
| /// If it is not possible to parse it, will return an option. | |
| pub fn parse_optional_select_item_ilike( | |
| &mut self, | |
| ) -> Result<Option<IlikeSelectItem>, ParserError> { | |
| let opt_ilike = if self.parse_keyword(Keyword::ILIKE) { | |
| let next_token = self.next_token(); | |
| let pattern = match next_token.token { | |
| Token::SingleQuotedString(s) => s, | |
| _ => return self.expected("ilike pattern", next_token), | |
| }; | |
| Some(IlikeSelectItem { pattern }) | |
| } else { | |
| None | |
| }; | |
| Ok(opt_ilike) | |
| } | |
| /// Parse an [`Exclude`](ExcludeSelectItem) information for wildcard select items. | |
| /// | |
| /// If it is not possible to parse it, will return an option. | |
| pub fn parse_optional_select_item_exclude( | |
| &mut self, | |
| ) -> Result<Option<ExcludeSelectItem>, ParserError> { | |
| let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) { | |
| if self.consume_token(&Token::LParen) { | |
| let columns = | |
| self.parse_comma_separated(|parser| parser.parse_object_name(false))?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(ExcludeSelectItem::Multiple(columns)) | |
| } else { | |
| let column = self.parse_object_name(false)?; | |
| Some(ExcludeSelectItem::Single(column)) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(opt_exclude) | |
| } | |
| /// Parse an [`Except`](ExceptSelectItem) information for wildcard select items. | |
| /// | |
| /// If it is not possible to parse it, will return an option. | |
| pub fn parse_optional_select_item_except( | |
| &mut self, | |
| ) -> Result<Option<ExceptSelectItem>, ParserError> { | |
| let opt_except = if self.parse_keyword(Keyword::EXCEPT) { | |
| if self.peek_token_ref().token == Token::LParen { | |
| let idents = self.parse_parenthesized_column_list(Mandatory, false)?; | |
| match &idents[..] { | |
| [] => { | |
| return self.expected_ref( | |
| "at least one column should be parsed by the expect clause", | |
| self.peek_token_ref(), | |
| )?; | |
| } | |
| [first, idents @ ..] => Some(ExceptSelectItem { | |
| first_element: first.clone(), | |
| additional_elements: idents.to_vec(), | |
| }), | |
| } | |
| } else { | |
| // Clickhouse allows EXCEPT column_name | |
| let ident = self.parse_identifier()?; | |
| Some(ExceptSelectItem { | |
| first_element: ident, | |
| additional_elements: vec![], | |
| }) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(opt_except) | |
| } | |
| /// Parse a [`Rename`](RenameSelectItem) information for wildcard select items. | |
| pub fn parse_optional_select_item_rename( | |
| &mut self, | |
| ) -> Result<Option<RenameSelectItem>, ParserError> { | |
| let opt_rename = if self.parse_keyword(Keyword::RENAME) { | |
| if self.consume_token(&Token::LParen) { | |
| let idents = | |
| self.parse_comma_separated(|parser| parser.parse_identifier_with_alias())?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(RenameSelectItem::Multiple(idents)) | |
| } else { | |
| let ident = self.parse_identifier_with_alias()?; | |
| Some(RenameSelectItem::Single(ident)) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(opt_rename) | |
| } | |
| /// Parse a [`Replace`](ReplaceSelectItem) information for wildcard select items. | |
| pub fn parse_optional_select_item_replace( | |
| &mut self, | |
| ) -> Result<Option<ReplaceSelectItem>, ParserError> { | |
| let opt_replace = if self.parse_keyword(Keyword::REPLACE) { | |
| if self.consume_token(&Token::LParen) { | |
| let items = self.parse_comma_separated(|parser| { | |
| Ok(Box::new(parser.parse_replace_elements()?)) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(ReplaceSelectItem { items }) | |
| } else { | |
| let tok = self.next_token(); | |
| return self.expected("( after REPLACE but", tok); | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(opt_replace) | |
| } | |
| /// Parse a single element of a `REPLACE (...)` select-item clause. | |
| pub fn parse_replace_elements(&mut self) -> Result<ReplaceSelectElement, ParserError> { | |
| let expr = self.parse_expr()?; | |
| let as_keyword = self.parse_keyword(Keyword::AS); | |
| let ident = self.parse_identifier()?; | |
| Ok(ReplaceSelectElement { | |
| expr, | |
| column_name: ident, | |
| as_keyword, | |
| }) | |
| } | |
| /// Parse ASC or DESC, returns an Option with true if ASC, false of DESC or `None` if none of | |
| /// them. | |
| pub fn parse_asc_desc(&mut self) -> Option<bool> { | |
| if self.parse_keyword(Keyword::ASC) { | |
| Some(true) | |
| } else if self.parse_keyword(Keyword::DESC) { | |
| Some(false) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parse ASC or DESC and map to [OrderBySort]. | |
| fn parse_optional_order_by_sort(&mut self) -> Option<OrderBySort> { | |
| match self.parse_asc_desc() { | |
| Some(true) => Some(OrderBySort::Asc), | |
| Some(false) => Some(OrderBySort::Desc), | |
| None => None, | |
| } | |
| } | |
| /// Parse an [OrderByExpr] expression. | |
| pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> { | |
| self.parse_order_by_expr_inner(false) | |
| .map(|(order_by, _)| order_by) | |
| } | |
| /// Parse an [IndexColumn]. | |
| pub fn parse_create_index_expr(&mut self) -> Result<IndexColumn, ParserError> { | |
| self.parse_order_by_expr_inner(true) | |
| .map(|(column, operator_class)| IndexColumn { | |
| column, | |
| operator_class, | |
| }) | |
| } | |
| fn parse_order_by_expr_inner( | |
| &mut self, | |
| with_operator_class: bool, | |
| ) -> Result<(OrderByExpr, Option<ObjectName>), ParserError> { | |
| let expr = self.parse_expr()?; | |
| let operator_class: Option<ObjectName> = if with_operator_class { | |
| // We check that if non of the following keywords are present, then we parse an | |
| // identifier as operator class. | |
| if self | |
| .peek_one_of_keywords(&[Keyword::ASC, Keyword::DESC, Keyword::NULLS, Keyword::WITH]) | |
| .is_some() | |
| { | |
| None | |
| } else { | |
| self.maybe_parse(|parser| parser.parse_object_name(false))? | |
| } | |
| } else { | |
| None | |
| }; | |
| let options = if !with_operator_class | |
| && self.dialect.supports_order_by_using_operator() | |
| && self.parse_keyword(Keyword::USING) | |
| { | |
| let op = self.parse_order_by_using_operator()?; | |
| OrderByOptions { | |
| sort: Some(OrderBySort::Using(op)), | |
| nulls_first: self.parse_null_ordering_modifier(), | |
| } | |
| } else { | |
| self.parse_order_by_options()? | |
| }; | |
| let with_fill = if self.dialect.supports_with_fill() | |
| && self.parse_keywords(&[Keyword::WITH, Keyword::FILL]) | |
| { | |
| Some(self.parse_with_fill()?) | |
| } else { | |
| None | |
| }; | |
| Ok(( | |
| OrderByExpr { | |
| expr, | |
| options, | |
| with_fill, | |
| }, | |
| operator_class, | |
| )) | |
| } | |
| fn parse_order_by_using_operator(&mut self) -> Result<ObjectName, ParserError> { | |
| if self.parse_keyword(Keyword::OPERATOR) { | |
| self.expect_token(&Token::LParen)?; | |
| let operator_name = self.parse_operator_name()?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(operator_name); | |
| } | |
| let token = self.next_token(); | |
| Ok(ObjectName::from(vec![Ident::new(token.token.to_string())])) | |
| } | |
| fn parse_null_ordering_modifier(&mut self) -> Option<bool> { | |
| if self.parse_keywords(&[Keyword::NULLS, Keyword::FIRST]) { | |
| Some(true) | |
| } else if self.parse_keywords(&[Keyword::NULLS, Keyword::LAST]) { | |
| Some(false) | |
| } else { | |
| None | |
| } | |
| } | |
| fn parse_order_by_options(&mut self) -> Result<OrderByOptions, ParserError> { | |
| let sort = self.parse_optional_order_by_sort(); | |
| let nulls_first = self.parse_null_ordering_modifier(); | |
| Ok(OrderByOptions { sort, nulls_first }) | |
| } | |
| // Parse a WITH FILL clause (ClickHouse dialect) | |
| // that follow the WITH FILL keywords in a ORDER BY clause | |
| /// Parse a `WITH FILL` clause used in ORDER BY (ClickHouse dialect). | |
| pub fn parse_with_fill(&mut self) -> Result<WithFill, ParserError> { | |
| let from = if self.parse_keyword(Keyword::FROM) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let to = if self.parse_keyword(Keyword::TO) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let step = if self.parse_keyword(Keyword::STEP) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(WithFill { from, to, step }) | |
| } | |
| /// Parse a set of comma separated INTERPOLATE expressions (ClickHouse dialect) | |
| /// that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier | |
| pub fn parse_interpolations(&mut self) -> Result<Option<Interpolate>, ParserError> { | |
| if !self.parse_keyword(Keyword::INTERPOLATE) { | |
| return Ok(None); | |
| } | |
| if self.consume_token(&Token::LParen) { | |
| let interpolations = | |
| self.parse_comma_separated0(|p| p.parse_interpolation(), Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| // INTERPOLATE () and INTERPOLATE ( ... ) variants | |
| return Ok(Some(Interpolate { | |
| exprs: Some(interpolations), | |
| })); | |
| } | |
| // INTERPOLATE | |
| Ok(Some(Interpolate { exprs: None })) | |
| } | |
| /// Parse a INTERPOLATE expression (ClickHouse dialect) | |
| pub fn parse_interpolation(&mut self) -> Result<InterpolateExpr, ParserError> { | |
| let column = self.parse_identifier()?; | |
| let expr = if self.parse_keyword(Keyword::AS) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| Ok(InterpolateExpr { column, expr }) | |
| } | |
| /// Parse a TOP clause, MSSQL equivalent of LIMIT, | |
| /// that follows after `SELECT [DISTINCT]`. | |
| pub fn parse_top(&mut self) -> Result<Top, ParserError> { | |
| let quantity = if self.consume_token(&Token::LParen) { | |
| let quantity = self.parse_expr()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(TopQuantity::Expr(quantity)) | |
| } else { | |
| let next_token = self.next_token(); | |
| let quantity = match next_token.token { | |
| Token::Number(s, _) => Self::parse::<u64>(s, next_token.span.start)?, | |
| _ => self.expected("literal int", next_token)?, | |
| }; | |
| Some(TopQuantity::Constant(quantity)) | |
| }; | |
| let percent = self.parse_keyword(Keyword::PERCENT); | |
| let with_ties = self.parse_keywords(&[Keyword::WITH, Keyword::TIES]); | |
| Ok(Top { | |
| with_ties, | |
| percent, | |
| quantity, | |
| }) | |
| } | |
| /// Parse a LIMIT clause | |
| pub fn parse_limit(&mut self) -> Result<Option<Expr>, ParserError> { | |
| if self.parse_keyword(Keyword::ALL) { | |
| Ok(None) | |
| } else { | |
| Ok(Some(self.parse_expr()?)) | |
| } | |
| } | |
| /// Parse an OFFSET clause | |
| pub fn parse_offset(&mut self) -> Result<Offset, ParserError> { | |
| let value = self.parse_expr()?; | |
| let rows = if self.parse_keyword(Keyword::ROW) { | |
| OffsetRows::Row | |
| } else if self.parse_keyword(Keyword::ROWS) { | |
| OffsetRows::Rows | |
| } else { | |
| OffsetRows::None | |
| }; | |
| Ok(Offset { value, rows }) | |
| } | |
| /// Parse a FETCH clause | |
| pub fn parse_fetch(&mut self) -> Result<Fetch, ParserError> { | |
| let _ = self.parse_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]); | |
| let (quantity, percent) = if self | |
| .parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS]) | |
| .is_some() | |
| { | |
| (None, false) | |
| } else { | |
| let quantity = Expr::Value(self.parse_value()?); | |
| let percent = self.parse_keyword(Keyword::PERCENT); | |
| let _ = self.parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS]); | |
| (Some(quantity), percent) | |
| }; | |
| let with_ties = if self.parse_keyword(Keyword::ONLY) { | |
| false | |
| } else { | |
| self.parse_keywords(&[Keyword::WITH, Keyword::TIES]) | |
| }; | |
| Ok(Fetch { | |
| with_ties, | |
| percent, | |
| quantity, | |
| }) | |
| } | |
| /// Parse a FOR UPDATE/FOR SHARE clause | |
| pub fn parse_lock(&mut self) -> Result<LockClause, ParserError> { | |
| let lock_type = match self.expect_one_of_keywords(&[Keyword::UPDATE, Keyword::SHARE])? { | |
| Keyword::UPDATE => LockType::Update, | |
| Keyword::SHARE => LockType::Share, | |
| unexpected_keyword => return Err(ParserError::ParserError( | |
| format!("Internal parser error: expected any of {{UPDATE, SHARE}}, got {unexpected_keyword:?}"), | |
| )), | |
| }; | |
| let of = if self.parse_keyword(Keyword::OF) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| let nonblock = if self.parse_keyword(Keyword::NOWAIT) { | |
| Some(NonBlock::Nowait) | |
| } else if self.parse_keywords(&[Keyword::SKIP, Keyword::LOCKED]) { | |
| Some(NonBlock::SkipLocked) | |
| } else { | |
| None | |
| }; | |
| Ok(LockClause { | |
| lock_type, | |
| of, | |
| nonblock, | |
| }) | |
| } | |
| /// Parse a PostgreSQL `LOCK` statement. | |
| pub fn parse_lock_statement(&mut self) -> Result<Lock, ParserError> { | |
| self.expect_keyword(Keyword::LOCK)?; | |
| if self.peek_keyword(Keyword::TABLES) { | |
| return self.expected_ref("TABLE or a table name", self.peek_token_ref()); | |
| } | |
| let _ = self.parse_keyword(Keyword::TABLE); | |
| let tables = self.parse_comma_separated(Parser::parse_lock_table_target)?; | |
| let lock_mode = if self.parse_keyword(Keyword::IN) { | |
| let lock_mode = self.parse_lock_table_mode()?; | |
| self.expect_keyword(Keyword::MODE)?; | |
| Some(lock_mode) | |
| } else { | |
| None | |
| }; | |
| let nowait = self.parse_keyword(Keyword::NOWAIT); | |
| Ok(Lock { | |
| tables, | |
| lock_mode, | |
| nowait, | |
| }) | |
| } | |
| fn parse_lock_table_target(&mut self) -> Result<LockTableTarget, ParserError> { | |
| let only = self.parse_keyword(Keyword::ONLY); | |
| let name = self.parse_object_name(false)?; | |
| let has_asterisk = self.consume_token(&Token::Mul); | |
| Ok(LockTableTarget { | |
| name, | |
| only, | |
| has_asterisk, | |
| }) | |
| } | |
| fn parse_lock_table_mode(&mut self) -> Result<LockTableMode, ParserError> { | |
| if self.parse_keywords(&[Keyword::ACCESS, Keyword::SHARE]) { | |
| Ok(LockTableMode::AccessShare) | |
| } else if self.parse_keywords(&[Keyword::ACCESS, Keyword::EXCLUSIVE]) { | |
| Ok(LockTableMode::AccessExclusive) | |
| } else if self.parse_keywords(&[Keyword::ROW, Keyword::SHARE]) { | |
| Ok(LockTableMode::RowShare) | |
| } else if self.parse_keywords(&[Keyword::ROW, Keyword::EXCLUSIVE]) { | |
| Ok(LockTableMode::RowExclusive) | |
| } else if self.parse_keywords(&[Keyword::SHARE, Keyword::UPDATE, Keyword::EXCLUSIVE]) { | |
| Ok(LockTableMode::ShareUpdateExclusive) | |
| } else if self.parse_keywords(&[Keyword::SHARE, Keyword::ROW, Keyword::EXCLUSIVE]) { | |
| Ok(LockTableMode::ShareRowExclusive) | |
| } else if self.parse_keyword(Keyword::SHARE) { | |
| Ok(LockTableMode::Share) | |
| } else if self.parse_keyword(Keyword::EXCLUSIVE) { | |
| Ok(LockTableMode::Exclusive) | |
| } else { | |
| self.expected_ref("a PostgreSQL LOCK TABLE mode", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse a VALUES clause | |
| pub fn parse_values( | |
| &mut self, | |
| allow_empty: bool, | |
| value_keyword: bool, | |
| ) -> Result<Values, ParserError> { | |
| let mut explicit_row = false; | |
| let rows = self.parse_comma_separated(|parser| { | |
| if parser.parse_keyword(Keyword::ROW) { | |
| explicit_row = true; | |
| } | |
| Ok(Parens { | |
| opening_token: parser.expect_token(&Token::LParen)?.into(), | |
| content: if allow_empty && parser.peek_token_ref().token == Token::RParen { | |
| vec![] | |
| } else { | |
| parser.parse_comma_separated(Parser::parse_expr)? | |
| }, | |
| closing_token: parser.expect_token(&Token::RParen)?.into(), | |
| }) | |
| })?; | |
| Ok(Values { | |
| explicit_row, | |
| rows, | |
| value_keyword, | |
| }) | |
| } | |
| /// Parse a 'START TRANSACTION' statement | |
| pub fn parse_start_transaction(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword_is(Keyword::TRANSACTION)?; | |
| Ok(Statement::StartTransaction { | |
| modes: self.parse_transaction_modes()?, | |
| begin: false, | |
| transaction: Some(BeginTransactionKind::Transaction), | |
| modifier: None, | |
| statements: vec![], | |
| exception: None, | |
| has_end_keyword: false, | |
| }) | |
| } | |
| /// Parse a transaction modifier keyword that can follow a `BEGIN` statement. | |
| pub(crate) fn parse_transaction_modifier(&mut self) -> Option<TransactionModifier> { | |
| if !self.dialect.supports_start_transaction_modifier() { | |
| None | |
| } else if self.parse_keyword(Keyword::DEFERRED) { | |
| Some(TransactionModifier::Deferred) | |
| } else if self.parse_keyword(Keyword::IMMEDIATE) { | |
| Some(TransactionModifier::Immediate) | |
| } else if self.parse_keyword(Keyword::EXCLUSIVE) { | |
| Some(TransactionModifier::Exclusive) | |
| } else if self.parse_keyword(Keyword::TRY) { | |
| Some(TransactionModifier::Try) | |
| } else if self.parse_keyword(Keyword::CATCH) { | |
| Some(TransactionModifier::Catch) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Parse a 'BEGIN' statement | |
| pub fn parse_begin(&mut self) -> Result<Statement, ParserError> { | |
| let modifier = self.parse_transaction_modifier(); | |
| let transaction = | |
| match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN]) | |
| { | |
| Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction), | |
| Some(Keyword::WORK) => Some(BeginTransactionKind::Work), | |
| Some(Keyword::TRAN) => Some(BeginTransactionKind::Tran), | |
| _ => None, | |
| }; | |
| Ok(Statement::StartTransaction { | |
| modes: self.parse_transaction_modes()?, | |
| begin: true, | |
| transaction, | |
| modifier, | |
| statements: vec![], | |
| exception: None, | |
| has_end_keyword: false, | |
| }) | |
| } | |
| /// Parse a 'BEGIN ... EXCEPTION ... END' block | |
| pub fn parse_begin_exception_end(&mut self) -> Result<Statement, ParserError> { | |
| let statements = self.parse_statement_list(&[Keyword::EXCEPTION, Keyword::END])?; | |
| let exception = if self.parse_keyword(Keyword::EXCEPTION) { | |
| let mut when = Vec::new(); | |
| // We can have multiple `WHEN` arms so we consume all cases until `END` | |
| while !self.peek_keyword(Keyword::END) { | |
| self.expect_keyword(Keyword::WHEN)?; | |
| // Each `WHEN` case can have one or more conditions, e.g. | |
| // WHEN EXCEPTION_1 [OR EXCEPTION_2] THEN | |
| // So we parse identifiers until the `THEN` keyword. | |
| let mut idents = Vec::new(); | |
| while !self.parse_keyword(Keyword::THEN) { | |
| let ident = self.parse_identifier()?; | |
| idents.push(ident); | |
| self.maybe_parse(|p| p.expect_keyword(Keyword::OR))?; | |
| } | |
| let statements = self.parse_statement_list(&[Keyword::WHEN, Keyword::END])?; | |
| when.push(ExceptionWhen { idents, statements }); | |
| } | |
| Some(when) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword(Keyword::END)?; | |
| Ok(Statement::StartTransaction { | |
| begin: true, | |
| statements, | |
| exception, | |
| has_end_keyword: true, | |
| transaction: None, | |
| modifier: None, | |
| modes: Default::default(), | |
| }) | |
| } | |
| /// Parse an 'END' statement | |
| pub fn parse_end(&mut self) -> Result<Statement, ParserError> { | |
| let modifier = if !self.dialect.supports_end_transaction_modifier() { | |
| None | |
| } else if self.parse_keyword(Keyword::TRY) { | |
| Some(TransactionModifier::Try) | |
| } else if self.parse_keyword(Keyword::CATCH) { | |
| Some(TransactionModifier::Catch) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::Commit { | |
| chain: self.parse_commit_rollback_chain()?, | |
| end: true, | |
| modifier, | |
| }) | |
| } | |
| /// Parse a list of transaction modes | |
| pub fn parse_transaction_modes(&mut self) -> Result<Vec<TransactionMode>, ParserError> { | |
| let mut modes = vec![]; | |
| let mut required = false; | |
| loop { | |
| let mode = if self.parse_keywords(&[Keyword::ISOLATION, Keyword::LEVEL]) { | |
| let iso_level = if self.parse_keywords(&[Keyword::READ, Keyword::UNCOMMITTED]) { | |
| TransactionIsolationLevel::ReadUncommitted | |
| } else if self.parse_keywords(&[Keyword::READ, Keyword::COMMITTED]) { | |
| TransactionIsolationLevel::ReadCommitted | |
| } else if self.parse_keywords(&[Keyword::REPEATABLE, Keyword::READ]) { | |
| TransactionIsolationLevel::RepeatableRead | |
| } else if self.parse_keyword(Keyword::SERIALIZABLE) { | |
| TransactionIsolationLevel::Serializable | |
| } else if self.parse_keyword(Keyword::SNAPSHOT) { | |
| TransactionIsolationLevel::Snapshot | |
| } else { | |
| self.expected_ref("isolation level", self.peek_token_ref())? | |
| }; | |
| TransactionMode::IsolationLevel(iso_level) | |
| } else if self.parse_keywords(&[Keyword::READ, Keyword::ONLY]) { | |
| TransactionMode::AccessMode(TransactionAccessMode::ReadOnly) | |
| } else if self.parse_keywords(&[Keyword::READ, Keyword::WRITE]) { | |
| TransactionMode::AccessMode(TransactionAccessMode::ReadWrite) | |
| } else if required { | |
| self.expected_ref("transaction mode", self.peek_token_ref())? | |
| } else { | |
| break; | |
| }; | |
| modes.push(mode); | |
| // ANSI requires a comma after each transaction mode, but | |
| // PostgreSQL, for historical reasons, does not. We follow | |
| // PostgreSQL in making the comma optional, since that is strictly | |
| // more general. | |
| required = self.consume_token(&Token::Comma); | |
| } | |
| Ok(modes) | |
| } | |
| /// Parse a 'COMMIT' statement | |
| pub fn parse_commit(&mut self) -> Result<Statement, ParserError> { | |
| Ok(Statement::Commit { | |
| chain: self.parse_commit_rollback_chain()?, | |
| end: false, | |
| modifier: None, | |
| }) | |
| } | |
| /// Parse a 'ROLLBACK' statement | |
| pub fn parse_rollback(&mut self) -> Result<Statement, ParserError> { | |
| let chain = self.parse_commit_rollback_chain()?; | |
| let savepoint = self.parse_rollback_savepoint()?; | |
| Ok(Statement::Rollback { chain, savepoint }) | |
| } | |
| /// Parse an 'ABORT' statement | |
| /// | |
| /// ```sql | |
| /// ABORT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ] | |
| /// ``` | |
| pub fn parse_abort(&mut self) -> Result<Statement, ParserError> { | |
| let chain = self.parse_commit_rollback_chain()?; | |
| Ok(Statement::Rollback { | |
| chain, | |
| savepoint: None, | |
| }) | |
| } | |
| /// Parse an optional `AND [NO] CHAIN` clause for `COMMIT` and `ROLLBACK` statements | |
| pub fn parse_commit_rollback_chain(&mut self) -> Result<bool, ParserError> { | |
| let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN]); | |
| if self.parse_keyword(Keyword::AND) { | |
| let chain = !self.parse_keyword(Keyword::NO); | |
| self.expect_keyword_is(Keyword::CHAIN)?; | |
| Ok(chain) | |
| } else { | |
| Ok(false) | |
| } | |
| } | |
| /// Parse an optional 'TO SAVEPOINT savepoint_name' clause for ROLLBACK statements | |
| pub fn parse_rollback_savepoint(&mut self) -> Result<Option<Ident>, ParserError> { | |
| if self.parse_keyword(Keyword::TO) { | |
| let _ = self.parse_keyword(Keyword::SAVEPOINT); | |
| let savepoint = self.parse_identifier()?; | |
| Ok(Some(savepoint)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse a 'RAISERROR' statement | |
| pub fn parse_raiserror(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let message = Box::new(self.parse_expr()?); | |
| self.expect_token(&Token::Comma)?; | |
| let severity = Box::new(self.parse_expr()?); | |
| self.expect_token(&Token::Comma)?; | |
| let state = Box::new(self.parse_expr()?); | |
| let arguments = if self.consume_token(&Token::Comma) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| let options = if self.parse_keyword(Keyword::WITH) { | |
| self.parse_comma_separated(Parser::parse_raiserror_option)? | |
| } else { | |
| vec![] | |
| }; | |
| Ok(Statement::RaisError { | |
| message, | |
| severity, | |
| state, | |
| arguments, | |
| options, | |
| }) | |
| } | |
| /// Parse a single `RAISERROR` option | |
| pub fn parse_raiserror_option(&mut self) -> Result<RaisErrorOption, ParserError> { | |
| match self.expect_one_of_keywords(&[Keyword::LOG, Keyword::NOWAIT, Keyword::SETERROR])? { | |
| Keyword::LOG => Ok(RaisErrorOption::Log), | |
| Keyword::NOWAIT => Ok(RaisErrorOption::NoWait), | |
| Keyword::SETERROR => Ok(RaisErrorOption::SetError), | |
| _ => self.expected_ref( | |
| "LOG, NOWAIT OR SETERROR raiserror option", | |
| self.peek_token_ref(), | |
| ), | |
| } | |
| } | |
| /// Parse a MSSQL `THROW` statement. | |
| /// | |
| /// See [Statement::Throw] | |
| pub fn parse_throw(&mut self) -> Result<ThrowStatement, ParserError> { | |
| self.expect_keyword_is(Keyword::THROW)?; | |
| let error_number = self.maybe_parse(|p| p.parse_expr().map(Box::new))?; | |
| let (message, state) = if error_number.is_some() { | |
| self.expect_token(&Token::Comma)?; | |
| let message = Box::new(self.parse_expr()?); | |
| self.expect_token(&Token::Comma)?; | |
| let state = Box::new(self.parse_expr()?); | |
| (Some(message), Some(state)) | |
| } else { | |
| (None, None) | |
| }; | |
| Ok(ThrowStatement { | |
| error_number, | |
| message, | |
| state, | |
| }) | |
| } | |
| /// Parse a SQL `DEALLOCATE` statement | |
| pub fn parse_deallocate(&mut self) -> Result<Statement, ParserError> { | |
| let prepare = self.parse_keyword(Keyword::PREPARE); | |
| let name = self.parse_identifier()?; | |
| Ok(Statement::Deallocate { name, prepare }) | |
| } | |
| /// Parse a SQL `EXECUTE` statement | |
| pub fn parse_execute(&mut self) -> Result<Statement, ParserError> { | |
| let immediate = | |
| self.dialect.supports_execute_immediate() && self.parse_keyword(Keyword::IMMEDIATE); | |
| // When `EXEC` is immediately followed by `(`, the content is a dynamic-SQL | |
| // expression — e.g. `EXEC (@sql)`, `EXEC ('SELECT ...')`, or | |
| // `EXEC ('SELECT ... FROM ' + @tbl + ' WHERE ...')`. | |
| // Skip name parsing; the expression ends up in `parameters` via the | |
| // `has_parentheses` path below, consistent with `EXECUTE IMMEDIATE <expr>`. | |
| let name = if immediate || matches!(self.peek_token_ref().token, Token::LParen) { | |
| None | |
| } else { | |
| Some(self.parse_object_name(false)?) | |
| }; | |
| let has_parentheses = self.consume_token(&Token::LParen); | |
| let end_kws = &[Keyword::USING, Keyword::OUTPUT, Keyword::DEFAULT]; | |
| let end_token = match (has_parentheses, self.peek_token().token) { | |
| (true, _) => Token::RParen, | |
| (false, Token::EOF) => Token::EOF, | |
| (false, Token::Word(w)) if end_kws.contains(&w.keyword) => Token::Word(w), | |
| (false, _) => Token::SemiColon, | |
| }; | |
| let parameters = self.parse_comma_separated0(Parser::parse_expr, end_token)?; | |
| if has_parentheses { | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| let into = if self.parse_keyword(Keyword::INTO) { | |
| self.parse_comma_separated(Self::parse_identifier)? | |
| } else { | |
| vec![] | |
| }; | |
| let using = if self.parse_keyword(Keyword::USING) { | |
| self.parse_comma_separated(Self::parse_expr_with_alias)? | |
| } else { | |
| vec![] | |
| }; | |
| let output = self.parse_keyword(Keyword::OUTPUT); | |
| let default = self.parse_keyword(Keyword::DEFAULT); | |
| Ok(Statement::Execute { | |
| immediate, | |
| name, | |
| parameters, | |
| has_parentheses, | |
| into, | |
| using, | |
| output, | |
| default, | |
| }) | |
| } | |
| /// Parse a SQL `PREPARE` statement | |
| pub fn parse_prepare(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_identifier()?; | |
| let mut data_types = vec![]; | |
| if self.consume_token(&Token::LParen) { | |
| data_types = self.parse_comma_separated(Parser::parse_data_type)?; | |
| self.expect_token(&Token::RParen)?; | |
| } | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let statement = Box::new(self.parse_statement()?); | |
| Ok(Statement::Prepare { | |
| name, | |
| data_types, | |
| statement, | |
| }) | |
| } | |
| /// Parse a SQL `UNLOAD` statement | |
| pub fn parse_unload(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword(Keyword::UNLOAD)?; | |
| self.expect_token(&Token::LParen)?; | |
| let (query, query_text) = | |
| if matches!(self.peek_token_ref().token, Token::SingleQuotedString(_)) { | |
| (None, Some(self.parse_literal_string()?)) | |
| } else { | |
| (Some(self.parse_query()?), None) | |
| }; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_keyword_is(Keyword::TO)?; | |
| let to = self.parse_identifier()?; | |
| let auth = if self.parse_keyword(Keyword::IAM_ROLE) { | |
| Some(self.parse_iam_role_kind()?) | |
| } else { | |
| None | |
| }; | |
| let with = self.parse_options(Keyword::WITH)?; | |
| let mut options = vec![]; | |
| while let Some(opt) = self.maybe_parse(|parser| parser.parse_copy_legacy_option())? { | |
| options.push(opt); | |
| } | |
| Ok(Statement::Unload { | |
| query, | |
| query_text, | |
| to, | |
| auth, | |
| with, | |
| options, | |
| }) | |
| } | |
| fn parse_select_into(&mut self) -> Result<SelectInto, ParserError> { | |
| let temporary = self | |
| .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) | |
| .is_some(); | |
| let unlogged = self.parse_keyword(Keyword::UNLOGGED); | |
| let table = self.parse_keyword(Keyword::TABLE); | |
| let targets = self.parse_comma_separated(Parser::parse_expr)?; | |
| Ok(SelectInto { | |
| temporary, | |
| unlogged, | |
| table, | |
| targets, | |
| }) | |
| } | |
| fn parse_pragma_value(&mut self) -> Result<ValueWithSpan, ParserError> { | |
| let v = self.parse_value()?; | |
| match &v.value { | |
| Value::SingleQuotedString(_) => Ok(v), | |
| Value::DoubleQuotedString(_) => Ok(v), | |
| Value::Number(_, _) => Ok(v), | |
| Value::Placeholder(_) => Ok(v), | |
| _ => { | |
| self.prev_token(); | |
| self.expected_ref("number or string or ? placeholder", self.peek_token_ref()) | |
| } | |
| } | |
| } | |
| /// PRAGMA [schema-name '.'] pragma-name [('=' pragma-value) | '(' pragma-value ')'] | |
| pub fn parse_pragma(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| if self.consume_token(&Token::LParen) { | |
| let value = self.parse_pragma_value()?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Statement::Pragma { | |
| name, | |
| value: Some(value), | |
| is_eq: false, | |
| }) | |
| } else if self.consume_token(&Token::Eq) { | |
| Ok(Statement::Pragma { | |
| name, | |
| value: Some(self.parse_pragma_value()?), | |
| is_eq: true, | |
| }) | |
| } else { | |
| Ok(Statement::Pragma { | |
| name, | |
| value: None, | |
| is_eq: false, | |
| }) | |
| } | |
| } | |
| /// `INSTALL [extension_name]` | |
| pub fn parse_install(&mut self) -> Result<Statement, ParserError> { | |
| let extension_name = self.parse_identifier()?; | |
| Ok(Statement::Install { extension_name }) | |
| } | |
| /// Parse a SQL LOAD statement | |
| pub fn parse_load(&mut self) -> Result<Statement, ParserError> { | |
| if self.dialect.supports_load_extension() { | |
| let extension_name = self.parse_identifier()?; | |
| Ok(Statement::Load { extension_name }) | |
| } else if self.parse_keyword(Keyword::DATA) && self.dialect.supports_load_data() { | |
| let local = self.parse_one_of_keywords(&[Keyword::LOCAL]).is_some(); | |
| self.expect_keyword_is(Keyword::INPATH)?; | |
| let inpath = self.parse_literal_string()?; | |
| let overwrite = self.parse_one_of_keywords(&[Keyword::OVERWRITE]).is_some(); | |
| self.expect_keyword_is(Keyword::INTO)?; | |
| self.expect_keyword_is(Keyword::TABLE)?; | |
| let table_name = self.parse_object_name(false)?; | |
| let partitioned = self.parse_insert_partition()?; | |
| let table_format = self.parse_load_data_table_format()?; | |
| Ok(Statement::LoadData { | |
| local, | |
| inpath, | |
| overwrite, | |
| table_name, | |
| partitioned, | |
| table_format, | |
| }) | |
| } else { | |
| self.expected_ref( | |
| "`DATA` or an extension name after `LOAD`", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| /// ClickHouse: | |
| /// ```sql | |
| /// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]] | |
| /// ``` | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize) | |
| /// | |
| /// Databricks: | |
| /// ```sql | |
| /// OPTIMIZE table_name [WHERE predicate] [ZORDER BY (col_name1 [, ...])] | |
| /// ``` | |
| /// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-optimize.html) | |
| pub fn parse_optimize_table(&mut self) -> Result<Statement, ParserError> { | |
| let has_table_keyword = self.parse_keyword(Keyword::TABLE); | |
| let name = self.parse_object_name(false)?; | |
| // ClickHouse-specific options | |
| let on_cluster = self.parse_optional_on_cluster()?; | |
| let partition = if self.parse_keyword(Keyword::PARTITION) { | |
| if self.parse_keyword(Keyword::ID) { | |
| Some(Partition::Identifier(self.parse_identifier()?)) | |
| } else { | |
| Some(Partition::Expr(self.parse_expr()?)) | |
| } | |
| } else { | |
| None | |
| }; | |
| let include_final = self.parse_keyword(Keyword::FINAL); | |
| let deduplicate = if self.parse_keyword(Keyword::DEDUPLICATE) { | |
| if self.parse_keyword(Keyword::BY) { | |
| Some(Deduplicate::ByExpression(self.parse_expr()?)) | |
| } else { | |
| Some(Deduplicate::All) | |
| } | |
| } else { | |
| None | |
| }; | |
| // Databricks-specific options | |
| let predicate = if self.parse_keyword(Keyword::WHERE) { | |
| Some(self.parse_expr()?) | |
| } else { | |
| None | |
| }; | |
| let zorder = if self.parse_keywords(&[Keyword::ZORDER, Keyword::BY]) { | |
| self.expect_token(&Token::LParen)?; | |
| let columns = self.parse_comma_separated(|p| p.parse_expr())?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(columns) | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::OptimizeTable { | |
| name, | |
| has_table_keyword, | |
| on_cluster, | |
| partition, | |
| include_final, | |
| deduplicate, | |
| predicate, | |
| zorder, | |
| }) | |
| } | |
| /// ```sql | |
| /// CREATE [ { TEMPORARY | TEMP } ] SEQUENCE [ IF NOT EXISTS ] <sequence_name> | |
| /// ``` | |
| /// | |
| /// See [Postgres docs](https://www.postgresql.org/docs/current/sql-createsequence.html) for more details. | |
| pub fn parse_create_sequence(&mut self, temporary: bool) -> Result<Statement, ParserError> { | |
| //[ IF NOT EXISTS ] | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| //name | |
| let name = self.parse_object_name(false)?; | |
| //[ AS data_type ] | |
| let mut data_type: Option<DataType> = None; | |
| if self.parse_keywords(&[Keyword::AS]) { | |
| data_type = Some(self.parse_data_type()?) | |
| } | |
| let sequence_options = self.parse_create_sequence_options()?; | |
| // [ OWNED BY { table_name.column_name | NONE } ] | |
| let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) { | |
| if self.parse_keywords(&[Keyword::NONE]) { | |
| Some(ObjectName::from(vec![Ident::new("NONE")])) | |
| } else { | |
| Some(self.parse_object_name(false)?) | |
| } | |
| } else { | |
| None | |
| }; | |
| Ok(Statement::CreateSequence { | |
| temporary, | |
| if_not_exists, | |
| name, | |
| data_type, | |
| sequence_options, | |
| owned_by, | |
| }) | |
| } | |
| fn parse_create_sequence_options(&mut self) -> Result<Vec<SequenceOptions>, ParserError> { | |
| let mut sequence_options = vec![]; | |
| //[ INCREMENT [ BY ] increment ] | |
| if self.parse_keywords(&[Keyword::INCREMENT]) { | |
| if self.parse_keywords(&[Keyword::BY]) { | |
| sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, true)); | |
| } else { | |
| sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, false)); | |
| } | |
| } | |
| //[ MINVALUE minvalue | NO MINVALUE ] | |
| if self.parse_keyword(Keyword::MINVALUE) { | |
| sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?))); | |
| } else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) { | |
| sequence_options.push(SequenceOptions::MinValue(None)); | |
| } | |
| //[ MAXVALUE maxvalue | NO MAXVALUE ] | |
| if self.parse_keywords(&[Keyword::MAXVALUE]) { | |
| sequence_options.push(SequenceOptions::MaxValue(Some(self.parse_number()?))); | |
| } else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) { | |
| sequence_options.push(SequenceOptions::MaxValue(None)); | |
| } | |
| //[ START [ WITH ] start ] | |
| if self.parse_keywords(&[Keyword::START]) { | |
| if self.parse_keywords(&[Keyword::WITH]) { | |
| sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true)); | |
| } else { | |
| sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false)); | |
| } | |
| } | |
| //[ CACHE cache ] | |
| if self.parse_keywords(&[Keyword::CACHE]) { | |
| sequence_options.push(SequenceOptions::Cache(self.parse_number()?)); | |
| } | |
| // [ [ NO ] CYCLE ] | |
| if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) { | |
| sequence_options.push(SequenceOptions::Cycle(true)); | |
| } else if self.parse_keywords(&[Keyword::CYCLE]) { | |
| sequence_options.push(SequenceOptions::Cycle(false)); | |
| } | |
| Ok(sequence_options) | |
| } | |
| /// Parse a `CREATE SERVER` statement. | |
| /// | |
| /// See [Statement::CreateServer] | |
| pub fn parse_pg_create_server(&mut self) -> Result<Statement, ParserError> { | |
| let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| let server_type = if self.parse_keyword(Keyword::TYPE) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| let version = if self.parse_keyword(Keyword::VERSION) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER])?; | |
| let foreign_data_wrapper = self.parse_object_name(false)?; | |
| let options = self.parse_pg_options_clause()?; | |
| Ok(Statement::CreateServer(CreateServerStatement { | |
| name, | |
| if_not_exists: ine, | |
| server_type, | |
| version, | |
| foreign_data_wrapper, | |
| options, | |
| })) | |
| } | |
| /// Parse an optional Postgres `OPTIONS ( key value [, ...] )` clause. | |
| fn parse_pg_options_clause(&mut self) -> Result<Option<Vec<CreateServerOption>>, ParserError> { | |
| if !self.parse_keyword(Keyword::OPTIONS) { | |
| return Ok(None); | |
| } | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated(|p| { | |
| let key = p.parse_identifier()?; | |
| let value = p.parse_identifier()?; | |
| Ok(CreateServerOption { key, value }) | |
| })?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Some(options)) | |
| } | |
| /// Parse a `CREATE FOREIGN TABLE` statement. | |
| /// | |
| /// Per-column `OPTIONS ( ... )`, `INHERITS`, and the `PARTITION OF` form are | |
| /// not parsed yet. | |
| /// | |
| /// See <https://www.postgresql.org/docs/current/sql-createforeigntable.html> | |
| pub fn parse_create_foreign_table(&mut self) -> Result<CreateForeignTable, ParserError> { | |
| let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); | |
| let name = self.parse_object_name(false)?; | |
| if self.peek_token_ref().token != Token::LParen { | |
| return self.expected_ref( | |
| "'(' before the column list of CREATE FOREIGN TABLE", | |
| self.peek_token_ref(), | |
| ); | |
| } | |
| let (columns, constraints) = self.parse_columns()?; | |
| self.expect_keyword_is(Keyword::SERVER)?; | |
| let server_name = self.parse_identifier()?; | |
| let options = self.parse_pg_options_clause()?; | |
| Ok(CreateForeignTable { | |
| name, | |
| if_not_exists, | |
| columns, | |
| constraints, | |
| server_name, | |
| options, | |
| }) | |
| } | |
| /// The index of the first unprocessed token. | |
| pub fn index(&self) -> usize { | |
| self.index | |
| } | |
| /// Parse a named window definition. | |
| pub fn parse_named_window(&mut self) -> Result<NamedWindowDefinition, ParserError> { | |
| let ident = self.parse_identifier()?; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let window_expr = if self.consume_token(&Token::LParen) { | |
| NamedWindowExpr::WindowSpec(self.parse_window_spec()?) | |
| } else if self.dialect.supports_window_clause_named_window_reference() { | |
| NamedWindowExpr::NamedWindow(self.parse_identifier()?) | |
| } else { | |
| return self.expected_ref("(", self.peek_token_ref()); | |
| }; | |
| Ok(NamedWindowDefinition(ident, window_expr)) | |
| } | |
| /// Parse `CREATE PROCEDURE` statement. | |
| pub fn parse_create_procedure(&mut self, or_alter: bool) -> Result<Statement, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| let params = self.parse_optional_procedure_parameters()?; | |
| let language = if self.parse_keyword(Keyword::LANGUAGE) { | |
| Some(self.parse_identifier()?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword_is(Keyword::AS)?; | |
| let body = self.parse_conditional_statements(&[Keyword::END])?; | |
| Ok(Statement::CreateProcedure { | |
| name, | |
| or_alter, | |
| params, | |
| language, | |
| body, | |
| }) | |
| } | |
| /// Parse a window specification. | |
| pub fn parse_window_spec(&mut self) -> Result<WindowSpec, ParserError> { | |
| let window_name = match &self.peek_token_ref().token { | |
| Token::Word(word) if word.keyword == Keyword::NoKeyword => { | |
| self.parse_optional_ident()? | |
| } | |
| _ => None, | |
| }; | |
| let partition_by = if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { | |
| self.parse_comma_separated(Parser::parse_order_by_expr)? | |
| } else { | |
| vec![] | |
| }; | |
| let window_frame = if !self.consume_token(&Token::RParen) { | |
| let window_frame = self.parse_window_frame()?; | |
| self.expect_token(&Token::RParen)?; | |
| Some(window_frame) | |
| } else { | |
| None | |
| }; | |
| Ok(WindowSpec { | |
| window_name, | |
| partition_by, | |
| order_by, | |
| window_frame, | |
| }) | |
| } | |
| /// Parse `CREATE TYPE` statement. | |
| pub fn parse_create_type(&mut self) -> Result<Statement, ParserError> { | |
| let name = self.parse_object_name(false)?; | |
| // Check if we have AS keyword | |
| let has_as = self.parse_keyword(Keyword::AS); | |
| if !has_as { | |
| // Two cases: CREATE TYPE name; or CREATE TYPE name (options); | |
| if self.consume_token(&Token::LParen) { | |
| // CREATE TYPE name (options) - SQL definition without AS | |
| let options = self.parse_create_type_sql_definition_options()?; | |
| self.expect_token(&Token::RParen)?; | |
| return Ok(Statement::CreateType { | |
| name, | |
| representation: Some(UserDefinedTypeRepresentation::SqlDefinition { options }), | |
| }); | |
| } | |
| // CREATE TYPE name; - no representation | |
| return Ok(Statement::CreateType { | |
| name, | |
| representation: None, | |
| }); | |
| } | |
| // We have AS keyword | |
| if self.parse_keyword(Keyword::ENUM) { | |
| // CREATE TYPE name AS ENUM (labels) | |
| self.parse_create_type_enum(name) | |
| } else if self.parse_keyword(Keyword::RANGE) { | |
| // CREATE TYPE name AS RANGE (options) | |
| self.parse_create_type_range(name) | |
| } else if self.consume_token(&Token::LParen) { | |
| // CREATE TYPE name AS (attributes) - Composite | |
| self.parse_create_type_composite(name) | |
| } else { | |
| self.expected_ref("ENUM, RANGE, or '(' after AS", self.peek_token_ref()) | |
| } | |
| } | |
| /// Parse remainder of `CREATE TYPE AS (attributes)` statement (composite type) | |
| /// | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html) | |
| fn parse_create_type_composite(&mut self, name: ObjectName) -> Result<Statement, ParserError> { | |
| if self.consume_token(&Token::RParen) { | |
| // Empty composite type | |
| return Ok(Statement::CreateType { | |
| name, | |
| representation: Some(UserDefinedTypeRepresentation::Composite { | |
| attributes: vec![], | |
| }), | |
| }); | |
| } | |
| let mut attributes = vec![]; | |
| loop { | |
| let attr_name = self.parse_identifier()?; | |
| let attr_data_type = self.parse_data_type()?; | |
| let attr_collation = if self.parse_keyword(Keyword::COLLATE) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| attributes.push(UserDefinedTypeCompositeAttributeDef { | |
| name: attr_name, | |
| data_type: attr_data_type, | |
| collation: attr_collation, | |
| }); | |
| if !self.consume_token(&Token::Comma) { | |
| break; | |
| } | |
| } | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Statement::CreateType { | |
| name, | |
| representation: Some(UserDefinedTypeRepresentation::Composite { attributes }), | |
| }) | |
| } | |
| /// Parse remainder of `CREATE TYPE AS ENUM` statement (see [Statement::CreateType] and [Self::parse_create_type]) | |
| /// | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html) | |
| pub fn parse_create_type_enum(&mut self, name: ObjectName) -> Result<Statement, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let labels = self.parse_comma_separated0(|p| p.parse_identifier(), Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Statement::CreateType { | |
| name, | |
| representation: Some(UserDefinedTypeRepresentation::Enum { labels }), | |
| }) | |
| } | |
| /// Parse remainder of `CREATE TYPE AS RANGE` statement | |
| /// | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html) | |
| fn parse_create_type_range(&mut self, name: ObjectName) -> Result<Statement, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated0(|p| p.parse_range_option(), Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(Statement::CreateType { | |
| name, | |
| representation: Some(UserDefinedTypeRepresentation::Range { options }), | |
| }) | |
| } | |
| /// Parse a single range option for a `CREATE TYPE AS RANGE` statement | |
| fn parse_range_option(&mut self) -> Result<UserDefinedTypeRangeOption, ParserError> { | |
| let keyword = self.parse_one_of_keywords(&[ | |
| Keyword::SUBTYPE, | |
| Keyword::SUBTYPE_OPCLASS, | |
| Keyword::COLLATION, | |
| Keyword::CANONICAL, | |
| Keyword::SUBTYPE_DIFF, | |
| Keyword::MULTIRANGE_TYPE_NAME, | |
| ]); | |
| match keyword { | |
| Some(Keyword::SUBTYPE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let data_type = self.parse_data_type()?; | |
| Ok(UserDefinedTypeRangeOption::Subtype(data_type)) | |
| } | |
| Some(Keyword::SUBTYPE_OPCLASS) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeRangeOption::SubtypeOpClass(name)) | |
| } | |
| Some(Keyword::COLLATION) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeRangeOption::Collation(name)) | |
| } | |
| Some(Keyword::CANONICAL) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeRangeOption::Canonical(name)) | |
| } | |
| Some(Keyword::SUBTYPE_DIFF) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeRangeOption::SubtypeDiff(name)) | |
| } | |
| Some(Keyword::MULTIRANGE_TYPE_NAME) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeRangeOption::MultirangeTypeName(name)) | |
| } | |
| _ => self.expected_ref("range option keyword", self.peek_token_ref()), | |
| } | |
| } | |
| /// Parse SQL definition options for CREATE TYPE (options) | |
| fn parse_create_type_sql_definition_options( | |
| &mut self, | |
| ) -> Result<Vec<UserDefinedTypeSqlDefinitionOption>, ParserError> { | |
| self.parse_comma_separated0(|p| p.parse_sql_definition_option(), Token::RParen) | |
| } | |
| /// Parse a single SQL definition option for CREATE TYPE (options) | |
| fn parse_sql_definition_option( | |
| &mut self, | |
| ) -> Result<UserDefinedTypeSqlDefinitionOption, ParserError> { | |
| let keyword = self.parse_one_of_keywords(&[ | |
| Keyword::INPUT, | |
| Keyword::OUTPUT, | |
| Keyword::RECEIVE, | |
| Keyword::SEND, | |
| Keyword::TYPMOD_IN, | |
| Keyword::TYPMOD_OUT, | |
| Keyword::ANALYZE, | |
| Keyword::SUBSCRIPT, | |
| Keyword::INTERNALLENGTH, | |
| Keyword::PASSEDBYVALUE, | |
| Keyword::ALIGNMENT, | |
| Keyword::STORAGE, | |
| Keyword::LIKE, | |
| Keyword::CATEGORY, | |
| Keyword::PREFERRED, | |
| Keyword::DEFAULT, | |
| Keyword::ELEMENT, | |
| Keyword::DELIMITER, | |
| Keyword::COLLATABLE, | |
| ]); | |
| match keyword { | |
| Some(Keyword::INPUT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Input(name)) | |
| } | |
| Some(Keyword::OUTPUT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Output(name)) | |
| } | |
| Some(Keyword::RECEIVE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Receive(name)) | |
| } | |
| Some(Keyword::SEND) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Send(name)) | |
| } | |
| Some(Keyword::TYPMOD_IN) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::TypmodIn(name)) | |
| } | |
| Some(Keyword::TYPMOD_OUT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::TypmodOut(name)) | |
| } | |
| Some(Keyword::ANALYZE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Analyze(name)) | |
| } | |
| Some(Keyword::SUBSCRIPT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Subscript(name)) | |
| } | |
| Some(Keyword::INTERNALLENGTH) => { | |
| self.expect_token(&Token::Eq)?; | |
| if self.parse_keyword(Keyword::VARIABLE) { | |
| Ok(UserDefinedTypeSqlDefinitionOption::InternalLength( | |
| UserDefinedTypeInternalLength::Variable, | |
| )) | |
| } else { | |
| let value = self.parse_literal_uint()?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::InternalLength( | |
| UserDefinedTypeInternalLength::Fixed(value), | |
| )) | |
| } | |
| } | |
| Some(Keyword::PASSEDBYVALUE) => Ok(UserDefinedTypeSqlDefinitionOption::PassedByValue), | |
| Some(Keyword::ALIGNMENT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let align_keyword = self.parse_one_of_keywords(&[ | |
| Keyword::CHAR, | |
| Keyword::INT2, | |
| Keyword::INT4, | |
| Keyword::DOUBLE, | |
| ]); | |
| match align_keyword { | |
| Some(Keyword::CHAR) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment( | |
| Alignment::Char, | |
| )), | |
| Some(Keyword::INT2) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment( | |
| Alignment::Int2, | |
| )), | |
| Some(Keyword::INT4) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment( | |
| Alignment::Int4, | |
| )), | |
| Some(Keyword::DOUBLE) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment( | |
| Alignment::Double, | |
| )), | |
| _ => self.expected_ref( | |
| "alignment value (char, int2, int4, or double)", | |
| self.peek_token_ref(), | |
| ), | |
| } | |
| } | |
| Some(Keyword::STORAGE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let storage_keyword = self.parse_one_of_keywords(&[ | |
| Keyword::PLAIN, | |
| Keyword::EXTERNAL, | |
| Keyword::EXTENDED, | |
| Keyword::MAIN, | |
| ]); | |
| match storage_keyword { | |
| Some(Keyword::PLAIN) => Ok(UserDefinedTypeSqlDefinitionOption::Storage( | |
| UserDefinedTypeStorage::Plain, | |
| )), | |
| Some(Keyword::EXTERNAL) => Ok(UserDefinedTypeSqlDefinitionOption::Storage( | |
| UserDefinedTypeStorage::External, | |
| )), | |
| Some(Keyword::EXTENDED) => Ok(UserDefinedTypeSqlDefinitionOption::Storage( | |
| UserDefinedTypeStorage::Extended, | |
| )), | |
| Some(Keyword::MAIN) => Ok(UserDefinedTypeSqlDefinitionOption::Storage( | |
| UserDefinedTypeStorage::Main, | |
| )), | |
| _ => self.expected_ref( | |
| "storage value (plain, external, extended, or main)", | |
| self.peek_token_ref(), | |
| ), | |
| } | |
| } | |
| Some(Keyword::LIKE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let name = self.parse_object_name(false)?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Like(name)) | |
| } | |
| Some(Keyword::CATEGORY) => { | |
| self.expect_token(&Token::Eq)?; | |
| let category_str = self.parse_literal_string()?; | |
| let category_char = category_str.chars().next().ok_or_else(|| { | |
| ParserError::ParserError( | |
| "CATEGORY value must be a single character".to_string(), | |
| ) | |
| })?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Category(category_char)) | |
| } | |
| Some(Keyword::PREFERRED) => { | |
| self.expect_token(&Token::Eq)?; | |
| let value = | |
| self.parse_keyword(Keyword::TRUE) || !self.parse_keyword(Keyword::FALSE); | |
| Ok(UserDefinedTypeSqlDefinitionOption::Preferred(value)) | |
| } | |
| Some(Keyword::DEFAULT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let expr = self.parse_expr()?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Default(expr)) | |
| } | |
| Some(Keyword::ELEMENT) => { | |
| self.expect_token(&Token::Eq)?; | |
| let data_type = self.parse_data_type()?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Element(data_type)) | |
| } | |
| Some(Keyword::DELIMITER) => { | |
| self.expect_token(&Token::Eq)?; | |
| let delimiter = self.parse_literal_string()?; | |
| Ok(UserDefinedTypeSqlDefinitionOption::Delimiter(delimiter)) | |
| } | |
| Some(Keyword::COLLATABLE) => { | |
| self.expect_token(&Token::Eq)?; | |
| let value = | |
| self.parse_keyword(Keyword::TRUE) || !self.parse_keyword(Keyword::FALSE); | |
| Ok(UserDefinedTypeSqlDefinitionOption::Collatable(value)) | |
| } | |
| _ => self.expected_ref("SQL definition option keyword", self.peek_token_ref()), | |
| } | |
| } | |
| fn parse_parenthesized_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> { | |
| self.expect_token(&Token::LParen)?; | |
| let idents = self.parse_comma_separated0(|p| p.parse_identifier(), Token::RParen)?; | |
| self.expect_token(&Token::RParen)?; | |
| Ok(idents) | |
| } | |
| fn parse_column_position(&mut self) -> Result<Option<MySQLColumnPosition>, ParserError> { | |
| if self.dialect.supports_alter_column_position() { | |
| if self.parse_keyword(Keyword::FIRST) { | |
| Ok(Some(MySQLColumnPosition::First)) | |
| } else if self.parse_keyword(Keyword::AFTER) { | |
| let ident = self.parse_identifier()?; | |
| Ok(Some(MySQLColumnPosition::After(ident))) | |
| } else { | |
| Ok(None) | |
| } | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| /// Parse [Statement::Print] | |
| fn parse_print(&mut self) -> Result<Statement, ParserError> { | |
| Ok(Statement::Print(PrintStatement { | |
| message: Box::new(self.parse_expr()?), | |
| })) | |
| } | |
| /// Parse [Statement::WaitFor] | |
| /// | |
| /// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql> | |
| fn parse_waitfor(&mut self) -> Result<Statement, ParserError> { | |
| let wait_type = if self.parse_keyword(Keyword::DELAY) { | |
| WaitForType::Delay | |
| } else if self.parse_keyword(Keyword::TIME) { | |
| WaitForType::Time | |
| } else { | |
| return self.expected_ref("DELAY or TIME", self.peek_token_ref()); | |
| }; | |
| let expr = self.parse_expr()?; | |
| Ok(Statement::WaitFor(WaitForStatement { wait_type, expr })) | |
| } | |
| /// Parse [Statement::Return] | |
| fn parse_return(&mut self) -> Result<Statement, ParserError> { | |
| match self.maybe_parse(|p| p.parse_expr())? { | |
| Some(expr) => Ok(Statement::Return(ReturnStatement { | |
| value: Some(ReturnStatementValue::Expr(expr)), | |
| })), | |
| None => Ok(Statement::Return(ReturnStatement { value: None })), | |
| } | |
| } | |
| /// /// Parse a `EXPORT DATA` statement. | |
| /// | |
| /// See [Statement::ExportData] | |
| fn parse_export_data(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keywords(&[Keyword::EXPORT, Keyword::DATA])?; | |
| let connection = if self.parse_keywords(&[Keyword::WITH, Keyword::CONNECTION]) { | |
| Some(self.parse_object_name(false)?) | |
| } else { | |
| None | |
| }; | |
| self.expect_keyword(Keyword::OPTIONS)?; | |
| self.expect_token(&Token::LParen)?; | |
| let options = self.parse_comma_separated(|p| p.parse_sql_option())?; | |
| self.expect_token(&Token::RParen)?; | |
| self.expect_keyword(Keyword::AS)?; | |
| let query = self.parse_query()?; | |
| Ok(Statement::ExportData(ExportData { | |
| options, | |
| query, | |
| connection, | |
| })) | |
| } | |
| fn parse_vacuum(&mut self) -> Result<Statement, ParserError> { | |
| self.expect_keyword(Keyword::VACUUM)?; | |
| let full = self.parse_keyword(Keyword::FULL); | |
| let sort_only = self.parse_keywords(&[Keyword::SORT, Keyword::ONLY]); | |
| let delete_only = self.parse_keywords(&[Keyword::DELETE, Keyword::ONLY]); | |
| let reindex = self.parse_keyword(Keyword::REINDEX); | |
| let recluster = self.parse_keyword(Keyword::RECLUSTER); | |
| let (table_name, threshold, boost) = | |
| match self.maybe_parse(|p| p.parse_object_name(false))? { | |
| Some(table_name) => { | |
| let threshold = if self.parse_keyword(Keyword::TO) { | |
| let value = self.parse_value()?; | |
| self.expect_keyword(Keyword::PERCENT)?; | |
| Some(value) | |
| } else { | |
| None | |
| }; | |
| let boost = self.parse_keyword(Keyword::BOOST); | |
| (Some(table_name), threshold, boost) | |
| } | |
| _ => (None, None, false), | |
| }; | |
| Ok(Statement::Vacuum(VacuumStatement { | |
| full, | |
| sort_only, | |
| delete_only, | |
| reindex, | |
| recluster, | |
| table_name, | |
| threshold, | |
| boost, | |
| })) | |
| } | |
| /// Consume the parser and return its underlying token buffer | |
| pub fn into_tokens(self) -> Vec<TokenWithSpan> { | |
| self.tokens | |
| } | |
| /// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH | |
| fn peek_sub_query(&mut self) -> bool { | |
| self.peek_one_of_keywords(&[Keyword::SELECT, Keyword::WITH]) | |
| .is_some() | |
| } | |
| pub(crate) fn parse_show_stmt_options(&mut self) -> Result<ShowStatementOptions, ParserError> { | |
| let show_in; | |
| let mut filter_position = None; | |
| if self.dialect.supports_show_like_before_in() { | |
| if let Some(filter) = self.parse_show_statement_filter()? { | |
| filter_position = Some(ShowStatementFilterPosition::Infix(filter)); | |
| } | |
| show_in = self.maybe_parse_show_stmt_in()?; | |
| } else { | |
| show_in = self.maybe_parse_show_stmt_in()?; | |
| if let Some(filter) = self.parse_show_statement_filter()? { | |
| filter_position = Some(ShowStatementFilterPosition::Suffix(filter)); | |
| } | |
| } | |
| let starts_with = self.maybe_parse_show_stmt_starts_with()?; | |
| let limit = self.maybe_parse_show_stmt_limit()?; | |
| let from = self.maybe_parse_show_stmt_from()?; | |
| Ok(ShowStatementOptions { | |
| filter_position, | |
| show_in, | |
| starts_with, | |
| limit, | |
| limit_from: from, | |
| }) | |
| } | |
| fn maybe_parse_show_stmt_in(&mut self) -> Result<Option<ShowStatementIn>, ParserError> { | |
| let clause = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { | |
| Some(Keyword::FROM) => ShowStatementInClause::FROM, | |
| Some(Keyword::IN) => ShowStatementInClause::IN, | |
| None => return Ok(None), | |
| _ => return self.expected_ref("FROM or IN", self.peek_token_ref()), | |
| }; | |
| let (parent_type, parent_name) = match self.parse_one_of_keywords(&[ | |
| Keyword::ACCOUNT, | |
| Keyword::DATABASE, | |
| Keyword::SCHEMA, | |
| Keyword::TABLE, | |
| Keyword::VIEW, | |
| ]) { | |
| // If we see these next keywords it means we don't have a parent name | |
| Some(Keyword::DATABASE) | |
| if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) | |
| | self.peek_keyword(Keyword::LIMIT) => | |
| { | |
| (Some(ShowStatementInParentType::Database), None) | |
| } | |
| Some(Keyword::SCHEMA) | |
| if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) | |
| | self.peek_keyword(Keyword::LIMIT) => | |
| { | |
| (Some(ShowStatementInParentType::Schema), None) | |
| } | |
| Some(parent_kw) => { | |
| // The parent name here is still optional, for example: | |
| // SHOW TABLES IN ACCOUNT, so parsing the object name | |
| // may fail because the statement ends. | |
| let parent_name = self.maybe_parse(|p| p.parse_object_name(false))?; | |
| match parent_kw { | |
| Keyword::ACCOUNT => (Some(ShowStatementInParentType::Account), parent_name), | |
| Keyword::DATABASE => (Some(ShowStatementInParentType::Database), parent_name), | |
| Keyword::SCHEMA => (Some(ShowStatementInParentType::Schema), parent_name), | |
| Keyword::TABLE => (Some(ShowStatementInParentType::Table), parent_name), | |
| Keyword::VIEW => (Some(ShowStatementInParentType::View), parent_name), | |
| _ => { | |
| return self.expected_ref( | |
| "one of ACCOUNT, DATABASE, SCHEMA, TABLE or VIEW", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| } | |
| } | |
| None => { | |
| // Parsing MySQL style FROM tbl_name FROM db_name | |
| // which is equivalent to FROM tbl_name.db_name | |
| let mut parent_name = self.parse_object_name(false)?; | |
| if self | |
| .parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) | |
| .is_some() | |
| { | |
| parent_name | |
| .0 | |
| .insert(0, ObjectNamePart::Identifier(self.parse_identifier()?)); | |
| } | |
| (None, Some(parent_name)) | |
| } | |
| }; | |
| Ok(Some(ShowStatementIn { | |
| clause, | |
| parent_type, | |
| parent_name, | |
| })) | |
| } | |
| fn maybe_parse_show_stmt_starts_with(&mut self) -> Result<Option<ValueWithSpan>, ParserError> { | |
| if self.parse_keywords(&[Keyword::STARTS, Keyword::WITH]) { | |
| Ok(Some(self.parse_value()?)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn maybe_parse_show_stmt_limit(&mut self) -> Result<Option<Expr>, ParserError> { | |
| if self.parse_keyword(Keyword::LIMIT) { | |
| Ok(self.parse_limit()?) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| fn maybe_parse_show_stmt_from(&mut self) -> Result<Option<ValueWithSpan>, ParserError> { | |
| if self.parse_keyword(Keyword::FROM) { | |
| Ok(Some(self.parse_value()?)) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| pub(crate) fn in_column_definition_state(&self) -> bool { | |
| matches!(self.state, ColumnDefinition) | |
| } | |
| /// Parses options provided in key-value format. | |
| /// | |
| /// * `parenthesized` - true if the options are enclosed in parenthesis | |
| /// * `end_words` - a list of keywords that any of them indicates the end of the options section | |
| pub(crate) fn parse_key_value_options( | |
| &mut self, | |
| parenthesized: bool, | |
| end_words: &[Keyword], | |
| ) -> Result<KeyValueOptions, ParserError> { | |
| let mut options: Vec<KeyValueOption> = Vec::new(); | |
| let mut delimiter = KeyValueOptionsDelimiter::Space; | |
| if parenthesized { | |
| self.expect_token(&Token::LParen)?; | |
| } | |
| loop { | |
| match self.next_token().token { | |
| Token::RParen => { | |
| if parenthesized { | |
| break; | |
| } else { | |
| return self.expected_ref(" another option or EOF", self.peek_token_ref()); | |
| } | |
| } | |
| Token::EOF => break, | |
| Token::SemiColon => { | |
| self.prev_token(); | |
| break; | |
| } | |
| Token::Comma => { | |
| delimiter = KeyValueOptionsDelimiter::Comma; | |
| continue; | |
| } | |
| Token::Word(w) if !end_words.contains(&w.keyword) => { | |
| options.push(self.parse_key_value_option(&w)?) | |
| } | |
| Token::Word(w) if end_words.contains(&w.keyword) => { | |
| self.prev_token(); | |
| break; | |
| } | |
| _ => { | |
| return self.expected_ref( | |
| "another option, EOF, SemiColon, Comma or ')'", | |
| self.peek_token_ref(), | |
| ) | |
| } | |
| }; | |
| } | |
| Ok(KeyValueOptions { delimiter, options }) | |
| } | |
| /// Parses a `KEY = VALUE` construct based on the specified key | |
| pub(crate) fn parse_key_value_option( | |
| &mut self, | |
| key: &Word, | |
| ) -> Result<KeyValueOption, ParserError> { | |
| self.expect_token(&Token::Eq)?; | |
| let peeked_token = self.peek_token(); | |
| match peeked_token.token { | |
| Token::SingleQuotedString(_) => Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::Single(self.parse_value()?), | |
| }), | |
| Token::Word(word) | |
| if word.keyword == Keyword::TRUE || word.keyword == Keyword::FALSE => | |
| { | |
| Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::Single(self.parse_value()?), | |
| }) | |
| } | |
| Token::Number(..) => Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::Single(self.parse_value()?), | |
| }), | |
| Token::Word(word) => { | |
| self.next_token(); | |
| Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::Single( | |
| Value::Placeholder(word.value.clone()).with_span(peeked_token.span), | |
| ), | |
| }) | |
| } | |
| Token::LParen => { | |
| // Can be a list of values or a list of key value properties. | |
| // Try to parse a list of values and if that fails, try to parse | |
| // a list of key-value properties. | |
| match self.maybe_parse(|parser| { | |
| parser.expect_token(&Token::LParen)?; | |
| let values = parser.parse_comma_separated0(|p| p.parse_value(), Token::RParen); | |
| parser.expect_token(&Token::RParen)?; | |
| values | |
| })? { | |
| Some(values) => Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::Multi(values), | |
| }), | |
| None => Ok(KeyValueOption { | |
| option_name: key.value.clone(), | |
| option_value: KeyValueOptionKind::KeyValueOptions(Box::new( | |
| self.parse_key_value_options(true, &[])?, | |
| )), | |
| }), | |
| } | |
| } | |
| _ => self.expected_ref("expected option value", self.peek_token_ref()), | |
| } | |
| } | |
| /// Parses a RESET statement | |
| fn parse_reset(&mut self) -> Result<ResetStatement, ParserError> { | |
| if self.parse_keyword(Keyword::ALL) { | |
| return Ok(ResetStatement { reset: Reset::ALL }); | |
| } | |
| if self.parse_keywords(&[Keyword::SESSION, Keyword::AUTHORIZATION]) { | |
| return Ok(ResetStatement { | |
| reset: Reset::SessionAuthorization, | |
| }); | |
| } | |
| let obj = self.parse_object_name(false)?; | |
| Ok(ResetStatement { | |
| reset: Reset::ConfigurationParameter(obj), | |
| }) | |
| } | |
| } | |
| fn maybe_prefixed_expr(expr: Expr, prefix: Option<Ident>) -> Expr { | |
| if let Some(prefix) = prefix { | |
| Expr::Prefixed { | |
| prefix, | |
| value: Box::new(expr), | |
| } | |
| } else { | |
| expr | |
| } | |
| } | |
| impl Word { | |
| /// Convert a reference to this word into an [`Ident`] by cloning the value. | |
| /// | |
| /// Use this method when you need to keep the original `Word` around. | |
| /// If you can consume the `Word`, prefer [`into_ident`](Self::into_ident) instead | |
| /// to avoid cloning. | |
| #[inline] | |
| pub fn to_ident(&self, span: Span) -> Ident { | |
| Ident { | |
| value: self.value.clone(), | |
| quote_style: self.quote_style, | |
| span, | |
| } | |
| } | |
| /// Convert this word into an [`Ident`] identifier, consuming the `Word`. | |
| /// | |
| /// This avoids cloning the string value. If you need to keep the original | |
| /// `Word`, use [`to_ident`](Self::to_ident) instead. | |
| pub fn into_ident(self, span: Span) -> Ident { | |
| Ident { | |
| value: self.value, | |
| quote_style: self.quote_style, | |
| span, | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use crate::test_utils::{all_dialects, TestedDialects}; | |
| use super::*; | |
| #[test] | |
| fn test_prev_index() { | |
| let sql = "SELECT version"; | |
| all_dialects().run_parser_method(sql, |parser| { | |
| assert_eq!(parser.peek_token(), Token::make_keyword("SELECT")); | |
| assert_eq!(parser.next_token(), Token::make_keyword("SELECT")); | |
| parser.prev_token(); | |
| assert_eq!(parser.next_token(), Token::make_keyword("SELECT")); | |
| assert_eq!(parser.next_token(), Token::make_word("version", None)); | |
| parser.prev_token(); | |
| assert_eq!(parser.peek_token(), Token::make_word("version", None)); | |
| assert_eq!(parser.next_token(), Token::make_word("version", None)); | |
| assert_eq!(parser.peek_token(), Token::EOF); | |
| parser.prev_token(); | |
| assert_eq!(parser.next_token(), Token::make_word("version", None)); | |
| assert_eq!(parser.next_token(), Token::EOF); | |
| assert_eq!(parser.next_token(), Token::EOF); | |
| parser.prev_token(); | |
| }); | |
| } | |
| #[test] | |
| fn test_peek_tokens() { | |
| all_dialects().run_parser_method("SELECT foo AS bar FROM baz", |parser| { | |
| assert!(matches!( | |
| parser.peek_tokens(), | |
| [Token::Word(Word { | |
| keyword: Keyword::SELECT, | |
| .. | |
| })] | |
| )); | |
| assert!(matches!( | |
| parser.peek_tokens(), | |
| [ | |
| Token::Word(Word { | |
| keyword: Keyword::SELECT, | |
| .. | |
| }), | |
| Token::Word(_), | |
| Token::Word(Word { | |
| keyword: Keyword::AS, | |
| .. | |
| }), | |
| ] | |
| )); | |
| for _ in 0..4 { | |
| parser.next_token(); | |
| } | |
| assert!(matches!( | |
| parser.peek_tokens(), | |
| [ | |
| Token::Word(Word { | |
| keyword: Keyword::FROM, | |
| .. | |
| }), | |
| Token::Word(_), | |
| Token::EOF, | |
| Token::EOF, | |
| ] | |
| )) | |
| }) | |
| } | |
| #[cfg(test)] | |
| mod test_parse_data_type { | |
| use crate::ast::{ | |
| CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo, | |
| }; | |
| use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect}; | |
| use crate::test_utils::TestedDialects; | |
| macro_rules! test_parse_data_type { | |
| ($dialect:expr, $input:expr, $expected_type:expr $(,)?) => {{ | |
| $dialect.run_parser_method(&*$input, |parser| { | |
| let data_type = parser.parse_data_type().unwrap(); | |
| assert_eq!($expected_type, data_type); | |
| assert_eq!($input.to_string(), data_type.to_string()); | |
| }); | |
| }}; | |
| } | |
| #[test] | |
| fn test_ansii_character_string_types() { | |
| // Character string types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#character-string-type> | |
| let dialect = | |
| TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]); | |
| test_parse_data_type!(dialect, "CHARACTER", DataType::Character(None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER(20)", | |
| DataType::Character(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: None | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER(20 CHARACTERS)", | |
| DataType::Character(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Characters) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER(20 OCTETS)", | |
| DataType::Character(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Octets) | |
| })) | |
| ); | |
| test_parse_data_type!(dialect, "CHAR", DataType::Char(None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR(20)", | |
| DataType::Char(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: None | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR(20 CHARACTERS)", | |
| DataType::Char(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Characters) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR(20 OCTETS)", | |
| DataType::Char(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Octets) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER VARYING(20)", | |
| DataType::CharacterVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: None | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER VARYING(20 CHARACTERS)", | |
| DataType::CharacterVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Characters) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER VARYING(20 OCTETS)", | |
| DataType::CharacterVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Octets) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR VARYING(20)", | |
| DataType::CharVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: None | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR VARYING(20 CHARACTERS)", | |
| DataType::CharVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Characters) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR VARYING(20 OCTETS)", | |
| DataType::CharVarying(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: Some(CharLengthUnits::Octets) | |
| })) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "VARCHAR(20)", | |
| DataType::Varchar(Some(CharacterLength::IntegerLength { | |
| length: 20, | |
| unit: None | |
| })) | |
| ); | |
| } | |
| #[test] | |
| fn test_ansii_character_large_object_types() { | |
| // Character large object types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#character-large-object-length> | |
| let dialect = | |
| TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER LARGE OBJECT", | |
| DataType::CharacterLargeObject(None) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHARACTER LARGE OBJECT(20)", | |
| DataType::CharacterLargeObject(Some(20)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR LARGE OBJECT", | |
| DataType::CharLargeObject(None) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "CHAR LARGE OBJECT(20)", | |
| DataType::CharLargeObject(Some(20)) | |
| ); | |
| test_parse_data_type!(dialect, "CLOB", DataType::Clob(None)); | |
| test_parse_data_type!(dialect, "CLOB(20)", DataType::Clob(Some(20))); | |
| } | |
| #[test] | |
| fn test_parse_custom_types() { | |
| let dialect = | |
| TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]); | |
| test_parse_data_type!( | |
| dialect, | |
| "GEOMETRY", | |
| DataType::Custom(ObjectName::from(vec!["GEOMETRY".into()]), vec![]) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "GEOMETRY(POINT)", | |
| DataType::Custom( | |
| ObjectName::from(vec!["GEOMETRY".into()]), | |
| vec!["POINT".to_string()] | |
| ) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "GEOMETRY(POINT, 4326)", | |
| DataType::Custom( | |
| ObjectName::from(vec!["GEOMETRY".into()]), | |
| vec!["POINT".to_string(), "4326".to_string()] | |
| ) | |
| ); | |
| } | |
| #[test] | |
| fn test_ansii_exact_numeric_types() { | |
| // Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type> | |
| let dialect = TestedDialects::new(vec![ | |
| Box::new(GenericDialect {}), | |
| Box::new(AnsiDialect {}), | |
| Box::new(PostgreSqlDialect {}), | |
| ]); | |
| test_parse_data_type!(dialect, "NUMERIC", DataType::Numeric(ExactNumberInfo::None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "NUMERIC(2)", | |
| DataType::Numeric(ExactNumberInfo::Precision(2)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "NUMERIC(2,10)", | |
| DataType::Numeric(ExactNumberInfo::PrecisionAndScale(2, 10)) | |
| ); | |
| test_parse_data_type!(dialect, "DECIMAL", DataType::Decimal(ExactNumberInfo::None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "DECIMAL(2)", | |
| DataType::Decimal(ExactNumberInfo::Precision(2)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DECIMAL(2,10)", | |
| DataType::Decimal(ExactNumberInfo::PrecisionAndScale(2, 10)) | |
| ); | |
| test_parse_data_type!(dialect, "DEC", DataType::Dec(ExactNumberInfo::None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "DEC(2)", | |
| DataType::Dec(ExactNumberInfo::Precision(2)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DEC(2,10)", | |
| DataType::Dec(ExactNumberInfo::PrecisionAndScale(2, 10)) | |
| ); | |
| // Test negative scale values. | |
| test_parse_data_type!( | |
| dialect, | |
| "NUMERIC(10,-2)", | |
| DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -2)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DECIMAL(1000,-10)", | |
| DataType::Decimal(ExactNumberInfo::PrecisionAndScale(1000, -10)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DEC(5,-1000)", | |
| DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -1000)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "NUMERIC(10,-5)", | |
| DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -5)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DECIMAL(20,-10)", | |
| DataType::Decimal(ExactNumberInfo::PrecisionAndScale(20, -10)) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "DEC(5,-2)", | |
| DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -2)) | |
| ); | |
| dialect.run_parser_method("NUMERIC(10,+5)", |parser| { | |
| let data_type = parser.parse_data_type().unwrap(); | |
| assert_eq!( | |
| DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, 5)), | |
| data_type | |
| ); | |
| // Note: Explicit '+' sign is not preserved in output, which is correct | |
| assert_eq!("NUMERIC(10,5)", data_type.to_string()); | |
| }); | |
| } | |
| #[test] | |
| fn test_ansii_date_type() { | |
| // Datetime types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#datetime-type> | |
| let dialect = | |
| TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]); | |
| test_parse_data_type!(dialect, "DATE", DataType::Date); | |
| test_parse_data_type!(dialect, "TIME", DataType::Time(None, TimezoneInfo::None)); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIME(6)", | |
| DataType::Time(Some(6), TimezoneInfo::None) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIME WITH TIME ZONE", | |
| DataType::Time(None, TimezoneInfo::WithTimeZone) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIME(6) WITH TIME ZONE", | |
| DataType::Time(Some(6), TimezoneInfo::WithTimeZone) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIME WITHOUT TIME ZONE", | |
| DataType::Time(None, TimezoneInfo::WithoutTimeZone) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIME(6) WITHOUT TIME ZONE", | |
| DataType::Time(Some(6), TimezoneInfo::WithoutTimeZone) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIMESTAMP", | |
| DataType::Timestamp(None, TimezoneInfo::None) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIMESTAMP(22)", | |
| DataType::Timestamp(Some(22), TimezoneInfo::None) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIMESTAMP(22) WITH TIME ZONE", | |
| DataType::Timestamp(Some(22), TimezoneInfo::WithTimeZone) | |
| ); | |
| test_parse_data_type!( | |
| dialect, | |
| "TIMESTAMP(33) WITHOUT TIME ZONE", | |
| DataType::Timestamp(Some(33), TimezoneInfo::WithoutTimeZone) | |
| ); | |
| } | |
| } | |
| #[test] | |
| fn test_parse_schema_name() { | |
| // The expected name should be identical as the input name, that's why I don't receive both | |
| macro_rules! test_parse_schema_name { | |
| ($input:expr, $expected_name:expr $(,)?) => {{ | |
| all_dialects().run_parser_method(&*$input, |parser| { | |
| let schema_name = parser.parse_schema_name().unwrap(); | |
| // Validate that the structure is the same as expected | |
| assert_eq!(schema_name, $expected_name); | |
| // Validate that the input and the expected structure serialization are the same | |
| assert_eq!(schema_name.to_string(), $input.to_string()); | |
| }); | |
| }}; | |
| } | |
| let dummy_name = ObjectName::from(vec![Ident::new("dummy_name")]); | |
| let dummy_authorization = Ident::new("dummy_authorization"); | |
| test_parse_schema_name!( | |
| format!("{dummy_name}"), | |
| SchemaName::Simple(dummy_name.clone()) | |
| ); | |
| test_parse_schema_name!( | |
| format!("AUTHORIZATION {dummy_authorization}"), | |
| SchemaName::UnnamedAuthorization(dummy_authorization.clone()), | |
| ); | |
| test_parse_schema_name!( | |
| format!("{dummy_name} AUTHORIZATION {dummy_authorization}"), | |
| SchemaName::NamedAuthorization(dummy_name.clone(), dummy_authorization.clone()), | |
| ); | |
| } | |
| #[test] | |
| fn mysql_parse_index_table_constraint() { | |
| macro_rules! test_parse_table_constraint { | |
| ($dialect:expr, $input:expr, $expected:expr $(,)?) => {{ | |
| $dialect.run_parser_method(&*$input, |parser| { | |
| let constraint = parser.parse_optional_table_constraint().unwrap().unwrap(); | |
| // Validate that the structure is the same as expected | |
| assert_eq!(constraint, $expected); | |
| // Validate that the input and the expected structure serialization are the same | |
| assert_eq!(constraint.to_string(), $input.to_string()); | |
| }); | |
| }}; | |
| } | |
| fn mk_expected_col(name: &str) -> IndexColumn { | |
| IndexColumn { | |
| column: OrderByExpr { | |
| expr: Expr::Identifier(name.into()), | |
| options: OrderByOptions { | |
| sort: None, | |
| nulls_first: None, | |
| }, | |
| with_fill: None, | |
| }, | |
| operator_class: None, | |
| } | |
| } | |
| let dialect = | |
| TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})]); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX (c1)", | |
| IndexConstraint { | |
| display_as_key: false, | |
| name: None, | |
| index_type: None, | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "KEY (c1)", | |
| IndexConstraint { | |
| display_as_key: true, | |
| name: None, | |
| index_type: None, | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX 'index' (c1, c2)", | |
| TableConstraint::Index(IndexConstraint { | |
| display_as_key: false, | |
| name: Some(Ident::with_quote('\'', "index")), | |
| index_type: None, | |
| columns: vec![mk_expected_col("c1"), mk_expected_col("c2")], | |
| index_options: vec![], | |
| }) | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX USING BTREE (c1)", | |
| IndexConstraint { | |
| display_as_key: false, | |
| name: None, | |
| index_type: Some(IndexType::BTree), | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX USING HASH (c1)", | |
| IndexConstraint { | |
| display_as_key: false, | |
| name: None, | |
| index_type: Some(IndexType::Hash), | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX idx_name USING BTREE (c1)", | |
| IndexConstraint { | |
| display_as_key: false, | |
| name: Some(Ident::new("idx_name")), | |
| index_type: Some(IndexType::BTree), | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| test_parse_table_constraint!( | |
| dialect, | |
| "INDEX idx_name USING HASH (c1)", | |
| IndexConstraint { | |
| display_as_key: false, | |
| name: Some(Ident::new("idx_name")), | |
| index_type: Some(IndexType::Hash), | |
| columns: vec![mk_expected_col("c1")], | |
| index_options: vec![], | |
| } | |
| .into() | |
| ); | |
| } | |
| #[test] | |
| fn test_tokenizer_error_loc() { | |
| let sql = "foo '"; | |
| let ast = Parser::parse_sql(&GenericDialect, sql); | |
| assert_eq!( | |
| ast, | |
| Err(ParserError::TokenizerError( | |
| "Unterminated string literal at Line: 1, Column: 5".to_string() | |
| )) | |
| ); | |
| } | |
| #[test] | |
| fn test_parser_error_loc() { | |
| let sql = "SELECT this is a syntax error"; | |
| let ParserError::ParserError(msg) = Parser::parse_sql(&GenericDialect, sql).unwrap_err() | |
| else { | |
| panic!("expected ParserError::ParserError"); | |
| }; | |
| assert!( | |
| msg.ends_with("found: a at Line: 1, Column: 16"), | |
| "unexpected error message: {msg}" | |
| ); | |
| } | |
| #[test] | |
| fn test_nested_explain_error() { | |
| let sql = "EXPLAIN EXPLAIN SELECT 1"; | |
| let ast = Parser::parse_sql(&GenericDialect, sql); | |
| assert_eq!( | |
| ast, | |
| Err(ParserError::ParserError( | |
| "Explain must be root of the plan".to_string() | |
| )) | |
| ); | |
| } | |
| #[test] | |
| fn test_parse_multipart_identifier_positive() { | |
| let dialect = TestedDialects::new(vec![Box::new(GenericDialect {})]); | |
| // parse multipart with quotes | |
| let expected = vec![ | |
| Ident { | |
| value: "CATALOG".to_string(), | |
| quote_style: None, | |
| span: Span::empty(), | |
| }, | |
| Ident { | |
| value: "F(o)o. \"bar".to_string(), | |
| quote_style: Some('"'), | |
| span: Span::empty(), | |
| }, | |
| Ident { | |
| value: "table".to_string(), | |
| quote_style: None, | |
| span: Span::empty(), | |
| }, | |
| ]; | |
| dialect.run_parser_method(r#"CATALOG."F(o)o. ""bar".table"#, |parser| { | |
| let actual = parser.parse_multipart_identifier().unwrap(); | |
| assert_eq!(expected, actual); | |
| }); | |
| // allow whitespace between ident parts | |
| let expected = vec![ | |
| Ident { | |
| value: "CATALOG".to_string(), | |
| quote_style: None, | |
| span: Span::empty(), | |
| }, | |
| Ident { | |
| value: "table".to_string(), | |
| quote_style: None, | |
| span: Span::empty(), | |
| }, | |
| ]; | |
| dialect.run_parser_method("CATALOG . table", |parser| { | |
| let actual = parser.parse_multipart_identifier().unwrap(); | |
| assert_eq!(expected, actual); | |
| }); | |
| } | |
| #[test] | |
| fn test_parse_multipart_identifier_negative() { | |
| macro_rules! test_parse_multipart_identifier_error { | |
| ($input:expr, $expected_err:expr $(,)?) => {{ | |
| all_dialects().run_parser_method(&*$input, |parser| { | |
| let actual_err = parser.parse_multipart_identifier().unwrap_err(); | |
| assert_eq!(actual_err.to_string(), $expected_err); | |
| }); | |
| }}; | |
| } | |
| test_parse_multipart_identifier_error!( | |
| "", | |
| "sql parser error: Empty input when parsing identifier", | |
| ); | |
| test_parse_multipart_identifier_error!( | |
| "*schema.table", | |
| "sql parser error: Unexpected token in identifier: *", | |
| ); | |
| test_parse_multipart_identifier_error!( | |
| "schema.table*", | |
| "sql parser error: Unexpected token in identifier: *", | |
| ); | |
| test_parse_multipart_identifier_error!( | |
| "schema.table.", | |
| "sql parser error: Trailing period in identifier", | |
| ); | |
| test_parse_multipart_identifier_error!( | |
| "schema.*", | |
| "sql parser error: Unexpected token following period in identifier: *", | |
| ); | |
| } | |
| #[test] | |
| fn test_mysql_partition_selection() { | |
| let sql = "SELECT * FROM employees PARTITION (p0, p2)"; | |
| let expected = vec!["p0", "p2"]; | |
| let ast: Vec<Statement> = Parser::parse_sql(&MySqlDialect {}, sql).unwrap(); | |
| assert_eq!(ast.len(), 1); | |
| if let Statement::Query(v) = &ast[0] { | |
| if let SetExpr::Select(select) = &*v.body { | |
| assert_eq!(select.from.len(), 1); | |
| let from: &TableWithJoins = &select.from[0]; | |
| let table_factor = &from.relation; | |
| if let TableFactor::Table { partitions, .. } = table_factor { | |
| let actual: Vec<&str> = partitions | |
| .iter() | |
| .map(|ident| ident.value.as_str()) | |
| .collect(); | |
| assert_eq!(expected, actual); | |
| } | |
| } | |
| } else { | |
| panic!("fail to parse mysql partition selection"); | |
| } | |
| } | |
| #[test] | |
| fn test_replace_into_placeholders() { | |
| let sql = "REPLACE INTO t (a) VALUES (&a)"; | |
| assert_eq!( | |
| ParserError::ParserError( | |
| "Expected: an expression, found: & at Line: 1, Column: 28".to_string() | |
| ), | |
| Parser::parse_sql(&GenericDialect {}, sql).unwrap_err() | |
| ); | |
| } | |
| #[test] | |
| fn test_replace_into_set_placeholder() { | |
| let sql = "REPLACE INTO t SET ?"; | |
| assert_eq!( | |
| ParserError::ParserError( | |
| "Expected: SELECT, VALUES, or a subquery in the query body, found: SET at Line: 1, Column: 16" | |
| .to_string() | |
| ), | |
| Parser::parse_sql(&GenericDialect {}, sql).unwrap_err() | |
| ); | |
| } | |
| #[test] | |
| fn test_replace_incomplete() { | |
| let sql = r#"REPLACE"#; | |
| assert_eq!( | |
| ParserError::ParserError("Expected: identifier, found: EOF".to_string()), | |
| Parser::parse_sql(&MySqlDialect {}, sql).unwrap_err() | |
| ); | |
| } | |
| #[test] | |
| fn test_placeholder_invalid_whitespace() { | |
| for (w, expected) in [ | |
| ( | |
| " ", | |
| "Expected: placeholder, found: at Line: 3, Column: 4", | |
| ), | |
| ( | |
| "/*invalid*/", | |
| "Expected: placeholder, found: /*invalid*/ at Line: 3, Column: 4", | |
| ), | |
| ] { | |
| let sql = format!("\nSELECT\n :{w}fooBar"); | |
| assert_eq!( | |
| ParserError::ParserError(expected.to_string()), | |
| Parser::parse_sql(&GenericDialect, &sql).unwrap_err() | |
| ); | |
| } | |
| } | |
| } |