---
phase: 44-show-describe-metadata-surface-enhancements
plan: 02
type: execute
wave: 2
depends_on: [44-01]
files_modified:
  - src/parse.rs
  - src/ddl/mod.rs
  - src/ddl/list.rs
  - src/ddl/show_columns.rs
  - src/lib.rs
  - test/sql/phase34_1_1_show_filtering.test
  - test/sql/phase44_show_terse_scope.test
  - test/sql/phase44_show_columns.test
  - test/sql/TEST_LIST
autonomous: true
requirements: [SHOW-02, SHOW-03, SHOW-04, SHOW-05]

must_haves:
  truths:
    - "SHOW SEMANTIC VIEWS IN SCHEMA schema_name returns only views in that schema"
    - "SHOW SEMANTIC VIEWS IN DATABASE db_name returns only views in that database"
    - "SHOW TERSE SEMANTIC VIEWS returns 5-column subset (created_on, name, kind, database_name, schema_name) -- no comment column"
    - "SHOW TERSE SEMANTIC VIEWS supports LIKE, STARTS WITH, LIMIT, IN SCHEMA, IN DATABASE clauses"
    - "SHOW COLUMNS IN SEMANTIC VIEW view_name returns unified list of dims, facts, and metrics with a kind column"
    - "SHOW COLUMNS excludes PRIVATE facts and metrics from output"
    - "Existing error test 'SHOW SEMANTIC VIEWS IN some_view' is updated to allow IN SCHEMA/DATABASE while still rejecting bare IN"
  artifacts:
    - path: "src/parse.rs"
      provides: "DdlKind::ShowTerse, ShowColumns variants, IN SCHEMA/DATABASE parsing"
      contains: "ShowTerse"
    - path: "src/ddl/show_columns.rs"
      provides: "ShowColumnsInSemanticViewVTab with 8-column output"
      contains: "ShowColumnsBindData"
    - path: "src/lib.rs"
      provides: "Registration of new VTab functions"
      contains: "show_columns_in_semantic_view"
    - path: "test/sql/phase44_show_terse_scope.test"
      provides: "Integration tests for TERSE and IN SCHEMA/DATABASE"
    - path: "test/sql/phase44_show_columns.test"
      provides: "Integration tests for SHOW COLUMNS IN SEMANTIC VIEW"
  key_links:
    - from: "src/parse.rs"
      to: "src/ddl/show_columns.rs"
      via: "DdlKind::ShowColumns -> show_columns_in_semantic_view function name"
      pattern: "ShowColumns.*show_columns"
    - from: "src/parse.rs"
      to: "src/ddl/list.rs"
      via: "DdlKind::ShowTerse -> list_terse_semantic_views function name"
      pattern: "ShowTerse.*list_terse"
    - from: "src/lib.rs"
      to: "src/ddl/show_columns.rs"
      via: "register_table_function_with_extra_info"
      pattern: "ShowColumnsInSemanticViewVTab"
---

<objective>
Add new DDL introspection forms: SHOW TERSE, IN SCHEMA/DATABASE scope filtering, and SHOW COLUMNS IN SEMANTIC VIEW.

Purpose: Complete the introspection surface to match Snowflake's SHOW command variants. TERSE mode and scope filtering are standard Snowflake patterns for managing large catalogs. SHOW COLUMNS provides a unified view of all semantic view fields.

Output: New DdlKind variants, new VTab for SHOW COLUMNS, parser extensions for TERSE and IN SCHEMA/DATABASE, integration tests.
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/44-show-describe-metadata-surface-enhancements/44-RESEARCH.md
@.planning/phases/44-show-describe-metadata-surface-enhancements/44-01-SUMMARY.md

<interfaces>
<!-- Key types and contracts from Plan 01 output and existing codebase -->

