Plan a `CREATE INDEX` statement. Supports: - `CREATE [UNIQUE] INDEX [IF NOT EXISTS] [name] ON table (col [COLLATE coll])` - `COLLATE NOCASE` / `COLLATE CI` / `COLLATE CASE_INSENSITIVE` on the indexed column → `case_insensitive = true`. Multi-column indexes, expression indexes, and predicate (`WHERE`) indexes are rejected with a typed error rather than silently dropped.
(ci: &ast::CreateIndex)
| 18 | /// Multi-column indexes, expression indexes, and predicate (`WHERE`) indexes |
| 19 | /// are rejected with a typed error rather than silently dropped. |
| 20 | pub fn plan_create_index(ci: &ast::CreateIndex) -> Result<SqlPlan> { |
| 21 | let collection = |
| 22 | normalize_object_name_checked(&ci.table_name).map_err(|_| SqlError::Parse { |
| 23 | detail: "CREATE INDEX: missing or schema-qualified table name".into(), |
| 24 | })?; |
| 25 | |
| 26 | let index_name = match ci.name.as_ref() { |
| 27 | Some(n) => Some(normalize_object_name_checked(n)?), |
| 28 | None => None, |
| 29 | }; |
| 30 | |
| 31 | if ci.columns.is_empty() { |
| 32 | return Err(SqlError::Parse { |
| 33 | detail: "CREATE INDEX: at least one column is required".into(), |
| 34 | }); |
| 35 | } |
| 36 | if ci.columns.len() > 1 { |
| 37 | return Err(SqlError::Unsupported { |
| 38 | detail: "CREATE INDEX: multi-column indexes are not supported".into(), |
| 39 | }); |
| 40 | } |
| 41 | |
| 42 | let col = &ci.columns[0]; |
| 43 | let (field_expr, case_insensitive) = strip_collate(&col.column.expr); |
| 44 | let field = match field_expr { |
| 45 | ast::Expr::Identifier(ident) => normalize_ident(ident), |
| 46 | ast::Expr::CompoundIdentifier(parts) if parts.len() == 1 => normalize_ident(&parts[0]), |
| 47 | other => { |
| 48 | return Err(SqlError::Unsupported { |
| 49 | detail: format!("CREATE INDEX: expression indexes are not supported: {other}"), |
| 50 | }); |
| 51 | } |
| 52 | }; |
| 53 | |
| 54 | if ci.predicate.is_some() { |
| 55 | return Err(SqlError::Unsupported { |
| 56 | detail: "CREATE INDEX: partial (WHERE) indexes are not supported".into(), |
| 57 | }); |
| 58 | } |
| 59 | |
| 60 | Ok(SqlPlan::CreateIndex { |
| 61 | index_name, |
| 62 | collection, |
| 63 | field, |
| 64 | unique: ci.unique, |
| 65 | if_not_exists: ci.if_not_exists, |
| 66 | case_insensitive, |
| 67 | }) |
| 68 | } |
| 69 | |
| 70 | /// Strip a `COLLATE <name>` wrapper from `expr` and return the inner |
| 71 | /// expression together with whether the collation is a recognised |
no test coverage detected