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, table_fields, view_fields,
66};
67use crate::functioncatalog::function_fields;
68use crate::settingcatalog::setting_fields;
69use crate::typecatalog::type_fields;
70
71/// Which table function a call resolved to.
72///
73/// An enum rather than a name, because the executor dispatches on this and a string comparison per
74/// operator build is a string comparison that can be spelled wrong.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum TableFunction {
77    /// `range(stop)`, `range(start, stop)`, `range(start, stop, step)`, stopping before the end.
78    Range,
79    /// The same three, stopping on the end.
80    GenerateSeries,
81    /// `read_parquet(path)`, the rows of a Parquet file.
82    ReadParquet,
83    /// `read_csv(path)`, the rows of a CSV file, with everything about how it is written sniffed.
84    ReadCsv,
85    /// `rudb_strategies()`, every seam and every implementation registered against it.
86    RudbStrategies,
87    /// `duckdb_keywords()`, every word the grammar knows and which class each one is in.
88    DuckdbKeywords,
89    /// `duckdb_types()`, every type name the engine knows and what each one stands for.
90    DuckdbTypes,
91    /// `duckdb_functions()`, every function the engine knows and what each one takes.
92    DuckdbFunctions,
93    /// `duckdb_settings()`, every setting `SET` will take and what each one is now.
94    DuckdbSettings,
95    /// `duckdb_databases()`, every database attached to this session.
96    DuckdbDatabases,
97    /// `duckdb_schemas()`, every schema in every one of them.
98    DuckdbSchemas,
99    /// `duckdb_tables()`, every base table somebody created.
100    DuckdbTables,
101    /// `duckdb_views()`, every view somebody created.
102    DuckdbViews,
103    /// `duckdb_columns()`, every column of every one of those.
104    DuckdbColumns,
105    /// `duckdb_extensions()`, every extension DuckDB names and whether this engine has it.
106    DuckdbExtensions,
107    /// `duckdb_optimizers()`, every name `SET disabled_optimizers` takes.
108    DuckdbOptimizers,
109    /// `duckdb_dialects()`, every installed SQL parser dialect.
110    DuckdbDialects,
111    /// `duckdb_grammar_extensions()`, every installed grammar extension.
112    DuckdbGrammarExtensions,
113    /// `pragma_table_info(name)`, the columns of one table or view, in SQLite's six columns.
114    PragmaTableInfo,
115    /// `pragma_show(name)`, the same columns again in the six `DESCRIBE` answers with.
116    PragmaShow,
117    /// `pragma_version()`, the version of the engine answering, in three columns.
118    PragmaVersion,
119    /// `pragma_platform()`, the operating system and processor this build was made for.
120    PragmaPlatform,
121    /// `pragma_user_agent()`, the one line a client sends when it says who it is.
122    PragmaUserAgent,
123    /// `pragma_database_size()`, what each attached database costs on disk and in memory.
124    PragmaDatabaseSize,
125}
126
127/// The name of the column `file_row_number=True` adds.
128///
129/// Here rather than in the binder because the executor is the half that fills it in and the two
130/// have to agree on the spelling. It is DuckDB's name for it, and the column is a row's ordinal
131/// inside its own file rather than inside the read, so a glob of three files counts from zero three
132/// times.
133pub const FILE_ROW_NUMBER: &str = "file_row_number";
134
135impl TableFunction {
136    /// The name the plan records and an error message says.
137    #[must_use]
138    pub const fn name(self) -> &'static str {
139        match self {
140            Self::Range => "range",
141            Self::GenerateSeries => "generate_series",
142            Self::ReadParquet => "read_parquet",
143            Self::ReadCsv => "read_csv",
144            Self::RudbStrategies => "rudb_strategies",
145            Self::DuckdbKeywords => "duckdb_keywords",
146            Self::DuckdbTypes => "duckdb_types",
147            Self::DuckdbFunctions => "duckdb_functions",
148            Self::DuckdbSettings => "duckdb_settings",
149            Self::DuckdbDatabases => "duckdb_databases",
150            Self::DuckdbSchemas => "duckdb_schemas",
151            Self::DuckdbTables => "duckdb_tables",
152            Self::DuckdbViews => "duckdb_views",
153            Self::DuckdbColumns => "duckdb_columns",
154            Self::DuckdbExtensions => "duckdb_extensions",
155            Self::DuckdbOptimizers => "duckdb_optimizers",
156            Self::DuckdbDialects => "duckdb_dialects",
157            Self::DuckdbGrammarExtensions => "duckdb_grammar_extensions",
158            Self::PragmaTableInfo => "pragma_table_info",
159            Self::PragmaShow => "pragma_show",
160            Self::PragmaVersion => "pragma_version",
161            Self::PragmaPlatform => "pragma_platform",
162            Self::PragmaUserAgent => "pragma_user_agent",
163            Self::PragmaDatabaseSize => "pragma_database_size",
164        }
165    }
166
167    /// Whether the call takes one table name and answers about whatever that names.
168    ///
169    /// The two pragmas are the only ones, and they are a family rather than a pair because the rest
170    /// of the `pragma_*` functions that take a name are the storage ones, which land here the day
171    /// rudb has storage to describe.
172    #[must_use]
173    pub const fn takes_a_name(self) -> bool {
174        matches!(self, Self::PragmaTableInfo | Self::PragmaShow)
175    }
176
177    /// Whether the last value is produced.
178    ///
179    /// Only the two series functions differ here. The file readers answer false and nothing asks
180    /// them.
181    #[must_use]
182    pub const fn inclusive(self) -> bool {
183        matches!(self, Self::GenerateSeries)
184    }
185
186    /// The named parameters the call takes, and the type each one wants.
187    ///
188    /// This is the list rudb acts on and not the list DuckDB prints, and the difference is worth
189    /// being plain about. `read_parquet` there takes seventeen named parameters and `read_csv`
190    /// takes around thirty. One of the Parquet ones is on the critical path, since the ClickBench
191    /// entry reads its file with `binary_as_string=True` and without it every string column in
192    /// `hits.parquet` comes back as `BLOB`, and the other sixteen have no caller here yet. A
193    /// parameter that is listed is one that does something, so this list grows as they land rather
194    /// than accepting names and ignoring them, which is the failure mode that makes an option look
195    /// supported when it is not.
196    ///
197    /// The CSV ones here are the ones that say how the file is written, which are the ones where
198    /// guessing wrong changes the answer rather than the speed. `sep` is DuckDB's other name for
199    /// `delim` and is a separate row rather than an alias, because the list is also what the
200    /// candidates on a misspelling are read out of and the binary prints both of them.
201    #[must_use]
202    pub fn parameters(self) -> &'static [(&'static str, LogicalType)] {
203        static READ_PARQUET: &[(&str, LogicalType)] = &[
204            ("binary_as_string", LogicalType::Boolean),
205            ("file_row_number", LogicalType::Boolean),
206        ];
207        static READ_CSV: &[(&str, LogicalType)] = &[
208            ("all_varchar", LogicalType::Boolean),
209            ("delim", LogicalType::Varchar),
210            ("escape", LogicalType::Varchar),
211            ("header", LogicalType::Boolean),
212            ("quote", LogicalType::Varchar),
213            ("sep", LogicalType::Varchar),
214        ];
215        match self {
216            Self::ReadParquet => READ_PARQUET,
217            Self::ReadCsv => READ_CSV,
218            _ => &[],
219        }
220    }
221
222    /// The function of that name, if there is one.
223    #[must_use]
224    pub fn lookup(name: &str) -> Option<Self> {
225        if name.eq_ignore_ascii_case("range") {
226            return Some(Self::Range);
227        }
228        if name.eq_ignore_ascii_case("generate_series") {
229            return Some(Self::GenerateSeries);
230        }
231        if name.eq_ignore_ascii_case("read_parquet") || name.eq_ignore_ascii_case("parquet_scan") {
232            return Some(Self::ReadParquet);
233        }
234        // `read_csv_auto` is the older spelling and DuckDB still answers to it. It meant sniffing
235        // back when `read_csv` did not sniff unless it was told to, and today they are the same
236        // function, which is why they are the same variant here.
237        if name.eq_ignore_ascii_case("read_csv") || name.eq_ignore_ascii_case("read_csv_auto") {
238            return Some(Self::ReadCsv);
239        }
240        if name.eq_ignore_ascii_case("rudb_strategies") {
241            return Some(Self::RudbStrategies);
242        }
243        if name.eq_ignore_ascii_case("duckdb_keywords") {
244            return Some(Self::DuckdbKeywords);
245        }
246        if name.eq_ignore_ascii_case("duckdb_types") {
247            return Some(Self::DuckdbTypes);
248        }
249        if name.eq_ignore_ascii_case("duckdb_functions") {
250            return Some(Self::DuckdbFunctions);
251        }
252        if name.eq_ignore_ascii_case("duckdb_settings") {
253            return Some(Self::DuckdbSettings);
254        }
255        if name.eq_ignore_ascii_case("duckdb_databases") {
256            return Some(Self::DuckdbDatabases);
257        }
258        if name.eq_ignore_ascii_case("duckdb_schemas") {
259            return Some(Self::DuckdbSchemas);
260        }
261        if name.eq_ignore_ascii_case("duckdb_tables") {
262            return Some(Self::DuckdbTables);
263        }
264        if name.eq_ignore_ascii_case("duckdb_views") {
265            return Some(Self::DuckdbViews);
266        }
267        if name.eq_ignore_ascii_case("duckdb_columns") {
268            return Some(Self::DuckdbColumns);
269        }
270        if name.eq_ignore_ascii_case("duckdb_extensions") {
271            return Some(Self::DuckdbExtensions);
272        }
273        if name.eq_ignore_ascii_case("duckdb_optimizers") {
274            return Some(Self::DuckdbOptimizers);
275        }
276        if name.eq_ignore_ascii_case("duckdb_dialects") {
277            return Some(Self::DuckdbDialects);
278        }
279        if name.eq_ignore_ascii_case("duckdb_grammar_extensions") {
280            return Some(Self::DuckdbGrammarExtensions);
281        }
282        if name.eq_ignore_ascii_case("pragma_table_info") {
283            return Some(Self::PragmaTableInfo);
284        }
285        if name.eq_ignore_ascii_case("pragma_show") {
286            return Some(Self::PragmaShow);
287        }
288        if name.eq_ignore_ascii_case("pragma_version") {
289            return Some(Self::PragmaVersion);
290        }
291        if name.eq_ignore_ascii_case("pragma_platform") {
292            return Some(Self::PragmaPlatform);
293        }
294        if name.eq_ignore_ascii_case("pragma_user_agent") {
295            return Some(Self::PragmaUserAgent);
296        }
297        if name.eq_ignore_ascii_case("pragma_database_size") {
298            return Some(Self::PragmaDatabaseSize);
299        }
300        None
301    }
302}
303
304/// Where a call's columns come from.
305///
306/// A table function that produces a fixed set of columns is resolved by this crate and nothing
307/// else has to be consulted. One that reads a file is not, because the columns are in the file, so
308/// the answer here is which file to open rather than what is in it. An enum rather than an empty
309/// column list, because an empty list is what `read_parquet` of a file with no columns would also
310/// give and a caller that forgot to handle the case would get an empty table instead of an error.
311#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum Columns {
313    /// The columns this call produces, with the names an unaliased call gives them.
314    Fixed(Vec<Field>),
315    /// The columns of the Parquet file the first argument names.
316    Parquet,
317    /// The columns of the CSV file the first argument names, which are sniffed out of its front.
318    Csv,
319}
320
321/// A resolved table function call.
322#[derive(Debug, Clone, PartialEq, Eq)]
323pub struct ResolvedTable {
324    /// Which function.
325    pub function: TableFunction,
326    /// What each argument has to be cast to, the same length as what was passed in.
327    pub arguments: Vec<LogicalType>,
328    /// Where the columns the call produces come from.
329    pub columns: Columns,
330}
331
332/// Resolve a table function call by name and the types of its arguments.
333///
334/// The series pair does not consult the types, only the count, because it takes integers in every
335/// position and the binder casts to that, so there is nothing there for a type to choose between.
336/// DuckDB also has a timestamp and interval form of both, which is a second set of columns rather
337/// than a second overload of the same ones, and adding it means adding it rather than widening this.
338///
339/// The file readers do consult them, because DuckDB does. `read_parquet(3)` and `read_csv(3)` are
340/// binder errors there rather than reads of a file called `3`, which was measured against the binary
341/// rather than assumed, and it is the right answer: a path that arrived as a number is a query that
342/// meant something else.
343///
344/// # Errors
345///
346/// When no table function has that name, or when it has that name and not those arguments.
347pub fn resolve_table(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
348    let Some(function) = TableFunction::lookup(name) else {
349        return Err(Error::catalog(format!("Table Function with name {name} does not exist!")));
350    };
351    if let Some(columns) = file_columns(function) {
352        // Two overloads, one path and a list of them, which is DuckDB's pair. The list is where
353        // `read_parquet(['a.parquet', 'b.parquet'])` binds, and an empty list arrives typed
354        // `INTEGER[]` there and here, so it lands on the no overload message rather than on a read
355        // of nothing.
356        let list = LogicalType::list(LogicalType::Varchar);
357        let single = arguments.len() == 1 && arguments[0] == LogicalType::Varchar;
358        let many = arguments.len() == 1 && arguments[0] == list;
359        // A bare null matches, and is a sentence about nulls rather than about overloads, which is
360        // what DuckDB answers `read_parquet(NULL)` with. It is left as a null rather than cast to a
361        // path so that the binder still has a null to recognise when it goes looking for the name.
362        let nothing = arguments.len() == 1 && arguments[0] == LogicalType::Null;
363        if !single && !many && !nothing {
364            return Err(no_overload(function, arguments));
365        }
366        let wanted = if many {
367            list
368        } else if nothing {
369            LogicalType::Null
370        } else {
371            LogicalType::Varchar
372        };
373        return Ok(ResolvedTable { function, arguments: vec![wanted], columns });
374    }
375    if function.takes_a_name() {
376        // One name, and a null is one of them. `pragma_table_info(NULL)` is a catalog error about a
377        // table called NULL on the pin rather than a complaint about the argument, because the
378        // pragma turns whatever it was given into text before it goes looking, so the null is left
379        // as a null here and the binder does the same thing with it.
380        let single = arguments.len() == 1
381            && matches!(arguments[0], LogicalType::Varchar | LogicalType::Null);
382        if !single {
383            return Err(one_name(function, arguments));
384        }
385        return Ok(ResolvedTable {
386            function,
387            arguments: vec![arguments[0].clone()],
388            columns: Columns::Fixed(name_columns(function)),
389        });
390    }
391    let arity = arguments.len();
392    // The metadata tables take nothing and their columns are fixed, which makes them the simplest
393    // case here. They are one arm rather than one each because the only thing that differs is the
394    // column list, and a name that is added to this list and not to `lookup` cannot be reached.
395    if let Some(columns) = fixed_columns(function) {
396        if arity != 0 {
397            return Err(nothing_at_all(function, arguments));
398        }
399        return Ok(ResolvedTable {
400            function,
401            arguments: Vec::new(),
402            columns: Columns::Fixed(columns),
403        });
404    }
405    if !(1..=3).contains(&arity) {
406        return Err(Error::binder(format!(
407            "Table function {}() takes between 1 and 3 arguments, {arity} were given",
408            function.name()
409        )));
410    }
411    Ok(ResolvedTable {
412        function,
413        arguments: vec![LogicalType::BigInt; arity],
414        columns: Columns::Fixed(vec![Field::new(function.name(), LogicalType::BigInt)]),
415    })
416}
417
418/// The same resolution for a call the user wrote as `PRAGMA name`, whose messages spell it so.
419///
420/// Every pragma is an ordinary table function under a longer name, so the resolution is
421/// [`resolve_table`] and nothing else. What changes is what a bad call says. Upstream writes both
422/// halves of that message in the form the user used, so `PRAGMA table_info('a', 'b')` is
423/// `'table_info(VARCHAR, VARCHAR)'` with a candidate line reading `PRAGMA "table_info"(VARCHAR)`.
424/// Handing back a complaint about a `pragma_table_info` nobody typed would be handing the user the
425/// rewrite to debug rather than their own statement.
426///
427/// Only two shapes can reach this. A pragma never reads a file and is never `range`, so the
428/// overload it has is either one name or nothing at all, and the candidate line says which.
429///
430/// # Errors
431///
432/// When the function has that name and not those arguments, and otherwise whatever
433/// [`resolve_table`] says.
434pub fn resolve_pragma(name: &str, arguments: &[LogicalType]) -> Result<ResolvedTable> {
435    let error = match resolve_table(name, arguments) {
436        Ok(resolved) => return Ok(resolved),
437        Err(error) => error,
438    };
439    let Some(function) = TableFunction::lookup(name) else {
440        return Err(error);
441    };
442    let spelled = name.strip_prefix("pragma_").unwrap_or(name);
443    // A pragma that takes nothing prints no parentheses at all on the candidate line, where the
444    // function spelling of the same complaint prints an empty pair. Measured on the pin, which
445    // answers `PRAGMA version(1)` with a candidate reading `PRAGMA "version"` and stopping there.
446    let takes = if function.takes_a_name() { "(VARCHAR)" } else { "" };
447    let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
448    Err(Error::binder(format!(
449        "No function matches the given name and argument types '{spelled}({})'. You might need to \
450         add explicit type casts.\n\tCandidate functions:\n\tPRAGMA \"{spelled}\"{takes}\n",
451        written.join(", ")
452    )))
453}
454
455/// Where a file reading table function's columns come from, and `None` for one that does not read
456/// a file.
457fn file_columns(function: TableFunction) -> Option<Columns> {
458    match function {
459        TableFunction::ReadParquet => Some(Columns::Parquet),
460        TableFunction::ReadCsv => Some(Columns::Csv),
461        TableFunction::Range
462        | TableFunction::GenerateSeries
463        | TableFunction::RudbStrategies
464        | TableFunction::DuckdbKeywords
465        | TableFunction::DuckdbTypes
466        | TableFunction::DuckdbFunctions
467        | TableFunction::DuckdbSettings
468        | TableFunction::DuckdbDatabases
469        | TableFunction::DuckdbSchemas
470        | TableFunction::DuckdbTables
471        | TableFunction::DuckdbViews
472        | TableFunction::DuckdbColumns
473        | TableFunction::DuckdbExtensions
474        | TableFunction::DuckdbOptimizers
475        | TableFunction::DuckdbDialects
476        | TableFunction::DuckdbGrammarExtensions
477        | TableFunction::PragmaTableInfo
478        | TableFunction::PragmaShow
479        | TableFunction::PragmaVersion
480        | TableFunction::PragmaPlatform
481        | TableFunction::PragmaUserAgent
482        | TableFunction::PragmaDatabaseSize => None,
483    }
484}
485
486/// The columns of a table function that takes no arguments and knows its own, and `None` for one
487/// that has to look at what it was called with.
488fn fixed_columns(function: TableFunction) -> Option<Vec<Field>> {
489    match function {
490        TableFunction::RudbStrategies => Some(strategy_fields()),
491        TableFunction::DuckdbKeywords => Some(keyword_fields()),
492        TableFunction::DuckdbTypes => Some(type_fields()),
493        TableFunction::DuckdbFunctions => Some(function_fields()),
494        TableFunction::DuckdbSettings => Some(setting_fields()),
495        TableFunction::DuckdbDatabases => Some(database_fields()),
496        TableFunction::DuckdbSchemas => Some(schema_fields()),
497        TableFunction::DuckdbTables => Some(table_fields()),
498        TableFunction::DuckdbViews => Some(view_fields()),
499        TableFunction::DuckdbColumns => Some(column_fields()),
500        TableFunction::DuckdbExtensions => Some(extension_fields()),
501        TableFunction::DuckdbOptimizers => Some(optimizer_fields()),
502        TableFunction::DuckdbDialects => Some(dialect_fields()),
503        TableFunction::DuckdbGrammarExtensions => Some(grammar_extension_fields()),
504        TableFunction::PragmaVersion => Some(version_fields()),
505        TableFunction::PragmaPlatform => Some(platform_fields()),
506        TableFunction::PragmaUserAgent => Some(user_agent_fields()),
507        TableFunction::PragmaDatabaseSize => Some(database_size_fields()),
508        TableFunction::Range
509        | TableFunction::GenerateSeries
510        | TableFunction::ReadParquet
511        | TableFunction::ReadCsv
512        | TableFunction::PragmaTableInfo
513        | TableFunction::PragmaShow => None,
514    }
515}
516
517/// The columns one of the two name taking pragmas produces.
518fn name_columns(function: TableFunction) -> Vec<Field> {
519    match function {
520        TableFunction::PragmaShow => describe_fields(),
521        _ => table_info_fields(),
522    }
523}
524
525/// The columns `pragma_table_info()` produces, which is SQLite's six.
526///
527/// DuckDB answers to this because SQLite did, and the six are SQLite's names, its order and its
528/// types right down to `cid` being a 32 bit integer where everything else in these tables is a
529/// bigint. The one departure from SQLite is that `notnull` and `pk` are booleans rather than the
530/// zero or one SQLite prints, which was measured rather than assumed.
531///
532/// `dflt_value` and `pk` are null and false on everything rudb can declare, because `DEFAULT`,
533/// `PRIMARY KEY` and `UNIQUE` are all refused by `CREATE TABLE` today. They are here rather than
534/// left out because the width of a result is part of the result. `DESCRIBE` says the same three
535/// nothings in its own three columns and for the same reason.
536#[must_use]
537pub fn table_info_fields() -> Vec<Field> {
538    vec![
539        Field::new("cid", LogicalType::Integer),
540        Field::new("name", LogicalType::Varchar),
541        Field::new("type", LogicalType::Varchar),
542        Field::new("notnull", LogicalType::Boolean),
543        Field::new("dflt_value", LogicalType::Varchar),
544        Field::new("pk", LogicalType::Boolean),
545    ]
546}
547
548/// The columns `DESCRIBE` answers with, which is what `pragma_show()` produces too.
549///
550/// One list rather than two because the two really are the same six columns: `pragma_show('t')` and
551/// `DESCRIBE t` return the same rows on the pin, which is what you would expect of a pragma that
552/// exists so a client can write the describe as a function call and select from it.
553#[must_use]
554pub fn describe_fields() -> Vec<Field> {
555    ["column_name", "column_type", "null", "key", "default", "extra"]
556        .iter()
557        .map(|name| Field::new(*name, LogicalType::Varchar))
558        .collect()
559}
560
561/// The columns `pragma_version()` produces.
562///
563/// Three columns rather than one, because a build has three things worth asking about: which release
564/// it is, which source it was made from and what that release is called. rudb answers all three
565/// about itself rather than reporting a DuckDB version, for the reason `crate` level compatibility
566/// does not extend to lying about which engine is running. `crates/rudb-exec/src/enginenames.rs` is
567/// where the three values are decided and it argues the case there.
568#[must_use]
569pub fn version_fields() -> Vec<Field> {
570    ["library_version", "source_id", "codename"]
571        .iter()
572        .map(|name| Field::new(*name, LogicalType::Varchar))
573        .collect()
574}
575
576/// The column `pragma_platform()` produces, which is the name a build is published under.
577#[must_use]
578pub fn platform_fields() -> Vec<Field> {
579    vec![Field::new("platform", LogicalType::Varchar)]
580}
581
582/// The column `pragma_user_agent()` produces, which is the line a client sends to say who it is.
583#[must_use]
584pub fn user_agent_fields() -> Vec<Field> {
585    vec![Field::new("user_agent", LogicalType::Varchar)]
586}
587
588/// The columns `pragma_database_size()` produces, one row per attached database.
589///
590/// Three of the nine are a size written for a person to read rather than a number, which is DuckDB's
591/// choice and not a helpful one for a client doing arithmetic, but the width and the types of a
592/// result are part of the result. The four block columns are the ones that mean something only once
593/// there is a file underneath, so they are the ones rudb answers zero to and says why.
594#[must_use]
595pub fn database_size_fields() -> Vec<Field> {
596    vec![
597        Field::new("database_name", LogicalType::Varchar),
598        Field::new("database_size", LogicalType::Varchar),
599        Field::new("block_size", LogicalType::BigInt),
600        Field::new("total_blocks", LogicalType::BigInt),
601        Field::new("used_blocks", LogicalType::BigInt),
602        Field::new("free_blocks", LogicalType::BigInt),
603        Field::new("wal_size", LogicalType::Varchar),
604        Field::new("memory_usage", LogicalType::Varchar),
605        Field::new("memory_limit", LogicalType::Varchar),
606    ]
607}
608
609/// The columns `rudb_strategies()` produces.
610///
611/// Named here rather than in the executor because the binder resolves the call and the executor
612/// fills it, and a table whose two halves disagree about its own columns is a bug that shows up as
613/// a wrong answer rather than as a compile error.
614///
615/// Nine columns and every one of them earns its place at a seam that has no implementations yet,
616/// which is twenty six of the twenty seven today. `seam`, `milestone` and `seam_description` say
617/// what the seam is and which milestone owes it its first two implementations, and they are filled
618/// whether or not anything is registered. The other six describe an implementation and are null
619/// when there is none, which is how the table says that a seam is planned rather than built without
620/// anybody having to read a design document to find out.
621#[must_use]
622pub fn strategy_fields() -> Vec<Field> {
623    vec![
624        Field::new("seam", LogicalType::Varchar),
625        Field::new("milestone", LogicalType::Varchar),
626        Field::new("seam_description", LogicalType::Varchar),
627        Field::new("implementation", LogicalType::Varchar),
628        Field::new("implementation_description", LogicalType::Varchar),
629        Field::new("provenance", LogicalType::Varchar),
630        Field::new("determinism", LogicalType::Varchar),
631        Field::new("is_reference", LogicalType::Boolean),
632        Field::new("is_default", LogicalType::Boolean),
633    ]
634}
635
636/// The columns `duckdb_keywords()` produces, which is DuckDB's two.
637#[must_use]
638pub fn keyword_fields() -> Vec<Field> {
639    vec![
640        Field::new("keyword_name", LogicalType::Varchar),
641        Field::new("keyword_category", LogicalType::Varchar),
642    ]
643}
644
645/// The columns `duckdb_extensions()` produces, which is DuckDB's ten in its order.
646///
647/// `aliases` is the one list column in any of these tables. It is the other names an extension
648/// answers to, so `httpfs` carries `[http, https, s3]` and most of them carry an empty list, and an
649/// empty list is not a null: the pin returns `[]` on every row that has no alias.
650#[must_use]
651pub fn extension_fields() -> Vec<Field> {
652    vec![
653        Field::new("extension_name", LogicalType::Varchar),
654        Field::new("loaded", LogicalType::Boolean),
655        Field::new("installed", LogicalType::Boolean),
656        Field::new("install_path", LogicalType::Varchar),
657        Field::new("description", LogicalType::Varchar),
658        Field::new("aliases", LogicalType::list(LogicalType::Varchar)),
659        Field::new("extension_version", LogicalType::Varchar),
660        Field::new("install_mode", LogicalType::Varchar),
661        Field::new("installed_from", LogicalType::Varchar),
662        Field::new("signature_key_fingerprint", LogicalType::Varchar),
663    ]
664}
665
666/// The columns `duckdb_optimizers()` produces, which is DuckDB's one.
667#[must_use]
668pub fn optimizer_fields() -> Vec<Field> {
669    vec![Field::new("name", LogicalType::Varchar)]
670}
671
672/// The column `duckdb_dialects()` produces.
673#[must_use]
674pub fn dialect_fields() -> Vec<Field> {
675    vec![Field::new("dialect_name", LogicalType::Varchar)]
676}
677
678/// The columns `duckdb_grammar_extensions()` produces.
679#[must_use]
680pub fn grammar_extension_fields() -> Vec<Field> {
681    vec![Field::new("name", LogicalType::Varchar), Field::new("description", LogicalType::Varchar)]
682}
683
684/// The four categories DuckDB sorts a keyword into.
685///
686/// The vendored grammar does not carry these. It carries five keyword rules, `reserved_keyword`,
687/// `unreserved_keyword`, `column_name_keyword`, `func_name_keyword` and `type_name_keyword`, and
688/// `rudb_parse::KEYWORDS` is a mask over those five because they are not disjoint. DuckDB's table
689/// reports PostgreSQL's four categories instead, where `type_function` is the one category that the
690/// grammar spells as two rules, because a word usable as a type name is usable as a function name.
691///
692/// So a word can produce two rows, and six of them do: `columns`, `generated`, `map`, `struct`,
693/// `try_cast` and `tuple` are each in the column name class and in the type function class. That is
694/// why the pinned binary returns 505 rows over 499 distinct words, and a table that deduplicated
695/// them would be 499 rows and wrong.
696///
697/// A word whose mask is zero is in no class at all. The grammar spells fifteen words directly in
698/// some rule, `ascending` and `variant` among them, which makes them matchable as literals and
699/// keywords nowhere, and the pinned binary leaves all fifteen out of this table.
700#[must_use]
701pub fn keyword_categories(classes: u8) -> Vec<&'static str> {
702    use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
703    let mut out = Vec::new();
704    if classes & RESERVED != 0 {
705        out.push("reserved");
706    }
707    if classes & UNRESERVED != 0 {
708        out.push("unreserved");
709    }
710    if classes & COLUMN_NAME != 0 {
711        out.push("column_name");
712    }
713    if classes & (FUNC_NAME | TYPE_NAME) != 0 {
714        out.push("type_function");
715    }
716    out
717}
718
719/// DuckDB's message for a call that matched a name and no overload of it.
720///
721/// The candidate list it prints carries fifteen named parameters that none of them accept here, so
722/// what is listed is the two overloads that exist. The first line is the one a test in the wild
723/// asserts on and it is reproduced exactly.
724fn no_overload(function: TableFunction, arguments: &[LogicalType]) -> Error {
725    let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
726    let name = function.name();
727    Error::binder(format!(
728        "No function matches the given name and argument types '{name}({})'. You might need to \
729         add explicit type casts.\n\tCandidate functions:\n\t{name}(VARCHAR)\n\t{name}(VARCHAR[])\n",
730        written.join(", ")
731    ))
732}
733
734/// The same message for a pragma, which has one overload and prints its own name quoted.
735///
736/// The quoting is upstream's and is not a mistake being copied for its own sake. A pragma is
737/// registered under a name the parser also spells as a statement, so the binary writes the
738/// candidate through its identifier rule and gets `"pragma_table_info"(VARCHAR)` where
739/// `read_parquet` gets no quotes. A client that matches on the line has to see the quotes.
740fn one_name(function: TableFunction, arguments: &[LogicalType]) -> Error {
741    let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
742    let name = function.name();
743    Error::binder(format!(
744        "No function matches the given name and argument types '{name}({})'. You might need to \
745         add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"(VARCHAR)\n",
746        written.join(", ")
747    ))
748}
749
750/// The same message again for a table function whose one overload takes nothing at all.
751///
752/// Every metadata table is one of these and upstream quotes all of their names, not only the ones
753/// the parser also spells as a statement, so `"duckdb_extensions"()` reads the same way
754/// `"pragma_version"()` does. Saying how many arguments were given instead would be a shorter
755/// sentence and a worse one, because a client that reads the candidate line to find out what it may
756/// call learns nothing from a count.
757fn nothing_at_all(function: TableFunction, arguments: &[LogicalType]) -> Error {
758    let written: Vec<String> = arguments.iter().map(ToString::to_string).collect();
759    let name = function.name();
760    Error::binder(format!(
761        "No function matches the given name and argument types '{name}({})'. You might need to \
762         add explicit type casts.\n\tCandidate functions:\n\t\"{name}\"()\n",
763        written.join(", ")
764    ))
765}
766
767/// The values `start`, `stop` and `step` produce, in order.
768///
769/// Whole rather than an iterator because the caller wants them in a vector to build a vector out
770/// of, and because the count is known up front, which is what keeps a three million row `range`
771/// from growing a `Vec` twenty times on the way there.
772///
773/// A step of zero is an error and is the one case that is not simply an empty result. Everything
774/// else that produces nothing produces nothing: a start past a stop with a positive step, a start
775/// before a stop with a negative one, and the two of them equal under `range`.
776///
777/// # Errors
778///
779/// When the step is zero, with DuckDB's own wording.
780pub fn series(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<Vec<i64>> {
781    let count = series_length(function, start, stop, step)?;
782    let mut out = Vec::with_capacity(count);
783    let mut at = start;
784    for _ in 0..count {
785        out.push(at);
786        // The count was worked out from the same three numbers, so this cannot pass the stop, and
787        // a saturating add is what keeps a step near the end of the range from wrapping into a
788        // value on the wrong side of it rather than stopping.
789        at = at.saturating_add(step);
790    }
791    Ok(out)
792}
793
794/// How many values the series has, without producing any of them.
795///
796/// The executor wants this and not the values. `range(100000000)` is a hundred row chunks a
797/// hundred thousand times over, and building the whole run first to find out how long it is would
798/// be eight hundred megabytes for a query whose answer is one number.
799///
800/// This is also where the step is checked, so the check happens once rather than in each of the
801/// two callers.
802///
803/// # Errors
804///
805/// When the step is zero, with DuckDB's own wording.
806pub fn series_length(function: TableFunction, start: i64, stop: i64, step: i64) -> Result<usize> {
807    if step == 0 {
808        return Err(Error::binder("interval cannot be 0!"));
809    }
810    Ok(length(function, start, stop, step))
811}
812
813/// How many values the series has.
814///
815/// In `i128` because `range(-9223372036854775808, 9223372036854775807)` is a legal call whose
816/// length does not fit in an `i64`, and a length that overflows into a negative is a `Vec` capacity
817/// that panics rather than a query that fails.
818fn length(function: TableFunction, start: i64, stop: i64, step: i64) -> usize {
819    let start = i128::from(start);
820    let stop = i128::from(stop);
821    let step = i128::from(step);
822    let span = if function.inclusive() {
823        if step > 0 { stop - start + 1 } else { stop - start - 1 }
824    } else {
825        stop - start
826    };
827    if (span > 0) != (step > 0) {
828        return 0;
829    }
830    // Rounding away from zero, since a span of five over a step of two is three values and not two.
831    let count = (span + step - step.signum()) / step;
832    usize::try_from(count).unwrap_or(usize::MAX)
833}
834
835#[cfg(test)]
836mod tests {
837    use super::*;
838
839    /// The fixed columns of a resolved call, which every function that does not read a file has.
840    fn fixed(resolved: &ResolvedTable) -> &[Field] {
841        match &resolved.columns {
842            Columns::Fixed(fields) => fields,
843            Columns::Parquet | Columns::Csv => {
844                panic!("{} resolves to a file", resolved.function.name())
845            }
846        }
847    }
848
849    /// A call of `count` integer arguments, which is what every series call looks like.
850    fn integers(count: usize) -> Vec<LogicalType> {
851        vec![LogicalType::BigInt; count]
852    }
853
854    #[test]
855    fn a_name_that_is_not_a_table_function_says_so_rather_than_binding() {
856        let error = resolve_table("read_csv", &integers(1)).unwrap_err();
857        assert!(error.to_string().contains("read_csv"), "{error}");
858    }
859
860    #[test]
861    fn both_names_resolve_and_each_one_names_its_own_column() {
862        let range = resolve_table("range", &integers(1)).unwrap();
863        assert_eq!(fixed(&range)[0].name, "range");
864        let series = resolve_table("GENERATE_SERIES", &integers(3)).unwrap();
865        assert_eq!(fixed(&series)[0].name, "generate_series");
866        assert_eq!(series.arguments.len(), 3);
867    }
868
869    #[test]
870    fn no_arguments_and_four_arguments_are_both_the_arity_error() {
871        assert!(resolve_table("range", &integers(0)).is_err());
872        assert!(resolve_table("range", &integers(4)).is_err());
873    }
874
875    #[test]
876    fn a_series_call_ignores_the_types_it_was_given_and_casts_them_all_to_bigint() {
877        let resolved =
878            resolve_table("range", &[LogicalType::Varchar, LogicalType::Double]).unwrap();
879        assert_eq!(resolved.arguments, integers(2));
880    }
881
882    #[test]
883    fn read_parquet_takes_one_string_and_says_its_columns_are_in_the_file() {
884        let resolved = resolve_table("read_parquet", &[LogicalType::Varchar]).unwrap();
885        assert_eq!(resolved.function, TableFunction::ReadParquet);
886        assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
887        assert_eq!(resolved.columns, Columns::Parquet);
888    }
889
890    #[test]
891    fn parquet_scan_is_the_same_function_under_duckdbs_other_name_for_it() {
892        assert_eq!(TableFunction::lookup("parquet_scan"), Some(TableFunction::ReadParquet));
893        // And it records itself under the one name, so a plan does not have two spellings in it.
894        let resolved = resolve_table("parquet_scan", &[LogicalType::Varchar]).unwrap();
895        assert_eq!(resolved.function.name(), "read_parquet");
896    }
897
898    #[test]
899    fn a_path_that_is_not_a_string_is_the_message_duckdb_gives_for_it() {
900        // Measured against v1.4.1 on server3: `read_parquet(3)` does not cast, it fails to match.
901        let error = resolve_table("read_parquet", &[LogicalType::Integer]).unwrap_err();
902        assert!(
903            error.message().starts_with(
904                "No function matches the given name and argument types 'read_parquet(INTEGER)'."
905            ),
906            "{error}"
907        );
908        assert!(error.message().contains("read_parquet(VARCHAR)"), "{error}");
909    }
910
911    #[test]
912    fn read_parquet_of_no_arguments_or_two_is_the_same_no_overload_message() {
913        let two = resolve_table("read_parquet", &[LogicalType::Varchar, LogicalType::Varchar]);
914        assert!(two.unwrap_err().message().contains("read_parquet(VARCHAR, VARCHAR)"));
915        let none = resolve_table("read_parquet", &[]);
916        assert!(none.unwrap_err().message().contains("read_parquet()"));
917    }
918
919    #[test]
920    fn range_stops_before_the_end_and_generate_series_stops_on_it() {
921        assert_eq!(series(TableFunction::Range, 0, 3, 1).unwrap(), vec![0, 1, 2]);
922        assert_eq!(series(TableFunction::GenerateSeries, 0, 3, 1).unwrap(), vec![0, 1, 2, 3]);
923    }
924
925    #[test]
926    fn a_step_that_does_not_divide_the_span_stops_before_the_end_of_it() {
927        // DuckDB gives 2, 4, 6 for both of these. The seven is not reached by either, which is
928        // where the two functions stop being different.
929        assert_eq!(series(TableFunction::Range, 2, 7, 2).unwrap(), vec![2, 4, 6]);
930        assert_eq!(series(TableFunction::GenerateSeries, 2, 7, 2).unwrap(), vec![2, 4, 6]);
931    }
932
933    #[test]
934    fn the_four_categories_come_out_of_the_grammars_five_rules() {
935        use rudb_parse::{COLUMN_NAME, FUNC_NAME, RESERVED, TYPE_NAME, UNRESERVED};
936        assert_eq!(keyword_categories(RESERVED), ["reserved"]);
937        assert_eq!(keyword_categories(UNRESERVED), ["unreserved"]);
938        assert_eq!(keyword_categories(COLUMN_NAME), ["column_name"]);
939        // The two rules that are one category. A word usable as a type name is usable as a function
940        // name, which is why the grammar has two rules where PostgreSQL has one category, and either
941        // rule on its own is still that one category rather than half of it.
942        assert_eq!(keyword_categories(FUNC_NAME | TYPE_NAME), ["type_function"]);
943        assert_eq!(keyword_categories(TYPE_NAME), ["type_function"]);
944        assert_eq!(keyword_categories(FUNC_NAME), ["type_function"]);
945        // Both, which is the case that makes one word two rows.
946        assert_eq!(keyword_categories(COLUMN_NAME | FUNC_NAME), ["column_name", "type_function"]);
947        // A word the grammar spells directly in a rule is in no class, and the pinned binary leaves
948        // all fifteen of those out of the table rather than giving them a category of their own.
949        assert!(keyword_categories(0).is_empty());
950    }
951
952    #[test]
953    fn a_metadata_table_given_an_argument_says_it_takes_none() {
954        for name in [
955            "rudb_strategies",
956            "duckdb_keywords",
957            "duckdb_types",
958            "duckdb_functions",
959            "duckdb_settings",
960            "duckdb_databases",
961            "duckdb_schemas",
962            "duckdb_tables",
963            "duckdb_columns",
964        ] {
965            let function = TableFunction::lookup(name).expect("a known function");
966            let error = resolve_table(name, &[LogicalType::BigInt]).expect_err("takes none");
967            assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
968            let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
969            assert_eq!(resolved.function, function);
970            assert!(matches!(resolved.columns, Columns::Fixed(_)));
971        }
972    }
973
974    #[test]
975    fn duckdb_keywords_has_duckdbs_two_columns_under_that_name() {
976        let resolved = resolve_table("DuckDB_Keywords", &[]).expect("a case insensitive name");
977        assert_eq!(resolved.function, TableFunction::DuckdbKeywords);
978        let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
979        let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
980        assert_eq!(names, ["keyword_name", "keyword_category"]);
981        assert!(fields.iter().all(|field| field.ty == LogicalType::Varchar));
982    }
983
984    #[test]
985    fn duckdb_types_has_duckdbs_seventeen_columns_under_that_name() {
986        let resolved = resolve_table("DuckDB_Types", &[]).expect("a case insensitive name");
987        assert_eq!(resolved.function, TableFunction::DuckdbTypes);
988        let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
989        let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
990        assert_eq!(names.len(), 17);
991        assert_eq!(names[0], "database_name");
992        assert_eq!(names[16], "varargs");
993        // The one column that is not a varchar, a bigint or a boolean, and the reason this table
994        // waited on the map vector.
995        let tags = fields.iter().find(|field| field.name == "tags").expect("a tags column");
996        assert_eq!(tags.ty, LogicalType::map(LogicalType::Varchar, LogicalType::Varchar));
997    }
998
999    #[test]
1000    fn duckdb_settings_has_duckdbs_seven_columns_under_that_name() {
1001        let resolved = resolve_table("DuckDB_Settings", &[]).expect("a case insensitive name");
1002        assert_eq!(resolved.function, TableFunction::DuckdbSettings);
1003        let Columns::Fixed(fields) = resolved.columns else { panic!("fixed columns") };
1004        let names: Vec<&str> = fields.iter().map(|field| field.name.as_str()).collect();
1005        assert_eq!(
1006            names,
1007            ["name", "value", "description", "input_type", "scope", "aliases", "typed_value"]
1008        );
1009        // The last one is a VARIANT in the pin and rudb has no such type, so it is text here.
1010        assert_eq!(fields[6].ty, LogicalType::Varchar);
1011    }
1012
1013    #[test]
1014    fn a_negative_step_counts_down_and_stops_on_the_same_rule() {
1015        assert_eq!(series(TableFunction::Range, 5, 1, -2).unwrap(), vec![5, 3]);
1016        assert_eq!(series(TableFunction::GenerateSeries, 5, 1, -2).unwrap(), vec![5, 3, 1]);
1017    }
1018
1019    #[test]
1020    fn a_step_going_the_wrong_way_produces_nothing_rather_than_running_forever() {
1021        assert!(series(TableFunction::Range, 0, 10, -1).unwrap().is_empty());
1022        assert!(series(TableFunction::Range, 10, 0, 1).unwrap().is_empty());
1023    }
1024
1025    #[test]
1026    fn an_empty_range_and_a_single_value_series_are_the_boundary_between_the_two() {
1027        assert!(series(TableFunction::Range, 4, 4, 1).unwrap().is_empty());
1028        assert_eq!(series(TableFunction::GenerateSeries, 4, 4, 1).unwrap(), vec![4]);
1029    }
1030
1031    #[test]
1032    fn a_step_of_zero_is_the_one_case_that_is_an_error_rather_than_nothing() {
1033        let error = series(TableFunction::Range, 1, 5, 0).unwrap_err();
1034        assert!(error.to_string().contains("interval cannot be 0"), "{error}");
1035    }
1036
1037    #[test]
1038    fn a_span_that_does_not_fit_in_an_i64_does_not_overflow_the_length() {
1039        // Not run, only counted. The point is that the count is worked out in i128, so this comes
1040        // out as a huge number rather than as a negative one that becomes a capacity panic.
1041        assert_eq!(length(TableFunction::Range, i64::MIN, i64::MAX, 1), usize::MAX);
1042    }
1043
1044    #[test]
1045    fn rudb_strategies_takes_no_arguments_and_produces_a_fixed_table() {
1046        let resolved = resolve_table("rudb_strategies", &[]).unwrap();
1047        assert_eq!(resolved.function, TableFunction::RudbStrategies);
1048        assert!(resolved.arguments.is_empty());
1049        assert_eq!(fixed(&resolved), strategy_fields());
1050    }
1051
1052    #[test]
1053    fn rudb_strategies_with_an_argument_says_it_takes_none() {
1054        let error = resolve_table("rudb_strategies", &[LogicalType::BigInt]).unwrap_err();
1055        assert!(error.to_string().contains("\"rudb_strategies\"()"), "{error}");
1056        assert!(error.to_string().contains("'rudb_strategies(BIGINT)'"), "{error}");
1057    }
1058
1059    #[test]
1060    fn the_two_pragmas_take_a_name_and_nothing_else_does() {
1061        assert!(TableFunction::PragmaTableInfo.takes_a_name());
1062        assert!(TableFunction::PragmaShow.takes_a_name());
1063        for other in [TableFunction::Range, TableFunction::DuckdbTables, TableFunction::ReadParquet]
1064        {
1065            assert!(!other.takes_a_name(), "{}", other.name());
1066        }
1067    }
1068
1069    #[test]
1070    fn pragma_table_info_answers_in_sqlites_six_columns() {
1071        let resolved = resolve_table("PRAGMA_Table_Info", &[LogicalType::Varchar])
1072            .expect("a case insensitive name");
1073        assert_eq!(resolved.function, TableFunction::PragmaTableInfo);
1074        assert_eq!(resolved.arguments, vec![LogicalType::Varchar]);
1075        let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1076        assert_eq!(names, ["cid", "name", "type", "notnull", "dflt_value", "pk"]);
1077    }
1078
1079    #[test]
1080    fn pragma_show_answers_in_the_six_columns_describe_answers_in() {
1081        let resolved =
1082            resolve_table("pragma_show", &[LogicalType::Varchar]).expect("one name, one overload");
1083        assert_eq!(resolved.function, TableFunction::PragmaShow);
1084        let names: Vec<&str> = fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1085        assert_eq!(names, ["column_name", "column_type", "null", "key", "default", "extra"]);
1086        assert!(fixed(&resolved).iter().all(|field| field.ty == LogicalType::Varchar));
1087    }
1088
1089    #[test]
1090    fn a_null_name_resolves_because_the_catalog_is_what_turns_it_down() {
1091        let resolved = resolve_table("pragma_table_info", &[LogicalType::Null]).expect("a null");
1092        assert_eq!(resolved.arguments, vec![LogicalType::Null]);
1093    }
1094
1095    #[test]
1096    fn a_pragma_given_the_wrong_arguments_lists_its_one_overload() {
1097        for count in [0, 2] {
1098            let error = resolve_table("pragma_table_info", &integers(count)).expect_err("one name");
1099            assert!(
1100                error.message().starts_with(
1101                    "No function matches the given name and argument types 'pragma_table_info("
1102                ),
1103                "{error}"
1104            );
1105            assert!(error.message().contains("\"pragma_table_info\"(VARCHAR)"), "{error}");
1106        }
1107        // A single argument of the wrong type is the same message, because the pin does not cast
1108        // an integer to a name any more than it casts one to a path.
1109        let error = resolve_table("pragma_show", &[LogicalType::Integer]).expect_err("a name");
1110        assert!(error.message().contains("'pragma_show(INTEGER)'"), "{error}");
1111    }
1112
1113    /// The same call written as a statement gets the same complaint spelled the way it was written.
1114    #[test]
1115    fn a_pragma_written_as_a_statement_is_complained_about_as_one() {
1116        let error = resolve_pragma("pragma_table_info", &integers(2)).expect_err("one name");
1117        assert!(
1118            error.message().starts_with(
1119                "No function matches the given name and argument types 'table_info(BIGINT, \
1120                 BIGINT)'"
1121            ),
1122            "{error}"
1123        );
1124        assert!(error.message().contains("\tPRAGMA \"table_info\"(VARCHAR)\n"), "{error}");
1125        // A pragma that takes nothing prints no parentheses on the candidate line at all, which is
1126        // the pin's spelling and is not the same as the empty pair the function form prints.
1127        let error = resolve_pragma("pragma_version", &integers(1)).expect_err("nothing");
1128        assert!(error.message().contains("'version(BIGINT)'"), "{error}");
1129        assert!(error.message().ends_with("\tPRAGMA \"version\"\n"), "{error}");
1130    }
1131
1132    /// A call that resolves comes back the same either way, because it is the same function.
1133    #[test]
1134    fn a_pragma_that_resolves_resolves_to_what_the_function_spelling_does() {
1135        let name = [LogicalType::Varchar];
1136        let written = resolve_pragma("pragma_table_info", &name).expect("one name");
1137        let called = resolve_table("pragma_table_info", &name).expect("one name");
1138        assert_eq!(written.function, called.function);
1139        assert_eq!(written.arguments, called.arguments);
1140        let written = resolve_pragma("pragma_version", &[]).expect("nothing");
1141        assert_eq!(written.function, TableFunction::PragmaVersion);
1142    }
1143
1144    #[test]
1145    fn the_four_pragmas_about_the_build_take_nothing_and_name_their_own_columns() {
1146        let wanted: [(&str, TableFunction, &[&str]); 4] = [
1147            (
1148                "PRAGMA_Version",
1149                TableFunction::PragmaVersion,
1150                &["library_version", "source_id", "codename"],
1151            ),
1152            ("pragma_platform", TableFunction::PragmaPlatform, &["platform"]),
1153            ("pragma_user_agent", TableFunction::PragmaUserAgent, &["user_agent"]),
1154            (
1155                "pragma_database_size",
1156                TableFunction::PragmaDatabaseSize,
1157                &[
1158                    "database_name",
1159                    "database_size",
1160                    "block_size",
1161                    "total_blocks",
1162                    "used_blocks",
1163                    "free_blocks",
1164                    "wal_size",
1165                    "memory_usage",
1166                    "memory_limit",
1167                ],
1168            ),
1169        ];
1170        for (name, function, columns) in wanted {
1171            let resolved = resolve_table(name, &[]).expect("takes none, and none were given");
1172            assert_eq!(resolved.function, function);
1173            assert!(resolved.arguments.is_empty());
1174            assert!(!function.takes_a_name(), "{name}");
1175            let written: Vec<&str> =
1176                fixed(&resolved).iter().map(|field| field.name.as_str()).collect();
1177            assert_eq!(written, columns);
1178            let error = resolve_table(name, &[LogicalType::Varchar]).expect_err("takes none");
1179            assert!(error.to_string().contains(&format!("\"{}\"()", function.name())), "{error}");
1180        }
1181    }
1182
1183    #[test]
1184    fn the_four_block_columns_are_the_only_numbers_pragma_database_size_reports() {
1185        // The pin writes three of the nine as text a person reads rather than as a number, which is
1186        // worth a test because a client doing arithmetic on `database_size` gets a cast error on
1187        // both engines and that is the compatible answer rather than a bug in either.
1188        let fields = database_size_fields();
1189        let numbers: Vec<&str> = fields
1190            .iter()
1191            .filter(|field| field.ty == LogicalType::BigInt)
1192            .map(|field| field.name.as_str())
1193            .collect();
1194        assert_eq!(numbers, ["block_size", "total_blocks", "used_blocks", "free_blocks"]);
1195        assert!(fields.iter().filter(|field| field.ty == LogicalType::Varchar).count() == 5);
1196    }
1197}