use std::collections::BTreeMap;
use std::rc::Rc;
use rupg_catalog::{Catalog, RelKind};
use rupg_common::Result;
use rupg_types::Value;
use crate::Body;
use crate::expr::{CastForm, Expr, ExprKind, PlaceHolder, Var};
use crate::from::{FromItem, JoinKind};
use crate::insert::WriteKind;
use crate::merge::MergeCommand;
use crate::select::Query;
struct Table {
columns: Vec<(u32, i32, bool)>,
exprs: Vec<Option<Expr>>,
}
struct Level {
tables: Vec<Option<Rc<Table>>>,
nulled: Vec<bool>,
copies: Vec<usize>,
}
struct Expand<'a> {
catalog: &'a Catalog,
tables: BTreeMap<u32, Option<Rc<Table>>>,
levels: Vec<Level>,
copies: usize,
}
pub fn expand_virtual(query: &mut Query, catalog: &Catalog) -> Result<()> {
Expand::new(catalog).query(query, None)
}
pub fn expand_virtual_body(body: &mut Body, catalog: &Catalog) -> Result<()> {
let mut expand = Expand::new(catalog);
match body {
Body::Value(expr) => expand.expr(expr),
Body::Query(query) => expand.query(query, None),
Body::Statements(_) => Ok(()),
}
}
fn mark(item: &FromItem, nullable: bool, nulled: &mut [bool]) {
match item {
FromItem::Relation(index) => {
if nullable && let Some(slot) = nulled.get_mut(*index) {
*slot = true;
}
}
FromItem::Join(join) => {
let (left, right) = match join.kind {
JoinKind::Inner | JoinKind::Semi | JoinKind::RightSemi => (false, false),
JoinKind::Left | JoinKind::Anti => (false, true),
JoinKind::Right | JoinKind::RightAnti => (true, false),
JoinKind::Full => (true, true),
};
mark(&join.left, nullable || left, nulled);
mark(&join.right, nullable || right, nulled);
}
}
}
fn join_exprs<'q>(item: &'q mut FromItem, out: &mut Vec<&'q mut Expr>) {
if let FromItem::Join(join) = item {
let join = &mut **join;
join_exprs(&mut join.left, out);
join_exprs(&mut join.right, out);
out.extend(join.on.as_mut());
out.extend(join.merged.iter_mut());
out.extend(join.columns.iter_mut().map(|(_, e)| e));
}
}
fn rebase(mut expr: Expr, relation: usize, levels_up: usize, shift: usize) -> Expr {
fn walk(expr: &mut Expr, relation: usize, levels_up: usize, shift: i16) {
if let ExprKind::Var(var) = &mut expr.kind
&& var.levels_up == 0
&& var.relation == 0
{
var.relation = relation;
var.levels_up = levels_up;
if var.attnum > 0 {
var.attnum = var.attnum.saturating_add(shift);
}
}
for child in expr.children_mut() {
walk(child, relation, levels_up, shift);
}
}
walk(&mut expr, relation, levels_up, i16::try_from(shift).unwrap_or(i16::MAX));
expr
}
impl<'a> Expand<'a> {
fn new(catalog: &'a Catalog) -> Self {
Expand { catalog, tables: BTreeMap::new(), levels: Vec::new(), copies: 1 }
}
fn table(&mut self, oid: u32) -> Result<Option<Rc<Table>>> {
if let Some(table) = self.tables.get(&oid) {
return Ok(table.clone());
}
let found = match self.catalog.relation(oid) {
Some(rel)
if matches!(rel.kind, RelKind::Table | RelKind::Partitioned)
&& rel.columns.iter().any(|c| c.generated == b'v' && !c.dropped) =>
{
let mut exprs = Vec::with_capacity(rel.columns.len());
for (index, column) in rel.columns.iter().enumerate() {
let attnum = i16::try_from(index + 1).unwrap_or(i16::MAX);
let expr = match self.catalog.default_of(oid, attnum) {
Some(default) if column.generated == b'v' && !column.dropped => {
Some(crate::node::read(&default.expr)?)
}
_ => None,
};
exprs.push(expr);
}
let columns = rel.columns.iter().map(|c| (c.ty, c.typmod, c.dropped)).collect();
Some(Rc::new(Table { columns, exprs }))
}
_ => None,
};
self.tables.insert(oid, found.clone());
Ok(found)
}
fn query(&mut self, query: &mut Query, table: Option<u32>) -> Result<()> {
let mut tables = Vec::with_capacity(query.relations.len());
for (index, relation) in query.relations.iter().enumerate() {
let oid = match table {
Some(table) if index == 0 => table,
_ if relation.subquery.is_some()
|| relation.function.is_some()
|| relation.values.is_some()
|| relation.cte.is_some() =>
{
0
}
_ => relation.oid,
};
tables.push(if oid == 0 { None } else { self.table(oid)? });
}
let mut nulled = vec![false; query.relations.len()];
for item in &query.from {
mark(item, false, &mut nulled);
}
let mut copies = vec![1; query.relations.len()];
if let Some(first) = copies.first_mut() {
*first = std::mem::replace(&mut self.copies, 1);
}
let kept = self.kept(query);
self.levels.push(Level { tables, nulled, copies });
let result = self.query_exprs(query, &kept);
self.levels.pop();
result?;
let Some(write) = query.write.as_mut() else { return Ok(()) };
let levels = std::mem::take(&mut self.levels);
let result = self.write(write);
self.levels = levels;
result
}
fn kept(&mut self, query: &Query) -> Vec<bool> {
let Some(write) = &query.write else { return Vec::new() };
if write.kind == WriteKind::Delete {
return Vec::new();
}
let Ok(Some(table)) = self.table(write.table) else { return Vec::new() };
let width = write.width.min(table.exprs.len());
table.exprs[..width].iter().map(Option::is_some).collect()
}
fn query_exprs(&mut self, query: &mut Query, kept: &[bool]) -> Result<()> {
let mut exprs = Vec::new();
for (index, target) in query.targets.iter_mut().enumerate() {
if kept.get(index).copied().unwrap_or(false) {
let mut null = Expr::new(ExprKind::Const(Value::Null), target.expr.ty);
null.typmod = target.expr.typmod;
target.expr = null;
} else {
exprs.push(&mut target.expr);
}
}
for item in &mut query.from {
join_exprs(item, &mut exprs);
}
exprs.extend(
query
.filter
.iter_mut()
.chain(&mut query.having)
.chain(&mut query.offset)
.chain(&mut query.limit),
);
for window in &mut query.windows {
exprs.extend(window.frame.start.iter_mut().chain(&mut window.frame.end));
}
for relation in &mut query.relations {
if let Some(function) = &mut relation.function {
exprs.extend(function.calls.iter_mut().map(|(call, _)| call));
}
exprs.extend(relation.values.iter_mut().flatten().flatten());
if let Some(sample) = &mut relation.tablesample {
exprs.extend(sample.args.iter_mut().chain(&mut sample.repeatable));
}
}
for expr in exprs {
self.expr(expr)?;
}
for relation in &mut query.relations {
if let Some(sub) = &mut relation.subquery {
self.query(sub, None)?;
}
}
for cte in &mut query.ctes {
self.query(&mut cte.query, None)?;
}
Ok(())
}
fn write(&mut self, write: &mut crate::Write) -> Result<()> {
if let Some(returning) = &mut write.returning {
self.returning(returning, write.kind)?;
}
if let Some(check) = &mut write.view_check {
self.query(&mut check.query, Some(write.table))?;
}
if let Some(recheck) = &mut write.recheck {
self.query(recheck, None)?;
}
let kept = match self.table(write.table)? {
Some(table) => table.exprs.iter().map(Option::is_some).collect(),
None => Vec::new(),
};
if let Some(update) = write.on_conflict.as_mut().and_then(|c| c.update.as_mut()) {
self.new_row(update, &kept)?;
}
for clause in write.merge.iter_mut().flat_map(|m| &mut m.clauses) {
if matches!(clause.command, MergeCommand::Insert | MergeCommand::Update) {
self.new_row(&mut clause.query, &kept)?;
} else {
self.query(&mut clause.query, None)?;
}
}
Ok(())
}
fn new_row(&mut self, query: &mut Query, kept: &[bool]) -> Result<()> {
for (target, kept) in query.targets.iter_mut().zip(kept) {
if *kept {
let mut null = Expr::new(ExprKind::Const(Value::Null), target.expr.ty);
null.typmod = target.expr.typmod;
target.expr = null;
}
}
self.query(query, None)
}
fn returning(&mut self, query: &mut Query, kind: WriteKind) -> Result<()> {
self.copies = if kind == WriteKind::Merge { 3 } else { 1 };
self.query(query, None)
}
fn expr(&mut self, expr: &mut Expr) -> Result<()> {
if let ExprKind::Var(var) = expr.kind {
if let Some(new) = self.var(expr, var) {
*expr = new;
}
return Ok(());
}
for child in expr.children_mut() {
self.expr(child)?;
}
if let ExprKind::SubLink(sub) = &mut expr.kind {
self.query(&mut sub.query, None)?;
}
Ok(())
}
fn var(&self, expr: &Expr, var: Var) -> Option<Expr> {
let level = self.levels.len().checked_sub(var.levels_up + 1)?;
let level = self.levels.get(level)?;
let table = level.tables.get(var.relation)?.as_ref()?;
let new = if var.attnum == 0 {
let fields = table
.columns
.iter()
.zip(&table.exprs)
.enumerate()
.filter(|(_, ((_, _, dropped), _))| !dropped)
.map(|(i, ((ty, typmod, _), gen_expr))| match gen_expr {
Some(e) => rebase(e.clone(), var.relation, var.levels_up, 0),
None => {
let attnum = i16::try_from(i + 1).unwrap_or(i16::MAX);
let column =
Var { relation: var.relation, attnum, levels_up: var.levels_up };
let mut field = Expr::new(ExprKind::Var(column), *ty);
field.typmod = *typmod;
field
}
})
.collect();
Expr { kind: ExprKind::Row(fields, CastForm::Implicit), ..expr.clone() }
} else {
let width = table.exprs.len();
let at = usize::try_from(var.attnum).ok()?.checked_sub(1)?;
if at / width >= level.copies.get(var.relation).copied().unwrap_or(1) {
return None;
}
let shift = at / width * width;
let gen_expr = table.exprs.get(at % width)?.as_ref()?;
let mut new = rebase(gen_expr.clone(), var.relation, var.levels_up, shift);
new.location = expr.location;
new
};
if !level.nulled.get(var.relation).copied().unwrap_or(false) {
return Some(new);
}
let (ty, typmod, location) = (new.ty, new.typmod, new.location);
let p = PlaceHolder { expr: new, relations: vec![var.relation], levels_up: var.levels_up };
Some(Expr { kind: ExprKind::PlaceHolder(Box::new(p)), ty, typmod, location })
}
}