use rupg_common::{Error, Result, SqlState};
use rupg_sql::nodes::{
A_ArrayExpr, A_Const, A_Expr, A_Expr_Kind, BoolExpr, BoolExprType, BoolTestType, CaseExpr,
CoercionForm, FuncCall, MinMaxOp, Node, NullTestType, SQLValueFunction, SQLValueFunctionOp,
SubLink as RawSubLink, SubLinkType, TypeCast,
};
use rupg_types::{Value, oid};
use crate::Analyzer;
use crate::agg::{Kind, Parts};
use crate::coerce::{AtOpt, Context, can_coerce, common_of};
use crate::expr::{
BoolOp, BoolTest, Case, CastForm, Expr, ExprKind, Func, FuncForm, NamedArg, NullTreatment,
RowCompare, RowCompareKind, SqlValue,
};
use crate::typename::{names, place};
use crate::types;
const C_COLLATIONS: [&str; 5] = ["default", "C", "POSIX", "ucs_basic", "pg_c_utf8"];
const OTHER_COLLATIONS: [&str; 2] = ["pg_unicode_fast", "unicode"];
fn not_yet(what: &str, at: Option<usize>) -> Error {
Error::new(SqlState::FEATURE_NOT_SUPPORTED, format!("{what} is not supported yet")).at_opt(at)
}
const COMPARE_EQ: u32 = 3;
const COMPARE_NE: u32 = 6;
fn compare_kinds(operator: u32) -> u8 {
let btree = |operator: u32| {
rupg_pgcatalog::builtin::amops_of_operator(operator)
.filter(|a| a.method == crate::typcache::BTREE && (1..=5).contains(&a.strategy))
.map(|a| a.strategy)
};
let kinds = btree(operator).fold(0u8, |bits, strategy| bits | 1 << strategy);
if kinds != 0 {
return kinds;
}
let negator = rupg_pgcatalog::builtin::operator_by_oid(operator).map_or(0, |o| o.negator);
if negator != 0 && btree(negator).any(|s| s == crate::typcache::EQUAL) {
return 1 << COMPARE_NE;
}
0
}
fn compare_proc(operator: u32, strategy: u32) -> Option<u32> {
let amop = rupg_pgcatalog::builtin::amops_of_operator(operator).find(|a| {
a.method == crate::typcache::BTREE && u32::try_from(a.strategy).ok() == Some(strategy)
})?;
rupg_pgcatalog::builtin::amprocs(amop.family, amop.left)
.iter()
.find(|p| p.right == amop.right && p.number == 1)
.map(|p| p.proc)
}
fn simple_op(name: &str, left: Option<Node>, right: Option<Node>, location: i32) -> Node {
Node::A_Expr(Box::new(A_Expr {
kind: A_Expr_Kind::AEXPR_OP,
name: vec![Some(Node::String(name.into()))],
lexpr: left,
rexpr: right,
location,
..A_Expr::default()
}))
}
fn bool_node(op: BoolExprType, args: Vec<Node>, location: i32) -> Node {
Node::BoolExpr(Box::new(BoolExpr {
boolop: op,
args: args.into_iter().map(Some).collect(),
location,
}))
}
impl Analyzer<'_> {
pub(crate) fn transform(&mut self, node: Option<&Node>) -> Result<Expr> {
let Some(node) = node else {
return Ok(Expr::constant(Value::Null, oid::UNKNOWN, -1, None));
};
self.depth += 1;
if self.depth > MAX_DEPTH {
self.depth -= 1;
return Err(Error::new(SqlState::STATEMENT_TOO_COMPLEX, "stack depth limit exceeded")
.with_hint("Increase the configuration parameter \"max_stack_depth\" (currently 2048kB), after ensuring the platform's stack depth limit is adequate."));
}
let result = self.transform_node(node);
self.depth -= 1;
result
}
fn transform_node(&mut self, node: &Node) -> Result<Expr> {
match node {
Node::A_Const(c) => self.transform_const(c),
Node::SetToDefault(d) => {
Err(Error::new(SqlState::SYNTAX_ERROR, "DEFAULT is not allowed in this context")
.at_opt(place(d.location)))
}
Node::ColumnRef(c) => match self.variables {
Some(variables) => self.column_or_variable(c, variables),
None => self.column_ref(c),
},
Node::ParamRef(p) => self.transform_param(p.number, place(p.location)),
Node::A_Expr(a) => self.transform_a_expr(a),
Node::BoolExpr(b) => self.transform_bool(b),
Node::FuncCall(f) => self.transform_func(f),
Node::NamedArgExpr(na) => {
let arg = self.transform(na.arg.as_ref())?;
let (ty, typmod) = (arg.ty, arg.typmod);
let name = na.name.as_deref().unwrap_or_default().to_owned();
let kind = ExprKind::NamedArg(Box::new(NamedArg { arg, name, number: 0 }));
Ok(Expr { kind, ty, typmod, location: place(na.location) })
}
Node::TypeCast(t) => self.transform_cast(t),
Node::CaseExpr(c) => {
let before = self.srfs.0;
let expr = self.transform_case(c)?;
self.no_srf_since(before, "CASE")?;
Ok(expr)
}
Node::CoalesceExpr(c) => {
let before = self.srfs.0;
let args = self.transform_list(&c.args)?;
let ty = self.common_type(&args, "COALESCE")?;
let args = args
.into_iter()
.map(|a| self.coerce_to_common(a, ty, "COALESCE"))
.collect::<Result<Vec<_>>>()?;
self.no_srf_since(before, "COALESCE")?;
Ok(Expr {
kind: ExprKind::Coalesce(args),
ty,
typmod: -1,
location: place(c.location),
})
}
Node::MinMaxExpr(m) => {
let construct = if m.op == MinMaxOp::IS_GREATEST { "GREATEST" } else { "LEAST" };
let args = self.transform_list(&m.args)?;
let ty = self.common_type(&args, construct)?;
let args = args
.into_iter()
.map(|a| self.coerce_to_common(a, ty, construct))
.collect::<Result<Vec<_>>>()?;
let at = place(m.location);
let less = self.comparison(ty, at)?;
Ok(Expr {
kind: ExprKind::MinMax { greatest: m.op == MinMaxOp::IS_GREATEST, less, args },
ty,
typmod: -1,
location: at,
})
}
Node::NullTest(n) => {
let arg = self.transform(n.arg.as_ref())?;
let is_null = n.nulltesttype == NullTestType::IS_NULL;
Ok(Expr::new(ExprKind::NullTest(Box::new(arg), is_null), oid::BOOL)
.at(place(n.location)))
}
Node::BooleanTest(b) => {
let (test, construct) = match b.booltesttype {
BoolTestType::IS_TRUE => (BoolTest::IsTrue, "IS TRUE"),
BoolTestType::IS_NOT_TRUE => (BoolTest::IsNotTrue, "IS NOT TRUE"),
BoolTestType::IS_FALSE => (BoolTest::IsFalse, "IS FALSE"),
BoolTestType::IS_NOT_FALSE => (BoolTest::IsNotFalse, "IS NOT FALSE"),
BoolTestType::IS_UNKNOWN => (BoolTest::IsUnknown, "IS UNKNOWN"),
_ => (BoolTest::IsNotUnknown, "IS NOT UNKNOWN"),
};
let arg = self.transform(b.arg.as_ref())?;
let arg = self.coerce_to_boolean(arg, construct)?;
Ok(Expr::new(ExprKind::BooleanTest(Box::new(arg), test), oid::BOOL)
.at(place(b.location)))
}
Node::SQLValueFunction(f) => self.transform_sql_value(f),
Node::A_ArrayExpr(a) => self.transform_array(a, 0, 0, -1),
Node::CollateClause(c) => {
let arg = self.transform(c.arg.as_ref())?;
let at = place(c.location);
if !types::collatable(arg.ty) && arg.ty != oid::UNKNOWN {
return Err(Error::new(
SqlState::DATATYPE_MISMATCH,
format!("collations are not supported by type {}", types::name(arg.ty)),
)
.at_opt(at));
}
self.collation_oid(&c.collname, at)?;
Ok(arg)
}
Node::RowExpr(r) => {
let args = self.transform_expression_list(&r.args)?;
Ok(Expr::new(ExprKind::Row(args, CastForm::Implicit), oid::RECORD)
.at(place(r.location)))
}
Node::SubLink(s) => self.transform_sublink(s),
Node::A_Indirection(i) => self.transform_indirection(i),
Node::GroupingFunc(g) => self.grouping_func(g),
Node::MergeSupportFunc(f) => self.merge_support_func(f),
Node::JsonObjectConstructor(c) => self.json_object(c),
Node::JsonArrayConstructor(c) => self.json_array(c),
Node::JsonArrayQueryConstructor(c) => self.json_array_query(c),
Node::JsonObjectAgg(a) => self.json_object_agg(a),
Node::JsonArrayAgg(a) => self.json_array_agg(a),
Node::JsonIsPredicate(p) => self.is_json(p),
Node::JsonParseExpr(e) => self.json_parse(e),
Node::JsonScalarExpr(e) => self.json_scalar(e),
Node::JsonSerializeExpr(e) => self.json_serialize(e),
Node::JsonFuncExpr(f) => self.json_func_expr(f),
Node::XmlExpr(x) => self.xml_expr(x),
Node::XmlSerialize(x) => self.xml_serialize(x),
_ => Err(not_yet("this kind of expression", None)),
}
}
pub(crate) fn collation_oid(&self, list: &[Option<Node>], at: Option<usize>) -> Result<u32> {
let parts = names(list);
let (schema, name) = self.split_name(&parts, at)?;
let in_catalog = schema.is_none_or(|ns| ns == crate::PG_CATALOG_NAMESPACE);
if in_catalog && C_COLLATIONS.contains(&name) {
let row = rupg_pgcatalog::builtin::named(rupg_pgcatalog::builtin::Named::Collation)
.iter()
.find(|r| r.name == name && (r.encoding == -1 || r.encoding == 6));
return row.map(|r| r.oid).ok_or_else(|| Error::internal("a built-in collation"));
}
let imported = || {
self.env.catalog().and_then(|c| c.collation_by_name(crate::PG_CATALOG_NAMESPACE, name))
};
if in_catalog && (OTHER_COLLATIONS.contains(&name) || imported().is_some()) {
return Err(not_yet(&format!("collation \"{name}\""), at));
}
Err(Error::new(
SqlState::UNDEFINED_OBJECT,
format!("collation \"{}\" for encoding \"UTF8\" does not exist", parts.join(".")),
)
.at_opt(at))
}
pub(crate) fn transform_list(&mut self, list: &[Option<Node>]) -> Result<Vec<Expr>> {
list.iter().map(|n| self.transform(n.as_ref())).collect()
}
pub(crate) fn transform_expression_list(&mut self, list: &[Option<Node>]) -> Result<Vec<Expr>> {
let mut out = Vec::with_capacity(list.len());
for node in list {
let columns = match node {
Some(Node::ColumnRef(c)) => self.expand_star(c)?,
Some(Node::A_Indirection(i)) => self.expand_indirection_star(i)?,
_ => None,
};
match columns {
Some(columns) => out.extend(columns.into_iter().map(|(_, e)| e)),
None => out.push(self.transform(node.as_ref())?),
}
}
Ok(out)
}
fn transform_const(&mut self, c: &A_Const) -> Result<Expr> {
let at = place(c.location);
if c.isnull {
return Ok(Expr::constant(Value::Null, oid::UNKNOWN, -1, at));
}
let (value, ty) = match &c.val {
Some(Node::Integer(v)) => (Value::Int4(*v), oid::INT4),
Some(Node::Float(text)) => match rupg_types::int8_in(text) {
Ok(v) => match i32::try_from(v) {
Ok(v) => (Value::Int4(v), oid::INT4),
Err(_) => (Value::Int8(v), oid::INT8),
},
Err(_) => {
let v =
rupg_types::numeric_in(text, -1).map_err(|e| Error::from(e).at_opt(at))?;
(Value::Numeric(v), oid::NUMERIC)
}
},
Some(Node::Boolean(v)) => (Value::Bool(*v), oid::BOOL),
Some(Node::String(s)) => (Value::text(&**s), oid::UNKNOWN),
Some(Node::BitString(s)) => {
let bits =
rupg_types::bit_in(s, -1, false).map_err(|e| Error::from(e).at_opt(at))?;
(Value::Text(bits), oid::BIT)
}
_ => return Err(Error::internal("unrecognized node type in A_Const")),
};
Ok(Expr::constant(value, ty, -1, at))
}
pub(crate) fn transform_param(&mut self, number: i32, at: Option<usize>) -> Result<Expr> {
let index = usize::try_from(number).ok().filter(|&n| n >= 1);
if let Some(variables) = self.variables {
return match index.and_then(|n| variables.param(n)) {
Some(variable) => Ok(self.variable_param(variable, at)),
None => Err(Error::new(
SqlState::UNDEFINED_PARAMETER,
format!("there is no parameter ${number}"),
)
.at_opt(at)),
};
}
if let Some(n) = index {
if self.variable && n > self.params.len() && n <= MAX_PARAMS {
self.params.resize(n, 0);
}
}
let Some(n) = index.filter(|&n| n <= self.params.len()) else {
return Err(Error::new(
SqlState::UNDEFINED_PARAMETER,
format!("there is no parameter ${number}"),
)
.at_opt(at));
};
let ty = match self.params[n - 1] {
0 => oid::UNKNOWN,
ty => ty,
};
Ok(Expr { kind: ExprKind::Param(n), ty, typmod: -1, location: at })
}
pub(crate) fn row_comparison(
&mut self,
op_names: &[&str],
left: Vec<Expr>,
right: Vec<Expr>,
at: Option<usize>,
) -> Result<Expr> {
if left.len() != right.len() {
return Err(Error::new(
SqlState::SYNTAX_ERROR,
"unequal number of entries in row expressions",
)
.at_opt(at));
}
if left.is_empty() {
return Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
"cannot compare rows of zero length",
)
.at_opt(at));
}
let mut ops = Vec::with_capacity(left.len());
for (l, r) in left.into_iter().zip(right) {
let op = self.make_op(op_names, Some(l), r, at)?;
let message = if op.ty != oid::BOOL {
format!(
"row comparison operator must yield type boolean, not type {}",
types::name(op.ty)
)
} else if op.first_set_call().is_some() {
"row comparison operator must not return a set".to_string()
} else {
ops.push(op);
continue;
};
return Err(Error::new(SqlState::DATATYPE_MISMATCH, message).at_opt(at));
}
if ops.len() == 1 {
return Ok(ops.remove(0));
}
let mut kinds = u8::MAX;
let mut operators = Vec::with_capacity(ops.len());
for op in &ops {
let ExprKind::Func(Func { form: FuncForm::Operator(operator), .. }) = op.kind else {
return Err(Error::internal("a row comparison that is not an operator"));
};
kinds &= compare_kinds(operator);
operators.push(operator);
}
let strategy = kinds.trailing_zeros();
if let Some(kind) = RowCompareKind::of_strategy(strategy) {
let compares = operators
.iter()
.map(|&operator| {
compare_proc(operator, strategy).ok_or_else(|| {
Error::internal(format!(
"missing support function 1 for the operator {operator}"
))
})
})
.collect::<Result<Vec<u32>>>()?;
let rc = RowCompare { kind, ops, compares };
return Ok(Expr::new(ExprKind::RowCompare(Box::new(rc)), oid::BOOL));
}
match strategy {
COMPARE_EQ => Ok(Expr::new(ExprKind::Bool(BoolOp::And, ops), oid::BOOL).at(at)),
COMPARE_NE => Ok(Expr::new(ExprKind::Bool(BoolOp::Or, ops), oid::BOOL).at(at)),
8 => Err(Error::new(
SqlState::FEATURE_NOT_SUPPORTED,
format!(
"could not determine interpretation of row comparison operator {}",
op_names.last().copied().unwrap_or_default()
),
)
.with_hint("Row comparison operators must be associated with btree operator families.")
.at_opt(at)),
_ => Err(Error::internal("a row comparison of an unknown kind")),
}
}
fn transform_distinct(&mut self, a: &A_Expr, op_names: &[&str]) -> Result<Expr> {
let at = place(a.location);
let not = a.kind == A_Expr_Kind::AEXPR_NOT_DISTINCT;
let is_null = |node: &Option<Node>| matches!(node, Some(Node::A_Const(c)) if c.isnull);
if is_null(&a.rexpr) || is_null(&a.lexpr) {
let null_right = is_null(&a.rexpr);
let (other, null) =
if null_right { (&a.lexpr, &a.rexpr) } else { (&a.rexpr, &a.lexpr) };
let arg = self.transform(other.as_ref())?;
if arg.ty != oid::RECORD && self.row_columns(arg.ty).is_none() {
return Ok(Expr::new(ExprKind::NullTest(Box::new(arg), not), oid::BOOL).at(at));
}
let null = self.transform(null.as_ref())?;
let (left, right) = if null_right { (arg, null) } else { (null, arg) };
return self.distinct_op(op_names, not, left, right, at);
}
let left = self.transform(a.lexpr.as_ref())?;
let right = self.transform(a.rexpr.as_ref())?;
let (ExprKind::Row(l, _), ExprKind::Row(r, _)) = (&left.kind, &right.kind) else {
return self.distinct_op(op_names, not, left, right, at);
};
if l.len() != r.len() {
return Err(Error::new(
SqlState::SYNTAX_ERROR,
"unequal number of entries in row expressions",
)
.at_opt(at));
}
let mut pairs = l.iter().cloned().zip(r.iter().cloned());
if l.len() == 1
&& let Some((left, right)) = pairs.next()
{
return self.distinct_op(op_names, not, left, right, at);
}
let mut result: Option<Expr> = None;
for (left, right) in pairs {
let test = self.distinct_op(op_names, false, left, right, at)?;
result = Some(match result {
None => test,
Some(before) => {
Expr::new(ExprKind::Bool(BoolOp::Or, vec![before, test]), oid::BOOL).at(at)
}
});
}
let result =
result.unwrap_or_else(|| Expr::constant(Value::Bool(false), oid::BOOL, -1, None));
Ok(if not {
Expr::new(ExprKind::Bool(BoolOp::Not, vec![result]), oid::BOOL).at(at)
} else {
result
})
}
fn distinct_op(
&mut self,
op_names: &[&str],
not: bool,
left: Expr,
right: Expr,
at: Option<usize>,
) -> Result<Expr> {
let name = op_names.last().copied().unwrap_or("=");
let (func, ty, left, right) = self.operator_function(name, left, right, at)?;
if ty != oid::BOOL {
return Err(Error::new(
SqlState::DATATYPE_MISMATCH,
"IS DISTINCT FROM requires = operator to yield boolean",
)
.at_opt(at));
}
Ok(Expr::new(ExprKind::Distinct { equal: func, not, args: vec![left, right] }, oid::BOOL)
.at(at))
}
fn transform_a_expr(&mut self, a: &A_Expr) -> Result<Expr> {
let at = place(a.location);
let op_names = names(&a.name);
match a.kind {
A_Expr_Kind::AEXPR_OP
| A_Expr_Kind::AEXPR_LIKE
| A_Expr_Kind::AEXPR_ILIKE
| A_Expr_Kind::AEXPR_SIMILAR => {
if let (Some(row @ Node::RowExpr(_)), Some(Node::SubLink(s))) = (&a.lexpr, &a.rexpr)
&& s.subLinkType == SubLinkType::EXPR_SUBLINK
{
let s = RawSubLink {
subLinkType: SubLinkType::ROWCOMPARE_SUBLINK,
testexpr: Some(row.clone()),
operName: a.name.clone(),
location: a.location,
..(**s).clone()
};
return self.transform_sublink(&s);
}
if let (Some(Node::RowExpr(l)), Some(Node::RowExpr(r))) = (&a.lexpr, &a.rexpr) {
let left = self.transform_expression_list(&l.args)?;
let right = self.transform_expression_list(&r.args)?;
return self.row_comparison(&op_names, left, right, at);
}
let left = match &a.lexpr {
Some(node) => Some(self.transform(Some(node))?),
None => None,
};
let right = self.transform(a.rexpr.as_ref())?;
self.make_op(&op_names, left, right, at)
}
A_Expr_Kind::AEXPR_OP_ANY | A_Expr_Kind::AEXPR_OP_ALL => {
let left = self.transform(a.lexpr.as_ref())?;
let right = self.transform(a.rexpr.as_ref())?;
self.scalar_array_op(
&op_names,
a.kind == A_Expr_Kind::AEXPR_OP_ANY,
left,
right,
at,
)
}
A_Expr_Kind::AEXPR_DISTINCT | A_Expr_Kind::AEXPR_NOT_DISTINCT => {
self.transform_distinct(a, &op_names)
}
A_Expr_Kind::AEXPR_NULLIF => {
let left = self.transform(a.lexpr.as_ref())?;
let right = self.transform(a.rexpr.as_ref())?;
let name = op_names.last().copied().unwrap_or("=");
let (func, ty, left, right) = self.operator_function(name, left, right, at)?;
if ty != oid::BOOL {
return Err(Error::new(
SqlState::DATATYPE_MISMATCH,
"NULLIF requires = operator to yield boolean",
)
.at_opt(at));
}
let result = left.ty;
Ok(Expr::new(ExprKind::NullIf { equal: func, args: vec![left, right] }, result)
.at(at))
}
A_Expr_Kind::AEXPR_IN => self.transform_in(a, &op_names),
A_Expr_Kind::AEXPR_BETWEEN
| A_Expr_Kind::AEXPR_NOT_BETWEEN
| A_Expr_Kind::AEXPR_BETWEEN_SYM
| A_Expr_Kind::AEXPR_NOT_BETWEEN_SYM => {
let Some(Node::List(bounds)) = &a.rexpr else {
return Err(Error::internal("BETWEEN needs two bounds"));
};
let (Some(Some(low)), Some(Some(high))) = (bounds.first(), bounds.get(1)) else {
return Err(Error::internal("BETWEEN needs two bounds"));
};
let value = a.lexpr.clone();
let l = a.location;
let range = |ge: &str, le: &str, join: BoolExprType, low: &Node, high: &Node| {
bool_node(
join,
vec![
simple_op(ge, value.clone(), Some(low.clone()), l),
simple_op(le, value.clone(), Some(high.clone()), l),
],
l,
)
};
let node = match a.kind {
A_Expr_Kind::AEXPR_BETWEEN => {
range(">=", "<=", BoolExprType::AND_EXPR, low, high)
}
A_Expr_Kind::AEXPR_NOT_BETWEEN => {
range("<", ">", BoolExprType::OR_EXPR, low, high)
}
A_Expr_Kind::AEXPR_BETWEEN_SYM => bool_node(
BoolExprType::OR_EXPR,
vec![
range(">=", "<=", BoolExprType::AND_EXPR, low, high),
range(">=", "<=", BoolExprType::AND_EXPR, high, low),
],
l,
),
_ => bool_node(
BoolExprType::AND_EXPR,
vec![
range("<", ">", BoolExprType::OR_EXPR, low, high),
range("<", ">", BoolExprType::OR_EXPR, high, low),
],
l,
),
};
self.transform(Some(&node))
}
_ => Err(not_yet("this kind of operator", at)),
}
}
fn scalar_array_op(
&mut self,
op_names: &[&str],
any: bool,
left: Expr,
right: Expr,
at: Option<usize>,
) -> Result<Expr> {
let array = right.ty;
let element = if array == oid::UNKNOWN {
oid::UNKNOWN
} else {
match types::element(types::base(array)) {
0 => {
return Err(Error::new(
SqlState::WRONG_OBJECT_TYPE,
"op ANY/ALL (array) requires array on right side",
)
.at_opt(at));
}
e => e,
}
};
let op = self.find_operator(op_names, left.ty, element, at)?;
let mut declared = [op.left, op.right];
let result = crate::poly::resolve(&[left.ty, element], &mut declared, op.result)?;
if result != oid::BOOL {
return Err(Error::new(
SqlState::WRONG_OBJECT_TYPE,
"op ANY/ALL (array) requires operator to yield boolean",
)
.at_opt(at));
}
let array_decl = if types::is_polymorphic(declared[1]) {
array
} else {
match types::array_of(declared[1]) {
0 => {
return Err(Error::new(
SqlState::UNDEFINED_OBJECT,
format!(
"could not find array type for data type {}",
types::name(declared[1])
),
)
.at_opt(at));
}
a => a,
}
};
let left = if left.ty == declared[0] {
left
} else {
self.coerce(left, declared[0], -1, Context::Implicit, None)?
};
let right = if right.ty == array_decl {
right
} else {
self.coerce(right, array_decl, -1, Context::Implicit, None)?
};
Ok(Expr::new(
ExprKind::ScalarArrayOp { func: op.code, any, args: vec![left, right] },
oid::BOOL,
)
.at(at))
}
fn transform_in(&mut self, a: &A_Expr, op_names: &[&str]) -> Result<Expr> {
let at = place(a.location);
let use_or = op_names.last() != Some(&"<>");
let left = self.transform(a.lexpr.as_ref())?;
let Some(Node::List(list)) = &a.rexpr else {
return Err(not_yet("IN with a subquery", at));
};
let mut values = self.transform_list(list)?;
let mut result: Option<Expr> = None;
if values.len() > 1 {
let mut all: Vec<u32> = vec![left.ty];
all.extend(values.iter().map(|v| v.ty));
let scalar = common_of(&all)
.ok()
.filter(|&t| all.iter().all(|&a| can_coerce(&[a], &[t], Context::Implicit)))
.filter(|&t| t != oid::RECORD);
let array = scalar.map_or(0, types::array_of);
if let (Some(scalar), true) = (scalar, array != 0) {
let elements = values
.drain(..)
.map(|v| self.coerce_to_common(v, scalar, "IN"))
.collect::<Result<Vec<_>>>()?;
let array_expr = Expr::new(
ExprKind::Array { element: scalar, multidims: false, elements },
array,
);
result =
Some(self.scalar_array_op(op_names, use_or, left.clone(), array_expr, at)?);
}
}
for value in values {
let cmp = self.make_op(op_names, Some(left.clone()), value, at)?;
let cmp = self.coerce_to_boolean(cmp, "IN")?;
result = Some(match result {
None => cmp,
Some(prev) => {
let op = if use_or { BoolOp::Or } else { BoolOp::And };
Expr::new(ExprKind::Bool(op, vec![prev, cmp]), oid::BOOL).at(at)
}
});
}
result.ok_or_else(|| Error::internal("IN with no values"))
}
fn transform_bool(&mut self, b: &BoolExpr) -> Result<Expr> {
let (op, construct) = match b.boolop {
BoolExprType::AND_EXPR => (BoolOp::And, "AND"),
BoolExprType::OR_EXPR => (BoolOp::Or, "OR"),
_ => (BoolOp::Not, "NOT"),
};
let mut args = Vec::with_capacity(b.args.len());
for arg in &b.args {
let arg = self.transform(arg.as_ref())?;
args.push(self.coerce_to_boolean(arg, construct)?);
}
Ok(Expr::new(ExprKind::Bool(op, args), oid::BOOL).at(place(b.location)))
}
fn transform_func(&mut self, f: &FuncCall) -> Result<Expr> {
let at = place(f.location);
let func_names = names(&f.funcname);
let srfs = self.srfs.0;
let mut args = self.transform_list(&f.args)?;
if f.agg_within_group {
for item in &f.agg_order {
let Some(Node::SortBy(by)) = item else {
return Err(Error::internal("an item of WITHIN GROUP that is not SortBy"));
};
args.push(self.transform(by.node.as_ref())?);
}
}
let filter = match &f.agg_filter {
Some(node) => {
let expr = self.with_kind(Kind::Filter, |a| a.transform(Some(node)))?;
Some(self.coerce_to_boolean(expr, "FILTER")?)
}
None => None,
};
let parts = Parts {
star: f.agg_star,
distinct: f.agg_distinct,
within_group: f.agg_within_group,
order: &f.agg_order,
filter,
over: f.over.as_deref(),
nulls: match f.ignore_nulls {
0 => NullTreatment::None,
1 => NullTreatment::Ignore,
_ => NullTreatment::Respect,
},
srfs,
};
let mut call =
self.make_func(&func_names, args, f.func_variadic, Some(parts), false, at)?;
if f.funcformat == CoercionForm::COERCE_SQL_SYNTAX
&& let ExprKind::Func(func) = &mut call.kind
&& func.form == FuncForm::Call
{
func.form = FuncForm::SqlSyntax;
}
Ok(call)
}
fn transform_cast(&mut self, t: &TypeCast) -> Result<Expr> {
let Some(type_name) = &t.typeName else {
return Err(Error::internal("a cast with no type"));
};
let (target, typmod) = self.type_name(type_name)?;
let expr = match &t.arg {
Some(Node::A_ArrayExpr(a)) => {
let base = types::base(target);
match types::element(base) {
0 => self.transform(t.arg.as_ref())?,
element => self.transform_array(a, base, element, typmod)?,
}
}
arg => self.transform(arg.as_ref())?,
};
let at = place(t.location).or(place(type_name.location));
let source = expr.ty;
let place_of_expr = expr.place();
match self.coerce_to_target(expr, target, typmod, Context::Explicit, at)? {
Some(expr) => Ok(expr),
None => Err(Error::new(
SqlState::CANNOT_COERCE,
format!("cannot cast type {} to {}", types::name(source), types::name(target)),
)
.at_opt(at.or(place_of_expr))),
}
}
fn transform_case(&mut self, c: &CaseExpr) -> Result<Expr> {
let at = place(c.location);
let arg = match &c.arg {
Some(node) => {
let arg = self.transform(Some(node))?;
let arg = if arg.ty == oid::UNKNOWN {
self.coerce_to_common(arg, oid::TEXT, "CASE")?
} else {
arg
};
Some(arg)
}
None => None,
};
let mut conditions = Vec::new();
let mut results = Vec::new();
for when in &c.args {
let Some(Node::CaseWhen(w)) = when else {
return Err(Error::internal("a CASE arm that is not WHEN"));
};
let condition = match &arg {
Some(arg) => {
let test = Expr::new(ExprKind::CaseTest, arg.ty);
let value = self.transform(w.expr.as_ref())?;
self.make_op(&["="], Some(test), value, place(w.location))?
}
None => self.transform(w.expr.as_ref())?,
};
conditions.push(self.coerce_to_boolean(condition, "CASE/WHEN")?);
results.push(self.transform(w.result.as_ref())?);
}
let default = match &c.defresult {
Some(node) => self.transform(Some(node))?,
None => Expr::constant(Value::Null, oid::UNKNOWN, -1, None),
};
let mut all = Vec::with_capacity(results.len() + 1);
all.push(default);
all.extend(results);
let ty = self.common_type(&all, "CASE")?;
let mut all = all
.into_iter()
.map(|e| self.coerce_to_common(e, ty, "CASE"))
.collect::<Result<Vec<_>>>()?
.into_iter();
let default = all.next().expect("the default");
let whens = conditions.into_iter().zip(all).collect();
Ok(Expr {
kind: ExprKind::Case(Case {
arg: arg.map(Box::new),
whens,
default: Box::new(default),
}),
ty,
typmod: -1,
location: at,
})
}
fn transform_sql_value(&mut self, f: &SQLValueFunction) -> Result<Expr> {
let at = place(f.location);
let precision = |s: &mut Self, what: &str, tz: bool| -> Result<Option<i32>> {
let tz = if tz { " WITH TIME ZONE" } else { "" };
let n = f.typmod;
s.precision(n, &format!("{what}({n}){tz}")).map(Some).map_err(|e| e.at_opt(at))
};
let (value, ty) = match f.op {
SQLValueFunctionOp::SVFOP_CURRENT_DATE => (SqlValue::CurrentDate, oid::DATE),
SQLValueFunctionOp::SVFOP_CURRENT_TIME => (SqlValue::CurrentTime(None), oid::TIMETZ),
SQLValueFunctionOp::SVFOP_CURRENT_TIME_N => {
(SqlValue::CurrentTime(precision(self, "TIME", true)?), oid::TIMETZ)
}
SQLValueFunctionOp::SVFOP_CURRENT_TIMESTAMP => {
(SqlValue::CurrentTimestamp(None), oid::TIMESTAMPTZ)
}
SQLValueFunctionOp::SVFOP_CURRENT_TIMESTAMP_N => {
(SqlValue::CurrentTimestamp(precision(self, "TIMESTAMP", true)?), oid::TIMESTAMPTZ)
}
SQLValueFunctionOp::SVFOP_LOCALTIME => (SqlValue::LocalTime(None), oid::TIME),
SQLValueFunctionOp::SVFOP_LOCALTIME_N => {
(SqlValue::LocalTime(precision(self, "TIME", false)?), oid::TIME)
}
SQLValueFunctionOp::SVFOP_LOCALTIMESTAMP => {
(SqlValue::LocalTimestamp(None), oid::TIMESTAMP)
}
SQLValueFunctionOp::SVFOP_LOCALTIMESTAMP_N => {
(SqlValue::LocalTimestamp(precision(self, "TIMESTAMP", false)?), oid::TIMESTAMP)
}
SQLValueFunctionOp::SVFOP_CURRENT_ROLE => (SqlValue::CurrentRole, oid::NAME),
SQLValueFunctionOp::SVFOP_CURRENT_USER => (SqlValue::CurrentUser, oid::NAME),
SQLValueFunctionOp::SVFOP_USER => (SqlValue::User, oid::NAME),
SQLValueFunctionOp::SVFOP_SESSION_USER => (SqlValue::SessionUser, oid::NAME),
SQLValueFunctionOp::SVFOP_CURRENT_CATALOG => (SqlValue::CurrentCatalog, oid::NAME),
_ => (SqlValue::CurrentSchema, oid::NAME),
};
let typmod = match value {
SqlValue::CurrentTime(Some(p))
| SqlValue::CurrentTimestamp(Some(p))
| SqlValue::LocalTime(Some(p))
| SqlValue::LocalTimestamp(Some(p)) => p,
_ => -1,
};
Ok(Expr { kind: ExprKind::SqlValue(value), ty, typmod, location: at })
}
fn transform_array(
&mut self,
a: &A_ArrayExpr,
array: u32,
element: u32,
typmod: i32,
) -> Result<Expr> {
let at = place(a.location);
let mut multidims = false;
let mut elements = Vec::with_capacity(a.elements.len());
for e in &a.elements {
let new = match e {
Some(Node::A_ArrayExpr(sub)) => {
multidims = true;
self.transform_array(sub, array, element, typmod)?
}
other => {
let new = self.transform(other.as_ref())?;
if !multidims
&& new.ty != oid::INT2VECTOR
&& new.ty != oid::OIDVECTOR
&& types::element(new.ty) != 0
{
multidims = true;
}
new
}
};
elements.push(new);
}
let (array, element, target, hard) = if array != 0 {
(array, element, if multidims { array } else { element }, true)
} else {
if elements.is_empty() {
return Err(Error::new(
SqlState::INDETERMINATE_DATATYPE,
"cannot determine type of empty array",
)
.with_hint("Explicitly cast to the desired type, for example ARRAY[]::integer[].")
.at_opt(at));
}
let common = self.common_type(&elements, "ARRAY")?;
if multidims {
let element = types::element(common);
if element == 0 {
return Err(Error::new(
SqlState::UNDEFINED_OBJECT,
format!(
"could not find element type for data type {}",
types::name(common)
),
)
.at_opt(at));
}
(common, element, common, false)
} else {
let array = types::array_of(common);
if array == 0 {
return Err(Error::new(
SqlState::UNDEFINED_OBJECT,
format!("could not find array type for data type {}", types::name(common)),
)
.at_opt(at));
}
(array, common, common, false)
}
};
let mut coerced = Vec::with_capacity(elements.len());
for e in elements {
let new = if hard {
let (source, place_of) = (e.ty, e.place());
match self.coerce_to_target(e, target, typmod, Context::Explicit, None)? {
Some(new) => new,
None => {
return Err(Error::new(
SqlState::CANNOT_COERCE,
format!(
"cannot cast type {} to {}",
types::name(source),
types::name(target)
),
)
.at_opt(place_of));
}
}
} else {
self.coerce_to_common(e, target, "ARRAY")?
};
coerced.push(new);
}
let typmod = match coerced.first() {
Some(first) if coerced.iter().all(|e| e.ty == first.ty && e.typmod == first.typmod) => {
first.typmod
}
_ => -1,
};
Ok(Expr {
kind: ExprKind::Array { element, multidims, elements: coerced },
ty: array,
typmod,
location: at,
})
}
fn comparison(&self, ty: u32, at: Option<usize>) -> Result<u32> {
let base = types::base(ty);
rupg_pgcatalog::builtin::operators_named("<")
.find(|op| {
op.kind == b'b' && op.left == base && op.right == base && op.namespace == PG_CATALOG
})
.map(|op| op.code)
.or_else(|| {
let lt = crate::typcache::operators(base).lt;
rupg_pgcatalog::builtin::operator_by_oid(lt).map(|op| op.code)
})
.ok_or_else(|| {
Error::new(
SqlState::UNDEFINED_FUNCTION,
format!(
"could not identify a comparison function for type {}",
types::name(ty)
),
)
.at_opt(at)
})
}
}
const PG_CATALOG: u32 = 11;
const MAX_DEPTH: usize = 1000;
const MAX_PARAMS: usize = 65535;