uqa_sql/schema/
indexes.rs1use 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_immutable_index_expression(engine, binding, table, expression, false)?;
24 if let Some(ty) = ty {
25 return Ok(ty);
26 }
27 *expression = Expr::Cast {
28 expr: Box::new(expression.clone()),
29 ty: "text".into(),
30 };
31 Ok(ColumnType::Text)
32}
33
34pub fn prepare_index_predicate(
35 engine: &dyn SchemaExpressionCatalog,
36 binding: &BindingContext<'_>,
37 table: &str,
38 expression: &mut Expr,
39) -> Result<(), SQLError> {
40 match bind_immutable_index_expression(engine, binding, table, expression, true)? {
41 Some(ColumnType::Boolean) => Ok(()),
42 None => {
43 if let Expr::Literal(value) = expression {
44 *value = crate::expr::cast_value(value, "boolean")?;
45 }
46 Ok(())
47 }
48 Some(_) => Err(SQLError::TypeMismatch(
49 "argument of WHERE must be type boolean".into(),
50 )),
51 }
52}
53
54fn bind_immutable_index_expression(
55 engine: &dyn SchemaExpressionCatalog,
56 binding: &BindingContext<'_>,
57 table: &str,
58 expression: &mut Expr,
59 predicate: bool,
60) -> Result<Option<ColumnType>, SQLError> {
61 let context = if predicate {
62 "index predicate"
63 } else {
64 "index expression"
65 };
66 let mut plan = ExpressionPlan::lower(expression.clone());
67 if !plan.subqueries.is_empty() {
68 return Err(index_error(
69 "0A000",
70 format!("cannot use subquery in {context}"),
71 ));
72 }
73 if crate::semantics::aggregates::contains_aggregate(engine, &plan.scalar) {
74 return Err(index_error(
75 "42803",
76 format!("aggregate functions are not allowed in {context}s"),
77 ));
78 }
79 if crate::semantics::windows::expr_has_window(&plan.scalar) {
80 return Err(index_error(
81 "42P20",
82 format!("window functions are not allowed in {context}s"),
83 ));
84 }
85 let columns = engine
86 .schema_expression_columns(table)?
87 .ok_or_else(|| SQLError::UnknownTable(table.into()))?;
88 let relation =
89 uqa_core::RelationIdentity::from_legacy_name(table).map_err(SQLError::Internal)?;
90 bind_schema_column_references(expression, &relation.name);
91 bind_schema_column_references(expression, table);
92 plan.scalar = ExpressionPlan::lower(expression.clone()).scalar;
93 let schema = RowSchema::with_types(
94 columns.iter().map(|column| column.name.clone()).collect(),
95 columns
96 .iter()
97 .map(|column| Some(column.ty.clone()))
98 .collect(),
99 );
100 if crate::semantics::sets::validation::expression_may_return_set(
101 engine,
102 engine,
103 &plan.scalar,
104 &schema,
105 &[],
106 )? {
107 return Err(index_error(
108 "0A000",
109 format!("set-returning functions are not allowed in {context}s"),
110 ));
111 }
112 typing::infer_generation_expression(engine, &columns, expression)?;
113 let ty = crate::binding::bind_expression_plan_routines_for_storage(
114 engine,
115 &mut plan,
116 &[],
117 binding,
118 &schema,
119 )?;
120 let references = crate::binding::stored_routines::collect_expression_routine_references(&plan)?;
121 crate::catalog::stored_ast::bind_stored_expression_routines(expression, &references)?;
122 Ok(ty)
123}
124
125fn index_error(sqlstate: &str, message: String) -> SQLError {
126 SQLError::Routine {
127 sqlstate: sqlstate.into(),
128 message,
129 }
130}
131
132pub mod keys;
133
134pub mod names;
135
136pub mod unique;
137
138pub mod options;
139pub mod vectors;
140
141pub mod removal;
142pub mod routines;