Skip to content
Merged
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
2 changes: 2 additions & 0 deletions crates/iridium_core/src/executor/metadata/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ pub(crate) fn lookup(schema: &str, name: &str) -> Option<Box<dyn VirtualTable>>
Some(Box::new(constraints::SysDefaultConstraints))
} else if name.eq_ignore_ascii_case("server_principals") {
Some(Box::new(tables::SysServerPrincipals))
} else if name.eq_ignore_ascii_case("server_role_members") {
Some(Box::new(tables::SysServerRoleMembers))
} else if name.eq_ignore_ascii_case("availability_replicas") {
Some(Box::new(hadr::SysAvailabilityReplicas))
} else if name.eq_ignore_ascii_case("availability_groups") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) use identity_columns::SysIdentityColumns;
pub(crate) use objects_misc::{
SysAssemblyModules, SysDataSpaces, SysEdgeConstraints, SysExtendedProperties,
SysForeignKeyColumns, SysIndexColumns, SysInternalTables, SysPeriods, SysSequences,
SysServerPrincipals, SysSqlExpressionDependencies, SysSqlModules, SysAllSqlModules, SysStats, SysStatsColumns,
SysServerPrincipals, SysServerRoleMembers, SysSqlExpressionDependencies, SysSqlModules, SysAllSqlModules, SysStats, SysStatsColumns,
SysSynonyms, SysSystemSqlModules, SysTriggerEvents, SysTriggers, SysXmlIndexes,
SysXmlSchemaCollections,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) struct SysSystemSqlModules;
pub(crate) struct SysStats;
pub(crate) struct SysStatsColumns;
pub(crate) struct SysServerPrincipals;
pub(crate) struct SysServerRoleMembers;
pub(crate) struct SysTriggerEvents;

impl VirtualTable for SysDataSpaces {
Expand Down Expand Up @@ -638,17 +639,51 @@ impl VirtualTable for SysServerPrincipals {
.and_hms_opt(0, 0, 0)
.unwrap(),
);
vec![StoredRow {
values: vec![
Value::Int(1),
Value::VarChar("sa".to_string()),
Value::Char("S".to_string()),
Value::VarChar("SQL_LOGIN".to_string()),
Value::Bit(false),
created.clone(),
created.clone(),
Value::VarChar("master".to_string()),
vec![
StoredRow {
values: vec![
Value::Int(1),
Value::VarChar("sa".to_string()),
Value::Char("S".to_string()),
Value::VarChar("SQL_LOGIN".to_string()),
Value::Bit(false),
created.clone(),
created.clone(),
Value::VarChar("master".to_string()),
],
deleted: false,
},
StoredRow {
values: vec![
Value::Int(3),
Value::VarChar("sysadmin".to_string()),
Value::Char("R".to_string()),
Value::VarChar("SERVER_ROLE".to_string()),
Value::Bit(false),
created.clone(),
created.clone(),
Value::Null,
],
deleted: false,
},
]
}
}

impl VirtualTable for SysServerRoleMembers {
fn definition(&self) -> crate::catalog::TableDef {
virtual_table_def(
"server_role_members",
vec![
("role_principal_id", DataType::Int, false),
("member_principal_id", DataType::Int, false),
],
)
}

fn rows(&self, _catalog: &dyn Catalog, _ctx: &ExecutionContext) -> Vec<StoredRow> {
vec![StoredRow {
values: vec![Value::Int(3), Value::Int(1)],
deleted: false,
}]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ const SYSTEM_PROCEDURES: &[&str] = &[
"sp_helpdb",
"sp_server_info",
"sp_monitor",
"sp_helpuser",
"sp_helprole",
"sp_helprolemember",
"sp_helpsrvrole",
"sp_helpsrvrolemember",
"sp_helpfile",
"sp_helpfilegroup",
];

pub(crate) fn is_system_procedure(name: &str) -> bool {
Expand Down Expand Up @@ -65,6 +72,20 @@ pub(crate) fn execute_system_procedure(
execute_sp_server_info()?
} else if name.eq_ignore_ascii_case("sp_monitor") {
execute_sp_monitor(exec)?
} else if name.eq_ignore_ascii_case("sp_helpuser") {
execute_sp_helpuser(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helprole") {
execute_sp_helprole(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helprolemember") {
execute_sp_helprolemember(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helpsrvrole") {
execute_sp_helpsrvrole(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helpsrvrolemember") {
execute_sp_helpsrvrolemember(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helpfile") {
execute_sp_helpfile(exec, ctx)?
} else if name.eq_ignore_ascii_case("sp_helpfilegroup") {
execute_sp_helpfilegroup(exec, ctx)?
} else if name.eq_ignore_ascii_case("xp_instance_regread") {
// Stub for registry reads. If it has an output parameter, set it to a default.
for arg in &stmt.args {
Expand Down Expand Up @@ -806,3 +827,87 @@ fn execute_sp_tables(exec: &mut ScriptExecutor<'_>) -> Result<QueryResult, DbErr
..Default::default()
})
}

fn execute_sp_helpuser(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT p.name AS UserName, p.type_desc AS RoleName, '' AS LoginName, '' AS DefDBName, '' AS DefSchemaName, p.principal_id AS UserId, p.principal_id AS SID FROM sys.database_principals p";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helpuser query".into())),
}
}

fn execute_sp_helprole(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT name AS RoleName, principal_id AS RoleId, 0 AS IsAppRole FROM sys.database_principals WHERE type = 'R'";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helprole query".into())),
}
}

fn execute_sp_helprolemember(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT r.name AS DbRole, m.name AS MemberName, m.principal_id AS MemberSID FROM sys.database_role_members rm JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helprolemember query".into())),
}
}

fn execute_sp_helpsrvrole(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT name AS ServerRole, principal_id AS RoleId FROM sys.server_principals WHERE type = 'R'";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helpsrvrole query".into())),
}
}

