1use super::{
10 generated::{bind_schema_column_references, typing},
11 SchemaExpressionCatalog,
12};
13use crate::plan::ExpressionPlan;
14use crate::RowSchema;
15use crate::{ast::Expr, binding::context::BindingContext, ColumnType, SQLError};
16
17pub fn prepare_index_expression(
18 engine: &dyn SchemaExpressionCatalog,
19 binding: &BindingContext<'_>,
20 table: &str,
21 expression: &mut Expr,
22) -> Result<ColumnType, SQLError> {
23 let ty = bind_index_expression(engine, binding, table, expression, false)?;
24 validate_index_expression_immutability(engine, table, expression, false)?;
25 if let Some(ty) = ty {
26 return Ok(ty);
27 }
28 *expression = Expr::Cast {
29 expr: Box::new(expression.clone()),
30 ty: "text".into(),
31 };
32 Ok(ColumnType::Text)
33}
34
35pub fn prepare_index_predicate(
36 engine: &dyn SchemaExpressionCatalog,
37 binding: &BindingContext<'_>,
38 table: &str,
39 expression: &mut Expr,
40) -> Result<(), SQLError> {
41 bind_index_predicate(engine, binding, table, expression)?;
42 validate_index_expression_immutability(engine, table, expression, true)
43}
44
45pub(super) fn bind_index_predicate(
46 engine: &dyn SchemaExpressionCatalog,
47 binding: &BindingContext<'_>,
48 table: &str,
49 expression: &mut Expr,
50) -> Result<(), SQLError> {
51 match bind_index_expression(engine, binding, table, expression, true)? {
52 Some(ColumnType::Boolean) => Ok(()),
53 None => {
54 if let Expr::Literal(value) = expression {
55 *value = crate::expr::cast_value(value, "boolean")?;
56 }
57 Ok(())
58 }
59 Some(_) => Err(SQLError::TypeMismatch(
60 "argument of WHERE must be type boolean".into(),
61 )),
62 }
63}
64
65pub(super) fn bind_index_expression(
66 engine: &dyn SchemaExpressionCatalog,
67 binding: &BindingContext<'_>,
68 table: &str,
69 expression: &mut Expr,
70 predicate: bool,
71) -> Result<Option<ColumnType>, SQLError> {
72 let context = if predicate {
73 "index predicate"
74 } else {
75 "index expression"
76 };
77 let mut plan = ExpressionPlan::lower(expression.clone());
78 if !plan.subqueries.is_empty() {
79 return Err(index_error(
80 "0A000",
81 format!("cannot use subquery in {context}"),
82 ));
83 }
84 if crate::semantics::aggregates::contains_aggregate(engine, &plan.scalar) {
85 return Err(index_error(
86 "42803",
87 format!("aggregate functions are not allowed in {context}s"),
88 ));
89 }
90 if crate::semantics::windows::expr_has_window(&plan.scalar) {
91 return Err(index_error(
92 "42P20",
93 format!("window functions are not allowed in {context}s"),
94 ));
95 }
96 let columns = engine
97 .schema_expression_columns(table)?
98 .ok_or_else(|| SQLError::UnknownTable(table.into()))?;
99 let relation =
100 uqa_core::RelationIdentity::from_legacy_name(table).map_err(SQLError::Internal)?;
101 bind_schema_column_references(expression, &relation.name);
102 bind_schema_column_references(expression, table);
103 plan.scalar = ExpressionPlan::lower(expression.clone()).scalar;
104 let schema = RowSchema::with_types(
105 columns.iter().map(|column| column.name.clone()).collect(),
106 columns
107 .iter()
108 .map(|column| Some(column.ty.clone()))
109 .collect(),
110 );
111 if crate::semantics::sets::validation::expression_may_return_set(
112 engine,
113 engine,
114 &plan.scalar,
115 &schema,
116 &[],
117 )? {
118 return Err(index_error(
119 "0A000",
120 format!("set-returning functions are not allowed in {context}s"),
121 ));
122 }
123 let ty = crate::binding::bind_expression_plan_routines_for_storage(
124 engine,
125 &mut plan,
126 &[],
127 binding,
128 &schema,
129 )?;
130 let references = crate::binding::stored_routines::collect_expression_routine_references(&plan)?;
131 crate::catalog::stored_ast::bind_stored_expression_routines(expression, &references)?;
132 Ok(ty)
133}
134
135pub(super) fn validate_index_expression_immutability(
136 engine: &dyn SchemaExpressionCatalog,
137 table: &str,
138 expression: &mut Expr,
139 predicate: bool,
140) -> Result<(), SQLError> {
141 let columns = engine
142 .schema_expression_columns(table)?
143 .ok_or_else(|| SQLError::UnknownTable(table.into()))?;
144 typing::infer_generation_expression(engine, &columns, expression)
145 .map(|_| ())
146 .map_err(|error| {
147 if error.sqlstate() == Some("42P17") {
148 let context = if predicate { "predicate" } else { "expression" };
149 index_error(
150 "42P17",
151 format!("functions in index {context} must be marked IMMUTABLE"),
152 )
153 } else {
154 error
155 }
156 })
157}
158
159fn index_error(sqlstate: &str, message: String) -> SQLError {
160 SQLError::Routine {
161 sqlstate: sqlstate.into(),
162 message,
163 }
164}
165
166pub mod keys;
167
168pub mod names;
169
170pub mod unique;
171
172pub mod options;
173pub mod vectors;
174
175pub mod removal;
176pub mod routines;