1use rudb_common::{Error, Field, LogicalType, Result};
63
64use crate::entrycatalog::{
65 column_fields, database_fields, schema_fields, table_fields, view_fields,
66};
67use crate::functioncatalog::function_fields;
68use crate::settingcatalog::setting_fields;
69use crate::typecatalog::type_fields;
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum TableFunction {
77 Range,
79 GenerateSeries,
81 ReadParquet,
83 ReadCsv,
85 RudbStrategies,
87 DuckdbKeywords,
89 DuckdbTypes,
91 DuckdbFunctions,
93 DuckdbSettings,
95 DuckdbDatabases,
97 DuckdbSchemas,
99 DuckdbTables,
101 DuckdbViews,
103 DuckdbColumns,
105 DuckdbExtensions,
107 DuckdbOptimizers,
109 DuckdbDialects,
111 DuckdbGrammarExtensions,
113 PragmaTableInfo,
115 PragmaShow,
117 PragmaVersion,
119 PragmaPlatform,
121 PragmaUserAgent,
123 PragmaDatabaseSize,
125}
126
127pub const FILE_ROW_NUMBER: &str = "file_row_number";
134
135impl TableFunction {
136 #[must_use]
138 pub const fn name(self) -> &'static str {
139 match self {
140 Self::Range => "range",
141 Self::GenerateSeries => "generate_series",
142 Self::ReadParquet => "read_parquet",
143 Self::ReadCsv => "read_csv",
144 Self::RudbStrategies => "rudb_strategies",
145 Self::DuckdbKeywords => "duckdb_keywords",
146 Self::DuckdbTypes => "duckdb_types",
147 Self::DuckdbFunctions => "duckdb_functions",
148 Self::DuckdbSettings => "duckdb_settings",
149 Self::DuckdbDatabases => "duckdb_databases",
150 Self::DuckdbSchemas => "duckdb_schemas",
151 Self::DuckdbTables => "duckdb_tables",
152 Self::DuckdbViews => "duckdb_views",
153 Self::DuckdbColumns => "duckdb_columns",
154 Self::DuckdbExtensions => "duckdb_extensions",
155 Self::DuckdbOptimizers => "duckdb_optimizers",
156 Self::DuckdbDialects => "duckdb_dialects",
157 Self::DuckdbGrammarExtensions => "duckdb_grammar_extensions",
158 Self::PragmaTableInfo => "pragma_table_info",
159 Self::PragmaShow => "pragma_show",
160 Self::PragmaVersion => "pragma_version",
161 Self::PragmaPlatform => "pragma_platform",
162 Self::PragmaUserAgent => "pragma_user_agent",
163 Self::PragmaDatabaseSize => "pragma_database_size",
164 }
165 }
166
167 #[must_use]
173 pub const fn takes_a_name(self) -> bool {
174 matches!(self, Self::PragmaTableInfo | Self::PragmaShow)
175 }
176
177 #[must_use]
182 pub const fn inclusive(self) -> bool {
183 matches!(self, Self::GenerateSeries)
184 }
185
186 #[must_use]
202 pub fn parameters(self) -> &'static [(&'static str, LogicalType)] {
203 static READ_PARQUET: &[(&str, LogicalType)] = &[
204 ("binary_as_string", LogicalType::Boolean),
205 ("file_row_number", LogicalType::Boolean),
206 ];
207 static READ_CSV: &[(&str, LogicalType)] = &[
208 ("all_varchar", LogicalType::Boolean),
209 ("delim", LogicalType::Varchar),
210 ("escape", LogicalType::Varchar),
211 ("header", LogicalType::Boolean),
212 ("quote", LogicalType::Varchar),
213 ("sep", LogicalType::Varchar),
214 ];
215 match self {
216 Self::ReadParquet => READ_PARQUET,
217 Self::ReadCsv => READ_CSV,
218 _ => &[],
219 }
220 }
221
222 #[must_use]
224 pub fn lookup(name: &str) -> Option<Self> {
225 if name.eq_ignore_ascii_case("range") {
226 return Some(Self::Range);
227 }
228 if name.eq_ignore_ascii_case("generate_series") {
229 return Some(Self::GenerateSeries);
230 }
231 if name.eq_ignore_ascii_case("read_parquet") || name.eq_ignore_ascii_case("parquet_scan") {
232 return Some(Self::ReadParquet);
233 }
234 if name.eq_ignore_ascii_case("read_csv") || name.eq_ignore_ascii_case("read_csv_auto") {
238 return Some(Self::ReadCsv);
239 }
240 if name.eq_ignore_ascii_case("rudb_strategies") {
241 return Some(Self::RudbStrategies);
242 }
243 if name.eq_ignore_ascii_case("duckdb_keywords") {
244 return Some(Self::DuckdbKeywords);
245 }
246 if name.eq_ignore_ascii_case("duckdb_types") {
247 return Some(Self::DuckdbTypes);
248 }
249 if name.eq_ignore_ascii_case("duckdb_functions") {
250 return Some(Self::DuckdbFunctions);
251 }
252 if name.eq_ignore_ascii_case("duckdb_settings") {
253 return Some(Self::DuckdbSettings);
254 }
255 if name.eq_ignore_ascii_case("duckdb_databases") {
256 return Some(Self::DuckdbDatabases);
257 }
258 if name.eq_ignore_ascii_case("duckdb_schemas") {
259 return Some(Self::DuckdbSchemas);
260 }
261 if name.eq_ignore_ascii_case("duckdb_tables") {
262 return Some(Self::DuckdbTables);
263 }
264 if name.eq_ignore_ascii_case("duckdb_views") {
265 return Some(Self::DuckdbViews);
266 }
267 if name.eq_ignore_ascii_case("duckdb_columns") {
268 return Some(Self::DuckdbColumns);
269 }
270 if name.eq_ignore_ascii_case("duckdb_extensions") {
271 return Some(Self::DuckdbExtensions);
272 }
273 if name.eq_ignore_ascii_case("duckdb_optimizers") {
274 return Some(Self::DuckdbOptimizers);
275 }
276 if name.eq_ignore_ascii_case("duckdb_dialects") {
277 return Some(Self::DuckdbDialects);
278 }
279 if name.eq_ignore_ascii_case("duckdb_grammar_extensions") {
280 return Some(Self::DuckdbGrammarExtensions);
281 }
282 if name.eq_ignore_ascii_case("pragma_table_info") {
283 return Some(Self::PragmaTableInfo);
284 }
285 if name.eq_ignore_ascii_case("pragma_show") {
286 return Some(Self::PragmaShow);
287 }
288 if name.eq_ignore_ascii_case("pragma_version") {
289 return Some(Self::PragmaVersion);
290 }
291 if name.eq_ignore_ascii_case("pragma_platform") {
292 return Some(Self::PragmaPlatform);
293 }
294 if name.eq_ignore_ascii_case("pragma_user_agent") {
295 return Some(Self::PragmaUserAgent);
296 }
297 if name.eq_ignore_ascii_case("pragma_database_size") {
298 return Some(Self::PragmaDatabaseSize);
299 }
300 None
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum Columns {
313 Fixed(Vec<Field>),
315 Parquet,
317 Csv,
319}
320
321#[derive(Debug, Clone, PartialEq, Eq)]
323pub struct ResolvedTable {
324 pub function: TableFunction,
326 pub arguments: Vec<LogicalType>,
328 pub columns: Columns,
330}
331
332pub fn resolve_table(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
348 let Some(function) = TableFunction::lookup(name) else {
349 return Err(Error::catalog(format!("Table Function with name {name} does not exist!")));
350 };
351 if let Some(columns) = file_columns(function) {
352 let list = LogicalType::list(LogicalType::Varchar);
357 let single = arguments.len() == 1 && arguments[0] == LogicalType::Varchar;
358 let many = arguments.len() == 1 && arguments[0] == list;
359 let nothing = arguments.len() == 1 && arguments[0] == LogicalType::Null;
363 if !single && !many && !nothing {
364 return Err(no_overload(function, arguments));
365 }
366 let wanted = if many {
367 list
368 } else if nothing {
369 LogicalType::Null
370 } else {
371 LogicalType::Varchar
372 };
373 return Ok(ResolvedTable { function, arguments: vec![wanted], columns });
374 }
375 if function.takes_a_name() {
376 let single = arguments.len() == 1
381 && matches!(arguments[0], LogicalType::Varchar | LogicalType::Null);
382 if !single {
383 return Err(one_name(function, arguments));
384 }
385 return Ok(ResolvedTable {
386 function,
387 arguments: vec![arguments[0].clone()],
388 columns: Columns::Fixed(name_columns(function)),
389 });
390 }
391 let arity = arguments.len();
392 if let Some(columns) = fixed_columns(function) {
396 if arity != 0 {
397 return Err(nothing_at_all(function, arguments));
398 }
399 return Ok(ResolvedTable {
400 function,
401 arguments: Vec::new(),
402 columns: Columns::Fixed(columns),
403 });
404 }
405 if !(1..=3).contains(&arity) {
406 return Err(Error::binder(format!(
407 "Table function {}() takes between 1 and 3 arguments, {arity} were given",
408 function.name()
409 )));
410 }
411 Ok(ResolvedTable {
412 function,
413 arguments: vec![LogicalType::BigInt; arity],
414 columns: Columns::Fixed(vec![Field::new(function.name(), LogicalType::BigInt)]),
415 })
416}
417
418pub fn resolve_pragma(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
435 let error = match resolve_table(name, arguments) {
436 Ok(resolved) => return Ok(resolved),
437 Err(error) => error,
438 };
439 let Some(function) = TableFunction::lookup(name) else {
440 return Err(error);
441 };
442 let spelled = name.strip_prefix("pragma_").unwrap_or(name);
443 let takes = if function.takes_a_name() { "(VARCHAR)" } else { "" };
447 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
448 Err(Error::binder(format!(
449 "No function matches the given name and argument types '{spelled}({})'. You might need to \
450 add explicit type casts.\n\tCandidate functions:\n\tPRAGMA \"{spelled}\"{takes}\n",
451 written.join(", ")
452 )))
453}
454
455fn file_columns(function: TableFunction) -> Option<Columns> {
458 match function {
459 TableFunction::ReadParquet => Some(Columns::Parquet),
460 TableFunction::ReadCsv => Some(Columns::Csv),
461 TableFunction::Range
462 | TableFunction::GenerateSeries
463 | TableFunction::RudbStrategies
464 | TableFunction::DuckdbKeywords
465 | TableFunction::DuckdbTypes
466 | TableFunction::DuckdbFunctions
467 | TableFunction::DuckdbSettings
468 | TableFunction::DuckdbDatabases
469 | TableFunction::DuckdbSchemas
470 | TableFunction::DuckdbTables
471 | TableFunction::DuckdbViews
472 | TableFunction::DuckdbColumns
473 | TableFunction::DuckdbExtensions
474 | TableFunction::DuckdbOptimizers
475 | TableFunction::DuckdbDialects
476 | TableFunction::DuckdbGrammarExtensions
477 | TableFunction::PragmaTableInfo
478 | TableFunction::PragmaShow
479 | TableFunction::PragmaVersion
480 | TableFunction::PragmaPlatform
481 | TableFunction::PragmaUserAgent
482 | TableFunction::PragmaDatabaseSize => None,
483 }
484}
485
486fn fixed_columns(function: TableFunction) -> Option<Vec<Field>> {
489 match function {
490 TableFunction::RudbStrategies => Some(strategy_fields()),
491 TableFunction::DuckdbKeywords => Some(keyword_fields()),
492 TableFunction::DuckdbTypes => Some(type_fields()),
493 TableFunction::DuckdbFunctions => Some(function_fields()),
494 TableFunction::DuckdbSettings => Some(setting_fields()),
495 TableFunction::DuckdbDatabases => Some(database_fields()),
496 TableFunction::DuckdbSchemas => Some(schema_fields()),
497 TableFunction::DuckdbTables => Some(table_fields()),
498 TableFunction::DuckdbViews => Some(view_fields()),
499 TableFunction::DuckdbColumns => Some(column_fields()),
500 TableFunction::DuckdbExtensions => Some(extension_fields()),
501 TableFunction::DuckdbOptimizers => Some(optimizer_fields()),
502 TableFunction::DuckdbDialects => Some(dialect_fields()),
503 TableFunction::DuckdbGrammarExtensions => Some(grammar_extension_fields()),
504 TableFunction::PragmaVersion => Some(version_fields()),
505 TableFunction::PragmaPlatform => Some(platform_fields()),
506 TableFunction::PragmaUserAgent => Some(user_agent_fields()),
507 TableFunction::PragmaDatabaseSize => Some(database_size_fields()),
508 TableFunction::Range
509 | TableFunction::GenerateSeries
510 | TableFunction::ReadParquet
511 | TableFunction::ReadCsv
512 | TableFunction::PragmaTableInfo
513 | TableFunction::PragmaShow => None,
514 }
515}
516
517fn name_columns(function: TableFunction) -> Vec<Field> {
519 match function {
520 TableFunction::PragmaShow => describe_fields(),
521 _ => table_info_fields(),
522 }
523}
524
525#[must_use]
537pub fn table_info_fields() -> Vec<Field> {
538 vec![
539 Field::new("cid", LogicalType::Integer),
540 Field::new("name", LogicalType::Varchar),
541 Field::new("type", LogicalType::Varchar),
542 Field::new("notnull", LogicalType::Boolean),
543 Field::new("dflt_value", LogicalType::Varchar),
544 Field::new("pk", LogicalType::Boolean),
545 ]
546}
547
548#[must_use]
554pub fn describe_fields() -> Vec<Field> {
555 ["column_name", "column_type", "null", "key", "default", "extra"]
556 .iter()
557 .map(|name| Field::new(*name, LogicalType::Varchar))
558 .collect()
559}
560
561#[must_use]
569pub fn version_fields() -> Vec<Field> {
570 ["library_version", "source_id", "codename"]
571 .iter()
572 .map(|name| Field::new(*name, LogicalType::Varchar))
573 .collect()
574}
575
576#[must_use]
578pub fn platform_fields() -> Vec<Field> {
579 vec![Field::new("platform", LogicalType::Varchar)]
580}
581
582#[must_use]
584pub fn user_agent_fields() -> Vec<Field> {
585 vec![Field::new("user_agent", LogicalType::Varchar)]
586}
587
588#[must_use]
595pub fn database_size_fields() -> Vec<Field> {
596 vec![
597 Field::new("database_name", LogicalType::Varchar),
598 Field::new("database_size", LogicalType::Varchar),
599 Field::new("block_size", LogicalType::BigInt),
600 Field::new("total_blocks", LogicalType::BigInt),
601 Field::new("used_blocks", LogicalType::BigInt),
602 Field::new("free_blocks", LogicalType::BigInt),
603 Field::new("wal_size", LogicalType::Varchar),
604 Field::new("memory_usage", LogicalType::Varchar),
605 Field::new("memory_limit", LogicalType::Varchar),
606 ]
607}
608
609#[must_use]
622pub fn strategy_fields() -> Vec<Field> {
623 vec![
624 Field::new("seam", LogicalType::Varchar),
625 Field::new("milestone", LogicalType::Varchar),
626 Field::new("seam_description", LogicalType::Varchar),
627 Field::new("implementation", LogicalType::Varchar),
628 Field::new("implementation_description", LogicalType::Varchar),
629 Field::new("provenance", LogicalType::Varchar),
630 Field::new("determinism", LogicalType::Varchar),
631 Field::new("is_reference", LogicalType::Boolean),
632 Field::new("is_default", LogicalType::Boolean),
633 ]
634}
635
636#[must_use]
638pub fn keyword_fields() -> Vec<Field> {
639 vec![
640 Field::new("keyword_name", LogicalType::Varchar),
641 Field::new("keyword_category", LogicalType::Varchar),
642 ]
643}
644
645#[must_use]
651pub fn extension_fields() -> Vec<Field> {
652 vec![
653 Field::new("extension_name", LogicalType::Varchar),
654 Field::new("loaded", LogicalType::Boolean),
655 Field::new("installed", LogicalType::Boolean),
656 Field::new("install_path", LogicalType::Varchar),
657 Field::new("description", LogicalType::Varchar),
658 Field::new("aliases", LogicalType::list(LogicalType::Varchar)),
659 Field::new("extension_version", LogicalType::Varchar),
660 Field::new("install_mode", LogicalType::Varchar),
661 Field::new("installed_from", LogicalType::Varchar),
662 Field::new("signature_key_fingerprint", LogicalType::Varchar),
663 ]
664}
665
666#[must_use]
668pub fn optimizer_fields() -> Vec<Field> {
669 vec![Field::new("name", LogicalType::Varchar)]
670}
671
672#[must_use]
674pub fn dialect_fields() -> Vec<Field> {
675 vec![Field::new("dialect_name", LogicalType::Varchar)]
676}
677
678#[must_use]
680pub fn grammar_extension_fields() -> Vec<Field> {
681 vec![Field::new("name", LogicalType::Varchar), Field::new("description", LogicalType::Varchar)]
682}
683
684#[must_use]
701pub fn keyword_categories(classes: u8) -> Vec<&'static str> {
702 use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
703 let mut out = Vec::new();
704 if classes & RESERVED != 0 {
705 out.push("reserved");
706 }
707 if classes & UNRESERVED != 0 {
708 out.push("unreserved");
709 }
710 if classes & COLUMN_NAME != 0 {
711 out.push("column_name");
712 }
713 if classes & (FUNC_NAME | TYPE_NAME) != 0 {
714 out.push("type_function");
715 }
716 out
717}
718
719fn no_overload(function: TableFunction, arguments: &[LogicalType]) -> Error {
725 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
726 let name = function.name();
727 Error::binder(format!(
728 "No function matches the given name and argument types '{name}({})'. You might need to \
729 add explicit type casts.\n\tCandidate functions:\n\t{name}(VARCHAR)\n\t{name}(VARCHAR[])\n",
730 written.join(", ")
731 ))
732}
733
734fn one_name(function: TableFunction, arguments: &[LogicalType]) -> Error {
741 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
742 let name = function.name();
743 Error::binder(format!(
744 "No function matches the given name and argument types '{name}({})'. You might need to \
745 add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"(VARCHAR)\n",
746 written.join(", ")
747 ))
748}
749
750fn nothing_at_all(function: TableFunction, arguments: &[LogicalType]) -> Error {
758 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
759 let name = function.name();
760 Error::binder(format!(
761 "No function matches the given name and argument types '{name}({})'. You might need to \
762 add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"()\n",
763 written.join(", ")
764 ))
765}
766
767pub fn series(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<Vec<i64>> {
781 let count = series_length(function, start, stop, step)?;
782 let mut out = Vec::with_capacity(count);
783 let mut at = start;
784 for _ in 0..count {
785 out.push(at);
786 at = at.saturating_add(step);
790 }
791 Ok(out)
792}
793
794pub fn series_length(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<usize> {
807 if step == 0 {
808 return Err(Error::binder("interval cannot be 0!"));
809 }
810 Ok(length(function, start, stop, step))
811}
812
813fn length(function: TableFunction, start: i64, stop: i64, step: i64) -> usize {
819 let start = i128::from(start);
820 let stop = i128::from(stop);
821 let step = i128::from(step);
822 let span = if function.inclusive() {
823 if step > 0 { stop - start + 1 } else { stop - start - 1 }
824 } else {
825 stop - start
826 };
827 if (span > 0) != (step > 0) {
828 return 0;
829 }
830 let count = (span + step - step.signum()) / step;
832 usize::try_from(count).unwrap_or(usize::MAX)
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838
839 fn fixed(resolved: &ResolvedTable) -> &[Field] {
841 match &resolved.columns {
842 Columns::Fixed(fields) => fields,
843 Columns::Parquet | Columns::Csv => {
844 panic!("{} resolves to a file", resolved.function.name())
845 }
846 }
847 }
848
849 fn integers(count: usize) -> Vec<LogicalType> {
851 vec![LogicalType::BigInt; count]
852 }
853
854 #[test]
855 fn a_name_that_is_not_a_table_function_says_so_rather_than_binding() {
856 let error = resolve_table("read_csv", &integers(1)).unwrap_err();
857 assert!(error.to_string().contains("read_csv"), "{error}");
858 }
859
860 #[test]
861 fn both_names_resolve_and_each_one_names_its_own_column() {
862 let range = resolve_table("range", &integers(1)).unwrap();
863 assert_eq!(fixed(&range)[0].name, "range");
864 let series = resolve_table("GENERATE_SERIES", &integers(3)).unwrap();
865 assert_eq!(fixed(&series)[0].name, "generate_series");
866 assert_eq!(series.arguments.len(), 3);
867 }
868
869 #[test]
870 fn no_arguments_and_four_arguments_are_both_the_arity_error() {
871 assert!(resolve_table("range", &integers(0)).is_err());
872 assert!(resolve_table("range", &integers(4)).is_err());
873 }
874
875 #[test]
876 fn a_series_call_ignores_the_types_it_was_given_and_casts_them_all_to_bigint() {
877 let resolved =
878 resolve_table("range", &[LogicalType::Varchar, LogicalType::Double]).unwrap();
879 assert_eq!(resolved.arguments, integers(2));
880 }
881
882 #[test]
883 fn read_parquet_takes_one_string_and_says_its_columns_are_in_the_file() {
884 let resolved = resolve_table("read_parquet", &[LogicalType::Varchar]).unwrap();
885 assert_eq!(resolved.function, TableFunction::ReadParquet);
886 assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
887 assert_eq!(resolved.columns, Columns::Parquet);
888 }
889
890 #[test]
891 fn parquet_scan_is_the_same_function_under_duckdbs_other_name_for_it() {
892 assert_eq!(TableFunction::lookup("parquet_scan"), Some(TableFunction::ReadParquet));
893 let resolved = resolve_table("parquet_scan", &[LogicalType::Varchar]).unwrap();
895 assert_eq!(resolved.function.name(), "read_parquet");
896 }
897
898 #[test]
899 fn a_path_that_is_not_a_string_is_the_message_duckdb_gives_for_it() {
900 let error = resolve_table("read_parquet", &[LogicalType::Integer]).unwrap_err();
902 assert!(
903 error.message().starts_with(
904 "No function matches the given name and argument types 'read_parquet(INTEGER)'."
905 ),
906 "{error}"
907 );
908 assert!(error.message().contains("read_parquet(VARCHAR)"), "{error}");
909 }
910
911 #[test]
912 fn read_parquet_of_no_arguments_or_two_is_the_same_no_overload_message() {
913 let two = resolve_table("read_parquet", &[LogicalType::Varchar, LogicalType::Varchar]);
914 assert!(two.unwrap_err().message().contains("read_parquet(VARCHAR, VARCHAR)"));
915 let none = resolve_table("read_parquet", &[]);
916 assert!(none.unwrap_err().message().contains("read_parquet()"));
917 }
918
919 #[test]
920 fn range_stops_before_the_end_and_generate_series_stops_on_it() {
921 assert_eq!(series(TableFunction::Range, 0, 3, 1).unwrap(), vec![0, 1, 2]);
922 assert_eq!(series(TableFunction::GenerateSeries, 0, 3, 1).unwrap(), vec![0, 1, 2, 3]);
923 }
924
925 #[test]
926 fn a_step_that_does_not_divide_the_span_stops_before_the_end_of_it() {
927 assert_eq!(series(TableFunction::Range, 2, 7, 2).unwrap(), vec![2, 4, 6]);
930 assert_eq!(series(TableFunction::GenerateSeries, 2, 7, 2).unwrap(), vec![2, 4, 6]);
931 }
932
933 #[test]
934 fn the_four_categories_come_out_of_the_grammars_five_rules() {
935 use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
936 assert_eq!(keyword_categories(RESERVED), ["reserved"]);
937 assert_eq!(keyword_categories(UNRESERVED), ["unreserved"]);
938 assert_eq!(keyword_categories(COLUMN_NAME), ["column_name"]);
939 assert_eq!(keyword_categories(FUNC_NAME | TYPE_NAME), ["type_function"]);
943 assert_eq!(keyword_categories(TYPE_NAME), ["type_function"]);
944 assert_eq!(keyword_categories(FUNC_NAME), ["type_function"]);
945 assert_eq!(keyword_categories(COLUMN_NAME | FUNC_NAME), ["column_name", "type_function"]);
947 assert!(keyword_categories(0).is_empty());
950 }
951
952 #[test]
953 fn a_metadata_table_given_an_argument_says_it_takes_none() {
954 for name in [
955 "rudb_strategies",
956 "duckdb_keywords",
957 "duckdb_types",
958 "duckdb_functions",
959 "duckdb_settings",
960 "duckdb_databases",
961 "duckdb_schemas",
962 "duckdb_tables",
963 "duckdb_columns",
964 ] {
965 let function = TableFunction::lookup(name).expect("a known function");
966 let error = resolve_table(name, &[LogicalType::BigInt]).expect_err("takes none");
967 assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
968 let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
969 assert_eq!(resolved.function, function);
970 assert!(matches!(resolved.columns, Columns::Fixed(_)));
971 }
972 }
973
974 #[test]
975 fn duckdb_keywords_has_duckdbs_two_columns_under_that_name() {
976 let resolved = resolve_table("DuckDB_Keywords", &[]).expect("a case insensitive name");
977 assert_eq!(resolved.function, TableFunction::DuckdbKeywords);
978 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
979 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
980 assert_eq!(names, ["keyword_name", "keyword_category"]);
981 assert!(fields.iter().all(|field| field.ty == LogicalType::Varchar));
982 }
983
984 #[test]
985 fn duckdb_types_has_duckdbs_seventeen_columns_under_that_name() {
986 let resolved = resolve_table("DuckDB_Types", &[]).expect("a case insensitive name");
987 assert_eq!(resolved.function, TableFunction::DuckdbTypes);
988 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
989 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
990 assert_eq!(names.len(), 17);
991 assert_eq!(names[0], "database_name");
992 assert_eq!(names[16], "varargs");
993 let tags = fields.iter().find(|field| field.name == "tags").expect("a tags column");
996 assert_eq!(tags.ty, LogicalType::map(LogicalType::Varchar, LogicalType::Varchar));
997 }
998
999 #[test]
1000 fn duckdb_settings_has_duckdbs_seven_columns_under_that_name() {
1001 let resolved = resolve_table("DuckDB_Settings", &[]).expect("a case insensitive name");
1002 assert_eq!(resolved.function, TableFunction::DuckdbSettings);
1003 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
1004 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
1005 assert_eq!(
1006 names,
1007 ["name", "value", "description", "input_type", "scope", "aliases", "typed_value"]
1008 );
1009 assert_eq!(fields[6].ty, LogicalType::Varchar);
1011 }
1012
1013 #[test]
1014 fn a_negative_step_counts_down_and_stops_on_the_same_rule() {
1015 assert_eq!(series(TableFunction::Range, 5, 1, -2).unwrap(), vec![5, 3]);
1016 assert_eq!(series(TableFunction::GenerateSeries, 5, 1, -2).unwrap(), vec![5, 3, 1]);
1017 }
1018
1019 #[test]
1020 fn a_step_going_the_wrong_way_produces_nothing_rather_than_running_forever() {
1021 assert!(series(TableFunction::Range, 0, 10, -1).unwrap().is_empty());
1022 assert!(series(TableFunction::Range, 10, 0, 1).unwrap().is_empty());
1023 }
1024
1025 #[test]
1026 fn an_empty_range_and_a_single_value_series_are_the_boundary_between_the_two() {
1027 assert!(series(TableFunction::Range, 4, 4, 1).unwrap().is_empty());
1028 assert_eq!(series(TableFunction::GenerateSeries, 4, 4, 1).unwrap(), vec![4]);
1029 }
1030
1031 #[test]
1032 fn a_step_of_zero_is_the_one_case_that_is_an_error_rather_than_nothing() {
1033 let error = series(TableFunction::Range, 1, 5, 0).unwrap_err();
1034 assert!(error.to_string().contains("interval cannot be 0"), "{error}");
1035 }
1036
1037 #[test]
1038 fn a_span_that_does_not_fit_in_an_i64_does_not_overflow_the_length() {
1039 assert_eq!(length(TableFunction::Range, i64::MIN, i64::MAX, 1), usize::MAX);
1042 }
1043
1044 #[test]
1045 fn rudb_strategies_takes_no_arguments_and_produces_a_fixed_table() {
1046 let resolved = resolve_table("rudb_strategies", &[]).unwrap();
1047 assert_eq!(resolved.function, TableFunction::RudbStrategies);
1048 assert!(resolved.arguments.is_empty());
1049 assert_eq!(fixed(&resolved), strategy_fields());
1050 }
1051
1052 #[test]
1053 fn rudb_strategies_with_an_argument_says_it_takes_none() {
1054 let error = resolve_table("rudb_strategies", &[LogicalType::BigInt]).unwrap_err();
1055 assert!(error.to_string().contains("\"rudb_strategies\"()"), "{error}");
1056 assert!(error.to_string().contains("'rudb_strategies(BIGINT)'"), "{error}");
1057 }
1058
1059 #[test]
1060 fn the_two_pragmas_take_a_name_and_nothing_else_does() {
1061 assert!(TableFunction::PragmaTableInfo.takes_a_name());
1062 assert!(TableFunction::PragmaShow.takes_a_name());
1063 for other in [TableFunction::Range, TableFunction::DuckdbTables, TableFunction::ReadParquet]
1064 {
1065 assert!(!other.takes_a_name(), "{}", other.name());
1066 }
1067 }
1068
1069 #[test]
1070 fn pragma_table_info_answers_in_sqlites_six_columns() {
1071 let resolved = resolve_table("PRAGMA_Table_Info", &[LogicalType::Varchar])
1072 .expect("a case insensitive name");
1073 assert_eq!(resolved.function, TableFunction::PragmaTableInfo);
1074 assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
1075 let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1076 assert_eq!(names, ["cid", "name", "type", "notnull", "dflt_value", "pk"]);
1077 }
1078
1079 #[test]
1080 fn pragma_show_answers_in_the_six_columns_describe_answers_in() {
1081 let resolved =
1082 resolve_table("pragma_show", &[LogicalType::Varchar]).expect("one name, one overload");
1083 assert_eq!(resolved.function, TableFunction::PragmaShow);
1084 let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1085 assert_eq!(names, ["column_name", "column_type", "null", "key", "default", "extra"]);
1086 assert!(fixed(&resolved).iter().all(|field| field.ty == LogicalType::Varchar));
1087 }
1088
1089 #[test]
1090 fn a_null_name_resolves_because_the_catalog_is_what_turns_it_down() {
1091 let resolved = resolve_table("pragma_table_info", &[LogicalType::Null]).expect("a null");
1092 assert_eq!(resolved.arguments, vec![LogicalType::Null]);
1093 }
1094
1095 #[test]
1096 fn a_pragma_given_the_wrong_arguments_lists_its_one_overload() {
1097 for count in [0, 2] {
1098 let error = resolve_table("pragma_table_info", &integers(count)).expect_err("one name");
1099 assert!(
1100 error.message().starts_with(
1101 "No function matches the given name and argument types 'pragma_table_info("
1102 ),
1103 "{error}"
1104 );
1105 assert!(error.message().contains("\"pragma_table_info\"(VARCHAR)"), "{error}");
1106 }
1107 let error = resolve_table("pragma_show", &[LogicalType::Integer]).expect_err("a name");
1110 assert!(error.message().contains("'pragma_show(INTEGER)'"), "{error}");
1111 }
1112
1113 #[test]
1115 fn a_pragma_written_as_a_statement_is_complained_about_as_one() {
1116 let error = resolve_pragma("pragma_table_info", &integers(2)).expect_err("one name");
1117 assert!(
1118 error.message().starts_with(
1119 "No function matches the given name and argument types 'table_info(BIGINT, \
1120 BIGINT)'"
1121 ),
1122 "{error}"
1123 );
1124 assert!(error.message().contains("\tPRAGMA \"table_info\"(VARCHAR)\n"), "{error}");
1125 let error = resolve_pragma("pragma_version", &integers(1)).expect_err("nothing");
1128 assert!(error.message().contains("'version(BIGINT)'"), "{error}");
1129 assert!(error.message().ends_with("\tPRAGMA \"version\"\n"), "{error}");
1130 }
1131
1132 #[test]
1134 fn a_pragma_that_resolves_resolves_to_what_the_function_spelling_does() {
1135 let name = [LogicalType::Varchar];
1136 let written = resolve_pragma("pragma_table_info", &name).expect("one name");
1137 let called = resolve_table("pragma_table_info", &name).expect("one name");
1138 assert_eq!(written.function, called.function);
1139 assert_eq!(written.arguments, called.arguments);
1140 let written = resolve_pragma("pragma_version", &[]).expect("nothing");
1141 assert_eq!(written.function, TableFunction::PragmaVersion);
1142 }
1143
1144 #[test]
1145 fn the_four_pragmas_about_the_build_take_nothing_and_name_their_own_columns() {
1146 let wanted: [(&str, TableFunction, &[&str]); 4] = [
1147 (
1148 "PRAGMA_Version",
1149 TableFunction::PragmaVersion,
1150 &["library_version", "source_id", "codename"],
1151 ),
1152 ("pragma_platform", TableFunction::PragmaPlatform, &["platform"]),
1153 ("pragma_user_agent", TableFunction::PragmaUserAgent, &["user_agent"]),
1154 (
1155 "pragma_database_size",
1156 TableFunction::PragmaDatabaseSize,
1157 &[
1158 "database_name",
1159 "database_size",
1160 "block_size",
1161 "total_blocks",
1162 "used_blocks",
1163 "free_blocks",
1164 "wal_size",
1165 "memory_usage",
1166 "memory_limit",
1167 ],
1168 ),
1169 ];
1170 for (name, function, columns) in wanted {
1171 let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
1172 assert_eq!(resolved.function, function);
1173 assert!(resolved.arguments.is_empty());
1174 assert!(!function.takes_a_name(), "{name}");
1175 let written: Vec<&str> =
1176 fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1177 assert_eq!(written, columns);
1178 let error = resolve_table(name, &[LogicalType::Varchar]).expect_err("takes none");
1179 assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
1180 }
1181 }
1182
1183 #[test]
1184 fn the_four_block_columns_are_the_only_numbers_pragma_database_size_reports() {
1185 let fields = database_size_fields();
1189 let numbers: Vec<&str> = fields
1190 .iter()
1191 .filter(|field| field.ty == LogicalType::BigInt)
1192 .map(|field| field.name.as_str())
1193 .collect();
1194 assert_eq!(numbers, ["block_size", "total_blocks", "used_blocks", "free_blocks"]);
1195 assert!(fields.iter().filter(|field| field.ty == LogicalType::Varchar).count() == 5);
1196 }
1197}