blob: d0b5510bf766481734f053172997163ec75d3976 [file]
// 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},
mem::discriminant,
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 },
},
};
struct ReferenceClauses {
match_kind: Option<ConstraintReferenceMatchKind>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
}
/// 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>>,
}
/// Whether an alias names a select item or a table.
#[derive(Debug, Clone, Copy)]
enum AliasKind {
Column,
Table,
}
/// 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;
}
if self.peek_token_ref().token == Token::EOF {
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();
let mut prev_token_end = None;
for t in self.tokens.into_iter() {
let comment = match t.token {
Token::Whitespace(Whitespace::SingleLineComment { comment, prefix }) => {
comments::Comment::SingleLine {
content: comment,
prefix,
}
}
Token::Whitespace(Whitespace::MultiLineComment(comment)) => {
comments::Comment::MultiLine(comment)
}
Token::Whitespace(_) | Token::EOF => continue,
_ => {
comments.set_next_token_start(t.span.start);
prev_token_end = Some(t.span.end);
continue;
}
};
comments.offer(comments::CommentWithSpan {
comment,
span: t.span,
prev_token_end,
next_token_start: None,
});
}
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_privileges_or_roles(
Keyword::TO,
Parser::parse_grant,
Parser::parse_grant_role,
),
Keyword::DENY => {
self.prev_token();
self.parse_deny()
}
Keyword::REVOKE => {
if self.peek_role_grant_option_for() {
self.parse_revoke_role().map(Into::into)
} else {
self.parse_privileges_or_roles(
Keyword::FROM,
Parser::parse_revoke,
Parser::parse_revoke_role,
)
}
}
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(),
Keyword::DO if self.dialect.supports_do_statement() => self.parse_do(),
// `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),
Keyword::REFRESH => self.parse_refresh_materialized_view().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::FOREIGN => {
self.expect_keyword_is(Keyword::TABLE)?;
(CommentObject::ForeignTable, 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) if w.keyword == Keyword::WITH => {
read_lock = self.parse_keywords(&[Keyword::READ, Keyword::LOCK]);
}
Token::Word(w) if w.keyword == Keyword::FOR => {
export = self.parse_keyword(Keyword::EXPORT);
}
Token::Word(_) => {
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, AliasKind::Column, 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 `DO` statement.
pub fn parse_do(&mut self) -> Result<Statement, ParserError> {
let mut options = Vec::new();
loop {
let language = self.parse_keyword(Keyword::LANGUAGE);
let string_literal = matches!(
self.peek_token_ref().token,
Token::SingleQuotedString(_)
| Token::DollarQuotedString(_)
| Token::EscapedStringLiteral(_)
| Token::UnicodeStringLiteral(_)
);
let option = match (language, string_literal) {
(true, true) => DoOption::LanguageLiteral(self.parse_value()?),
(true, false) => match self.next_token() {
// PostgreSQL lexes `N'...'` as the `NCHAR` keyword followed
// by a string literal, so `LANGUAGE N'...'` is the language
// name `nchar` followed by the code block.
TokenWithSpan {
token: Token::NationalStringLiteral(code),
span,
} => {
let quote = Location::new(span.start.line, span.start.column + 1);
let language = Ident::with_span(Span::new(span.start, quote), "nchar");
options.push(DoOption::Language(language));
let code = Value::SingleQuotedString(code);
DoOption::Code(code.with_span(Span::new(quote, span.end)))
}
_ => {
self.prev_token();
DoOption::Language(self.parse_identifier()?)
}
},
(false, true) => DoOption::Code(self.parse_value()?),
(false, false) if options.is_empty() => {
return self.expected_ref("string literal", self.peek_token_ref());
}
(false, false) => break,
};
options.push(option);
}
Ok(Statement::Do(DoStatement { options }))
}
/// 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()?))
}
_ => match self.geometric_type_kind(w.keyword) {
Some(kind) => Ok(Some(self.parse_geometric_type(kind)?)),
None => Ok(None),
},
}
}
fn geometric_type_kind(&self, keyword: Keyword) -> Option<GeometricTypeKind> {
if !self.dialect.supports_geometric_types() {
return None;
}
match keyword {
Keyword::CIRCLE => Some(GeometricTypeKind::Circle),
Keyword::BOX => Some(GeometricTypeKind::GeometricBox),
Keyword::PATH => Some(GeometricTypeKind::GeometricPath),
Keyword::LINE => Some(GeometricTypeKind::Line),
Keyword::LSEG => Some(GeometricTypeKind::LineSegment),
Keyword::POINT => Some(GeometricTypeKind::Point),
Keyword::POLYGON => Some(GeometricTypeKind::Polygon),
_ => 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(),
})
}
// 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
}
}
fn peek_custom_typed_string(&self, first_keyword: Keyword, name: &ObjectName) -> bool {
let qualified = name.0.len() > 1;
matches!(
self.peek_token_ref().token,
Token::SingleQuotedString(_)
| Token::EscapedStringLiteral(_)
| Token::UnicodeStringLiteral(_)
| Token::DollarQuotedString(_)
) && self
.dialect
.is_custom_type_typed_string(first_keyword, qualified)
&& (qualified || self.geometric_type_kind(first_keyword).is_none())
}
/// 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 first_keyword = match &self.peek_token_ref().token {
Token::Word(w) => Some(w.keyword),
_ => None,
};
let opt_expr = self.maybe_parse(|parser| {
match parser.parse_data_type()? {
DataType::Interval { .. } => parser.parse_interval(),
// Custom names need dialect consent, else `NOT 'a'` would read as a typed string.
DataType::Custom(name, modifiers)
if modifiers.is_empty()
&& ((Self::is_simple_unquoted_object_name(&name, "xml")
&& parser.dialect.supports_xml_expressions())
|| first_keyword
.is_some_and(|kw| parser.peek_custom_typed_string(kw, &name))) =>
{
Ok(Expr::TypedString(TypedString {
data_type: DataType::Custom(name, modifiers),
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));
}
None => {
let ident = self.parse_identifier()?;
let field = if self.peek_token_ref().token == Token::LParen {
self.parse_function(ObjectName::from(vec![ident]))?
} else {
Expr::Identifier(ident)
};
chain.push(AccessExpr::Dot(field));
}
}
}
}
} 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 {
let over_is_alias = self.dialect.supports_over_keyword_as_column_alias()
&& match &self.peek_token_ref().token {
Token::Word(w) => {
let kw = w.keyword;
let dialect = self.dialect;
!dialect.is_column_alias(&kw, self)
}
Token::SingleQuotedString(_) | Token::DoubleQuotedString(_) => false,
_ => true,
};
if over_is_alias {
self.prev_token();
None
} 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 {
matches!(
&self.peek_token_ref().token,
Token::Word(word) if self.dialect.is_interval_qualifier(word.keyword)
)
}
/// 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::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.dialect.supports_is_operator() {
let op = if self.parse_keyword(Keyword::NOT) {
BinaryOperator::IsNot
} else {
BinaryOperator::Is
};
// SQLite gives `IS` the same precedence as `=` (https://www.sqlite.org/lang_expr.html).
Ok(Expr::BinaryOp {
left: Box::new(expr),
op,
right: Box::new(
self.parse_subexpr(self.dialect.prec_value(Precedence::Eq))?,
),
})
} 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_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
let target = if !global && self.parse_keyword(Keyword::UNNEST) {
self.expect_token(&Token::LParen)?;
let array_expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
InTarget::Unnest(Box::new(array_expr))
} else if !global
&& self.dialect.supports_in_table_expression()
&& self.next_word_is_table_name()
{
let name = self.parse_object_name(false)?;
let args = if self.consume_token(&Token::LParen) {
let args = self.parse_comma_separated0(Self::parse_function_args, Token::RParen)?;
self.expect_token(&Token::RParen)?;
Some(args)
} else {
None
};
InTarget::Table { name, args }
} else if self.dialect.supports_in_unparenthesized_expr()
&& self.peek_token_ref().token != Token::LParen
{
InTarget::List(vec![
self.parse_subexpr(self.dialect.prec_value(Precedence::Eq))?
])
} else {
self.parse_in_parenthesized_target()?
};
Ok(Expr::In {
expr: Box::new(expr),
target,
negated,
global,
})
}
/// Returns true if the next token is a word the dialect accepts as a table
/// name. The word is consumed while asking, so `is_table_factor` peeks past
/// it as it does at the trailing comma check.
fn next_word_is_table_name(&mut self) -> bool {
let Token::Word(word) = &self.peek_token_ref().token else {
return false;
};
let keyword = word.keyword;
self.advance_token();
let is_name = self.dialect.is_table_factor(&keyword, self);
self.prev_token();
is_name
}
/// Parses the parenthesized subquery or value list of an `IN` predicate.
fn parse_in_parenthesized_target(&mut self) -> Result<InTarget, ParserError> {
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(InTarget::Subquery(subquery));
}
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(InTarget::List(list)),
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> {
let mut low = self.parse_subexpr(self.dialect.between_low_bound_precedence())?;
if self.dialect.supports_comparison_in_between_low_bound()
&& matches!(
self.peek_token_ref().token,
Token::Eq
| Token::DoubleEq
| Token::Neq
| Token::Lt
| Token::LtEq
| Token::Gt
| Token::GtEq
| Token::Spaceship
)
{
low = self.parse_infix(low, 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>()
))
})
}
#[cfg(not(feature = "bigdecimal"))]
fn parse_number_literal(s: String, _loc: Location) -> Result<String, ParserError> {
Ok(s)
}
#[cfg(feature = "bigdecimal")]
fn parse_number_literal(
s: String,
loc: Location,
) -> Result<bigdecimal::BigDecimal, ParserError> {
let radix = match s.get(..2) {
Some("0x" | "0X") => 16,
Some("0o" | "0O") => 8,
Some("0b" | "0B") => 2,
_ => return Self::parse(s, loc),
};
let digits = s[2..].strip_prefix('_').unwrap_or(&s[2..]);
bigdecimal::num_bigint::BigInt::parse_bytes(digits.as_bytes(), radix)
.map(bigdecimal::BigDecimal::from)
.ok_or_else(|| {
ParserError::ParserError(format!("Could not parse '{s}' as an integer{loc}"))
})
}
/// 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 dialect = self.dialect;
let trailing_commas = dialect.supports_from_trailing_commas();
let mut tables: Vec<TableWithJoins> = vec![];
self.parse_comma_separated_with_trailing_commas(
|parser| {
let table = parser.parse_table_and_joins()?;
match tables.last_mut() {
Some(previous)
if dialect.supports_comma_join_constraint()
&& table.joins.is_empty()
&& parser
.peek_one_of_keywords(&[Keyword::ON, Keyword::USING])
.is_some() =>
{
let constraint = parser.parse_join_constraint(false)?;
previous.joins.push(Join {
relation: table.relation,
global: false,
join_operator: JoinOperator::Join(constraint),
});
previous.joins.extend(parser.parse_joins()?);
}
_ => tables.push(table),
}
Ok(())
},
trailing_commas,
|kw, parser| !dialect.is_table_factor(kw, parser),
)?;
Ok(tables)
}
/// 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()?;
// `or_replace` is caught by an earlier arm. It stays so that reordering cannot bypass it.
let has_modifier = or_replace
|| or_alter
|| temporary
|| global.is_some()
|| transient
|| volatile
|| multiset.is_some()
|| persistent
|| create_view_params.is_some();
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::RULE) {
self.parse_create_rule(or_replace).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.peek_keywords(&[Keyword::USER, Keyword::MAPPING, Keyword::FOR])
|| self.peek_keywords(&[
Keyword::USER,
Keyword::MAPPING,
Keyword::IF,
Keyword::NOT,
Keyword::EXISTS,
])
{
if has_modifier {
return parser_err!(
"CREATE USER MAPPING does not accept this modifier",
modifier_loc
);
}
self.advance_token(); // USER
self.advance_token(); // MAPPING
self.parse_create_user_mapping().map(Into::into)
} else if self.parse_keyword(Keyword::USER) {
self.parse_create_user(or_replace).map(Into::into)
} else if self.parse_keyword(Keyword::AGGREGATE) {
self.parse_create_aggregate(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 AGGREGATE 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(RoleKeyword::Role).map(Into::into)
} else if self.dialect.supports_create_group() && self.parse_keyword(Keyword::GROUP) {
self.parse_create_role(RoleKeyword::Group).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]) {
if has_modifier {
return parser_err!(
"CREATE FOREIGN TABLE does not accept this modifier",
modifier_loc
);
}
self.parse_create_foreign_table().map(Into::into)
} else if self.parse_keywords(&[Keyword::FOREIGN, Keyword::DATA, Keyword::WRAPPER]) {
if has_modifier {
return parser_err!(
"CREATE FOREIGN DATA WRAPPER does not accept this modifier",
modifier_loc
);
}
self.parse_create_foreign_data_wrapper().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 table_flag = if self.parse_keyword(Keyword::TABLE) {
None
} else {
let flag = self.parse_object_name(false)?;
if !self.parse_keyword(Keyword::TABLE) {
if self.peek_token_ref().token == Token::EOF {
self.prev_token();
}
return self.expected_ref("a `TABLE` keyword", self.peek_token_ref());
}
Some(flag)
};
let table_name = self.parse_object_name(false)?;
let options = self.parse_options(Keyword::OPTIONS)?;
let (has_as, query) = if self.peek_token_ref().token == Token::EOF {
(false, None)
} else {
let (has_as, query) = self.parse_as_query()?;
(has_as, Some(query))
};
Ok(Statement::Cache {
table_flag,
table_name,
has_as,
options,
query,
})
}
/// 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>,
leakproof: Option<bool>,
window: Option<()>,
cost: Option<Expr>,
rows: Option<Expr>,
support: Option<ObjectName>,
}
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 }")?;
body.parallel = Some(self.parse_function_parallel()?);
} 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::LEAKPROOF) {
ensure_not_set(&body.leakproof, "[NOT] LEAKPROOF")?;
body.leakproof = Some(true);
} else if self.parse_keywords(&[Keyword::NOT, Keyword::LEAKPROOF]) {
ensure_not_set(&body.leakproof, "[NOT] LEAKPROOF")?;
body.leakproof = Some(false);
} else if self.parse_keyword(Keyword::WINDOW) {
ensure_not_set(&body.window, "WINDOW")?;
body.window = Some(());
} else if self.parse_keyword(Keyword::COST) {
ensure_not_set(&body.cost, "COST")?;
body.cost = Some(self.parse_number()?);
} else if self.parse_keyword(Keyword::ROWS) {
ensure_not_set(&body.rows, "ROWS")?;
body.rows = Some(self.parse_number()?);
} else if self.parse_keyword(Keyword::SUPPORT) {
ensure_not_set(&body.support, "SUPPORT")?;
body.support = Some(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 {
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 if let Some(statements) = self.maybe_parse_begin_atomic()? {
ensure_not_set(&body.function_body, "BEGIN ATOMIC")?;
body.function_body = Some(CreateFunctionBody::BeginAtomic(statements));
} 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,
leakproof: body.leakproof,
window: body.window.is_some(),
cost: body.cost,
rows: body.rows,
support: body.support,
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,
leakproof: None,
window: false,
cost: None,
rows: None,
support: 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,
leakproof: None,
window: false,
cost: None,
rows: None,
support: 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,
leakproof: None,
window: false,
cost: None,
rows: None,
support: 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))
}
/// Parses `BEGIN ATOMIC <statements> END` when the next tokens are `BEGIN ATOMIC`.
fn maybe_parse_begin_atomic(&mut self) -> Result<Option<BeginEndStatements>, ParserError> {
if !self.peek_keywords(&[Keyword::BEGIN, Keyword::ATOMIC]) {
return Ok(None);
}
let begin_token = self.next_token();
self.advance_token();
let statements = self.parse_statement_list(&[Keyword::END])?;
let end_token = self.expect_keyword(Keyword::END)?;
Ok(Some(BeginEndStatements {
begin_token: AttachedToken(begin_token),
statements,
end_token: AttachedToken(end_token),
}))
}
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();
// Argument-list boundaries cannot be parameter types.
fn parse_data_type_no_arg_boundary(parser: &mut Parser) -> Result<DataType, ParserError> {
if parser.peek_keyword(Keyword::DEFAULT) || parser.peek_keyword(Keyword::ORDER) {
parser_err!(
"Argument boundary 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_arg_boundary)? {
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` or `CREATE GROUP` statement, after the keyword has been consumed.
pub fn parse_create_role(
&mut self,
role_keyword: RoleKeyword,
) -> Result<CreateRole, ParserError> {
let supports_if_not_exists = matches!(role_keyword, RoleKeyword::Role)
&& self.dialect.supports_create_role_if_not_exists();
if !supports_if_not_exists
&& self.peek_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS])
{
return parser_err!(
format!("IF NOT EXISTS is not supported for CREATE {role_keyword}"),
self.peek_token_ref().span.start
);
}
let if_not_exists = supports_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(self.parse_number()?);
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 {
keyword: role_keyword,
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 [ OR REPLACE ] RULE name AS ON event
/// TO table_name [ WHERE condition ]
/// DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
/// ```
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createrule.html)
pub fn parse_create_rule(&mut self, or_replace: bool) -> Result<CreateRule, ParserError> {
let name = self.parse_identifier()?;
self.expect_keyword_is(Keyword::AS)?;
self.expect_keyword_is(Keyword::ON)?;
let event = match &self.peek_token_ref().token {
Token::Word(Word {
keyword: Keyword::SELECT,
..
}) => CreateRuleEvent::Select,
Token::Word(Word {
keyword: Keyword::INSERT,
..
}) => CreateRuleEvent::Insert,
Token::Word(Word {
keyword: Keyword::UPDATE,
..
}) => CreateRuleEvent::Update,
Token::Word(Word {
keyword: Keyword::DELETE,
..
}) => CreateRuleEvent::Delete,
_ => {
return self.expected_ref(
"one of SELECT or INSERT or UPDATE or DELETE",
self.peek_token_ref(),
)
}
};
self.advance_token();
self.expect_keyword_is(Keyword::TO)?;
let table_name = self.parse_object_name(false)?;
let condition = if self.parse_keyword(Keyword::WHERE) {
Some(self.parse_expr()?)
} else {
None
};
self.expect_keyword_is(Keyword::DO)?;
let do_kind = if self.parse_keyword(Keyword::ALSO) {
Some(CreateRuleDoKind::Also)
} else if self.parse_keyword(Keyword::INSTEAD) {
Some(CreateRuleDoKind::Instead)
} else {
None
};
let action = if self.parse_keyword(Keyword::NOTHING) {
CreateRuleAction::Nothing
} else if self.consume_token(&Token::LParen) {
let mut statements = Vec::new();
loop {
while self.consume_token(&Token::SemiColon) {}
if self.consume_token(&Token::RParen) {
break;
}
statements.push(self.parse_statement()?);
if !self.consume_token(&Token::SemiColon) {
self.expect_token(&Token::RParen)?;
break;
}
}
CreateRuleAction::Statements(statements)
} else {
CreateRuleAction::Statement(Box::new(self.parse_statement()?))
};
Ok(CreateRule {
or_replace,
name,
event,
table_name,
condition,
do_kind,
action,
})
}
/// ```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::CreateAggregate]
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createaggregate.html)
pub fn parse_create_aggregate(
&mut self,
or_replace: bool,
) -> Result<CreateAggregate, ParserError> {
let name = self.parse_object_name(false)?;
// The legacy and modern forms (see [`CreateAggregateArgs::Legacy`]) differ
// only in whether a second parenthesized list follows the first, so look
// that far ahead before committing to either branch.
let args = if self.peek_create_aggregate_arg_list()? {
self.parse_create_aggregate_args()?
} else {
CreateAggregateArgs::Legacy
};
self.expect_token(&Token::LParen)?;
let options = self.parse_comma_separated(|parser| {
let keyword = parser.parse_create_aggregate_option_key()?;
parser.parse_create_aggregate_option(keyword)
})?;
self.expect_token(&Token::RParen)?;
Ok(CreateAggregate {
or_replace,
name,
args,
options,
})
}
fn parse_create_aggregate_args(&mut self) -> Result<CreateAggregateArgs, ParserError> {
self.expect_token(&Token::LParen)?;
let args = if self.consume_token(&Token::Mul) {
CreateAggregateArgs::Star
} else {
let parse_arg = |parser: &mut Parser| {
let location = parser.peek_token_ref().span.start;
let arg = parser.parse_function_arg()?;
if matches!(arg.mode, Some(ArgMode::Out | ArgMode::InOut)) {
return parser_err!("Aggregates cannot have output arguments", location);
}
if arg.default_expr.is_some() {
return parser_err!("Aggregate arguments cannot have defaults", location);
}
Ok(arg)
};
let direct = if self.peek_keyword(Keyword::ORDER) {
vec![]
} else {
self.parse_comma_separated(parse_arg)?
};
if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
CreateAggregateArgs::OrderedSet {
direct,
aggregated: self.parse_comma_separated(parse_arg)?,
}
} else {
CreateAggregateArgs::List(direct)
}
};
self.expect_token(&Token::RParen)?;
Ok(args)
}
/// True when the list at the current position is the modern form's argument
/// list, meaning a second parenthesized list follows it.
///
/// Errors when the list is never closed rather than guessing a form, since
/// committing to either one buries the missing `)` under whichever error
/// that branch happens to hit first.
fn peek_create_aggregate_arg_list(&self) -> Result<bool, ParserError> {
let mut tokens = self
.tokens
.iter()
.skip(self.index)
.filter(|token| !matches!(token.token, Token::Whitespace(_)));
if tokens.next().map(|token| &token.token) != Some(&Token::LParen) {
return Ok(false);
}
let mut depth = 1usize;
while let Some(token) = tokens.next() {
match token.token {
Token::LParen => depth += 1,
Token::RParen => {
depth -= 1;
if depth == 0 {
return Ok(tokens.next().map(|token| &token.token) == Some(&Token::LParen));
}
}
// The list is still open at the statement boundary, so stop
// before the next statement's parentheses answer for this one.
Token::SemiColon => return self.expected_ref(")", token),
_ => {}
}
}
self.expected_ref(")", &EOF_TOKEN)
}
/// Parse `PARALLEL` qualifier value: `{ SAFE | RESTRICTED | UNSAFE }`.
fn parse_function_parallel(&mut self) -> Result<FunctionParallel, ParserError> {
match self.expect_one_of_keywords(&[Keyword::SAFE, Keyword::RESTRICTED, Keyword::UNSAFE])? {
Keyword::SAFE => Ok(FunctionParallel::Safe),
Keyword::RESTRICTED => Ok(FunctionParallel::Restricted),
Keyword::UNSAFE => Ok(FunctionParallel::Unsafe),
_ => self.expected_ref("one of SAFE | RESTRICTED | UNSAFE", self.peek_token_ref()),
}
}
/// Every key listed here needs an arm in [`Self::parse_create_aggregate_option`].
fn parse_create_aggregate_option_key(&mut self) -> Result<Keyword, ParserError> {
self.expect_one_of_keywords(&[
Keyword::SFUNC,
Keyword::STYPE,
Keyword::SSPACE,
Keyword::FINALFUNC,
Keyword::FINALFUNC_EXTRA,
Keyword::FINALFUNC_MODIFY,
Keyword::COMBINEFUNC,
Keyword::SERIALFUNC,
Keyword::DESERIALFUNC,
Keyword::INITCOND,
Keyword::MSFUNC,
Keyword::MINVFUNC,
Keyword::MSTYPE,
Keyword::MSSPACE,
Keyword::MFINALFUNC,
Keyword::MFINALFUNC_EXTRA,
Keyword::MFINALFUNC_MODIFY,
Keyword::MINITCOND,
Keyword::SORTOP,
Keyword::PARALLEL,
Keyword::HYPOTHETICAL,
Keyword::BASETYPE,
])
}
/// Parse the value of a single `CREATE AGGREGATE` option, given its
/// already-consumed key.
fn parse_create_aggregate_option(
&mut self,
keyword: Keyword,
) -> Result<CreateAggregateOption, ParserError> {
// Every option but the three valueless flags is spelled `KEY = value`.
if !matches!(
keyword,
Keyword::FINALFUNC_EXTRA | Keyword::MFINALFUNC_EXTRA | Keyword::HYPOTHETICAL
) {
self.expect_token(&Token::Eq)?;
}
Ok(match keyword {
Keyword::FINALFUNC_EXTRA => CreateAggregateOption::FinalFunctionExtra,
Keyword::MFINALFUNC_EXTRA => CreateAggregateOption::MovingFinalFunctionExtra,
Keyword::HYPOTHETICAL => CreateAggregateOption::Hypothetical,
Keyword::SFUNC => {
CreateAggregateOption::StateTransitionFunction(self.parse_object_name(false)?)
}
Keyword::STYPE => CreateAggregateOption::StateDataType(self.parse_data_type()?),
Keyword::SSPACE => CreateAggregateOption::StateDataSize(self.parse_literal_uint()?),
Keyword::FINALFUNC => {
CreateAggregateOption::FinalFunction(self.parse_object_name(false)?)
}
Keyword::FINALFUNC_MODIFY => {
CreateAggregateOption::FinalFunctionModify(self.parse_aggregate_modify_kind()?)
}
Keyword::COMBINEFUNC => {
CreateAggregateOption::CombineFunction(self.parse_object_name(false)?)
}
Keyword::SERIALFUNC => {
CreateAggregateOption::SerialFunction(self.parse_object_name(false)?)
}
Keyword::DESERIALFUNC => {
CreateAggregateOption::DeserialFunction(self.parse_object_name(false)?)
}
Keyword::INITCOND => CreateAggregateOption::InitialCondition(self.parse_value()?),
Keyword::MSFUNC => {
CreateAggregateOption::MovingStateTransitionFunction(self.parse_object_name(false)?)
}
Keyword::MINVFUNC => CreateAggregateOption::MovingInverseTransitionFunction(
self.parse_object_name(false)?,
),
Keyword::MSTYPE => CreateAggregateOption::MovingStateDataType(self.parse_data_type()?),
Keyword::MSSPACE => {
CreateAggregateOption::MovingStateDataSize(self.parse_literal_uint()?)
}
Keyword::MFINALFUNC => {
CreateAggregateOption::MovingFinalFunction(self.parse_object_name(false)?)
}
Keyword::MFINALFUNC_MODIFY => CreateAggregateOption::MovingFinalFunctionModify(
self.parse_aggregate_modify_kind()?,
),
Keyword::MINITCOND => {
CreateAggregateOption::MovingInitialCondition(self.parse_value()?)
}
Keyword::SORTOP => {
let operator = if self.parse_keyword(Keyword::OPERATOR) {
self.expect_token(&Token::LParen)?;
let operator = self.parse_operator_name()?;
self.expect_token(&Token::RParen)?;
operator
} else {
self.parse_operator_name()?
};
CreateAggregateOption::SortOperator(operator)
}
Keyword::PARALLEL => CreateAggregateOption::Parallel(self.parse_function_parallel()?),
Keyword::BASETYPE => CreateAggregateOption::BaseType(self.parse_data_type()?),
_ => {
return self.expected_ref("a CREATE AGGREGATE option", self.peek_token_ref());
}
})
}
/// Parse the value of `FINALFUNC_MODIFY` / `MFINALFUNC_MODIFY`.
fn parse_aggregate_modify_kind(&mut self) -> Result<AggregateModifyKind, ParserError> {
match self.expect_one_of_keywords(&[
Keyword::READ_ONLY,
Keyword::SHAREABLE,
Keyword::READ_WRITE,
])? {
Keyword::READ_ONLY => Ok(AggregateModifyKind::ReadOnly),
Keyword::SHAREABLE => Ok(AggregateModifyKind::Shareable),
Keyword::READ_WRITE => Ok(AggregateModifyKind::ReadWrite),
_ => self.expected_ref(
"one of READ_ONLY | SHAREABLE | READ_WRITE",
self.peek_token_ref(),
),
}
}
/// 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
};
let purpose = self.parse_operator_purpose()?;
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_keywords(&[Keyword::FOREIGN, Keyword::TABLE]) {
ObjectType::ForeignTable
} 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.dialect.supports_drop_group() && self.parse_keyword(Keyword::GROUP) {
ObjectType::Group
} 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::RULE) {
return self.parse_drop_rule().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, FOREIGN TABLE, FUNCTION, GROUP, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, RULE, 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 matches!(object_type, ObjectType::Role | ObjectType::Group)
&& (cascade || restrict || purge)
{
return parser_err!(
format_args!("Cannot specify CASCADE, RESTRICT, or PURGE in DROP {object_type}"),
loc
);
}
let table = if self.parse_keyword(Keyword::ON) {
Some(self.parse_object_name(false)?)
} else {
None
};
if object_type == ObjectType::ForeignTable && (temporary || purge || table.is_some()) {
return parser_err!(
"Cannot specify TEMPORARY, PURGE, or ON in DROP FOREIGN TABLE",
loc
);
}
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 RULE [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
/// ```
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-droprule.html)
fn parse_drop_rule(&mut self) -> Result<DropRule, 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(DropRule {
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,
}],
separator: DeclareSeparator::Semicolon,
})
}
/// 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,
}],
separator: DeclareSeparator::Semicolon,
})
}
/// 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,
separator: DeclareSeparator::Semicolon,
})
}
/// 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,
separator: DeclareSeparator::Comma,
})
}
/// 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 with_keyword = self.parse_keyword(Keyword::WITH);
let mut schema = None;
let mut version = None;
let mut cascade = false;
loop {
let loc = self.peek_token_ref().span.start;
let Some(keyword) =
self.parse_one_of_keywords(&[Keyword::SCHEMA, Keyword::VERSION, Keyword::CASCADE])
else {
break;
};
let already_set = match keyword {
Keyword::SCHEMA => schema.replace(self.parse_identifier()?).is_some(),
Keyword::VERSION => version.replace(self.parse_identifier()?).is_some(),
_ => core::mem::replace(&mut cascade, true),
};
if already_set {
return parser_err!(format!("{keyword:?} specified more than once"), loc);
}
}
Ok(CreateExtension {
name,
if_not_exists,
with_keyword,
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) = if partition_of.is_some() {
self.parse_partition_of_columns()?
} else {
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").into(),
)));
}
// 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: key.into(),
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> {
if !self.consume_token(&Token::LParen) {
return Ok(None);
}
let mut params = vec![];
if 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> {
self.parse_table_elements(Parser::parse_column_def)
}
/// Parse the columns and constraints of a `PARTITION OF` table, whose column entries
/// carry no data type.
///
/// See <https://www.postgresql.org/docs/current/sql-createtable.html>
fn parse_partition_of_columns(
&mut self,
) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> {
self.parse_table_elements(|parser| {
let name = parser.parse_identifier()?;
let _ = parser.parse_keywords(&[Keyword::WITH, Keyword::OPTIONS]); // [ WITH OPTIONS ]
Ok(ColumnDef {
name,
data_type: DataType::Unspecified,
options: parser.parse_column_option_defs()?,
})
})
}
fn parse_table_elements(
&mut self,
mut parse_column: impl FnMut(&mut Self) -> Result<ColumnDef, ParserError>,
) -> 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(parse_column(self)?);
} else if matches!(&self.peek_token_ref().token, Token::SingleQuotedString(_))
&& self.dialect.supports_string_literal_column_names()
{
columns.push(parse_column(self)?);
} 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()?
};
Ok(ColumnDef {
name: col_name,
data_type,
options: self.parse_column_option_defs()?,
})
}
fn parse_column_option_defs(&mut self) -> Result<Vec<ColumnOptionDef>, ParserError> {
let mut options = vec![];
loop {
if self.parse_keyword(Keyword::CONSTRAINT) {
let name = Some(self.parse_identifier()?);
let Some(option) = self.parse_optional_column_option()? else {
return self.expected_ref(
"constraint details after CONSTRAINT <name>",
self.peek_token_ref(),
);
};
options.push(ColumnOptionDef { name, option });
} else if let Some(option) = self.parse_optional_column_option()? {
options.push(ColumnOptionDef { name: None, option });
} else {
break;
};
}
Ok(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_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 nulls_distinct = self.parse_optional_nulls_distinct()?;
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,
}
.into(),
))
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY])
|| (self.dialect.supports_key_column_option() && self.parse_keyword(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::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 ReferenceClauses {
match_kind,
on_delete,
on_update,
} = self.parse_reference_clauses()?;
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 self.dialect.supports_column_generic_options()
&& self.peek_keyword(Keyword::OPTIONS)
{
Ok(self
.parse_pg_options_clause()?
.map(ColumnOption::GenericOptions))
} 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_sequence_options(false)?;
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_sequence_options(false)?;
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 optional `MATCH`, `ON DELETE`, and `ON UPDATE` reference clauses.
fn parse_reference_clauses(&mut self) -> Result<ReferenceClauses, ParserError> {
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;
}
}
Ok(ReferenceClauses {
match_kind,
on_delete,
on_update,
})
}
/// 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_index_name()?;
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_index_name()?;
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 ReferenceClauses {
match_kind,
on_delete,
on_update,
} = self.parse_reference_clauses()?;
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 = self.parse_optional_index_name()?;
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)
}
}
Token::Word(w)
if w.keyword == Keyword::NOT
&& self.dialect.supports_table_not_null_constraint()
&& self.peek_keyword(Keyword::NULL) =>
{
self.advance_token(); // NULL
let column = self.parse_identifier()?;
let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]);
Ok(Some(
NotNullConstraint {
name,
column,
no_inherit,
}
.into(),
))
}
_ => {
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())
}
/// Parse an optional index name, leaving a `USING` index type for the caller.
fn parse_optional_index_name(&mut self) -> Result<Option<Ident>, ParserError> {
if self.peek_keyword(Keyword::USING) {
Ok(None)
} else {
self.parse_optional_ident()
}
}
#[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(ObjectName::from(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 = ObjectName::from(self.parse_period_separated(|p| p.parse_identifier())?);
if self.dialect.supports_sql_option_without_value()
&& matches!(self.peek_token_ref().token, Token::Comma | Token::RParen)
{
return Ok(SqlOption::Ident(name));
}
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 self.dialect.supports_add_column_if_not_exists() {
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_sequence_options(false)?);
self.expect_token(&Token::RParen)?;
}
AlterColumnOperation::AddGenerated {
generated_as,
sequence_options,
}
} else if self.parse_keyword(Keyword::OPTIONS) {
AlterColumnOperation::GenericOptions {
options: self.parse_alter_generic_options()?,
}
} else {
let message = if is_postgresql {
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE, ADD GENERATED, or OPTIONS after ALTER COLUMN"
} else {
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE, or OPTIONS 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 self.dialect.supports_attach_partition_of()
&& self.parse_keywords(&[Keyword::ATTACH, Keyword::PARTITION])
{
AlterTableOperation::AttachPartitionOf {
partition: self.parse_object_name(false)?,
for_values: self.parse_partition_for_values()?,
}
} else if dialect_of!(self is ClickHouseDialect|GenericDialect)
&& self.parse_keyword(Keyword::ATTACH)
{
AlterTableOperation::AttachPartition {
partition: self.parse_part_or_partition()?,
}
} else if self.dialect.supports_detach_partition_of()
&& self.parse_keywords(&[Keyword::DETACH, Keyword::PARTITION])
{
let partition = self.parse_object_name(false)?;
let mode = match self.parse_one_of_keywords(&[Keyword::CONCURRENTLY, Keyword::FINALIZE])
{
Some(Keyword::CONCURRENTLY) => Some(DetachPartitionMode::Concurrently),
Some(Keyword::FINALIZE) => Some(DetachPartitionMode::Finalize),
_ => None,
};
AlterTableOperation::DetachPartitionOf { partition, mode }
} 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_keyword(Keyword::INHERIT) {
AlterTableOperation::Inherit {
parent: self.parse_object_name(false)?,
}
} else if self.parse_keyword(Keyword::NO) {
self.expect_keyword_is(Keyword::INHERIT)?;
AlterTableOperation::NoInherit {
parent: self.parse_object_name(false)?,
}
} else if self.parse_keywords(&[Keyword::SET, Keyword::LOGGED]) {
AlterTableOperation::SetLogged
} else if self.parse_keywords(&[Keyword::SET, Keyword::UNLOGGED]) {
AlterTableOperation::SetUnlogged
} else if self.parse_keyword(Keyword::RESET) {
self.expect_token(&Token::LParen)?;
let options = self.parse_comma_separated(|p| p.parse_object_name(false))?;
self.expect_token(&Token::RParen)?;
AlterTableOperation::ResetOptions { options }
} else if self.parse_keyword(Keyword::OPTIONS) {
AlterTableOperation::GenericOptions {
options: self.parse_alter_generic_options()?,
}
} 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);
}
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::PRIVILEGES]) {
return self.parse_alter_default_privileges().map(Into::into);
}
let index = self.index;
self.advance_token();
let object_type = match &self.get_current_token().token {
Token::Word(word) => word.keyword,
_ => Keyword::NoKeyword,
};
match object_type {
Keyword::SCHEMA => {
self.prev_token();
self.prev_token();
self.parse_alter_schema()
}
Keyword::VIEW => self.parse_alter_view(false),
Keyword::MATERIALIZED => {
self.expect_keyword_is(Keyword::VIEW)?;
self.parse_alter_view(true)
}
Keyword::TYPE => self.parse_alter_type(),
Keyword::COLLATION => self.parse_alter_collation().map(Into::into),
Keyword::TABLE => self.parse_alter_table(None),
Keyword::ICEBERG => {
self.expect_keyword(Keyword::TABLE)?;
self.parse_alter_table(Some(AlterTableType::Iceberg))
}
Keyword::FOREIGN => {
self.expect_keyword(Keyword::TABLE)?;
self.parse_alter_table(Some(AlterTableType::Foreign))
}
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::TRIGGER => self.parse_alter_trigger().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),
_ => {
self.index = index;
self.expected_ref(
"one of VIEW or MATERIALIZED VIEW or TYPE or COLLATION or TABLE or INDEX or FUNCTION or AGGREGATE or ROLE or POLICY or TRIGGER or CONNECTOR or ICEBERG or FOREIGN TABLE or SCHEMA or USER or OPERATOR",
self.peek_token_ref(),
)
}
}
}
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) {
Some(AlterFunctionAction::Parallel(
self.parse_function_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,
table_type: Option<AlterTableType>,
) -> 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_loc = self.peek_token_ref().span.start;
let on_cluster = self.parse_optional_on_cluster()?;
let foreign = table_type == Some(AlterTableType::Foreign);
if foreign && on_cluster.is_some() {
return parser_err!(
"Cannot specify ON CLUSTER in ALTER FOREIGN TABLE",
on_cluster_loc
);
}
let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?;
let location_loc = self.peek_token_ref().span.start;
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()?,
});
}
if foreign && location.is_some() {
return parser_err!(
"Cannot specify LOCATION in ALTER FOREIGN TABLE",
location_loc
);
}
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,
end_token: AttachedToken(end_token),
}
.into())
}
/// Parse an `ALTER [ MATERIALIZED ] VIEW` statement.
pub fn parse_alter_view(&mut self, materialized: bool) -> Result<Statement, ParserError> {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let name = self.parse_object_name(false)?;
let operation = if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
AlterViewOperation::OwnerTo(self.parse_owner()?)
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
AlterViewOperation::SetSchema {
schema_name: self.parse_object_name(false)?,
}
} else if self.parse_keyword(Keyword::SET) {
self.expect_token(&Token::LParen)?;
let options = self.parse_comma_separated(|parser| {
let key = ObjectName::from(parser.parse_identifier()?);
Ok(if parser.consume_token(&Token::Eq) {
SqlOption::KeyValue {
key,
value: parser.parse_expr()?,
}
} else {
SqlOption::Ident(key)
})
})?;
self.expect_token(&Token::RParen)?;
AlterViewOperation::SetOptions { options }
} else if self.parse_keyword(Keyword::RESET) {
self.expect_token(&Token::LParen)?;
let options = self.parse_comma_separated(Parser::parse_identifier)?;
self.expect_token(&Token::RParen)?;
AlterViewOperation::ResetOptions { options }
} else if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
AlterViewOperation::RenameTo {
new_name: self.parse_object_name(false)?,
}
} else if self.parse_keyword(Keyword::RENAME) {
let _ = self.parse_keyword(Keyword::COLUMN);
let old_column_name = self.parse_identifier()?;
self.expect_keyword_is(Keyword::TO)?;
AlterViewOperation::RenameColumn {
old_column_name,
new_column_name: self.parse_identifier()?,
}
} else if self.parse_keyword(Keyword::ALTER) {
let _ = self.parse_keyword(Keyword::COLUMN);
let column_name = self.parse_identifier()?;
let op = 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 {
return self.expected_ref(
"SET DEFAULT or DROP DEFAULT after ALTER COLUMN",
self.peek_token_ref(),
);
};
AlterViewOperation::AlterColumn { column_name, op }
} else {
let columns = self.parse_parenthesized_column_list(Optional, false)?;
let with_options = self.parse_options(Keyword::WITH)?;
self.expect_keyword_is(Keyword::AS)?;
AlterViewOperation::As {
columns,
query: self.parse_query()?,
with_options,
}
};
Ok(AlterView {
materialized,
name,
if_exists,
operation,
}
.into())
}
/// 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,
})
}
fn parse_operator_purpose(&mut self) -> Result<Option<OperatorPurpose>, ParserError> {
if !self.parse_keyword(Keyword::FOR) {
return Ok(None);
}
if self.parse_keyword(Keyword::SEARCH) {
Ok(Some(OperatorPurpose::ForSearch))
} else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
let sort_family = self.parse_object_name(false)?;
Ok(Some(OperatorPurpose::ForOrderBy { sort_family }))
} else {
self.expected_ref("SEARCH or ORDER BY after FOR", self.peek_token_ref())
}
}
/// 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)?;
let purpose = self.parse_operator_purpose()?;
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 payload = if matches!(target, CopyTarget::Stdin) {
self.take_copy_payload()
} else {
None
};
Ok(Statement::Copy {
source,
to,
target,
options,
legacy_options,
payload,
})
}
/// Take the raw inline payload of a `COPY ... FROM STDIN` statement, or
/// `None` when the statement carried no inline data.
fn take_copy_payload(&mut self) -> Option<CopyPayload> {
let Token::CopyData { data, terminated } = &self.peek_token_ref().token else {
return None;
};
let (data, terminated) = (data.clone(), *terminated);
self.next_token();
Some(CopyPayload { data, terminated })
}
/// 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 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,
},
),
},
// A BigDecimal with the bigdecimal feature, the literal as written otherwise.
Token::Number(n, l) => {
ok_value(Value::Number(Self::parse_number_literal(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) => 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}"))
.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;
if let Some(data_type) = dialect.parse_data_type(self) {
return data_type.map(|data_type| (data_type, false.into()));
}
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::NATIONAL if self.peek_keyword(Keyword::CHARACTER) => {
let _ = self.parse_keyword(Keyword::CHARACTER);
if self.parse_keyword(Keyword::VARYING) {
Ok(DataType::NationalCharacterVarying(
self.parse_optional_character_length()?,
))
} else {
Ok(DataType::NationalCharacter(
self.parse_optional_character_length()?,
))
}
}
Keyword::NATIONAL if self.peek_keyword(Keyword::CHAR) => {
let _ = self.parse_keyword(Keyword::CHAR);
Ok(DataType::NationalChar(
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, AliasKind::Column, 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, AliasKind::Table, 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<TableIndexDirective>, 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(TableIndexDirective::MySql(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), AliasKind::Column, 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]>,
kind: AliasKind,
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 match kind {
AliasKind::Column => {
self.dialect.supports_string_literal_column_alias(after_as)
}
AliasKind::Table => self.dialect.supports_string_literal_table_alias(after_as),
} =>
{
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 &current_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<TypeModifier>>, 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(TypeModifier::Identifier(w.to_ident(next_token.span)))
}
Token::Number(n, long) => modifiers.push(TypeModifier::Number(n, long)),
Token::SingleQuotedString(s) => modifiers.push(TypeModifier::String(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 {
let tables = self.parse_comma_separated(|p| p.parse_object_name(false))?;
self.expect_keyword_is(Keyword::FROM)?;
(tables, true)
}
} 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.dialect.supports_singular_value_keyword()
&& 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_empty_projection() {
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
},
})
}
/// Returns true if the next token ends a `SELECT` target list before it begins.
fn peek_empty_projection(&self) -> bool {
match &self.peek_token_ref().token {
Token::EOF | Token::SemiColon | Token::RParen => true,
Token::Word(w) => matches!(
w.keyword,
Keyword::INTO
| Keyword::FROM
| Keyword::WHERE
| Keyword::GROUP
| Keyword::HAVING
| Keyword::WINDOW
| Keyword::ORDER
| Keyword::LIMIT
| Keyword::OFFSET
| Keyword::FETCH
| Keyword::FOR
| Keyword::UNION
| Keyword::INTERSECT
| Keyword::EXCEPT
),
_ => false,
}
}
/// 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 inheritance_markers = self.dialect.supports_table_inheritance_markers();
let only = inheritance_markers && self.parse_keyword(Keyword::ONLY);
let only_parenthesized = only && self.consume_token(&Token::LParen);
// Unquoted reserved keywords, e.g. `ON`, are not table names. The
// keyword is consumed first so that `is_table_factor` peeks past it,
// as it does at the trailing comma check.
self.advance_token();
let keyword = match &self.get_current_token().token {
Token::Word(w) => w.keyword,
_ => Keyword::NoKeyword,
};
let is_table_name = self.dialect.is_table_factor(&keyword, self);
self.prev_token();
if !is_table_name {
return self.expected_ref("table name", self.peek_token_ref());
}
let name = self.parse_object_name(true)?;
let inheritance = if only {
if only_parenthesized {
self.expect_token(&Token::RParen)?;
}
Some(TableInheritance::Only {
parenthesized: only_parenthesized,
})
} else if inheritance_markers && self.consume_token(&Token::Mul) {
Some(TableInheritance::IncludeDescendants)
} else {
None
};
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()?;
let mut index_directives = if self.dialect.supports_table_hints() {
self.maybe_parse(|p| p.parse_table_index_hints())?
.unwrap_or(vec![])
} else {
vec![]
};
if self.dialect.supports_indexed_by() {
if self.parse_keywords(&[Keyword::INDEXED, Keyword::BY]) {
index_directives.push(TableIndexDirective::IndexedBy(self.parse_identifier()?));
} else if self.parse_keywords(&[Keyword::NOT, Keyword::INDEXED]) {
index_directives.push(TableIndexDirective::NotIndexed);
}
}
// 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_directives,
inheritance,
};
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_directives: vec![],
inheritance: None,
})
}
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,
Keyword::RESERVOIR,
]) {
Some(Keyword::BERNOULLI) => Some(TableSampleMethod::Bernoulli),
Some(Keyword::ROW) => Some(TableSampleMethod::Row),
Some(Keyword::SYSTEM) => Some(TableSampleMethod::System),
Some(Keyword::BLOCK) => Some(TableSampleMethod::Block),
Some(Keyword::RESERVOIR) => Some(TableSampleMethod::Reservoir),
_ => 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, AliasKind::Column, 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,
})
}
/// Parse a `GRANT` of role membership, after the `GRANT` keyword.
pub fn parse_grant_role(&mut self) -> Result<GrantRole, ParserError> {
let roles = self.parse_comma_separated(Parser::parse_identifier)?;
self.expect_keyword_is(Keyword::TO)?;
let grantees = self.parse_grantees()?;
let options = if self.parse_keyword(Keyword::WITH) {
self.parse_comma_separated(Parser::parse_role_grant_option)?
} else {
vec![]
};
let granted_by = if self.parse_keywords(&[Keyword::GRANTED, Keyword::BY]) {
Some(self.parse_identifier()?)
} else {
None
};
Ok(GrantRole {
roles,
grantees,
options,
granted_by,
})
}
fn parse_role_grant_option(&mut self) -> Result<RoleGrantOption, ParserError> {
let name = self.parse_identifier()?;
let value =
match self.parse_one_of_keywords(&[Keyword::OPTION, Keyword::TRUE, Keyword::FALSE]) {
Some(Keyword::OPTION) => RoleGrantOptionValue::Option,
Some(Keyword::TRUE) => RoleGrantOptionValue::True,
Some(Keyword::FALSE) => RoleGrantOptionValue::False,
_ => return self.expected_ref("OPTION, TRUE or FALSE", self.peek_token_ref()),
};
Ok(RoleGrantOption { name, value })
}
/// Parses a statement body with `privileges`, and with `roles` instead when that fails and
/// the body opens with a role list followed by `keyword`.
fn parse_privileges_or_roles<P: Into<Statement>, R: Into<Statement>>(
&mut self,
keyword: Keyword,
privileges: fn(&mut Self) -> Result<P, ParserError>,
roles: fn(&mut Self) -> Result<R, ParserError>,
) -> Result<Statement, ParserError> {
let index = self.index;
let privileges_err = match privileges(self) {
Ok(statement) => return Ok(statement.into()),
Err(err) => err,
};
self.index = index;
let is_role_list = self.parse_comma_separated(Parser::parse_identifier).is_ok()
&& self.parse_keyword(keyword);
self.index = index;
if is_role_list {
roles(self).map(Into::into)
} else {
Err(privileges_err)
}
}
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 if grantee_type == GranteesType::Public {
// PUBLIC does not carry over to the next grantee.
GranteesType::None
} 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)
}
/// Whether the keyword at the next token introduces a grant object type. A table named after
/// the keyword is followed by `TO`, `FROM`, `,` or `.`, as in `GRANT SELECT ON function TO r`.
fn peek_grant_object_type(&self) -> bool {
!matches!(
&self.peek_nth_token_ref(1).token,
Token::Comma
| Token::Period
| Token::Word(Word {
keyword: Keyword::TO | Keyword::FROM,
..
})
)
}
/// 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 = self.parse_privileges()?;
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::ALL,
Keyword::PROCEDURES,
Keyword::IN,
Keyword::SCHEMA,
]) {
Some(GrantObjects::AllProceduresInSchema {
schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[
Keyword::ALL,
Keyword::ROUTINES,
Keyword::IN,
Keyword::SCHEMA,
]) {
Some(GrantObjects::AllRoutinesInSchema {
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 if self.peek_grant_object_type() && self.parse_keyword(Keyword::FUNCTION) {
Some(GrantObjects::Functions(
self.parse_comma_separated(Parser::parse_function_desc)?,
))
} else if self.peek_grant_object_type() && self.parse_keyword(Keyword::PROCEDURE) {
Some(GrantObjects::Procedures(
self.parse_comma_separated(Parser::parse_function_desc)?,
))
} else if self.peek_grant_object_type() && self.parse_keyword(Keyword::ROUTINE) {
Some(GrantObjects::Routines(
self.parse_comma_separated(Parser::parse_function_desc)?,
))
} else {
let object_type = if self.peek_grant_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,
])
} else {
None
};
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?)),
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))
}
/// 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::MAINTAIN) {
Ok(Action::Maintain)
} 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 a `REVOKE` of role membership, after the `REVOKE` keyword.
pub fn parse_revoke_role(&mut self) -> Result<RevokeRole, ParserError> {
let option_for = self.parse_role_grant_option_for();
let roles = self.parse_comma_separated(Parser::parse_identifier)?;
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(RevokeRole {
option_for,
roles,
grantees,
granted_by,
cascade,
})
}
/// Parses `name OPTION FOR`, leaving `GRANT OPTION FOR` to the privilege form.
fn parse_role_grant_option_for(&mut self) -> Option<Ident> {
let index = self.index;
if !self.peek_keyword(Keyword::GRANT) {
if let Ok(name) = self.parse_identifier() {
if self.parse_keywords(&[Keyword::OPTION, Keyword::FOR]) {
return Some(name);
}
}
}
self.index = index;
None
}
fn peek_role_grant_option_for(&mut self) -> bool {
let index = self.index;
let matched = self.parse_role_grant_option_for().is_some();
self.index = index;
matched
}
/// Parse `ALTER DEFAULT PRIVILEGES`, whose keywords are already consumed.
///
/// See [PostgreSQL docs](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html).
pub fn parse_alter_default_privileges(
&mut self,
) -> Result<AlterDefaultPrivileges, ParserError> {
// `FOR USER` is a synonym of `FOR ROLE` and is normalised to it.
let mut options = Vec::new();
loop {
if self.parse_keyword(Keyword::FOR) {
self.expect_one_of_keywords(&[Keyword::ROLE, Keyword::USER])?;
options.push(DefaultPrivilegesOption::ForRole(
self.parse_comma_separated(|p| p.parse_identifier())?,
));
} else if self.parse_keywords(&[Keyword::IN, Keyword::SCHEMA]) {
options.push(DefaultPrivilegesOption::InSchema(
self.parse_comma_separated(|p| p.parse_object_name(false))?,
));
} else {
break;
}
}
let is_grant = if self.parse_keyword(Keyword::GRANT) {
true
} else if self.parse_keyword(Keyword::REVOKE) {
false
} else {
return self.expected_ref("GRANT or REVOKE", self.peek_token_ref());
};
let grant_option_for =
!is_grant && self.parse_keywords(&[Keyword::GRANT, Keyword::OPTION, Keyword::FOR]);
let privileges = self.parse_privileges()?;
self.expect_keyword_is(Keyword::ON)?;
let object_type = self.parse_default_privileges_object_type()?;
self.expect_keyword_is(if is_grant { Keyword::TO } else { Keyword::FROM })?;
let grantees = self.parse_grantees()?;
let operation = if is_grant {
AlterDefaultPrivilegesOperation::Grant {
privileges,
object_type,
grantees,
with_grant_option: self.parse_keywords(&[
Keyword::WITH,
Keyword::GRANT,
Keyword::OPTION,
]),
}
} else {
AlterDefaultPrivilegesOperation::Revoke {
grant_option_for,
privileges,
object_type,
grantees,
cascade: self.parse_cascade_option(),
}
};
Ok(AlterDefaultPrivileges { options, operation })
}
/// Parse `ALL [PRIVILEGES]` or a comma separated privilege list.
fn parse_privileges(&mut self) -> Result<Privileges, ParserError> {
if self.parse_keyword(Keyword::ALL) {
Ok(Privileges::All {
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
})
} else {
Ok(Privileges::Actions(self.parse_actions_list()?))
}
}
fn parse_default_privileges_object_type(
&mut self,
) -> Result<DefaultPrivilegesObjectType, ParserError> {
if self.parse_keywords(&[Keyword::LARGE, Keyword::OBJECTS]) {
return Ok(DefaultPrivilegesObjectType::LargeObjects);
}
for (keyword, object_type) in [
(Keyword::TABLES, DefaultPrivilegesObjectType::Tables),
(Keyword::SEQUENCES, DefaultPrivilegesObjectType::Sequences),
(Keyword::FUNCTIONS, DefaultPrivilegesObjectType::Functions),
(Keyword::ROUTINES, DefaultPrivilegesObjectType::Routines),
(Keyword::TYPES, DefaultPrivilegesObjectType::Types),
(Keyword::SCHEMAS, DefaultPrivilegesObjectType::Schemas),
] {
if self.parse_keyword(keyword) {
return Ok(object_type);
}
}
self.expected_ref(
"TABLES, SEQUENCES, FUNCTIONS, ROUTINES, TYPES, SCHEMAS or LARGE OBJECTS",
self.peek_token_ref(),
)
}
/// 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 = self.parse_keyword(Keyword::INTO);
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 {
let columns =
self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?;
// PostgreSQL: `( column [, ...] ) [ WHERE index_predicate ]`.
let predicate = if self.parse_keyword(Keyword::WHERE) {
Some(self.parse_expr()?)
} else {
None
};
Some(ConflictTarget::Columns { columns, predicate })
} 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 = if duplicate_treatment != Some(DuplicateTreatment::Distinct)
&& self.dialect.supports_aggregate_order_by_with_empty_args()
&& self.peek_keywords(&[Keyword::ORDER, Keyword::BY])
{
vec![]
} else {
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)?;
let sequence_options = self.parse_sequence_options(true)?;
Ok(Statement::CreateSequence {
temporary,
if_not_exists,
name,
sequence_options,
})
}
/// Parse the sequence options shared by `CREATE SEQUENCE` and identity
/// columns. The options form an unordered list, each allowed at most once.
///
/// `AS <data_type>` and `OWNED BY` are options of `CREATE SEQUENCE` only, so
/// `allow_type_and_owner` gates them off for an identity column.
fn parse_sequence_options(
&mut self,
allow_type_and_owner: bool,
) -> Result<Vec<SequenceOptions>, ParserError> {
let mut sequence_options: Vec<SequenceOptions> = vec![];
loop {
let (option, name) = if self.parse_keyword(Keyword::INCREMENT) {
//[ INCREMENT [ BY ] increment ]
let by = self.parse_keyword(Keyword::BY);
(
SequenceOptions::IncrementBy(self.parse_number()?, by),
"INCREMENT",
)
} else if self.parse_keyword(Keyword::MINVALUE) {
//[ MINVALUE minvalue | NO MINVALUE ]
(
SequenceOptions::MinValue(Some(self.parse_number()?)),
"MINVALUE | NO MINVALUE",
)
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
(SequenceOptions::MinValue(None), "MINVALUE | NO MINVALUE")
} else if self.parse_keyword(Keyword::MAXVALUE) {
//[ MAXVALUE maxvalue | NO MAXVALUE ]
(
SequenceOptions::MaxValue(Some(self.parse_number()?)),
"MAXVALUE | NO MAXVALUE",
)
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
(SequenceOptions::MaxValue(None), "MAXVALUE | NO MAXVALUE")
} else if self.parse_keyword(Keyword::START) {
//[ START [ WITH ] start ]
let with = self.parse_keyword(Keyword::WITH);
(
SequenceOptions::StartWith(self.parse_number()?, with),
"START",
)
} else if self.parse_keyword(Keyword::CACHE) {
//[ CACHE cache ]
(SequenceOptions::Cache(self.parse_number()?), "CACHE")
} else if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
// [ [ NO ] CYCLE ]
(SequenceOptions::Cycle(true), "CYCLE | NO CYCLE")
} else if self.parse_keyword(Keyword::CYCLE) {
(SequenceOptions::Cycle(false), "CYCLE | NO CYCLE")
} else if allow_type_and_owner && self.parse_keyword(Keyword::AS) {
//[ AS data_type ]
(SequenceOptions::DataType(self.parse_data_type()?), "AS")
} else if allow_type_and_owner && self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
// [ OWNED BY { table_name.column_name | NONE } ]
// Only the single bare word `NONE` means no owner, `none.c` is a column.
let owner = self.parse_object_name(false)?;
let is_none = matches!(
owner.0.as_slice(),
[ObjectNamePart::Identifier(ident)]
if ident.quote_style.is_none() && ident.value.eq_ignore_ascii_case("NONE")
);
(
SequenceOptions::OwnedBy((!is_none).then_some(owner)),
"OWNED BY",
)
} else {
break;
};
if sequence_options
.iter()
.any(|seen| discriminant(seen) == discriminant(&option))
{
return Err(ParserError::ParserError(format!(
"{name} specified more than once"
)));
}
sequence_options.push(option);
}
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(Parser::parse_pg_option)?;
self.expect_token(&Token::RParen)?;
Ok(Some(options))
}
fn parse_pg_option(&mut self) -> Result<CreateServerOption, ParserError> {
let key = self.parse_identifier()?;
let value = self.parse_identifier()?;
Ok(CreateServerOption { key, value })
}
/// Parse the parenthesized list following `OPTIONS` in an `ALTER` statement.
fn parse_alter_generic_options(&mut self) -> Result<Vec<AlterGenericOption>, ParserError> {
self.expect_token(&Token::LParen)?;
let options = self.parse_comma_separated(|p| {
// Before a string literal, `ADD`, `SET` and `DROP` name an option.
let action = match p.peek_nth_token_ref(1).token {
Token::SingleQuotedString(_) => None,
_ => p.parse_one_of_keywords(&[Keyword::ADD, Keyword::SET, Keyword::DROP]),
};
match action {
Some(Keyword::SET) => Ok(AlterGenericOption::Set(p.parse_pg_option()?)),
Some(Keyword::DROP) => Ok(AlterGenericOption::Drop(p.parse_identifier()?)),
action => Ok(AlterGenericOption::Add {
option: p.parse_pg_option()?,
add_keyword: action.is_some(),
}),
}
})?;
self.expect_token(&Token::RParen)?;
Ok(options)
}
/// Parse a `CREATE FOREIGN TABLE` statement.
///
/// 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)?;
let partition_of = if self.parse_keywords(&[Keyword::PARTITION, Keyword::OF]) {
Some(self.parse_object_name(false)?)
} else {
None
};
let (columns, constraints) = if partition_of.is_some() {
self.parse_partition_of_columns()?
} else if self.peek_token_ref().token == Token::LParen {
self.parse_columns()?
} else {
return self.expected_ref(
"'(' before the column list of CREATE FOREIGN TABLE",
self.peek_token_ref(),
);
};
// `INHERITS` belongs to the column-list form only.
let inherits = if partition_of.is_none() && self.parse_keyword(Keyword::INHERITS) {
Some(self.parse_parenthesized_qualified_column_list(IsOptional::Mandatory, false)?)
} else {
None
};
let for_values = if partition_of.is_some() {
if self
.peek_one_of_keywords(&[Keyword::FOR, Keyword::DEFAULT])
.is_none()
{
return self.expected_ref(
"FOR VALUES or DEFAULT after PARTITION OF",
self.peek_token_ref(),
);
}
Some(self.parse_partition_for_values()?)
} else {
None
};
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,
inherits,
partition_of,
for_values,
server_name,
options,
})
}
/// Parse a `CREATE FOREIGN DATA WRAPPER` statement.
///
/// See <https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html>
pub fn parse_create_foreign_data_wrapper(
&mut self,
) -> Result<CreateForeignDataWrapper, ParserError> {
let name = self.parse_identifier()?;
let mut routines = Vec::new();
loop {
let routine = if self.parse_keyword(Keyword::HANDLER) {
ForeignDataWrapperRoutineClause::Handler(self.parse_object_name(false)?)
} else if self.parse_keywords(&[Keyword::NO, Keyword::HANDLER]) {
ForeignDataWrapperRoutineClause::NoHandler
} else if self.parse_keyword(Keyword::VALIDATOR) {
ForeignDataWrapperRoutineClause::Validator(self.parse_object_name(false)?)
} else if self.parse_keywords(&[Keyword::NO, Keyword::VALIDATOR]) {
ForeignDataWrapperRoutineClause::NoValidator
} else {
break;
};
routines.push(routine);
}
let options = self.parse_pg_options_clause()?;
Ok(CreateForeignDataWrapper {
name,
routines,
options,
})
}
/// Parse a `CREATE USER MAPPING` statement, after `CREATE USER MAPPING`.
///
/// See <https://www.postgresql.org/docs/current/sql-createusermapping.html>
pub fn parse_create_user_mapping(&mut self) -> Result<CreateUserMapping, ParserError> {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
self.expect_keyword_is(Keyword::FOR)?;
let user = match self.parse_one_of_keywords(&[
Keyword::USER,
Keyword::CURRENT_ROLE,
Keyword::CURRENT_USER,
Keyword::SESSION_USER,
Keyword::PUBLIC,
]) {
Some(Keyword::USER) => UserMappingUser::User,
Some(Keyword::CURRENT_ROLE) => UserMappingUser::CurrentRole,
Some(Keyword::CURRENT_USER) => UserMappingUser::CurrentUser,
Some(Keyword::SESSION_USER) => UserMappingUser::SessionUser,
Some(Keyword::PUBLIC) => UserMappingUser::Public,
_ => UserMappingUser::Name(self.parse_identifier()?),
};
self.expect_keyword_is(Keyword::SERVER)?;
let server_name = self.parse_identifier()?;
let options = self.parse_pg_options_clause()?;
Ok(CreateUserMapping {
if_not_exists,
user,
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
};
let body = match self.maybe_parse_begin_atomic()? {
Some(statements) => CreateProcedureBody::BeginAtomic(statements),
None => {
self.expect_keyword_is(Keyword::AS)?;
CreateProcedureBody::As(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(w)
if !matches!(
w.keyword,
Keyword::PARTITION
| Keyword::ORDER
| Keyword::ROWS
| Keyword::RANGE
| Keyword::GROUPS
) =>
{
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),
})
}
/// Parse a `REFRESH MATERIALIZED VIEW` statement, after the `REFRESH` keyword.
fn parse_refresh_materialized_view(&mut self) -> Result<RefreshMaterializedView, ParserError> {
self.expect_keywords(&[Keyword::MATERIALIZED, Keyword::VIEW])?;
let concurrently = self.parse_keyword(Keyword::CONCURRENTLY);
let name = self.parse_object_name(false)?;
let with_data = if self.parse_keywords(&[Keyword::WITH, Keyword::DATA]) {
Some(true)
} else if self.parse_keywords(&[Keyword::WITH, Keyword::NO, Keyword::DATA]) {
Some(false)
} else {
None
};
Ok(RefreshMaterializedView {
concurrently,
name,
with_data,
})
}
}
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, Ident, ObjectName,
TimezoneInfo, TypeModifier,
};
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_national_character_string_types() {
// National character string types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#national-character-string-type>
let dialect =
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]);
let length = Some(CharacterLength::IntegerLength {
length: 20,
unit: None,
});
test_parse_data_type!(
dialect,
"NATIONAL CHARACTER",
DataType::NationalCharacter(None)
);
test_parse_data_type!(
dialect,
"NATIONAL CHARACTER(20)",
DataType::NationalCharacter(length)
);
test_parse_data_type!(
dialect,
"NATIONAL CHARACTER VARYING(20)",
DataType::NationalCharacterVarying(length)
);
test_parse_data_type!(dialect, "NATIONAL CHAR", DataType::NationalChar(None));
test_parse_data_type!(dialect, "NATIONAL CHAR(20)", DataType::NationalChar(length));
}
#[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![TypeModifier::Identifier(Ident::new("POINT"))]
)
);
test_parse_data_type!(
dialect,
"GEOMETRY(POINT, 4326)",
DataType::Custom(
ObjectName::from(vec!["GEOMETRY".into()]),
vec![
TypeModifier::Identifier(Ident::new("POINT")),
TypeModifier::Number("4326".to_string(), false),
]
)
);
}
#[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()
);
}
}
}