Skip to main content

nodedb_sql/planner/index_ddl/
drop.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Plan a `DROP INDEX` statement parsed by sqlparser.
4
5use crate::types::DropIndexPlan;
6use sqlparser::ast;
7
8use crate::SqlPlan;
9use crate::error::{Result, SqlError};
10use crate::parser::normalize::normalize_object_name_checked;
11
12/// Plan a `DROP INDEX [IF EXISTS] name [ON collection]` statement.
13///
14/// A missing or schema-qualified index name is a hard error — silently
15/// defaulting to an empty string would produce cryptic "index not found"
16/// failures downstream.
17pub fn plan_drop_index(stmt: &ast::Statement) -> Result<SqlPlan> {
18    let ast::Statement::Drop {
19        object_type: ast::ObjectType::Index,
20        names,
21        if_exists,
22        table,
23        ..
24    } = stmt
25    else {
26        return Err(SqlError::Parse {
27            detail: "DROP INDEX: unexpected statement shape".into(),
28        });
29    };
30
31    let name = names.first().ok_or_else(|| SqlError::Parse {
32        detail: "DROP INDEX: an index name is required".into(),
33    })?;
34    let index_name = normalize_object_name_checked(name)?;
35    if index_name.is_empty() {
36        return Err(SqlError::Parse {
37            detail: "DROP INDEX: empty index name".into(),
38        });
39    }
40
41    let collection = match table.as_ref() {
42        Some(t) => Some(normalize_object_name_checked(t)?),
43        None => None,
44    };
45
46    Ok(SqlPlan::DropIndex(DropIndexPlan {
47        index_name,
48        collection,
49        if_exists: *if_exists,
50    }))
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::parser::statement::parse_sql;
57
58    fn plan(sql: &str) -> Result<SqlPlan> {
59        let stmts = parse_sql(sql).expect("parse");
60        plan_drop_index(&stmts[0])
61    }
62
63    #[test]
64    fn basic_drop() {
65        let SqlPlan::DropIndex(DropIndexPlan {
66            index_name,
67            collection,
68            if_exists,
69        }) = plan("DROP INDEX idx_users_email").unwrap()
70        else {
71            panic!("expected DropIndex");
72        };
73        assert_eq!(index_name, "idx_users_email");
74        assert!(collection.is_none());
75        assert!(!if_exists);
76    }
77
78    #[test]
79    fn if_exists_honored() {
80        let SqlPlan::DropIndex(DropIndexPlan { if_exists, .. }) =
81            plan("DROP INDEX IF EXISTS idx").unwrap()
82        else {
83            panic!("expected DropIndex");
84        };
85        assert!(if_exists);
86    }
87
88    #[test]
89    fn schema_qualified_name_rejected() {
90        let err = plan("DROP INDEX public.idx").unwrap_err();
91        assert!(matches!(err, SqlError::Unsupported { .. }), "{err:?}");
92    }
93}