uqa_sql/schema/indexes/
keys.rs1use crate::ast::{Expr, GeneratedColumnKind, IndexKey};
10use crate::schema::SchemaBindingContext;
11use crate::{ast::CreateIndex, ColumnType, SQLError};
12
13pub fn key_names(keys: &[IndexKey]) -> Vec<String> {
14 let mut names = Vec::with_capacity(keys.len());
15 for key in keys {
16 let label = match key {
17 IndexKey::Column(column) => column.clone(),
18 IndexKey::Expression(expression) => {
19 expression_name(expression).map_or_else(|| "expr".into(), |(name, _)| name)
20 }
21 };
22 let mut name = label.clone();
23 let mut suffix = 1_u64;
24 while names.contains(&name) {
25 name = format!("{label}{suffix}");
26 suffix += 1;
27 }
28 names.push(name);
29 }
30 names
31}
32
33fn expression_name(expression: &Expr) -> Option<(String, bool)> {
34 match expression {
35 Expr::Column(name) | Expr::QualifiedColumn { column: name, .. } => {
36 Some((name.clone(), true))
37 }
38 Expr::Func { name, .. } => Some((
39 crate::parse_regobject_name(name)
40 .and_then(|mut names| names.pop())
41 .unwrap_or_else(|| name.clone()),
42 true,
43 )),
44 Expr::Cast { expr, ty } => {
45 let inner = expression_name(expr);
46 if inner.as_ref().is_some_and(|(_, strong)| *strong) {
47 inner
48 } else {
49 Some((
50 crate::parse_regtype_name(ty)
51 .ok()
52 .flatten()
53 .and_then(|mut name| name.names.pop())
54 .unwrap_or_else(|| ty.clone()),
55 false,
56 ))
57 }
58 }
59 Expr::Case { else_branch, .. } => {
60 let inner = else_branch.as_deref().and_then(expression_name);
61 Some(
62 inner
63 .filter(|(_, strong)| *strong)
64 .unwrap_or_else(|| ("case".into(), false)),
65 )
66 }
67 Expr::Array(_) => Some(("array".into(), true)),
68 Expr::Row(_) => Some(("row".into(), true)),
69 _ => None,
70 }
71}
72
73pub fn require_column_key<'a>(key: &'a IndexKey, method: &str) -> Result<&'a str, SQLError> {
74 key.column().ok_or_else(|| {
75 SQLError::Unsupported(format!(
76 "expression keys for access method `{method}` are not implemented"
77 ))
78 })
79}
80
81pub fn prepare_index_keys(
82 context: &SchemaBindingContext<'_, '_>,
83 statement: &mut CreateIndex,
84) -> Result<Vec<ColumnType>, SQLError> {
85 let definitions = context
86 .catalog
87 .schema_expression_columns(&statement.table)?
88 .ok_or_else(|| SQLError::UnknownTable(statement.table.clone()))?;
89 let mut types = Vec::with_capacity(statement.columns.len());
90 for key in &mut statement.columns {
91 match key {
92 IndexKey::Column(name) => {
93 let Some(column) = definitions.iter().find(|column| column.name == *name) else {
94 if definitions.is_empty() {
95 types.push(ColumnType::Text);
96 continue;
97 }
98 return Err(SQLError::UnknownColumn(name.clone()));
99 };
100 if column
101 .generated
102 .as_ref()
103 .is_some_and(|generated| generated.kind == GeneratedColumnKind::Virtual)
104 {
105 return Err(SQLError::Unsupported(format!(
106 "indexes on virtual generated column `{name}` are not supported"
107 )));
108 }
109 types.push(column.ty.clone());
110 }
111 IndexKey::Expression(expression) => {
112 for column in &definitions {
113 if column
114 .generated
115 .as_ref()
116 .is_some_and(|generated| generated.kind == GeneratedColumnKind::Virtual)
117 && crate::schema::dependencies::schema_expr_references_column(
118 expression,
119 &column.name,
120 )
121 {
122 return Err(SQLError::Unsupported(format!(
123 "index expressions cannot use virtual generated column `{}`",
124 column.name
125 )));
126 }
127 }
128 let ty = super::prepare_index_expression(
129 context.catalog,
130 context.binding,
131 &statement.table,
132 expression,
133 )?;
134 let column = match expression.as_ref() {
135 crate::ast::Expr::Column(name) => Some(name.clone()),
136 crate::ast::Expr::Cast { expr, .. } => {
137 if let crate::ast::Expr::Column(name) = expr.as_ref() {
138 definitions
139 .iter()
140 .any(|column| column.name == *name && column.ty == ty)
141 .then(|| name.clone())
142 } else {
143 None
144 }
145 }
146 _ => None,
147 };
148 if let Some(column) = column {
149 *key = IndexKey::Column(column);
150 }
151 types.push(ty);
152 }
153 }
154 }
155 let mut included = std::collections::BTreeSet::new();
156 for name in &statement.included_columns {
157 if !definitions.is_empty() && !definitions.iter().any(|column| column.name == *name) {
158 return Err(SQLError::UnknownColumn(name.clone()));
159 }
160 if !included.insert(name)
161 || statement
162 .columns
163 .iter()
164 .any(|key| key.column() == Some(name.as_str()))
165 {
166 return Err(SQLError::Routine {
167 sqlstate: "42701".into(),
168 message: format!("column \"{name}\" included more than once"),
169 });
170 }
171 }
172 if !included.is_empty() && statement.access_method == "gin" {
173 return Err(SQLError::Unsupported(
174 "access method \"gin\" does not support included columns".into(),
175 ));
176 }
177 Ok(types)
178}
179
180pub fn prepare_index_definition(
182 catalog: &dyn crate::schema::SchemaExpressionCatalog,
183 bindings: &dyn crate::semantics::conflict::InferenceBindingScope,
184 c: &mut CreateIndex,
185) -> Result<crate::catalog::index::IndexDefinition, SQLError> {
186 let attribute_keys = c
187 .columns
188 .iter()
189 .cloned()
190 .chain(
191 c.included_columns
192 .iter()
193 .cloned()
194 .map(crate::ast::IndexKey::Column),
195 )
196 .collect::<Vec<_>>();
197 let key_names = key_names(&attribute_keys);
198 let binding = bindings.binding_scope()?;
199 let key_types = prepare_index_keys(
200 &SchemaBindingContext {
201 catalog,
202 binding: &binding.context(),
203 },
204 c,
205 )?;
206 if let Some(predicate) = c.predicate.as_deref_mut() {
207 let binding = bindings.binding_scope()?;
208 crate::schema::indexes::prepare_index_predicate(
209 catalog,
210 &binding.context(),
211 &c.table,
212 predicate,
213 )?;
214 }
215 Ok(crate::catalog::index::IndexDefinition {
216 key_names,
217 key_types,
218 included_columns: c.included_columns.clone(),
219 column_order: c.column_order.clone(),
220 predicate: c.predicate.clone(),
221 unique: c.unique,
222 nulls_not_distinct: c.nulls_not_distinct,
223 })
224}