Skip to content

fix(core): normalize the name of session property#1331

Merged
douenergy merged 2 commits intoCanner:mainfrom
goldmedal:fix/normalize-session-property
Sep 30, 2025
Merged

fix(core): normalize the name of session property#1331
douenergy merged 2 commits intoCanner:mainfrom
goldmedal:fix/normalize-session-property

Conversation

@goldmedal
Copy link
Copy Markdown
Contributor

@goldmedal goldmedal commented Sep 24, 2025

Description

The name of a session property should be case-insensitive. This PR adds the field normalized_name for SessionProperty which is used to match the required property.

Summary by CodeRabbit

  • Bug Fixes

    • Session properties are now handled case-insensitively across manifests and request headers, ensuring consistent matching.
    • Access control (row/column-level) validation and defaults now reliably use normalized session property names.
    • Error messages are more consistent when required session properties are missing or mismatched.
  • Tests

    • Expanded coverage for session-level access scenarios, including differing result shapes based on session property values and cache behavior.
    • Added roundtrip checks to ensure session property normalization and serialization behave as expected.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 24, 2025

Walkthrough

Implements case-insensitive session property handling by introducing SessionProperty.normalized_name, custom deserialization, and refactoring access-control logic to use normalized names. Updates constructors, accessors, and tests (including headers and manifests) to reflect normalized lookups and varied results based on session_level values.

Changes

Cohort / File(s) Summary
Tests: ibis Postgres CLAC
ibis-server/tests/routers/v3/connector/postgres/test_query.py
Updates tests to use mixed-case requiredProperties (Session_level), adjust headers, and add follow-up requests verifying different shapes per session_level and cache behavior.
SessionProperty model and serde
wren-core-base/manifest-macro/src/lib.rs
Adds normalized_name field (lowercased), removes derive(Deserialize), implements custom Deserialize to derive normalized_name from name, and adds constructor initializing normalized_name.
SessionProperty builders and roundtrip tests
wren-core-base/src/mdl/builder.rs
Refactors constructors to delegate to SessionProperty::new; adds tests verifying JSON roundtrip omits normalizedName and ensures normalization equality.
SessionProperty accessor
wren-core-base/src/mdl/manifest.rs
Adds public accessor normalized_name(&self) -> &str.
Python binding constructor
wren-core-base/src/mdl/py_method.rs
Makes SessionProperty::new public and initializes normalized_name via to_lowercase.
Access control normalization
wren-core/core/src/logical_plan/analyze/access_control.rs
Refactors RLAC/CLAC validation and filter-building to compare against property.normalized_name(); updates presence checks and lookups; changes is_property_present to accept SessionProperty.
Core tests and header handling
wren-core/core/src/mdl/mod.rs
Adjusts test session property casing (Session_level) and lowercases header keys at insertion; updates error snapshots accordingly.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server/API
    participant AC as AccessControl
    participant SP as SessionProperty

    Note over C,S: Request with headers (e.g., "Session_level": "1")
    C->>S: Execute query (headers, manifest)
    S->>AC: Validate RLAC/CLAC and build filters
    AC->>SP: Deserialize manifest SessionProperty
    Note over SP: normalized_name = name.to_lowercase()
    AC->>AC: Lookup header by property.normalized_name()
    alt Required property missing
        AC-->>S: Error (missing required property)
        S-->>C: 4xx error
    else Present or defaulted
        AC->>AC: Apply rule/filter using normalized names
        S-->>C: Result (shape depends on session_level)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

core, ibis, rust, python

Suggested reviewers

  • douenergy

Poem

A bun taps keys with whiskered grace,
Lowercasing names in every place.
Headers hop, rules align,
Filters shape the data fine.
Cache nibbles twice—one, then two—
Case-insensitive, job well through.
(_/)\u2009✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix(core): normalize the name of session property" is concise and accurately captures the main change: introducing and using a normalized_name for SessionProperty to make name matching case-insensitive in core, and it correctly scopes the change to the core area.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@github-actions github-actions bot added core ibis rust Pull requests that update Rust code python Pull requests that update Python code labels Sep 24, 2025
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: 0

