---
phase: 11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views
plan: "04"
type: execute
wave: 3
depends_on: ["02", "03"]
files_modified:
  - src/ddl/define.rs
  - src/lib.rs
autonomous: true
requirements:
  - DDL-03
  - DDL-04
  - DDL-05

must_haves:
  truths:
    - "DefineSemanticView::signatures() returns a 6-arg positional signature with LIST(STRUCT) types"
    - "DefineSemanticView::invoke() calls parse_define_args and serializes result to JSON for catalog storage"
    - "define_or_replace_semantic_view uses the same new signature (reuses DefineState.or_replace)"
    - "semantic_query is renamed to semantic_view in lib.rs registration"
    - "The extension still compiles with --features extension"
    - "explain_semantic_view registration is also renamed in lib.rs comments/docs if needed"
  artifacts:
    - path: "src/ddl/define.rs"
      provides: "Updated DefineSemanticView VScalar with 6-arg STRUCT/LIST signature"
    - path: "src/lib.rs"
      provides: "semantic_view registration (renamed from semantic_query)"
  key_links:
    - from: "src/ddl/define.rs"
      to: "src/ddl/parse_args.rs"
      via: "parse_define_args called in invoke()"
    - from: "src/lib.rs"
      to: "src/query/table_function.rs"
      via: "register_table_function_with_extra_info::<SemanticViewVTab> with name 'semantic_view'"
---

<objective>
Wire the new parse_args module into DefineSemanticView and rename semantic_query to semantic_view in the extension registration.

Purpose: This plan connects the STRUCT/LIST parser (Plan 02) to the actual VScalar invoke path, and updates the function name registrations in lib.rs. After this plan, the Rust code implements the complete new interface — the only remaining work is updating the .test files.

Output: Updated src/ddl/define.rs with new 6-arg VScalar signature; src/lib.rs with "semantic_view" registration.
</objective>

<execution_context>
@/Users/paul/.claude/get-shit-done/workflows/execute-plan.md
@/Users/paul/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/phases/11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views/11.1-CONTEXT.md
@.planning/phases/11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views/11.1-RESEARCH.md
@.planning/phases/11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views/11.1-02-SUMMARY.md
@.planning/phases/11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views/11.1-03-SUMMARY.md

<interfaces>
<!-- From Plan 02 parse_args.rs -->
```rust
pub struct ParsedDefineArgs {
    pub name: String,
    pub def: SemanticViewDefinition,
}
pub unsafe fn parse_define_args(
    input: &mut DataChunkHandle,
    row: usize,
) -> Result<ParsedDefineArgs, String>;
```

<!-- Current define.rs VScalar signatures() — to be replaced -->
```rust
fn signatures() -> Vec<ScalarFunctionSignature> {
    vec![ScalarFunctionSignature::exact(
        vec![
            LogicalTypeHandle::from(LogicalTypeId::Varchar), // view name
            LogicalTypeHandle::from(LogicalTypeId::Varchar), // definition JSON
        ],
        LogicalTypeHandle::from(LogicalTypeId::Varchar),
    )]
}
```

<!-- New signatures() — from RESEARCH.md code examples -->
```rust
fn signatures() -> Vec<ScalarFunctionSignature> {
    // Build the type handles for each LIST(STRUCT) parameter
    let varchar = || LogicalTypeHandle::from(LogicalTypeId::Varchar);

    let tables_type = LogicalTypeHandle::list(&LogicalTypeHandle::struct_type(&[
        ("alias", varchar()),
        ("table", varchar()),
    ]));
    let col_pair_struct = LogicalTypeHandle::struct_type(&[
        ("from", varchar()),
        ("to", varchar()),
    ]);
    let join_columns_type = LogicalTypeHandle::list(&col_pair_struct);
    let relationships_type = LogicalTypeHandle::list(&LogicalTypeHandle::struct_type(&[
        ("from_table", varchar()),
        ("to_table", varchar()),
        ("join_columns", join_columns_type),
    ]));
    let dimensions_type = LogicalTypeHandle::list(&LogicalTypeHandle::struct_type(&[
        ("name", varchar()),
        ("expr", varchar()),
        ("source_table", varchar()),
    ]));
    let time_dimensions_type = LogicalTypeHandle::list(&LogicalTypeHandle::struct_type(&[
        ("name", varchar()),
        ("expr", varchar()),
        ("granularity", varchar()),
    ]));
    let metrics_type = LogicalTypeHandle::list(&LogicalTypeHandle::struct_type(&[
        ("name", varchar()),
        ("expr", varchar()),
        ("source_table", varchar()),
    ]));

    vec![ScalarFunctionSignature::exact(
        vec![
            varchar(),             // 0: view name
            tables_type,           // 1: tables
            relationships_type,    // 2: relationships
            dimensions_type,       // 3: dimensions
            time_dimensions_type,  // 4: time_dimensions
            metrics_type,          // 5: metrics
        ],
        varchar(),  // returns view name
    )]
}
```

