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 if matches!(parser.token, Token::RParen) {
255 break;
256 }
257 let direction = match &parser.token {
258 Token::Ident(_, Keyword::IN) => {
259 let in_ = parser.consume_keyword(Keyword::IN)?;
260 if let Some(out) = parser.skip_keyword(Keyword::OUT) {
261 Some(FunctionParamDirection::InOut(in_.join_span(&out)))
262 } else {
263 Some(FunctionParamDirection::In(in_))
264 }
265 }
266 Token::Ident(_, Keyword::OUT) => Some(FunctionParamDirection::Out(
267 parser.consume_keyword(Keyword::OUT)?,
268 )),
269 Token::Ident(_, Keyword::INOUT) => Some(FunctionParamDirection::InOut(
270 parser.consume_keyword(Keyword::INOUT)?,
271 )),
272 _ => None,
273 };
274
275 let name = if parser.options.dialect.is_postgresql() {
276 let is_unnamed = matches!(
280 parser.peek(),
281 Token::Comma
282 | Token::RParen
283 | Token::LParen
284 | Token::Eq
285 | Token::Ident(_, Keyword::DEFAULT)
286 | Token::LBracket
287 );
288 if is_unnamed {
289 None
290 } else {
291 Some(parser.consume_plain_identifier_unreserved()?)
292 }
293 } else {
294 Some(parser.consume_plain_identifier_unreserved()?)
295 };
296 let type_ = parse_data_type(parser, DataTypeContext::FunctionParam)?;
297 let default = if let Some(eq_span) = parser.skip_token(Token::Eq) {
299 Some((eq_span, parse_expression_unreserved(parser, PRIORITY_MAX)?))
300 } else if let Some(default_span) = parser.skip_keyword(Keyword::DEFAULT) {
301 Some((
302 default_span,
303 parse_expression_unreserved(parser, PRIORITY_MAX)?,
304 ))
305 } else {
306 None
307 };
308 params.push(FunctionParam {
309 direction,
310 name,
311 type_,
312 default,
313 });
314 if parser.skip_token(Token::Comma).is_none() {
315 break;
316 }
317 }
318 Ok(())
319 })?;
320 parser.consume_token(Token::RParen)?;
321 let returns_span = parser.consume_keyword(Keyword::RETURNS)?;
322 let return_type = parse_data_type(parser, DataTypeContext::FunctionReturn)?;
323 let mut body: Option<FunctionBody<'_>> = None;
324 let mut characteristics = Vec::new();
325 loop {
326 let f = match &parser.token {
327 Token::Ident(_, Keyword::LANGUAGE) => {
328 let lg = parser.consume();
329 match &parser.token {
330 Token::Ident(_, Keyword::SQL) => FunctionCharacteristic::Language(
331 lg,
332 FunctionLanguage::Sql(parser.consume()),
333 ),
334 Token::Ident(_, Keyword::PLPGSQL) => FunctionCharacteristic::Language(
335 lg,
336 FunctionLanguage::Plpgsql(parser.consume()),
337 ),
338 Token::Ident(_, _) if parser.options.dialect.is_postgresql() => {
339 FunctionCharacteristic::Language(
340 lg,
341 FunctionLanguage::Other(parser.consume_plain_identifier_unreserved()?),
342 )
343 }
344 _ => parser.expected_failure("language name")?,
345 }
346 }
347 Token::Ident(_, Keyword::NOT) => FunctionCharacteristic::NotDeterministic(
348 parser.consume_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC])?,
349 ),
350 Token::Ident(_, Keyword::DETERMINISTIC) => FunctionCharacteristic::Deterministic(
351 parser.consume_keyword(Keyword::DETERMINISTIC)?,
352 ),
353 Token::Ident(_, Keyword::CONTAINS) => FunctionCharacteristic::ContainsSql(
354 parser.consume_keywords(&[Keyword::CONTAINS, Keyword::SQL])?,
355 ),
356 Token::Ident(_, Keyword::NO) => FunctionCharacteristic::NoSql(
357 parser.consume_keywords(&[Keyword::NO, Keyword::SQL])?,
358 ),
359 Token::Ident(_, Keyword::READS) => {
360 FunctionCharacteristic::ReadsSqlData(parser.consume_keywords(&[
361 Keyword::READS,
362 Keyword::SQL,
363 Keyword::DATA,
364 ])?)
365 }
366 Token::Ident(_, Keyword::MODIFIES) => {
367 FunctionCharacteristic::ModifiesSqlData(parser.consume_keywords(&[
368 Keyword::MODIFIES,
369 Keyword::SQL,
370 Keyword::DATA,
371 ])?)
372 }
373 Token::Ident(_, Keyword::COMMENT) => {
374 parser.consume_keyword(Keyword::COMMENT)?;
375 FunctionCharacteristic::Comment(parser.consume_string()?)
376 }
377 Token::Ident(_, Keyword::SQL) => {
378 let span = parser.consume_keywords(&[Keyword::SQL, Keyword::SECURITY])?;
379 match &parser.token {
380 Token::Ident(_, Keyword::DEFINER) => {
381 FunctionCharacteristic::SqlSecurityDefiner(
382 parser.consume_keyword(Keyword::DEFINER)?.join_span(&span),
383 )
384 }
385 Token::Ident(_, Keyword::USER) => FunctionCharacteristic::SqlSecurityUser(
386 parser.consume_keyword(Keyword::USER)?.join_span(&span),
387 ),
388 _ => parser.expected_failure("'DEFINER' or 'USER'")?,
389 }
390 }
391 Token::Ident(_, Keyword::IMMUTABLE) => {
392 FunctionCharacteristic::Immutable(parser.consume_keyword(Keyword::IMMUTABLE)?)
393 }
394 Token::Ident(_, Keyword::STABLE) => {
395 FunctionCharacteristic::Stable(parser.consume_keyword(Keyword::STABLE)?)
396 }
397 Token::Ident(_, Keyword::VOLATILE) => {
398 FunctionCharacteristic::Volatile(parser.consume_keyword(Keyword::VOLATILE)?)
399 }
400 Token::Ident(_, Keyword::STRICT) => {
401 FunctionCharacteristic::Strict(parser.consume_keyword(Keyword::STRICT)?)
402 }
403 Token::Ident(_, Keyword::CALLED) if parser.options.dialect.is_postgresql() => {
404 FunctionCharacteristic::CalledOnNullInput(parser.consume_keywords(&[
405 Keyword::CALLED,
406 Keyword::ON,
407 Keyword::NULL,
408 Keyword::INPUT,
409 ])?)
410 }
411 Token::Ident(_, Keyword::RETURNS) if parser.options.dialect.is_postgresql() => {
412 FunctionCharacteristic::ReturnsNullOnNullInput(parser.consume_keywords(&[
413 Keyword::RETURNS,
414 Keyword::NULL,
415 Keyword::ON,
416 Keyword::NULL,
417 Keyword::INPUT,
418 ])?)
419 }
420 Token::Ident(_, Keyword::PARALLEL) => {
421 let parallel_span = parser.consume_keyword(Keyword::PARALLEL)?;
422 let level = match parser.consume_plain_identifier_unreserved()?.value {
423 v if v.eq_ignore_ascii_case("safe") => {
424 FunctionParallel::Safe(parallel_span.clone())
425 }
426 v if v.eq_ignore_ascii_case("unsafe") => {
427 FunctionParallel::Unsafe(parallel_span.clone())
428 }
429 v if v.eq_ignore_ascii_case("restricted") => {
430 FunctionParallel::Restricted(parallel_span.clone())
431 }
432 _ => {
433 parser.expected_error("SAFE, UNSAFE, or RESTRICTED");
434 FunctionParallel::Unsafe(parallel_span.clone())
435 }
436 };
437 FunctionCharacteristic::Parallel(parallel_span, level)
438 }
439 Token::Ident(_, Keyword::AS) if parser.options.dialect.is_postgresql() => {
440 let as_span = parser.consume_keyword(Keyword::AS)?;
441 let mut strings = Vec::new();
442 match &parser.token {
443 Token::String(_, _) => {
444 strings.push(parser.consume_string()?);
445 while parser.skip_token(Token::Comma).is_some() {
447 if matches!(&parser.token, Token::String(_, _)) {
448 strings.push(parser.consume_string()?);
449 } else {
450 break;
451 }
452 }
453 }
454 _ => {
455 parser.expected_error("'$$' or string");
456 }
457 }
458 body = Some(FunctionBody { as_span, strings });
459 continue;
460 }
461 _ => break,
462 };
463 characteristics.push(f);
464 }
465
466 if parser.options.dialect.is_postgresql()
467 && body.is_some()
468 && !characteristics
469 .iter()
470 .any(|c| matches!(c, FunctionCharacteristic::Language(_, _)))
471 {
472 parser.expected_failure("LANGUAGE")?;
473 }
474
475 let return_ = if parser.options.dialect.is_maria() {
476 let old = core::mem::replace(&mut parser.permit_compound_statements, true);
477 let r = match parse_statement(parser)? {
478 Some(v) => Some(v),
479 None => parser.expected_failure("statement")?,
480 };
481 parser.permit_compound_statements = old;
482 r
483 } else if matches!(&parser.token, Token::Ident(_, Keyword::RETURN)) {
484 match parse_statement(parser)? {
486 Some(v) => Some(v),
487 None => parser.expected_failure("statement after RETURN")?,
488 }
489 } else {
490 None
491 };
492
493 Ok(CreateFunction {
494 create_span,
495 create_options,
496 function_span,
497 if_not_exists,
498 name,
499 params,
500 return_type,
501 characteristics,
502 body,
503 return_,
504 returns_span,
505 })
506}
507
508#[derive(Clone, Debug)]
512pub struct CreateProcedure<'a> {
513 pub create_span: Span,
515 pub create_options: Vec<CreateOption<'a>>,
517 pub procedure_span: Span,
519 pub if_not_exists: Option<Span>,
521 pub name: Identifier<'a>,
523 pub params: Vec<FunctionParam<'a>>,
525 pub characteristics: Vec<FunctionCharacteristic<'a>>,
527 pub body: Option<Statement<'a>>,
529}
530
531impl<'a> Spanned for CreateProcedure<'a> {
532 fn span(&self) -> Span {
533 self.create_span
534 .join_span(&self.create_options)
535 .join_span(&self.procedure_span)
536 .join_span(&self.if_not_exists)
537 .join_span(&self.name)
538 .join_span(&self.params)
539 .join_span(&self.characteristics)
540 .join_span(&self.body)
541 }
542}
543
544pub(crate) fn parse_create_procedure<'a>(
545 parser: &mut Parser<'a, '_>,
546 create_span: Span,
547 create_options: Vec<CreateOption<'a>>,
548) -> Result<CreateProcedure<'a>, ParseError> {
549 let procedure_span = parser.consume_keyword(Keyword::PROCEDURE)?;
550
551 let if_not_exists = if let Some(if_) = parser.skip_keyword(Keyword::IF) {
552 Some(
553 parser
554 .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
555 .join_span(&if_),
556 )
557 } else {
558 None
559 };
560
561 let name = parser.consume_plain_identifier_unreserved()?;
562 let mut params = Vec::new();
563 parser.consume_token(Token::LParen)?;
564 parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
565 loop {
566 if matches!(parser.token, Token::RParen) {
567 break;
568 }
569 let direction = match &parser.token {
570 Token::Ident(_, Keyword::IN) => {
571 let in_ = parser.consume_keyword(Keyword::IN)?;
572 if let Some(out) = parser.skip_keyword(Keyword::OUT) {
573 Some(FunctionParamDirection::InOut(in_.join_span(&out)))
574 } else {
575 Some(FunctionParamDirection::In(in_))
576 }
577 }
578 Token::Ident(_, Keyword::OUT) => Some(FunctionParamDirection::Out(
579 parser.consume_keyword(Keyword::OUT)?,
580 )),
581 Token::Ident(_, Keyword::INOUT) => Some(FunctionParamDirection::InOut(
582 parser.consume_keyword(Keyword::INOUT)?,
583 )),
584 _ => None,
585 };
586 let name = Some(parser.consume_plain_identifier_unreserved()?);
587 let type_ = parse_data_type(parser, DataTypeContext::FunctionParam)?;
588 params.push(FunctionParam {
589 direction,
590 name,
591 type_,
592 default: None,
593 });
594 if parser.skip_token(Token::Comma).is_none() {
595 break;
596 }
597 }
598 Ok(())
599 })?;
600 parser.consume_token(Token::RParen)?;
601
602 let mut characteristics = Vec::new();
603 loop {
604 let f = match &parser.token {
605 Token::Ident(_, Keyword::NOT) => FunctionCharacteristic::NotDeterministic(
606 parser.consume_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC])?,
607 ),
608 Token::Ident(_, Keyword::DETERMINISTIC) => FunctionCharacteristic::Deterministic(
609 parser.consume_keyword(Keyword::DETERMINISTIC)?,
610 ),
611 Token::Ident(_, Keyword::CONTAINS) => FunctionCharacteristic::ContainsSql(
612 parser.consume_keywords(&[Keyword::CONTAINS, Keyword::SQL])?,
613 ),
614 Token::Ident(_, Keyword::NO) => FunctionCharacteristic::NoSql(
615 parser.consume_keywords(&[Keyword::NO, Keyword::SQL])?,
616 ),
617 Token::Ident(_, Keyword::READS) => {
618 FunctionCharacteristic::ReadsSqlData(parser.consume_keywords(&[
619 Keyword::READS,
620 Keyword::SQL,
621 Keyword::DATA,
622 ])?)
623 }
624 Token::Ident(_, Keyword::MODIFIES) => {
625 FunctionCharacteristic::ModifiesSqlData(parser.consume_keywords(&[
626 Keyword::MODIFIES,
627 Keyword::SQL,
628 Keyword::DATA,
629 ])?)
630 }
631 Token::Ident(_, Keyword::COMMENT) => {
632 parser.consume_keyword(Keyword::COMMENT)?;
633 FunctionCharacteristic::Comment(parser.consume_string()?)
634 }
635 Token::Ident(_, Keyword::SQL) => {
636 let span = parser.consume_keywords(&[Keyword::SQL, Keyword::SECURITY])?;
637 match &parser.token {
638 Token::Ident(_, Keyword::DEFINER) => {
639 FunctionCharacteristic::SqlSecurityDefiner(
640 parser.consume_keyword(Keyword::DEFINER)?.join_span(&span),
641 )
642 }
643 Token::Ident(_, Keyword::USER) => FunctionCharacteristic::SqlSecurityUser(
644 parser.consume_keyword(Keyword::USER)?.join_span(&span),
645 ),
646 _ => parser.expected_failure("'DEFINER' or 'USER'")?,
647 }
648 }
649 _ => break,
650 };
651 characteristics.push(f);
652 }
653
654 let old = core::mem::replace(&mut parser.permit_compound_statements, true);
655 let body = parse_statement(parser)?;
656 parser.permit_compound_statements = old;
657
658 Ok(CreateProcedure {
659 create_span,
660 create_options,
661 procedure_span,
662 if_not_exists,
663 name,
664 params,
665 characteristics,
666 body,
667 })
668}