1use rupg_catalog::{
6 Catalog, Function, FunctionBody, ObjRef, PG_PROC, PG_TYPE, eliminate_duplicates,
7};
8use rupg_common::{Error, Result, SqlState};
9use rupg_pgcatalog::builtin;
10use rupg_sql::nodes::{
11 CreateFunctionStmt, DefElem, FunctionParameterMode, Node, VariableSetKind, VariableSetStmt,
12};
13use rupg_types::oid;
14
15use super::{Definer, not_yet};
16use crate::agg::Kind;
17use crate::coerce::{AtOpt, Context};
18use crate::expr::Expr;
19use crate::select::Query;
20use crate::typename::{names, place};
21use crate::{Analyzer, Env, Params, node, poly, types};
22
23const SQL_LANGUAGE: u32 = 14;
25const PLPGSQL_LANGUAGE: u32 = 13708;
27const INTERNAL_LANGUAGE: u32 = 12;
29const C_LANGUAGE: u32 = 13;
31const DEFAULT_COST: f32 = 100.0;
33const DLSUFFIX: &str = ".so";
35const REGRESS_LIBRARY: &str = "regress";
37
38pub const REGRESS_SYMBOLS: &[&str] = &[
40 "interpt_pp",
41 "overpaid",
42 "widget_in",
43 "widget_out",
44 "pt_in_widget",
45 "reverse_name",
46 "trigger_return_old",
47 "int44in",
48 "int44out",
49 "test_canonicalize_path",
50 "make_tuple_indirect",
51 "get_environ",
52 "regress_setenv",
53 "wait_pid",
54 "test_atomic_ops",
55 "test_fdw_handler",
56 "test_fdw_connection",
57 "is_catalog_text_unique_index_oid",
58 "test_support_func",
59 "test_inline_in_from_support_func",
60 "test_opclass_options_func",
61 "test_enc_setup",
62 "test_enc_conversion",
63 "test_bytea_to_text",
64 "test_text_to_bytea",
65 "test_mblen_func",
66 "test_text_to_wchars",
67 "test_wchars_to_text",
68 "test_valid_server_encoding",
69 "binary_coercible",
70 "test_relpath",
71 "test_translation",
72 "test_instr_time",
73 "test_pglz_compress",
74 "test_pglz_decompress",
75 "test_pg_locale_apis",
76];
77
78pub fn find_library(name: &str) -> Result<String> {
84 let file = name.rsplit_once('/').map(|(_, file)| file);
85 let stem = file.map(|file| file.strip_suffix(DLSUFFIX).unwrap_or(file));
86 if stem != Some(REGRESS_LIBRARY) {
87 return Err(Error::new(
88 SqlState::UNDEFINED_FILE,
89 format!("could not access file \"{name}\": No such file or directory"),
90 ));
91 }
92 Ok(if name.ends_with(DLSUFFIX) { name.to_string() } else { format!("{name}{DLSUFFIX}") })
93}
94const DEFAULT_ROWS: u32 = 1000;
96
97#[derive(Debug)]
99pub enum Body {
100 Value(Expr),
102 Query(Box<Query>),
104 Statements(Vec<u32>),
106}
107
108struct Options {
110 language: u32,
112 language_name: String,
114 strict: bool,
115 leakproof: bool,
116 volatile: u8,
117 parallel: u8,
118 cost: Option<f32>,
119 rows: Option<u32>,
120 support: Option<Vec<String>>,
121 src: Option<String>,
123 link: Option<String>,
125 src_at: Option<usize>,
127 config: Vec<String>,
129 secdef: bool,
131}
132
133pub(super) fn update_config(
135 env: &dyn Env,
136 config: &mut Vec<String>,
137 set: &VariableSetStmt,
138) -> Result<()> {
139 if set.kind == VariableSetKind::VAR_RESET_ALL {
140 config.clear();
141 return Ok(());
142 }
143 let (name, value) = env.function_setting(set)?;
144 let prefix = format!("{name}=");
145 let at = config.iter().position(|item| item.starts_with(&prefix));
146 match (value, at) {
147 (Some(value), Some(at)) => config[at] = format!("{prefix}{value}"),
148 (Some(value), None) => config.push(format!("{prefix}{value}")),
149 (None, Some(at)) => {
150 config.remove(at);
151 }
152 (None, None) => {}
153 }
154 Ok(())
155}
156
157fn sets_path(config: &[String]) -> bool {
159 config.iter().any(|item| item.starts_with("search_path="))
160}
161
162pub(super) fn only_superuser_leakproof() -> Error {
164 Error::new(SqlState::INSUFFICIENT_PRIVILEGE, "only superuser can define a leakproof function")
165}
166
167fn option_text(def: &DefElem) -> Option<&str> {
169 match &def.arg {
170 Some(Node::String(s)) => Some(s),
171 _ => None,
172 }
173}
174
175fn option_list(def: &DefElem) -> Option<Vec<String>> {
177 match &def.arg {
178 Some(Node::List(list)) => list
179 .iter()
180 .map(|n| match n {
181 Some(Node::String(s)) => Some(s.to_string()),
182 _ => None,
183 })
184 .collect(),
185 _ => None,
186 }
187}
188
189fn options(stmt: &CreateFunctionStmt, env: &dyn Env) -> Result<Options> {
191 let mut out = Options {
192 language: SQL_LANGUAGE,
193 language_name: "sql".to_string(),
194 strict: false,
195 leakproof: false,
196 volatile: b'v',
197 parallel: b'u',
198 cost: None,
199 rows: None,
200 support: None,
201 src: None,
202 link: None,
203 src_at: None,
204 config: Vec::new(),
205 secdef: false,
206 };
207 for def in stmt.options.iter().flatten() {
208 let Node::DefElem(def) = def else { continue };
209 let name = def.defname.as_deref().unwrap_or_default();
210 let at = place(def.location);
211 let bad = || not_yet(&format!("the option {name} of a function"), at);
212 let attribute = matches!(
213 name,
214 "volatility"
215 | "strict"
216 | "leakproof"
217 | "cost"
218 | "rows"
219 | "support"
220 | "parallel"
221 | "window"
222 );
223 if stmt.is_procedure && attribute {
224 return Err(Error::new(
225 SqlState::INVALID_FUNCTION_DEFINITION,
226 "invalid attribute in procedure definition",
227 )
228 .at_opt(at));
229 }
230 match (name, option_text(def)) {
231 ("language", Some(language)) => {
232 out.language = match language {
233 "sql" => SQL_LANGUAGE,
234 "plpgsql" => PLPGSQL_LANGUAGE,
235 "internal" => INTERNAL_LANGUAGE,
236 "c" => C_LANGUAGE,
237 _ => {
238 return Err(not_yet(&format!("a function in the language {language}"), at));
239 }
240 };
241 out.language_name = language.to_string();
242 }
243 ("strict", _) => out.strict = matches!(def.arg, Some(Node::Boolean(true))),
244 ("leakproof", _) => out.leakproof = matches!(def.arg, Some(Node::Boolean(true))),
245 ("volatility", Some("immutable")) => out.volatile = b'i',
246 ("volatility", Some("stable")) => out.volatile = b's',
247 ("volatility", Some("volatile")) => out.volatile = b'v',
248 ("parallel", Some("safe")) => out.parallel = b's',
249 ("parallel", Some("restricted")) => out.parallel = b'r',
250 ("parallel", Some("unsafe")) => out.parallel = b'u',
251 ("cost", _) => out.cost = Some(super::alter_function::positive_cost(def)?),
252 ("rows", _) => match def.arg {
253 Some(Node::Integer(rows)) if rows > 0 => out.rows = Some(rows.unsigned_abs()),
254 _ => return Err(bad()),
255 },
256 ("support", _) => out.support = Some(option_list(def).ok_or_else(bad)?),
257 ("set", _) => match &def.arg {
258 Some(Node::VariableSetStmt(set)) => update_config(env, &mut out.config, set)?,
259 _ => return Err(bad()),
260 },
261 ("security", _) => out.secdef = matches!(def.arg, Some(Node::Boolean(true))),
262 ("as", _) => match option_list(def).as_deref() {
263 Some([src, rest @ ..]) if rest.len() < 2 => {
265 out.src = Some(src.clone());
266 out.link = rest.first().cloned();
267 out.src_at = at;
268 }
269 _ => return Err(bad()),
270 },
271 _ => return Err(bad()),
272 }
273 }
274 Ok(out)
275}
276
277fn interpret_as(
279 stmt: &CreateFunctionStmt,
280 options: &Options,
281 name: &str,
282) -> Result<(String, String)> {
283 let bad = |message: String| Err(Error::new(SqlState::INVALID_FUNCTION_DEFINITION, message));
284 if matches!(options.language, INTERNAL_LANGUAGE | C_LANGUAGE) {
285 match (&options.src, &stmt.sql_body) {
286 (None, None) => return bad("no function body specified".into()),
287 (Some(_), Some(_)) => return bad("duplicate function body specified".into()),
288 (None, Some(_)) => {
289 return bad("inline SQL function body only valid for language SQL".into());
290 }
291 (Some(_), None) => {}
292 }
293 }
294 let src = options.src.clone().unwrap_or_default();
295 if options.language == C_LANGUAGE {
296 let link = options.link.as_deref().filter(|link| *link != "-").unwrap_or(name);
297 return Ok((link.to_string(), src));
298 }
299 if options.link.is_some() {
300 return bad(format!("only one AS item needed for language \"{}\"", options.language_name));
301 }
302 if options.language == INTERNAL_LANGUAGE && src.is_empty() {
303 return Ok((name.to_string(), String::new()));
304 }
305 Ok((src, String::new()))
306}
307
308fn check_untrusted(function: &Function) -> Result<()> {
310 if function.lang == INTERNAL_LANGUAGE {
311 let found =
312 builtin::procs().iter().any(|p| p.lang == INTERNAL_LANGUAGE && p.src == function.src);
313 if !found {
314 return Err(Error::new(
315 SqlState::UNDEFINED_FUNCTION,
316 format!("there is no built-in function named \"{}\"", function.src),
317 ));
318 }
319 return Ok(());
320 }
321 let file = find_library(&function.bin)?;
322 if !REGRESS_SYMBOLS.contains(&function.src.as_str()) {
323 return Err(Error::new(
324 SqlState::UNDEFINED_FUNCTION,
325 format!("could not find function \"{}\" in file \"{file}\"", function.src),
326 ));
327 }
328 Ok(())
329}
330
331enum Statements<'s> {
333 Return(Option<&'s Node>),
334 Atomic(Vec<&'s Node>),
335 String(Vec<(Node, String)>),
337}
338
339fn statements<'s>(stmt: &'s CreateFunctionStmt, src: Option<&str>) -> Result<Statements<'s>> {
341 match (&stmt.sql_body, src) {
342 (Some(Node::ReturnStmt(ret)), None) => Ok(Statements::Return(ret.returnval.as_ref())),
343 (Some(Node::List(outer)), None) => match outer.first() {
344 Some(Some(Node::List(list))) => Ok(Statements::Atomic(list.iter().flatten().collect())),
345 Some(None) | None => Ok(Statements::Atomic(Vec::new())),
347 Some(Some(_)) => Err(Error::internal("a BEGIN ATOMIC body that is not a list")),
348 },
349 (Some(_), Some(_)) => Err(Error::new(
350 SqlState::INVALID_FUNCTION_DEFINITION,
351 "duplicate function body specified",
352 )),
353 (Some(_), None) => Err(Error::internal("a function body of an unknown form")),
354 (None, Some(src)) => {
355 let (list, _) = rupg_sql::parse(src).map_err(|error| {
356 let at = error.location;
357 Error::from(error).with_position(at)
358 })?;
359 let stmts = list
360 .into_iter()
361 .flatten()
362 .filter_map(|node| match node {
363 Node::RawStmt(raw) => {
364 let start = usize::try_from(raw.stmt_location).unwrap_or(0);
365 let end = match usize::try_from(raw.stmt_len) {
367 Ok(0) | Err(_) => src.len(),
368 Ok(len) => start + len,
369 };
370 let text = src.get(start..end).unwrap_or(src).to_string();
371 raw.stmt.map(|stmt| (stmt, text))
372 }
373 _ => None,
374 })
375 .collect();
376 Ok(Statements::String(stmts))
377 }
378 (None, None) => {
379 Err(Error::new(SqlState::INVALID_FUNCTION_DEFINITION, "no function body specified"))
380 }
381 }
382}
383
384fn result_types(function: &Function, inputs: &[u32]) -> Result<Vec<u32>> {
386 let declared: Vec<u32> = if function.rettype == oid::RECORD {
387 function
388 .allargtypes
389 .iter()
390 .zip(&function.argmodes)
391 .filter(|(_, mode)| matches!(mode, b'o' | b'b' | b't'))
392 .map(|(ty, _)| *ty)
393 .collect()
394 } else {
395 vec![function.rettype]
396 };
397 declared
398 .into_iter()
399 .map(|ty| {
400 if types::is_polymorphic(ty) {
401 poly::resolve(inputs, &mut function.argtypes.clone(), ty)
402 } else {
403 Ok(ty)
404 }
405 })
406 .collect()
407}
408
409fn body_place(text: &str, src: &str, as_at: Option<usize>) -> Option<usize> {
411 let as_at = as_at?;
412 let quoted = src.replace('\'', "''");
413 let local = [src, quoted.as_str()].into_iter().find_map(|raw| {
414 text.match_indices(raw)
415 .map(|(i, _)| i)
416 .find(|&i| matches!(text.as_bytes()[..i].last(), Some(b'$' | b'\'')))
417 })?;
418 let keyword = text[..local].to_ascii_lowercase().rfind("as")?;
420 Some(as_at.checked_sub(keyword)? + local)
421}
422
423fn input_names(function: &Function) -> Vec<String> {
425 if function.argmodes.is_empty() {
426 return function.argnames.clone();
427 }
428 function
429 .argmodes
430 .iter()
431 .zip(&function.argnames)
432 .filter(|(mode, _)| matches!(mode, b'i' | b'b' | b'v'))
433 .map(|(_, name)| name.clone())
434 .collect()
435}
436
437fn mismatch(rettype: u32, detail: String) -> Error {
439 Error::new(
440 SqlState::INVALID_FUNCTION_DEFINITION,
441 format!("return type mismatch in function declared to return {}", types::name(rettype)),
442 )
443 .with_detail(detail)
444}
445
446fn no_final_query(rettype: u32) -> Error {
448 mismatch(
449 rettype,
450 "Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.".into(),
451 )
452}
453
454fn planned(node: &Node) -> bool {
456 match node {
457 Node::SelectStmt(select) => select.intoClause.is_none(),
458 Node::InsertStmt(_)
459 | Node::UpdateStmt(_)
460 | Node::DeleteStmt(_)
461 | Node::MergeStmt(_)
462 | Node::CallStmt(_) => true,
463 _ => false,
464 }
465}
466
467fn statement_query(
469 env: &dyn Env,
470 function: &Function,
471 path: Option<&[u32]>,
472 inputs: &[u32],
473 node: &Node,
474 last: bool,
475) -> Result<Option<Query>> {
476 let params = Params { types: inputs.to_vec(), variable: false };
477 let mut an = Analyzer::new(env, ¶ms);
478 if let Some(path) = path {
479 an.set_path(path);
480 }
481 an.sql_function = Some((function.name.clone(), input_names(function)));
482 let query = if planned(node) { Some(an.statement(node)?) } else { None };
483 if let Some(query) = &query
484 && !query.out_args.is_empty()
485 {
486 return Err(Error::new(
487 SqlState::FEATURE_NOT_SUPPORTED,
488 "calling procedures with output arguments is not supported in SQL functions",
489 ));
490 }
491 let Some(mut query) = query else {
492 if last {
493 let (rettype, _) = an.result_shape(function, inputs)?;
494 if rettype != oid::VOID {
495 return Err(no_final_query(rettype));
496 }
497 }
498 return Ok(None);
499 };
500 if last {
501 let (rettype, columns) = an.result_shape(function, inputs)?;
502 if rettype != oid::VOID && matches!(node, Node::CallStmt(_)) {
504 return Err(no_final_query(rettype));
505 }
506 an.result_columns(&mut query, rettype, &columns)?;
507 }
508 an.finish(query).map(Some)
509}
510
511fn check_statements(
513 env: &dyn Env,
514 function: &Function,
515 path: Option<&[u32]>,
516 list: &[&Node],
517) -> Result<Vec<Query>> {
518 let inputs = &function.argtypes;
519 if list.is_empty() {
520 let rettype = result_type(function, &result_types(function, inputs)?);
521 if rettype != oid::VOID {
522 return Err(no_final_query(rettype));
523 }
524 }
525 let mut queries = Vec::new();
526 for (i, node) in list.iter().enumerate() {
527 let last = i + 1 == list.len();
528 queries.extend(statement_query(env, function, path, inputs, node, last)?);
529 }
530 Ok(queries)
531}
532
533fn result_type(function: &Function, columns: &[u32]) -> u32 {
535 match columns {
536 [ty] if function.rettype != oid::RECORD => *ty,
537 _ => function.rettype,
538 }
539}
540
541fn body_references(catalog: &Catalog, body: &Expr) -> Result<Vec<ObjRef>> {
543 super::shared_constants(&[body])?;
544 let mut refs = Vec::new();
545 super::view::expr_references(catalog, body, &[], &mut refs);
546 Ok(eliminate_duplicates(&refs))
547}
548
549impl Analyzer<'_> {
550 fn return_body(&mut self, value: Option<&Node>, rettype: u32) -> Result<Expr> {
552 let mut expr = self.with_kind(Kind::Select, |an| an.transform(value))?;
553 if rettype == oid::VOID {
555 return Ok(expr);
556 }
557 if expr.ty == oid::UNKNOWN {
558 expr = self.coerce(expr, oid::TEXT, -1, Context::Implicit, None)?;
559 }
560 let actual = expr.ty;
561 self.coerce_to_target(expr, rettype, -1, Context::Assignment, None)?.ok_or_else(|| {
562 mismatch(rettype, format!("Actual return type is {}.", types::name(actual)))
563 })
564 }
565
566 fn list_body(&mut self, stmts: &[&Node], rettype: u32, columns: &[u32]) -> Result<Body> {
568 let [Node::SelectStmt(select)] = stmts else {
569 return Ok(Body::Statements(columns.to_vec()));
570 };
571 let mut query = self.select(select)?;
572 query.params.clone_from(&self.params);
573 self.result_columns(&mut query, rettype, columns)?;
574 Ok(Body::Query(Box::new(query)))
575 }
576
577 fn result_columns(&mut self, query: &mut Query, rettype: u32, columns: &[u32]) -> Result<()> {
579 if rettype == oid::VOID {
580 return Ok(());
581 }
582 let query = if query.write.is_some() {
583 let returning = query.write.as_mut().and_then(|w| w.returning.as_deref_mut());
584 returning.ok_or_else(|| no_final_query(rettype))?
585 } else {
586 query
587 };
588 let width = query.targets.iter().take_while(|t| !t.junk).count();
589 let row = rettype == oid::RECORD || columns.len() != 1 || columns != [rettype];
590 let whole_row = [rettype];
591 let columns: &[u32] = if row && width == 1 && query.targets[0].expr.ty == rettype {
592 &whole_row
593 } else {
594 columns
595 };
596 if !row && width != 1 {
597 return Err(mismatch(
598 rettype,
599 "Final statement must return exactly one column.".into(),
600 ));
601 }
602 if row && !columns.is_empty() && width != columns.len() {
603 let what = if width > columns.len() { "many" } else { "few" };
604 return Err(mismatch(rettype, format!("Final statement returns too {what} columns.")));
605 }
606 for (i, &ty) in columns.iter().enumerate() {
607 let target = &mut query.targets[i];
608 let actual = target.expr.ty;
609 if actual == ty {
610 continue;
611 }
612 let sorted = query.sort.iter().chain(&query.group).chain(&query.distinct);
613 if sorted.into_iter().any(|s| s.target == i) {
614 return Err(not_yet("a cast of a sorted column of the result of a function", None));
615 }
616 let cast =
617 self.coerce_to_target(target.expr.clone(), ty, -1, Context::Assignment, None)?;
618 target.expr = cast.ok_or_else(|| {
619 mismatch(
620 rettype,
621 format!(
622 "Final statement returns {} instead of {} at column {}.",
623 types::name(actual),
624 types::name(ty),
625 i + 1
626 ),
627 )
628 })?;
629 }
630 Ok(())
631 }
632
633 fn result_shape(&self, function: &Function, inputs: &[u32]) -> Result<(u32, Vec<u32>)> {
635 let mut columns = result_types(function, inputs)?;
636 let rettype = result_type(function, &columns);
637 if rettype != oid::RECORD
638 && let Some(fields) = self.row_columns(rettype)
639 {
640 columns = fields.iter().map(|c| c.ty).collect();
641 }
642 Ok((rettype, columns))
643 }
644
645 fn body(
647 &mut self,
648 function: &Function,
649 stmts: &Statements<'_>,
650 inputs: &[u32],
651 ) -> Result<Body> {
652 let (rettype, columns) = self.result_shape(function, inputs)?;
653 match stmts {
654 Statements::Return(value) => Ok(Body::Value(self.return_body(*value, rettype)?)),
655 Statements::Atomic(list) => self.list_body(list, rettype, &columns),
656 Statements::String(list) => {
657 let list: Vec<&Node> = list.iter().map(|(node, _)| node).collect();
658 self.list_body(&list, rettype, &columns)
659 }
660 }
661 }
662
663 fn support_function(&self, names: &[String]) -> Result<u32> {
665 let parts: Vec<&str> = names.iter().map(String::as_str).collect();
666 let (schema, name) = self.split_name(&parts, None)?;
667 let path = schema.map_or_else(|| self.path.clone(), |schema| vec![schema]);
668 let found = path.iter().find_map(|&namespace| {
669 builtin::procs_named(name)
670 .find(|p| p.namespace == namespace && p.argtypes == [oid::INTERNAL])
671 });
672 let proc = found.ok_or_else(|| {
673 Error::new(
674 SqlState::UNDEFINED_FUNCTION,
675 format!("function {}(internal) does not exist", parts.join(".")),
676 )
677 })?;
678 if proc.rettype != oid::INTERNAL {
679 return Err(Error::new(
680 SqlState::INVALID_OBJECT_DEFINITION,
681 format!("support function {} must return type internal", parts.join(".")),
682 ));
683 }
684 Ok(proc.oid)
685 }
686}
687
688pub fn function_body(env: &dyn Env, oid: u32, inputs: &[u32]) -> Result<Option<Body>> {
694 with_statement(env, oid, |function, body, stmt| {
695 let src = (!function.src.is_empty()).then_some(function.src.as_str());
696 let stmts = statements(stmt, src)?;
697 let params = Params { types: inputs.to_vec(), variable: false };
698 let mut an = Analyzer::new(env, ¶ms);
699 if stmt.sql_body.is_some() || !sets_path(&function.config) {
701 an.set_path(&body.path);
702 }
703 an.sql_function = Some((function.name.clone(), input_names(function)));
704 let mut body = an.body(function, &stmts, inputs)?;
705 if let Some(catalog) = env.catalog() {
706 crate::expand_virtual_body(&mut body, catalog)?;
707 }
708 Ok(Some(body))
709 })
710}
711
712#[derive(Debug)]
714pub struct Script {
715 function: Function,
716 path: Option<Vec<u32>>,
718 pub statements: Vec<(Node, String)>,
720}
721
722impl Script {
723 pub fn name(&self) -> &str {
725 &self.function.name
726 }
727
728 pub fn src(&self) -> &str {
730 &self.function.src
731 }
732
733 pub fn read_only(&self) -> bool {
735 self.function.volatile != b'v'
736 }
737
738 pub fn planned(&self, i: usize) -> bool {
740 self.statements.get(i).is_some_and(|(node, _)| planned(node))
741 }
742
743 pub fn analyze(&self, env: &dyn Env, inputs: &[u32], i: usize) -> Result<Option<Query>> {
749 let Some((node, _)) = self.statements.get(i) else { return Ok(None) };
750 let last = i + 1 == self.statements.len();
751 statement_query(env, &self.function, self.path.as_deref(), inputs, node, last)
752 }
753}
754
755pub fn function_script(env: &dyn Env, oid: u32) -> Result<Option<Script>> {
761 with_statement(env, oid, |function, body, stmt| {
762 let src = (!function.src.is_empty()).then_some(function.src.as_str());
763 let statements = match statements(stmt, src)? {
764 Statements::Return(_) => return Ok(None),
765 Statements::Atomic(list) => {
766 list.into_iter().map(|node| (node.clone(), String::new())).collect()
767 }
768 Statements::String(list) => list,
769 };
770 if statements.is_empty() && function.rettype != oid::VOID {
771 return Err(no_final_query(function.rettype));
772 }
773 let path =
774 (stmt.sql_body.is_some() || !sets_path(&function.config)).then(|| body.path.clone());
775 Ok(Some(Script { function: function.clone(), path, statements }))
776 })
777}
778
779#[derive(Debug)]
781pub enum SqlBody {
782 Return(Expr),
784 Atomic(Vec<Query>),
786}
787
788pub fn function_sqlbody(env: &dyn Env, oid: u32) -> Result<Option<SqlBody>> {
794 with_statement(env, oid, |function, body, stmt| {
795 if stmt.sql_body.is_none() {
796 return Ok(None);
797 }
798 let params = Params { types: function.argtypes.clone(), variable: false };
799 let analyzer = || {
800 let mut an = Analyzer::new(env, ¶ms);
801 an.set_path(&body.path);
802 an.sql_function = Some((function.name.clone(), input_names(function)));
803 an
804 };
805 Ok(Some(match statements(stmt, None)? {
806 Statements::Return(value) => {
807 let mut an = analyzer();
808 let expr = an.with_kind(Kind::Select, |an| an.transform(value))?;
809 if expr.ty == oid::UNKNOWN {
811 SqlBody::Return(an.coerce(expr, oid::TEXT, -1, Context::Implicit, None)?)
812 } else {
813 SqlBody::Return(expr)
814 }
815 }
816 Statements::Atomic(list) => SqlBody::Atomic(
817 list.into_iter()
818 .map(|node| crate::analyze_in(analyzer(), node, None))
819 .collect::<Result<_>>()?,
820 ),
821 Statements::String(_) => return Ok(None),
822 }))
823 })
824}
825
826fn with_statement<T>(
828 env: &dyn Env,
829 oid: u32,
830 f: impl FnOnce(&Function, &FunctionBody, &CreateFunctionStmt) -> Result<Option<T>>,
831) -> Result<Option<T>> {
832 let built_in;
833 let function = match env.catalog().and_then(|c| c.function(oid)) {
834 Some(function) => function,
835 None => {
836 let Some(function) = builtin_function(oid) else { return Ok(None) };
837 built_in = function;
838 &built_in
839 }
840 };
841 let Some(body) = function.body.as_ref().filter(|_| function.lang == SQL_LANGUAGE) else {
842 return Ok(None);
843 };
844 let (list, _) = rupg_sql::parse(&body.text).map_err(Error::from)?;
845 let stmt = list.iter().flatten().find_map(|node| match node {
846 Node::RawStmt(raw) => match raw.stmt.as_ref() {
847 Some(Node::CreateFunctionStmt(stmt)) => Some(stmt),
848 _ => None,
849 },
850 _ => None,
851 });
852 let stmt =
853 stmt.ok_or_else(|| Error::internal("the body of a function is not CREATE FUNCTION"))?;
854 f(function, body, stmt)
855}
856
857fn builtin_function(oid: u32) -> Option<Function> {
859 let proc = builtin::proc_by_oid(oid)?;
860 let text = proc.sqlbody?;
861 Some(Function {
862 oid,
863 name: proc.name.into(),
864 namespace: proc.namespace,
865 owner: 10,
867 lang: proc.lang,
868 cost: DEFAULT_COST,
869 rows: 0,
870 support: 0,
871 kind: b'f',
872 strict: proc.strict,
873 leakproof: false,
874 volatile: proc.volatile,
875 parallel: b's',
876 rettype: proc.rettype,
877 retset: proc.retset,
878 argtypes: proc.argtypes.to_vec(),
879 allargtypes: proc.allargtypes.unwrap_or_default().to_vec(),
880 argmodes: proc.argmodes.unwrap_or_default().to_vec(),
881 argnames: proc.argnames.unwrap_or_default().iter().map(|n| (*n).into()).collect(),
882 argdefaults: None,
884 nargdefaults: 0,
885 src: String::new(),
886 bin: String::new(),
887 body: Some(FunctionBody { text: text.into(), path: vec![crate::PG_CATALOG_NAMESPACE] }),
888 config: Vec::new(),
889 secdef: false,
890 })
891}
892
893impl Definer<'_, '_> {
894 pub(super) fn create_function(&mut self, stmt: &CreateFunctionStmt, text: &str) -> Result<u32> {
896 let parts = names(&stmt.funcname);
897 let (schema, name) = self.an.split_name(&parts, None)?;
898 let namespace = match schema {
899 Some(namespace) => namespace,
900 None => self.an.env.search_path().first().copied().ok_or_else(|| {
901 Error::new(SqlState::UNDEFINED_SCHEMA, "no schema has been selected to create in")
902 })?,
903 };
904 self.check_create_in(namespace, None)?;
905 let options = options(stmt, self.an.env)?;
906 let untrusted = matches!(options.language, INTERNAL_LANGUAGE | C_LANGUAGE);
908 if untrusted && !self.is_superuser() {
909 return Err(Error::new(
910 SqlState::INSUFFICIENT_PRIVILEGE,
911 format!("permission denied for language {}", options.language_name),
912 ));
913 }
914 if options.leakproof && !self.is_superuser() {
915 return Err(only_superuser_leakproof());
916 }
917 let procedure = stmt.is_procedure;
918 let mut argtypes = Vec::new();
919 let mut allargtypes = Vec::new();
920 let mut argmodes = Vec::new();
921 let mut argnames = Vec::new();
922 let mut outs = Vec::new();
923 let mut defaults = Vec::new();
924 let bad = |message: &str, at| {
925 Err(Error::new(SqlState::INVALID_FUNCTION_DEFINITION, message.to_string()).at_opt(at))
926 };
927 for param in stmt.parameters.iter().flatten() {
928 let Node::FunctionParameter(param) = param else { continue };
929 let at = place(param.location);
930 let mode = match param.mode {
931 FunctionParameterMode::FUNC_PARAM_OUT => b'o',
932 FunctionParameterMode::FUNC_PARAM_INOUT => b'b',
933 FunctionParameterMode::FUNC_PARAM_TABLE => b't',
934 FunctionParameterMode::FUNC_PARAM_VARIADIC => b'v',
935 _ => b'i',
936 };
937 let ty = param.argType.as_deref().ok_or_else(|| Error::internal("an argument type"))?;
938 if ty.setof && procedure {
939 return bad("procedures cannot accept set arguments", at);
940 }
941 if ty.setof {
942 return bad("functions cannot accept set arguments", at);
943 }
944 let ty = self.an.type_name(ty)?.0;
945 let input = matches!(mode, b'i' | b'b' | b'v');
946 if input && argmodes.contains(&b'v') {
947 return bad("VARIADIC parameter must be the last input parameter", at);
948 }
949 if procedure && !input && argmodes.contains(&b'v') {
951 return bad("VARIADIC parameter must be the last parameter", at);
952 }
953 if mode == b'v'
954 && !matches!(ty, oid::ANYARRAY | oid::ANYCOMPATIBLEARRAY | oid::ANY)
955 && types::element(ty) == 0
956 {
957 return bad("VARIADIC parameter must be an array", at);
958 }
959 let name = param.name.as_deref().unwrap_or_default();
961 let clash = argnames.iter().zip(&argmodes).any(|(other, &other_mode)| {
962 let pure = |m: u8| match m {
963 b'i' | b'v' => Some(true),
964 b'o' | b't' => Some(false),
965 _ => None,
966 };
967 let apart = matches!((pure(mode), pure(other_mode)), (Some(a), Some(b)) if a != b);
968 !name.is_empty() && other == name && !apart
969 });
970 if clash {
971 return bad(&format!("parameter name \"{name}\" used more than once"), at);
972 }
973 if let Some(raw) = ¶m.defexpr {
974 if !input {
975 return bad("only input parameters can have default values", at);
976 }
977 let expr =
978 self.an.with_kind(Kind::FunctionDefault, |an| an.transform(Some(raw)))?;
979 defaults.push(self.an.coerce_to_specific(expr, ty, "DEFAULT")?);
980 } else if input && !defaults.is_empty() {
981 return bad(
982 "input parameters after one with a default value must also have defaults",
983 at,
984 );
985 } else if procedure && !defaults.is_empty() {
986 return bad(
987 "procedure OUT parameters cannot appear after one with a default value",
988 at,
989 );
990 }
991 if input {
992 argtypes.push(ty);
993 }
994 if matches!(mode, b'o' | b'b' | b't') {
995 outs.push(ty);
996 }
997 allargtypes.push(ty);
998 argmodes.push(mode);
999 argnames.push(name.to_string());
1000 }
1001 if argnames.iter().all(String::is_empty) {
1002 argnames.clear();
1003 }
1004 if argmodes.iter().all(|&mode| mode == b'i') {
1005 allargtypes.clear();
1006 argmodes.clear();
1007 }
1008 let required = match outs.as_slice() {
1011 [] => None,
1012 [ty] if !procedure => Some(*ty),
1013 _ => Some(oid::RECORD),
1014 };
1015 let (rettype, retset) = match (stmt.returnType.as_deref(), required) {
1016 (_, required) if procedure => (required.unwrap_or(oid::VOID), false),
1017 (Some(ret), _) => {
1018 let rettype = self.an.type_name(ret)?.0;
1019 if let Some(required) = required.filter(|&r| r != rettype) {
1020 return Err(Error::new(
1021 SqlState::INVALID_FUNCTION_DEFINITION,
1022 format!(
1023 "function result type must be {} because of OUT parameters",
1024 types::name(required)
1025 ),
1026 ));
1027 }
1028 (rettype, ret.setof)
1029 }
1030 (None, Some(required)) => (required, false),
1031 (None, None) => {
1032 return Err(Error::new(
1033 SqlState::INVALID_FUNCTION_DEFINITION,
1034 "function result type must be specified",
1035 ));
1036 }
1037 };
1038 if options.rows.is_some() && !retset {
1039 return Err(Error::new(
1040 SqlState::INVALID_PARAMETER_VALUE,
1041 "ROWS is not applicable when function does not return a set",
1042 ));
1043 }
1044 let support = match &options.support {
1045 Some(names) => self.an.support_function(names)?,
1046 None => 0,
1047 };
1048 let (src, bin) = interpret_as(stmt, &options, name)?;
1049 let function = Function {
1050 oid: 0,
1051 name: name.to_string(),
1052 namespace,
1053 owner: self.user,
1054 lang: options.language,
1055 cost: options.cost.unwrap_or(if untrusted { 1.0 } else { DEFAULT_COST }),
1056 rows: if retset { options.rows.unwrap_or(DEFAULT_ROWS) } else { 0 },
1057 support,
1058 kind: if stmt.is_procedure { b'p' } else { b'f' },
1059 strict: options.strict,
1060 leakproof: options.leakproof,
1061 volatile: options.volatile,
1062 parallel: options.parallel,
1063 rettype,
1064 retset,
1065 argtypes,
1066 allargtypes,
1067 argmodes,
1068 argnames,
1069 argdefaults: if defaults.is_empty() {
1070 None
1071 } else {
1072 Some(node::write_list(&defaults)?)
1073 },
1074 nargdefaults: i16::try_from(defaults.len()).unwrap_or(i16::MAX),
1075 src,
1076 bin,
1077 body: (!untrusted)
1078 .then(|| FunctionBody { text: text.to_string(), path: self.an.path.clone() }),
1079 config: options.config.clone(),
1080 secdef: options.secdef,
1081 };
1082 let transpose = |error: Error| match (&options.src, error.position()) {
1084 (Some(src), Some(inner)) => {
1085 let outer = body_place(text, src, options.src_at).map(|at| at + inner);
1086 error.with_position(outer)
1087 }
1088 _ => error,
1089 };
1090 let checked = |error: Error| match error.position() {
1092 Some(_) => transpose(error),
1093 None => error.with_context(format!("SQL function \"{name}\"")),
1094 };
1095 let old = self
1096 .catalog
1097 .functions()
1098 .find(|f| f.namespace == namespace && f.name == name && f.argtypes == function.argtypes)
1099 .cloned();
1100 if let Some(old) = &old {
1101 if !stmt.replace {
1102 return Err(Error::new(
1103 SqlState::DUPLICATE_FUNCTION,
1104 format!("function \"{name}\" already exists with same argument types"),
1105 ));
1106 }
1107 self.check_replace(old, &function)?;
1108 }
1109 let pseudo = |ty: u32| {
1110 builtin::type_by_oid(ty).is_some_and(|t| t.kind == b'p') && !types::is_polymorphic(ty)
1111 };
1112 if untrusted {
1113 check_untrusted(&function)?;
1114 return self.store_function(old, function, &defaults, support, Vec::new());
1115 }
1116 if function.lang == PLPGSQL_LANGUAGE {
1117 if self.an.env.check_function_bodies() {
1119 self.with_settings(&function.config, |d| d.check_plpgsql(stmt, &function))
1120 .map_err(transpose)?;
1121 }
1122 return self.store_function(old, function, &defaults, support, Vec::new());
1123 }
1124 if pseudo(rettype) && !matches!(rettype, oid::VOID | oid::RECORD) {
1126 return Err(Error::new(
1127 SqlState::INVALID_FUNCTION_DEFINITION,
1128 format!("SQL functions cannot return type {}", types::name(rettype)),
1129 ));
1130 }
1131 if let Some(&ty) = function.argtypes.iter().find(|&&ty| pseudo(ty)) {
1132 return Err(Error::new(
1133 SqlState::INVALID_FUNCTION_DEFINITION,
1134 format!("SQL functions cannot have arguments of type {}", types::name(ty)),
1135 ));
1136 }
1137 let stmts = statements(stmt, options.src.as_deref()).map_err(transpose)?;
1138 let polymorphic = function.argtypes.iter().any(|&ty| types::is_polymorphic(ty));
1140 if let Statements::String(_) = &stmts {
1141 let oid = self.store_function(old, function.clone(), &defaults, support, Vec::new())?;
1143 if !polymorphic && self.an.env.check_function_bodies() {
1144 self.an.env.command_counter_increment(self.catalog);
1145 self.with_settings(&function.config, |d| d.sql_body(&function, &stmts))
1146 .map_err(checked)?;
1147 }
1148 return Ok(oid);
1149 }
1150 let (body, queries) = self.sql_body(&function, &stmts).map_err(checked)?;
1151 let refs = match &body {
1152 Body::Value(expr) => body_references(self.catalog, expr)?,
1153 Body::Query(query) => {
1154 eliminate_duplicates(&super::view::function_references(self.catalog, query))
1155 }
1156 Body::Statements(_) => {
1157 let refs: Vec<ObjRef> = queries
1158 .iter()
1159 .flat_map(|query| super::view::function_references(self.catalog, query))
1160 .collect();
1161 eliminate_duplicates(&refs)
1162 }
1163 };
1164 self.store_function(old, function, &defaults, support, refs)
1165 }
1166
1167 fn with_settings<T>(
1169 &mut self,
1170 config: &[String],
1171 f: impl FnOnce(&mut Self) -> Result<T>,
1172 ) -> Result<T> {
1173 if config.is_empty() {
1174 return f(self);
1175 }
1176 self.an.env.push_function_settings(config)?;
1177 let result = f(self);
1178 self.an.env.pop_function_settings();
1179 result
1180 }
1181
1182 fn sql_body(
1184 &mut self,
1185 function: &Function,
1186 stmts: &Statements<'_>,
1187 ) -> Result<(Body, Vec<Query>)> {
1188 let params = Params { types: function.argtypes.clone(), variable: false };
1189 let mut an = Analyzer::new(self.an.env, ¶ms);
1190 let path = (!matches!(stmts, Statements::String(_)) || !sets_path(&function.config))
1192 .then(|| self.an.path.clone());
1193 if let Some(path) = &path {
1194 an.set_path(path);
1195 }
1196 an.sql_function = Some((function.name.clone(), input_names(function)));
1197 let body = an.body(function, stmts, &function.argtypes)?;
1198 self.an.notices.append(&mut an.notices);
1199 let mut queries = Vec::new();
1200 if let Body::Statements(_) = &body {
1201 let list: Vec<&Node> = match stmts {
1202 Statements::Return(_) => Vec::new(),
1203 Statements::Atomic(list) => list.clone(),
1204 Statements::String(list) => list.iter().map(|(node, _)| node).collect(),
1205 };
1206 queries = check_statements(self.an.env, function, path.as_deref(), &list)?;
1207 for query in &mut queries {
1208 self.an.notices.append(&mut query.notices);
1209 }
1210 }
1211 Ok((body, queries))
1212 }
1213
1214 fn store_function(
1216 &mut self,
1217 old: Option<Function>,
1218 function: Function,
1219 defaults: &[Expr],
1220 support: u32,
1221 refs: Vec<ObjRef>,
1222 ) -> Result<u32> {
1223 let refs = self.function_refs(&function, defaults, support, refs)?;
1224 match old {
1225 Some(old) => self.catalog.replace_function(old.oid, function, &refs).map(|()| old.oid),
1226 None => {
1227 let oid = self.catalog.create_function(function, &refs)?;
1228 self.apply_default_acl(ObjRef::new(PG_PROC, oid))?;
1229 Ok(oid)
1230 }
1231 }
1232 }
1233
1234 pub(super) fn function_refs(
1236 &self,
1237 function: &Function,
1238 defaults: &[Expr],
1239 support: u32,
1240 mut refs: Vec<ObjRef>,
1241 ) -> Result<Vec<ObjRef>> {
1242 if support != 0 {
1243 refs.push(ObjRef::new(PG_PROC, support));
1244 }
1245 for default in defaults {
1246 for found in body_references(self.catalog, default)? {
1247 if !refs.contains(&found) {
1248 refs.push(found);
1249 }
1250 }
1251 }
1252 let args = if function.allargtypes.is_empty() {
1254 &function.argtypes
1255 } else {
1256 &function.allargtypes
1257 };
1258 for &ty in std::iter::once(&function.rettype).chain(args) {
1259 let found = ObjRef::new(PG_TYPE, ty);
1260 if !refs.contains(&found) {
1261 refs.push(found);
1262 }
1263 }
1264 Ok(refs)
1265 }
1266
1267 fn check_plpgsql(&self, stmt: &CreateFunctionStmt, function: &Function) -> Result<()> {
1269 if stmt.sql_body.is_some() {
1270 return Err(Error::new(
1271 SqlState::INVALID_FUNCTION_DEFINITION,
1272 "inline SQL function body only valid for language SQL",
1273 ));
1274 }
1275 if function.src.is_empty()
1276 && !stmt
1277 .options
1278 .iter()
1279 .flatten()
1280 .any(|o| matches!(o, Node::DefElem(def) if def.defname.as_deref() == Some("as")))
1281 {
1282 return Err(Error::new(
1283 SqlState::INVALID_FUNCTION_DEFINITION,
1284 "no function body specified",
1285 ));
1286 }
1287 let pseudo = |ty: u32| builtin::type_by_oid(ty).is_some_and(|t| t.kind == b'p');
1288 let ret = function.rettype;
1289 if pseudo(ret)
1290 && !matches!(ret, oid::TRIGGER | oid::EVENT_TRIGGER | oid::RECORD | oid::VOID)
1291 && !types::is_polymorphic(ret)
1292 {
1293 return Err(Error::new(
1294 SqlState::FEATURE_NOT_SUPPORTED,
1295 format!("PL/pgSQL functions cannot return type {}", types::name(ret)),
1296 ));
1297 }
1298 let args = if function.allargtypes.is_empty() {
1299 &function.argtypes
1300 } else {
1301 &function.allargtypes
1302 };
1303 if let Some(&ty) =
1304 args.iter().find(|&&ty| pseudo(ty) && ty != oid::RECORD && !types::is_polymorphic(ty))
1305 {
1306 return Err(Error::new(
1307 SqlState::FEATURE_NOT_SUPPORTED,
1308 format!("PL/pgSQL functions cannot accept type {}", types::name(ty)),
1309 ));
1310 }
1311 self.an.env.check_function(function)
1312 }
1313
1314 pub(super) fn check_replace(&self, old: &Function, new: &Function) -> Result<()> {
1316 if !self.is_superuser() && old.owner != self.user {
1317 return Err(super::comment::not_owner("function", &old.name));
1318 }
1319 if old.kind != new.kind {
1320 let what = match old.kind {
1321 b'a' => "an aggregate function",
1322 b'p' => "a procedure",
1323 b'w' => "a window function",
1324 _ => "a function",
1325 };
1326 return Err(Error::new(SqlState::WRONG_OBJECT_TYPE, "cannot change routine kind")
1327 .with_detail(format!("\"{}\" is {what}.", old.name)));
1328 }
1329 let signature = self.describe(ObjRef::new(PG_PROC, old.oid));
1330 let signature = signature.split_once(' ').map_or(&*signature, |(_, rest)| rest);
1331 let drop = match new.kind {
1332 b'p' => "DROP PROCEDURE",
1333 b'a' => "DROP AGGREGATE",
1334 _ => "DROP FUNCTION",
1335 };
1336 let error = |message: String| {
1337 Error::new(SqlState::INVALID_FUNCTION_DEFINITION, message)
1338 .with_hint(format!("Use {drop} {signature} first."))
1339 };
1340 let changed = "cannot change return type of existing function".to_string();
1341 if (old.rettype != new.rettype || old.retset != new.retset) && new.kind == b'p' {
1342 return Err(error("cannot change whether a procedure has output parameters".into()));
1343 }
1344 if old.rettype != new.rettype || old.retset != new.retset {
1345 return Err(error(changed));
1346 }
1347 let outs = |f: &Function| -> Vec<(u32, String)> {
1348 f.allargtypes
1349 .iter()
1350 .zip(&f.argmodes)
1351 .zip(f.argnames.iter().map(String::as_str).chain(std::iter::repeat("")))
1352 .filter(|((_, mode), _)| matches!(mode, b'o' | b'b' | b't'))
1353 .map(|((&ty, _), name)| (ty, name.to_string()))
1354 .collect()
1355 };
1356 if old.rettype == oid::RECORD && outs(old) != outs(new) {
1357 return Err(
1358 error(changed).with_detail("Row type defined by OUT parameters is different.")
1359 );
1360 }
1361 let names = input_names(new);
1362 for (i, name) in input_names(old).iter().enumerate() {
1363 if !name.is_empty() && names.get(i) != Some(name) {
1364 return Err(error(format!("cannot change name of input parameter \"{name}\"")));
1365 }
1366 }
1367 if old.nargdefaults > 0 {
1368 if new.nargdefaults < old.nargdefaults {
1369 return Err(error(
1370 "cannot remove parameter defaults from existing function".into(),
1371 ));
1372 }
1373 let defaults =
1374 |f: &Function| f.argdefaults.as_deref().map_or(Ok(Vec::new()), node::read_list);
1375 let (olds, news) = (defaults(old)?, defaults(new)?);
1376 let skip = news.len().saturating_sub(olds.len());
1377 if olds.iter().zip(&news[skip..]).any(|(o, n)| o.ty != n.ty) {
1378 return Err(error(
1379 "cannot change data type of existing parameter default value".into(),
1380 ));
1381 }
1382 }
1383 Ok(())
1384 }
1385}