nodedb_sql/resolver/expr/
functions.rs1use sqlparser::ast;
4
5use crate::error::{Result, SqlError};
6use crate::parser::normalize::{SCHEMA_QUALIFIED_MSG, normalize_ident};
7use crate::types::*;
8
9use super::convert::convert_expr_depth;
10
11pub(super) fn convert_function_depth(func: &ast::Function, depth: &mut usize) -> Result<SqlExpr> {
12 if func.name.0.len() == 1 {
15 let raw_name = match &func.name.0[0] {
16 ast::ObjectNamePart::Identifier(ident) => ident.value.to_ascii_lowercase(),
17 _ => String::new(),
18 };
19 if let Some(expr) = intercept_fts_function(&raw_name, func, depth)? {
20 return Ok(expr);
21 }
22 }
23
24 if func.name.0.len() > 1 {
25 let qualified: String = func
26 .name
27 .0
28 .iter()
29 .map(|p| match p {
30 ast::ObjectNamePart::Identifier(ident) => ident.value.clone(),
31 _ => String::new(),
32 })
33 .collect::<Vec<_>>()
34 .join(".");
35 return Err(SqlError::Unsupported {
36 detail: format!("schema-qualified function name '{qualified}': {SCHEMA_QUALIFIED_MSG}"),
37 });
38 }
39 let name = func
40 .name
41 .0
42 .iter()
43 .map(|p| match p {
44 ast::ObjectNamePart::Identifier(ident) => normalize_ident(ident),
45 _ => String::new(),
46 })
47 .collect::<Vec<_>>()
48 .join(".");
49
50 if (name == "current_schemas" || name == "current_schema")
54 && let Some(expr) = intercept_catalog_function(&name, func, depth)?
55 {
56 return Ok(expr);
57 }
58
59 let args = match &func.args {
60 ast::FunctionArguments::None => Vec::new(),
61 ast::FunctionArguments::Subquery(_) => {
62 return Err(SqlError::Unsupported {
63 detail: "subquery in function args".into(),
64 });
65 }
66 ast::FunctionArguments::List(arg_list) => arg_list
67 .args
68 .iter()
69 .filter_map(|a| match a {
70 ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr(e)) => {
71 Some(convert_expr_depth(e, depth))
72 }
73 ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Wildcard) => {
74 Some(Ok(SqlExpr::Wildcard))
75 }
76 ast::FunctionArg::Named {
77 arg: ast::FunctionArgExpr::Expr(e),
78 ..
79 } => Some(convert_expr_depth(e, depth)),
80 _ => None,
81 })
82 .collect::<Result<Vec<_>>>()?,
83 };
84
85 let distinct = match &func.args {
86 ast::FunctionArguments::List(arg_list) => {
87 matches!(
88 arg_list.duplicate_treatment,
89 Some(ast::DuplicateTreatment::Distinct)
90 )
91 }
92 _ => false,
93 };
94
95 Ok(SqlExpr::Function {
96 name,
97 args,
98 distinct,
99 })
100}
101
102fn intercept_fts_function(
108 name: &str,
109 func: &ast::Function,
110 depth: &mut usize,
111) -> Result<Option<SqlExpr>> {
112 use crate::functions::fts_ops::pg_fts_funcs;
113
114 let args = collect_function_args(func, depth)?;
115 match name {
116 "to_tsvector" => Ok(Some(SqlExpr::Function {
117 name: "pg_to_tsvector".into(),
118 args,
119 distinct: false,
120 })),
121 "to_tsquery" => Ok(Some(pg_fts_funcs::lower_pg_to_tsquery(args))),
122 "plainto_tsquery" => Ok(Some(pg_fts_funcs::lower_pg_plainto_tsquery(args))),
123 "phraseto_tsquery" => Ok(Some(pg_fts_funcs::lower_phraseto_tsquery(args))),
124 "websearch_to_tsquery" => Ok(Some(pg_fts_funcs::lower_pg_websearch_to_tsquery(args))),
125 "ts_rank" => Ok(Some(SqlExpr::Function {
126 name: "pg_ts_rank".into(),
127 args,
128 distinct: false,
129 })),
130 "ts_rank_cd" => Err(SqlError::Unsupported {
131 detail: "ts_rank_cd is not supported; use ts_rank or bm25_score instead".into(),
132 }),
133 "ts_headline" => Ok(Some(SqlExpr::Function {
134 name: "pg_ts_headline".into(),
135 args,
136 distinct: false,
137 })),
138 _ => Ok(None),
139 }
140}
141
142fn intercept_catalog_function(
150 name: &str,
151 func: &ast::Function,
152 depth: &mut usize,
153) -> Result<Option<SqlExpr>> {
154 let args = collect_function_args(func, depth)?;
155 match name {
156 "current_schemas" => {
157 let include_implicit = match args.first() {
159 Some(SqlExpr::Literal(SqlValue::Bool(b))) => *b,
160 _ => false,
162 };
163 let mut schemas: Vec<SqlExpr> = Vec::new();
164 if include_implicit {
165 schemas.push(SqlExpr::Literal(SqlValue::String("pg_catalog".into())));
166 }
167 schemas.push(SqlExpr::Literal(SqlValue::String("public".into())));
168 Ok(Some(SqlExpr::ArrayLiteral(schemas)))
169 }
170 "current_schema" => Ok(Some(SqlExpr::Literal(SqlValue::String("public".into())))),
171 _ => Ok(None),
172 }
173}
174
175fn collect_function_args(func: &ast::Function, depth: &mut usize) -> Result<Vec<SqlExpr>> {
177 match &func.args {
178 ast::FunctionArguments::None => Ok(Vec::new()),
179 ast::FunctionArguments::Subquery(_) => Err(SqlError::Unsupported {
180 detail: "subquery in function args".into(),
181 }),
182 ast::FunctionArguments::List(arg_list) => arg_list
183 .args
184 .iter()
185 .filter_map(|a| match a {
186 ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr(e)) => {
187 Some(convert_expr_depth(e, depth))
188 }
189 ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Wildcard) => {
190 Some(Ok(SqlExpr::Wildcard))
191 }
192 ast::FunctionArg::Named {
193 arg: ast::FunctionArgExpr::Expr(e),
194 ..
195 } => Some(convert_expr_depth(e, depth)),
196 _ => None,
197 })
198 .collect::<Result<Vec<_>>>(),
199 }
200}