Skip to content

Commit 6339305

Browse files
authored
Do not use INSERT OR REPLACE in SQLite. (cashubtc#620)
Instead, use `INSERT` and `ON CONFLICT`. The reason is that in case of conflicts, the `REPLACE` will trigger a DELETE and then perform an INSERT, as outlined in the documentation[1], and that may cause a cascade of deletion due to our FOREIGN KEYs. Here is the official documentation: ``` When a UNIQUE or PRIMARY KEY constraint violation occurs, the REPLACE algorithm deletes pre-existing rows that are causing the constraint violation prior to inserting or updating the current row and the command continues executing normally. If a NOT NULL constraint violation occurs, the REPLACE conflict resolution replaces the NULL value with the default value for that column, or if the column has no default value, then the ABORT algorithm is used. If a CHECK constraint or foreign key constraint violation occurs, the REPLACE conflict resolution algorithm works like ABORT. When the REPLACE conflict resolution strategy deletes rows in order to satisfy a constraint, delete triggers fire if and only if recursive triggers are enabled. The update hook is not invoked for rows that are deleted by the REPLACE conflict resolution strategy. Nor does REPLACE increment the change counter. The exceptional behaviors defined in this paragraph might change in a future release. ``` [1] https://www.sqlite.org/lang_conflict.html
1 parent 062d7be commit 6339305

File tree

2 files changed

+126
-26
lines changed

2 files changed

+126
-26
lines changed

crates/cdk-sqlite/src/mint/mod.rs

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,23 @@ WHERE active = 1
205205

206206
let res = sqlx::query(
207207
r#"
208-
INSERT OR REPLACE INTO mint_quote
208+
INSERT INTO mint_quote
209209
(id, amount, unit, request, state, expiry, request_lookup_id, pubkey)
210-
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
210+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
211+
ON CONFLICT(id) DO UPDATE SET
212+
amount = excluded.amount,
213+
unit = excluded.unit,
214+
request = excluded.request,
215+
state = excluded.state,
216+
expiry = excluded.expiry,
217+
request_lookup_id = excluded.request_lookup_id
218+
ON CONFLICT(request_lookup_id) DO UPDATE SET
219+
amount = excluded.amount,
220+
unit = excluded.unit,
221+
request = excluded.request,
222+
state = excluded.state,
223+
expiry = excluded.expiry,
224+
id = excluded.id
211225
"#,
212226
)
213227
.bind(quote.id.to_string())
@@ -438,8 +452,8 @@ FROM mint_quote
438452
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
439453
let rec = sqlx::query(
440454
r#"
441-
SELECT *
442-
FROM mint_quote
455+
SELECT *
456+
FROM mint_quote
443457
WHERE state = ?
444458
"#,
445459
)
@@ -502,9 +516,28 @@ WHERE id=?
502516
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
503517
let res = sqlx::query(
504518
r#"
505-
INSERT OR REPLACE INTO melt_quote
519+
INSERT INTO melt_quote
506520
(id, unit, amount, request, fee_reserve, state, expiry, payment_preimage, request_lookup_id, msat_to_pay)
507-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
521+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
522+
ON CONFLICT(id) DO UPDATE SET
523+
unit = excluded.unit,
524+
amount = excluded.amount,
525+
request = excluded.request,
526+
fee_reserve = excluded.fee_reserve,
527+
state = excluded.state,
528+
expiry = excluded.expiry,
529+
payment_preimage = excluded.payment_preimage,
530+
request_lookup_id = excluded.request_lookup_id,
531+
msat_to_pay = excluded.msat_to_pay
532+
ON CONFLICT(request_lookup_id) DO UPDATE SET
533+
unit = excluded.unit,
534+
amount = excluded.amount,
535+
request = excluded.request,
536+
fee_reserve = excluded.fee_reserve,
537+
state = excluded.state,
538+
expiry = excluded.expiry,
539+
payment_preimage = excluded.payment_preimage,
540+
id = excluded.id;
508541
"#,
509542
)
510543
.bind(quote.id.to_string())
@@ -690,9 +723,18 @@ WHERE id=?
690723
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
691724
let res = sqlx::query(
692725
r#"
693-
INSERT OR REPLACE INTO keyset
726+
INSERT INTO keyset
694727
(id, unit, active, valid_from, valid_to, derivation_path, max_order, input_fee_ppk, derivation_path_index)
695-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
728+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
729+
ON CONFLICT(id) DO UPDATE SET
730+
unit = excluded.unit,
731+
active = excluded.active,
732+
valid_from = excluded.valid_from,
733+
valid_to = excluded.valid_to,
734+
derivation_path = excluded.derivation_path,
735+
max_order = excluded.max_order,
736+
input_fee_ppk = excluded.input_fee_ppk,
737+
derivation_path_index = excluded.derivation_path_index
696738
"#,
697739
)
698740
.bind(keyset.id.to_string())
@@ -1176,9 +1218,14 @@ WHERE keyset_id=?;
11761218

11771219
let res = sqlx::query(
11781220
r#"
1179-
INSERT OR REPLACE INTO melt_request
1221+
INSERT INTO melt_request
11801222
(id, inputs, outputs, method, unit)
1181-
VALUES (?, ?, ?, ?, ?);
1223+
VALUES (?, ?, ?, ?, ?)
1224+
ON CONFLICT(id) DO UPDATE SET
1225+
inputs = excluded.inputs,
1226+
outputs = excluded.outputs,
1227+
method = excluded.method,
1228+
unit = excluded.unit
11821229
"#,
11831230
)
11841231
.bind(melt_request.quote)
@@ -1290,9 +1337,12 @@ WHERE quote_id=?;
12901337

12911338
let res = sqlx::query(
12921339
r#"
1293-
INSERT OR REPLACE INTO config
1340+
INSERT INTO config
12941341
(id, value)
1295-
VALUES (?, ?);
1342+
VALUES (?, ?)
1343+
ON CONFLICT(id) DO UPDATE SET
1344+
value = excluded.value
1345+
;
12961346
"#,
12971347
)
12981348
.bind("mint_info")
@@ -1361,9 +1411,12 @@ WHERE id=?;
13611411

13621412
let res = sqlx::query(
13631413
r#"
1364-
INSERT OR REPLACE INTO config
1414+
INSERT INTO config
13651415
(id, value)
1366-
VALUES (?, ?);
1416+
VALUES (?, ?)
1417+
ON CONFLICT(id) DO UPDATE SET
1418+
value = excluded.value
1419+
;
13671420
"#,
13681421
)
13691422
.bind("quote_ttl")

crates/cdk-sqlite/src/wallet/mod.rs

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,22 @@ impl WalletDatabase for WalletSqliteDatabase {
130130

131131
sqlx::query(
132132
r#"
133-
INSERT OR REPLACE INTO mint
133+
INSERT INTO mint
134134
(mint_url, name, pubkey, version, description, description_long, contact, nuts, icon_url, urls, motd, mint_time)
135-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
135+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
136+
ON CONFLICT(mint_url) DO UPDATE SET
137+
name = excluded.name,
138+
pubkey = excluded.pubkey,
139+
version = excluded.version,
140+
description = excluded.description,
141+
description_long = excluded.description_long,
142+
contact = excluded.contact,
143+
nuts = excluded.nuts,
144+
icon_url = excluded.icon_url,
145+
urls = excluded.urls,
146+
motd = excluded.motd,
147+
mint_time = excluded.mint_time
148+
;
136149
"#,
137150
)
138151
.bind(mint_url.to_string())
@@ -345,9 +358,18 @@ WHERE id=?
345358
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
346359
sqlx::query(
347360
r#"
348-
INSERT OR REPLACE INTO mint_quote
361+
INSERT INTO mint_quote
349362
(id, mint_url, amount, unit, request, state, expiry, secret_key)
350-
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
363+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
364+
ON CONFLICT(id) DO UPDATE SET
365+
mint_url = excluded.mint_url,
366+
amount = excluded.amount,
367+
unit = excluded.unit,
368+
request = excluded.request,
369+
state = excluded.state,
370+
expiry = excluded.expiry,
371+
secret_key = excluded.secret_key
372+
;
351373
"#,
352374
)
353375
.bind(quote.id.to_string())
@@ -429,9 +451,17 @@ WHERE id=?
429451
async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err> {
430452
sqlx::query(
431453
r#"
432-
INSERT OR REPLACE INTO melt_quote
454+
INSERT INTO melt_quote
433455
(id, unit, amount, request, fee_reserve, state, expiry)
434-
VALUES (?, ?, ?, ?, ?, ?, ?);
456+
VALUES (?, ?, ?, ?, ?, ?, ?)
457+
ON CONFLICT(id) DO UPDATE SET
458+
unit = excluded.unit,
459+
amount = excluded.amount,
460+
request = excluded.request,
461+
fee_reserve = excluded.fee_reserve,
462+
state = excluded.state,
463+
expiry = excluded.expiry
464+
;
435465
"#,
436466
)
437467
.bind(quote.id.to_string())
@@ -492,9 +522,12 @@ WHERE id=?
492522
async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err> {
493523
sqlx::query(
494524
r#"
495-
INSERT OR REPLACE INTO key
525+
INSERT INTO key
496526
(id, keys)
497-
VALUES (?, ?);
527+
VALUES (?, ?)
528+
ON CONFLICT(id) DO UPDATE SET
529+
keys = excluded.keys
530+
;
498531
"#,
499532
)
500533
.bind(Id::from(&keys).to_string())
@@ -556,9 +589,20 @@ WHERE id=?
556589
for proof in added {
557590
sqlx::query(
558591
r#"
559-
INSERT OR REPLACE INTO proof
592+
INSERT INTO proof
560593
(y, mint_url, state, spending_condition, unit, amount, keyset_id, secret, c, witness)
561-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
594+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
595+
ON CONFLICT(y) DO UPDATE SET
596+
mint_url = excluded.mint_url,
597+
state = excluded.state,
598+
spending_condition = excluded.spending_condition,
599+
unit = excluded.unit,
600+
amount = excluded.amount,
601+
keyset_id = excluded.keyset_id,
602+
secret = excluded.secret,
603+
c = excluded.c,
604+
witness = excluded.witness
605+
;
562606
"#,
563607
)
564608
.bind(proof.y.to_bytes().to_vec())
@@ -765,9 +809,12 @@ WHERE key=?;
765809
) -> Result<(), Self::Err> {
766810
sqlx::query(
767811
r#"
768-
INSERT OR REPLACE INTO nostr_last_checked
812+
INSERT INTO nostr_last_checked
769813
(key, last_check)
770-
VALUES (?, ?);
814+
VALUES (?, ?)
815+
ON CONFLICT(key) DO UPDATE SET
816+
last_check = excluded.last_check
817+
;
771818
"#,
772819
)
773820
.bind(verifying_key.to_bytes().to_vec())

0 commit comments

Comments
 (0)