Skip to main content

fsqlite/
lib.rs

1//! Public API facade for FrankenSQLite.
2//!
3//! This crate will grow a stable, ergonomic API surface over time. In early
4//! phases it also re-exports selected internal crates for integration tests.
5
6// The re-exported `Connection` futures from `fsqlite-core` are deeply nested
7// (statement dispatch → DML → triggers → nested statement execution); the
8// default limit overflows while type-checking them here too.
9#![recursion_limit = "512"]
10// bd-h9o9r: this crate drives fsqlite-core's deliberately non-`Send`,
11// deeply nested engine futures (the same nesting behind the
12// `recursion_limit` above); `future_not_send` and `large_futures`
13// contradict that design — see fsqlite-core/src/lib.rs for the full
14// rationale, including why boxing was rejected by the perf ledger.
15#![allow(clippy::future_not_send)]
16#![allow(clippy::large_futures)]
17
18pub use fsqlite_core::connection::{
19    Connection, ConnectionEnv, IoPollStrategy, PreparedStatement, Row, RuntimeConfig,
20    RuntimeContext, TraceEvent, TraceMask, init_global_runtime,
21};
22pub use fsqlite_error::FrankenError;
23pub use fsqlite_types::SqliteValue;
24pub use fsqlite_vfs;
25pub use fsqlite_vfs::FileIdentity;
26#[cfg(all(feature = "native", any(unix, windows)))]
27pub use fsqlite_vfs::{
28    DatabaseNamespaceGenerationTransition, NamespaceGenerationTransitionOutcome,
29    begin_database_namespace_generation_transition,
30};
31
32#[cfg(feature = "session")]
33/// Manual session/changeset API facade re-exported from `fsqlite-ext-session`.
34pub mod session {
35    pub use fsqlite_ext_session::{
36        ApplyOutcome, ChangeOp, Changeset, ChangesetRow, ChangesetValue, ConflictAction,
37        ConflictType, Session, SimpleTarget, TableChangeset, TableInfo, changeset_varint_len,
38        extension_name,
39    };
40}
41
42#[cfg(feature = "async-api")]
43pub mod async_api;
44#[cfg(feature = "async-api")]
45pub use async_api::AsyncConnection;
46
47pub mod compat;
48pub mod migrate;
49
50#[cfg(test)]
51#[allow(
52    clippy::too_many_lines,
53    clippy::items_after_statements,
54    clippy::needless_collect,
55    clippy::single_match_else,
56    clippy::branches_sharing_code
57)]
58mod tests {
59    use super::{
60        Connection, ConnectionEnv, FileIdentity, IoPollStrategy, RuntimeConfig, RuntimeContext,
61        init_global_runtime,
62    };
63    use fsqlite_ast::{CreateTableBody, Statement};
64    use fsqlite_error::FrankenError;
65    use fsqlite_parser::parse_first_statement_with_tail;
66    use fsqlite_types::value::SqliteValue;
67    use std::io::{BufRead, BufReader};
68    use std::process::{Command, Stdio};
69    use std::sync::{Arc, mpsc};
70    use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
71
72    const CONCURRENT_STRESS_CHILD_ENV: &str = "FSQLITE_CONCURRENT_STRESS_CHILD";
73    const CONCURRENT_STRESS_RECEIPT_ENV: &str = "FSQLITE_CONCURRENT_STRESS_RECEIPT";
74    const CONCURRENT_STRESS_RECEIPT_PREFIX: &str = "FSQLITE_CONCURRENT_STRESS_COMPLETE:";
75    const CONCURRENT_STRESS_TEST_NAME: &str = "tests::concurrent_writers_stress_conservation";
76    const CONCURRENT_STRESS_CHILD_TIMEOUT: Duration = Duration::from_secs(90);
77    const CONCURRENT_STRESS_STARTUP_TIMEOUT: Duration = Duration::from_secs(10);
78    const CONCURRENT_STRESS_WORKER_TIMEOUT: Duration = Duration::from_secs(60);
79    const CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT: u64 = 512;
80    const CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER: u64 = 2_560;
81    const CONCURRENT_READER_MAX_OPEN_ATTEMPTS: u64 = 4;
82
83    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
84    enum ConcurrentStressStartDecision {
85        Run,
86        Abort,
87    }
88
89    #[derive(Debug)]
90    enum ConcurrentStressStartup {
91        Ready { worker_id: usize },
92        Failed { worker_id: usize, error: String },
93    }
94
95    struct ConcurrentStressStartGate {
96        senders: Vec<mpsc::SyncSender<ConcurrentStressStartDecision>>,
97        armed: bool,
98    }
99
100    impl ConcurrentStressStartGate {
101        fn new(senders: Vec<mpsc::SyncSender<ConcurrentStressStartDecision>>) -> Self {
102            Self {
103                senders,
104                armed: true,
105            }
106        }
107
108        fn release(mut self) -> Result<(), String> {
109            let mut failures = Vec::new();
110            for (worker_id, sender) in self.senders.iter().enumerate() {
111                if sender.send(ConcurrentStressStartDecision::Run).is_err() {
112                    failures.push(worker_id);
113                }
114            }
115            if failures.is_empty() {
116                self.armed = false;
117                Ok(())
118            } else {
119                Err(format!(
120                    "workers disconnected before the run decision: {failures:?}"
121                ))
122            }
123        }
124    }
125
126    impl Drop for ConcurrentStressStartGate {
127        fn drop(&mut self) {
128            if !self.armed {
129                return;
130            }
131            for sender in &self.senders {
132                let _ = sender.try_send(ConcurrentStressStartDecision::Abort);
133            }
134        }
135    }
136
137    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
138    struct ConcurrentStressRetryCounts {
139        busy: u64,
140        busy_recovery: u64,
141        busy_snapshot: u64,
142        database_locked: u64,
143        write_conflict: u64,
144        serialization_failure: u64,
145    }
146
147    impl ConcurrentStressRetryCounts {
148        fn record(&mut self, error: &FrankenError) -> bool {
149            match error {
150                FrankenError::Busy => self.busy += 1,
151                FrankenError::BusyRecovery => self.busy_recovery += 1,
152                FrankenError::BusySnapshot { .. } => self.busy_snapshot += 1,
153                FrankenError::DatabaseLocked { .. } => self.database_locked += 1,
154                FrankenError::WriteConflict { .. } => self.write_conflict += 1,
155                FrankenError::SerializationFailure { .. } => self.serialization_failure += 1,
156                _ => return false,
157            }
158            true
159        }
160
161        const fn total(self) -> u64 {
162            self.busy
163                + self.busy_recovery
164                + self.busy_snapshot
165                + self.database_locked
166                + self.write_conflict
167                + self.serialization_failure
168        }
169    }
170
171    #[derive(Debug)]
172    struct ConcurrentStressTransfer {
173        from_id: i64,
174        to_id: i64,
175        amount: i64,
176        begin_seq: u64,
177        commit_seq: u64,
178    }
179
180    #[derive(Debug)]
181    struct ConcurrentStressStockDiagnostic {
182        row_count: i64,
183        balance_sum: i64,
184        point_count: i64,
185        scan_count: i64,
186        integrity: Vec<String>,
187        balances: Vec<(i64, i64)>,
188    }
189
190    #[derive(Debug)]
191    struct ConcurrentStressWorkerOutcome {
192        worker_id: usize,
193        concurrent_mode_default: bool,
194        commits: u64,
195        attempts: u64,
196        max_attempts_for_commit: u64,
197        retries: ConcurrentStressRetryCounts,
198        elapsed: Duration,
199        failure: Option<String>,
200        committed_transfers: Vec<ConcurrentStressTransfer>,
201    }
202
203    impl ConcurrentStressWorkerOutcome {
204        fn pending(worker_id: usize) -> Self {
205            Self {
206                worker_id,
207                concurrent_mode_default: false,
208                commits: 0,
209                attempts: 0,
210                max_attempts_for_commit: 0,
211                retries: ConcurrentStressRetryCounts::default(),
212                elapsed: Duration::ZERO,
213                failure: Some("worker exited without recording an outcome".to_owned()),
214                committed_transfers: Vec::new(),
215            }
216        }
217    }
218
219    fn supervise_concurrent_writer_stress() -> bool {
220        match (
221            std::env::var_os(CONCURRENT_STRESS_CHILD_ENV),
222            std::env::var_os(CONCURRENT_STRESS_RECEIPT_ENV),
223        ) {
224            (Some(child_token), Some(receipt_token)) if child_token == receipt_token => {
225                return false;
226            }
227            (None, None) => {}
228            _ => panic!("inconsistent inherited concurrent-stress supervision environment"),
229        }
230
231        let receipt_token = format!(
232            "{}-{}",
233            std::process::id(),
234            SystemTime::now()
235                .duration_since(UNIX_EPOCH)
236                .expect("system clock must be after the Unix epoch")
237                .as_nanos()
238        );
239        let expected_receipt = format!("{CONCURRENT_STRESS_RECEIPT_PREFIX}{receipt_token}");
240        let mut child =
241            Command::new(std::env::current_exe().expect("resolve current fsqlite test executable"))
242                .args([
243                    "--exact",
244                    CONCURRENT_STRESS_TEST_NAME,
245                    "--include-ignored",
246                    "--nocapture",
247                ])
248                .env(CONCURRENT_STRESS_CHILD_ENV, &receipt_token)
249                .env(CONCURRENT_STRESS_RECEIPT_ENV, &receipt_token)
250                .stdout(Stdio::piped())
251                .spawn()
252                .expect("spawn supervised concurrent-writer stress child");
253        let child_stdout = child
254            .stdout
255            .take()
256            .expect("capture concurrent-writer stress child stdout");
257        let mut receipt_reader = Some(std::thread::spawn(move || {
258            let mut receipt_found = false;
259            for line in BufReader::new(child_stdout).lines() {
260                if line.expect("read concurrent-writer stress child stdout") == expected_receipt {
261                    receipt_found = true;
262                }
263            }
264            receipt_found
265        }));
266        let deadline = Instant::now() + CONCURRENT_STRESS_CHILD_TIMEOUT;
267
268        loop {
269            match child
270                .try_wait()
271                .expect("poll supervised concurrent-writer stress child")
272            {
273                Some(status) => {
274                    let receipt_found = receipt_reader
275                        .take()
276                        .expect("receipt reader must be present")
277                        .join()
278                        .expect("concurrent-writer stress receipt reader must not panic");
279                    assert!(
280                        status.success(),
281                        "supervised concurrent-writer stress child failed with {status}"
282                    );
283                    assert!(
284                        receipt_found,
285                        "supervised concurrent-writer stress child exited without its completion receipt"
286                    );
287                    return true;
288                }
289                None if Instant::now() >= deadline => {
290                    let _ = child.kill();
291                    child
292                        .wait()
293                        .expect("reap timed-out concurrent-writer stress child");
294                    receipt_reader
295                        .take()
296                        .expect("receipt reader must be present")
297                        .join()
298                        .expect("concurrent-writer stress receipt reader must not panic");
299                    panic!(
300                        "supervised concurrent-writer stress test exceeded {:?}",
301                        CONCURRENT_STRESS_CHILD_TIMEOUT
302                    );
303                }
304                None => std::thread::sleep(Duration::from_millis(50)),
305            }
306        }
307    }
308
309    fn concurrent_stress_attempt_budget_error(
310        attempts_for_commit: u64,
311        total_attempts: u64,
312        elapsed: Duration,
313    ) -> Option<String> {
314        if attempts_for_commit >= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT {
315            Some(format!(
316                "exhausted {CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT} attempts for one commit"
317            ))
318        } else if total_attempts >= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER {
319            Some(format!(
320                "exhausted {CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER} total attempts"
321            ))
322        } else if elapsed >= CONCURRENT_STRESS_WORKER_TIMEOUT {
323            Some(format!(
324                "exceeded worker deadline {:?}",
325                CONCURRENT_STRESS_WORKER_TIMEOUT
326            ))
327        } else {
328            None
329        }
330    }
331
332    fn concurrent_stress_backoff(attempts_for_commit: u64, participant_id: u64) {
333        let exponent = attempts_for_commit.saturating_sub(1).min(5) as u32;
334        let base_millis = 1_u64 << exponent;
335        let jitter_millis = participant_id
336            .wrapping_mul(11)
337            .wrapping_add(attempts_for_commit.wrapping_mul(7))
338            % (base_millis + 1);
339        std::thread::sleep(Duration::from_millis(base_millis + jitter_millis));
340    }
341
342    async fn concurrent_stress_rollback_precommit_transient(
343        conn: &Connection,
344        outcome: &mut ConcurrentStressWorkerOutcome,
345        phase: &str,
346        primary_error: &FrankenError,
347    ) -> Result<(), String> {
348        if !outcome.retries.record(primary_error) {
349            return Err(format!("unexpected {phase} error: {primary_error:?}"));
350        }
351
352        // BusySnapshot invalidates the whole transaction. Full ROLLBACK is the
353        // engine contract that releases page locks and reloads committed state
354        // before the next BEGIN binds a fresh publication snapshot.
355        if let Err(rollback_error) = conn.execute("ROLLBACK;").await {
356            let _ = outcome.retries.record(&rollback_error);
357            // Full rollback clears the explicit-transaction state before it
358            // reloads the newly committed pager image. A peer may hold the
359            // recovery fence during that reload, yielding BusyRecovery even
360            // though rollback already released this worker's page locks and
361            // ended its transaction. In that exact state the next bounded
362            // BEGIN is the recovery retry; every other rollback error remains
363            // a hard failure.
364            if matches!(rollback_error, FrankenError::BusyRecovery) && !conn.in_transaction() {
365                return Ok(());
366            }
367            return Err(format!(
368                "ROLLBACK after retryable {phase} error {primary_error:?} failed: \
369                 {rollback_error:?}"
370            ));
371        }
372
373        Ok(())
374    }
375
376    fn concurrent_stress_panic_message(payload: &(dyn std::any::Any + Send)) -> &str {
377        if let Some(message) = payload.downcast_ref::<String>() {
378            message
379        } else if let Some(message) = payload.downcast_ref::<&'static str>() {
380            message
381        } else {
382            "non-string panic payload"
383        }
384    }
385
386    fn row_values(row: &super::Row) -> Vec<SqliteValue> {
387        row.values().to_vec()
388    }
389
390    #[cfg(all(feature = "native", any(unix, windows)))]
391    fn native_suffixed_path(path: &std::path::Path, suffix: &str) -> std::path::PathBuf {
392        let mut suffixed = path.as_os_str().to_owned();
393        suffixed.push(suffix);
394        std::path::PathBuf::from(suffixed)
395    }
396
397    #[cfg(all(feature = "native", any(unix, windows)))]
398    fn native_database_artifacts(path: &std::path::Path) -> [std::path::PathBuf; 8] {
399        [
400            path.to_owned(),
401            native_suffixed_path(path, "-journal"),
402            native_suffixed_path(path, "-wal"),
403            native_suffixed_path(path, "-wal-fec"),
404            native_suffixed_path(path, "-shm"),
405            native_suffixed_path(path, "-lock-shared"),
406            native_suffixed_path(path, "-lock-reserved"),
407            native_suffixed_path(path, "-lock-pending"),
408        ]
409    }
410
411    #[cfg(all(feature = "native", any(unix, windows)))]
412    fn snapshot_native_artifacts(paths: &[std::path::PathBuf]) -> Vec<Option<Vec<u8>>> {
413        paths
414            .iter()
415            .map(|path| match std::fs::read(path) {
416                Ok(bytes) => Some(bytes),
417                Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
418                Err(error) => panic!("snapshot {}: {error}", path.display()),
419            })
420            .collect()
421    }
422
423    #[cfg(all(feature = "native", windows))]
424    fn seed_windows_database(path: &std::path::Path) {
425        let seed = rusqlite::Connection::open(path).expect("create valid SQLite database");
426        seed.execute_batch(
427            "PRAGMA journal_mode = DELETE;
428             CREATE TABLE identity_probe(value INTEGER NOT NULL);
429             INSERT INTO identity_probe VALUES (1);",
430        )
431        .expect("seed valid SQLite database");
432    }
433
434    #[cfg(all(feature = "native", windows))]
435    fn windows_file_identity(path: &std::path::Path) -> FileIdentity {
436        let file = std::fs::File::open(path).expect("open Windows identity handle");
437        FileIdentity::from_file(&file)
438            .expect("query Windows identity handle")
439            .expect("Windows file identity must be available")
440    }
441
442    #[cfg(all(feature = "native", windows))]
443    fn seed_windows_auxiliary_sentinels(artifacts: &[std::path::PathBuf; 8]) {
444        std::fs::write(&artifacts[1], b"journal sentinel").expect("seed journal sentinel");
445        std::fs::write(&artifacts[2], b"WAL sentinel").expect("seed WAL sentinel");
446        std::fs::write(&artifacts[3], b"WAL-FEC sentinel").expect("seed WAL-FEC sentinel");
447        std::fs::write(&artifacts[4], b"SHM sentinel").expect("seed SHM sentinel");
448    }
449
450    #[test]
451    fn test_connection_open_and_path() {
452        asupersync::test_utils::run_test(|| async {
453            let conn = Connection::open(":memory:")
454                .await
455                .expect("in-memory connection should open");
456            assert_eq!(conn.path(), ":memory:");
457        });
458    }
459
460    #[test]
461    fn in_memory_connection_has_no_filesystem_identity() {
462        asupersync::test_utils::run_test(|| async {
463            let conn = Connection::open(":memory:")
464                .await
465                .expect("in-memory connection should open");
466            assert_eq!(conn.file_identity().await.unwrap(), None);
467        });
468    }
469
470    #[cfg(all(feature = "native", any(unix, windows)))]
471    #[test]
472    fn namespace_lifetime_connections_to_the_same_database_identity_coexist() {
473        asupersync::test_utils::run_test(|| async {
474            let dir = tempfile::tempdir().expect("create temp dir");
475            let database_path = dir.path().join("shared-generation.db");
476            let database_path = database_path.to_string_lossy().into_owned();
477            let first = Connection::open(database_path.clone())
478                .await
479                .expect("open first connection");
480            let second = Connection::open_existing(database_path)
481                .await
482                .expect("join the initialized live database generation");
483            first
484                .execute("PRAGMA fsqlite.stmt_microbatch = OFF;")
485                .await
486                .expect("disable statement carry on first connection");
487            second
488                .execute("PRAGMA fsqlite.stmt_microbatch = OFF;")
489                .await
490                .expect("disable statement carry on peer connection");
491            first
492                .execute_batch(
493                    "CREATE TABLE shared_generation(value INTEGER NOT NULL);
494                 INSERT INTO shared_generation VALUES (1);",
495                )
496                .await
497                .expect("seed the shared generation");
498
499            let first_identity = first
500                .file_identity()
501                .await
502                .expect("query first connection identity")
503                .expect("native database has a stable identity");
504            assert_eq!(
505                second.file_identity().await.expect("query peer identity"),
506                Some(first_identity),
507                "both connections must remain leased to the same database object"
508            );
509
510            second
511                .execute("INSERT INTO shared_generation VALUES (2);")
512                .await
513                .expect("peer connection writes through the shared generation");
514            let rows = first
515                .query("SELECT value FROM shared_generation ORDER BY value;")
516                .await
517                .expect("first connection observes the peer commit");
518            assert_eq!(
519                rows.iter().map(row_values).collect::<Vec<_>>(),
520                vec![vec![SqliteValue::Integer(1)], vec![SqliteValue::Integer(2)]]
521            );
522        });
523    }
524
525    #[cfg(all(feature = "native", any(unix, windows)))]
526    #[test]
527    fn namespace_generation_transition_reopens_quarantined_replacement() {
528        asupersync::test_utils::run_test(|| async {
529            let dir = tempfile::tempdir().expect("create temp dir");
530            let database_path = dir.path().join("recover.db");
531            let quarantine_path = dir.path().join("recover.db.quarantined");
532            let replacement_stage = dir.path().join("recover.db.replacement");
533            let database_path_string = database_path.to_string_lossy().into_owned();
534
535            {
536                let generation_a =
537                    rusqlite::Connection::open(&database_path).expect("create generation A");
538                generation_a
539                    .execute_batch(
540                        "PRAGMA journal_mode = DELETE;
541                         CREATE TABLE generation(value INTEGER NOT NULL);
542                         INSERT INTO generation VALUES (1);",
543                    )
544                    .expect("seed generation A");
545            }
546            let generation_a = Connection::open_existing(database_path_string.clone())
547                .await
548                .expect("open generation A through the public facade");
549            let old_identity = generation_a
550                .file_identity()
551                .await
552                .expect("query generation A identity")
553                .expect("native database identity");
554            let rows = generation_a
555                .query("SELECT value FROM generation;")
556                .await
557                .expect("query generation A");
558            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
559            drop(generation_a);
560
561            {
562                let generation_b =
563                    rusqlite::Connection::open(&replacement_stage).expect("create generation B");
564                generation_b
565                    .execute_batch(
566                        "PRAGMA journal_mode = DELETE;
567                         CREATE TABLE generation(value INTEGER NOT NULL);
568                         INSERT INTO generation VALUES (2);",
569                    )
570                    .expect("seed generation B");
571            }
572            let replacement_file = std::fs::File::open(&replacement_stage)
573                .expect("retain generation B identity handle");
574            let replacement_identity = FileIdentity::from_file(&replacement_file)
575                .expect("query generation B identity")
576                .expect("native database identity");
577            assert_ne!(old_identity, replacement_identity);
578
579            let mut transition =
580                super::begin_database_namespace_generation_transition(&database_path, old_identity)
581                    .expect("guard generation A before pathname mutation");
582            std::fs::rename(&database_path, &quarantine_path)
583                .expect("quarantine fully quiescent generation A under guard");
584            std::fs::rename(&replacement_stage, &database_path)
585                .expect("install generation B at the stable pathname");
586
587            assert_eq!(
588                transition
589                    .publish_replacement(replacement_identity)
590                    .expect("publish generation B"),
591                super::NamespaceGenerationTransitionOutcome::Published
592            );
593            assert_eq!(
594                transition.finish().expect("finish generation B transition"),
595                replacement_identity
596            );
597            let generation_b = Connection::open_existing_with_expected_identity(
598                database_path_string,
599                replacement_identity,
600            )
601            .await
602            .expect("reopen generation B through the public facade");
603            let rows = generation_b
604                .query("SELECT value FROM generation;")
605                .await
606                .expect("query generation B");
607            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(2)]);
608            drop(generation_b);
609
610            super::begin_database_namespace_generation_transition(
611                &database_path,
612                replacement_identity,
613            )
614            .expect("reacquire exact published generation")
615            .finish()
616            .expect("finish no-op exact reacquisition");
617        });
618    }
619
620    #[cfg(all(feature = "native", any(unix, windows)))]
621    #[test]
622    fn reserved_identity_open_never_synthesizes_a_missing_path() {
623        asupersync::test_utils::run_test(|| async {
624            let dir = tempfile::tempdir().expect("create temp dir");
625            let identity_path = dir.path().join("existing-identity.db");
626            let missing_path = dir.path().join("missing-reservation.db");
627            drop(std::fs::File::create(&identity_path).expect("create identity source"));
628            let identity_file = std::fs::File::open(&identity_path).expect("open identity source");
629            let expected_identity = FileIdentity::from_file(&identity_file)
630                .expect("query filesystem identity")
631                .expect("native filesystem identity must be available");
632
633            let artifacts = [
634                missing_path.clone(),
635                native_suffixed_path(&missing_path, "-journal"),
636                native_suffixed_path(&missing_path, "-wal"),
637                native_suffixed_path(&missing_path, "-wal-fec"),
638                native_suffixed_path(&missing_path, "-shm"),
639                native_suffixed_path(&missing_path, "-lock-shared"),
640                native_suffixed_path(&missing_path, "-lock-reserved"),
641                native_suffixed_path(&missing_path, "-lock-pending"),
642            ];
643            assert!(artifacts.iter().all(|path| !path.exists()));
644
645            let error = Connection::open_reserved_with_expected_identity(
646                missing_path.to_string_lossy().into_owned(),
647                expected_identity,
648            )
649            .await
650            .expect_err("missing reservation must not be created");
651
652            assert!(matches!(error, FrankenError::CannotOpen { .. }));
653            assert!(
654                artifacts.iter().all(|path| !path.exists()),
655                "identity-bound reserved open must leave the missing main path and every sidecar absent"
656            );
657        });
658    }
659
660    #[cfg(all(feature = "native", any(unix, windows)))]
661    #[test]
662    fn reserved_identity_open_refuses_a_preexisting_recovery_artifact_without_mutation() {
663        asupersync::test_utils::run_test(|| async {
664            let dir = tempfile::tempdir().expect("create temp dir");
665            let database_path = dir.path().join("reserved-empty.db");
666            let artifacts = native_database_artifacts(&database_path);
667            drop(std::fs::File::create(&database_path).expect("reserve empty database path"));
668            let reservation = std::fs::File::open(&database_path).expect("open reservation handle");
669            let expected_identity = FileIdentity::from_file(&reservation)
670                .expect("query reservation identity")
671                .expect("native filesystem identity must be available");
672            std::fs::write(&artifacts[1], b"reserved journal sentinel")
673                .expect("seed recovery artifact");
674            let before = snapshot_native_artifacts(&artifacts);
675            assert!(
676                before[2..].iter().all(Option::is_none),
677                "every unseeded recovery and advisory-lock sidecar must start absent"
678            );
679
680            let error = Connection::open_reserved_with_expected_identity(
681                database_path.to_string_lossy().into_owned(),
682                expected_identity,
683            )
684            .await
685            .expect_err("a reserved-empty open must refuse a pre-existing recovery artifact");
686
687            assert!(matches!(error, FrankenError::CannotOpen { .. }));
688            assert_eq!(
689                snapshot_native_artifacts(&artifacts),
690                before,
691                "refusal must leave main, recovery artifacts, and advisory-lock sidecars unchanged"
692            );
693        });
694    }
695
696    #[cfg(all(feature = "native", unix))]
697    #[test]
698    fn reserved_identity_open_refuses_dangling_recovery_artifact_symlinks() {
699        asupersync::test_utils::run_test(|| async {
700            use std::os::unix::fs::symlink;
701
702            let dir = tempfile::tempdir().expect("create temp dir");
703            for (index, suffix) in ["-journal", "-wal", "-wal-fec", "-shm"]
704                .into_iter()
705                .enumerate()
706            {
707                let database_path = dir.path().join(format!("reserved-dangling-{index}.db"));
708                drop(std::fs::File::create(&database_path).expect("reserve empty database path"));
709                let reservation =
710                    std::fs::File::open(&database_path).expect("open reservation handle");
711                let expected_identity = FileIdentity::from_file(&reservation)
712                    .expect("query reservation identity")
713                    .expect("Unix filesystem identity must be available");
714                let dangling_target = dir.path().join(format!("missing-target-{index}"));
715                let artifact_path = native_suffixed_path(&database_path, suffix);
716                symlink(&dangling_target, &artifact_path).expect("seed dangling artifact symlink");
717
718                let error = Connection::open_reserved_with_expected_identity(
719                    database_path.to_string_lossy().into_owned(),
720                    expected_identity,
721                )
722                .await
723                .expect_err("a dangling recovery-artifact symlink must refuse initialization");
724
725                assert!(matches!(error, FrankenError::CannotOpen { .. }));
726                assert_eq!(
727                    std::fs::metadata(&database_path).unwrap().len(),
728                    0,
729                    "refusal must leave the reserved main file empty"
730                );
731                assert!(
732                    std::fs::symlink_metadata(&artifact_path)
733                        .expect("refusal must preserve the dangling artifact")
734                        .file_type()
735                        .is_symlink(),
736                    "refusal must preserve the {suffix} symlink itself"
737                );
738                assert!(
739                    !dangling_target.exists(),
740                    "refusal must not create the dangling symlink target"
741                );
742            }
743        });
744    }
745
746    #[cfg(all(feature = "native", any(unix, windows)))]
747    #[test]
748    fn reserved_identity_open_refuses_a_nonempty_file_without_artifact_mutation() {
749        asupersync::test_utils::run_test(|| async {
750            let dir = tempfile::tempdir().expect("create temp dir");
751            let database_path = dir.path().join("reserved-nonempty.db");
752            std::fs::write(&database_path, b"nonempty reservation sentinel")
753                .expect("seed nonempty reserved file");
754            let reservation = std::fs::File::open(&database_path).expect("open reservation handle");
755            let expected_identity = FileIdentity::from_file(&reservation)
756                .expect("query reservation identity")
757                .expect("native filesystem identity must be available");
758            let artifacts = native_database_artifacts(&database_path);
759            let before = snapshot_native_artifacts(&artifacts);
760            assert!(
761                before[1..].iter().all(Option::is_none),
762                "every database sidecar must start absent"
763            );
764
765            let error = Connection::open_reserved_with_expected_identity(
766                database_path.to_string_lossy().into_owned(),
767                expected_identity,
768            )
769            .await
770            .expect_err("a reserved-empty open must refuse a nonempty file");
771
772            assert!(matches!(error, FrankenError::CannotOpen { .. }));
773            assert_eq!(
774                snapshot_native_artifacts(&artifacts),
775                before,
776                "nonempty refusal must leave the main file and every sidecar unchanged"
777            );
778        });
779    }
780
781    #[cfg(all(feature = "native", any(unix, windows)))]
782    #[test]
783    fn namespace_lifetime_reserved_open_refuses_wal_segment_and_fec_rewrite_artifacts() {
784        asupersync::test_utils::run_test(|| async {
785            let dir = tempfile::tempdir().expect("create temp dir");
786
787            for artifact_kind in ["wal-segment", "wal-fec-rewrite"] {
788                let database_path = dir.path().join(format!("reserved-{artifact_kind}.db"));
789                drop(std::fs::File::create(&database_path).expect("reserve empty database path"));
790                let reservation =
791                    std::fs::File::open(&database_path).expect("retain reservation handle");
792                let expected_identity = FileIdentity::from_file(&reservation)
793                    .expect("query reservation identity")
794                    .expect("native filesystem identity must be available");
795                let artifact_path = match artifact_kind {
796                    "wal-segment" => dir.path().join("reserved-wal-segment.db-wal-seg-00000001"),
797                    "wal-fec-rewrite" => native_suffixed_path(&database_path, "-wal-fec")
798                        .with_extension("wal-fec.tmp"),
799                    _ => unreachable!("artifact cases are exhaustive"),
800                };
801                let sentinel = format!("{artifact_kind} sentinel").into_bytes();
802                std::fs::write(&artifact_path, &sentinel).expect("seed forbidden artifact");
803
804                let error = Connection::open_reserved_with_expected_identity(
805                    database_path.to_string_lossy().into_owned(),
806                    expected_identity,
807                )
808                .await
809                .expect_err("reserved bootstrap must reject every pre-existing WAL artifact");
810
811                assert!(matches!(error, FrankenError::CannotOpen { .. }));
812                assert_eq!(
813                    std::fs::metadata(&database_path).unwrap().len(),
814                    0,
815                    "refusal must leave the reserved main file empty"
816                );
817                assert_eq!(
818                    std::fs::read(&artifact_path).expect("read preserved forbidden artifact"),
819                    sentinel,
820                    "refusal must not mutate the forbidden artifact"
821                );
822            }
823        });
824    }
825
826    #[cfg(all(feature = "native", unix))]
827    #[test]
828    fn connection_identity_remains_bound_to_open_file_after_path_swap() {
829        asupersync::test_utils::run_test(|| async {
830            use std::fs::File;
831
832            let dir = tempfile::tempdir().expect("create temp dir");
833            let database_path = dir.path().join("identity.db");
834            let displaced_path = dir.path().join("identity.opened.db");
835            let conn = Connection::open(database_path.to_string_lossy().into_owned())
836                .await
837                .expect("open file-backed connection");
838
839            let leased_file = File::open(&database_path).expect("lease opened database descriptor");
840            let leased_identity = FileIdentity::from_file(&leased_file)
841                .expect("read leased descriptor identity")
842                .expect("Unix descriptors have stable identities");
843            let connection_identity = conn
844                .file_identity()
845                .await
846                .expect("read connection identity")
847                .expect("Unix VFS exposes an open-file identity");
848            assert_eq!(connection_identity, leased_identity);
849
850            std::fs::rename(&database_path, &displaced_path)
851                .expect("displace opened database path");
852            drop(File::create(&database_path).expect("create replacement path"));
853            let replacement_file =
854                File::open(&database_path).expect("lease replacement descriptor");
855            let replacement_identity = FileIdentity::from_file(&replacement_file)
856                .expect("read replacement descriptor identity")
857                .expect("Unix descriptors have stable identities");
858
859            assert_ne!(connection_identity, replacement_identity);
860            assert_eq!(conn.file_identity().await.unwrap(), Some(leased_identity));
861            drop(conn);
862        });
863    }
864
865    #[cfg(all(feature = "native", unix))]
866    async fn exercise_live_namespace_replacement_rejection(journal_mode: &str) {
867        let dir = tempfile::tempdir().expect("create temp dir");
868        let database_path = dir.path().join(format!("live-{journal_mode}.db"));
869        let displaced_path = dir.path().join(format!("live-{journal_mode}.displaced.db"));
870        let replacement_stage = dir
871            .path()
872            .join(format!("live-{journal_mode}.replacement.db"));
873        let database_path_string = database_path.to_string_lossy().into_owned();
874
875        let live = Connection::open(database_path_string.clone())
876            .await
877            .expect("open live generation");
878        live.execute("PRAGMA fsqlite.stmt_microbatch = OFF;")
879            .await
880            .expect("disable retained statement carry for namespace boundary proof");
881        live.execute(&format!("PRAGMA journal_mode = '{journal_mode}';"))
882            .await
883            .expect("select requested journal mode");
884        live.execute_batch(
885            "CREATE TABLE identity_probe(value INTEGER NOT NULL);
886             INSERT INTO identity_probe VALUES (1);",
887        )
888        .await
889        .expect("seed live generation");
890        let live_identity = live
891            .file_identity()
892            .await
893            .expect("query live identity")
894            .expect("Unix database has a stable identity");
895
896        {
897            let replacement = rusqlite::Connection::open(&replacement_stage)
898                .expect("create valid replacement database");
899            replacement
900                .execute_batch(
901                    "PRAGMA journal_mode = DELETE;
902                     CREATE TABLE identity_probe(value INTEGER NOT NULL);
903                     INSERT INTO identity_probe VALUES (9001);",
904                )
905                .expect("seed replacement database");
906        }
907        let replacement_staged_bytes =
908            std::fs::read(&replacement_stage).expect("snapshot staged replacement");
909
910        std::fs::rename(&database_path, &displaced_path).expect("displace live main file");
911        std::fs::rename(&replacement_stage, &database_path)
912            .expect("install replacement at the live pathname");
913        let replacement_file =
914            std::fs::File::open(&database_path).expect("open replacement identity handle");
915        let replacement_identity = FileIdentity::from_file(&replacement_file)
916            .expect("query replacement identity")
917            .expect("Unix database has a stable identity");
918        assert_ne!(live_identity, replacement_identity);
919
920        let write_error = live
921            .execute("INSERT INTO identity_probe VALUES (2);")
922            .await
923            .expect_err("live connection must reject a replaced main pathname");
924        assert!(matches!(write_error, FrankenError::CannotOpen { .. }));
925        assert_eq!(
926            std::fs::read(&database_path).expect("read rejected replacement"),
927            replacement_staged_bytes,
928            "rejection must precede every write to the replacement database object"
929        );
930
931        let join_error = Connection::open(database_path_string.clone())
932            .await
933            .expect_err("a peer must not join the replacement while the old generation is live");
934        assert!(matches!(join_error, FrankenError::CannotOpen { .. }));
935        assert!(matches!(
936            super::begin_database_namespace_generation_transition(&database_path, live_identity),
937            Err(FrankenError::Busy)
938        ));
939        assert_eq!(
940            std::fs::read(&database_path).expect("read replacement after rejected join"),
941            replacement_staged_bytes,
942            "rejected admission must not mutate the replacement"
943        );
944
945        drop(live);
946        std::fs::rename(&database_path, &replacement_stage)
947            .expect("restage replacement before acquiring transition guard");
948        std::fs::rename(&displaced_path, &database_path)
949            .expect("restore live generation before acquiring transition guard");
950        let mut transition =
951            super::begin_database_namespace_generation_transition(&database_path, live_identity)
952                .expect("guard the quiescent old generation");
953        std::fs::rename(&database_path, &displaced_path)
954            .expect("quarantine old generation under guard");
955        std::fs::rename(&replacement_stage, &database_path)
956            .expect("activate replacement under guard");
957        assert_eq!(
958            transition
959                .publish_replacement(replacement_identity)
960                .expect("publish the quiescent replacement generation"),
961            super::NamespaceGenerationTransitionOutcome::Published
962        );
963        transition.finish().expect("finish replacement transition");
964        let replacement = Connection::open_existing(database_path_string)
965            .await
966            .expect("replacement becomes a new generation after the old lease drops");
967        assert_eq!(
968            replacement.file_identity().await.unwrap(),
969            Some(replacement_identity)
970        );
971        let rows = replacement
972            .query("SELECT value FROM identity_probe;")
973            .await
974            .expect("query the replacement generation");
975        assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(9001)]);
976        replacement
977            .execute("INSERT INTO identity_probe VALUES (9002);")
978            .await
979            .expect("new generation is writable");
980    }
981
982    #[cfg(all(feature = "native", unix))]
983    #[test]
984    fn namespace_lifetime_delete_mode_replacement_is_rejected() {
985        asupersync::test_utils::run_test(|| async {
986            exercise_live_namespace_replacement_rejection("DELETE").await;
987        });
988    }
989
990    #[cfg(all(feature = "native", unix))]
991    #[test]
992    fn namespace_lifetime_wal_mode_replacement_is_rejected() {
993        asupersync::test_utils::run_test(|| async {
994            exercise_live_namespace_replacement_rejection("WAL").await;
995        });
996    }
997
998    #[cfg(all(feature = "native", any(unix, windows)))]
999    #[test]
1000    fn namespace_lifetime_relative_path_remains_anchored_after_cwd_change() {
1001        asupersync::test_utils::run_test(|| async {
1002            const CHILD_ROOT: &str = "FSQLITE_NAMESPACE_CWD_CHILD_ROOT";
1003
1004            if let Some(root) = std::env::var_os(CHILD_ROOT) {
1005                let root = std::path::PathBuf::from(root);
1006                let original_dir = root.join("original-cwd");
1007                let later_dir = root.join("later-cwd");
1008                std::env::set_current_dir(&original_dir).expect("enter original cwd");
1009
1010                let conn = Connection::open("anchored.db")
1011                    .await
1012                    .expect("open relative database path");
1013                let expected_path = original_dir.join("anchored.db");
1014                let expected_canonical_path = expected_path
1015                    .canonicalize()
1016                    .expect("canonicalize newly opened relative database path");
1017                assert_eq!(std::path::Path::new(conn.path()), expected_canonical_path);
1018                conn.execute_batch(
1019                    "CREATE TABLE cwd_probe(value INTEGER NOT NULL);
1020                 INSERT INTO cwd_probe VALUES (1);",
1021                )
1022                .await
1023                .expect("seed database from original cwd");
1024
1025                std::env::set_current_dir(&later_dir).expect("change process cwd");
1026                conn.execute("INSERT INTO cwd_probe VALUES (2);")
1027                    .await
1028                    .expect("write remains bound to original absolute path");
1029                drop(conn);
1030
1031                assert!(expected_path.exists());
1032                assert!(
1033                    !later_dir.join("anchored.db").exists(),
1034                    "no operation may re-resolve the configured relative path against the new cwd"
1035                );
1036                let verification =
1037                    rusqlite::Connection::open(expected_path).expect("open anchored database");
1038                let values = verification
1039                    .prepare("SELECT value FROM cwd_probe ORDER BY value")
1040                    .expect("prepare anchored verification")
1041                    .query_map([], |row| row.get::<_, i64>(0))
1042                    .expect("query anchored verification")
1043                    .collect::<rusqlite::Result<Vec<_>>>()
1044                    .expect("collect anchored verification");
1045                assert_eq!(values, vec![1, 2]);
1046                return;
1047            }
1048
1049            let dir = tempfile::tempdir().expect("create parent temp dir");
1050            std::fs::create_dir(dir.path().join("original-cwd")).expect("create original cwd");
1051            std::fs::create_dir(dir.path().join("later-cwd")).expect("create later cwd");
1052            let output = std::process::Command::new(
1053                std::env::current_exe().expect("locate current Rust test binary"),
1054            )
1055            .args([
1056                "--exact",
1057                "tests::namespace_lifetime_relative_path_remains_anchored_after_cwd_change",
1058                "--nocapture",
1059            ])
1060            .env(CHILD_ROOT, dir.path())
1061            .current_dir(dir.path())
1062            .output()
1063            .expect("run cwd-isolated child test");
1064            assert!(
1065                output.status.success(),
1066                "cwd-isolated child failed\nstdout:\n{}\nstderr:\n{}",
1067                String::from_utf8_lossy(&output.stdout),
1068                String::from_utf8_lossy(&output.stderr)
1069            );
1070        });
1071    }
1072
1073    #[cfg(all(feature = "native", unix))]
1074    #[test]
1075    fn expected_identity_refuses_swapped_hot_journal_without_mutation() {
1076        asupersync::test_utils::run_test(|| async {
1077            use fsqlite_pager::{JournalHeader, JournalPageRecord};
1078            use std::fs::File;
1079
1080            let dir = tempfile::tempdir().expect("create temp dir");
1081            let database_path = dir.path().join("identity-bound.db");
1082            let displaced_path = dir.path().join("identity-bound.leased.db");
1083            let journal_path = dir.path().join("identity-bound.db-journal");
1084
1085            {
1086                let leased_seed = rusqlite::Connection::open(&database_path)
1087                    .expect("create identity-leased database");
1088                leased_seed
1089                    .execute_batch(
1090                        "PRAGMA journal_mode = DELETE;
1091                     CREATE TABLE leased_marker(value INTEGER);
1092                     INSERT INTO leased_marker VALUES (1);",
1093                    )
1094                    .expect("seed identity-leased database");
1095            }
1096            let leased_file =
1097                File::open(&database_path).expect("lease original database descriptor");
1098            let leased_identity = FileIdentity::from_file(&leased_file)
1099                .expect("read leased descriptor identity")
1100                .expect("Unix descriptors have stable identities");
1101            std::fs::rename(&database_path, &displaced_path)
1102                .expect("replace leased database pathname");
1103
1104            let (page_size, page_count) = {
1105                let replacement = rusqlite::Connection::open(&database_path)
1106                    .expect("create replacement database");
1107                replacement
1108                    .execute_batch(
1109                        "PRAGMA page_size = 4096;
1110                     PRAGMA journal_mode = DELETE;
1111                     CREATE TABLE replacement_marker(value INTEGER);
1112                     INSERT INTO replacement_marker VALUES (2);",
1113                    )
1114                    .expect("seed replacement database");
1115                let page_size: i64 = replacement
1116                    .query_row("PRAGMA page_size", [], |row| row.get(0))
1117                    .expect("read replacement page size");
1118                let page_count: i64 = replacement
1119                    .query_row("PRAGMA page_count", [], |row| row.get(0))
1120                    .expect("read replacement page count");
1121                (
1122                    u32::try_from(page_size).expect("page size fits u32"),
1123                    u32::try_from(page_count).expect("page count fits u32"),
1124                )
1125            };
1126            assert!(page_count >= 2, "replacement database must have page 2");
1127
1128            let replacement_file =
1129                File::open(&database_path).expect("open replacement database descriptor");
1130            let replacement_identity = FileIdentity::from_file(&replacement_file)
1131                .expect("read replacement descriptor identity")
1132                .expect("Unix descriptors have stable identities");
1133            assert_ne!(replacement_identity, leased_identity);
1134
1135            let page_size_usize = usize::try_from(page_size).expect("page size fits usize");
1136            let replacement_pristine =
1137                std::fs::read(&database_path).expect("read replacement database");
1138            let mut replacement_bytes = replacement_pristine.clone();
1139            assert!(replacement_bytes.len() >= page_size_usize * 2);
1140            let page_two_preimage =
1141                replacement_bytes[page_size_usize..page_size_usize * 2].to_vec();
1142            replacement_bytes[page_size_usize] ^= 0xff;
1143            std::fs::write(&database_path, &replacement_bytes)
1144                .expect("write simulated interrupted page update");
1145
1146            let nonce = 0x4653_514c;
1147            let journal_header = JournalHeader {
1148                page_count: 1,
1149                nonce,
1150                initial_db_size: page_count,
1151                sector_size: 512,
1152                page_size,
1153            };
1154            let mut journal_bytes = journal_header.encode_padded();
1155            journal_bytes.extend(JournalPageRecord::new(2, page_two_preimage, nonce).encode());
1156            std::fs::write(&journal_path, &journal_bytes).expect("write valid hot journal");
1157
1158            let database_before =
1159                std::fs::read(&database_path).expect("snapshot replacement bytes");
1160            let journal_before = std::fs::read(&journal_path).expect("snapshot hot journal bytes");
1161            let error = Connection::open_existing_with_expected_identity(
1162                database_path.to_string_lossy().into_owned(),
1163                leased_identity,
1164            )
1165            .await
1166            .expect_err("identity-bound open must reject the replacement database");
1167
1168            assert!(matches!(error, FrankenError::CannotOpen { .. }));
1169            assert_eq!(
1170                std::fs::read(&database_path).unwrap(),
1171                database_before,
1172                "identity refusal must precede hot-journal recovery writes"
1173            );
1174            assert_eq!(
1175                std::fs::read(&journal_path).unwrap(),
1176                journal_before,
1177                "identity refusal must not invalidate or delete the hot journal"
1178            );
1179
1180            let control_database_path = dir.path().join("identity-bound-control.db");
1181            let control_journal_path = dir.path().join("identity-bound-control.db-journal");
1182            std::fs::write(&control_database_path, &database_before)
1183                .expect("copy interrupted database into recovery control");
1184            std::fs::write(&control_journal_path, &journal_before)
1185                .expect("copy hot journal into recovery control");
1186
1187            let control =
1188                Connection::open_existing(control_database_path.to_string_lossy().into_owned())
1189                    .await
1190                    .expect("plain write-existing open must recover the control copy");
1191            drop(control);
1192
1193            let control_after =
1194                std::fs::read(&control_database_path).expect("read recovered control database");
1195            assert_ne!(
1196                control_after, database_before,
1197                "control recovery must prove the hot journal is not inert"
1198            );
1199            assert_eq!(
1200                &control_after[page_size_usize..page_size_usize * 2],
1201                &replacement_pristine[page_size_usize..page_size_usize * 2],
1202                "control recovery must restore the original page-two preimage"
1203            );
1204            assert!(
1205                !control_journal_path.exists()
1206                    || std::fs::read(&control_journal_path).unwrap().is_empty(),
1207                "control recovery must consume or invalidate the hot journal"
1208            );
1209        });
1210    }
1211
1212    #[cfg(all(feature = "native", windows))]
1213    #[test]
1214    fn windows_expected_identity_refuses_before_any_database_artifact_mutation() {
1215        asupersync::test_utils::run_test(|| async {
1216            let dir = tempfile::tempdir().expect("create temp dir");
1217            let identity_path = dir.path().join("identity.db");
1218            let candidate_path = dir.path().join("candidate.db");
1219            for path in [&identity_path, &candidate_path] {
1220                seed_windows_database(path);
1221            }
1222
1223            let expected_identity = windows_file_identity(&identity_path);
1224            let candidate_identity = windows_file_identity(&candidate_path);
1225            assert_ne!(expected_identity, candidate_identity);
1226
1227            let artifacts = native_database_artifacts(&candidate_path);
1228            seed_windows_auxiliary_sentinels(&artifacts);
1229            let before = snapshot_native_artifacts(&artifacts);
1230            assert!(
1231                before[5..].iter().all(Option::is_none),
1232                "advisory lock sidecars must start absent"
1233            );
1234
1235            let error = Connection::open_existing_with_expected_identity(
1236                candidate_path.to_string_lossy().into_owned(),
1237                expected_identity,
1238            )
1239            .await
1240            .expect_err("wrong expected identity must refuse the candidate database");
1241
1242            assert!(matches!(error, FrankenError::CannotOpen { .. }));
1243            assert_eq!(
1244                snapshot_native_artifacts(&artifacts),
1245                before,
1246                "identity refusal must leave main, journal, WAL, SHM, and advisory sidecars unchanged"
1247            );
1248        });
1249    }
1250
1251    #[cfg(all(feature = "native", windows))]
1252    #[test]
1253    fn windows_schema_only_identity_mismatch_precedes_artifact_mutation() {
1254        asupersync::test_utils::run_test(|| async {
1255            let dir = tempfile::tempdir().expect("create temp dir");
1256            let identity_path = dir.path().join("schema-identity.db");
1257            let candidate_path = dir.path().join("schema-candidate.db");
1258            seed_windows_database(&identity_path);
1259            seed_windows_database(&candidate_path);
1260
1261            let expected_identity = windows_file_identity(&identity_path);
1262            assert_ne!(expected_identity, windows_file_identity(&candidate_path));
1263
1264            let artifacts = native_database_artifacts(&candidate_path);
1265            seed_windows_auxiliary_sentinels(&artifacts);
1266            let before = snapshot_native_artifacts(&artifacts);
1267            assert!(
1268                before[5..].iter().all(Option::is_none),
1269                "advisory lock sidecars must start absent"
1270            );
1271
1272            let error = Connection::open_schema_only_with_expected_identity(
1273                candidate_path.to_string_lossy().into_owned(),
1274                expected_identity,
1275            )
1276            .await
1277            .expect_err("wrong expected identity must refuse schema-only open");
1278
1279            assert!(matches!(error, FrankenError::CannotOpen { .. }));
1280            assert_eq!(
1281                snapshot_native_artifacts(&artifacts),
1282                before,
1283                "schema-only refusal must leave every database artifact unchanged"
1284            );
1285        });
1286    }
1287
1288    #[cfg(all(feature = "native", windows))]
1289    #[test]
1290    fn windows_existing_expected_identity_accepts_matching_file() {
1291        asupersync::test_utils::run_test(|| async {
1292            let dir = tempfile::tempdir().expect("create temp dir");
1293            let database_path = dir.path().join("existing-matching-identity.db");
1294            seed_windows_database(&database_path);
1295
1296            let leased_file =
1297                std::fs::File::open(&database_path).expect("retain existing database handle");
1298            let expected_identity = FileIdentity::from_file(&leased_file)
1299                .expect("query existing database identity")
1300                .expect("Windows file identity must be available");
1301            let conn = Connection::open_existing_with_expected_identity(
1302                database_path.to_string_lossy().into_owned(),
1303                expected_identity,
1304            )
1305            .await
1306            .expect("matching identity must open the existing database");
1307
1308            assert_eq!(conn.file_identity().await.unwrap(), Some(expected_identity));
1309            let rows = conn
1310                .query("SELECT COUNT(*) FROM identity_probe;")
1311                .await
1312                .expect("matching identity connection must query the seeded table");
1313            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
1314            drop(conn);
1315            drop(leased_file);
1316        });
1317    }
1318
1319    #[cfg(all(feature = "native", windows))]
1320    #[test]
1321    fn windows_schema_only_expected_identity_accepts_matching_file() {
1322        asupersync::test_utils::run_test(|| async {
1323            let dir = tempfile::tempdir().expect("create temp dir");
1324            let database_path = dir.path().join("schema-matching-identity.db");
1325            seed_windows_database(&database_path);
1326
1327            let leased_file =
1328                std::fs::File::open(&database_path).expect("retain schema database handle");
1329            let expected_identity = FileIdentity::from_file(&leased_file)
1330                .expect("query schema database identity")
1331                .expect("Windows file identity must be available");
1332            let conn = Connection::open_schema_only_with_expected_identity(
1333                database_path.to_string_lossy().into_owned(),
1334                expected_identity,
1335            )
1336            .await
1337            .expect("matching identity must open the schema-only connection");
1338
1339            assert_eq!(conn.file_identity().await.unwrap(), Some(expected_identity));
1340            let rows = conn
1341                .query("SELECT COUNT(*) FROM sqlite_master WHERE name = 'identity_probe';")
1342                .await
1343                .expect("matching identity schema connection must load the seeded schema");
1344            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
1345            drop(conn);
1346            drop(leased_file);
1347        });
1348    }
1349
1350    #[cfg(all(feature = "native", windows))]
1351    #[test]
1352    fn windows_reserved_empty_open_is_identity_bound() {
1353        asupersync::test_utils::run_test(|| async {
1354            let dir = tempfile::tempdir().expect("create temp dir");
1355            let identity_path = dir.path().join("reservation-identity.db");
1356            let candidate_path = dir.path().join("reservation-candidate.db");
1357            let accepted_path = dir.path().join("reservation-accepted.db");
1358            for path in [&identity_path, &candidate_path, &accepted_path] {
1359                drop(std::fs::File::create(path).expect("reserve empty database path"));
1360            }
1361
1362            let wrong_identity = windows_file_identity(&identity_path);
1363            assert_ne!(wrong_identity, windows_file_identity(&candidate_path));
1364            let candidate_artifacts = native_database_artifacts(&candidate_path);
1365            seed_windows_auxiliary_sentinels(&candidate_artifacts);
1366            let candidate_before = snapshot_native_artifacts(&candidate_artifacts);
1367            assert!(
1368                candidate_before[5..].iter().all(Option::is_none),
1369                "advisory lock sidecars must start absent"
1370            );
1371
1372            let error = Connection::open_reserved_with_expected_identity(
1373                candidate_path.to_string_lossy().into_owned(),
1374                wrong_identity,
1375            )
1376            .await
1377            .expect_err("wrong reservation identity must refuse initialization");
1378            assert!(matches!(error, FrankenError::CannotOpen { .. }));
1379            assert_eq!(
1380                snapshot_native_artifacts(&candidate_artifacts),
1381                candidate_before,
1382                "wrong reservation identity must leave the empty file and sidecars untouched"
1383            );
1384
1385            let accepted_reservation =
1386                std::fs::File::open(&accepted_path).expect("retain accepted reservation handle");
1387            let accepted_identity = FileIdentity::from_file(&accepted_reservation)
1388                .expect("query accepted reservation identity")
1389                .expect("Windows file identity must be available");
1390            let conn = Connection::open_reserved_with_expected_identity(
1391                accepted_path.to_string_lossy().into_owned(),
1392                accepted_identity,
1393            )
1394            .await
1395            .expect("matching reservation identity must initialize the database");
1396            assert_eq!(conn.file_identity().await.unwrap(), Some(accepted_identity));
1397            assert!(
1398                std::fs::metadata(&accepted_path).unwrap().len() > 0,
1399                "matching reservation must initialize the empty database image"
1400            );
1401            conn.execute("CREATE TABLE reservation_probe(value INTEGER NOT NULL);")
1402                .await
1403                .expect("initialized reservation must accept SQL");
1404        });
1405    }
1406
1407    #[test]
1408    fn test_public_api_query_expression() {
1409        asupersync::test_utils::run_test(|| async {
1410            let conn = Connection::open(":memory:")
1411                .await
1412                .expect("in-memory connection should open");
1413            let rows = conn
1414                .query("SELECT 1 + 2, 'ab' || 'cd';")
1415                .await
1416                .expect("query should succeed");
1417            assert_eq!(rows.len(), 1);
1418            assert_eq!(
1419                row_values(&rows[0]),
1420                vec![SqliteValue::Integer(3), SqliteValue::Text("abcd".into()),]
1421            );
1422        });
1423    }
1424
1425    #[test]
1426    fn test_public_api_query_with_params() {
1427        asupersync::test_utils::run_test(|| async {
1428            let conn = Connection::open(":memory:")
1429                .await
1430                .expect("in-memory connection should open");
1431            let rows = conn
1432                .query_with_params(
1433                    "SELECT ?1 + ?2, ?3;",
1434                    &[
1435                        SqliteValue::Integer(4),
1436                        SqliteValue::Integer(5),
1437                        SqliteValue::Text("ok".into()),
1438                    ],
1439                )
1440                .await
1441                .expect("query_with_params should succeed");
1442            assert_eq!(rows.len(), 1);
1443            assert_eq!(
1444                row_values(&rows[0]),
1445                vec![SqliteValue::Integer(9), SqliteValue::Text("ok".into())]
1446            );
1447        });
1448    }
1449
1450    #[test]
1451    fn test_public_api_query_row_multiple_rows_error() {
1452        asupersync::test_utils::run_test(|| async {
1453            let conn = Connection::open(":memory:")
1454                .await
1455                .expect("in-memory connection should open");
1456            let error = conn
1457                .query_row("VALUES (10), (20), (30);")
1458                .await
1459                .expect_err("query_row should fail when more than one row is returned");
1460            assert!(matches!(error, FrankenError::QueryReturnedMultipleRows));
1461        });
1462    }
1463
1464    #[test]
1465    fn test_public_api_query_row_empty_error() {
1466        asupersync::test_utils::run_test(|| async {
1467            let conn = Connection::open(":memory:")
1468                .await
1469                .expect("in-memory connection should open");
1470            let error = conn
1471                .query_row("SELECT 1 WHERE 0;")
1472                .await
1473                .expect_err("query_row should fail for empty result set");
1474            assert!(matches!(error, FrankenError::QueryReturnedNoRows));
1475        });
1476    }
1477
1478    #[test]
1479    fn test_public_api_execute_returns_row_count() {
1480        asupersync::test_utils::run_test(|| async {
1481            let conn = Connection::open(":memory:")
1482                .await
1483                .expect("in-memory connection should open");
1484            let count = conn
1485                .execute("VALUES (1), (2), (3);")
1486                .await
1487                .expect("execute should succeed");
1488            assert_eq!(count, 3);
1489        });
1490    }
1491
1492    // ── Connection::open error paths ────────────────────────────────────
1493
1494    #[test]
1495    fn open_empty_path_fails() {
1496        asupersync::test_utils::run_test(|| async {
1497            let err = Connection::open("")
1498                .await
1499                .expect_err("empty path should fail");
1500            assert!(matches!(err, FrankenError::CannotOpen { .. }));
1501        });
1502    }
1503
1504    #[test]
1505    fn runtime_api_is_reexported() {
1506        asupersync::test_utils::run_test(|| async {
1507            let runtime = init_global_runtime(RuntimeConfig {
1508                worker_threads: 2,
1509                io_poll_strategy: IoPollStrategy::Blocking,
1510            });
1511            assert_eq!(runtime.config().worker_threads, 2);
1512            assert_eq!(runtime.config().io_poll_strategy, IoPollStrategy::Blocking);
1513
1514            let parent_cx = fsqlite_types::cx::Cx::new().with_trace_context(11, 0, 0);
1515            let explicit_runtime = Arc::new(RuntimeContext::new_with_root_cx(
1516                RuntimeConfig {
1517                    worker_threads: 1,
1518                    io_poll_strategy: IoPollStrategy::Auto,
1519                },
1520                &parent_cx,
1521            ));
1522            let env = ConnectionEnv::new(Arc::clone(&explicit_runtime));
1523            let conn = Connection::open_with_env(":memory:", env)
1524                .await
1525                .expect("connection should open");
1526            assert_eq!(conn.path(), ":memory:");
1527        });
1528    }
1529
1530    // ── Row accessors ────────────────────────────────────────────────────
1531
1532    #[test]
1533    fn row_get_valid_index() {
1534        asupersync::test_utils::run_test(|| async {
1535            let conn = Connection::open(":memory:").await.unwrap();
1536            let row = conn.query_row("SELECT 42, 'hello';").await.unwrap();
1537            assert_eq!(row.get(0), Some(&SqliteValue::Integer(42)));
1538            assert_eq!(row.get(1), Some(&SqliteValue::Text("hello".into())));
1539        });
1540    }
1541
1542    #[test]
1543    fn row_get_out_of_bounds() {
1544        asupersync::test_utils::run_test(|| async {
1545            let conn = Connection::open(":memory:").await.unwrap();
1546            let row = conn.query_row("SELECT 1;").await.unwrap();
1547            assert_eq!(row.get(99), None);
1548        });
1549    }
1550
1551    #[test]
1552    fn row_values_returns_all_columns() {
1553        asupersync::test_utils::run_test(|| async {
1554            let conn = Connection::open(":memory:").await.unwrap();
1555            let row = conn.query_row("SELECT 1, 2, 3;").await.unwrap();
1556            assert_eq!(row.values().len(), 3);
1557        });
1558    }
1559
1560    // ── PreparedStatement ────────────────────────────────────────────────
1561
1562    #[test]
1563    fn prepared_query() {
1564        asupersync::test_utils::run_test(|| async {
1565            let conn = Connection::open(":memory:").await.unwrap();
1566            let stmt = conn.prepare("SELECT 7 * 6;").await.unwrap();
1567            let rows = stmt.query().await.unwrap();
1568            assert_eq!(rows.len(), 1);
1569            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(42)]);
1570        });
1571    }
1572
1573    #[test]
1574    fn prepared_query_with_params() {
1575        asupersync::test_utils::run_test(|| async {
1576            let conn = Connection::open(":memory:").await.unwrap();
1577            let stmt = conn.prepare("SELECT ?1 + ?2;").await.unwrap();
1578            let rows = stmt
1579                .query_with_params(&[SqliteValue::Integer(10), SqliteValue::Integer(20)])
1580                .await
1581                .unwrap();
1582            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(30)]);
1583        });
1584    }
1585
1586    #[test]
1587    fn prepared_query_row() {
1588        asupersync::test_utils::run_test(|| async {
1589            let conn = Connection::open(":memory:").await.unwrap();
1590            let stmt = conn.prepare("SELECT 99;").await.unwrap();
1591            let row = stmt.query_row().await.unwrap();
1592            assert_eq!(row_values(&row), vec![SqliteValue::Integer(99)]);
1593        });
1594    }
1595
1596    #[test]
1597    fn prepared_query_row_with_params() {
1598        asupersync::test_utils::run_test(|| async {
1599            let conn = Connection::open(":memory:").await.unwrap();
1600            let stmt = conn.prepare("SELECT ?1;").await.unwrap();
1601            let row = stmt
1602                .query_row_with_params(&[SqliteValue::Text("xyz".into())])
1603                .await
1604                .unwrap();
1605            assert_eq!(row_values(&row), vec![SqliteValue::Text("xyz".into())]);
1606        });
1607    }
1608
1609    #[test]
1610    fn prepared_execute() {
1611        asupersync::test_utils::run_test(|| async {
1612            let conn = Connection::open(":memory:").await.unwrap();
1613            let stmt = conn.prepare("VALUES (1), (2);").await.unwrap();
1614            assert_eq!(stmt.execute().await.unwrap(), 2);
1615        });
1616    }
1617
1618    #[test]
1619    fn prepared_execute_with_params() {
1620        asupersync::test_utils::run_test(|| async {
1621            let conn = Connection::open(":memory:").await.unwrap();
1622            let stmt = conn.prepare("SELECT ?1;").await.unwrap();
1623            assert_eq!(
1624                stmt.execute_with_params(&[SqliteValue::Integer(1)])
1625                    .await
1626                    .unwrap(),
1627                1
1628            );
1629        });
1630    }
1631
1632    #[test]
1633    fn prepared_explain_not_empty() {
1634        asupersync::test_utils::run_test(|| async {
1635            let conn = Connection::open(":memory:").await.unwrap();
1636            let stmt = conn.prepare("SELECT 1 + 2;").await.unwrap();
1637            let explain = stmt.explain();
1638            assert!(!explain.is_empty());
1639        });
1640    }
1641
1642    #[cfg(feature = "session")]
1643    #[test]
1644    fn session_feature_reexports_manual_session_api() {
1645        assert_eq!(super::session::extension_name(), "session");
1646
1647        let mut session = super::session::Session::new();
1648        session.attach_table("users", 2, vec![true, false]);
1649        session.record_insert(
1650            "users",
1651            vec![
1652                super::session::ChangesetValue::Integer(1),
1653                super::session::ChangesetValue::Text("alice".to_owned()),
1654            ],
1655        );
1656
1657        let encoded = session.changeset().encode();
1658        let decoded = super::session::Changeset::decode(&encoded)
1659            .expect("re-exported session API should round-trip changesets");
1660        assert_eq!(decoded.encode(), encoded);
1661    }
1662
1663    #[test]
1664    fn prepared_indexed_equality_explain_uses_duplicate_run_probe() {
1665        asupersync::test_utils::run_test(|| async {
1666            let dir = tempfile::tempdir().unwrap();
1667            let db_path = dir.path().join("indexed-equality.db");
1668            let db = db_path.to_string_lossy().to_string();
1669
1670            {
1671                let conn = Connection::open(&db).await.unwrap();
1672                conn.execute(
1673                "CREATE TABLE t (id INTEGER PRIMARY KEY, b INTEGER NOT NULL, name TEXT NOT NULL);",
1674            )
1675            .await
1676            .unwrap();
1677                conn.execute("CREATE INDEX idx_t_b ON t(b);").await.unwrap();
1678                conn.execute(
1679                    "INSERT INTO t (id, b, name) VALUES \
1680                 (1, 42, 'alice'), (2, 42, 'bruce'), (5, 42, 'claire'), (9, 99, 'dora');",
1681                )
1682                .await
1683                .unwrap();
1684            }
1685
1686            let conn = Connection::open(&db).await.unwrap();
1687            let stmt = conn
1688                .prepare("SELECT name FROM t WHERE b = ?1;")
1689                .await
1690                .unwrap();
1691
1692            let explain = stmt.explain();
1693            assert!(explain.contains("idx_t_b"));
1694            assert!(explain.contains("SeekGE"));
1695            assert!(explain.contains("IdxRowid"));
1696            assert!(explain.contains("SeekRowid"));
1697            assert!(
1698                explain.contains("-9223372036854775808"),
1699                "expected synthetic low-rowid probe in explain output: {explain}"
1700            );
1701
1702            let rows = stmt
1703                .query_with_params(&[SqliteValue::Integer(42)])
1704                .await
1705                .unwrap();
1706            assert_eq!(
1707                rows.iter()
1708                    .map(|row| row.get(0).cloned().unwrap())
1709                    .collect::<Vec<_>>(),
1710                vec![
1711                    SqliteValue::Text("alice".to_owned().into()),
1712                    SqliteValue::Text("bruce".to_owned().into()),
1713                    SqliteValue::Text("claire".to_owned().into()),
1714                ]
1715            );
1716        });
1717    }
1718
1719    // ── Connection::query_row_with_params ────────────────────────────────
1720
1721    #[test]
1722    fn query_row_with_params() {
1723        asupersync::test_utils::run_test(|| async {
1724            let conn = Connection::open(":memory:").await.unwrap();
1725            let row = conn
1726                .query_row_with_params("SELECT ?1 * 2;", &[SqliteValue::Integer(5)])
1727                .await
1728                .unwrap();
1729            assert_eq!(row_values(&row), vec![SqliteValue::Integer(10)]);
1730        });
1731    }
1732
1733    // ── Connection::execute_with_params ──────────────────────────────────
1734
1735    #[test]
1736    fn execute_with_params_returns_count() {
1737        asupersync::test_utils::run_test(|| async {
1738            let conn = Connection::open(":memory:").await.unwrap();
1739            let count = conn
1740                .execute_with_params("SELECT ?1;", &[SqliteValue::Integer(1)])
1741                .await
1742                .unwrap();
1743            assert_eq!(count, 1);
1744        });
1745    }
1746
1747    // ── DDL ──────────────────────────────────────────────────────────────
1748
1749    #[test]
1750    fn create_table_and_insert_select() {
1751        asupersync::test_utils::run_test(|| async {
1752            let conn = Connection::open(":memory:").await.unwrap();
1753            conn.execute("CREATE TABLE t1 (a INTEGER, b TEXT);")
1754                .await
1755                .unwrap();
1756            conn.execute("INSERT INTO t1 VALUES (1, 'one');")
1757                .await
1758                .unwrap();
1759            conn.execute("INSERT INTO t1 VALUES (2, 'two');")
1760                .await
1761                .unwrap();
1762            let rows = conn.query("SELECT a, b FROM t1;").await.unwrap();
1763            assert_eq!(rows.len(), 2);
1764        });
1765    }
1766
1767    #[test]
1768    fn create_table_if_not_exists_no_error() {
1769        asupersync::test_utils::run_test(|| async {
1770            let conn = Connection::open(":memory:").await.unwrap();
1771            conn.execute("CREATE TABLE t1 (x INTEGER);").await.unwrap();
1772            // Should not error with IF NOT EXISTS
1773            conn.execute("CREATE TABLE IF NOT EXISTS t1 (x INTEGER);")
1774                .await
1775                .unwrap();
1776        });
1777    }
1778
1779    #[test]
1780    fn create_duplicate_table_errors() {
1781        asupersync::test_utils::run_test(|| async {
1782            let conn = Connection::open(":memory:").await.unwrap();
1783            conn.execute("CREATE TABLE t1 (x INTEGER);").await.unwrap();
1784            let err = conn
1785                .execute("CREATE TABLE t1 (x INTEGER);")
1786                .await
1787                .expect_err("duplicate table should fail");
1788            assert!(matches!(err, FrankenError::Internal(_)));
1789        });
1790    }
1791
1792    #[test]
1793    fn public_api_writable_schema_allows_filebacked_sqlite_master_insert() {
1794        asupersync::test_utils::run_test(|| async {
1795            let dir = tempfile::tempdir().expect("create temp dir");
1796            let db_path = dir.path().join("writable-schema.db");
1797            let conn = Connection::open(db_path.to_string_lossy().into_owned())
1798                .await
1799                .unwrap();
1800
1801            conn.execute("CREATE TABLE real_table (id INTEGER);")
1802                .await
1803                .unwrap();
1804            let before = conn.query_row("PRAGMA writable_schema;").await.unwrap();
1805            assert_eq!(row_values(&before), vec![SqliteValue::Integer(0)]);
1806
1807            conn.execute("PRAGMA writable_schema = ON;").await.unwrap();
1808            let after = conn.query_row("PRAGMA writable_schema;").await.unwrap();
1809            assert_eq!(row_values(&after), vec![SqliteValue::Integer(1)]);
1810
1811            let inserted = conn
1812                .execute(
1813                    "INSERT INTO sqlite_master(type, name, tbl_name, rootpage, sql) \
1814                 VALUES('table', 'fake_tbl', 'fake_tbl', 0, 'CREATE TABLE fake_tbl(x)');",
1815                )
1816                .await
1817                .unwrap();
1818            assert_eq!(inserted, 1);
1819
1820            let deleted = conn
1821                .execute("DELETE FROM sqlite_master WHERE name = 'fake_tbl';")
1822                .await
1823                .unwrap();
1824            assert_eq!(deleted, 1);
1825        });
1826    }
1827
1828    // ── DML affected-row counts (bd-118o) ─────────────────────────────────
1829
1830    #[test]
1831    fn execute_insert_returns_affected_count() {
1832        asupersync::test_utils::run_test(|| async {
1833            let conn = Connection::open(":memory:").await.unwrap();
1834            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
1835            assert_eq!(conn.execute("INSERT INTO t VALUES (1);").await.unwrap(), 1);
1836            assert_eq!(
1837                conn.execute("INSERT INTO t VALUES (2), (3), (4);")
1838                    .await
1839                    .unwrap(),
1840                3,
1841            );
1842        });
1843    }
1844
1845    #[test]
1846    fn execute_update_returns_affected_count() {
1847        asupersync::test_utils::run_test(|| async {
1848            let conn = Connection::open(":memory:").await.unwrap();
1849            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
1850            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
1851            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
1852            conn.execute("INSERT INTO t VALUES (3);").await.unwrap();
1853            assert_eq!(conn.execute("UPDATE t SET v = 0;").await.unwrap(), 3);
1854            assert_eq!(
1855                conn.execute("UPDATE t SET v = 99 WHERE v = 0;")
1856                    .await
1857                    .unwrap(),
1858                3
1859            );
1860        });
1861    }
1862
1863    #[test]
1864    fn execute_delete_returns_affected_count() {
1865        asupersync::test_utils::run_test(|| async {
1866            let conn = Connection::open(":memory:").await.unwrap();
1867            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
1868            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
1869            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
1870            conn.execute("INSERT INTO t VALUES (3);").await.unwrap();
1871            assert_eq!(conn.execute("DELETE FROM t WHERE v = 2;").await.unwrap(), 1);
1872            assert_eq!(conn.execute("DELETE FROM t;").await.unwrap(), 2);
1873        });
1874    }
1875
1876    // ── DML: UPDATE / DELETE ─────────────────────────────────────────────
1877
1878    #[test]
1879    fn update_modifies_rows() {
1880        asupersync::test_utils::run_test(|| async {
1881            let conn = Connection::open(":memory:").await.unwrap();
1882            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
1883            conn.execute("INSERT INTO t VALUES (10);").await.unwrap();
1884            conn.execute("INSERT INTO t VALUES (20);").await.unwrap();
1885            conn.execute("UPDATE t SET v = 99 WHERE v = 10;")
1886                .await
1887                .unwrap();
1888            let rows = conn.query("SELECT v FROM t;").await.unwrap();
1889            let vals: Vec<_> = rows.iter().map(row_values).collect();
1890            assert!(vals.contains(&vec![SqliteValue::Integer(99)]));
1891            assert!(vals.contains(&vec![SqliteValue::Integer(20)]));
1892        });
1893    }
1894
1895    #[test]
1896    fn update_preserves_integer_primary_key_rowid_alias() {
1897        asupersync::test_utils::run_test(|| async {
1898            let conn = Connection::open(":memory:").await.unwrap();
1899            conn.execute("CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER);")
1900                .await
1901                .unwrap();
1902            conn.execute("INSERT INTO accounts VALUES (1, 100);")
1903                .await
1904                .unwrap();
1905            conn.execute("INSERT INTO accounts VALUES (2, 200);")
1906                .await
1907                .unwrap();
1908
1909            conn.execute("UPDATE accounts SET balance = balance + 5 WHERE id = 1;")
1910                .await
1911                .unwrap();
1912
1913            let rows = conn
1914                .query("SELECT id, balance FROM accounts ORDER BY id;")
1915                .await
1916                .unwrap();
1917            assert_eq!(rows.len(), 2, "update must not create or lose rows");
1918            assert_eq!(
1919                row_values(&rows[0]),
1920                vec![SqliteValue::Integer(1), SqliteValue::Integer(105)],
1921                "id=1 row must be updated in place"
1922            );
1923            assert_eq!(
1924                row_values(&rows[1]),
1925                vec![SqliteValue::Integer(2), SqliteValue::Integer(200)],
1926                "id=2 row must remain unchanged"
1927            );
1928        });
1929    }
1930
1931    #[test]
1932    fn concurrent_same_row_deposit_commits_must_conflict_or_serialize() {
1933        asupersync::test_utils::run_test(|| async {
1934            let dir = tempfile::tempdir().unwrap();
1935            let db_path = dir.path().join("concurrent_same_row_deposit.db");
1936            let db = db_path.to_string_lossy().to_string();
1937
1938            {
1939                let conn = Connection::open(&db).await.unwrap();
1940                conn.execute("PRAGMA fsqlite.concurrent_mode=ON;")
1941                    .await
1942                    .unwrap();
1943                conn.execute(
1944                    "CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER NOT NULL);",
1945                )
1946                .await
1947                .unwrap();
1948                conn.execute("INSERT INTO accounts VALUES (1, 0);")
1949                    .await
1950                    .unwrap();
1951            }
1952
1953            let conn1 = Connection::open(&db).await.unwrap();
1954            let conn2 = Connection::open(&db).await.unwrap();
1955            conn1
1956                .execute("PRAGMA fsqlite.concurrent_mode=ON;")
1957                .await
1958                .unwrap();
1959            conn2
1960                .execute("PRAGMA fsqlite.concurrent_mode=ON;")
1961                .await
1962                .unwrap();
1963
1964            conn1.execute("BEGIN CONCURRENT;").await.unwrap();
1965            conn2.execute("BEGIN CONCURRENT;").await.unwrap();
1966
1967            assert_eq!(
1968                conn1
1969                    .execute("UPDATE accounts SET balance = balance + 1 WHERE id = 1;")
1970                    .await
1971                    .unwrap(),
1972                1
1973            );
1974            let update2 = conn2
1975                .execute("UPDATE accounts SET balance = balance + 1 WHERE id = 1;")
1976                .await;
1977
1978            let commit1 = conn1.execute("COMMIT;").await;
1979            let commit2 = match update2 {
1980                Ok(changes2) => {
1981                    assert_eq!(changes2, 1, "second update should affect one row");
1982                    conn2.execute("COMMIT;").await
1983                }
1984                Err(err) => {
1985                    assert!(
1986                        err.is_transient(),
1987                        "second concurrent writer should fail transiently on conflict, got: {err}"
1988                    );
1989                    let rollback = conn2.execute("ROLLBACK;").await;
1990                    assert!(
1991                        rollback.is_ok(),
1992                        "second writer should remain rollback-able after transient conflict: {rollback:?}"
1993                    );
1994                    Err(err)
1995                }
1996            };
1997
1998            let verify = Connection::open(&db).await.unwrap();
1999            let row = verify
2000                .query_row("SELECT balance FROM accounts WHERE id = 1;")
2001                .await
2002                .unwrap();
2003            let balance = row.get(0).cloned().unwrap_or(SqliteValue::Null);
2004            match (commit1, commit2) {
2005                (Ok(_), Ok(_)) => {
2006                    assert_eq!(
2007                        balance,
2008                        SqliteValue::Integer(2),
2009                        "if both commits succeed, both deposits must be visible"
2010                    );
2011                }
2012                (Ok(_), Err(err)) | (Err(err), Ok(_)) => {
2013                    assert!(
2014                        err.is_transient(),
2015                        "conflicting concurrent writer should fail with transient busy snapshot/busy, got: {err}"
2016                    );
2017                    assert_eq!(
2018                        balance,
2019                        SqliteValue::Integer(1),
2020                        "if one writer aborts, exactly one deposit should persist"
2021                    );
2022                }
2023                (Err(err1), Err(err2)) => {
2024                    panic!("at least one concurrent writer must commit: err1={err1}; err2={err2}");
2025                }
2026            }
2027        });
2028    }
2029
2030    #[test]
2031    fn delete_removes_rows() {
2032        asupersync::test_utils::run_test(|| async {
2033            let conn = Connection::open(":memory:").await.unwrap();
2034            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2035            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
2036            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
2037            conn.execute("INSERT INTO t VALUES (3);").await.unwrap();
2038            conn.execute("DELETE FROM t WHERE v = 2;").await.unwrap();
2039            let rows = conn.query("SELECT v FROM t;").await.unwrap();
2040            assert_eq!(rows.len(), 2);
2041            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2042            assert!(vals.contains(&SqliteValue::Integer(1)));
2043            assert!(vals.contains(&SqliteValue::Integer(3)));
2044        });
2045    }
2046
2047    // ── Type handling ────────────────────────────────────────────────────
2048
2049    #[test]
2050    fn null_value_roundtrip() {
2051        asupersync::test_utils::run_test(|| async {
2052            let conn = Connection::open(":memory:").await.unwrap();
2053            let row = conn.query_row("SELECT NULL;").await.unwrap();
2054            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
2055        });
2056    }
2057
2058    #[test]
2059    #[allow(clippy::approx_constant)]
2060    fn real_value_roundtrip() {
2061        asupersync::test_utils::run_test(|| async {
2062            let conn = Connection::open(":memory:").await.unwrap();
2063            let row = conn.query_row("SELECT 3.14;").await.unwrap();
2064            if let SqliteValue::Float(v) = &row_values(&row)[0] {
2065                assert!((*v - 3.14).abs() < f64::EPSILON);
2066            } else {
2067                unreachable!("expected Float value");
2068            }
2069        });
2070    }
2071
2072    #[test]
2073    fn text_value_roundtrip() {
2074        asupersync::test_utils::run_test(|| async {
2075            let conn = Connection::open(":memory:").await.unwrap();
2076            let row = conn.query_row("SELECT 'hello world';").await.unwrap();
2077            assert_eq!(
2078                row_values(&row),
2079                vec![SqliteValue::Text("hello world".into())]
2080            );
2081        });
2082    }
2083
2084    #[test]
2085    fn blob_value_via_params() {
2086        asupersync::test_utils::run_test(|| async {
2087            let conn = Connection::open(":memory:").await.unwrap();
2088            let blob = vec![0xDE, 0xAD, 0xBE, 0xEF];
2089            let row = conn
2090                .query_row_with_params("SELECT ?1;", &[SqliteValue::Blob(blob.clone().into())])
2091                .await
2092                .unwrap();
2093            assert_eq!(row_values(&row), vec![SqliteValue::Blob(blob.into())]);
2094        });
2095    }
2096
2097    // ── Transaction control ──────────────────────────────────────────────
2098
2099    #[test]
2100    fn in_transaction_flag() {
2101        asupersync::test_utils::run_test(|| async {
2102            let conn = Connection::open(":memory:").await.unwrap();
2103            assert!(!conn.in_transaction());
2104            conn.execute("BEGIN;").await.unwrap();
2105            assert!(conn.in_transaction());
2106            conn.execute("COMMIT;").await.unwrap();
2107            assert!(!conn.in_transaction());
2108        });
2109    }
2110
2111    #[test]
2112    fn begin_commit_persists_changes() {
2113        asupersync::test_utils::run_test(|| async {
2114            let conn = Connection::open(":memory:").await.unwrap();
2115            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2116            conn.execute("BEGIN;").await.unwrap();
2117            conn.execute("INSERT INTO t VALUES (42);").await.unwrap();
2118            conn.execute("COMMIT;").await.unwrap();
2119            let rows = conn.query("SELECT v FROM t;").await.unwrap();
2120            assert_eq!(rows.len(), 1);
2121            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(42)]);
2122        });
2123    }
2124
2125    #[test]
2126    fn rollback_reverts_changes() {
2127        asupersync::test_utils::run_test(|| async {
2128            let conn = Connection::open(":memory:").await.unwrap();
2129            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2130            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
2131
2132            let m1 = conn.query("SELECT * FROM sqlite_master;").await.unwrap();
2133            eprintln!("MAIN BEFORE BEGIN: {:?}", m1);
2134
2135            conn.execute("BEGIN;").await.unwrap();
2136            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
2137            conn.execute("ROLLBACK;").await.unwrap();
2138
2139            let m2 = conn.query("SELECT * FROM sqlite_master;").await.unwrap();
2140            eprintln!("MAIN AFTER ROLLBACK: {:?}", m2);
2141
2142            let rows = conn.query("SELECT v FROM t;").await.unwrap();
2143            assert_eq!(rows.len(), 1);
2144            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
2145        });
2146    }
2147
2148    #[test]
2149    fn nested_begin_errors() {
2150        asupersync::test_utils::run_test(|| async {
2151            let conn = Connection::open(":memory:").await.unwrap();
2152            conn.execute("BEGIN;").await.unwrap();
2153            let err = conn
2154                .execute("BEGIN;")
2155                .await
2156                .expect_err("nested begin should fail");
2157            assert!(matches!(err, FrankenError::Internal(_)));
2158        });
2159    }
2160
2161    #[test]
2162    fn commit_without_transaction_errors() {
2163        asupersync::test_utils::run_test(|| async {
2164            let conn = Connection::open(":memory:").await.unwrap();
2165            let err = conn
2166                .execute("COMMIT;")
2167                .await
2168                .expect_err("commit without txn should fail");
2169            assert!(matches!(err, FrankenError::Internal(_)));
2170        });
2171    }
2172
2173    #[test]
2174    fn rollback_without_transaction_errors() {
2175        asupersync::test_utils::run_test(|| async {
2176            let conn = Connection::open(":memory:").await.unwrap();
2177            let err = conn
2178                .execute("ROLLBACK;")
2179                .await
2180                .expect_err("rollback without txn should fail");
2181            assert!(matches!(err, FrankenError::Internal(_)));
2182        });
2183    }
2184
2185    // ── Savepoint ────────────────────────────────────────────────────────
2186
2187    #[test]
2188    fn savepoint_and_rollback_to() {
2189        asupersync::test_utils::run_test(|| async {
2190            let conn = Connection::open(":memory:").await.unwrap();
2191            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2192            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
2193            conn.execute("SAVEPOINT sp1;").await.unwrap();
2194            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
2195            conn.execute("ROLLBACK TO sp1;").await.unwrap();
2196            let rows = conn.query("SELECT v FROM t;").await.unwrap();
2197            assert_eq!(rows.len(), 1);
2198            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
2199        });
2200    }
2201
2202    #[test]
2203    fn savepoint_release_commits_changes() {
2204        asupersync::test_utils::run_test(|| async {
2205            let conn = Connection::open(":memory:").await.unwrap();
2206            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2207            conn.execute("SAVEPOINT sp1;").await.unwrap();
2208            conn.execute("INSERT INTO t VALUES (100);").await.unwrap();
2209            conn.execute("RELEASE sp1;").await.unwrap();
2210            let rows = conn.query("SELECT v FROM t;").await.unwrap();
2211            assert_eq!(rows.len(), 1);
2212            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(100)]);
2213        });
2214    }
2215
2216    #[test]
2217    fn release_nonexistent_savepoint_errors() {
2218        asupersync::test_utils::run_test(|| async {
2219            let conn = Connection::open(":memory:").await.unwrap();
2220            conn.execute("BEGIN;").await.unwrap();
2221            let err = conn
2222                .execute("RELEASE nosuch;")
2223                .await
2224                .expect_err("release nonexistent savepoint should fail");
2225            assert!(matches!(err, FrankenError::Internal(_)));
2226        });
2227    }
2228
2229    // ── Parse error ──────────────────────────────────────────────────────
2230
2231    #[test]
2232    fn parse_error_on_invalid_sql() {
2233        asupersync::test_utils::run_test(|| async {
2234            let conn = Connection::open(":memory:").await.unwrap();
2235            assert!(conn.query("NOT VALID SQL;").await.is_err());
2236        });
2237    }
2238
2239    // ── Multiple statements ──────────────────────────────────────────────
2240
2241    #[test]
2242    fn multiple_statements_in_query() {
2243        asupersync::test_utils::run_test(|| async {
2244            let conn = Connection::open(":memory:").await.unwrap();
2245            conn.execute("CREATE TABLE t (v INTEGER);").await.unwrap();
2246            // query() processes all statements, returns rows from last
2247            let rows = conn
2248                .query("INSERT INTO t VALUES (1); INSERT INTO t VALUES (2); SELECT v FROM t;")
2249                .await
2250                .unwrap();
2251            assert_eq!(rows.len(), 2);
2252        });
2253    }
2254
2255    // ── Expression arithmetic ────────────────────────────────────────────
2256
2257    #[test]
2258    fn arithmetic_expressions() {
2259        asupersync::test_utils::run_test(|| async {
2260            let conn = Connection::open(":memory:").await.unwrap();
2261            let row = conn
2262                .query_row("SELECT 10 - 3, 4 * 5, 20 / 4;")
2263                .await
2264                .unwrap();
2265            assert_eq!(
2266                row_values(&row),
2267                vec![
2268                    SqliteValue::Integer(7),
2269                    SqliteValue::Integer(20),
2270                    SqliteValue::Integer(5),
2271                ]
2272            );
2273        });
2274    }
2275
2276    #[test]
2277    fn string_concatenation() {
2278        asupersync::test_utils::run_test(|| async {
2279            let conn = Connection::open(":memory:").await.unwrap();
2280            let row = conn.query_row("SELECT 'foo' || 'bar';").await.unwrap();
2281            assert_eq!(row_values(&row), vec![SqliteValue::Text("foobar".into())]);
2282        });
2283    }
2284
2285    // ── Compound WHERE predicates (bd-2832) ────────────────────────────
2286
2287    async fn setup_three_rows(conn: &Connection) {
2288        conn.execute("CREATE TABLE t3 (a INTEGER, b TEXT);")
2289            .await
2290            .unwrap();
2291        conn.execute("INSERT INTO t3 VALUES (1, 'one');")
2292            .await
2293            .unwrap();
2294        conn.execute("INSERT INTO t3 VALUES (2, 'two');")
2295            .await
2296            .unwrap();
2297        conn.execute("INSERT INTO t3 VALUES (3, 'three');")
2298            .await
2299            .unwrap();
2300    }
2301
2302    #[test]
2303    fn where_and_predicate() {
2304        asupersync::test_utils::run_test(|| async {
2305            let conn = Connection::open(":memory:").await.unwrap();
2306            setup_three_rows(&conn).await;
2307            let rows = conn
2308                .query("SELECT a FROM t3 WHERE a > 1 AND b = 'two';")
2309                .await
2310                .unwrap();
2311            assert_eq!(rows.len(), 1);
2312            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(2)]);
2313        });
2314    }
2315
2316    #[test]
2317    fn where_or_predicate() {
2318        asupersync::test_utils::run_test(|| async {
2319            let conn = Connection::open(":memory:").await.unwrap();
2320            setup_three_rows(&conn).await;
2321            let rows = conn
2322                .query("SELECT a FROM t3 WHERE a = 1 OR a = 3;")
2323                .await
2324                .unwrap();
2325            assert_eq!(rows.len(), 2);
2326            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2327            assert!(vals.contains(&SqliteValue::Integer(1)));
2328            assert!(vals.contains(&SqliteValue::Integer(3)));
2329        });
2330    }
2331
2332    #[test]
2333    fn where_comparison_operators() {
2334        asupersync::test_utils::run_test(|| async {
2335            let conn = Connection::open(":memory:").await.unwrap();
2336            setup_three_rows(&conn).await;
2337            // Greater than
2338            let rows = conn.query("SELECT a FROM t3 WHERE a > 2;").await.unwrap();
2339            assert_eq!(rows.len(), 1);
2340            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(3)]);
2341            // Less than or equal
2342            let rows = conn.query("SELECT a FROM t3 WHERE a <= 2;").await.unwrap();
2343            assert_eq!(rows.len(), 2);
2344            // Not equal
2345            let rows = conn.query("SELECT a FROM t3 WHERE a != 2;").await.unwrap();
2346            assert_eq!(rows.len(), 2);
2347        });
2348    }
2349
2350    // ── NULL handling (WHERE) ──────────────────────────────────────────
2351
2352    #[test]
2353    fn where_is_null() {
2354        asupersync::test_utils::run_test(|| async {
2355            let conn = Connection::open(":memory:").await.unwrap();
2356            conn.execute("CREATE TABLE tn (a INTEGER, b TEXT);")
2357                .await
2358                .unwrap();
2359            conn.execute("INSERT INTO tn VALUES (1, 'x');")
2360                .await
2361                .unwrap();
2362            conn.execute("INSERT INTO tn VALUES (2, NULL);")
2363                .await
2364                .unwrap();
2365            let rows = conn
2366                .query("SELECT a FROM tn WHERE b IS NULL;")
2367                .await
2368                .unwrap();
2369            assert_eq!(rows.len(), 1);
2370            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(2)]);
2371        });
2372    }
2373
2374    #[test]
2375    fn where_is_not_null() {
2376        asupersync::test_utils::run_test(|| async {
2377            let conn = Connection::open(":memory:").await.unwrap();
2378            conn.execute("CREATE TABLE tn2 (a INTEGER, b TEXT);")
2379                .await
2380                .unwrap();
2381            conn.execute("INSERT INTO tn2 VALUES (1, 'x');")
2382                .await
2383                .unwrap();
2384            conn.execute("INSERT INTO tn2 VALUES (2, NULL);")
2385                .await
2386                .unwrap();
2387            let rows = conn
2388                .query("SELECT a FROM tn2 WHERE b IS NOT NULL;")
2389                .await
2390                .unwrap();
2391            assert_eq!(rows.len(), 1);
2392            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
2393        });
2394    }
2395
2396    // ── NULL handling (expression) ─────────────────────────────────────
2397
2398    #[test]
2399    fn coalesce_expression() {
2400        asupersync::test_utils::run_test(|| async {
2401            let conn = Connection::open(":memory:").await.unwrap();
2402            let row = conn
2403                .query_row("SELECT COALESCE(NULL, NULL, 42);")
2404                .await
2405                .unwrap();
2406            assert_eq!(row_values(&row), vec![SqliteValue::Integer(42)]);
2407        });
2408    }
2409
2410    #[test]
2411    fn nullif_expression() {
2412        asupersync::test_utils::run_test(|| async {
2413            let conn = Connection::open(":memory:").await.unwrap();
2414            let row = conn.query_row("SELECT NULLIF(1, 1);").await.unwrap();
2415            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
2416            let row = conn.query_row("SELECT NULLIF(1, 2);").await.unwrap();
2417            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
2418        });
2419    }
2420
2421    // ── CASE WHEN ──────────────────────────────────────────────────────
2422
2423    #[test]
2424    fn case_when_expression() {
2425        asupersync::test_utils::run_test(|| async {
2426            let conn = Connection::open(":memory:").await.unwrap();
2427            let row = conn
2428                .query_row("SELECT CASE WHEN 1 > 0 THEN 'yes' ELSE 'no' END;")
2429                .await
2430                .unwrap();
2431            assert_eq!(row_values(&row), vec![SqliteValue::Text("yes".into())]);
2432        });
2433    }
2434
2435    #[test]
2436    fn case_simple_form() {
2437        asupersync::test_utils::run_test(|| async {
2438            let conn = Connection::open(":memory:").await.unwrap();
2439            let row = conn
2440                .query_row("SELECT CASE 2 WHEN 1 THEN 'a' WHEN 2 THEN 'b' ELSE 'c' END;")
2441                .await
2442                .unwrap();
2443            assert_eq!(row_values(&row), vec![SqliteValue::Text("b".into())]);
2444        });
2445    }
2446
2447    // ── Built-in functions ─────────────────────────────────────────────
2448
2449    #[test]
2450    fn builtin_abs() {
2451        asupersync::test_utils::run_test(|| async {
2452            let conn = Connection::open(":memory:").await.unwrap();
2453            let row = conn.query_row("SELECT ABS(-42);").await.unwrap();
2454            assert_eq!(row_values(&row), vec![SqliteValue::Integer(42)]);
2455        });
2456    }
2457
2458    #[test]
2459    fn builtin_length() {
2460        asupersync::test_utils::run_test(|| async {
2461            let conn = Connection::open(":memory:").await.unwrap();
2462            let row = conn.query_row("SELECT LENGTH('hello');").await.unwrap();
2463            assert_eq!(row_values(&row), vec![SqliteValue::Integer(5)]);
2464        });
2465    }
2466
2467    #[test]
2468    fn builtin_upper_lower() {
2469        asupersync::test_utils::run_test(|| async {
2470            let conn = Connection::open(":memory:").await.unwrap();
2471            let row = conn
2472                .query_row("SELECT UPPER('hello'), LOWER('WORLD');")
2473                .await
2474                .unwrap();
2475            assert_eq!(
2476                row_values(&row),
2477                vec![
2478                    SqliteValue::Text("HELLO".into()),
2479                    SqliteValue::Text("world".into()),
2480                ]
2481            );
2482        });
2483    }
2484
2485    #[test]
2486    fn builtin_typeof() {
2487        asupersync::test_utils::run_test(|| async {
2488            let conn = Connection::open(":memory:").await.unwrap();
2489            let row = conn.query_row("SELECT TYPEOF(42);").await.unwrap();
2490            assert_eq!(row_values(&row), vec![SqliteValue::Text("integer".into())]);
2491        });
2492    }
2493
2494    // ── CAST ───────────────────────────────────────────────────────────
2495
2496    #[test]
2497    fn cast_integer_to_text() {
2498        asupersync::test_utils::run_test(|| async {
2499            let conn = Connection::open(":memory:").await.unwrap();
2500            let row = conn.query_row("SELECT CAST(42 AS TEXT);").await.unwrap();
2501            assert_eq!(row_values(&row), vec![SqliteValue::Text("42".into())]);
2502        });
2503    }
2504
2505    #[test]
2506    fn cast_text_to_integer() {
2507        asupersync::test_utils::run_test(|| async {
2508            let conn = Connection::open(":memory:").await.unwrap();
2509            let row = conn
2510                .query_row("SELECT CAST('123' AS INTEGER);")
2511                .await
2512                .unwrap();
2513            assert_eq!(row_values(&row), vec![SqliteValue::Integer(123)]);
2514        });
2515    }
2516
2517    // ── Blob literal ───────────────────────────────────────────────────
2518
2519    #[test]
2520    fn blob_literal_hex() {
2521        asupersync::test_utils::run_test(|| async {
2522            let conn = Connection::open(":memory:").await.unwrap();
2523            let row = conn.query_row("SELECT X'DEADBEEF';").await.unwrap();
2524            assert_eq!(
2525                row_values(&row),
2526                vec![SqliteValue::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF].into())]
2527            );
2528        });
2529    }
2530
2531    // ── Unary operators ────────────────────────────────────────────────
2532
2533    #[test]
2534    fn unary_minus() {
2535        asupersync::test_utils::run_test(|| async {
2536            let conn = Connection::open(":memory:").await.unwrap();
2537            let row = conn.query_row("SELECT -42;").await.unwrap();
2538            assert_eq!(row_values(&row), vec![SqliteValue::Integer(-42)]);
2539        });
2540    }
2541
2542    #[test]
2543    fn not_operator() {
2544        asupersync::test_utils::run_test(|| async {
2545            let conn = Connection::open(":memory:").await.unwrap();
2546            let row = conn.query_row("SELECT NOT 0;").await.unwrap();
2547            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
2548        });
2549    }
2550
2551    // ── ORDER BY / LIMIT (expression path) ─────────────────────────────
2552
2553    #[test]
2554    fn values_order_by() {
2555        asupersync::test_utils::run_test(|| async {
2556            let conn = Connection::open(":memory:").await.unwrap();
2557            assert!(
2558                conn.query("VALUES (3), (1), (2) ORDER BY 1;")
2559                    .await
2560                    .is_err(),
2561                "bare VALUES cannot carry an ORDER BY clause in SQLite grammar"
2562            );
2563            let rows = conn
2564                .query("SELECT * FROM (VALUES (3), (1), (2)) ORDER BY 1;")
2565                .await
2566                .unwrap();
2567            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2568            assert_eq!(
2569                vals,
2570                vec![
2571                    SqliteValue::Integer(1),
2572                    SqliteValue::Integer(2),
2573                    SqliteValue::Integer(3),
2574                ]
2575            );
2576        });
2577    }
2578
2579    #[test]
2580    fn values_order_by_desc_with_limit() {
2581        asupersync::test_utils::run_test(|| async {
2582            let conn = Connection::open(":memory:").await.unwrap();
2583            let rows = conn
2584                .query("SELECT * FROM (VALUES (3), (1), (2)) ORDER BY 1 DESC LIMIT 2;")
2585                .await
2586                .unwrap();
2587            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2588            assert_eq!(vals, vec![SqliteValue::Integer(3), SqliteValue::Integer(2)]);
2589        });
2590    }
2591
2592    #[test]
2593    fn values_limit_offset() {
2594        asupersync::test_utils::run_test(|| async {
2595            let conn = Connection::open(":memory:").await.unwrap();
2596            assert!(
2597                conn.query("VALUES (10), (20), (30), (40) LIMIT 2 OFFSET 1;")
2598                    .await
2599                    .is_err(),
2600                "bare VALUES cannot carry a LIMIT clause in SQLite grammar"
2601            );
2602            let rows = conn
2603                .query("SELECT * FROM (VALUES (10), (20), (30), (40)) ORDER BY 1 LIMIT 2 OFFSET 1;")
2604                .await
2605                .unwrap();
2606            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2607            assert_eq!(
2608                vals,
2609                vec![SqliteValue::Integer(20), SqliteValue::Integer(30)]
2610            );
2611        });
2612    }
2613
2614    // ── DELETE without WHERE (all rows) ────────────────────────────────
2615
2616    #[test]
2617    fn delete_all_rows() {
2618        asupersync::test_utils::run_test(|| async {
2619            let conn = Connection::open(":memory:").await.unwrap();
2620            setup_three_rows(&conn).await;
2621            conn.execute("DELETE FROM t3;").await.unwrap();
2622            let rows = conn.query("SELECT a FROM t3;").await.unwrap();
2623            assert_eq!(rows.len(), 0);
2624        });
2625    }
2626
2627    // ── Non-column result expressions (bd-19g7) ────────────────────────
2628
2629    #[test]
2630    fn select_expression_column_arithmetic() {
2631        asupersync::test_utils::run_test(|| async {
2632            let conn = Connection::open(":memory:").await.unwrap();
2633            conn.execute("CREATE TABLE te (a INTEGER);").await.unwrap();
2634            conn.execute("INSERT INTO te VALUES (10);").await.unwrap();
2635            conn.execute("INSERT INTO te VALUES (20);").await.unwrap();
2636            let rows = conn.query("SELECT a + 1 FROM te;").await.unwrap();
2637            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2638            assert!(vals.contains(&SqliteValue::Integer(11)));
2639            assert!(vals.contains(&SqliteValue::Integer(21)));
2640        });
2641    }
2642
2643    #[test]
2644    fn select_expression_column_with_literal() {
2645        asupersync::test_utils::run_test(|| async {
2646            let conn = Connection::open(":memory:").await.unwrap();
2647            conn.execute("CREATE TABLE te2 (a INTEGER, b TEXT);")
2648                .await
2649                .unwrap();
2650            conn.execute("INSERT INTO te2 VALUES (5, 'hello');")
2651                .await
2652                .unwrap();
2653            let rows = conn.query("SELECT a * 2, b FROM te2;").await.unwrap();
2654            assert_eq!(rows.len(), 1);
2655            assert_eq!(
2656                row_values(&rows[0]),
2657                vec![SqliteValue::Integer(10), SqliteValue::Text("hello".into())]
2658            );
2659        });
2660    }
2661
2662    // ── Multi-row INSERT (bd-2of2) ────────────────────────────────────
2663
2664    #[test]
2665    fn insert_multi_row_values() {
2666        asupersync::test_utils::run_test(|| async {
2667            let conn = Connection::open(":memory:").await.unwrap();
2668            conn.execute("CREATE TABLE tm (v INTEGER);").await.unwrap();
2669            conn.execute("INSERT INTO tm VALUES (1), (2), (3);")
2670                .await
2671                .unwrap();
2672            let rows = conn.query("SELECT v FROM tm;").await.unwrap();
2673            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2674            assert_eq!(vals.len(), 3);
2675            assert!(vals.contains(&SqliteValue::Integer(1)));
2676            assert!(vals.contains(&SqliteValue::Integer(2)));
2677            assert!(vals.contains(&SqliteValue::Integer(3)));
2678        });
2679    }
2680
2681    // ── IN / BETWEEN / LIKE (bd-3vpo) ─────────────────────────────────
2682
2683    #[test]
2684    fn in_expression_only() {
2685        asupersync::test_utils::run_test(|| async {
2686            // Test IN without any table - pure expression evaluation
2687            let conn = Connection::open(":memory:").await.unwrap();
2688            let row = conn.query_row("SELECT 2 IN (1, 2, 3);").await.unwrap();
2689            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
2690        });
2691    }
2692
2693    #[test]
2694    fn between_expression_only() {
2695        asupersync::test_utils::run_test(|| async {
2696            let conn = Connection::open(":memory:").await.unwrap();
2697            let row = conn.query_row("SELECT 2 BETWEEN 1 AND 3;").await.unwrap();
2698            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
2699        });
2700    }
2701
2702    #[test]
2703    fn where_in_operator() {
2704        asupersync::test_utils::run_test(|| async {
2705            let conn = Connection::open(":memory:").await.unwrap();
2706            setup_three_rows(&conn).await;
2707            let rows = conn
2708                .query("SELECT a FROM t3 WHERE a IN (1, 3);")
2709                .await
2710                .unwrap();
2711            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2712            assert_eq!(vals.len(), 2);
2713            assert!(vals.contains(&SqliteValue::Integer(1)));
2714            assert!(vals.contains(&SqliteValue::Integer(3)));
2715        });
2716    }
2717
2718    #[test]
2719    fn where_between_operator() {
2720        asupersync::test_utils::run_test(|| async {
2721            let conn = Connection::open(":memory:").await.unwrap();
2722            setup_three_rows(&conn).await;
2723            let rows = conn
2724                .query("SELECT a FROM t3 WHERE a BETWEEN 1 AND 2;")
2725                .await
2726                .unwrap();
2727            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2728            assert_eq!(vals.len(), 2);
2729            assert!(vals.contains(&SqliteValue::Integer(1)));
2730            assert!(vals.contains(&SqliteValue::Integer(2)));
2731        });
2732    }
2733
2734    #[test]
2735    fn where_like_operator() {
2736        asupersync::test_utils::run_test(|| async {
2737            let conn = Connection::open(":memory:").await.unwrap();
2738            setup_three_rows(&conn).await;
2739            let rows = conn
2740                .query("SELECT b FROM t3 WHERE b LIKE 't%';")
2741                .await
2742                .unwrap();
2743            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2744            assert_eq!(vals.len(), 2);
2745            assert!(vals.contains(&SqliteValue::Text("two".into())));
2746            assert!(vals.contains(&SqliteValue::Text("three".into())));
2747        });
2748    }
2749
2750    // ── Aggregates (bd-xldj) ────────────────────────────────────────────
2751
2752    #[test]
2753    fn aggregate_count_star() {
2754        asupersync::test_utils::run_test(|| async {
2755            let conn = Connection::open(":memory:").await.unwrap();
2756            setup_three_rows(&conn).await;
2757            let row = conn.query_row("SELECT COUNT(*) FROM t3;").await.unwrap();
2758            assert_eq!(row_values(&row), vec![SqliteValue::Integer(3)]);
2759        });
2760    }
2761
2762    #[test]
2763    fn aggregate_sum_min_max() {
2764        asupersync::test_utils::run_test(|| async {
2765            let conn = Connection::open(":memory:").await.unwrap();
2766            setup_three_rows(&conn).await;
2767            let row = conn
2768                .query_row("SELECT SUM(a), MIN(a), MAX(a) FROM t3;")
2769                .await
2770                .unwrap();
2771            assert_eq!(
2772                row_values(&row),
2773                vec![
2774                    SqliteValue::Integer(6),
2775                    SqliteValue::Integer(1),
2776                    SqliteValue::Integer(3),
2777                ]
2778            );
2779        });
2780    }
2781
2782    #[test]
2783    fn aggregate_avg() {
2784        asupersync::test_utils::run_test(|| async {
2785            let conn = Connection::open(":memory:").await.unwrap();
2786            setup_three_rows(&conn).await;
2787            let row = conn.query_row("SELECT AVG(a) FROM t3;").await.unwrap();
2788            // AVG(1,2,3) = 2.0
2789            assert_eq!(row_values(&row), vec![SqliteValue::Float(2.0)]);
2790        });
2791    }
2792
2793    // ── UPDATE all rows (no WHERE) ─────────────────────────────────────
2794
2795    #[test]
2796    fn update_all_rows() {
2797        asupersync::test_utils::run_test(|| async {
2798            let conn = Connection::open(":memory:").await.unwrap();
2799            conn.execute("CREATE TABLE tu (v INTEGER);").await.unwrap();
2800            conn.execute("INSERT INTO tu VALUES (1);").await.unwrap();
2801            conn.execute("INSERT INTO tu VALUES (2);").await.unwrap();
2802            conn.execute("UPDATE tu SET v = 0;").await.unwrap();
2803            let rows = conn.query("SELECT v FROM tu;").await.unwrap();
2804            assert!(
2805                rows.iter()
2806                    .all(|r| row_values(r) == vec![SqliteValue::Integer(0)])
2807            );
2808        });
2809    }
2810
2811    // ═══════════════════════════════════════════════════════════════════
2812    // bd-2832: Expanded SQL pattern coverage (IvoryWaterfall)
2813    // ═══════════════════════════════════════════════════════════════════
2814
2815    async fn setup_bd2832(conn: &Connection) {
2816        conn.execute("CREATE TABLE tp (a INTEGER, b TEXT, c REAL);")
2817            .await
2818            .unwrap();
2819        conn.execute("INSERT INTO tp VALUES (1, 'alpha', 1.5);")
2820            .await
2821            .unwrap();
2822        conn.execute("INSERT INTO tp VALUES (2, 'beta', 2.5);")
2823            .await
2824            .unwrap();
2825        conn.execute("INSERT INTO tp VALUES (3, 'gamma', 3.5);")
2826            .await
2827            .unwrap();
2828        conn.execute("INSERT INTO tp VALUES (4, NULL, 4.5);")
2829            .await
2830            .unwrap();
2831        conn.execute("INSERT INTO tp VALUES (5, 'delta', 5.5);")
2832            .await
2833            .unwrap();
2834    }
2835
2836    // ── WHERE NOT ───────────────────────────────────────────────────────
2837
2838    #[test]
2839    fn where_not_predicate() {
2840        asupersync::test_utils::run_test(|| async {
2841            let conn = Connection::open(":memory:").await.unwrap();
2842            setup_bd2832(&conn).await;
2843            let rows = conn
2844                .query("SELECT a FROM tp WHERE NOT (a > 3);")
2845                .await
2846                .unwrap();
2847            assert_eq!(rows.len(), 3);
2848            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2849            assert!(vals.contains(&SqliteValue::Integer(1)));
2850            assert!(vals.contains(&SqliteValue::Integer(2)));
2851            assert!(vals.contains(&SqliteValue::Integer(3)));
2852        });
2853    }
2854
2855    // ── Comparison operators (>=, <) ────────────────────────────────────
2856
2857    #[test]
2858    fn where_greater_equal() {
2859        asupersync::test_utils::run_test(|| async {
2860            let conn = Connection::open(":memory:").await.unwrap();
2861            setup_bd2832(&conn).await;
2862            let rows = conn.query("SELECT a FROM tp WHERE a >= 4;").await.unwrap();
2863            assert_eq!(rows.len(), 2);
2864        });
2865    }
2866
2867    #[test]
2868    fn where_less_than() {
2869        asupersync::test_utils::run_test(|| async {
2870            let conn = Connection::open(":memory:").await.unwrap();
2871            setup_bd2832(&conn).await;
2872            let rows = conn.query("SELECT a FROM tp WHERE a < 3;").await.unwrap();
2873            assert_eq!(rows.len(), 2);
2874        });
2875    }
2876
2877    // ── Table-backed ORDER BY ASC / DESC ────────────────────────────────
2878
2879    #[test]
2880    fn table_order_by_asc() {
2881        asupersync::test_utils::run_test(|| async {
2882            let conn = Connection::open(":memory:").await.unwrap();
2883            conn.execute("CREATE TABLE tord (v INTEGER);")
2884                .await
2885                .unwrap();
2886            conn.execute("INSERT INTO tord VALUES (3);").await.unwrap();
2887            conn.execute("INSERT INTO tord VALUES (1);").await.unwrap();
2888            conn.execute("INSERT INTO tord VALUES (2);").await.unwrap();
2889            let rows = conn.query("SELECT v FROM tord ORDER BY v;").await.unwrap();
2890            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2891            assert_eq!(
2892                vals,
2893                vec![
2894                    SqliteValue::Integer(1),
2895                    SqliteValue::Integer(2),
2896                    SqliteValue::Integer(3),
2897                ]
2898            );
2899        });
2900    }
2901
2902    #[test]
2903    fn table_order_by_desc() {
2904        asupersync::test_utils::run_test(|| async {
2905            let conn = Connection::open(":memory:").await.unwrap();
2906            conn.execute("CREATE TABLE tord2 (v INTEGER);")
2907                .await
2908                .unwrap();
2909            conn.execute("INSERT INTO tord2 VALUES (3);").await.unwrap();
2910            conn.execute("INSERT INTO tord2 VALUES (1);").await.unwrap();
2911            conn.execute("INSERT INTO tord2 VALUES (2);").await.unwrap();
2912            let rows = conn
2913                .query("SELECT v FROM tord2 ORDER BY v DESC;")
2914                .await
2915                .unwrap();
2916            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2917            assert_eq!(
2918                vals,
2919                vec![
2920                    SqliteValue::Integer(3),
2921                    SqliteValue::Integer(2),
2922                    SqliteValue::Integer(1),
2923                ]
2924            );
2925        });
2926    }
2927
2928    // ── Table-backed LIMIT / OFFSET ─────────────────────────────────────
2929
2930    #[test]
2931    fn table_limit() {
2932        asupersync::test_utils::run_test(|| async {
2933            let conn = Connection::open(":memory:").await.unwrap();
2934            setup_bd2832(&conn).await;
2935            let rows = conn.query("SELECT a FROM tp LIMIT 3;").await.unwrap();
2936            assert_eq!(rows.len(), 3);
2937        });
2938    }
2939
2940    #[test]
2941    fn table_limit_offset() {
2942        asupersync::test_utils::run_test(|| async {
2943            let conn = Connection::open(":memory:").await.unwrap();
2944            setup_bd2832(&conn).await;
2945            let rows = conn
2946                .query("SELECT a FROM tp LIMIT 2 OFFSET 2;")
2947                .await
2948                .unwrap();
2949            assert_eq!(rows.len(), 2);
2950            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2951            assert_eq!(vals, vec![SqliteValue::Integer(3), SqliteValue::Integer(4)]);
2952        });
2953    }
2954
2955    // ── WHERE + LIMIT ───────────────────────────────────────────────────
2956
2957    #[test]
2958    fn where_with_limit() {
2959        asupersync::test_utils::run_test(|| async {
2960            let conn = Connection::open(":memory:").await.unwrap();
2961            setup_bd2832(&conn).await;
2962            let rows = conn
2963                .query("SELECT a FROM tp WHERE a > 1 LIMIT 2;")
2964                .await
2965                .unwrap();
2966            assert_eq!(rows.len(), 2);
2967            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
2968            assert_eq!(vals, vec![SqliteValue::Integer(2), SqliteValue::Integer(3)]);
2969        });
2970    }
2971
2972    // ── CASE WHEN on table-backed SELECT ────────────────────────────────
2973
2974    #[test]
2975    fn case_when_table_backed() {
2976        asupersync::test_utils::run_test(|| async {
2977            let conn = Connection::open(":memory:").await.unwrap();
2978            setup_bd2832(&conn).await;
2979            let rows = conn
2980                .query("SELECT CASE WHEN a > 3 THEN 'big' ELSE 'small' END FROM tp;")
2981                .await
2982                .unwrap();
2983            assert_eq!(rows.len(), 5);
2984            assert_eq!(rows[0].values()[0], SqliteValue::Text("small".into()));
2985            assert_eq!(rows[3].values()[0], SqliteValue::Text("big".into()));
2986        });
2987    }
2988
2989    // ── CAST on table column ────────────────────────────────────────────
2990
2991    #[test]
2992    fn cast_table_backed() {
2993        asupersync::test_utils::run_test(|| async {
2994            let conn = Connection::open(":memory:").await.unwrap();
2995            conn.execute("CREATE TABLE tcast (v INTEGER);")
2996                .await
2997                .unwrap();
2998            conn.execute("INSERT INTO tcast VALUES (42);")
2999                .await
3000                .unwrap();
3001            let row = conn
3002                .query_row("SELECT CAST(v AS TEXT) FROM tcast;")
3003                .await
3004                .unwrap();
3005            assert_eq!(row_values(&row), vec![SqliteValue::Text("42".into())]);
3006        });
3007    }
3008
3009    // ── IS NULL / IS NOT NULL on table ──────────────────────────────────
3010
3011    #[test]
3012    fn where_column_is_null_correct() {
3013        asupersync::test_utils::run_test(|| async {
3014            let conn = Connection::open(":memory:").await.unwrap();
3015            setup_bd2832(&conn).await;
3016            let rows = conn
3017                .query("SELECT a FROM tp WHERE b IS NULL;")
3018                .await
3019                .unwrap();
3020            assert_eq!(rows.len(), 1);
3021            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(4)]);
3022        });
3023    }
3024
3025    #[test]
3026    fn where_column_is_not_null_correct() {
3027        asupersync::test_utils::run_test(|| async {
3028            let conn = Connection::open(":memory:").await.unwrap();
3029            setup_bd2832(&conn).await;
3030            let rows = conn
3031                .query("SELECT a FROM tp WHERE b IS NOT NULL;")
3032                .await
3033                .unwrap();
3034            assert_eq!(rows.len(), 4);
3035        });
3036    }
3037
3038    // ── Unary minus on table column ─────────────────────────────────────
3039
3040    #[test]
3041    fn unary_minus_table_column() {
3042        asupersync::test_utils::run_test(|| async {
3043            let conn = Connection::open(":memory:").await.unwrap();
3044            conn.execute("CREATE TABLE tneg (x INTEGER);")
3045                .await
3046                .unwrap();
3047            conn.execute("INSERT INTO tneg VALUES (42);").await.unwrap();
3048            let row = conn.query_row("SELECT -x FROM tneg;").await.unwrap();
3049            assert_eq!(row_values(&row), vec![SqliteValue::Integer(-42)]);
3050        });
3051    }
3052
3053    // ── Built-in functions: additional coverage ─────────────────────────
3054
3055    #[test]
3056    fn builtin_typeof_all_types() {
3057        asupersync::test_utils::run_test(|| async {
3058            let conn = Connection::open(":memory:").await.unwrap();
3059            assert_eq!(
3060                row_values(&conn.query_row("SELECT typeof(3.14);").await.unwrap()),
3061                vec![SqliteValue::Text("real".into())]
3062            );
3063            assert_eq!(
3064                row_values(&conn.query_row("SELECT typeof('abc');").await.unwrap()),
3065                vec![SqliteValue::Text("text".into())]
3066            );
3067            assert_eq!(
3068                row_values(&conn.query_row("SELECT typeof(NULL);").await.unwrap()),
3069                vec![SqliteValue::Text("null".into())]
3070            );
3071            assert_eq!(
3072                row_values(&conn.query_row("SELECT typeof(X'FF');").await.unwrap()),
3073                vec![SqliteValue::Text("blob".into())]
3074            );
3075        });
3076    }
3077
3078    #[test]
3079    fn builtin_substr() {
3080        asupersync::test_utils::run_test(|| async {
3081            let conn = Connection::open(":memory:").await.unwrap();
3082            let row = conn
3083                .query_row("SELECT substr('hello world', 7, 5);")
3084                .await
3085                .unwrap();
3086            assert_eq!(row_values(&row), vec![SqliteValue::Text("world".into())]);
3087        });
3088    }
3089
3090    #[test]
3091    fn builtin_replace() {
3092        asupersync::test_utils::run_test(|| async {
3093            let conn = Connection::open(":memory:").await.unwrap();
3094            let row = conn
3095                .query_row("SELECT replace('hello world', 'world', 'rust');")
3096                .await
3097                .unwrap();
3098            assert_eq!(
3099                row_values(&row),
3100                vec![SqliteValue::Text("hello rust".into())]
3101            );
3102        });
3103    }
3104
3105    #[test]
3106    fn builtin_trim() {
3107        asupersync::test_utils::run_test(|| async {
3108            let conn = Connection::open(":memory:").await.unwrap();
3109            let row = conn.query_row("SELECT trim('  hello  ');").await.unwrap();
3110            assert_eq!(row_values(&row), vec![SqliteValue::Text("hello".into())]);
3111        });
3112    }
3113
3114    #[test]
3115    fn builtin_instr() {
3116        asupersync::test_utils::run_test(|| async {
3117            let conn = Connection::open(":memory:").await.unwrap();
3118            let row = conn
3119                .query_row("SELECT instr('hello world', 'world');")
3120                .await
3121                .unwrap();
3122            assert_eq!(row_values(&row), vec![SqliteValue::Integer(7)]);
3123        });
3124    }
3125
3126    #[test]
3127    fn builtin_hex() {
3128        asupersync::test_utils::run_test(|| async {
3129            let conn = Connection::open(":memory:").await.unwrap();
3130            let row = conn.query_row("SELECT hex(X'CAFE');").await.unwrap();
3131            assert_eq!(row_values(&row), vec![SqliteValue::Text("CAFE".into())]);
3132        });
3133    }
3134
3135    // ── IS NULL expression context ──────────────────────────────────────
3136
3137    #[test]
3138    fn is_null_expression() {
3139        asupersync::test_utils::run_test(|| async {
3140            let conn = Connection::open(":memory:").await.unwrap();
3141            let row = conn.query_row("SELECT NULL IS NULL;").await.unwrap();
3142            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
3143            let row = conn.query_row("SELECT 42 IS NULL;").await.unwrap();
3144            assert_eq!(row_values(&row), vec![SqliteValue::Integer(0)]);
3145        });
3146    }
3147
3148    // ── SOUNDEX NULL ────────────────────────────────────────────────────
3149
3150    #[test]
3151    fn soundex_null_returns_question_marks() {
3152        asupersync::test_utils::run_test(|| async {
3153            let conn = Connection::open(":memory:").await.unwrap();
3154            let row = conn.query_row("SELECT soundex(NULL);").await.unwrap();
3155            assert_eq!(row_values(&row), vec![SqliteValue::Text("?000".into())]);
3156        });
3157    }
3158
3159    // ── LIKE underscore wildcard ─────────────────────────────────────────
3160
3161    #[test]
3162    fn like_underscore_wildcard() {
3163        asupersync::test_utils::run_test(|| async {
3164            let conn = Connection::open(":memory:").await.unwrap();
3165            setup_bd2832(&conn).await;
3166            let rows = conn
3167                .query("SELECT b FROM tp WHERE b LIKE 'b_ta';")
3168                .await
3169                .unwrap();
3170            assert_eq!(rows.len(), 1);
3171            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Text("beta".into())]);
3172        });
3173    }
3174
3175    // ── NOT IN / NOT BETWEEN ────────────────────────────────────────────
3176
3177    #[test]
3178    fn where_not_in() {
3179        asupersync::test_utils::run_test(|| async {
3180            let conn = Connection::open(":memory:").await.unwrap();
3181            setup_bd2832(&conn).await;
3182            let rows = conn
3183                .query("SELECT a FROM tp WHERE a NOT IN (1, 3, 5);")
3184                .await
3185                .unwrap();
3186            assert_eq!(rows.len(), 2);
3187            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
3188            assert!(vals.contains(&SqliteValue::Integer(2)));
3189            assert!(vals.contains(&SqliteValue::Integer(4)));
3190        });
3191    }
3192
3193    #[test]
3194    fn where_in_subquery() {
3195        asupersync::test_utils::run_test(|| async {
3196            let conn = Connection::open(":memory:").await.unwrap();
3197            conn.execute("CREATE TABLE t1 (a INTEGER);").await.unwrap();
3198            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3199            conn.execute("INSERT INTO t1 VALUES (1), (2), (3);")
3200                .await
3201                .unwrap();
3202            conn.execute("INSERT INTO t2 VALUES (2), (3), (4);")
3203                .await
3204                .unwrap();
3205
3206            let rows = conn
3207                .query("SELECT a FROM t1 WHERE a IN (SELECT b FROM t2) ORDER BY a;")
3208                .await
3209                .unwrap();
3210            assert_eq!(rows.len(), 2);
3211            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(2)]);
3212            assert_eq!(row_values(&rows[1]), vec![SqliteValue::Integer(3)]);
3213        });
3214    }
3215
3216    #[test]
3217    fn where_in_table_name() {
3218        asupersync::test_utils::run_test(|| async {
3219            let conn = Connection::open(":memory:").await.unwrap();
3220            conn.execute("CREATE TABLE t1 (a INTEGER);").await.unwrap();
3221            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3222            conn.execute("INSERT INTO t1 VALUES (1), (2), (3);")
3223                .await
3224                .unwrap();
3225            conn.execute("INSERT INTO t2 VALUES (2), (3), (4);")
3226                .await
3227                .unwrap();
3228
3229            let rows = conn
3230                .query("SELECT a FROM t1 WHERE a IN t2 ORDER BY a;")
3231                .await
3232                .unwrap();
3233            assert_eq!(rows.len(), 2);
3234            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(2)]);
3235            assert_eq!(row_values(&rows[1]), vec![SqliteValue::Integer(3)]);
3236        });
3237    }
3238
3239    #[test]
3240    fn where_not_in_table_name() {
3241        asupersync::test_utils::run_test(|| async {
3242            let conn = Connection::open(":memory:").await.unwrap();
3243            conn.execute("CREATE TABLE t1 (a INTEGER);").await.unwrap();
3244            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3245            conn.execute("INSERT INTO t1 VALUES (1), (2), (3);")
3246                .await
3247                .unwrap();
3248            conn.execute("INSERT INTO t2 VALUES (2), (3), (4);")
3249                .await
3250                .unwrap();
3251
3252            let rows = conn
3253                .query("SELECT a FROM t1 WHERE a NOT IN t2 ORDER BY a;")
3254                .await
3255                .unwrap();
3256            assert_eq!(rows.len(), 1);
3257            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
3258        });
3259    }
3260
3261    #[test]
3262    fn where_exists_subquery() {
3263        asupersync::test_utils::run_test(|| async {
3264            let conn = Connection::open(":memory:").await.unwrap();
3265            conn.execute("CREATE TABLE t1 (a INTEGER);").await.unwrap();
3266            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3267            conn.execute("INSERT INTO t1 VALUES (1), (2);")
3268                .await
3269                .unwrap();
3270            conn.execute("INSERT INTO t2 VALUES (7);").await.unwrap();
3271
3272            let rows = conn
3273                .query("SELECT a FROM t1 WHERE EXISTS (SELECT b FROM t2) ORDER BY a;")
3274                .await
3275                .unwrap();
3276            assert_eq!(rows.len(), 2);
3277
3278            conn.execute("DELETE FROM t2;").await.unwrap();
3279            let rows = conn
3280                .query("SELECT a FROM t1 WHERE EXISTS (SELECT b FROM t2);")
3281                .await
3282                .unwrap();
3283            assert_eq!(rows.len(), 0);
3284        });
3285    }
3286
3287    #[test]
3288    fn scalar_subquery_expression() {
3289        asupersync::test_utils::run_test(|| async {
3290            let conn = Connection::open(":memory:").await.unwrap();
3291            conn.execute("CREATE TABLE s (v INTEGER);").await.unwrap();
3292            conn.execute("INSERT INTO s VALUES (41);").await.unwrap();
3293
3294            let row = conn
3295                .query_row("SELECT (SELECT v FROM s) + 1;")
3296                .await
3297                .unwrap();
3298            assert_eq!(row_values(&row), vec![SqliteValue::Integer(42)]);
3299        });
3300    }
3301
3302    #[test]
3303    fn update_where_in_table_name() {
3304        asupersync::test_utils::run_test(|| async {
3305            let conn = Connection::open(":memory:").await.unwrap();
3306            conn.execute("CREATE TABLE t1 (a INTEGER, flag TEXT);")
3307                .await
3308                .unwrap();
3309            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3310            conn.execute("INSERT INTO t1 VALUES (1, 'orig'), (2, 'orig'), (3, 'orig');")
3311                .await
3312                .unwrap();
3313            conn.execute("INSERT INTO t2 VALUES (2), (3);")
3314                .await
3315                .unwrap();
3316
3317            conn.execute("UPDATE t1 SET flag='hit' WHERE a IN t2;")
3318                .await
3319                .unwrap();
3320
3321            let rows = conn
3322                .query("SELECT a, flag FROM t1 ORDER BY a;")
3323                .await
3324                .unwrap();
3325            assert_eq!(rows.len(), 3);
3326            assert_eq!(
3327                row_values(&rows[0]),
3328                vec![SqliteValue::Integer(1), SqliteValue::Text("orig".into())]
3329            );
3330            assert_eq!(
3331                row_values(&rows[1]),
3332                vec![SqliteValue::Integer(2), SqliteValue::Text("hit".into())]
3333            );
3334            assert_eq!(
3335                row_values(&rows[2]),
3336                vec![SqliteValue::Integer(3), SqliteValue::Text("hit".into())]
3337            );
3338        });
3339    }
3340
3341    #[test]
3342    fn delete_where_in_table_name() {
3343        asupersync::test_utils::run_test(|| async {
3344            let conn = Connection::open(":memory:").await.unwrap();
3345            conn.execute("CREATE TABLE t1 (a INTEGER);").await.unwrap();
3346            conn.execute("CREATE TABLE t2 (b INTEGER);").await.unwrap();
3347            conn.execute("INSERT INTO t1 VALUES (1), (2), (3);")
3348                .await
3349                .unwrap();
3350            conn.execute("INSERT INTO t2 VALUES (2), (3);")
3351                .await
3352                .unwrap();
3353
3354            conn.execute("DELETE FROM t1 WHERE a IN t2;").await.unwrap();
3355
3356            let rows = conn.query("SELECT a FROM t1 ORDER BY a;").await.unwrap();
3357            assert_eq!(rows.len(), 1);
3358            assert_eq!(row_values(&rows[0]), vec![SqliteValue::Integer(1)]);
3359        });
3360    }
3361
3362    #[test]
3363    fn where_not_between() {
3364        asupersync::test_utils::run_test(|| async {
3365            let conn = Connection::open(":memory:").await.unwrap();
3366            setup_bd2832(&conn).await;
3367            let rows = conn
3368                .query("SELECT a FROM tp WHERE a NOT BETWEEN 2 AND 4;")
3369                .await
3370                .unwrap();
3371            assert_eq!(rows.len(), 2);
3372            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
3373            assert!(vals.contains(&SqliteValue::Integer(1)));
3374            assert!(vals.contains(&SqliteValue::Integer(5)));
3375        });
3376    }
3377
3378    // ── NULL semantics for IN / BETWEEN ────────────────────────────────
3379
3380    #[test]
3381    fn between_null_operand_returns_null() {
3382        asupersync::test_utils::run_test(|| async {
3383            let conn = Connection::open(":memory:").await.unwrap();
3384            // NULL BETWEEN 1 AND 5 → NULL (not TRUE)
3385            let row = conn
3386                .query_row("SELECT NULL BETWEEN 1 AND 5;")
3387                .await
3388                .unwrap();
3389            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3390        });
3391    }
3392
3393    #[test]
3394    fn between_null_low_bound() {
3395        asupersync::test_utils::run_test(|| async {
3396            let conn = Connection::open(":memory:").await.unwrap();
3397            // 3 BETWEEN NULL AND 5: (3 >= NULL) AND (3 <= 5) = NULL AND TRUE = NULL
3398            let row = conn
3399                .query_row("SELECT 3 BETWEEN NULL AND 5;")
3400                .await
3401                .unwrap();
3402            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3403        });
3404    }
3405
3406    #[test]
3407    fn between_null_high_bound() {
3408        asupersync::test_utils::run_test(|| async {
3409            let conn = Connection::open(":memory:").await.unwrap();
3410            // 3 BETWEEN 1 AND NULL: (3 >= 1) AND (3 <= NULL) = TRUE AND NULL = NULL
3411            let row = conn
3412                .query_row("SELECT 3 BETWEEN 1 AND NULL;")
3413                .await
3414                .unwrap();
3415            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3416        });
3417    }
3418
3419    #[test]
3420    fn between_null_bound_out_of_range_returns_false() {
3421        asupersync::test_utils::run_test(|| async {
3422            let conn = Connection::open(":memory:").await.unwrap();
3423            // 3 BETWEEN 4 AND NULL: (3 >= 4) AND (3 <= NULL) = FALSE AND NULL = FALSE
3424            let row = conn
3425                .query_row("SELECT 3 BETWEEN 4 AND NULL;")
3426                .await
3427                .unwrap();
3428            assert_eq!(row_values(&row), vec![SqliteValue::Integer(0)]);
3429        });
3430    }
3431
3432    #[test]
3433    fn in_null_operand_returns_null() {
3434        asupersync::test_utils::run_test(|| async {
3435            let conn = Connection::open(":memory:").await.unwrap();
3436            // NULL IN (1, 2, 3) → NULL (not FALSE)
3437            let row = conn.query_row("SELECT NULL IN (1, 2, 3);").await.unwrap();
3438            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3439        });
3440    }
3441
3442    #[test]
3443    fn in_list_with_null_no_match_returns_null() {
3444        asupersync::test_utils::run_test(|| async {
3445            let conn = Connection::open(":memory:").await.unwrap();
3446            // 2 IN (1, NULL, 3): no exact match, but NULL in list → NULL
3447            let row = conn.query_row("SELECT 2 IN (1, NULL, 3);").await.unwrap();
3448            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3449        });
3450    }
3451
3452    #[test]
3453    fn in_list_with_null_match_returns_true() {
3454        asupersync::test_utils::run_test(|| async {
3455            let conn = Connection::open(":memory:").await.unwrap();
3456            // 1 IN (1, NULL, 3): exact match on 1 → TRUE (integer 1)
3457            let row = conn.query_row("SELECT 1 IN (1, NULL, 3);").await.unwrap();
3458            assert_eq!(row_values(&row), vec![SqliteValue::Integer(1)]);
3459        });
3460    }
3461
3462    #[test]
3463    fn not_in_null_operand_returns_null() {
3464        asupersync::test_utils::run_test(|| async {
3465            let conn = Connection::open(":memory:").await.unwrap();
3466            // NULL NOT IN (1, 2) → NULL
3467            let row = conn.query_row("SELECT NULL NOT IN (1, 2);").await.unwrap();
3468            assert_eq!(row_values(&row), vec![SqliteValue::Null]);
3469        });
3470    }
3471
3472    // ── DISTINCT ──────────────────────────────────────────────────────
3473
3474    #[test]
3475    fn distinct_table_backed_select() {
3476        asupersync::test_utils::run_test(|| async {
3477            let conn = Connection::open(":memory:").await.unwrap();
3478            conn.execute("CREATE TABLE td (id INTEGER, flag INTEGER);")
3479                .await
3480                .unwrap();
3481            conn.execute("INSERT INTO td VALUES (1, 1);").await.unwrap();
3482            conn.execute("INSERT INTO td VALUES (2, 0);").await.unwrap();
3483            conn.execute("INSERT INTO td VALUES (3, 1);").await.unwrap();
3484            conn.execute("INSERT INTO td VALUES (4, 0);").await.unwrap();
3485            conn.execute("INSERT INTO td VALUES (5, 1);").await.unwrap();
3486
3487            let rows = conn.query("SELECT DISTINCT flag FROM td;").await.unwrap();
3488            assert_eq!(rows.len(), 2);
3489            let vals: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
3490            assert!(vals.contains(&SqliteValue::Integer(0)));
3491            assert!(vals.contains(&SqliteValue::Integer(1)));
3492        });
3493    }
3494
3495    // ── Aggregate + GROUP BY ───────────────────────────────────────────
3496
3497    #[test]
3498    fn aggregate_group_by_count() {
3499        asupersync::test_utils::run_test(|| async {
3500            let conn = Connection::open(":memory:").await.unwrap();
3501            conn.execute("CREATE TABLE tg (k TEXT);").await.unwrap();
3502            conn.execute("INSERT INTO tg VALUES ('a');").await.unwrap();
3503            conn.execute("INSERT INTO tg VALUES ('a');").await.unwrap();
3504            conn.execute("INSERT INTO tg VALUES ('b');").await.unwrap();
3505
3506            let rows = conn
3507                .query("SELECT k, COUNT(*) FROM tg GROUP BY k ORDER BY k;")
3508                .await
3509                .unwrap();
3510            assert_eq!(rows.len(), 2);
3511            assert_eq!(
3512                row_values(&rows[0]),
3513                vec![SqliteValue::Text("a".into()), SqliteValue::Integer(2)]
3514            );
3515            assert_eq!(
3516                row_values(&rows[1]),
3517                vec![SqliteValue::Text("b".into()), SqliteValue::Integer(1)]
3518            );
3519        });
3520    }
3521
3522    #[test]
3523    fn group_by_alias_star_expansion() {
3524        asupersync::test_utils::run_test(|| async {
3525            let conn = Connection::open(":memory:").await.unwrap();
3526            conn.execute("CREATE TABLE ga (k TEXT, v INTEGER);")
3527                .await
3528                .unwrap();
3529            conn.execute("INSERT INTO ga VALUES ('a', 10);")
3530                .await
3531                .unwrap();
3532            conn.execute("INSERT INTO ga VALUES ('a', 10);")
3533                .await
3534                .unwrap();
3535            conn.execute("INSERT INTO ga VALUES ('b', 20);")
3536                .await
3537                .unwrap();
3538
3539            let rows = conn
3540                .query("SELECT t.* FROM ga AS t GROUP BY t.k, t.v ORDER BY t.k, t.v;")
3541                .await
3542                .unwrap();
3543            assert_eq!(rows.len(), 2);
3544            assert_eq!(
3545                row_values(&rows[0]),
3546                vec![SqliteValue::Text("a".into()), SqliteValue::Integer(10)]
3547            );
3548            assert_eq!(
3549                row_values(&rows[1]),
3550                vec![SqliteValue::Text("b".into()), SqliteValue::Integer(20)]
3551            );
3552        });
3553    }
3554
3555    #[test]
3556    fn right_join_null_extension() {
3557        asupersync::test_utils::run_test(|| async {
3558            let conn = Connection::open(":memory:").await.unwrap();
3559            conn.execute("CREATE TABLE l (id INTEGER, name TEXT);")
3560                .await
3561                .unwrap();
3562            conn.execute("CREATE TABLE r (l_id INTEGER, tag TEXT);")
3563                .await
3564                .unwrap();
3565            conn.execute("INSERT INTO l VALUES (1, 'left-a'), (2, 'left-b');")
3566                .await
3567                .unwrap();
3568            conn.execute("INSERT INTO r VALUES (2, 'right-b'), (3, 'right-c');")
3569                .await
3570                .unwrap();
3571
3572            let rows = conn
3573                .query("SELECT l.name, r.tag FROM l RIGHT JOIN r ON l.id = r.l_id;")
3574                .await
3575                .unwrap();
3576            assert_eq!(rows.len(), 2);
3577
3578            let projected: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
3579            assert!(projected.contains(&vec![
3580                SqliteValue::Text("left-b".into()),
3581                SqliteValue::Text("right-b".into())
3582            ]));
3583            assert!(projected.contains(&vec![
3584                SqliteValue::Null,
3585                SqliteValue::Text("right-c".into())
3586            ]));
3587        });
3588    }
3589
3590    #[test]
3591    fn full_outer_join_null_extension() {
3592        asupersync::test_utils::run_test(|| async {
3593            let conn = Connection::open(":memory:").await.unwrap();
3594            conn.execute("CREATE TABLE l (id INTEGER, name TEXT);")
3595                .await
3596                .unwrap();
3597            conn.execute("CREATE TABLE r (l_id INTEGER, tag TEXT);")
3598                .await
3599                .unwrap();
3600            conn.execute("INSERT INTO l VALUES (1, 'left-a'), (2, 'left-b');")
3601                .await
3602                .unwrap();
3603            conn.execute("INSERT INTO r VALUES (2, 'right-b'), (3, 'right-c');")
3604                .await
3605                .unwrap();
3606
3607            let rows = conn
3608                .query("SELECT l.name, r.tag FROM l FULL OUTER JOIN r ON l.id = r.l_id;")
3609                .await
3610                .unwrap();
3611            assert_eq!(rows.len(), 3);
3612
3613            let projected: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
3614            assert!(
3615                projected.contains(&vec![SqliteValue::Text("left-a".into()), SqliteValue::Null])
3616            );
3617            assert!(projected.contains(&vec![
3618                SqliteValue::Text("left-b".into()),
3619                SqliteValue::Text("right-b".into())
3620            ]));
3621            assert!(projected.contains(&vec![
3622                SqliteValue::Null,
3623                SqliteValue::Text("right-c".into())
3624            ]));
3625        });
3626    }
3627
3628    #[test]
3629    fn right_join_using_nulls_do_not_match() {
3630        asupersync::test_utils::run_test(|| async {
3631            let conn = Connection::open(":memory:").await.unwrap();
3632            conn.execute("CREATE TABLE l (id INTEGER, name TEXT);")
3633                .await
3634                .unwrap();
3635            conn.execute("CREATE TABLE r (id INTEGER, tag TEXT);")
3636                .await
3637                .unwrap();
3638            conn.execute("INSERT INTO l VALUES (NULL, 'left-null'), (1, 'left-one');")
3639                .await
3640                .unwrap();
3641            conn.execute("INSERT INTO r VALUES (NULL, 'right-null'), (1, 'right-one');")
3642                .await
3643                .unwrap();
3644
3645            let rows = conn
3646                .query("SELECT l.name, r.tag FROM l RIGHT JOIN r USING (id);")
3647                .await
3648                .unwrap();
3649            assert_eq!(rows.len(), 2);
3650            let projected: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
3651            assert!(projected.contains(&vec![
3652                SqliteValue::Text("left-one".into()),
3653                SqliteValue::Text("right-one".into())
3654            ]));
3655            assert!(projected.contains(&vec![
3656                SqliteValue::Null,
3657                SqliteValue::Text("right-null".into())
3658            ]));
3659        });
3660    }
3661
3662    #[test]
3663    fn aggregate_group_by_sum() {
3664        asupersync::test_utils::run_test(|| async {
3665            let conn = Connection::open(":memory:").await.unwrap();
3666            conn.execute("CREATE TABLE gs (dept TEXT, salary INTEGER);")
3667                .await
3668                .unwrap();
3669            conn.execute("INSERT INTO gs VALUES ('eng', 100);")
3670                .await
3671                .unwrap();
3672            conn.execute("INSERT INTO gs VALUES ('eng', 200);")
3673                .await
3674                .unwrap();
3675            conn.execute("INSERT INTO gs VALUES ('sales', 50);")
3676                .await
3677                .unwrap();
3678
3679            let rows = conn
3680                .query("SELECT dept, SUM(salary) FROM gs GROUP BY dept;")
3681                .await
3682                .unwrap();
3683            assert_eq!(rows.len(), 2);
3684            let vals: Vec<(SqliteValue, SqliteValue)> = rows
3685                .iter()
3686                .map(|r| {
3687                    let v = row_values(r);
3688                    (v[0].clone(), v[1].clone())
3689                })
3690                .collect();
3691            assert!(vals.contains(&(SqliteValue::Text("eng".into()), SqliteValue::Integer(300))));
3692            assert!(vals.contains(&(SqliteValue::Text("sales".into()), SqliteValue::Integer(50))));
3693        });
3694    }
3695
3696    #[test]
3697    fn aggregate_group_by_multiple_aggs() {
3698        asupersync::test_utils::run_test(|| async {
3699            let conn = Connection::open(":memory:").await.unwrap();
3700            conn.execute("CREATE TABLE gm (cat TEXT, val INTEGER);")
3701                .await
3702                .unwrap();
3703            conn.execute("INSERT INTO gm VALUES ('a', 10);")
3704                .await
3705                .unwrap();
3706            conn.execute("INSERT INTO gm VALUES ('a', 20);")
3707                .await
3708                .unwrap();
3709            conn.execute("INSERT INTO gm VALUES ('a', 30);")
3710                .await
3711                .unwrap();
3712            conn.execute("INSERT INTO gm VALUES ('b', 5);")
3713                .await
3714                .unwrap();
3715
3716            let rows = conn
3717                .query("SELECT cat, COUNT(*), MIN(val), MAX(val) FROM gm GROUP BY cat;")
3718                .await
3719                .unwrap();
3720            assert_eq!(rows.len(), 2);
3721            let a_row = rows
3722                .iter()
3723                .find(|r| row_values(r)[0] == SqliteValue::Text("a".into()))
3724                .unwrap();
3725            assert_eq!(
3726                row_values(a_row),
3727                vec![
3728                    SqliteValue::Text("a".into()),
3729                    SqliteValue::Integer(3),
3730                    SqliteValue::Integer(10),
3731                    SqliteValue::Integer(30),
3732                ]
3733            );
3734            let b_row = rows
3735                .iter()
3736                .find(|r| row_values(r)[0] == SqliteValue::Text("b".into()))
3737                .unwrap();
3738            assert_eq!(
3739                row_values(b_row),
3740                vec![
3741                    SqliteValue::Text("b".into()),
3742                    SqliteValue::Integer(1),
3743                    SqliteValue::Integer(5),
3744                    SqliteValue::Integer(5),
3745                ]
3746            );
3747        });
3748    }
3749
3750    // ── Aggregate: count(col) excludes NULL ──────────────────────────────
3751
3752    #[test]
3753    fn aggregate_count_column_excludes_null() {
3754        asupersync::test_utils::run_test(|| async {
3755            let conn = Connection::open(":memory:").await.unwrap();
3756            setup_bd2832(&conn).await;
3757            let row = conn.query_row("SELECT count(b) FROM tp;").await.unwrap();
3758            assert_eq!(row_values(&row), vec![SqliteValue::Integer(4)]);
3759        });
3760    }
3761
3762    // ── execute() with_params affected row count (bd-118o) ────────────
3763
3764    #[test]
3765    fn execute_with_params_insert_returns_count() {
3766        asupersync::test_utils::run_test(|| async {
3767            let conn = Connection::open(":memory:").await.unwrap();
3768            conn.execute("CREATE TABLE ewp (v INTEGER);").await.unwrap();
3769            let count = conn
3770                .execute_with_params("INSERT INTO ewp VALUES (?1);", &[SqliteValue::Integer(42)])
3771                .await
3772                .unwrap();
3773            assert_eq!(count, 1, "INSERT via execute_with_params should return 1");
3774        });
3775    }
3776
3777    #[test]
3778    fn execute_with_params_insert_respects_explicit_column_order() {
3779        asupersync::test_utils::run_test(|| async {
3780            let conn = Connection::open(":memory:").await.unwrap();
3781            conn.execute(
3782                "CREATE TABLE message_payloads(
3783                id INTEGER PRIMARY KEY,
3784                attachments TEXT NOT NULL DEFAULT '[]',
3785                recipients_json TEXT NOT NULL DEFAULT '{}'
3786            );",
3787            )
3788            .await
3789            .unwrap();
3790
3791            let recipients = r#"{"to":["BlueLake"],"cc":[],"bcc":[]}"#;
3792            let attachments = r#"[{"name":"artifact.txt","path":"attachments/demo.txt","content_type":"text/plain","size":"128"}]"#;
3793
3794            conn.execute_with_params(
3795                "INSERT INTO message_payloads(recipients_json, attachments) VALUES (?1, ?2);",
3796                &[
3797                    SqliteValue::Text(recipients.into()),
3798                    SqliteValue::Text(attachments.into()),
3799                ],
3800            )
3801            .await
3802            .unwrap();
3803
3804            let row = conn
3805                .query_row("SELECT recipients_json, attachments FROM message_payloads LIMIT 1;")
3806                .await
3807                .unwrap();
3808            assert_eq!(
3809                row_values(&row),
3810                vec![
3811                    SqliteValue::Text(recipients.into()),
3812                    SqliteValue::Text(attachments.into())
3813                ]
3814            );
3815        });
3816    }
3817
3818    #[test]
3819    fn execute_with_params_insert_duplicate_target_columns_keep_first_assignment() {
3820        asupersync::test_utils::run_test(|| async {
3821            let conn = Connection::open(":memory:").await.unwrap();
3822            conn.execute("CREATE TABLE dup_targets(a INTEGER, b INTEGER);")
3823                .await
3824                .unwrap();
3825
3826            conn.execute_with_params(
3827                "INSERT INTO dup_targets(a, a, b) VALUES (?1, ?2, ?3);",
3828                &[
3829                    SqliteValue::Integer(1),
3830                    SqliteValue::Integer(2),
3831                    SqliteValue::Integer(3),
3832                ],
3833            )
3834            .await
3835            .unwrap();
3836
3837            let row = conn
3838                .query_row("SELECT a, b FROM dup_targets;")
3839                .await
3840                .unwrap();
3841            assert_eq!(
3842                row_values(&row),
3843                vec![SqliteValue::Integer(1), SqliteValue::Integer(3)]
3844            );
3845        });
3846    }
3847
3848    #[test]
3849    fn execute_select_returns_row_count() {
3850        asupersync::test_utils::run_test(|| async {
3851            let conn = Connection::open(":memory:").await.unwrap();
3852            conn.execute("CREATE TABLE es (v INTEGER);").await.unwrap();
3853            conn.execute("INSERT INTO es VALUES (1);").await.unwrap();
3854            conn.execute("INSERT INTO es VALUES (2);").await.unwrap();
3855            let count = conn.execute("SELECT * FROM es;").await.unwrap();
3856            assert_eq!(count, 2, "SELECT via execute() should return row count");
3857        });
3858    }
3859
3860    // ── Bug fix regression: SAVEPOINT RELEASE implicit transaction ───
3861
3862    #[test]
3863    fn savepoint_release_ends_implicit_transaction() {
3864        asupersync::test_utils::run_test(|| async {
3865            let conn = Connection::open(":memory:").await.unwrap();
3866            conn.execute("CREATE TABLE sr (v INTEGER);").await.unwrap();
3867
3868            // SAVEPOINT starts an implicit transaction.
3869            conn.execute("SAVEPOINT sp1;").await.unwrap();
3870            assert!(conn.in_transaction());
3871            conn.execute("INSERT INTO sr VALUES (1);").await.unwrap();
3872
3873            // RELEASE ends the implicit transaction.
3874            conn.execute("RELEASE sp1;").await.unwrap();
3875            assert!(
3876                !conn.in_transaction(),
3877                "RELEASE of last implicit savepoint should end transaction"
3878            );
3879
3880            // After release, data should be committed.
3881            let rows = conn.query("SELECT * FROM sr;").await.unwrap();
3882            assert_eq!(rows.len(), 1);
3883        });
3884    }
3885
3886    #[test]
3887    fn explicit_begin_savepoint_release_keeps_transaction() {
3888        asupersync::test_utils::run_test(|| async {
3889            let conn = Connection::open(":memory:").await.unwrap();
3890            conn.execute("CREATE TABLE bsr (v INTEGER);").await.unwrap();
3891
3892            // Explicit BEGIN, then SAVEPOINT, then RELEASE.
3893            conn.execute("BEGIN;").await.unwrap();
3894            conn.execute("SAVEPOINT sp1;").await.unwrap();
3895            conn.execute("INSERT INTO bsr VALUES (1);").await.unwrap();
3896            conn.execute("RELEASE sp1;").await.unwrap();
3897
3898            // Transaction should still be active (explicit BEGIN requires COMMIT).
3899            assert!(
3900                conn.in_transaction(),
3901                "RELEASE after explicit BEGIN should not end the transaction"
3902            );
3903            conn.execute("COMMIT;").await.unwrap();
3904            assert!(!conn.in_transaction());
3905        });
3906    }
3907
3908    // ── Probe tests for SQL feature coverage ─────────────────────
3909
3910    #[test]
3911    fn probe_update_self_ref_expr() {
3912        asupersync::test_utils::run_test(|| async {
3913            let conn = Connection::open(":memory:").await.unwrap();
3914            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
3915                .await
3916                .unwrap();
3917            conn.execute("INSERT INTO t VALUES (1, 10);").await.unwrap();
3918            conn.execute("INSERT INTO t VALUES (2, 20);").await.unwrap();
3919            conn.execute("UPDATE t SET val = val + 5;").await.unwrap();
3920            let rows = conn
3921                .query("SELECT id, val FROM t ORDER BY id;")
3922                .await
3923                .unwrap();
3924            assert_eq!(
3925                row_values(&rows[0]),
3926                vec![SqliteValue::Integer(1), SqliteValue::Integer(15)]
3927            );
3928            assert_eq!(
3929                row_values(&rows[1]),
3930                vec![SqliteValue::Integer(2), SqliteValue::Integer(25)]
3931            );
3932        });
3933    }
3934
3935    #[test]
3936    fn probe_delete_compound_where() {
3937        asupersync::test_utils::run_test(|| async {
3938            let conn = Connection::open(":memory:").await.unwrap();
3939            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
3940                .await
3941                .unwrap();
3942            conn.execute("INSERT INTO t VALUES (1, 'a');")
3943                .await
3944                .unwrap();
3945            conn.execute("INSERT INTO t VALUES (2, 'b');")
3946                .await
3947                .unwrap();
3948            conn.execute("INSERT INTO t VALUES (3, 'c');")
3949                .await
3950                .unwrap();
3951            conn.execute("DELETE FROM t WHERE id > 1 AND val = 'b';")
3952                .await
3953                .unwrap();
3954            let rows = conn.query("SELECT id FROM t ORDER BY id;").await.unwrap();
3955            assert_eq!(rows.len(), 2);
3956            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
3957            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(3));
3958        });
3959    }
3960
3961    #[test]
3962    fn probe_coalesce_nulls() {
3963        asupersync::test_utils::run_test(|| async {
3964            let conn = Connection::open(":memory:").await.unwrap();
3965            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a TEXT, b TEXT);")
3966                .await
3967                .unwrap();
3968            conn.execute("INSERT INTO t VALUES (1, NULL, 'fallback');")
3969                .await
3970                .unwrap();
3971            conn.execute("INSERT INTO t VALUES (2, 'present', 'fallback');")
3972                .await
3973                .unwrap();
3974            let rows = conn
3975                .query("SELECT id, COALESCE(a, b) FROM t ORDER BY id;")
3976                .await
3977                .unwrap();
3978            assert_eq!(rows.len(), 2);
3979            assert_eq!(
3980                row_values(&rows[0])[1],
3981                SqliteValue::Text("fallback".into())
3982            );
3983            assert_eq!(row_values(&rows[1])[1], SqliteValue::Text("present".into()));
3984        });
3985    }
3986
3987    #[test]
3988    fn probe_case_when_null() {
3989        asupersync::test_utils::run_test(|| async {
3990            let conn = Connection::open(":memory:").await.unwrap();
3991            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
3992                .await
3993                .unwrap();
3994            conn.execute("INSERT INTO t VALUES (1, NULL);")
3995                .await
3996                .unwrap();
3997            conn.execute("INSERT INTO t VALUES (2, 5);").await.unwrap();
3998            conn.execute("INSERT INTO t VALUES (3, 15);").await.unwrap();
3999            let rows = conn
4000            .query(
4001                "SELECT id, CASE WHEN val IS NULL THEN 'null' WHEN val < 10 THEN 'small' ELSE 'big' END FROM t ORDER BY id;",
4002            )
4003            .await
4004            .unwrap();
4005            assert_eq!(rows.len(), 3);
4006            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("null".into()));
4007            assert_eq!(row_values(&rows[1])[1], SqliteValue::Text("small".into()));
4008            assert_eq!(row_values(&rows[2])[1], SqliteValue::Text("big".into()));
4009        });
4010    }
4011
4012    #[test]
4013    fn probe_union_all() {
4014        asupersync::test_utils::run_test(|| async {
4015            let conn = Connection::open(":memory:").await.unwrap();
4016            conn.execute("CREATE TABLE t1 (id INTEGER PRIMARY KEY, val TEXT);")
4017                .await
4018                .unwrap();
4019            conn.execute("CREATE TABLE t2 (id INTEGER PRIMARY KEY, val TEXT);")
4020                .await
4021                .unwrap();
4022            conn.execute("INSERT INTO t1 VALUES (1, 'a');")
4023                .await
4024                .unwrap();
4025            conn.execute("INSERT INTO t2 VALUES (2, 'b');")
4026                .await
4027                .unwrap();
4028            let rows = conn
4029                .query("SELECT val FROM t1 UNION ALL SELECT val FROM t2;")
4030                .await
4031                .unwrap();
4032            assert_eq!(rows.len(), 2);
4033        });
4034    }
4035
4036    #[test]
4037    fn probe_union_dedup() {
4038        asupersync::test_utils::run_test(|| async {
4039            let conn = Connection::open(":memory:").await.unwrap();
4040            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4041                .await
4042                .unwrap();
4043            conn.execute("INSERT INTO t VALUES (1, 'a');")
4044                .await
4045                .unwrap();
4046            conn.execute("INSERT INTO t VALUES (2, 'a');")
4047                .await
4048                .unwrap();
4049            conn.execute("INSERT INTO t VALUES (3, 'b');")
4050                .await
4051                .unwrap();
4052            let rows = conn
4053                .query("SELECT val FROM t UNION SELECT val FROM t;")
4054                .await
4055                .unwrap();
4056            assert_eq!(
4057                rows.len(),
4058                2,
4059                "UNION should deduplicate: got {:?}",
4060                rows.iter().map(row_values).collect::<Vec<_>>()
4061            );
4062        });
4063    }
4064
4065    #[test]
4066    fn probe_except() {
4067        asupersync::test_utils::run_test(|| async {
4068            let conn = Connection::open(":memory:").await.unwrap();
4069            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4070                .await
4071                .unwrap();
4072            conn.execute("INSERT INTO t VALUES (1, 'a');")
4073                .await
4074                .unwrap();
4075            conn.execute("INSERT INTO t VALUES (2, 'b');")
4076                .await
4077                .unwrap();
4078            conn.execute("INSERT INTO t VALUES (3, 'c');")
4079                .await
4080                .unwrap();
4081            let rows = conn
4082                .query("SELECT val FROM t EXCEPT SELECT val FROM t WHERE id = 2;")
4083                .await
4084                .unwrap();
4085            assert_eq!(
4086                rows.len(),
4087                2,
4088                "EXCEPT should remove 'b': got {:?}",
4089                rows.iter().map(row_values).collect::<Vec<_>>()
4090            );
4091        });
4092    }
4093
4094    #[test]
4095    fn probe_intersect() {
4096        asupersync::test_utils::run_test(|| async {
4097            let conn = Connection::open(":memory:").await.unwrap();
4098            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4099                .await
4100                .unwrap();
4101            conn.execute("INSERT INTO t VALUES (1, 'a');")
4102                .await
4103                .unwrap();
4104            conn.execute("INSERT INTO t VALUES (2, 'b');")
4105                .await
4106                .unwrap();
4107            conn.execute("INSERT INTO t VALUES (3, 'c');")
4108                .await
4109                .unwrap();
4110            let rows = conn
4111                .query("SELECT val FROM t INTERSECT SELECT val FROM t WHERE id <= 2;")
4112                .await
4113                .unwrap();
4114            assert_eq!(
4115                rows.len(),
4116                2,
4117                "INTERSECT should keep 'a' and 'b': got {:?}",
4118                rows.iter().map(row_values).collect::<Vec<_>>()
4119            );
4120        });
4121    }
4122
4123    #[test]
4124    fn probe_insert_select() {
4125        asupersync::test_utils::run_test(|| async {
4126            let conn = Connection::open(":memory:").await.unwrap();
4127            conn.execute("CREATE TABLE src (id INTEGER PRIMARY KEY, val TEXT);")
4128                .await
4129                .unwrap();
4130            conn.execute("CREATE TABLE dst (id INTEGER PRIMARY KEY, val TEXT);")
4131                .await
4132                .unwrap();
4133            conn.execute("INSERT INTO src VALUES (1, 'a');")
4134                .await
4135                .unwrap();
4136            conn.execute("INSERT INTO src VALUES (2, 'b');")
4137                .await
4138                .unwrap();
4139            conn.execute("INSERT INTO dst SELECT * FROM src;")
4140                .await
4141                .unwrap();
4142            let rows = conn
4143                .query("SELECT id, val FROM dst ORDER BY id;")
4144                .await
4145                .unwrap();
4146            assert_eq!(rows.len(), 2);
4147            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("a".into()));
4148        });
4149    }
4150
4151    #[test]
4152    fn probe_limit_offset() {
4153        asupersync::test_utils::run_test(|| async {
4154            let conn = Connection::open(":memory:").await.unwrap();
4155            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4156                .await
4157                .unwrap();
4158            for i in 1..=10 {
4159                conn.execute(&format!("INSERT INTO t VALUES ({i}, 'v{i}');"))
4160                    .await
4161                    .unwrap();
4162            }
4163            let rows = conn
4164                .query("SELECT id FROM t ORDER BY id LIMIT 3 OFFSET 2;")
4165                .await
4166                .unwrap();
4167            assert_eq!(rows.len(), 3, "LIMIT 3 OFFSET 2 should return 3 rows");
4168            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
4169            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(4));
4170            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(5));
4171        });
4172    }
4173
4174    #[test]
4175    fn probe_group_by_multi_col() {
4176        asupersync::test_utils::run_test(|| async {
4177            let conn = Connection::open(":memory:").await.unwrap();
4178            conn.execute(
4179                "CREATE TABLE t (id INTEGER PRIMARY KEY, cat TEXT, sub TEXT, val INTEGER);",
4180            )
4181            .await
4182            .unwrap();
4183            conn.execute("INSERT INTO t VALUES (1, 'A', 'x', 10);")
4184                .await
4185                .unwrap();
4186            conn.execute("INSERT INTO t VALUES (2, 'A', 'x', 20);")
4187                .await
4188                .unwrap();
4189            conn.execute("INSERT INTO t VALUES (3, 'A', 'y', 30);")
4190                .await
4191                .unwrap();
4192            conn.execute("INSERT INTO t VALUES (4, 'B', 'x', 40);")
4193                .await
4194                .unwrap();
4195            let rows = conn
4196                .query("SELECT cat, sub, SUM(val) FROM t GROUP BY cat, sub ORDER BY cat, sub;")
4197                .await
4198                .unwrap();
4199            assert_eq!(
4200                rows.len(),
4201                3,
4202                "Should have 3 groups: got {:?}",
4203                rows.iter().map(row_values).collect::<Vec<_>>()
4204            );
4205            assert_eq!(row_values(&rows[0])[2], SqliteValue::Integer(30));
4206            assert_eq!(row_values(&rows[1])[2], SqliteValue::Integer(30));
4207            assert_eq!(row_values(&rows[2])[2], SqliteValue::Integer(40));
4208        });
4209    }
4210
4211    #[test]
4212    fn probe_having_aggregate() {
4213        asupersync::test_utils::run_test(|| async {
4214            let conn = Connection::open(":memory:").await.unwrap();
4215            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, cat TEXT, val INTEGER);")
4216                .await
4217                .unwrap();
4218            conn.execute("INSERT INTO t VALUES (1, 'A', 10);")
4219                .await
4220                .unwrap();
4221            conn.execute("INSERT INTO t VALUES (2, 'A', 20);")
4222                .await
4223                .unwrap();
4224            conn.execute("INSERT INTO t VALUES (3, 'B', 30);")
4225                .await
4226                .unwrap();
4227            let rows = conn
4228                .query("SELECT cat, COUNT(*) as cnt FROM t GROUP BY cat HAVING cnt > 1;")
4229                .await
4230                .unwrap();
4231            assert_eq!(rows.len(), 1, "Only group A has count > 1");
4232            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("A".into()));
4233        });
4234    }
4235
4236    #[test]
4237    fn having_between_filters_groups() {
4238        asupersync::test_utils::run_test(|| async {
4239            let conn = Connection::open(":memory:").await.unwrap();
4240            conn.execute("CREATE TABLE hb (grp INTEGER, val INTEGER);")
4241                .await
4242                .unwrap();
4243            conn.execute(
4244                "INSERT INTO hb VALUES (1, 10), (1, 20), (2, 30), (3, 40), (3, 50), (3, 60);",
4245            )
4246            .await
4247            .unwrap();
4248            // COUNT(*) for groups: 1→2, 2→1, 3→3. HAVING cnt BETWEEN 2 AND 3 keeps 1,3.
4249            let rows = conn
4250                .query(
4251                    "SELECT grp, COUNT(*) as cnt FROM hb GROUP BY grp HAVING cnt BETWEEN 2 AND 3;",
4252                )
4253                .await
4254                .unwrap();
4255            assert_eq!(rows.len(), 2);
4256            let grps: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
4257            assert!(grps.contains(&SqliteValue::Integer(1)));
4258            assert!(grps.contains(&SqliteValue::Integer(3)));
4259        });
4260    }
4261
4262    #[test]
4263    fn having_in_filters_groups() {
4264        asupersync::test_utils::run_test(|| async {
4265            let conn = Connection::open(":memory:").await.unwrap();
4266            conn.execute("CREATE TABLE hi (grp TEXT, val INTEGER);")
4267                .await
4268                .unwrap();
4269            conn.execute("INSERT INTO hi VALUES ('A', 1), ('B', 2), ('C', 3);")
4270                .await
4271                .unwrap();
4272            let rows = conn
4273                .query("SELECT grp FROM hi GROUP BY grp HAVING grp IN ('A', 'C');")
4274                .await
4275                .unwrap();
4276            assert_eq!(rows.len(), 2);
4277            let grps: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
4278            assert!(grps.contains(&SqliteValue::Text("A".into())));
4279            assert!(grps.contains(&SqliteValue::Text("C".into())));
4280        });
4281    }
4282
4283    #[test]
4284    fn having_case_expression() {
4285        asupersync::test_utils::run_test(|| async {
4286            let conn = Connection::open(":memory:").await.unwrap();
4287            conn.execute("CREATE TABLE hc (grp TEXT, val INTEGER);")
4288                .await
4289                .unwrap();
4290            conn.execute("INSERT INTO hc VALUES ('X', 1), ('Y', 2), ('X', 3);")
4291                .await
4292                .unwrap();
4293            // CASE grp WHEN 'X' THEN 1 ELSE 0 END = 1 keeps only 'X'
4294            let rows = conn
4295            .query("SELECT grp, SUM(val) FROM hc GROUP BY grp HAVING CASE grp WHEN 'X' THEN 1 ELSE 0 END = 1;")
4296            .await
4297            .unwrap();
4298            assert_eq!(rows.len(), 1);
4299            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("X".into()));
4300        });
4301    }
4302
4303    #[test]
4304    fn like_null_operand_returns_null() {
4305        asupersync::test_utils::run_test(|| async {
4306            let conn = Connection::open(":memory:").await.unwrap();
4307            let rows = conn.query("SELECT NULL LIKE 'abc';").await.unwrap();
4308            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4309        });
4310    }
4311
4312    #[test]
4313    fn like_null_pattern_returns_null() {
4314        asupersync::test_utils::run_test(|| async {
4315            let conn = Connection::open(":memory:").await.unwrap();
4316            let rows = conn.query("SELECT 'abc' LIKE NULL;").await.unwrap();
4317            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4318        });
4319    }
4320
4321    #[test]
4322    fn like_null_both_returns_null() {
4323        asupersync::test_utils::run_test(|| async {
4324            let conn = Connection::open(":memory:").await.unwrap();
4325            let rows = conn.query("SELECT NULL LIKE NULL;").await.unwrap();
4326            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4327        });
4328    }
4329
4330    #[test]
4331    fn not_like_null_returns_null() {
4332        asupersync::test_utils::run_test(|| async {
4333            let conn = Connection::open(":memory:").await.unwrap();
4334            let rows = conn.query("SELECT 'abc' NOT LIKE NULL;").await.unwrap();
4335            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4336        });
4337    }
4338
4339    #[test]
4340    fn like_integer_coercion() {
4341        asupersync::test_utils::run_test(|| async {
4342            let conn = Connection::open(":memory:").await.unwrap();
4343            // SQLite coerces non-text to text for LIKE comparison.
4344            let rows = conn.query("SELECT 123 LIKE '123';").await.unwrap();
4345            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
4346        });
4347    }
4348
4349    #[test]
4350    fn like_null_in_join_where() {
4351        asupersync::test_utils::run_test(|| async {
4352            let conn = Connection::open(":memory:").await.unwrap();
4353            conn.execute("CREATE TABLE lnj (id INTEGER PRIMARY KEY, name TEXT);")
4354                .await
4355                .unwrap();
4356            conn.execute("INSERT INTO lnj VALUES (1, 'alice'), (2, NULL), (3, 'bob');")
4357                .await
4358                .unwrap();
4359            // NULL name LIKE '%' should not match (NULL result, not truthy).
4360            let rows = conn
4361                .query("SELECT id FROM lnj WHERE name LIKE '%' ORDER BY id;")
4362                .await
4363                .unwrap();
4364            assert_eq!(rows.len(), 2);
4365            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
4366            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(3));
4367        });
4368    }
4369
4370    #[test]
4371    fn having_like_filters_groups() {
4372        asupersync::test_utils::run_test(|| async {
4373            let conn = Connection::open(":memory:").await.unwrap();
4374            conn.execute("CREATE TABLE hlk (grp TEXT, val INTEGER);")
4375                .await
4376                .unwrap();
4377            conn.execute("INSERT INTO hlk VALUES ('apple', 1), ('banana', 2), ('apricot', 3);")
4378                .await
4379                .unwrap();
4380            // HAVING grp LIKE 'ap%' keeps only 'apple' and 'apricot'.
4381            let rows = conn
4382                .query("SELECT grp, SUM(val) FROM hlk GROUP BY grp HAVING grp LIKE 'ap%';")
4383                .await
4384                .unwrap();
4385            assert_eq!(rows.len(), 2);
4386            let grps: Vec<_> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
4387            assert!(grps.contains(&SqliteValue::Text("apple".into())));
4388            assert!(grps.contains(&SqliteValue::Text("apricot".into())));
4389        });
4390    }
4391
4392    #[test]
4393    fn case_null_base_does_not_match_null() {
4394        asupersync::test_utils::run_test(|| async {
4395            let conn = Connection::open(":memory:").await.unwrap();
4396            // NULL = NULL is UNKNOWN, not TRUE — CASE should go to ELSE.
4397            let rows = conn
4398                .query("SELECT CASE NULL WHEN NULL THEN 'match' ELSE 'no match' END;")
4399                .await
4400                .unwrap();
4401            assert_eq!(
4402                row_values(&rows[0])[0],
4403                SqliteValue::Text("no match".into())
4404            );
4405        });
4406    }
4407
4408    #[test]
4409    fn case_null_base_skips_all_whens() {
4410        asupersync::test_utils::run_test(|| async {
4411            let conn = Connection::open(":memory:").await.unwrap();
4412            let rows = conn
4413                .query("SELECT CASE NULL WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'none' END;")
4414                .await
4415                .unwrap();
4416            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("none".into()));
4417        });
4418    }
4419
4420    #[test]
4421    fn case_null_when_value_skipped() {
4422        asupersync::test_utils::run_test(|| async {
4423            let conn = Connection::open(":memory:").await.unwrap();
4424            // CASE 1 WHEN NULL should skip because 1 = NULL is UNKNOWN.
4425            let rows = conn
4426                .query("SELECT CASE 1 WHEN NULL THEN 'bad' WHEN 1 THEN 'ok' ELSE 'miss' END;")
4427                .await
4428                .unwrap();
4429            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("ok".into()));
4430        });
4431    }
4432
4433    #[test]
4434    fn case_null_in_join_filter() {
4435        asupersync::test_utils::run_test(|| async {
4436            let conn = Connection::open(":memory:").await.unwrap();
4437            conn.execute("CREATE TABLE cj (id INTEGER PRIMARY KEY, val TEXT);")
4438                .await
4439                .unwrap();
4440            conn.execute("INSERT INTO cj VALUES (1, NULL), (2, 'x'), (3, 'y');")
4441                .await
4442                .unwrap();
4443            // CASE val WHEN NULL: should never match, so id=1 gets 'other'.
4444            let rows = conn
4445            .query(
4446                "SELECT id, CASE val WHEN 'x' THEN 'found' ELSE 'other' END AS r FROM cj ORDER BY id;",
4447            )
4448            .await
4449            .unwrap();
4450            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("other".into()));
4451            assert_eq!(row_values(&rows[1])[1], SqliteValue::Text("found".into()));
4452        });
4453    }
4454
4455    #[test]
4456    fn cast_null_as_integer_returns_null() {
4457        asupersync::test_utils::run_test(|| async {
4458            let conn = Connection::open(":memory:").await.unwrap();
4459            let rows = conn.query("SELECT CAST(NULL AS INTEGER);").await.unwrap();
4460            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4461        });
4462    }
4463
4464    #[test]
4465    fn cast_null_as_real_returns_null() {
4466        asupersync::test_utils::run_test(|| async {
4467            let conn = Connection::open(":memory:").await.unwrap();
4468            let rows = conn.query("SELECT CAST(NULL AS REAL);").await.unwrap();
4469            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4470        });
4471    }
4472
4473    #[test]
4474    fn cast_null_as_text_returns_null() {
4475        asupersync::test_utils::run_test(|| async {
4476            let conn = Connection::open(":memory:").await.unwrap();
4477            let rows = conn.query("SELECT CAST(NULL AS TEXT);").await.unwrap();
4478            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4479        });
4480    }
4481
4482    #[test]
4483    fn cast_null_from_table_returns_null() {
4484        asupersync::test_utils::run_test(|| async {
4485            let conn = Connection::open(":memory:").await.unwrap();
4486            conn.execute("CREATE TABLE cn (id INTEGER PRIMARY KEY, val TEXT);")
4487                .await
4488                .unwrap();
4489            conn.execute("INSERT INTO cn VALUES (1, NULL), (2, '5');")
4490                .await
4491                .unwrap();
4492            // CAST(NULL AS INTEGER) should be NULL, not 0.
4493            let rows = conn
4494                .query("SELECT id, CAST(val AS INTEGER) FROM cn ORDER BY id;")
4495                .await
4496                .unwrap();
4497            assert_eq!(row_values(&rows[0])[1], SqliteValue::Null);
4498            assert_eq!(row_values(&rows[1])[1], SqliteValue::Integer(5));
4499        });
4500    }
4501
4502    #[test]
4503    fn collate_in_join_does_not_return_null() {
4504        asupersync::test_utils::run_test(|| async {
4505            let conn = Connection::open(":memory:").await.unwrap();
4506            conn.execute("CREATE TABLE cl (id INTEGER PRIMARY KEY, name TEXT);")
4507                .await
4508                .unwrap();
4509            conn.execute("INSERT INTO cl VALUES (1, 'Alice'), (2, 'bob');")
4510                .await
4511                .unwrap();
4512            // COLLATE should not silently return NULL — it should evaluate the inner expr.
4513            let rows = conn
4514                .query("SELECT id FROM cl WHERE name COLLATE NOCASE = 'alice' ORDER BY id;")
4515                .await
4516                .unwrap();
4517            // At minimum, id=1 should match (exact case match with 'Alice' compared via nocase).
4518            assert!(!rows.is_empty());
4519        });
4520    }
4521
4522    #[test]
4523    fn probe_nested_functions() {
4524        asupersync::test_utils::run_test(|| async {
4525            let conn = Connection::open(":memory:").await.unwrap();
4526            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4527                .await
4528                .unwrap();
4529            conn.execute("INSERT INTO t VALUES (1, '  hello  ');")
4530                .await
4531                .unwrap();
4532            let rows = conn.query("SELECT UPPER(TRIM(val)) FROM t;").await.unwrap();
4533            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("HELLO".into()));
4534        });
4535    }
4536
4537    #[test]
4538    fn replace_null_arg_returns_null() {
4539        asupersync::test_utils::run_test(|| async {
4540            let conn = Connection::open(":memory:").await.unwrap();
4541            let rows = conn.query("SELECT REPLACE(NULL, 'a', 'b');").await.unwrap();
4542            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4543            let rows = conn
4544                .query("SELECT REPLACE('hello', NULL, 'b');")
4545                .await
4546                .unwrap();
4547            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4548        });
4549    }
4550
4551    #[test]
4552    fn trim_null_returns_null() {
4553        asupersync::test_utils::run_test(|| async {
4554            let conn = Connection::open(":memory:").await.unwrap();
4555            let rows = conn.query("SELECT TRIM(NULL);").await.unwrap();
4556            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4557            let rows = conn.query("SELECT LTRIM(NULL);").await.unwrap();
4558            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4559            let rows = conn.query("SELECT RTRIM(NULL);").await.unwrap();
4560            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4561        });
4562    }
4563
4564    #[test]
4565    fn hex_null_returns_empty_string() {
4566        asupersync::test_utils::run_test(|| async {
4567            // C SQLite: hex(NULL) returns '' (empty string), not NULL.
4568            let conn = Connection::open(":memory:").await.unwrap();
4569            let rows = conn.query("SELECT HEX(NULL);").await.unwrap();
4570            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("".into()));
4571        });
4572    }
4573
4574    #[test]
4575    fn instr_null_returns_null() {
4576        asupersync::test_utils::run_test(|| async {
4577            let conn = Connection::open(":memory:").await.unwrap();
4578            let rows = conn.query("SELECT INSTR(NULL, 'x');").await.unwrap();
4579            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4580            let rows = conn.query("SELECT INSTR('hello', NULL);").await.unwrap();
4581            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4582        });
4583    }
4584
4585    #[test]
4586    fn substr_null_returns_null() {
4587        asupersync::test_utils::run_test(|| async {
4588            let conn = Connection::open(":memory:").await.unwrap();
4589            let rows = conn.query("SELECT SUBSTR(NULL, 1, 3);").await.unwrap();
4590            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4591        });
4592    }
4593
4594    #[test]
4595    fn substr_negative_start() {
4596        asupersync::test_utils::run_test(|| async {
4597            let conn = Connection::open(":memory:").await.unwrap();
4598            // Negative start counts from right: -1 = last char.
4599            let rows = conn.query("SELECT SUBSTR('hello', -1);").await.unwrap();
4600            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("o".into()));
4601            let rows = conn.query("SELECT SUBSTR('hello', -3);").await.unwrap();
4602            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("llo".into()));
4603        });
4604    }
4605
4606    #[test]
4607    fn limit_negative_returns_all_rows() {
4608        asupersync::test_utils::run_test(|| async {
4609            let conn = Connection::open(":memory:").await.unwrap();
4610            conn.execute("CREATE TABLE ln (id INTEGER PRIMARY KEY);")
4611                .await
4612                .unwrap();
4613            conn.execute("INSERT INTO ln VALUES (1), (2), (3), (4), (5);")
4614                .await
4615                .unwrap();
4616            // LIMIT -1 means unlimited in SQLite.
4617            let rows = conn
4618                .query("SELECT id FROM ln ORDER BY id LIMIT -1;")
4619                .await
4620                .unwrap();
4621            assert_eq!(rows.len(), 5);
4622        });
4623    }
4624
4625    #[test]
4626    fn offset_negative_treated_as_zero() {
4627        asupersync::test_utils::run_test(|| async {
4628            let conn = Connection::open(":memory:").await.unwrap();
4629            conn.execute("CREATE TABLE on_ (id INTEGER PRIMARY KEY);")
4630                .await
4631                .unwrap();
4632            conn.execute("INSERT INTO on_ VALUES (1), (2), (3);")
4633                .await
4634                .unwrap();
4635            // Negative OFFSET should be treated as 0.
4636            let rows = conn
4637                .query("SELECT id FROM on_ ORDER BY id LIMIT 2 OFFSET -5;")
4638                .await
4639                .unwrap();
4640            assert_eq!(rows.len(), 2);
4641            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
4642        });
4643    }
4644
4645    #[test]
4646    fn null_comparison_returns_null_in_join() {
4647        asupersync::test_utils::run_test(|| async {
4648            let conn = Connection::open(":memory:").await.unwrap();
4649            conn.execute("CREATE TABLE nc (id INTEGER PRIMARY KEY, val INTEGER);")
4650                .await
4651                .unwrap();
4652            conn.execute("INSERT INTO nc VALUES (1, NULL), (2, 5), (3, NULL);")
4653                .await
4654                .unwrap();
4655            // NULL = 5 should be NULL (not truthy), so row 1 excluded.
4656            // NULL = NULL should be NULL (not truthy), so row 3 excluded.
4657            let rows = conn
4658                .query("SELECT id FROM nc WHERE val = 5 ORDER BY id;")
4659                .await
4660                .unwrap();
4661            assert_eq!(rows.len(), 1);
4662            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
4663        });
4664    }
4665
4666    #[test]
4667    fn null_and_true_returns_null() {
4668        asupersync::test_utils::run_test(|| async {
4669            let conn = Connection::open(":memory:").await.unwrap();
4670            // NULL AND 1 should be NULL, not 0.
4671            let rows = conn.query("SELECT NULL AND 1;").await.unwrap();
4672            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4673        });
4674    }
4675
4676    #[test]
4677    fn null_or_false_returns_null() {
4678        asupersync::test_utils::run_test(|| async {
4679            let conn = Connection::open(":memory:").await.unwrap();
4680            // NULL OR 0 should be NULL, not 0.
4681            let rows = conn.query("SELECT NULL OR 0;").await.unwrap();
4682            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4683        });
4684    }
4685
4686    #[test]
4687    fn false_and_null_returns_false() {
4688        asupersync::test_utils::run_test(|| async {
4689            let conn = Connection::open(":memory:").await.unwrap();
4690            // 0 AND NULL should be 0 (FALSE short-circuits).
4691            let rows = conn.query("SELECT 0 AND NULL;").await.unwrap();
4692            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(0));
4693        });
4694    }
4695
4696    #[test]
4697    fn null_ne_in_where_excludes_row() {
4698        asupersync::test_utils::run_test(|| async {
4699            let conn = Connection::open(":memory:").await.unwrap();
4700            conn.execute("CREATE TABLE nne (id INTEGER PRIMARY KEY, val INTEGER);")
4701                .await
4702                .unwrap();
4703            conn.execute("INSERT INTO nne VALUES (1, NULL), (2, 5), (3, 10);")
4704                .await
4705                .unwrap();
4706            // NULL != 5 is NULL (not truthy), so id=1 excluded. 5 != 5 is false, so id=2 excluded.
4707            let rows = conn
4708                .query("SELECT id FROM nne WHERE val != 5 ORDER BY id;")
4709                .await
4710                .unwrap();
4711            assert_eq!(rows.len(), 1);
4712            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
4713        });
4714    }
4715
4716    #[test]
4717    fn mixed_type_comparison_uses_type_ordering() {
4718        asupersync::test_utils::run_test(|| async {
4719            let conn = Connection::open(":memory:").await.unwrap();
4720            conn.execute("CREATE TABLE mt (id INTEGER PRIMARY KEY, val);")
4721                .await
4722                .unwrap();
4723            // Integer 5 < Text 'hello' in SQLite type ordering (numeric < text).
4724            conn.execute("INSERT INTO mt VALUES (1, 5), (2, 'hello'), (3, 10);")
4725                .await
4726                .unwrap();
4727            // 5 = 'hello' should be FALSE (different type classes).
4728            let rows = conn
4729                .query("SELECT id FROM mt WHERE val = 'hello';")
4730                .await
4731                .unwrap();
4732            assert_eq!(rows.len(), 1);
4733            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
4734        });
4735    }
4736
4737    #[test]
4738    fn integer_less_than_text_in_type_ordering() {
4739        asupersync::test_utils::run_test(|| async {
4740            let conn = Connection::open(":memory:").await.unwrap();
4741            conn.execute("CREATE TABLE ilt (id INTEGER PRIMARY KEY, val);")
4742                .await
4743                .unwrap();
4744            conn.execute("INSERT INTO ilt VALUES (1, 42), (2, 'abc');")
4745                .await
4746                .unwrap();
4747            // Integer 42 < Text 'abc' in SQLite type ordering.
4748            let rows = conn
4749                .query("SELECT id FROM ilt WHERE val < 'abc';")
4750                .await
4751                .unwrap();
4752            assert_eq!(rows.len(), 1);
4753            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
4754        });
4755    }
4756
4757    #[test]
4758    fn blob_greater_than_text_in_type_ordering() {
4759        asupersync::test_utils::run_test(|| async {
4760            let conn = Connection::open(":memory:").await.unwrap();
4761            conn.execute("CREATE TABLE bgt (id INTEGER PRIMARY KEY, val);")
4762                .await
4763                .unwrap();
4764            conn.execute("INSERT INTO bgt VALUES (1, 'text'), (2, X'DEADBEEF');")
4765                .await
4766                .unwrap();
4767            // Blob > Text in SQLite type ordering.
4768            let rows = conn
4769                .query("SELECT id FROM bgt WHERE val > 'text';")
4770                .await
4771                .unwrap();
4772            assert_eq!(rows.len(), 1);
4773            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
4774        });
4775    }
4776
4777    #[test]
4778    fn large_integer_float_precision_comparison() {
4779        asupersync::test_utils::run_test(|| async {
4780            let conn = Connection::open(":memory:").await.unwrap();
4781            // 2^53 + 1 = 9007199254740993 cannot be exactly represented as f64.
4782            // 9007199254740993 > 9007199254740992.0 should be true.
4783            conn.execute("CREATE TABLE lip (id INTEGER PRIMARY KEY, ival INTEGER, fval REAL);")
4784                .await
4785                .unwrap();
4786            conn.execute("INSERT INTO lip VALUES (1, 9007199254740993, 9007199254740992.0);")
4787                .await
4788                .unwrap();
4789            let rows = conn
4790                .query("SELECT id FROM lip WHERE ival > fval;")
4791                .await
4792                .unwrap();
4793            assert_eq!(rows.len(), 1, "large integer should be greater than float");
4794        });
4795    }
4796
4797    #[test]
4798    fn not_null_returns_null() {
4799        asupersync::test_utils::run_test(|| async {
4800            let conn = Connection::open(":memory:").await.unwrap();
4801            // NOT NULL should be NULL, not 1.
4802            let rows = conn.query("SELECT NOT NULL;").await.unwrap();
4803            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4804        });
4805    }
4806
4807    #[test]
4808    fn not_null_in_where_excludes_row() {
4809        asupersync::test_utils::run_test(|| async {
4810            let conn = Connection::open(":memory:").await.unwrap();
4811            conn.execute("CREATE TABLE nn (id INTEGER PRIMARY KEY, flag INTEGER);")
4812                .await
4813                .unwrap();
4814            conn.execute("INSERT INTO nn VALUES (1, NULL), (2, 0), (3, 1);")
4815                .await
4816                .unwrap();
4817            // NOT flag: NOT NULL=NULL (excluded), NOT 0=1 (included), NOT 1=0 (excluded).
4818            let rows = conn
4819                .query("SELECT id FROM nn WHERE NOT flag ORDER BY id;")
4820                .await
4821                .unwrap();
4822            assert_eq!(rows.len(), 1);
4823            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
4824        });
4825    }
4826
4827    #[test]
4828    fn bitnot_null_returns_null() {
4829        asupersync::test_utils::run_test(|| async {
4830            let conn = Connection::open(":memory:").await.unwrap();
4831            // ~NULL should be NULL.
4832            let rows = conn.query("SELECT ~NULL;").await.unwrap();
4833            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
4834        });
4835    }
4836
4837    #[test]
4838    fn probe_update_where_column_cmp() {
4839        asupersync::test_utils::run_test(|| async {
4840            let conn = Connection::open(":memory:").await.unwrap();
4841            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER);")
4842                .await
4843                .unwrap();
4844            conn.execute("INSERT INTO t VALUES (1, 5, 10);")
4845                .await
4846                .unwrap();
4847            conn.execute("INSERT INTO t VALUES (2, 15, 10);")
4848                .await
4849                .unwrap();
4850            conn.execute("UPDATE t SET a = a * 2 WHERE a < b;")
4851                .await
4852                .unwrap();
4853            let rows = conn
4854                .query("SELECT id, a FROM t ORDER BY id;")
4855                .await
4856                .unwrap();
4857            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(10));
4858            assert_eq!(row_values(&rows[1])[1], SqliteValue::Integer(15));
4859        });
4860    }
4861
4862    #[test]
4863    fn probe_nullif() {
4864        asupersync::test_utils::run_test(|| async {
4865            let conn = Connection::open(":memory:").await.unwrap();
4866            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4867                .await
4868                .unwrap();
4869            conn.execute("INSERT INTO t VALUES (1, 'x');")
4870                .await
4871                .unwrap();
4872            conn.execute("INSERT INTO t VALUES (2, 'skip');")
4873                .await
4874                .unwrap();
4875            let rows = conn
4876                .query("SELECT id, NULLIF(val, 'skip') FROM t ORDER BY id;")
4877                .await
4878                .unwrap();
4879            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("x".into()));
4880            assert_eq!(row_values(&rows[1])[1], SqliteValue::Null);
4881        });
4882    }
4883
4884    #[test]
4885    fn probe_iif() {
4886        asupersync::test_utils::run_test(|| async {
4887            let conn = Connection::open(":memory:").await.unwrap();
4888            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
4889                .await
4890                .unwrap();
4891            conn.execute("INSERT INTO t VALUES (1, 5);").await.unwrap();
4892            conn.execute("INSERT INTO t VALUES (2, 15);").await.unwrap();
4893            let rows = conn
4894                .query("SELECT id, IIF(val > 10, 'big', 'small') FROM t ORDER BY id;")
4895                .await
4896                .unwrap();
4897            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("small".into()));
4898            assert_eq!(row_values(&rows[1])[1], SqliteValue::Text("big".into()));
4899        });
4900    }
4901
4902    #[test]
4903    fn probe_select_distinct() {
4904        asupersync::test_utils::run_test(|| async {
4905            let conn = Connection::open(":memory:").await.unwrap();
4906            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4907                .await
4908                .unwrap();
4909            conn.execute("INSERT INTO t VALUES (1, 'a');")
4910                .await
4911                .unwrap();
4912            conn.execute("INSERT INTO t VALUES (2, 'b');")
4913                .await
4914                .unwrap();
4915            conn.execute("INSERT INTO t VALUES (3, 'a');")
4916                .await
4917                .unwrap();
4918            conn.execute("INSERT INTO t VALUES (4, 'b');")
4919                .await
4920                .unwrap();
4921            conn.execute("INSERT INTO t VALUES (5, 'c');")
4922                .await
4923                .unwrap();
4924            let rows = conn
4925                .query("SELECT DISTINCT val FROM t ORDER BY val;")
4926                .await
4927                .unwrap();
4928            assert_eq!(rows.len(), 3, "DISTINCT should return 3 unique values");
4929            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("a".into()));
4930            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("b".into()));
4931            assert_eq!(row_values(&rows[2])[0], SqliteValue::Text("c".into()));
4932        });
4933    }
4934
4935    #[test]
4936    fn probe_order_by_desc() {
4937        asupersync::test_utils::run_test(|| async {
4938            let conn = Connection::open(":memory:").await.unwrap();
4939            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
4940                .await
4941                .unwrap();
4942            conn.execute("INSERT INTO t VALUES (1, 30);").await.unwrap();
4943            conn.execute("INSERT INTO t VALUES (2, 10);").await.unwrap();
4944            conn.execute("INSERT INTO t VALUES (3, 20);").await.unwrap();
4945            let rows = conn
4946                .query("SELECT id, val FROM t ORDER BY val DESC;")
4947                .await
4948                .unwrap();
4949            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
4950            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(3));
4951            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(2));
4952        });
4953    }
4954
4955    #[test]
4956    fn probe_insert_or_replace() {
4957        asupersync::test_utils::run_test(|| async {
4958            let conn = Connection::open(":memory:").await.unwrap();
4959            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4960                .await
4961                .unwrap();
4962            conn.execute("INSERT INTO t VALUES (1, 'old');")
4963                .await
4964                .unwrap();
4965            conn.execute("INSERT OR REPLACE INTO t VALUES (1, 'new');")
4966                .await
4967                .unwrap();
4968            let rows = conn.query("SELECT id, val FROM t;").await.unwrap();
4969            assert_eq!(rows.len(), 1);
4970            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("new".into()));
4971        });
4972    }
4973
4974    #[test]
4975    fn probe_insert_or_ignore() {
4976        asupersync::test_utils::run_test(|| async {
4977            let conn = Connection::open(":memory:").await.unwrap();
4978            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
4979                .await
4980                .unwrap();
4981            conn.execute("INSERT INTO t VALUES (1, 'first');")
4982                .await
4983                .unwrap();
4984            conn.execute("INSERT OR IGNORE INTO t VALUES (1, 'second');")
4985                .await
4986                .unwrap();
4987            let rows = conn.query("SELECT id, val FROM t;").await.unwrap();
4988            assert_eq!(rows.len(), 1);
4989            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("first".into()));
4990        });
4991    }
4992
4993    #[test]
4994    fn probe_between() {
4995        asupersync::test_utils::run_test(|| async {
4996            let conn = Connection::open(":memory:").await.unwrap();
4997            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
4998                .await
4999                .unwrap();
5000            for i in 1..=10 {
5001                conn.execute(&format!("INSERT INTO t VALUES ({i}, {i});"))
5002                    .await
5003                    .unwrap();
5004            }
5005            let rows = conn
5006                .query("SELECT val FROM t WHERE val BETWEEN 3 AND 7 ORDER BY val;")
5007                .await
5008                .unwrap();
5009            assert_eq!(rows.len(), 5);
5010            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
5011            assert_eq!(row_values(&rows[4])[0], SqliteValue::Integer(7));
5012        });
5013    }
5014
5015    #[test]
5016    fn probe_in_list() {
5017        asupersync::test_utils::run_test(|| async {
5018            let conn = Connection::open(":memory:").await.unwrap();
5019            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5020                .await
5021                .unwrap();
5022            conn.execute("INSERT INTO t VALUES (1, 'a');")
5023                .await
5024                .unwrap();
5025            conn.execute("INSERT INTO t VALUES (2, 'b');")
5026                .await
5027                .unwrap();
5028            conn.execute("INSERT INTO t VALUES (3, 'c');")
5029                .await
5030                .unwrap();
5031            let rows = conn
5032                .query("SELECT id FROM t WHERE val IN ('a', 'c') ORDER BY id;")
5033                .await
5034                .unwrap();
5035            assert_eq!(rows.len(), 2);
5036            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
5037            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(3));
5038        });
5039    }
5040
5041    #[test]
5042    fn probe_like_pattern() {
5043        asupersync::test_utils::run_test(|| async {
5044            let conn = Connection::open(":memory:").await.unwrap();
5045            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5046                .await
5047                .unwrap();
5048            conn.execute("INSERT INTO t VALUES (1, 'Alice');")
5049                .await
5050                .unwrap();
5051            conn.execute("INSERT INTO t VALUES (2, 'Bob');")
5052                .await
5053                .unwrap();
5054            conn.execute("INSERT INTO t VALUES (3, 'Charlie');")
5055                .await
5056                .unwrap();
5057            let rows = conn
5058                .query("SELECT name FROM t WHERE name LIKE '%li%' ORDER BY name;")
5059                .await
5060                .unwrap();
5061            assert_eq!(rows.len(), 2);
5062            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("Alice".into()));
5063            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("Charlie".into()));
5064        });
5065    }
5066
5067    #[test]
5068    fn probe_subquery_in_where() {
5069        asupersync::test_utils::run_test(|| async {
5070            let conn = Connection::open(":memory:").await.unwrap();
5071            conn.execute("CREATE TABLE t1 (id INTEGER PRIMARY KEY, val TEXT);")
5072                .await
5073                .unwrap();
5074            conn.execute("CREATE TABLE t2 (id INTEGER PRIMARY KEY, t1_id INTEGER);")
5075                .await
5076                .unwrap();
5077            conn.execute("INSERT INTO t1 VALUES (1, 'a');")
5078                .await
5079                .unwrap();
5080            conn.execute("INSERT INTO t1 VALUES (2, 'b');")
5081                .await
5082                .unwrap();
5083            conn.execute("INSERT INTO t1 VALUES (3, 'c');")
5084                .await
5085                .unwrap();
5086            conn.execute("INSERT INTO t2 VALUES (1, 1);").await.unwrap();
5087            conn.execute("INSERT INTO t2 VALUES (2, 3);").await.unwrap();
5088            // This may not be supported - check if it errors gracefully
5089            let result = conn
5090                .query("SELECT val FROM t1 WHERE id IN (SELECT t1_id FROM t2) ORDER BY val;")
5091                .await;
5092            if let Ok(rows) = result {
5093                assert_eq!(rows.len(), 2);
5094                assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("a".into()));
5095                assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("c".into()));
5096            } else {
5097                // IN subquery not yet supported — that's fine for now
5098            }
5099        });
5100    }
5101
5102    // Test: INSERT ... RETURNING *
5103    #[test]
5104    fn probe_insert_returning_star() {
5105        asupersync::test_utils::run_test(|| async {
5106            let conn = Connection::open(":memory:").await.unwrap();
5107            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")
5108                .await
5109                .unwrap();
5110            let rows = conn
5111                .query("INSERT INTO t VALUES (1, 'Alice', 30) RETURNING *;")
5112                .await
5113                .unwrap();
5114            assert_eq!(rows.len(), 1, "RETURNING * should produce 1 row");
5115            // RETURNING * includes all columns: id (rowid alias), name, age
5116            assert_eq!(
5117                row_values(&rows[0]),
5118                vec![
5119                    SqliteValue::Integer(1),
5120                    SqliteValue::Text("Alice".into()),
5121                    SqliteValue::Integer(30),
5122                ]
5123            );
5124        });
5125    }
5126
5127    // Test: INSERT ... RETURNING specific columns
5128    #[test]
5129    fn probe_insert_returning_columns() {
5130        asupersync::test_utils::run_test(|| async {
5131            let conn = Connection::open(":memory:").await.unwrap();
5132            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")
5133                .await
5134                .unwrap();
5135            let rows = conn
5136                .query("INSERT INTO t VALUES (1, 'Bob', 25) RETURNING name, age;")
5137                .await
5138                .unwrap();
5139            assert_eq!(rows.len(), 1);
5140            assert_eq!(
5141                row_values(&rows[0]),
5142                vec![SqliteValue::Text("Bob".into()), SqliteValue::Integer(25),]
5143            );
5144        });
5145    }
5146
5147    // Test: INSERT ... RETURNING rowid
5148    #[test]
5149    fn probe_insert_returning_rowid() {
5150        asupersync::test_utils::run_test(|| async {
5151            let conn = Connection::open(":memory:").await.unwrap();
5152            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5153                .await
5154                .unwrap();
5155            let rows = conn
5156                .query("INSERT INTO t VALUES (42, 'test') RETURNING id;")
5157                .await
5158                .unwrap();
5159            assert_eq!(rows.len(), 1);
5160            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(42));
5161        });
5162    }
5163
5164    // Test: Multi-row INSERT ... RETURNING
5165    #[test]
5166    fn probe_insert_returning_multi_row() {
5167        asupersync::test_utils::run_test(|| async {
5168            let conn = Connection::open(":memory:").await.unwrap();
5169            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5170                .await
5171                .unwrap();
5172            conn.execute("INSERT INTO t VALUES (1, 'a');")
5173                .await
5174                .unwrap();
5175            conn.execute("INSERT INTO t VALUES (2, 'b');")
5176                .await
5177                .unwrap();
5178            // INSERT SELECT with RETURNING
5179            conn.execute("CREATE TABLE t2 (id INTEGER PRIMARY KEY, val TEXT);")
5180                .await
5181                .unwrap();
5182            let rows = conn
5183                .query("INSERT INTO t2 SELECT * FROM t RETURNING *;")
5184                .await
5185                .unwrap();
5186            assert_eq!(
5187                rows.len(),
5188                2,
5189                "Multi-row INSERT RETURNING should produce 2 rows"
5190            );
5191        });
5192    }
5193
5194    // Test: UPDATE ... RETURNING *
5195    #[test]
5196    fn probe_update_returning_star() {
5197        asupersync::test_utils::run_test(|| async {
5198            let conn = Connection::open(":memory:").await.unwrap();
5199            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")
5200                .await
5201                .unwrap();
5202            conn.execute("INSERT INTO t VALUES (1, 'Alice', 30);")
5203                .await
5204                .unwrap();
5205            conn.execute("INSERT INTO t VALUES (2, 'Bob', 25);")
5206                .await
5207                .unwrap();
5208            let rows = conn
5209                .query("UPDATE t SET age = age + 1 WHERE id = 1 RETURNING *;")
5210                .await
5211                .unwrap();
5212            assert_eq!(rows.len(), 1, "UPDATE RETURNING should produce 1 row");
5213            assert_eq!(
5214                row_values(&rows[0]),
5215                vec![
5216                    SqliteValue::Integer(1),
5217                    SqliteValue::Text("Alice".into()),
5218                    SqliteValue::Integer(31),
5219                ]
5220            );
5221        });
5222    }
5223
5224    // Test: UPDATE ... RETURNING specific columns
5225    #[test]
5226    fn probe_update_returning_columns() {
5227        asupersync::test_utils::run_test(|| async {
5228            let conn = Connection::open(":memory:").await.unwrap();
5229            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
5230                .await
5231                .unwrap();
5232            conn.execute("INSERT INTO t VALUES (1, 10);").await.unwrap();
5233            conn.execute("INSERT INTO t VALUES (2, 20);").await.unwrap();
5234            let rows = conn
5235                .query("UPDATE t SET val = val * 2 RETURNING id, val;")
5236                .await
5237                .unwrap();
5238            assert_eq!(rows.len(), 2, "UPDATE RETURNING should produce 2 rows");
5239        });
5240    }
5241
5242    // Test: DELETE ... RETURNING *
5243    #[test]
5244    fn probe_delete_returning_star() {
5245        asupersync::test_utils::run_test(|| async {
5246            let conn = Connection::open(":memory:").await.unwrap();
5247            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5248                .await
5249                .unwrap();
5250            conn.execute("INSERT INTO t VALUES (1, 'Alice');")
5251                .await
5252                .unwrap();
5253            conn.execute("INSERT INTO t VALUES (2, 'Bob');")
5254                .await
5255                .unwrap();
5256            let rows = conn
5257                .query("DELETE FROM t WHERE id = 2 RETURNING *;")
5258                .await
5259                .unwrap();
5260            assert_eq!(rows.len(), 1, "DELETE RETURNING should produce 1 row");
5261            assert_eq!(
5262                row_values(&rows[0]),
5263                vec![SqliteValue::Integer(2), SqliteValue::Text("Bob".into()),]
5264            );
5265            // Verify the row is actually deleted
5266            let remaining = conn.query("SELECT COUNT(*) FROM t;").await.unwrap();
5267            assert_eq!(row_values(&remaining[0])[0], SqliteValue::Integer(1));
5268        });
5269    }
5270
5271    // Test: DELETE ... RETURNING specific column
5272    #[test]
5273    fn probe_delete_returning_column() {
5274        asupersync::test_utils::run_test(|| async {
5275            let conn = Connection::open(":memory:").await.unwrap();
5276            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5277                .await
5278                .unwrap();
5279            conn.execute("INSERT INTO t VALUES (1, 'a');")
5280                .await
5281                .unwrap();
5282            conn.execute("INSERT INTO t VALUES (2, 'b');")
5283                .await
5284                .unwrap();
5285            conn.execute("INSERT INTO t VALUES (3, 'c');")
5286                .await
5287                .unwrap();
5288            let rows = conn
5289                .query("DELETE FROM t WHERE id > 1 RETURNING val;")
5290                .await
5291                .unwrap();
5292            assert_eq!(rows.len(), 2, "DELETE RETURNING should produce 2 rows");
5293        });
5294    }
5295
5296    // Test: INSERT DEFAULT VALUES
5297    #[test]
5298    fn probe_insert_default_values() {
5299        asupersync::test_utils::run_test(|| async {
5300            let conn = Connection::open(":memory:").await.unwrap();
5301            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, val INTEGER);")
5302                .await
5303                .unwrap();
5304            conn.execute("INSERT INTO t DEFAULT VALUES;").await.unwrap();
5305            let rows = conn.query("SELECT id, name, val FROM t;").await.unwrap();
5306            assert_eq!(rows.len(), 1, "DEFAULT VALUES should insert 1 row");
5307            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
5308            // name and val should be NULL (defaults)
5309            assert_eq!(row_values(&rows[0])[1], SqliteValue::Null);
5310            assert_eq!(row_values(&rows[0])[2], SqliteValue::Null);
5311        });
5312    }
5313
5314    #[test]
5315    fn insert_default_values_uses_column_defaults() {
5316        asupersync::test_utils::run_test(|| async {
5317            let conn = Connection::open(":memory:").await.unwrap();
5318            conn.execute(
5319            "CREATE TABLE td (id INTEGER PRIMARY KEY, status TEXT DEFAULT 'active', count INTEGER DEFAULT 42, ratio REAL DEFAULT 2.5);",
5320        )
5321        .await
5322        .unwrap();
5323            conn.execute("INSERT INTO td DEFAULT VALUES;")
5324                .await
5325                .unwrap();
5326            let rows = conn
5327                .query("SELECT id, status, count, ratio FROM td;")
5328                .await
5329                .unwrap();
5330            assert_eq!(rows.len(), 1);
5331            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
5332            assert_eq!(
5333                row_values(&rows[0])[1],
5334                SqliteValue::Text("active".to_string().into()),
5335                "status should use DEFAULT 'active'"
5336            );
5337            assert_eq!(
5338                row_values(&rows[0])[2],
5339                SqliteValue::Integer(42),
5340                "count should use DEFAULT 42"
5341            );
5342            assert_eq!(
5343                row_values(&rows[0])[3],
5344                SqliteValue::Float(2.5),
5345                "ratio should use DEFAULT 2.5"
5346            );
5347        });
5348    }
5349
5350    #[test]
5351    fn insert_default_values_uses_expression_defaults() {
5352        asupersync::test_utils::run_test(|| async {
5353            let conn = Connection::open(":memory:").await.unwrap();
5354            conn.execute(
5355            "CREATE TABLE td (id INTEGER PRIMARY KEY, total INTEGER DEFAULT (40 + 2), label TEXT DEFAULT lower('HELLO'));",
5356        )
5357        .await
5358        .unwrap();
5359            conn.execute("INSERT INTO td DEFAULT VALUES;")
5360                .await
5361                .unwrap();
5362            let rows = conn
5363                .query("SELECT id, total, label FROM td;")
5364                .await
5365                .unwrap();
5366            assert_eq!(rows.len(), 1);
5367            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
5368            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(42));
5369            assert_eq!(row_values(&rows[0])[2], SqliteValue::Text("hello".into()));
5370        });
5371    }
5372
5373    #[test]
5374    fn create_table_rejects_non_constant_default_expression() {
5375        asupersync::test_utils::run_test(|| async {
5376            let conn = Connection::open(":memory:").await.unwrap();
5377            let err = conn
5378                .execute("CREATE TABLE t(a INTEGER, b INTEGER DEFAULT (a + 1));")
5379                .await
5380                .expect_err("column-reference DEFAULT should be rejected");
5381            let msg = err.to_string();
5382            assert!(
5383                msg.contains("default value of column [b] is not constant"),
5384                "unexpected error: {msg}"
5385            );
5386        });
5387    }
5388
5389    #[test]
5390    fn create_table_rejects_aggregate_default_expression() {
5391        asupersync::test_utils::run_test(|| async {
5392            let conn = Connection::open(":memory:").await.unwrap();
5393            let err = conn
5394                .execute("CREATE TABLE t(a INTEGER, b INTEGER DEFAULT (sum(1)));")
5395                .await
5396                .expect_err("aggregate DEFAULT should be rejected");
5397            let msg = err.to_string();
5398            assert!(
5399                msg.contains("default value of column [b] is not constant"),
5400                "unexpected error: {msg}"
5401            );
5402        });
5403    }
5404
5405    #[test]
5406    fn create_table_rejects_hidden_aggregate_default_expression() {
5407        asupersync::test_utils::run_test(|| async {
5408            let conn = Connection::open(":memory:").await.unwrap();
5409            let err = conn
5410                .execute("CREATE TABLE t(a INTEGER, b INTEGER DEFAULT (1 IN (sum(1))));")
5411                .await
5412                .expect_err("aggregate hidden inside IN DEFAULT should be rejected");
5413            let msg = err.to_string();
5414            assert!(
5415                msg.contains("default value of column [b] is not constant"),
5416                "unexpected error: {msg}"
5417            );
5418        });
5419    }
5420
5421    #[test]
5422    fn alter_table_add_column_rejects_non_constant_default_expression() {
5423        asupersync::test_utils::run_test(|| async {
5424            let conn = Connection::open(":memory:").await.unwrap();
5425            conn.execute("CREATE TABLE t(a INTEGER);").await.unwrap();
5426            let err = conn
5427                .execute("ALTER TABLE t ADD COLUMN b INTEGER DEFAULT (a + 1);")
5428                .await
5429                .expect_err("column-reference DEFAULT should be rejected");
5430            let msg = err.to_string();
5431            assert!(
5432                msg.contains("default value of column [b] is not constant"),
5433                "unexpected error: {msg}"
5434            );
5435        });
5436    }
5437
5438    #[test]
5439    fn alter_table_add_column_rejects_hidden_aggregate_default_expression() {
5440        asupersync::test_utils::run_test(|| async {
5441            let conn = Connection::open(":memory:").await.unwrap();
5442            conn.execute("CREATE TABLE t(a INTEGER);").await.unwrap();
5443            let err = conn
5444                .execute("ALTER TABLE t ADD COLUMN b INTEGER DEFAULT (1 IN (sum(1)));")
5445                .await
5446                .expect_err("aggregate hidden inside IN DEFAULT should be rejected");
5447            let msg = err.to_string();
5448            assert!(
5449                msg.contains("default value of column [b] is not constant"),
5450                "unexpected error: {msg}"
5451            );
5452        });
5453    }
5454
5455    #[test]
5456    fn alter_table_add_column_rejects_aggregate_default_expression() {
5457        asupersync::test_utils::run_test(|| async {
5458            let conn = Connection::open(":memory:").await.unwrap();
5459            conn.execute("CREATE TABLE t(a INTEGER);").await.unwrap();
5460            let err = conn
5461                .execute("ALTER TABLE t ADD COLUMN b INTEGER DEFAULT (sum(1));")
5462                .await
5463                .expect_err("aggregate DEFAULT should be rejected");
5464            let msg = err.to_string();
5465            assert!(
5466                msg.contains("default value of column [b] is not constant"),
5467                "unexpected error: {msg}"
5468            );
5469        });
5470    }
5471
5472    #[test]
5473    fn insert_explicit_cols_uses_defaults_for_omitted() {
5474        asupersync::test_utils::run_test(|| async {
5475            let conn = Connection::open(":memory:").await.unwrap();
5476            conn.execute(
5477            "CREATE TABLE te (id INTEGER PRIMARY KEY, name TEXT, status TEXT DEFAULT 'pending');",
5478        )
5479        .await
5480        .unwrap();
5481            // Only specify name, omit status — should get DEFAULT 'pending'.
5482            conn.execute("INSERT INTO te (name) VALUES ('alice');")
5483                .await
5484                .unwrap();
5485            let rows = conn
5486                .query("SELECT id, name, status FROM te;")
5487                .await
5488                .unwrap();
5489            assert_eq!(rows.len(), 1);
5490            assert_eq!(
5491                row_values(&rows[0])[1],
5492                SqliteValue::Text("alice".to_string().into())
5493            );
5494            assert_eq!(
5495                row_values(&rows[0])[2],
5496                SqliteValue::Text("pending".to_string().into()),
5497                "omitted column should use DEFAULT 'pending'"
5498            );
5499        });
5500    }
5501
5502    // Test: INSERT DEFAULT VALUES with RETURNING (IPK column)
5503    #[test]
5504    fn probe_insert_default_values_returning() {
5505        asupersync::test_utils::run_test(|| async {
5506            let conn = Connection::open(":memory:").await.unwrap();
5507            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5508                .await
5509                .unwrap();
5510            // Use RETURNING id (IPK column) — tests that IPK columns emit Rowid
5511            // instead of Column (which would return Null for DEFAULT VALUES).
5512            let rows = conn
5513                .query("INSERT INTO t DEFAULT VALUES RETURNING id;")
5514                .await
5515                .unwrap();
5516            assert_eq!(
5517                rows.len(),
5518                1,
5519                "DEFAULT VALUES RETURNING should produce 1 row"
5520            );
5521            // rowid should be auto-assigned (1)
5522            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
5523        });
5524    }
5525
5526    // =================================================================
5527    // IPK integration tests (bd-3l6e / PARITY-B5)
5528    // =================================================================
5529
5530    /// NULL IPK should auto-generate an incrementing rowid.
5531    #[test]
5532    fn ipk_null_auto_generates_rowid() {
5533        asupersync::test_utils::run_test(|| async {
5534            let conn = Connection::open(":memory:").await.unwrap();
5535            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5536                .await
5537                .unwrap();
5538            let r1 = conn
5539                .query("INSERT INTO t VALUES (NULL, 'a') RETURNING id;")
5540                .await
5541                .unwrap();
5542            let r2 = conn
5543                .query("INSERT INTO t VALUES (NULL, 'b') RETURNING id;")
5544                .await
5545                .unwrap();
5546            let id1 = &row_values(&r1[0])[0];
5547            let id2 = &row_values(&r2[0])[0];
5548            // Both should be positive integers.
5549            assert!(
5550                matches!(id1, SqliteValue::Integer(n) if *n > 0),
5551                "NULL IPK should auto-generate positive id, got {id1:?}"
5552            );
5553            // Second should be greater than first.
5554            if let (SqliteValue::Integer(a), SqliteValue::Integer(b)) = (id1, id2) {
5555                assert!(
5556                    b > a,
5557                    "successive NULL IPK inserts should increment: {a} < {b}"
5558                );
5559            }
5560        });
5561    }
5562
5563    /// Explicit IPK value of 0 should be stored as rowid 0.
5564    #[test]
5565    fn ipk_zero_is_valid_rowid() {
5566        asupersync::test_utils::run_test(|| async {
5567            let conn = Connection::open(":memory:").await.unwrap();
5568            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5569                .await
5570                .unwrap();
5571            let rows = conn
5572                .query("INSERT INTO t VALUES (0, 'zero') RETURNING id;")
5573                .await
5574                .unwrap();
5575            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(0));
5576        });
5577    }
5578
5579    /// Negative IPK values should be stored as negative rowids.
5580    #[test]
5581    fn ipk_negative_is_valid_rowid() {
5582        asupersync::test_utils::run_test(|| async {
5583            let conn = Connection::open(":memory:").await.unwrap();
5584            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5585                .await
5586                .unwrap();
5587            let rows = conn
5588                .query("INSERT INTO t VALUES (-5, 'neg') RETURNING id;")
5589                .await
5590                .unwrap();
5591            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(-5));
5592        });
5593    }
5594
5595    /// Multi-row INSERT with explicit IPK values.
5596    #[test]
5597    fn ipk_multi_row_explicit_values() {
5598        asupersync::test_utils::run_test(|| async {
5599            let conn = Connection::open(":memory:").await.unwrap();
5600            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5601                .await
5602                .unwrap();
5603            let rows = conn
5604                .query("INSERT INTO t VALUES (10,'a'),(20,'b'),(30,'c') RETURNING id;")
5605                .await
5606                .unwrap();
5607            assert_eq!(rows.len(), 3);
5608            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(10));
5609            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(20));
5610            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(30));
5611        });
5612    }
5613
5614    /// Mixed NULL and explicit IPK in multi-row INSERT.
5615    #[test]
5616    fn ipk_mixed_null_and_explicit() {
5617        asupersync::test_utils::run_test(|| async {
5618            let conn = Connection::open(":memory:").await.unwrap();
5619            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5620                .await
5621                .unwrap();
5622            conn.execute("INSERT INTO t VALUES (10, 'explicit');")
5623                .await
5624                .unwrap();
5625            let rows = conn
5626                .query("INSERT INTO t VALUES (NULL, 'auto') RETURNING id;")
5627                .await
5628                .unwrap();
5629            // Auto-generated id should be > 10 (the max existing rowid).
5630            if let SqliteValue::Integer(id) = &row_values(&rows[0])[0] {
5631                assert!(
5632                    *id > 10,
5633                    "auto-generated id after max=10 should be > 10, got {id}"
5634                );
5635            } else {
5636                panic!("expected Integer, got {:?}", row_values(&rows[0])[0]);
5637            }
5638        });
5639    }
5640
5641    /// RETURNING * should include the correct IPK value.
5642    #[test]
5643    fn ipk_returning_star_includes_correct_id() {
5644        asupersync::test_utils::run_test(|| async {
5645            let conn = Connection::open(":memory:").await.unwrap();
5646            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5647                .await
5648                .unwrap();
5649            let rows = conn
5650                .query("INSERT INTO t VALUES (42, 'x') RETURNING *;")
5651                .await
5652                .unwrap();
5653            let vals = row_values(&rows[0]);
5654            assert_eq!(vals[0], SqliteValue::Integer(42));
5655            assert_eq!(vals[1], SqliteValue::Text("x".into()));
5656        });
5657    }
5658
5659    /// SELECT after INSERT should see the correct IPK values.
5660    #[test]
5661    fn ipk_roundtrip_select_after_insert() {
5662        asupersync::test_utils::run_test(|| async {
5663            let conn = Connection::open(":memory:").await.unwrap();
5664            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5665                .await
5666                .unwrap();
5667            conn.execute("INSERT INTO t VALUES (42, 'Alice');")
5668                .await
5669                .unwrap();
5670            conn.execute("INSERT INTO t VALUES (100, 'Bob');")
5671                .await
5672                .unwrap();
5673            let rows = conn.query("SELECT * FROM t ORDER BY id;").await.unwrap();
5674            assert_eq!(rows.len(), 2);
5675            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(42));
5676            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("Alice".into()));
5677            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(100));
5678        });
5679    }
5680
5681    /// Explicit column list in non-schema order should store values correctly.
5682    #[test]
5683    fn ipk_column_list_reorder() {
5684        asupersync::test_utils::run_test(|| async {
5685            let conn = Connection::open(":memory:").await.unwrap();
5686            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5687                .await
5688                .unwrap();
5689            // Column list reverses schema order: (name, id) vs schema (id, name).
5690            let rows = conn
5691                .query("INSERT INTO t(name, id) VALUES ('Alice', 42) RETURNING *;")
5692                .await
5693                .unwrap();
5694            let vals = row_values(&rows[0]);
5695            assert_eq!(
5696                vals[0],
5697                SqliteValue::Integer(42),
5698                "id should be 42 (from column-list position 1)"
5699            );
5700            assert_eq!(
5701                vals[1],
5702                SqliteValue::Text("Alice".into()),
5703                "name should be Alice (from column-list position 0)"
5704            );
5705            // Also verify via SELECT that the stored record is correct.
5706            let sel = conn.query("SELECT id, name FROM t;").await.unwrap();
5707            let sv = row_values(&sel[0]);
5708            assert_eq!(sv[0], SqliteValue::Integer(42));
5709            assert_eq!(sv[1], SqliteValue::Text("Alice".into()));
5710        });
5711    }
5712
5713    #[test]
5714    fn insert_select_without_from_reorders_targets_and_fills_defaults() {
5715        asupersync::test_utils::run_test(|| async {
5716            let conn = Connection::open(":memory:").await.unwrap();
5717            conn.execute(
5718            "CREATE TABLE dst (id INTEGER PRIMARY KEY, label TEXT DEFAULT 'seed', qty INTEGER);",
5719        )
5720        .await
5721        .unwrap();
5722
5723            let rows = conn
5724                .query("INSERT INTO dst(qty) SELECT 11 RETURNING label, qty;")
5725                .await
5726                .unwrap();
5727            assert_eq!(
5728                row_values(&rows[0]),
5729                vec![SqliteValue::Text("seed".into()), SqliteValue::Integer(11)]
5730            );
5731
5732            let rows = conn
5733                .query("INSERT INTO dst(qty, label) SELECT 22, 'fresh' RETURNING label, qty;")
5734                .await
5735                .unwrap();
5736            assert_eq!(
5737                row_values(&rows[0]),
5738                vec![SqliteValue::Text("fresh".into()), SqliteValue::Integer(22)]
5739            );
5740        });
5741    }
5742
5743    #[test]
5744    fn insert_select_without_from_explicit_rowid_is_preserved() {
5745        asupersync::test_utils::run_test(|| async {
5746            let conn = Connection::open(":memory:").await.unwrap();
5747            conn.execute("CREATE TABLE t (payload TEXT);")
5748                .await
5749                .unwrap();
5750
5751            let rows = conn
5752                .query("INSERT INTO t(rowid, payload) SELECT 7, 'x' RETURNING rowid, payload;")
5753                .await
5754                .unwrap();
5755            assert_eq!(
5756                row_values(&rows[0]),
5757                vec![SqliteValue::Integer(7), SqliteValue::Text("x".into())]
5758            );
5759        });
5760    }
5761
5762    #[test]
5763    fn insert_values_rowid_family_uses_last_target_assignment() {
5764        asupersync::test_utils::run_test(|| async {
5765            let conn = Connection::open(":memory:").await.unwrap();
5766            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, payload TEXT);")
5767                .await
5768                .unwrap();
5769
5770            let rows = conn
5771                .query("INSERT INTO t(rowid, id, payload) VALUES (7, 8, 'x') RETURNING rowid, id;")
5772                .await
5773                .unwrap();
5774            assert_eq!(
5775                row_values(&rows[0]),
5776                vec![SqliteValue::Integer(8), SqliteValue::Integer(8)]
5777            );
5778
5779            let rows = conn
5780                .query("INSERT INTO t(id, rowid, payload) VALUES (9, 10, 'y') RETURNING rowid, id;")
5781                .await
5782                .unwrap();
5783            assert_eq!(
5784                row_values(&rows[0]),
5785                vec![SqliteValue::Integer(10), SqliteValue::Integer(10)]
5786            );
5787        });
5788    }
5789
5790    #[test]
5791    fn ipk_insert_select_column_list_reorder() {
5792        asupersync::test_utils::run_test(|| async {
5793            let conn = Connection::open(":memory:").await.unwrap();
5794            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5795                .await
5796                .unwrap();
5797            let rows = conn
5798                .query("INSERT INTO t(name, id) SELECT 'Alice', 42 RETURNING id, name;")
5799                .await
5800                .unwrap();
5801            let vals = row_values(&rows[0]);
5802            assert_eq!(vals[0], SqliteValue::Integer(42));
5803            assert_eq!(vals[1], SqliteValue::Text("Alice".into()));
5804
5805            let sel = conn.query("SELECT id, name FROM t;").await.unwrap();
5806            let stored = row_values(&sel[0]);
5807            assert_eq!(stored[0], SqliteValue::Integer(42));
5808            assert_eq!(stored[1], SqliteValue::Text("Alice".into()));
5809        });
5810    }
5811
5812    #[test]
5813    fn ipk_insert_select_hidden_rowid_alias_honors_explicit_rowid() {
5814        asupersync::test_utils::run_test(|| async {
5815            let conn = Connection::open(":memory:").await.unwrap();
5816            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5817                .await
5818                .unwrap();
5819            let rows = conn
5820                .query("INSERT INTO t(rowid, name) SELECT 7, 'Bob' RETURNING id, rowid, name;")
5821                .await
5822                .unwrap();
5823            let vals = row_values(&rows[0]);
5824            assert_eq!(vals[0], SqliteValue::Integer(7));
5825            assert_eq!(vals[1], SqliteValue::Integer(7));
5826            assert_eq!(vals[2], SqliteValue::Text("Bob".into()));
5827
5828            let sel = conn.query("SELECT id, rowid, name FROM t;").await.unwrap();
5829            let stored = row_values(&sel[0]);
5830            assert_eq!(stored[0], SqliteValue::Integer(7));
5831            assert_eq!(stored[1], SqliteValue::Integer(7));
5832            assert_eq!(stored[2], SqliteValue::Text("Bob".into()));
5833        });
5834    }
5835
5836    #[test]
5837    fn upsert_do_update_resolves_hidden_rowid_aliases() {
5838        asupersync::test_utils::run_test(|| async {
5839            let conn = Connection::open(":memory:").await.unwrap();
5840            conn.execute("CREATE TABLE t (u TEXT UNIQUE, v INTEGER);")
5841                .await
5842                .unwrap();
5843            conn.execute("INSERT INTO t(rowid, u, v) VALUES (1, 'dup', 10);")
5844                .await
5845                .unwrap();
5846            conn.execute(
5847                "INSERT INTO t(rowid, u, v) VALUES (7, 'dup', 99)
5848             ON CONFLICT(u) DO UPDATE SET v = excluded.rowid + rowid;",
5849            )
5850            .await
5851            .unwrap();
5852
5853            let rows = conn
5854                .query("SELECT rowid, u, v FROM t ORDER BY rowid;")
5855                .await
5856                .unwrap();
5857            let vals = row_values(&rows[0]);
5858            assert_eq!(vals[0], SqliteValue::Integer(1));
5859            assert_eq!(vals[1], SqliteValue::Text("dup".into()));
5860            assert_eq!(vals[2], SqliteValue::Integer(8));
5861        });
5862    }
5863
5864    #[test]
5865    fn alter_table_preserves_primary_key_sql_in_sqlite_master() {
5866        asupersync::test_utils::run_test(|| async {
5867            let conn = Connection::open(":memory:").await.unwrap();
5868            conn.execute("CREATE TABLE t(id TEXT PRIMARY KEY, body TEXT);")
5869                .await
5870                .unwrap();
5871            conn.execute("ALTER TABLE t RENAME COLUMN body TO payload;")
5872                .await
5873                .unwrap();
5874
5875            let rows = conn
5876                .query("SELECT sql FROM sqlite_master WHERE name = 't';")
5877                .await
5878                .unwrap();
5879            let row = row_values(&rows[0]);
5880            let sql = match &row[0] {
5881                SqliteValue::Text(sql) => sql,
5882                other => panic!("expected SQL text, got {other:?}"),
5883            };
5884            assert!(sql.contains("PRIMARY KEY"), "{sql}");
5885            assert!(!sql.contains("UNIQUE"), "{sql}");
5886        });
5887    }
5888
5889    #[test]
5890    fn alter_table_preserves_typeless_without_rowid_sql_in_sqlite_master() {
5891        asupersync::test_utils::run_test(|| async {
5892            let conn = Connection::open(":memory:").await.unwrap();
5893            conn.execute("CREATE TABLE wr(id TEXT, body, PRIMARY KEY(id, body)) WITHOUT ROWID;")
5894                .await
5895                .unwrap();
5896            conn.execute("ALTER TABLE wr ADD COLUMN note TEXT;")
5897                .await
5898                .unwrap();
5899
5900            let rows = conn
5901                .query("SELECT sql FROM sqlite_master WHERE name = 'wr';")
5902                .await
5903                .unwrap();
5904            let row = row_values(&rows[0]);
5905            let sql = match &row[0] {
5906                SqliteValue::Text(sql) => sql,
5907                other => panic!("expected SQL text, got {other:?}"),
5908            };
5909            assert!(sql.contains("PRIMARY KEY"), "{sql}");
5910            assert!(sql.contains("WITHOUT ROWID"), "{sql}");
5911            assert!(sql.contains("body"), "{sql}");
5912            assert!(!sql.contains("body BLOB"), "{sql}");
5913        });
5914    }
5915
5916    #[test]
5917    fn alter_table_drop_primary_key_column_is_rejected() {
5918        asupersync::test_utils::run_test(|| async {
5919            let conn = Connection::open(":memory:").await.unwrap();
5920            conn.execute("CREATE TABLE t(id TEXT PRIMARY KEY, body TEXT);")
5921                .await
5922                .unwrap();
5923            let err = conn
5924                .execute("ALTER TABLE t DROP COLUMN id;")
5925                .await
5926                .expect_err("dropping a primary key column must fail");
5927            let msg = err.to_string();
5928            assert!(
5929                msg.contains("cannot drop PRIMARY KEY column") && msg.contains("id"),
5930                "{msg}"
5931            );
5932        });
5933    }
5934
5935    /// Explicit column list omitting IPK should auto-generate rowid.
5936    #[test]
5937    fn ipk_column_list_omit_ipk() {
5938        asupersync::test_utils::run_test(|| async {
5939            let conn = Connection::open(":memory:").await.unwrap();
5940            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5941                .await
5942                .unwrap();
5943            let rows = conn
5944                .query("INSERT INTO t(name) VALUES ('Bob') RETURNING id, name;")
5945                .await
5946                .unwrap();
5947            let vals = row_values(&rows[0]);
5948            // id should be auto-generated (positive integer).
5949            assert!(
5950                matches!(vals[0], SqliteValue::Integer(n) if n > 0),
5951                "omitted IPK should auto-generate, got {:?}",
5952                vals[0]
5953            );
5954            assert_eq!(vals[1], SqliteValue::Text("Bob".into()));
5955        });
5956    }
5957
5958    /// DELETE then reinsert with same IPK should work.
5959    #[test]
5960    fn ipk_delete_reinsert_same_id() {
5961        asupersync::test_utils::run_test(|| async {
5962            let conn = Connection::open(":memory:").await.unwrap();
5963            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
5964                .await
5965                .unwrap();
5966            conn.execute("INSERT INTO t VALUES (1, 'original');")
5967                .await
5968                .unwrap();
5969            conn.execute("DELETE FROM t WHERE id = 1;").await.unwrap();
5970            conn.execute("INSERT INTO t VALUES (1, 'reinserted');")
5971                .await
5972                .unwrap();
5973            let rows = conn.query("SELECT val FROM t WHERE id = 1;").await.unwrap();
5974            assert_eq!(rows.len(), 1);
5975            assert_eq!(
5976                row_values(&rows[0])[0],
5977                SqliteValue::Text("reinserted".into())
5978            );
5979        });
5980    }
5981
5982    // ══════════════════════════════════════════════════════════════════════════
5983    // Index Maintenance Tests (Phase 5I - bd-1nmg)
5984    // ══════════════════════════════════════════════════════════════════════════
5985
5986    // ── Basic Operations ──────────────────────────────────────────────────────
5987
5988    /// INSERT should create index entries for single-column indexes.
5989    #[test]
5990    fn index_insert_single_column() {
5991        asupersync::test_utils::run_test(|| async {
5992            let conn = Connection::open(":memory:").await.unwrap();
5993            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
5994                .await
5995                .unwrap();
5996            conn.execute("CREATE INDEX idx_name ON t(name);")
5997                .await
5998                .unwrap();
5999
6000            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6001                .await
6002                .unwrap();
6003            conn.execute("INSERT INTO t VALUES (2, 'bob');")
6004                .await
6005                .unwrap();
6006            conn.execute("INSERT INTO t VALUES (3, 'charlie');")
6007                .await
6008                .unwrap();
6009
6010            // Verify index is used for lookups (entries exist).
6011            let rows = conn
6012                .query("SELECT id FROM t WHERE name = 'bob';")
6013                .await
6014                .unwrap();
6015            assert_eq!(rows.len(), 1);
6016            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
6017
6018            let rows = conn
6019                .query("SELECT id FROM t WHERE name = 'alice';")
6020                .await
6021                .unwrap();
6022            assert_eq!(rows.len(), 1);
6023            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
6024        });
6025    }
6026
6027    /// INSERT should create index entries for multi-column indexes.
6028    #[test]
6029    fn index_insert_multi_column() {
6030        asupersync::test_utils::run_test(|| async {
6031            let conn = Connection::open(":memory:").await.unwrap();
6032            conn.execute("CREATE TABLE t (a INT, b INT, c TEXT);")
6033                .await
6034                .unwrap();
6035            conn.execute("CREATE INDEX idx_ab ON t(a, b);")
6036                .await
6037                .unwrap();
6038
6039            conn.execute("INSERT INTO t VALUES (1, 10, 'x');")
6040                .await
6041                .unwrap();
6042            conn.execute("INSERT INTO t VALUES (1, 20, 'y');")
6043                .await
6044                .unwrap();
6045            conn.execute("INSERT INTO t VALUES (2, 10, 'z');")
6046                .await
6047                .unwrap();
6048
6049            // Query using both columns of the index.
6050            let rows = conn
6051                .query("SELECT c FROM t WHERE a = 1 AND b = 20;")
6052                .await
6053                .unwrap();
6054            assert_eq!(rows.len(), 1);
6055            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("y".into()));
6056
6057            // Query using only first column prefix.
6058            let rows = conn.query("SELECT c FROM t WHERE a = 1;").await.unwrap();
6059            assert_eq!(rows.len(), 2); // Should find both a=1 rows.
6060        });
6061    }
6062
6063    /// DELETE should remove index entries.
6064    #[test]
6065    fn index_delete_removes_entry() {
6066        asupersync::test_utils::run_test(|| async {
6067            let conn = Connection::open(":memory:").await.unwrap();
6068            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6069                .await
6070                .unwrap();
6071            conn.execute("CREATE INDEX idx_name ON t(name);")
6072                .await
6073                .unwrap();
6074
6075            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6076                .await
6077                .unwrap();
6078            conn.execute("INSERT INTO t VALUES (2, 'bob');")
6079                .await
6080                .unwrap();
6081
6082            // Verify both are findable.
6083            let rows = conn
6084                .query("SELECT id FROM t WHERE name = 'alice';")
6085                .await
6086                .unwrap();
6087            assert_eq!(rows.len(), 1);
6088
6089            // Delete alice.
6090            conn.execute("DELETE FROM t WHERE id = 1;").await.unwrap();
6091
6092            // Alice should no longer be findable via index.
6093            let rows = conn
6094                .query("SELECT id FROM t WHERE name = 'alice';")
6095                .await
6096                .unwrap();
6097            assert_eq!(rows.len(), 0);
6098
6099            // Bob should still be findable.
6100            let rows = conn
6101                .query("SELECT id FROM t WHERE name = 'bob';")
6102                .await
6103                .unwrap();
6104            assert_eq!(rows.len(), 1);
6105        });
6106    }
6107
6108    /// UPDATE should maintain index entries when indexed column changes.
6109    #[test]
6110    fn index_update_indexed_column() {
6111        asupersync::test_utils::run_test(|| async {
6112            let conn = Connection::open(":memory:").await.unwrap();
6113            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6114                .await
6115                .unwrap();
6116            conn.execute("CREATE INDEX idx_name ON t(name);")
6117                .await
6118                .unwrap();
6119
6120            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6121                .await
6122                .unwrap();
6123
6124            // Verify initial state.
6125            let rows = conn
6126                .query("SELECT id FROM t WHERE name = 'alice';")
6127                .await
6128                .unwrap();
6129            assert_eq!(rows.len(), 1);
6130
6131            // Update name.
6132            conn.execute("UPDATE t SET name = 'alicia' WHERE id = 1;")
6133                .await
6134                .unwrap();
6135
6136            // Old name should not be findable.
6137            let rows = conn
6138                .query("SELECT id FROM t WHERE name = 'alice';")
6139                .await
6140                .unwrap();
6141            assert_eq!(rows.len(), 0);
6142
6143            // New name should be findable.
6144            let rows = conn
6145                .query("SELECT id FROM t WHERE name = 'alicia';")
6146                .await
6147                .unwrap();
6148            assert_eq!(rows.len(), 1);
6149            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
6150        });
6151    }
6152
6153    /// UPDATE should preserve index entries when non-indexed column changes.
6154    #[test]
6155    fn index_update_non_indexed_column() {
6156        asupersync::test_utils::run_test(|| async {
6157            let conn = Connection::open(":memory:").await.unwrap();
6158            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, age INT);")
6159                .await
6160                .unwrap();
6161            conn.execute("CREATE INDEX idx_name ON t(name);")
6162                .await
6163                .unwrap();
6164
6165            conn.execute("INSERT INTO t VALUES (1, 'alice', 30);")
6166                .await
6167                .unwrap();
6168
6169            // Update non-indexed column.
6170            conn.execute("UPDATE t SET age = 31 WHERE id = 1;")
6171                .await
6172                .unwrap();
6173
6174            // Index should still work correctly.
6175            let rows = conn
6176                .query("SELECT age FROM t WHERE name = 'alice';")
6177                .await
6178                .unwrap();
6179            assert_eq!(rows.len(), 1);
6180            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(31));
6181        });
6182    }
6183
6184    // ── Multiple Indexes ──────────────────────────────────────────────────────
6185
6186    /// Table with multiple indexes should maintain all of them.
6187    #[test]
6188    fn index_multiple_indexes_on_table() {
6189        asupersync::test_utils::run_test(|| async {
6190            let conn = Connection::open(":memory:").await.unwrap();
6191            conn.execute("CREATE TABLE t (a INT, b INT, c INT, d INT);")
6192                .await
6193                .unwrap();
6194            conn.execute("CREATE INDEX idx_a ON t(a);").await.unwrap();
6195            conn.execute("CREATE INDEX idx_b ON t(b);").await.unwrap();
6196            conn.execute("CREATE INDEX idx_ab ON t(a, b);")
6197                .await
6198                .unwrap();
6199            conn.execute("CREATE INDEX idx_cd ON t(c, d);")
6200                .await
6201                .unwrap();
6202
6203            conn.execute("INSERT INTO t VALUES (1, 2, 3, 4);")
6204                .await
6205                .unwrap();
6206            conn.execute("INSERT INTO t VALUES (5, 6, 7, 8);")
6207                .await
6208                .unwrap();
6209
6210            // All indexes should be searchable.
6211            let rows = conn.query("SELECT b FROM t WHERE a = 1;").await.unwrap();
6212            assert_eq!(rows.len(), 1);
6213            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
6214
6215            let rows = conn.query("SELECT a FROM t WHERE b = 6;").await.unwrap();
6216            assert_eq!(rows.len(), 1);
6217            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(5));
6218
6219            let rows = conn
6220                .query("SELECT c FROM t WHERE a = 1 AND b = 2;")
6221                .await
6222                .unwrap();
6223            assert_eq!(rows.len(), 1);
6224            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
6225
6226            let rows = conn
6227                .query("SELECT a FROM t WHERE c = 7 AND d = 8;")
6228                .await
6229                .unwrap();
6230            assert_eq!(rows.len(), 1);
6231            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(5));
6232        });
6233    }
6234
6235    /// DELETE should remove entries from all indexes.
6236    #[test]
6237    fn index_delete_removes_from_all_indexes() {
6238        asupersync::test_utils::run_test(|| async {
6239            let conn = Connection::open(":memory:").await.unwrap();
6240            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INT, b INT);")
6241                .await
6242                .unwrap();
6243            conn.execute("CREATE INDEX idx_a ON t(a);").await.unwrap();
6244            conn.execute("CREATE INDEX idx_b ON t(b);").await.unwrap();
6245
6246            conn.execute("INSERT INTO t VALUES (1, 10, 100);")
6247                .await
6248                .unwrap();
6249            conn.execute("INSERT INTO t VALUES (2, 20, 200);")
6250                .await
6251                .unwrap();
6252
6253            // Delete row 1.
6254            conn.execute("DELETE FROM t WHERE id = 1;").await.unwrap();
6255
6256            // Neither index should find the deleted row.
6257            let rows = conn.query("SELECT id FROM t WHERE a = 10;").await.unwrap();
6258            assert_eq!(rows.len(), 0);
6259
6260            let rows = conn.query("SELECT id FROM t WHERE b = 100;").await.unwrap();
6261            assert_eq!(rows.len(), 0);
6262
6263            // Row 2 should still be findable via both indexes.
6264            let rows = conn.query("SELECT id FROM t WHERE a = 20;").await.unwrap();
6265            assert_eq!(rows.len(), 1);
6266
6267            let rows = conn.query("SELECT id FROM t WHERE b = 200;").await.unwrap();
6268            assert_eq!(rows.len(), 1);
6269        });
6270    }
6271
6272    /// UPDATE should maintain all affected indexes.
6273    #[test]
6274    fn index_update_maintains_all_indexes() {
6275        asupersync::test_utils::run_test(|| async {
6276            let conn = Connection::open(":memory:").await.unwrap();
6277            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INT, b INT);")
6278                .await
6279                .unwrap();
6280            conn.execute("CREATE INDEX idx_a ON t(a);").await.unwrap();
6281            conn.execute("CREATE INDEX idx_b ON t(b);").await.unwrap();
6282
6283            conn.execute("INSERT INTO t VALUES (1, 10, 100);")
6284                .await
6285                .unwrap();
6286
6287            // Update both indexed columns.
6288            conn.execute("UPDATE t SET a = 11, b = 101 WHERE id = 1;")
6289                .await
6290                .unwrap();
6291
6292            // Old values should not be findable.
6293            let rows = conn.query("SELECT id FROM t WHERE a = 10;").await.unwrap();
6294            assert_eq!(rows.len(), 0);
6295            let rows = conn.query("SELECT id FROM t WHERE b = 100;").await.unwrap();
6296            assert_eq!(rows.len(), 0);
6297
6298            // New values should be findable.
6299            let rows = conn.query("SELECT id FROM t WHERE a = 11;").await.unwrap();
6300            assert_eq!(rows.len(), 1);
6301            let rows = conn.query("SELECT id FROM t WHERE b = 101;").await.unwrap();
6302            assert_eq!(rows.len(), 1);
6303        });
6304    }
6305
6306    // ── NULL Handling ─────────────────────────────────────────────────────────
6307    // Fixed in bd-36eh.1: NULL value handling in index B-trees.
6308    // The fix sets NULLEQ flag (0x80) in WHERE Ne comparisons so NULL != value
6309    // correctly skips rows with NULL values.
6310
6311    /// Index should handle NULL values correctly.
6312    #[test]
6313    fn index_with_null_values() {
6314        asupersync::test_utils::run_test(|| async {
6315            let conn = Connection::open(":memory:").await.unwrap();
6316            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6317                .await
6318                .unwrap();
6319            conn.execute("CREATE INDEX idx_name ON t(name);")
6320                .await
6321                .unwrap();
6322
6323            conn.execute("INSERT INTO t VALUES (1, NULL);")
6324                .await
6325                .unwrap();
6326            conn.execute("INSERT INTO t VALUES (2, 'alice');")
6327                .await
6328                .unwrap();
6329            conn.execute("INSERT INTO t VALUES (3, NULL);")
6330                .await
6331                .unwrap();
6332
6333            // Query for NULL via IS NULL.
6334            let rows = conn
6335                .query("SELECT id FROM t WHERE name IS NULL;")
6336                .await
6337                .unwrap();
6338            assert_eq!(rows.len(), 2);
6339
6340            // Query for non-NULL.
6341            let rows = conn
6342                .query("SELECT id FROM t WHERE name = 'alice';")
6343                .await
6344                .unwrap();
6345            assert_eq!(rows.len(), 1);
6346            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
6347        });
6348    }
6349
6350    /// UPDATE NULL to non-NULL should update index correctly.
6351    #[test]
6352    fn index_update_null_to_non_null() {
6353        asupersync::test_utils::run_test(|| async {
6354            let conn = Connection::open(":memory:").await.unwrap();
6355            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6356                .await
6357                .unwrap();
6358            conn.execute("CREATE INDEX idx_name ON t(name);")
6359                .await
6360                .unwrap();
6361
6362            conn.execute("INSERT INTO t VALUES (1, NULL);")
6363                .await
6364                .unwrap();
6365            conn.execute("INSERT INTO t VALUES (2, NULL);")
6366                .await
6367                .unwrap();
6368
6369            // Initially 2 NULLs.
6370            let rows = conn
6371                .query("SELECT id FROM t WHERE name IS NULL;")
6372                .await
6373                .unwrap();
6374            assert_eq!(rows.len(), 2);
6375
6376            // Update one NULL to non-NULL.
6377            conn.execute("UPDATE t SET name = 'bob' WHERE id = 1;")
6378                .await
6379                .unwrap();
6380
6381            // Now only 1 NULL.
6382            let rows = conn
6383                .query("SELECT id FROM t WHERE name IS NULL;")
6384                .await
6385                .unwrap();
6386            assert_eq!(rows.len(), 1);
6387            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
6388
6389            // And bob is findable.
6390            let rows = conn
6391                .query("SELECT id FROM t WHERE name = 'bob';")
6392                .await
6393                .unwrap();
6394            assert_eq!(rows.len(), 1);
6395            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
6396        });
6397    }
6398
6399    /// UPDATE non-NULL to NULL should update index correctly.
6400    #[test]
6401    fn index_update_non_null_to_null() {
6402        asupersync::test_utils::run_test(|| async {
6403            let conn = Connection::open(":memory:").await.unwrap();
6404            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6405                .await
6406                .unwrap();
6407            conn.execute("CREATE INDEX idx_name ON t(name);")
6408                .await
6409                .unwrap();
6410
6411            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6412                .await
6413                .unwrap();
6414
6415            // alice is findable.
6416            let rows = conn
6417                .query("SELECT id FROM t WHERE name = 'alice';")
6418                .await
6419                .unwrap();
6420            assert_eq!(rows.len(), 1);
6421
6422            // Update to NULL.
6423            conn.execute("UPDATE t SET name = NULL WHERE id = 1;")
6424                .await
6425                .unwrap();
6426
6427            // alice is no longer findable.
6428            let rows = conn
6429                .query("SELECT id FROM t WHERE name = 'alice';")
6430                .await
6431                .unwrap();
6432            assert_eq!(rows.len(), 0);
6433
6434            // NULL is findable.
6435            let rows = conn
6436                .query("SELECT id FROM t WHERE name IS NULL;")
6437                .await
6438                .unwrap();
6439            assert_eq!(rows.len(), 1);
6440        });
6441    }
6442
6443    // ── Bulk Operations ───────────────────────────────────────────────────────
6444
6445    /// Bulk INSERT should maintain index for all rows.
6446    #[test]
6447    fn index_bulk_insert() {
6448        asupersync::test_utils::run_test(|| async {
6449            let conn = Connection::open(":memory:").await.unwrap();
6450            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, value INT);")
6451                .await
6452                .unwrap();
6453            conn.execute("CREATE INDEX idx_value ON t(value);")
6454                .await
6455                .unwrap();
6456
6457            // Insert 100 rows.
6458            for i in 0..100 {
6459                conn.execute(&format!("INSERT INTO t VALUES ({}, {});", i, i * 2))
6460                    .await
6461                    .unwrap();
6462            }
6463
6464            // Verify index works for various values.
6465            for i in [0, 25, 50, 75, 99] {
6466                let rows = conn
6467                    .query(&format!("SELECT id FROM t WHERE value = {};", i * 2))
6468                    .await
6469                    .unwrap();
6470                assert_eq!(rows.len(), 1, "Should find row with value={}", i * 2);
6471                assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(i));
6472            }
6473        });
6474    }
6475
6476    /// Bulk DELETE should remove all index entries.
6477    #[test]
6478    fn index_bulk_delete() {
6479        asupersync::test_utils::run_test(|| async {
6480            let conn = Connection::open(":memory:").await.unwrap();
6481            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, value INT);")
6482                .await
6483                .unwrap();
6484            conn.execute("CREATE INDEX idx_value ON t(value);")
6485                .await
6486                .unwrap();
6487
6488            // Insert 50 rows.
6489            for i in 0..50 {
6490                conn.execute(&format!("INSERT INTO t VALUES ({}, {});", i, i))
6491                    .await
6492                    .unwrap();
6493            }
6494
6495            // Delete half (even values).
6496            for i in (0..50).step_by(2) {
6497                conn.execute(&format!("DELETE FROM t WHERE id = {};", i))
6498                    .await
6499                    .unwrap();
6500            }
6501
6502            // Even values should not be findable.
6503            for i in (0..50).step_by(2) {
6504                let rows = conn
6505                    .query(&format!("SELECT id FROM t WHERE value = {};", i))
6506                    .await
6507                    .unwrap();
6508                assert_eq!(
6509                    rows.len(),
6510                    0,
6511                    "Deleted row with value={} should not exist",
6512                    i
6513                );
6514            }
6515
6516            // Odd values should still be findable.
6517            for i in (1..50).step_by(2) {
6518                let rows = conn
6519                    .query(&format!("SELECT id FROM t WHERE value = {};", i))
6520                    .await
6521                    .unwrap();
6522                assert_eq!(rows.len(), 1, "Row with value={} should exist", i);
6523            }
6524        });
6525    }
6526
6527    /// Bulk UPDATE should maintain all index entries.
6528    #[test]
6529    fn index_bulk_update() {
6530        asupersync::test_utils::run_test(|| async {
6531            let conn = Connection::open(":memory:").await.unwrap();
6532            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, value INT);")
6533                .await
6534                .unwrap();
6535            conn.execute("CREATE INDEX idx_value ON t(value);")
6536                .await
6537                .unwrap();
6538
6539            // Insert 50 rows.
6540            for i in 0..50 {
6541                conn.execute(&format!("INSERT INTO t VALUES ({}, {});", i, i))
6542                    .await
6543                    .unwrap();
6544            }
6545
6546            // Update all values: value = value + 1000.
6547            conn.execute("UPDATE t SET value = value + 1000;")
6548                .await
6549                .unwrap();
6550
6551            // Old values should not be findable.
6552            for i in 0..50 {
6553                let rows = conn
6554                    .query(&format!("SELECT id FROM t WHERE value = {};", i))
6555                    .await
6556                    .unwrap();
6557                assert_eq!(rows.len(), 0);
6558            }
6559
6560            // New values should be findable.
6561            for i in 0..50 {
6562                let rows = conn
6563                    .query(&format!("SELECT id FROM t WHERE value = {};", i + 1000))
6564                    .await
6565                    .unwrap();
6566                assert_eq!(rows.len(), 1);
6567                assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(i));
6568            }
6569        });
6570    }
6571
6572    // ── Transaction Rollback ──────────────────────────────────────────────────
6573
6574    /// Index entries should be rolled back with transaction.
6575    #[test]
6576    fn index_rollback_insert() {
6577        asupersync::test_utils::run_test(|| async {
6578            let conn = Connection::open(":memory:").await.unwrap();
6579            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6580                .await
6581                .unwrap();
6582            conn.execute("CREATE INDEX idx_name ON t(name);")
6583                .await
6584                .unwrap();
6585            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6586                .await
6587                .unwrap();
6588
6589            conn.execute("BEGIN;").await.unwrap();
6590            conn.execute("INSERT INTO t VALUES (2, 'bob');")
6591                .await
6592                .unwrap();
6593
6594            // Bob should be visible in transaction.
6595            let rows = conn
6596                .query("SELECT id FROM t WHERE name = 'bob';")
6597                .await
6598                .unwrap();
6599            assert_eq!(rows.len(), 1);
6600
6601            conn.execute("ROLLBACK;").await.unwrap();
6602
6603            // Bob should NOT be visible after rollback.
6604            let rows = conn
6605                .query("SELECT id FROM t WHERE name = 'bob';")
6606                .await
6607                .unwrap();
6608            assert_eq!(rows.len(), 0);
6609
6610            // Alice should still be there.
6611            let rows = conn
6612                .query("SELECT id FROM t WHERE name = 'alice';")
6613                .await
6614                .unwrap();
6615            assert_eq!(rows.len(), 1);
6616        });
6617    }
6618
6619    /// Index entries should be rolled back on DELETE rollback.
6620    #[test]
6621    fn index_rollback_delete() {
6622        asupersync::test_utils::run_test(|| async {
6623            let conn = Connection::open(":memory:").await.unwrap();
6624            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6625                .await
6626                .unwrap();
6627            conn.execute("CREATE INDEX idx_name ON t(name);")
6628                .await
6629                .unwrap();
6630            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6631                .await
6632                .unwrap();
6633
6634            conn.execute("BEGIN;").await.unwrap();
6635            conn.execute("DELETE FROM t WHERE id = 1;").await.unwrap();
6636
6637            // Alice should not be visible in transaction.
6638            let rows = conn
6639                .query("SELECT id FROM t WHERE name = 'alice';")
6640                .await
6641                .unwrap();
6642            assert_eq!(rows.len(), 0);
6643
6644            conn.execute("ROLLBACK;").await.unwrap();
6645
6646            // Alice should be restored after rollback.
6647            let rows = conn
6648                .query("SELECT id FROM t WHERE name = 'alice';")
6649                .await
6650                .unwrap();
6651            assert_eq!(rows.len(), 1);
6652        });
6653    }
6654
6655    /// Index entries should be rolled back on UPDATE rollback.
6656    #[test]
6657    fn index_rollback_update() {
6658        asupersync::test_utils::run_test(|| async {
6659            let conn = Connection::open(":memory:").await.unwrap();
6660            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6661                .await
6662                .unwrap();
6663            conn.execute("CREATE INDEX idx_name ON t(name);")
6664                .await
6665                .unwrap();
6666            conn.execute("INSERT INTO t VALUES (1, 'alice');")
6667                .await
6668                .unwrap();
6669
6670            conn.execute("BEGIN;").await.unwrap();
6671            conn.execute("UPDATE t SET name = 'bob' WHERE id = 1;")
6672                .await
6673                .unwrap();
6674
6675            // Bob should be visible, alice not.
6676            let rows = conn
6677                .query("SELECT id FROM t WHERE name = 'bob';")
6678                .await
6679                .unwrap();
6680            assert_eq!(rows.len(), 1);
6681            let rows = conn
6682                .query("SELECT id FROM t WHERE name = 'alice';")
6683                .await
6684                .unwrap();
6685            assert_eq!(rows.len(), 0);
6686
6687            conn.execute("ROLLBACK;").await.unwrap();
6688
6689            // Alice should be restored, bob gone.
6690            let rows = conn
6691                .query("SELECT id FROM t WHERE name = 'alice';")
6692                .await
6693                .unwrap();
6694            assert_eq!(rows.len(), 1);
6695            let rows = conn
6696                .query("SELECT id FROM t WHERE name = 'bob';")
6697                .await
6698                .unwrap();
6699            assert_eq!(rows.len(), 0);
6700        });
6701    }
6702
6703    // ── No REINDEX Required ───────────────────────────────────────────────────
6704
6705    /// All operations should work WITHOUT manual REINDEX.
6706    #[test]
6707    fn index_no_reindex_needed() {
6708        asupersync::test_utils::run_test(|| async {
6709            let conn = Connection::open(":memory:").await.unwrap();
6710            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6711                .await
6712                .unwrap();
6713            conn.execute("CREATE INDEX idx_name ON t(name);")
6714                .await
6715                .unwrap();
6716
6717            // Perform many operations.
6718            for i in 0..100 {
6719                conn.execute(&format!("INSERT INTO t VALUES ({}, 'name{}');", i, i))
6720                    .await
6721                    .unwrap();
6722            }
6723            for i in 0..50 {
6724                conn.execute(&format!("DELETE FROM t WHERE id = {};", i))
6725                    .await
6726                    .unwrap();
6727            }
6728            for i in 50..100 {
6729                conn.execute(&format!(
6730                    "UPDATE t SET name = 'updated{}' WHERE id = {};",
6731                    i, i
6732                ))
6733                .await
6734                .unwrap();
6735            }
6736
6737            // All remaining rows should be findable via index WITHOUT REINDEX.
6738            for i in 50..100 {
6739                let rows = conn
6740                    .query(&format!("SELECT id FROM t WHERE name = 'updated{}';", i))
6741                    .await
6742                    .unwrap();
6743                assert_eq!(
6744                    rows.len(),
6745                    1,
6746                    "Row with updated name for id={} should be findable",
6747                    i
6748                );
6749                assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(i));
6750            }
6751
6752            // Deleted rows should not be findable.
6753            for i in 0..50 {
6754                let rows = conn
6755                    .query(&format!("SELECT id FROM t WHERE name = 'name{}';", i))
6756                    .await
6757                    .unwrap();
6758                assert_eq!(
6759                    rows.len(),
6760                    0,
6761                    "Deleted row with name{} should not be findable",
6762                    i
6763                );
6764            }
6765
6766            // Verify total count.
6767            let rows = conn.query("SELECT COUNT(*) FROM t;").await.unwrap();
6768            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(50));
6769        });
6770    }
6771
6772    // ── INSERT with index on IPK column ───────────────────────────────────────
6773
6774    /// Index on INTEGER PRIMARY KEY column should work correctly.
6775    #[test]
6776    fn index_on_ipk_column() {
6777        asupersync::test_utils::run_test(|| async {
6778            let conn = Connection::open(":memory:").await.unwrap();
6779            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);")
6780                .await
6781                .unwrap();
6782            conn.execute("CREATE INDEX idx_id ON t(id);").await.unwrap();
6783
6784            conn.execute("INSERT INTO t VALUES (100, 'alice');")
6785                .await
6786                .unwrap();
6787            conn.execute("INSERT INTO t VALUES (200, 'bob');")
6788                .await
6789                .unwrap();
6790
6791            // Index on IPK should work.
6792            let rows = conn
6793                .query("SELECT name FROM t WHERE id = 100;")
6794                .await
6795                .unwrap();
6796            assert_eq!(rows.len(), 1);
6797            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("alice".into()));
6798        });
6799    }
6800
6801    // ── Mixed operations sequence ─────────────────────────────────────────────
6802
6803    /// Complex sequence of operations should maintain index consistency.
6804    #[test]
6805    fn index_mixed_operations_sequence() {
6806        asupersync::test_utils::run_test(|| async {
6807            let conn = Connection::open(":memory:").await.unwrap();
6808            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, a INT, b TEXT);")
6809                .await
6810                .unwrap();
6811            conn.execute("CREATE INDEX idx_a ON t(a);").await.unwrap();
6812            conn.execute("CREATE INDEX idx_b ON t(b);").await.unwrap();
6813
6814            // Insert
6815            conn.execute("INSERT INTO t VALUES (1, 10, 'x');")
6816                .await
6817                .unwrap();
6818            conn.execute("INSERT INTO t VALUES (2, 20, 'y');")
6819                .await
6820                .unwrap();
6821            conn.execute("INSERT INTO t VALUES (3, 30, 'z');")
6822                .await
6823                .unwrap();
6824
6825            // Update
6826            conn.execute("UPDATE t SET a = 15 WHERE id = 1;")
6827                .await
6828                .unwrap();
6829
6830            // Delete
6831            conn.execute("DELETE FROM t WHERE id = 2;").await.unwrap();
6832
6833            // Insert more
6834            conn.execute("INSERT INTO t VALUES (4, 40, 'w');")
6835                .await
6836                .unwrap();
6837
6838            // Verify state via indexes.
6839            let rows = conn.query("SELECT id FROM t WHERE a = 10;").await.unwrap();
6840            assert_eq!(rows.len(), 0); // Was updated to 15
6841
6842            let rows = conn.query("SELECT id FROM t WHERE a = 15;").await.unwrap();
6843            assert_eq!(rows.len(), 1);
6844            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
6845
6846            let rows = conn.query("SELECT id FROM t WHERE b = 'y';").await.unwrap();
6847            assert_eq!(rows.len(), 0); // Was deleted
6848
6849            let rows = conn.query("SELECT id FROM t WHERE a = 40;").await.unwrap();
6850            assert_eq!(rows.len(), 1);
6851            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(4));
6852        });
6853    }
6854
6855    // ══════════════════════════════════════════════════════════════════════════
6856    // Concurrent Writer Stress Tests (Phase 5E.6 - bd-1299)
6857    // ══════════════════════════════════════════════════════════════════════════
6858
6859    /// Multi-threaded concurrent writer stress test.
6860    ///
6861    /// Eight writer threads perform 20 committed transfer operations each.
6862    /// The parent process enforces a hard deadline; the child process uses
6863    /// bounded, typed retries and fail-closed startup coordination.
6864    #[test]
6865    fn concurrent_writers_stress_conservation() {
6866        if supervise_concurrent_writer_stress() {
6867            return;
6868        }
6869
6870        use rand::prelude::*;
6871        use std::thread;
6872
6873        const NUM_ACCOUNTS: i64 = 100;
6874        const INITIAL_BALANCE: i64 = 1_000;
6875        const EXPECTED_TOTAL: i64 = NUM_ACCOUNTS * INITIAL_BALANCE;
6876        const NUM_WRITERS: usize = 8;
6877        const OPS_PER_WRITER: u64 = 20;
6878
6879        let dir = tempfile::tempdir().expect("create concurrent-stress temp dir");
6880        let db_path = dir.path().join("stress.db");
6881        let db_path_string = db_path.to_string_lossy().into_owned();
6882
6883        asupersync::test_utils::run_test(|| async {
6884            let conn = Connection::open(&db_path_string)
6885                .await
6886                .expect("open concurrent-stress database for setup");
6887            assert!(
6888                conn.is_concurrent_mode_default(),
6889                "setup connection must preserve the concurrent-writer default"
6890            );
6891            conn.execute(
6892                "CREATE TABLE accounts (
6893                    id INTEGER PRIMARY KEY,
6894                    balance INTEGER,
6895                    payload TEXT NOT NULL
6896                );",
6897            )
6898            .await
6899            .expect("create accounts table");
6900            let payload = "x".repeat(512);
6901            for account_id in 0..NUM_ACCOUNTS {
6902                conn.execute_with_params(
6903                    "INSERT INTO accounts VALUES (?1, ?2, ?3);",
6904                    &[
6905                        SqliteValue::Integer(account_id),
6906                        SqliteValue::Integer(INITIAL_BALANCE),
6907                        SqliteValue::Text(payload.clone().into()),
6908                    ],
6909                )
6910                .await
6911                .expect("insert initial account");
6912            }
6913            let rows = conn
6914                .query(
6915                    "SELECT COUNT(*), SUM(balance), MIN(length(payload)), MAX(length(payload))
6916                     FROM accounts;",
6917                )
6918                .await
6919                .expect("query initial account invariants");
6920            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(NUM_ACCOUNTS));
6921            assert_eq!(
6922                row_values(&rows[0])[1],
6923                SqliteValue::Integer(EXPECTED_TOTAL)
6924            );
6925            assert_eq!(row_values(&rows[0])[2], SqliteValue::Integer(512));
6926            assert_eq!(row_values(&rows[0])[3], SqliteValue::Integer(512));
6927            let page_count = conn
6928                .query_row("PRAGMA page_count;")
6929                .await
6930                .expect("query multi-page setup size");
6931            assert!(
6932                matches!(row_values(&page_count).as_slice(), [SqliteValue::Integer(count)] if *count > 2),
6933                "fixed-width account payloads must span multiple database pages: {page_count:?}"
6934            );
6935            conn.close()
6936                .await
6937                .expect("close concurrent-stress setup connection");
6938        });
6939
6940        let (startup_tx, startup_rx) = mpsc::channel::<ConcurrentStressStartup>();
6941        let mut start_senders = Vec::with_capacity(NUM_WRITERS);
6942        let mut handles = Vec::with_capacity(NUM_WRITERS);
6943
6944        for worker_id in 0..NUM_WRITERS {
6945            let path = db_path_string.clone();
6946            let startup_tx = startup_tx.clone();
6947            let (start_tx, start_rx) = mpsc::sync_channel(1);
6948            start_senders.push(start_tx);
6949            handles.push(thread::spawn(move || {
6950                let mut outcome = ConcurrentStressWorkerOutcome::pending(worker_id);
6951                let started_at = Instant::now();
6952                asupersync::test_utils::run_test(|| async {
6953                    let mut open_attempts = 0_u64;
6954                    let mut last_open_error = None;
6955                    let mut conn = loop {
6956                        if open_attempts >= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT
6957                            || outcome.attempts >= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER
6958                            || started_at.elapsed() >= CONCURRENT_STRESS_STARTUP_TIMEOUT
6959                        {
6960                            let message = format!(
6961                                "connection open exhausted its bounded retry budget after {open_attempts} attempts; last transient error: {}",
6962                                last_open_error.as_deref().unwrap_or("none")
6963                            );
6964                            let _ = startup_tx.send(ConcurrentStressStartup::Failed {
6965                                worker_id,
6966                                error: message.clone(),
6967                            });
6968                            outcome.failure = Some(message);
6969                            return;
6970                        }
6971
6972                        open_attempts += 1;
6973                        outcome.attempts += 1;
6974                        match Connection::open(&path).await {
6975                            Ok(conn) => break conn,
6976                            Err(error) if outcome.retries.record(&error) => {
6977                                last_open_error = Some(format!("connection open: {error:?}"));
6978                                concurrent_stress_backoff(
6979                                    open_attempts,
6980                                    u64::try_from(worker_id).expect("worker id fits u64"),
6981                                );
6982                            }
6983                            Err(error) => {
6984                                let message = format!("connection open failed: {error:?}");
6985                                let _ = startup_tx.send(ConcurrentStressStartup::Failed {
6986                                    worker_id,
6987                                    error: message.clone(),
6988                                });
6989                                outcome.failure = Some(message);
6990                                return;
6991                            }
6992                        }
6993                    };
6994                    'worker: {
6995                    outcome.concurrent_mode_default = conn.is_concurrent_mode_default();
6996                    if !outcome.concurrent_mode_default {
6997                        let message = "concurrent mode is not enabled by default".to_owned();
6998                        let _ = startup_tx.send(ConcurrentStressStartup::Failed {
6999                            worker_id,
7000                            error: message.clone(),
7001                        });
7002                        outcome.failure = Some(message);
7003                        break 'worker;
7004                    }
7005                    if startup_tx
7006                        .send(ConcurrentStressStartup::Ready { worker_id })
7007                        .is_err()
7008                    {
7009                        outcome.failure = Some("startup coordinator disconnected".to_owned());
7010                        break 'worker;
7011                    }
7012                    match start_rx.recv() {
7013                        Ok(ConcurrentStressStartDecision::Run) => {}
7014                        Ok(ConcurrentStressStartDecision::Abort) => {
7015                            outcome.failure = Some("startup coordinator aborted the run".to_owned());
7016                            break 'worker;
7017                        }
7018                        Err(error) => {
7019                            outcome.failure =
7020                                Some(format!("startup decision channel disconnected: {error}"));
7021                            break 'worker;
7022                        }
7023                    }
7024
7025                    outcome.failure = None;
7026                    let mut attempts_for_commit = 0_u64;
7027                    let mut last_transient_error: Option<String> = None;
7028                    let mut rng = rand::rngs::StdRng::seed_from_u64(worker_id as u64);
7029
7030                    'transfers: while outcome.commits < OPS_PER_WRITER {
7031                        if let Some(budget_error) = concurrent_stress_attempt_budget_error(
7032                            attempts_for_commit,
7033                            outcome.attempts,
7034                            started_at.elapsed(),
7035                        ) {
7036                            outcome.failure = Some(format!(
7037                                "{budget_error}; last transient error: {}",
7038                                last_transient_error.as_deref().unwrap_or("none")
7039                            ));
7040                            break;
7041                        }
7042
7043                        outcome.attempts += 1;
7044                        attempts_for_commit += 1;
7045                        outcome.max_attempts_for_commit =
7046                            outcome.max_attempts_for_commit.max(attempts_for_commit);
7047
7048                        let from_id = rng.random_range(0..NUM_ACCOUNTS);
7049                        let to_id = rng.random_range(0..NUM_ACCOUNTS);
7050                        if from_id == to_id {
7051                            continue;
7052                        }
7053                        let amount = rng.random_range(1..=10_i64);
7054
7055                        if let Err(error) = conn.execute("BEGIN;").await {
7056                            if outcome.retries.record(&error) {
7057                                last_transient_error = Some(format!("BEGIN: {error:?}"));
7058                                concurrent_stress_backoff(
7059                                    attempts_for_commit,
7060                                    u64::try_from(worker_id).expect("worker id fits u64"),
7061                                );
7062                                continue;
7063                            }
7064                            outcome.failure =
7065                                Some(format!("unexpected BEGIN error: {error:?}"));
7066                            break;
7067                        }
7068                        let begin_seq = conn
7069                            .current_concurrent_snapshot_seq()
7070                            .expect("successful concurrent BEGIN must bind its snapshot sequence");
7071
7072                        let from_balance = match conn
7073                            .query(&format!(
7074                                "SELECT balance FROM accounts WHERE id = {from_id};"
7075                            ))
7076                            .await
7077                        {
7078                            Ok(rows) if rows.len() == 1 => match &row_values(&rows[0])[0] {
7079                                SqliteValue::Integer(balance) => *balance,
7080                                other => {
7081                                    if let Err(rollback_error) = conn.execute("ROLLBACK;").await {
7082                                        outcome.failure = Some(format!(
7083                                            "rollback after invalid balance type failed: {rollback_error:?}"
7084                                        ));
7085                                    } else {
7086                                        outcome.failure = Some(format!(
7087                                            "balance query returned invalid value: {other:?}"
7088                                        ));
7089                                    }
7090                                    break 'transfers;
7091                                }
7092                            },
7093                            Ok(rows) => {
7094                                if let Err(rollback_error) = conn.execute("ROLLBACK;").await {
7095                                    outcome.failure = Some(format!(
7096                                        "rollback after missing account failed: {rollback_error:?}"
7097                                    ));
7098                                } else {
7099                                    outcome.failure = Some(format!(
7100                                        "balance query returned {} rows for account {from_id}",
7101                                        rows.len()
7102                                    ));
7103                                }
7104                                break;
7105                            }
7106                            Err(error) => {
7107                                let transient_error = format!("balance query: {error:?}");
7108                                match concurrent_stress_rollback_precommit_transient(
7109                                    &conn,
7110                                    &mut outcome,
7111                                    "balance query",
7112                                    &error,
7113                                )
7114                                .await
7115                                {
7116                                    Ok(()) => {
7117                                        last_transient_error = Some(transient_error);
7118                                        concurrent_stress_backoff(
7119                                            attempts_for_commit,
7120                                            u64::try_from(worker_id).expect("worker id fits u64"),
7121                                        );
7122                                        continue;
7123                                    }
7124                                    Err(recovery_error) => {
7125                                        outcome.failure = Some(recovery_error);
7126                                        break;
7127                                    }
7128                                }
7129                            }
7130                        };
7131
7132                        if from_balance < amount {
7133                            if let Err(error) = conn.execute("ROLLBACK;").await {
7134                                outcome.failure = Some(format!(
7135                                    "rollback after insufficient balance failed: {error:?}"
7136                                ));
7137                                break;
7138                            }
7139                            continue;
7140                        }
7141
7142                        match conn
7143                            .execute(&format!(
7144                                "UPDATE accounts SET balance = balance - {amount} WHERE id = {from_id};"
7145                            ))
7146                            .await
7147                        {
7148                            Ok(1) => {}
7149                            Ok(affected) => {
7150                                if let Err(rollback_error) = conn.execute("ROLLBACK;").await {
7151                                    outcome.failure = Some(format!(
7152                                        "rollback after debit affected {affected} rows failed: {rollback_error:?}"
7153                                    ));
7154                                } else {
7155                                    outcome.failure = Some(format!(
7156                                        "debit affected {affected} rows; expected 1"
7157                                    ));
7158                                }
7159                                break;
7160                            }
7161                            Err(error) => {
7162                                let transient_error = format!("debit: {error:?}");
7163                                match concurrent_stress_rollback_precommit_transient(
7164                                    &conn,
7165                                    &mut outcome,
7166                                    "debit",
7167                                    &error,
7168                                )
7169                                .await
7170                                {
7171                                    Ok(()) => {
7172                                        last_transient_error = Some(transient_error);
7173                                        concurrent_stress_backoff(
7174                                            attempts_for_commit,
7175                                            u64::try_from(worker_id).expect("worker id fits u64"),
7176                                        );
7177                                        continue;
7178                                    }
7179                                    Err(recovery_error) => {
7180                                        outcome.failure = Some(recovery_error);
7181                                        break;
7182                                    }
7183                                }
7184                            }
7185                        }
7186
7187                        match conn
7188                            .execute(&format!(
7189                                "UPDATE accounts SET balance = balance + {amount} WHERE id = {to_id};"
7190                            ))
7191                            .await
7192                        {
7193                            Ok(1) => {}
7194                            Ok(affected) => {
7195                                if let Err(rollback_error) = conn.execute("ROLLBACK;").await {
7196                                    outcome.failure = Some(format!(
7197                                        "rollback after credit affected {affected} rows failed: {rollback_error:?}"
7198                                    ));
7199                                } else {
7200                                    outcome.failure = Some(format!(
7201                                        "credit affected {affected} rows; expected 1"
7202                                    ));
7203                                }
7204                                break;
7205                            }
7206                            Err(error) => {
7207                                let transient_error = format!("credit: {error:?}");
7208                                match concurrent_stress_rollback_precommit_transient(
7209                                    &conn,
7210                                    &mut outcome,
7211                                    "credit",
7212                                    &error,
7213                                )
7214                                .await
7215                                {
7216                                    Ok(()) => {
7217                                        last_transient_error = Some(transient_error);
7218                                        concurrent_stress_backoff(
7219                                            attempts_for_commit,
7220                                            u64::try_from(worker_id).expect("worker id fits u64"),
7221                                        );
7222                                        continue;
7223                                    }
7224                                    Err(recovery_error) => {
7225                                        outcome.failure = Some(recovery_error);
7226                                        break;
7227                                    }
7228                                }
7229                            }
7230                        }
7231
7232                        match conn.execute("COMMIT;").await {
7233                            Ok(_) => {
7234                                outcome.commits += 1;
7235                                let commit_seq = conn.last_local_commit_seq().expect(
7236                                    "successful concurrent commit must publish its sequence",
7237                                );
7238                                outcome.committed_transfers.push(ConcurrentStressTransfer {
7239                                    from_id,
7240                                    to_id,
7241                                    amount,
7242                                    begin_seq,
7243                                    commit_seq,
7244                                });
7245                                attempts_for_commit = 0;
7246                                last_transient_error = None;
7247                            }
7248                            Err(error) => {
7249                                let transient_error = format!("commit: {error:?}");
7250                                match concurrent_stress_rollback_precommit_transient(
7251                                    &conn,
7252                                    &mut outcome,
7253                                    "commit",
7254                                    &error,
7255                                )
7256                                .await
7257                                {
7258                                    Ok(()) => {
7259                                        last_transient_error = Some(transient_error);
7260                                        concurrent_stress_backoff(
7261                                            attempts_for_commit,
7262                                            u64::try_from(worker_id).expect("worker id fits u64"),
7263                                        );
7264                                    }
7265                                    Err(recovery_error) => {
7266                                        outcome.failure = Some(recovery_error);
7267                                        break;
7268                                    }
7269                                }
7270                            }
7271                        }
7272                    }
7273
7274                    outcome.elapsed = started_at.elapsed();
7275                    if outcome.failure.is_none()
7276                        && outcome.elapsed > CONCURRENT_STRESS_WORKER_TIMEOUT
7277                    {
7278                        outcome.failure = Some(format!(
7279                            "completed after worker deadline {:?}: {:?}",
7280                            CONCURRENT_STRESS_WORKER_TIMEOUT, outcome.elapsed
7281                        ));
7282                    }
7283                    }
7284                    if let Err(error) = conn.close_without_checkpoint_in_place().await {
7285                        let close_failure = format!("worker connection close failed: {error:?}");
7286                        if let Some(failure) = &mut outcome.failure {
7287                            failure.push_str("; ");
7288                            failure.push_str(&close_failure);
7289                        } else {
7290                            outcome.failure = Some(close_failure);
7291                        }
7292                        conn.close_best_effort_in_place().await;
7293                    }
7294                });
7295                outcome
7296            }));
7297        }
7298        drop(startup_tx);
7299
7300        let mut ready = [false; NUM_WRITERS];
7301        let mut ready_count = 0_usize;
7302        let startup_deadline = Instant::now() + CONCURRENT_STRESS_STARTUP_TIMEOUT;
7303        let mut startup_failure = None;
7304        while ready_count < NUM_WRITERS {
7305            let remaining = startup_deadline.saturating_duration_since(Instant::now());
7306            if remaining.is_zero() {
7307                startup_failure = Some(format!(
7308                    "startup timed out with {ready_count}/{NUM_WRITERS} workers ready"
7309                ));
7310                break;
7311            }
7312            match startup_rx.recv_timeout(remaining) {
7313                Ok(ConcurrentStressStartup::Ready { worker_id }) => {
7314                    if worker_id >= NUM_WRITERS {
7315                        startup_failure =
7316                            Some(format!("out-of-range startup worker id {worker_id}"));
7317                        break;
7318                    }
7319                    if std::mem::replace(&mut ready[worker_id], true) {
7320                        startup_failure =
7321                            Some(format!("duplicate startup receipt from worker {worker_id}"));
7322                        break;
7323                    }
7324                    ready_count += 1;
7325                }
7326                Ok(ConcurrentStressStartup::Failed { worker_id, error }) => {
7327                    startup_failure = Some(format!("worker {worker_id} startup failed: {error}"));
7328                    break;
7329                }
7330                Err(mpsc::RecvTimeoutError::Timeout) => {
7331                    startup_failure = Some(format!(
7332                        "startup timed out with {ready_count}/{NUM_WRITERS} workers ready"
7333                    ));
7334                    break;
7335                }
7336                Err(mpsc::RecvTimeoutError::Disconnected) => {
7337                    startup_failure = Some(format!(
7338                        "startup channel closed with {ready_count}/{NUM_WRITERS} workers ready"
7339                    ));
7340                    break;
7341                }
7342            }
7343        }
7344
7345        let start_gate = ConcurrentStressStartGate::new(start_senders);
7346        let startup_result = if let Some(error) = startup_failure {
7347            drop(start_gate);
7348            Err(error)
7349        } else {
7350            start_gate.release()
7351        };
7352
7353        let mut results = Vec::with_capacity(NUM_WRITERS);
7354        let mut panics = Vec::new();
7355        for (worker_id, handle) in handles.into_iter().enumerate() {
7356            match handle.join() {
7357                Ok(outcome) => results.push(outcome),
7358                Err(payload) => panics.push(format!(
7359                    "worker {worker_id} panicked: {}",
7360                    concurrent_stress_panic_message(payload.as_ref())
7361                )),
7362            }
7363        }
7364        assert!(
7365            panics.is_empty(),
7366            "concurrent-stress worker panics: {panics:?}"
7367        );
7368        assert!(
7369            startup_result.is_ok(),
7370            "concurrent-stress startup failed: {}; outcomes: {results:#?}",
7371            startup_result
7372                .as_ref()
7373                .expect_err("failed startup must carry a diagnostic")
7374        );
7375        assert_eq!(results.len(), NUM_WRITERS, "missing worker outcomes");
7376        results.sort_by_key(|outcome| outcome.worker_id);
7377
7378        for outcome in &results {
7379            eprintln!("concurrent stress worker outcome: {outcome:#?}");
7380        }
7381
7382        // Always collect an independent committed-file verdict before any
7383        // worker assertion can abort this keeper. A worker may fail because
7384        // its transaction assembled a mixed page-version view, or because a
7385        // writer actually published a malformed B-tree. Stock SQLite's scan,
7386        // point lookup, aggregate, and integrity checker distinguish those
7387        // two release-critical failure classes after every worker is closed.
7388        let stock_diagnostic = (|| -> Result<ConcurrentStressStockDiagnostic, String> {
7389            let stock = rusqlite::Connection::open(&db_path).map_err(|error| error.to_string())?;
7390            let row_count = stock
7391                .query_row("SELECT COUNT(*) FROM accounts;", [], |row| row.get(0))
7392                .map_err(|error| error.to_string())?;
7393            let balance_sum = stock
7394                .query_row("SELECT SUM(balance) FROM accounts;", [], |row| row.get(0))
7395                .map_err(|error| error.to_string())?;
7396            let point_count = stock
7397                .query_row("SELECT COUNT(*) FROM accounts WHERE id = 9;", [], |row| {
7398                    row.get(0)
7399                })
7400                .map_err(|error| error.to_string())?;
7401            let scan_count = stock
7402                .query_row(
7403                    "SELECT COUNT(*) FROM accounts NOT INDEXED WHERE id + 0 = 9;",
7404                    [],
7405                    |row| row.get(0),
7406                )
7407                .map_err(|error| error.to_string())?;
7408            let mut statement = stock
7409                .prepare("PRAGMA integrity_check;")
7410                .map_err(|error| error.to_string())?;
7411            let integrity = statement
7412                .query_map([], |row| row.get::<_, String>(0))
7413                .map_err(|error| error.to_string())?
7414                .collect::<rusqlite::Result<Vec<_>>>()
7415                .map_err(|error| error.to_string())?;
7416            let mut statement = stock
7417                .prepare("SELECT id, balance FROM accounts ORDER BY id;")
7418                .map_err(|error| error.to_string())?;
7419            let balances = statement
7420                .query_map([], |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)))
7421                .map_err(|error| error.to_string())?
7422                .collect::<rusqlite::Result<Vec<_>>>()
7423                .map_err(|error| error.to_string())?;
7424            Ok(ConcurrentStressStockDiagnostic {
7425                row_count,
7426                balance_sum,
7427                point_count,
7428                scan_count,
7429                integrity,
7430                balances,
7431            })
7432        })();
7433        eprintln!("concurrent stress stock SQLite diagnostic: {stock_diagnostic:#?}");
7434
7435        if let Ok(ConcurrentStressStockDiagnostic {
7436            balances: durable_balances,
7437            ..
7438        }) = &stock_diagnostic
7439        {
7440            let account_count =
7441                usize::try_from(NUM_ACCOUNTS).expect("concurrent stress account count fits usize");
7442            let mut expected_balances = vec![INITIAL_BALANCE; account_count];
7443            for transfer in results
7444                .iter()
7445                .flat_map(|outcome| &outcome.committed_transfers)
7446            {
7447                assert!(
7448                    transfer.commit_seq > transfer.begin_seq,
7449                    "committed transfer must advance its BEGIN snapshot: {transfer:?}"
7450                );
7451                let from_index = usize::try_from(transfer.from_id)
7452                    .expect("concurrent stress source account fits usize");
7453                let to_index = usize::try_from(transfer.to_id)
7454                    .expect("concurrent stress target account fits usize");
7455                expected_balances[from_index] -= transfer.amount;
7456                expected_balances[to_index] += transfer.amount;
7457            }
7458            let balance_mismatches = durable_balances
7459                .iter()
7460                .filter_map(|&(account_id, durable_balance)| {
7461                    let account_index = usize::try_from(account_id)
7462                        .expect("durable concurrent stress account id fits usize");
7463                    let expected_balance = expected_balances[account_index];
7464                    (durable_balance != expected_balance).then_some((
7465                        account_id,
7466                        expected_balance,
7467                        durable_balance,
7468                        durable_balance - expected_balance,
7469                    ))
7470                })
7471                .collect::<Vec<_>>();
7472            eprintln!(
7473                "concurrent stress durable balance mismatches (id, expected, durable, delta): \
7474                 {balance_mismatches:?}"
7475            );
7476            for (account_id, _, _, _) in &balance_mismatches {
7477                let mut touching_transfers = results
7478                    .iter()
7479                    .flat_map(|outcome| &outcome.committed_transfers)
7480                    .filter(|transfer| {
7481                        transfer.from_id == *account_id || transfer.to_id == *account_id
7482                    })
7483                    .collect::<Vec<_>>();
7484                touching_transfers.sort_by_key(|transfer| transfer.commit_seq);
7485                eprintln!(
7486                    "concurrent stress committed touches for account {account_id}: \
7487                     {touching_transfers:?}"
7488                );
7489            }
7490        }
7491
7492        let mut total_commits = 0_u64;
7493        let mut total_retries = 0_u64;
7494        for (expected_worker_id, outcome) in results.iter().enumerate() {
7495            assert_eq!(
7496                outcome.worker_id, expected_worker_id,
7497                "worker ids must be unique and contiguous"
7498            );
7499            assert!(
7500                outcome.concurrent_mode_default,
7501                "worker {} lost the concurrent-writer default",
7502                outcome.worker_id
7503            );
7504            assert!(
7505                outcome.failure.is_none(),
7506                "worker {} failed: {:?}",
7507                outcome.worker_id,
7508                outcome.failure
7509            );
7510            assert_eq!(
7511                outcome.commits, OPS_PER_WRITER,
7512                "worker {} committed the wrong number of transfers",
7513                outcome.worker_id
7514            );
7515            assert!(
7516                outcome.attempts <= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER,
7517                "worker {} exceeded its attempt budget",
7518                outcome.worker_id
7519            );
7520            assert!(
7521                outcome.max_attempts_for_commit <= CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT,
7522                "worker {} exceeded its per-commit attempt budget",
7523                outcome.worker_id
7524            );
7525            assert!(
7526                outcome.elapsed <= CONCURRENT_STRESS_WORKER_TIMEOUT,
7527                "worker {} exceeded its elapsed-time budget",
7528                outcome.worker_id
7529            );
7530            total_commits += outcome.commits;
7531            total_retries += outcome.retries.total();
7532        }
7533        assert_eq!(
7534            total_commits,
7535            u64::try_from(NUM_WRITERS).expect("writer count fits u64") * OPS_PER_WRITER
7536        );
7537        eprintln!("concurrent stress total retries: {total_retries}");
7538
7539        let stock_diagnostic = stock_diagnostic.expect("stock SQLite diagnostic must complete");
7540        assert_eq!(stock_diagnostic.row_count, NUM_ACCOUNTS);
7541        assert_eq!(stock_diagnostic.balance_sum, EXPECTED_TOTAL);
7542        assert_eq!(stock_diagnostic.point_count, 1);
7543        assert_eq!(stock_diagnostic.scan_count, 1);
7544        assert_eq!(stock_diagnostic.integrity, ["ok"]);
7545
7546        let mut final_invariants = None;
7547        let mut final_integrity = None;
7548        asupersync::test_utils::run_test(|| async {
7549            let conn = Connection::open(&db_path_string)
7550                .await
7551                .expect("reopen concurrent-stress database for verification");
7552            assert!(
7553                conn.is_concurrent_mode_default(),
7554                "verification connection must preserve the concurrent-writer default"
7555            );
7556            let rows = conn
7557                .query(
7558                    "SELECT COUNT(*), SUM(balance),\
7559                     SUM(CASE WHEN balance < 0 THEN 1 ELSE 0 END),\
7560                     MIN(length(payload)), MAX(length(payload)) FROM accounts;",
7561                )
7562                .await
7563                .expect("query final account invariants");
7564            final_invariants = Some(rows.iter().map(row_values).collect::<Vec<_>>());
7565            let integrity = conn
7566                .query("PRAGMA integrity_check;")
7567                .await
7568                .expect("run final integrity check");
7569            final_integrity = Some(integrity.iter().map(row_values).collect::<Vec<_>>());
7570            conn.close()
7571                .await
7572                .expect("close concurrent-stress verification connection");
7573        });
7574        assert_eq!(
7575            final_invariants,
7576            Some(vec![vec![
7577                SqliteValue::Integer(NUM_ACCOUNTS),
7578                SqliteValue::Integer(EXPECTED_TOTAL),
7579                SqliteValue::Integer(0),
7580                SqliteValue::Integer(512),
7581                SqliteValue::Integer(512),
7582            ]]),
7583            "final multi-page aggregate invariants must have exact INTEGER storage classes"
7584        );
7585        assert_eq!(
7586            final_integrity,
7587            Some(vec![vec![SqliteValue::Text("ok".into())]]),
7588            "integrity_check must return exactly one TEXT ok row"
7589        );
7590
7591        let receipt_token = std::env::var(CONCURRENT_STRESS_RECEIPT_ENV)
7592            .expect("supervised child must inherit its receipt token");
7593        println!("{CONCURRENT_STRESS_RECEIPT_PREFIX}{receipt_token}");
7594    }
7595
7596    #[test]
7597    fn concurrent_credit_conflict_rollback_preserves_staged_debit() {
7598        asupersync::test_utils::run_test(|| async {
7599            const NUM_ACCOUNTS: i64 = 100;
7600            const INITIAL_BALANCE: i64 = 1_000;
7601            const EXPECTED_TOTAL: i64 = NUM_ACCOUNTS * INITIAL_BALANCE;
7602
7603            let dir = tempfile::tempdir().expect("create rollback-atomicity temp dir");
7604            let db_path = dir.path().join("rollback-atomicity.db");
7605            let db_path = db_path.to_string_lossy().into_owned();
7606            let setup = Connection::open(&db_path)
7607                .await
7608                .expect("open rollback-atomicity setup connection");
7609            setup
7610                .execute(
7611                    "CREATE TABLE accounts (
7612                        id INTEGER PRIMARY KEY,
7613                        balance INTEGER,
7614                        payload TEXT NOT NULL
7615                    );",
7616                )
7617                .await
7618                .expect("create rollback-atomicity accounts table");
7619            let payload = "x".repeat(512);
7620            for account_id in 0..NUM_ACCOUNTS {
7621                setup
7622                    .execute_with_params(
7623                        "INSERT INTO accounts VALUES (?1, ?2, ?3);",
7624                        &[
7625                            SqliteValue::Integer(account_id),
7626                            SqliteValue::Integer(INITIAL_BALANCE),
7627                            SqliteValue::Text(payload.clone().into()),
7628                        ],
7629                    )
7630                    .await
7631                    .expect("insert rollback-atomicity account");
7632            }
7633            setup.close().await.expect("close setup connection");
7634
7635            let debit = Connection::open(&db_path)
7636                .await
7637                .expect("open debit connection");
7638            let credit_blocker = Connection::open(&db_path)
7639                .await
7640                .expect("open credit-blocker connection");
7641            debit.execute("BEGIN;").await.expect("begin debit txn");
7642            credit_blocker
7643                .execute("BEGIN;")
7644                .await
7645                .expect("begin credit-blocker txn");
7646            assert_eq!(
7647                debit
7648                    .execute("UPDATE accounts SET balance = balance - 9 WHERE id = 0;")
7649                    .await
7650                    .expect("stage debit on first leaf"),
7651                1
7652            );
7653            assert_eq!(
7654                credit_blocker
7655                    .execute("UPDATE accounts SET balance = balance + 1 WHERE id = 99;")
7656                    .await
7657                    .expect("lock credit leaf from peer txn"),
7658                1
7659            );
7660            let credit_error = debit
7661                .execute("UPDATE accounts SET balance = balance + 9 WHERE id = 99;")
7662                .await
7663                .expect_err("credit page held by peer must reject the partial transfer");
7664            assert!(
7665                matches!(
7666                    credit_error,
7667                    FrankenError::Busy | FrankenError::BusySnapshot { .. }
7668                ),
7669                "credit conflict must be retryable, got {credit_error:?}"
7670            );
7671            debit
7672                .execute("ROLLBACK;")
7673                .await
7674                .expect("rollback staged debit after credit conflict");
7675            credit_blocker
7676                .execute("ROLLBACK;")
7677                .await
7678                .expect("rollback credit blocker");
7679            debit.close().await.expect("close debit connection");
7680            credit_blocker
7681                .close()
7682                .await
7683                .expect("close credit-blocker connection");
7684
7685            let verify = Connection::open(&db_path)
7686                .await
7687                .expect("open rollback-atomicity verification connection");
7688            let row = verify
7689                .query_row(
7690                    "SELECT COUNT(*), SUM(balance),
7691                     (SELECT balance FROM accounts WHERE id = 0),
7692                     (SELECT balance FROM accounts WHERE id = 99)
7693                     FROM accounts;",
7694                )
7695                .await
7696                .expect("query rollback-atomicity invariants");
7697            assert_eq!(
7698                row_values(&row),
7699                vec![
7700                    SqliteValue::Integer(NUM_ACCOUNTS),
7701                    SqliteValue::Integer(EXPECTED_TOTAL),
7702                    SqliteValue::Integer(INITIAL_BALANCE),
7703                    SqliteValue::Integer(INITIAL_BALANCE),
7704                ],
7705                "rolling back a transfer after its credit conflicts must discard its staged debit"
7706            );
7707            verify
7708                .close()
7709                .await
7710                .expect("close rollback-atomicity verification connection");
7711        });
7712    }
7713
7714    #[test]
7715    fn concurrent_stress_start_gate_aborts_all_waiters_on_failure() {
7716        let mut senders = Vec::new();
7717        let mut handles = Vec::new();
7718        for _ in 0..3 {
7719            let (sender, receiver) = mpsc::sync_channel(1);
7720            senders.push(sender);
7721            handles.push(std::thread::spawn(move || {
7722                receiver
7723                    .recv_timeout(Duration::from_millis(100))
7724                    .expect("abort decision must reach every startup waiter")
7725            }));
7726        }
7727        drop(ConcurrentStressStartGate::new(senders));
7728        for handle in handles {
7729            assert_eq!(
7730                handle.join().expect("startup waiter must be joined"),
7731                ConcurrentStressStartDecision::Abort
7732            );
7733        }
7734    }
7735
7736    #[test]
7737    fn concurrent_stress_retry_budget_is_exact_and_finite() {
7738        let mut attempts = 0_u64;
7739        let mut retries = ConcurrentStressRetryCounts::default();
7740        let exhaustion = loop {
7741            if let Some(error) =
7742                concurrent_stress_attempt_budget_error(attempts, attempts, Duration::ZERO)
7743            {
7744                break error;
7745            }
7746            attempts += 1;
7747            assert!(retries.record(&FrankenError::Busy));
7748        };
7749        assert_eq!(attempts, CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT);
7750        assert_eq!(retries.busy, CONCURRENT_STRESS_MAX_ATTEMPTS_PER_COMMIT);
7751        assert!(exhaustion.contains("attempts for one commit"));
7752        assert!(retries.record(&FrankenError::BusyRecovery));
7753        assert_eq!(retries.busy_recovery, 1);
7754        assert!(
7755            concurrent_stress_attempt_budget_error(
7756                0,
7757                CONCURRENT_STRESS_MAX_ATTEMPTS_PER_WORKER,
7758                Duration::ZERO,
7759            )
7760            .expect("the total-attempt budget must be finite")
7761            .contains("total attempts")
7762        );
7763        assert!(
7764            concurrent_stress_attempt_budget_error(0, 0, CONCURRENT_STRESS_WORKER_TIMEOUT,)
7765                .expect("the elapsed-time budget must be finite")
7766                .contains("worker deadline")
7767        );
7768    }
7769
7770    /// Verify that concurrent readers see consistent snapshots.
7771    #[test]
7772    fn concurrent_readers_consistency() {
7773        asupersync::test_utils::run_test(|| async {
7774            use std::thread;
7775
7776            let dir = tempfile::tempdir().expect("create temp dir");
7777            let db_path = dir.path().join("readers.db");
7778            let db_path_str = db_path.to_str().unwrap();
7779
7780            // Setup: create table with known data.
7781            {
7782                let conn = Connection::open(db_path_str)
7783                    .await
7784                    .expect("open db for setup");
7785                conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);")
7786                    .await
7787                    .expect("create table");
7788                for i in 0..100 {
7789                    conn.execute(&format!("INSERT INTO t VALUES ({}, {});", i, i * 10))
7790                        .await
7791                        .expect("insert row");
7792                }
7793                conn.close()
7794                    .await
7795                    .expect("close concurrent-reader setup connection");
7796            }
7797
7798            const NUM_READERS: usize = 4;
7799            const READS_PER_THREAD: usize = 50;
7800
7801            let (startup_tx, startup_rx) = mpsc::channel::<ConcurrentStressStartup>();
7802            let mut start_senders = Vec::with_capacity(NUM_READERS);
7803            let mut handles = Vec::with_capacity(NUM_READERS);
7804            for thread_id in 0..NUM_READERS {
7805                let path = db_path_str.to_owned();
7806                let startup_tx = startup_tx.clone();
7807                let (start_tx, start_rx) = mpsc::sync_channel(1);
7808                start_senders.push(start_tx);
7809
7810                handles.push(thread::spawn(move || {
7811                    let mut consistent = true;
7812                    asupersync::test_utils::run_test(|| async {
7813                        let mut open_attempts = 0_u64;
7814                        let mut conn = loop {
7815                            open_attempts += 1;
7816                            match Connection::open(&path).await {
7817                                Ok(conn) => break conn,
7818                                Err(error @ (FrankenError::Busy | FrankenError::BusyRecovery))
7819                                    if open_attempts < CONCURRENT_READER_MAX_OPEN_ATTEMPTS =>
7820                                {
7821                                    eprintln!(
7822                                        "reader {thread_id} open attempt {open_attempts} hit transient {error:?}"
7823                                    );
7824                                    concurrent_stress_backoff(
7825                                        open_attempts,
7826                                        u64::try_from(thread_id).expect("reader id fits u64"),
7827                                    );
7828                                }
7829                                Err(error) => {
7830                                    let error = format!(
7831                                        "open failed after {open_attempts} attempt(s): {error:?}"
7832                                    );
7833                                    let _ = startup_tx.send(ConcurrentStressStartup::Failed {
7834                                        worker_id: thread_id,
7835                                        error,
7836                                    });
7837                                    consistent = false;
7838                                    return;
7839                                }
7840                            }
7841                        };
7842
7843                        if startup_tx
7844                            .send(ConcurrentStressStartup::Ready {
7845                                worker_id: thread_id,
7846                            })
7847                            .is_err()
7848                        {
7849                            eprintln!("reader {thread_id} startup coordinator disconnected");
7850                            conn.close_best_effort_in_place().await;
7851                            consistent = false;
7852                            return;
7853                        }
7854                        if !matches!(
7855                            start_rx.recv_timeout(CONCURRENT_STRESS_STARTUP_TIMEOUT),
7856                            Ok(ConcurrentStressStartDecision::Run)
7857                        ) {
7858                            eprintln!("reader {thread_id} did not receive the run decision");
7859                            conn.close_best_effort_in_place().await;
7860                            consistent = false;
7861                            return;
7862                        }
7863
7864                        for _ in 0..READS_PER_THREAD {
7865                            // Start a read transaction.
7866                            conn.execute("BEGIN;").await.expect("begin");
7867
7868                            // Read sum - should always be consistent.
7869                            let rows = conn
7870                                .query("SELECT SUM(val) FROM t;")
7871                                .await
7872                                .expect("sum query");
7873                            let sum = if let SqliteValue::Integer(n) = &row_values(&rows[0])[0] {
7874                                *n
7875                            } else {
7876                                consistent = false;
7877                                break;
7878                            };
7879
7880                            // Expected sum: 0 + 10 + 20 + ... + 990 = 10 * (0 + 1 + ... + 99) = 10 * 4950 = 49500
7881                            let expected = 10 * (99 * 100 / 2);
7882                            if sum != expected {
7883                                eprintln!(
7884                                    "Thread {} saw inconsistent sum: {} (expected {})",
7885                                    thread_id, sum, expected
7886                                );
7887                                consistent = false;
7888                            }
7889
7890                            conn.execute("COMMIT;").await.expect("commit");
7891                        }
7892                        if let Err(error) = conn.close_without_checkpoint_in_place().await {
7893                            eprintln!("reader {thread_id} close failed: {error:?}");
7894                            conn.close_best_effort_in_place().await;
7895                            consistent = false;
7896                        }
7897                    });
7898
7899                    consistent
7900                }));
7901            }
7902            drop(startup_tx);
7903
7904            let mut ready = [false; NUM_READERS];
7905            let mut ready_count = 0_usize;
7906            let startup_deadline = Instant::now() + CONCURRENT_STRESS_STARTUP_TIMEOUT;
7907            let mut startup_failure = None;
7908            while ready_count < NUM_READERS {
7909                let remaining = startup_deadline.saturating_duration_since(Instant::now());
7910                if remaining.is_zero() {
7911                    startup_failure = Some(format!(
7912                        "startup timed out with {ready_count}/{NUM_READERS} readers ready"
7913                    ));
7914                    break;
7915                }
7916                match startup_rx.recv_timeout(remaining) {
7917                    Ok(ConcurrentStressStartup::Ready { worker_id }) => {
7918                        if worker_id >= NUM_READERS {
7919                            startup_failure = Some(format!("out-of-range reader id {worker_id}"));
7920                            break;
7921                        }
7922                        if std::mem::replace(&mut ready[worker_id], true) {
7923                            startup_failure =
7924                                Some(format!("duplicate startup receipt from reader {worker_id}"));
7925                            break;
7926                        }
7927                        ready_count += 1;
7928                    }
7929                    Ok(ConcurrentStressStartup::Failed { worker_id, error }) => {
7930                        startup_failure =
7931                            Some(format!("reader {worker_id} startup failed: {error}"));
7932                        break;
7933                    }
7934                    Err(mpsc::RecvTimeoutError::Timeout) => {
7935                        startup_failure = Some(format!(
7936                            "startup timed out with {ready_count}/{NUM_READERS} readers ready"
7937                        ));
7938                        break;
7939                    }
7940                    Err(mpsc::RecvTimeoutError::Disconnected) => {
7941                        startup_failure = Some(format!(
7942                            "startup channel closed with {ready_count}/{NUM_READERS} readers ready"
7943                        ));
7944                        break;
7945                    }
7946                }
7947            }
7948
7949            let start_gate = ConcurrentStressStartGate::new(start_senders);
7950            let startup_result = if let Some(error) = startup_failure {
7951                drop(start_gate);
7952                Err(error)
7953            } else {
7954                start_gate.release()
7955            };
7956
7957            let results = handles
7958                .into_iter()
7959                .map(|handle| handle.join())
7960                .collect::<Vec<_>>();
7961
7962            assert!(
7963                startup_result.is_ok(),
7964                "concurrent-reader startup failed: {}",
7965                startup_result
7966                    .as_ref()
7967                    .expect_err("failed startup must carry a diagnostic")
7968            );
7969
7970            // Join every reader before asserting so the database remains live
7971            // long enough to report every worker outcome.
7972            for (i, result) in results.into_iter().enumerate() {
7973                let consistent = result.expect("reader thread panicked");
7974                assert!(consistent, "Reader thread {} saw inconsistent data", i);
7975            }
7976        });
7977    }
7978
7979    // ── Conformance gap probes (fixtures 017–021) ──────────────────────
7980
7981    #[test]
7982    fn conformance_017_type_affinity_edge_numeric_coercion() {
7983        asupersync::test_utils::run_test(|| async {
7984            // '3.0e+5' into NUMERIC should coerce to integer 300000
7985            let conn = Connection::open(":memory:").await.unwrap();
7986            conn.execute("CREATE TABLE q1(a NUMERIC, b TEXT, c INTEGER)")
7987                .await
7988                .unwrap();
7989            conn.execute("INSERT INTO q1 VALUES('3.0e+5', 123, '0042')")
7990                .await
7991                .unwrap();
7992            let rows = conn
7993                .query("SELECT typeof(a), a, typeof(b), b, typeof(c), c FROM q1")
7994                .await
7995                .unwrap();
7996            assert_eq!(rows.len(), 1);
7997            let vals = row_values(&rows[0]);
7998            // SQLite behavior: '3.0e+5' → NUMERIC → integer 300000
7999            assert_eq!(vals[0], SqliteValue::Text("integer".into()));
8000            assert_eq!(vals[1], SqliteValue::Integer(300_000));
8001            // 123 into TEXT → text "123"
8002            assert_eq!(vals[2], SqliteValue::Text("text".into()));
8003            assert_eq!(vals[3], SqliteValue::Text("123".into()));
8004            // '0042' into INTEGER → integer 42
8005            assert_eq!(vals[4], SqliteValue::Text("integer".into()));
8006            assert_eq!(vals[5], SqliteValue::Integer(42));
8007        });
8008    }
8009
8010    #[test]
8011    fn conformance_018_collation_nocase_ascii_only() {
8012        asupersync::test_utils::run_test(|| async {
8013            // NOCASE is ASCII-insensitive: 'a' = 'A' → 1
8014            // but Unicode-sensitive: 'æ' ≠ 'Æ' → 0
8015            let conn = Connection::open(":memory:").await.unwrap();
8016            let rows = conn.query("SELECT 'a' = 'A' COLLATE NOCASE").await.unwrap();
8017            assert_eq!(rows.len(), 1);
8018            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
8019        });
8020    }
8021
8022    #[test]
8023    fn conformance_018_collation_nocase_unicode_sensitive() {
8024        asupersync::test_utils::run_test(|| async {
8025            let conn = Connection::open(":memory:").await.unwrap();
8026            // Unicode chars: NOCASE does NOT fold them
8027            let rows = conn.query("SELECT 'æ' = 'Æ' COLLATE NOCASE").await.unwrap();
8028            assert_eq!(rows.len(), 1);
8029            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(0));
8030        });
8031    }
8032
8033    #[test]
8034    fn conformance_019_null_unique_allows_multiple_nulls() {
8035        asupersync::test_utils::run_test(|| async {
8036            let conn = Connection::open(":memory:").await.unwrap();
8037            conn.execute("CREATE TABLE q3(a INTEGER UNIQUE, note TEXT)")
8038                .await
8039                .unwrap();
8040            conn.execute("INSERT INTO q3(a, note) VALUES(NULL, 'first-null')")
8041                .await
8042                .unwrap();
8043            // UNIQUE allows multiple NULLs
8044            conn.execute("INSERT INTO q3(a, note) VALUES(NULL, 'second-null')")
8045                .await
8046                .unwrap();
8047            conn.execute("INSERT INTO q3(a, note) VALUES(7, 'first-seven')")
8048                .await
8049                .unwrap();
8050            let rows = conn.query("SELECT COUNT(*) FROM q3").await.unwrap();
8051            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
8052        });
8053    }
8054
8055    #[test]
8056    fn conformance_019_null_unique_rejects_duplicate_non_null() {
8057        asupersync::test_utils::run_test(|| async {
8058            let conn = Connection::open(":memory:").await.unwrap();
8059            conn.execute("CREATE TABLE q3b(a INTEGER UNIQUE, note TEXT)")
8060                .await
8061                .unwrap();
8062            conn.execute("INSERT INTO q3b(a, note) VALUES(7, 'first-seven')")
8063                .await
8064                .unwrap();
8065            // Duplicate non-NULL should be rejected
8066            let result = conn
8067                .execute("INSERT INTO q3b(a, note) VALUES(7, 'dup')")
8068                .await;
8069            assert!(
8070                result.is_err(),
8071                "Duplicate non-NULL unique value should fail"
8072            );
8073        });
8074    }
8075
8076    #[test]
8077    fn conformance_020_integer_overflow_promotes_to_real() {
8078        asupersync::test_utils::run_test(|| async {
8079            let conn = Connection::open(":memory:").await.unwrap();
8080            // i64::MAX + 1 should overflow to real
8081            let rows = conn
8082                .query("SELECT typeof(9223372036854775807 + 1)")
8083                .await
8084                .unwrap();
8085            assert_eq!(rows.len(), 1);
8086            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("real".into()));
8087        });
8088    }
8089
8090    #[test]
8091    fn conformance_020_integer_underflow_promotes_to_real() {
8092        asupersync::test_utils::run_test(|| async {
8093            let conn = Connection::open(":memory:").await.unwrap();
8094            // i64::MIN - 1 should underflow to real
8095            let rows = conn
8096                .query("SELECT typeof(-9223372036854775808 - 1)")
8097                .await
8098                .unwrap();
8099            assert_eq!(rows.len(), 1);
8100            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("real".into()));
8101        });
8102    }
8103
8104    #[test]
8105    fn conformance_021_savepoint_rollback_preserves_outer() {
8106        asupersync::test_utils::run_test(|| async {
8107            let conn = Connection::open(":memory:").await.unwrap();
8108            conn.execute("CREATE TABLE q5(id INTEGER PRIMARY KEY, note TEXT)")
8109                .await
8110                .unwrap();
8111            conn.execute("BEGIN").await.unwrap();
8112            conn.execute("INSERT INTO q5 VALUES(1, 'outer')")
8113                .await
8114                .unwrap();
8115            conn.execute("SAVEPOINT s1").await.unwrap();
8116            conn.execute("INSERT INTO q5 VALUES(2, 'inner')")
8117                .await
8118                .unwrap();
8119            conn.execute("ROLLBACK TO s1").await.unwrap();
8120            conn.execute("RELEASE s1").await.unwrap();
8121            conn.execute("COMMIT").await.unwrap();
8122            let rows = conn
8123                .query("SELECT id, note FROM q5 ORDER BY id")
8124                .await
8125                .unwrap();
8126            assert_eq!(rows.len(), 1, "ROLLBACK TO s1 should undo inner insert");
8127            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
8128            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("outer".into()));
8129        });
8130    }
8131
8132    #[test]
8133    fn conformance_021_nested_begin_errors() {
8134        asupersync::test_utils::run_test(|| async {
8135            let conn = Connection::open(":memory:").await.unwrap();
8136            conn.execute("BEGIN").await.unwrap();
8137            // Nested BEGIN inside an active transaction should error
8138            let result = conn.execute("BEGIN").await;
8139            assert!(result.is_err(), "Nested BEGIN should produce an error");
8140            conn.execute("ROLLBACK").await.unwrap();
8141        });
8142    }
8143
8144    // ── SQL Parity: REPLACE statement ────────────────────────────────────
8145
8146    #[test]
8147    fn parity_replace_into_inserts_new_row() {
8148        asupersync::test_utils::run_test(|| async {
8149            let conn = Connection::open(":memory:").await.unwrap();
8150            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
8151                .await
8152                .unwrap();
8153            conn.execute("REPLACE INTO t VALUES (1, 'first');")
8154                .await
8155                .unwrap();
8156            let rows = conn.query("SELECT id, val FROM t;").await.unwrap();
8157            assert_eq!(rows.len(), 1);
8158            assert_eq!(row_values(&rows[0])[1], SqliteValue::Text("first".into()));
8159        });
8160    }
8161
8162    #[test]
8163    fn parity_replace_into_overwrites_existing() {
8164        asupersync::test_utils::run_test(|| async {
8165            let conn = Connection::open(":memory:").await.unwrap();
8166            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
8167                .await
8168                .unwrap();
8169            conn.execute("INSERT INTO t VALUES (1, 'old');")
8170                .await
8171                .unwrap();
8172            conn.execute("REPLACE INTO t VALUES (1, 'new');")
8173                .await
8174                .unwrap();
8175            let rows = conn.query("SELECT val FROM t WHERE id = 1;").await.unwrap();
8176            assert_eq!(rows.len(), 1);
8177            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("new".into()));
8178        });
8179    }
8180
8181    // ── SQL Parity: INSERT OR IGNORE ─────────────────────────────────────
8182
8183    #[test]
8184    fn parity_insert_or_ignore_skips_conflict() {
8185        asupersync::test_utils::run_test(|| async {
8186            let conn = Connection::open(":memory:").await.unwrap();
8187            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
8188                .await
8189                .unwrap();
8190            conn.execute("INSERT INTO t VALUES (1, 'first');")
8191                .await
8192                .unwrap();
8193            // INSERT OR IGNORE should silently skip the conflicting row
8194            conn.execute("INSERT OR IGNORE INTO t VALUES (1, 'dup');")
8195                .await
8196                .unwrap();
8197            let rows = conn.query("SELECT val FROM t WHERE id = 1;").await.unwrap();
8198            assert_eq!(rows.len(), 1);
8199            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("first".into()));
8200        });
8201    }
8202
8203    // ── SQL Parity: Multi-column ORDER BY ────────────────────────────────
8204
8205    #[test]
8206    fn parity_multi_column_order_by() {
8207        asupersync::test_utils::run_test(|| async {
8208            let conn = Connection::open(":memory:").await.unwrap();
8209            conn.execute("CREATE TABLE t (a INTEGER, b INTEGER, c TEXT);")
8210                .await
8211                .unwrap();
8212            conn.execute("INSERT INTO t VALUES (2, 1, 'x');")
8213                .await
8214                .unwrap();
8215            conn.execute("INSERT INTO t VALUES (1, 2, 'y');")
8216                .await
8217                .unwrap();
8218            conn.execute("INSERT INTO t VALUES (1, 1, 'z');")
8219                .await
8220                .unwrap();
8221            let rows = conn.query("SELECT c FROM t ORDER BY a, b;").await.unwrap();
8222            assert_eq!(rows.len(), 3);
8223            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("z".into()));
8224            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("y".into()));
8225            assert_eq!(row_values(&rows[2])[0], SqliteValue::Text("x".into()));
8226        });
8227    }
8228
8229    // ── SQL Parity: LIMIT with OFFSET ────────────────────────────────────
8230
8231    #[test]
8232    fn parity_limit_with_offset() {
8233        asupersync::test_utils::run_test(|| async {
8234            let conn = Connection::open(":memory:").await.unwrap();
8235            conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, val TEXT);")
8236                .await
8237                .unwrap();
8238            for i in 1..=5 {
8239                conn.execute(&format!("INSERT INTO t VALUES ({i}, 'r{i}');"))
8240                    .await
8241                    .unwrap();
8242            }
8243            let rows = conn
8244                .query("SELECT val FROM t ORDER BY id LIMIT 2 OFFSET 2;")
8245                .await
8246                .unwrap();
8247            assert_eq!(rows.len(), 2);
8248            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("r3".into()));
8249            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("r4".into()));
8250        });
8251    }
8252
8253    // ── SQL Parity: Subquery in WHERE ────────────────────────────────────
8254
8255    #[test]
8256    fn parity_subquery_in_where() {
8257        asupersync::test_utils::run_test(|| async {
8258            let conn = Connection::open(":memory:").await.unwrap();
8259            conn.execute("CREATE TABLE t1 (id INTEGER, val TEXT);")
8260                .await
8261                .unwrap();
8262            conn.execute("CREATE TABLE t2 (ref_id INTEGER);")
8263                .await
8264                .unwrap();
8265            conn.execute("INSERT INTO t1 VALUES (1, 'a');")
8266                .await
8267                .unwrap();
8268            conn.execute("INSERT INTO t1 VALUES (2, 'b');")
8269                .await
8270                .unwrap();
8271            conn.execute("INSERT INTO t1 VALUES (3, 'c');")
8272                .await
8273                .unwrap();
8274            conn.execute("INSERT INTO t2 VALUES (1);").await.unwrap();
8275            conn.execute("INSERT INTO t2 VALUES (3);").await.unwrap();
8276            let rows = conn
8277                .query("SELECT val FROM t1 WHERE id IN (SELECT ref_id FROM t2) ORDER BY id;")
8278                .await
8279                .unwrap();
8280            assert_eq!(rows.len(), 2);
8281            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("a".into()));
8282            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("c".into()));
8283        });
8284    }
8285
8286    // ── SQL Parity: CAST in expressions ──────────────────────────────────
8287
8288    #[test]
8289    fn parity_cast_integer_to_text() {
8290        asupersync::test_utils::run_test(|| async {
8291            let conn = Connection::open(":memory:").await.unwrap();
8292            let row = conn.query_row("SELECT CAST(42 AS TEXT);").await.unwrap();
8293            assert_eq!(row_values(&row)[0], SqliteValue::Text("42".into()));
8294        });
8295    }
8296
8297    #[test]
8298    fn parity_cast_text_to_integer() {
8299        asupersync::test_utils::run_test(|| async {
8300            let conn = Connection::open(":memory:").await.unwrap();
8301            let row = conn
8302                .query_row("SELECT CAST('123' AS INTEGER);")
8303                .await
8304                .unwrap();
8305            assert_eq!(row_values(&row)[0], SqliteValue::Integer(123));
8306        });
8307    }
8308
8309    // ── SQL Parity: EXISTS subquery ──────────────────────────────────────
8310
8311    #[test]
8312    fn parity_exists_subquery() {
8313        asupersync::test_utils::run_test(|| async {
8314            let conn = Connection::open(":memory:").await.unwrap();
8315            conn.execute("CREATE TABLE t (id INTEGER);").await.unwrap();
8316            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
8317            let row = conn
8318                .query_row("SELECT EXISTS (SELECT 1 FROM t WHERE id = 1);")
8319                .await
8320                .unwrap();
8321            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8322            let row = conn
8323                .query_row("SELECT EXISTS (SELECT 1 FROM t WHERE id = 999);")
8324                .await
8325                .unwrap();
8326            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
8327        });
8328    }
8329
8330    // ── SQL Parity: COUNT(DISTINCT ...) ──────────────────────────────────
8331
8332    #[test]
8333    fn parity_count_distinct() {
8334        asupersync::test_utils::run_test(|| async {
8335            let conn = Connection::open(":memory:").await.unwrap();
8336            conn.execute("CREATE TABLE t (val INTEGER);").await.unwrap();
8337            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
8338            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
8339            conn.execute("INSERT INTO t VALUES (1);").await.unwrap();
8340            conn.execute("INSERT INTO t VALUES (3);").await.unwrap();
8341            conn.execute("INSERT INTO t VALUES (2);").await.unwrap();
8342            let row = conn
8343                .query_row("SELECT COUNT(DISTINCT val) FROM t;")
8344                .await
8345                .unwrap();
8346            assert_eq!(row_values(&row)[0], SqliteValue::Integer(3));
8347        });
8348    }
8349
8350    // ── SQL Parity: GROUP_CONCAT ─────────────────────────────────────────
8351
8352    #[test]
8353    fn parity_group_concat_basic() {
8354        asupersync::test_utils::run_test(|| async {
8355            let conn = Connection::open(":memory:").await.unwrap();
8356            conn.execute("CREATE TABLE t (grp TEXT, val TEXT);")
8357                .await
8358                .unwrap();
8359            conn.execute("INSERT INTO t VALUES ('a', 'x');")
8360                .await
8361                .unwrap();
8362            conn.execute("INSERT INTO t VALUES ('a', 'y');")
8363                .await
8364                .unwrap();
8365            conn.execute("INSERT INTO t VALUES ('b', 'z');")
8366                .await
8367                .unwrap();
8368            let rows = conn
8369                .query("SELECT grp, GROUP_CONCAT(val, ',') FROM t GROUP BY grp ORDER BY grp;")
8370                .await
8371                .unwrap();
8372            assert_eq!(rows.len(), 2);
8373            // Group 'a' should have x,y (in insertion order)
8374            let a_val = &row_values(&rows[0])[1];
8375            match a_val {
8376                SqliteValue::Text(s) => {
8377                    assert!(&**s == "x,y" || &**s == "y,x", "group_concat for 'a' = {s}");
8378                }
8379                other => panic!("expected Text, got {other:?}"),
8380            }
8381        });
8382    }
8383
8384    // ── DISTINCT aggregate edge-case tests ─────────────────────────────
8385
8386    #[test]
8387    fn parity_count_distinct_with_nulls() {
8388        asupersync::test_utils::run_test(|| async {
8389            let conn = Connection::open(":memory:").await.unwrap();
8390            conn.execute("CREATE TABLE d2(x INTEGER);").await.unwrap();
8391            conn.execute("INSERT INTO d2 VALUES(1);").await.unwrap();
8392            conn.execute("INSERT INTO d2 VALUES(NULL);").await.unwrap();
8393            conn.execute("INSERT INTO d2 VALUES(2);").await.unwrap();
8394            conn.execute("INSERT INTO d2 VALUES(NULL);").await.unwrap();
8395            let row = conn
8396                .query_row("SELECT COUNT(DISTINCT x) FROM d2;")
8397                .await
8398                .unwrap();
8399            // COUNT(DISTINCT x) ignores NULLs → 2 (values 1, 2)
8400            assert_eq!(row_values(&row)[0], SqliteValue::Integer(2));
8401        });
8402    }
8403
8404    #[test]
8405    fn parity_sum_distinct() {
8406        asupersync::test_utils::run_test(|| async {
8407            let conn = Connection::open(":memory:").await.unwrap();
8408            conn.execute("CREATE TABLE d3(x INTEGER);").await.unwrap();
8409            conn.execute("INSERT INTO d3 VALUES(10);").await.unwrap();
8410            conn.execute("INSERT INTO d3 VALUES(20);").await.unwrap();
8411            conn.execute("INSERT INTO d3 VALUES(10);").await.unwrap();
8412            conn.execute("INSERT INTO d3 VALUES(30);").await.unwrap();
8413            conn.execute("INSERT INTO d3 VALUES(20);").await.unwrap();
8414            let row = conn
8415                .query_row("SELECT SUM(DISTINCT x) FROM d3;")
8416                .await
8417                .unwrap();
8418            // SUM(DISTINCT x) = 10 + 20 + 30 = 60
8419            assert_eq!(row_values(&row)[0], SqliteValue::Integer(60));
8420        });
8421    }
8422
8423    #[test]
8424    fn parity_count_vs_count_distinct() {
8425        asupersync::test_utils::run_test(|| async {
8426            let conn = Connection::open(":memory:").await.unwrap();
8427            conn.execute("CREATE TABLE d4(x INTEGER);").await.unwrap();
8428            conn.execute("INSERT INTO d4 VALUES(1);").await.unwrap();
8429            conn.execute("INSERT INTO d4 VALUES(1);").await.unwrap();
8430            conn.execute("INSERT INTO d4 VALUES(2);").await.unwrap();
8431            conn.execute("INSERT INTO d4 VALUES(2);").await.unwrap();
8432            conn.execute("INSERT INTO d4 VALUES(2);").await.unwrap();
8433            let r1 = conn.query_row("SELECT COUNT(x) FROM d4;").await.unwrap();
8434            assert_eq!(row_values(&r1)[0], SqliteValue::Integer(5));
8435            let r2 = conn
8436                .query_row("SELECT COUNT(DISTINCT x) FROM d4;")
8437                .await
8438                .unwrap();
8439            assert_eq!(row_values(&r2)[0], SqliteValue::Integer(2));
8440        });
8441    }
8442
8443    #[test]
8444    fn parity_count_distinct_group_by() {
8445        asupersync::test_utils::run_test(|| async {
8446            let conn = Connection::open(":memory:").await.unwrap();
8447            conn.execute("CREATE TABLE d5(grp TEXT, val INTEGER);")
8448                .await
8449                .unwrap();
8450            conn.execute("INSERT INTO d5 VALUES('a', 1);")
8451                .await
8452                .unwrap();
8453            conn.execute("INSERT INTO d5 VALUES('a', 2);")
8454                .await
8455                .unwrap();
8456            conn.execute("INSERT INTO d5 VALUES('a', 1);")
8457                .await
8458                .unwrap();
8459            conn.execute("INSERT INTO d5 VALUES('b', 10);")
8460                .await
8461                .unwrap();
8462            conn.execute("INSERT INTO d5 VALUES('b', 10);")
8463                .await
8464                .unwrap();
8465            conn.execute("INSERT INTO d5 VALUES('b', 20);")
8466                .await
8467                .unwrap();
8468            let rows = conn
8469                .query("SELECT grp, COUNT(DISTINCT val) FROM d5 GROUP BY grp ORDER BY grp;")
8470                .await
8471                .unwrap();
8472            assert_eq!(rows.len(), 2);
8473            // Group 'a': {1,2} → 2
8474            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("a".into()));
8475            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(2));
8476            // Group 'b': {10,20} → 2
8477            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("b".into()));
8478            assert_eq!(row_values(&rows[1])[1], SqliteValue::Integer(2));
8479        });
8480    }
8481
8482    #[test]
8483    fn parity_count_distinct_all_same() {
8484        asupersync::test_utils::run_test(|| async {
8485            let conn = Connection::open(":memory:").await.unwrap();
8486            conn.execute("CREATE TABLE d6(x INTEGER);").await.unwrap();
8487            conn.execute("INSERT INTO d6 VALUES(42);").await.unwrap();
8488            conn.execute("INSERT INTO d6 VALUES(42);").await.unwrap();
8489            conn.execute("INSERT INTO d6 VALUES(42);").await.unwrap();
8490            let row = conn
8491                .query_row("SELECT COUNT(DISTINCT x) FROM d6;")
8492                .await
8493                .unwrap();
8494            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8495        });
8496    }
8497
8498    #[test]
8499    fn parity_count_distinct_empty_table() {
8500        asupersync::test_utils::run_test(|| async {
8501            let conn = Connection::open(":memory:").await.unwrap();
8502            conn.execute("CREATE TABLE d7(x INTEGER);").await.unwrap();
8503            let row = conn
8504                .query_row("SELECT COUNT(DISTINCT x) FROM d7;")
8505                .await
8506                .unwrap();
8507            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
8508        });
8509    }
8510
8511    // ── Scalar subquery tests ──────────────────────────────────────────
8512
8513    #[test]
8514    fn parity_scalar_subquery_aggregate() {
8515        asupersync::test_utils::run_test(|| async {
8516            let conn = Connection::open(":memory:").await.unwrap();
8517            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8518            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
8519            conn.execute("INSERT INTO t VALUES(20);").await.unwrap();
8520            conn.execute("INSERT INTO t VALUES(30);").await.unwrap();
8521            let row = conn
8522                .query_row("SELECT (SELECT COUNT(*) FROM t);")
8523                .await
8524                .unwrap();
8525            assert_eq!(row_values(&row)[0], SqliteValue::Integer(3));
8526        });
8527    }
8528
8529    #[test]
8530    fn parity_scalar_subquery_max() {
8531        asupersync::test_utils::run_test(|| async {
8532            let conn = Connection::open(":memory:").await.unwrap();
8533            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8534            conn.execute("INSERT INTO t VALUES(5);").await.unwrap();
8535            conn.execute("INSERT INTO t VALUES(15);").await.unwrap();
8536            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
8537            let row = conn
8538                .query_row("SELECT (SELECT MAX(x) FROM t);")
8539                .await
8540                .unwrap();
8541            assert_eq!(row_values(&row)[0], SqliteValue::Integer(15));
8542        });
8543    }
8544
8545    #[test]
8546    fn parity_scalar_subquery_no_from() {
8547        asupersync::test_utils::run_test(|| async {
8548            let conn = Connection::open(":memory:").await.unwrap();
8549            let row = conn.query_row("SELECT (SELECT 42);").await.unwrap();
8550            assert_eq!(row_values(&row)[0], SqliteValue::Integer(42));
8551        });
8552    }
8553
8554    #[test]
8555    fn parity_scalar_subquery_first_row() {
8556        asupersync::test_utils::run_test(|| async {
8557            let conn = Connection::open(":memory:").await.unwrap();
8558            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8559            conn.execute("INSERT INTO t VALUES(100);").await.unwrap();
8560            conn.execute("INSERT INTO t VALUES(200);").await.unwrap();
8561            let row = conn.query_row("SELECT (SELECT x FROM t);").await.unwrap();
8562            // Should return the first row value (100).
8563            assert_eq!(row_values(&row)[0], SqliteValue::Integer(100));
8564        });
8565    }
8566
8567    #[test]
8568    fn parity_scalar_subquery_empty_table_is_null() {
8569        asupersync::test_utils::run_test(|| async {
8570            let conn = Connection::open(":memory:").await.unwrap();
8571            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8572            let row = conn.query_row("SELECT (SELECT x FROM t);").await.unwrap();
8573            assert_eq!(row_values(&row)[0], SqliteValue::Null);
8574        });
8575    }
8576
8577    // ── EXISTS subquery tests ──────────────────────────────────────────
8578
8579    #[test]
8580    fn parity_exists_true() {
8581        asupersync::test_utils::run_test(|| async {
8582            let conn = Connection::open(":memory:").await.unwrap();
8583            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8584            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
8585            let row = conn
8586                .query_row("SELECT EXISTS (SELECT 1 FROM t);")
8587                .await
8588                .unwrap();
8589            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8590        });
8591    }
8592
8593    #[test]
8594    fn parity_exists_false_empty() {
8595        asupersync::test_utils::run_test(|| async {
8596            let conn = Connection::open(":memory:").await.unwrap();
8597            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8598            let row = conn
8599                .query_row("SELECT EXISTS (SELECT 1 FROM t);")
8600                .await
8601                .unwrap();
8602            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
8603        });
8604    }
8605
8606    #[test]
8607    fn parity_not_exists_true_empty() {
8608        asupersync::test_utils::run_test(|| async {
8609            let conn = Connection::open(":memory:").await.unwrap();
8610            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8611            let row = conn
8612                .query_row("SELECT NOT EXISTS (SELECT 1 FROM t);")
8613                .await
8614                .unwrap();
8615            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8616        });
8617    }
8618
8619    #[test]
8620    fn parity_exists_no_from() {
8621        asupersync::test_utils::run_test(|| async {
8622            // EXISTS (SELECT 1) is always true — no table needed.
8623            let conn = Connection::open(":memory:").await.unwrap();
8624            let row = conn.query_row("SELECT EXISTS (SELECT 1);").await.unwrap();
8625            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8626        });
8627    }
8628
8629    #[test]
8630    fn parity_exists_with_where() {
8631        asupersync::test_utils::run_test(|| async {
8632            let conn = Connection::open(":memory:").await.unwrap();
8633            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8634            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
8635            conn.execute("INSERT INTO t VALUES(20);").await.unwrap();
8636
8637            // EXISTS with WHERE that matches.
8638            let row = conn
8639                .query_row("SELECT EXISTS (SELECT 1 FROM t WHERE x = 10);")
8640                .await
8641                .unwrap();
8642            assert_eq!(row_values(&row)[0], SqliteValue::Integer(1));
8643
8644            // EXISTS with WHERE that doesn't match.
8645            let row = conn
8646                .query_row("SELECT EXISTS (SELECT 1 FROM t WHERE x = 99);")
8647                .await
8648                .unwrap();
8649            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
8650        });
8651    }
8652
8653    // ── FILTER clause parity tests ──────────────────────────────────────
8654
8655    #[test]
8656    fn parity_count_filter() {
8657        asupersync::test_utils::run_test(|| async {
8658            let conn = Connection::open(":memory:").await.unwrap();
8659            conn.execute("CREATE TABLE f1(x INTEGER);").await.unwrap();
8660            conn.execute("INSERT INTO f1 VALUES(1);").await.unwrap();
8661            conn.execute("INSERT INTO f1 VALUES(2);").await.unwrap();
8662            conn.execute("INSERT INTO f1 VALUES(3);").await.unwrap();
8663            conn.execute("INSERT INTO f1 VALUES(4);").await.unwrap();
8664            conn.execute("INSERT INTO f1 VALUES(5);").await.unwrap();
8665            // COUNT(*) FILTER (WHERE x > 3) → 2 rows (4, 5).
8666            let row = conn
8667                .query_row("SELECT COUNT(*) FILTER (WHERE x > 3) FROM f1;")
8668                .await
8669                .unwrap();
8670            assert_eq!(row_values(&row)[0], SqliteValue::Integer(2));
8671        });
8672    }
8673
8674    #[test]
8675    fn parity_sum_filter() {
8676        asupersync::test_utils::run_test(|| async {
8677            let conn = Connection::open(":memory:").await.unwrap();
8678            conn.execute("CREATE TABLE f2(x INTEGER);").await.unwrap();
8679            conn.execute("INSERT INTO f2 VALUES(10);").await.unwrap();
8680            conn.execute("INSERT INTO f2 VALUES(20);").await.unwrap();
8681            conn.execute("INSERT INTO f2 VALUES(30);").await.unwrap();
8682            // SUM(x) FILTER (WHERE x >= 20) → 50.
8683            let row = conn
8684                .query_row("SELECT SUM(x) FILTER (WHERE x >= 20) FROM f2;")
8685                .await
8686                .unwrap();
8687            assert_eq!(row_values(&row)[0], SqliteValue::Integer(50));
8688        });
8689    }
8690
8691    #[test]
8692    fn parity_count_filter_none_match() {
8693        asupersync::test_utils::run_test(|| async {
8694            let conn = Connection::open(":memory:").await.unwrap();
8695            conn.execute("CREATE TABLE f3(x INTEGER);").await.unwrap();
8696            conn.execute("INSERT INTO f3 VALUES(1);").await.unwrap();
8697            conn.execute("INSERT INTO f3 VALUES(2);").await.unwrap();
8698            // COUNT(*) FILTER (WHERE x > 100) → 0.
8699            let row = conn
8700                .query_row("SELECT COUNT(*) FILTER (WHERE x > 100) FROM f3;")
8701                .await
8702                .unwrap();
8703            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
8704        });
8705    }
8706
8707    #[test]
8708    fn parity_filter_no_group_by_same_table() {
8709        asupersync::test_utils::run_test(|| async {
8710            // Diagnostic: verify FILTER works on the SAME table/query without GROUP BY.
8711            let conn = Connection::open(":memory:").await.unwrap();
8712            conn.execute("CREATE TABLE f4b(city TEXT, age INTEGER);")
8713                .await
8714                .unwrap();
8715            conn.execute("INSERT INTO f4b VALUES('A', 10);")
8716                .await
8717                .unwrap();
8718            conn.execute("INSERT INTO f4b VALUES('A', 30);")
8719                .await
8720                .unwrap();
8721            conn.execute("INSERT INTO f4b VALUES('B', 20);")
8722                .await
8723                .unwrap();
8724            conn.execute("INSERT INTO f4b VALUES('B', 40);")
8725                .await
8726                .unwrap();
8727            // COUNT(*) FILTER (WHERE age > 25) → 2 rows (30, 40).
8728            let row = conn
8729                .query_row("SELECT COUNT(*) FILTER (WHERE age > 25) FROM f4b;")
8730                .await
8731                .unwrap();
8732            assert_eq!(row_values(&row)[0], SqliteValue::Integer(2));
8733        });
8734    }
8735
8736    #[test]
8737    fn parity_filter_group_by_always_false() {
8738        asupersync::test_utils::run_test(|| async {
8739            // FILTER (WHERE 0) should always exclude → count = 0 per group.
8740            let conn = Connection::open(":memory:").await.unwrap();
8741            conn.execute("CREATE TABLE f4z(city TEXT, val INTEGER);")
8742                .await
8743                .unwrap();
8744            conn.execute("INSERT INTO f4z VALUES('A', 1);")
8745                .await
8746                .unwrap();
8747            conn.execute("INSERT INTO f4z VALUES('A', 2);")
8748                .await
8749                .unwrap();
8750            conn.execute("INSERT INTO f4z VALUES('B', 3);")
8751                .await
8752                .unwrap();
8753            let rows = conn
8754                .query("SELECT city, COUNT(*) FILTER (WHERE 0) FROM f4z GROUP BY city;")
8755                .await
8756                .unwrap();
8757            let mut results: Vec<(String, i64)> = rows
8758                .iter()
8759                .map(|r| {
8760                    let vals = row_values(r);
8761                    let city = match &vals[0] {
8762                        SqliteValue::Text(s) => s.to_string(),
8763                        _ => panic!("expected text"),
8764                    };
8765                    let cnt = match vals[1] {
8766                        SqliteValue::Integer(n) => n,
8767                        _ => panic!("expected integer, got {:?}", vals[1]),
8768                    };
8769                    (city, cnt)
8770                })
8771                .collect();
8772            results.sort_by(|a, b| a.0.cmp(&b.0));
8773            assert_eq!(results, vec![("A".into(), 0), ("B".into(), 0)]);
8774        });
8775    }
8776
8777    #[test]
8778    fn parity_filter_with_group_by() {
8779        asupersync::test_utils::run_test(|| async {
8780            let conn = Connection::open(":memory:").await.unwrap();
8781            conn.execute("CREATE TABLE f4(city TEXT, age INTEGER);")
8782                .await
8783                .unwrap();
8784            conn.execute("INSERT INTO f4 VALUES('A', 10);")
8785                .await
8786                .unwrap();
8787            conn.execute("INSERT INTO f4 VALUES('A', 30);")
8788                .await
8789                .unwrap();
8790            conn.execute("INSERT INTO f4 VALUES('B', 20);")
8791                .await
8792                .unwrap();
8793            conn.execute("INSERT INTO f4 VALUES('B', 40);")
8794                .await
8795                .unwrap();
8796            // COUNT(*) FILTER (WHERE age > 25) per group:
8797            //   A: 1 (only age=30), B: 1 (only age=40).
8798            let rows = conn
8799                .query("SELECT city, COUNT(*) FILTER (WHERE age > 25) FROM f4 GROUP BY city;")
8800                .await
8801                .unwrap();
8802            let mut results: Vec<(String, i64)> = rows
8803                .iter()
8804                .map(|r| {
8805                    let vals = row_values(r);
8806                    let city = match &vals[0] {
8807                        SqliteValue::Text(s) => s.to_string(),
8808                        _ => panic!("expected text"),
8809                    };
8810                    let cnt = match vals[1] {
8811                        SqliteValue::Integer(n) => n,
8812                        _ => panic!("expected integer"),
8813                    };
8814                    (city, cnt)
8815                })
8816                .collect();
8817            results.sort_by(|a, b| a.0.cmp(&b.0));
8818            assert_eq!(results, vec![("A".into(), 1), ("B".into(), 1)]);
8819        });
8820    }
8821
8822    #[test]
8823    fn parity_filter_multiple_aggregates() {
8824        asupersync::test_utils::run_test(|| async {
8825            let conn = Connection::open(":memory:").await.unwrap();
8826            conn.execute("CREATE TABLE f5(x INTEGER);").await.unwrap();
8827            conn.execute("INSERT INTO f5 VALUES(1);").await.unwrap();
8828            conn.execute("INSERT INTO f5 VALUES(2);").await.unwrap();
8829            conn.execute("INSERT INTO f5 VALUES(3);").await.unwrap();
8830            conn.execute("INSERT INTO f5 VALUES(4);").await.unwrap();
8831            // Two aggregates with different filters in the same query.
8832            let row = conn
8833            .query_row(
8834                "SELECT COUNT(*) FILTER (WHERE x <= 2), COUNT(*) FILTER (WHERE x >= 3) FROM f5;",
8835            )
8836            .await
8837            .unwrap();
8838            let vals = row_values(&row);
8839            assert_eq!(vals[0], SqliteValue::Integer(2)); // x<=2: 1,2
8840            assert_eq!(vals[1], SqliteValue::Integer(2)); // x>=3: 3,4
8841        });
8842    }
8843
8844    #[test]
8845    fn parity_count_filter_vs_no_filter() {
8846        asupersync::test_utils::run_test(|| async {
8847            let conn = Connection::open(":memory:").await.unwrap();
8848            conn.execute("CREATE TABLE f6(x INTEGER);").await.unwrap();
8849            conn.execute("INSERT INTO f6 VALUES(1);").await.unwrap();
8850            conn.execute("INSERT INTO f6 VALUES(2);").await.unwrap();
8851            conn.execute("INSERT INTO f6 VALUES(3);").await.unwrap();
8852            // Mix of filtered and unfiltered aggregates.
8853            let row = conn
8854                .query_row("SELECT COUNT(*), COUNT(*) FILTER (WHERE x > 1) FROM f6;")
8855                .await
8856                .unwrap();
8857            let vals = row_values(&row);
8858            assert_eq!(vals[0], SqliteValue::Integer(3)); // all rows
8859            assert_eq!(vals[1], SqliteValue::Integer(2)); // only x>1: 2,3
8860        });
8861    }
8862
8863    // ── CASE WHEN parity tests ───────────────────────────────────────────
8864
8865    #[test]
8866    fn parity_case_simple() {
8867        asupersync::test_utils::run_test(|| async {
8868            let conn = Connection::open(":memory:").await.unwrap();
8869            let row = conn
8870                .query_row("SELECT CASE 2 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'other' END;")
8871                .await
8872                .unwrap();
8873            assert_eq!(row_values(&row)[0], SqliteValue::Text("two".into()));
8874        });
8875    }
8876
8877    #[test]
8878    fn parity_case_searched() {
8879        asupersync::test_utils::run_test(|| async {
8880            let conn = Connection::open(":memory:").await.unwrap();
8881            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8882            conn.execute("INSERT INTO t VALUES(15);").await.unwrap();
8883            let row = conn
8884            .query_row(
8885                "SELECT CASE WHEN x < 10 THEN 'low' WHEN x < 20 THEN 'mid' ELSE 'high' END FROM t;",
8886            )
8887            .await
8888            .unwrap();
8889            assert_eq!(row_values(&row)[0], SqliteValue::Text("mid".into()));
8890        });
8891    }
8892
8893    #[test]
8894    fn parity_case_no_else_returns_null() {
8895        asupersync::test_utils::run_test(|| async {
8896            let conn = Connection::open(":memory:").await.unwrap();
8897            let row = conn
8898                .query_row("SELECT CASE 5 WHEN 1 THEN 'one' WHEN 2 THEN 'two' END;")
8899                .await
8900                .unwrap();
8901            assert_eq!(row_values(&row)[0], SqliteValue::Null);
8902        });
8903    }
8904
8905    // ── COALESCE / NULLIF / IIF parity tests ─────────────────────────────
8906
8907    #[test]
8908    fn parity_coalesce_basic() {
8909        asupersync::test_utils::run_test(|| async {
8910            let conn = Connection::open(":memory:").await.unwrap();
8911            let row = conn
8912                .query_row("SELECT COALESCE(NULL, NULL, 42, 10);")
8913                .await
8914                .unwrap();
8915            assert_eq!(row_values(&row)[0], SqliteValue::Integer(42));
8916        });
8917    }
8918
8919    #[test]
8920    fn parity_coalesce_all_null() {
8921        asupersync::test_utils::run_test(|| async {
8922            let conn = Connection::open(":memory:").await.unwrap();
8923            let row = conn
8924                .query_row("SELECT COALESCE(NULL, NULL);")
8925                .await
8926                .unwrap();
8927            assert_eq!(row_values(&row)[0], SqliteValue::Null);
8928        });
8929    }
8930
8931    #[test]
8932    fn parity_nullif_equal() {
8933        asupersync::test_utils::run_test(|| async {
8934            let conn = Connection::open(":memory:").await.unwrap();
8935            let row = conn.query_row("SELECT NULLIF(5, 5);").await.unwrap();
8936            assert_eq!(row_values(&row)[0], SqliteValue::Null);
8937        });
8938    }
8939
8940    #[test]
8941    fn parity_nullif_not_equal() {
8942        asupersync::test_utils::run_test(|| async {
8943            let conn = Connection::open(":memory:").await.unwrap();
8944            let row = conn.query_row("SELECT NULLIF(5, 3);").await.unwrap();
8945            assert_eq!(row_values(&row)[0], SqliteValue::Integer(5));
8946        });
8947    }
8948
8949    #[test]
8950    fn parity_iif_true() {
8951        asupersync::test_utils::run_test(|| async {
8952            let conn = Connection::open(":memory:").await.unwrap();
8953            let row = conn
8954                .query_row("SELECT IIF(1=1, 'yes', 'no');")
8955                .await
8956                .unwrap();
8957            assert_eq!(row_values(&row)[0], SqliteValue::Text("yes".into()));
8958        });
8959    }
8960
8961    #[test]
8962    fn parity_iif_false() {
8963        asupersync::test_utils::run_test(|| async {
8964            let conn = Connection::open(":memory:").await.unwrap();
8965            let row = conn
8966                .query_row("SELECT IIF(1=0, 'yes', 'no');")
8967                .await
8968                .unwrap();
8969            assert_eq!(row_values(&row)[0], SqliteValue::Text("no".into()));
8970        });
8971    }
8972
8973    // ── BETWEEN parity tests ─────────────────────────────────────────────
8974
8975    #[test]
8976    fn parity_between_basic() {
8977        asupersync::test_utils::run_test(|| async {
8978            let conn = Connection::open(":memory:").await.unwrap();
8979            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8980            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
8981            conn.execute("INSERT INTO t VALUES(5);").await.unwrap();
8982            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
8983            conn.execute("INSERT INTO t VALUES(15);").await.unwrap();
8984            let rows = conn
8985                .query("SELECT x FROM t WHERE x BETWEEN 5 AND 10 ORDER BY x;")
8986                .await
8987                .unwrap();
8988            assert_eq!(rows.len(), 2);
8989            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(5));
8990            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(10));
8991        });
8992    }
8993
8994    #[test]
8995    fn parity_not_between() {
8996        asupersync::test_utils::run_test(|| async {
8997            let conn = Connection::open(":memory:").await.unwrap();
8998            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
8999            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
9000            conn.execute("INSERT INTO t VALUES(5);").await.unwrap();
9001            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
9002            let rows = conn
9003                .query("SELECT x FROM t WHERE x NOT BETWEEN 3 AND 7 ORDER BY x;")
9004                .await
9005                .unwrap();
9006            assert_eq!(rows.len(), 2);
9007            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
9008            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(10));
9009        });
9010    }
9011
9012    // ── LIKE parity tests ────────────────────────────────────────────────
9013
9014    #[test]
9015    fn parity_like_percent() {
9016        asupersync::test_utils::run_test(|| async {
9017            let conn = Connection::open(":memory:").await.unwrap();
9018            conn.execute("CREATE TABLE t(name TEXT);").await.unwrap();
9019            conn.execute("INSERT INTO t VALUES('apple');")
9020                .await
9021                .unwrap();
9022            conn.execute("INSERT INTO t VALUES('banana');")
9023                .await
9024                .unwrap();
9025            conn.execute("INSERT INTO t VALUES('apricot');")
9026                .await
9027                .unwrap();
9028            let rows = conn
9029                .query("SELECT name FROM t WHERE name LIKE 'ap%' ORDER BY name;")
9030                .await
9031                .unwrap();
9032            assert_eq!(rows.len(), 2);
9033            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("apple".into()));
9034            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("apricot".into()));
9035        });
9036    }
9037
9038    #[test]
9039    fn parity_like_underscore() {
9040        asupersync::test_utils::run_test(|| async {
9041            let conn = Connection::open(":memory:").await.unwrap();
9042            conn.execute("CREATE TABLE t(code TEXT);").await.unwrap();
9043            conn.execute("INSERT INTO t VALUES('a1');").await.unwrap();
9044            conn.execute("INSERT INTO t VALUES('b2');").await.unwrap();
9045            conn.execute("INSERT INTO t VALUES('abc');").await.unwrap();
9046            let rows = conn
9047                .query("SELECT code FROM t WHERE code LIKE '_2';")
9048                .await
9049                .unwrap();
9050            assert_eq!(rows.len(), 1);
9051            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("b2".into()));
9052        });
9053    }
9054
9055    #[test]
9056    fn parity_not_like() {
9057        asupersync::test_utils::run_test(|| async {
9058            let conn = Connection::open(":memory:").await.unwrap();
9059            conn.execute("CREATE TABLE t(name TEXT);").await.unwrap();
9060            conn.execute("INSERT INTO t VALUES('cat');").await.unwrap();
9061            conn.execute("INSERT INTO t VALUES('dog');").await.unwrap();
9062            conn.execute("INSERT INTO t VALUES('car');").await.unwrap();
9063            let rows = conn
9064                .query("SELECT name FROM t WHERE name NOT LIKE 'ca%' ORDER BY name;")
9065                .await
9066                .unwrap();
9067            assert_eq!(rows.len(), 1);
9068            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("dog".into()));
9069        });
9070    }
9071
9072    // ── JOIN parity tests ────────────────────────────────────────────────
9073
9074    #[test]
9075    fn parity_inner_join() {
9076        asupersync::test_utils::run_test(|| async {
9077            let conn = Connection::open(":memory:").await.unwrap();
9078            conn.execute("CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT);")
9079                .await
9080                .unwrap();
9081            conn.execute(
9082                "CREATE TABLE orders(id INTEGER PRIMARY KEY, user_id INTEGER, item TEXT);",
9083            )
9084            .await
9085            .unwrap();
9086            conn.execute("INSERT INTO users VALUES(1, 'Alice');")
9087                .await
9088                .unwrap();
9089            conn.execute("INSERT INTO users VALUES(2, 'Bob');")
9090                .await
9091                .unwrap();
9092            conn.execute("INSERT INTO orders VALUES(1, 1, 'Book');")
9093                .await
9094                .unwrap();
9095            conn.execute("INSERT INTO orders VALUES(2, 1, 'Pen');")
9096                .await
9097                .unwrap();
9098            conn.execute("INSERT INTO orders VALUES(3, 2, 'Notebook');")
9099                .await
9100                .unwrap();
9101            let rows = conn
9102            .query(
9103                "SELECT users.name, orders.item FROM users JOIN orders ON users.id = orders.user_id;",
9104            )
9105            .await
9106            .unwrap();
9107            assert_eq!(rows.len(), 3);
9108            // Verify all expected name-item pairs (order may vary).
9109            let mut pairs: Vec<(String, String)> = rows
9110                .iter()
9111                .map(|r| {
9112                    let v = row_values(r);
9113                    let name = match &v[0] {
9114                        SqliteValue::Text(s) => s.to_string(),
9115                        other => panic!("expected Text, got {other:?}"),
9116                    };
9117                    let item = match &v[1] {
9118                        SqliteValue::Text(s) => s.to_string(),
9119                        other => panic!("expected Text, got {other:?}"),
9120                    };
9121                    (name, item)
9122                })
9123                .collect();
9124            pairs.sort();
9125            assert_eq!(
9126                pairs,
9127                vec![
9128                    ("Alice".into(), "Book".into()),
9129                    ("Alice".into(), "Pen".into()),
9130                    ("Bob".into(), "Notebook".into()),
9131                ]
9132            );
9133        });
9134    }
9135
9136    #[test]
9137    fn parity_left_join_with_nulls() {
9138        asupersync::test_utils::run_test(|| async {
9139            let conn = Connection::open(":memory:").await.unwrap();
9140            conn.execute("CREATE TABLE a(id INTEGER PRIMARY KEY, val TEXT);")
9141                .await
9142                .unwrap();
9143            conn.execute("CREATE TABLE b(id INTEGER PRIMARY KEY, a_id INTEGER, info TEXT);")
9144                .await
9145                .unwrap();
9146            conn.execute("INSERT INTO a VALUES(1, 'x');").await.unwrap();
9147            conn.execute("INSERT INTO a VALUES(2, 'y');").await.unwrap();
9148            conn.execute("INSERT INTO b VALUES(1, 1, 'linked');")
9149                .await
9150                .unwrap();
9151            let rows = conn
9152                .query("SELECT a.val, b.info FROM a LEFT JOIN b ON a.id = b.a_id;")
9153                .await
9154                .unwrap();
9155            assert_eq!(rows.len(), 2);
9156            // Collect results (order may vary).
9157            let mut results: Vec<(String, Option<String>)> = rows
9158                .iter()
9159                .map(|r| {
9160                    let v = row_values(r);
9161                    let val = match &v[0] {
9162                        SqliteValue::Text(s) => s.to_string(),
9163                        other => panic!("expected Text, got {other:?}"),
9164                    };
9165                    let info = match &v[1] {
9166                        SqliteValue::Text(s) => Some(s.to_string()),
9167                        SqliteValue::Null => None,
9168                        other => panic!("expected Text or Null, got {other:?}"),
9169                    };
9170                    (val, info)
9171                })
9172                .collect();
9173            results.sort();
9174            assert_eq!(
9175                results,
9176                vec![("x".into(), Some("linked".into())), ("y".into(), None),]
9177            );
9178        });
9179    }
9180
9181    #[test]
9182    fn parity_cross_join() {
9183        asupersync::test_utils::run_test(|| async {
9184            let conn = Connection::open(":memory:").await.unwrap();
9185            conn.execute("CREATE TABLE a(x INTEGER);").await.unwrap();
9186            conn.execute("CREATE TABLE b(y INTEGER);").await.unwrap();
9187            conn.execute("INSERT INTO a VALUES(1);").await.unwrap();
9188            conn.execute("INSERT INTO a VALUES(2);").await.unwrap();
9189            conn.execute("INSERT INTO b VALUES(10);").await.unwrap();
9190            conn.execute("INSERT INTO b VALUES(20);").await.unwrap();
9191            let rows = conn
9192                .query("SELECT x, y FROM a, b ORDER BY x, y;")
9193                .await
9194                .unwrap();
9195            // Cross product: 2*2 = 4 rows
9196            assert_eq!(rows.len(), 4);
9197            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
9198            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(10));
9199            assert_eq!(row_values(&rows[3])[0], SqliteValue::Integer(2));
9200            assert_eq!(row_values(&rows[3])[1], SqliteValue::Integer(20));
9201        });
9202    }
9203
9204    // ── UNION / set operations parity tests ──────────────────────────────
9205
9206    #[test]
9207    fn parity_union_removes_duplicates() {
9208        asupersync::test_utils::run_test(|| async {
9209            let conn = Connection::open(":memory:").await.unwrap();
9210            conn.execute("CREATE TABLE a(x INTEGER);").await.unwrap();
9211            conn.execute("CREATE TABLE b(x INTEGER);").await.unwrap();
9212            conn.execute("INSERT INTO a VALUES(1);").await.unwrap();
9213            conn.execute("INSERT INTO a VALUES(2);").await.unwrap();
9214            conn.execute("INSERT INTO b VALUES(2);").await.unwrap();
9215            conn.execute("INSERT INTO b VALUES(3);").await.unwrap();
9216            let rows = conn
9217                .query("SELECT x FROM a UNION SELECT x FROM b ORDER BY x;")
9218                .await
9219                .unwrap();
9220            assert_eq!(rows.len(), 3);
9221            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
9222            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(2));
9223            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(3));
9224        });
9225    }
9226
9227    #[test]
9228    fn parity_union_all_keeps_duplicates() {
9229        asupersync::test_utils::run_test(|| async {
9230            let conn = Connection::open(":memory:").await.unwrap();
9231            conn.execute("CREATE TABLE a(x INTEGER);").await.unwrap();
9232            conn.execute("CREATE TABLE b(x INTEGER);").await.unwrap();
9233            conn.execute("INSERT INTO a VALUES(1);").await.unwrap();
9234            conn.execute("INSERT INTO a VALUES(2);").await.unwrap();
9235            conn.execute("INSERT INTO b VALUES(2);").await.unwrap();
9236            conn.execute("INSERT INTO b VALUES(3);").await.unwrap();
9237            let rows = conn
9238                .query("SELECT x FROM a UNION ALL SELECT x FROM b ORDER BY x;")
9239                .await
9240                .unwrap();
9241            assert_eq!(rows.len(), 4);
9242            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
9243            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(2));
9244            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(2));
9245            assert_eq!(row_values(&rows[3])[0], SqliteValue::Integer(3));
9246        });
9247    }
9248
9249    // ── UPDATE / DELETE parity tests ─────────────────────────────────────
9250
9251    #[test]
9252    fn parity_update_multiple_columns() {
9253        asupersync::test_utils::run_test(|| async {
9254            let conn = Connection::open(":memory:").await.unwrap();
9255            conn.execute("CREATE TABLE t(a INTEGER, b TEXT, c REAL);")
9256                .await
9257                .unwrap();
9258            conn.execute("INSERT INTO t VALUES(1, 'old', 1.0);")
9259                .await
9260                .unwrap();
9261            conn.execute("UPDATE t SET b = 'new', c = 2.5 WHERE a = 1;")
9262                .await
9263                .unwrap();
9264            let row = conn.query_row("SELECT a, b, c FROM t;").await.unwrap();
9265            let vals = row_values(&row);
9266            assert_eq!(vals[0], SqliteValue::Integer(1));
9267            assert_eq!(vals[1], SqliteValue::Text("new".into()));
9268            assert_eq!(vals[2], SqliteValue::Float(2.5));
9269        });
9270    }
9271
9272    #[test]
9273    fn parity_delete_with_where() {
9274        asupersync::test_utils::run_test(|| async {
9275            let conn = Connection::open(":memory:").await.unwrap();
9276            conn.execute("CREATE TABLE t(id INTEGER PRIMARY KEY, val TEXT);")
9277                .await
9278                .unwrap();
9279            conn.execute("INSERT INTO t VALUES(1, 'a');").await.unwrap();
9280            conn.execute("INSERT INTO t VALUES(2, 'b');").await.unwrap();
9281            conn.execute("INSERT INTO t VALUES(3, 'c');").await.unwrap();
9282            conn.execute("DELETE FROM t WHERE id > 1;").await.unwrap();
9283            let rows = conn.query("SELECT val FROM t;").await.unwrap();
9284            assert_eq!(rows.len(), 1);
9285            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("a".into()));
9286        });
9287    }
9288
9289    // ── HAVING parity tests ──────────────────────────────────────────────
9290
9291    #[test]
9292    fn parity_having_basic() {
9293        asupersync::test_utils::run_test(|| async {
9294            let conn = Connection::open(":memory:").await.unwrap();
9295            conn.execute("CREATE TABLE sales(product TEXT, amount INTEGER);")
9296                .await
9297                .unwrap();
9298            conn.execute("INSERT INTO sales VALUES('A', 10);")
9299                .await
9300                .unwrap();
9301            conn.execute("INSERT INTO sales VALUES('A', 20);")
9302                .await
9303                .unwrap();
9304            conn.execute("INSERT INTO sales VALUES('B', 5);")
9305                .await
9306                .unwrap();
9307            conn.execute("INSERT INTO sales VALUES('C', 30);")
9308                .await
9309                .unwrap();
9310            conn.execute("INSERT INTO sales VALUES('C', 40);")
9311                .await
9312                .unwrap();
9313            let rows = conn
9314            .query(
9315                "SELECT product, SUM(amount) FROM sales GROUP BY product HAVING SUM(amount) > 15 ORDER BY product;",
9316            )
9317            .await
9318            .unwrap();
9319            assert_eq!(rows.len(), 2);
9320            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("A".into()));
9321            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(30));
9322            assert_eq!(row_values(&rows[1])[0], SqliteValue::Text("C".into()));
9323            assert_eq!(row_values(&rows[1])[1], SqliteValue::Integer(70));
9324        });
9325    }
9326
9327    // ── IN operator parity tests ─────────────────────────────────────────
9328
9329    #[test]
9330    fn parity_in_values_list() {
9331        asupersync::test_utils::run_test(|| async {
9332            let conn = Connection::open(":memory:").await.unwrap();
9333            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9334            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
9335            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
9336            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
9337            conn.execute("INSERT INTO t VALUES(4);").await.unwrap();
9338            let rows = conn
9339                .query("SELECT x FROM t WHERE x IN (1, 3) ORDER BY x;")
9340                .await
9341                .unwrap();
9342            assert_eq!(rows.len(), 2);
9343            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
9344            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(3));
9345        });
9346    }
9347
9348    #[test]
9349    fn parity_not_in_values_list() {
9350        asupersync::test_utils::run_test(|| async {
9351            let conn = Connection::open(":memory:").await.unwrap();
9352            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9353            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
9354            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
9355            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
9356            let rows = conn
9357                .query("SELECT x FROM t WHERE x NOT IN (1, 3) ORDER BY x;")
9358                .await
9359                .unwrap();
9360            assert_eq!(rows.len(), 1);
9361            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
9362        });
9363    }
9364
9365    // ── Expression tests ─────────────────────────────────────────────────
9366
9367    #[test]
9368    fn parity_unary_minus() {
9369        asupersync::test_utils::run_test(|| async {
9370            let conn = Connection::open(":memory:").await.unwrap();
9371            let row = conn.query_row("SELECT -42;").await.unwrap();
9372            assert_eq!(row_values(&row)[0], SqliteValue::Integer(-42));
9373        });
9374    }
9375
9376    #[test]
9377    fn parity_string_concat_operator() {
9378        asupersync::test_utils::run_test(|| async {
9379            let conn = Connection::open(":memory:").await.unwrap();
9380            let row = conn
9381                .query_row("SELECT 'hello' || ' ' || 'world';")
9382                .await
9383                .unwrap();
9384            assert_eq!(row_values(&row)[0], SqliteValue::Text("hello world".into()));
9385        });
9386    }
9387
9388    #[test]
9389    fn parity_typeof_function() {
9390        asupersync::test_utils::run_test(|| async {
9391            let conn = Connection::open(":memory:").await.unwrap();
9392            let row = conn.query_row("SELECT typeof(42);").await.unwrap();
9393            assert_eq!(row_values(&row)[0], SqliteValue::Text("integer".into()));
9394            let row = conn.query_row("SELECT typeof(3.14);").await.unwrap();
9395            assert_eq!(row_values(&row)[0], SqliteValue::Text("real".into()));
9396            let row = conn.query_row("SELECT typeof('hi');").await.unwrap();
9397            assert_eq!(row_values(&row)[0], SqliteValue::Text("text".into()));
9398            let row = conn.query_row("SELECT typeof(NULL);").await.unwrap();
9399            assert_eq!(row_values(&row)[0], SqliteValue::Text("null".into()));
9400        });
9401    }
9402
9403    #[test]
9404    fn parity_abs_function() {
9405        asupersync::test_utils::run_test(|| async {
9406            let conn = Connection::open(":memory:").await.unwrap();
9407            let row = conn.query_row("SELECT ABS(-10);").await.unwrap();
9408            assert_eq!(row_values(&row)[0], SqliteValue::Integer(10));
9409            let row = conn.query_row("SELECT ABS(10);").await.unwrap();
9410            assert_eq!(row_values(&row)[0], SqliteValue::Integer(10));
9411        });
9412    }
9413
9414    #[test]
9415    fn parity_upper_lower_functions() {
9416        asupersync::test_utils::run_test(|| async {
9417            let conn = Connection::open(":memory:").await.unwrap();
9418            let row = conn.query_row("SELECT UPPER('hello');").await.unwrap();
9419            assert_eq!(row_values(&row)[0], SqliteValue::Text("HELLO".into()));
9420            let row = conn.query_row("SELECT LOWER('WORLD');").await.unwrap();
9421            assert_eq!(row_values(&row)[0], SqliteValue::Text("world".into()));
9422        });
9423    }
9424
9425    #[test]
9426    fn parity_length_function() {
9427        asupersync::test_utils::run_test(|| async {
9428            let conn = Connection::open(":memory:").await.unwrap();
9429            let row = conn.query_row("SELECT LENGTH('hello');").await.unwrap();
9430            assert_eq!(row_values(&row)[0], SqliteValue::Integer(5));
9431            let row = conn.query_row("SELECT LENGTH('');").await.unwrap();
9432            assert_eq!(row_values(&row)[0], SqliteValue::Integer(0));
9433        });
9434    }
9435
9436    #[test]
9437    fn parity_min_max_aggregate() {
9438        asupersync::test_utils::run_test(|| async {
9439            let conn = Connection::open(":memory:").await.unwrap();
9440            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9441            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
9442            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
9443            conn.execute("INSERT INTO t VALUES(4);").await.unwrap();
9444            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
9445            conn.execute("INSERT INTO t VALUES(5);").await.unwrap();
9446            let row = conn
9447                .query_row("SELECT MIN(x), MAX(x) FROM t;")
9448                .await
9449                .unwrap();
9450            let vals = row_values(&row);
9451            assert_eq!(vals[0], SqliteValue::Integer(1));
9452            assert_eq!(vals[1], SqliteValue::Integer(5));
9453        });
9454    }
9455
9456    #[test]
9457    fn parity_avg_aggregate() {
9458        asupersync::test_utils::run_test(|| async {
9459            let conn = Connection::open(":memory:").await.unwrap();
9460            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9461            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
9462            conn.execute("INSERT INTO t VALUES(20);").await.unwrap();
9463            conn.execute("INSERT INTO t VALUES(30);").await.unwrap();
9464            let row = conn.query_row("SELECT AVG(x) FROM t;").await.unwrap();
9465            assert_eq!(row_values(&row)[0], SqliteValue::Float(20.0));
9466        });
9467    }
9468
9469    // ── UPDATE with subquery in WHERE ────────────────────────────────────
9470
9471    #[test]
9472    fn parity_update_where_in_subquery() {
9473        asupersync::test_utils::run_test(|| async {
9474            let conn = Connection::open(":memory:").await.unwrap();
9475            conn.execute("CREATE TABLE items(id INTEGER PRIMARY KEY, name TEXT, price INTEGER);")
9476                .await
9477                .unwrap();
9478            conn.execute("INSERT INTO items VALUES(1, 'apple', 10);")
9479                .await
9480                .unwrap();
9481            conn.execute("INSERT INTO items VALUES(2, 'banana', 20);")
9482                .await
9483                .unwrap();
9484            conn.execute("INSERT INTO items VALUES(3, 'cherry', 30);")
9485                .await
9486                .unwrap();
9487            conn.execute("CREATE TABLE expensive(id INTEGER);")
9488                .await
9489                .unwrap();
9490            conn.execute("INSERT INTO expensive VALUES(2);")
9491                .await
9492                .unwrap();
9493            conn.execute("INSERT INTO expensive VALUES(3);")
9494                .await
9495                .unwrap();
9496            // UPDATE items SET price = price * 2 WHERE id IN (SELECT id FROM expensive);
9497            conn.execute(
9498                "UPDATE items SET price = price * 2 WHERE id IN (SELECT id FROM expensive);",
9499            )
9500            .await
9501            .unwrap();
9502            let rows = conn
9503                .query("SELECT id, price FROM items ORDER BY id;")
9504                .await
9505                .unwrap();
9506            let results: Vec<(i64, i64)> = rows
9507                .iter()
9508                .map(|r| {
9509                    let vals = row_values(r);
9510                    (vals[0].to_integer(), vals[1].to_integer())
9511                })
9512                .collect();
9513            assert_eq!(results, vec![(1, 10), (2, 40), (3, 60)]);
9514        });
9515    }
9516
9517    // ── DELETE with subquery in WHERE ────────────────────────────────────
9518
9519    #[test]
9520    fn parity_delete_where_in_subquery() {
9521        asupersync::test_utils::run_test(|| async {
9522            let conn = Connection::open(":memory:").await.unwrap();
9523            conn.execute("CREATE TABLE data(id INTEGER, val TEXT);")
9524                .await
9525                .unwrap();
9526            conn.execute("INSERT INTO data VALUES(1, 'a');")
9527                .await
9528                .unwrap();
9529            conn.execute("INSERT INTO data VALUES(2, 'b');")
9530                .await
9531                .unwrap();
9532            conn.execute("INSERT INTO data VALUES(3, 'c');")
9533                .await
9534                .unwrap();
9535            conn.execute("CREATE TABLE to_remove(id INTEGER);")
9536                .await
9537                .unwrap();
9538            conn.execute("INSERT INTO to_remove VALUES(1);")
9539                .await
9540                .unwrap();
9541            conn.execute("INSERT INTO to_remove VALUES(3);")
9542                .await
9543                .unwrap();
9544            // DELETE FROM data WHERE id IN (SELECT id FROM to_remove);
9545            conn.execute("DELETE FROM data WHERE id IN (SELECT id FROM to_remove);")
9546                .await
9547                .unwrap();
9548            let rows = conn
9549                .query("SELECT id, val FROM data ORDER BY id;")
9550                .await
9551                .unwrap();
9552            let results: Vec<(i64, String)> = rows
9553                .iter()
9554                .map(|r| {
9555                    let vals = row_values(r);
9556                    (
9557                        vals[0].to_integer(),
9558                        match &vals[1] {
9559                            SqliteValue::Text(s) => s.to_string(),
9560                            _ => panic!("expected text"),
9561                        },
9562                    )
9563                })
9564                .collect();
9565            assert_eq!(results, vec![(2, "b".into())]);
9566        });
9567    }
9568
9569    // ── DateTime function probes ─────────────────────────────────────────
9570
9571    #[test]
9572    fn parity_datetime_date_function() {
9573        asupersync::test_utils::run_test(|| async {
9574            let conn = Connection::open(":memory:").await.unwrap();
9575            let row = conn
9576                .query_row("SELECT date('2023-06-15 14:30:00');")
9577                .await
9578                .unwrap();
9579            assert_eq!(row_values(&row)[0], SqliteValue::Text("2023-06-15".into()));
9580        });
9581    }
9582
9583    #[test]
9584    fn parity_datetime_time_function() {
9585        asupersync::test_utils::run_test(|| async {
9586            let conn = Connection::open(":memory:").await.unwrap();
9587            let row = conn
9588                .query_row("SELECT time('2023-06-15 14:30:45');")
9589                .await
9590                .unwrap();
9591            assert_eq!(row_values(&row)[0], SqliteValue::Text("14:30:45".into()));
9592        });
9593    }
9594
9595    #[test]
9596    fn parity_datetime_strftime() {
9597        asupersync::test_utils::run_test(|| async {
9598            let conn = Connection::open(":memory:").await.unwrap();
9599            let row = conn
9600                .query_row("SELECT strftime('%Y', '2023-06-15');")
9601                .await
9602                .unwrap();
9603            assert_eq!(row_values(&row)[0], SqliteValue::Text("2023".into()));
9604        });
9605    }
9606
9607    // ── TOTAL aggregate ──────────────────────────────────────────────────
9608
9609    #[test]
9610    fn parity_total_aggregate() {
9611        asupersync::test_utils::run_test(|| async {
9612            // TOTAL() returns 0.0 for empty set, unlike SUM() which returns NULL.
9613            let conn = Connection::open(":memory:").await.unwrap();
9614            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9615            let row = conn.query_row("SELECT TOTAL(x) FROM t;").await.unwrap();
9616            assert_eq!(row_values(&row)[0], SqliteValue::Float(0.0));
9617        });
9618    }
9619
9620    #[test]
9621    fn parity_total_aggregate_with_values() {
9622        asupersync::test_utils::run_test(|| async {
9623            let conn = Connection::open(":memory:").await.unwrap();
9624            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
9625            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
9626            conn.execute("INSERT INTO t VALUES(20);").await.unwrap();
9627            let row = conn.query_row("SELECT TOTAL(x) FROM t;").await.unwrap();
9628            assert_eq!(row_values(&row)[0], SqliteValue::Float(30.0));
9629        });
9630    }
9631
9632    // ── GLOB operator ────────────────────────────────────────────────────
9633
9634    #[test]
9635    fn parity_glob_operator() {
9636        asupersync::test_utils::run_test(|| async {
9637            let conn = Connection::open(":memory:").await.unwrap();
9638            conn.execute("CREATE TABLE files(name TEXT);")
9639                .await
9640                .unwrap();
9641            conn.execute("INSERT INTO files VALUES('readme.txt');")
9642                .await
9643                .unwrap();
9644            conn.execute("INSERT INTO files VALUES('main.rs');")
9645                .await
9646                .unwrap();
9647            conn.execute("INSERT INTO files VALUES('test.txt');")
9648                .await
9649                .unwrap();
9650            let rows = conn
9651                .query("SELECT name FROM files WHERE name GLOB '*.txt' ORDER BY name;")
9652                .await
9653                .unwrap();
9654            let results: Vec<String> = rows
9655                .iter()
9656                .map(|r| match &row_values(r)[0] {
9657                    SqliteValue::Text(s) => s.to_string(),
9658                    _ => panic!("expected text"),
9659                })
9660                .collect();
9661            assert_eq!(results, vec!["readme.txt", "test.txt"]);
9662        });
9663    }
9664
9665    // ── REPLACE function ─────────────────────────────────────────────────
9666
9667    #[test]
9668    fn parity_replace_function() {
9669        asupersync::test_utils::run_test(|| async {
9670            let conn = Connection::open(":memory:").await.unwrap();
9671            let row = conn
9672                .query_row("SELECT replace('hello world', 'world', 'rust');")
9673                .await
9674                .unwrap();
9675            assert_eq!(row_values(&row)[0], SqliteValue::Text("hello rust".into()));
9676        });
9677    }
9678
9679    // ── ZEROBLOB function ────────────────────────────────────────────────
9680
9681    #[test]
9682    fn parity_zeroblob_function() {
9683        asupersync::test_utils::run_test(|| async {
9684            let conn = Connection::open(":memory:").await.unwrap();
9685            let row = conn
9686                .query_row("SELECT typeof(zeroblob(4)), length(zeroblob(4));")
9687                .await
9688                .unwrap();
9689            let vals = row_values(&row);
9690            assert_eq!(vals[0], SqliteValue::Text("blob".into()));
9691            assert_eq!(vals[1], SqliteValue::Integer(4));
9692        });
9693    }
9694
9695    // ── UNICODE / CHAR functions ─────────────────────────────────────────
9696
9697    #[test]
9698    fn parity_unicode_function() {
9699        asupersync::test_utils::run_test(|| async {
9700            let conn = Connection::open(":memory:").await.unwrap();
9701            let row = conn.query_row("SELECT unicode('A');").await.unwrap();
9702            assert_eq!(row_values(&row)[0], SqliteValue::Integer(65));
9703        });
9704    }
9705
9706    #[test]
9707    fn parity_char_function() {
9708        asupersync::test_utils::run_test(|| async {
9709            let conn = Connection::open(":memory:").await.unwrap();
9710            let row = conn.query_row("SELECT char(65, 66, 67);").await.unwrap();
9711            assert_eq!(row_values(&row)[0], SqliteValue::Text("ABC".into()));
9712        });
9713    }
9714
9715    // ── INSTR with multi-byte ────────────────────────────────────────────
9716
9717    #[test]
9718    fn parity_instr_multi_occurrence() {
9719        asupersync::test_utils::run_test(|| async {
9720            let conn = Connection::open(":memory:").await.unwrap();
9721            // INSTR returns position of FIRST occurrence (1-based).
9722            let row = conn
9723                .query_row("SELECT instr('abcabc', 'bc');")
9724                .await
9725                .unwrap();
9726            assert_eq!(row_values(&row)[0], SqliteValue::Integer(2));
9727        });
9728    }
9729
9730    // ── PRINTF / FORMAT function ─────────────────────────────────────────
9731
9732    #[test]
9733    fn parity_printf_function() {
9734        asupersync::test_utils::run_test(|| async {
9735            let conn = Connection::open(":memory:").await.unwrap();
9736            let row = conn
9737                .query_row("SELECT printf('%d + %d = %d', 1, 2, 3);")
9738                .await
9739                .unwrap();
9740            assert_eq!(row_values(&row)[0], SqliteValue::Text("1 + 2 = 3".into()));
9741        });
9742    }
9743
9744    // ── Window function probe ────────────────────────────────────────────
9745
9746    #[test]
9747    fn parity_row_number_window() {
9748        asupersync::test_utils::run_test(|| async {
9749            let conn = Connection::open(":memory:").await.unwrap();
9750            conn.execute("CREATE TABLE w(name TEXT, val INTEGER);")
9751                .await
9752                .unwrap();
9753            conn.execute("INSERT INTO w VALUES('a', 10);")
9754                .await
9755                .unwrap();
9756            conn.execute("INSERT INTO w VALUES('b', 20);")
9757                .await
9758                .unwrap();
9759            conn.execute("INSERT INTO w VALUES('c', 30);")
9760                .await
9761                .unwrap();
9762            let rows = conn
9763                .query("SELECT name, ROW_NUMBER() OVER (ORDER BY val) FROM w;")
9764                .await
9765                .unwrap();
9766            let results: Vec<(String, i64)> = rows
9767                .iter()
9768                .map(|r| {
9769                    let vals = row_values(r);
9770                    let name = match &vals[0] {
9771                        SqliteValue::Text(s) => s.to_string(),
9772                        _ => panic!("expected text"),
9773                    };
9774                    (name, vals[1].to_integer())
9775                })
9776                .collect();
9777            assert_eq!(
9778                results,
9779                vec![("a".into(), 1), ("b".into(), 2), ("c".into(), 3),]
9780            );
9781        });
9782    }
9783
9784    #[test]
9785    fn window_row_number_partition_by() {
9786        asupersync::test_utils::run_test(|| async {
9787            let conn = Connection::open(":memory:").await.unwrap();
9788            conn.execute("CREATE TABLE wp(dept TEXT, name TEXT, val INTEGER);")
9789                .await
9790                .unwrap();
9791            conn.execute("INSERT INTO wp VALUES('eng','a',10);")
9792                .await
9793                .unwrap();
9794            conn.execute("INSERT INTO wp VALUES('eng','b',20);")
9795                .await
9796                .unwrap();
9797            conn.execute("INSERT INTO wp VALUES('sales','c',5);")
9798                .await
9799                .unwrap();
9800            conn.execute("INSERT INTO wp VALUES('sales','d',15);")
9801                .await
9802                .unwrap();
9803            let rows = conn
9804            .query("SELECT dept, name, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY val) FROM wp;")
9805            .await
9806            .unwrap();
9807            let results: Vec<(String, String, i64)> = rows
9808                .iter()
9809                .map(|r| {
9810                    let vals = row_values(r);
9811                    let dept = match &vals[0] {
9812                        SqliteValue::Text(s) => s.to_string(),
9813                        _ => panic!("expected text"),
9814                    };
9815                    let name = match &vals[1] {
9816                        SqliteValue::Text(s) => s.to_string(),
9817                        _ => panic!("expected text"),
9818                    };
9819                    (dept, name, vals[2].to_integer())
9820                })
9821                .collect();
9822            assert_eq!(
9823                results,
9824                vec![
9825                    ("eng".into(), "a".into(), 1),
9826                    ("eng".into(), "b".into(), 2),
9827                    ("sales".into(), "c".into(), 1),
9828                    ("sales".into(), "d".into(), 2),
9829                ]
9830            );
9831        });
9832    }
9833
9834    #[test]
9835    fn window_rank_and_dense_rank() {
9836        asupersync::test_utils::run_test(|| async {
9837            let conn = Connection::open(":memory:").await.unwrap();
9838            conn.execute("CREATE TABLE wr(name TEXT, score INTEGER);")
9839                .await
9840                .unwrap();
9841            conn.execute("INSERT INTO wr VALUES('a', 100);")
9842                .await
9843                .unwrap();
9844            conn.execute("INSERT INTO wr VALUES('b', 100);")
9845                .await
9846                .unwrap();
9847            conn.execute("INSERT INTO wr VALUES('c', 90);")
9848                .await
9849                .unwrap();
9850            conn.execute("INSERT INTO wr VALUES('d', 80);")
9851                .await
9852                .unwrap();
9853            let rows = conn
9854                .query(
9855                    "SELECT name, RANK() OVER (ORDER BY score DESC), \
9856                 DENSE_RANK() OVER (ORDER BY score DESC) FROM wr;",
9857                )
9858                .await
9859                .unwrap();
9860            let results: Vec<(String, i64, i64)> = rows
9861                .iter()
9862                .map(|r| {
9863                    let vals = row_values(r);
9864                    let name = match &vals[0] {
9865                        SqliteValue::Text(s) => s.to_string(),
9866                        _ => panic!("expected text"),
9867                    };
9868                    (name, vals[1].to_integer(), vals[2].to_integer())
9869                })
9870                .collect();
9871            // a=100, b=100 are tied at rank 1; c=90 rank 3; d=80 rank 4
9872            // dense_rank: a,b=1; c=2; d=3
9873            assert_eq!(
9874                results,
9875                vec![
9876                    ("a".into(), 1, 1),
9877                    ("b".into(), 1, 1),
9878                    ("c".into(), 3, 2),
9879                    ("d".into(), 4, 3),
9880                ]
9881            );
9882        });
9883    }
9884
9885    #[test]
9886    fn window_row_number_desc_order() {
9887        asupersync::test_utils::run_test(|| async {
9888            let conn = Connection::open(":memory:").await.unwrap();
9889            conn.execute("CREATE TABLE wd(x INTEGER);").await.unwrap();
9890            conn.execute("INSERT INTO wd VALUES(1);").await.unwrap();
9891            conn.execute("INSERT INTO wd VALUES(2);").await.unwrap();
9892            conn.execute("INSERT INTO wd VALUES(3);").await.unwrap();
9893            let rows = conn
9894                .query("SELECT x, ROW_NUMBER() OVER (ORDER BY x DESC) FROM wd;")
9895                .await
9896                .unwrap();
9897            let results: Vec<(i64, i64)> = rows
9898                .iter()
9899                .map(|r| {
9900                    let vals = row_values(r);
9901                    (vals[0].to_integer(), vals[1].to_integer())
9902                })
9903                .collect();
9904            // x=3 is first (row_number=1), x=2 second, x=1 third
9905            assert_eq!(results, vec![(3, 1), (2, 2), (1, 3)]);
9906        });
9907    }
9908
9909    #[test]
9910    fn window_multiple_window_functions() {
9911        asupersync::test_utils::run_test(|| async {
9912            let conn = Connection::open(":memory:").await.unwrap();
9913            conn.execute("CREATE TABLE wm(name TEXT, val INTEGER);")
9914                .await
9915                .unwrap();
9916            conn.execute("INSERT INTO wm VALUES('a', 10);")
9917                .await
9918                .unwrap();
9919            conn.execute("INSERT INTO wm VALUES('b', 20);")
9920                .await
9921                .unwrap();
9922            conn.execute("INSERT INTO wm VALUES('c', 30);")
9923                .await
9924                .unwrap();
9925            let rows = conn
9926                .query(
9927                    "SELECT name, ROW_NUMBER() OVER (ORDER BY val), \
9928                 DENSE_RANK() OVER (ORDER BY val) FROM wm;",
9929                )
9930                .await
9931                .unwrap();
9932            let results: Vec<(String, i64, i64)> = rows
9933                .iter()
9934                .map(|r| {
9935                    let vals = row_values(r);
9936                    let name = match &vals[0] {
9937                        SqliteValue::Text(s) => s.to_string(),
9938                        _ => panic!("expected text"),
9939                    };
9940                    (name, vals[1].to_integer(), vals[2].to_integer())
9941                })
9942                .collect();
9943            // All values distinct, so rank matches row_number
9944            assert_eq!(
9945                results,
9946                vec![("a".into(), 1, 1), ("b".into(), 2, 2), ("c".into(), 3, 3),]
9947            );
9948        });
9949    }
9950
9951    #[test]
9952    fn window_lag_negative_offset_reads_following_row() {
9953        asupersync::test_utils::run_test(|| async {
9954            let conn = Connection::open(":memory:").await.unwrap();
9955            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER);")
9956                .await
9957                .unwrap();
9958            conn.execute("INSERT INTO t1 VALUES(1,10),(2,20),(3,30);")
9959                .await
9960                .unwrap();
9961            let rows = conn
9962                .query("SELECT id, lag(val,-1,'D') OVER (ORDER BY id) FROM t1 ORDER BY id;")
9963                .await
9964                .unwrap();
9965            let results: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
9966            assert_eq!(
9967                results,
9968                vec![
9969                    vec![SqliteValue::Integer(1), SqliteValue::Integer(20)],
9970                    vec![SqliteValue::Integer(2), SqliteValue::Integer(30)],
9971                    vec![SqliteValue::Integer(3), SqliteValue::Text("D".into())],
9972                ]
9973            );
9974        });
9975    }
9976
9977    #[test]
9978    fn window_lag_lead_fractional_offset_uses_default() {
9979        asupersync::test_utils::run_test(|| async {
9980            let conn = Connection::open(":memory:").await.unwrap();
9981            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT, off REAL);")
9982                .await
9983                .unwrap();
9984            conn.execute("INSERT INTO t1 VALUES(1,'a',1.5),(2,'b',1.5),(3,'c',1.5);")
9985                .await
9986                .unwrap();
9987            let rows = conn
9988                .query(
9989                    "SELECT id, \
9990                 lag(val,off,'D') OVER (ORDER BY id), \
9991                 lead(val,off,'D') OVER (ORDER BY id) \
9992                 FROM t1 ORDER BY id;",
9993                )
9994                .await
9995                .unwrap();
9996            let results: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
9997            assert_eq!(
9998                results,
9999                vec![
10000                    vec![
10001                        SqliteValue::Integer(1),
10002                        SqliteValue::Text("D".into()),
10003                        SqliteValue::Text("D".into()),
10004                    ],
10005                    vec![
10006                        SqliteValue::Integer(2),
10007                        SqliteValue::Text("D".into()),
10008                        SqliteValue::Text("D".into()),
10009                    ],
10010                    vec![
10011                        SqliteValue::Integer(3),
10012                        SqliteValue::Text("D".into()),
10013                        SqliteValue::Text("D".into()),
10014                    ],
10015                ]
10016            );
10017        });
10018    }
10019
10020    #[test]
10021    fn window_grouped_ntile_advances_two_pass_position() {
10022        asupersync::test_utils::run_test(|| async {
10023            let conn = Connection::open(":memory:").await.unwrap();
10024            conn.execute("CREATE TABLE t(id INTEGER, val INTEGER);")
10025                .await
10026                .unwrap();
10027            conn.execute("INSERT INTO t VALUES(1,10),(2,20),(3,30);")
10028                .await
10029                .unwrap();
10030            let rows = conn
10031                .query("SELECT id, ntile(2) OVER (ORDER BY id) FROM t GROUP BY id ORDER BY id;")
10032                .await
10033                .unwrap();
10034            let results: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
10035            assert_eq!(
10036                results,
10037                vec![
10038                    vec![SqliteValue::Integer(1), SqliteValue::Integer(1)],
10039                    vec![SqliteValue::Integer(2), SqliteValue::Integer(1)],
10040                    vec![SqliteValue::Integer(3), SqliteValue::Integer(2)],
10041                ]
10042            );
10043        });
10044    }
10045
10046    #[test]
10047    fn window_nth_value_uses_current_row_n_for_full_frame() {
10048        asupersync::test_utils::run_test(|| async {
10049            let conn = Connection::open(":memory:").await.unwrap();
10050            conn.execute("CREATE TABLE t(x TEXT, n INTEGER);")
10051                .await
10052                .unwrap();
10053            conn.execute("INSERT INTO t VALUES('a',1),('b',2),('c',3);")
10054                .await
10055                .unwrap();
10056            let rows = conn
10057                .query(
10058                    "SELECT x, n, nth_value(x,n) OVER (\
10059                 ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING\
10060                 ) FROM t ORDER BY x;",
10061                )
10062                .await
10063                .unwrap();
10064            let results: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
10065            assert_eq!(
10066                results,
10067                vec![
10068                    vec![
10069                        SqliteValue::Text("a".into()),
10070                        SqliteValue::Integer(1),
10071                        SqliteValue::Text("a".into()),
10072                    ],
10073                    vec![
10074                        SqliteValue::Text("b".into()),
10075                        SqliteValue::Integer(2),
10076                        SqliteValue::Text("b".into()),
10077                    ],
10078                    vec![
10079                        SqliteValue::Text("c".into()),
10080                        SqliteValue::Integer(3),
10081                        SqliteValue::Text("c".into()),
10082                    ],
10083                ]
10084            );
10085        });
10086    }
10087
10088    #[test]
10089    fn window_nth_value_uses_current_row_n_for_sliding_frame() {
10090        asupersync::test_utils::run_test(|| async {
10091            let conn = Connection::open(":memory:").await.unwrap();
10092            conn.execute("CREATE TABLE t(x TEXT, n INTEGER);")
10093                .await
10094                .unwrap();
10095            conn.execute("INSERT INTO t VALUES('a',1),('b',2),('c',1);")
10096                .await
10097                .unwrap();
10098            let rows = conn
10099                .query(
10100                    "SELECT x, n, nth_value(x,n) OVER (\
10101                 ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW\
10102                 ) FROM t ORDER BY x;",
10103                )
10104                .await
10105                .unwrap();
10106            let results: Vec<Vec<SqliteValue>> = rows.iter().map(row_values).collect();
10107            assert_eq!(
10108                results,
10109                vec![
10110                    vec![
10111                        SqliteValue::Text("a".into()),
10112                        SqliteValue::Integer(1),
10113                        SqliteValue::Text("a".into()),
10114                    ],
10115                    vec![
10116                        SqliteValue::Text("b".into()),
10117                        SqliteValue::Integer(2),
10118                        SqliteValue::Text("b".into()),
10119                    ],
10120                    vec![
10121                        SqliteValue::Text("c".into()),
10122                        SqliteValue::Integer(1),
10123                        SqliteValue::Text("b".into()),
10124                    ],
10125                ]
10126            );
10127        });
10128    }
10129
10130    #[test]
10131    fn window_nth_value_rejects_fractional_n() {
10132        asupersync::test_utils::run_test(|| async {
10133            let conn = Connection::open(":memory:").await.unwrap();
10134            let err = conn
10135                .query(
10136                    "WITH t(x,n) AS (VALUES ('a',1.5),('b',1.5)) \
10137                 SELECT nth_value(x,n) OVER (\
10138                 ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING\
10139                 ) FROM t;",
10140                )
10141                .await
10142                .expect_err("fractional nth_value offset should fail");
10143            assert!(
10144                matches!(&err, FrankenError::FunctionError(message) if message == "second argument to nth_value must be a positive integer"),
10145                "unexpected error: {err:?}"
10146            );
10147        });
10148    }
10149
10150    #[test]
10151    fn window_nth_value_rejects_text_numeric_prefix_n() {
10152        asupersync::test_utils::run_test(|| async {
10153            let conn = Connection::open(":memory:").await.unwrap();
10154            let err = conn
10155                .query(
10156                    "WITH t(x,n) AS (VALUES ('a','2x'),('b','2x')) \
10157                 SELECT nth_value(x,n) OVER (\
10158                 ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING\
10159                 ) FROM t;",
10160                )
10161                .await
10162                .expect_err("numeric-prefix nth_value offset should fail");
10163            assert!(
10164                matches!(&err, FrankenError::FunctionError(message) if message == "second argument to nth_value must be a positive integer"),
10165                "unexpected error: {err:?}"
10166            );
10167        });
10168    }
10169
10170    #[test]
10171    fn window_nth_value_rejects_blob_integer_n() {
10172        asupersync::test_utils::run_test(|| async {
10173            let conn = Connection::open(":memory:").await.unwrap();
10174            let err = conn
10175                .query(
10176                    "WITH t(x,n) AS (VALUES ('a',x'32'),('b',x'32')) \
10177                 SELECT nth_value(x,n) OVER (\
10178                 ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING\
10179                 ) FROM t;",
10180                )
10181                .await
10182                .expect_err("blob nth_value offset should fail");
10183            assert!(
10184                matches!(&err, FrankenError::FunctionError(message) if message == "second argument to nth_value must be a positive integer"),
10185                "unexpected error: {err:?}"
10186            );
10187        });
10188    }
10189
10190    // ── CTE (WITH) probe ─────────────────────────────────────────────────
10191
10192    #[test]
10193    fn parity_cte_basic() {
10194        asupersync::test_utils::run_test(|| async {
10195            let conn = Connection::open(":memory:").await.unwrap();
10196            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10197            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10198            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10199            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10200            let rows = conn
10201                .query(
10202                    "WITH doubled AS (SELECT x * 2 AS d FROM t) SELECT d FROM doubled ORDER BY d;",
10203                )
10204                .await
10205                .unwrap();
10206            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10207            assert_eq!(results, vec![2, 4, 6]);
10208        });
10209    }
10210
10211    // ── Recursive CTE probe ─────────────────────────────────────────────
10212
10213    #[test]
10214    fn parity_recursive_cte() {
10215        asupersync::test_utils::run_test(|| async {
10216            let conn = Connection::open(":memory:").await.unwrap();
10217            let rows = conn
10218                .query(
10219                    "WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<5) \
10220                 SELECT x FROM cnt;",
10221                )
10222                .await
10223                .unwrap();
10224            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10225            assert_eq!(results, vec![1, 2, 3, 4, 5]);
10226        });
10227    }
10228
10229    // ── HAVING with multiple conditions ──────────────────────────────────
10230
10231    #[test]
10232    fn parity_having_count_and_sum() {
10233        asupersync::test_utils::run_test(|| async {
10234            let conn = Connection::open(":memory:").await.unwrap();
10235            conn.execute("CREATE TABLE sales(region TEXT, amount INTEGER);")
10236                .await
10237                .unwrap();
10238            conn.execute("INSERT INTO sales VALUES('East', 100);")
10239                .await
10240                .unwrap();
10241            conn.execute("INSERT INTO sales VALUES('East', 200);")
10242                .await
10243                .unwrap();
10244            conn.execute("INSERT INTO sales VALUES('West', 50);")
10245                .await
10246                .unwrap();
10247            conn.execute("INSERT INTO sales VALUES('West', 60);")
10248                .await
10249                .unwrap();
10250            conn.execute("INSERT INTO sales VALUES('West', 70);")
10251                .await
10252                .unwrap();
10253            // HAVING COUNT(*) > 2 → only West (3 rows)
10254            let rows = conn
10255                .query("SELECT region, SUM(amount) FROM sales GROUP BY region HAVING COUNT(*) > 2;")
10256                .await
10257                .unwrap();
10258            let results: Vec<(String, i64)> = rows
10259                .iter()
10260                .map(|r| {
10261                    let vals = row_values(r);
10262                    (
10263                        match &vals[0] {
10264                            SqliteValue::Text(s) => s.to_string(),
10265                            _ => panic!("expected text"),
10266                        },
10267                        vals[1].to_integer(),
10268                    )
10269                })
10270                .collect();
10271            assert_eq!(results, vec![("West".into(), 180)]);
10272        });
10273    }
10274
10275    // ── Multi-table UPDATE with JOIN subquery ────────────────────────────
10276
10277    #[test]
10278    fn parity_update_with_exists_subquery() {
10279        asupersync::test_utils::run_test(|| async {
10280            let conn = Connection::open(":memory:").await.unwrap();
10281            conn.execute("CREATE TABLE products(pid INTEGER, name TEXT, active INTEGER);")
10282                .await
10283                .unwrap();
10284            conn.execute("INSERT INTO products VALUES(1, 'Widget', 1);")
10285                .await
10286                .unwrap();
10287            conn.execute("INSERT INTO products VALUES(2, 'Gadget', 1);")
10288                .await
10289                .unwrap();
10290            conn.execute("INSERT INTO products VALUES(3, 'Doohickey', 1);")
10291                .await
10292                .unwrap();
10293            conn.execute("CREATE TABLE discontinued(product_id INTEGER);")
10294                .await
10295                .unwrap();
10296            conn.execute("INSERT INTO discontinued VALUES(1);")
10297                .await
10298                .unwrap();
10299            conn.execute("INSERT INTO discontinued VALUES(3);")
10300                .await
10301                .unwrap();
10302            // Correlated EXISTS subquery: update rows where a matching row exists.
10303            conn.execute(
10304                "UPDATE products SET active = 0 WHERE EXISTS \
10305             (SELECT 1 FROM discontinued WHERE discontinued.product_id = products.pid);",
10306            )
10307            .await
10308            .unwrap();
10309            let rows = conn
10310                .query("SELECT pid, active FROM products ORDER BY pid;")
10311                .await
10312                .unwrap();
10313            let results: Vec<(i64, i64)> = rows
10314                .iter()
10315                .map(|r| {
10316                    let vals = row_values(r);
10317                    (vals[0].to_integer(), vals[1].to_integer())
10318                })
10319                .collect();
10320            assert_eq!(results, vec![(1, 0), (2, 1), (3, 0)]);
10321        });
10322    }
10323
10324    #[test]
10325    fn probe_correlated_exists_select() {
10326        asupersync::test_utils::run_test(|| async {
10327            // Diagnostic: does correlated EXISTS work in a SELECT context?
10328            let conn = Connection::open(":memory:").await.unwrap();
10329            conn.execute("CREATE TABLE a(x INTEGER);").await.unwrap();
10330            conn.execute("INSERT INTO a VALUES(1);").await.unwrap();
10331            conn.execute("INSERT INTO a VALUES(2);").await.unwrap();
10332            conn.execute("INSERT INTO a VALUES(3);").await.unwrap();
10333            conn.execute("CREATE TABLE b(y INTEGER);").await.unwrap();
10334            conn.execute("INSERT INTO b VALUES(1);").await.unwrap();
10335            conn.execute("INSERT INTO b VALUES(3);").await.unwrap();
10336            let rows = conn
10337                .query("SELECT x FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.y = a.x) ORDER BY x;")
10338                .await
10339                .unwrap();
10340            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10341            assert_eq!(results, vec![1, 3]);
10342        });
10343    }
10344
10345    // ── Conformance Probes: Edge Cases ──────────────────────────────────
10346
10347    #[test]
10348    fn probe_order_by_expression() {
10349        asupersync::test_utils::run_test(|| async {
10350            let conn = Connection::open(":memory:").await.unwrap();
10351            conn.execute("CREATE TABLE t(x INTEGER, y TEXT);")
10352                .await
10353                .unwrap();
10354            conn.execute("INSERT INTO t VALUES(3, 'c');").await.unwrap();
10355            conn.execute("INSERT INTO t VALUES(1, 'a');").await.unwrap();
10356            conn.execute("INSERT INTO t VALUES(2, 'b');").await.unwrap();
10357            let rows = conn
10358                .query("SELECT y FROM t ORDER BY x * -1;")
10359                .await
10360                .unwrap();
10361            let results: Vec<String> = rows
10362                .iter()
10363                .map(|r| match &row_values(r)[0] {
10364                    SqliteValue::Text(s) => s.to_string(),
10365                    _ => panic!("expected text"),
10366                })
10367                .collect();
10368            assert_eq!(results, vec!["c", "b", "a"]);
10369        });
10370    }
10371
10372    #[test]
10373    fn probe_group_by_expression() {
10374        asupersync::test_utils::run_test(|| async {
10375            let conn = Connection::open(":memory:").await.unwrap();
10376            conn.execute("CREATE TABLE t(x INTEGER, v INTEGER);")
10377                .await
10378                .unwrap();
10379            conn.execute("INSERT INTO t VALUES(1, 10);").await.unwrap();
10380            conn.execute("INSERT INTO t VALUES(2, 20);").await.unwrap();
10381            conn.execute("INSERT INTO t VALUES(3, 30);").await.unwrap();
10382            conn.execute("INSERT INTO t VALUES(4, 40);").await.unwrap();
10383            let rows = conn
10384                .query("SELECT x % 2 AS grp, SUM(v) FROM t GROUP BY x % 2 ORDER BY grp;")
10385                .await
10386                .unwrap();
10387            let results: Vec<(i64, i64)> = rows
10388                .iter()
10389                .map(|r| {
10390                    let vals = row_values(r);
10391                    (vals[0].to_integer(), vals[1].to_integer())
10392                })
10393                .collect();
10394            assert_eq!(results, vec![(0, 60), (1, 40)]);
10395        });
10396    }
10397
10398    #[test]
10399    fn probe_having_with_expression() {
10400        asupersync::test_utils::run_test(|| async {
10401            let conn = Connection::open(":memory:").await.unwrap();
10402            conn.execute("CREATE TABLE t(cat TEXT, val INTEGER);")
10403                .await
10404                .unwrap();
10405            conn.execute("INSERT INTO t VALUES('a', 10);")
10406                .await
10407                .unwrap();
10408            conn.execute("INSERT INTO t VALUES('a', 20);")
10409                .await
10410                .unwrap();
10411            conn.execute("INSERT INTO t VALUES('b', 5);").await.unwrap();
10412            let rows = conn
10413            .query(
10414                "SELECT cat, SUM(val) AS s FROM t GROUP BY cat HAVING SUM(val) > 10 ORDER BY cat;",
10415            )
10416            .await
10417            .unwrap();
10418            let results: Vec<(String, i64)> = rows
10419                .iter()
10420                .map(|r| {
10421                    let vals = row_values(r);
10422                    let cat = match &vals[0] {
10423                        SqliteValue::Text(s) => s.to_string(),
10424                        _ => panic!("expected text"),
10425                    };
10426                    (cat, vals[1].to_integer())
10427                })
10428                .collect();
10429            assert_eq!(results, vec![("a".to_string(), 30)]);
10430        });
10431    }
10432
10433    #[test]
10434    fn probe_scalar_subquery_in_select() {
10435        asupersync::test_utils::run_test(|| async {
10436            let conn = Connection::open(":memory:").await.unwrap();
10437            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10438            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10439            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10440            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10441            let rows = conn
10442                .query("SELECT x, (SELECT MAX(x) FROM t) AS mx FROM t ORDER BY x;")
10443                .await
10444                .unwrap();
10445            let results: Vec<(i64, i64)> = rows
10446                .iter()
10447                .map(|r| {
10448                    let vals = row_values(r);
10449                    (vals[0].to_integer(), vals[1].to_integer())
10450                })
10451                .collect();
10452            assert_eq!(results, vec![(1, 3), (2, 3), (3, 3)]);
10453        });
10454    }
10455
10456    #[test]
10457    fn probe_nested_case_when() {
10458        asupersync::test_utils::run_test(|| async {
10459            let conn = Connection::open(":memory:").await.unwrap();
10460            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10461            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10462            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10463            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10464            let rows = conn
10465                .query(
10466                    "SELECT CASE WHEN x < 2 THEN 'low' \
10467                 WHEN x < 3 THEN 'mid' ELSE 'high' END AS label FROM t ORDER BY x;",
10468                )
10469                .await
10470                .unwrap();
10471            let results: Vec<String> = rows
10472                .iter()
10473                .map(|r| match &row_values(r)[0] {
10474                    SqliteValue::Text(s) => s.to_string(),
10475                    _ => panic!("expected text"),
10476                })
10477                .collect();
10478            assert_eq!(results, vec!["low", "mid", "high"]);
10479        });
10480    }
10481
10482    #[test]
10483    fn probe_coalesce_multi_arg() {
10484        asupersync::test_utils::run_test(|| async {
10485            let conn = Connection::open(":memory:").await.unwrap();
10486            let rows = conn
10487                .query("SELECT COALESCE(NULL, NULL, NULL, 42);")
10488                .await
10489                .unwrap();
10490            assert_eq!(row_values(&rows[0])[0].to_integer(), 42);
10491        });
10492    }
10493
10494    #[test]
10495    fn probe_nullif_function() {
10496        asupersync::test_utils::run_test(|| async {
10497            let conn = Connection::open(":memory:").await.unwrap();
10498            let rows = conn
10499                .query("SELECT NULLIF(5, 5), NULLIF(5, 3);")
10500                .await
10501                .unwrap();
10502            let vals = row_values(&rows[0]);
10503            assert_eq!(vals[0], SqliteValue::Null);
10504            assert_eq!(vals[1].to_integer(), 5);
10505        });
10506    }
10507
10508    #[test]
10509    fn probe_iif_function() {
10510        asupersync::test_utils::run_test(|| async {
10511            let conn = Connection::open(":memory:").await.unwrap();
10512            let rows = conn
10513                .query("SELECT IIF(1 > 0, 'yes', 'no'), IIF(1 < 0, 'yes', 'no');")
10514                .await
10515                .unwrap();
10516            let vals = row_values(&rows[0]);
10517            let a = match &vals[0] {
10518                SqliteValue::Text(s) => s.to_string(),
10519                _ => panic!("expected text"),
10520            };
10521            let b = match &vals[1] {
10522                SqliteValue::Text(s) => s.to_string(),
10523                _ => panic!("expected text"),
10524            };
10525            assert_eq!(a, "yes");
10526            assert_eq!(b, "no");
10527        });
10528    }
10529
10530    #[test]
10531    fn probe_like_escape() {
10532        asupersync::test_utils::run_test(|| async {
10533            let conn = Connection::open(":memory:").await.unwrap();
10534            conn.execute("CREATE TABLE t(s TEXT);").await.unwrap();
10535            conn.execute("INSERT INTO t VALUES('100% done');")
10536                .await
10537                .unwrap();
10538            conn.execute("INSERT INTO t VALUES('50 percent');")
10539                .await
10540                .unwrap();
10541            let rows = conn
10542                .query("SELECT s FROM t WHERE s LIKE '%!%%' ESCAPE '!';")
10543                .await
10544                .unwrap();
10545            assert_eq!(rows.len(), 1);
10546            let val = match &row_values(&rows[0])[0] {
10547                SqliteValue::Text(s) => s.to_string(),
10548                _ => panic!("expected text"),
10549            };
10550            assert_eq!(val, "100% done");
10551        });
10552    }
10553
10554    #[test]
10555    fn probe_between_with_expressions() {
10556        asupersync::test_utils::run_test(|| async {
10557            let conn = Connection::open(":memory:").await.unwrap();
10558            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10559            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10560            conn.execute("INSERT INTO t VALUES(5);").await.unwrap();
10561            conn.execute("INSERT INTO t VALUES(10);").await.unwrap();
10562            let rows = conn
10563                .query("SELECT x FROM t WHERE x BETWEEN 2 + 1 AND 4 * 2 ORDER BY x;")
10564                .await
10565                .unwrap();
10566            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10567            assert_eq!(results, vec![5]);
10568        });
10569    }
10570
10571    #[test]
10572    fn probe_distinct_with_expression() {
10573        asupersync::test_utils::run_test(|| async {
10574            let conn = Connection::open(":memory:").await.unwrap();
10575            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10576            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10577            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10578            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10579            conn.execute("INSERT INTO t VALUES(4);").await.unwrap();
10580            let rows = conn
10581                .query("SELECT DISTINCT x % 2 AS mod2 FROM t ORDER BY mod2;")
10582                .await
10583                .unwrap();
10584            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10585            assert_eq!(results, vec![0, 1]);
10586        });
10587    }
10588
10589    #[test]
10590    fn probe_insert_or_ignore_keeps_existing() {
10591        asupersync::test_utils::run_test(|| async {
10592            let conn = Connection::open(":memory:").await.unwrap();
10593            conn.execute("CREATE TABLE t(id INTEGER PRIMARY KEY, v TEXT);")
10594                .await
10595                .unwrap();
10596            conn.execute("INSERT INTO t VALUES(1, 'first');")
10597                .await
10598                .unwrap();
10599            conn.execute("INSERT OR IGNORE INTO t VALUES(1, 'second');")
10600                .await
10601                .unwrap();
10602            let rows = conn.query("SELECT v FROM t WHERE id = 1;").await.unwrap();
10603            let val = match &row_values(&rows[0])[0] {
10604                SqliteValue::Text(s) => s.to_string(),
10605                _ => panic!("expected text"),
10606            };
10607            assert_eq!(val, "first");
10608        });
10609    }
10610
10611    #[test]
10612    fn probe_insert_or_replace_overwrites() {
10613        asupersync::test_utils::run_test(|| async {
10614            let conn = Connection::open(":memory:").await.unwrap();
10615            conn.execute("CREATE TABLE t(id INTEGER PRIMARY KEY, v TEXT);")
10616                .await
10617                .unwrap();
10618            conn.execute("INSERT INTO t VALUES(1, 'first');")
10619                .await
10620                .unwrap();
10621            conn.execute("INSERT OR REPLACE INTO t VALUES(1, 'second');")
10622                .await
10623                .unwrap();
10624            let rows = conn.query("SELECT v FROM t WHERE id = 1;").await.unwrap();
10625            let val = match &row_values(&rows[0])[0] {
10626                SqliteValue::Text(s) => s.to_string(),
10627                _ => panic!("expected text"),
10628            };
10629            assert_eq!(val, "second");
10630        });
10631    }
10632
10633    #[test]
10634    fn probe_delete_with_correlated_exists() {
10635        asupersync::test_utils::run_test(|| async {
10636            let conn = Connection::open(":memory:").await.unwrap();
10637            conn.execute("CREATE TABLE items(id INTEGER, active INTEGER);")
10638                .await
10639                .unwrap();
10640            conn.execute("INSERT INTO items VALUES(1, 1);")
10641                .await
10642                .unwrap();
10643            conn.execute("INSERT INTO items VALUES(2, 1);")
10644                .await
10645                .unwrap();
10646            conn.execute("INSERT INTO items VALUES(3, 1);")
10647                .await
10648                .unwrap();
10649            conn.execute("CREATE TABLE retired(item_id INTEGER);")
10650                .await
10651                .unwrap();
10652            conn.execute("INSERT INTO retired VALUES(2);")
10653                .await
10654                .unwrap();
10655            conn.execute(
10656                "DELETE FROM items WHERE EXISTS \
10657             (SELECT 1 FROM retired WHERE retired.item_id = items.id);",
10658            )
10659            .await
10660            .unwrap();
10661            let rows = conn
10662                .query("SELECT id FROM items ORDER BY id;")
10663                .await
10664                .unwrap();
10665            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10666            assert_eq!(results, vec![1, 3]);
10667        });
10668    }
10669
10670    #[test]
10671    fn probe_aggregate_in_order_by() {
10672        asupersync::test_utils::run_test(|| async {
10673            let conn = Connection::open(":memory:").await.unwrap();
10674            conn.execute("CREATE TABLE t(cat TEXT, val INTEGER);")
10675                .await
10676                .unwrap();
10677            conn.execute("INSERT INTO t VALUES('b', 10);")
10678                .await
10679                .unwrap();
10680            conn.execute("INSERT INTO t VALUES('a', 30);")
10681                .await
10682                .unwrap();
10683            conn.execute("INSERT INTO t VALUES('c', 20);")
10684                .await
10685                .unwrap();
10686            let rows = conn
10687                .query("SELECT cat, SUM(val) AS s FROM t GROUP BY cat ORDER BY SUM(val) DESC;")
10688                .await
10689                .unwrap();
10690            let results: Vec<(String, i64)> = rows
10691                .iter()
10692                .map(|r| {
10693                    let vals = row_values(r);
10694                    let cat = match &vals[0] {
10695                        SqliteValue::Text(s) => s.to_string(),
10696                        _ => panic!("expected text"),
10697                    };
10698                    (cat, vals[1].to_integer())
10699                })
10700                .collect();
10701            assert_eq!(
10702                results,
10703                vec![
10704                    ("a".to_string(), 30),
10705                    ("c".to_string(), 20),
10706                    ("b".to_string(), 10)
10707                ]
10708            );
10709        });
10710    }
10711
10712    #[test]
10713    fn probe_subquery_in_from() {
10714        asupersync::test_utils::run_test(|| async {
10715            let conn = Connection::open(":memory:").await.unwrap();
10716            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10717            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10718            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10719            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10720            let rows = conn
10721            .query("SELECT sub.doubled FROM (SELECT x * 2 AS doubled FROM t) AS sub ORDER BY sub.doubled;")
10722            .await
10723            .unwrap();
10724            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10725            assert_eq!(results, vec![2, 4, 6]);
10726        });
10727    }
10728
10729    #[test]
10730    fn probe_multi_column_order_by_mixed() {
10731        asupersync::test_utils::run_test(|| async {
10732            let conn = Connection::open(":memory:").await.unwrap();
10733            conn.execute("CREATE TABLE t(a INTEGER, b INTEGER);")
10734                .await
10735                .unwrap();
10736            conn.execute("INSERT INTO t VALUES(1, 3);").await.unwrap();
10737            conn.execute("INSERT INTO t VALUES(1, 1);").await.unwrap();
10738            conn.execute("INSERT INTO t VALUES(2, 2);").await.unwrap();
10739            conn.execute("INSERT INTO t VALUES(2, 4);").await.unwrap();
10740            let rows = conn
10741                .query("SELECT a, b FROM t ORDER BY a ASC, b DESC;")
10742                .await
10743                .unwrap();
10744            let results: Vec<(i64, i64)> = rows
10745                .iter()
10746                .map(|r| {
10747                    let vals = row_values(r);
10748                    (vals[0].to_integer(), vals[1].to_integer())
10749                })
10750                .collect();
10751            assert_eq!(results, vec![(1, 3), (1, 1), (2, 4), (2, 2)]);
10752        });
10753    }
10754
10755    #[test]
10756    fn probe_null_handling_order_by() {
10757        asupersync::test_utils::run_test(|| async {
10758            let conn = Connection::open(":memory:").await.unwrap();
10759            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10760            conn.execute("INSERT INTO t VALUES(3);").await.unwrap();
10761            conn.execute("INSERT INTO t VALUES(NULL);").await.unwrap();
10762            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10763            // SQLite: NULLs sort first in ASC order.
10764            let rows = conn.query("SELECT x FROM t ORDER BY x ASC;").await.unwrap();
10765            let results: Vec<SqliteValue> = rows.iter().map(|r| row_values(r)[0].clone()).collect();
10766            assert_eq!(results[0], SqliteValue::Null);
10767            assert_eq!(results[1].to_integer(), 1);
10768            assert_eq!(results[2].to_integer(), 3);
10769        });
10770    }
10771
10772    #[test]
10773    fn probe_count_distinct() {
10774        asupersync::test_utils::run_test(|| async {
10775            let conn = Connection::open(":memory:").await.unwrap();
10776            conn.execute("CREATE TABLE t(x INTEGER);").await.unwrap();
10777            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10778            conn.execute("INSERT INTO t VALUES(2);").await.unwrap();
10779            conn.execute("INSERT INTO t VALUES(1);").await.unwrap();
10780            conn.execute("INSERT INTO t VALUES(NULL);").await.unwrap();
10781            let rows = conn
10782                .query("SELECT COUNT(DISTINCT x) FROM t;")
10783                .await
10784                .unwrap();
10785            // COUNT(DISTINCT x) should count distinct non-NULL values → 2
10786            assert_eq!(row_values(&rows[0])[0].to_integer(), 2);
10787        });
10788    }
10789
10790    #[test]
10791    fn probe_cast_in_where() {
10792        asupersync::test_utils::run_test(|| async {
10793            let conn = Connection::open(":memory:").await.unwrap();
10794            conn.execute("CREATE TABLE t(x TEXT);").await.unwrap();
10795            conn.execute("INSERT INTO t VALUES('123');").await.unwrap();
10796            conn.execute("INSERT INTO t VALUES('456');").await.unwrap();
10797            conn.execute("INSERT INTO t VALUES('abc');").await.unwrap();
10798            let rows = conn
10799                .query("SELECT x FROM t WHERE CAST(x AS INTEGER) > 200;")
10800                .await
10801                .unwrap();
10802            assert_eq!(rows.len(), 1);
10803            let val = match &row_values(&rows[0])[0] {
10804                SqliteValue::Text(s) => s.to_string(),
10805                _ => panic!("expected text"),
10806            };
10807            assert_eq!(val, "456");
10808        });
10809    }
10810
10811    #[test]
10812    fn probe_union_all_three_way() {
10813        asupersync::test_utils::run_test(|| async {
10814            let conn = Connection::open(":memory:").await.unwrap();
10815            let rows = conn
10816                .query("SELECT 1 AS v UNION ALL SELECT 2 UNION ALL SELECT 1;")
10817                .await
10818                .unwrap();
10819            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10820            assert_eq!(results, vec![1, 2, 1]);
10821        });
10822    }
10823
10824    #[test]
10825    fn probe_union_dedup_three_way() {
10826        asupersync::test_utils::run_test(|| async {
10827            let conn = Connection::open(":memory:").await.unwrap();
10828            let rows = conn
10829                .query("SELECT 1 AS v UNION SELECT 2 UNION SELECT 1 ORDER BY v;")
10830                .await
10831                .unwrap();
10832            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10833            assert_eq!(results, vec![1, 2]);
10834        });
10835    }
10836
10837    #[test]
10838    fn probe_except_compound() {
10839        asupersync::test_utils::run_test(|| async {
10840            let conn = Connection::open(":memory:").await.unwrap();
10841            let rows = conn
10842                .query("SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 EXCEPT SELECT 2;")
10843                .await
10844                .unwrap();
10845            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10846            // EXCEPT removes rows from right. Order: 1, 3
10847            assert!(results.contains(&1));
10848            assert!(results.contains(&3));
10849            assert!(!results.contains(&2));
10850        });
10851    }
10852
10853    #[test]
10854    fn probe_intersect_compound() {
10855        asupersync::test_utils::run_test(|| async {
10856            let conn = Connection::open(":memory:").await.unwrap();
10857            let rows = conn
10858                .query(
10859                    "SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 \
10860                 INTERSECT SELECT 2 UNION ALL SELECT 3;",
10861                )
10862                .await
10863                .unwrap();
10864            let results: Vec<i64> = rows.iter().map(|r| row_values(r)[0].to_integer()).collect();
10865            assert!(results.contains(&2) || results.contains(&3));
10866        });
10867    }
10868
10869    // -----------------------------------------------------------------------
10870    // Conformance suite 024: string functions
10871    // -----------------------------------------------------------------------
10872
10873    #[test]
10874    fn conformance_024_substr() {
10875        asupersync::test_utils::run_test(|| async {
10876            let conn = Connection::open(":memory:").await.unwrap();
10877            let r = conn.query("SELECT substr('hello world', 7)").await.unwrap();
10878            assert_eq!(row_values(&r[0])[0].to_text(), "world");
10879            let r = conn
10880                .query("SELECT substr('hello world', 1, 5)")
10881                .await
10882                .unwrap();
10883            assert_eq!(row_values(&r[0])[0].to_text(), "hello");
10884            let r = conn.query("SELECT substr('hello', -3)").await.unwrap();
10885            assert_eq!(row_values(&r[0])[0].to_text(), "llo");
10886        });
10887    }
10888
10889    #[test]
10890    fn conformance_024_replace() {
10891        asupersync::test_utils::run_test(|| async {
10892            let conn = Connection::open(":memory:").await.unwrap();
10893            let r = conn
10894                .query("SELECT replace('hello world', 'world', 'rust')")
10895                .await
10896                .unwrap();
10897            assert_eq!(row_values(&r[0])[0].to_text(), "hello rust");
10898            let r = conn
10899                .query("SELECT replace('aaa', 'a', 'bb')")
10900                .await
10901                .unwrap();
10902            assert_eq!(row_values(&r[0])[0].to_text(), "bbbbbb");
10903        });
10904    }
10905
10906    #[test]
10907    fn conformance_024_trim() {
10908        asupersync::test_utils::run_test(|| async {
10909            let conn = Connection::open(":memory:").await.unwrap();
10910            let r = conn.query("SELECT trim('  hello  ')").await.unwrap();
10911            assert_eq!(row_values(&r[0])[0].to_text(), "hello");
10912            let r = conn.query("SELECT ltrim('  hello  ')").await.unwrap();
10913            assert_eq!(row_values(&r[0])[0].to_text(), "hello  ");
10914            let r = conn.query("SELECT rtrim('  hello  ')").await.unwrap();
10915            assert_eq!(row_values(&r[0])[0].to_text(), "  hello");
10916        });
10917    }
10918
10919    #[test]
10920    fn conformance_024_instr() {
10921        asupersync::test_utils::run_test(|| async {
10922            let conn = Connection::open(":memory:").await.unwrap();
10923            let r = conn
10924                .query("SELECT instr('hello world', 'world')")
10925                .await
10926                .unwrap();
10927            assert_eq!(row_values(&r[0])[0].to_text(), "7");
10928            let r = conn.query("SELECT instr('hello', 'xyz')").await.unwrap();
10929            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10930            let r = conn.query("SELECT instr('hello', '')").await.unwrap();
10931            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10932        });
10933    }
10934
10935    #[test]
10936    fn conformance_024_hex_zeroblob() {
10937        asupersync::test_utils::run_test(|| async {
10938            let conn = Connection::open(":memory:").await.unwrap();
10939            let r = conn.query("SELECT hex(zeroblob(4))").await.unwrap();
10940            assert_eq!(row_values(&r[0])[0].to_text(), "00000000");
10941            let r = conn.query("SELECT typeof(zeroblob(4))").await.unwrap();
10942            assert_eq!(row_values(&r[0])[0].to_text(), "blob");
10943        });
10944    }
10945
10946    #[test]
10947    fn conformance_024_char_unicode() {
10948        asupersync::test_utils::run_test(|| async {
10949            let conn = Connection::open(":memory:").await.unwrap();
10950            let r = conn.query("SELECT char(65, 66, 67)").await.unwrap();
10951            assert_eq!(row_values(&r[0])[0].to_text(), "ABC");
10952            let r = conn.query("SELECT unicode('A')").await.unwrap();
10953            assert_eq!(row_values(&r[0])[0].to_text(), "65");
10954        });
10955    }
10956
10957    // -----------------------------------------------------------------------
10958    // Conformance suite 025: expression operators
10959    // -----------------------------------------------------------------------
10960
10961    #[test]
10962    fn conformance_025_between() {
10963        asupersync::test_utils::run_test(|| async {
10964            let conn = Connection::open(":memory:").await.unwrap();
10965            let r = conn.query("SELECT 5 BETWEEN 1 AND 10").await.unwrap();
10966            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10967            let r = conn.query("SELECT 15 BETWEEN 1 AND 10").await.unwrap();
10968            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10969            let r = conn.query("SELECT 5 NOT BETWEEN 1 AND 10").await.unwrap();
10970            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10971        });
10972    }
10973
10974    #[test]
10975    fn conformance_025_in_operator() {
10976        asupersync::test_utils::run_test(|| async {
10977            let conn = Connection::open(":memory:").await.unwrap();
10978            let r = conn.query("SELECT 3 IN (1, 2, 3, 4)").await.unwrap();
10979            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10980            let r = conn.query("SELECT 5 IN (1, 2, 3, 4)").await.unwrap();
10981            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10982            let r = conn.query("SELECT 3 NOT IN (1, 2, 3, 4)").await.unwrap();
10983            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10984        });
10985    }
10986
10987    #[test]
10988    fn conformance_025_like_glob() {
10989        asupersync::test_utils::run_test(|| async {
10990            let conn = Connection::open(":memory:").await.unwrap();
10991            let r = conn.query("SELECT 'hello' LIKE 'hel%'").await.unwrap();
10992            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10993            let r = conn.query("SELECT 'hello' LIKE 'HEL%'").await.unwrap();
10994            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10995            let r = conn.query("SELECT 'hello' GLOB 'hel*'").await.unwrap();
10996            assert_eq!(row_values(&r[0])[0].to_text(), "1");
10997            let r = conn.query("SELECT 'hello' GLOB 'HEL*'").await.unwrap();
10998            assert_eq!(row_values(&r[0])[0].to_text(), "0");
10999        });
11000    }
11001
11002    #[test]
11003    fn conformance_025_coalesce_nullif_iif() {
11004        asupersync::test_utils::run_test(|| async {
11005            let conn = Connection::open(":memory:").await.unwrap();
11006            let r = conn
11007                .query("SELECT coalesce(NULL, NULL, 'found')")
11008                .await
11009                .unwrap();
11010            assert_eq!(row_values(&r[0])[0].to_text(), "found");
11011            let r = conn.query("SELECT nullif(5, 5)").await.unwrap();
11012            assert!(row_values(&r[0])[0].is_null());
11013            let r = conn.query("SELECT nullif(5, 3)").await.unwrap();
11014            assert_eq!(row_values(&r[0])[0].to_text(), "5");
11015            let r = conn.query("SELECT iif(1, 'yes', 'no')").await.unwrap();
11016            assert_eq!(row_values(&r[0])[0].to_text(), "yes");
11017            let r = conn.query("SELECT iif(0, 'yes', 'no')").await.unwrap();
11018            assert_eq!(row_values(&r[0])[0].to_text(), "no");
11019        });
11020    }
11021
11022    #[test]
11023    fn conformance_025_bitwise() {
11024        asupersync::test_utils::run_test(|| async {
11025            let conn = Connection::open(":memory:").await.unwrap();
11026            let r = conn.query("SELECT 6 & 3").await.unwrap();
11027            assert_eq!(row_values(&r[0])[0].to_text(), "2");
11028            let r = conn.query("SELECT 6 | 3").await.unwrap();
11029            assert_eq!(row_values(&r[0])[0].to_text(), "7");
11030            let r = conn.query("SELECT ~0").await.unwrap();
11031            assert_eq!(row_values(&r[0])[0].to_text(), "-1");
11032            let r = conn.query("SELECT 1 << 4").await.unwrap();
11033            assert_eq!(row_values(&r[0])[0].to_text(), "16");
11034            let r = conn.query("SELECT 16 >> 2").await.unwrap();
11035            assert_eq!(row_values(&r[0])[0].to_text(), "4");
11036        });
11037    }
11038
11039    #[test]
11040    fn conformance_025_cast() {
11041        asupersync::test_utils::run_test(|| async {
11042            let conn = Connection::open(":memory:").await.unwrap();
11043            let r = conn.query("SELECT CAST('123' AS INTEGER)").await.unwrap();
11044            assert_eq!(row_values(&r[0])[0].to_text(), "123");
11045            let r = conn
11046                .query("SELECT typeof(CAST(123 AS TEXT))")
11047                .await
11048                .unwrap();
11049            assert_eq!(row_values(&r[0])[0].to_text(), "text");
11050            let r = conn.query("SELECT CAST(3.14 AS INTEGER)").await.unwrap();
11051            assert_eq!(row_values(&r[0])[0].to_text(), "3");
11052        });
11053    }
11054
11055    #[test]
11056    fn conformance_025_unary_operators() {
11057        asupersync::test_utils::run_test(|| async {
11058            let conn = Connection::open(":memory:").await.unwrap();
11059            let r = conn.query("SELECT -(-5)").await.unwrap();
11060            assert_eq!(row_values(&r[0])[0].to_text(), "5");
11061            let r = conn.query("SELECT +42").await.unwrap();
11062            assert_eq!(row_values(&r[0])[0].to_text(), "42");
11063            let r = conn.query("SELECT NOT 0").await.unwrap();
11064            assert_eq!(row_values(&r[0])[0].to_text(), "1");
11065            let r = conn.query("SELECT NOT 1").await.unwrap();
11066            assert_eq!(row_values(&r[0])[0].to_text(), "0");
11067        });
11068    }
11069
11070    #[test]
11071    fn conformance_025_is_null() {
11072        asupersync::test_utils::run_test(|| async {
11073            let conn = Connection::open(":memory:").await.unwrap();
11074            let r = conn.query("SELECT NULL IS NULL").await.unwrap();
11075            assert_eq!(row_values(&r[0])[0].to_text(), "1");
11076            let r = conn.query("SELECT 5 IS NOT NULL").await.unwrap();
11077            assert_eq!(row_values(&r[0])[0].to_text(), "1");
11078            let r = conn.query("SELECT NULL IS NOT NULL").await.unwrap();
11079            assert_eq!(row_values(&r[0])[0].to_text(), "0");
11080        });
11081    }
11082
11083    // -----------------------------------------------------------------------
11084    // Conformance suite 026: subquery and CTE
11085    // -----------------------------------------------------------------------
11086
11087    #[test]
11088    fn conformance_026_scalar_subquery() {
11089        asupersync::test_utils::run_test(|| async {
11090            let conn = Connection::open(":memory:").await.unwrap();
11091            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
11092                .await
11093                .unwrap();
11094            conn.execute("INSERT INTO t1 VALUES (1, 10), (2, 20), (3, 30)")
11095                .await
11096                .unwrap();
11097            let r = conn
11098                .query("SELECT (SELECT MAX(val) FROM t1)")
11099                .await
11100                .unwrap();
11101            assert_eq!(row_values(&r[0])[0].to_text(), "30");
11102            let r = conn
11103                .query("SELECT (SELECT COUNT(*) FROM t1)")
11104                .await
11105                .unwrap();
11106            assert_eq!(row_values(&r[0])[0].to_text(), "3");
11107        });
11108    }
11109
11110    #[test]
11111    fn conformance_026_exists_subquery() {
11112        asupersync::test_utils::run_test(|| async {
11113            let conn = Connection::open(":memory:").await.unwrap();
11114            conn.execute("CREATE TABLE items(id INTEGER PRIMARY KEY, name TEXT)")
11115                .await
11116                .unwrap();
11117            conn.execute("INSERT INTO items VALUES (1, 'apple'), (2, 'banana')")
11118                .await
11119                .unwrap();
11120            let r = conn
11121                .query("SELECT EXISTS(SELECT 1 FROM items WHERE name = 'apple')")
11122                .await
11123                .unwrap();
11124            assert_eq!(row_values(&r[0])[0].to_text(), "1");
11125            let r = conn
11126                .query("SELECT EXISTS(SELECT 1 FROM items WHERE name = 'cherry')")
11127                .await
11128                .unwrap();
11129            assert_eq!(row_values(&r[0])[0].to_text(), "0");
11130        });
11131    }
11132
11133    #[test]
11134    fn conformance_026_cte_basic() {
11135        asupersync::test_utils::run_test(|| async {
11136            let conn = Connection::open(":memory:").await.unwrap();
11137            conn.execute(
11138            "CREATE TABLE employees(id INTEGER PRIMARY KEY, name TEXT, dept TEXT, salary INTEGER)",
11139        )
11140        .await
11141        .unwrap();
11142            conn.execute(
11143                "INSERT INTO employees VALUES \
11144             (1, 'Alice', 'eng', 100000), \
11145             (2, 'Bob', 'eng', 95000), \
11146             (3, 'Charlie', 'sales', 80000)",
11147            )
11148            .await
11149            .unwrap();
11150            let r = conn
11151                .query(
11152                    "WITH eng AS (SELECT * FROM employees WHERE dept = 'eng') \
11153                 SELECT name FROM eng ORDER BY name",
11154                )
11155                .await
11156                .unwrap();
11157            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11158            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
11159            assert_eq!(r.len(), 2);
11160        });
11161    }
11162
11163    #[test]
11164    fn conformance_026_cte_explicit_columns() {
11165        asupersync::test_utils::run_test(|| async {
11166            let conn = Connection::open(":memory:").await.unwrap();
11167            conn.execute("CREATE TABLE nums(n INTEGER)").await.unwrap();
11168            conn.execute("INSERT INTO nums VALUES (10), (20), (30)")
11169                .await
11170                .unwrap();
11171            let r = conn
11172                .query(
11173                    "WITH doubled(val) AS (SELECT n * 2 FROM nums) \
11174                 SELECT val FROM doubled ORDER BY val",
11175                )
11176                .await
11177                .unwrap();
11178            assert_eq!(row_values(&r[0])[0].to_text(), "20");
11179            assert_eq!(row_values(&r[1])[0].to_text(), "40");
11180            assert_eq!(row_values(&r[2])[0].to_text(), "60");
11181        });
11182    }
11183
11184    #[test]
11185    fn conformance_026_recursive_cte() {
11186        asupersync::test_utils::run_test(|| async {
11187            let conn = Connection::open(":memory:").await.unwrap();
11188            let r = conn
11189                .query(
11190                    "WITH RECURSIVE cnt(x) AS (\
11191                 SELECT 1 UNION ALL SELECT x + 1 FROM cnt WHERE x < 5\
11192                 ) SELECT x FROM cnt",
11193                )
11194                .await
11195                .unwrap();
11196            let vals: Vec<String> = r.iter().map(|row| row_values(row)[0].to_text()).collect();
11197            assert_eq!(vals, ["1", "2", "3", "4", "5"]);
11198        });
11199    }
11200
11201    #[test]
11202    fn conformance_026_derived_table() {
11203        asupersync::test_utils::run_test(|| async {
11204            let conn = Connection::open(":memory:").await.unwrap();
11205            conn.execute("CREATE TABLE scores(student TEXT, score INTEGER)")
11206                .await
11207                .unwrap();
11208            conn.execute(
11209                "INSERT INTO scores VALUES \
11210             ('Alice', 90), ('Alice', 85), ('Bob', 70), ('Bob', 80)",
11211            )
11212            .await
11213            .unwrap();
11214            let r = conn
11215                .query(
11216                    "SELECT student, avg_score FROM \
11217                 (SELECT student, AVG(score) as avg_score \
11218                  FROM scores GROUP BY student) ORDER BY student",
11219                )
11220                .await
11221                .unwrap();
11222            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11223            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
11224        });
11225    }
11226
11227    // -----------------------------------------------------------------------
11228    // Conformance suite 027: window functions (not yet implemented)
11229    // -----------------------------------------------------------------------
11230
11231    #[test]
11232
11233    fn conformance_027_row_number() {
11234        asupersync::test_utils::run_test(|| async {
11235            let conn = Connection::open(":memory:").await.unwrap();
11236            conn.execute("CREATE TABLE sales(id INTEGER PRIMARY KEY, region TEXT, amount REAL)")
11237                .await
11238                .unwrap();
11239            conn.execute(
11240                "INSERT INTO sales VALUES \
11241             (1, 'North', 100.0), (2, 'South', 200.0), \
11242             (3, 'North', 150.0), (4, 'South', 175.0)",
11243            )
11244            .await
11245            .unwrap();
11246            let r = conn
11247                .query(
11248                    "SELECT region, amount, \
11249                 ROW_NUMBER() OVER (ORDER BY amount DESC) as rn \
11250                 FROM sales",
11251                )
11252                .await
11253                .unwrap();
11254            assert_eq!(row_values(&r[0])[2].to_text(), "1");
11255        });
11256    }
11257
11258    #[test]
11259
11260    fn conformance_027_rank_dense_rank() {
11261        asupersync::test_utils::run_test(|| async {
11262            let conn = Connection::open(":memory:").await.unwrap();
11263            conn.execute("CREATE TABLE scores(id INTEGER PRIMARY KEY, name TEXT, score INTEGER)")
11264                .await
11265                .unwrap();
11266            conn.execute(
11267                "INSERT INTO scores VALUES \
11268             (1, 'A', 100), (2, 'B', 100), (3, 'C', 90), (4, 'D', 80)",
11269            )
11270            .await
11271            .unwrap();
11272            let r = conn
11273                .query(
11274                    "SELECT name, RANK() OVER (ORDER BY score DESC) as rnk \
11275                 FROM scores",
11276                )
11277                .await
11278                .unwrap();
11279            assert_eq!(row_values(&r[0])[1].to_text(), "1");
11280            assert_eq!(row_values(&r[1])[1].to_text(), "1");
11281            assert_eq!(row_values(&r[2])[1].to_text(), "3");
11282        });
11283    }
11284
11285    #[test]
11286
11287    fn conformance_027_sum_over() {
11288        asupersync::test_utils::run_test(|| async {
11289            let conn = Connection::open(":memory:").await.unwrap();
11290            conn.execute("CREATE TABLE txns(id INTEGER PRIMARY KEY, amount REAL)")
11291                .await
11292                .unwrap();
11293            conn.execute("INSERT INTO txns VALUES (1, 10.0), (2, 20.0), (3, 30.0)")
11294                .await
11295                .unwrap();
11296            let r = conn
11297                .query(
11298                    "SELECT id, SUM(amount) OVER (ORDER BY id) as running \
11299                 FROM txns",
11300                )
11301                .await
11302                .unwrap();
11303            assert_eq!(row_values(&r[0])[1].to_text(), "10.0");
11304            assert_eq!(row_values(&r[1])[1].to_text(), "30.0");
11305            assert_eq!(row_values(&r[2])[1].to_text(), "60.0");
11306        });
11307    }
11308
11309    #[test]
11310
11311    fn conformance_027_lag_lead() {
11312        asupersync::test_utils::run_test(|| async {
11313            let conn = Connection::open(":memory:").await.unwrap();
11314            conn.execute("CREATE TABLE seq(id INTEGER PRIMARY KEY, val TEXT)")
11315                .await
11316                .unwrap();
11317            conn.execute("INSERT INTO seq VALUES (1, 'a'), (2, 'b'), (3, 'c')")
11318                .await
11319                .unwrap();
11320            let r = conn
11321                .query(
11322                    "SELECT val, LAG(val) OVER (ORDER BY id) as prev \
11323                 FROM seq",
11324                )
11325                .await
11326                .unwrap();
11327            assert!(row_values(&r[0])[1].is_null());
11328            assert_eq!(row_values(&r[1])[1].to_text(), "a");
11329        });
11330    }
11331
11332    // -----------------------------------------------------------------------
11333    // Conformance suite 028: views
11334    // -----------------------------------------------------------------------
11335
11336    #[test]
11337    fn conformance_028_create_select_drop_view() {
11338        asupersync::test_utils::run_test(|| async {
11339            let conn = Connection::open(":memory:").await.unwrap();
11340            conn.execute(
11341                "CREATE TABLE products(\
11342             id INTEGER PRIMARY KEY, name TEXT, price REAL, category TEXT)",
11343            )
11344            .await
11345            .unwrap();
11346            conn.execute(
11347                "INSERT INTO products VALUES \
11348             (1, 'Widget', 9.99, 'gadgets'), \
11349             (2, 'Gizmo', 24.99, 'gadgets'), \
11350             (3, 'Doohickey', 4.99, 'tools')",
11351            )
11352            .await
11353            .unwrap();
11354            conn.execute("CREATE VIEW expensive AS SELECT * FROM products WHERE price > 10.0")
11355                .await
11356                .unwrap();
11357            let r = conn
11358                .query("SELECT name FROM expensive ORDER BY name")
11359                .await
11360                .unwrap();
11361            assert_eq!(row_values(&r[0])[0].to_text(), "Gizmo");
11362            assert_eq!(r.len(), 1);
11363            conn.execute("DROP VIEW expensive").await.unwrap();
11364        });
11365    }
11366
11367    #[test]
11368    fn conformance_028_view_with_aggregate() {
11369        asupersync::test_utils::run_test(|| async {
11370            let conn = Connection::open(":memory:").await.unwrap();
11371            conn.execute(
11372                "CREATE TABLE products(\
11373             id INTEGER PRIMARY KEY, name TEXT, price REAL, category TEXT)",
11374            )
11375            .await
11376            .unwrap();
11377            conn.execute(
11378                "INSERT INTO products VALUES \
11379             (1, 'Widget', 9.99, 'gadgets'), \
11380             (2, 'Gizmo', 24.99, 'gadgets'), \
11381             (3, 'Doohickey', 4.99, 'tools'), \
11382             (4, 'Thingamajig', 14.99, 'tools'), \
11383             (5, 'Whatchamacallit', 49.99, 'gadgets')",
11384            )
11385            .await
11386            .unwrap();
11387            conn.execute(
11388                "CREATE VIEW category_stats AS \
11389             SELECT category, COUNT(*) as cnt \
11390             FROM products GROUP BY category",
11391            )
11392            .await
11393            .unwrap();
11394            let r = conn
11395                .query("SELECT category, cnt FROM category_stats ORDER BY category")
11396                .await
11397                .unwrap();
11398            assert_eq!(row_values(&r[0])[0].to_text(), "gadgets");
11399            assert_eq!(row_values(&r[0])[1].to_text(), "3");
11400            assert_eq!(row_values(&r[1])[0].to_text(), "tools");
11401            assert_eq!(row_values(&r[1])[1].to_text(), "2");
11402        });
11403    }
11404
11405    #[test]
11406    fn conformance_028_view_with_join() {
11407        asupersync::test_utils::run_test(|| async {
11408            let conn = Connection::open(":memory:").await.unwrap();
11409            conn.execute(
11410                "CREATE TABLE products(\
11411             id INTEGER PRIMARY KEY, name TEXT, price REAL)",
11412            )
11413            .await
11414            .unwrap();
11415            conn.execute(
11416                "INSERT INTO products VALUES \
11417             (1, 'Widget', 9.99), (2, 'Gizmo', 24.99)",
11418            )
11419            .await
11420            .unwrap();
11421            conn.execute(
11422                "CREATE TABLE orders(\
11423             id INTEGER PRIMARY KEY, product_id INTEGER, qty INTEGER)",
11424            )
11425            .await
11426            .unwrap();
11427            conn.execute("INSERT INTO orders VALUES (1, 1, 10), (2, 2, 5)")
11428                .await
11429                .unwrap();
11430            conn.execute(
11431                "CREATE VIEW order_details AS \
11432             SELECT o.id as order_id, p.name, o.qty, \
11433             p.price * o.qty as total \
11434             FROM orders o JOIN products p ON o.product_id = p.id",
11435            )
11436            .await
11437            .unwrap();
11438            let r = conn
11439                .query(
11440                    "SELECT order_id, name, total \
11441                 FROM order_details ORDER BY order_id",
11442                )
11443                .await
11444                .unwrap();
11445            assert_eq!(row_values(&r[0])[1].to_text(), "Widget");
11446            assert_eq!(row_values(&r[0])[2].to_text(), "99.9");
11447            assert_eq!(row_values(&r[1])[1].to_text(), "Gizmo");
11448        });
11449    }
11450
11451    #[test]
11452    fn conformance_028_create_view_if_not_exists() {
11453        asupersync::test_utils::run_test(|| async {
11454            let conn = Connection::open(":memory:").await.unwrap();
11455            conn.execute("CREATE TABLE t1(a INTEGER)").await.unwrap();
11456            conn.execute("INSERT INTO t1 VALUES (1), (2), (3)")
11457                .await
11458                .unwrap();
11459            conn.execute("CREATE VIEW v1 AS SELECT a FROM t1")
11460                .await
11461                .unwrap();
11462            conn.execute("CREATE VIEW IF NOT EXISTS v1 AS SELECT 999")
11463                .await
11464                .unwrap();
11465            let r = conn.query("SELECT COUNT(*) FROM v1").await.unwrap();
11466            assert_eq!(row_values(&r[0])[0].to_text(), "3");
11467        });
11468    }
11469
11470    // -----------------------------------------------------------------------
11471    // Conformance suite 029: GROUP BY and HAVING
11472    // -----------------------------------------------------------------------
11473
11474    #[test]
11475    fn conformance_029_group_by_basic() {
11476        asupersync::test_utils::run_test(|| async {
11477            let conn = Connection::open(":memory:").await.unwrap();
11478            conn.execute(
11479                "CREATE TABLE orders(\
11480             id INTEGER PRIMARY KEY, customer TEXT, amount REAL)",
11481            )
11482            .await
11483            .unwrap();
11484            conn.execute(
11485                "INSERT INTO orders VALUES \
11486             (1,'Alice',50.0),(2,'Bob',30.0),(3,'Alice',70.0),\
11487             (4,'Bob',20.0),(5,'Charlie',100.0)",
11488            )
11489            .await
11490            .unwrap();
11491            let r = conn
11492                .query(
11493                    "SELECT customer, SUM(amount) as total \
11494                 FROM orders GROUP BY customer ORDER BY customer",
11495                )
11496                .await
11497                .unwrap();
11498            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11499            assert_eq!(row_values(&r[0])[1].to_text(), "120.0");
11500            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
11501            assert_eq!(row_values(&r[1])[1].to_text(), "50.0");
11502        });
11503    }
11504
11505    #[test]
11506    fn conformance_029_having() {
11507        asupersync::test_utils::run_test(|| async {
11508            let conn = Connection::open(":memory:").await.unwrap();
11509            conn.execute(
11510                "CREATE TABLE orders(\
11511             id INTEGER PRIMARY KEY, customer TEXT, amount REAL)",
11512            )
11513            .await
11514            .unwrap();
11515            conn.execute(
11516                "INSERT INTO orders VALUES \
11517             (1,'Alice',50.0),(2,'Bob',30.0),(3,'Alice',70.0),\
11518             (4,'Bob',20.0),(5,'Charlie',100.0)",
11519            )
11520            .await
11521            .unwrap();
11522            let r = conn
11523                .query(
11524                    "SELECT customer, SUM(amount) as total \
11525                 FROM orders GROUP BY customer \
11526                 HAVING total > 60 ORDER BY customer",
11527                )
11528                .await
11529                .unwrap();
11530            assert_eq!(r.len(), 2);
11531            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11532            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
11533        });
11534    }
11535
11536    #[test]
11537    fn conformance_029_group_by_count_min_max_avg() {
11538        asupersync::test_utils::run_test(|| async {
11539            let conn = Connection::open(":memory:").await.unwrap();
11540            conn.execute(
11541                "CREATE TABLE scores(\
11542             student TEXT, subject TEXT, score INTEGER)",
11543            )
11544            .await
11545            .unwrap();
11546            conn.execute(
11547                "INSERT INTO scores VALUES \
11548             ('Alice','Math',90),('Alice','Sci',85),\
11549             ('Bob','Math',70),('Bob','Sci',80)",
11550            )
11551            .await
11552            .unwrap();
11553            let r = conn
11554                .query(
11555                    "SELECT student, COUNT(*) as cnt, MIN(score), MAX(score) \
11556                 FROM scores GROUP BY student ORDER BY student",
11557                )
11558                .await
11559                .unwrap();
11560            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11561            assert_eq!(row_values(&r[0])[1].to_text(), "2");
11562            assert_eq!(row_values(&r[0])[2].to_text(), "85");
11563            assert_eq!(row_values(&r[0])[3].to_text(), "90");
11564        });
11565    }
11566
11567    #[test]
11568    fn conformance_029_group_by_multi_column() {
11569        asupersync::test_utils::run_test(|| async {
11570            let conn = Connection::open(":memory:").await.unwrap();
11571            conn.execute(
11572                "CREATE TABLE log(\
11573             dept TEXT, year INTEGER, revenue REAL)",
11574            )
11575            .await
11576            .unwrap();
11577            conn.execute(
11578                "INSERT INTO log VALUES \
11579             ('eng',2024,100.0),('eng',2024,200.0),\
11580             ('eng',2025,150.0),('sales',2024,80.0)",
11581            )
11582            .await
11583            .unwrap();
11584            let r = conn
11585                .query(
11586                    "SELECT dept, year, SUM(revenue) as total \
11587                 FROM log GROUP BY dept, year ORDER BY dept, year",
11588                )
11589                .await
11590                .unwrap();
11591            assert_eq!(r.len(), 3);
11592            assert_eq!(row_values(&r[0])[0].to_text(), "eng");
11593            assert_eq!(row_values(&r[0])[1].to_text(), "2024");
11594            assert_eq!(row_values(&r[0])[2].to_text(), "300.0");
11595        });
11596    }
11597
11598    // -----------------------------------------------------------------------
11599    // Conformance suite 030: CASE expressions
11600    // -----------------------------------------------------------------------
11601
11602    #[test]
11603    fn conformance_030_case_searched() {
11604        asupersync::test_utils::run_test(|| async {
11605            let conn = Connection::open(":memory:").await.unwrap();
11606            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, score INTEGER)")
11607                .await
11608                .unwrap();
11609            conn.execute("INSERT INTO t1 VALUES (1, 95), (2, 72), (3, 45)")
11610                .await
11611                .unwrap();
11612            let r = conn
11613                .query(
11614                    "SELECT id, \
11615                 CASE WHEN score >= 90 THEN 'A' \
11616                      WHEN score >= 70 THEN 'B' \
11617                      ELSE 'F' END as grade \
11618                 FROM t1 ORDER BY id",
11619                )
11620                .await
11621                .unwrap();
11622            assert_eq!(row_values(&r[0])[1].to_text(), "A");
11623            assert_eq!(row_values(&r[1])[1].to_text(), "B");
11624            assert_eq!(row_values(&r[2])[1].to_text(), "F");
11625        });
11626    }
11627
11628    #[test]
11629    fn conformance_030_case_simple() {
11630        asupersync::test_utils::run_test(|| async {
11631            let conn = Connection::open(":memory:").await.unwrap();
11632            let r = conn
11633                .query(
11634                    "SELECT CASE 2 \
11635                 WHEN 1 THEN 'one' \
11636                 WHEN 2 THEN 'two' \
11637                 WHEN 3 THEN 'three' \
11638                 ELSE 'other' END",
11639                )
11640                .await
11641                .unwrap();
11642            assert_eq!(row_values(&r[0])[0].to_text(), "two");
11643        });
11644    }
11645
11646    #[test]
11647    fn conformance_030_case_null() {
11648        asupersync::test_utils::run_test(|| async {
11649            let conn = Connection::open(":memory:").await.unwrap();
11650            let r = conn
11651                .query(
11652                    "SELECT CASE NULL \
11653                 WHEN NULL THEN 'match' \
11654                 ELSE 'no match' END",
11655                )
11656                .await
11657                .unwrap();
11658            assert_eq!(row_values(&r[0])[0].to_text(), "no match");
11659            let r = conn
11660                .query(
11661                    "SELECT CASE WHEN NULL THEN 'truthy' \
11662                 ELSE 'falsy' END",
11663                )
11664                .await
11665                .unwrap();
11666            assert_eq!(row_values(&r[0])[0].to_text(), "falsy");
11667        });
11668    }
11669
11670    #[test]
11671    fn conformance_030_case_in_aggregate() {
11672        asupersync::test_utils::run_test(|| async {
11673            let conn = Connection::open(":memory:").await.unwrap();
11674            conn.execute("CREATE TABLE items(id INTEGER PRIMARY KEY, status TEXT)")
11675                .await
11676                .unwrap();
11677            conn.execute(
11678                "INSERT INTO items VALUES \
11679             (1,'active'),(2,'inactive'),(3,'active'),\
11680             (4,'active'),(5,'inactive')",
11681            )
11682            .await
11683            .unwrap();
11684            let r = conn
11685                .query(
11686                    "SELECT SUM(CASE WHEN status = 'active' \
11687                 THEN 1 ELSE 0 END) as active_count FROM items",
11688                )
11689                .await
11690                .unwrap();
11691            assert_eq!(row_values(&r[0])[0].to_text(), "3");
11692        });
11693    }
11694
11695    // -----------------------------------------------------------------------
11696    // Conformance suite 031: INSERT conflict handling
11697    // -----------------------------------------------------------------------
11698
11699    #[test]
11700    fn conformance_031_insert_or_replace() {
11701        asupersync::test_utils::run_test(|| async {
11702            let conn = Connection::open(":memory:").await.unwrap();
11703            conn.execute("CREATE TABLE kv(key TEXT PRIMARY KEY, value TEXT)")
11704                .await
11705                .unwrap();
11706            conn.execute("INSERT INTO kv VALUES ('a', 'first')")
11707                .await
11708                .unwrap();
11709            conn.execute("INSERT OR REPLACE INTO kv VALUES ('a', 'replaced')")
11710                .await
11711                .unwrap();
11712            let r = conn
11713                .query("SELECT value FROM kv WHERE key = 'a'")
11714                .await
11715                .unwrap();
11716            assert_eq!(row_values(&r[0])[0].to_text(), "replaced");
11717        });
11718    }
11719
11720    #[test]
11721    fn conformance_031_insert_or_ignore() {
11722        asupersync::test_utils::run_test(|| async {
11723            let conn = Connection::open(":memory:").await.unwrap();
11724            conn.execute("CREATE TABLE kv(key TEXT PRIMARY KEY, value TEXT)")
11725                .await
11726                .unwrap();
11727            conn.execute("INSERT INTO kv VALUES ('a', 'first')")
11728                .await
11729                .unwrap();
11730            conn.execute("INSERT OR IGNORE INTO kv VALUES ('a', 'ignored')")
11731                .await
11732                .unwrap();
11733            let r = conn
11734                .query("SELECT value FROM kv WHERE key = 'a'")
11735                .await
11736                .unwrap();
11737            assert_eq!(row_values(&r[0])[0].to_text(), "first");
11738            conn.execute("INSERT OR IGNORE INTO kv VALUES ('b', 'new')")
11739                .await
11740                .unwrap();
11741            // Verify both rows exist (original 'a' + new 'b').
11742            let r = conn.query("SELECT key FROM kv ORDER BY key").await.unwrap();
11743            assert_eq!(r.len(), 2);
11744            assert_eq!(row_values(&r[0])[0].to_text(), "a");
11745            assert_eq!(row_values(&r[1])[0].to_text(), "b");
11746        });
11747    }
11748
11749    #[test]
11750    fn conformance_031_replace_into() {
11751        asupersync::test_utils::run_test(|| async {
11752            let conn = Connection::open(":memory:").await.unwrap();
11753            conn.execute(
11754                "CREATE TABLE t1(\
11755             id INTEGER PRIMARY KEY, name TEXT, \
11756             score INTEGER DEFAULT 0)",
11757            )
11758            .await
11759            .unwrap();
11760            conn.execute("INSERT INTO t1 VALUES (1, 'Alice', 100)")
11761                .await
11762                .unwrap();
11763            conn.execute("REPLACE INTO t1 VALUES (1, 'Alice Updated', 200)")
11764                .await
11765                .unwrap();
11766            let r = conn
11767                .query("SELECT name, score FROM t1 WHERE id = 1")
11768                .await
11769                .unwrap();
11770            assert_eq!(row_values(&r[0])[0].to_text(), "Alice Updated");
11771            assert_eq!(row_values(&r[0])[1].to_text(), "200");
11772        });
11773    }
11774
11775    // -----------------------------------------------------------------------
11776    // Conformance suite 032: ALTER TABLE
11777    // -----------------------------------------------------------------------
11778
11779    #[test]
11780    fn conformance_032_rename_table() {
11781        asupersync::test_utils::run_test(|| async {
11782            let conn = Connection::open(":memory:").await.unwrap();
11783            conn.execute(
11784                "CREATE TABLE original(\
11785             id INTEGER PRIMARY KEY, name TEXT)",
11786            )
11787            .await
11788            .unwrap();
11789            conn.execute("INSERT INTO original VALUES (1, 'Alice'), (2, 'Bob')")
11790                .await
11791                .unwrap();
11792            conn.execute("ALTER TABLE original RENAME TO renamed")
11793                .await
11794                .unwrap();
11795            let r = conn
11796                .query("SELECT name FROM renamed ORDER BY id")
11797                .await
11798                .unwrap();
11799            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11800            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
11801        });
11802    }
11803
11804    #[test]
11805    fn conformance_032_add_column() {
11806        asupersync::test_utils::run_test(|| async {
11807            let conn = Connection::open(":memory:").await.unwrap();
11808            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
11809                .await
11810                .unwrap();
11811            conn.execute("INSERT INTO t1 VALUES (1, 'Alice'), (2, 'Bob')")
11812                .await
11813                .unwrap();
11814            conn.execute("ALTER TABLE t1 ADD COLUMN score INTEGER DEFAULT 0")
11815                .await
11816                .unwrap();
11817            let r = conn
11818                .query("SELECT name, score FROM t1 ORDER BY id")
11819                .await
11820                .unwrap();
11821            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11822            assert_eq!(row_values(&r[0])[1].to_text(), "0");
11823            conn.execute("INSERT INTO t1 VALUES (3, 'Charlie', 95)")
11824                .await
11825                .unwrap();
11826            let r = conn
11827                .query("SELECT name, score FROM t1 WHERE id = 3")
11828                .await
11829                .unwrap();
11830            assert_eq!(row_values(&r[0])[0].to_text(), "Charlie");
11831            assert_eq!(row_values(&r[0])[1].to_text(), "95");
11832        });
11833    }
11834
11835    #[test]
11836    fn conformance_032_add_multiple_columns() {
11837        asupersync::test_utils::run_test(|| async {
11838            let conn = Connection::open(":memory:").await.unwrap();
11839            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
11840                .await
11841                .unwrap();
11842            conn.execute("INSERT INTO t1 VALUES (1, 'Alice'), (2, 'Bob')")
11843                .await
11844                .unwrap();
11845            conn.execute("ALTER TABLE t1 ADD COLUMN score INTEGER DEFAULT 0")
11846                .await
11847                .unwrap();
11848            conn.execute("ALTER TABLE t1 ADD COLUMN active INTEGER DEFAULT 1")
11849                .await
11850                .unwrap();
11851            let r = conn
11852                .query("SELECT name, active FROM t1 ORDER BY id")
11853                .await
11854                .unwrap();
11855            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
11856            assert_eq!(row_values(&r[0])[1].to_text(), "1");
11857            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
11858            assert_eq!(row_values(&r[1])[1].to_text(), "1");
11859        });
11860    }
11861
11862    #[test]
11863    fn alter_table_preserves_without_rowid_in_schema_sql() {
11864        asupersync::test_utils::run_test(|| async {
11865            let conn = Connection::open(":memory:").await.unwrap();
11866            conn.execute("CREATE TABLE wr(id INTEGER PRIMARY KEY, body TEXT) WITHOUT ROWID;")
11867                .await
11868                .unwrap();
11869            conn.execute("ALTER TABLE wr ADD COLUMN extra INTEGER DEFAULT 0;")
11870                .await
11871                .unwrap();
11872
11873            let rows = conn
11874                .query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'wr';")
11875                .await
11876                .unwrap();
11877            let sql = row_values(&rows[0])[0].to_text();
11878            assert!(
11879                sql.to_ascii_uppercase().contains("WITHOUT ROWID"),
11880                "ALTER TABLE must preserve WITHOUT ROWID in sqlite_master SQL: {sql}"
11881            );
11882        });
11883    }
11884
11885    #[test]
11886    fn alter_table_preserves_typeless_columns_in_schema_sql() {
11887        asupersync::test_utils::run_test(|| async {
11888            let conn = Connection::open(":memory:").await.unwrap();
11889            conn.execute("CREATE TABLE typeless(payload);")
11890                .await
11891                .unwrap();
11892            conn.execute("ALTER TABLE typeless ADD COLUMN note;")
11893                .await
11894                .unwrap();
11895
11896            let rows = conn
11897                .query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'typeless';")
11898                .await
11899                .unwrap();
11900            let sql = row_values(&rows[0])[0].to_text();
11901            let Some((Statement::CreateTable(create), _)) =
11902                parse_first_statement_with_tail(&sql).expect("sqlite_master sql should parse")
11903            else {
11904                panic!("expected CREATE TABLE sql, got: {sql}");
11905            };
11906            let CreateTableBody::Columns { columns, .. } = create.body else {
11907                panic!("expected CREATE TABLE column definition body, got: {sql}");
11908            };
11909            assert_eq!(
11910                columns.len(),
11911                2,
11912                "ALTER TABLE should preserve both columns in sqlite_master sql: {sql}"
11913            );
11914            assert_eq!(columns[0].name, "payload", "{sql}");
11915            assert!(
11916                columns[0].type_name.is_none(),
11917                "ALTER TABLE must not synthesize a declared type for existing typeless columns: {sql}"
11918            );
11919            assert_eq!(columns[1].name, "note", "{sql}");
11920            assert!(
11921                columns[1].type_name.is_none(),
11922                "ALTER TABLE must not synthesize a declared type for added typeless columns: {sql}"
11923            );
11924        });
11925    }
11926
11927    #[test]
11928    fn alter_table_preserves_embedded_quote_identifiers_in_schema_sql() {
11929        asupersync::test_utils::run_test(|| async {
11930            let conn = Connection::open(":memory:").await.unwrap();
11931            conn.execute(r#"CREATE TABLE "te""st"("co""l" TEXT PRIMARY KEY);"#)
11932                .await
11933                .unwrap();
11934            conn.execute(r#"ALTER TABLE "te""st" ADD COLUMN "no""te" TEXT;"#)
11935                .await
11936                .unwrap();
11937
11938            let rows = conn
11939                .query(r#"SELECT sql FROM sqlite_master WHERE type='table' AND name='te"st';"#)
11940                .await
11941                .unwrap();
11942            let sql = row_values(&rows[0])[0].to_text();
11943            let Some((Statement::CreateTable(create), _)) =
11944                parse_first_statement_with_tail(&sql).expect("sqlite_master sql should parse")
11945            else {
11946                panic!("expected CREATE TABLE sql, got: {sql}");
11947            };
11948            let CreateTableBody::Columns { columns, .. } = create.body else {
11949                panic!("expected CREATE TABLE column definition body, got: {sql}");
11950            };
11951            assert_eq!(create.name.name, "te\"st", "{sql}");
11952            assert_eq!(columns[0].name, "co\"l", "{sql}");
11953            assert_eq!(columns[1].name, "no\"te", "{sql}");
11954        });
11955    }
11956
11957    #[test]
11958    fn alter_table_rename_preserves_embedded_quote_identifiers_in_index_sql() {
11959        asupersync::test_utils::run_test(|| async {
11960            let conn = Connection::open(":memory:").await.unwrap();
11961            conn.execute(r#"CREATE TABLE "te""st"("co""l" TEXT);"#)
11962                .await
11963                .unwrap();
11964            conn.execute(r#"CREATE INDEX "ix""q" ON "te""st"("co""l");"#)
11965                .await
11966                .unwrap();
11967            conn.execute(r#"ALTER TABLE "te""st" RENAME TO "ta""rget";"#)
11968                .await
11969                .unwrap();
11970
11971            let rows = conn
11972                .query(r#"SELECT sql FROM sqlite_master WHERE type='index' AND name='ix"q';"#)
11973                .await
11974                .unwrap();
11975            let sql = row_values(&rows[0])[0].to_text();
11976            let Some((Statement::CreateIndex(create), _)) =
11977                parse_first_statement_with_tail(&sql).expect("sqlite_master sql should parse")
11978            else {
11979                panic!("expected CREATE INDEX sql, got: {sql}");
11980            };
11981            assert_eq!(create.name.name, "ix\"q", "{sql}");
11982            assert_eq!(create.table, "ta\"rget", "{sql}");
11983        });
11984    }
11985
11986    #[test]
11987    fn foreign_key_cascade_supports_embedded_quote_identifiers() {
11988        asupersync::test_utils::run_test(|| async {
11989            let conn = Connection::open(":memory:").await.unwrap();
11990            conn.execute("PRAGMA foreign_keys = ON;").await.unwrap();
11991            conn.execute(r#"CREATE TABLE "par""ent"("id""x" INTEGER PRIMARY KEY);"#)
11992                .await
11993                .unwrap();
11994            conn.execute(
11995            r#"CREATE TABLE "chi""ld"("fk""x" INTEGER REFERENCES "par""ent"("id""x") ON DELETE CASCADE);"#,
11996        )
11997        .await
11998        .unwrap();
11999            conn.execute(r#"INSERT INTO "par""ent"("id""x") VALUES (1);"#)
12000                .await
12001                .unwrap();
12002            conn.execute(r#"INSERT INTO "chi""ld"("fk""x") VALUES (1);"#)
12003                .await
12004                .unwrap();
12005
12006            conn.execute(r#"DELETE FROM "par""ent" WHERE "id""x" = 1;"#)
12007                .await
12008                .unwrap();
12009
12010            let rows = conn
12011                .query(r#"SELECT COUNT(*) FROM "chi""ld";"#)
12012                .await
12013                .unwrap();
12014            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(0));
12015        });
12016    }
12017
12018    // -----------------------------------------------------------------------
12019    // Conformance suite 033: JOINs
12020    // -----------------------------------------------------------------------
12021
12022    #[test]
12023    fn conformance_033_inner_join() {
12024        asupersync::test_utils::run_test(|| async {
12025            let conn = Connection::open(":memory:").await.unwrap();
12026            conn.execute("CREATE TABLE dept(id INTEGER PRIMARY KEY, name TEXT)")
12027                .await
12028                .unwrap();
12029            conn.execute("CREATE TABLE emp(id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER)")
12030                .await
12031                .unwrap();
12032            conn.execute("INSERT INTO dept VALUES (1,'Eng'),(2,'Sales'),(3,'HR')")
12033                .await
12034                .unwrap();
12035            conn.execute(
12036                "INSERT INTO emp VALUES (1,'Alice',1),(2,'Bob',2),(3,'Charlie',1),(4,'Diana',2)",
12037            )
12038            .await
12039            .unwrap();
12040            let r = conn
12041                .query(
12042                    "SELECT emp.name, dept.name FROM emp \
12043                 INNER JOIN dept ON emp.dept_id = dept.id \
12044                 ORDER BY emp.name",
12045                )
12046                .await
12047                .unwrap();
12048            assert_eq!(r.len(), 4);
12049            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12050            assert_eq!(row_values(&r[0])[1].to_text(), "Eng");
12051            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12052            assert_eq!(row_values(&r[1])[1].to_text(), "Sales");
12053            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie");
12054            assert_eq!(row_values(&r[2])[1].to_text(), "Eng");
12055            assert_eq!(row_values(&r[3])[0].to_text(), "Diana");
12056            assert_eq!(row_values(&r[3])[1].to_text(), "Sales");
12057        });
12058    }
12059
12060    #[test]
12061    fn conformance_033_left_join() {
12062        asupersync::test_utils::run_test(|| async {
12063            let conn = Connection::open(":memory:").await.unwrap();
12064            conn.execute("CREATE TABLE dept(id INTEGER PRIMARY KEY, name TEXT)")
12065                .await
12066                .unwrap();
12067            conn.execute("CREATE TABLE emp(id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER)")
12068                .await
12069                .unwrap();
12070            conn.execute("INSERT INTO dept VALUES (1,'Eng'),(2,'Sales'),(3,'HR')")
12071                .await
12072                .unwrap();
12073            conn.execute("INSERT INTO emp VALUES (1,'Alice',1),(2,'Bob',2)")
12074                .await
12075                .unwrap();
12076            let r = conn
12077                .query(
12078                    "SELECT dept.name, emp.name FROM dept \
12079                 LEFT JOIN emp ON dept.id = emp.dept_id \
12080                 ORDER BY dept.name",
12081                )
12082                .await
12083                .unwrap();
12084            assert_eq!(r.len(), 3);
12085            // Eng has Alice
12086            assert_eq!(row_values(&r[0])[0].to_text(), "Eng");
12087            assert_eq!(row_values(&r[0])[1].to_text(), "Alice");
12088            // HR has no employees — NULL
12089            assert_eq!(row_values(&r[1])[0].to_text(), "HR");
12090            assert!(row_values(&r[1])[1].is_null());
12091            // Sales has Bob
12092            assert_eq!(row_values(&r[2])[0].to_text(), "Sales");
12093            assert_eq!(row_values(&r[2])[1].to_text(), "Bob");
12094        });
12095    }
12096
12097    #[test]
12098    fn conformance_033_cross_join() {
12099        asupersync::test_utils::run_test(|| async {
12100            let conn = Connection::open(":memory:").await.unwrap();
12101            conn.execute("CREATE TABLE colors(c TEXT)").await.unwrap();
12102            conn.execute("CREATE TABLE sizes(s TEXT)").await.unwrap();
12103            conn.execute("INSERT INTO colors VALUES ('red'),('blue')")
12104                .await
12105                .unwrap();
12106            conn.execute("INSERT INTO sizes VALUES ('S'),('M'),('L')")
12107                .await
12108                .unwrap();
12109            let r = conn
12110                .query("SELECT c, s FROM colors CROSS JOIN sizes ORDER BY c, s")
12111                .await
12112                .unwrap();
12113            assert_eq!(r.len(), 6);
12114            assert_eq!(row_values(&r[0])[0].to_text(), "blue");
12115            assert_eq!(row_values(&r[0])[1].to_text(), "L");
12116            assert_eq!(row_values(&r[5])[0].to_text(), "red");
12117            assert_eq!(row_values(&r[5])[1].to_text(), "S");
12118        });
12119    }
12120
12121    #[test]
12122    fn conformance_033_self_join() {
12123        asupersync::test_utils::run_test(|| async {
12124            let conn = Connection::open(":memory:").await.unwrap();
12125            conn.execute("CREATE TABLE emp(id INTEGER PRIMARY KEY, name TEXT, mgr_id INTEGER)")
12126                .await
12127                .unwrap();
12128            conn.execute(
12129                "INSERT INTO emp VALUES (1,'Boss',NULL),(2,'Alice',1),(3,'Bob',1),(4,'Charlie',2)",
12130            )
12131            .await
12132            .unwrap();
12133            let r = conn
12134                .query(
12135                    "SELECT e.name, m.name FROM emp e \
12136                 INNER JOIN emp m ON e.mgr_id = m.id \
12137                 ORDER BY e.name",
12138                )
12139                .await
12140                .unwrap();
12141            assert_eq!(r.len(), 3);
12142            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12143            assert_eq!(row_values(&r[0])[1].to_text(), "Boss");
12144            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12145            assert_eq!(row_values(&r[1])[1].to_text(), "Boss");
12146            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie");
12147            assert_eq!(row_values(&r[2])[1].to_text(), "Alice");
12148        });
12149    }
12150
12151    #[test]
12152    fn regression_join_literal_text_numeric_comparison_uses_storage_class_order() {
12153        asupersync::test_utils::run_test(|| async {
12154            let conn = Connection::open(":memory:").await.unwrap();
12155            conn.execute("CREATE TABLE a(x INTEGER)").await.unwrap();
12156            conn.execute("CREATE TABLE b(y INTEGER)").await.unwrap();
12157            conn.execute("INSERT INTO a VALUES (1)").await.unwrap();
12158            conn.execute("INSERT INTO b VALUES (2)").await.unwrap();
12159
12160            let rows = conn
12161                .query("SELECT a.x FROM a JOIN b ON '123' = 123;")
12162                .await
12163                .unwrap();
12164            assert_eq!(rows.len(), 0);
12165
12166            let rows = conn
12167                .query("SELECT a.x FROM a JOIN b ON '123' < 124;")
12168                .await
12169                .unwrap();
12170            assert_eq!(rows.len(), 0);
12171
12172            let rows = conn
12173                .query("SELECT a.x FROM (SELECT 1 AS x) a JOIN (SELECT 2 AS y) b ON '123' = 123;")
12174                .await
12175                .unwrap();
12176            assert_eq!(rows.len(), 0);
12177
12178            let rows = conn
12179                .query("SELECT a.x FROM (SELECT 1 AS x) a JOIN (SELECT 2 AS y) b ON '123' < 124;")
12180                .await
12181                .unwrap();
12182            assert_eq!(rows.len(), 0);
12183
12184            let rows = conn
12185                .query("SELECT a.x FROM a JOIN b ON a.rowid = '1';")
12186                .await
12187                .unwrap();
12188            assert_eq!(rows.len(), 1);
12189
12190            let rows = conn
12191                .query("SELECT a.x FROM a JOIN b ON a.rowid > '0';")
12192                .await
12193                .unwrap();
12194            assert_eq!(rows.len(), 1);
12195
12196            conn.execute("CREATE TABLE txt(v TEXT)").await.unwrap();
12197            conn.execute("CREATE TABLE num(v NUMERIC)").await.unwrap();
12198            conn.execute("INSERT INTO txt VALUES ('9')").await.unwrap();
12199            conn.execute("INSERT INTO num VALUES (CAST('9' AS TEXT))")
12200                .await
12201                .unwrap();
12202
12203            let rows = conn
12204                .query("SELECT v FROM (SELECT v FROM txt) a JOIN (SELECT 10 AS n) b ON v < n;")
12205                .await
12206                .unwrap();
12207            assert_eq!(rows.len(), 0);
12208
12209            let rows = conn
12210                .query("SELECT v FROM (SELECT v FROM num) a JOIN (SELECT 10 AS n) b ON v < n;")
12211                .await
12212                .unwrap();
12213            assert_eq!(rows.len(), 1);
12214        });
12215    }
12216
12217    #[test]
12218    fn regression_join_ambiguous_column_surfaces_typed_error() {
12219        asupersync::test_utils::run_test(|| async {
12220            let conn = Connection::open(":memory:").await.unwrap();
12221            conn.execute("CREATE TABLE a(x INTEGER)").await.unwrap();
12222            conn.execute("CREATE TABLE b(x INTEGER)").await.unwrap();
12223
12224            let err = conn
12225                .query("SELECT a.x FROM a JOIN b ON x = 1;")
12226                .await
12227                .expect_err("unqualified duplicate JOIN column should fail");
12228            assert!(
12229                matches!(err, FrankenError::AmbiguousColumn { ref name } if name == "x"),
12230                "unexpected error: {err:?}"
12231            );
12232        });
12233    }
12234
12235    #[test]
12236    fn regression_table_alias_hides_base_table_qualifier() {
12237        asupersync::test_utils::run_test(|| async {
12238            let conn = Connection::open(":memory:").await.unwrap();
12239            conn.execute("CREATE TABLE t(x INTEGER)").await.unwrap();
12240
12241            let err = conn
12242                .query("SELECT t.x FROM t AS a;")
12243                .await
12244                .expect_err("base table qualifier should not resolve after aliasing");
12245            let message = err.to_string();
12246            assert!(
12247                message.contains("no such table: t") || message.contains("no such column"),
12248                "unexpected error: {err:?}"
12249            );
12250        });
12251    }
12252
12253    async fn assert_wrong_function_arity(conn: &Connection, sql: &str, name: &str) {
12254        let err = conn
12255            .query(sql)
12256            .await
12257            .expect_err("known function with wrong arity should fail");
12258        let expected = format!("wrong number of arguments to function {name}()");
12259        assert!(
12260            matches!(&err, FrankenError::FunctionError(message) if message == &expected),
12261            "unexpected error for {sql}: {err:?}"
12262        );
12263    }
12264
12265    #[test]
12266    fn regression_aggregate_wrong_arity_surfaces_function_error() {
12267        asupersync::test_utils::run_test(|| async {
12268            let conn = Connection::open(":memory:").await.unwrap();
12269            conn.execute("CREATE TABLE t(v INTEGER, s TEXT)")
12270                .await
12271                .unwrap();
12272            conn.execute("INSERT INTO t VALUES (1, 'a'), (2, 'b')")
12273                .await
12274                .unwrap();
12275
12276            assert_wrong_function_arity(&conn, "SELECT sum() FROM t;", "sum").await;
12277            assert_wrong_function_arity(&conn, "SELECT group_concat() FROM t;", "group_concat")
12278                .await;
12279            assert_wrong_function_arity(
12280                &conn,
12281                "SELECT group_concat(s, '-', '!') FROM t;",
12282                "group_concat",
12283            )
12284            .await;
12285        });
12286    }
12287
12288    #[test]
12289    fn regression_window_wrong_arity_surfaces_function_error() {
12290        asupersync::test_utils::run_test(|| async {
12291            let conn = Connection::open(":memory:").await.unwrap();
12292            conn.execute("CREATE TABLE t(v INTEGER)").await.unwrap();
12293            conn.execute("INSERT INTO t VALUES (1), (2)").await.unwrap();
12294
12295            assert_wrong_function_arity(&conn, "SELECT rank(1) OVER (ORDER BY v) FROM t;", "rank")
12296                .await;
12297            assert_wrong_function_arity(&conn, "SELECT lag() OVER (ORDER BY v) FROM t;", "lag")
12298                .await;
12299            assert_wrong_function_arity(
12300                &conn,
12301                "SELECT lag(v, 1, 0, 0) OVER (ORDER BY v) FROM t;",
12302                "lag",
12303            )
12304            .await;
12305            assert_wrong_function_arity(&conn, "SELECT count(1, 2) OVER () FROM t;", "count").await;
12306        });
12307    }
12308
12309    #[test]
12310    fn conformance_033_join_with_where() {
12311        asupersync::test_utils::run_test(|| async {
12312            let conn = Connection::open(":memory:").await.unwrap();
12313            conn.execute("CREATE TABLE dept(id INTEGER PRIMARY KEY, name TEXT)")
12314                .await
12315                .unwrap();
12316            conn.execute(
12317            "CREATE TABLE emp(id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER, salary INTEGER)",
12318        )
12319        .await
12320        .unwrap();
12321            conn.execute("INSERT INTO dept VALUES (1,'Eng'),(2,'Sales')")
12322                .await
12323                .unwrap();
12324            conn.execute(
12325                "INSERT INTO emp VALUES (1,'Alice',1,100),(2,'Bob',2,80),(3,'Charlie',1,120)",
12326            )
12327            .await
12328            .unwrap();
12329            let r = conn
12330                .query(
12331                    "SELECT emp.name, dept.name FROM emp \
12332                 JOIN dept ON emp.dept_id = dept.id \
12333                 WHERE emp.salary > 90 ORDER BY emp.name",
12334                )
12335                .await
12336                .unwrap();
12337            assert_eq!(r.len(), 2);
12338            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12339            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
12340        });
12341    }
12342
12343    // -----------------------------------------------------------------------
12344    // Conformance suite 034: UPDATE
12345    // -----------------------------------------------------------------------
12346
12347    #[test]
12348    fn conformance_034_update_basic() {
12349        asupersync::test_utils::run_test(|| async {
12350            let conn = Connection::open(":memory:").await.unwrap();
12351            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT, score INTEGER)")
12352                .await
12353                .unwrap();
12354            conn.execute("INSERT INTO t1 VALUES (1,'Alice',80),(2,'Bob',90),(3,'Charlie',70)")
12355                .await
12356                .unwrap();
12357            conn.execute("UPDATE t1 SET score = 95 WHERE id = 2")
12358                .await
12359                .unwrap();
12360            let r = conn
12361                .query("SELECT score FROM t1 WHERE id = 2")
12362                .await
12363                .unwrap();
12364            assert_eq!(row_values(&r[0])[0].to_text(), "95");
12365        });
12366    }
12367
12368    #[test]
12369    fn conformance_034_update_multiple_columns() {
12370        asupersync::test_utils::run_test(|| async {
12371            let conn = Connection::open(":memory:").await.unwrap();
12372            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT, score INTEGER)")
12373                .await
12374                .unwrap();
12375            conn.execute("INSERT INTO t1 VALUES (1,'Alice',80)")
12376                .await
12377                .unwrap();
12378            conn.execute("UPDATE t1 SET name = 'Alicia', score = 99 WHERE id = 1")
12379                .await
12380                .unwrap();
12381            let r = conn
12382                .query("SELECT name, score FROM t1 WHERE id = 1")
12383                .await
12384                .unwrap();
12385            assert_eq!(row_values(&r[0])[0].to_text(), "Alicia");
12386            assert_eq!(row_values(&r[0])[1].to_text(), "99");
12387        });
12388    }
12389
12390    #[test]
12391    fn conformance_034_update_all_rows() {
12392        asupersync::test_utils::run_test(|| async {
12393            let conn = Connection::open(":memory:").await.unwrap();
12394            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, active INTEGER)")
12395                .await
12396                .unwrap();
12397            conn.execute("INSERT INTO t1 VALUES (1,1),(2,1),(3,0)")
12398                .await
12399                .unwrap();
12400            conn.execute("UPDATE t1 SET active = 0").await.unwrap();
12401            let r = conn
12402                .query("SELECT active FROM t1 ORDER BY id")
12403                .await
12404                .unwrap();
12405            assert_eq!(r.len(), 3);
12406            assert_eq!(row_values(&r[0])[0].to_text(), "0");
12407            assert_eq!(row_values(&r[1])[0].to_text(), "0");
12408            assert_eq!(row_values(&r[2])[0].to_text(), "0");
12409        });
12410    }
12411
12412    #[test]
12413    fn conformance_034_update_with_expression() {
12414        asupersync::test_utils::run_test(|| async {
12415            let conn = Connection::open(":memory:").await.unwrap();
12416            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
12417                .await
12418                .unwrap();
12419            conn.execute("INSERT INTO t1 VALUES (1,10),(2,20),(3,30)")
12420                .await
12421                .unwrap();
12422            conn.execute("UPDATE t1 SET val = val * 2 WHERE id <= 2")
12423                .await
12424                .unwrap();
12425            let r = conn.query("SELECT val FROM t1 ORDER BY id").await.unwrap();
12426            assert_eq!(row_values(&r[0])[0].to_text(), "20");
12427            assert_eq!(row_values(&r[1])[0].to_text(), "40");
12428            assert_eq!(row_values(&r[2])[0].to_text(), "30");
12429        });
12430    }
12431
12432    // -----------------------------------------------------------------------
12433    // Conformance suite 035: DELETE
12434    // -----------------------------------------------------------------------
12435
12436    #[test]
12437    fn conformance_035_delete_with_where() {
12438        asupersync::test_utils::run_test(|| async {
12439            let conn = Connection::open(":memory:").await.unwrap();
12440            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12441                .await
12442                .unwrap();
12443            conn.execute("INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie')")
12444                .await
12445                .unwrap();
12446            conn.execute("DELETE FROM t1 WHERE id = 2").await.unwrap();
12447            let r = conn.query("SELECT name FROM t1 ORDER BY id").await.unwrap();
12448            assert_eq!(r.len(), 2);
12449            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12450            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
12451        });
12452    }
12453
12454    #[test]
12455    fn conformance_035_delete_all() {
12456        asupersync::test_utils::run_test(|| async {
12457            let conn = Connection::open(":memory:").await.unwrap();
12458            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12459                .await
12460                .unwrap();
12461            conn.execute("INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob')")
12462                .await
12463                .unwrap();
12464            conn.execute("DELETE FROM t1").await.unwrap();
12465            let r = conn.query("SELECT COUNT(*) FROM t1").await.unwrap();
12466            assert_eq!(row_values(&r[0])[0].to_text(), "0");
12467        });
12468    }
12469
12470    #[test]
12471    fn conformance_035_delete_with_in() {
12472        asupersync::test_utils::run_test(|| async {
12473            let conn = Connection::open(":memory:").await.unwrap();
12474            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12475                .await
12476                .unwrap();
12477            conn.execute("INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie'),(4,'Diana')")
12478                .await
12479                .unwrap();
12480            conn.execute("DELETE FROM t1 WHERE id IN (2, 4)")
12481                .await
12482                .unwrap();
12483            let r = conn.query("SELECT name FROM t1 ORDER BY id").await.unwrap();
12484            assert_eq!(r.len(), 2);
12485            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12486            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
12487        });
12488    }
12489
12490    // -----------------------------------------------------------------------
12491    // Conformance suite 036: Compound queries (UNION, INTERSECT, EXCEPT)
12492    // -----------------------------------------------------------------------
12493
12494    #[test]
12495    fn conformance_036_union_all() {
12496        asupersync::test_utils::run_test(|| async {
12497            let conn = Connection::open(":memory:").await.unwrap();
12498            conn.execute("CREATE TABLE t1(name TEXT)").await.unwrap();
12499            conn.execute("CREATE TABLE t2(name TEXT)").await.unwrap();
12500            conn.execute("INSERT INTO t1 VALUES ('Alice'),('Bob')")
12501                .await
12502                .unwrap();
12503            conn.execute("INSERT INTO t2 VALUES ('Bob'),('Charlie')")
12504                .await
12505                .unwrap();
12506            let r = conn
12507                .query("SELECT name FROM t1 UNION ALL SELECT name FROM t2 ORDER BY name")
12508                .await
12509                .unwrap();
12510            assert_eq!(r.len(), 4);
12511            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12512            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12513            assert_eq!(row_values(&r[2])[0].to_text(), "Bob");
12514            assert_eq!(row_values(&r[3])[0].to_text(), "Charlie");
12515        });
12516    }
12517
12518    #[test]
12519    fn conformance_036_union_distinct() {
12520        asupersync::test_utils::run_test(|| async {
12521            let conn = Connection::open(":memory:").await.unwrap();
12522            conn.execute("CREATE TABLE t1(name TEXT)").await.unwrap();
12523            conn.execute("CREATE TABLE t2(name TEXT)").await.unwrap();
12524            conn.execute("INSERT INTO t1 VALUES ('Alice'),('Bob')")
12525                .await
12526                .unwrap();
12527            conn.execute("INSERT INTO t2 VALUES ('Bob'),('Charlie')")
12528                .await
12529                .unwrap();
12530            let r = conn
12531                .query("SELECT name FROM t1 UNION SELECT name FROM t2 ORDER BY name")
12532                .await
12533                .unwrap();
12534            assert_eq!(r.len(), 3);
12535            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12536            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12537            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie");
12538        });
12539    }
12540
12541    #[test]
12542    fn conformance_036_intersect() {
12543        asupersync::test_utils::run_test(|| async {
12544            let conn = Connection::open(":memory:").await.unwrap();
12545            conn.execute("CREATE TABLE t1(name TEXT)").await.unwrap();
12546            conn.execute("CREATE TABLE t2(name TEXT)").await.unwrap();
12547            conn.execute("INSERT INTO t1 VALUES ('Alice'),('Bob'),('Charlie')")
12548                .await
12549                .unwrap();
12550            conn.execute("INSERT INTO t2 VALUES ('Bob'),('Charlie'),('Diana')")
12551                .await
12552                .unwrap();
12553            let r = conn
12554                .query("SELECT name FROM t1 INTERSECT SELECT name FROM t2 ORDER BY name")
12555                .await
12556                .unwrap();
12557            assert_eq!(r.len(), 2);
12558            assert_eq!(row_values(&r[0])[0].to_text(), "Bob");
12559            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
12560        });
12561    }
12562
12563    #[test]
12564    fn conformance_036_except() {
12565        asupersync::test_utils::run_test(|| async {
12566            let conn = Connection::open(":memory:").await.unwrap();
12567            conn.execute("CREATE TABLE t1(name TEXT)").await.unwrap();
12568            conn.execute("CREATE TABLE t2(name TEXT)").await.unwrap();
12569            conn.execute("INSERT INTO t1 VALUES ('Alice'),('Bob'),('Charlie')")
12570                .await
12571                .unwrap();
12572            conn.execute("INSERT INTO t2 VALUES ('Bob'),('Diana')")
12573                .await
12574                .unwrap();
12575            let r = conn
12576                .query("SELECT name FROM t1 EXCEPT SELECT name FROM t2 ORDER BY name")
12577                .await
12578                .unwrap();
12579            assert_eq!(r.len(), 2);
12580            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12581            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
12582        });
12583    }
12584
12585    // -----------------------------------------------------------------------
12586    // Conformance suite 037: ORDER BY, LIMIT, OFFSET, DISTINCT
12587    // -----------------------------------------------------------------------
12588
12589    #[test]
12590    fn conformance_037_order_by_desc() {
12591        asupersync::test_utils::run_test(|| async {
12592            let conn = Connection::open(":memory:").await.unwrap();
12593            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12594                .await
12595                .unwrap();
12596            conn.execute("INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie')")
12597                .await
12598                .unwrap();
12599            let r = conn
12600                .query("SELECT name FROM t1 ORDER BY id DESC")
12601                .await
12602                .unwrap();
12603            assert_eq!(row_values(&r[0])[0].to_text(), "Charlie");
12604            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12605            assert_eq!(row_values(&r[2])[0].to_text(), "Alice");
12606        });
12607    }
12608
12609    #[test]
12610    fn conformance_037_order_by_multiple() {
12611        asupersync::test_utils::run_test(|| async {
12612            let conn = Connection::open(":memory:").await.unwrap();
12613            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, dept TEXT, name TEXT)")
12614                .await
12615                .unwrap();
12616            conn.execute(
12617                "INSERT INTO t1 VALUES (1,'A','Charlie'),(2,'B','Alice'),\
12618             (3,'A','Alice'),(4,'B','Bob')",
12619            )
12620            .await
12621            .unwrap();
12622            let r = conn
12623                .query("SELECT dept, name FROM t1 ORDER BY dept ASC, name ASC")
12624                .await
12625                .unwrap();
12626            assert_eq!(row_values(&r[0])[1].to_text(), "Alice");
12627            assert_eq!(row_values(&r[0])[0].to_text(), "A");
12628            assert_eq!(row_values(&r[1])[1].to_text(), "Charlie");
12629            assert_eq!(row_values(&r[2])[1].to_text(), "Alice");
12630            assert_eq!(row_values(&r[2])[0].to_text(), "B");
12631            assert_eq!(row_values(&r[3])[1].to_text(), "Bob");
12632        });
12633    }
12634
12635    #[test]
12636    fn conformance_037_limit() {
12637        asupersync::test_utils::run_test(|| async {
12638            let conn = Connection::open(":memory:").await.unwrap();
12639            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12640                .await
12641                .unwrap();
12642            conn.execute(
12643                "INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie'),(4,'Diana'),(5,'Eve')",
12644            )
12645            .await
12646            .unwrap();
12647            let r = conn
12648                .query("SELECT name FROM t1 ORDER BY id LIMIT 3")
12649                .await
12650                .unwrap();
12651            assert_eq!(r.len(), 3);
12652            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12653            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie");
12654        });
12655    }
12656
12657    #[test]
12658    fn conformance_037_limit_offset() {
12659        asupersync::test_utils::run_test(|| async {
12660            let conn = Connection::open(":memory:").await.unwrap();
12661            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
12662                .await
12663                .unwrap();
12664            conn.execute(
12665                "INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie'),(4,'Diana'),(5,'Eve')",
12666            )
12667            .await
12668            .unwrap();
12669            let r = conn
12670                .query("SELECT name FROM t1 ORDER BY id LIMIT 2 OFFSET 2")
12671                .await
12672                .unwrap();
12673            assert_eq!(r.len(), 2);
12674            assert_eq!(row_values(&r[0])[0].to_text(), "Charlie");
12675            assert_eq!(row_values(&r[1])[0].to_text(), "Diana");
12676        });
12677    }
12678
12679    #[test]
12680    fn conformance_037_distinct() {
12681        asupersync::test_utils::run_test(|| async {
12682            let conn = Connection::open(":memory:").await.unwrap();
12683            conn.execute("CREATE TABLE t1(name TEXT)").await.unwrap();
12684            conn.execute("INSERT INTO t1 VALUES ('Alice'),('Bob'),('Alice'),('Charlie'),('Bob')")
12685                .await
12686                .unwrap();
12687            let r = conn
12688                .query("SELECT DISTINCT name FROM t1 ORDER BY name")
12689                .await
12690                .unwrap();
12691            assert_eq!(r.len(), 3);
12692            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12693            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12694            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie");
12695        });
12696    }
12697
12698    #[test]
12699    fn conformance_037_order_by_nulls() {
12700        asupersync::test_utils::run_test(|| async {
12701            let conn = Connection::open(":memory:").await.unwrap();
12702            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
12703                .await
12704                .unwrap();
12705            conn.execute("INSERT INTO t1 VALUES (1,'B'),(2,NULL),(3,'A'),(4,NULL),(5,'C')")
12706                .await
12707                .unwrap();
12708            // SQLite: NULLs sort first in ASC order
12709            let r = conn
12710                .query("SELECT val FROM t1 ORDER BY val ASC")
12711                .await
12712                .unwrap();
12713            assert_eq!(r.len(), 5);
12714            assert!(row_values(&r[0])[0].is_null());
12715            assert!(row_values(&r[1])[0].is_null());
12716            assert_eq!(row_values(&r[2])[0].to_text(), "A");
12717            assert_eq!(row_values(&r[3])[0].to_text(), "B");
12718            assert_eq!(row_values(&r[4])[0].to_text(), "C");
12719        });
12720    }
12721
12722    // -----------------------------------------------------------------------
12723    // Conformance suite 038: INSERT ... SELECT
12724    // -----------------------------------------------------------------------
12725
12726    #[test]
12727    fn conformance_038_insert_select_basic() {
12728        asupersync::test_utils::run_test(|| async {
12729            let conn = Connection::open(":memory:").await.unwrap();
12730            conn.execute("CREATE TABLE src(id INTEGER PRIMARY KEY, name TEXT)")
12731                .await
12732                .unwrap();
12733            conn.execute("CREATE TABLE dst(id INTEGER PRIMARY KEY, name TEXT)")
12734                .await
12735                .unwrap();
12736            conn.execute("INSERT INTO src VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie')")
12737                .await
12738                .unwrap();
12739            conn.execute("INSERT INTO dst SELECT * FROM src WHERE id <= 2")
12740                .await
12741                .unwrap();
12742            let r = conn
12743                .query("SELECT name FROM dst ORDER BY id")
12744                .await
12745                .unwrap();
12746            assert_eq!(r.len(), 2);
12747            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
12748            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
12749        });
12750    }
12751
12752    #[test]
12753    fn conformance_038_insert_select_with_transform() {
12754        asupersync::test_utils::run_test(|| async {
12755            let conn = Connection::open(":memory:").await.unwrap();
12756            conn.execute("CREATE TABLE src(id INTEGER PRIMARY KEY, name TEXT)")
12757                .await
12758                .unwrap();
12759            conn.execute("CREATE TABLE dst(name TEXT)").await.unwrap();
12760            conn.execute("INSERT INTO src VALUES (1,'Alice'),(2,'Bob')")
12761                .await
12762                .unwrap();
12763            conn.execute("INSERT INTO dst SELECT upper(name) FROM src")
12764                .await
12765                .unwrap();
12766            let r = conn
12767                .query("SELECT name FROM dst ORDER BY name")
12768                .await
12769                .unwrap();
12770            assert_eq!(r.len(), 2);
12771            assert_eq!(row_values(&r[0])[0].to_text(), "ALICE");
12772            assert_eq!(row_values(&r[1])[0].to_text(), "BOB");
12773        });
12774    }
12775
12776    // -----------------------------------------------------------------------
12777    // Conformance suite 039: NULL handling
12778    // -----------------------------------------------------------------------
12779
12780    #[test]
12781    fn conformance_039_null_comparison() {
12782        asupersync::test_utils::run_test(|| async {
12783            let conn = Connection::open(":memory:").await.unwrap();
12784            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
12785                .await
12786                .unwrap();
12787            conn.execute("INSERT INTO t1 VALUES (1,'A'),(2,NULL),(3,'B')")
12788                .await
12789                .unwrap();
12790            // NULL = NULL is not true in SQL
12791            let r = conn
12792                .query("SELECT COUNT(*) FROM t1 WHERE val = NULL")
12793                .await
12794                .unwrap();
12795            assert_eq!(row_values(&r[0])[0].to_text(), "0");
12796            // IS NULL works
12797            let r = conn
12798                .query("SELECT COUNT(*) FROM t1 WHERE val IS NULL")
12799                .await
12800                .unwrap();
12801            assert_eq!(row_values(&r[0])[0].to_text(), "1");
12802            // IS NOT NULL
12803            let r = conn
12804                .query("SELECT COUNT(*) FROM t1 WHERE val IS NOT NULL")
12805                .await
12806                .unwrap();
12807            assert_eq!(row_values(&r[0])[0].to_text(), "2");
12808        });
12809    }
12810
12811    #[test]
12812    fn conformance_039_coalesce() {
12813        asupersync::test_utils::run_test(|| async {
12814            let conn = Connection::open(":memory:").await.unwrap();
12815            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, a TEXT, b TEXT)")
12816                .await
12817                .unwrap();
12818            conn.execute("INSERT INTO t1 VALUES (1,NULL,'fallback'),(2,'value',NULL)")
12819                .await
12820                .unwrap();
12821            let r = conn
12822                .query("SELECT COALESCE(a, b) FROM t1 ORDER BY id")
12823                .await
12824                .unwrap();
12825            assert_eq!(row_values(&r[0])[0].to_text(), "fallback");
12826            assert_eq!(row_values(&r[1])[0].to_text(), "value");
12827        });
12828    }
12829
12830    #[test]
12831    fn conformance_039_ifnull() {
12832        asupersync::test_utils::run_test(|| async {
12833            let conn = Connection::open(":memory:").await.unwrap();
12834            let r = conn
12835                .query("SELECT IFNULL(NULL, 'default'), IFNULL('value', 'default')")
12836                .await
12837                .unwrap();
12838            assert_eq!(row_values(&r[0])[0].to_text(), "default");
12839            assert_eq!(row_values(&r[0])[1].to_text(), "value");
12840        });
12841    }
12842
12843    #[test]
12844    fn conformance_039_nullif() {
12845        asupersync::test_utils::run_test(|| async {
12846            let conn = Connection::open(":memory:").await.unwrap();
12847            let r = conn
12848                .query("SELECT NULLIF(1, 1), NULLIF(1, 2)")
12849                .await
12850                .unwrap();
12851            assert!(row_values(&r[0])[0].is_null());
12852            assert_eq!(row_values(&r[0])[1].to_text(), "1");
12853        });
12854    }
12855
12856    #[test]
12857    fn conformance_039_null_in_aggregate() {
12858        asupersync::test_utils::run_test(|| async {
12859            let conn = Connection::open(":memory:").await.unwrap();
12860            conn.execute("CREATE TABLE t1(val INTEGER)").await.unwrap();
12861            conn.execute("INSERT INTO t1 VALUES (1),(NULL),(3),(NULL),(5)")
12862                .await
12863                .unwrap();
12864            let r = conn
12865                .query("SELECT COUNT(*), COUNT(val), SUM(val), AVG(val) FROM t1")
12866                .await
12867                .unwrap();
12868            // COUNT(*) counts all rows including NULL
12869            assert_eq!(row_values(&r[0])[0].to_text(), "5");
12870            // COUNT(val) skips NULLs
12871            assert_eq!(row_values(&r[0])[1].to_text(), "3");
12872            // SUM skips NULLs: 1+3+5=9
12873            assert_eq!(row_values(&r[0])[2].to_text(), "9");
12874            // AVG skips NULLs: 9/3=3.0
12875            assert_eq!(row_values(&r[0])[3].to_text(), "3.0");
12876        });
12877    }
12878
12879    // -----------------------------------------------------------------------
12880    // Conformance suite 040: CREATE INDEX / DROP INDEX
12881    // -----------------------------------------------------------------------
12882
12883    #[test]
12884    fn conformance_040_create_index_basic() {
12885        asupersync::test_utils::run_test(|| async {
12886            let conn = Connection::open(":memory:").await.unwrap();
12887            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
12888                .await
12889                .unwrap();
12890            conn.execute("CREATE INDEX idx_name ON t1(name)")
12891                .await
12892                .unwrap();
12893            // Index should appear in sqlite_master
12894            let r = conn
12895            .query("SELECT type, name, tbl_name FROM sqlite_master WHERE type = 'index' AND name = 'idx_name'")
12896            .await
12897            .unwrap();
12898            assert_eq!(r.len(), 1);
12899            assert_eq!(row_values(&r[0])[0].to_text(), "index");
12900            assert_eq!(row_values(&r[0])[1].to_text(), "idx_name");
12901            assert_eq!(row_values(&r[0])[2].to_text(), "t1");
12902        });
12903    }
12904
12905    #[test]
12906    fn conformance_040_create_index_if_not_exists() {
12907        asupersync::test_utils::run_test(|| async {
12908            let conn = Connection::open(":memory:").await.unwrap();
12909            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
12910                .await
12911                .unwrap();
12912            conn.execute("CREATE INDEX idx1 ON t1(val)").await.unwrap();
12913            // Second create with IF NOT EXISTS should succeed silently
12914            conn.execute("CREATE INDEX IF NOT EXISTS idx1 ON t1(val)")
12915                .await
12916                .unwrap();
12917            let r = conn
12918                .query("SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx1'")
12919                .await
12920                .unwrap();
12921            assert_eq!(r.len(), 1);
12922        });
12923    }
12924
12925    #[test]
12926    fn conformance_040_drop_index() {
12927        asupersync::test_utils::run_test(|| async {
12928            let conn = Connection::open(":memory:").await.unwrap();
12929            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
12930                .await
12931                .unwrap();
12932            conn.execute("CREATE INDEX idx1 ON t1(val)").await.unwrap();
12933            conn.execute("DROP INDEX idx1").await.unwrap();
12934            let r = conn
12935                .query("SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx1'")
12936                .await
12937                .unwrap();
12938            assert_eq!(r.len(), 0);
12939        });
12940    }
12941
12942    #[test]
12943    fn conformance_040_unique_index() {
12944        asupersync::test_utils::run_test(|| async {
12945            let conn = Connection::open(":memory:").await.unwrap();
12946            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, code TEXT)")
12947                .await
12948                .unwrap();
12949            conn.execute("CREATE UNIQUE INDEX idx_code ON t1(code)")
12950                .await
12951                .unwrap();
12952            conn.execute("INSERT INTO t1 VALUES (1, 'abc')")
12953                .await
12954                .unwrap();
12955            // Inserting duplicate should fail
12956            let result = conn.execute("INSERT INTO t1 VALUES (2, 'abc')").await;
12957            assert!(result.is_err());
12958        });
12959    }
12960
12961    // -----------------------------------------------------------------------
12962    // Conformance suite 041: Triggers
12963    // -----------------------------------------------------------------------
12964
12965    #[test]
12966    fn conformance_041_trigger_after_insert() {
12967        asupersync::test_utils::run_test(|| async {
12968            let conn = Connection::open(":memory:").await.unwrap();
12969            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
12970                .await
12971                .unwrap();
12972            conn.execute("CREATE TABLE log(msg TEXT)").await.unwrap();
12973            conn.execute(
12974                "CREATE TRIGGER t1_after_insert AFTER INSERT ON t1 \
12975             BEGIN INSERT INTO log VALUES ('inserted ' || NEW.val); END",
12976            )
12977            .await
12978            .unwrap();
12979            conn.execute("INSERT INTO t1 VALUES (1, 'hello')")
12980                .await
12981                .unwrap();
12982            let r = conn.query("SELECT msg FROM log").await.unwrap();
12983            assert_eq!(r.len(), 1);
12984            assert_eq!(row_values(&r[0])[0].to_text(), "inserted hello");
12985        });
12986    }
12987
12988    #[test]
12989    fn conformance_041_trigger_before_insert() {
12990        asupersync::test_utils::run_test(|| async {
12991            let conn = Connection::open(":memory:").await.unwrap();
12992            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
12993                .await
12994                .unwrap();
12995            conn.execute("CREATE TABLE audit(action TEXT)")
12996                .await
12997                .unwrap();
12998            conn.execute(
12999                "CREATE TRIGGER t1_before BEFORE INSERT ON t1 \
13000             BEGIN INSERT INTO audit VALUES ('before_insert'); END",
13001            )
13002            .await
13003            .unwrap();
13004            conn.execute("INSERT INTO t1 VALUES (1, 42)").await.unwrap();
13005            let r = conn.query("SELECT action FROM audit").await.unwrap();
13006            assert_eq!(r.len(), 1);
13007            assert_eq!(row_values(&r[0])[0].to_text(), "before_insert");
13008        });
13009    }
13010
13011    #[test]
13012    fn conformance_041_trigger_drop() {
13013        asupersync::test_utils::run_test(|| async {
13014            let conn = Connection::open(":memory:").await.unwrap();
13015            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY)")
13016                .await
13017                .unwrap();
13018            conn.execute("CREATE TABLE log(x TEXT)").await.unwrap();
13019            conn.execute(
13020                "CREATE TRIGGER trg1 AFTER INSERT ON t1 \
13021             BEGIN INSERT INTO log VALUES ('fired'); END",
13022            )
13023            .await
13024            .unwrap();
13025            conn.execute("DROP TRIGGER trg1").await.unwrap();
13026            conn.execute("INSERT INTO t1 VALUES (1)").await.unwrap();
13027            let r = conn.query("SELECT x FROM log").await.unwrap();
13028            // Trigger was dropped, so log should be empty
13029            assert_eq!(r.len(), 0);
13030        });
13031    }
13032
13033    // -----------------------------------------------------------------------
13034    // Conformance suite 042: AUTOINCREMENT
13035    // -----------------------------------------------------------------------
13036
13037    #[test]
13038    fn conformance_042_autoincrement_basic() {
13039        asupersync::test_utils::run_test(|| async {
13040            let conn = Connection::open(":memory:").await.unwrap();
13041            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)")
13042                .await
13043                .unwrap();
13044            conn.execute("INSERT INTO t1(val) VALUES ('a')")
13045                .await
13046                .unwrap();
13047            conn.execute("INSERT INTO t1(val) VALUES ('b')")
13048                .await
13049                .unwrap();
13050            conn.execute("INSERT INTO t1(val) VALUES ('c')")
13051                .await
13052                .unwrap();
13053            let r = conn
13054                .query("SELECT id, val FROM t1 ORDER BY id")
13055                .await
13056                .unwrap();
13057            assert_eq!(r.len(), 3);
13058            assert_eq!(row_values(&r[0])[0].to_text(), "1");
13059            assert_eq!(row_values(&r[1])[0].to_text(), "2");
13060            assert_eq!(row_values(&r[2])[0].to_text(), "3");
13061        });
13062    }
13063
13064    #[test]
13065    fn conformance_042_autoincrement_after_delete() {
13066        asupersync::test_utils::run_test(|| async {
13067            let conn = Connection::open(":memory:").await.unwrap();
13068            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)")
13069                .await
13070                .unwrap();
13071            conn.execute("INSERT INTO t1(val) VALUES ('a')")
13072                .await
13073                .unwrap();
13074            conn.execute("INSERT INTO t1(val) VALUES ('b')")
13075                .await
13076                .unwrap();
13077            // Delete row with id=2
13078            conn.execute("DELETE FROM t1 WHERE id = 2").await.unwrap();
13079            // Next insert should get id=3, not reuse id=2
13080            conn.execute("INSERT INTO t1(val) VALUES ('c')")
13081                .await
13082                .unwrap();
13083            let r = conn.query("SELECT id FROM t1 ORDER BY id").await.unwrap();
13084            assert_eq!(row_values(&r[0])[0].to_text(), "1");
13085            assert_eq!(row_values(&r[1])[0].to_text(), "3");
13086        });
13087    }
13088
13089    #[test]
13090    fn conformance_042_sqlite_sequence_table() {
13091        asupersync::test_utils::run_test(|| async {
13092            let conn = Connection::open(":memory:").await.unwrap();
13093            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)")
13094                .await
13095                .unwrap();
13096            conn.execute("INSERT INTO t1(val) VALUES ('x')")
13097                .await
13098                .unwrap();
13099            conn.execute("INSERT INTO t1(val) VALUES ('y')")
13100                .await
13101                .unwrap();
13102            // sqlite_sequence should track the max assigned rowid
13103            let r = conn
13104                .query("SELECT seq FROM sqlite_sequence WHERE name = 't1'")
13105                .await
13106                .unwrap();
13107            assert_eq!(r.len(), 1);
13108            assert_eq!(row_values(&r[0])[0].to_text(), "2");
13109        });
13110    }
13111
13112    // -----------------------------------------------------------------------
13113    // Conformance suite 043: DEFAULT values
13114    // -----------------------------------------------------------------------
13115
13116    #[test]
13117    fn conformance_043_default_literal() {
13118        asupersync::test_utils::run_test(|| async {
13119            let conn = Connection::open(":memory:").await.unwrap();
13120            conn.execute(
13121                "CREATE TABLE t1(\
13122             id INTEGER PRIMARY KEY, \
13123             status TEXT DEFAULT 'active', \
13124             count INTEGER DEFAULT 0)",
13125            )
13126            .await
13127            .unwrap();
13128            conn.execute("INSERT INTO t1(id) VALUES (1)").await.unwrap();
13129            let r = conn
13130                .query("SELECT status, count FROM t1 WHERE id = 1")
13131                .await
13132                .unwrap();
13133            assert_eq!(row_values(&r[0])[0].to_text(), "active");
13134            assert_eq!(row_values(&r[0])[1].to_text(), "0");
13135        });
13136    }
13137
13138    #[test]
13139    fn conformance_043_default_null() {
13140        asupersync::test_utils::run_test(|| async {
13141            let conn = Connection::open(":memory:").await.unwrap();
13142            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
13143                .await
13144                .unwrap();
13145            // Column without DEFAULT clause defaults to NULL
13146            conn.execute("INSERT INTO t1(id) VALUES (1)").await.unwrap();
13147            let r = conn.query("SELECT val FROM t1 WHERE id = 1").await.unwrap();
13148            assert!(row_values(&r[0])[0].is_null());
13149        });
13150    }
13151
13152    #[test]
13153    fn conformance_043_default_override() {
13154        asupersync::test_utils::run_test(|| async {
13155            let conn = Connection::open(":memory:").await.unwrap();
13156            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT DEFAULT 'default_val')")
13157                .await
13158                .unwrap();
13159            // Explicit value should override DEFAULT
13160            conn.execute("INSERT INTO t1 VALUES (1, 'custom')")
13161                .await
13162                .unwrap();
13163            let r = conn.query("SELECT val FROM t1 WHERE id = 1").await.unwrap();
13164            assert_eq!(row_values(&r[0])[0].to_text(), "custom");
13165        });
13166    }
13167
13168    // -----------------------------------------------------------------------
13169    // Conformance suite 044: Multi-column ORDER BY and expression sorting
13170    // -----------------------------------------------------------------------
13171
13172    #[test]
13173    fn conformance_044_order_by_expression() {
13174        asupersync::test_utils::run_test(|| async {
13175            let conn = Connection::open(":memory:").await.unwrap();
13176            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
13177                .await
13178                .unwrap();
13179            conn.execute("INSERT INTO t1 VALUES (1, 10), (2, -5), (3, 3)")
13180                .await
13181                .unwrap();
13182            let r = conn
13183                .query("SELECT id, ABS(val) as a FROM t1 ORDER BY ABS(val)")
13184                .await
13185                .unwrap();
13186            assert_eq!(row_values(&r[0])[0].to_text(), "3"); // ABS(3)=3
13187            assert_eq!(row_values(&r[1])[0].to_text(), "2"); // ABS(-5)=5
13188            assert_eq!(row_values(&r[2])[0].to_text(), "1"); // ABS(10)=10
13189        });
13190    }
13191
13192    #[test]
13193    fn conformance_044_order_by_column_index() {
13194        asupersync::test_utils::run_test(|| async {
13195            let conn = Connection::open(":memory:").await.unwrap();
13196            conn.execute("CREATE TABLE t1(a TEXT, b INTEGER)")
13197                .await
13198                .unwrap();
13199            conn.execute("INSERT INTO t1 VALUES ('x', 3), ('y', 1), ('z', 2)")
13200                .await
13201                .unwrap();
13202            // ORDER BY column index (1-based)
13203            let r = conn.query("SELECT a, b FROM t1 ORDER BY 2").await.unwrap();
13204            assert_eq!(row_values(&r[0])[0].to_text(), "y"); // b=1
13205            assert_eq!(row_values(&r[1])[0].to_text(), "z"); // b=2
13206            assert_eq!(row_values(&r[2])[0].to_text(), "x"); // b=3
13207        });
13208    }
13209
13210    #[test]
13211    fn conformance_044_order_by_alias() {
13212        asupersync::test_utils::run_test(|| async {
13213            let conn = Connection::open(":memory:").await.unwrap();
13214            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, first TEXT, last TEXT)")
13215                .await
13216                .unwrap();
13217            conn.execute(
13218                "INSERT INTO t1 VALUES (1, 'Bob', 'Smith'), \
13219             (2, 'Alice', 'Jones'), (3, 'Charlie', 'Adams')",
13220            )
13221            .await
13222            .unwrap();
13223            // ORDER BY using column alias
13224            let r = conn
13225                .query("SELECT first || ' ' || last AS full_name FROM t1 ORDER BY full_name")
13226                .await
13227                .unwrap();
13228            assert_eq!(row_values(&r[0])[0].to_text(), "Alice Jones");
13229            assert_eq!(row_values(&r[1])[0].to_text(), "Bob Smith");
13230            assert_eq!(row_values(&r[2])[0].to_text(), "Charlie Adams");
13231        });
13232    }
13233
13234    // ── Conformance 048: Math functions (§13.2) via SQL pipeline ─────
13235
13236    #[test]
13237    fn conformance_048_abs() {
13238        asupersync::test_utils::run_test(|| async {
13239            let conn = Connection::open(":memory:").await.unwrap();
13240            let r = conn
13241                .query("SELECT abs(-42), abs(3.14), abs(0)")
13242                .await
13243                .unwrap();
13244            assert_eq!(row_values(&r[0])[0].to_text(), "42");
13245            assert_eq!(row_values(&r[0])[1].to_text(), "3.14");
13246            assert_eq!(row_values(&r[0])[2].to_text(), "0");
13247        });
13248    }
13249
13250    #[test]
13251    fn conformance_048_abs_null() {
13252        asupersync::test_utils::run_test(|| async {
13253            let conn = Connection::open(":memory:").await.unwrap();
13254            let r = conn.query("SELECT abs(NULL)").await.unwrap();
13255            assert!(row_values(&r[0])[0].is_null());
13256        });
13257    }
13258
13259    #[test]
13260    fn conformance_048_round() {
13261        asupersync::test_utils::run_test(|| async {
13262            let conn = Connection::open(":memory:").await.unwrap();
13263            let r = conn
13264                .query("SELECT round(3.14159), round(3.14159, 2), round(3.5)")
13265                .await
13266                .unwrap();
13267            assert_eq!(row_values(&r[0])[0].to_text(), "3.0");
13268            assert_eq!(row_values(&r[0])[1].to_text(), "3.14");
13269            assert_eq!(row_values(&r[0])[2].to_text(), "4.0");
13270        });
13271    }
13272
13273    #[test]
13274    fn conformance_048_sign() {
13275        asupersync::test_utils::run_test(|| async {
13276            let conn = Connection::open(":memory:").await.unwrap();
13277            let r = conn
13278                .query("SELECT sign(-5), sign(0), sign(42), sign(NULL)")
13279                .await
13280                .unwrap();
13281            assert_eq!(row_values(&r[0])[0].to_text(), "-1");
13282            assert_eq!(row_values(&r[0])[1].to_text(), "0");
13283            assert_eq!(row_values(&r[0])[2].to_text(), "1");
13284            assert!(row_values(&r[0])[3].is_null());
13285        });
13286    }
13287
13288    #[test]
13289    fn conformance_048_trig_basic() {
13290        asupersync::test_utils::run_test(|| async {
13291            let conn = Connection::open(":memory:").await.unwrap();
13292            let r = conn.query("SELECT sin(0), cos(0), tan(0)").await.unwrap();
13293            assert_eq!(row_values(&r[0])[0].to_text(), "0.0");
13294            assert_eq!(row_values(&r[0])[1].to_text(), "1.0");
13295            assert_eq!(row_values(&r[0])[2].to_text(), "0.0");
13296        });
13297    }
13298
13299    #[test]
13300    fn conformance_048_acos_asin_atan() {
13301        asupersync::test_utils::run_test(|| async {
13302            let conn = Connection::open(":memory:").await.unwrap();
13303            let r = conn
13304                .query("SELECT acos(1), asin(0), atan(0)")
13305                .await
13306                .unwrap();
13307            assert_eq!(row_values(&r[0])[0].to_text(), "0.0");
13308            assert_eq!(row_values(&r[0])[1].to_text(), "0.0");
13309            assert_eq!(row_values(&r[0])[2].to_text(), "0.0");
13310        });
13311    }
13312
13313    #[test]
13314    fn conformance_048_acos_domain_error() {
13315        asupersync::test_utils::run_test(|| async {
13316            let conn = Connection::open(":memory:").await.unwrap();
13317            let r = conn.query("SELECT acos(2.0)").await.unwrap();
13318            assert!(row_values(&r[0])[0].is_null());
13319        });
13320    }
13321
13322    #[test]
13323    fn conformance_048_sqrt() {
13324        asupersync::test_utils::run_test(|| async {
13325            let conn = Connection::open(":memory:").await.unwrap();
13326            let r = conn.query("SELECT sqrt(144), sqrt(2)").await.unwrap();
13327            assert_eq!(row_values(&r[0])[0].to_text(), "12.0");
13328            let v = row_values(&r[0])[1].to_text();
13329            assert!(v.starts_with("1.41421356"), "got {v}");
13330        });
13331    }
13332
13333    #[test]
13334    fn conformance_048_sqrt_negative() {
13335        asupersync::test_utils::run_test(|| async {
13336            let conn = Connection::open(":memory:").await.unwrap();
13337            let r = conn.query("SELECT sqrt(-1)").await.unwrap();
13338            assert!(row_values(&r[0])[0].is_null());
13339        });
13340    }
13341
13342    #[test]
13343    fn conformance_048_pow() {
13344        asupersync::test_utils::run_test(|| async {
13345            let conn = Connection::open(":memory:").await.unwrap();
13346            let r = conn.query("SELECT pow(2, 10), power(3, 2)").await.unwrap();
13347            assert_eq!(row_values(&r[0])[0].to_text(), "1024.0");
13348            assert_eq!(row_values(&r[0])[1].to_text(), "9.0");
13349        });
13350    }
13351
13352    #[test]
13353    fn conformance_048_exp_ln() {
13354        asupersync::test_utils::run_test(|| async {
13355            let conn = Connection::open(":memory:").await.unwrap();
13356            let r = conn.query("SELECT exp(0), ln(1)").await.unwrap();
13357            assert_eq!(row_values(&r[0])[0].to_text(), "1.0");
13358            assert_eq!(row_values(&r[0])[1].to_text(), "0.0");
13359        });
13360    }
13361
13362    #[test]
13363    fn conformance_048_log_variants() {
13364        asupersync::test_utils::run_test(|| async {
13365            let conn = Connection::open(":memory:").await.unwrap();
13366            let r = conn
13367                .query("SELECT log(100), log10(1000), log2(8)")
13368                .await
13369                .unwrap();
13370            assert_eq!(row_values(&r[0])[0].to_text(), "2.0");
13371            assert_eq!(row_values(&r[0])[1].to_text(), "3.0");
13372            assert_eq!(row_values(&r[0])[2].to_text(), "3.0");
13373        });
13374    }
13375
13376    #[test]
13377    fn conformance_048_log_two_arg() {
13378        asupersync::test_utils::run_test(|| async {
13379            let conn = Connection::open(":memory:").await.unwrap();
13380            let r = conn.query("SELECT log(2, 8)").await.unwrap();
13381            assert_eq!(row_values(&r[0])[0].to_text(), "3.0");
13382        });
13383    }
13384
13385    #[test]
13386    fn conformance_048_ln_negative_null() {
13387        asupersync::test_utils::run_test(|| async {
13388            let conn = Connection::open(":memory:").await.unwrap();
13389            let r = conn.query("SELECT ln(-1), ln(0)").await.unwrap();
13390            assert!(row_values(&r[0])[0].is_null());
13391            assert!(row_values(&r[0])[1].is_null());
13392        });
13393    }
13394
13395    #[test]
13396    fn conformance_048_pi() {
13397        asupersync::test_utils::run_test(|| async {
13398            let conn = Connection::open(":memory:").await.unwrap();
13399            let r = conn.query("SELECT pi()").await.unwrap();
13400            let v = row_values(&r[0])[0].to_text();
13401            assert!(v.starts_with("3.14159265"), "got {v}");
13402        });
13403    }
13404
13405    #[test]
13406    fn conformance_048_ceil_floor_trunc() {
13407        asupersync::test_utils::run_test(|| async {
13408            let conn = Connection::open(":memory:").await.unwrap();
13409            let r = conn
13410                .query("SELECT ceil(1.2), floor(1.7), trunc(2.9)")
13411                .await
13412                .unwrap();
13413            assert_eq!(row_values(&r[0])[0].to_text(), "2.0");
13414            assert_eq!(row_values(&r[0])[1].to_text(), "1.0");
13415            assert_eq!(row_values(&r[0])[2].to_text(), "2.0");
13416        });
13417    }
13418
13419    #[test]
13420    fn conformance_048_ceil_floor_negative() {
13421        asupersync::test_utils::run_test(|| async {
13422            let conn = Connection::open(":memory:").await.unwrap();
13423            let r = conn
13424                .query("SELECT ceil(-1.2), floor(-1.2), trunc(-2.9)")
13425                .await
13426                .unwrap();
13427            assert_eq!(row_values(&r[0])[0].to_text(), "-1.0");
13428            assert_eq!(row_values(&r[0])[1].to_text(), "-2.0");
13429            assert_eq!(row_values(&r[0])[2].to_text(), "-2.0");
13430        });
13431    }
13432
13433    #[test]
13434    fn conformance_048_degrees_radians() {
13435        asupersync::test_utils::run_test(|| async {
13436            let conn = Connection::open(":memory:").await.unwrap();
13437            let r = conn
13438                .query("SELECT degrees(pi()), radians(180)")
13439                .await
13440                .unwrap();
13441            assert_eq!(row_values(&r[0])[0].to_text(), "180.0");
13442            let v = row_values(&r[0])[1].to_text();
13443            assert!(v.starts_with("3.14159265"), "got {v}");
13444        });
13445    }
13446
13447    #[test]
13448    fn conformance_048_mod_func() {
13449        asupersync::test_utils::run_test(|| async {
13450            let conn = Connection::open(":memory:").await.unwrap();
13451            let r = conn.query("SELECT mod(10, 3), mod(10, 0)").await.unwrap();
13452            assert_eq!(row_values(&r[0])[0].to_text(), "1.0");
13453            assert!(row_values(&r[0])[1].is_null());
13454        });
13455    }
13456
13457    #[test]
13458    fn conformance_048_atan2() {
13459        asupersync::test_utils::run_test(|| async {
13460            let conn = Connection::open(":memory:").await.unwrap();
13461            let r = conn.query("SELECT atan2(0, 1), atan2(1, 0)").await.unwrap();
13462            assert_eq!(row_values(&r[0])[0].to_text(), "0.0");
13463            let v = row_values(&r[0])[1].to_text();
13464            assert!(v.starts_with("1.57079632"), "got {v}");
13465        });
13466    }
13467
13468    #[test]
13469    fn conformance_048_math_null_propagation() {
13470        asupersync::test_utils::run_test(|| async {
13471            let conn = Connection::open(":memory:").await.unwrap();
13472            let r = conn
13473                .query("SELECT sin(NULL), sqrt(NULL), pow(NULL, 2), mod(NULL, 3)")
13474                .await
13475                .unwrap();
13476            assert!(row_values(&r[0])[0].is_null());
13477            assert!(row_values(&r[0])[1].is_null());
13478            assert!(row_values(&r[0])[2].is_null());
13479            assert!(row_values(&r[0])[3].is_null());
13480        });
13481    }
13482
13483    #[test]
13484    fn conformance_048_math_with_table() {
13485        asupersync::test_utils::run_test(|| async {
13486            let conn = Connection::open(":memory:").await.unwrap();
13487            conn.execute("CREATE TABLE nums(x REAL)").await.unwrap();
13488            conn.execute("INSERT INTO nums VALUES (4.0),(9.0),(16.0),(25.0)")
13489                .await
13490                .unwrap();
13491            let r = conn
13492                .query("SELECT sqrt(x) FROM nums ORDER BY x")
13493                .await
13494                .unwrap();
13495            assert_eq!(r.len(), 4);
13496            assert_eq!(row_values(&r[0])[0].to_text(), "2.0");
13497            assert_eq!(row_values(&r[1])[0].to_text(), "3.0");
13498            assert_eq!(row_values(&r[2])[0].to_text(), "4.0");
13499            assert_eq!(row_values(&r[3])[0].to_text(), "5.0");
13500        });
13501    }
13502
13503    #[test]
13504    fn conformance_048_hyperbolic() {
13505        asupersync::test_utils::run_test(|| async {
13506            let conn = Connection::open(":memory:").await.unwrap();
13507            let r = conn
13508                .query("SELECT sinh(0), cosh(0), tanh(0)")
13509                .await
13510                .unwrap();
13511            assert_eq!(row_values(&r[0])[0].to_text(), "0.0");
13512            assert_eq!(row_values(&r[0])[1].to_text(), "1.0");
13513            assert_eq!(row_values(&r[0])[2].to_text(), "0.0");
13514        });
13515    }
13516
13517    // ── Conformance 049: String functions and typeof ─────────────────
13518
13519    #[test]
13520    fn conformance_049_length() {
13521        asupersync::test_utils::run_test(|| async {
13522            let conn = Connection::open(":memory:").await.unwrap();
13523            let r = conn
13524                .query("SELECT length('hello'), length(''), length(NULL)")
13525                .await
13526                .unwrap();
13527            assert_eq!(row_values(&r[0])[0].to_text(), "5");
13528            assert_eq!(row_values(&r[0])[1].to_text(), "0");
13529            assert!(row_values(&r[0])[2].is_null());
13530        });
13531    }
13532
13533    #[test]
13534    fn conformance_049_upper_lower() {
13535        asupersync::test_utils::run_test(|| async {
13536            let conn = Connection::open(":memory:").await.unwrap();
13537            let r = conn
13538                .query("SELECT upper('hello'), lower('WORLD')")
13539                .await
13540                .unwrap();
13541            assert_eq!(row_values(&r[0])[0].to_text(), "HELLO");
13542            assert_eq!(row_values(&r[0])[1].to_text(), "world");
13543        });
13544    }
13545
13546    #[test]
13547    fn conformance_049_typeof() {
13548        asupersync::test_utils::run_test(|| async {
13549            let conn = Connection::open(":memory:").await.unwrap();
13550            let r = conn
13551                .query("SELECT typeof(42), typeof(3.14), typeof('hi'), typeof(NULL), typeof(X'00')")
13552                .await
13553                .unwrap();
13554            assert_eq!(row_values(&r[0])[0].to_text(), "integer");
13555            assert_eq!(row_values(&r[0])[1].to_text(), "real");
13556            assert_eq!(row_values(&r[0])[2].to_text(), "text");
13557            assert_eq!(row_values(&r[0])[3].to_text(), "null");
13558            assert_eq!(row_values(&r[0])[4].to_text(), "blob");
13559        });
13560    }
13561
13562    #[test]
13563    fn conformance_049_max_min_scalar() {
13564        asupersync::test_utils::run_test(|| async {
13565            let conn = Connection::open(":memory:").await.unwrap();
13566            let r = conn
13567                .query("SELECT max(1, 5, 3), min(10, 2, 7)")
13568                .await
13569                .unwrap();
13570            assert_eq!(row_values(&r[0])[0].to_text(), "5");
13571            assert_eq!(row_values(&r[0])[1].to_text(), "2");
13572        });
13573    }
13574
13575    #[test]
13576    fn conformance_049_total_changes() {
13577        asupersync::test_utils::run_test(|| async {
13578            let conn = Connection::open(":memory:").await.unwrap();
13579            conn.execute("CREATE TABLE t1(x INTEGER)").await.unwrap();
13580            conn.execute("INSERT INTO t1 VALUES (1),(2),(3)")
13581                .await
13582                .unwrap();
13583            let r = conn.query("SELECT changes()").await.unwrap();
13584            assert_eq!(row_values(&r[0])[0].to_text(), "3");
13585        });
13586    }
13587
13588    // ── Conformance 050: Type coercion and string concatenation ──────
13589
13590    #[test]
13591    fn conformance_050_string_concat() {
13592        asupersync::test_utils::run_test(|| async {
13593            let conn = Connection::open(":memory:").await.unwrap();
13594            let r = conn
13595                .query("SELECT 'hello' || ' ' || 'world'")
13596                .await
13597                .unwrap();
13598            assert_eq!(row_values(&r[0])[0].to_text(), "hello world");
13599        });
13600    }
13601
13602    #[test]
13603    fn conformance_050_concat_with_numbers() {
13604        asupersync::test_utils::run_test(|| async {
13605            let conn = Connection::open(":memory:").await.unwrap();
13606            let r = conn.query("SELECT 'val=' || 42").await.unwrap();
13607            assert_eq!(row_values(&r[0])[0].to_text(), "val=42");
13608        });
13609    }
13610
13611    #[test]
13612    fn conformance_050_concat_null() {
13613        asupersync::test_utils::run_test(|| async {
13614            let conn = Connection::open(":memory:").await.unwrap();
13615            let r = conn.query("SELECT 'abc' || NULL").await.unwrap();
13616            assert!(row_values(&r[0])[0].is_null());
13617        });
13618    }
13619
13620    #[test]
13621    fn conformance_050_numeric_string_comparison() {
13622        asupersync::test_utils::run_test(|| async {
13623            let conn = Connection::open(":memory:").await.unwrap();
13624            let r = conn
13625                .query("SELECT CASE WHEN 10 = '10' THEN 'equal' ELSE 'not_equal' END")
13626                .await
13627                .unwrap();
13628            assert_eq!(row_values(&r[0])[0].to_text(), "not_equal");
13629        });
13630    }
13631
13632    #[test]
13633    fn conformance_050_mixed_arithmetic() {
13634        asupersync::test_utils::run_test(|| async {
13635            let conn = Connection::open(":memory:").await.unwrap();
13636            let r = conn
13637                .query("SELECT 1 + 2.5, 10 / 3, 10.0 / 3")
13638                .await
13639                .unwrap();
13640            assert_eq!(row_values(&r[0])[0].to_text(), "3.5");
13641            assert_eq!(row_values(&r[0])[1].to_text(), "3");
13642            let v = row_values(&r[0])[2].to_text();
13643            assert!(v.starts_with("3.333"), "got {v}");
13644        });
13645    }
13646
13647    #[test]
13648    fn conformance_050_cast_types() {
13649        asupersync::test_utils::run_test(|| async {
13650            let conn = Connection::open(":memory:").await.unwrap();
13651            let r = conn
13652                .query("SELECT CAST('123' AS INTEGER), CAST(3.14 AS INTEGER), CAST(42 AS TEXT)")
13653                .await
13654                .unwrap();
13655            assert_eq!(row_values(&r[0])[0].to_text(), "123");
13656            assert_eq!(row_values(&r[0])[1].to_text(), "3");
13657            assert_eq!(row_values(&r[0])[2].to_text(), "42");
13658        });
13659    }
13660
13661    // ── Conformance 051: Expression edge cases ──────────────────────
13662
13663    #[test]
13664    fn conformance_051_unary_minus() {
13665        asupersync::test_utils::run_test(|| async {
13666            let conn = Connection::open(":memory:").await.unwrap();
13667            let r = conn.query("SELECT -(-5), -(3.14)").await.unwrap();
13668            assert_eq!(row_values(&r[0])[0].to_text(), "5");
13669            assert_eq!(row_values(&r[0])[1].to_text(), "-3.14");
13670        });
13671    }
13672
13673    #[test]
13674    fn conformance_051_modulo_operator() {
13675        asupersync::test_utils::run_test(|| async {
13676            let conn = Connection::open(":memory:").await.unwrap();
13677            let r = conn.query("SELECT 17 % 5, -7 % 3").await.unwrap();
13678            assert_eq!(row_values(&r[0])[0].to_text(), "2");
13679            assert_eq!(row_values(&r[0])[1].to_text(), "-1");
13680        });
13681    }
13682
13683    #[test]
13684    fn conformance_051_boolean_expressions() {
13685        asupersync::test_utils::run_test(|| async {
13686            let conn = Connection::open(":memory:").await.unwrap();
13687            let r = conn
13688                .query("SELECT 1 AND 1, 1 AND 0, 0 OR 1, 0 OR 0, NOT 0, NOT 1")
13689                .await
13690                .unwrap();
13691            assert_eq!(row_values(&r[0])[0].to_text(), "1");
13692            assert_eq!(row_values(&r[0])[1].to_text(), "0");
13693            assert_eq!(row_values(&r[0])[2].to_text(), "1");
13694            assert_eq!(row_values(&r[0])[3].to_text(), "0");
13695            assert_eq!(row_values(&r[0])[4].to_text(), "1");
13696            assert_eq!(row_values(&r[0])[5].to_text(), "0");
13697        });
13698    }
13699
13700    #[test]
13701    fn conformance_051_comparison_operators() {
13702        asupersync::test_utils::run_test(|| async {
13703            let conn = Connection::open(":memory:").await.unwrap();
13704            let r = conn
13705                .query("SELECT 5 > 3, 5 < 3, 5 >= 5, 5 <= 4, 5 != 3, 5 == 5")
13706                .await
13707                .unwrap();
13708            assert_eq!(row_values(&r[0])[0].to_text(), "1");
13709            assert_eq!(row_values(&r[0])[1].to_text(), "0");
13710            assert_eq!(row_values(&r[0])[2].to_text(), "1");
13711            assert_eq!(row_values(&r[0])[3].to_text(), "0");
13712            assert_eq!(row_values(&r[0])[4].to_text(), "1");
13713            assert_eq!(row_values(&r[0])[5].to_text(), "1");
13714        });
13715    }
13716
13717    // ── Conformance 052: Complex multi-table queries ─────────────────
13718
13719    #[test]
13720    fn conformance_052_multi_table_join_aggregate() {
13721        asupersync::test_utils::run_test(|| async {
13722            let conn = Connection::open(":memory:").await.unwrap();
13723            conn.execute("CREATE TABLE departments(id INTEGER PRIMARY KEY, name TEXT)")
13724                .await
13725                .unwrap();
13726            conn.execute("CREATE TABLE employees(id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER, salary REAL)")
13727            .await
13728            .unwrap();
13729            conn.execute("INSERT INTO departments VALUES (1,'Engineering'),(2,'Sales'),(3,'HR')")
13730                .await
13731                .unwrap();
13732            conn.execute("INSERT INTO employees VALUES (1,'Alice',1,80000),(2,'Bob',1,90000),(3,'Charlie',2,70000),(4,'Diana',2,75000),(5,'Eve',3,60000)")
13733            .await
13734            .unwrap();
13735            let r = conn
13736            .query("SELECT d.name, COUNT(e.id), SUM(e.salary) FROM departments d JOIN employees e ON d.id = e.dept_id GROUP BY d.name ORDER BY d.name")
13737            .await
13738            .unwrap();
13739            assert_eq!(r.len(), 3);
13740            assert_eq!(row_values(&r[0])[0].to_text(), "Engineering");
13741            assert_eq!(row_values(&r[0])[1].to_text(), "2");
13742            assert_eq!(row_values(&r[0])[2].to_text(), "170000.0");
13743            assert_eq!(row_values(&r[1])[0].to_text(), "HR");
13744            assert_eq!(row_values(&r[1])[1].to_text(), "1");
13745            assert_eq!(row_values(&r[1])[2].to_text(), "60000.0");
13746            assert_eq!(row_values(&r[2])[0].to_text(), "Sales");
13747            assert_eq!(row_values(&r[2])[1].to_text(), "2");
13748            assert_eq!(row_values(&r[2])[2].to_text(), "145000.0");
13749        });
13750    }
13751
13752    #[test]
13753    fn conformance_052_subquery_in_where() {
13754        asupersync::test_utils::run_test(|| async {
13755            let conn = Connection::open(":memory:").await.unwrap();
13756            conn.execute("CREATE TABLE products(id INTEGER PRIMARY KEY, name TEXT, price REAL)")
13757                .await
13758                .unwrap();
13759            conn.execute(
13760                "INSERT INTO products VALUES (1,'A',10.0),(2,'B',20.0),(3,'C',30.0),(4,'D',15.0)",
13761            )
13762            .await
13763            .unwrap();
13764            let r = conn
13765            .query("SELECT name FROM products WHERE price > (SELECT AVG(price) FROM products) ORDER BY name")
13766            .await
13767            .unwrap();
13768            assert_eq!(r.len(), 2);
13769            assert_eq!(row_values(&r[0])[0].to_text(), "B");
13770            assert_eq!(row_values(&r[1])[0].to_text(), "C");
13771        });
13772    }
13773
13774    #[test]
13775    fn conformance_052_insert_with_subquery() {
13776        asupersync::test_utils::run_test(|| async {
13777            let conn = Connection::open(":memory:").await.unwrap();
13778            conn.execute("CREATE TABLE src(val INTEGER)").await.unwrap();
13779            conn.execute("CREATE TABLE dst(val INTEGER)").await.unwrap();
13780            conn.execute("INSERT INTO src VALUES (1),(2),(3),(4),(5)")
13781                .await
13782                .unwrap();
13783            conn.execute("INSERT INTO dst SELECT val FROM src WHERE val > 3")
13784                .await
13785                .unwrap();
13786            let r = conn
13787                .query("SELECT val FROM dst ORDER BY val")
13788                .await
13789                .unwrap();
13790            assert_eq!(r.len(), 2);
13791            assert_eq!(row_values(&r[0])[0].to_text(), "4");
13792            assert_eq!(row_values(&r[1])[0].to_text(), "5");
13793        });
13794    }
13795
13796    // -----------------------------------------------------------------------
13797    // Conformance suite 053: GROUP_CONCAT aggregate
13798    // -----------------------------------------------------------------------
13799
13800    #[test]
13801    fn conformance_053_group_concat_basic() {
13802        asupersync::test_utils::run_test(|| async {
13803            let conn = Connection::open(":memory:").await.unwrap();
13804            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, grp TEXT, val TEXT)")
13805                .await
13806                .unwrap();
13807            conn.execute("INSERT INTO t1 VALUES (1,'a','x'),(2,'a','y'),(3,'b','z'),(4,'a','w')")
13808                .await
13809                .unwrap();
13810            let r = conn
13811                .query("SELECT grp, GROUP_CONCAT(val) FROM t1 GROUP BY grp ORDER BY grp")
13812                .await
13813                .unwrap();
13814            assert_eq!(r.len(), 2);
13815            // Default separator is comma
13816            let a_vals = row_values(&r[0])[1].to_text();
13817            assert!(a_vals.contains('x'));
13818            assert!(a_vals.contains('y'));
13819            assert!(a_vals.contains('w'));
13820            assert_eq!(row_values(&r[1])[1].to_text(), "z");
13821        });
13822    }
13823
13824    #[test]
13825    fn conformance_053_group_concat_custom_separator() {
13826        asupersync::test_utils::run_test(|| async {
13827            let conn = Connection::open(":memory:").await.unwrap();
13828            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val TEXT)")
13829                .await
13830                .unwrap();
13831            conn.execute("INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c')")
13832                .await
13833                .unwrap();
13834            let r = conn
13835                .query("SELECT GROUP_CONCAT(val, ' | ') FROM t1")
13836                .await
13837                .unwrap();
13838            let result = row_values(&r[0])[0].to_text();
13839            // All values should be present with custom separator
13840            assert!(result.contains('a'));
13841            assert!(result.contains('b'));
13842            assert!(result.contains('c'));
13843            assert!(result.contains('|'));
13844        });
13845    }
13846
13847    #[test]
13848    fn conformance_053_group_concat_null_skip() {
13849        asupersync::test_utils::run_test(|| async {
13850            let conn = Connection::open(":memory:").await.unwrap();
13851            conn.execute("CREATE TABLE t1(val TEXT)").await.unwrap();
13852            conn.execute("INSERT INTO t1 VALUES ('a'),(NULL),('c')")
13853                .await
13854                .unwrap();
13855            let r = conn
13856                .query("SELECT GROUP_CONCAT(val) FROM t1")
13857                .await
13858                .unwrap();
13859            let result = row_values(&r[0])[0].to_text();
13860            // NULL values should be skipped
13861            assert!(result.contains('a'));
13862            assert!(result.contains('c'));
13863            assert!(!result.contains("NULL"));
13864        });
13865    }
13866
13867    // -----------------------------------------------------------------------
13868    // Conformance suite 054: PRAGMA
13869    // -----------------------------------------------------------------------
13870
13871    #[test]
13872    fn conformance_054_pragma_table_info() {
13873        asupersync::test_utils::run_test(|| async {
13874            let conn = Connection::open(":memory:").await.unwrap();
13875            conn.execute(
13876            "CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT NOT NULL, score REAL DEFAULT 0.0)",
13877        )
13878        .await
13879        .unwrap();
13880            let r = conn.query("PRAGMA table_info(t1)").await.unwrap();
13881            assert!(r.len() >= 3);
13882            // Check column names are present
13883            let col_names: Vec<String> = r.iter().map(|row| row_values(row)[1].to_text()).collect();
13884            assert!(col_names.contains(&"id".to_owned()));
13885            assert!(col_names.contains(&"name".to_owned()));
13886            assert!(col_names.contains(&"score".to_owned()));
13887        });
13888    }
13889
13890    #[test]
13891    fn pragma_table_info_preserves_declared_type_arguments() {
13892        asupersync::test_utils::run_test(|| async {
13893            let conn = Connection::open(":memory:").await.unwrap();
13894            conn.execute("CREATE TABLE metrics(amount DECIMAL(10, 2), name VARCHAR(255))")
13895                .await
13896                .unwrap();
13897            let rows = conn.query("PRAGMA table_info(metrics)").await.unwrap();
13898            let amount = rows
13899                .iter()
13900                .find(|row| row_values(row)[1].to_text() == "amount")
13901                .expect("amount column metadata");
13902            let name = rows
13903                .iter()
13904                .find(|row| row_values(row)[1].to_text() == "name")
13905                .expect("name column metadata");
13906            assert_eq!(row_values(amount)[2].to_text(), "DECIMAL(10, 2)");
13907            assert_eq!(row_values(name)[2].to_text(), "VARCHAR(255)");
13908        });
13909    }
13910
13911    #[test]
13912    fn conformance_054_pragma_user_version() {
13913        asupersync::test_utils::run_test(|| async {
13914            let conn = Connection::open(":memory:").await.unwrap();
13915            conn.execute("PRAGMA user_version = 42").await.unwrap();
13916            let r = conn.query("PRAGMA user_version").await.unwrap();
13917            assert_eq!(r.len(), 1);
13918            assert_eq!(row_values(&r[0])[0].to_text(), "42");
13919        });
13920    }
13921
13922    #[test]
13923    fn conformance_054_pragma_table_list() {
13924        asupersync::test_utils::run_test(|| async {
13925            let conn = Connection::open(":memory:").await.unwrap();
13926            conn.execute("CREATE TABLE alpha(x INTEGER)").await.unwrap();
13927            conn.execute("CREATE TABLE beta(y TEXT)").await.unwrap();
13928            // sqlite_master should list both tables
13929            let r = conn
13930                .query("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
13931                .await
13932                .unwrap();
13933            assert!(r.len() >= 2);
13934            let names: Vec<String> = r.iter().map(|row| row_values(row)[0].to_text()).collect();
13935            assert!(names.contains(&"alpha".to_owned()));
13936            assert!(names.contains(&"beta".to_owned()));
13937        });
13938    }
13939
13940    // -----------------------------------------------------------------------
13941    // Conformance suite 055: Nested and correlated subqueries
13942    // -----------------------------------------------------------------------
13943
13944    #[test]
13945    fn conformance_055_nested_subquery() {
13946        asupersync::test_utils::run_test(|| async {
13947            let conn = Connection::open(":memory:").await.unwrap();
13948            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
13949                .await
13950                .unwrap();
13951            conn.execute("INSERT INTO t1 VALUES (1,10),(2,20),(3,30),(4,40),(5,50)")
13952                .await
13953                .unwrap();
13954            // Subquery in subquery
13955            let r = conn
13956                .query(
13957                    "SELECT val FROM t1 WHERE val > \
13958                 (SELECT AVG(val) FROM t1 WHERE val < \
13959                 (SELECT MAX(val) FROM t1)) ORDER BY val",
13960                )
13961                .await
13962                .unwrap();
13963            // AVG of vals < 50 = (10+20+30+40)/4 = 25
13964            // vals > 25: 30, 40, 50
13965            assert_eq!(r.len(), 3);
13966            assert_eq!(row_values(&r[0])[0].to_text(), "30");
13967            assert_eq!(row_values(&r[1])[0].to_text(), "40");
13968            assert_eq!(row_values(&r[2])[0].to_text(), "50");
13969        });
13970    }
13971
13972    #[test]
13973    fn conformance_055_in_subquery() {
13974        asupersync::test_utils::run_test(|| async {
13975            let conn = Connection::open(":memory:").await.unwrap();
13976            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
13977                .await
13978                .unwrap();
13979            conn.execute("CREATE TABLE t2(id INTEGER PRIMARY KEY, t1_id INTEGER)")
13980                .await
13981                .unwrap();
13982            conn.execute("INSERT INTO t1 VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie')")
13983                .await
13984                .unwrap();
13985            conn.execute("INSERT INTO t2 VALUES (1,1),(2,3)")
13986                .await
13987                .unwrap();
13988            let r = conn
13989                .query("SELECT name FROM t1 WHERE id IN (SELECT t1_id FROM t2) ORDER BY name")
13990                .await
13991                .unwrap();
13992            assert_eq!(r.len(), 2);
13993            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
13994            assert_eq!(row_values(&r[1])[0].to_text(), "Charlie");
13995        });
13996    }
13997
13998    #[test]
13999    fn conformance_055_not_in_subquery() {
14000        asupersync::test_utils::run_test(|| async {
14001            let conn = Connection::open(":memory:").await.unwrap();
14002            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, name TEXT)")
14003                .await
14004                .unwrap();
14005            conn.execute("CREATE TABLE t2(ref_id INTEGER)")
14006                .await
14007                .unwrap();
14008            conn.execute("INSERT INTO t1 VALUES (1,'A'),(2,'B'),(3,'C')")
14009                .await
14010                .unwrap();
14011            conn.execute("INSERT INTO t2 VALUES (1),(3)").await.unwrap();
14012            let r = conn
14013                .query("SELECT name FROM t1 WHERE id NOT IN (SELECT ref_id FROM t2)")
14014                .await
14015                .unwrap();
14016            assert_eq!(r.len(), 1);
14017            assert_eq!(row_values(&r[0])[0].to_text(), "B");
14018        });
14019    }
14020
14021    // -----------------------------------------------------------------------
14022    // Conformance suite 056: Multi-table relationships
14023    // -----------------------------------------------------------------------
14024
14025    #[test]
14026    fn conformance_056_three_table_join() {
14027        asupersync::test_utils::run_test(|| async {
14028            let conn = Connection::open(":memory:").await.unwrap();
14029            conn.execute("CREATE TABLE departments(id INTEGER PRIMARY KEY, name TEXT)")
14030                .await
14031                .unwrap();
14032            conn.execute(
14033                "CREATE TABLE employees(id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER)",
14034            )
14035            .await
14036            .unwrap();
14037            conn.execute(
14038                "CREATE TABLE projects(id INTEGER PRIMARY KEY, title TEXT, emp_id INTEGER)",
14039            )
14040            .await
14041            .unwrap();
14042            conn.execute("INSERT INTO departments VALUES (1,'Eng'),(2,'Sales')")
14043                .await
14044                .unwrap();
14045            conn.execute("INSERT INTO employees VALUES (1,'Alice',1),(2,'Bob',1),(3,'Charlie',2)")
14046                .await
14047                .unwrap();
14048            conn.execute("INSERT INTO projects VALUES (1,'Widget',1),(2,'Gadget',2),(3,'Deal',3)")
14049                .await
14050                .unwrap();
14051            let r = conn
14052                .query(
14053                    "SELECT d.name, e.name, p.title \
14054                 FROM departments d \
14055                 JOIN employees e ON e.dept_id = d.id \
14056                 JOIN projects p ON p.emp_id = e.id \
14057                 WHERE d.name = 'Eng' \
14058                 ORDER BY e.name",
14059                )
14060                .await
14061                .unwrap();
14062            assert_eq!(r.len(), 2);
14063            assert_eq!(row_values(&r[0])[1].to_text(), "Alice");
14064            assert_eq!(row_values(&r[0])[2].to_text(), "Widget");
14065            assert_eq!(row_values(&r[1])[1].to_text(), "Bob");
14066            assert_eq!(row_values(&r[1])[2].to_text(), "Gadget");
14067        });
14068    }
14069
14070    #[test]
14071    fn conformance_056_left_join_with_aggregate() {
14072        asupersync::test_utils::run_test(|| async {
14073            let conn = Connection::open(":memory:").await.unwrap();
14074            conn.execute("CREATE TABLE teams(id INTEGER PRIMARY KEY, name TEXT)")
14075                .await
14076                .unwrap();
14077            conn.execute(
14078                "CREATE TABLE members(id INTEGER PRIMARY KEY, team_id INTEGER, name TEXT)",
14079            )
14080            .await
14081            .unwrap();
14082            conn.execute("INSERT INTO teams VALUES (1,'Alpha'),(2,'Beta'),(3,'Gamma')")
14083                .await
14084                .unwrap();
14085            conn.execute("INSERT INTO members VALUES (1,1,'A1'),(2,1,'A2'),(3,2,'B1')")
14086                .await
14087                .unwrap();
14088            let r = conn
14089            .query(
14090                "SELECT t.name, SUM(CASE WHEN m.id IS NOT NULL THEN 1 ELSE 0 END) as member_count \
14091                 FROM teams t \
14092                 LEFT JOIN members m ON m.team_id = t.id \
14093                 GROUP BY t.id, t.name \
14094                 ORDER BY t.name",
14095            )
14096            .await
14097            .unwrap();
14098            assert_eq!(r.len(), 3);
14099            assert_eq!(row_values(&r[0])[0].to_text(), "Alpha");
14100            assert_eq!(row_values(&r[0])[1].to_text(), "2");
14101            assert_eq!(row_values(&r[1])[0].to_text(), "Beta");
14102            assert_eq!(row_values(&r[1])[1].to_text(), "1");
14103            assert_eq!(row_values(&r[2])[0].to_text(), "Gamma");
14104            assert_eq!(row_values(&r[2])[1].to_text(), "0");
14105        });
14106    }
14107
14108    // -----------------------------------------------------------------------
14109    // Conformance suite 057: Expression edge cases
14110    // -----------------------------------------------------------------------
14111
14112    #[test]
14113    fn conformance_057_integer_division() {
14114        asupersync::test_utils::run_test(|| async {
14115            let conn = Connection::open(":memory:").await.unwrap();
14116            // SQLite integer division truncates toward zero
14117            let r = conn.query("SELECT 7/2, -7/2, 1/3").await.unwrap();
14118            assert_eq!(row_values(&r[0])[0].to_text(), "3");
14119            assert_eq!(row_values(&r[0])[1].to_text(), "-3");
14120            assert_eq!(row_values(&r[0])[2].to_text(), "0");
14121        });
14122    }
14123
14124    #[test]
14125    fn conformance_057_real_division() {
14126        asupersync::test_utils::run_test(|| async {
14127            let conn = Connection::open(":memory:").await.unwrap();
14128            let r = conn.query("SELECT 7.0/2, 1.0/3.0").await.unwrap();
14129            assert_eq!(row_values(&r[0])[0].to_text(), "3.5");
14130            // 1/3 as float
14131            let third: f64 = row_values(&r[0])[1].to_text().parse().unwrap();
14132            assert!((third - 1.0 / 3.0).abs() < 1e-10);
14133        });
14134    }
14135
14136    #[test]
14137    fn conformance_057_string_concatenation_operator() {
14138        asupersync::test_utils::run_test(|| async {
14139            let conn = Connection::open(":memory:").await.unwrap();
14140            let r = conn
14141                .query("SELECT 'hello' || ' ' || 'world'")
14142                .await
14143                .unwrap();
14144            assert_eq!(row_values(&r[0])[0].to_text(), "hello world");
14145        });
14146    }
14147
14148    #[test]
14149    fn conformance_057_null_arithmetic() {
14150        asupersync::test_utils::run_test(|| async {
14151            let conn = Connection::open(":memory:").await.unwrap();
14152            // Any arithmetic with NULL produces NULL
14153            let r = conn
14154                .query("SELECT NULL + 1, NULL * 5, NULL || 'text'")
14155                .await
14156                .unwrap();
14157            assert!(row_values(&r[0])[0].is_null());
14158            assert!(row_values(&r[0])[1].is_null());
14159            assert!(row_values(&r[0])[2].is_null());
14160        });
14161    }
14162
14163    // -----------------------------------------------------------------------
14164    // Conformance suite 058: Correlated subqueries and CREATE TABLE AS SELECT
14165    // -----------------------------------------------------------------------
14166
14167    #[test]
14168    fn conformance_058_correlated_subquery_in_where() {
14169        asupersync::test_utils::run_test(|| async {
14170            let conn = Connection::open(":memory:").await.unwrap();
14171            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
14172                .await
14173                .unwrap();
14174            conn.execute("CREATE TABLE t2(id INTEGER, t1_id INTEGER, score INTEGER)")
14175                .await
14176                .unwrap();
14177            conn.execute("INSERT INTO t1 VALUES (1, 100), (2, 200), (3, 300)")
14178                .await
14179                .unwrap();
14180            conn.execute("INSERT INTO t2 VALUES (1,1,10),(2,1,20),(3,2,30),(4,3,5)")
14181                .await
14182                .unwrap();
14183            // Correlated subquery: get t1 rows where max t2 score > 15
14184            let r = conn
14185            .query(
14186                "SELECT t1.id, t1.val FROM t1 WHERE (SELECT MAX(score) FROM t2 WHERE t2.t1_id = t1.id) > 15 ORDER BY t1.id",
14187            )
14188            .await
14189            .unwrap();
14190            assert_eq!(r.len(), 2);
14191            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(1));
14192            assert_eq!(row_values(&r[0])[1], SqliteValue::Integer(100));
14193            assert_eq!(row_values(&r[1])[0], SqliteValue::Integer(2));
14194            assert_eq!(row_values(&r[1])[1], SqliteValue::Integer(200));
14195        });
14196    }
14197
14198    #[test]
14199    fn conformance_058_correlated_subquery_in_select() {
14200        asupersync::test_utils::run_test(|| async {
14201            let conn = Connection::open(":memory:").await.unwrap();
14202            conn.execute(
14203                "CREATE TABLE orders(id INTEGER PRIMARY KEY, customer_id INTEGER, amount REAL)",
14204            )
14205            .await
14206            .unwrap();
14207            conn.execute("CREATE TABLE customers(id INTEGER PRIMARY KEY, name TEXT)")
14208                .await
14209                .unwrap();
14210            conn.execute("INSERT INTO customers VALUES (1, 'Alice'), (2, 'Bob')")
14211                .await
14212                .unwrap();
14213            conn.execute("INSERT INTO orders VALUES (1,1,100.0),(2,1,200.0),(3,2,50.0)")
14214                .await
14215                .unwrap();
14216            // Correlated subquery in SELECT list
14217            let r = conn
14218            .query(
14219                "SELECT c.name, (SELECT SUM(amount) FROM orders o WHERE o.customer_id = c.id) AS total FROM customers c ORDER BY c.name",
14220            )
14221            .await
14222            .unwrap();
14223            assert_eq!(r.len(), 2);
14224            assert_eq!(row_values(&r[0])[0].to_text(), "Alice");
14225            assert_eq!(row_values(&r[0])[1].to_text(), "300.0");
14226            assert_eq!(row_values(&r[1])[0].to_text(), "Bob");
14227            assert_eq!(row_values(&r[1])[1].to_text(), "50.0");
14228        });
14229    }
14230
14231    #[test]
14232    fn conformance_058_create_table_as_select() {
14233        asupersync::test_utils::run_test(|| async {
14234            let conn = Connection::open(":memory:").await.unwrap();
14235            conn.execute("CREATE TABLE src(id INTEGER PRIMARY KEY, val INTEGER)")
14236                .await
14237                .unwrap();
14238            conn.execute("INSERT INTO src VALUES (1, 100), (2, 200), (3, 300)")
14239                .await
14240                .unwrap();
14241            // CREATE TABLE AS SELECT
14242            conn.execute(
14243                "CREATE TABLE dst AS SELECT id, val * 2 AS doubled FROM src WHERE id <= 2",
14244            )
14245            .await
14246            .unwrap();
14247            let r = conn.query("SELECT * FROM dst ORDER BY id").await.unwrap();
14248            assert_eq!(r.len(), 2);
14249            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(1));
14250            assert_eq!(row_values(&r[0])[1], SqliteValue::Integer(200));
14251            assert_eq!(row_values(&r[1])[0], SqliteValue::Integer(2));
14252            assert_eq!(row_values(&r[1])[1], SqliteValue::Integer(400));
14253        });
14254    }
14255
14256    // -----------------------------------------------------------------------
14257    // Conformance suite 059: GROUP BY expressions and HAVING edge cases
14258    // -----------------------------------------------------------------------
14259
14260    #[test]
14261    fn conformance_059_group_by_expression() {
14262        asupersync::test_utils::run_test(|| async {
14263            let conn = Connection::open(":memory:").await.unwrap();
14264            conn.execute("CREATE TABLE t1(id INTEGER PRIMARY KEY, val INTEGER)")
14265                .await
14266                .unwrap();
14267            conn.execute("INSERT INTO t1 VALUES (1, 100), (2, 200), (3, 300)")
14268                .await
14269                .unwrap();
14270            // GROUP BY an expression, not just a column
14271            let r = conn
14272            .query(
14273                "SELECT val / 100 AS bucket, COUNT(*) FROM t1 GROUP BY val / 100 ORDER BY bucket",
14274            )
14275            .await
14276            .unwrap();
14277            assert_eq!(r.len(), 3);
14278            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(1));
14279            assert_eq!(row_values(&r[0])[1], SqliteValue::Integer(1));
14280            assert_eq!(row_values(&r[1])[0], SqliteValue::Integer(2));
14281            assert_eq!(row_values(&r[1])[1], SqliteValue::Integer(1));
14282            assert_eq!(row_values(&r[2])[0], SqliteValue::Integer(3));
14283            assert_eq!(row_values(&r[2])[1], SqliteValue::Integer(1));
14284        });
14285    }
14286
14287    #[test]
14288    fn conformance_059_group_by_function() {
14289        asupersync::test_utils::run_test(|| async {
14290            let conn = Connection::open(":memory:").await.unwrap();
14291            conn.execute("CREATE TABLE names(id INTEGER PRIMARY KEY, name TEXT)")
14292                .await
14293                .unwrap();
14294            conn.execute("INSERT INTO names VALUES (1,'Alice'),(2,'alice'),(3,'Bob'),(4,'BOB')")
14295                .await
14296                .unwrap();
14297            // GROUP BY UPPER(name) collapses case-variants
14298            let r = conn
14299            .query("SELECT UPPER(name) AS uname, COUNT(*) FROM names GROUP BY UPPER(name) ORDER BY uname")
14300            .await
14301            .unwrap();
14302            assert_eq!(r.len(), 2);
14303            assert_eq!(row_values(&r[0])[0].to_text(), "ALICE");
14304            assert_eq!(row_values(&r[0])[1], SqliteValue::Integer(2));
14305            assert_eq!(row_values(&r[1])[0].to_text(), "BOB");
14306            assert_eq!(row_values(&r[1])[1], SqliteValue::Integer(2));
14307        });
14308    }
14309
14310    #[test]
14311    fn conformance_059_having_without_group_by() {
14312        asupersync::test_utils::run_test(|| async {
14313            let conn = Connection::open(":memory:").await.unwrap();
14314            conn.execute("CREATE TABLE t1(val INTEGER)").await.unwrap();
14315            conn.execute("INSERT INTO t1 VALUES (1),(2),(3)")
14316                .await
14317                .unwrap();
14318            // HAVING without GROUP BY: implicit single-group aggregation
14319            // C SQLite verified: returns one row with COUNT(*) = 3
14320            let r = conn
14321                .query("SELECT COUNT(*) FROM t1 HAVING COUNT(*) > 2")
14322                .await
14323                .unwrap();
14324            assert_eq!(r.len(), 1);
14325            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(3));
14326        });
14327    }
14328
14329    #[test]
14330    fn conformance_059_having_without_group_by_no_match() {
14331        asupersync::test_utils::run_test(|| async {
14332            let conn = Connection::open(":memory:").await.unwrap();
14333            conn.execute("CREATE TABLE t1(val INTEGER)").await.unwrap();
14334            conn.execute("INSERT INTO t1 VALUES (1),(2),(3)")
14335                .await
14336                .unwrap();
14337            // HAVING condition not met: returns empty result
14338            let r = conn
14339                .query("SELECT COUNT(*) FROM t1 HAVING COUNT(*) > 5")
14340                .await
14341                .unwrap();
14342            assert_eq!(r.len(), 0);
14343        });
14344    }
14345
14346    // ── Conformance suite 060: Regression tests for function name case,
14347    //    ORDER BY column index, HAVING, and comparison coercion ────────
14348
14349    #[test]
14350    fn conformance_060_lowercase_function_names() {
14351        asupersync::test_utils::run_test(|| async {
14352            let conn = Connection::open(":memory:").await.unwrap();
14353            // Verify lowercase function names resolve correctly in the registry.
14354            let r = conn.query("SELECT typeof(42)").await.unwrap();
14355            assert_eq!(row_values(&r[0])[0].to_text(), "integer");
14356
14357            let r = conn.query("SELECT typeof(3.14)").await.unwrap();
14358            assert_eq!(row_values(&r[0])[0].to_text(), "real");
14359
14360            let r = conn.query("SELECT typeof('hello')").await.unwrap();
14361            assert_eq!(row_values(&r[0])[0].to_text(), "text");
14362
14363            let r = conn.query("SELECT typeof(NULL)").await.unwrap();
14364            assert_eq!(row_values(&r[0])[0].to_text(), "null");
14365        });
14366    }
14367
14368    #[test]
14369    fn conformance_060_mixed_case_function_names() {
14370        asupersync::test_utils::run_test(|| async {
14371            let conn = Connection::open(":memory:").await.unwrap();
14372            // Mixed-case function calls should all resolve.
14373            let r = conn.query("SELECT abs(-7)").await.unwrap();
14374            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(7));
14375
14376            let r = conn.query("SELECT ABS(-7)").await.unwrap();
14377            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(7));
14378
14379            let r = conn.query("SELECT Abs(-7)").await.unwrap();
14380            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(7));
14381        });
14382    }
14383
14384    #[test]
14385    fn conformance_060_hex_upper_lower() {
14386        asupersync::test_utils::run_test(|| async {
14387            let conn = Connection::open(":memory:").await.unwrap();
14388            let r = conn.query("SELECT hex(255)").await.unwrap();
14389            assert_eq!(row_values(&r[0])[0].to_text(), "323535");
14390
14391            let r = conn.query("SELECT HEX(255)").await.unwrap();
14392            assert_eq!(row_values(&r[0])[0].to_text(), "323535");
14393        });
14394    }
14395
14396    #[test]
14397    fn conformance_060_order_by_column_index_multi() {
14398        asupersync::test_utils::run_test(|| async {
14399            let conn = Connection::open(":memory:").await.unwrap();
14400            conn.execute("CREATE TABLE t2(x TEXT, y INTEGER, z REAL)")
14401                .await
14402                .unwrap();
14403            conn.execute("INSERT INTO t2 VALUES ('a', 3, 1.0), ('b', 1, 3.0), ('c', 2, 2.0)")
14404                .await
14405                .unwrap();
14406            // ORDER BY first column (text, ascending)
14407            let r = conn.query("SELECT x, y FROM t2 ORDER BY 1").await.unwrap();
14408            assert_eq!(row_values(&r[0])[0].to_text(), "a");
14409            assert_eq!(row_values(&r[1])[0].to_text(), "b");
14410            assert_eq!(row_values(&r[2])[0].to_text(), "c");
14411        });
14412    }
14413
14414    #[test]
14415    fn conformance_060_order_by_column_index_desc() {
14416        asupersync::test_utils::run_test(|| async {
14417            let conn = Connection::open(":memory:").await.unwrap();
14418            conn.execute("CREATE TABLE t3(a TEXT, b INTEGER)")
14419                .await
14420                .unwrap();
14421            conn.execute("INSERT INTO t3 VALUES ('x', 3), ('y', 1), ('z', 2)")
14422                .await
14423                .unwrap();
14424            // ORDER BY 2 DESC — sort by b descending
14425            let r = conn
14426                .query("SELECT a, b FROM t3 ORDER BY 2 DESC")
14427                .await
14428                .unwrap();
14429            assert_eq!(row_values(&r[0])[0].to_text(), "x"); // b=3
14430            assert_eq!(row_values(&r[1])[0].to_text(), "z"); // b=2
14431            assert_eq!(row_values(&r[2])[0].to_text(), "y"); // b=1
14432        });
14433    }
14434
14435    #[test]
14436    fn conformance_060_having_sum_aggregate() {
14437        asupersync::test_utils::run_test(|| async {
14438            let conn = Connection::open(":memory:").await.unwrap();
14439            conn.execute("CREATE TABLE sales(amount REAL)")
14440                .await
14441                .unwrap();
14442            conn.execute("INSERT INTO sales VALUES (100.0),(200.0),(300.0)")
14443                .await
14444                .unwrap();
14445            // HAVING with SUM aggregate, no GROUP BY
14446            let r = conn
14447                .query("SELECT SUM(amount) FROM sales HAVING SUM(amount) > 500")
14448                .await
14449                .unwrap();
14450            assert_eq!(r.len(), 1);
14451            assert_eq!(row_values(&r[0])[0], SqliteValue::Float(600.0));
14452        });
14453    }
14454
14455    #[test]
14456    fn conformance_060_numeric_text_comparison_variants() {
14457        asupersync::test_utils::run_test(|| async {
14458            let conn = Connection::open(":memory:").await.unwrap();
14459            // Integer != text-integer without affinity
14460            let r = conn
14461                .query("SELECT CASE WHEN 42 = '42' THEN 1 ELSE 0 END")
14462                .await
14463                .unwrap();
14464            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(0));
14465
14466            // Float != text-float without affinity
14467            let r = conn
14468                .query("SELECT CASE WHEN 3.14 = '3.14' THEN 1 ELSE 0 END")
14469                .await
14470                .unwrap();
14471            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(0));
14472
14473            // Text that doesn't parse as number should NOT equal an integer
14474            let r = conn
14475                .query("SELECT CASE WHEN 10 = 'ten' THEN 1 ELSE 0 END")
14476                .await
14477                .unwrap();
14478            assert_eq!(row_values(&r[0])[0], SqliteValue::Integer(0));
14479        });
14480    }
14481
14482    // -----------------------------------------------------------------------
14483    // Regression: HAVING aggregate not in SELECT list (review fix)
14484    // -----------------------------------------------------------------------
14485
14486    #[test]
14487    fn regression_having_aggregate_not_in_select() {
14488        asupersync::test_utils::run_test(|| async {
14489            let conn = Connection::open(":memory:").await.unwrap();
14490            conn.execute("CREATE TABLE emp (dept TEXT, salary INTEGER);")
14491                .await
14492                .unwrap();
14493            conn.execute(
14494                "INSERT INTO emp VALUES ('A', 100), ('A', 200), ('B', 50), ('B', 60), ('B', 70);",
14495            )
14496            .await
14497            .unwrap();
14498            // COUNT(*) is only in HAVING, not in the SELECT list.
14499            let rows = conn
14500                .query("SELECT dept FROM emp GROUP BY dept HAVING COUNT(*) >= 3;")
14501                .await
14502                .unwrap();
14503            assert_eq!(rows.len(), 1);
14504            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("B".into()));
14505        });
14506    }
14507
14508    #[test]
14509    fn regression_having_sum_not_in_select() {
14510        asupersync::test_utils::run_test(|| async {
14511            let conn = Connection::open(":memory:").await.unwrap();
14512            conn.execute("CREATE TABLE sales (product TEXT, amount INTEGER);")
14513                .await
14514                .unwrap();
14515            conn.execute("INSERT INTO sales VALUES ('X', 10), ('X', 20), ('Y', 100), ('Y', 200);")
14516                .await
14517                .unwrap();
14518            // SUM(amount) is only in HAVING, not in SELECT.
14519            let rows = conn
14520                .query("SELECT product FROM sales GROUP BY product HAVING SUM(amount) > 50;")
14521                .await
14522                .unwrap();
14523            assert_eq!(rows.len(), 1);
14524            assert_eq!(row_values(&rows[0])[0], SqliteValue::Text("Y".into()));
14525        });
14526    }
14527
14528    #[test]
14529    fn regression_null_comparison_returns_null_not_zero() {
14530        asupersync::test_utils::run_test(|| async {
14531            let conn = Connection::open(":memory:").await.unwrap();
14532            // SQL three-valued logic: comparison with NULL produces NULL, not 0.
14533            let rows = conn.query("SELECT (1 = NULL);").await.unwrap();
14534            assert_eq!(rows.len(), 1);
14535            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
14536
14537            let rows = conn.query("SELECT (NULL > 5);").await.unwrap();
14538            assert_eq!(rows.len(), 1);
14539            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
14540
14541            let rows = conn.query("SELECT (NULL = NULL);").await.unwrap();
14542            assert_eq!(rows.len(), 1);
14543            assert_eq!(row_values(&rows[0])[0], SqliteValue::Null);
14544
14545            // IS / IS NOT should still return 0/1, not NULL.
14546            let rows = conn.query("SELECT (1 IS NULL);").await.unwrap();
14547            assert_eq!(rows.len(), 1);
14548            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(0));
14549
14550            let rows = conn.query("SELECT (NULL IS NULL);").await.unwrap();
14551            assert_eq!(rows.len(), 1);
14552            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(1));
14553        });
14554    }
14555
14556    // -----------------------------------------------------------------------
14557    // Generated columns (VIRTUAL / STORED) — F-SQL.19
14558    // -----------------------------------------------------------------------
14559
14560    #[test]
14561    fn generated_column_stored_basic() {
14562        asupersync::test_utils::run_test(|| async {
14563            let conn = Connection::open(":memory:").await.unwrap();
14564            conn.execute(
14565            "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER GENERATED ALWAYS AS (a + b) STORED)",
14566        )
14567        .await
14568        .unwrap();
14569            conn.execute("INSERT INTO t (a, b) VALUES (3, 7)")
14570                .await
14571                .unwrap();
14572            let rows = conn.query("SELECT a, b, c FROM t").await.unwrap();
14573            assert_eq!(rows.len(), 1);
14574            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(3));
14575            assert_eq!(row_values(&rows[0])[1], SqliteValue::Integer(7));
14576            assert_eq!(
14577                row_values(&rows[0])[2],
14578                SqliteValue::Integer(10),
14579                "STORED generated column c = a + b should be 10"
14580            );
14581        });
14582    }
14583
14584    #[test]
14585    fn generated_column_stored_multiplication() {
14586        asupersync::test_utils::run_test(|| async {
14587            let conn = Connection::open(":memory:").await.unwrap();
14588            conn.execute(
14589            "CREATE TABLE prices (qty INTEGER, unit_price INTEGER, total INTEGER GENERATED ALWAYS AS (qty * unit_price) STORED)",
14590        )
14591        .await
14592        .unwrap();
14593            conn.execute("INSERT INTO prices (qty, unit_price) VALUES (5, 12)")
14594                .await
14595                .unwrap();
14596            let rows = conn.query("SELECT total FROM prices").await.unwrap();
14597            assert_eq!(
14598                row_values(&rows[0])[0],
14599                SqliteValue::Integer(60),
14600                "STORED generated column total = qty * unit_price should be 60"
14601            );
14602        });
14603    }
14604
14605    #[test]
14606    fn generated_column_stored_multi_row() {
14607        asupersync::test_utils::run_test(|| async {
14608            let conn = Connection::open(":memory:").await.unwrap();
14609            conn.execute(
14610                "CREATE TABLE t (x INTEGER, doubled INTEGER GENERATED ALWAYS AS (x * 2) STORED)",
14611            )
14612            .await
14613            .unwrap();
14614            conn.execute("INSERT INTO t (x) VALUES (1)").await.unwrap();
14615            conn.execute("INSERT INTO t (x) VALUES (5)").await.unwrap();
14616            conn.execute("INSERT INTO t (x) VALUES (100)")
14617                .await
14618                .unwrap();
14619            let rows = conn
14620                .query("SELECT doubled FROM t ORDER BY x")
14621                .await
14622                .unwrap();
14623            assert_eq!(rows.len(), 3);
14624            assert_eq!(row_values(&rows[0])[0], SqliteValue::Integer(2));
14625            assert_eq!(row_values(&rows[1])[0], SqliteValue::Integer(10));
14626            assert_eq!(row_values(&rows[2])[0], SqliteValue::Integer(200));
14627        });
14628    }
14629
14630    #[test]
14631    fn generated_column_stored_update_recomputes() {
14632        asupersync::test_utils::run_test(|| async {
14633            let conn = Connection::open(":memory:").await.unwrap();
14634            conn.execute(
14635            "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER GENERATED ALWAYS AS (a + b) STORED)",
14636        )
14637        .await
14638        .unwrap();
14639            conn.execute("INSERT INTO t (a, b) VALUES (3, 7)")
14640                .await
14641                .unwrap();
14642            conn.execute("UPDATE t SET a = 10 WHERE b = 7")
14643                .await
14644                .unwrap();
14645            let rows = conn.query("SELECT c FROM t").await.unwrap();
14646            assert_eq!(
14647                row_values(&rows[0])[0],
14648                SqliteValue::Integer(17),
14649                "STORED generated column should recompute after UPDATE: 10 + 7 = 17"
14650            );
14651        });
14652    }
14653
14654    // -----------------------------------------------------------------------
14655    // Foreign Key enforcement — bd-thqgm
14656    // -----------------------------------------------------------------------
14657
14658    #[test]
14659    fn fk_insert_valid_parent_succeeds() {
14660        asupersync::test_utils::run_test(|| async {
14661            let conn = Connection::open(":memory:").await.unwrap();
14662            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14663            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY, name TEXT)")
14664                .await
14665                .unwrap();
14666            conn.execute(
14667            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
14668        )
14669        .await
14670        .unwrap();
14671            conn.execute("INSERT INTO parent VALUES (1, 'Alice')")
14672                .await
14673                .unwrap();
14674            // Child references existing parent — should succeed.
14675            conn.execute("INSERT INTO child VALUES (1, 1)")
14676                .await
14677                .unwrap();
14678            let rows = conn.query("SELECT * FROM child").await.unwrap();
14679            assert_eq!(rows.len(), 1);
14680        });
14681    }
14682
14683    #[test]
14684    fn fk_insert_missing_parent_fails() {
14685        asupersync::test_utils::run_test(|| async {
14686            let conn = Connection::open(":memory:").await.unwrap();
14687            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14688            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY, name TEXT)")
14689                .await
14690                .unwrap();
14691            conn.execute(
14692            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
14693        )
14694        .await
14695        .unwrap();
14696            // No parent row with id=99 — should fail.
14697            let result = conn.execute("INSERT INTO child VALUES (1, 99)").await;
14698            assert!(result.is_err(), "INSERT with missing FK parent should fail");
14699        });
14700    }
14701
14702    #[test]
14703    fn fk_insert_null_fk_value_succeeds() {
14704        asupersync::test_utils::run_test(|| async {
14705            let conn = Connection::open(":memory:").await.unwrap();
14706            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14707            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
14708                .await
14709                .unwrap();
14710            conn.execute(
14711            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
14712        )
14713        .await
14714        .unwrap();
14715            // NULL FK value should always succeed (SQL standard).
14716            conn.execute("INSERT INTO child VALUES (1, NULL)")
14717                .await
14718                .unwrap();
14719            let rows = conn.query("SELECT * FROM child").await.unwrap();
14720            assert_eq!(rows.len(), 1);
14721        });
14722    }
14723
14724    #[test]
14725    fn fk_off_by_default() {
14726        asupersync::test_utils::run_test(|| async {
14727            let conn = Connection::open(":memory:").await.unwrap();
14728            // FK enforcement is OFF by default (matching SQLite).
14729            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
14730                .await
14731                .unwrap();
14732            conn.execute(
14733            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
14734        )
14735        .await
14736        .unwrap();
14737            // Should succeed even without parent, because FK enforcement is off.
14738            conn.execute("INSERT INTO child VALUES (1, 99)")
14739                .await
14740                .unwrap();
14741        });
14742    }
14743
14744    #[test]
14745    fn fk_delete_parent_with_children_fails() {
14746        asupersync::test_utils::run_test(|| async {
14747            let conn = Connection::open(":memory:").await.unwrap();
14748            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14749            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY, name TEXT)")
14750                .await
14751                .unwrap();
14752            conn.execute(
14753            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
14754        )
14755        .await
14756        .unwrap();
14757            conn.execute("INSERT INTO parent VALUES (1, 'Alice')")
14758                .await
14759                .unwrap();
14760            conn.execute("INSERT INTO child VALUES (1, 1)")
14761                .await
14762                .unwrap();
14763            // Deleting parent with child references should fail (default NO ACTION).
14764            let result = conn.execute("DELETE FROM parent WHERE id = 1").await;
14765            assert!(
14766                result.is_err(),
14767                "DELETE parent with child references should fail with FK ON"
14768            );
14769        });
14770    }
14771
14772    #[test]
14773    fn fk_delete_cascade() {
14774        asupersync::test_utils::run_test(|| async {
14775            let conn = Connection::open(":memory:").await.unwrap();
14776            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14777            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY, name TEXT)")
14778                .await
14779                .unwrap();
14780            conn.execute(
14781            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id) ON DELETE CASCADE)",
14782        )
14783        .await
14784        .unwrap();
14785            conn.execute("INSERT INTO parent VALUES (1, 'Alice')")
14786                .await
14787                .unwrap();
14788            conn.execute("INSERT INTO child VALUES (1, 1)")
14789                .await
14790                .unwrap();
14791            conn.execute("INSERT INTO child VALUES (2, 1)")
14792                .await
14793                .unwrap();
14794            // CASCADE should delete children too.
14795            conn.execute("DELETE FROM parent WHERE id = 1")
14796                .await
14797                .unwrap();
14798            let rows = conn.query("SELECT * FROM child").await.unwrap();
14799            assert_eq!(
14800                rows.len(),
14801                0,
14802                "ON DELETE CASCADE should delete all child rows"
14803            );
14804        });
14805    }
14806
14807    #[test]
14808    fn fk_delete_set_null() {
14809        asupersync::test_utils::run_test(|| async {
14810            let conn = Connection::open(":memory:").await.unwrap();
14811            conn.execute("PRAGMA foreign_keys = ON").await.unwrap();
14812            conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY, name TEXT)")
14813                .await
14814                .unwrap();
14815            conn.execute(
14816            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id) ON DELETE SET NULL)",
14817        )
14818        .await
14819        .unwrap();
14820            conn.execute("INSERT INTO parent VALUES (1, 'Alice')")
14821                .await
14822                .unwrap();
14823            conn.execute("INSERT INTO child VALUES (1, 1)")
14824                .await
14825                .unwrap();
14826            // SET NULL should null out the FK column in children.
14827            conn.execute("DELETE FROM parent WHERE id = 1")
14828                .await
14829                .unwrap();
14830            let rows = conn.query("SELECT parent_id FROM child").await.unwrap();
14831            assert_eq!(rows.len(), 1);
14832            assert_eq!(
14833                row_values(&rows[0])[0],
14834                SqliteValue::Null,
14835                "ON DELETE SET NULL should set FK column to NULL"
14836            );
14837        });
14838    }
14839}