blob: 8c9cc005e77358d9779e6f5d8bcaee80f4517518 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String};
use crate::ast::{BinaryOperator, DataType, Expr, ObjectName, Statement};
use crate::dialect::{BindPlaceholderStyle, Dialect, Precedence};
use crate::keywords::{self, Keyword};
use crate::parser::{Parser, ParserError};
use crate::tokenizer::Token;
/// Precedence of `<`, `<=`, `>` and `>=`, between `=` and the bitwise operators, see
/// <https://www.sqlite.org/lang_expr.html#operators_and_parse_affecting_attributes>
const COMPARISON_PREC: u8 = 21;
/// A [`Dialect`] for [SQLite](https://www.sqlite.org)
///
/// This dialect allows columns in a
/// [`CREATE TABLE`](https://sqlite.org/lang_createtable.html) statement with no
/// type specified, as in `CREATE TABLE t1 (a)`. In the AST, these columns will
/// have the data type [`Unspecified`](crate::ast::DataType::Unspecified).
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SQLiteDialect {}
impl Dialect for SQLiteDialect {
// see https://www.sqlite.org/lang_keywords.html
// parse `...`, [...] and "..." as identifier
// TODO: support depending on the context tread '...' as identifier too.
fn is_delimited_identifier_start(&self, ch: char) -> bool {
ch == '`' || ch == '"' || ch == '['
}
fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
Some('`')
}
fn ordered_bind_placeholder_style(&self) -> Option<BindPlaceholderStyle> {
Some(BindPlaceholderStyle::QuestionMark)
}
fn is_identifier_start(&self, ch: char) -> bool {
// See https://www.sqlite.org/draft/tokenreq.html
ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_' || ch >= '\u{0080}'
}
fn supports_filter_during_aggregation(&self) -> bool {
true
}
fn supports_start_transaction_modifier(&self) -> bool {
true
}
fn is_identifier_part(&self, ch: char) -> bool {
self.is_identifier_start(ch) || ch.is_ascii_digit() || ch == '$'
}
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
if parser.parse_keyword(Keyword::REPLACE) {
parser.prev_token();
Some(parser.parse_insert(parser.get_current_token().clone()))
} else {
None
}
}
fn parse_data_type(&self, parser: &mut Parser) -> Option<Result<DataType, ParserError>> {
parse_type_with_numeric_modifiers(parser).map(Ok)
}
fn parse_infix(
&self,
parser: &mut crate::parser::Parser,
expr: &crate::ast::Expr,
precedence: u8,
) -> Option<Result<crate::ast::Expr, ParserError>> {
// Parse MATCH, REGEXP and GLOB as operators
// See <https://www.sqlite.org/lang_expr.html#the_like_glob_regexp_match_and_extract_operators>
for (keyword, op) in [
(Keyword::REGEXP, BinaryOperator::Regexp),
(Keyword::MATCH, BinaryOperator::Match),
(Keyword::GLOB, BinaryOperator::Glob),
] {
if parser.parse_keyword(keyword) {
let left = Box::new(expr.clone());
let right = Box::new(match parser.parse_subexpr(precedence) {
Ok(expr) => expr,
Err(e) => return Some(Err(e)),
});
return Some(Ok(Expr::BinaryOp { left, op, right }));
}
}
None
}
/// `&`, `|`, `<<`, and `>>` share one left-associative precedence level.
/// See <https://www.sqlite.org/lang_expr.html#operators_and_parse_affecting_attributes>
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
match parser.peek_token_ref().token {
Token::Ampersand | Token::Pipe | Token::ShiftLeft | Token::ShiftRight => {
Some(Ok(self.prec_value(Precedence::Ampersand)))
}
Token::Lt | Token::LtEq | Token::Gt | Token::GtEq => Some(Ok(COMPARISON_PREC)),
_ => None,
}
}
fn prec_value(&self, prec: Precedence) -> u8 {
match prec {
Precedence::Period => 100,
Precedence::DoubleColon => 50,
Precedence::AtTz => 41,
Precedence::MulDivModOp => 40,
Precedence::PlusMinus => 30,
Precedence::Xor => 25,
Precedence::Ampersand => 24,
Precedence::Caret => 23,
Precedence::Pipe | Precedence::Colon | Precedence::PgOther => 22,
Precedence::Between | Precedence::Eq => 20,
Precedence::Like => 19,
Precedence::Is => 17,
Precedence::UnaryNot => 15,
Precedence::And => 10,
Precedence::Or => 5,
}
}
fn supports_in_empty_list(&self) -> bool {
true
}
fn supports_limit_comma(&self) -> bool {
true
}
fn supports_asc_desc_in_column_definition(&self) -> bool {
true
}
fn supports_dollar_placeholder(&self) -> bool {
true
}
/// SQLite supports `NOTNULL` as aliases for `IS NOT NULL`
/// See: <https://sqlite.org/syntax/expr.html>
fn supports_notnull_operator(&self) -> bool {
true
}
fn supports_comma_separated_trim(&self) -> bool {
true
}
fn supports_numeric_literal_underscores(&self) -> bool {
true
}
fn supports_double_eq_assignment(&self) -> bool {
true
}
fn supports_string_literal_column_names(&self) -> bool {
true
}
fn supports_cast_empty_data_type_to_unspecified(&self) -> bool {
true
}
fn supports_national_string_literal(&self) -> bool {
false
}
fn supports_parens_around_table_factor(&self) -> bool {
true
}
fn supports_unterminated_block_comments(&self) -> bool {
true
}
fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
// https://www.sqlite.org/lang_keywords.html
!matches!(
kw,
Keyword::SELECT
| Keyword::WHERE
| Keyword::GROUP
| Keyword::HAVING
| Keyword::ORDER
| Keyword::LIMIT
| Keyword::UNION
| Keyword::EXCEPT
| Keyword::INTERSECT
| Keyword::RETURNING
| Keyword::VALUES
| Keyword::FROM
| Keyword::INTO
)
}
fn supports_over_keyword_as_column_alias(&self) -> bool {
true
}
fn supports_indexed_by(&self) -> bool {
true
}
/// `INDEXED BY` and `NOT INDEXED` follow the table name or alias.
fn is_table_alias(&self, kw: &Keyword, parser: &mut Parser) -> bool {
match kw {
Keyword::INDEXED => false,
Keyword::NOT => !parser.peek_keyword(Keyword::INDEXED),
_ => !keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw),
}
}
fn supports_comma_join_constraint(&self) -> bool {
true
}
fn supports_aggregate_order_by_with_empty_args(&self) -> bool {
true
}
/// See <https://www.sqlite.org/lang_expr.html#binaryops>
fn supports_bitwise_shift_operators(&self) -> bool {
true
}
fn supports_in_table_expression(&self) -> bool {
true
}
/// The keywords SQLite does not accept as a name, as opposed to the
/// keywords its grammar lets fall back to an identifier.
/// See <https://www.sqlite.org/lang_keywords.html> and the `%fallback ID`
/// list in [parse.y](https://sqlite.org/src/file?name=src/parse.y)
fn is_table_factor(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
!matches!(
kw,
Keyword::ADD
| Keyword::ALL
| Keyword::ALTER
| Keyword::AND
| Keyword::AS
| Keyword::AUTOINCREMENT
| Keyword::BETWEEN
| Keyword::CASE
| Keyword::CHECK
| Keyword::COLLATE
| Keyword::COMMIT
| Keyword::CONSTRAINT
| Keyword::CREATE
| Keyword::DEFAULT
| Keyword::DEFERRABLE
| Keyword::DELETE
| Keyword::DISTINCT
| Keyword::DROP
| Keyword::ELSE
| Keyword::ESCAPE
| Keyword::EXCEPT
| Keyword::EXISTS
| Keyword::FOREIGN
| Keyword::FROM
| Keyword::GROUP
| Keyword::HAVING
| Keyword::IN
| Keyword::INDEX
| Keyword::INSERT
| Keyword::INTERSECT
| Keyword::INTO
| Keyword::IS
| Keyword::JOIN
| Keyword::LIMIT
| Keyword::NOT
| Keyword::NOTHING
| Keyword::NOTNULL
| Keyword::NULL
| Keyword::ON
| Keyword::OR
| Keyword::ORDER
| Keyword::PRIMARY
| Keyword::REFERENCES
| Keyword::RETURNING
| Keyword::SELECT
| Keyword::SET
| Keyword::TABLE
| Keyword::THEN
| Keyword::TO
| Keyword::TRANSACTION
| Keyword::UNION
| Keyword::UNIQUE
| Keyword::UPDATE
| Keyword::USING
| Keyword::VALUES
| Keyword::WHEN
| Keyword::WHERE
)
}
}
/// SQLite permits signed and decimal numeric type modifiers.
fn parse_type_with_numeric_modifiers(parser: &mut Parser) -> Option<DataType> {
let (data_type, token_count) = {
let first = parser.peek_token_ref();
let Token::Word(word) = &first.token else {
return None;
};
if parser.peek_nth_token_ref(1).token != Token::LParen {
return None;
}
let mut modifiers = [None; 2];
let mut nth = 2;
let mut is_nonstandard = false;
let mut closed = false;
for modifier in &mut modifiers {
let sign = match parser.peek_nth_token_ref(nth).token {
Token::Minus => "-",
Token::Plus => "+",
_ => "",
};
if !sign.is_empty() {
is_nonstandard = true;
nth += 1;
}
let Token::Number(number, _) = &parser.peek_nth_token_ref(nth).token else {
return None;
};
is_nonstandard |= number.parse::<u64>().is_err();
*modifier = Some((sign, number));
match parser.peek_nth_token_ref(nth + 1).token {
Token::RParen => {
closed = true;
break;
}
Token::Comma => nth += 2,
_ => return None,
}
}
if !closed || !is_nonstandard {
return None;
}
let modifiers = modifiers
.into_iter()
.flatten()
.map(|(sign, number)| {
let mut value = String::with_capacity(sign.len() + number.len());
value.push_str(sign);
value.push_str(number);
crate::ast::TypeModifier::Number(value, false)
})
.collect();
(
DataType::Custom(
ObjectName::from(word.clone().into_ident(first.span)),
modifiers,
),
nth + 2,
)
};
for _ in 0..token_count {
parser.advance_token();
}
Some(data_type)
}