From src/parse.rs (DdlKind enum -- before Plan 02 additions):
```rust
pub enum DdlKind {
    Create, CreateOrReplace, CreateIfNotExists,
    Drop, DropIfExists,
    Describe,
    Show,               // SHOW SEMANTIC VIEWS
    AlterRename, AlterRenameIfExists,
    ShowDimensions,     // SHOW SEMANTIC DIMENSIONS
    ShowMetrics,        // SHOW SEMANTIC METRICS
    ShowFacts,          // SHOW SEMANTIC FACTS
}

fn detect_ddl_prefix(trimmed: &str) -> Option<(DdlKind, usize)>;
fn function_name(kind: DdlKind) -> &'static str;
fn parse_show_filter_clauses<'a>(after_prefix: &'a str, kind: DdlKind) -> Result<ShowClauses<'a>, String>;
fn build_filter_suffix(like_pattern, starts_with, limit) -> String;
fn rewrite_ddl(query: &str) -> Result<String, String>;

struct ShowClauses<'a> {
    like_pattern: Option<String>,
    in_view: Option<&'a str>,
    for_metric: Option<&'a str>,
    starts_with: Option<String>,
    limit: Option<u64>,
}
```

From src/ddl/list.rs (after Plan 01, 6 columns):
```rust
struct ListRow { created_on, name, kind, database_name, schema_name, comment: String }
pub struct ListSemanticViewsVTab;  // Returns 6 columns
```

From src/model.rs:
```rust
pub struct SemanticViewDefinition {
    pub database_name: Option<String>,
    pub schema_name: Option<String>,
    pub comment: Option<String>,
    pub dimensions: Vec<Dimension>,
    pub metrics: Vec<Metric>,
    pub facts: Vec<Fact>,
    pub tables: Vec<TableRef>,
    // ...
}
pub enum AccessModifier { Public, Private }
impl AccessModifier { pub fn is_default(&self) -> bool { matches!(self, Self::Public) } }
```

