Skip to content

Commit a603b3d

Browse files
feat(planner): suggest function name on typo (#10759)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent f1c7624 commit a603b3d

File tree

5 files changed

+205
-133
lines changed

5 files changed

+205
-133
lines changed

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/query/expression/src/function.rs

Lines changed: 112 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,43 @@ use crate::Expr;
4343
use crate::FunctionDomain;
4444
use crate::Scalar;
4545

46+
pub type AutoCastRules<'a> = &'a [(DataType, DataType)];
47+
/// A function to build function depending on the const parameters and the type of arguments (before coercion).
48+
///
49+
/// The first argument is the const parameters and the second argument is the types of arguments.
50+
pub type FunctionFactory =
51+
Box<dyn Fn(&[usize], &[DataType]) -> Option<Arc<Function>> + Send + Sync + 'static>;
52+
53+
pub struct Function {
54+
pub signature: FunctionSignature,
55+
pub eval: FunctionEval,
56+
}
57+
4658
#[derive(Debug, Clone)]
4759
pub struct FunctionSignature {
4860
pub name: String,
4961
pub args_type: Vec<DataType>,
5062
pub return_type: DataType,
5163
}
5264

53-
pub type AutoCastRules<'a> = &'a [(DataType, DataType)];
65+
#[derive(EnumAsInner)]
66+
#[allow(clippy::type_complexity)]
67+
pub enum FunctionEval {
68+
/// Scalar function that returns a single value.
69+
Scalar {
70+
/// Given the domains of the arguments, return the domain of the output value.
71+
calc_domain: Box<dyn Fn(&[Domain]) -> FunctionDomain<AnyType> + Send + Sync>,
72+
/// Given a set of arguments, return a single value.
73+
/// The result must be in the same length as the input arguments if its a column.
74+
eval: Box<dyn Fn(&[ValueRef<AnyType>], &mut EvalContext) -> Value<AnyType> + Send + Sync>,
75+
},
76+
/// Set returning function that returns a series of values.
77+
SRF {
78+
/// Given a set of arguments, return a series of chunks of result and the repeat time of each chunk.
79+
eval:
80+
Box<dyn Fn(&[ValueRef<AnyType>], usize) -> Vec<(Value<AnyType>, usize)> + Send + Sync>,
81+
},
82+
}
5483

5584
#[derive(Clone, Copy, Default)]
5685
pub struct FunctionContext {
@@ -70,62 +99,8 @@ pub struct EvalContext<'a> {
7099
pub errors: Option<(MutableBitmap, String)>,
71100
}
72101

73-
impl<'a> EvalContext<'a> {
74-
#[inline]
75-
pub fn set_error(&mut self, row: usize, error_msg: impl Into<String>) {
76-
// If the row is NULL, we don't need to set error.
77-
if self
78-
.validity
79-
.as_ref()
80-
.map(|b| !b.get_bit(row))
81-
.unwrap_or(false)
82-
{
83-
return;
84-
}
85-
86-
match self.errors.as_mut() {
87-
Some((valids, _)) => {
88-
valids.set(row, false);
89-
}
90-
None => {
91-
let mut valids = constant_bitmap(true, self.num_rows.max(1));
92-
valids.set(row, false);
93-
self.errors = Some((valids, error_msg.into()));
94-
}
95-
}
96-
}
97-
98-
pub fn render_error(&self, span: Span, args: &[Value<AnyType>], func_name: &str) -> Result<()> {
99-
match &self.errors {
100-
Some((valids, error)) => {
101-
let first_error_row = valids
102-
.iter()
103-
.enumerate()
104-
.filter(|(_, valid)| !valid)
105-
.take(1)
106-
.next()
107-
.unwrap()
108-
.0;
109-
let args = args
110-
.iter()
111-
.map(|arg| {
112-
let arg_ref = arg.as_ref();
113-
arg_ref.index(first_error_row).unwrap().to_string()
114-
})
115-
.join(", ");
116-
117-
Err(ErrorCode::Internal(format!(
118-
"{error} while evaluating function `{func_name}({args})`"
119-
))
120-
.set_span(span))
121-
}
122-
None => Ok(()),
123-
}
124-
}
125-
}
126-
127-
/// `FunctionID` is a unique identifier for a function. It's used to construct
128-
/// the exactly same function from the remote execution nodes.
102+
/// `FunctionID` is a unique identifier for a function in the registry. It's used to
103+
/// construct the exactly same function in the remote execution nodes.
129104
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
130105
pub enum FunctionID {
131106
Builtin {
@@ -140,22 +115,22 @@ pub enum FunctionID {
140115
},
141116
}
142117

143-
#[derive(EnumAsInner)]
144-
#[allow(clippy::type_complexity)]
145-
pub enum FunctionEval {
146-
Scalar {
147-
calc_domain: Box<dyn Fn(&[Domain]) -> FunctionDomain<AnyType> + Send + Sync>,
148-
eval: Box<dyn Fn(&[ValueRef<AnyType>], &mut EvalContext) -> Value<AnyType> + Send + Sync>,
149-
},
150-
SRF {
151-
eval:
152-
Box<dyn Fn(&[ValueRef<AnyType>], usize) -> Vec<(Value<AnyType>, usize)> + Send + Sync>,
153-
},
154-
}
118+
#[derive(Default)]
119+
pub struct FunctionRegistry {
120+
pub funcs: HashMap<String, Vec<(Arc<Function>, usize)>>,
121+
pub factories: HashMap<String, Vec<(FunctionFactory, usize)>>,
155122

156-
pub struct Function {
157-
pub signature: FunctionSignature,
158-
pub eval: FunctionEval,
123+
/// Aliases map from alias function name to original function name.
124+
pub aliases: HashMap<String, String>,
125+
126+
/// Default cast rules for all functions.
127+
pub default_cast_rules: Vec<(DataType, DataType)>,
128+
/// Cast rules for specific functions, excluding the default cast rules.
129+
pub additional_cast_rules: HashMap<String, Vec<(DataType, DataType)>>,
130+
/// The auto rules that should use TRY_CAST instead of CAST.
131+
pub auto_try_cast_rules: Vec<(DataType, DataType)>,
132+
133+
pub properties: HashMap<String, FunctionProperty>,
159134
}
160135

161136
impl Function {
@@ -180,30 +155,6 @@ impl Function {
180155
}
181156
}
182157

183-
/// A function to build function depending on the const parameters and the type of arguments (before coercion).
184-
///
185-
/// The first argument is the const parameters and the second argument is the types of arguments.
186-
pub type FunctionFactory =
187-
Box<dyn Fn(&[usize], &[DataType]) -> Option<Arc<Function>> + Send + Sync + 'static>;
188-
189-
#[derive(Default)]
190-
pub struct FunctionRegistry {
191-
pub funcs: HashMap<String, Vec<(Arc<Function>, usize)>>,
192-
pub factories: HashMap<String, Vec<(FunctionFactory, usize)>>,
193-
194-
/// Aliases map from alias function name to original function name.
195-
pub aliases: HashMap<String, String>,
196-
197-
/// Default cast rules for all functions.
198-
pub default_cast_rules: Vec<(DataType, DataType)>,
199-
/// Cast rules for specific functions, excluding the default cast rules.
200-
pub additional_cast_rules: HashMap<String, Vec<(DataType, DataType)>>,
201-
/// The auto rules that should use TRY_CAST instead of CAST.
202-
pub auto_try_cast_rules: Vec<(DataType, DataType)>,
203-
204-
pub properties: HashMap<String, FunctionProperty>,
205-
}
206-
207158
impl FunctionRegistry {
208159
pub fn empty() -> Self {
209160
Self::default()
@@ -376,6 +327,17 @@ impl FunctionRegistry {
376327
+ self.factories.get(name).map(|f| f.len()).unwrap_or(0)
377328
}
378329

330+
pub fn all_function_names(&self) -> Vec<String> {
331+
self.aliases
332+
.keys()
333+
.chain(self.funcs.keys())
334+
.chain(self.factories.keys())
335+
.map(|s| s.to_string())
336+
.sorted()
337+
.dedup()
338+
.collect()
339+
}
340+
379341
pub fn check_ambiguity(&self) {
380342
for (name, funcs) in &self.funcs {
381343
let auto_cast_rules = self.get_auto_cast_rules(name);
@@ -437,6 +399,60 @@ impl FunctionID {
437399
}
438400
}
439401

402+
impl<'a> EvalContext<'a> {
403+
#[inline]
404+
pub fn set_error(&mut self, row: usize, error_msg: impl Into<String>) {
405+
// If the row is NULL, we don't need to set error.
406+
if self
407+
.validity
408+
.as_ref()
409+
.map(|b| !b.get_bit(row))
410+
.unwrap_or(false)
411+
{
412+
return;
413+
}
414+
415+
match self.errors.as_mut() {
416+
Some((valids, _)) => {
417+
valids.set(row, false);
418+
}
419+
None => {
420+
let mut valids = constant_bitmap(true, self.num_rows.max(1));
421+
valids.set(row, false);
422+
self.errors = Some((valids, error_msg.into()));
423+
}
424+
}
425+
}
426+
427+
pub fn render_error(&self, span: Span, args: &[Value<AnyType>], func_name: &str) -> Result<()> {
428+
match &self.errors {
429+
Some((valids, error)) => {
430+
let first_error_row = valids
431+
.iter()
432+
.enumerate()
433+
.filter(|(_, valid)| !valid)
434+
.take(1)
435+
.next()
436+
.unwrap()
437+
.0;
438+
let args = args
439+
.iter()
440+
.map(|arg| {
441+
let arg_ref = arg.as_ref();
442+
arg_ref.index(first_error_row).unwrap().to_string()
443+
})
444+
.join(", ");
445+
446+
Err(ErrorCode::Internal(format!(
447+
"{error} while evaluating function `{func_name}({args})`"
448+
))
449+
.set_span(span))
450+
}
451+
None => Ok(()),
452+
}
453+
}
454+
}
455+
440456
pub fn wrap_nullable<F>(f: F) -> impl Fn(&[ValueRef<AnyType>], &mut EvalContext) -> Value<AnyType>
441457
where F: Fn(&[ValueRef<AnyType>], &mut EvalContext) -> Value<AnyType> {
442458
move |args, ctx| {

src/query/sql/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ percent-encoding = "2"
5858
regex = "1.6.0"
5959
roaring = "0.10.1"
6060
serde = { workspace = true }
61+
simsearch = "0.2"
6162
time = "0.3.14"
6263
tracing = "0.1.36"
6364
url = { version = "2.3" }

0 commit comments

Comments
 (0)