use inillucent_value::Collation;
use super::{Binder, BoundExpr, SubqueryKind};
use crate::ast::ExprId;
use crate::diagnostic::ParseError;
use crate::function;
impl Binder<'_> {
pub(super) fn bind_subtype(&mut self, argument: ExprId) -> Result<BoundExpr, ParseError> {
let bound = self.bind_expr(argument)?;
if let BoundExpr::Aggregate { slot, .. } = &bound {
let carries = matches!(
self.aggregates.get(*slot).map(|held| held.func),
Some(
function::AggregateFunc::JsonGroupArray
| function::AggregateFunc::JsonGroupObject
)
);
return Ok(BoundExpr::Integer(if carries { 74 } else { 0 }));
}
Ok(match json_subtype(&bound) {
Subtyped::Always => BoundExpr::Integer(74),
Subtyped::Never => BoundExpr::Integer(0),
Subtyped::WhenShaped => BoundExpr::Function {
func: function::ScalarFunc::Subtype,
arguments: vec![bound],
collation: Collation::Binary,
},
})
}
pub(super) fn marked_as_json(&self, argument: BoundExpr) -> BoundExpr {
let wrapper = match &argument {
BoundExpr::Aggregate { slot, .. } => {
json_aggregate_wrapper(self.aggregates.get(*slot).map(|held| held.func))
}
BoundExpr::Subquery {
kind: SubqueryKind::Scalar,
block,
..
} => match block.columns.as_slice() {
[only] => match &only.expr {
BoundExpr::Aggregate { slot, .. } => {
json_aggregate_wrapper(block.aggregates.get(*slot).map(|held| held.func))
}
other if json_subtype(other) == Subtyped::Always => {
Some(function::JsonFunc::Json)
}
_ => None,
},
_ => None,
},
_ => None,
};
match wrapper {
Some(func) => BoundExpr::Json {
func,
arguments: vec![argument],
},
None => argument,
}
}
}
fn json_aggregate_wrapper(func: Option<function::AggregateFunc>) -> Option<function::JsonFunc> {
match func? {
function::AggregateFunc::JsonGroupArray | function::AggregateFunc::JsonGroupObject => {
Some(function::JsonFunc::Json)
}
function::AggregateFunc::JsonbGroupArray | function::AggregateFunc::JsonbGroupObject => {
Some(function::JsonFunc::Jsonb)
}
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Subtyped {
Always,
Never,
WhenShaped,
}
fn json_subtype(bound: &BoundExpr) -> Subtyped {
let BoundExpr::Json { func, .. } = bound else {
return Subtyped::Never;
};
use function::JsonFunc;
match func {
JsonFunc::Extract | JsonFunc::Arrow => Subtyped::WhenShaped,
JsonFunc::Jsonb
| JsonFunc::ArrayB
| JsonFunc::ExtractB
| JsonFunc::InsertB
| JsonFunc::ObjectB
| JsonFunc::PatchB
| JsonFunc::RemoveB
| JsonFunc::ReplaceB
| JsonFunc::SetB
| JsonFunc::ArrayInsertB
| JsonFunc::ArrowShift
| JsonFunc::ArrayLength
| JsonFunc::ErrorPosition
| JsonFunc::Type
| JsonFunc::Valid
| JsonFunc::Pretty => Subtyped::Never,
_ => Subtyped::Always,
}
}