From src/lib.rs (registration pattern):
```rust
con.register_table_function_with_extra_info::<ListSemanticViewsVTab, _>(
    "list_semantic_views", &catalog_state,
);
```
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Add SHOW TERSE and IN SCHEMA/DATABASE parsing and VTab</name>
  <files>
    src/parse.rs,
    src/ddl/list.rs,
    src/lib.rs,
    test/sql/phase34_1_1_show_filtering.test,
    test/sql/phase44_show_terse_scope.test,
    test/sql/TEST_LIST
  </files>
  <read_first>
    src/parse.rs,
    src/ddl/list.rs,
    src/lib.rs,
    src/ddl/mod.rs,
    test/sql/phase34_1_1_show_filtering.test
  </read_first>
  <behavior>
    - Test: SHOW TERSE SEMANTIC VIEWS returns 5 columns (created_on, name, kind, database_name, schema_name) -- no comment
    - Test: SHOW TERSE SEMANTIC VIEWS LIKE '%pattern%' filters correctly
    - Test: SHOW TERSE SEMANTIC VIEWS LIMIT 1 limits output
    - Test: SHOW SEMANTIC VIEWS IN SCHEMA main returns views in 'main' schema
    - Test: SHOW SEMANTIC VIEWS IN SCHEMA nonexistent returns empty result
    - Test: SHOW SEMANTIC VIEWS IN DATABASE memory returns views in 'memory' database
    - Test: SHOW SEMANTIC VIEWS IN DATABASE nonexistent returns empty result
    - Test: SHOW TERSE SEMANTIC VIEWS IN SCHEMA main returns terse output filtered by schema
    - Test: SHOW TERSE SEMANTIC VIEWS IN DATABASE memory returns terse output filtered by database
    - Test: SHOW SEMANTIC VIEWS IN some_view (no SCHEMA/DATABASE keyword) still produces an error
    - Test: SHOW SEMANTIC VIEWS IN SCHEMA main LIKE '%pattern%' combines scope and filter
  </behavior>
  <action>
    **Step 1: Add DdlKind::ShowTerse variant to parse.rs:**

    Add `ShowTerse` to the `DdlKind` enum (after `Show`).

    **Step 2: Add ShowTerse prefix detection in `detect_ddl_prefix`:**
    Add BEFORE the `SHOW SEMANTIC VIEWS` match (longest-first rule):
    ```rust
    // SHOW TERSE SEMANTIC VIEWS (4 keywords) -- before SHOW SEMANTIC VIEWS
    if let Some(n) = match_keyword_prefix(b, &[b"show", b"terse", b"semantic", b"views"]) {
        return Some((DdlKind::ShowTerse, n));
    }
    ```
    This must go before the existing `SHOW SEMANTIC DIMENSIONS` match since "SHOW TERSE" is a different 4-keyword prefix that won't conflict with "SHOW SEMANTIC".

    **Step 3: Add function_name mapping for ShowTerse:**
    ```rust
    DdlKind::ShowTerse => "list_terse_semantic_views",
    ```

    **Step 4: Extend ShowClauses with in_schema and in_database fields:**
    Add to `ShowClauses<'a>`:
    ```rust
    in_schema: Option<&'a str>,
    in_database: Option<&'a str>,
    ```

    **Step 5: Modify `parse_show_filter_clauses` to handle IN SCHEMA/DATABASE for Show and ShowTerse:**
    In the "Check for IN keyword" section (step 2), replace the current Show rejection with:
    ```rust
    if rest.len() >= 2
        && rest[..2].eq_ignore_ascii_case("IN")
        && (rest.len() == 2 || rest.as_bytes()[2].is_ascii_whitespace())
    {
        if kind == DdlKind::Show || kind == DdlKind::ShowTerse {
            // IN SCHEMA or IN DATABASE scope for SHOW [TERSE] SEMANTIC VIEWS
            rest = rest[2..].trim_start();
            if rest.len() >= 6 && rest[..6].eq_ignore_ascii_case("SCHEMA")
                && (rest.len() == 6 || rest.as_bytes()[6].is_ascii_whitespace())
            {
                rest = rest[6..].trim_start();
                if rest.is_empty() {
                    return Err("Missing schema name after IN SCHEMA".to_string());
                }
                let name_end = rest.find(|c: char| c.is_whitespace()).unwrap_or(rest.len());
                in_schema = Some(&rest[..name_end]);
                rest = rest[name_end..].trim_start();
            } else if rest.len() >= 8 && rest[..8].eq_ignore_ascii_case("DATABASE")
                && (rest.len() == 8 || rest.as_bytes()[8].is_ascii_whitespace())
            {
                rest = rest[8..].trim_start();
                if rest.is_empty() {
                    return Err("Missing database name after IN DATABASE".to_string());
                }
                let name_end = rest.find(|c: char| c.is_whitespace()).unwrap_or(rest.len());
                in_database = Some(&rest[..name_end]);
                rest = rest[name_end..].trim_start();
            } else {
                return Err(
                    "SHOW SEMANTIC VIEWS requires IN SCHEMA <name> or IN DATABASE <name>".to_string()
                );
            }
        } else {
            // Existing behavior for DIMS/METRICS/FACTS: IN view_name
            rest = rest[2..].trim_start();
            if rest.is_empty() {
                return Err("Missing view name after IN".to_string());
            }
            let name_end = rest.find(|c: char| c.is_whitespace()).unwrap_or(rest.len());
            in_view = Some(&rest[..name_end]);
            rest = rest[name_end..].trim_start();
        }
    }
    ```

    **Step 6: Update ShowClauses return to include in_schema and in_database:**
    Return `in_schema` and `in_database` from the function.

    **Step 7: Extend `build_filter_suffix` to accept in_schema and in_database:**
    Change signature to:
    ```rust
    fn build_filter_suffix(
        like_pattern: Option<&str>,
        starts_with: Option<&str>,
        limit: Option<u64>,
        in_schema: Option<&str>,
        in_database: Option<&str>,
    ) -> String {
    ```
    Add to the parts building:
    ```rust
    if let Some(schema) = in_schema {
        let escaped = schema.replace('\'', "''");
        parts.push(format!("schema_name = '{escaped}'"));
    }
    if let Some(db) = in_database {
        let escaped = db.replace('\'', "''");
        parts.push(format!("database_name = '{escaped}'"));
    }
    ```
    Update ALL callers of `build_filter_suffix` to pass `clauses.in_schema` and `clauses.in_database` (or `None, None` for non-SHOW VIEWS kinds).

    **Step 8: Handle ShowTerse in rewrite_ddl match arm:**
    Add `DdlKind::ShowTerse` to the existing show match arm:
    ```rust
    DdlKind::Show | DdlKind::ShowTerse | DdlKind::ShowDimensions | DdlKind::ShowMetrics | DdlKind::ShowFacts => {
    ```
    In the `all_fn` match within this arm, add:
    ```rust
    DdlKind::ShowTerse => "list_terse_semantic_views",
    ```

    **Step 9: Create ListTerseSemanticViewsVTab in list.rs:**
    Add a new `ListTerseRow` struct (5 fields: created_on, name, kind, database_name, schema_name -- NO comment).
    Add `ListTerseBindData`, `ListTerseInitData`, and `ListTerseSemanticViewsVTab` following the exact same pattern as `ListSemanticViewsVTab` but with 5 output columns instead of 6. The bind function reads the catalog the same way but does NOT read `def.comment`.

    **Step 10: Register new VTab in lib.rs:**
    After the existing `list_semantic_views` registration, add:
    ```rust
    con.register_table_function_with_extra_info::<ListTerseSemanticViewsVTab, _>(
        "list_terse_semantic_views",
        &catalog_state,
    );
    ```
    Import `ListTerseSemanticViewsVTab` from `ddl::list`.

    **Step 11: Update entity match for ShowTerse in parse_show_filter_clauses:**
    Add to the entity match: `DdlKind::ShowTerse => "VIEWS",`

    **Step 12: Handle `extract_ddl_name` for ShowTerse:**
    In `extract_ddl_name` function, add `DdlKind::ShowTerse` to the `Show | ShowDimensions | ...` arm that returns `Ok(None)`.

    **Step 13: Update test/sql/phase34_1_1_show_filtering.test:**
    Change test 26 ("Error: SHOW SEMANTIC VIEWS IN some_view") which currently expects error "IN is not valid". Update the error message expectation to match the new error: "SHOW SEMANTIC VIEWS requires IN SCHEMA <name> or IN DATABASE <name>". The statement `SHOW SEMANTIC VIEWS IN some_view` should still error because `some_view` is neither SCHEMA nor DATABASE keyword.

    **Step 14: Create test/sql/phase44_show_terse_scope.test:**
    Integration test file covering:
    - Setup: create table + 2 semantic views (one with COMMENT, one without)
    - Test 1: `SHOW TERSE SEMANTIC VIEWS` -- 5 columns, 2 rows (use SELECT to skip created_on)
    - Test 2: `SHOW TERSE SEMANTIC VIEWS LIKE '%pattern%'` -- filtered
    - Test 3: `SHOW TERSE SEMANTIC VIEWS LIMIT 1` -- limited
    - Test 4: `SHOW SEMANTIC VIEWS IN SCHEMA main` -- both views
    - Test 5: `SHOW SEMANTIC VIEWS IN SCHEMA nonexistent` -- empty result
    - Test 6: `SHOW SEMANTIC VIEWS IN DATABASE memory` -- both views
    - Test 7: `SHOW SEMANTIC VIEWS IN DATABASE nonexistent` -- empty result
    - Test 8: `SHOW TERSE SEMANTIC VIEWS IN SCHEMA main` -- terse + schema filter
    - Test 9: `SHOW TERSE SEMANTIC VIEWS IN DATABASE memory LIKE '%pattern%'` -- combined
    - Test 10: Error: `SHOW SEMANTIC VIEWS IN some_view` -- must error (no SCHEMA/DATABASE keyword)
    - Test 11: Error: `SHOW SEMANTIC VIEWS IN SCHEMA` -- must error (missing name)

    Add `test/sql/phase44_show_terse_scope.test` to `test/sql/TEST_LIST`.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test 2>&1 | tail -5 && just build && just test-sql 2>&1 | tail -10</automated>
  </verify>
  <acceptance_criteria>
    - src/parse.rs DdlKind enum contains `ShowTerse`
    - src/parse.rs detect_ddl_prefix contains `match_keyword_prefix(b, &[b"show", b"terse", b"semantic", b"views"])`
    - src/parse.rs function_name contains `DdlKind::ShowTerse => "list_terse_semantic_views"`
    - src/parse.rs ShowClauses struct contains `in_schema: Option<&'a str>` and `in_database: Option<&'a str>`
    - src/parse.rs parse_show_filter_clauses contains `eq_ignore_ascii_case("SCHEMA")` and `eq_ignore_ascii_case("DATABASE")`
    - src/parse.rs build_filter_suffix contains `schema_name =` and `database_name =`
    - src/ddl/list.rs contains `pub struct ListTerseSemanticViewsVTab`
    - src/lib.rs contains `register_table_function_with_extra_info::<ListTerseSemanticViewsVTab`
    - src/lib.rs contains `"list_terse_semantic_views"`
    - test/sql/phase34_1_1_show_filtering.test error test for "SHOW SEMANTIC VIEWS IN some_view" contains updated error message
    - test/sql/phase44_show_terse_scope.test exists with SHOW TERSE and IN SCHEMA/DATABASE tests
    - test/sql/TEST_LIST contains phase44_show_terse_scope.test
    - `cargo test` passes
    - `just test-sql` passes
  </acceptance_criteria>
  <done>
    SHOW TERSE SEMANTIC VIEWS returns 5-column output. SHOW SEMANTIC VIEWS IN SCHEMA/DATABASE filters by scope. SHOW TERSE supports LIKE/STARTS WITH/LIMIT and IN SCHEMA/DATABASE. Bare "SHOW SEMANTIC VIEWS IN name" (without SCHEMA/DATABASE) still errors.
  </done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Create SHOW COLUMNS IN SEMANTIC VIEW VTab and parser support</name>
  <files>
    src/parse.rs,
    src/ddl/mod.rs,
    src/ddl/show_columns.rs,
    src/lib.rs,
    test/sql/phase44_show_columns.test,
    test/sql/TEST_LIST
  </files>
  <read_first>
    src/parse.rs,
    src/ddl/mod.rs,
    src/ddl/list.rs,
    src/ddl/show_dims.rs,
    src/lib.rs,
    src/model.rs,
    test/sql/TEST_LIST
  </read_first>
  <behavior>
    - Test: SHOW COLUMNS IN SEMANTIC VIEW returns 8 columns (database_name, schema_name, semantic_view_name, column_name, data_type, kind, expression, comment)
    - Test: Dimensions appear with kind=DIMENSION
    - Test: Facts appear with kind=FACT
    - Test: Regular metrics appear with kind=METRIC
    - Test: Derived metrics appear with kind=DERIVED_METRIC
    - Test: PRIVATE facts and metrics are excluded from SHOW COLUMNS output
    - Test: SHOW COLUMNS IN SEMANTIC VIEW nonexistent_view produces an error
    - Test: Columns are sorted by (kind, column_name) for deterministic output
  </behavior>
  <action>
    **Step 1: Add DdlKind::ShowColumns variant to parse.rs:**
    Add `ShowColumns` to the `DdlKind` enum.

    **Step 2: Add ShowColumns prefix detection in `detect_ddl_prefix`:**
    Add BEFORE all other SHOW matches (5-keyword prefix, longest-first):
    ```rust
    // SHOW COLUMNS IN SEMANTIC VIEW (5 keywords) -- before all SHOW SEMANTIC matches
    if let Some(n) = match_keyword_prefix(b, &[b"show", b"columns", b"in", b"semantic", b"view"]) {
        return Some((DdlKind::ShowColumns, n));
    }
    ```

    **Step 3: Add function_name mapping for ShowColumns:**
    ```rust
    DdlKind::ShowColumns => "show_columns_in_semantic_view",
    ```

    **Step 4: Add rewrite_ddl match arm for ShowColumns:**
    In the `rewrite_ddl` function, add ShowColumns as a name-only form (like Describe):
    ```rust
    DdlKind::ShowColumns => {
        let name = extract_name_only(trimmed, plen)?;
        let safe_name = name.replace('\'', "''");
        Ok(format!("SELECT * FROM {fn_name}('{safe_name}')"))
    }
    ```

    **Step 5: Handle `extract_ddl_name` for ShowColumns:**
    Add `DdlKind::ShowColumns` to the name-returning arm alongside `Drop | DropIfExists | Describe`.

    **Step 6: Create src/ddl/show_columns.rs:**

    Create a new VTab file following the established pattern:

    ```rust
    // Row struct: 8 columns
    struct ShowColumnRow {
        database_name: String,
        schema_name: String,
        semantic_view_name: String,
        column_name: String,
        data_type: String,
        kind: String,          // "DIMENSION", "FACT", "METRIC", "DERIVED_METRIC"
        expression: String,
        comment: String,
    }
    ```

    Bind function:
    - Declare 8 VARCHAR output columns: database_name, schema_name, semantic_view_name, column_name, data_type, kind, expression, comment
    - Read view name parameter, look up in catalog
    - Parse SemanticViewDefinition from JSON
    - Build alias_map via `def.alias_to_table_map()`
    - Collect rows from dimensions (kind="DIMENSION", include all), facts (kind="FACT", skip PRIVATE via `fact.access == AccessModifier::Private`), regular metrics (kind="METRIC", skip PRIVATE), derived metrics (kind="DERIVED_METRIC", skip PRIVATE)
    - For each item: column_name = item.name, data_type = item.output_type.unwrap_or_default(), expression = item.expr, comment = item.comment.unwrap_or_default()
    - Sort rows by (kind, column_name) for deterministic output
    - Determine if metric is derived: `metric.source_table.is_none()` -> "DERIVED_METRIC", else "METRIC"

    Func function: emit rows into 8 flat vectors (0-7).

    Parameters: `Some(vec![LogicalTypeHandle::from(LogicalTypeId::Varchar)])` (one positional parameter: view name).

    Import `AccessModifier` from `crate::model`.

    **Step 7: Add module to ddl/mod.rs:**
    Add `pub mod show_columns;` to `src/ddl/mod.rs`.

    **Step 8: Register VTab in lib.rs:**
    After existing VTab registrations, add:
    ```rust
    con.register_table_function_with_extra_info::<ShowColumnsInSemanticViewVTab, _>(
        "show_columns_in_semantic_view",
        &catalog_state,
    );
    ```
    Import `ShowColumnsInSemanticViewVTab` from `ddl::show_columns`.

    **Step 9: Create test/sql/phase44_show_columns.test:**
    Integration test:
    - Setup: Create table + semantic view with:
      - 2 dimensions (one with COMMENT)
      - 2 facts (one PRIVATE, one PUBLIC with COMMENT)
      - 2 metrics (one PRIVATE, one PUBLIC with COMMENT)
      - 1 derived metric (PUBLIC)
    - Test 1: `SHOW COLUMNS IN SEMANTIC VIEW my_view` -- verify 8-column output
    - Test 2: Verify dimensions show kind=DIMENSION
    - Test 3: Verify PUBLIC fact shows kind=FACT, PRIVATE fact is excluded
    - Test 4: Verify PUBLIC metric shows kind=METRIC, PRIVATE metric is excluded
    - Test 5: Verify derived metric shows kind=DERIVED_METRIC
    - Test 6: Verify comment column is populated where set
    - Test 7: Error: `SHOW COLUMNS IN SEMANTIC VIEW nonexistent` produces error

    Use `query TTTTTTTT rowsort` for 8-column queries. Use SELECT on specific columns where needed for clarity.

    Add `test/sql/phase44_show_columns.test` to `test/sql/TEST_LIST`.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test 2>&1 | tail -5 && just build && just test-sql 2>&1 | tail -10</automated>
  </verify>
  <acceptance_criteria>
    - src/parse.rs DdlKind enum contains `ShowColumns`
    - src/parse.rs detect_ddl_prefix contains `match_keyword_prefix(b, &[b"show", b"columns", b"in", b"semantic", b"view"])`
    - src/parse.rs function_name contains `DdlKind::ShowColumns => "show_columns_in_semantic_view"`
    - src/ddl/show_columns.rs exists and contains `pub struct ShowColumnsInSemanticViewVTab`
    - src/ddl/show_columns.rs contains `AccessModifier::Private` for PRIVATE filtering
    - src/ddl/show_columns.rs contains `"DIMENSION"` and `"FACT"` and `"METRIC"` and `"DERIVED_METRIC"` kind strings
    - src/ddl/mod.rs contains `pub mod show_columns`
    - src/lib.rs contains `register_table_function_with_extra_info::<ShowColumnsInSemanticViewVTab`
    - src/lib.rs contains `"show_columns_in_semantic_view"`
    - test/sql/phase44_show_columns.test exists and contains `SHOW COLUMNS IN SEMANTIC VIEW` queries
    - test/sql/TEST_LIST contains phase44_show_columns.test
    - `cargo test` passes
    - `just test-sql` passes
  </acceptance_criteria>
  <done>
    SHOW COLUMNS IN SEMANTIC VIEW returns 8-column unified output of all dims, facts, and metrics with a kind column. PRIVATE items are excluded. Derived metrics show as DERIVED_METRIC. Non-existent view produces an error.
  </done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| DDL input -> SQL rewrite | Schema/database names from IN SCHEMA/DATABASE flow through parse.rs into SQL WHERE clause |
