Skip to main content

max / goingson

Bump sqlx to 0.9, pin libsqlite3-sys 0.37 sqlx 0.8 -> 0.9.0; libsqlite3-sys 0.30 -> 0.37 so goingson's sqlx-sqlite co-links with synckit-client's rusqlite 0.39 (both on libsqlite3-sys 0.37). 0.9 changes applied: dynamic SQL wrapped in sqlx::AssertSqlSafe (repository query builders are structurally dynamic with bound values), the ArgumentBuffer / SqliteArguments associated types dropped their lifetime (custom Encode/Decode impls updated), and borrowed &str query params take AssertSqlSafe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-21 01:23 UTC
Commit: 3a245f595a39a5d6b2f343f9856c4f9394acd3c3
Parent: 27ed972
20 files changed, +183 insertions, -222 deletions
M Cargo.lock +115 -154
@@ -14,7 +14,7 @@ version = "0.5.2"
14 14 source = "registry+https://github.com/rust-lang/crates.io-index"
15 15 checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
16 16 dependencies = [
17 - "crypto-common",
17 + "crypto-common 0.1.7",
18 18 "generic-array",
19 19 ]
20 20
@@ -30,18 +30,6 @@ dependencies = [
30 30 ]
31 31
32 32 [[package]]
33 - name = "ahash"
34 - version = "0.8.12"
35 - source = "registry+https://github.com/rust-lang/crates.io-index"
36 - checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
37 - dependencies = [
38 - "cfg-if",
39 - "once_cell",
40 - "version_check",
41 - "zerocopy",
42 - ]
43 -
44 - [[package]]
45 33 name = "aho-corasick"
46 34 version = "1.1.4"
47 35 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -523,7 +511,7 @@ version = "0.10.6"
523 511 source = "registry+https://github.com/rust-lang/crates.io-index"
524 512 checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
525 513 dependencies = [
526 - "digest",
514 + "digest 0.10.7",
527 515 ]
528 516
529 517 [[package]]
@@ -536,6 +524,15 @@ dependencies = [
536 524 ]
537 525
538 526 [[package]]
527 + name = "block-buffer"
528 + version = "0.12.1"
529 + source = "registry+https://github.com/rust-lang/crates.io-index"
530 + checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
531 + dependencies = [
532 + "hybrid-array",
533 + ]
534 +
535 + [[package]]
539 536 name = "block-padding"
540 537 version = "0.3.3"
541 538 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -837,7 +834,7 @@ version = "0.4.4"
837 834 source = "registry+https://github.com/rust-lang/crates.io-index"
838 835 checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
839 836 dependencies = [
840 - "crypto-common",
837 + "crypto-common 0.1.7",
841 838 "inout",
842 839 "zeroize",
843 840 ]
@@ -852,6 +849,12 @@ dependencies = [
852 849 ]
853 850
854 851 [[package]]
852 + name = "cmov"
853 + version = "0.5.4"
854 + source = "registry+https://github.com/rust-lang/crates.io-index"
855 + checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
856 +
857 + [[package]]
855 858 name = "combine"
856 859 version = "4.6.7"
857 860 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -901,12 +904,6 @@ dependencies = [
901 904 ]
902 905
903 906 [[package]]
904 - name = "const-oid"
905 - version = "0.9.6"
906 - source = "registry+https://github.com/rust-lang/crates.io-index"
907 - checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
908 -
909 - [[package]]
910 907 name = "convert_case"
911 908 version = "0.4.0"
912 909 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1092,6 +1089,15 @@ dependencies = [
1092 1089 ]
1093 1090
1094 1091 [[package]]
1092 + name = "crypto-common"
1093 + version = "0.2.2"
1094 + source = "registry+https://github.com/rust-lang/crates.io-index"
1095 + checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
1096 + dependencies = [
1097 + "hybrid-array",
1098 + ]
1099 +
1100 + [[package]]
1095 1101 name = "csscolorparser"
1096 1102 version = "0.6.2"
1097 1103 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1184,6 +1190,15 @@ dependencies = [
1184 1190 ]
1185 1191
1186 1192 [[package]]
1193 + name = "ctutils"
1194 + version = "0.4.2"
1195 + source = "registry+https://github.com/rust-lang/crates.io-index"
1196 + checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
1197 + dependencies = [
1198 + "cmov",
1199 + ]
1200 +
1201 + [[package]]
1187 1202 name = "darling"
1188 1203 version = "0.23.0"
1189 1204 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1230,17 +1245,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1230 1245 checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4"
1231 1246
1232 1247 [[package]]
1233 - name = "der"
1234 - version = "0.7.10"
1235 - source = "registry+https://github.com/rust-lang/crates.io-index"
1236 - checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
1237 - dependencies = [
1238 - "const-oid",
1239 - "pem-rfc7468",
1240 - "zeroize",
1241 - ]
1242 -
1243 - [[package]]
1244 1248 name = "deranged"
1245 1249 version = "0.5.8"
1246 1250 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1302,13 +1306,23 @@ version = "0.10.7"
1302 1306 source = "registry+https://github.com/rust-lang/crates.io-index"
1303 1307 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
1304 1308 dependencies = [
1305 - "block-buffer",
1306 - "const-oid",
1307 - "crypto-common",
1309 + "block-buffer 0.10.4",
1310 + "crypto-common 0.1.7",
1308 1311 "subtle",
1309 1312 ]
1310 1313
1311 1314 [[package]]
1315 + name = "digest"
1316 + version = "0.11.3"
1317 + source = "registry+https://github.com/rust-lang/crates.io-index"
1318 + checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
1319 + dependencies = [
1320 + "block-buffer 0.12.1",
1321 + "crypto-common 0.2.2",
1322 + "ctutils",
1323 + ]
1324 +
1325 + [[package]]
1312 1326 name = "dirs"
1313 1327 version = "6.0.0"
1314 1328 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1566,13 +1580,12 @@ dependencies = [
1566 1580
1567 1581 [[package]]
1568 1582 name = "etcetera"
1569 - version = "0.8.0"
1583 + version = "0.11.0"
1570 1584 source = "registry+https://github.com/rust-lang/crates.io-index"
1571 - checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
1585 + checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
1572 1586 dependencies = [
1573 1587 "cfg-if",
1574 - "home",
1575 - "windows-sys 0.48.0",
1588 + "windows-sys 0.61.2",
1576 1589 ]
1577 1590
1578 1591 [[package]]
@@ -1716,9 +1729,9 @@ dependencies = [
1716 1729
1717 1730 [[package]]
1718 1731 name = "flume"
1719 - version = "0.11.1"
1732 + version = "0.12.0"
1720 1733 source = "registry+https://github.com/rust-lang/crates.io-index"
1721 - checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
1734 + checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
1722 1735 dependencies = [
1723 1736 "futures-core",
1724 1737 "futures-sink",
@@ -2233,7 +2246,7 @@ dependencies = [
2233 2246 "chrono-tz",
2234 2247 "serde",
2235 2248 "serde_json",
2236 - "sha2",
2249 + "sha2 0.10.9",
2237 2250 "sqlx",
2238 2251 "strum",
2239 2252 "strum_macros",
@@ -2290,7 +2303,7 @@ dependencies = [
2290 2303 "rfd",
2291 2304 "serde",
2292 2305 "serde_json",
2293 - "sha2",
2306 + "sha2 0.10.9",
2294 2307 "sqlx",
2295 2308 "synckit-client",
2296 2309 "tauri",
@@ -2407,21 +2420,10 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
2407 2420
2408 2421 [[package]]
2409 2422 name = "hashbrown"
2410 - version = "0.14.5"
2411 - source = "registry+https://github.com/rust-lang/crates.io-index"
2412 - checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
2413 - dependencies = [
2414 - "ahash",
2415 - ]
2416 -
2417 - [[package]]
2418 - name = "hashbrown"
2419 2423 version = "0.15.5"
2420 2424 source = "registry+https://github.com/rust-lang/crates.io-index"
2421 2425 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
2422 2426 dependencies = [
2423 - "allocator-api2",
2424 - "equivalent",
2425 2427 "foldhash 0.1.5",
2426 2428 ]
2427 2429
@@ -2449,20 +2451,11 @@ dependencies = [
2449 2451
2450 2452 [[package]]
2451 2453 name = "hashlink"
2452 - version = "0.9.1"
2453 - source = "registry+https://github.com/rust-lang/crates.io-index"
2454 - checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
2455 - dependencies = [
2456 - "hashbrown 0.14.5",
2457 - ]
2458 -
2459 - [[package]]
2460 - name = "hashlink"
2461 - version = "0.10.0"
2454 + version = "0.11.1"
2462 2455 source = "registry+https://github.com/rust-lang/crates.io-index"
2463 - checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
2456 + checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f"
2464 2457 dependencies = [
2465 - "hashbrown 0.15.5",
2458 + "hashbrown 0.16.1",
2466 2459 ]
2467 2460
2468 2461 [[package]]
@@ -2495,7 +2488,16 @@ version = "0.12.4"
2495 2488 source = "registry+https://github.com/rust-lang/crates.io-index"
2496 2489 checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
2497 2490 dependencies = [
2498 - "hmac",
2491 + "hmac 0.12.1",
2492 + ]
2493 +
2494 + [[package]]
2495 + name = "hkdf"
2496 + version = "0.13.0"
2497 + source = "registry+https://github.com/rust-lang/crates.io-index"
2498 + checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
2499 + dependencies = [
2500 + "hmac 0.13.0",
2499 2501 ]
2500 2502
2501 2503 [[package]]
@@ -2504,16 +2506,16 @@ version = "0.12.1"
2504 2506 source = "registry+https://github.com/rust-lang/crates.io-index"
2505 2507 checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
2506 2508 dependencies = [
2507 - "digest",
2509 + "digest 0.10.7",
2508 2510 ]
2509 2511
2510 2512 [[package]]
2511 - name = "home"
2512 - version = "0.5.12"
2513 + name = "hmac"
2514 + version = "0.13.0"
2513 2515 source = "registry+https://github.com/rust-lang/crates.io-index"
2514 - checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
2516 + checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
2515 2517 dependencies = [
2516 - "windows-sys 0.61.2",
2518 + "digest 0.11.3",
2517 2519 ]
2518 2520
2519 2521 [[package]]
@@ -2594,6 +2596,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2594 2596 checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
2595 2597
2596 2598 [[package]]
2599 + name = "hybrid-array"
2600 + version = "0.4.13"
2601 + source = "registry+https://github.com/rust-lang/crates.io-index"
2602 + checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
2603 + dependencies = [
2604 + "typenum",
2605 + ]
2606 +
2607 + [[package]]
2597 2608 name = "hyper"
2598 2609 version = "1.8.1"
2599 2610 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3241,9 +3252,6 @@ name = "lazy_static"
3241 3252 version = "1.5.0"
3242 3253 source = "registry+https://github.com/rust-lang/crates.io-index"
3243 3254 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
3244 - dependencies = [
3245 - "spin",
3246 - ]
3247 3255
3248 3256 [[package]]
3249 3257 name = "leb128fmt"
@@ -3337,9 +3345,9 @@ dependencies = [
3337 3345
3338 3346 [[package]]
3339 3347 name = "libsqlite3-sys"
3340 - version = "0.30.1"
3348 + version = "0.37.0"
3341 3349 source = "registry+https://github.com/rust-lang/crates.io-index"
3342 - checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
3350 + checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1"
3343 3351 dependencies = [
3344 3352 "cc",
3345 3353 "pkg-config",
@@ -3529,12 +3537,12 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
3529 3537
3530 3538 [[package]]
3531 3539 name = "md-5"
3532 - version = "0.10.6"
3540 + version = "0.11.0"
3533 3541 source = "registry+https://github.com/rust-lang/crates.io-index"
3534 - checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
3542 + checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98"
3535 3543 dependencies = [
3536 3544 "cfg-if",
3537 - "digest",
3545 + "digest 0.11.3",
3538 3546 ]
3539 3547
3540 3548 [[package]]
@@ -3829,22 +3837,6 @@ dependencies = [
3829 3837 ]
3830 3838
3831 3839 [[package]]
3832 - name = "num-bigint-dig"
3833 - version = "0.8.6"
3834 - source = "registry+https://github.com/rust-lang/crates.io-index"
3835 - checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
3836 - dependencies = [
3837 - "lazy_static",
3838 - "libm",
3839 - "num-integer",
3840 - "num-iter",
3841 - "num-traits",
3842 - "rand 0.8.5",
3843 - "smallvec",
3844 - "zeroize",
3845 - ]
3846 -
3847 - [[package]]
3848 3840 name = "num-complex"
3849 3841 version = "0.4.6"
3850 3842 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3908,7 +3900,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3908 3900 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
3909 3901 dependencies = [
3910 3902 "autocfg",
3911 - "libm",
3912 3903 ]
3913 3904
3914 3905 [[package]]
@@ -4291,15 +4282,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
4291 4282 checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
4292 4283
4293 4284 [[package]]
4294 - name = "pem-rfc7468"
4295 - version = "0.7.0"
4296 - source = "registry+https://github.com/rust-lang/crates.io-index"
4297 - checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
4298 - dependencies = [
4299 - "base64ct",
4300 - ]
4301 -
4302 - [[package]]
4303 4285 name = "percent-encoding"
4304 4286 version = "2.3.2"
4305 4287 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4596,27 +4578,6 @@ dependencies = [
4596 4578 ]
4597 4579
4598 4580 [[package]]
4599 - name = "pkcs1"
4600 - version = "0.7.5"
4601 - source = "registry+https://github.com/rust-lang/crates.io-index"
4602 - checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
4603 - dependencies = [
4604 - "der",
4605 - "pkcs8",
4606 - "spki",
4607 - ]
4608 -
4609 - [[package]]
4610 - name = "pkcs8"
4611 - version = "0.10.2"
4612 - source = "registry+https://github.com/rust-lang/crates.io-index"
4613 - checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
4614 - dependencies = [
4615 - "der",
4616 - "spki",
4617 - ]
4618 -
4619 - [[package]]
4620 4581 name = "pkg-config"
4621 4582 version = "0.3.32"
4622 4583 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5350,37 +5311,28 @@ dependencies = [
5350 5311 ]
5351 5312
5352 5313 [[package]]
5353 - name = "rsa"
5354 - version = "0.9.10"
5314 + name = "rsqlite-vfs"
5315 + version = "0.1.1"
5355 5316 source = "registry+https://github.com/rust-lang/crates.io-index"
5356 - checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
5317 + checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
5357 5318 dependencies = [
5358 - "const-oid",
5359 - "digest",
5360 - "num-bigint-dig",
5361 - "num-integer",
5362 - "num-traits",
5363 - "pkcs1",
5364 - "pkcs8",
5365 - "rand_core 0.6.4",
5366 - "signature",
5367 - "spki",
5368 - "subtle",
5369 - "zeroize",
5319 + "hashbrown 0.16.1",
5320 + "thiserror 2.0.18",
5370 5321 ]
5371 5322
5372 5323 [[package]]
5373 5324 name = "rusqlite"
5374 - version = "0.32.1"
5325 + version = "0.39.0"
5375 5326 source = "registry+https://github.com/rust-lang/crates.io-index"
5376 - checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
5327 + checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e"
5377 5328 dependencies = [
5378 5329 "bitflags 2.13.1",
5379 5330 "fallible-iterator",
5380 5331 "fallible-streaming-iterator",
5381 - "hashlink 0.9.1",
5332 + "hashlink",
5382 5333 "libsqlite3-sys",
5383 5334 "smallvec",
5335 + "sqlite-wasm-rs",
5384 5336 ]
5385 5337
5386 5338 [[package]]
@@ -5600,11 +5552,11 @@ dependencies = [
5600 5552 "futures-util",
5601 5553 "generic-array",
5602 5554 "getrandom 0.2.17",
5603 - "hkdf",
5555 + "hkdf 0.12.4",
5604 5556 "num",
5605 5557 "once_cell",
5606 5558 "serde",
5607 - "sha2",
5559 + "sha2 0.10.9",
5608 5560 "zbus",
5609 5561 ]
5610 5562
@@ -5876,13 +5828,13 @@ dependencies = [
5876 5828
5877 5829 [[package]]
5878 5830 name = "sha1"
5879 - version = "0.10.6"
5831 + version = "0.11.0"
5880 5832 source = "registry+https://github.com/rust-lang/crates.io-index"
5881 - checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
5833 + checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
5882 5834 dependencies = [
5883 5835 "cfg-if",
5884 - "cpufeatures 0.2.17",
5885 - "digest",
5836 + "cpufeatures 0.3.0",
5837 + "digest 0.11.3",
5886 5838 ]
5887 5839
5888 5840 [[package]]
@@ -5899,7 +5851,18 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
5899 5851 dependencies = [
5900 5852 "cfg-if",
5901 5853 "cpufeatures 0.2.17",
5902 - "digest",
5854 + "digest 0.10.7",
5855 + ]
5856 +
5857 + [[package]]
5858 + name = "sha2"
5859 + version = "0.11.0"
5860 + source = "registry+https://github.com/rust-lang/crates.io-index"
5861 + checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
5862 + dependencies = [
5863 + "cfg-if",
5864 + "cpufeatures 0.3.0",
5865 + "digest 0.11.3",
5903 5866 ]
5904 5867
5905 5868 [[package]]
@@ -5949,16 +5912,6 @@ dependencies = [
5949 5912 ]
5950 5913
5951 5914 [[package]]
5952 - name = "signature"
5953 - version = "2.2.0"
Lines truncated
M Cargo.toml +1 -1
@@ -28,7 +28,7 @@ thiserror = "2"
28 28 tokio = { version = "1.49", features = ["rt-multi-thread", "macros"] }
29 29
30 30 # Database - SQLx
31 - sqlx = { version = "0.8", features = ["runtime-tokio"] }
31 + sqlx = { version = "0.9", features = ["runtime-tokio"] }
32 32
33 33 # Authentication
34 34 argon2 = "0.5"
@@ -88,7 +88,7 @@ macro_rules! define_uuid_id {
88 88 impl<'q> sqlx::Encode<'q, sqlx::Sqlite> for $name {
89 89 fn encode_by_ref(
90 90 &self,
91 - buf: &mut <sqlx::Sqlite as sqlx::Database>::ArgumentBuffer<'q>,
91 + buf: &mut <sqlx::Sqlite as sqlx::Database>::ArgumentBuffer,
92 92 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
93 93 <Uuid as sqlx::Encode<'q, sqlx::Sqlite>>::encode_by_ref(&self.0, buf)
94 94 }
@@ -52,7 +52,7 @@ pub(crate) async fn get_annotations_for_tasks(
52 52 bind_placeholders(task_ids.len())
53 53 );
54 54
55 - let mut q = sqlx::query_as::<_, AnnotationRow>(&query);
55 + let mut q = sqlx::query_as::<_, AnnotationRow>(sqlx::AssertSqlSafe(query.as_str()));
56 56 for id in task_ids {
57 57 q = q.bind(id);
58 58 }
@@ -95,7 +95,7 @@ impl AttachmentRepository for SqliteAttachmentRepository {
95 95 "SELECT {} FROM attachments WHERE task_id = ? AND user_id = ? ORDER BY created_at ASC",
96 96 SELECT_COLS
97 97 );
98 - let rows = sqlx::query_as::<_, AttachmentRow>(&sql)
98 + let rows = sqlx::query_as::<_, AttachmentRow>(sqlx::AssertSqlSafe(sql.as_str()))
99 99 .bind(task_id.to_string())
100 100 .bind(user_id.to_string())
101 101 .fetch_all(&self.pool)
@@ -111,7 +111,7 @@ impl AttachmentRepository for SqliteAttachmentRepository {
111 111 "SELECT {} FROM attachments WHERE project_id = ? AND user_id = ? ORDER BY created_at ASC",
112 112 SELECT_COLS
113 113 );
114 - let rows = sqlx::query_as::<_, AttachmentRow>(&sql)
114 + let rows = sqlx::query_as::<_, AttachmentRow>(sqlx::AssertSqlSafe(sql.as_str()))
115 115 .bind(project_id.to_string())
116 116 .bind(user_id.to_string())
117 117 .fetch_all(&self.pool)
@@ -127,7 +127,7 @@ impl AttachmentRepository for SqliteAttachmentRepository {
127 127 "SELECT {} FROM attachments WHERE id = ? AND user_id = ?",
128 128 SELECT_COLS
129 129 );
130 - let row = sqlx::query_as::<_, AttachmentRow>(&sql)
130 + let row = sqlx::query_as::<_, AttachmentRow>(sqlx::AssertSqlSafe(sql.as_str()))
131 131 .bind(id.to_string())
132 132 .bind(user_id.to_string())
133 133 .fetch_optional(&self.pool)
@@ -155,7 +155,7 @@ impl AttachmentRepository for SqliteAttachmentRepository {
155 155 "SELECT {} FROM attachments WHERE blob_hash = ? AND user_id = ? ORDER BY created_at ASC",
156 156 SELECT_COLS
157 157 );
158 - let rows = sqlx::query_as::<_, AttachmentRow>(&sql)
158 + let rows = sqlx::query_as::<_, AttachmentRow>(sqlx::AssertSqlSafe(sql.as_str()))
159 159 .bind(blob_hash)
160 160 .bind(user_id.to_string())
161 161 .fetch_all(&self.pool)
@@ -184,7 +184,7 @@ impl AttachmentRepository for SqliteAttachmentRepository {
184 184 "SELECT {} FROM attachments WHERE user_id = ? ORDER BY created_at ASC",
185 185 SELECT_COLS
186 186 );
187 - let rows = sqlx::query_as::<_, AttachmentRow>(&sql)
187 + let rows = sqlx::query_as::<_, AttachmentRow>(sqlx::AssertSqlSafe(sql.as_str()))
188 188 .bind(user_id.to_string())
189 189 .fetch_all(&self.pool)
190 190 .await
@@ -180,7 +180,7 @@ impl SqliteContactRepository {
180 180 placeholders
181 181 );
182 182
183 - let mut query = sqlx::query_as::<_, ContactEmailRow>(&sql);
183 + let mut query = sqlx::query_as::<_, ContactEmailRow>(sqlx::AssertSqlSafe(sql.as_str()));
184 184 for id in ids {
185 185 query = query.bind(id);
186 186 }
@@ -210,7 +210,7 @@ impl SqliteContactRepository {
210 210 placeholders
211 211 );
212 212
213 - let mut query = sqlx::query_as::<_, ContactPhoneRow>(&sql);
213 + let mut query = sqlx::query_as::<_, ContactPhoneRow>(sqlx::AssertSqlSafe(sql.as_str()));
214 214 for id in ids {
215 215 query = query.bind(id);
216 216 }
@@ -240,7 +240,7 @@ impl SqliteContactRepository {
240 240 placeholders
241 241 );
242 242
243 - let mut query = sqlx::query_as::<_, SocialHandleRow>(&sql);
243 + let mut query = sqlx::query_as::<_, SocialHandleRow>(sqlx::AssertSqlSafe(sql.as_str()));
244 244 for id in ids {
245 245 query = query.bind(id);
246 246 }
@@ -270,7 +270,7 @@ impl SqliteContactRepository {
270 270 placeholders
271 271 );
272 272
273 - let mut query = sqlx::query_as::<_, CustomFieldRow>(&sql);
273 + let mut query = sqlx::query_as::<_, CustomFieldRow>(sqlx::AssertSqlSafe(sql.as_str()));
274 274 for id in ids {
275 275 query = query.bind(id);
276 276 }
@@ -498,7 +498,7 @@ impl ContactRepository for SqliteContactRepository {
498 498 let user_id_str = user_id.to_string();
499 499 let placeholders = bind_placeholders(ids.len());
500 500 let sql = format!("DELETE FROM contacts WHERE user_id = ? AND id IN ({placeholders})");
501 - let mut query = sqlx::query(&sql).bind(&user_id_str);
501 + let mut query = sqlx::query(sqlx::AssertSqlSafe(sql.as_str())).bind(&user_id_str);
502 502 for id in ids {
503 503 query = query.bind(id.to_string());
504 504 }
@@ -526,7 +526,7 @@ impl ContactRepository for SqliteContactRepository {
526 526 WHERE user_id = ? AND id IN ({placeholders})
527 527 AND (tags IS NULL OR tags NOT LIKE ? ESCAPE '\')"#,
528 528 );
529 - let mut query = sqlx::query(&sql)
529 + let mut query = sqlx::query(sqlx::AssertSqlSafe(sql.as_str()))
530 530 .bind(tag)
531 531 .bind(tag)
532 532 .bind(&user_id_str);
@@ -596,7 +596,7 @@ impl ContactRepository for SqliteContactRepository {
596 596 conditions.join(" AND ")
597 597 );
598 598
599 - let mut query = sqlx::query_as::<_, ContactRow>(&sql);
599 + let mut query = sqlx::query_as::<_, ContactRow>(sqlx::AssertSqlSafe(sql.as_str()));
600 600 for bind in &binds {
601 601 query = query.bind(bind);
602 602 }
@@ -617,7 +617,7 @@ impl ContactRepository for SqliteContactRepository {
617 617 ORDER BY c.display_name ASC, ce.is_primary DESC",
618 618 implicit_filter
619 619 );
620 - let rows: Vec<(String, String, i64)> = sqlx::query_as(&sql)
620 + let rows: Vec<(String, String, i64)> = sqlx::query_as(sqlx::AssertSqlSafe(sql.as_str()))
621 621 .bind(user_id.to_string())
622 622 .fetch_all(&self.pool)
623 623 .await
@@ -670,7 +670,7 @@ impl ContactRepository for SqliteContactRepository {
670 670 placeholders
671 671 );
672 672
673 - let mut q = sqlx::query_as::<_, (String,)>(&query).bind(user_id.to_string());
673 + let mut q = sqlx::query_as::<_, (String,)>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string());
674 674 for addr in addresses {
675 675 q = q.bind(addr.to_lowercase());
676 676 }
@@ -96,7 +96,7 @@ impl EmailAccountRepository for SqliteEmailAccountRepository {
96 96 #[tracing::instrument(skip_all)]
97 97 async fn list_by_user(&self, user_id: UserId) -> Result<Vec<EmailAccount>> {
98 98 let query = format!("{} WHERE user_id = ? ORDER BY account_name ASC", Self::SELECT_ALL);
99 - let rows = sqlx::query_as::<_, EmailAccountRow>(&query)
99 + let rows = sqlx::query_as::<_, EmailAccountRow>(sqlx::AssertSqlSafe(query.as_str()))
100 100 .bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
101 101 rows.into_iter().map(EmailAccount::try_from).collect()
102 102 }
@@ -104,7 +104,7 @@ impl EmailAccountRepository for SqliteEmailAccountRepository {
104 104 #[tracing::instrument(skip_all)]
105 105 async fn get_by_id(&self, id: EmailAccountId, user_id: UserId) -> Result<Option<EmailAccount>> {
106 106 let query = format!("{} WHERE id = ? AND user_id = ?", Self::SELECT_ALL);
107 - let row = sqlx::query_as::<_, EmailAccountRow>(&query)
107 + let row = sqlx::query_as::<_, EmailAccountRow>(sqlx::AssertSqlSafe(query.as_str()))
108 108 .bind(id.to_string()).bind(user_id.to_string()).fetch_optional(&self.pool).await.map_err(CoreError::database)?;
109 109 row.map(EmailAccount::try_from).transpose()
110 110 }
@@ -333,7 +333,7 @@ impl EmailAccountRepository for SqliteEmailAccountRepository {
333 333 OR datetime(last_sync_at, '+' || sync_interval_minutes || ' minutes') <= datetime('now'))",
334 334 Self::SELECT_ALL
335 335 );
336 - let rows = sqlx::query_as::<_, EmailAccountRow>(&query)
336 + let rows = sqlx::query_as::<_, EmailAccountRow>(sqlx::AssertSqlSafe(query.as_str()))
337 337 .bind(user_id.to_string())
338 338 .fetch_all(&self.pool).await.map_err(CoreError::database)?;
339 339 rows.into_iter().map(EmailAccount::try_from).collect()
@@ -136,7 +136,7 @@ impl EmailRepository for SqliteEmailRepository {
136 136 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.is_draft = 0 {} ORDER BY e.received_at DESC",
137 137 EMAIL_SELECT_COLUMNS, archived_filter
138 138 );
139 - let rows = sqlx::query_as::<_, EmailRow>(&query).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
139 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
140 140 rows.into_iter().map(Email::try_from).collect()
141 141 }
142 142
@@ -147,7 +147,7 @@ impl EmailRepository for SqliteEmailRepository {
147 147 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.is_draft = 0 {} ORDER BY e.received_at DESC LIMIT {}",
148 148 EMAIL_LIST_COLUMNS, archived_filter, EMAIL_LIST_CAP
149 149 );
150 - let rows = sqlx::query_as::<_, EmailRow>(&query).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
150 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
151 151 rows.into_iter().map(Email::try_from).collect()
152 152 }
153 153
@@ -169,7 +169,7 @@ impl EmailRepository for SqliteEmailRepository {
169 169 "SELECT COUNT(DISTINCT COALESCE(e.thread_id, e.id)) FROM emails e WHERE e.user_id = ? AND e.is_draft = 0 {} {} {}",
170 170 archived_filter, folder_filter, label_filter
171 171 );
172 - let mut count_q = sqlx::query_as::<_, (i64,)>(&count_sql).bind(&uid);
172 + let mut count_q = sqlx::query_as::<_, (i64,)>(sqlx::AssertSqlSafe(count_sql.as_str())).bind(&uid);
173 173 if let Some(f) = folder { count_q = count_q.bind(f); }
174 174 if let Some(l) = label { count_q = count_q.bind(l); }
175 175 let (total,) = count_q.fetch_one(&self.pool).await.map_err(CoreError::database)?;
@@ -223,7 +223,7 @@ impl EmailRepository for SqliteEmailRepository {
223 223 archived_filter, folder_filter, label_filter,
224 224 );
225 225
226 - let mut summary_q = sqlx::query_as::<_, ThreadSummary>(&summary_sql).bind(&uid);
226 + let mut summary_q = sqlx::query_as::<_, ThreadSummary>(sqlx::AssertSqlSafe(summary_sql.as_str())).bind(&uid);
227 227 if let Some(f) = folder { summary_q = summary_q.bind(f); }
228 228 if let Some(l) = label { summary_q = summary_q.bind(l); }
229 229 let summaries = summary_q
@@ -245,7 +245,7 @@ impl EmailRepository for SqliteEmailRepository {
245 245 EMAIL_SELECT_COLUMNS, placeholders
246 246 );
247 247
248 - let mut q = sqlx::query_as::<_, EmailRow>(&emails_sql).bind(&uid);
248 + let mut q = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(emails_sql.as_str())).bind(&uid);
249 249 for id in &email_ids {
250 250 q = q.bind(id);
251 251 }
@@ -288,7 +288,7 @@ impl EmailRepository for SqliteEmailRepository {
288 288 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.project_id = ? ORDER BY e.received_at DESC LIMIT {}",
289 289 EMAIL_LIST_COLUMNS, EMAIL_LIST_CAP
290 290 );
291 - let rows = sqlx::query_as::<_, EmailRow>(&query).bind(user_id.to_string()).bind(user_id.to_string()).bind(project_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
291 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string()).bind(user_id.to_string()).bind(project_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
292 292 rows.into_iter().map(Email::try_from).collect()
293 293 }
294 294
@@ -302,7 +302,7 @@ impl EmailRepository for SqliteEmailRepository {
302 302 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND (LOWER(e.from_address) IN ({placeholders}) OR LOWER(e.to_address) IN ({placeholders})) ORDER BY e.received_at DESC LIMIT 200",
303 303 EMAIL_SELECT_COLUMNS
304 304 );
305 - let mut q = sqlx::query_as::<_, EmailRow>(&query)
305 + let mut q = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
306 306 .bind(user_id.to_string())
307 307 .bind(user_id.to_string());
308 308 // Bind addresses twice (once for from_address IN, once for to_address IN)
@@ -325,7 +325,7 @@ impl EmailRepository for SqliteEmailRepository {
325 325 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.project_id IS NULL AND e.is_archived = 0 ORDER BY e.received_at DESC LIMIT {}",
326 326 EMAIL_LIST_COLUMNS, EMAIL_LIST_CAP
327 327 );
328 - let rows = sqlx::query_as::<_, EmailRow>(&query).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
328 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string()).bind(user_id.to_string()).fetch_all(&self.pool).await.map_err(CoreError::database)?;
329 329 rows.into_iter().map(Email::try_from).collect()
330 330 }
331 331
@@ -335,7 +335,7 @@ impl EmailRepository for SqliteEmailRepository {
335 335 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.id = ? AND e.user_id = ?",
336 336 EMAIL_SELECT_COLUMNS
337 337 );
338 - let row = sqlx::query_as::<_, EmailRow>(&query).bind(user_id.to_string()).bind(id.to_string()).bind(user_id.to_string()).fetch_optional(&self.pool).await.map_err(CoreError::database)?;
338 + let row = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string()).bind(id.to_string()).bind(user_id.to_string()).fetch_optional(&self.pool).await.map_err(CoreError::database)?;
339 339 row.map(Email::try_from).transpose()
340 340 }
341 341
@@ -522,7 +522,7 @@ impl EmailRepository for SqliteEmailRepository {
522 522 placeholders
523 523 );
524 524
525 - let mut q = sqlx::query_as::<_, (String,)>(&query).bind(user_id.to_string());
525 + let mut q = sqlx::query_as::<_, (String,)>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string());
526 526 for msg_id in message_ids {
527 527 q = q.bind(*msg_id);
528 528 }
@@ -545,7 +545,7 @@ impl EmailRepository for SqliteEmailRepository {
545 545 placeholders
546 546 );
547 547
548 - let mut q = sqlx::query_as::<_, (String,)>(&query).bind(user_id.to_string());
548 + let mut q = sqlx::query_as::<_, (String,)>(sqlx::AssertSqlSafe(query.as_str())).bind(user_id.to_string());
549 549 for addr in addresses {
550 550 q = q.bind(addr.to_lowercase());
551 551 }
@@ -586,7 +586,7 @@ impl EmailRepository for SqliteEmailRepository {
586 586 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.snoozed_until IS NOT NULL AND datetime(e.snoozed_until) > datetime('now') ORDER BY e.snoozed_until ASC",
587 587 EMAIL_SELECT_COLUMNS
588 588 );
589 - let rows = sqlx::query_as::<_, EmailRow>(&query)
589 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
590 590 .bind(user_id.to_string())
591 591 .bind(user_id.to_string())
592 592 .fetch_all(&self.pool)
@@ -634,7 +634,7 @@ impl EmailRepository for SqliteEmailRepository {
634 634 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.waiting_for_response = 1 ORDER BY e.expected_response_date ASC",
635 635 EMAIL_SELECT_COLUMNS
636 636 );
637 - let rows = sqlx::query_as::<_, EmailRow>(&query)
637 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
638 638 .bind(user_id.to_string())
639 639 .bind(user_id.to_string())
640 640 .fetch_all(&self.pool)
@@ -649,7 +649,7 @@ impl EmailRepository for SqliteEmailRepository {
649 649 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.thread_id = ? ORDER BY e.received_at ASC",
650 650 EMAIL_SELECT_COLUMNS
651 651 );
652 - let rows = sqlx::query_as::<_, EmailRow>(&query)
652 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
653 653 .bind(user_id.to_string())
654 654 .bind(user_id.to_string())
655 655 .bind(thread_id)
@@ -665,7 +665,7 @@ impl EmailRepository for SqliteEmailRepository {
665 665 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.is_draft = 1 ORDER BY e.received_at DESC",
666 666 EMAIL_SELECT_COLUMNS
667 667 );
668 - let rows = sqlx::query_as::<_, EmailRow>(&query)
668 + let rows = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
669 669 .bind(user_id.to_string())
670 670 .bind(user_id.to_string())
671 671 .fetch_all(&self.pool)
@@ -721,7 +721,7 @@ impl EmailRepository for SqliteEmailRepository {
721 721 "SELECT {} FROM emails e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? WHERE e.user_id = ? AND e.message_id = ?",
722 722 EMAIL_SELECT_COLUMNS
723 723 );
724 - let row = sqlx::query_as::<_, EmailRow>(&query)
724 + let row = sqlx::query_as::<_, EmailRow>(sqlx::AssertSqlSafe(query.as_str()))
725 725 .bind(user_id.to_string())
726 726 .bind(user_id.to_string())
727 727 .bind(message_id)
@@ -109,7 +109,7 @@ impl EventRepository for SqliteEventRepository {
109 109 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? ORDER BY e.start_time ASC",
110 110 EVENT_SELECT_COLUMNS
111 111 );
112 - let rows = sqlx::query_as::<_, EventRow>(&query)
112 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
113 113 .bind(user_id.to_string())
114 114 .bind(user_id.to_string())
115 115 .fetch_all(&self.pool)
@@ -124,7 +124,7 @@ impl EventRepository for SqliteEventRepository {
124 124 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.project_id = ? ORDER BY e.start_time ASC",
125 125 EVENT_SELECT_COLUMNS
126 126 );
127 - let rows = sqlx::query_as::<_, EventRow>(&query)
127 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
128 128 .bind(user_id.to_string())
129 129 .bind(user_id.to_string())
130 130 .bind(project_id.to_string())
@@ -140,7 +140,7 @@ impl EventRepository for SqliteEventRepository {
140 140 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.contact_id = ? ORDER BY e.start_time DESC",
141 141 EVENT_SELECT_COLUMNS
142 142 );
143 - let rows = sqlx::query_as::<_, EventRow>(&query)
143 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
144 144 .bind(user_id.to_string())
145 145 .bind(user_id.to_string())
146 146 .bind(contact_id.to_string())
@@ -156,7 +156,7 @@ impl EventRepository for SqliteEventRepository {
156 156 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.id = ? AND e.user_id = ?",
157 157 EVENT_SELECT_COLUMNS
158 158 );
159 - let row = sqlx::query_as::<_, EventRow>(&query)
159 + let row = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
160 160 .bind(user_id.to_string())
161 161 .bind(id.to_string())
162 162 .bind(user_id.to_string())
@@ -321,7 +321,7 @@ impl EventRepository for SqliteEventRepository {
321 321 let user_id_str = user_id.to_string();
322 322 let placeholders = bind_placeholders(ids.len());
323 323 let sql = format!("DELETE FROM events WHERE user_id = ? AND id IN ({placeholders})");
324 - let mut query = sqlx::query(&sql).bind(&user_id_str);
324 + let mut query = sqlx::query(sqlx::AssertSqlSafe(sql.as_str())).bind(&user_id_str);
325 325 for id in ids {
326 326 query = query.bind(id.to_string());
327 327 }
@@ -335,7 +335,7 @@ impl EventRepository for SqliteEventRepository {
335 335 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.start_time >= datetime('now') AND e.start_time <= datetime('now', ? || ' days') ORDER BY e.start_time ASC",
336 336 EVENT_SELECT_COLUMNS
337 337 );
338 - let rows = sqlx::query_as::<_, EventRow>(&query)
338 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
339 339 .bind(user_id.to_string())
340 340 .bind(user_id.to_string())
341 341 .bind(format!("+{}", days))
@@ -351,7 +351,7 @@ impl EventRepository for SqliteEventRepository {
351 351 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.linked_task_id = ?",
352 352 EVENT_SELECT_COLUMNS
353 353 );
354 - let row = sqlx::query_as::<_, EventRow>(&query)
354 + let row = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
355 355 .bind(user_id.to_string())
356 356 .bind(user_id.to_string())
357 357 .bind(task_id.to_string())
@@ -381,7 +381,7 @@ impl EventRepository for SqliteEventRepository {
381 381 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.start_time <= ? AND (e.end_time IS NULL OR e.end_time >= ?) ORDER BY e.start_time ASC",
382 382 EVENT_SELECT_COLUMNS
383 383 );
384 - let rows = sqlx::query_as::<_, EventRow>(&query)
384 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
385 385 .bind(user_id.to_string())
386 386 .bind(user_id.to_string())
387 387 .bind(&date_end)
@@ -400,7 +400,7 @@ impl EventRepository for SqliteEventRepository {
400 400 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.start_time <= ? AND (e.end_time IS NULL OR e.end_time >= ?) ORDER BY e.start_time ASC",
401 401 EVENT_SELECT_COLUMNS
402 402 );
403 - let rows = sqlx::query_as::<_, EventRow>(&query)
403 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
404 404 .bind(user_id.to_string())
405 405 .bind(user_id.to_string())
406 406 .bind(&end_str)
@@ -417,7 +417,7 @@ impl EventRepository for SqliteEventRepository {
417 417 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND (e.recurrence != 'None' OR e.recurrence_rule IS NOT NULL) ORDER BY e.start_time ASC",
418 418 EVENT_SELECT_COLUMNS
419 419 );
420 - let rows = sqlx::query_as::<_, EventRow>(&query)
420 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
421 421 .bind(user_id.to_string())
422 422 .bind(user_id.to_string())
423 423 .fetch_all(&self.pool)
@@ -432,7 +432,7 @@ impl EventRepository for SqliteEventRepository {
432 432 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.external_source = ? AND e.external_id = ?",
433 433 EVENT_SELECT_COLUMNS
434 434 );
435 - let row = sqlx::query_as::<_, EventRow>(&query)
435 + let row = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
436 436 .bind(user_id.to_string())
437 437 .bind(user_id.to_string())
438 438 .bind(source)
@@ -485,7 +485,7 @@ impl EventRepository for SqliteEventRepository {
485 485 "SELECT {} FROM events e LEFT JOIN projects p ON e.project_id = p.id AND p.user_id = ? LEFT JOIN contacts ct ON ct.id = e.contact_id WHERE e.user_id = ? AND e.snoozed_until IS NOT NULL AND e.snoozed_until > datetime('now') ORDER BY e.snoozed_until ASC",
486 486 EVENT_SELECT_COLUMNS
487 487 );
488 - let rows = sqlx::query_as::<_, EventRow>(&query)
488 + let rows = sqlx::query_as::<_, EventRow>(sqlx::AssertSqlSafe(query.as_str()))
489 489 .bind(user_id.to_string())
490 490 .bind(user_id.to_string())
491 491 .fetch_all(&self.pool)
@@ -285,7 +285,7 @@ async fn run_fts_query<R>(
285 285 where
286 286 R: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> + Send + Unpin,
287 287 {
288 - let mut db_query = sqlx::query_as::<_, R>(sql);
288 + let mut db_query = sqlx::query_as::<_, R>(sqlx::AssertSqlSafe(sql));
289 289
290 290 if let Some(term) = search_term {
291 291 db_query = db_query.bind(term).bind(user_id);
@@ -60,7 +60,7 @@ pub(crate) async fn get_tokens_for_tasks(
60 60 bind_placeholders(task_ids.len())
61 61 );
62 62
63 - let mut q = sqlx::query_as::<_, StatusTokenRow>(&query);
63 + let mut q = sqlx::query_as::<_, StatusTokenRow>(sqlx::AssertSqlSafe(query.as_str()));
64 64 for id in task_ids {
65 65 q = q.bind(id);
66 66 }
@@ -55,7 +55,7 @@ pub(crate) async fn get_subtasks_for_tasks(
55 55 bind_placeholders(task_ids.len())
56 56 );
57 57
58 - let mut q = sqlx::query_as::<_, SubtaskRow>(&query);
58 + let mut q = sqlx::query_as::<_, SubtaskRow>(sqlx::AssertSqlSafe(query.as_str()));
59 59 for id in task_ids {
60 60 q = q.bind(id);
61 61 }
@@ -228,7 +228,7 @@ pub(crate) async fn get_task_by_id(pool: &SqlitePool, id: TaskId, user_id: UserI
228 228 "#,
229 229 TASK_SELECT_COLUMNS
230 230 );
231 - let row = sqlx::query_as::<_, TaskRowWithProject>(&sql)
231 + let row = sqlx::query_as::<_, TaskRowWithProject>(sqlx::AssertSqlSafe(sql.as_str()))
232 232 .bind(user_id.to_string())
233 233 .bind(id.to_string())
234 234 .bind(user_id.to_string())
@@ -255,7 +255,7 @@ pub(crate) async fn get_task_by_id(pool: &SqlitePool, id: TaskId, user_id: UserI
255 255 /// Handles the common pattern of: format SQL with TASK_SELECT_COLUMNS,
256 256 /// bind string params in order, fetch rows, convert via rows_to_tasks.
257 257 pub(crate) async fn query_tasks(pool: &SqlitePool, sql: &str, binds: &[String]) -> Result<Vec<Task>> {
258 - let mut query = sqlx::query_as::<_, TaskRowWithProject>(sql);
258 + let mut query = sqlx::query_as::<_, TaskRowWithProject>(sqlx::AssertSqlSafe(sql));
259 259 for b in binds {
260 260 query = query.bind(b);
261 261 }
@@ -357,7 +357,7 @@ impl TaskCrud for SqliteTaskRepository {
357 357
358 358 // First, get total count for pagination
359 359 let count_sql = format!("SELECT COUNT(*) FROM tasks t WHERE {}", where_clause);
360 - let mut count_query = sqlx::query_as::<_, (i64,)>(&count_sql);
360 + let mut count_query = sqlx::query_as::<_, (i64,)>(sqlx::AssertSqlSafe(count_sql.as_str()));
361 361 for value in &bind_values {
362 362 count_query = count_query.bind(value);
363 363 }
@@ -429,7 +429,7 @@ impl TaskCrud for SqliteTaskRepository {
429 429 );
430 430
431 431 // Build query with dynamic bindings
432 - let mut sqlx_query = sqlx::query_as::<_, TaskRowWithProject>(&sql);
432 + let mut sqlx_query = sqlx::query_as::<_, TaskRowWithProject>(sqlx::AssertSqlSafe(sql.as_str()));
433 433
434 434 // Bind user_id for the JOIN
435 435 sqlx_query = sqlx_query.bind(user_id.to_string());
@@ -626,7 +626,7 @@ impl TaskCrud for SqliteTaskRepository {
626 626 let sql = format!(
627 627 "UPDATE tasks SET project_id = ? WHERE user_id = ? AND id IN ({placeholders})"
628 628 );
629 - let mut q = sqlx::query(&sql)
629 + let mut q = sqlx::query(sqlx::AssertSqlSafe(sql.as_str()))
630 630 .bind(project_id.map(|p| p.to_string()))
631 631 .bind(user_id.to_string());
632 632 for id in ids {
@@ -377,7 +377,7 @@ pub(crate) async fn list_available_for_focus(
377 377 );
378 378
379 379 // This query has an extra i64 bind, so we handle it directly
380 - let rows = sqlx::query_as::<_, super::task_repo::TaskRowWithProject>(&sql)
380 + let rows = sqlx::query_as::<_, super::task_repo::TaskRowWithProject>(sqlx::AssertSqlSafe(sql.as_str()))
381 381 .bind(user_id.to_string())
382 382 .bind(user_id.to_string())
383 383 .bind(limit)
@@ -56,7 +56,7 @@ pub(crate) async fn get_active_sessions_for_tasks(
56 56 placeholders
57 57 );
58 58
59 - let mut query = sqlx::query_as::<_, TimeSessionRow>(&sql);
59 + let mut query = sqlx::query_as::<_, TimeSessionRow>(sqlx::AssertSqlSafe(sql.as_str()));
60 60 for id in task_ids {
61 61 query = query.bind(id);
62 62 }
@@ -108,7 +108,7 @@ features = ["image-png", "tray-icon"]
108 108 # Android: bundle SQLite (system SQLite not accessible via NDK)
109 109 # and vendor OpenSSL (native-tls deps need it for cross-compilation)
110 110 [target.'cfg(target_os = "android")'.dependencies]
111 - libsqlite3-sys = { version = "0.30", features = ["bundled"] }
111 + libsqlite3-sys = { version = "0.37", features = ["bundled"] }
112 112 openssl = { version = "0.10", features = ["vendored"] }
113 113
114 114 [dev-dependencies]
@@ -163,7 +163,7 @@ pub(crate) async fn apply_upsert(
163 163 table, col_list, placeholders, conflict
164 164 );
165 165
166 - let mut query = sqlx::query(&sql);
166 + let mut query = sqlx::query(sqlx::AssertSqlSafe(sql.as_str()));
167 167
168 168 for col in &included {
169 169 query = bind_json_value(query, &data[*col]);
@@ -186,7 +186,7 @@ async fn not_null_columns(
186 186 conn: &mut SqliteConnection,
187 187 table: &str,
188 188 ) -> Result<std::collections::HashSet<String>, CoreError> {
189 - let rows = sqlx::query(&format!("PRAGMA table_info({})", table))
189 + let rows = sqlx::query(sqlx::AssertSqlSafe(format!("PRAGMA table_info({})", table)))
190 190 .fetch_all(&mut *conn)
191 191 .await
192 192 .map_err(CoreError::database)?;
@@ -235,7 +235,7 @@ async fn apply_email_account_upsert(
235 235 col_list, placeholders, update_set
236 236 );
237 237
238 - let mut query = sqlx::query(&sql);
238 + let mut query = sqlx::query(sqlx::AssertSqlSafe(sql.as_str()));
239 239
240 240 // Bind the 16 sync columns from data
241 241 for col in cols {
@@ -255,9 +255,9 @@ async fn apply_email_account_upsert(
255 255
256 256 /// Bind a JSON value to a sqlx query.
257 257 pub(crate) fn bind_json_value<'q>(
258 - query: sqlx::query::Query<'q, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'q>>,
258 + query: sqlx::query::Query<'q, sqlx::Sqlite, sqlx::sqlite::SqliteArguments>,
259 259 val: &'q serde_json::Value,
260 - ) -> sqlx::query::Query<'q, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'q>> {
260 + ) -> sqlx::query::Query<'q, sqlx::Sqlite, sqlx::sqlite::SqliteArguments> {
261 261 match val {
262 262 serde_json::Value::String(s) => query.bind(s.as_str()),
263 263 serde_json::Value::Number(n) => {
@@ -287,7 +287,7 @@ pub(crate) async fn apply_delete(conn: &mut SqliteConnection, table: &str, row_i
287 287 }
288 288
289 289 let sql = format!("DELETE FROM {} WHERE id = ?", table);
290 - sqlx::query(&sql)
290 + sqlx::query(sqlx::AssertSqlSafe(sql.as_str()))
291 291 .bind(row_id)
292 292 .execute(&mut *conn)
293 293 .await
@@ -155,7 +155,7 @@ pub(crate) async fn load_committed_hlcs(
155 155 "SELECT table_name, row_id, hlc_wall, hlc_counter, hlc_node \
156 156 FROM sync_committed_hlc WHERE row_id IN ({placeholders})"
157 157 );
158 - let mut query = sqlx::query_as::<_, (String, String, i64, i64, String)>(&sql);
158 + let mut query = sqlx::query_as::<_, (String, String, i64, i64, String)>(sqlx::AssertSqlSafe(sql.as_str()));
159 159 for (_, row_id) in keys {
160 160 query = query.bind(row_id);
161 161 }
@@ -183,7 +183,7 @@ pub async fn create_initial_snapshot(pool: &SqlitePool) -> Result<i64, CoreError
183 183 WHERE NOT EXISTS (SELECT 1 FROM sync_changelog sc WHERE sc.table_name = '{table}' AND sc.row_id = {table}.id)",
184 184 );
185 185
186 - let result = sqlx::query(&sql)
186 + let result = sqlx::query(sqlx::AssertSqlSafe(sql.as_str()))
187 187 .execute(pool)
188 188 .await
189 189 .map_err(CoreError::database)?;
@@ -26,7 +26,7 @@ pub async fn get_sync_states_batch(pool: &SqlitePool, keys: &[&str]) -> Result<H
26 26 let placeholders = keys.iter().map(|_| "?").collect::<Vec<_>>().join(",");
27 27 let query = format!("SELECT key, value FROM sync_state WHERE key IN ({})", placeholders);
28 28
29 - let mut q = sqlx::query_as::<_, (String, String)>(&query);
29 + let mut q = sqlx::query_as::<_, (String, String)>(sqlx::AssertSqlSafe(query.as_str()));
30 30 for key in keys {
31 31 q = q.bind(*key);
32 32 }