🧹 Nitpick comments (4)
wren-core-base/src/mdl/py_method.rs (1)

76-82: Unify normalization: prefer ASCII-lowercasing for property keys

To keep normalization consistent and predictable across platforms/locales, use to_ascii_lowercase() here (headers and other call sites also lower-case).

-        pub fn new(name: String, required: bool, default_expr: Option<String>) -> Self {
-            Self {
-                normalized_name: name.to_lowercase(),
+        pub fn new(name: String, required: bool, default_expr: Option<String>) -> Self {
+            Self {
+                normalized_name: name.to_ascii_lowercase(),
                 name,
                 required,
                 default_expr,
             }
         }
wren-core/core/src/mdl/mod.rs (1)

3665-3665: Normalize headers with ASCII-lowercase for consistency

Use to_ascii_lowercase() to match other normalization paths and avoid locale surprises.

-            headers.insert(key.to_lowercase(), value.clone());
+            headers.insert(key.to_ascii_lowercase(), value.clone());
wren-core/core/src/logical_plan/analyze/access_control.rs (2)

142-149: Normalize with ASCII-lowercase for consistency

Elsewhere we lower-case header keys; prefer to_ascii_lowercase() here for uniform behavior (and to match collect_condition).

-                let property_name =
-                    value.trim_start_matches("@").to_string().to_lowercase();
+                let property_name =
+                    value.trim_start_matches("@").to_ascii_lowercase();

366-370: Treat whitespace-only values as missing

Current check counts " " as present. Trim before checking emptiness to fail fast on invalid required values and avoid later parse errors.

-    headers
-        .get(property.normalized_name())
-        .map(|v| v.as_ref().is_some_and(|value| !value.is_empty()))
-        .unwrap_or(false)
+    headers
+        .get(property.normalized_name())
+        .map(|v| v.as_ref().is_some_and(|value| !value.trim().is_empty()))
+        .unwrap_or(false)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 269fb48 and 5603f25.

📒 Files selected for processing (7)
  • ibis-server/tests/routers/v3/connector/postgres/test_query.py (2 hunks)
  • wren-core-base/manifest-macro/src/lib.rs (1 hunks)
  • wren-core-base/src/mdl/builder.rs (2 hunks)
  • wren-core-base/src/mdl/manifest.rs (1 hunks)
  • wren-core-base/src/mdl/py_method.rs (1 hunks)
  • wren-core/core/src/logical_plan/analyze/access_control.rs (6 hunks)
  • wren-core/core/src/mdl/mod.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-05T02:27:29.829Z
Learnt from: goldmedal
PR: Canner/wren-engine#1161
File: ibis-server/app/routers/v3/connector.py:78-83
Timestamp: 2025-05-05T02:27:29.829Z
Learning: The row-level access control implementation in Wren Engine filters headers with the prefix `X_WREN_VARIABLE_PREFIX` in `EmbeddedEngineRewriter.get_session_properties` and validates session property expressions in `access_control.rs` to ensure they only contain literal values, preventing SQL injection.

Applied to files:

  • wren-core/core/src/mdl/mod.rs
  • wren-core/core/src/logical_plan/analyze/access_control.rs
🧬 Code graph analysis (5)
wren-core-base/manifest-macro/src/lib.rs (2)
wren-core-base/src/mdl/manifest.rs (5)
  • name (260-262)
  • name (305-307)
  • name (324-326)
  • name (330-332)
  • normalized_name (336-338)
wren-core-base/src/mdl/py_method.rs (2)
  • new (76-83)
  • new (90-96)
ibis-server/tests/routers/v3/connector/postgres/test_query.py (2)
ibis-server/tests/conftest.py (1)
  • client (18-23)
ibis-server/tests/routers/v3/connector/postgres/conftest.py (1)
  • connection_info (54-61)
wren-core/core/src/mdl/mod.rs (1)
wren-core-base/src/mdl/builder.rs (1)
  • new_required (177-179)
wren-core/core/src/logical_plan/analyze/access_control.rs (1)
wren-core/core/src/mdl/context.rs (1)
  • properties (93-99)
wren-core-base/src/mdl/builder.rs (2)
wren-core-base/src/mdl/py_method.rs (2)
  • new (76-83)
  • new (90-96)
wren-core-base/src/mdl/manifest.rs (5)
  • name (260-262)
  • name (305-307)
  • name (324-326)
  • name (330-332)
  • normalized_name (336-338)
⏰ 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). (3)
  • GitHub Check: cargo check
  • GitHub Check: test
  • GitHub Check: ci
🔇 Additional comments (15)
wren-core-base/src/mdl/manifest.rs (1)

335-339: Accessor looks good

Public getter is minimal and correct; aligns with serde skip-serialize field.

wren-core/core/src/mdl/mod.rs (2)

2662-2666: Case-insensitive property test: LGTM

Using "Session_level" in CLAC requiredProperties to verify case-insensitive handling is appropriate.


2706-2706: Snapshot expectation change aligns with behavior

Error message reflecting original name "Session_level" is expected; no action.

wren-core-base/src/mdl/builder.rs (2)

176-182: Centralizing construction via SessionProperty::new: good move

Removes duplication and guarantees normalized_name initialization.


841-850: Roundtrip test is solid

  • Confirms normalizedName is not serialized.
  • Ensures normalized_name equals lowercased name.
ibis-server/tests/routers/v3/connector/postgres/test_query.py (3)

81-83: Good coverage for case-insensitive property names

Using "Session_level" exercises the new normalization path.


676-677: Header for required property path: LGTM

Ensures required property present; consistent with case-insensitive lookups.


684-699: Extra case validates shape variance and cache keying

Adding the “session_level=2” call is valuable to assert column-shape changes and cache partitioning by variables.

wren-core/core/src/logical_plan/analyze/access_control.rs (3)

97-105: Use of normalized_name() in RLAC validation is correct

Aligns requiredProperties validation with case-insensitive normalization.


258-268: Swapping to property-aware presence checks is correct

Routing both required/optional paths through is_property_present(&SessionProperty) is cleaner and less error-prone.


304-305: Lookup by normalized key: LGTM

properties.get(property.normalized_name()) matches the canonicalization strategy.

wren-core-base/manifest-macro/src/lib.rs (4)

407-407: Removed Deserialize from derive macro for custom deserialization.

The Deserialize trait has been correctly removed from the automatic derive since a custom implementation is provided below. This prevents compilation conflicts.


413-416: Added normalized_name field with appropriate serde configuration.

The field is properly configured to skip serialization and use a default value during deserialization, which ensures backward compatibility with existing JSON manifests.


418-429: Constructor implementation follows consistent pattern.

The constructor correctly initializes the normalized_name field using to_lowercase() and is properly guarded with the feature flag to avoid conflicts with Python bindings. The implementation matches the pattern used in py_method.rs as shown in the relevant code snippets.


431-452: Custom deserialization ensures normalized_name is always computed.

The custom deserialize implementation correctly:

  • Uses a helper struct to deserialize the original fields
  • Computes normalized_name from the deserialized name field using to_lowercase()
  • Maintains field order consistency with the struct definition

This approach ensures that all SessionProperty instances have a properly initialized normalized_name field regardless of how they are created.

@goldmedal goldmedal requested a review from douenergy September 24, 2025 04:53
@douenergy
Copy link
Copy Markdown
Contributor

Thanks @goldmedal

@douenergy douenergy merged commit 95bc169 into Canner:main Sep 30, 2025
14 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core ibis python Pull requests that update Python code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants