rudb-exec 0.3.19

Operators, morsels, the scheduler, hash tables, sorting and spilling.
Documentation
//! `duckdb_extensions()` and `duckdb_optimizers()`, the two tables that describe the engine itself.
//!
//! Both are a list of names a tool reads on connect to find out what it is talking to, and both are
//! a place where rudb has to answer about itself rather than reproduce what the pin says. The two
//! answers come out different ways round and it is worth saying why.
//!
//! `duckdb_optimizers()` is the whole of DuckDB's list, all forty four, because the table means "the
//! names `SET disabled_optimizers` takes" and rudb takes all forty four. `rudb_opt::UPSTREAM` is
//! already that list and already the thing the setting is checked against, so this table reads the
//! same constant rather than a second copy of it. Accepting a name for a pass rudb has not written
//! is not a pretence: turning off a pass that does not exist is a request that has already been
//! granted, and refusing it would fail a `SET` and end a corpus file over a pass whose absence
//! changes no answer. `rudb_opt::PASSES` is the eight that are actually written and it is not what
//! this table returns, because a client reading this table is asking what it may name.
//!
//! `duckdb_extensions()` is the other way round. The names and the descriptions and the aliases are
//! the pin's, because they are facts about the extensions rather than about the engine, and the two
//! boolean columns are rudb's own answer. `parquet` and `core_functions` are loaded and installed
//! here, because `read_parquet` and `COPY TO` really do read and write Parquet and the function
//! library really is there, and everything else is `NOT_INSTALLED`. That includes three the pin has
//! statically linked. `icu` is false because there is no session time zone and no collation, which
//! is a later box on the same milestone. `json` is false because `rudb-json` is a scaffold. `shell`
//! and `autocomplete` are false because they are the DuckDB shell's and rudb's command line tool is
//! not that shell.
//!
//! A row for an extension rudb does not have is worth returning rather than leaving out. A tool that
//! asks whether `spatial` is available gets false, which is the answer, where an empty table would
//! make it guess.
//!
//! The empty strings are measured and are not nulls. An extension that is not installed reports an
//! empty `install_path`, an empty `extension_version` and an empty `installed_from`, and only
//! `signature_key_fingerprint` is null, on every row including the loaded ones.
//!
//! The extension rows come out in the pin's own order without anything being done about it, because
//! the pin returns them sorted by name and the list is written sorted. The optimizer rows do not:
//! the pin returns them in the order its pipeline runs the passes in and rudb returns them sorted,
//! which is the same divergence `crate::keywords` has and it is left for the same reason. The order
//! a list happens to be built in is not a fact about the language, and a sqllogictest record that
//! cares about order says so.

use rudb_common::{LogicalType, Result, Value};
use rudb_functions::{extension_fields, optimizer_fields};
use rudb_opt::UPSTREAM;
use rudb_plan::{Plan, Slice};

use crate::metadata::{Metadata, text};

/// What a built in extension reports as its `install_path`, which is the pin's spelling.
const BUILT_IN: &str = "(BUILT-IN)";

/// Every extension DuckDB's default build advertises, and whether rudb has it.
///
/// The name, then whether rudb provides it, then the aliases, then the description. The aliases and
/// the description are the pin's word for word, because they say what the extension is rather than
/// what this engine does about it.
const EXTENSIONS: &[(&str, bool, &[&str], &str)] = &[
    ("autocomplete", false, &[], "Adds support for autocomplete in the shell"),
    ("avro", false, &[], "Adds support for reading Avro files"),
    ("aws", false, &[], "Provides features that depend on the AWS SDK"),
    ("azure", false, &[], "Adds a filesystem abstraction for Azure blob storage to DuckDB"),
    ("core_functions", true, &[], "Core function library"),
    ("delta", false, &[], "Adds support for Delta Lake"),
    ("ducklake", false, &[], "Adds support for DuckLake, SQL as a Lakehouse Format"),
    ("encodings", false, &[], "All unicode encodings to UTF-8"),
    ("excel", false, &[], "Adds support for Excel-like format strings"),
    ("fts", false, &[], "Adds support for Full-Text Search Indexes"),
    (
        "httpfs",
        false,
        &["http", "https", "s3"],
        "Adds support for reading and writing files over a HTTP(S) connection",
    ),
    ("iceberg", false, &[], "Adds support for Apache Iceberg"),
    ("icu", false, &[], "Adds support for time zones and collations using the ICU library"),
    ("inet", false, &[], "Adds support for IP-related data types and functions"),
    ("json", false, &[], "Adds support for JSON operations"),
    ("lance", false, &[], "Adds support for querying Lance datasets"),
    ("motherduck", false, &["md"], "Enables motherduck integration with the system"),
    ("mysql_scanner", false, &["mysql"], "Adds support for connecting to a MySQL database"),
    ("odbc_scanner", false, &["odbc"], "Adds support for connecting to remote databases over ODBC"),
    ("parquet", true, &[], "Adds support for reading and writing parquet files"),
    (
        "postgres_scanner",
        false,
        &["postgres"],
        "Adds support for connecting to a Postgres database",
    ),
    ("quack", false, &[], "The DuckDB 'Quack' Client/Server Protocol"),
    ("shell", false, &[], "Adds CLI-specific support and functionalities"),
    (
        "spatial",
        false,
        &[],
        "Geospatial extension that adds support for working with spatial data and functions",
    ),
    (
        "sqlite_scanner",
        false,
        &["sqlite", "sqlite3"],
        "Adds support for reading and writing SQLite database files",
    ),
    ("tpcds", false, &[], "Adds TPC-DS data generation and query support"),
    ("tpch", false, &[], "Adds TPC-H data generation and query support"),
    ("ui", false, &[], "Adds local UI for DuckDB"),
    ("unity_catalog", false, &["uc_catalog"], "Adds support for connecting to Unity Catalog"),
    (
        "vortex",
        false,
        &[],
        "Adds support for reading and writing files using the Vortex file format",
    ),
    ("vss", false, &[], "Adds indexing support to accelerate Vector Similarity Search"),
];

/// Every extension and whether this engine has it, in the columns the plan asked for.
///
/// # Errors
///
/// If the plan asks for a column this table does not have.
pub(crate) fn extensions(plan: &Plan, index: u32, columns: Slice) -> Result<Metadata> {
    let mut rows = Vec::with_capacity(EXTENSIONS.len());
    for (name, held, aliases, description) in EXTENSIONS {
        let version = if *held { env!("CARGO_PKG_VERSION") } else { "" };
        rows.push(vec![
            text(name),
            Value::Boolean(*held),
            Value::Boolean(*held),
            text(if *held { BUILT_IN } else { "" }),
            text(description),
            Value::List {
                element: LogicalType::Varchar,
                values: aliases.iter().map(|alias| text(alias)).collect(),
            },
            text(version),
            text(if *held { "STATICALLY_LINKED" } else { "NOT_INSTALLED" }),
            text(""),
            Value::Null,
        ]);
    }
    Metadata::new("duckdb_extensions", &extension_fields(), &rows, plan, index, columns)
}

/// Every name `SET disabled_optimizers` takes, in the columns the plan asked for.
///
/// # Errors
///
/// If the plan asks for a column this table does not have.
pub(crate) fn optimizers(plan: &Plan, index: u32, columns: Slice) -> Result<Metadata> {
    let rows: Vec<Vec<Value>> = UPSTREAM.iter().map(|name| vec![text(name)]).collect();
    Metadata::new("duckdb_optimizers", &optimizer_fields(), &rows, plan, index, columns)
}

#[cfg(test)]
mod tests {
    use super::EXTENSIONS;

    /// The pin returns thirty one rows and so does this, and the names are sorted there and here.
    #[test]
    fn the_extension_list_is_the_pins_list_in_the_pins_order() {
        assert_eq!(EXTENSIONS.len(), 31);
        let mut sorted: Vec<&str> = EXTENSIONS.iter().map(|(name, ..)| *name).collect();
        let written = sorted.clone();
        sorted.sort_unstable();
        assert_eq!(written, sorted);
    }

    /// Two are held and the rest are not, and a held one is one somebody can point at.
    #[test]
    fn the_two_extensions_this_engine_claims_are_the_two_it_has() {
        let held: Vec<&str> =
            EXTENSIONS.iter().filter(|(_, held, ..)| *held).map(|(name, ..)| *name).collect();
        assert_eq!(held, vec!["core_functions", "parquet"]);
    }
}