1use rudb_common::{Error, Field, LogicalType, Result};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum TableFunction {
28 Range,
30 GenerateSeries,
32 ReadParquet,
34}
35
36impl TableFunction {
37 #[must_use]
39 pub const fn name(self) -> &'static str {
40 match self {
41 Self::Range => "range",
42 Self::GenerateSeries => "generate_series",
43 Self::ReadParquet => "read_parquet",
44 }
45 }
46
47 #[must_use]
51 pub const fn inclusive(self) -> bool {
52 matches!(self, Self::GenerateSeries)
53 }
54
55 #[must_use]
57 pub fn lookup(name: &str) -> Option<Self> {
58 if name.eq_ignore_ascii_case("range") {
59 return Some(Self::Range);
60 }
61 if name.eq_ignore_ascii_case("generate_series") {
62 return Some(Self::GenerateSeries);
63 }
64 if name.eq_ignore_ascii_case("read_parquet") || name.eq_ignore_ascii_case("parquet_scan") {
65 return Some(Self::ReadParquet);
66 }
67 None
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum Columns {
80 Fixed(Vec<Field>),
82 Parquet,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct ResolvedTable {
89 pub function: TableFunction,
91 pub arguments: Vec<LogicalType>,
93 pub columns: Columns,
95}
96
97pub fn resolve_table(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
113 let Some(function) = TableFunction::lookup(name) else {
114 return Err(Error::catalog(format!("Table Function with name {name} does not exist!")));
115 };
116 if function == TableFunction::ReadParquet {
117 if arguments.len() != 1 || arguments[0] != LogicalType::Varchar {
118 return Err(no_overload(function, arguments));
119 }
120 return Ok(ResolvedTable {
121 function,
122 arguments: vec![LogicalType::Varchar],
123 columns: Columns::Parquet,
124 });
125 }
126 let arity = arguments.len();
127 if !(1..=3).contains(&arity) {
128 return Err(Error::binder(format!(
129 "Table function {}() takes between 1 and 3 arguments, {arity} were given",
130 function.name()
131 )));
132 }
133 Ok(ResolvedTable {
134 function,
135 arguments: vec![LogicalType::BigInt; arity],
136 columns: Columns::Fixed(vec![Field::new(function.name(), LogicalType::BigInt)]),
137 })
138}
139
140fn no_overload(function: TableFunction, arguments: &[LogicalType]) -> Error {
146 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
147 Error::binder(format!(
148 "No function matches the given name and argument types '{}({})'. You might need to add \
149 explicit type casts.\n\tCandidate functions:\n\t{}(VARCHAR)\n",
150 function.name(),
151 written.join(", "),
152 function.name()
153 ))
154}
155
156pub fn series(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<Vec<i64>> {
170 let count = series_length(function, start, stop, step)?;
171 let mut out = Vec::with_capacity(count);
172 let mut at = start;
173 for _ in 0..count {
174 out.push(at);
175 at = at.saturating_add(step);
179 }
180 Ok(out)
181}
182
183pub fn series_length(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<usize> {
196 if step == 0 {
197 return Err(Error::binder("interval cannot be 0!"));
198 }
199 Ok(length(function, start, stop, step))
200}
201
202fn length(function: TableFunction, start: i64, stop: i64, step: i64) -> usize {
208 let start = i128::from(start);
209 let stop = i128::from(stop);
210 let step = i128::from(step);
211 let span = if function.inclusive() {
212 if step > 0 { stop - start + 1 } else { stop - start - 1 }
213 } else {
214 stop - start
215 };
216 if (span > 0) != (step > 0) {
217 return 0;
218 }
219 let count = (span + step - step.signum()) / step;
221 usize::try_from(count).unwrap_or(usize::MAX)
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 fn fixed(resolved: &ResolvedTable) -> &[Field] {
230 match &resolved.columns {
231 Columns::Fixed(fields) => fields,
232 Columns::Parquet => panic!("{} resolves to a file", resolved.function.name()),
233 }
234 }
235
236 fn integers(count: usize) -> Vec<LogicalType> {
238 vec![LogicalType::BigInt; count]
239 }
240
241 #[test]
242 fn a_name_that_is_not_a_table_function_says_so_rather_than_binding() {
243 let error = resolve_table("read_csv", &integers(1)).unwrap_err();
244 assert!(error.to_string().contains("read_csv"), "{error}");
245 }
246
247 #[test]
248 fn both_names_resolve_and_each_one_names_its_own_column() {
249 let range = resolve_table("range", &integers(1)).unwrap();
250 assert_eq!(fixed(&range)[0].name, "range");
251 let series = resolve_table("GENERATE_SERIES", &integers(3)).unwrap();
252 assert_eq!(fixed(&series)[0].name, "generate_series");
253 assert_eq!(series.arguments.len(), 3);
254 }
255
256 #[test]
257 fn no_arguments_and_four_arguments_are_both_the_arity_error() {
258 assert!(resolve_table("range", &integers(0)).is_err());
259 assert!(resolve_table("range", &integers(4)).is_err());
260 }
261
262 #[test]
263 fn a_series_call_ignores_the_types_it_was_given_and_casts_them_all_to_bigint() {
264 let resolved =
265 resolve_table("range", &[LogicalType::Varchar, LogicalType::Double]).unwrap();
266 assert_eq!(resolved.arguments, integers(2));
267 }
268
269 #[test]
270 fn read_parquet_takes_one_string_and_says_its_columns_are_in_the_file() {
271 let resolved = resolve_table("read_parquet", &[LogicalType::Varchar]).unwrap();
272 assert_eq!(resolved.function, TableFunction::ReadParquet);
273 assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
274 assert_eq!(resolved.columns, Columns::Parquet);
275 }
276
277 #[test]
278 fn parquet_scan_is_the_same_function_under_duckdbs_other_name_for_it() {
279 assert_eq!(TableFunction::lookup("parquet_scan"), Some(TableFunction::ReadParquet));
280 let resolved = resolve_table("parquet_scan", &[LogicalType::Varchar]).unwrap();
282 assert_eq!(resolved.function.name(), "read_parquet");
283 }
284
285 #[test]
286 fn a_path_that_is_not_a_string_is_the_message_duckdb_gives_for_it() {
287 let error = resolve_table("read_parquet", &[LogicalType::Integer]).unwrap_err();
289 assert!(
290 error.message().starts_with(
291 "No function matches the given name and argument types 'read_parquet(INTEGER)'."
292 ),
293 "{error}"
294 );
295 assert!(error.message().contains("read_parquet(VARCHAR)"), "{error}");
296 }
297
298 #[test]
299 fn read_parquet_of_no_arguments_or_two_is_the_same_no_overload_message() {
300 let two = resolve_table("read_parquet", &[LogicalType::Varchar, LogicalType::Varchar]);
301 assert!(two.unwrap_err().message().contains("read_parquet(VARCHAR, VARCHAR)"));
302 let none = resolve_table("read_parquet", &[]);
303 assert!(none.unwrap_err().message().contains("read_parquet()"));
304 }
305
306 #[test]
307 fn range_stops_before_the_end_and_generate_series_stops_on_it() {
308 assert_eq!(series(TableFunction::Range, 0, 3, 1).unwrap(), vec![0, 1, 2]);
309 assert_eq!(series(TableFunction::GenerateSeries, 0, 3, 1).unwrap(), vec![0, 1, 2, 3]);
310 }
311
312 #[test]
313 fn a_step_that_does_not_divide_the_span_stops_before_the_end_of_it() {
314 assert_eq!(series(TableFunction::Range, 2, 7, 2).unwrap(), vec![2, 4, 6]);
317 assert_eq!(series(TableFunction::GenerateSeries, 2, 7, 2).unwrap(), vec![2, 4, 6]);
318 }
319
320 #[test]
321 fn a_negative_step_counts_down_and_stops_on_the_same_rule() {
322 assert_eq!(series(TableFunction::Range, 5, 1, -2).unwrap(), vec![5, 3]);
323 assert_eq!(series(TableFunction::GenerateSeries, 5, 1, -2).unwrap(), vec![5, 3, 1]);
324 }
325
326 #[test]
327 fn a_step_going_the_wrong_way_produces_nothing_rather_than_running_forever() {
328 assert!(series(TableFunction::Range, 0, 10, -1).unwrap().is_empty());
329 assert!(series(TableFunction::Range, 10, 0, 1).unwrap().is_empty());
330 }
331
332 #[test]
333 fn an_empty_range_and_a_single_value_series_are_the_boundary_between_the_two() {
334 assert!(series(TableFunction::Range, 4, 4, 1).unwrap().is_empty());
335 assert_eq!(series(TableFunction::GenerateSeries, 4, 4, 1).unwrap(), vec![4]);
336 }
337
338 #[test]
339 fn a_step_of_zero_is_the_one_case_that_is_an_error_rather_than_nothing() {
340 let error = series(TableFunction::Range, 1, 5, 0).unwrap_err();
341 assert!(error.to_string().contains("interval cannot be 0"), "{error}");
342 }
343
344 #[test]
345 fn a_span_that_does_not_fit_in_an_i64_does_not_overflow_the_length() {
346 assert_eq!(length(TableFunction::Range, i64::MIN, i64::MAX, 1), usize::MAX);
349 }
350}