feat(xugu): expose programmable object metadata
This commit is contained in:
1 parent
d3887c9936
commit
08cd8903f4
36 files changed
+431
-62
No files matched your search
@@ -40,6 +40,7 @@ pub async fn list_objects(pool: &MySqlPool, database: &str) -> Result<Vec<Object
|
||||
name: table.name,
|
||||
object_type: "TABLE".to_string(),
|
||||
schema: Some(database.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: table.comment,
|
||||
created_at: None,
|
||||
@@ -100,6 +101,7 @@ fn plugin_object(
|
||||
name: name.to_string(),
|
||||
object_type: "FUNCTION".to_string(),
|
||||
schema: Some(database.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: if comment_parts.is_empty() { None } else { Some(comment_parts.join(", ")) },
|
||||
created_at: None,
|
||||
|
||||
@@ -2148,6 +2148,7 @@ fn row_to_object(row: &mysql_async::Row, database: &str) -> ObjectInfo {
|
||||
name: get_str_by_name(row, "object_name"),
|
||||
object_type: get_str_by_name(row, "object_type"),
|
||||
schema: Some(database.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: get_opt_str(row, "object_comment")
|
||||
.map(|s| fix_potential_double_encoding(&s))
|
||||
@@ -2283,6 +2284,7 @@ pub async fn list_table_objects_show(pool: &MySqlPool, database: &str) -> Result
|
||||
name: table.name,
|
||||
object_type: if table.table_type.eq_ignore_ascii_case("VIEW") { "VIEW" } else { "TABLE" }.to_string(),
|
||||
schema: Some(database.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: table.comment,
|
||||
created_at: meta.and_then(|meta| meta.created_at.clone()),
|
||||
|
||||
@@ -104,6 +104,7 @@ pub async fn list_objects(pool: &mysql_async::Pool, schema: &str) -> Result<Vec<
|
||||
name: get_str(row, 0),
|
||||
object_type: get_str(row, 1),
|
||||
schema: Some(schema.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
|
||||
@@ -2125,6 +2125,7 @@ pub async fn list_objects(pool: &Pool, schema: &str) -> Result<Vec<ObjectInfo>,
|
||||
name: pg_row_try_string(row, 0),
|
||||
object_type: pg_row_try_string(row, 1),
|
||||
schema: Some(schema.to_string()),
|
||||
valid: None,
|
||||
comment: row.try_get::<_, Option<String>>(2).ok().flatten().filter(|s| !s.is_empty()),
|
||||
created_at: row.try_get::<_, Option<String>>(3).ok().flatten().filter(|s| !s.is_empty()),
|
||||
updated_at: row.try_get::<_, Option<String>>(4).ok().flatten().filter(|s| !s.is_empty()),
|
||||
|
||||
@@ -12,6 +12,7 @@ pub async fn list_objects(pool: &Pool, schema: &str) -> Result<Vec<ObjectInfo>,
|
||||
name: t.name.clone(),
|
||||
object_type: t.table_type.clone(),
|
||||
schema: None,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: t.comment.clone(),
|
||||
created_at: None,
|
||||
|
||||
@@ -1330,6 +1330,7 @@ pub async fn list_objects(client: &mut SqlServerClient, schema: &str) -> Result<
|
||||
name: row.get::<&str, _>(0).unwrap_or("").to_string(),
|
||||
object_type: row.get::<&str, _>(1).unwrap_or("TABLE").to_string(),
|
||||
schema: Some(schema.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: row.get::<&str, _>(4).filter(|s: &&str| !s.is_empty()).map(|s: &str| s.to_string()),
|
||||
created_at: row.get::<chrono::NaiveDateTime, _>(2).map(|value| value.to_string()),
|
||||
|
||||
@@ -320,9 +320,12 @@ fn object_type_keyword(object_type: &ObjectSourceKind) -> &'static str {
|
||||
ObjectSourceKind::MaterializedView => "MATERIALIZED_VIEW",
|
||||
ObjectSourceKind::Procedure => "PROCEDURE",
|
||||
ObjectSourceKind::Function => "FUNCTION",
|
||||
ObjectSourceKind::Trigger => "TRIGGER",
|
||||
ObjectSourceKind::Sequence => "SEQUENCE",
|
||||
ObjectSourceKind::Package => "PACKAGE",
|
||||
ObjectSourceKind::PackageBody => "PACKAGE BODY",
|
||||
ObjectSourceKind::Type => "TYPE",
|
||||
ObjectSourceKind::TypeBody => "TYPE BODY",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,12 +822,18 @@ fn parse_object_source_kind(value: &str) -> Option<ObjectSourceKind> {
|
||||
Some(ObjectSourceKind::Procedure)
|
||||
} else if value.eq_ignore_ascii_case("FUNCTION") {
|
||||
Some(ObjectSourceKind::Function)
|
||||
} else if value.eq_ignore_ascii_case("TRIGGER") {
|
||||
Some(ObjectSourceKind::Trigger)
|
||||
} else if value.eq_ignore_ascii_case("SEQUENCE") {
|
||||
Some(ObjectSourceKind::Sequence)
|
||||
} else if value.eq_ignore_ascii_case("PACKAGE") {
|
||||
Some(ObjectSourceKind::Package)
|
||||
} else if value.eq_ignore_ascii_case("PACKAGE BODY") || value.eq_ignore_ascii_case("PACKAGE_BODY") {
|
||||
Some(ObjectSourceKind::PackageBody)
|
||||
} else if value.eq_ignore_ascii_case("TYPE") {
|
||||
Some(ObjectSourceKind::Type)
|
||||
} else if value.eq_ignore_ascii_case("TYPE BODY") || value.eq_ignore_ascii_case("TYPE_BODY") {
|
||||
Some(ObjectSourceKind::TypeBody)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1251,6 +1260,14 @@ mod tests {
|
||||
assert_eq!(sql, "CREATE OR REPLACE PACKAGE BODY PAYROLL AS\nEND PAYROLL;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_programmable_metadata_object_kinds() {
|
||||
assert_eq!(parse_object_source_kind("TRIGGER"), Some(ObjectSourceKind::Trigger));
|
||||
assert_eq!(parse_object_source_kind("TYPE"), Some(ObjectSourceKind::Type));
|
||||
assert_eq!(parse_object_source_kind("TYPE_BODY"), Some(ObjectSourceKind::TypeBody));
|
||||
assert_eq!(parse_object_source_kind("PACKAGE BODY"), Some(ObjectSourceKind::PackageBody));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn postgres_procedure_rename_adds_drop_cleanup() {
|
||||
let statements = build_executable_object_source_statements(input(
|
||||
|
||||
@@ -2219,6 +2219,7 @@ async fn external_driver_presto_like_objects(
|
||||
name: table.name,
|
||||
object_type: table.table_type,
|
||||
schema: Some(schema.to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: table.comment,
|
||||
created_at: None,
|
||||
@@ -2601,6 +2602,7 @@ mod tests {
|
||||
name: name.to_string(),
|
||||
object_type: object_type.to_string(),
|
||||
schema: Some("app".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -3318,6 +3320,7 @@ mod tests {
|
||||
name: "ORDERS".to_string(),
|
||||
object_type: "TABLE".to_string(),
|
||||
schema: Some("DBX_TEST".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -3329,6 +3332,7 @@ mod tests {
|
||||
name: "ORDERS_VIEW".to_string(),
|
||||
object_type: "VIEW".to_string(),
|
||||
schema: Some("DBX_TEST".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -3340,6 +3344,7 @@ mod tests {
|
||||
name: "REFRESH_ORDERS".to_string(),
|
||||
object_type: "PROCEDURE".to_string(),
|
||||
schema: Some("DBX_TEST".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -3763,6 +3768,7 @@ async fn list_objects_once(
|
||||
name: table.name,
|
||||
object_type: table.table_type,
|
||||
schema: None,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: table.comment,
|
||||
created_at: None,
|
||||
@@ -3940,6 +3946,7 @@ async fn list_objects_once(
|
||||
name: table.name,
|
||||
object_type: table.table_type,
|
||||
schema: if schema.is_empty() { None } else { Some(schema.to_string()) },
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: table.comment,
|
||||
created_at: None,
|
||||
@@ -4920,9 +4927,12 @@ fn sqlite_object_type(kind: &db::ObjectSourceKind) -> &'static str {
|
||||
db::ObjectSourceKind::View | db::ObjectSourceKind::MaterializedView => "view",
|
||||
db::ObjectSourceKind::Procedure
|
||||
| db::ObjectSourceKind::Function
|
||||
| db::ObjectSourceKind::Trigger
|
||||
| db::ObjectSourceKind::Sequence
|
||||
| db::ObjectSourceKind::Package
|
||||
| db::ObjectSourceKind::PackageBody => "routine",
|
||||
| db::ObjectSourceKind::PackageBody
|
||||
| db::ObjectSourceKind::Type
|
||||
| db::ObjectSourceKind::TypeBody => "routine",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4931,9 +4941,12 @@ fn sqlserver_object_type_filter(kind: &db::ObjectSourceKind) -> &'static str {
|
||||
db::ObjectSourceKind::View => "'V'",
|
||||
db::ObjectSourceKind::Procedure => "'P'",
|
||||
db::ObjectSourceKind::Function => "'FN','IF','TF','FS','FT'",
|
||||
db::ObjectSourceKind::Trigger => "'TR'",
|
||||
db::ObjectSourceKind::Sequence
|
||||
| db::ObjectSourceKind::Package
|
||||
| db::ObjectSourceKind::PackageBody
|
||||
| db::ObjectSourceKind::Type
|
||||
| db::ObjectSourceKind::TypeBody
|
||||
| db::ObjectSourceKind::MaterializedView => "''",
|
||||
}
|
||||
}
|
||||
@@ -5055,7 +5068,11 @@ fn postgres_object_source_sql_inner(
|
||||
sql_string(name)
|
||||
)
|
||||
}
|
||||
db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => "SELECT NULL WHERE FALSE".to_string(),
|
||||
db::ObjectSourceKind::Trigger
|
||||
| db::ObjectSourceKind::Package
|
||||
| db::ObjectSourceKind::PackageBody
|
||||
| db::ObjectSourceKind::Type
|
||||
| db::ObjectSourceKind::TypeBody => "SELECT NULL WHERE FALSE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5065,9 +5082,12 @@ pub fn oracle_object_source_sql(schema: &str, name: &str, kind: &db::ObjectSourc
|
||||
db::ObjectSourceKind::MaterializedView => "MATERIALIZED_VIEW",
|
||||
db::ObjectSourceKind::Procedure => "PROCEDURE",
|
||||
db::ObjectSourceKind::Function => "FUNCTION",
|
||||
db::ObjectSourceKind::Trigger => "TRIGGER",
|
||||
db::ObjectSourceKind::Sequence => "SEQUENCE",
|
||||
db::ObjectSourceKind::Package => "PACKAGE",
|
||||
db::ObjectSourceKind::PackageBody => "PACKAGE_BODY",
|
||||
db::ObjectSourceKind::Type => "TYPE",
|
||||
db::ObjectSourceKind::TypeBody => "TYPE_BODY",
|
||||
};
|
||||
if schema.trim().is_empty() {
|
||||
format!("SELECT DBMS_METADATA.GET_DDL({}, {}) FROM DUAL", sql_string(object_type), sql_string(name))
|
||||
@@ -5095,9 +5115,12 @@ pub fn mysql_object_source_sql(database: &str, name: &str, kind: &db::ObjectSour
|
||||
db::ObjectSourceKind::View => format!("SHOW CREATE VIEW {qualified_name}"),
|
||||
db::ObjectSourceKind::Procedure => format!("SHOW CREATE PROCEDURE {qualified_name}"),
|
||||
db::ObjectSourceKind::Function => format!("SHOW CREATE FUNCTION {qualified_name}"),
|
||||
db::ObjectSourceKind::Sequence
|
||||
db::ObjectSourceKind::Trigger
|
||||
| db::ObjectSourceKind::Sequence
|
||||
| db::ObjectSourceKind::Package
|
||||
| db::ObjectSourceKind::PackageBody
|
||||
| db::ObjectSourceKind::Type
|
||||
| db::ObjectSourceKind::TypeBody
|
||||
| db::ObjectSourceKind::MaterializedView => String::new(),
|
||||
}
|
||||
}
|
||||
@@ -5313,6 +5336,7 @@ async fn oracle_agent_list_objects(
|
||||
name,
|
||||
object_type,
|
||||
schema,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
|
||||
@@ -235,6 +235,7 @@ mod tests {
|
||||
name: "orders".to_string(),
|
||||
object_type: "BASE TABLE".to_string(),
|
||||
schema: None,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -246,6 +247,7 @@ mod tests {
|
||||
name: "active_orders".to_string(),
|
||||
object_type: "MATERIALIZED_VIEW".to_string(),
|
||||
schema: None,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -257,6 +259,7 @@ mod tests {
|
||||
name: "payroll".to_string(),
|
||||
object_type: "PACKAGE BODY".to_string(),
|
||||
schema: None,
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -308,6 +311,7 @@ mod tests {
|
||||
name: "ORDERS".to_string(),
|
||||
object_type: "TABLE".to_string(),
|
||||
schema: Some("HR".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
@@ -319,6 +323,7 @@ mod tests {
|
||||
name: "bin$deleted".to_string(),
|
||||
object_type: "TABLE".to_string(),
|
||||
schema: Some("HR".to_string()),
|
||||
valid: None,
|
||||
signature: None,
|
||||
comment: None,
|
||||
created_at: None,
|
||||
|
||||
@@ -4594,6 +4594,9 @@ where
|
||||
db::ObjectSourceKind::Sequence | db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => {
|
||||
object.source.clone()
|
||||
}
|
||||
db::ObjectSourceKind::Trigger | db::ObjectSourceKind::Type | db::ObjectSourceKind::TypeBody => {
|
||||
object.source.clone()
|
||||
}
|
||||
};
|
||||
let statements = build_executable_object_source_statements(EditableObjectSourceSqlInput {
|
||||
database_type: DatabaseType::Postgres,
|
||||
|
||||
@@ -62,6 +62,8 @@ pub struct ObjectInfo {
|
||||
pub object_type: String,
|
||||
pub schema: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub valid: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub signature: Option<String>,
|
||||
pub comment: Option<String>,
|
||||
pub created_at: Option<String>,
|
||||
@@ -93,9 +95,12 @@ pub enum ObjectSourceKind {
|
||||
MaterializedView,
|
||||
Procedure,
|
||||
Function,
|
||||
Trigger,
|
||||
Sequence,
|
||||
Package,
|
||||
PackageBody,
|
||||
Type,
|
||||
TypeBody,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -310,3 +315,18 @@ pub struct OwnerInfo {
|
||||
pub object_type: String,
|
||||
pub owner: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ObjectInfo;
|
||||
|
||||
#[test]
|
||||
fn list_objects_payload_preserves_optional_validity() {
|
||||
let objects: Vec<ObjectInfo> =
|
||||
serde_json::from_str(r#"[{"name":"TRG_AUDIT","object_type":"TRIGGER","schema":"APP","valid":false}]"#)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(objects[0].valid, Some(false));
|
||||
assert_eq!(objects[0].object_type, "TRIGGER");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user