Skip to content

Commit 06d18b0

Browse files
committed
bindings-macro: add custom index name to prevent index name collisions
1 parent e33cefb commit 06d18b0

1 file changed

Lines changed: 27 additions & 30 deletions

File tree

crates/bindings-macro/src/table.rs

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,6 @@ impl IndexArg {
243243
check_duplicate(&accessor, &meta)?;
244244
accessor = Some(meta.value()?.parse()?);
245245
}
246-
sym::name => {
247-
check_duplicate(&canonical_name, &meta)?;
248-
canonical_name = Some(meta.value()?.parse()?);
249-
}
250246

251247
sym::btree => {
252248
check_duplicate_msg(&algo, &meta, "index algorithm specified twice")?;
@@ -263,40 +259,33 @@ impl IndexArg {
263259
sym::name => {
264260
// If the user is trying to specify a `name`, do a bit of guessing at what their goal is.
265261
// This is going to be best-effort, and we're not going to try to do lookahead or anything.
266-
267-
return Err(if accessor.is_some() {
268-
// If the user's already specified an `accessor`,
269-
// then probably they're trying to specify the canonical name,
270-
// like you can for tables.
271-
// Print an error that says this is unsupported.
272-
meta.error(
273-
"Unexpected argument `name` in index definition.
274-
275-
Overwriting the `name` of an index is currently unsupported.",
276-
)
277-
} else if let Ok(sym) = meta.value().and_then(|val| val.parse::<Ident>()) {
262+
let val = meta.value()?;
263+
264+
if let Ok(name) = val.parse::<LitStr>() {
265+
// User provided a string literal - use it as custom index name
266+
check_duplicate(&canonical_name, &meta)?;
267+
canonical_name = Some(name);
268+
} else if accessor.is_none()
269+
&& let Ok(sym) = val.parse::<Ident>()
270+
{
278271
// If we haven't seen an `accessor` yet, and the value is an ident,
279272
// then probably this is 1.* syntax that needs a migration.
280273
// Note that, if the user specifies `name = {ident}` followed by `accessor = {ident}`,
281274
// we'll hit this branch, even though the diagnostic doesn't apply and we'd prefer not to.
282275
// I (pgoldman 2026-02-18) don't see a good way to distinguish this case
283276
// without making our parsing dramatically more complicated,
284277
// and it seems unlikely to occur.
285-
meta.error(format_args!(
278+
return Err(meta.error(format_args!(
286279
"Expected an `accessor` in index definition, but got a `name` instead.
287280
288-
If you're migrating from SpacetimeDB 1.*, replace `name = {sym}` with `accessor = {sym}`."
289-
))
281+
If you're migrating from SpacetimeDB 1.*, replace `name = {sym}` with `accessor = {sym}`.
282+
If you want to specify custom index name use string literal, , replace `name = {sym}` with `name = \"{sym}\"`"
283+
)));
290284
} else {
291-
// If we haven't seen an `accessor` yet, but the value is not an ident,
292-
// then we're not really sure what's going wrong, so print a more generic error message.
293-
meta.error(format_args!(
294-
"Unexpected argument `name` in index definition.
295-
296-
Overwriting the `name` of an index is currently unsupported.
297-
Did you mean to specify an `accessor` instead? Do so with `accessor = my_index`, where `my_index` is an unquoted identifier."
298-
))
299-
});
285+
return Err(meta.error(format_args!(
286+
"Use a string literal for a custom index name: `name = \"{val}\"`.",
287+
)));
288+
}
300289
}
301290
});
302291
Ok(())
@@ -369,6 +358,7 @@ Did you mean to specify an `accessor` instead? Do so with `accessor = my_index`,
369358
/// Parses an inline `#[index(btree)]`, `#[index(hash)]`, or `#[index(direct)]` attribute on a field.
370359
fn parse_index_attr(field: &Ident, attr: &syn::Attribute) -> syn::Result<Self> {
371360
let mut kind = None;
361+
let mut name = None;
372362
attr.parse_nested_meta(|meta| {
373363
match_meta!(match meta {
374364
sym::btree => {
@@ -387,6 +377,10 @@ Did you mean to specify an `accessor` instead? Do so with `accessor = my_index`,
387377
check_duplicate_msg(&kind, &meta, "index type specified twice")?;
388378
kind = Some(IndexType::Direct { column: field.clone() })
389379
}
380+
sym::name => {
381+
check_duplicate(&name, &meta)?;
382+
name = Some(meta.value()?.parse()?);
383+
}
390384
});
391385
Ok(())
392386
})?;
@@ -395,7 +389,7 @@ Did you mean to specify an `accessor` instead? Do so with `accessor = my_index`,
395389

396390
// Default accessor = field name if not provided
397391
let accessor = field.clone();
398-
Ok(IndexArg::new(accessor, kind, None))
392+
Ok(IndexArg::new(accessor, kind, name))
399393
}
400394

401395
fn validate<'a>(&'a self, table_name: &str, cols: &'a [Column<'a>]) -> syn::Result<ValidatedIndex<'a>> {
@@ -436,7 +430,10 @@ Did you mean to specify an `accessor` instead? Do so with `accessor = my_index`,
436430
is_unique: self.is_unique,
437431
// This must be the canonical name (name used internally in database),
438432
// as it is used in `index_id_from_name` abi.
439-
index_name: gen_index_name(),
433+
index_name: match self.canonical_name.as_ref() {
434+
Some(s) => s.value(),
435+
None => gen_index_name(),
436+
},
440437
accessor_name: &self.accessor,
441438
kind,
442439
canonical_name: self.canonical_name.as_ref().map(|s| s.value()),

0 commit comments

Comments
 (0)