use rudb_catalog::{Catalog, Database, Schema, Table};
use rudb_common::{LogicalType, Result, Value};
use rudb_functions::{
DUCKDB, canonical, column_fields, database_fields, numeric_facts, schema_fields, table_fields,
type_oid, view_fields,
};
use rudb_parse::quoted;
use rudb_plan::{Plan, Slice};
use crate::metadata::{Metadata, text};
pub(crate) fn databasenames(
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let mut rows = Vec::with_capacity(catalog.databases().len());
for database in catalog.databases() {
rows.push(vec![
text(database.name()),
Value::BigInt(database.oid()),
Value::Null,
Value::Null,
empty(),
Value::Boolean(false),
text(DUCKDB),
Value::Boolean(false),
Value::Boolean(false),
Value::Null,
empty(),
]);
}
Metadata::new("duckdb_databases", &database_fields(), &rows, plan, index, columns)
}
pub(crate) fn schemanames(
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let mut rows = Vec::new();
for database in catalog.databases() {
for schema in database.schemas() {
rows.push(vec![
Value::BigInt(schema.oid()),
text(database.name()),
Value::BigInt(database.oid()),
text(schema.name()),
Value::Null,
empty(),
Value::Boolean(true),
Value::Null,
Value::Null,
Value::Null,
]);
}
}
Metadata::new("duckdb_schemas", &schema_fields(), &rows, plan, index, columns)
}
pub(crate) fn tablenames(
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let mut rows = Vec::new();
for (database, schema, table) in entries(catalog) {
let count = i64::try_from(table.columns().len()).unwrap_or(i64::MAX);
rows.push(vec![
text(database.name()),
Value::BigInt(database.oid()),
text(schema.name()),
Value::BigInt(schema.oid()),
text(&table.name().table),
Value::BigInt(table.oid()),
Value::Null,
empty(),
Value::Boolean(false),
Value::Boolean(false),
Value::Boolean(false),
Value::BigInt(i64::try_from(table.rows().len()).unwrap_or(i64::MAX)),
Value::BigInt(count),
Value::BigInt(0),
Value::BigInt(0),
text(&create_table(table)),
]);
}
Metadata::new("duckdb_tables", &table_fields(), &rows, plan, index, columns)
}
pub(crate) fn viewnames(
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let mut rows = Vec::new();
for database in catalog.databases() {
for schema in database.schemas() {
for view in schema.views() {
let count = i64::try_from(view.columns().len()).unwrap_or(i64::MAX);
rows.push(vec![
text(database.name()),
Value::BigInt(database.oid()),
text(schema.name()),
Value::BigInt(schema.oid()),
text(&view.name().table),
Value::BigInt(view.oid()),
Value::Null,
empty(),
Value::Boolean(false),
Value::Boolean(false),
Value::BigInt(count),
text(view.statement()),
Value::Boolean(true),
]);
}
}
}
Metadata::new("duckdb_views", &view_fields(), &rows, plan, index, columns)
}
pub(crate) fn columnnames(
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let mut rows = Vec::new();
for database in catalog.databases() {
for schema in database.schemas() {
for table in schema.tables() {
for (at, column) in table.columns().iter().enumerate() {
rows.push(column_row(
database,
schema,
&table.name().table,
table.oid(),
at,
&column.name,
&column.ty,
!column.not_null,
));
}
}
for view in schema.views() {
for (at, field) in view.columns().iter().enumerate() {
rows.push(column_row(
database,
schema,
&view.name().table,
view.oid(),
at,
&field.name,
&field.ty,
true,
));
}
}
}
}
Metadata::new("duckdb_columns", &column_fields(), &rows, plan, index, columns)
}
#[expect(clippy::too_many_arguments, reason = "a row of a twenty one column table")]
fn column_row(
database: &Database,
schema: &Schema,
table: &str,
oid: i64,
at: usize,
name: &str,
ty: &LogicalType,
nullable: bool,
) -> Vec<Value> {
let (precision, radix, scale) = numeric_facts(ty);
vec![
text(database.name()),
Value::BigInt(database.oid()),
text(schema.name()),
Value::BigInt(schema.oid()),
text(table),
Value::BigInt(oid),
text(name),
Value::Integer(i32::try_from(at + 1).unwrap_or(i32::MAX)),
Value::Null,
Value::Boolean(false),
Value::Null,
Value::Boolean(nullable),
text(&ty.to_string()),
type_oid(&canonical(ty)).map_or(Value::Null, Value::BigInt),
Value::Null,
precision.map_or(Value::Null, Value::Integer),
radix.map_or(Value::Null, Value::Integer),
scale.map_or(Value::Null, Value::Integer),
empty(),
Value::Boolean(false),
Value::Null,
]
}
fn entries(catalog: &Catalog) -> impl Iterator<Item = (&Database, &Schema, &Table)> {
catalog.databases().iter().flat_map(|database| {
database.schemas().iter().flat_map(move |schema| {
schema.tables().iter().map(move |table| (database, schema, table))
})
})
}
fn create_table(table: &Table) -> String {
let columns: Vec<String> = table
.columns()
.iter()
.map(|column| {
let null = if column.not_null { " NOT NULL" } else { "" };
format!("{} {}{null}", quoted(&column.name), column.ty)
})
.collect();
format!("CREATE TABLE {}({});", quoted(&table.name().table), columns.join(", "))
}
fn empty() -> Value {
Value::map(LogicalType::Varchar, LogicalType::Varchar, Vec::new())
}