use rupg_common::{Error, Result, SqlState};
use rupg_pgcatalog::builtin;
use rupg_sql::nodes::{List, Node, WindowDef};
use rupg_types::oid;
use crate::Analyzer;
use crate::coerce::AtOpt;
use crate::expr::{Aggref, Expr, ExprKind, GroupingFunc, NullTreatment, Var};
use crate::select::Target;
use crate::sort::{GroupingSet, SortGroup, expand_grouping_sets};
use crate::typename::place;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum Kind {
#[default]
Other,
Select,
Where,
JoinOn,
Having,
Filter,
GroupBy,
OrderBy,
DistinctOn,
Limit,
Offset,
WindowPartition,
WindowOrder,
WindowRange,
WindowRows,
WindowGroups,
ColumnDefault,
FunctionDefault,
Check,
IndexExpression,
StatsExpression,
IndexPredicate,
FromFunction,
Values,
ValuesSingle,
UpdateSource,
MergeWhen,
ExecuteParameter,
Returning,
Transform,
TriggerWhen,
CallArgument,
PartitionBound,
PartitionExpression,
Generated,
}
impl Kind {
pub(crate) fn name(self) -> &'static str {
match self {
Kind::Other => "???",
Kind::Select => "SELECT",
Kind::Where => "WHERE",
Kind::JoinOn => "JOIN/ON",
Kind::Having => "HAVING",
Kind::Filter => "FILTER",
Kind::GroupBy => "GROUP BY",
Kind::OrderBy => "ORDER BY",
Kind::DistinctOn => "DISTINCT ON",
Kind::Limit => "LIMIT",
Kind::Offset => "OFFSET",
Kind::WindowPartition => "window PARTITION BY",
Kind::WindowOrder => "window ORDER BY",
Kind::WindowRange => "window RANGE",
Kind::WindowRows => "window ROWS",
Kind::WindowGroups => "window GROUPS",
Kind::ColumnDefault | Kind::FunctionDefault => "DEFAULT expressions",
Kind::Check => "check constraints",
Kind::IndexExpression => "index expressions",
Kind::StatsExpression => "statistics expressions",
Kind::IndexPredicate => "index predicates",
Kind::FromFunction => "functions in FROM",
Kind::Values | Kind::ValuesSingle => "VALUES",
Kind::UpdateSource => "UPDATE",
Kind::MergeWhen => "MERGE WHEN conditions",
Kind::ExecuteParameter => "EXECUTE parameters",
Kind::Returning => "RETURNING",
Kind::Transform => "transform expressions",
Kind::TriggerWhen => "trigger WHEN conditions",
Kind::CallArgument => "CALL arguments",
Kind::PartitionBound => "partition bound",
Kind::PartitionExpression => "partition key expressions",
Kind::Generated => "column generation expressions",
}
}
pub(crate) fn is_standalone(self) -> bool {
matches!(
self,
Kind::ColumnDefault
| Kind::FunctionDefault
| Kind::Check
| Kind::IndexExpression
| Kind::StatsExpression
| Kind::IndexPredicate
| Kind::ExecuteParameter
| Kind::Transform
| Kind::TriggerWhen
| Kind::CallArgument
| Kind::PartitionBound
| Kind::PartitionExpression
| Kind::Generated
)
}
pub(crate) fn subquery_name(self) -> &'static str {
match self {
Kind::ColumnDefault | Kind::FunctionDefault => "DEFAULT expression",
Kind::Check => "check constraint",
Kind::IndexExpression => "index expression",
Kind::StatsExpression => "statistics expression",
Kind::ExecuteParameter => "EXECUTE parameter",
Kind::Transform => "transform expression",
Kind::TriggerWhen => "trigger WHEN condition",
Kind::CallArgument => "CALL argument",
Kind::PartitionBound => "partition bound",
Kind::PartitionExpression => "partition key expression",
Kind::Generated => "column generation expression",
_ => "index predicate",
}
}
}
pub(crate) struct Parts<'a> {
pub(crate) star: bool,
pub(crate) distinct: bool,
pub(crate) within_group: bool,
pub(crate) order: &'a List,
pub(crate) filter: Option<Expr>,
pub(crate) over: Option<&'a WindowDef>,
pub(crate) nulls: NullTreatment,
pub(crate) srfs: usize,
}
pub(crate) const SRF_HINT: &str =
"You might be able to move the set-returning function into a LATERAL FROM item.";
fn nested_set(expr: &Expr) -> Option<&Expr> {
match &expr.kind {
ExprKind::Window(_) => Some(expr),
ExprKind::Func(f) if f.retset => Some(expr),
_ => expr.children().into_iter().find_map(nested_set),
}
}
fn nested_agg(expr: &Expr) -> Option<&Expr> {
match &expr.kind {
ExprKind::Agg(_) | ExprKind::Grouping(_) => Some(expr),
_ => expr.children().into_iter().find_map(nested_agg),
}
}
fn agg_level<'a>(args: impl Iterator<Item = &'a Expr>) -> usize {
args.filter_map(|e| {
let mut min: Option<usize> = None;
e.find(0, &mut |e, depth| {
if let ExprKind::Var(var) = e.kind
&& var.levels_up >= depth
{
let up = var.levels_up - depth;
min = Some(min.map_or(up, |m| m.min(up)));
}
None::<()>
});
min
})
.min()
.unwrap_or(0)
}
pub(crate) fn grouping_error(message: String, at: Option<usize>) -> Error {
Error::new(SqlState::GROUPING_ERROR, message).at_opt(at)
}
impl Analyzer<'_> {
pub(crate) fn with_kind<T>(
&mut self,
kind: Kind,
f: impl FnOnce(&mut Self) -> Result<T>,
) -> Result<T> {
let saved = std::mem::replace(&mut self.kind, kind);
let result = f(self);
self.kind = saved;
result
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn aggregate_call(
&mut self,
oid: u32,
kind: u8,
mut args: Vec<Expr>,
parts: Parts<'_>,
variadic: bool,
ty: u32,
at: Option<usize>,
) -> Result<Expr> {
let ordered_set = kind != b'n';
let direct = if ordered_set {
args.drain(..args.len() - parts.order.len()).collect()
} else {
Vec::new()
};
let nargs = args.len();
let mut targets: Vec<Target> = args
.into_iter()
.map(|expr| Target { name: String::new(), expr, origin: None, junk: false })
.collect();
let order = if ordered_set {
let mut order = Vec::with_capacity(nargs);
for (target, item) in parts.order.iter().enumerate() {
let Some(Node::SortBy(by)) = item else {
return Err(Error::internal("an item of WITHIN GROUP that is not SortBy"));
};
self.add_sort(by, target, &mut targets, &mut order)?;
}
order
} else {
self.with_kind(Kind::OrderBy, |a| a.sort_clause_sql99(parts.order, &mut targets))?
};
let distinct = if parts.distinct {
let list = self.distinct_clause(&mut targets, &order, true)?;
for item in &list {
if item.operator == 0 {
let expr = &targets[item.target].expr;
return Err(Error::new(
SqlState::UNDEFINED_FUNCTION,
format!(
"could not identify an ordering operator for type {}",
crate::types::name(expr.ty)
),
)
.with_detail("Aggregates with DISTINCT must be able to sort their inputs.")
.at_opt(expr.place()));
}
}
list
} else {
Vec::new()
};
let args: Vec<Expr> = targets.into_iter().map(|t| t.expr).collect();
let filter = parts.filter.map(Box::new);
let inner = args
.iter()
.chain(filter.as_deref())
.find_map(nested_set)
.or_else(|| args.iter().chain(filter.as_deref()).find_map(nested_agg))
.or_else(|| direct.iter().find_map(nested_set))
.or_else(|| direct.iter().find_map(nested_agg));
if let Some(inner) = inner {
match inner.kind {
ExprKind::Agg(_) | ExprKind::Grouping(_) => {
return Err(grouping_error(
"aggregate function calls cannot be nested".into(),
inner.location,
));
}
ExprKind::Window(_) => {
return Err(grouping_error(
"aggregate function calls cannot contain window function calls".into(),
inner.location,
));
}
_ => {}
}
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
"aggregate function calls cannot contain set-returning function calls",
)
.with_hint(SRF_HINT)
.at_opt(inner.location));
}
let level = agg_level(args.iter().chain(filter.as_deref()));
self.check_agg_place(level, "aggregate functions", at)?;
let agg = Aggref {
oid,
kind,
direct,
args,
nargs,
order,
distinct,
star: parts.star,
variadic,
filter,
levels_up: level,
};
Ok(Expr { kind: ExprKind::Agg(Box::new(agg)), ty, typmod: -1, location: at })
}
pub(crate) fn grouping_func(&mut self, g: &rupg_sql::nodes::GroupingFunc) -> Result<Expr> {
let at = place(g.location);
if g.args.len() > 31 {
return Err(Error::new(
SqlState::TOO_MANY_ARGUMENTS,
"GROUPING must have fewer than 32 arguments",
)
.at_opt(at));
}
let args = self.transform_expression_list(&g.args)?;
let inner = args.iter().find_map(nested_set).or_else(|| args.iter().find_map(nested_agg));
if let Some(inner) = inner {
let message = match inner.kind {
ExprKind::Agg(_) | ExprKind::Grouping(_) => {
"aggregate function calls cannot be nested"
}
ExprKind::Window(_) => {
"aggregate function calls cannot contain window function calls"
}
_ => {
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
"aggregate function calls cannot contain set-returning function calls",
)
.with_hint(SRF_HINT)
.at_opt(inner.location));
}
};
return Err(grouping_error(message.into(), inner.location));
}
let levels_up = agg_level(args.iter());
self.check_agg_place(levels_up, "grouping operations", at)?;
let grouping = GroupingFunc { args, refs: Vec::new(), levels_up };
Ok(Expr::new(ExprKind::Grouping(Box::new(grouping)), oid::INT4).at(at))
}
fn check_agg_place(&mut self, level: usize, what: &str, at: Option<usize>) -> Result<()> {
let outer = self.outer_kinds.len().checked_sub(level).filter(|_| level > 0);
let place = match outer {
Some(i) => self.outer_kinds.get(i).and_then(|(kind, _)| *kind),
None => Some(self.kind),
};
let Some(place) = place else {
return Err(grouping_error(
format!("{what} are not allowed in FROM clause of their own query level"),
at,
));
};
match place {
Kind::JoinOn => {
return Err(grouping_error(
format!("{what} are not allowed in JOIN conditions"),
at,
));
}
Kind::Where
| Kind::Filter
| Kind::GroupBy
| Kind::Limit
| Kind::Offset
| Kind::WindowRange
| Kind::WindowRows
| Kind::WindowGroups
| Kind::ColumnDefault
| Kind::FunctionDefault
| Kind::Check
| Kind::IndexExpression
| Kind::StatsExpression
| Kind::IndexPredicate
| Kind::FromFunction
| Kind::Values
| Kind::ValuesSingle
| Kind::UpdateSource
| Kind::MergeWhen
| Kind::ExecuteParameter
| Kind::Returning
| Kind::Transform
| Kind::TriggerWhen
| Kind::CallArgument
| Kind::PartitionBound
| Kind::PartitionExpression
| Kind::Generated => {
return Err(grouping_error(
format!("{what} are not allowed in {}", place.name()),
at,
));
}
_ => {}
}
match outer.and_then(|i| self.outer_kinds.get_mut(i)) {
Some((_, has_aggs)) => *has_aggs = true,
None => self.has_aggs = true,
}
Ok(())
}
pub(crate) fn check_grouping(
&self,
targets: &mut [Target],
mut having: Option<&mut Expr>,
group: &[SortGroup],
sets: &mut Vec<GroupingSet>,
distinct: bool,
) -> Result<Vec<u32>> {
let mut common = None;
if !sets.is_empty() {
let Some(expanded) = expand_grouping_sets(sets, distinct, 4096) else {
return Err(Error::new(
SqlState::STATEMENT_TOO_COMPLEX,
"too many grouping sets present (maximum 4096)",
));
};
let mut items = expanded.first().cloned().unwrap_or_default();
for set in expanded.iter().skip(1) {
items.retain(|item| set.contains(item));
}
if expanded.len() == 1 && !group.is_empty() {
sets.clear();
} else {
common = Some(items);
}
}
let grouped: Vec<(usize, Expr)> =
group.iter().map(|g| (g.target, targets[g.target].expr.clone())).collect();
let non_var = grouped.iter().any(|(_, e)| !matches!(e.kind, ExprKind::Var(_)));
let level_vars = |all: bool| -> Vec<Var> {
grouped
.iter()
.filter(|(t, _)| all || common.as_ref().is_none_or(|c| c.contains(t)))
.filter_map(|(_, e)| match e.kind {
ExprKind::Var(var) if var.levels_up == 0 => Some(var),
_ => None,
})
.collect()
};
let vars = level_vars(true);
let common_vars = level_vars(false);
let exprs: Vec<&Expr> = grouped.iter().map(|(_, e)| e).collect();
let mut check = Check {
analyzer: self,
grouped: &exprs,
raised: Vec::new(),
non_var,
vars: &vars,
common: &common_vars,
proven: Vec::new(),
deps: Vec::new(),
};
for target in targets.iter_mut() {
finalize_grouping(&mut target.expr, 0, &grouped, non_var)?;
}
for target in targets.iter() {
check.expr(&target.expr, 0)?;
}
if let Some(having) = having.as_deref_mut() {
finalize_grouping(having, 0, &grouped, non_var)?;
check.expr(having, 0)?;
}
let deps = check.deps;
self.check_recursive_aggs(targets.iter().map(|t| &t.expr).chain(having.as_deref()))?;
Ok(deps)
}
}
fn finalize_grouping(
expr: &mut Expr,
depth: usize,
grouped: &[(usize, Expr)],
non_var: bool,
) -> Result<()> {
match &mut expr.kind {
ExprKind::Grouping(g) if g.levels_up == depth => {
let mut refs = Vec::with_capacity(g.args.len());
for arg in &g.args {
let found = match arg.kind {
ExprKind::Var(var) if var.levels_up == depth => {
let var = Var { levels_up: 0, ..var };
grouped.iter().find(|(_, e)| matches!(e.kind, ExprKind::Var(v) if v == var))
}
ExprKind::Var(_) => None,
_ if non_var && depth == 0 => grouped.iter().find(|(_, e)| e.same(arg)),
_ => None,
};
let Some(&(target, _)) = found else {
return Err(grouping_error(
"arguments to GROUPING must be grouping expressions of the associated query level".into(),
arg.place(),
));
};
refs.push(target);
}
g.refs = refs;
return Ok(());
}
ExprKind::Grouping(_) => return Ok(()),
ExprKind::Agg(agg) if agg.levels_up >= depth => return Ok(()),
_ => {}
}
for child in expr.children_mut() {
finalize_grouping(child, depth, grouped, non_var)?;
}
if let ExprKind::SubLink(sub) = &mut expr.kind {
let mut result = Ok(());
sub.query.each_expr_mut(depth + 1, &mut |e, depth| {
if result.is_ok() {
result = finalize_grouping(e, depth, grouped, non_var);
}
});
result?;
}
Ok(())
}
struct Check<'a, 'b> {
analyzer: &'a Analyzer<'b>,
grouped: &'a [&'a Expr],
raised: Vec<Vec<Expr>>,
non_var: bool,
vars: &'a [Var],
common: &'a [Var],
proven: Vec<usize>,
deps: Vec<u32>,
}
impl Check<'_, '_> {
fn expr(&mut self, expr: &Expr, depth: usize) -> Result<()> {
if let ExprKind::Agg(agg) = &expr.kind
&& agg.levels_up == depth
{
return agg.direct.iter().try_for_each(|e| self.expr(e, depth));
}
if let ExprKind::Grouping(g) = &expr.kind
&& g.levels_up >= depth
{
return Ok(());
}
if self.non_var && self.is_grouped(expr, depth) {
return Ok(());
}
match &expr.kind {
ExprKind::Const(_) | ExprKind::Param(_) => Ok(()),
ExprKind::Var(var) if var.levels_up == depth => {
self.var(Var { levels_up: 0, ..*var }, depth, expr.location)
}
ExprKind::Var(_) => Ok(()),
_ => {
for child in expr.children() {
self.expr(child, depth)?;
}
if let ExprKind::SubLink(sub) = &expr.kind {
for (e, depth) in sub.query.all_exprs(depth + 1) {
self.expr(e, depth)?;
}
}
Ok(())
}
}
}
fn is_grouped(&mut self, expr: &Expr, depth: usize) -> bool {
if depth == 0 {
return self.grouped.iter().any(|g| g.same(expr));
}
while self.raised.len() < depth {
let levels = self.raised.len() + 1;
let raised = self
.grouped
.iter()
.map(|g| {
let mut g = (*g).clone();
g.raise(levels);
g
})
.collect();
self.raised.push(raised);
}
self.raised[depth - 1].iter().any(|g| g.same(expr))
}
fn var(&mut self, var: Var, depth: usize, at: Option<usize>) -> Result<()> {
if !self.non_var && self.vars.contains(&var) {
return Ok(());
}
if self.proven.contains(&var.relation) {
return Ok(());
}
let relid = self.analyzer.scope.relations[var.relation].oid;
let key = match builtin::primary_key(relid) {
Some(key) => Some((0, key.to_vec())),
None => primary_key(self.analyzer, relid),
};
if let Some((constraint, key)) = key
&& key.iter().all(|&attnum| self.common.contains(&Var { attnum, ..var }))
{
self.proven.push(var.relation);
if constraint != 0 {
self.deps.push(constraint);
}
return Ok(());
}
let (table, column) = self.analyzer.column_names(var);
let message = if depth == 0 {
format!(
"column \"{table}.{column}\" must appear in the GROUP BY clause or be used in an aggregate function"
)
} else {
format!("subquery uses ungrouped column \"{table}.{column}\" from outer query")
};
Err(grouping_error(message, at))
}
}
fn primary_key(analyzer: &Analyzer<'_>, relid: u32) -> Option<(u32, Vec<i16>)> {
let catalog = analyzer.env.catalog()?;
let key = catalog.constraints_of(relid).find(|c| c.kind == rupg_catalog::ConKind::Primary)?;
(!key.deferrable).then(|| (key.oid, key.keys.clone()))
}