1use crate::{
13 DataType, Expression, Identifier, SString, Span, Spanned, Statement,
14 create_option::CreateOption,
15 data_type::{DataTypeContext, parse_data_type},
16 expression::{PRIORITY_MAX, parse_expression_unreserved},
17 keywords::Keyword,
18 lexer::Token,
19 parser::{ParseError, Parser},
20 statement::parse_statement,
21};
22use alloc::vec::Vec;
23
24#[derive(Clone, Debug)]
26pub enum FunctionLanguage<'a> {
27 Sql(Span),
28 Plpgsql(Span),
29 Other(Identifier<'a>),
30}
31
32impl<'a> Spanned for FunctionLanguage<'a> {
33 fn span(&self) -> Span {
34 match &self {
35 FunctionLanguage::Sql(v) => v.span(),
36 FunctionLanguage::Plpgsql(v) => v.span(),
37 FunctionLanguage::Other(v) => v.span(),
38 }
39 }
40}
41
42#[derive(Clone, Debug)]
44pub enum FunctionParallel {
45 Safe(Span),
46 Unsafe(Span),
47 Restricted(Span),
48}
49
50impl Spanned for FunctionParallel {
51 fn span(&self) -> Span {
52 match self {
53 FunctionParallel::Safe(s) => s.clone(),
54 FunctionParallel::Unsafe(s) => s.clone(),
55 FunctionParallel::Restricted(s) => s.clone(),
56 }
57 }
58}
59
60#[derive(Clone, Debug)]
62pub enum FunctionCharacteristic<'a> {
63 Language(Span, FunctionLanguage<'a>),
64 Immutable(Span),
65 Stable(Span),
66 Volatile(Span),
67 Strict(Span),
68 CalledOnNullInput(Span),
69 ReturnsNullOnNullInput(Span),
70 Parallel(Span, FunctionParallel),
71 NotDeterministic(Span),
72 Deterministic(Span),
73 ContainsSql(Span),
74 NoSql(Span),
75 ReadsSqlData(Span),
76 ModifiesSqlData(Span),
77 SqlSecurityDefiner(Span),
78 SqlSecurityUser(Span),
79 Comment(SString<'a>),
80}
81
82impl<'a> Spanned for FunctionCharacteristic<'a> {
83 fn span(&self) -> Span {
84 match &self {
85 FunctionCharacteristic::Language(s, v) => s.join_span(v),
86 FunctionCharacteristic::NotDeterministic(v) => v.span(),
87 FunctionCharacteristic::Deterministic(v) => v.span(),
88 FunctionCharacteristic::ContainsSql(v) => v.span(),
89 FunctionCharacteristic::NoSql(v) => v.span(),
90 FunctionCharacteristic::ReadsSqlData(v) => v.span(),
91 FunctionCharacteristic::ModifiesSqlData(v) => v.span(),
92 FunctionCharacteristic::SqlSecurityDefiner(v) => v.span(),
93 FunctionCharacteristic::SqlSecurityUser(v) => v.span(),
94 FunctionCharacteristic::Comment(v) => v.span(),
95 FunctionCharacteristic::Immutable(v) => v.span(),
96 FunctionCharacteristic::Stable(v) => v.span(),
97 FunctionCharacteristic::Volatile(v) => v.span(),
98 FunctionCharacteristic::Strict(v) => v.span(),
99 FunctionCharacteristic::CalledOnNullInput(v) => v.span(),
100 FunctionCharacteristic::ReturnsNullOnNullInput(v) => v.span(),
101 FunctionCharacteristic::Parallel(s, v) => s.join_span(v),
102 }
103 }
104}
105
106#[derive(Clone, Debug)]
108pub enum FunctionParamDirection {
109 In(Span),
110 Out(Span),
111 InOut(Span),
112}
113
114impl Spanned for FunctionParamDirection {
115 fn span(&self) -> Span {
116 match &self {
117 FunctionParamDirection::In(v) => v.span(),
118 FunctionParamDirection::Out(v) => v.span(),
119 FunctionParamDirection::InOut(v) => v.span(),
120 }
121 }
122}
123
124#[derive(Clone, Debug)]
126pub struct FunctionParam<'a> {
127 pub direction: Option<FunctionParamDirection>,
129 pub name: Option<Identifier<'a>>,
131 pub type_: DataType<'a>,
133 pub default: Option<(Span, Expression<'a>)>,
135}
136
137impl<'a> Spanned for FunctionParam<'a> {
138 fn span(&self) -> Span {
139 self.type_
140 .join_span(&self.direction)
141 .join_span(&self.name)
142 .join_span(&self.default.as_ref().map(|(s, e)| s.join_span(e)))
143 }
144}
145
146#[derive(Clone, Debug)]
148pub struct FunctionBody<'a> {
149 pub as_span: Span,
151 pub strings: Vec<SString<'a>>,
153}
154
155impl<'a> Spanned for FunctionBody<'a> {
156 fn span(&self) -> Span {
157 self.as_span.join_span(&self.strings)
158 }
159}
160
161#[derive(Clone, Debug)]
191pub struct CreateFunction<'a> {
192 pub create_span: Span,
194 pub create_options: Vec<CreateOption<'a>>,
196 pub function_span: Span,
198 pub if_not_exists: Option<Span>,
200 pub name: Identifier<'a>,
202 pub params: Vec<FunctionParam<'a>>,
204 pub returns_span: Span,
206 pub return_type: DataType<'a>,
208 pub characteristics: Vec<FunctionCharacteristic<'a>>,
210 pub body: Option<FunctionBody<'a>>,
212 pub return_: Option<Statement<'a>>,
214}
215
216impl<'a> Spanned for CreateFunction<'a> {
217 fn span(&self) -> Span {
218 self.create_span
219 .join_span(&self.create_options)
220 .join_span(&self.function_span)
221 .join_span(&self.if_not_exists)
222 .join_span(&self.name)
223 .join_span(&self.params)
224 .join_span(&self.returns_span)
225 .join_span(&self.return_type)
226 .join_span(&self.characteristics)
227 .join_span(&self.body)
228 .join_span(&self.return_)
229 }
230}
231
232pub(crate) fn parse_create_function<'a>(
233 parser: &mut Parser<'a, '_>,
234 create_span: Span,
235 create_options: Vec<CreateOption<'a>>,
236) -> Result<CreateFunction<'a>, ParseError> {
237 let function_span = parser.consume_keyword(Keyword::FUNCTION)?;
238
239 let if_not_exists = if let Some(if_) = parser.skip_keyword(Keyword::IF) {
240 Some(
241 parser
242 .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
243 .join_span(&if_),
244 )
245 } else {
246 None
247 };
248
249 let name = parser.consume_plain_identifier_unreserved()?;
250 let mut params = Vec::new();
251 parser.consume_token(Token::LParen)?;
252 parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
253 loop {
254 let direction = match &parser.token {
255 Token::Ident(_, Keyword::IN) => {
256 let in_ = parser.consume_keyword(Keyword::IN)?;
257 if let Some(out) = parser.skip_keyword(Keyword::OUT) {
258 Some(FunctionParamDirection::InOut(in_.join_span(&out)))
259 } else {
260 Some(FunctionParamDirection::In(in_))
261 }
262 }
263 Token::Ident(_, Keyword::OUT) => Some(FunctionParamDirection::Out(
264 parser.consume_keyword(Keyword::OUT)?,
265 )),
266 Token::Ident(_, Keyword::INOUT) => Some(FunctionParamDirection::InOut(
267 parser.consume_keyword(Keyword::INOUT)?,
268 )),
269 _ => None,
270 };
271
272 let name = if parser.options.dialect.is_postgresql() {
273 let is_unnamed = matches!(
277 parser.peek(),
278 Token::Comma
279 | Token::RParen
280 | Token::LParen
281 | Token::Eq
282 | Token::Ident(_, Keyword::DEFAULT)
283 | Token::LBracket
284 );
285 if is_unnamed {
286 None
287 } else {
288 Some(parser.consume_plain_identifier_unreserved()?)
289 }
290 } else {
291 Some(parser.consume_plain_identifier_unreserved()?)
292 };
293 let type_ = parse_data_type(parser, DataTypeContext::FunctionParam)?;
294 let default = if let Some(eq_span) = parser.skip_token(Token::Eq) {
296 Some((eq_span, parse_expression_unreserved(parser, PRIORITY_MAX)?))
297 } else if let Some(default_span) = parser.skip_keyword(Keyword::DEFAULT) {
298 Some((
299 default_span,
300 parse_expression_unreserved(parser, PRIORITY_MAX)?,
301 ))
302 } else {
303 None
304 };
305 params.push(FunctionParam {
306 direction,
307 name,
308 type_,
309 default,
310 });
311 if parser.skip_token(Token::Comma).is_none() {
312 break;
313 }
314 }
315 Ok(())
316 })?;
317 parser.consume_token(Token::RParen)?;
318 let returns_span = parser.consume_keyword(Keyword::RETURNS)?;
319 let return_type = parse_data_type(parser, DataTypeContext::FunctionReturn)?;
320 let mut body: Option<FunctionBody<'_>> = None;
321 let mut characteristics = Vec::new();
322 loop {
323 let f = match &parser.token {
324 Token::Ident(_, Keyword::LANGUAGE) => {
325 let lg = parser.consume();
326 match &parser.token {
327 Token::Ident(_, Keyword::SQL) => FunctionCharacteristic::Language(
328 lg,
329 FunctionLanguage::Sql(parser.consume()),
330 ),
331 Token::Ident(_, Keyword::PLPGSQL) => FunctionCharacteristic::Language(
332 lg,
333 FunctionLanguage::Plpgsql(parser.consume()),
334 ),
335 Token::Ident(_, _) if parser.options.dialect.is_postgresql() => {
336 FunctionCharacteristic::Language(
337 lg,
338 FunctionLanguage::Other(parser.consume_plain_identifier_unreserved()?),
339 )
340 }
341 _ => parser.expected_failure("language name")?,
342 }
343 }
344 Token::Ident(_, Keyword::NOT) => FunctionCharacteristic::NotDeterministic(
345 parser.consume_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC])?,
346 ),
347 Token::Ident(_, Keyword::DETERMINISTIC) => FunctionCharacteristic::Deterministic(
348 parser.consume_keyword(Keyword::DETERMINISTIC)?,
349 ),
350 Token::Ident(_, Keyword::CONTAINS) => FunctionCharacteristic::ContainsSql(
351 parser.consume_keywords(&[Keyword::CONTAINS, Keyword::SQL])?,
352 ),
353 Token::Ident(_, Keyword::NO) => FunctionCharacteristic::NoSql(
354 parser.consume_keywords(&[Keyword::NO, Keyword::SQL])?,
355 ),
356 Token::Ident(_, Keyword::READS) => {
357 FunctionCharacteristic::ReadsSqlData(parser.consume_keywords(&[
358 Keyword::READS,
359 Keyword::SQL,
360 Keyword::DATA,
361 ])?)
362 }
363 Token::Ident(_, Keyword::MODIFIES) => {
364 FunctionCharacteristic::ModifiesSqlData(parser.consume_keywords(&[
365 Keyword::MODIFIES,
366 Keyword::SQL,
367 Keyword::DATA,
368 ])?)
369 }
370 Token::Ident(_, Keyword::COMMENT) => {
371 parser.consume_keyword(Keyword::COMMENT)?;
372 FunctionCharacteristic::Comment(parser.consume_string()?)
373 }
374 Token::Ident(_, Keyword::SQL) => {
375 let span = parser.consume_keywords(&[Keyword::SQL, Keyword::SECURITY])?;
376 match &parser.token {
377 Token::Ident(_, Keyword::DEFINER) => {
378 FunctionCharacteristic::SqlSecurityDefiner(
379 parser.consume_keyword(Keyword::DEFINER)?.join_span(&span),
380 )
381 }
382 Token::Ident(_, Keyword::USER) => FunctionCharacteristic::SqlSecurityUser(
383 parser.consume_keyword(Keyword::USER)?.join_span(&span),
384 ),
385 _ => parser.expected_failure("'DEFINER' or 'USER'")?,
386 }
387 }
388 Token::Ident(_, Keyword::IMMUTABLE) => {
389 FunctionCharacteristic::Immutable(parser.consume_keyword(Keyword::IMMUTABLE)?)
390 }
391 Token::Ident(_, Keyword::STABLE) => {
392 FunctionCharacteristic::Stable(parser.consume_keyword(Keyword::STABLE)?)
393 }
394 Token::Ident(_, Keyword::VOLATILE) => {
395 FunctionCharacteristic::Volatile(parser.consume_keyword(Keyword::VOLATILE)?)
396 }
397 Token::Ident(_, Keyword::STRICT) => {
398 FunctionCharacteristic::Strict(parser.consume_keyword(Keyword::STRICT)?)
399 }
400 Token::Ident(_, Keyword::CALLED) if parser.options.dialect.is_postgresql() => {
401 FunctionCharacteristic::CalledOnNullInput(parser.consume_keywords(&[
402 Keyword::CALLED,
403 Keyword::ON,
404 Keyword::NULL,
405 Keyword::INPUT,
406 ])?)
407 }
408 Token::Ident(_, Keyword::RETURNS) if parser.options.dialect.is_postgresql() => {
409 FunctionCharacteristic::ReturnsNullOnNullInput(parser.consume_keywords(&[
410 Keyword::RETURNS,
411 Keyword::NULL,
412 Keyword::ON,
413 Keyword::NULL,
414 Keyword::INPUT,
415 ])?)
416 }
417 Token::Ident(_, Keyword::PARALLEL) => {
418 let parallel_span = parser.consume_keyword(Keyword::PARALLEL)?;
419 let level = match parser.consume_plain_identifier_unreserved()?.value {
420 v if v.eq_ignore_ascii_case("safe") => {
421 FunctionParallel::Safe(parallel_span.clone())
422 }
423 v if v.eq_ignore_ascii_case("unsafe") => {
424 FunctionParallel::Unsafe(parallel_span.clone())
425 }
426 v if v.eq_ignore_ascii_case("restricted") => {
427 FunctionParallel::Restricted(parallel_span.clone())
428 }
429 _ => {
430 parser.expected_error("SAFE, UNSAFE, or RESTRICTED");
431 FunctionParallel::Unsafe(parallel_span.clone())
432 }
433 };
434 FunctionCharacteristic::Parallel(parallel_span, level)
435 }
436 Token::Ident(_, Keyword::AS) if parser.options.dialect.is_postgresql() => {
437 let as_span = parser.consume_keyword(Keyword::AS)?;
438 let mut strings = Vec::new();
439 match &parser.token {
440 Token::String(_, _) => {
441 strings.push(parser.consume_string()?);
442 while parser.skip_token(Token::Comma).is_some() {
444 if matches!(&parser.token, Token::String(_, _)) {
445 strings.push(parser.consume_string()?);
446 } else {
447 break;
448 }
449 }
450 }
451 _ => {
452 parser.expected_error("'$$' or string");
453 }
454 }
455 body = Some(FunctionBody { as_span, strings });
456 break;
457 }
458 _ => break,
459 };
460 characteristics.push(f);
461 }
462
463 if parser.options.dialect.is_postgresql()
464 && !characteristics
465 .iter()
466 .any(|c| matches!(c, FunctionCharacteristic::Language(_, _)))
467 {
468 parser.expected_failure("LANGUAGE")?;
469 }
470
471 let return_ = if parser.options.dialect.is_maria() {
472 let old = core::mem::replace(&mut parser.permit_compound_statements, true);
473 let r = match parse_statement(parser)? {
474 Some(v) => Some(v),
475 None => parser.expected_failure("statement")?,
476 };
477 parser.permit_compound_statements = old;
478 r
479 } else if matches!(&parser.token, Token::Ident(_, Keyword::RETURN)) {
480 match parse_statement(parser)? {
482 Some(v) => Some(v),
483 None => parser.expected_failure("statement after RETURN")?,
484 }
485 } else {
486 None
487 };
488
489 Ok(CreateFunction {
490 create_span,
491 create_options,
492 function_span,
493 if_not_exists,
494 name,
495 params,
496 return_type,
497 characteristics,
498 body,
499 return_,
500 returns_span,
501 })
502}
503
504#[derive(Clone, Debug)]
508pub struct CreateProcedure<'a> {
509 pub create_span: Span,
511 pub create_options: Vec<CreateOption<'a>>,
513 pub procedure_span: Span,
515 pub if_not_exists: Option<Span>,
517 pub name: Identifier<'a>,
519 pub params: Vec<FunctionParam<'a>>,
521 pub characteristics: Vec<FunctionCharacteristic<'a>>,
523 pub body: Option<Statement<'a>>,
525}
526
527impl<'a> Spanned for CreateProcedure<'a> {
528 fn span(&self) -> Span {
529 self.create_span
530 .join_span(&self.create_options)
531 .join_span(&self.procedure_span)
532 .join_span(&self.if_not_exists)
533 .join_span(&self.name)
534 .join_span(&self.params)
535 .join_span(&self.characteristics)
536 .join_span(&self.body)
537 }
538}
539
540pub(crate) fn parse_create_procedure<'a>(
541 parser: &mut Parser<'a, '_>,
542 create_span: Span,
543 create_options: Vec<CreateOption<'a>>,
544) -> Result<CreateProcedure<'a>, ParseError> {
545 let procedure_span = parser.consume_keyword(Keyword::PROCEDURE)?;
546
547 let if_not_exists = if let Some(if_) = parser.skip_keyword(Keyword::IF) {
548 Some(
549 parser
550 .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
551 .join_span(&if_),
552 )
553 } else {
554 None
555 };
556
557 let name = parser.consume_plain_identifier_unreserved()?;
558 let mut params = Vec::new();
559 parser.consume_token(Token::LParen)?;
560 parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
561 loop {
562 if matches!(parser.token, Token::RParen) {
563 break;
564 }
565 let direction = match &parser.token {
566 Token::Ident(_, Keyword::IN) => {
567 let in_ = parser.consume_keyword(Keyword::IN)?;
568 if let Some(out) = parser.skip_keyword(Keyword::OUT) {
569 Some(FunctionParamDirection::InOut(in_.join_span(&out)))
570 } else {
571 Some(FunctionParamDirection::In(in_))
572 }
573 }
574 Token::Ident(_, Keyword::OUT) => Some(FunctionParamDirection::Out(
575 parser.consume_keyword(Keyword::OUT)?,
576 )),
577 Token::Ident(_, Keyword::INOUT) => Some(FunctionParamDirection::InOut(
578 parser.consume_keyword(Keyword::INOUT)?,
579 )),
580 _ => None,
581 };
582 let name = Some(parser.consume_plain_identifier_unreserved()?);
583 let type_ = parse_data_type(parser, DataTypeContext::FunctionParam)?;
584 params.push(FunctionParam {
585 direction,
586 name,
587 type_,
588 default: None,
589 });
590 if parser.skip_token(Token::Comma).is_none() {
591 break;
592 }
593 }
594 Ok(())
595 })?;
596 parser.consume_token(Token::RParen)?;
597
598 let mut characteristics = Vec::new();
599 loop {
600 let f = match &parser.token {
601 Token::Ident(_, Keyword::NOT) => FunctionCharacteristic::NotDeterministic(
602 parser.consume_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC])?,
603 ),
604 Token::Ident(_, Keyword::DETERMINISTIC) => FunctionCharacteristic::Deterministic(
605 parser.consume_keyword(Keyword::DETERMINISTIC)?,
606 ),
607 Token::Ident(_, Keyword::CONTAINS) => FunctionCharacteristic::ContainsSql(
608 parser.consume_keywords(&[Keyword::CONTAINS, Keyword::SQL])?,
609 ),
610 Token::Ident(_, Keyword::NO) => FunctionCharacteristic::NoSql(
611 parser.consume_keywords(&[Keyword::NO, Keyword::SQL])?,
612 ),
613 Token::Ident(_, Keyword::READS) => {
614 FunctionCharacteristic::ReadsSqlData(parser.consume_keywords(&[
615 Keyword::READS,
616 Keyword::SQL,
617 Keyword::DATA,
618 ])?)
619 }
620 Token::Ident(_, Keyword::MODIFIES) => {
621 FunctionCharacteristic::ModifiesSqlData(parser.consume_keywords(&[
622 Keyword::MODIFIES,
623 Keyword::SQL,
624 Keyword::DATA,
625 ])?)
626 }
627 Token::Ident(_, Keyword::COMMENT) => {
628 parser.consume_keyword(Keyword::COMMENT)?;
629 FunctionCharacteristic::Comment(parser.consume_string()?)
630 }
631 Token::Ident(_, Keyword::SQL) => {
632 let span = parser.consume_keywords(&[Keyword::SQL, Keyword::SECURITY])?;
633 match &parser.token {
634 Token::Ident(_, Keyword::DEFINER) => {
635 FunctionCharacteristic::SqlSecurityDefiner(
636 parser.consume_keyword(Keyword::DEFINER)?.join_span(&span),
637 )
638 }
639 Token::Ident(_, Keyword::USER) => FunctionCharacteristic::SqlSecurityUser(
640 parser.consume_keyword(Keyword::USER)?.join_span(&span),
641 ),
642 _ => parser.expected_failure("'DEFINER' or 'USER'")?,
643 }
644 }
645 _ => break,
646 };
647 characteristics.push(f);
648 }
649
650 let old = core::mem::replace(&mut parser.permit_compound_statements, true);
651 let body = parse_statement(parser)?;
652 parser.permit_compound_statements = old;
653
654 Ok(CreateProcedure {
655 create_span,
656 create_options,
657 procedure_span,
658 if_not_exists,
659 name,
660 params,
661 characteristics,
662 body,
663 })
664}