1use rudb_common::{Error, Field, LogicalType, Result};
63
64use crate::entrycatalog::{
65 column_fields, database_fields, schema_fields, show_database_fields, show_expanded_fields,
66 show_table_fields, table_fields, view_fields,
67};
68use crate::functioncatalog::function_fields;
69use crate::settingcatalog::setting_fields;
70use crate::typecatalog::type_fields;
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum TableFunction {
78 Range,
80 GenerateSeries,
82 ReadParquet,
84 ReadCsv,
86 RudbStrategies,
88 DuckdbKeywords,
90 DuckdbTypes,
92 DuckdbFunctions,
94 DuckdbSettings,
96 DuckdbDatabases,
98 DuckdbSchemas,
100 DuckdbTables,
102 DuckdbViews,
104 DuckdbColumns,
106 DuckdbExtensions,
108 DuckdbOptimizers,
110 DuckdbDialects,
112 DuckdbGrammarExtensions,
114 PragmaTableInfo,
116 PragmaShow,
118 PragmaVersion,
120 PragmaPlatform,
122 PragmaUserAgent,
124 PragmaDatabaseSize,
126 PragmaShowTables,
128 PragmaShowDatabases,
130 PragmaShowTablesExpanded,
132}
133
134pub const FILE_ROW_NUMBER: &str = "file_row_number";
141
142impl TableFunction {
143 #[must_use]
145 pub const fn name(self) -> &'static str {
146 match self {
147 Self::Range => "range",
148 Self::GenerateSeries => "generate_series",
149 Self::ReadParquet => "read_parquet",
150 Self::ReadCsv => "read_csv",
151 Self::RudbStrategies => "rudb_strategies",
152 Self::DuckdbKeywords => "duckdb_keywords",
153 Self::DuckdbTypes => "duckdb_types",
154 Self::DuckdbFunctions => "duckdb_functions",
155 Self::DuckdbSettings => "duckdb_settings",
156 Self::DuckdbDatabases => "duckdb_databases",
157 Self::DuckdbSchemas => "duckdb_schemas",
158 Self::DuckdbTables => "duckdb_tables",
159 Self::DuckdbViews => "duckdb_views",
160 Self::DuckdbColumns => "duckdb_columns",
161 Self::DuckdbExtensions => "duckdb_extensions",
162 Self::DuckdbOptimizers => "duckdb_optimizers",
163 Self::DuckdbDialects => "duckdb_dialects",
164 Self::DuckdbGrammarExtensions => "duckdb_grammar_extensions",
165 Self::PragmaTableInfo => "pragma_table_info",
166 Self::PragmaShow => "pragma_show",
167 Self::PragmaVersion => "pragma_version",
168 Self::PragmaPlatform => "pragma_platform",
169 Self::PragmaUserAgent => "pragma_user_agent",
170 Self::PragmaDatabaseSize => "pragma_database_size",
171 Self::PragmaShowTables => "pragma_show_tables",
172 Self::PragmaShowDatabases => "pragma_show_databases",
173 Self::PragmaShowTablesExpanded => "pragma_show_tables_expanded",
174 }
175 }
176
177 #[must_use]
187 pub const fn reachable_as_a_function(self) -> bool {
188 !matches!(
189 self,
190 Self::PragmaShowTables | Self::PragmaShowDatabases | Self::PragmaShowTablesExpanded
191 )
192 }
193
194 #[must_use]
200 pub const fn takes_a_name(self) -> bool {
201 matches!(self, Self::PragmaTableInfo | Self::PragmaShow)
202 }
203
204 #[must_use]
209 pub const fn inclusive(self) -> bool {
210 matches!(self, Self::GenerateSeries)
211 }
212
213 #[must_use]
229 pub fn parameters(self) -> &'static [(&'static str, LogicalType)] {
230 static READ_PARQUET: &[(&str, LogicalType)] = &[
231 ("binary_as_string", LogicalType::Boolean),
232 ("file_row_number", LogicalType::Boolean),
233 ];
234 static READ_CSV: &[(&str, LogicalType)] = &[
235 ("all_varchar", LogicalType::Boolean),
236 ("delim", LogicalType::Varchar),
237 ("escape", LogicalType::Varchar),
238 ("header", LogicalType::Boolean),
239 ("quote", LogicalType::Varchar),
240 ("sep", LogicalType::Varchar),
241 ];
242 match self {
243 Self::ReadParquet => READ_PARQUET,
244 Self::ReadCsv => READ_CSV,
245 _ => &[],
246 }
247 }
248
249 #[must_use]
251 pub fn lookup(name: &str) -> Option<Self> {
252 if name.eq_ignore_ascii_case("range") {
253 return Some(Self::Range);
254 }
255 if name.eq_ignore_ascii_case("generate_series") {
256 return Some(Self::GenerateSeries);
257 }
258 if name.eq_ignore_ascii_case("read_parquet") || name.eq_ignore_ascii_case("parquet_scan") {
259 return Some(Self::ReadParquet);
260 }
261 if name.eq_ignore_ascii_case("read_csv") || name.eq_ignore_ascii_case("read_csv_auto") {
265 return Some(Self::ReadCsv);
266 }
267 if name.eq_ignore_ascii_case("rudb_strategies") {
268 return Some(Self::RudbStrategies);
269 }
270 if name.eq_ignore_ascii_case("duckdb_keywords") {
271 return Some(Self::DuckdbKeywords);
272 }
273 if name.eq_ignore_ascii_case("duckdb_types") {
274 return Some(Self::DuckdbTypes);
275 }
276 if name.eq_ignore_ascii_case("duckdb_functions") {
277 return Some(Self::DuckdbFunctions);
278 }
279 if name.eq_ignore_ascii_case("duckdb_settings") {
280 return Some(Self::DuckdbSettings);
281 }
282 if name.eq_ignore_ascii_case("duckdb_databases") {
283 return Some(Self::DuckdbDatabases);
284 }
285 if name.eq_ignore_ascii_case("duckdb_schemas") {
286 return Some(Self::DuckdbSchemas);
287 }
288 if name.eq_ignore_ascii_case("duckdb_tables") {
289 return Some(Self::DuckdbTables);
290 }
291 if name.eq_ignore_ascii_case("duckdb_views") {
292 return Some(Self::DuckdbViews);
293 }
294 if name.eq_ignore_ascii_case("duckdb_columns") {
295 return Some(Self::DuckdbColumns);
296 }
297 if name.eq_ignore_ascii_case("duckdb_extensions") {
298 return Some(Self::DuckdbExtensions);
299 }
300 if name.eq_ignore_ascii_case("duckdb_optimizers") {
301 return Some(Self::DuckdbOptimizers);
302 }
303 if name.eq_ignore_ascii_case("duckdb_dialects") {
304 return Some(Self::DuckdbDialects);
305 }
306 if name.eq_ignore_ascii_case("duckdb_grammar_extensions") {
307 return Some(Self::DuckdbGrammarExtensions);
308 }
309 if name.eq_ignore_ascii_case("pragma_table_info") {
310 return Some(Self::PragmaTableInfo);
311 }
312 if name.eq_ignore_ascii_case("pragma_show") {
313 return Some(Self::PragmaShow);
314 }
315 if name.eq_ignore_ascii_case("pragma_version") {
316 return Some(Self::PragmaVersion);
317 }
318 if name.eq_ignore_ascii_case("pragma_platform") {
319 return Some(Self::PragmaPlatform);
320 }
321 if name.eq_ignore_ascii_case("pragma_user_agent") {
322 return Some(Self::PragmaUserAgent);
323 }
324 if name.eq_ignore_ascii_case("pragma_database_size") {
325 return Some(Self::PragmaDatabaseSize);
326 }
327 if name.eq_ignore_ascii_case("pragma_show_tables") {
328 return Some(Self::PragmaShowTables);
329 }
330 if name.eq_ignore_ascii_case("pragma_show_databases") {
331 return Some(Self::PragmaShowDatabases);
332 }
333 if name.eq_ignore_ascii_case("pragma_show_tables_expanded") {
334 return Some(Self::PragmaShowTablesExpanded);
335 }
336 None
337 }
338}
339
340#[derive(Debug, Clone, PartialEq, Eq)]
348pub enum Columns {
349 Fixed(Vec<Field>),
351 Parquet,
353 Csv,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
359pub struct ResolvedTable {
360 pub function: TableFunction,
362 pub arguments: Vec<LogicalType>,
364 pub columns: Columns,
366}
367
368pub fn resolve_table(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
384 let function = match TableFunction::lookup(name) {
385 Some(function) if function.reachable_as_a_function() => function,
389 _ => {
390 return Err(Error::catalog(format!("Table Function with name {name} does not exist!")));
391 }
392 };
393 resolve_found(function, arguments)
394}
395
396fn resolve_found(function: TableFunction, arguments: &[LogicalType]) -> Result<ResolvedTable> {
401 if let Some(columns) = file_columns(function) {
402 let list = LogicalType::list(LogicalType::Varchar);
407 let single = arguments.len() == 1 && arguments[0] == LogicalType::Varchar;
408 let many = arguments.len() == 1 && arguments[0] == list;
409 let nothing = arguments.len() == 1 && arguments[0] == LogicalType::Null;
413 if !single && !many && !nothing {
414 return Err(no_overload(function, arguments));
415 }
416 let wanted = if many {
417 list
418 } else if nothing {
419 LogicalType::Null
420 } else {
421 LogicalType::Varchar
422 };
423 return Ok(ResolvedTable { function, arguments: vec![wanted], columns });
424 }
425 if function.takes_a_name() {
426 let single = arguments.len() == 1
431 && matches!(arguments[0], LogicalType::Varchar | LogicalType::Null);
432 if !single {
433 return Err(one_name(function, arguments));
434 }
435 return Ok(ResolvedTable {
436 function,
437 arguments: vec![arguments[0].clone()],
438 columns: Columns::Fixed(name_columns(function)),
439 });
440 }
441 let arity = arguments.len();
442 if let Some(columns) = fixed_columns(function) {
446 if arity != 0 {
447 return Err(nothing_at_all(function, arguments));
448 }
449 return Ok(ResolvedTable {
450 function,
451 arguments: Vec::new(),
452 columns: Columns::Fixed(columns),
453 });
454 }
455 if !(1..=3).contains(&arity) {
456 return Err(Error::binder(format!(
457 "Table function {}() takes between 1 and 3 arguments, {arity} were given",
458 function.name()
459 )));
460 }
461 Ok(ResolvedTable {
462 function,
463 arguments: vec![LogicalType::BigInt; arity],
464 columns: Columns::Fixed(vec![Field::new(function.name(), LogicalType::BigInt)]),
465 })
466}
467
468pub fn resolve_pragma(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
485 let Some(function) = TableFunction::lookup(name) else {
486 return Err(Error::catalog(format!("Table Function with name {name} does not exist!")));
487 };
488 if let Ok(resolved) = resolve_found(function, arguments) {
491 return Ok(resolved);
492 }
493 let spelled = name.strip_prefix("pragma_").unwrap_or(name);
494 let takes = if function.takes_a_name() { "(VARCHAR)" } else { "" };
498 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
499 Err(Error::binder(format!(
500 "No function matches the given name and argument types '{spelled}({})'. You might need to \
501 add explicit type casts.\n\tCandidate functions:\n\tPRAGMA \"{spelled}\"{takes}\n",
502 written.join(", ")
503 )))
504}
505
506fn file_columns(function: TableFunction) -> Option<Columns> {
509 match function {
510 TableFunction::ReadParquet => Some(Columns::Parquet),
511 TableFunction::ReadCsv => Some(Columns::Csv),
512 TableFunction::Range
513 | TableFunction::GenerateSeries
514 | TableFunction::RudbStrategies
515 | TableFunction::DuckdbKeywords
516 | TableFunction::DuckdbTypes
517 | TableFunction::DuckdbFunctions
518 | TableFunction::DuckdbSettings
519 | TableFunction::DuckdbDatabases
520 | TableFunction::DuckdbSchemas
521 | TableFunction::DuckdbTables
522 | TableFunction::DuckdbViews
523 | TableFunction::DuckdbColumns
524 | TableFunction::DuckdbExtensions
525 | TableFunction::DuckdbOptimizers
526 | TableFunction::DuckdbDialects
527 | TableFunction::DuckdbGrammarExtensions
528 | TableFunction::PragmaTableInfo
529 | TableFunction::PragmaShow
530 | TableFunction::PragmaVersion
531 | TableFunction::PragmaPlatform
532 | TableFunction::PragmaUserAgent
533 | TableFunction::PragmaDatabaseSize
534 | TableFunction::PragmaShowTables
535 | TableFunction::PragmaShowDatabases
536 | TableFunction::PragmaShowTablesExpanded => None,
537 }
538}
539
540fn fixed_columns(function: TableFunction) -> Option<Vec<Field>> {
543 match function {
544 TableFunction::RudbStrategies => Some(strategy_fields()),
545 TableFunction::DuckdbKeywords => Some(keyword_fields()),
546 TableFunction::DuckdbTypes => Some(type_fields()),
547 TableFunction::DuckdbFunctions => Some(function_fields()),
548 TableFunction::DuckdbSettings => Some(setting_fields()),
549 TableFunction::DuckdbDatabases => Some(database_fields()),
550 TableFunction::DuckdbSchemas => Some(schema_fields()),
551 TableFunction::DuckdbTables => Some(table_fields()),
552 TableFunction::DuckdbViews => Some(view_fields()),
553 TableFunction::DuckdbColumns => Some(column_fields()),
554 TableFunction::DuckdbExtensions => Some(extension_fields()),
555 TableFunction::DuckdbOptimizers => Some(optimizer_fields()),
556 TableFunction::DuckdbDialects => Some(dialect_fields()),
557 TableFunction::DuckdbGrammarExtensions => Some(grammar_extension_fields()),
558 TableFunction::PragmaVersion => Some(version_fields()),
559 TableFunction::PragmaPlatform => Some(platform_fields()),
560 TableFunction::PragmaUserAgent => Some(user_agent_fields()),
561 TableFunction::PragmaDatabaseSize => Some(database_size_fields()),
562 TableFunction::PragmaShowTables => Some(show_table_fields()),
563 TableFunction::PragmaShowDatabases => Some(show_database_fields()),
564 TableFunction::PragmaShowTablesExpanded => Some(show_expanded_fields()),
565 TableFunction::Range
566 | TableFunction::GenerateSeries
567 | TableFunction::ReadParquet
568 | TableFunction::ReadCsv
569 | TableFunction::PragmaTableInfo
570 | TableFunction::PragmaShow => None,
571 }
572}
573
574fn name_columns(function: TableFunction) -> Vec<Field> {
576 match function {
577 TableFunction::PragmaShow => describe_fields(),
578 _ => table_info_fields(),
579 }
580}
581
582#[must_use]
594pub fn table_info_fields() -> Vec<Field> {
595 vec![
596 Field::new("cid", LogicalType::Integer),
597 Field::new("name", LogicalType::Varchar),
598 Field::new("type", LogicalType::Varchar),
599 Field::new("notnull", LogicalType::Boolean),
600 Field::new("dflt_value", LogicalType::Varchar),
601 Field::new("pk", LogicalType::Boolean),
602 ]
603}
604
605#[must_use]
611pub fn describe_fields() -> Vec<Field> {
612 ["column_name", "column_type", "null", "key", "default", "extra"]
613 .iter()
614 .map(|name| Field::new(*name, LogicalType::Varchar))
615 .collect()
616}
617
618#[must_use]
626pub fn version_fields() -> Vec<Field> {
627 ["library_version", "source_id", "codename"]
628 .iter()
629 .map(|name| Field::new(*name, LogicalType::Varchar))
630 .collect()
631}
632
633#[must_use]
635pub fn platform_fields() -> Vec<Field> {
636 vec![Field::new("platform", LogicalType::Varchar)]
637}
638
639#[must_use]
641pub fn user_agent_fields() -> Vec<Field> {
642 vec![Field::new("user_agent", LogicalType::Varchar)]
643}
644
645#[must_use]
652pub fn database_size_fields() -> Vec<Field> {
653 vec![
654 Field::new("database_name", LogicalType::Varchar),
655 Field::new("database_size", LogicalType::Varchar),
656 Field::new("block_size", LogicalType::BigInt),
657 Field::new("total_blocks", LogicalType::BigInt),
658 Field::new("used_blocks", LogicalType::BigInt),
659 Field::new("free_blocks", LogicalType::BigInt),
660 Field::new("wal_size", LogicalType::Varchar),
661 Field::new("memory_usage", LogicalType::Varchar),
662 Field::new("memory_limit", LogicalType::Varchar),
663 ]
664}
665
666#[must_use]
679pub fn strategy_fields() -> Vec<Field> {
680 vec![
681 Field::new("seam", LogicalType::Varchar),
682 Field::new("milestone", LogicalType::Varchar),
683 Field::new("seam_description", LogicalType::Varchar),
684 Field::new("implementation", LogicalType::Varchar),
685 Field::new("implementation_description", LogicalType::Varchar),
686 Field::new("provenance", LogicalType::Varchar),
687 Field::new("determinism", LogicalType::Varchar),
688 Field::new("is_reference", LogicalType::Boolean),
689 Field::new("is_default", LogicalType::Boolean),
690 ]
691}
692
693#[must_use]
695pub fn keyword_fields() -> Vec<Field> {
696 vec![
697 Field::new("keyword_name", LogicalType::Varchar),
698 Field::new("keyword_category", LogicalType::Varchar),
699 ]
700}
701
702#[must_use]
708pub fn extension_fields() -> Vec<Field> {
709 vec![
710 Field::new("extension_name", LogicalType::Varchar),
711 Field::new("loaded", LogicalType::Boolean),
712 Field::new("installed", LogicalType::Boolean),
713 Field::new("install_path", LogicalType::Varchar),
714 Field::new("description", LogicalType::Varchar),
715 Field::new("aliases", LogicalType::list(LogicalType::Varchar)),
716 Field::new("extension_version", LogicalType::Varchar),
717 Field::new("install_mode", LogicalType::Varchar),
718 Field::new("installed_from", LogicalType::Varchar),
719 Field::new("signature_key_fingerprint", LogicalType::Varchar),
720 ]
721}
722
723#[must_use]
725pub fn optimizer_fields() -> Vec<Field> {
726 vec![Field::new("name", LogicalType::Varchar)]
727}
728
729#[must_use]
731pub fn dialect_fields() -> Vec<Field> {
732 vec![Field::new("dialect_name", LogicalType::Varchar)]
733}
734
735#[must_use]
737pub fn grammar_extension_fields() -> Vec<Field> {
738 vec![Field::new("name", LogicalType::Varchar), Field::new("description", LogicalType::Varchar)]
739}
740
741#[must_use]
758pub fn keyword_categories(classes: u8) -> Vec<&'static str> {
759 use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
760 let mut out = Vec::new();
761 if classes & RESERVED != 0 {
762 out.push("reserved");
763 }
764 if classes & UNRESERVED != 0 {
765 out.push("unreserved");
766 }
767 if classes & COLUMN_NAME != 0 {
768 out.push("column_name");
769 }
770 if classes & (FUNC_NAME | TYPE_NAME) != 0 {
771 out.push("type_function");
772 }
773 out
774}
775
776fn no_overload(function: TableFunction, arguments: &[LogicalType]) -> Error {
782 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
783 let name = function.name();
784 Error::binder(format!(
785 "No function matches the given name and argument types '{name}({})'. You might need to \
786 add explicit type casts.\n\tCandidate functions:\n\t{name}(VARCHAR)\n\t{name}(VARCHAR[])\n",
787 written.join(", ")
788 ))
789}
790
791fn one_name(function: TableFunction, arguments: &[LogicalType]) -> Error {
798 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
799 let name = function.name();
800 Error::binder(format!(
801 "No function matches the given name and argument types '{name}({})'. You might need to \
802 add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"(VARCHAR)\n",
803 written.join(", ")
804 ))
805}
806
807fn nothing_at_all(function: TableFunction, arguments: &[LogicalType]) -> Error {
815 let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
816 let name = function.name();
817 Error::binder(format!(
818 "No function matches the given name and argument types '{name}({})'. You might need to \
819 add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"()\n",
820 written.join(", ")
821 ))
822}
823
824pub fn series(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<Vec<i64>> {
838 let count = series_length(function, start, stop, step)?;
839 let mut out = Vec::with_capacity(count);
840 let mut at = start;
841 for _ in 0..count {
842 out.push(at);
843 at = at.saturating_add(step);
847 }
848 Ok(out)
849}
850
851pub fn series_length(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<usize> {
864 if step == 0 {
865 return Err(Error::binder("interval cannot be 0!"));
866 }
867 Ok(length(function, start, stop, step))
868}
869
870fn length(function: TableFunction, start: i64, stop: i64, step: i64) -> usize {
876 let start = i128::from(start);
877 let stop = i128::from(stop);
878 let step = i128::from(step);
879 let span = if function.inclusive() {
880 if step > 0 { stop - start + 1 } else { stop - start - 1 }
881 } else {
882 stop - start
883 };
884 if (span > 0) != (step > 0) {
885 return 0;
886 }
887 let count = (span + step - step.signum()) / step;
889 usize::try_from(count).unwrap_or(usize::MAX)
890}
891
892#[cfg(test)]
893mod tests {
894 use super::*;
895
896 fn fixed(resolved: &ResolvedTable) -> &[Field] {
898 match &resolved.columns {
899 Columns::Fixed(fields) => fields,
900 Columns::Parquet | Columns::Csv => {
901 panic!("{} resolves to a file", resolved.function.name())
902 }
903 }
904 }
905
906 fn integers(count: usize) -> Vec<LogicalType> {
908 vec![LogicalType::BigInt; count]
909 }
910
911 #[test]
912 fn a_name_that_is_not_a_table_function_says_so_rather_than_binding() {
913 let error = resolve_table("read_csv", &integers(1)).unwrap_err();
914 assert!(error.to_string().contains("read_csv"), "{error}");
915 }
916
917 #[test]
918 fn both_names_resolve_and_each_one_names_its_own_column() {
919 let range = resolve_table("range", &integers(1)).unwrap();
920 assert_eq!(fixed(&range)[0].name, "range");
921 let series = resolve_table("GENERATE_SERIES", &integers(3)).unwrap();
922 assert_eq!(fixed(&series)[0].name, "generate_series");
923 assert_eq!(series.arguments.len(), 3);
924 }
925
926 #[test]
927 fn no_arguments_and_four_arguments_are_both_the_arity_error() {
928 assert!(resolve_table("range", &integers(0)).is_err());
929 assert!(resolve_table("range", &integers(4)).is_err());
930 }
931
932 #[test]
933 fn a_series_call_ignores_the_types_it_was_given_and_casts_them_all_to_bigint() {
934 let resolved =
935 resolve_table("range", &[LogicalType::Varchar, LogicalType::Double]).unwrap();
936 assert_eq!(resolved.arguments, integers(2));
937 }
938
939 #[test]
940 fn read_parquet_takes_one_string_and_says_its_columns_are_in_the_file() {
941 let resolved = resolve_table("read_parquet", &[LogicalType::Varchar]).unwrap();
942 assert_eq!(resolved.function, TableFunction::ReadParquet);
943 assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
944 assert_eq!(resolved.columns, Columns::Parquet);
945 }
946
947 #[test]
948 fn parquet_scan_is_the_same_function_under_duckdbs_other_name_for_it() {
949 assert_eq!(TableFunction::lookup("parquet_scan"), Some(TableFunction::ReadParquet));
950 let resolved = resolve_table("parquet_scan", &[LogicalType::Varchar]).unwrap();
952 assert_eq!(resolved.function.name(), "read_parquet");
953 }
954
955 #[test]
956 fn a_path_that_is_not_a_string_is_the_message_duckdb_gives_for_it() {
957 let error = resolve_table("read_parquet", &[LogicalType::Integer]).unwrap_err();
959 assert!(
960 error.message().starts_with(
961 "No function matches the given name and argument types 'read_parquet(INTEGER)'."
962 ),
963 "{error}"
964 );
965 assert!(error.message().contains("read_parquet(VARCHAR)"), "{error}");
966 }
967
968 #[test]
969 fn read_parquet_of_no_arguments_or_two_is_the_same_no_overload_message() {
970 let two = resolve_table("read_parquet", &[LogicalType::Varchar, LogicalType::Varchar]);
971 assert!(two.unwrap_err().message().contains("read_parquet(VARCHAR, VARCHAR)"));
972 let none = resolve_table("read_parquet", &[]);
973 assert!(none.unwrap_err().message().contains("read_parquet()"));
974 }
975
976 #[test]
977 fn range_stops_before_the_end_and_generate_series_stops_on_it() {
978 assert_eq!(series(TableFunction::Range, 0, 3, 1).unwrap(), vec![0, 1, 2]);
979 assert_eq!(series(TableFunction::GenerateSeries, 0, 3, 1).unwrap(), vec![0, 1, 2, 3]);
980 }
981
982 #[test]
983 fn a_step_that_does_not_divide_the_span_stops_before_the_end_of_it() {
984 assert_eq!(series(TableFunction::Range, 2, 7, 2).unwrap(), vec![2, 4, 6]);
987 assert_eq!(series(TableFunction::GenerateSeries, 2, 7, 2).unwrap(), vec![2, 4, 6]);
988 }
989
990 #[test]
991 fn the_four_categories_come_out_of_the_grammars_five_rules() {
992 use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
993 assert_eq!(keyword_categories(RESERVED), ["reserved"]);
994 assert_eq!(keyword_categories(UNRESERVED), ["unreserved"]);
995 assert_eq!(keyword_categories(COLUMN_NAME), ["column_name"]);
996 assert_eq!(keyword_categories(FUNC_NAME | TYPE_NAME), ["type_function"]);
1000 assert_eq!(keyword_categories(TYPE_NAME), ["type_function"]);
1001 assert_eq!(keyword_categories(FUNC_NAME), ["type_function"]);
1002 assert_eq!(keyword_categories(COLUMN_NAME | FUNC_NAME), ["column_name", "type_function"]);
1004 assert!(keyword_categories(0).is_empty());
1007 }
1008
1009 #[test]
1010 fn a_metadata_table_given_an_argument_says_it_takes_none() {
1011 for name in [
1012 "rudb_strategies",
1013 "duckdb_keywords",
1014 "duckdb_types",
1015 "duckdb_functions",
1016 "duckdb_settings",
1017 "duckdb_databases",
1018 "duckdb_schemas",
1019 "duckdb_tables",
1020 "duckdb_columns",
1021 ] {
1022 let function = TableFunction::lookup(name).expect("a known function");
1023 let error = resolve_table(name, &[LogicalType::BigInt]).expect_err("takes none");
1024 assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
1025 let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
1026 assert_eq!(resolved.function, function);
1027 assert!(matches!(resolved.columns, Columns::Fixed(_)));
1028 }
1029 }
1030
1031 #[test]
1032 fn duckdb_keywords_has_duckdbs_two_columns_under_that_name() {
1033 let resolved = resolve_table("DuckDB_Keywords", &[]).expect("a case insensitive name");
1034 assert_eq!(resolved.function, TableFunction::DuckdbKeywords);
1035 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
1036 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
1037 assert_eq!(names, ["keyword_name", "keyword_category"]);
1038 assert!(fields.iter().all(|field| field.ty == LogicalType::Varchar));
1039 }
1040
1041 #[test]
1042 fn duckdb_types_has_duckdbs_seventeen_columns_under_that_name() {
1043 let resolved = resolve_table("DuckDB_Types", &[]).expect("a case insensitive name");
1044 assert_eq!(resolved.function, TableFunction::DuckdbTypes);
1045 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
1046 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
1047 assert_eq!(names.len(), 17);
1048 assert_eq!(names[0], "database_name");
1049 assert_eq!(names[16], "varargs");
1050 let tags = fields.iter().find(|field| field.name == "tags").expect("a tags column");
1053 assert_eq!(tags.ty, LogicalType::map(LogicalType::Varchar, LogicalType::Varchar));
1054 }
1055
1056 #[test]
1057 fn duckdb_settings_has_duckdbs_seven_columns_under_that_name() {
1058 let resolved = resolve_table("DuckDB_Settings", &[]).expect("a case insensitive name");
1059 assert_eq!(resolved.function, TableFunction::DuckdbSettings);
1060 let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
1061 let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
1062 assert_eq!(
1063 names,
1064 ["name", "value", "description", "input_type", "scope", "aliases", "typed_value"]
1065 );
1066 assert_eq!(fields[6].ty, LogicalType::Varchar);
1068 }
1069
1070 #[test]
1071 fn a_negative_step_counts_down_and_stops_on_the_same_rule() {
1072 assert_eq!(series(TableFunction::Range, 5, 1, -2).unwrap(), vec![5, 3]);
1073 assert_eq!(series(TableFunction::GenerateSeries, 5, 1, -2).unwrap(), vec![5, 3, 1]);
1074 }
1075
1076 #[test]
1077 fn a_step_going_the_wrong_way_produces_nothing_rather_than_running_forever() {
1078 assert!(series(TableFunction::Range, 0, 10, -1).unwrap().is_empty());
1079 assert!(series(TableFunction::Range, 10, 0, 1).unwrap().is_empty());
1080 }
1081
1082 #[test]
1083 fn an_empty_range_and_a_single_value_series_are_the_boundary_between_the_two() {
1084 assert!(series(TableFunction::Range, 4, 4, 1).unwrap().is_empty());
1085 assert_eq!(series(TableFunction::GenerateSeries, 4, 4, 1).unwrap(), vec![4]);
1086 }
1087
1088 #[test]
1089 fn a_step_of_zero_is_the_one_case_that_is_an_error_rather_than_nothing() {
1090 let error = series(TableFunction::Range, 1, 5, 0).unwrap_err();
1091 assert!(error.to_string().contains("interval cannot be 0"), "{error}");
1092 }
1093
1094 #[test]
1095 fn a_span_that_does_not_fit_in_an_i64_does_not_overflow_the_length() {
1096 assert_eq!(length(TableFunction::Range, i64::MIN, i64::MAX, 1), usize::MAX);
1099 }
1100
1101 #[test]
1102 fn rudb_strategies_takes_no_arguments_and_produces_a_fixed_table() {
1103 let resolved = resolve_table("rudb_strategies", &[]).unwrap();
1104 assert_eq!(resolved.function, TableFunction::RudbStrategies);
1105 assert!(resolved.arguments.is_empty());
1106 assert_eq!(fixed(&resolved), strategy_fields());
1107 }
1108
1109 #[test]
1110 fn rudb_strategies_with_an_argument_says_it_takes_none() {
1111 let error = resolve_table("rudb_strategies", &[LogicalType::BigInt]).unwrap_err();
1112 assert!(error.to_string().contains("\"rudb_strategies\"()"), "{error}");
1113 assert!(error.to_string().contains("'rudb_strategies(BIGINT)'"), "{error}");
1114 }
1115
1116 #[test]
1117 fn the_two_pragmas_take_a_name_and_nothing_else_does() {
1118 assert!(TableFunction::PragmaTableInfo.takes_a_name());
1119 assert!(TableFunction::PragmaShow.takes_a_name());
1120 for other in [TableFunction::Range, TableFunction::DuckdbTables, TableFunction::ReadParquet]
1121 {
1122 assert!(!other.takes_a_name(), "{}", other.name());
1123 }
1124 }
1125
1126 #[test]
1127 fn pragma_table_info_answers_in_sqlites_six_columns() {
1128 let resolved = resolve_table("PRAGMA_Table_Info", &[LogicalType::Varchar])
1129 .expect("a case insensitive name");
1130 assert_eq!(resolved.function, TableFunction::PragmaTableInfo);
1131 assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
1132 let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1133 assert_eq!(names, ["cid", "name", "type", "notnull", "dflt_value", "pk"]);
1134 }
1135
1136 #[test]
1137 fn pragma_show_answers_in_the_six_columns_describe_answers_in() {
1138 let resolved =
1139 resolve_table("pragma_show", &[LogicalType::Varchar]).expect("one name, one overload");
1140 assert_eq!(resolved.function, TableFunction::PragmaShow);
1141 let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1142 assert_eq!(names, ["column_name", "column_type", "null", "key", "default", "extra"]);
1143 assert!(fixed(&resolved).iter().all(|field| field.ty == LogicalType::Varchar));
1144 }
1145
1146 #[test]
1147 fn a_null_name_resolves_because_the_catalog_is_what_turns_it_down() {
1148 let resolved = resolve_table("pragma_table_info", &[LogicalType::Null]).expect("a null");
1149 assert_eq!(resolved.arguments, vec![LogicalType::Null]);
1150 }
1151
1152 #[test]
1153 fn a_pragma_given_the_wrong_arguments_lists_its_one_overload() {
1154 for count in [0, 2] {
1155 let error = resolve_table("pragma_table_info", &integers(count)).expect_err("one name");
1156 assert!(
1157 error.message().starts_with(
1158 "No function matches the given name and argument types 'pragma_table_info("
1159 ),
1160 "{error}"
1161 );
1162 assert!(error.message().contains("\"pragma_table_info\"(VARCHAR)"), "{error}");
1163 }
1164 let error = resolve_table("pragma_show", &[LogicalType::Integer]).expect_err("a name");
1167 assert!(error.message().contains("'pragma_show(INTEGER)'"), "{error}");
1168 }
1169
1170 #[test]
1172 fn a_pragma_written_as_a_statement_is_complained_about_as_one() {
1173 let error = resolve_pragma("pragma_table_info", &integers(2)).expect_err("one name");
1174 assert!(
1175 error.message().starts_with(
1176 "No function matches the given name and argument types 'table_info(BIGINT, \
1177 BIGINT)'"
1178 ),
1179 "{error}"
1180 );
1181 assert!(error.message().contains("\tPRAGMA \"table_info\"(VARCHAR)\n"), "{error}");
1182 let error = resolve_pragma("pragma_version", &integers(1)).expect_err("nothing");
1185 assert!(error.message().contains("'version(BIGINT)'"), "{error}");
1186 assert!(error.message().ends_with("\tPRAGMA \"version\"\n"), "{error}");
1187 }
1188
1189 #[test]
1191 fn a_pragma_that_resolves_resolves_to_what_the_function_spelling_does() {
1192 let name = [LogicalType::Varchar];
1193 let written = resolve_pragma("pragma_table_info", &name).expect("one name");
1194 let called = resolve_table("pragma_table_info", &name).expect("one name");
1195 assert_eq!(written.function, called.function);
1196 assert_eq!(written.arguments, called.arguments);
1197 let written = resolve_pragma("pragma_version", &[]).expect("nothing");
1198 assert_eq!(written.function, TableFunction::PragmaVersion);
1199 }
1200
1201 #[test]
1202 fn the_four_pragmas_about_the_build_take_nothing_and_name_their_own_columns() {
1203 let wanted: [(&str, TableFunction, &[&str]); 4] = [
1204 (
1205 "PRAGMA_Version",
1206 TableFunction::PragmaVersion,
1207 &["library_version", "source_id", "codename"],
1208 ),
1209 ("pragma_platform", TableFunction::PragmaPlatform, &["platform"]),
1210 ("pragma_user_agent", TableFunction::PragmaUserAgent, &["user_agent"]),
1211 (
1212 "pragma_database_size",
1213 TableFunction::PragmaDatabaseSize,
1214 &[
1215 "database_name",
1216 "database_size",
1217 "block_size",
1218 "total_blocks",
1219 "used_blocks",
1220 "free_blocks",
1221 "wal_size",
1222 "memory_usage",
1223 "memory_limit",
1224 ],
1225 ),
1226 ];
1227 for (name, function, columns) in wanted {
1228 let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
1229 assert_eq!(resolved.function, function);
1230 assert!(resolved.arguments.is_empty());
1231 assert!(!function.takes_a_name(), "{name}");
1232 let written: Vec<&str> =
1233 fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1234 assert_eq!(written, columns);
1235 let error = resolve_table(name, &[LogicalType::Varchar]).expect_err("takes none");
1236 assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
1237 }
1238 }
1239
1240 #[test]
1241 fn the_four_block_columns_are_the_only_numbers_pragma_database_size_reports() {
1242 let fields = database_size_fields();
1246 let numbers: Vec<&str> = fields
1247 .iter()
1248 .filter(|field| field.ty == LogicalType::BigInt)
1249 .map(|field| field.name.as_str())
1250 .collect();
1251 assert_eq!(numbers, ["block_size", "total_blocks", "used_blocks", "free_blocks"]);
1252 assert!(fields.iter().filter(|field| field.ty == LogicalType::Varchar).count() == 5);
1253 }
1254}