Skip to content

Conversation

kyounghunJang
Copy link
Contributor

@kyounghunJang kyounghunJang commented May 21, 2025

This PR introduces the following changes:

  • SQLite support has been added to the SQL output feature, enabling users to output data directly to SQLite databases.
  • Refactored the SQL output YAML configuration to be more intuitive and user-friendly. The new format makes it easier to understand and configure SQL output settings.
output:
  type: "sql"
  database_type: "mysql"
  uri: "mysql://root:1234@localhost:3306/arkflow"
  table_name: "arkflow_test"

@chenquan Please take a look when you get a chance — your feedback would be appreciated!

#274

Summary by CodeRabbit

  • New Features

    • Added support for SQLite database connections and insert operations in the SQL output plugin.
  • Improvements

    • Simplified and clarified configuration by renaming and flattening the database type specification in the output settings.
  • Compatibility

    • Expanded supported SQL database backends to include SQLite, in addition to MySQL and PostgreSQL.

Copy link
Contributor

coderabbitai bot commented May 21, 2025

Walkthrough

The changes introduce SQLite support to the SQL output plugin by updating dependency features, extending enums and connection logic, and adjusting insert execution to handle SQLite. Configuration fields are renamed and restructured for clarity, and the example YAML is updated to match the new configuration format.

Changes

File(s) Change Summary
Cargo.toml Added "sqlite" to sqlx crate features, enabling SQLite backend support.
crates/arkflow-plugin/src/output/sql.rs Added SQLite support: extended enums, connection logic, and insert execution; renamed config fields.
examples/sql_output_example.yaml Updated configuration structure: renamed and flattened database type and connection parameters.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SqlOutput
    participant DatabaseConnection
    participant SqliteConnection

    User->>SqlOutput: init_connect()
    SqlOutput->>SqlOutput: determine database_type
    alt database_type is Sqlite
        SqlOutput->>SqliteConnection: generate_sqlite_conn()
        SqliteConnection-->>SqlOutput: SqliteConnection instance
        SqlOutput-->>User: DatabaseConnection::Sqlite
    else database_type is MySQL/Postgres
        SqlOutput->>DatabaseConnection: generate respective connection
        DatabaseConnection-->>SqlOutput: Connection instance
        SqlOutput-->>User: DatabaseConnection variant
    end

    User->>SqlOutput: execute_insert(config, columns, rows)
    SqlOutput->>DatabaseConnection: execute_insert()
    alt DatabaseConnection is Sqlite
        DatabaseConnection->>SqliteConnection: build and execute insert query
    else DatabaseConnection is MySQL/Postgres
        DatabaseConnection->>respective connection: build and execute insert query
    end
    DatabaseConnection-->>SqlOutput: Result
    SqlOutput-->>User: Result
Loading

Possibly related PRs

Poem

In the warren of code, a new path appears,
SQLite joins friends, allaying old fears.
Queries now hop to a new database den,
With config made simple, for rabbits and men.
🐇 Hooray for more choices, let data flow free—
As we burrow through SQL, with glee!

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 0

🧹 Nitpick comments (4)
crates/arkflow-plugin/src/output/sql.rs (4)

424-429: Consider adding SQLite connection validation.

While the implementation works, consider adding validation for the SQLite URI format to prevent runtime errors with invalid connection strings.

 async fn generate_sqlite_conn(&self) -> Result<DatabaseConnection, Error> {
+    // Validate SQLite URI format
+    if !self.sql_config.uri.starts_with("sqlite:") {
+        return Err(Error::Config(format!(
+            "Invalid SQLite URI format: {}. URI should start with 'sqlite:'",
+            self.sql_config.uri
+        )));
+    }
+
     let sqlite_conn = SqliteConnection::connect(&self.sql_config.uri)
         .await
         .map_err(|e| Error::Config(format!("Failed to connect to SQLite: {}", e)))?;
     Ok(DatabaseConnection::Sqlite(sqlite_conn))
 }

60-158: Consider reducing code duplication in execute_insert.

There's significant code duplication between the Postgres and SQLite implementations in execute_insert. Consider refactoring to reduce duplication.