| DDL input -> VTab parameter | View name from SHOW COLUMNS IN SEMANTIC VIEW flows to VTab bind as parameter |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-44-03 | Tampering | parse.rs IN SCHEMA/DATABASE | mitigate | Schema/database name is SQL-escaped via `replace('\'', "''")` in `build_filter_suffix`, matching existing pattern for all SHOW filter values. The value flows into a WHERE clause as `schema_name = 'escaped_value'`. |
| T-44-04 | Tampering | parse.rs ShowColumns view name | mitigate | View name extracted via `extract_name_only` and escaped with `replace('\'', "''")` before insertion into `SELECT * FROM show_columns_in_semantic_view('escaped_name')` -- same pattern as DESCRIBE and all other name-only forms. |
| T-44-05 | Information Disclosure | show_columns.rs PRIVATE filtering | mitigate | SHOW COLUMNS excludes PRIVATE facts and metrics from output, consistent with PRIVATE enforcement in expansion engine (Phase 43). PRIVATE items are hidden from all user-facing surfaces. |
</threat_model>

<verification>
1. `cargo test` -- all Rust unit tests pass (including new parse.rs tests for ShowTerse/ShowColumns prefix detection)
2. `just build && just test-sql` -- all sqllogictest files pass, including:
   - Updated phase34_1_1_show_filtering.test (IN error message change)
   - New phase44_show_terse_scope.test (TERSE, IN SCHEMA, IN DATABASE)
   - New phase44_show_columns.test (SHOW COLUMNS)
3. `just test-all` -- full quality gate passes
</verification>

<success_criteria>
- SHOW TERSE SEMANTIC VIEWS returns 5-column output without comment
- SHOW SEMANTIC VIEWS IN SCHEMA main / IN DATABASE memory filters correctly
- SHOW TERSE supports all existing filter clauses (LIKE, STARTS WITH, LIMIT) plus IN SCHEMA/DATABASE
- SHOW COLUMNS IN SEMANTIC VIEW returns 8-column unified output with kind column
- PRIVATE items excluded from SHOW COLUMNS
- All existing tests pass, 3 new integration test files
</success_criteria>

<output>
After completion, create `.planning/phases/44-show-describe-metadata-surface-enhancements/44-02-SUMMARY.md`
</output>
