Skip to main content

rudb/
database.rs

1//! The handle everything else hangs off.
2
3use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
4
5use rudb_bind::{Bound, Parameters};
6use rudb_catalog::{Catalog, Entry, View};
7use rudb_common::{Cancel, Error, Field, LogicalType, Memory, Result, Value};
8use rudb_metrics::{Document, Report, Span};
9
10use rudb_parse::ast::Ast;
11use rudb_vector::{Chunk, Vector};
12
13use crate::config::Config;
14use crate::connection::{Connection, single};
15use crate::prepared::Prepared;
16use crate::result::QueryResult;
17use crate::settings::Settings;
18
19/// The name that means no file, which is DuckDB's spelling and SQLite's before it.
20const MEMORY: &str = ":memory:";
21
22/// An in process database.
23///
24/// One catalog, held in memory, with no file behind it. `ATTACH` and the storage format are E2, and
25/// the shape of this type does not change when they arrive: a database with a file behind it is a
26/// catalog whose tables read from a block manager rather than from a `Vec` of chunks, which is a
27/// change under [`rudb_catalog::Table`] and not a change here.
28///
29/// A handle rather than the thing itself. Cloning one is cheap and gives another handle on the same
30/// database, and [`Database::connect`] gives a [`Connection`], which is the same sharing with a
31/// name that says what it is for. The catalog is behind a lock, so every method here takes `&self`
32/// and a write from one thread is serialized against a read from another rather than refused by the
33/// compiler. That is what an embedded database has to do, because the program embedding it is the
34/// one that decided how many threads it has.
35#[derive(Debug, Clone)]
36pub struct Database {
37    shared: Shared,
38}
39
40/// The state one database is, however many handles are on it.
41#[derive(Debug, Clone)]
42pub(crate) struct Shared {
43    inner: Arc<Inner>,
44}
45
46#[derive(Debug)]
47struct Inner {
48    catalog: RwLock<Catalog>,
49    settings: Settings,
50    memory: Memory,
51}
52
53impl Default for Database {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl Database {
60    /// An empty database with the default catalog and schema, held in memory.
61    #[must_use]
62    pub fn new() -> Self {
63        Self::with_config(Config::default())
64    }
65
66    /// An empty database held in memory, opened with these settings.
67    #[must_use]
68    pub fn with_config(config: Config) -> Self {
69        let memory = Memory::new(config.memory_limit());
70        let settings = Settings::new(config);
71        let inner = Inner { catalog: RwLock::new(Catalog::new()), settings, memory };
72        Self { shared: Shared { inner: Arc::new(inner) } }
73    }
74
75    /// What this database is running with now.
76    ///
77    /// By value rather than by reference, because `SET` changes it while the database is open and a
78    /// reference into the settings would be a lock held for as long as the caller kept it. A
79    /// `Config` is three numbers, so a copy costs nothing worth avoiding.
80    #[must_use]
81    pub fn config(&self) -> Config {
82        self.shared.inner.settings.config()
83    }
84
85    /// What this database was opened with, which is what `RESET` puts a setting back to.
86    #[must_use]
87    pub fn opened_with(&self) -> Config {
88        self.shared.inner.settings.defaults()
89    }
90
91    /// One setting, by the name `SET` uses for it, in the spelling DuckDB prints.
92    ///
93    /// The Rust side of reading a setting back. `current_setting()` is the SQL side and it is not
94    /// written yet, because a scalar function over engine state is a shape no function in rudb has.
95    ///
96    /// # Errors
97    ///
98    /// For a name that is not a setting, with the names there are.
99    pub fn setting(&self, name: &str) -> Result<String> {
100        self.shared.inner.settings.value(name)
101    }
102
103    /// Which implementation runs at each seam, as this session has left it.
104    ///
105    /// The session half of the three surfaces. The other two reach the same place: a process flag
106    /// is a `SET` the shell runs before anything else, and a per query hint is this with the
107    /// query's own pins laid on top, which is [`Database::seams_for`].
108    #[must_use]
109    pub fn seams(&self) -> rudb_seam::Settings {
110        self.shared.inner.settings.seams()
111    }
112
113    /// The seam settings one query runs under, which is [`Database::seams`] plus its hints.
114    ///
115    /// `SELECT /*+ hash.table(unchained) */ ...` pins a seam for one statement and leaves the
116    /// session alone, which is what a researcher comparing two implementations of one thing over a
117    /// suite needs, because the alternative is a `SET` before every query and a `RESET` after it
118    /// that somebody eventually forgets.
119    ///
120    /// # Errors
121    ///
122    /// A parse error, and everything a hint naming a seam nobody has raises.
123    pub fn seams_for(&self, sql: &str) -> Result<rudb_seam::Settings> {
124        self.shared.seams(sql)
125    }
126
127    /// The memory budget every query against this database is held to.
128    ///
129    /// One budget for the database rather than one per query, which is what
130    /// [`Config::memory_limit`] means: two queries running at once share the limit rather than
131    /// getting one each. Public because [`rudb_common::Memory::used`] is the only way to see what
132    /// is being held, and a program that sets a limit wants to know how close it is.
133    #[must_use]
134    pub fn memory(&self) -> &Memory {
135        &self.shared.inner.memory
136    }
137
138    /// Opens a database by name.
139    ///
140    /// `:memory:` and the empty string are an in memory database, which are DuckDB's two spellings
141    /// of it. Anything else names a file, and a file needs a storage format, which is #103. It is an
142    /// error here rather than a silent in memory database, because a program that opened a file and
143    /// wrote to it would be told nothing until it looked for its data again.
144    ///
145    /// # Errors
146    ///
147    /// When the name is a file.
148    pub fn open(path: &str) -> Result<Self> {
149        Self::open_with(path, Config::default())
150    }
151
152    /// Opens a database by name, with these settings.
153    ///
154    /// # Errors
155    ///
156    /// When the name is a file.
157    pub fn open_with(path: &str, config: Config) -> Result<Self> {
158        if path.is_empty() || path == MEMORY {
159            return Ok(Self::with_config(config));
160        }
161        Err(Error::not_implemented(format!(
162            "cannot open \"{path}\", because there is no storage format yet, see \
163             https://github.com/tamnd/rudb/issues/103"
164        )))
165    }
166
167    /// A connection to this database.
168    #[must_use]
169    pub fn connect(&self) -> Connection {
170        Connection::new(self.shared.clone())
171    }
172
173    /// Parses a statement so it can be run more than once, with values for its parameters.
174    ///
175    /// The same call as [`Connection::prepare`].
176    ///
177    /// # Errors
178    ///
179    /// A parse error. A name that does not resolve or a type that does not work out is an error at
180    /// execution rather than here, because a parameter has no type until it has a value.
181    pub fn prepare(&self, sql: &str) -> Result<Prepared> {
182        Prepared::new(self.shared.clone(), sql)
183    }
184
185    /// Reads the catalog.
186    ///
187    /// A closure rather than a returned reference, because the catalog is behind a lock and a
188    /// reference out of it would outlive the guard. The lock is held for the call and no longer.
189    pub fn with_catalog<T>(&self, read: impl FnOnce(&Catalog) -> T) -> T {
190        read(&self.shared.read())
191    }
192
193    /// Writes the catalog.
194    ///
195    /// Public because a program that builds its own catalog rather than parsing SQL to build one is
196    /// a real thing an embedded database gets used for.
197    pub fn with_catalog_mut<T>(&self, write: impl FnOnce(&mut Catalog) -> T) -> T {
198        write(&mut self.shared.write())
199    }
200
201    /// Defines a table.
202    ///
203    /// The name is `table`, `schema.table` or `catalog.schema.table`, and anything unqualified goes
204    /// to the default catalog and schema, which is what an unqualified name in a query resolves
205    /// against too.
206    ///
207    /// # Errors
208    ///
209    /// If the name has more than three parts, if the catalog or the schema does not exist, if the
210    /// table already exists, or if two of the columns have the same name.
211    pub fn create_table(&self, name: &str, columns: Vec<Field>) -> Result<()> {
212        let parts: Vec<&str> = name.split('.').collect();
213        let mut catalog = self.shared.write();
214        let resolved = catalog.resolve_for_create(&parts)?;
215        catalog.create_table(resolved, columns)
216    }
217
218    /// Drops a table.
219    ///
220    /// # Errors
221    ///
222    /// If the name does not resolve or the table does not exist.
223    pub fn drop_table(&self, name: &str) -> Result<()> {
224        let parts: Vec<&str> = name.split('.').collect();
225        let mut catalog = self.shared.write();
226        let resolved = catalog.resolve(&parts)?;
227        catalog.drop_table(&resolved)
228    }
229
230    /// Appends rows to a table, each row left to right in the table's column order.
231    ///
232    /// The row shaped write path, because the caller with rows in hand is the common case and the
233    /// caller with columns in hand can reach [`Database::with_catalog_mut`] and append a
234    /// [`rudb_vector::Chunk`] directly. Values are converted to the column's type on the way in, so
235    /// an `Integer` lands in a `BIGINT` column.
236    ///
237    /// # Errors
238    ///
239    /// If the name does not resolve, if a row is not as wide as the table, or if a value cannot be
240    /// converted to its column's type.
241    pub fn append(&self, name: &str, rows: &[Vec<Value>]) -> Result<()> {
242        let parts: Vec<&str> = name.split('.').collect();
243        let mut catalog = self.shared.write();
244        let resolved = catalog.resolve(&parts)?;
245        catalog.table_mut(&resolved)?.append_rows(rows)
246    }
247
248    /// How many rows a table holds.
249    ///
250    /// # Errors
251    ///
252    /// If the name does not resolve or the table does not exist.
253    pub fn table_len(&self, name: &str) -> Result<usize> {
254        let parts: Vec<&str> = name.split('.').collect();
255        let catalog = self.shared.read();
256        let resolved = catalog.resolve(&parts)?;
257        Ok(catalog.table(&resolved)?.rows().len())
258    }
259
260    /// Every table in the database, unqualified, in creation order.
261    ///
262    /// Unqualified because that is what a person typing `.tables` wants to read and what they would
263    /// then type into a query. Two tables of the same name in different schemas both appear, which
264    /// is the same thing DuckDB's `.tables` does.
265    #[must_use]
266    pub fn table_names(&self) -> Vec<String> {
267        self.shared.read().tables().map(|table| table.name().table.clone()).collect()
268    }
269
270    /// The `CREATE TABLE` that would define a table as it stands.
271    ///
272    /// Built from the catalog rather than remembered from the statement that made it, so a table
273    /// defined by [`Database::create_table`] describes itself as well as one defined by SQL. It
274    /// carries the column names, the types and `NOT NULL`, and nothing else, because nothing else
275    /// is in the catalog yet. Defaults, primary keys and check constraints appear here the day the
276    /// catalog holds them.
277    ///
278    /// # Errors
279    ///
280    /// If the name does not resolve or the table does not exist.
281    pub fn table_sql(&self, name: &str) -> Result<String> {
282        let parts: Vec<&str> = name.split('.').collect();
283        let catalog = self.shared.read();
284        let resolved = catalog.resolve(&parts)?;
285        let table = catalog.table(&resolved)?;
286        let columns: Vec<String> = table
287            .columns()
288            .iter()
289            .map(|field| {
290                let null = if field.not_null { " NOT NULL" } else { "" };
291                format!("{} {}{null}", field.name, field.ty)
292            })
293            .collect();
294        Ok(format!("CREATE TABLE {}({});", resolved.table, columns.join(", ")))
295    }
296
297    /// Runs one query and returns every row it produced.
298    ///
299    /// The same call as [`Connection::query`], for a program that has one database and no reason to
300    /// name a connection.
301    ///
302    /// The query timeout in [`Database::config`] applies, and nothing can interrupt it, because an
303    /// interrupt needs somebody holding the other end of a token and a bare database hands out no
304    /// token. [`Connection::interrupt`] is that other end.
305    ///
306    /// # Errors
307    ///
308    /// A parse error, a binder error, or anything the operators raise while running, which is
309    /// mostly cast failures and arithmetic that leaves the range of its type.
310    pub fn query(&self, sql: &str) -> Result<QueryResult> {
311        self.shared.query(sql, &self.shared.token())
312    }
313
314    /// Runs one statement, which may change the database.
315    ///
316    /// # Errors
317    ///
318    /// A parse error, a binder error, a catalog error, or anything the operators raise.
319    pub fn execute(&self, sql: &str) -> Result<QueryResult> {
320        self.shared.execute(sql, &self.shared.token())
321    }
322
323    /// The plan for a query, in the textual form `spec/07-execution.md` describes, without running
324    /// it.
325    ///
326    /// The same plan `EXPLAIN` prints, without the estimates and as a `String` rather than a result
327    /// set, which is what the plan tests and the optimizer work read. The text round trips:
328    /// `rudb_plan::Plan::parse` of this string gives back the plan it was printed from, and that is
329    /// why the estimates are not on it.
330    ///
331    /// # Errors
332    ///
333    /// A parse error or a binder error.
334    pub fn plan(&self, sql: &str) -> Result<String> {
335        self.shared.plan(sql)
336    }
337
338    /// Runs a query and returns the single value it produced.
339    ///
340    /// A convenience for `SELECT count(*) FROM t` and the rest of the one cell queries, which are
341    /// most of what a program embedded in something else asks.
342    ///
343    /// # Errors
344    ///
345    /// Everything [`Database::query`] can raise, plus an error if the result is not one row of one
346    /// column.
347    pub fn value(&self, sql: &str) -> Result<Value> {
348        single(&self.query(sql)?)
349    }
350}
351
352impl Shared {
353    /// The catalog, for reading.
354    ///
355    /// A poisoned lock is taken rather than reported. Poisoning says some thread panicked while it
356    /// held the lock, and the catalog is a `Vec` of chunks rather than an invariant somebody was
357    /// halfway through breaking, so refusing every later query would turn one panicked query into a
358    /// dead database.
359    fn read(&self) -> RwLockReadGuard<'_, Catalog> {
360        self.inner.catalog.read().unwrap_or_else(PoisonError::into_inner)
361    }
362
363    /// The catalog, for writing.
364    fn write(&self) -> RwLockWriteGuard<'_, Catalog> {
365        self.inner.catalog.write().unwrap_or_else(PoisonError::into_inner)
366    }
367
368    /// Runs one query and returns every row it produced.
369    ///
370    /// `EXPLAIN` comes through here as well as through [`Shared::execute`], because it answers with
371    /// rows and this is the path that reads rows back. It takes the read lock like any other query,
372    /// since printing a plan changes nothing. A statement that writes is refused here rather than
373    /// run under a read lock.
374    pub(crate) fn query(&self, sql: &str, cancel: &Cancel) -> Result<QueryResult> {
375        let catalog = self.read();
376        let seams = self.seams(sql)?;
377        let context = self.optimizer(&catalog)?;
378        let ast = rudb_parse::parse_ast(sql)?;
379        match rudb_bind::bind_statement_with(&ast, &catalog, &Parameters::new())? {
380            Bound::Query(mut plan) => {
381                rudb_opt::optimize_with(&mut plan, &context)?;
382                run(sql, &plan, &catalog, cancel, &self.inner.memory, context.statistics(), &seams)
383            }
384            Bound::Explain { mut plan, analyze } => {
385                rudb_opt::optimize_with(&mut plan, &context)?;
386                let seams = rudb_opt::explain::Seams::new(&seams, rudb_exec::registries());
387                explaining(
388                    &plan,
389                    &catalog,
390                    cancel,
391                    &self.inner.memory,
392                    &context,
393                    seams,
394                    analyze,
395                    sql,
396                )
397            }
398            _ => Err(Error::not_implemented("a statement that is not a query, on the query path")),
399        }
400    }
401
402    /// The seam settings a statement runs under, which is the session's with its hints on top.
403    ///
404    /// What the settings choose goes nowhere yet, because no seam has a second implementation to
405    /// choose between until F1 and every one of the twenty seven is unregistered. What they do
406    /// today is fail a statement whose hint names a seam nobody has, and feed the seam section of
407    /// `EXPLAIN`, which is the half of the behaviour worth having before the other half arrives: a
408    /// hint that is quietly ignored is a measurement of the wrong thing.
409    pub(crate) fn seams(&self, sql: &str) -> Result<rudb_seam::Settings> {
410        let mut seams = self.inner.settings.seams();
411        for hint in rudb_parse::hints(sql)? {
412            seams.hint(hint)?;
413        }
414        Ok(seams)
415    }
416
417    /// The passes this database's queries run, as `SET disabled_optimizers` has left them, with
418    /// the row counts the catalog holds.
419    ///
420    /// Rebuilt for each statement rather than held, because the statement before this one may have
421    /// been the `SET` and the statement before that may have been an `INSERT`. It cannot fail: the
422    /// names were checked when they were set, and the `?` is here because nothing stops a later
423    /// version from having a pass that goes away.
424    ///
425    /// The catalog comes in as an argument rather than being read from the lock here, because
426    /// every caller is already holding that lock and one of them is holding it for writing. This
427    /// is also the seam that stops the optimizer from reaching the catalog on its own: what it
428    /// gets is a copy of the counts, which is the whole of what estimation reads today.
429    fn optimizer(&self, catalog: &Catalog) -> Result<rudb_opt::pass::Context> {
430        let mut context =
431            rudb_opt::pass::Context::without(&self.inner.settings.disabled_optimizers())?;
432        let mut statistics = rudb_opt::estimate::Statistics::new();
433        for table in catalog.tables() {
434            let name = table.name();
435            let rows = u64::try_from(table.rows().len()).unwrap_or(u64::MAX);
436            statistics.record(&name.catalog, &name.schema, &name.table, rows);
437        }
438        context.measure(statistics);
439        Ok(context)
440    }
441
442    /// The query timeout this database was opened with.
443    pub(crate) fn timeout(&self) -> Option<std::time::Duration> {
444        self.inner.settings.config().query_timeout()
445    }
446
447    /// The token a statement of this database's runs under, when nobody holds one of their own.
448    ///
449    /// It carries the configured query timeout and nothing can interrupt it, because there is
450    /// nobody holding the other half. [`Connection`] is where the other half lives.
451    pub(crate) fn token(&self) -> Cancel {
452        match self.inner.settings.config().query_timeout() {
453            Some(timeout) => Cancel::after(timeout),
454            None => Cancel::new(),
455        }
456    }
457
458    /// The plan a query runs.
459    pub(crate) fn plan(&self, sql: &str) -> Result<String> {
460        let catalog = self.read();
461        Ok(planned(sql, &catalog, &self.optimizer(&catalog)?)?.to_string())
462    }
463
464    /// Runs one statement, which may change the database.
465    ///
466    /// The write lock is taken for the whole statement rather than for the part that writes,
467    /// because the part that writes is decided by what the part that reads produced. `INSERT INTO t
468    /// SELECT * FROM t` would otherwise read the table under a read lock, let go, and append to
469    /// whatever the table had become in between.
470    pub(crate) fn execute(&self, sql: &str, cancel: &Cancel) -> Result<QueryResult> {
471        let ast = rudb_parse::parse_ast(sql)?;
472        self.execute_ast(&ast, sql, &Parameters::new(), cancel)
473    }
474
475    /// Runs one parsed statement, with values for its parameters.
476    ///
477    /// The prepared statement path, and the path an ordinary statement takes once it is parsed, so
478    /// that there is one description of what running a statement does.
479    pub(crate) fn execute_ast(
480        &self,
481        ast: &Ast,
482        sql: &str,
483        parameters: &Parameters,
484        cancel: &Cancel,
485    ) -> Result<QueryResult> {
486        let seams = self.seams(sql)?;
487        let mut catalog = self.write();
488        let context = self.optimizer(&catalog)?;
489        match rudb_bind::bind_statement_with(ast, &catalog, parameters)? {
490            Bound::Query(mut plan) => {
491                rudb_opt::optimize_with(&mut plan, &context)?;
492                run(sql, &plan, &catalog, cancel, &self.inner.memory, context.statistics(), &seams)
493            }
494            Bound::Explain { mut plan, analyze } => {
495                rudb_opt::optimize_with(&mut plan, &context)?;
496                let seams = rudb_opt::explain::Seams::new(&seams, rudb_exec::registries());
497                explaining(
498                    &plan,
499                    &catalog,
500                    cancel,
501                    &self.inner.memory,
502                    &context,
503                    seams,
504                    analyze,
505                    sql,
506                )
507            }
508            Bound::Setting(setting) => {
509                let value = setting.value.as_ref();
510                self.inner.settings.apply(
511                    &self.inner.memory,
512                    &setting.name,
513                    setting.scope,
514                    value,
515                )?;
516                Ok(QueryResult::empty())
517            }
518            Bound::CreateTable(create) => {
519                create_table(
520                    sql,
521                    create,
522                    &mut catalog,
523                    cancel,
524                    &self.inner.memory,
525                    &context,
526                    &seams,
527                )?;
528                Ok(QueryResult::empty())
529            }
530            Bound::CreateView(create) => {
531                create_view(create, &mut catalog)?;
532                Ok(QueryResult::empty())
533            }
534            Bound::DropTable(drop) => {
535                for name in &drop.names {
536                    match drop.kind {
537                        Entry::Table => catalog.drop_table(name)?,
538                        Entry::View => catalog.drop_view(name)?,
539                    }
540                }
541                Ok(QueryResult::empty())
542            }
543            Bound::Insert(mut insert) => {
544                // The source runs to completion before anything is appended, which is not an
545                // implementation detail. `INSERT INTO t SELECT * FROM t` reads the table it writes,
546                // and a version of this that appended chunk by chunk would either read its own
547                // output forever or depend on how the scan holds its chunks.
548                rudb_opt::optimize_with(&mut insert.source, &context)?;
549                let result = run(
550                    sql,
551                    &insert.source,
552                    &catalog,
553                    cancel,
554                    &self.inner.memory,
555                    context.statistics(),
556                    &seams,
557                )?;
558                let table = catalog.table_mut(&insert.name)?;
559                for chunk in result.into_chunks() {
560                    table.append(chunk)?;
561                }
562                Ok(QueryResult::empty())
563            }
564        }
565    }
566}
567
568/// A query bound and then optimized, which is the plan that runs.
569///
570/// What [`Database::plan`] dumps, and it optimizes rather than stopping at the bound plan because a
571/// dump of the bound plan next to a run of the optimized one would make the dump a description of
572/// something nobody executes, which is the one thing a plan dump must not be. `EXPLAIN` and the
573/// query path do the same two steps in that order for the same reason.
574fn planned(
575    sql: &str,
576    catalog: &Catalog,
577    context: &rudb_opt::pass::Context,
578) -> Result<rudb_plan::Plan> {
579    let mut plan = rudb_bind::bind_sql(sql, catalog)?;
580    rudb_opt::optimize_with(&mut plan, context)?;
581    Ok(plan)
582}
583
584/// Builds and drains one plan, stopping if the token says to or if it runs out of memory.
585///
586/// The result is materialized, so it is charged, and the charge is handed to the result and
587/// released when the result is dropped. That is what makes a program holding ten results at once
588/// count as holding ten results: the limit is on the database and a result outlives the query.
589///
590/// This is also the one place a metrics document is made. Everything in it below the top level
591/// comes out of the report the builder filled, and the two spans here are the two things only this
592/// function knows: how long the tree took to build and how long it took to drain. Parsing, binding
593/// and optimizing happened before this was called and their timings stay at zero until the clock
594/// moves up to the statement path.
595///
596/// A query that fails part way through has a document too, and it is thrown away here, because an
597/// error is a [`rudb_common::Error`] and that type is two ranks below the one the document lives
598/// in. Carrying it out of a failure is worth doing and it is a change to how an error is reported
599/// rather than a change to this function.
600fn run(
601    sql: &str,
602    plan: &rudb_plan::Plan,
603    catalog: &Catalog,
604    cancel: &Cancel,
605    memory: &Memory,
606    statistics: &rudb_opt::estimate::Statistics,
607    seams: &rudb_seam::Settings,
608) -> Result<QueryResult> {
609    // The budget is shared by the database and its high-water mark survives a query. Reset it to
610    // what is live now before measuring this execution, otherwise a metrics document either says
611    // zero forever (when nobody copies the mark) or inherits the largest earlier query. A caller
612    // with concurrent statements cannot attribute the shared budget to one query; this field is a
613    // database-level peak in that case. The CLI benchmark path has one statement in flight.
614    memory.forget_peak();
615    let report = Report::new();
616    let building = Span::start();
617    let mut root = rudb_exec::build_measured(plan, catalog, cancel, memory, seams, &report)?;
618    let (built_wall, built_cpu) = building.stop();
619    let names = root.schema().names();
620    let types = root.schema().types();
621    let mut held = memory.reservation();
622    let mut chunks = Vec::new();
623    // This loop is the root pipeline's driver. Every other pipeline is drained by the loop that
624    // fills its sink and that loop reports its own time, and this one is pulled from here, so this
625    // is the only place that can report it. The span it hands back is the whole execution including
626    // the pipelines that ran inside it, which is what `execute_ns` is; what the driver keeps for
627    // itself is that minus what they charged.
628    let driver = report.driving(rudb_plan::ROOT);
629    let driving = driver.running();
630    while let Some(chunk) = root.next()? {
631        if chunk.is_empty() {
632            continue;
633        }
634        // flatten: this is the top of the query and the chunk is about to become a result set that
635        // somebody outside the engine reads. A caller holding a `Result` gets a value at a time, so
636        // a dictionary or a constant here would be a form every one of them has to understand to
637        // read a row. The decode stops at this line and nothing below it sees a flat column.
638        let chunk = chunk.flatten()?;
639        held.grow(u64::try_from(chunk.footprint()).unwrap_or(u64::MAX))?;
640        chunks.push(chunk);
641    }
642    let (ran_wall, ran_cpu) = driving.stop();
643    let mut metrics = Document::new(sql);
644    metrics.settings.memory_limit = memory.limit();
645    metrics.settings.threads = 1;
646    metrics.timing.physical_ns = built_wall;
647    metrics.timing.execute_ns = ran_wall;
648    metrics.timing.total_ns = built_wall.saturating_add(ran_wall);
649    metrics.resource.cpu_ns = built_cpu.saturating_add(ran_cpu);
650    metrics.resource.build_cpu_ns = built_cpu;
651    metrics.resource.peak_bytes = memory.peak();
652    report.fill(&mut metrics);
653    rudb_opt::explain::record_estimates(plan, statistics, &mut metrics);
654    Ok(QueryResult::new(names, types, chunks, held).measured(metrics))
655}
656
657/// The plan `EXPLAIN` prints, run first if `ANALYZE` was asked for.
658///
659/// `ANALYZE` runs the query and throws the rows away. That is the whole difference between the two,
660/// and it is deliberately the only difference: the plan that is printed is the plan that was built
661/// and drained, so a number on a line came from the operator on that line rather than from an
662/// operator something else would have built.
663///
664/// The rows are dropped rather than returned because the result set of `EXPLAIN ANALYZE` is the
665/// plan. DuckDB does the same and calls the row `analyzed_plan`, and a client that gets a query's
666/// rows back from an `EXPLAIN` has no way to tell which it asked for.
667#[allow(clippy::too_many_arguments)]
668fn explaining(
669    plan: &rudb_plan::Plan,
670    catalog: &Catalog,
671    cancel: &Cancel,
672    memory: &Memory,
673    context: &rudb_opt::pass::Context,
674    seams: rudb_opt::explain::Seams<'_>,
675    analyze: bool,
676    sql: &str,
677) -> Result<QueryResult> {
678    let statistics = context.statistics();
679    if !analyze {
680        return explained(
681            "logical_plan",
682            &rudb_opt::explain::explain_with(plan, statistics, seams),
683        );
684    }
685    let result = run(sql, plan, catalog, cancel, memory, statistics, seams.settings())?;
686    let measured = result.metrics().expect("a query that ran reports what it did");
687    let text = rudb_opt::explain::analyzed(plan, statistics, seams, measured);
688    explained("analyzed_plan", &text)
689}
690
691/// One row of two strings, which is the result set `EXPLAIN` hands back.
692///
693/// The column names and the shape are DuckDB's, `explain_key` and `explain_value`, because a
694/// client reading a result set has to cope with whatever comes out and there is no reason to make
695/// it cope with something new. The text in the second column is ours, since
696/// `spec/12-duckdb-compat.md` section 12.5 excludes explain output from the guarantee.
697///
698/// One row rather than one per operator. DuckDB puts its whole tree in a single value and every
699/// shell prints it as a block, and splitting it into rows would mean a shell's column width
700/// deciding where a plan wraps.
701fn explained(key: &str, text: &str) -> Result<QueryResult> {
702    let key = Vector::from_values(LogicalType::Varchar, &[Value::Varchar(key.to_owned())])?;
703    let value = Vector::from_values(LogicalType::Varchar, &[Value::Varchar(text.to_owned())])?;
704    Ok(QueryResult::new(
705        vec!["explain_key".to_owned(), "explain_value".to_owned()],
706        vec![LogicalType::Varchar, LogicalType::Varchar],
707        vec![Chunk::new(vec![key, value])?],
708        Memory::unlimited().reservation(),
709    ))
710}
711
712/// The `CREATE VIEW` half of a statement.
713///
714/// There is nothing to run. The body was bound by the binder to check that it can be, and what is
715/// kept is the text, so this is the two modifiers and a catalog call.
716fn create_view(create: rudb_bind::CreateView, catalog: &mut Catalog) -> Result<()> {
717    if create.if_not_exists && catalog.entry(&create.name).is_ok() {
718        return Ok(());
719    }
720    if create.or_replace && catalog.view(&create.name).is_ok() {
721        catalog.drop_view(&create.name)?;
722    }
723    catalog.create_view(View::new(create.name, create.sql, create.aliases))
724}
725
726/// The `CREATE TABLE` half of a statement.
727fn create_table(
728    sql: &str,
729    mut create: rudb_bind::CreateTable,
730    catalog: &mut Catalog,
731    cancel: &Cancel,
732    memory: &Memory,
733    context: &rudb_opt::pass::Context,
734    seams: &rudb_seam::Settings,
735) -> Result<()> {
736    if create.if_not_exists && catalog.table(&create.name).is_ok() {
737        return Ok(());
738    }
739    // The query runs before the old table is dropped, so `CREATE OR REPLACE TABLE t AS SELECT * FROM
740    // t` reads the table it is about to replace rather than the empty new one.
741    let rows = match &mut create.source {
742        Some(plan) => {
743            rudb_opt::optimize_with(plan, context)?;
744            Some(run(sql, plan, catalog, cancel, memory, context.statistics(), seams)?)
745        }
746        None => None,
747    };
748    if create.or_replace && catalog.table(&create.name).is_ok() {
749        catalog.drop_table(&create.name)?;
750    }
751    catalog.create_table(create.name.clone(), create.columns)?;
752    if let Some(rows) = rows {
753        let table = catalog.table_mut(&create.name)?;
754        for chunk in rows.into_chunks() {
755            table.append(chunk)?;
756        }
757    }
758    Ok(())
759}