Skip to content

Commit a21580a

Browse files
authored
Merge pull request #79 from powersync-ja/reuse-prepared-statements
sync_local: Re-use prepared statements
2 parents fee756b + 42e3da1 commit a21580a

File tree

1 file changed

+59
-19
lines changed

1 file changed

+59
-19
lines changed

crates/core/src/sync_local.rs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,17 @@ impl<'a> SyncOperation<'a> {
103103

104104
self.collect_tables()?;
105105
let statement = self.collect_full_operations()?;
106-
// TODO: cache statements
106+
107+
// We cache the last insert and delete statements for each row
108+
let mut last_insert_table: Option<String> = None;
109+
let mut last_insert_statement: Option<ManagedStmt> = None;
110+
111+
let mut last_delete_table: Option<String> = None;
112+
let mut last_delete_statement: Option<ManagedStmt> = None;
113+
114+
let mut untyped_delete_statement: Option<ManagedStmt> = None;
115+
let mut untyped_insert_statement: Option<ManagedStmt> = None;
116+
107117
while statement.step().into_db_result(self.db)? == ResultCode::ROW {
108118
let type_name = statement.column_text(0)?;
109119
let id = statement.column_text(1)?;
@@ -118,40 +128,70 @@ impl<'a> SyncOperation<'a> {
118128
// NULL data means no PUT operations found, so we delete the row.
119129
if data.is_err() {
120130
// DELETE
121-
let delete_statement = self
122-
.db
123-
.prepare_v2(&format!("DELETE FROM {} WHERE id = ?", quoted))
124-
.into_db_result(self.db)?;
131+
if last_delete_table.as_deref() != Some(&quoted) {
132+
// Prepare statement when the table changed
133+
last_delete_statement = Some(
134+
self.db
135+
.prepare_v2(&format!("DELETE FROM {} WHERE id = ?", quoted))
136+
.into_db_result(self.db)?,
137+
);
138+
last_delete_table = Some(quoted.clone());
139+
}
140+
let delete_statement = last_delete_statement.as_mut().unwrap();
141+
142+
delete_statement.reset()?;
125143
delete_statement.bind_text(1, id, sqlite::Destructor::STATIC)?;
126144
delete_statement.exec()?;
127145
} else {
128146
// INSERT/UPDATE
129-
let insert_statement = self
130-
.db
131-
.prepare_v2(&format!("REPLACE INTO {}(id, data) VALUES(?, ?)", quoted))
132-
.into_db_result(self.db)?;
147+
if last_insert_table.as_deref() != Some(&quoted) {
148+
// Prepare statement when the table changed
149+
last_insert_statement = Some(
150+
self.db
151+
.prepare_v2(&format!(
152+
"REPLACE INTO {}(id, data) VALUES(?, ?)",
153+
quoted
154+
))
155+
.into_db_result(self.db)?,
156+
);
157+
last_insert_table = Some(quoted.clone());
158+
}
159+
let insert_statement = last_insert_statement.as_mut().unwrap();
160+
insert_statement.reset()?;
133161
insert_statement.bind_text(1, id, sqlite::Destructor::STATIC)?;
134162
insert_statement.bind_text(2, data?, sqlite::Destructor::STATIC)?;
135163
insert_statement.exec()?;
136164
}
137165
} else {
138166
if data.is_err() {
139167
// DELETE
140-
// language=SQLite
141-
let delete_statement = self
142-
.db
143-
.prepare_v2("DELETE FROM ps_untyped WHERE type = ? AND id = ?")
144-
.into_db_result(self.db)?;
168+
if untyped_delete_statement.is_none() {
169+
// Prepare statement on first use
170+
untyped_delete_statement = Some(
171+
self.db
172+
.prepare_v2("DELETE FROM ps_untyped WHERE type = ? AND id = ?")
173+
.into_db_result(self.db)?,
174+
);
175+
}
176+
let delete_statement = untyped_delete_statement.as_mut().unwrap();
177+
delete_statement.reset()?;
145178
delete_statement.bind_text(1, type_name, sqlite::Destructor::STATIC)?;
146179
delete_statement.bind_text(2, id, sqlite::Destructor::STATIC)?;
147180
delete_statement.exec()?;
148181
} else {
149182
// INSERT/UPDATE
150-
// language=SQLite
151-
let insert_statement = self
152-
.db
153-
.prepare_v2("REPLACE INTO ps_untyped(type, id, data) VALUES(?, ?, ?)")
154-
.into_db_result(self.db)?;
183+
if untyped_insert_statement.is_none() {
184+
// Prepare statement on first use
185+
untyped_insert_statement = Some(
186+
self.db
187+
.prepare_v2(
188+
"REPLACE INTO ps_untyped(type, id, data) VALUES(?, ?, ?)",
189+
)
190+
.into_db_result(self.db)?,
191+
);
192+
}
193+
let insert_statement = untyped_insert_statement.as_mut().unwrap();
194+
insert_statement.reset()?;
155195
insert_statement.bind_text(1, type_name, sqlite::Destructor::STATIC)?;
156196
insert_statement.bind_text(2, id, sqlite::Destructor::STATIC)?;
157197
insert_statement.bind_text(3, data?, sqlite::Destructor::STATIC)?;

0 commit comments

Comments
 (0)