Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/driver/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::collections::HashMap;
pub const Q_GET_TABLES: &str = "\
SELECT \
t.name, \
TRY_CONVERT(nvarchar(max), ep.value) AS comment \
CONVERT(nvarchar(max), ep.value) AS comment \
FROM sys.tables t \
JOIN sys.schemas s ON t.schema_id = s.schema_id \
LEFT JOIN sys.extended_properties ep \
Expand Down Expand Up @@ -57,7 +57,7 @@ SELECT \
AND i.is_primary_key = 1 \
), 0) AS BIT) AS is_pk, \
dc.definition AS default_value, \
TRY_CONVERT(nvarchar(max), ep.value) AS comment \
CONVERT(nvarchar(max), ep.value) AS comment \
FROM sys.columns c \
JOIN sys.types ty ON c.user_type_id = ty.user_type_id \
LEFT JOIN sys.default_constraints dc \
Expand Down Expand Up @@ -218,7 +218,7 @@ SELECT \
AND i.is_primary_key = 1 \
), 0) AS BIT) AS is_pk, \
dc.definition AS default_value, \
TRY_CONVERT(nvarchar(max), ep.value) AS comment \
CONVERT(nvarchar(max), ep.value) AS comment \
FROM sys.columns c \
JOIN sys.tables t ON c.object_id = t.object_id \
JOIN sys.schemas s ON t.schema_id = s.schema_id \
Expand Down
6 changes: 3 additions & 3 deletions src/driver/introspection/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn q_get_tables_queries_descriptions_with_object_scope() {
assert!(Q_GET_TABLES.contains("ep.major_id = t.object_id"));
assert!(Q_GET_TABLES.contains("ep.minor_id = 0"));
assert!(Q_GET_TABLES.contains("ep.name = N'MS_Description'"));
assert!(Q_GET_TABLES.contains("TRY_CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_TABLES.contains("CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_TABLES.contains("@P1"));
assert!(Q_GET_TABLES.contains("ORDER BY t.name"));
}
Expand All @@ -29,7 +29,7 @@ fn q_get_columns_joins_sys_types_and_reports_pk() {
assert!(Q_GET_COLUMNS.contains("ep.major_id = c.object_id"));
assert!(Q_GET_COLUMNS.contains("ep.minor_id = c.column_id"));
assert!(Q_GET_COLUMNS.contains("ep.name = N'MS_Description'"));
assert!(Q_GET_COLUMNS.contains("TRY_CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_COLUMNS.contains("CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_COLUMNS.contains("c.is_computed AS is_generated"));
assert!(Q_GET_COLUMNS.contains("OBJECT_ID(@P1)"));
assert!(Q_GET_COLUMNS.contains("ORDER BY c.column_id"));
Expand Down Expand Up @@ -167,7 +167,7 @@ fn q_get_all_columns_batch_groups_by_table() {
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("ep.major_id = c.object_id"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("ep.minor_id = c.column_id"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("ep.name = N'MS_Description'"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("TRY_CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("CONVERT(nvarchar(max), ep.value) AS comment"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("@P1"));
assert!(Q_GET_ALL_COLUMNS_BATCH.contains("ORDER BY t.name, c.column_id"));
// Must emit the table name so the caller can group rows.
Expand Down
2 changes: 1 addition & 1 deletion src/driver/triggers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SELECT tr.[name], tb.[name],
WHERE te.[object_id] = tr.[object_id]
ORDER BY te.[type_desc]
FOR XML PATH(N''), TYPE
).value(N'.', N'nvarchar(max)'), 1, 4, N''),
).value('.', 'nvarchar(max)'), 1, 4, N''),
CASE WHEN tr.[is_instead_of_trigger] = 1 THEN N'INSTEAD OF' ELSE N'AFTER' END,
sm.[definition]
FROM sys.triggers tr
Expand Down
5 changes: 5 additions & 0 deletions src/driver/triggers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ fn drop_trigger_uses_schema_qualified_identifier() {
"DROP TRIGGER [dbo].[audit_orders]"
);
}

#[test]
fn list_triggers_xml_value_uses_non_unicode_literals() {
assert!(LIST_TRIGGERS.contains(".value('.', 'nvarchar(max)')"));
}
55 changes: 55 additions & 0 deletions tests/live_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,61 @@ fn zero_row_select_preserves_column_headers() {
assert!(result_rows(&result).is_empty());
}

#[test]
fn get_triggers_lists_events_and_timing() {
let mut plugin = Plugin::with_scratch_database();
plugin.reset_table("trigger_target", "id INT NOT NULL PRIMARY KEY");
plugin.execute(format!(
"EXEC(N'CREATE TRIGGER [{TEST_SCHEMA}].[trg_target_audit] ON [{TEST_SCHEMA}].[trigger_target] \
AFTER INSERT, UPDATE AS SET NOCOUNT ON')"
));

let triggers = plugin.call_ok(
"get_triggers",
json!({ "params": connection_params(), "schema": TEST_SCHEMA }),
);
let trigger = triggers
.as_array()
.unwrap()
.iter()
.find(|t| t["name"] == "trg_target_audit")
.expect("trigger listed");
assert_eq!(trigger["table_name"], "trigger_target");
assert_eq!(trigger["event"], "INSERT OR UPDATE");
assert_eq!(trigger["timing"], "AFTER");

plugin.execute(format!("DROP TABLE [{TEST_SCHEMA}].[trigger_target]"));
}

#[test]
fn metadata_introspection_works_at_compatibility_level_100() {
let mut plugin = Plugin::spawn();
let master = connection_params_for("master", "ss003-master");
plugin.execute_with(
&master,
"IF DB_ID(N'tabularis_compat100') IS NULL EXEC(N'CREATE DATABASE [tabularis_compat100]'); \
ALTER DATABASE [tabularis_compat100] SET COMPATIBILITY_LEVEL = 100",
);
let params = connection_params_for("tabularis_compat100", "ss003-compat100");
plugin.execute_with(
&params,
"DROP TABLE IF EXISTS dbo.legacy; CREATE TABLE dbo.legacy (id INT NOT NULL PRIMARY KEY)",
);

for (method, extra) in [
("get_tables", json!({})),
("get_columns", json!({ "table": "legacy" })),
("get_all_columns_batch", json!({})),
] {
let mut request = json!({ "params": params, "schema": "dbo" });
request
.as_object_mut()
.unwrap()
.extend(extra.as_object().unwrap().clone());
plugin.call_ok(method, request);
}
}

#[test]
fn multi_statement_and_batch_rpc_preserve_result_sets_and_temp_table_session() {
let mut plugin = Plugin::with_scratch_database();
Expand Down
Loading