Skip to content

Add SQL commands for local database operations#2108

Merged
findolor merged 10 commits intomainfrom
cli-local-db-sql-commands
Oct 6, 2025
Merged

Add SQL commands for local database operations#2108
findolor merged 10 commits intomainfrom
cli-local-db-sql-commands

Conversation

@findolor
Copy link
Copy Markdown
Collaborator

@findolor findolor commented Aug 29, 2025

Caution

Chained PR
Do not merge before #2104

Note

This PR rainlanguage/rainix#88 also needs to be merged to enable sqlite3 command in nix shell

Motivation

Previously, we could fetch and decode events but lacked the ability to convert decoded events to SQL
format and create complete database dumps.

Solution

This PR adds two new CLI commands for local database SQL operations:

  1. decoded-events-to-sql - Converts decoded JSON events to SQL INSERT statements with configurable output file
    and end block parameters
  2. dump - Creates complete database dumps by setting up schema, importing data, and generating compressed SQL
    dumps

Key changes include:

  • Added DecodedEventsToSql command with comprehensive test coverage for JSON parsing, file I/O, and error
    handling
  • Added DbDump command that orchestrates schema creation, data import, and database dumping with gzip compression
  • Enhanced .gitignore to exclude generated SQL files and database artifacts
  • Added sync status table to the database schema for tracking synchronization state
  • Both commands include extensive unit tests covering success scenarios, error conditions, and edge cases

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • New Features

    • Added CLI commands to convert decoded event JSON into SQL and to export the local DB as a gzipped SQL dump.
    • Added a sync_status table to track last synced block and timestamp.
    • Added deserialization support for decoded event types to enable round-trip JSON handling.
  • Improvements

    • Improved default output path resolution and status messages for local DB/event commands.
  • Chores

    • Expanded .gitignore to exclude generated local DB artifacts and SQL event outputs.

@findolor findolor self-assigned this Aug 29, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 29, 2025

Walkthrough

Adds two new local-db CLI commands (DecodedEventsToSql and Dump), updates CLI wiring and exports, extends SQLite schema with a sync_status table, and expands .gitignore to omit generated local_db event SQL and related artifacts. Includes tests for the new CLI behaviors.

Changes

Cohort / File(s) Change Summary
CLI: New commands
crates/cli/src/commands/local_db/decoded_events_to_sql.rs, crates/cli/src/commands/local_db/dump.rs
Add DecodedEventsToSql and DbDump structs with async fn execute(self) -> Result<()> implementations: JSON→SQL conversion and file output; SQLite reconstruction, .dump generation, and gzip packaging. Includes tests covering paths, defaults, error cases, and round-trip validation.
CLI: Local DB module & wiring
crates/cli/src/commands/local_db/mod.rs, crates/cli/src/lib.rs
Register and re-export new modules; extend LocalDb enum with DecodedEventsToSql and Dump variants and dispatch to their execute methods.
CLI: Ignore patterns
crates/cli/.gitignore
Expand ignore rules to include src/commands/local_db/events_*.sql, src/commands/local_db/local_db_*, alongside existing JSON ignores.
Common: Decoding types
crates/common/src/raindex_client/sqlite_web/decode.rs
Derive Deserialize on EventType, DecodedEventData<T>, DecodedEvent, and UnknownEventDecoded; add serde deserialize bound for DecodedEventData<T>.
Common: SQLite schema
crates/common/src/raindex_client/sqlite_web/sql/tables.sql
Add sync_status table (singleton row tracking last_synced_block and updated_at) and initialization insert.
CLI: decode_events adjustments
crates/cli/src/commands/local_db/decode_events.rs
Adjust output filename resolution to derive default path from input parent, use PathBuf displays in messages, and simplify test path handling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CLI as CLI (LocalDb)
  participant CMD as DecodedEventsToSql
  participant FS as File System
  participant Conv as Converter

  U->>CLI: local-db decoded-events-to-sql --input <json> [--output <sql>]
  CLI->>CMD: execute()
  CMD->>FS: read <json>
  CMD->>Conv: parse JSON → generate SQL
  Conv-->>CMD: SQL text
  CMD->>FS: write SQL to output (provided or default)
  CMD-->>CLI: Result / message
  CLI-->>U: exit status
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant CLI as CLI (LocalDb)
  participant D as DbDump
  participant Shell as sqlite3
  participant FS as File System
  participant GZ as gzip

  U->>CLI: local-db dump --input <data.sql> --table-schema <schema.sql> [--db <path>] [--dump <path>]
  CLI->>D: execute()
  D->>FS: resolve paths, remove existing DB if present
  D->>Shell: create DB, apply schema.sql
  D->>Shell: apply data.sql
  D->>Shell: .dump -> dump.sql
  D->>GZ: gzip dump.sql -> dump.sql.gz
  D-->>CLI: Result / message
  CLI-->>U: exit status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

rust

Suggested reviewers

  • hardyjosh
  • 0xgleb

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the primary change—adding SQL-focused CLI commands for local database workflows—which aligns with the introduction of decoded-events-to-sql and dump commands and related schema updates. It is clear, specific, and reflects the main purpose of the pull request without unnecessary detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cli-local-db-sql-commands

Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor changed the base branch from local-db-cli-decode-events to cli-local-db-decode September 2, 2025 15:09
@findolor findolor requested review from 0xgleb and hardyjosh October 2, 2025 10:54
@findolor
Copy link
Copy Markdown
Collaborator Author

findolor commented Oct 2, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 2, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e323fbb and f750227.

📒 Files selected for processing (6)
  • crates/cli/.gitignore (1 hunks)
  • crates/cli/src/commands/local_db/decoded_events_to_sql.rs (1 hunks)
  • crates/cli/src/commands/local_db/dump.rs (1 hunks)
  • crates/cli/src/commands/local_db/mod.rs (1 hunks)
  • crates/cli/src/lib.rs (2 hunks)
  • crates/common/src/raindex_client/sqlite_web/sql/tables.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: git-clean
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: copilot-setup-steps

Comment on lines +34 to +55
let _ = Command::new("sqlite3")
.arg(&db_path)
.arg(format!(".read {}", tables_sql_path))
.status()?;

let _ = Command::new("sqlite3")
.arg(&db_path)
.arg(format!(".read {}", sql_file_path))
.status()?;

let output = Command::new("sqlite3")
.arg(&db_path)
.arg(".dump")
.output()?;

fs::write(&dump_file_path, output.stdout)?;

Command::new("gzip")
.arg("-f")
.arg(&dump_file_path)
.status()?;

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Propagate sqlite3/gzip failures

We’re ignoring every external command’s exit status. If sqlite3 or gzip exits non‑zero (bad schema/data, disk full, etc.), the CLI happily proceeds and produces a corrupted/empty dump without signalling failure. Please capture the status, ensure it succeeded, and surface stderr for debugging.

-use anyhow::Result;
+use anyhow::{ensure, Context, Result};
@@
-        let _ = Command::new("sqlite3")
-            .arg(&db_path)
-            .arg(format!(".read {}", tables_sql_path))
-            .status()?;
-
-        let _ = Command::new("sqlite3")
-            .arg(&db_path)
-            .arg(format!(".read {}", sql_file_path))
-            .status()?;
-
-        let output = Command::new("sqlite3")
-            .arg(&db_path)
-            .arg(".dump")
-            .output()?;
-
-        fs::write(&dump_file_path, output.stdout)?;
-
-        Command::new("gzip")
-            .arg("-f")
-            .arg(&dump_file_path)
-            .status()?;
+        let schema_status = Command::new("sqlite3")
+            .arg(&db_path)
+            .arg(format!(".read {}", tables_sql_path))
+            .status()
+            .context("sqlite3 failed while applying table schema")?;
+        ensure!(schema_status.success(), "sqlite3 returned {}", schema_status);
+
+        let data_status = Command::new("sqlite3")
+            .arg(&db_path)
+            .arg(format!(".read {}", sql_file_path))
+            .status()
+            .context("sqlite3 failed while applying data dump")?;
+        ensure!(data_status.success(), "sqlite3 returned {}", data_status);
+
+        let output = Command::new("sqlite3")
+            .arg(&db_path)
+            .arg(".dump")
+            .output()
+            .context("sqlite3 failed while dumping database")?;
+        ensure!(
+            output.status.success(),
+            "sqlite3 dump failed: {}",
+            String::from_utf8_lossy(&output.stderr)
+        );
+
+        fs::write(&dump_file_path, output.stdout)?;
+
+        let gzip_status = Command::new("gzip")
+            .arg("-f")
+            .arg(&dump_file_path)
+            .status()
+            .context("gzip failed while compressing dump")?;
+        ensure!(gzip_status.success(), "gzip returned {}", gzip_status);
🤖 Prompt for AI Agents
In crates/cli/src/commands/local_db/dump.rs around lines 34 to 55, the code
currently ignores exit statuses of sqlite3/gzip commands which can produce
corrupted or empty dumps; change each Command::new(...).status()? call to run
the command with .output()? so you can check output.status.success() and, on
failure, return an Err (or bail!) that includes the command, its args, exit code
and stderr (converted to string) for debugging; specifically for the two ".read"
invocations and the final "gzip" invocation capture output, assert success, and
for the dump step check output.status.success() before writing dump_file_path
and include stdout/stderr in any error returned.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fine we are going to get these errors any way

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, understood. I'll leave the error handling as-is. 👍

@findolor findolor changed the base branch from cli-local-db-decode to local-db-cli October 3, 2025 16:32
Base automatically changed from local-db-cli to main October 4, 2025 07:49
@findolor findolor enabled auto-merge October 6, 2025 05:17
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f750227 and c3e33af.

📒 Files selected for processing (3)
  • crates/cli/src/commands/local_db/decode_events.rs (3 hunks)
  • crates/cli/src/commands/local_db/decoded_events_to_sql.rs (1 hunks)
  • crates/common/src/raindex_client/sqlite_web/decode.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/{src,tests,benches,examples}/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/{src,tests,benches,examples}/**/*.rs: Rust crates: lint with cargo clippy --workspace --all-targets --all-features -D warnings
Rust crates: ensure cargo test --workspace passes
Rust crates: build with cargo build during development/readiness checks

Files:

  • crates/cli/src/commands/local_db/decoded_events_to_sql.rs
  • crates/common/src/raindex_client/sqlite_web/decode.rs
  • crates/cli/src/commands/local_db/decode_events.rs
🧬 Code graph analysis (1)
crates/cli/src/commands/local_db/decoded_events_to_sql.rs (3)
crates/common/src/raindex_client/sqlite_web/insert.rs (1)
  • decoded_events_to_sql (84-142)
crates/cli/src/commands/local_db/decode_events.rs (1)
  • execute (20-54)
crates/cli/src/lib.rs (2)
  • execute (22-31)
  • execute (66-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, test-js-bindings)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-test)
  • GitHub Check: standard-tests (ubuntu-latest, ob-rs-test, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-artifacts, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
  • GitHub Check: test
  • GitHub Check: test
  • GitHub Check: test
  • GitHub Check: Deploy-Docs-Preview
  • GitHub Check: Deploy-Preview-Push
🔇 Additional comments (7)
crates/cli/src/commands/local_db/decode_events.rs (3)

8-8: LGTM!

The Path and PathBuf imports are necessary for the enhanced output path derivation logic introduced in this PR.


34-40: LGTM!

The output path derivation logic is well-designed, properly handling the edge case where the input file has no parent directory. This approach is consistent with the new DecodedEventsToSql command introduced in this PR.


43-52: LGTM!

The test simplification removes unnecessary complexity while maintaining correctness. The use of .display() for PathBuf formatting in error messages and logs is idiomatic Rust.

Also applies to: 178-178

crates/cli/src/commands/local_db/decoded_events_to_sql.rs (3)

11-21: LGTM!

The struct definition follows clap conventions correctly. The end_block parameter is appropriately included to support the sync_status table update in the generated SQL.


37-43: LGTM!

The output path derivation logic is consistent with the pattern used in decode_events.rs, with an appropriate default filename for SQL output.


53-154: LGTM!

The test suite provides comprehensive coverage of success scenarios, error conditions, and edge cases. The tests follow proper tokio async testing conventions.

crates/common/src/raindex_client/sqlite_web/decode.rs (1)

14-14: LGTM!

The addition of Deserialize derives to these types enables the new CLI commands to read decoded events from JSON files. The serde bounds on DecodedEventData are correctly specified to require T: Deserialize<'de> for deserialization, maintaining proper generic constraints.

Also applies to: 24-24, 70-71, 81-81, 162-162

Comment on lines +27 to +28
let file = File::open(&self.input_file)
.with_context(|| format!("Failed to open input file: {:?}", self.input_file))?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use {} instead of {:?} for cleaner error messages.

The {:?} format specifier will display the string with surrounding quotes. For consistency with decode_events.rs (line 24) and cleaner error messages, use {} instead.

Apply this diff:

-        let file = File::open(&self.input_file)
-            .with_context(|| format!("Failed to open input file: {:?}", self.input_file))?;
+        let file = File::open(&self.input_file)
+            .with_context(|| format!("Failed to open input file: {}", self.input_file))?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let file = File::open(&self.input_file)
.with_context(|| format!("Failed to open input file: {:?}", self.input_file))?;
let file = File::open(&self.input_file)
.with_context(|| format!("Failed to open input file: {}", self.input_file))?;
🤖 Prompt for AI Agents
In crates/cli/src/commands/local_db/decoded_events_to_sql.rs around lines 27-28,
replace the debug-style formatting that uses "{:?}" in the with_context format
string with a display-style formatting; change the call to format!("Failed to
open input file: {:?}", self.input_file) to use "{}" and call
self.input_file.display() (e.g. format!("Failed to open input file: {}",
self.input_file.display())) so the error message is clean and consistent with
decode_events.rs.

@findolor findolor merged commit 19cc37b into main Oct 6, 2025
19 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Oct 6, 2025

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 6, 2025

Tip

For best results, initiate chat on the files or code changes.

SIZE=M


🧠 Learnings used
Learnt from: rouzwelt
PR: rainlanguage/rain.orderbook#0
File: :0-0
Timestamp: 2025-05-21T21:24:42.275Z
Learning: For PR #1884, after each commit review, I should provide a new comment with a PR size report in the format: `TOTAL=number ADDITIONS=number DELETIONS=number`, excluding irrelevant files such as lock files (package-lock.json, cargo.lock, etc.).

@findolor findolor deleted the cli-local-db-sql-commands branch October 6, 2025 09:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants