From 447685c1c49edf794bc9bef3420b5c8ce91ed6cb Mon Sep 17 00:00:00 2001 From: Jianjun Chen Date: Tue, 8 Sep 2026 10:26:43 +0800 Subject: [PATCH 1/2] fix(lineage): hint column-level needs an existing table in missing-table note (#154) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/main.rs | 2 +- tests/regress_issue_154_lineage_targets.rs | 29 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index fdc1fcc..273900d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1594,7 +1594,7 @@ fn cmd_lineage( } graph::lineage::TableLookup::Missing => { eprintln!( - "note: no table '{}' found — interpreting '{}' as a table reference", + "note: no table '{}' found — interpreting '{}' as a table reference (for column-level lineage, the table must exist)", table_name, target ); (target, None) diff --git a/tests/regress_issue_154_lineage_targets.rs b/tests/regress_issue_154_lineage_targets.rs index 016403c..c171755 100644 --- a/tests/regress_issue_154_lineage_targets.rs +++ b/tests/regress_issue_154_lineage_targets.rs @@ -206,6 +206,35 @@ fn should_report_clean_error_for_unknown_nodekey_target() { ); } +#[test] +fn should_hint_column_level_requires_table_when_target_missing() { + let tmp = TempDir::new().unwrap(); + let root = project_with_sql(&tmp, FIXTURE_SQL); + let out = run_codeweb_in( + &root, + &[ + "lineage", + "missing_table.some_col", + "-p", + root.to_str().unwrap(), + ], + ); + let stderr = String::from_utf8_lossy(&out.stderr); + assert!(out.status.success()); + assert!( + stderr.contains("interpreting"), + "existing fallback note must remain, stderr:\n{stderr}" + ); + assert!( + stderr.contains("the table must exist"), + "missing-table fallback must explain the column-level prerequisite, stderr:\n{stderr}" + ); + assert!( + stderr.contains("No table found matching"), + "final table-resolution error must still surface, stderr:\n{stderr}" + ); +} + #[test] fn should_say_ambiguous_when_table_half_is_ambiguous() { let tmp = TempDir::new().unwrap(); From 827b7a35096b4c4f63beffa95959c3f9d6231703 Mon Sep 17 00:00:00 2001 From: Jianjun Chen Date: Tue, 8 Sep 2026 10:27:44 +0800 Subject: [PATCH 2/2] test(graph): pin jsp/jspsql node-key tags under the jsp feature (#154) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/graph/key.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/graph/key.rs b/src/graph/key.rs index a3309f0..f32950a 100644 --- a/src/graph/key.rs +++ b/src/graph/key.rs @@ -92,7 +92,8 @@ pub enum NodeKey { /// Node-key type tags exactly as emitted by the [`fmt::Display`] implementation below. /// Keep in sync with its match arms; `should_detect_every_display_tag_roundtrip` pins -/// the fixed tags (the custom and unresolved formats are intentionally excluded). +/// non-JSP tags always and JSP tags under `cfg(feature = "jsp")` (the custom and +/// unresolved formats are intentionally excluded). const TYPE_TAG_PREFIXES: &[&str] = &[ "proc", "func", "mapper", "method", "class", "table", "view", "pkg", "trigger", "type", "seq", "idx", "mview", "syn", "event", "builtin", "javasql", "jsp", "jspsql", @@ -529,5 +530,31 @@ mod tests { "tag not detected for Display key: {key}" ); } + + #[cfg(feature = "jsp")] + { + let jsp_cases = [ + format!( + "{}", + NodeKey::JspPage { + path: "WEB-INF/a.jsp".into() + } + ), + format!( + "{}", + NodeKey::JspSql { + file: "WEB-INF/a.jsp".into(), + line: 7, + sql_hash: "abc123".into() + } + ), + ]; + for key in &jsp_cases { + assert!( + split_type_prefix(key).is_some(), + "tag not detected for Display key: {key}" + ); + } + } } }