Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse create function #21

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/alter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
lexer::Token,
parser::{ParseError, Parser},
qualified_name::parse_qualified_name,
DataType, Identifier, Issue, QualifiedName, SString, Span, Spanned, Statement,
DataType, Identifier, QualifiedName, SString, Span, Spanned, Statement,
};

/// Option on an index
Expand Down
105 changes: 16 additions & 89 deletions src/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
qualified_name::parse_qualified_name,
select::{parse_select, Select},
statement::parse_statement,
DataType, Expression, Identifier, Issue, QualifiedName, SString, Span, Spanned, Statement,
DataType, Expression, Identifier, QualifiedName, SString, Span, Spanned, Statement,
};

/// Options on created table
Expand Down Expand Up @@ -505,7 +505,7 @@ impl Spanned for FunctionParamDirection {
///
/// This is not fully implemented yet
///
/// ```ignore
/// ```
/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, CreateFunction, Statement};
/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
/// # let mut issues = Vec::new();
Expand All @@ -515,20 +515,21 @@ impl Spanned for FunctionParamDirection {
/// BEGIN
/// SET c = 100;
/// RETURN a + b;
/// END;
/// END
/// $$
/// DELIMITER ;";
/// let mut stmts = parse_statements(sql, &mut issues, &options);
///
/// assert!(issues.is_empty());
///
/// assert!(issues.is_empty(),"Issues: {:#?}", issues);
/// #
/// let create: CreateFunction = match stmts.pop() {
/// Some(Statement::CreateFunction(c)) => c,
/// _ => panic!("We should get an create function statement")
/// };
///
/// assert!(create.name.as_str() == "add_func3");
/// println!("{:#?}", create.return_)
///
/// ```
#[derive(Clone, Debug)]
pub struct CreateFunction<'a> {
Expand All @@ -548,10 +549,8 @@ pub struct CreateFunction<'a> {
pub returns_span: Span,
/// Type of return value
pub return_type: DataType<'a>,
/// Characteristics of created function
pub characteristics: Vec<FunctionCharacteristic<'a>>,
/// Statement computing return value
pub return_: Option<Box<Statement<'a>>>,
/// Statement of function body
pub statement: Box<Statement<'a>>,
}

impl<'a> Spanned for CreateFunction<'a> {
Expand All @@ -562,8 +561,7 @@ impl<'a> Spanned for CreateFunction<'a> {
.join_span(&self.if_not_exists)
.join_span(&self.name)
.join_span(&self.return_type)
.join_span(&self.characteristics)
.join_span(&self.return_)
.join_span(&self.statement)
}
}

Expand Down Expand Up @@ -607,10 +605,6 @@ fn parse_create_function<'a>(
_ => None,
};

if parser.options.dialect.is_maria() && direction.is_none() {
parser.expected_error("'IN', 'OUT' or 'INOUT'");
}

let name = parser.consume_plain_identifier()?;
let type_ = parse_data_type(parser, false)?;
params.push((direction, name, type_));
Expand Down Expand Up @@ -638,78 +632,12 @@ fn parse_create_function<'a>(
}
}

let mut characteristics = Vec::new();
loop {
let f = match &parser.token {
Token::Ident(_, Keyword::LANGUAGE) => {
let lg = parser.consume();
match &parser.token {
Token::Ident(_, Keyword::SQL) => {
FunctionCharacteristic::LanguageSql(lg.join_span(&parser.consume()))
}
Token::Ident(_, Keyword::PLPGSQL) => {
FunctionCharacteristic::LanguagePlpgsql(lg.join_span(&parser.consume()))
}
_ => parser.expected_failure("language name")?,
}
}
Token::Ident(_, Keyword::NOT) => FunctionCharacteristic::NotDeterministic(
parser.consume_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC])?,
),
Token::Ident(_, Keyword::DETERMINISTIC) => FunctionCharacteristic::Deterministic(
parser.consume_keyword(Keyword::DETERMINISTIC)?,
),
Token::Ident(_, Keyword::CONTAINS) => FunctionCharacteristic::ContainsSql(
parser.consume_keywords(&[Keyword::CONTAINS, Keyword::SQL])?,
),
Token::Ident(_, Keyword::NO) => FunctionCharacteristic::NoSql(
parser.consume_keywords(&[Keyword::NO, Keyword::SQL])?,
),
Token::Ident(_, Keyword::READS) => {
FunctionCharacteristic::ReadsSqlData(parser.consume_keywords(&[
Keyword::READS,
Keyword::SQL,
Keyword::DATA,
])?)
}
Token::Ident(_, Keyword::MODIFIES) => {
FunctionCharacteristic::ModifiesSqlData(parser.consume_keywords(&[
Keyword::MODIFIES,
Keyword::SQL,
Keyword::DATA,
])?)
}
Token::Ident(_, Keyword::COMMENT) => {
parser.consume_keyword(Keyword::COMMENT)?;
FunctionCharacteristic::Comment(parser.consume_string()?)
}
Token::Ident(_, Keyword::SQL) => {
let span = parser.consume_keywords(&[Keyword::SQL, Keyword::SECURITY])?;
match &parser.token {
Token::Ident(_, Keyword::DEFINER) => {
FunctionCharacteristic::SqlSecurityDefiner(
parser.consume_keyword(Keyword::DEFINER)?.join_span(&span),
)
}
Token::Ident(_, Keyword::USER) => FunctionCharacteristic::SqlSecurityUser(
parser.consume_keyword(Keyword::USER)?.join_span(&span),
),
_ => parser.expected_failure("'DEFINER' or 'USER'")?,
}
}
_ => break,
};
characteristics.push(f);
}

let return_ = if parser.options.dialect.is_maria() {
match parse_statement(parser)? {
Some(v) => Some(Box::new(v)),
None => parser.expected_failure("statement")?,
}
} else {
None
let old = core::mem::replace(&mut parser.permit_compound_statements, true);
let statement = match parse_statement(parser)? {
Some(v) => v,
None => parser.expected_failure("statement")?,
};
parser.permit_compound_statements = old;

Ok(Statement::CreateFunction(CreateFunction {
create_span,
Expand All @@ -718,10 +646,9 @@ fn parse_create_function<'a>(
if_not_exists,
name,
params,
return_type,
characteristics,
return_,
returns_span,
return_type,
statement: Box::new(statement),
}))
}

Expand Down
2 changes: 1 addition & 1 deletion src/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
parser::{ParseError, Parser},
qualified_name::parse_qualified_name,
select::{parse_select_expr, parse_table_reference},
Issue, QualifiedName, SelectExpr, Span, Spanned, TableReference,
QualifiedName, SelectExpr, Span, Spanned, TableReference,
};

/// Flags for deletion
Expand Down
2 changes: 1 addition & 1 deletion src/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
lexer::Token,
parser::{ParseError, Parser},
qualified_name::parse_qualified_name,
Identifier, Issue, QualifiedName, Span, Spanned, Statement,
Identifier, QualifiedName, Span, Spanned, Statement,
};

/// Represent a drop table statement
Expand Down
5 changes: 3 additions & 2 deletions src/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use alloc::{string::String, vec::Vec};

use crate::{SString, Span, Spanned};
use crate::{Span, Spanned};