<!-- Current invoke() reads 2 VARCHAR args — to be replaced with parse_define_args call -->
```rust
unsafe fn invoke(state: &Self::State, input: &mut DataChunkHandle, output: &mut dyn WritableVector)
    -> Result<(), Box<dyn std::error::Error>>
{
    let name_col = input.flat_vector(0);
    let names = name_col.as_slice_with_len::<duckdb_string_t>(input.len());
    let json_col = input.flat_vector(1);
    let jsons = json_col.as_slice_with_len::<duckdb_string_t>(input.len());
    let out = output.flat_vector();
    for i in 0..input.len() {
        let name = ...;
        let json = ...;
        // persist + catalog_insert/upsert
        out.insert(i, name.as_str());
    }
    Ok(())
}
```

<!-- Current lib.rs registration for semantic_query — line ~173 -->
```rust
con.register_table_function_with_extra_info::<SemanticViewVTab, _>(
    "semantic_query",
    &query_state,
)?;
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Update DefineSemanticView to 6-arg STRUCT/LIST signature; wire parse_define_args</name>
  <files>src/ddl/define.rs</files>
  <action>
Rewrite src/ddl/define.rs:

1. **Update imports** — add `use crate::ddl::parse_args::parse_define_args;`. Remove `duckdb_string_t` import if no longer needed directly. Keep all other imports.

2. **Replace signatures()** with the new 6-arg version shown in the interfaces section above. The helper closure `let varchar = || LogicalTypeHandle::from(LogicalTypeId::Varchar);` keeps the code readable. Do NOT use named parameters — this is a positional VScalar.

3. **Rewrite invoke()** to use parse_define_args:
```rust
unsafe fn invoke(
    state: &Self::State,
    input: &mut DataChunkHandle,
    output: &mut dyn WritableVector,
) -> Result<(), Box<dyn std::error::Error>> {
    let out = output.flat_vector();
    for i in 0..input.len() {
        let parsed = parse_define_args(input, i)
            .map_err(|e| Box::<dyn std::error::Error>::from(e))?;
        let json = serde_json::to_string(&parsed.def)
            .map_err(|e| Box::<dyn std::error::Error>::from(e.to_string()))?;

        // Persist to DuckDB table FIRST (write-first pattern, same as before).
        if let Some(conn) = state.persist_conn {
            persist_define(conn, &parsed.name, &json)
                .map_err(|e| Box::<dyn std::error::Error>::from(e))?;
        }

        // Update in-memory catalog.
        if state.or_replace {
            catalog_upsert(&state.catalog, &parsed.name, &json)?;
        } else {
            catalog_insert(&state.catalog, &parsed.name, &json)?;
        }

        out.insert(i, parsed.name.as_str());
    }
    Ok(())
}
```

4. **Keep persist_define unchanged** — it still takes `name: &str` and `json: &str` and writes to semantic_layer._definitions. No change needed.

5. **Keep DefineState unchanged** — catalog, persist_conn, or_replace fields are identical.

After editing, verify the file compiles by checking for obvious syntax errors (but full extension build is deferred to Plan 05 make test).
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo check --features extension 2>&1 | tail -30</automated>
  </verify>
  <done>cargo check --features extension passes without errors on define.rs changes; signatures() has 6 args; invoke() calls parse_define_args</done>
</task>

<task type="auto">
  <name>Task 2: Rename semantic_query to semantic_view in lib.rs</name>
  <files>src/lib.rs</files>
  <action>
In src/lib.rs, find the registration call for semantic_query (around line 173-176):
```rust
con.register_table_function_with_extra_info::<SemanticViewVTab, _>(
    "semantic_query",
    &query_state,
)?;
```

Change the string "semantic_query" to "semantic_view":
```rust
con.register_table_function_with_extra_info::<SemanticViewVTab, _>(
    "semantic_view",
    &query_state,
)?;
```

Also update any comment that references "semantic_query" in the same block to say "semantic_view". For example, update:
```rust
// Register the semantic_query table function.
```
to:
```rust
// Register the semantic_view table function.
```

The QueryState struct name and the SemanticViewVTab type are NOT renamed — only the registered DuckDB function name string changes.

Also update the comment on the QueryState creation above that says "for semantic_query" to say "for semantic_view".

No other changes to lib.rs.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && grep -n "semantic_query\|semantic_view" src/lib.rs 2>&1</automated>
  </verify>
  <done>The string "semantic_query" no longer appears in lib.rs registration calls; "semantic_view" is used instead; cargo check --features extension passes</done>
</task>

</tasks>

<verification>
`cargo check --features extension` passes (type-checks the extension build without full linking). The new 6-arg signature is in place and invoke() calls parse_define_args. "semantic_view" is the registered name.
</verification>

<success_criteria>
- define.rs signatures() returns 6-arg exact signature with LIST(STRUCT) types
- define.rs invoke() uses parse_define_args and serde_json::to_string for JSON persistence
- lib.rs registers "semantic_view" not "semantic_query"
- cargo check --features extension passes without errors
</success_criteria>

<output>
After completion, create `.planning/phases/11.1-review-possible-ddl-and-query-syntax-options-and-bring-it-as-close-as-we-can-to-snowflake-semantic-views/11.1-04-SUMMARY.md`
</output>