You could extract common logic to a helper function:

 pub async fn execute_insert(
     &mut self,
     output_config: &SqlOutputConfig,
     columns: Vec<String>,
     rows: Vec<Vec<SqlValue>>,
 ) -> Result<(), Error> {
     match self {
         DatabaseConnection::Mysql(conn) => {
             let mut query_builder = QueryBuilder::<sqlx::MySql>::new(format!(
                 "INSERT INTO {} ({})",
                 output_config.table_name,
                 columns
                     .iter()
                     .map(|c| format!("`{}`", c))
                     .collect::<Vec<_>>()
                     .join(", "),
             ));
             // MySQL specific implementation...
         }
-        DatabaseConnection::Postgres(conn) => {
-            let mut query_builder = QueryBuilder::<sqlx::Postgres>::new(format!(
-                "INSERT INTO {} ({})",
-                output_config.table_name,
-                columns
-                    .iter()
-                    .map(|c| format!("\"{}\"", c))
-                    .collect::<Vec<_>>()
-                    .join(", "),
-            ));
-            query_builder.push_values(rows, |mut b, row| {
-                for value in row {
-                    match value {
-                        SqlValue::String(s) => b.push_bind(s),
-                        SqlValue::Int64(i) => b.push_bind(i),
-                        SqlValue::UInt64(u) => b.push_bind(u as i64),
-                        SqlValue::Float64(f) => b.push_bind(f),
-                        SqlValue::Boolean(bool) => b.push_bind(bool),
-                        SqlValue::Null => b.push_bind(None::<String>),
-                    };
-                }
-            });
-
-            let query = query_builder.build();
-            query.execute(conn).await.map_err(|e| {
-                Error::Process(format!("Failed to execute PostgresSQL query: {}", e))
-            })?;
-
-            Ok(())
-        }
-        DatabaseConnection::Sqlite(conn) => {
-            let mut query_builder = QueryBuilder::<sqlx::Sqlite>::new(format!(
-                "INSERT INTO {} ({})",
-                output_config.table_name,
-                columns
-                    .iter()
-                    .map(|c| format!("\"{}\"", c))
-                    .collect::<Vec<_>>()
-                    .join(", "),
-            ));
-            query_builder.push_values(rows, |mut b, row| {
-                for value in row {
-                    match value {
-                        SqlValue::String(s) => b.push_bind(s),
-                        SqlValue::Int64(i) => b.push_bind(i),
-                        SqlValue::UInt64(u) => b.push_bind(u as i64),
-                        SqlValue::Float64(f) => b.push_bind(f),
-                        SqlValue::Boolean(bool) => b.push_bind(bool),
-                        SqlValue::Null => b.push_bind(None::<String>),
-                    };
-                }
-            });
-
-            let query = query_builder.build();
-            query.execute(conn).await.map_err(|e| {
-                Error::Process(format!("Failed to execute SQLite query: {}", e))
-            })?;
-
-            Ok(())
-        }
+        DatabaseConnection::Postgres(conn) => {
+            self.execute_quoted_insert::<sqlx::Postgres>(conn, output_config, columns, rows, "PostgresSQL").await
+        }
+        DatabaseConnection::Sqlite(conn) => {
+            self.execute_quoted_insert::<sqlx::Sqlite>(conn, output_config, columns, rows, "SQLite").await
+        }
     }
 }
+
+async fn execute_quoted_insert<'a, DB>(
+    &self,
+    conn: &mut DB::Connection, 
+    output_config: &SqlOutputConfig,
+    columns: Vec<String>,
+    rows: Vec<Vec<SqlValue>>,
+    db_name: &str
+) -> Result<(), Error>
+where
+    DB: sqlx::Database,
+    DB::Connection: sqlx::Executor<'a>,
+    for<'b> <DB as sqlx::database::HasArguments<'b>>::Arguments: sqlx::IntoArguments<'b, DB>,
+{
+    let mut query_builder = QueryBuilder::<DB>::new(format!(
+        "INSERT INTO {} ({})",
+        output_config.table_name,
+        columns
+            .iter()
+            .map(|c| format!("\"{}\"", c))
+            .collect::<Vec<_>>()
+            .join(", "),
+    ));
+    query_builder.push_values(rows, |mut b, row| {
+        for value in row {
+            match value {
+                SqlValue::String(s) => b.push_bind(s),
+                SqlValue::Int64(i) => b.push_bind(i),
+                SqlValue::UInt64(u) => b.push_bind(u as i64),
+                SqlValue::Float64(f) => b.push_bind(f),
+                SqlValue::Boolean(bool) => b.push_bind(bool),
+                SqlValue::Null => b.push_bind(None::<String>),
+            };
+        }
+    });
+
+    let query = query_builder.build();
+    query.execute(conn).await.map_err(|e| {
+        Error::Process(format!("Failed to execute {} query: {}", db_name, e))
+    })?;
+
+    Ok(())
+}

Note: The above refactoring might require additional type constraints or adjustments based on the specific requirements of the sqlx library.


300-307: Consider adding documentation for the SQLite option.

The init_connect method should have its documentation updated to reflect the addition of SQLite support.

     /// Initialize a new DB connection.  
     /// If `ssl` is configured, apply root certificates to the SSL options.
+    /// Supports MySQL, PostgreSQL, and SQLite connection types.
     async fn init_connect(&self) -> Result<DatabaseConnection, Error> {
         let conn = match self.sql_config.database_type {
             DatabaseType::Mysql => self.generate_mysql_conn().await?,
             DatabaseType::Postgres => self.generate_postgres_conn().await?,
             DatabaseType::Sqlite => self.generate_sqlite_conn().await?,
         };
         Ok(conn)
     }

424-429: Consider documenting absence of SSL support for SQLite.

Unlike MySQL and PostgreSQL, your SQLite implementation doesn't include SSL configuration options. Consider adding a documented reason for this difference.

+    /// Generates a SQLite connection
+    /// Note: SSL configuration is not applicable for SQLite as it's a file-based database
     async fn generate_sqlite_conn(&self) -> Result<DatabaseConnection, Error> {
+        // SQLite uses file-based connections, so SSL is not applicable
         let sqlite_conn = SqliteConnection::connect(&self.sql_config.uri)
             .await
             .map_err(|e| Error::Config(format!("Failed to connect to SQLite: {}", e)))?;
         Ok(DatabaseConnection::Sqlite(sqlite_conn))
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c271fd8 and d9fe510.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml (1 hunks)
  • crates/arkflow-plugin/src/output/sql.rs (7 hunks)
  • examples/sql_output_example.yaml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (8)
Cargo.toml (1)

47-47: Good addition of SQLite feature to sqlx dependency.

Adding the "sqlite" feature to sqlx dependency enables the SQLite database backend integration in the SQL output plugin, which aligns perfectly with the PR objective of adding SQLite support.

examples/sql_output_example.yaml (1)

19-21: Configuration structure improvement looks good.

The configuration has been simplified by replacing the nested output_type with a flattened structure using database_type, making it more intuitive and user-friendly.

crates/arkflow-plugin/src/output/sql.rs (6)

32-32: Correctly updated import for SqliteConnection.

The import statement has been appropriately updated to include SqliteConnection, which is necessary for the SQLite implementation.


45-50: Good use of snake_case serialization and addition of SQLite variant.

Adding the Sqlite variant to the DatabaseType enum and ensuring consistent snake_case serialization enhances the API's usability.


54-54: Correctly extended DatabaseConnection enum.

The DatabaseConnection enum has been properly extended to include the new SQLite connection type.


129-157: SQLite insert implementation follows good patterns.

The SQLite implementation mirrors the PostgreSQL approach, which maintains consistency in the codebase. The code correctly handles:

  • Proper column name quoting
  • Type conversions (especially UInt64 to i64)
  • Error handling with descriptive messages

168-168: Improved field naming from output_type to database_type.

Renaming from output_type to database_type more accurately reflects the field's purpose, as it specifies the database type rather than the output type.


304-305: Good integration of SQLite in the connection initialization flow.

The SQLite connection handling has been seamlessly integrated into the existing connection initialization flow.

@chenquan chenquan self-assigned this May 22, 2025
@chenquan chenquan added the enhancement New feature or request label May 25, 2025
@chenquan chenquan marked this pull request as draft May 28, 2025 04:27
@chenquan chenquan marked this pull request as ready for review May 28, 2025 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants