Skip to content

fix: address CodeRabbit v3 — allow UNION queries, document --api-key#2552

Open
tusharsharma20021114-rgb wants to merge 4 commits into
Canner:mainfrom
tusharsharma20021114-rgb:codereview-fixes-v3
Open

fix: address CodeRabbit v3 — allow UNION queries, document --api-key#2552
tusharsharma20021114-rgb wants to merge 4 commits into
Canner:mainfrom
tusharsharma20021114-rgb:codereview-fixes-v3

Conversation

@tusharsharma20021114-rgb

@tusharsharma20021114-rgb tusharsharma20021114-rgb commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Fixes the 2 new CodeRabbit comments from the third review on PR #2551:

  1. policy.py — Change whitelist from (exp.Select, exp.Explain) to (exp.Query, exp.Explain, exp.Subquery) to allow UNION, INTERSECT, EXCEPT, and parenthesized SELECT queries (BI tools compatibility).

  2. cli.md — Add --api-key row to the wren serve mcp options table.

Verification

  • pytest: 987 passed, 65 failed (pre-existing), 59 skipped
  • cargo check --all-targets --all-features: clean

Summary by CodeRabbit

  • New Features
    • Added optional bearer-token authentication for MCP over HTTP.
    • Added safer, parameterized row-limit handling across database connectors.
    • Added SQL safety checks to reject empty, multi-statement, or non-read-only queries.
  • Bug Fixes
    • Queries now clearly require an MDL to be loaded before execution.
    • Improved handling of invalid limits and planning errors without unexpected crashes.
  • Changes
    • Strict mode is now enabled by default, with function names normalized consistently.
  • Documentation
    • Updated MCP and CLI guidance for HTTP authentication.

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file python Pull requests that update Python code rust Pull requests that update rust code core ci wasm labels Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

Rust runtime hardening

Layer / File(s) Summary
Workspace lint enforcement
core/wren-core*/Cargo.toml
Workspace Clippy settings deny unwrap and expect, with package manifests inheriting the configuration.
Planning error propagation
core/wren-core/core/src/logical_plan/..., core/wren-core/core/src/mdl/...
Panic-based assumptions are replaced with DataFusion errors, safe fallbacks, and checked lookups.
MDL context and runtime isolation
core/wren-core/core/src/mdl/context.rs, core/wren-core/core/src/mdl/mod.rs, core/wren-core-wasm/src/lib.rs
Local runtime catalogs are filtered, SQL transformation runtimes are cached, and WASM queries require a loaded MDL.

Parameterized connector execution

Layer / File(s) Summary
Shared limit helpers
core/wren/src/wren/connector/base.py
Shared limit normalization and parameterized SQL wrapping are added.
Connector query integration
core/wren/src/wren/connector/*.py
Connectors use normalized limits or driver-specific placeholders for query and dry-run execution.
Connector behavior tests
core/wren/tests/unit/test_*connector*.py, core/wren/tests/unit/test_*semicolon*.py
Tests cover parameter passing, semicolon handling, dry-run limits, and limit normalization.

Python policy and service controls

Layer / File(s) Summary
Configuration and SQL safety
core/wren/src/wren/config.py, core/wren/src/wren/policy.py, core/wren/src/wren/engine.py
Strict mode defaults to enabled, configured function names are normalized, and planning rejects unsafe SQL shapes.
MCP bearer authentication
core/wren/src/wren/mcp_server.py, core/wren/src/wren/serve_cli.py, docs/core/...
HTTP MCP serving supports optional API-key authentication, CLI validation, token verification, and updated documentation.

Workflow safety documentation

Layer / File(s) Summary
Labeler workflow trigger
.github/workflows/labeler.yaml
The labeler trigger and permissions behavior are documented for pull request target execution.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

  • Canner/WrenAI#2438 — Extends the MCP serving implementation that this change adds bearer-token authentication to.
  • Canner/WrenAI#2313 — Introduced the connector refactor used by the new shared parameterized limit handling.
  • Canner/WrenAI#2495 — Related to catalog-list isolation in apply_wren_on_ctx.

Suggested reviewers: bartok9

Poem

A rabbit bounds through queries bright,
With safe-bound limits tucked just right.
Rust errors bloom where panics stood,
Bearer tokens guard the wood.
MDLs load before we hop—
Clean, careful code from root to top!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: broader read-only SQL support and documenting the MCP API key option.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
.github/workflows/labeler.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


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

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

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
core/wren-core/core/src/mdl/utils.rs (1)

182-186: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Eliminate unnecessary Vec allocation.

Since models is immediately consumed via .into_iter() just below, you can leave it as a BTreeSet and skip the final .collect::<Vec<String>>().

♻️ Proposed refactor
     let models = required_fields
         .iter()
         .filter_map(|c| c.relation.as_ref().map(|r| r.table().to_string()))
-        .collect::<BTreeSet<_>>() // Collect into a BTreeSet to remove duplicates
-        .into_iter() // Convert BTreeSet back into an iterator
-        .collect::<Vec<String>>();
+        .collect::<BTreeSet<_>>(); // Collect into a BTreeSet to remove duplicates
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/wren-core/core/src/mdl/utils.rs` around lines 182 - 186, Update the
models collection in the surrounding relationship-processing function to retain
the BTreeSet produced by the existing collect, removing the immediate into_iter
and Vec<String> collection. Ensure subsequent consumers use the BTreeSet
directly while preserving duplicate removal and iteration behavior.
core/wren/src/wren/connector/postgres.py (1)

254-269: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Preserve the original SQL query in error metadata.

When a query fails during execution, logging the original user-submitted query (rather than the internal LIMIT-wrapped subquery) provides better visibility for debugging and matches the dry_run behavior. The refactoring either overwrites the original SQL string or explicitly passes the wrapped string into the error metadata.

  • core/wren/src/wren/connector/postgres.py#L254-L269: Use a different variable name (executed, params = ...) so the sql variable retains the original query when passed to metadata={DIALECT_SQL: sql} in the except block.
  • core/wren/src/wren/connector/canner.py#L262-L262: Change metadata={DIALECT_SQL: executed} to metadata={DIALECT_SQL: sql} to retain the original query string.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/wren/src/wren/connector/postgres.py` around lines 254 - 269, Preserve
the original query in execution-error metadata: in
core/wren/src/wren/connector/postgres.py lines 254-269, update the
_apply_limit_param result variable to executed while keeping sql unchanged for
metadata={DIALECT_SQL: sql}; in core/wren/src/wren/connector/canner.py line 262,
change the metadata value from executed to sql.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/wren/src/wren/connector/duckdb.py`:
- Around line 17-47: Update _init_duckdb_s3, _init_duckdb_minio, and
_init_duckdb_gcs so DuckDB CREATE SECRET and SET statements use safely quoted
literal values rather than bound parameters. Preserve the existing credential,
region, endpoint, and SSL values while escaping or quoting them through a safe
helper before embedding them in each SQL statement.

---

Nitpick comments:
In `@core/wren-core/core/src/mdl/utils.rs`:
- Around line 182-186: Update the models collection in the surrounding
relationship-processing function to retain the BTreeSet produced by the existing
collect, removing the immediate into_iter and Vec<String> collection. Ensure
subsequent consumers use the BTreeSet directly while preserving duplicate
removal and iteration behavior.

In `@core/wren/src/wren/connector/postgres.py`:
- Around line 254-269: Preserve the original query in execution-error metadata:
in core/wren/src/wren/connector/postgres.py lines 254-269, update the
_apply_limit_param result variable to executed while keeping sql unchanged for
metadata={DIALECT_SQL: sql}; in core/wren/src/wren/connector/canner.py line 262,
change the metadata value from executed to sql.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4bf555da-da5f-4482-a526-5dad20d6fe0b

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0c4f3 and b6c97eb.

📒 Files selected for processing (52)
  • .github/workflows/labeler.yaml
  • core/wren-core-base/Cargo.toml
  • core/wren-core-wasm/src/lib.rs
  • core/wren-core/Cargo.toml
  • core/wren-core/benchmarks/Cargo.toml
  • core/wren-core/core/Cargo.toml
  • core/wren-core/core/src/logical_plan/analyze/model_generation.rs
  • core/wren-core/core/src/logical_plan/analyze/plan.rs
  • core/wren-core/core/src/logical_plan/analyze/relation_chain.rs
  • core/wren-core/core/src/logical_plan/unparser.rs
  • core/wren-core/core/src/mdl/context.rs
  • core/wren-core/core/src/mdl/dataset.rs
  • core/wren-core/core/src/mdl/dialect/inner_dialect.rs
  • core/wren-core/core/src/mdl/dialect/wren_dialect.rs
  • core/wren-core/core/src/mdl/function/remote_function.rs
  • core/wren-core/core/src/mdl/lineage.rs
  • core/wren-core/core/src/mdl/mod.rs
  • core/wren-core/core/src/mdl/type_planner.rs
  • core/wren-core/core/src/mdl/utils.rs
  • core/wren-core/sqllogictest/Cargo.toml
  • core/wren-core/wren-example/Cargo.toml
  • core/wren/src/wren/config.py
  • core/wren/src/wren/connector/athena.py
  • core/wren/src/wren/connector/base.py
  • core/wren/src/wren/connector/canner.py
  • core/wren/src/wren/connector/clickhouse.py
  • core/wren/src/wren/connector/databricks.py
  • core/wren/src/wren/connector/datafusion.py
  • core/wren/src/wren/connector/duckdb.py
  • core/wren/src/wren/connector/mssql.py
  • core/wren/src/wren/connector/mysql.py
  • core/wren/src/wren/connector/oracle.py
  • core/wren/src/wren/connector/postgres.py
  • core/wren/src/wren/connector/redshift.py
  • core/wren/src/wren/connector/snowflake.py
  • core/wren/src/wren/connector/trino.py
  • core/wren/src/wren/engine.py
  • core/wren/src/wren/mcp_server.py
  • core/wren/src/wren/policy.py
  • core/wren/src/wren/serve_cli.py
  • core/wren/tests/conftest.py
  • core/wren/tests/unit/test_athena_connector.py
  • core/wren/tests/unit/test_athena_limit_pushdown.py
  • core/wren/tests/unit/test_canner_semicolon.py
  • core/wren/tests/unit/test_config.py
  • core/wren/tests/unit/test_connector_base.py
  • core/wren/tests/unit/test_duckdb_file_listing.py
  • core/wren/tests/unit/test_mysql_helpers.py
  • core/wren/tests/unit/test_postgres_semicolon_unlimited.py
  • core/wren/tests/unit/test_redshift_semicolon.py
  • docs/core/guides/mcp.md
  • docs/core/reference/cli.md

Comment on lines +17 to +47
connection.execute(
"CREATE SECRET wren_s3 (TYPE S3, KEY_ID ?, SECRET ?, REGION ?)",
[
info.access_key.get_secret_value(),
info.secret_key.get_secret_value(),
info.region,
],
)


def _init_duckdb_minio(connection, info: MinioFileConnectionInfo):
connection.execute(f"""
CREATE SECRET wren_minio (
TYPE S3,
KEY_ID '{_escape_sql(info.access_key.get_secret_value())}',
SECRET '{_escape_sql(info.secret_key.get_secret_value())}',
REGION 'ap-northeast-1'
)""")
connection.execute(
"CREATE SECRET wren_minio (TYPE S3, KEY_ID ?, SECRET ?, REGION 'ap-northeast-1')",
[
info.access_key.get_secret_value(),
info.secret_key.get_secret_value(),
],
)
connection.execute("SET s3_endpoint=?", [info.endpoint])
connection.execute("SET s3_url_style='path'")
connection.execute("SET s3_use_ssl=?", [info.ssl_enabled])


def _init_duckdb_gcs(connection, info: GcsFileConnectionInfo):
connection.execute(f"""
CREATE SECRET wren_gcs (
TYPE GCS,
KEY_ID '{_escape_sql(info.key_id.get_secret_value())}',
SECRET '{_escape_sql(info.secret_key.get_secret_value())}'
)""")
connection.execute(
"CREATE SECRET wren_gcs (TYPE GCS, KEY_ID ?, SECRET ?)",
[
info.key_id.get_secret_value(),
info.secret_key.get_secret_value(),
],
)

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.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python -c "
import duckdb
conn = duckdb.connect()

try:
    conn.execute('CREATE SECRET my_sec (TYPE S3, KEY_ID ?, SECRET ?)', ['test', 'test'])
    print('CREATE SECRET parameterized: SUCCESS')
except Exception as e:
    print('CREATE SECRET parameterized: ERROR:', str(e))

try:
    conn.execute('SET s3_endpoint=?', ['test'])
    print('SET parameterized: SUCCESS')
except Exception as e:
    print('SET parameterized: ERROR:', str(e))
"

Repository: Canner/WrenAI

Length of output: 271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and nearby context.
sed -n '1,220p' core/wren/src/wren/connector/duckdb.py

# Find DuckDB dependency/version declarations in the repo.
rg -n "duckdb|DuckDB" -g 'pyproject.toml' -g 'requirements*.txt' -g 'poetry.lock' -g 'Pipfile*' -g 'uv.lock' -g 'package.json' -g 'package-lock.json' -g 'pnpm-lock.yaml' -g 'yarn.lock' .

# Search for other CREATE SECRET / SET usages that might indicate the intended pattern.
rg -n "CREATE SECRET|SET s3_|s3_endpoint|s3_url_style|s3_use_ssl" core/wren/src .

Repository: Canner/WrenAI

Length of output: 18956


🌐 Web query:

DuckDB CREATE SECRET parameter binding utility statements SET ? prepared parameters documentation

💡 Result:

In DuckDB, the CREATE SECRET statement does not support the use of prepared statement parameters (such as ?, $1, or named parameters) directly within the command [1]. Attempts to use this syntax will typically result in a parser error [1]. If you need to use dynamic values for secrets, the recommended approach is to use expressions instead of standard prepared parameters. Recent updates to DuckDB allow for the use of expressions within the CREATE SECRET statement [2][3]. For example, you can set a session variable and then use that variable within the secret definition [2][3]: SET VARIABLE my_secret_value = 'your_secret_here'; CREATE SECRET my_secret (TYPE s3, SECRET getvariable('my_secret_value')); Regarding the SET statement, DuckDB does support prepared parameters in combination with SET VARIABLE, allowing for dynamic assignment of session variables through prepared statements [4][5]. However, general configuration settings used with the SET command (e.g., SET memory_limit = '1GB') may behave differently depending on the specific implementation, and it is standard practice to use SET VARIABLE for dynamic data injection [4][5]. Top results: [1][4][5][2][3]

Citations:


🌐 Web query:

DuckDB SET statement prepared parameters s3_endpoint s3_use_ssl ? parameter binding documentation

💡 Result:

In DuckDB, the SET statement is used to modify configuration options [1][2]. While the s3_endpoint and s3_use_ssl settings were historically used to configure S3 access [3][4], they are now considered part of a legacy authentication scheme [3][4]. The recommended modern approach is to use the Secrets manager via the CREATE SECRET statement [3][5][6]. Regarding prepared parameters and the SET statement: DuckDB does support using prepared statements for SET operations [7]. You can use parameter binding syntax (such as?, $1, or $param) within a prepared SET statement to dynamically set configuration values, including legacy S3 settings if necessary [8][9][7]. For example, using positional parameters in a prepared SET statement: PREPARE set_s3_endpoint AS SET s3_endpoint = $1; EXECUTE set_s3_endpoint('my-endpoint.com'); However, for S3 configuration, you should prefer the modern secret-based approach [3][10]: CREATE OR REPLACE SECRET my_s3_secret ( TYPE s3, PROVIDER config, ENDPOINT $endpoint_param, USE_SSL $use_ssl_param,...); Note that the availability of parameter binding depends on the specific client driver being used; for instance, some clients like JDBC primarily support auto-incremented (?) parameters [8][9]. Top results: [3][8][7][2][5]

Citations:


🌐 Web query:

DuckDB Python connection.execute parameterized SET statement ? s3_endpoint s3_use_ssl

💡 Result:

In DuckDB, the SET statement is a DDL/configuration command, not a standard DML query (like SELECT or INSERT). Consequently, you cannot use parameterized inputs (e.g., ? or $1) directly with connection.execute("SET s3_endpoint = ?", [endpoint]) [1][2]. If you need to configure S3 settings programmatically, you should use f-strings or string formatting in Python to construct the SET statement, ensuring you sanitize the input to prevent injection if the values come from untrusted sources [1][2]. Recommended approach: con.execute(f"SET s3_endpoint = '{endpoint}'") con.execute(f"SET s3_use_ssl = {str(use_ssl).lower}") Important Note on S3 Configuration: While SET s3_endpoint and SET s3_use_ssl are available as legacy configuration methods, they are deprecated for managing secrets and sensitive credentials [3][4]. The modern, recommended way to configure S3 access is through DuckDB's Secret Manager [5][6]. This allows you to store configurations securely: CREATE OR REPLACE SECRET my_s3_secret ( TYPE S3, ENDPOINT 'your_endpoint_here', USE_SSL true, KEY_ID '...', SECRET '...'); Using the Secret Manager is safer as it avoids risks associated with leaking credentials through logs or accidental output [3][7].

Citations:


Inline literals for DuckDB secret/setup statements (core/wren/src/wren/connector/duckdb.py:17-37) CREATE SECRET and SET here don’t accept bound params, so S3/MinIO/GCS initialization will fail at runtime. Use safely quoted literals instead of execute(..., params).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/wren/src/wren/connector/duckdb.py` around lines 17 - 47, Update
_init_duckdb_s3, _init_duckdb_minio, and _init_duckdb_gcs so DuckDB CREATE
SECRET and SET statements use safely quoted literal values rather than bound
parameters. Preserve the existing credential, region, endpoint, and SSL values
while escaping or quoting them through a safe helper before embedding them in
each SQL statement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci core dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation python Pull requests that update Python code rust Pull requests that update rust code wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant