Skip to content

Commit 7e41705

Browse files
committed
index alias fix on restart
1 parent 81120b9 commit 7e41705

3 files changed

Lines changed: 131 additions & 2 deletions

File tree

crates/datastore/src/locking_tx_datastore/state_view.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use core::ops::RangeBounds;
1616
use spacetimedb_lib::ConnectionId;
1717
use spacetimedb_primitives::{ColList, TableId};
1818
use spacetimedb_sats::AlgebraicValue;
19-
use spacetimedb_schema::schema::{ColumnSchema, TableSchema, ViewDefInfo};
19+
use spacetimedb_schema::schema::{ColumnSchema, IndexSchema, TableSchema, ViewDefInfo};
2020
use spacetimedb_table::table::IndexScanPointIter;
2121
use spacetimedb_table::{
2222
blob_store::HashMapBlobStore,
@@ -120,6 +120,22 @@ pub trait StateView {
120120
.transpose()
121121
}
122122

123+
/// Look up an `st_index_accessor` row by its canonical index name.
124+
fn find_st_index_accessor_row_by_index_name(&self, index_name: &str) -> Result<Option<StIndexAccessorRow>> {
125+
match self.iter_by_col_eq(
126+
ST_INDEX_ACCESSOR_ID,
127+
StIndexAccessorFields::IndexName,
128+
&index_name.into(),
129+
) {
130+
Ok(mut iter) => iter.next().map(StIndexAccessorRow::try_from).transpose(),
131+
// `schema_for_table_raw` is called while restoring snapshots,
132+
// before `migrate_system_tables` creates newer system tables.
133+
// Treat a missing `st_index_accessor` as "no aliases yet" here.
134+
Err(DatastoreError::Table(TableError::IdNotFound(..))) => Ok(None),
135+
Err(e) => Err(e),
136+
}
137+
}
138+
123139
/// Look up an `st_column_accessor` row by its canonical table and column names
124140
fn find_st_column_accessor_row(&self, table_name: &str, col_name: &str) -> Result<Option<StColumnAccessorRow>> {
125141
match self.iter_by_col_eq(
@@ -187,7 +203,14 @@ pub trait StateView {
187203
// Look up the indexes for the table in question.
188204
let indexes = self
189205
.iter_by_col_eq(ST_INDEX_ID, StIndexFields::TableId, value_eq)?
190-
.map(|row| StIndexRow::try_from(row).map(Into::into))
206+
.map(|row| {
207+
let row = StIndexRow::try_from(row)?;
208+
let mut index_schema = IndexSchema::from(row);
209+
index_schema.alias = self
210+
.find_st_index_accessor_row_by_index_name(index_schema.index_name.as_ref())?
211+
.map(|row| row.accessor_name);
212+
Ok(index_schema)
213+
})
191214
.collect::<Result<Vec<_>>>()?;
192215

193216
let schedule = self

crates/smoketests/tests/smoketests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ mod sql;
3838
mod sql_connect_hook;
3939
mod templates;
4040
mod timestamp_route;
41+
mod typescript_index_source_name;
4142
mod views;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use spacetimedb_smoketests::{random_string, require_local_server, require_pnpm, Smoketest};
2+
3+
const TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server";
4+
5+
const users = table(
6+
{ name: "users", public: false },
7+
{
8+
id: t.u64().primaryKey().autoInc(),
9+
name: t.string(),
10+
emailAddress: t.string().index("btree"),
11+
},
12+
);
13+
14+
const spacetimedb = schema({
15+
users,
16+
});
17+
export default spacetimedb;
18+
19+
export const insert_user = spacetimedb.reducer(
20+
{
21+
name: t.string(),
22+
emailAddress: t.string(),
23+
},
24+
(ctx, { name, emailAddress }) => {
25+
ctx.db.users.insert({
26+
id: 0n,
27+
name,
28+
emailAddress,
29+
});
30+
},
31+
);
32+
"#;
33+
34+
const TYPESCRIPT_MODULE_WITH_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server";
35+
36+
const users = table(
37+
{ name: "users", public: false },
38+
{
39+
id: t.u64().primaryKey().autoInc(),
40+
name: t.string(),
41+
emailAddress: t.string().index("btree"),
42+
age: t.number().optional().default(undefined),
43+
isActive: t.bool().default(false).index(),
44+
},
45+
);
46+
47+
const spacetimedb = schema({
48+
users,
49+
});
50+
export default spacetimedb;
51+
52+
export const find_user_by_email = spacetimedb.reducer(
53+
{ emailAddress: t.string() },
54+
(ctx, { emailAddress }) => {
55+
let count = 0;
56+
for (const _row of ctx.db.users.emailAddress.filter(emailAddress)) {
57+
count += 1;
58+
}
59+
console.info(`matched ${count}`);
60+
},
61+
);
62+
63+
export const find_users_by_active_status = spacetimedb.reducer(
64+
{ isActive: t.bool() },
65+
(ctx, { isActive }) => {
66+
let count = 0;
67+
for (const _row of ctx.db.users.isActive.filter(isActive)) {
68+
count += 1;
69+
}
70+
console.info(`matched active users ${count}`);
71+
},
72+
);
73+
"#;
74+
75+
#[test]
76+
fn test_typescript_add_optional_columns() {
77+
require_pnpm!();
78+
require_local_server!();
79+
80+
let mut test = Smoketest::builder().autopublish(false).build();
81+
let module_name = format!("typescript-add-optional-columns-{}", random_string());
82+
83+
let database_identity = test
84+
.publish_typescript_module_source(
85+
"typescript-add-optional-columns-v1",
86+
&module_name,
87+
TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS,
88+
)
89+
.unwrap();
90+
91+
test.call("insert_user", &["Alice", "alice@example.com"]).unwrap();
92+
93+
test.restart_server();
94+
95+
test.publish_typescript_module_source_clear(
96+
"typescript-add-optional-columns-v2",
97+
&database_identity,
98+
TYPESCRIPT_MODULE_WITH_NEW_COLUMNS,
99+
false,
100+
)
101+
.unwrap();
102+
103+
test.call("find_user_by_email", &["alice@example.com"]).unwrap();
104+
test.call("find_users_by_active_status", &["false"]).unwrap();
105+
}

0 commit comments

Comments
 (0)