/// Level of an issues
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
Expand Down Expand Up @@ -66,7 +66,8 @@ impl<'a> Issue<'a> {
span: &impl Spanned,
sql_segment: &'a str,
) -> Self {
self.fragments.push((message.into(), span.span(),sql_segment));
self.fragments
.push((message.into(), span.span(), sql_segment));
self
}
}
2 changes: 1 addition & 1 deletion src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ impl<'a> Lexer<'a> {
'`' => {
while matches!(
self.chars.peek(),
Some((_, '_' | 'a'..='z' | 'A'..='Z' | '0'..='9' | '-'))
Some((_, '_' | 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '%'))
) {
self.chars.next();
}
Expand Down
23 changes: 23 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,26 @@ pub fn parse_use_index() {
let _result = parse_statement(sql, &mut issues, &options);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}

#[test]
pub fn parse_create_function_sql_with_schema() {
let sql = "
DELIMITER $$
CREATE DEFINER=`root`@`%` FUNCTION `name_for_id`(id INT(11))
RETURNS VARCHAR(20)
COMMENT ''
BEGIN
RETURN(SELECT name FROM student tb WHERE tb.id = id);
END
$$
DELIMITER ;";
let options = ParseOptions::new()
.dialect(SQLDialect::MariaDB)
.arguments(SQLArguments::QuestionMark)
.warn_unquoted_identifiers(false);

let mut issues = Vec::new();
let _result = parse_statements(sql, &mut issues, &options);
// assert!(result.len() <= 0, "result: {:#?}", &result);
assert!(issues.is_empty(), "Issues: {:#?}", issues);
}
2 changes: 1 addition & 1 deletion src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use alloc::{boxed::Box, vec::Vec};

use crate::qualified_name::parse_qualified_name;
use crate::QualifiedName;
use crate::{
expression::{parse_expression, Expression},
keywords::Keyword,
Expand All @@ -21,7 +22,6 @@ use crate::{
statement::parse_compound_query,
Identifier, Span, Spanned, Statement,
};
use crate::{Issue, QualifiedName};

/// Value in select
#[derive(Debug, Clone)]
Expand Down
68 changes: 68 additions & 0 deletions src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ pub enum Statement<'a> {
TruncateTable(TruncateTable<'a>),
RenameTable(RenameTable<'a>),
WithQuery(WithQuery<'a>),
Return(Return<'a>),
}

impl<'a> Spanned for Statement<'a> {
Expand Down Expand Up @@ -318,6 +319,7 @@ impl<'a> Spanned for Statement<'a> {
Statement::TruncateTable(v) => v.span(),
Statement::RenameTable(v) => v.span(),
Statement::WithQuery(v) => v.span(),
Statement::Return(v) => v.span(),
}
}
}
Expand Down Expand Up @@ -366,6 +368,7 @@ pub(crate) fn parse_statement<'a>(
Some(Statement::RenameTable(parse_rename_table(parser)?))
}
Token::Ident(_, Keyword::WITH) => Some(Statement::WithQuery(parse_with_query(parser)?)),
Token::Ident(_, Keyword::RETURN) => Some(Statement::Return(parse_return(parser)?)),
_ => None,
})
}
Expand Down Expand Up @@ -742,3 +745,68 @@ pub(crate) fn parse_statements<'a>(parser: &mut Parser<'a, '_>) -> Vec<Statement
}
}
}

#[derive(Clone, Debug)]
pub enum ReturnExpression<'a> {
Expression(Box<Expression<'a>>),
Select(Box<Select<'a>>),
}

impl<'a> Spanned for ReturnExpression<'a> {
fn span(&self) -> Span {
match &self {
ReturnExpression::Expression(v) => v.span(),
ReturnExpression::Select(v) => v.span(),
}
}
}

#[derive(Clone, Debug)]
pub struct Return<'a> {
pub return_span: Span,
pub statement: Option<ReturnExpression<'a>>,
}

impl<'a> Spanned for Return<'a> {
fn span(&self) -> Span {
match &self.statement {
Some(v) => v.join_span(&self.return_span),
None => self.return_span.span(),
}
}
}

fn parse_return<'a>(parser: &mut Parser<'a, '_>) -> Result<Return<'a>, ParseError> {
let return_span = parser.consume_keyword(Keyword::RETURN)?;
loop {
if Token::LParen == parser.token {
let _ = parser.skip_token(Token::LParen);
} else {
break;
}
}
let statement = match &parser.token {
Token::Ident(_, Keyword::SELECT) => {
Some(ReturnExpression::Select(Box::new(parse_select(parser)?)))
}
Token::Eof => None,
_ => Some(ReturnExpression::Expression(Box::new(parse_expression(
parser, false,
)?))),
};
loop {
match &parser.token {
Token::RParen => {
parser.skip_token(Token::RParen);
}
// Token::SemiColon => {
// parser.skip_token(Token::SemiColon);
// }
_ => break,
}
}
Ok(Return {
return_span,
statement,
})
}
2 changes: 1 addition & 1 deletion src/with_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
lexer::Token,
parser::{ParseError, Parser},
statement::parse_statement,
Identifier, Issue, Span, Spanned, Statement,
Identifier, Span, Spanned, Statement,
};

#[derive(Clone, Debug)]
Expand Down
Loading