uqa_sql/schema/indexes/
removal.rs1use crate::{ast::ColumnType, catalog::resolution::RelationResolution, SQLError};
9use uqa_core::RelationIdentity;
10
11pub fn resolve_drop_index_name(
12 resolution: RelationResolution,
13 requested: &str,
14 if_exists: bool,
15 notice: &mut dyn FnMut(&str),
16) -> Result<Option<String>, SQLError> {
17 match resolution {
18 RelationResolution::Found(canonical, "index") => Ok(Some(canonical)),
19 RelationResolution::Found(_, found) => {
20 let local = uqa_core::RelationIdentity::parse_reference(requested)
21 .map_err(SQLError::Internal)?
22 .1;
23 Err(crate::schema::removal::wrong_drop_kind_error(
24 &local, "index", found,
25 ))
26 }
27 RelationResolution::MissingSchema(schema) if if_exists => {
28 notice(&format!("schema \"{schema}\" does not exist, skipping"));
29 Ok(None)
30 }
31 RelationResolution::MissingSchema(schema) => Err(SQLError::Routine {
32 sqlstate: "3F000".into(),
33 message: format!("schema \"{schema}\" does not exist"),
34 }),
35 RelationResolution::MissingRelation if if_exists => {
36 let local = uqa_core::RelationIdentity::parse_reference(requested)
37 .map_err(SQLError::Internal)?
38 .1;
39 notice(&format!("index \"{local}\" does not exist, skipping"));
40 Ok(None)
41 }
42 RelationResolution::MissingRelation => {
43 let local = uqa_core::RelationIdentity::parse_reference(requested)
44 .map_err(SQLError::Internal)?
45 .1;
46 Err(SQLError::Routine {
47 sqlstate: "42704".into(),
48 message: format!("index \"{local}\" does not exist"),
49 })
50 }
51 }
52}
53pub fn catalog_index_columns(
54 relation: &RelationIdentity,
55 columns_json: &str,
56 action: &str,
57) -> Result<Vec<String>, SQLError> {
58 serde_json::from_str(columns_json).map_err(|e| {
59 SQLError::Internal(format!(
60 "{action} `{}`: invalid index column metadata: {e}",
61 relation.qualified_name()
62 ))
63 })
64}
65
66pub struct IndexRemovalCandidate<'a> {
68 pub relation: &'a RelationIdentity,
69 pub table: &'a str,
70 pub method: &'a str,
71 pub columns_json: &'a str,
72}
73pub fn gin_field_is_referenced<'a>(
74 relation: &RelationIdentity,
75 table: &str,
76 field: &str,
77 candidates: impl IntoIterator<Item = IndexRemovalCandidate<'a>>,
78) -> Result<bool, SQLError> {
79 for candidate in candidates {
80 if candidate.relation == relation
81 || candidate.table != table
82 || !candidate.method.eq_ignore_ascii_case("gin")
83 {
84 continue;
85 }
86 if catalog_index_columns(candidate.relation, candidate.columns_json, "DROP INDEX")?
87 .iter()
88 .any(|candidate_field| candidate_field == field)
89 {
90 return Ok(true);
91 }
92 }
93 Ok(false)
94}
95pub fn vector_index_dimensions(
96 relation: &RelationIdentity,
97 table: &str,
98 column: &str,
99 column_type: Option<ColumnType>,
100) -> Result<u32, SQLError> {
101 match column_type {
102 Some(ColumnType::Vector(dim) | ColumnType::Tensor(dim)) => Ok(dim),
103 Some(other) => Err(SQLError::Unsupported(format!(
104 "DROP INDEX `{}`: vector-index column `{}`.`{column}` is no longer VECTOR or TENSOR, got {other:?}",
105 relation.qualified_name(), table
106 ))),
107 None => Err(SQLError::Unsupported(format!(
108 "DROP INDEX `{}`: column `{}`.`{column}` does not exist",
109 relation.qualified_name(), table
110 ))),
111 }
112}