fn execute_sp_helpsrvrolemember(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT r.name AS ServerRole, m.name AS MemberName, m.principal_id AS MemberSID FROM sys.server_role_members srm JOIN sys.server_principals r ON srm.role_principal_id = r.principal_id JOIN sys.server_principals m ON srm.member_principal_id = m.principal_id";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helpsrvrolemember query".into())),
}
}

fn execute_sp_helpfile(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT name, file_id, physical_name, type_desc AS usage, size FROM sys.database_files";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helpfile query".into())),
}
}

fn execute_sp_helpfilegroup(
exec: &mut ScriptExecutor<'_>,
ctx: &mut ExecutionContext<'_>,
) -> Result<QueryResult, DbError> {
let sql = "SELECT name, data_space_id AS groupid, type_desc AS groupname FROM sys.filegroups";
let batch = crate::parser::parse_batch(sql)?;
match exec.execute_batch(&batch, ctx)? {
crate::error::StmtOutcome::Ok(Some(res)) => Ok(res),
_ => Err(DbError::Execution("Failed to execute sp_helpfilegroup query".into())),
}
}
39 changes: 39 additions & 0 deletions crates/iridium_core/tests/sql_server_2025_parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,42 @@ fn test_like_escape_parity() {
assert_eq!(res.rows.len(), 1);
assert_eq!(res.rows[0][0].to_string_value(), "10%");
}

#[test]
fn test_new_system_procedures_2025_extended() {
let engine = Engine::new();

// sp_helpuser
let res = engine.query("EXEC sp_helpuser").unwrap();
assert_eq!(res.columns[0], "UserName");
assert!(res.rows.iter().any(|r| r[0].to_string_value() == "dbo"));

// sp_helprole
let res = engine.query("EXEC sp_helprole").unwrap();
assert_eq!(res.columns[0], "RoleName");
// Currently sys.database_principals has no roles by default, but let's check columns

// sp_helprolemember
let res = engine.query("EXEC sp_helprolemember").unwrap();
assert_eq!(res.columns[0], "DbRole");

// sp_helpsrvrole
let res = engine.query("EXEC sp_helpsrvrole").unwrap();
assert_eq!(res.columns[0], "ServerRole");
assert!(res.rows.iter().any(|r| r[0].to_string_value() == "sysadmin"));

// sp_helpsrvrolemember
let res = engine.query("EXEC sp_helpsrvrolemember").unwrap();
assert_eq!(res.columns[0], "ServerRole");
assert!(res.rows.iter().any(|r| r[0].to_string_value() == "sysadmin" && r[1].to_string_value() == "sa"));

// sp_helpfile
let res = engine.query("EXEC sp_helpfile").unwrap();
assert_eq!(res.columns[0], "name");
assert!(res.rows.iter().any(|r| r[0].to_string_value() == "iridium_sql"));

// sp_helpfilegroup
let res = engine.query("EXEC sp_helpfilegroup").unwrap();
assert_eq!(res.columns[0], "name");
assert!(res.rows.iter().any(|r| r[0].to_string_value() == "PRIMARY"));
}
10 changes: 5 additions & 5 deletions docs/sql-server-2025-implementation-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,19 +364,19 @@ This document tracks the implementation status of SQL Server 2025 features in Ir
| sp_helpdevice | ❌ Pending |
| sp_helpdownloadlist | ❌ Pending |
| sp_helpextendedproc | ❌ Pending |
| sp_helpfile | ❌ Pending |
| sp_helpfilegroup | ❌ Pending |
| sp_helpfile | ✅ Implemented |
| sp_helpfilegroup | ✅ Implemented |
| sp_helpgroup | ❌ Pending |
| sp_helpindex | ✅ Implemented |
| sp_helplinkedsrvlogin | ❌ Pending |
| sp_helplogins | ❌ Pending |
| sp_helpremotelogin | ❌ Pending |
| sp_helpremotelogin_90 | ❌ Pending |
| sp_helpserver | ❌ Pending |
| sp_helpsrvrole | ❌ Pending |
| sp_helpsrvrolemember | ❌ Pending |
| sp_helpsrvrole | ✅ Implemented |
| sp_helpsrvrolemember | ✅ Implemented |
| sp_helptext_jobstep | ❌ Pending |
| sp_helpuser | ❌ Pending |
| sp_helpuser | ✅ Implemented |
| sp_indexes | ❌ Pending |
| sp_kill_filestream_non_transacted_handles | ❌ Pending |
| sp_link_publication | ❌ Pending |
Expand Down
Loading