Configure connection pragmas. * in_memory — :memory: DB (synchronous OFF, no journal file). * read_only — query-only connection opened SQLITE_OPEN_READONLY. Runs * ONLY non-writing pragmas (foreign_keys, temp_store, * busy_timeout, mmap_size) and SKIPS journal_mode=WAL, * wal_checkpoint and synchronous — those WRITE to the DB/WAL *
| 418 | * fail on a read-only DB file / filesystem. |
| 419 | * in_memory and read_only are mutually exclusive. */ |
| 420 | static int configure_pragmas(cbm_store_t *s, bool in_memory, bool read_only) { |
| 421 | int rc; |
| 422 | rc = exec_sql(s, "PRAGMA foreign_keys = ON;"); |
| 423 | if (rc != CBM_STORE_OK) { |
| 424 | return rc; |
| 425 | } |
| 426 | rc = exec_sql(s, "PRAGMA temp_store = MEMORY;"); |
| 427 | if (rc != CBM_STORE_OK) { |
| 428 | return rc; |
| 429 | } |
| 430 | |
| 431 | if (in_memory) { |
| 432 | rc = exec_sql(s, "PRAGMA synchronous = OFF;"); |
| 433 | } else if (read_only) { |
| 434 | /* Non-writing pragmas only — see the function comment. */ |
| 435 | rc = exec_sql(s, "PRAGMA busy_timeout = 10000;"); |
| 436 | if (rc != CBM_STORE_OK) { |
| 437 | return rc; |
| 438 | } |
| 439 | char mmap_sql[ST_BUF_64]; |
| 440 | snprintf(mmap_sql, sizeof(mmap_sql), "PRAGMA mmap_size = %lld;", |
| 441 | (long long)cbm_store_resolve_mmap_size()); |
| 442 | rc = exec_sql(s, mmap_sql); |
| 443 | } else { |
| 444 | rc = exec_sql(s, "PRAGMA busy_timeout = 10000;"); |
| 445 | if (rc != CBM_STORE_OK) { |
| 446 | return rc; |
| 447 | } |
| 448 | rc = exec_sql(s, "PRAGMA journal_mode = WAL;"); |
| 449 | if (rc != CBM_STORE_OK) { |
| 450 | return rc; |
| 451 | } |
| 452 | /* Recover stale WAL from previous crash (best-effort). |
| 453 | * PASSIVE never blocks readers and never ftruncates. |
| 454 | * May fail with SQLITE_BUSY if another process holds a lock. */ |
| 455 | (void)sqlite3_exec(s->db, "PRAGMA wal_checkpoint(PASSIVE)", NULL, NULL, NULL); |
| 456 | rc = exec_sql(s, "PRAGMA synchronous = NORMAL;"); |
| 457 | if (rc != CBM_STORE_OK) { |
| 458 | return rc; |
| 459 | } |
| 460 | /* #1083: bound the WAL file so a checkpoint-starved log is physically |
| 461 | * reclaimed the next time a checkpoint can reset it. Shared/live |
| 462 | * checkpoints are PASSIVE (they never ftruncate — see |
| 463 | * cbm_store_checkpoint's SIGBUS note), so without a size limit the -wal |
| 464 | * file only ever grows; journal_size_limit truncates it back to N bytes |
| 465 | * on the next successful |
| 466 | * reset. N is far above the healthy WAL (~4 MiB under the default |
| 467 | * 1000-page autocheckpoint), so normal indexing never triggers |
| 468 | * truncate/regrow churn — it only fires after abnormal growth. |
| 469 | * Shared/live paths do NOT use a TRUNCATE checkpoint: truncating the WAL |
| 470 | * to zero can raise SIGBUS in a sibling process that has the DB mmap'd |
| 471 | * on macOS. Exclusive staging publication seals separately below. */ |
| 472 | rc = exec_sql(s, "PRAGMA journal_size_limit = 268435456;"); /* 256 MiB */ |
| 473 | if (rc != CBM_STORE_OK) { |
| 474 | return rc; |
| 475 | } |
| 476 | char mmap_sql[ST_BUF_64]; |
| 477 | snprintf(mmap_sql, sizeof(mmap_sql), "PRAGMA mmap_size = %lld;", |
no test coverage detected