Skip to content

fix(lineage): resolve node-key targets and bare schema.table without column misparse (#154) - #156

Merged
c2j merged 8 commits into
mainfrom
fix/issue-154
Sep 7, 2026
Merged

c2j merged 8 commits into
mainfrom
fix/issue-154

Conversation

@c2j

@c2j c2j commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Closes #154

问题

codeweb lineage 的 target 在任何节点解析之前就按最后一个 . 切分(src/main.rsrsplit_once('.')),导致:

  1. 节点键语法不支持codeweb lineage "table:bigfund.mid_yjqs_detail" 被拆成 表=table:bigfund / 列=mid_yjqs_detail,误入列级分支,静默返回 "No column lineage"——报错消息与用户输入字面相同,极具误导性
  2. schema.table 误判:不带前缀的 schema 限定表同样被误判为 table.column 列级查询

根因:lineage 是唯一在解析前预切分 target 的命令;trace/detail/impact 均通过 resolve_single_node + name_index(存 NodeKey Display 键,如 table:schema.table)天然支持节点键语法。

修复(三层增量,全部向后兼容)

  1. 节点键快速路径src/graph/key.rs + src/main.rs):新增 split_type_prefix() 检测 <known-tag>: 前缀(标签清单与 NodeKey Display 实现同文件,roundtrip 单测防漂移);命中即整体作为节点键走表级分支,与其他命令语法一致
  2. 列级判定回退src/main.rs + src/graph/lineage.rs):dot 切分出的表半部分无法解析时,回退为整体表引用;lookup_table_node() 返回 TableLookup { Found, Ambiguous, Missing } 区分"未找到"与"跨 schema 歧义",note 措辞如实区分两种情况。表半部分可解析时列级行为完全不变(含"表存在但列无映射 → No column lineage 提示")
  3. 顺带修正find_table_node):schema 比较改为大小写不敏感(与表名比较一致;Bigfund.mid_yjqs_detail.amt 不再因 schema 大小写不同而误触发回退)

同时更新 clap help 文档节点键语法。

兼容性红线(均有测试锁定)

  • 裸名 my_table → 表级(不变,含子串模糊匹配与 Multiple matches 提示)
  • table.column / schema.table.column → 列级(不变)
  • 无点号节点键 table:my_table → 行为不变(原本就走表级)

测试(TDD,Red → Green)

新增 tests/regress_issue_154_lineage_targets.rs(7 个行为测试):

测试 行为
should_treat_nodekey_target_as_table_level_lineage 节点键走表级血缘
should_fall_back_to_table_level_for_bare_schema_qualified_target 裸 schema.table 回退表级 + 透明 note
should_keep_column_level_for_existing_table_and_column 三段式列级查询不变
should_keep_no_column_lineage_hint_when_table_exists_but_column_unknown 表存在列未知仍为列级提示(回退不劫持)
should_report_clean_error_for_unknown_nodekey_target 节点键未命中干净报错
should_resolve_column_query_with_differently_cased_schema schema 大小写不敏感
should_say_ambiguous_when_table_half_is_ambiguous 歧义表半部分 note 如实说明

src/graph/key.rs 新增 3 个单元测试(含 NodeKey Display 标签 roundtrip 防漂移)。

门禁

  • cargo fmt --all -- --check
  • cargo clippy --features full -- -D warnings
  • cargo test --features full -- --skip test_path_mapping_applied --skip test_serve_ ✅(671 unit + 36 套件 0 失败)
  • 人类已有测试零改动(regress_column_lineage / regress_lineage_table_upstream / regress_issue_140 全绿)

c2j and others added 7 commits September 7, 2026 09:23
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…column misparse (#154)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…f dead-end fallback (#154)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Comment thread src/main.rs
let (table_name, column_name) = match column_name {
Some(column) => match graph::lineage::lookup_table_node(graph, table_name) {
graph::lineage::TableLookup::Found(_) => (table_name, Some(column)),
graph::lineage::TableLookup::Ambiguous => {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] When the dotted table half is TableLookup::Ambiguous, the CLI still falls back to treating the whole target as a table reference (same recovery as Missing). For mid_yjqs_detail.nonexistent_col with that table in two schemas, the user almost certainly meant column-level lineage and only failed to schema-qualify; reinterpreting mid_yjqs_detail.nonexistent_col as a table name yields a follow-on No table found matching '…' that hides the actionable fix (qualify as schema.mid_yjqs_detail.col). The new Ambiguous variant is the right distinction, but it is not used as a terminal error.

Suggestion: On Ambiguous, print that the table name is ambiguous across schemas, tell the user to qualify (schema.table or schema.table.column), and return. Keep the Missing → whole-target table fallback only for the true schema.table mis-split case.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in commit c8a4249. An ambiguous table half is now a terminal error with an actionable schema-qualification hint (qualify it as 'schema.X' for table-level lineage or 'schema.X.col' for column-level lineage). The whole-target fallback remains only for TableLookup::Missing, which is the true schema.table mis-split recovery path. This behavior is locked by should_say_ambiguous_when_table_half_is_ambiguous.

Comment thread src/main.rs Outdated
target
);
return Ok(());
// Issue #154: `type:name` node keys (e.g. `table:schema.table`) resolve as whole

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The two // Issue #154: blocks (prefix fast path and column-spec fallback) narrate the ticket, restate the branches, and embed design history. They explain WHAT the next if/match already shows rather than a non-obvious constraint.

Suggestion: Drop the issue-number / changelog narration. If a comment stays, keep one short WHY (e.g. node keys must not be last-dot-split; missing table half means the split was probably schema.table).

Comment thread src/graph/key.rs Outdated
/// never mistaken for `table.column` targets (#154).
pub fn split_type_prefix(target: &str) -> Option<(&str, &str)> {
let (tag, rest) = target.split_once(':')?;
if rest.is_empty() || !TYPE_TAG_PREFIXES.contains(&tag) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] split_type_prefix compares the tag with TYPE_TAG_PREFIXES.contains(&tag), which is case-sensitive. resolve_single_node / name_index lower-case the query, and Display tags are lowercase, so a copied key works — but Table:schema.table or TABLE:… still takes the last-dot column path and can reproduce the original misparse.

Suggestion: Compare the tag case-insensitively (e.g. eq_ignore_ascii_case against the prefix list) so the fast path matches the rest of CLI node resolution.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. One severity correction based on the resolution path: search_nodes_limit lowercases the query (src/graph/store.rs:780) and searches the lowercase name_index, so a Table:... target still resolved correctly through the Missing fallback rather than reproducing the original misparse; the defect was the spurious fallback note. Commit 53a1361 makes type-tag detection case-insensitive, removing that note and routing mixed-case tags through the node-key fast path consistently with the rest of CLI node resolution.

"bare schema.table should fall back to table-level lineage, got:\n{stdout}"
);
assert!(
stderr.contains("interpreting") || stderr.contains("treating"),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] should_fall_back_to_table_level_for_bare_schema_qualified_target accepts stderr.contains("interpreting") || stderr.contains("treating"). Production text only uses interpreting; treating cannot fail the assertion and looks like a leftover from an earlier wording.

Suggestion: Assert the actual note (interpreting plus the original target), same as the Ambiguous/Missing eprintln! format strings.

…ghten fallback assertion (#154)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@c2j
c2j merged commit c88156c into main Sep 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant