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