Skip to main content

rudb_functions/
table.rs

1//! What a table function call resolves to.
2//!
3//! A table function is a function written where a table goes, so `FROM range(10)` produces ten rows
4//! of one column the same way `FROM t` produces whatever is in `t`. That makes it a different
5//! resolution problem from [`crate::signature`]: the answer is not a return type, it is a list of
6//! columns, because the caller can alias them and select from them and join against them.
7//!
8//! Twenty two of them are here. `range` and `generate_series` between them account for two thousand
9//! records in DuckDB's `sqllogictest` corpus, because a test that needs a thousand rows should not
10//! have to write a thousand rows, and the corpus uses them the way a person uses a for loop. The
11//! difference between those two is one row: `range` stops before the end and `generate_series`
12//! stops on it, which is the difference between a half open interval and a closed one, and it is
13//! the only difference. Nothing else about them differs, including the name of the column, which is
14//! the function's own name in both cases.
15//!
16//! `read_parquet` and `read_csv` are the other two and they are a different kind of thing, because
17//! their columns are in the file rather than in this table. That is what [`Columns`] exists to say.
18//! A caller that resolves one of those has to open the file to finish resolving it, and
19//! [`crate::file`] is where that happens. For CSV there is nothing in the file that states the
20//! columns either, so opening it means sniffing it.
21//!
22//! The next ten are the third kind, a table whose rows are a fact about the engine rather than data
23//! somebody stored. All ten take no arguments and all ten know their own columns, so resolving one
24//! is the simplest case in this file and they share an arm. `rudb_strategies()` is not
25//! a DuckDB function at all: it lists every seam in the engine and every implementation registered
26//! against it, which is how a reader finds out what this engine will let them swap and what it lets
27//! them swap today. `duckdb_keywords()` is every word the grammar knows about, which this crate can
28//! answer because the grammar is vendored. `duckdb_types()` is every type name the engine has and
29//! `duckdb_functions()` is every function, and both of their lists are in this crate because which
30//! names exist is a fact about the type system and the function library rather than about the
31//! executor. `duckdb_settings()` is every setting `SET` will take, and it is the one of the five
32//! whose rows are not all known here: the names and the descriptions are, and the values come from
33//! the session the query is running in. `duckdb_databases()`, `duckdb_schemas()`, `duckdb_tables()`
34//! `duckdb_views()` and `duckdb_columns()` are the last five and they are further from this crate
35//! again, because their rows are whatever somebody created, so only their columns are here and
36//! [`crate::entrycatalog`] says why.
37//!
38//! `duckdb_extensions()` and `duckdb_optimizers()` are two more of that third kind and they are the
39//! two where rudb has to answer about itself rather than reproduce a list. `duckdb_optimizers()` is
40//! every name `SET disabled_optimizers` takes, which is DuckDB's forty four, because rudb takes all
41//! forty four and turning off a pass that was never written is a request that has already been
42//! granted. `duckdb_extensions()` is the same names DuckDB's default build advertises with rudb's own
43//! answer in the two boolean columns, and `rudb_exec` says which two are true and why.
44//!
45//! `pragma_version()`, `pragma_platform()`, `pragma_user_agent()` and `pragma_database_size()` are
46//! four more of that third kind and they are the four where the fact is about this build and this
47//! process rather than about the language. The first three are a constant worked out from the crate
48//! version and the target, and the fourth reads the catalog and the memory budget, so like
49//! `duckdb_settings()` its rows are not all known here. `rudb_exec::enginenames` decides all four
50//! values and argues there for why they describe rudb rather than reporting DuckDB's answers.
51//!
52//! D2 adds a few more of that third kind. Each one is a column list here and a list of rows in
53//! `rudb_exec::metadata`, and nothing else.
54//!
55//! `pragma_table_info()` and `pragma_show()` are a fourth kind and the first two of the pragma
56//! family. They take one table name and describe whatever it names, so their columns are fixed and
57//! their rows are not a fact about the engine at all, they are a fact about one entry in a catalog.
58//! That makes them the first table functions here whose answer the binder settles on its own: it
59//! binds the name the way `DESCRIBE` binds one and hands back the rows, which is why nothing in
60//! `rudb_exec` knows either name.
61
62use 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/// Which table function a call resolved to.
73///
74/// An enum rather than a name, because the executor dispatches on this and a string comparison per
75/// operator build is a string comparison that can be spelled wrong.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum TableFunction {
78    /// `range(stop)`, `range(start, stop)`, `range(start, stop, step)`, stopping before the end.
79    Range,
80    /// The same three, stopping on the end.
81    GenerateSeries,
82    /// `read_parquet(path)`, the rows of a Parquet file.
83    ReadParquet,
84    /// `read_csv(path)`, the rows of a CSV file, with everything about how it is written sniffed.
85    ReadCsv,
86    /// `rudb_strategies()`, every seam and every implementation registered against it.
87    RudbStrategies,
88    /// `duckdb_keywords()`, every word the grammar knows and which class each one is in.
89    DuckdbKeywords,
90    /// `duckdb_types()`, every type name the engine knows and what each one stands for.
91    DuckdbTypes,
92    /// `duckdb_functions()`, every function the engine knows and what each one takes.
93    DuckdbFunctions,
94    /// `duckdb_settings()`, every setting `SET` will take and what each one is now.
95    DuckdbSettings,
96    /// `duckdb_databases()`, every database attached to this session.
97    DuckdbDatabases,
98    /// `duckdb_schemas()`, every schema in every one of them.
99    DuckdbSchemas,
100    /// `duckdb_tables()`, every base table somebody created.
101    DuckdbTables,
102    /// `duckdb_views()`, every view somebody created.
103    DuckdbViews,
104    /// `duckdb_columns()`, every column of every one of those.
105    DuckdbColumns,
106    /// `duckdb_extensions()`, every extension DuckDB names and whether this engine has it.
107    DuckdbExtensions,
108    /// `duckdb_optimizers()`, every name `SET disabled_optimizers` takes.
109    DuckdbOptimizers,
110    /// `duckdb_dialects()`, every installed SQL parser dialect.
111    DuckdbDialects,
112    /// `duckdb_grammar_extensions()`, every installed grammar extension.
113    DuckdbGrammarExtensions,
114    /// `pragma_table_info(name)`, the columns of one table or view, in SQLite's six columns.
115    PragmaTableInfo,
116    /// `pragma_show(name)`, the same columns again in the six `DESCRIBE` answers with.
117    PragmaShow,
118    /// `pragma_version()`, the version of the engine answering, in three columns.
119    PragmaVersion,
120    /// `pragma_platform()`, the operating system and processor this build was made for.
121    PragmaPlatform,
122    /// `pragma_user_agent()`, the one line a client sends when it says who it is.
123    PragmaUserAgent,
124    /// `pragma_database_size()`, what each attached database costs on disk and in memory.
125    PragmaDatabaseSize,
126    /// `PRAGMA show_tables`, the name of everything an unqualified name can reach.
127    PragmaShowTables,
128    /// `PRAGMA show_databases`, the name of everything that is attached.
129    PragmaShowDatabases,
130    /// `PRAGMA show_tables_expanded`, every table and view anywhere with its columns beside it.
131    PragmaShowTablesExpanded,
132}
133
134/// The name of the column `file_row_number=True` adds.
135///
136/// Here rather than in the binder because the executor is the half that fills it in and the two
137/// have to agree on the spelling. It is DuckDB's name for it, and the column is a row's ordinal
138/// inside its own file rather than inside the read, so a glob of three files counts from zero three
139/// times.
140pub const FILE_ROW_NUMBER: &str = "file_row_number";
141
142impl TableFunction {
143    /// The name the plan records and an error message says.
144    #[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    /// Whether the name can be written where a table goes, rather than only after the word `PRAGMA`.
178    ///
179    /// Nine of the pin's nineteen query pragmas answer to `pragma_name()` in a `FROM` clause and ten
180    /// do not, and which is which was measured rather than guessed. `SELECT * FROM
181    /// pragma_show_tables()` on the pin is `Catalog Error: Table Function with name
182    /// pragma_show_tables does not exist!` while `PRAGMA show_tables` returns rows, so the two
183    /// namespaces really are separate and a name in one is not a name in the other. These three are
184    /// the ones rudb has from the pragma only half of that, and the rest of that half are `ATTACH`
185    /// and `COPY` in disguise or want something rudb has not written.
186    #[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    /// Whether the call takes one table name and answers about whatever that names.
195    ///
196    /// The two pragmas are the only ones, and they are a family rather than a pair because the rest
197    /// of the `pragma_*` functions that take a name are the storage ones, which land here the day
198    /// rudb has storage to describe.
199    #[must_use]
200    pub const fn takes_a_name(self) -> bool {
201        matches!(self, Self::PragmaTableInfo | Self::PragmaShow)
202    }
203
204    /// Whether the last value is produced.
205    ///
206    /// Only the two series functions differ here. The file readers answer false and nothing asks
207    /// them.
208    #[must_use]
209    pub const fn inclusive(self) -> bool {
210        matches!(self, Self::GenerateSeries)
211    }
212
213    /// The named parameters the call takes, and the type each one wants.
214    ///
215    /// This is the list rudb acts on and not the list DuckDB prints, and the difference is worth
216    /// being plain about. `read_parquet` there takes seventeen named parameters and `read_csv`
217    /// takes around thirty. One of the Parquet ones is on the critical path, since the ClickBench
218    /// entry reads its file with `binary_as_string=True` and without it every string column in
219    /// `hits.parquet` comes back as `BLOB`, and the other sixteen have no caller here yet. A
220    /// parameter that is listed is one that does something, so this list grows as they land rather
221    /// than accepting names and ignoring them, which is the failure mode that makes an option look
222    /// supported when it is not.
223    ///
224    /// The CSV ones here are the ones that say how the file is written, which are the ones where
225    /// guessing wrong changes the answer rather than the speed. `sep` is DuckDB's other name for
226    /// `delim` and is a separate row rather than an alias, because the list is also what the
227    /// candidates on a misspelling are read out of and the binary prints both of them.
228    #[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    /// The function of that name, if there is one.
250    #[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        // `read_csv_auto` is the older spelling and DuckDB still answers to it. It meant sniffing
262        // back when `read_csv` did not sniff unless it was told to, and today they are the same
263        // function, which is why they are the same variant here.
264        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/// Where a call's columns come from.
341///
342/// A table function that produces a fixed set of columns is resolved by this crate and nothing
343/// else has to be consulted. One that reads a file is not, because the columns are in the file, so
344/// the answer here is which file to open rather than what is in it. An enum rather than an empty
345/// column list, because an empty list is what `read_parquet` of a file with no columns would also
346/// give and a caller that forgot to handle the case would get an empty table instead of an error.
347#[derive(Debug, Clone, PartialEq, Eq)]
348pub enum Columns {
349    /// The columns this call produces, with the names an unaliased call gives them.
350    Fixed(Vec<Field>),
351    /// The columns of the Parquet file the first argument names.
352    Parquet,
353    /// The columns of the CSV file the first argument names, which are sniffed out of its front.
354    Csv,
355}
356
357/// A resolved table function call.
358#[derive(Debug, Clone, PartialEq, Eq)]
359pub struct ResolvedTable {
360    /// Which function.
361    pub function: TableFunction,
362    /// What each argument has to be cast to, the same length as what was passed in.
363    pub arguments: Vec<LogicalType>,
364    /// Where the columns the call produces come from.
365    pub columns: Columns,
366}
367
368/// Resolve a table function call by name and the types of its arguments.
369///
370/// The series pair does not consult the types, only the count, because it takes integers in every
371/// position and the binder casts to that, so there is nothing there for a type to choose between.
372/// DuckDB also has a timestamp and interval form of both, which is a second set of columns rather
373/// than a second overload of the same ones, and adding it means adding it rather than widening this.
374///
375/// The file readers do consult them, because DuckDB does. `read_parquet(3)` and `read_csv(3)` are
376/// binder errors there rather than reads of a file called `3`, which was measured against the binary
377/// rather than assumed, and it is the right answer: a path that arrived as a number is a query that
378/// meant something else.
379///
380/// # Errors
381///
382/// When no table function has that name, or when it has that name and not those arguments.
383pub fn resolve_table(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
384    let function = match TableFunction::lookup(name) {
385        // A pragma only name written in a `FROM` clause is a name that does not exist there, which
386        // is the pin's answer and not a shortcut: the two namespaces are separate and this is the
387        // side of the fence the caller is standing on.
388        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
396/// The same resolution once the name has been settled, which is where the two spellings meet.
397///
398/// Split out of [`resolve_table`] because a pragma only name has to get here without going past the
399/// check that turns it down in a `FROM` clause.
400fn resolve_found(function: TableFunction, arguments: &[LogicalType]) -> Result<ResolvedTable> {
401    if let Some(columns) = file_columns(function) {
402        // Two overloads, one path and a list of them, which is DuckDB's pair. The list is where
403        // `read_parquet(['a.parquet', 'b.parquet'])` binds, and an empty list arrives typed
404        // `INTEGER[]` there and here, so it lands on the no overload message rather than on a read
405        // of nothing.
406        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        // A bare null matches, and is a sentence about nulls rather than about overloads, which is
410        // what DuckDB answers `read_parquet(NULL)` with. It is left as a null rather than cast to a
411        // path so that the binder still has a null to recognise when it goes looking for the name.
412        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        // One name, and a null is one of them. `pragma_table_info(NULL)` is a catalog error about a
427        // table called NULL on the pin rather than a complaint about the argument, because the
428        // pragma turns whatever it was given into text before it goes looking, so the null is left
429        // as a null here and the binder does the same thing with it.
430        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    // The metadata tables take nothing and their columns are fixed, which makes them the simplest
443    // case here. They are one arm rather than one each because the only thing that differs is the
444    // column list, and a name that is added to this list and not to `lookup` cannot be reached.
445    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
468/// The same resolution for a call the user wrote as `PRAGMA name`, whose messages spell it so.
469///
470/// Every pragma is an ordinary table function under a longer name, so the resolution is
471/// [`resolve_table`] and nothing else. What changes is what a bad call says. Upstream writes both
472/// halves of that message in the form the user used, so `PRAGMA table_info('a', 'b')` is
473/// `'table_info(VARCHAR, VARCHAR)'` with a candidate line reading `PRAGMA "table_info"(VARCHAR)`.
474/// Handing back a complaint about a `pragma_table_info` nobody typed would be handing the user the
475/// rewrite to debug rather than their own statement.
476///
477/// Only two shapes can reach this. A pragma never reads a file and is never `range`, so the
478/// overload it has is either one name or nothing at all, and the candidate line says which.
479///
480/// # Errors
481///
482/// When the function has that name and not those arguments, and otherwise whatever
483/// [`resolve_table`] says.
484pub 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    // Through [`resolve_found`] rather than [`resolve_table`], because three of these names only
489    // exist after the word `PRAGMA` and the other spelling is where they are turned down.
490    if let Ok(resolved) = resolve_found(function, arguments) {
491        return Ok(resolved);
492    }
493    let spelled = name.strip_prefix("pragma_").unwrap_or(name);
494    // A pragma that takes nothing prints no parentheses at all on the candidate line, where the
495    // function spelling of the same complaint prints an empty pair. Measured on the pin, which
496    // answers `PRAGMA version(1)` with a candidate reading `PRAGMA "version"` and stopping there.
497    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
506/// Where a file reading table function's columns come from, and `None` for one that does not read
507/// a file.
508fn 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
540/// The columns of a table function that takes no arguments and knows its own, and `None` for one
541/// that has to look at what it was called with.
542fn 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
574/// The columns one of the two name taking pragmas produces.
575fn name_columns(function: TableFunction) -> Vec<Field> {
576    match function {
577        TableFunction::PragmaShow => describe_fields(),
578        _ => table_info_fields(),
579    }
580}
581
582/// The columns `pragma_table_info()` produces, which is SQLite's six.
583///
584/// DuckDB answers to this because SQLite did, and the six are SQLite's names, its order and its
585/// types right down to `cid` being a 32 bit integer where everything else in these tables is a
586/// bigint. The one departure from SQLite is that `notnull` and `pk` are booleans rather than the
587/// zero or one SQLite prints, which was measured rather than assumed.
588///
589/// `dflt_value` and `pk` are null and false on everything rudb can declare, because `DEFAULT`,
590/// `PRIMARY KEY` and `UNIQUE` are all refused by `CREATE TABLE` today. They are here rather than
591/// left out because the width of a result is part of the result. `DESCRIBE` says the same three
592/// nothings in its own three columns and for the same reason.
593#[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/// The columns `DESCRIBE` answers with, which is what `pragma_show()` produces too.
606///
607/// One list rather than two because the two really are the same six columns: `pragma_show('t')` and
608/// `DESCRIBE t` return the same rows on the pin, which is what you would expect of a pragma that
609/// exists so a client can write the describe as a function call and select from it.
610#[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/// The columns `pragma_version()` produces.
619///
620/// Three columns rather than one, because a build has three things worth asking about: which release
621/// it is, which source it was made from and what that release is called. rudb answers all three
622/// about itself rather than reporting a DuckDB version, for the reason `crate` level compatibility
623/// does not extend to lying about which engine is running. `crates/rudb-exec/src/enginenames.rs` is
624/// where the three values are decided and it argues the case there.
625#[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/// The column `pragma_platform()` produces, which is the name a build is published under.
634#[must_use]
635pub fn platform_fields() -> Vec<Field> {
636    vec![Field::new("platform", LogicalType::Varchar)]
637}
638
639/// The column `pragma_user_agent()` produces, which is the line a client sends to say who it is.
640#[must_use]
641pub fn user_agent_fields() -> Vec<Field> {
642    vec![Field::new("user_agent", LogicalType::Varchar)]
643}
644
645/// The columns `pragma_database_size()` produces, one row per attached database.
646///
647/// Three of the nine are a size written for a person to read rather than a number, which is DuckDB's
648/// choice and not a helpful one for a client doing arithmetic, but the width and the types of a
649/// result are part of the result. The four block columns are the ones that mean something only once
650/// there is a file underneath, so they are the ones rudb answers zero to and says why.
651#[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/// The columns `rudb_strategies()` produces.
667///
668/// Named here rather than in the executor because the binder resolves the call and the executor
669/// fills it, and a table whose two halves disagree about its own columns is a bug that shows up as
670/// a wrong answer rather than as a compile error.
671///
672/// Nine columns and every one of them earns its place at a seam that has no implementations yet,
673/// which is twenty six of the twenty seven today. `seam`, `milestone` and `seam_description` say
674/// what the seam is and which milestone owes it its first two implementations, and they are filled
675/// whether or not anything is registered. The other six describe an implementation and are null
676/// when there is none, which is how the table says that a seam is planned rather than built without
677/// anybody having to read a design document to find out.
678#[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/// The columns `duckdb_keywords()` produces, which is DuckDB's two.
694#[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/// The columns `duckdb_extensions()` produces, which is DuckDB's ten in its order.
703///
704/// `aliases` is the one list column in any of these tables. It is the other names an extension
705/// answers to, so `httpfs` carries `[http, https, s3]` and most of them carry an empty list, and an
706/// empty list is not a null: the pin returns `[]` on every row that has no alias.
707#[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/// The columns `duckdb_optimizers()` produces, which is DuckDB's one.
724#[must_use]
725pub fn optimizer_fields() -> Vec<Field> {
726    vec![Field::new("name", LogicalType::Varchar)]
727}
728
729/// The column `duckdb_dialects()` produces.
730#[must_use]
731pub fn dialect_fields() -> Vec<Field> {
732    vec![Field::new("dialect_name", LogicalType::Varchar)]
733}
734
735/// The columns `duckdb_grammar_extensions()` produces.
736#[must_use]
737pub fn grammar_extension_fields() -> Vec<Field> {
738    vec![Field::new("name", LogicalType::Varchar), Field::new("description", LogicalType::Varchar)]
739}
740
741/// The four categories DuckDB sorts a keyword into.
742///
743/// The vendored grammar does not carry these. It carries five keyword rules, `reserved_keyword`,
744/// `unreserved_keyword`, `column_name_keyword`, `func_name_keyword` and `type_name_keyword`, and
745/// `rudb_parse::KEYWORDS` is a mask over those five because they are not disjoint. DuckDB's table
746/// reports PostgreSQL's four categories instead, where `type_function` is the one category that the
747/// grammar spells as two rules, because a word usable as a type name is usable as a function name.
748///
749/// So a word can produce two rows, and six of them do: `columns`, `generated`, `map`, `struct`,
750/// `try_cast` and `tuple` are each in the column name class and in the type function class. That is
751/// why the pinned binary returns 505 rows over 499 distinct words, and a table that deduplicated
752/// them would be 499 rows and wrong.
753///
754/// A word whose mask is zero is in no class at all. The grammar spells fifteen words directly in
755/// some rule, `ascending` and `variant` among them, which makes them matchable as literals and
756/// keywords nowhere, and the pinned binary leaves all fifteen out of this table.
757#[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
776/// DuckDB's message for a call that matched a name and no overload of it.
777///
778/// The candidate list it prints carries fifteen named parameters that none of them accept here, so
779/// what is listed is the two overloads that exist. The first line is the one a test in the wild
780/// asserts on and it is reproduced exactly.
781fn 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
791/// The same message for a pragma, which has one overload and prints its own name quoted.
792///
793/// The quoting is upstream's and is not a mistake being copied for its own sake. A pragma is
794/// registered under a name the parser also spells as a statement, so the binary writes the
795/// candidate through its identifier rule and gets `"pragma_table_info"(VARCHAR)` where
796/// `read_parquet` gets no quotes. A client that matches on the line has to see the quotes.
797fn 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
807/// The same message again for a table function whose one overload takes nothing at all.
808///
809/// Every metadata table is one of these and upstream quotes all of their names, not only the ones
810/// the parser also spells as a statement, so `"duckdb_extensions"()` reads the same way
811/// `"pragma_version"()` does. Saying how many arguments were given instead would be a shorter
812/// sentence and a worse one, because a client that reads the candidate line to find out what it may
813/// call learns nothing from a count.
814fn 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
824/// The values `start`, `stop` and `step` produce, in order.
825///
826/// Whole rather than an iterator because the caller wants them in a vector to build a vector out
827/// of, and because the count is known up front, which is what keeps a three million row `range`
828/// from growing a `Vec` twenty times on the way there.
829///
830/// A step of zero is an error and is the one case that is not simply an empty result. Everything
831/// else that produces nothing produces nothing: a start past a stop with a positive step, a start
832/// before a stop with a negative one, and the two of them equal under `range`.
833///
834/// # Errors
835///
836/// When the step is zero, with DuckDB's own wording.
837pub 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        // The count was worked out from the same three numbers, so this cannot pass the stop, and
844        // a saturating add is what keeps a step near the end of the range from wrapping into a
845        // value on the wrong side of it rather than stopping.
846        at = at.saturating_add(step);
847    }
848    Ok(out)
849}
850
851/// How many values the series has, without producing any of them.
852///
853/// The executor wants this and not the values. `range(100000000)` is a hundred row chunks a
854/// hundred thousand times over, and building the whole run first to find out how long it is would
855/// be eight hundred megabytes for a query whose answer is one number.
856///
857/// This is also where the step is checked, so the check happens once rather than in each of the
858/// two callers.
859///
860/// # Errors
861///
862/// When the step is zero, with DuckDB's own wording.
863pub 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
870/// How many values the series has.
871///
872/// In `i128` because `range(-9223372036854775808, 9223372036854775807)` is a legal call whose
873/// length does not fit in an `i64`, and a length that overflows into a negative is a `Vec` capacity
874/// that panics rather than a query that fails.
875fn 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    // Rounding away from zero, since a span of five over a step of two is three values and not two.
888    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    /// The fixed columns of a resolved call, which every function that does not read a file has.
897    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    /// A call of `count` integer arguments, which is what every series call looks like.
907    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        // And it records itself under the one name, so a plan does not have two spellings in it.
951        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        // Measured against v1.4.1 on server3: `read_parquet(3)` does not cast, it fails to match.
958        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        // DuckDB gives 2, 4, 6 for both of these. The seven is not reached by either, which is
985        // where the two functions stop being different.
986        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        // The two rules that are one category. A word usable as a type name is usable as a function
997        // name, which is why the grammar has two rules where PostgreSQL has one category, and either
998        // rule on its own is still that one category rather than half of it.
999        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        // Both, which is the case that makes one word two rows.
1003        assert_eq!(keyword_categories(COLUMN_NAME | FUNC_NAME), ["column_name", "type_function"]);
1004        // A word the grammar spells directly in a rule is in no class, and the pinned binary leaves
1005        // all fifteen of those out of the table rather than giving them a category of their own.
1006        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        // The one column that is not a varchar, a bigint or a boolean, and the reason this table
1051        // waited on the map vector.
1052        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        // The last one is a VARIANT in the pin and rudb has no such type, so it is text here.
1067        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        // Not run, only counted. The point is that the count is worked out in i128, so this comes
1097        // out as a huge number rather than as a negative one that becomes a capacity panic.
1098        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        // A single argument of the wrong type is the same message, because the pin does not cast
1165        // an integer to a name any more than it casts one to a path.
1166        let error = resolve_table("pragma_show", &[LogicalType::Integer]).expect_err("a name");
1167        assert!(error.message().contains("'pragma_show(INTEGER)'"), "{error}");
1168    }
1169
1170    /// The same call written as a statement gets the same complaint spelled the way it was written.
1171    #[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        // A pragma that takes nothing prints no parentheses on the candidate line at all, which is
1183        // the pin's spelling and is not the same as the empty pair the function form prints.
1184        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    /// A call that resolves comes back the same either way, because it is the same function.
1190    #[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        // The pin writes three of the nine as text a person reads rather than as a number, which is
1243        // worth a test because a client doing arithmetic on `database_size` gets a cast error on
1244        // both engines and that is the compatible answer rather than a bug in either.
1245        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}