mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-07 16:02:27 +00:00
fix: fix DB Manager not working with db resources with 4+ path segments (#7809)
* support more than 3 path segments * Fix explore db resource not working with 4+ path segments * don't assume 3 segments * ?table= syntax impl * update parsers * more nit fixes * fix sql query * claude nit * Update SQLx metadata --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
windmill-internal-app[bot]
parent
6673507824
commit
5cdecd795c
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -87,7 +87,14 @@ impl AssetCollector {
|
||||
.map(|id| id.as_ident().map(|id| id.value.clone()))
|
||||
.collect::<Option<Vec<String>>>()?
|
||||
.join(".");
|
||||
let path = format!("{}/{}", path, specific_table);
|
||||
|
||||
// For Resource assets, use ?table= query parameter syntax
|
||||
// For Ducklake and DataTable, maintain /table syntax
|
||||
let path = if *kind == AssetKind::Resource {
|
||||
format!("{}?table={}", path, specific_table)
|
||||
} else {
|
||||
format!("{}/{}", path, specific_table)
|
||||
};
|
||||
return Some(ParseAssetsResult { kind: *kind, access_type, path, columns: None });
|
||||
}
|
||||
}
|
||||
@@ -104,7 +111,14 @@ impl AssetCollector {
|
||||
.map(|id| id.as_ident().map(|id| id.value.clone()))
|
||||
.collect::<Option<Vec<String>>>()?
|
||||
.join(".");
|
||||
format!("{}/{}", path, specific_table)
|
||||
|
||||
// For Resource assets, use ?table= query parameter syntax
|
||||
// For Ducklake and DataTable, maintain /table syntax
|
||||
if *kind == AssetKind::Resource {
|
||||
format!("{}?table={}", path, specific_table)
|
||||
} else {
|
||||
format!("{}/{}", path, specific_table)
|
||||
}
|
||||
} else {
|
||||
path.clone()
|
||||
};
|
||||
@@ -845,7 +859,7 @@ mod tests {
|
||||
s.map_err(|e| e.to_string()),
|
||||
Ok(vec![ParseAssetsResult {
|
||||
kind: AssetKind::Resource,
|
||||
path: "u/user/pg_resource/table1".to_string(),
|
||||
path: "u/user/pg_resource?table=table1".to_string(),
|
||||
access_type: Some(R),
|
||||
columns: None
|
||||
},])
|
||||
@@ -870,6 +884,75 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sql_asset_parser_resource_vs_ducklake_syntax() {
|
||||
// Test that Resource uses ?table= while Ducklake uses /table
|
||||
let input_resource = r#"
|
||||
ATTACH 'res://u/user/pg_resource' AS db (TYPE postgres);
|
||||
SELECT * FROM db.users;
|
||||
"#;
|
||||
let s = parse_assets(input_resource).map(|s| s.assets);
|
||||
assert_eq!(
|
||||
s.map_err(|e| e.to_string()),
|
||||
Ok(vec![ParseAssetsResult {
|
||||
kind: AssetKind::Resource,
|
||||
path: "u/user/pg_resource?table=users".to_string(),
|
||||
access_type: Some(R),
|
||||
columns: None
|
||||
},])
|
||||
);
|
||||
|
||||
let input_ducklake = r#"
|
||||
ATTACH 'ducklake://my_lake' AS dl;
|
||||
SELECT * FROM dl.users;
|
||||
"#;
|
||||
let s = parse_assets(input_ducklake).map(|s| s.assets);
|
||||
assert_eq!(
|
||||
s.map_err(|e| e.to_string()),
|
||||
Ok(vec![ParseAssetsResult {
|
||||
kind: AssetKind::Ducklake,
|
||||
path: "my_lake/users".to_string(),
|
||||
access_type: Some(R),
|
||||
columns: None
|
||||
},])
|
||||
);
|
||||
|
||||
let input_datatable = r#"
|
||||
ATTACH 'datatable://dt1' AS dt;
|
||||
SELECT * FROM dt.users;
|
||||
"#;
|
||||
let s = parse_assets(input_datatable).map(|s| s.assets);
|
||||
assert_eq!(
|
||||
s.map_err(|e| e.to_string()),
|
||||
Ok(vec![ParseAssetsResult {
|
||||
kind: AssetKind::DataTable,
|
||||
path: "dt1/users".to_string(),
|
||||
access_type: Some(R),
|
||||
columns: None
|
||||
},])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sql_asset_parser_resource_with_long_path() {
|
||||
// Test that Resource works with paths longer than 3 components
|
||||
let input = r#"
|
||||
ATTACH 'res://u/diego/a/b/c/my_postgres_resource' AS db (TYPE postgres);
|
||||
USE db;
|
||||
SELECT * FROM my_table;
|
||||
"#;
|
||||
let s = parse_assets(input).map(|s| s.assets);
|
||||
assert_eq!(
|
||||
s.map_err(|e| e.to_string()),
|
||||
Ok(vec![ParseAssetsResult {
|
||||
kind: AssetKind::Resource,
|
||||
path: "u/diego/a/b/c/my_postgres_resource?table=my_table".to_string(),
|
||||
access_type: Some(R),
|
||||
columns: None
|
||||
},])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sql_asset_parser_table_with_schema() {
|
||||
let input = r#"
|
||||
|
||||
@@ -114,13 +114,14 @@ fn merge_access_types(a: AssetUsageAccessType, b: AssetUsageAccessType) -> Asset
|
||||
// But never used it. In that case we don't know which table is being used,
|
||||
// but we still want to add the main datatable as an asset with unknown access type.
|
||||
//
|
||||
// This function takes care of the fact that assets can be suffixed (e.g. "main/users")
|
||||
// This function takes care of the fact that assets can be suffixed (e.g. "main/users" or "u/user/resource?table=table1")
|
||||
pub fn asset_was_used(assets: &Vec<ParseAssetsResult>, (kind, path): (AssetKind, &String)) -> bool {
|
||||
assets.iter().any(|a| {
|
||||
let a_path = a.path.as_str();
|
||||
// Check for /table suffix (Ducklake, DataTable) or ?table= suffix (Resource)
|
||||
let has_same_path_base = a_path
|
||||
.strip_prefix(path)
|
||||
.map(|p| p.starts_with('/'))
|
||||
.map(|p| p.starts_with('/') || p.starts_with('?'))
|
||||
.unwrap_or(false);
|
||||
(has_same_path_base || a_path == path) && a.kind == kind
|
||||
})
|
||||
|
||||
@@ -184,7 +184,13 @@ async fn list_assets(
|
||||
FROM asset
|
||||
INNER JOIN asset_summary ON asset.path = asset_summary.path AND asset.kind = asset_summary.kind
|
||||
LEFT JOIN resource ON asset.kind = 'resource'
|
||||
AND array_to_string((string_to_array(asset.path, '/'))[1:3], '/') = resource.path
|
||||
AND (
|
||||
-- Extract base path before '?' for ?table= syntax
|
||||
CASE
|
||||
WHEN asset.path LIKE '%?%' THEN split_part(asset.path, '?', 1)
|
||||
ELSE asset.path
|
||||
END
|
||||
) = resource.path
|
||||
AND resource.workspace_id = $1
|
||||
LEFT JOIN v2_job job ON asset.usage_kind = 'job'
|
||||
AND asset.usage_path = job.id::text
|
||||
|
||||
Reference in New Issue
Block a user