parent
215b0cf7d4
commit
871fc57c52
|
|
@ -831,7 +831,7 @@ pub async fn list_indexes(client: &mut SqlServerClient, schema: &str, table: &st
|
|||
} else {
|
||||
Some(inc_str.split(',').map(|s| s.to_string()).collect())
|
||||
},
|
||||
comment: None,
|
||||
comment: row.get::<&str, _>(7).filter(|s: &&str| !s.is_empty()).map(|s: &str| s.to_string()),
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
|
|
@ -853,8 +853,10 @@ fn sqlserver_indexes_sql(schema: &str, table: &str) -> String {
|
|||
WHERE ic3.object_id = i.object_id AND ic3.index_id = i.index_id AND ic3.is_included_column = 1 \
|
||||
ORDER BY ic3.index_column_id \
|
||||
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '') AS included_cols, \
|
||||
i.filter_definition \
|
||||
i.filter_definition, \
|
||||
ep.value AS index_comment \
|
||||
FROM sys.indexes i \
|
||||
OUTER APPLY (SELECT CAST(ep.value AS NVARCHAR(MAX)) AS value FROM sys.extended_properties ep WHERE ep.major_id = i.object_id AND ep.minor_id = i.index_id AND ep.name = N'MS_Description' AND ep.class = 7) ep \
|
||||
WHERE i.object_id = OBJECT_ID('{s}.{t}') AND i.name IS NOT NULL \
|
||||
ORDER BY i.name",
|
||||
s = schema.replace('\'', "''"),
|
||||
|
|
@ -893,6 +895,29 @@ pub async fn list_foreign_keys(
|
|||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_table_comment(
|
||||
client: &mut SqlServerClient,
|
||||
schema: &str,
|
||||
table: &str,
|
||||
) -> Result<Option<String>, String> {
|
||||
let sql = sqlserver_table_comment_sql(schema, table);
|
||||
let stream = client.query(&*sql, &[]).await.map_err(|e| e.to_string())?;
|
||||
let rows = stream.into_first_result().await.map_err(|e| e.to_string())?;
|
||||
Ok(rows.first().and_then(|row| row.get::<&str, _>(0)).filter(|s| !s.is_empty()).map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
fn sqlserver_table_comment_sql(schema: &str, table: &str) -> String {
|
||||
let s = schema.replace('\'', "''");
|
||||
let t = table.replace('\'', "''");
|
||||
format!(
|
||||
"SELECT CAST(ep.value AS NVARCHAR(MAX)) \
|
||||
FROM sys.extended_properties ep \
|
||||
WHERE ep.major_id = OBJECT_ID(QUOTENAME('{s}') + '.' + QUOTENAME('{t}')) \
|
||||
AND ep.minor_id = 0 \
|
||||
AND ep.name = N'MS_Description'"
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn list_triggers(
|
||||
client: &mut SqlServerClient,
|
||||
schema: &str,
|
||||
|
|
@ -1084,7 +1109,7 @@ mod tests {
|
|||
use super::{
|
||||
build_spatial_safe_sqlserver_query, is_sqlserver_spatial_column, requires_simple_query_batch,
|
||||
sqlserver_cell_to_json, sqlserver_columns_sql, sqlserver_indexes_sql, sqlserver_list_objects_sql,
|
||||
SqlServerDescribedColumn, SqlServerResultSet,
|
||||
sqlserver_table_comment_sql, SqlServerDescribedColumn, SqlServerResultSet,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use std::time::Instant;
|
||||
|
|
@ -1160,6 +1185,26 @@ mod tests {
|
|||
assert!(sql.contains("OBJECT_ID('dbo.DF_Rule')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_indexes_sql_includes_index_comment_via_extended_properties() {
|
||||
let sql = sqlserver_indexes_sql("dbo", "orders");
|
||||
|
||||
assert!(sql.contains("sys.extended_properties ep"));
|
||||
assert!(sql.contains("ep.minor_id = i.index_id"));
|
||||
assert!(sql.contains("MS_Description"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_table_comment_sql_queries_extended_properties() {
|
||||
let sql = sqlserver_table_comment_sql("dbo", "users");
|
||||
|
||||
assert!(sql.contains("sys.extended_properties ep"));
|
||||
assert!(sql.contains("ep.minor_id = 0"));
|
||||
assert!(sql.contains("MS_Description"));
|
||||
assert!(sql.contains("QUOTENAME('dbo')"));
|
||||
assert!(sql.contains("QUOTENAME('users')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_metadata_sql_escapes_literals() {
|
||||
let columns_sql = sqlserver_columns_sql("d'bo", "t'able");
|
||||
|
|
|
|||
|
|
@ -1452,6 +1452,67 @@ mod ddl_tests {
|
|||
"SELECT pg_get_tabledef('\"tenant''s schema\".\"active users\"')"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_comment_ddl_includes_table_comment() {
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", Some("User accounts table"), &[], &[]);
|
||||
|
||||
assert!(ddl.contains("EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'User accounts table'"));
|
||||
assert!(ddl.contains("@level0type=N'SCHEMA', @level0name=N'dbo'"));
|
||||
assert!(ddl.contains("@level1type=N'TABLE', @level1name=N'users'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_comment_ddl_includes_column_comments() {
|
||||
let mut email_col = column("email", "nvarchar(255)");
|
||||
email_col.comment = Some("User's email address".to_string());
|
||||
let mut name_col = column("name", "nvarchar(100)");
|
||||
name_col.comment = Some("User's display name".to_string());
|
||||
let columns = vec![email_col, name_col];
|
||||
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", None, &columns, &[]);
|
||||
|
||||
assert!(ddl.contains("@level2type=N'COLUMN', @level2name=N'email'"));
|
||||
assert!(ddl.contains("@value=N'User''s email address'"));
|
||||
assert!(ddl.contains("@level2type=N'COLUMN', @level2name=N'name'"));
|
||||
assert!(ddl.contains("@value=N'User''s display name'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_comment_ddl_includes_index_comments() {
|
||||
let idx = db::IndexInfo {
|
||||
name: "IX_users_email".to_string(),
|
||||
columns: vec!["email".to_string()],
|
||||
is_unique: true,
|
||||
is_primary: false,
|
||||
filter: None,
|
||||
index_type: Some("NONCLUSTERED".to_string()),
|
||||
included_columns: None,
|
||||
comment: Some("Unique email index".to_string()),
|
||||
};
|
||||
let indexes = vec![idx];
|
||||
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", None, &[], &indexes);
|
||||
|
||||
assert!(ddl.contains("@level2type=N'INDEX', @level2name=N'IX_users_email'"));
|
||||
assert!(ddl.contains("@value=N'Unique email index'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_comment_ddl_skips_empty_comments() {
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", Some(""), &[], &[]);
|
||||
assert!(ddl.is_empty());
|
||||
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", None, &[], &[]);
|
||||
assert!(ddl.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_comment_ddl_escapes_single_quotes() {
|
||||
let ddl = render_sqlserver_comment_ddl("dbo", "users", Some("User's table for 'admin'"), &[], &[]);
|
||||
|
||||
assert!(ddl.contains("@value=N'User''s table for ''admin'''"));
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mysql_ddl(pool: &db::mysql::MySqlPool, table: &str) -> Result<String, String> {
|
||||
|
|
@ -1592,6 +1653,7 @@ pub async fn build_sqlserver_ddl(
|
|||
let columns = db::sqlserver::get_columns(client, schema, table).await?;
|
||||
let indexes = db::sqlserver::list_indexes(client, schema, table).await?;
|
||||
let fkeys = db::sqlserver::list_foreign_keys(client, schema, table).await?;
|
||||
let table_comment = db::sqlserver::get_table_comment(client, schema, table).await?;
|
||||
|
||||
let mut ddl = format!("CREATE TABLE [{schema}].[{table}] (\n");
|
||||
let col_lines: Vec<String> = columns
|
||||
|
|
@ -1643,5 +1705,61 @@ pub async fn build_sqlserver_ddl(
|
|||
idx.name
|
||||
));
|
||||
}
|
||||
|
||||
let comment_ddl = render_sqlserver_comment_ddl(schema, table, table_comment.as_deref(), &columns, &indexes);
|
||||
if !comment_ddl.is_empty() {
|
||||
ddl.push('\n');
|
||||
ddl.push_str(comment_ddl.trim_end());
|
||||
}
|
||||
|
||||
Ok(ddl)
|
||||
}
|
||||
|
||||
fn render_sqlserver_comment_ddl(
|
||||
schema: &str,
|
||||
table: &str,
|
||||
table_comment: Option<&str>,
|
||||
columns: &[db::ColumnInfo],
|
||||
indexes: &[db::IndexInfo],
|
||||
) -> String {
|
||||
let mut ddl = String::new();
|
||||
let escaped_schema = schema.replace('\'', "''");
|
||||
let escaped_table = table.replace('\'', "''");
|
||||
|
||||
if let Some(comment) = table_comment.filter(|s| !s.is_empty()) {
|
||||
let escaped_comment = comment.replace('\'', "''");
|
||||
ddl.push_str(&format!(
|
||||
"EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{escaped_comment}', \
|
||||
@level0type=N'SCHEMA', @level0name=N'{escaped_schema}', \
|
||||
@level1type=N'TABLE', @level1name=N'{escaped_table}';\n"
|
||||
));
|
||||
}
|
||||
|
||||
for col in columns {
|
||||
if let Some(comment) = col.comment.as_deref().filter(|s| !s.is_empty()) {
|
||||
let escaped_comment = comment.replace('\'', "''");
|
||||
let escaped_col = col.name.replace('\'', "''");
|
||||
ddl.push_str(&format!(
|
||||
"EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{escaped_comment}', \
|
||||
@level0type=N'SCHEMA', @level0name=N'{escaped_schema}', \
|
||||
@level1type=N'TABLE', @level1name=N'{escaped_table}', \
|
||||
@level2type=N'COLUMN', @level2name=N'{escaped_col}';\n"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for idx in indexes {
|
||||
if let Some(comment) = idx.comment.as_deref().filter(|s| !s.is_empty()) {
|
||||
let escaped_comment = comment.replace('\'', "''");
|
||||
let escaped_idx = idx.name.replace('\'', "''");
|
||||
ddl.push_str(&format!(
|
||||
"EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{escaped_comment}', \
|
||||
@level0type=N'SCHEMA', @level0name=N'{escaped_schema}', \
|
||||
@level1type=N'TABLE', @level1name=N'{escaped_table}', \
|
||||
@level2type=N'INDEX', @level2name=N'{escaped_idx}';\n"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
ddl
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue