Skip to content

Commit 5375dda

Browse files
committed
Added HAS_DBACCESS, enabled method-level test parallelism for the SqlClient wire suite.
1 parent 0ff26f3 commit 5375dda

5 files changed

Lines changed: 75 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

SqlServerSimulator.Tests/DbIdNameTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,38 @@ public void DbName_NullArg_ReturnsNull()
6060
[TestMethod]
6161
public void DbId_DbName_RoundTrip()
6262
=> AreEqual("simulated", new Simulation().ExecuteScalar("select db_name(db_id())"));
63+
64+
[TestMethod]
65+
public void HasDbAccess_HostedDatabase_Returns1()
66+
=> AreEqual(1, new Simulation().ExecuteScalar("select has_dbaccess('master')"));
67+
68+
[TestMethod]
69+
public void HasDbAccess_CaseInsensitive()
70+
=> AreEqual(1, new Simulation().ExecuteScalar("select has_dbaccess('SiMuLaTeD')"));
71+
72+
// SSMS probes msdb at connect to decide whether to surface Agent
73+
// features; the simulator doesn't model msdb, so NULL — same as a real
74+
// server where the login can't see it.
75+
[TestMethod]
76+
public void HasDbAccess_UnknownDatabase_ReturnsNull()
77+
=> AreEqual(DBNull.Value, new Simulation().ExecuteScalar("select has_dbaccess('msdb')"));
78+
79+
[TestMethod]
80+
public void HasDbAccess_EmptyOrNullName_ReturnsNull()
81+
{
82+
var simulation = new Simulation();
83+
AreEqual(DBNull.Value, simulation.ExecuteScalar("select has_dbaccess('')"));
84+
AreEqual(DBNull.Value, simulation.ExecuteScalar("select has_dbaccess(null)"));
85+
}
86+
87+
[TestMethod]
88+
public void HasDbAccess_NoArgument_RaisesMsg174()
89+
{
90+
var ex = new Simulation().AssertSqlError("select has_dbaccess()", 174);
91+
AreEqual("The has_dbaccess function requires 1 argument(s).", ex.Message);
92+
}
93+
94+
[TestMethod]
95+
public void HasDbAccess_VariableArgument_Resolves()
96+
=> AreEqual(1, new Simulation().ExecuteScalar("declare @n sysname = 'master' select has_dbaccess(@n)"));
6397
}

SqlServerSimulator/Parser/Expression.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ private static Expression ResolveBuiltIn(string name, ParserContext context)
759759
"CONTEXT_INFO" => new ContextInfoFunction(context),
760760
"DATEDIFF_BIG" => new DateDiff.Big(context),
761761
"ERROR_NUMBER" => new ErrorNumberFunction(context),
762+
"HAS_DBACCESS" => new HasDbAccess(context),
762763
"PERCENT_RANK" => WindowExpression.ParsePercentRank(context),
763764
"ROWCOUNT_BIG" => new RowCountBig(context),
764765
"SWITCHOFFSET" => new SwitchOffset(context),

SqlServerSimulator/Parser/Expressions/DatabaseScalarFunctions.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,40 @@ public override SqlValue Run(RuntimeContext runtime)
120120

121121
internal override string DebugDisplay() => this.idArg is null ? "DB_NAME()" : $"DB_NAME({this.idArg.DebugDisplay()})";
122122
}
123+
124+
/// <summary>
125+
/// SQL <c>HAS_DBACCESS('name')</c>: 1 when the named database is hosted (the
126+
/// simulator has no per-login database-access model, so every hosted database
127+
/// is accessible), NULL for an unknown / empty / NULL name. Result type is
128+
/// <see cref="SqlType.Int32"/>. Probe-confirmed against SQL Server 2025
129+
/// (2026-07-15): 1 for every accessible database, name lookup is
130+
/// case-insensitive, and a missing argument raises Msg 174. SSMS calls
131+
/// <c>has_dbaccess('msdb')</c> at connect to decide whether to surface
132+
/// Agent features — msdb isn't modeled, so it gets NULL, same as a real
133+
/// server without msdb access.
134+
/// </summary>
135+
internal sealed class HasDbAccess : Expression
136+
{
137+
private readonly Expression nameArg;
138+
139+
public HasDbAccess(ParserContext context)
140+
{
141+
if (context.Token is Tokens.Operator { Character: ')' })
142+
throw SimulatedSqlException.FunctionRequiresNArguments("has_dbaccess", 1);
143+
this.nameArg = Parse(context);
144+
if (context.Token is not Tokens.Operator { Character: ')' })
145+
throw SimulatedSqlException.SyntaxErrorNear(context);
146+
}
147+
148+
public override SqlValue Run(RuntimeContext runtime)
149+
{
150+
var name = this.nameArg.Run(runtime);
151+
return !name.IsNull && runtime.Batch.Connection.Simulation.Databases.ContainsKey(name.CoerceTo(SqlType.NVarchar).AsString)
152+
? SqlValue.FromInt32(1)
153+
: SqlValue.Null(SqlType.Int32);
154+
}
155+
156+
public override SqlType GetSqlType(BatchContext batch, Func<MultiPartName, SqlType> resolveColumnType) => SqlType.Int32;
157+
158+
internal override string DebugDisplay() => $"HAS_DBACCESS({this.nameArg.DebugDisplay()})";
159+
}

docs/claude/schemas.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ Database ids: **master is always `database_id` 1, and user databases take 5, 6,
5959

6060
**`DB_ID([name])`** / **`DB_NAME([id])`** (`Parser/Expressions/DatabaseScalarFunctions.cs`): round-trip the connection's view of `Simulation.Databases` by name and id via `DbId.DatabasesWithIds` (master = 1, user databases from 5). Zero-arg `DB_ID()` returns the current database's id, zero-arg `DB_NAME()` returns its name. Unknown name / out-of-range id → NULL. NULL arg → NULL. Result types: `DB_ID``smallint`; `DB_NAME``sysname`.
6161

62+
**`HAS_DBACCESS('name')`** (same file): int 1 when the named database is hosted — the simulator has no per-login database-access model, so hosted ⇒ accessible — NULL for unknown / empty / NULL names (case-insensitive lookup; missing argument → Msg 174). Probe-confirmed against SQL Server 2025 (2026-07-15). SSMS calls `has_dbaccess('msdb')` at connect to gate Agent features; unmodeled msdb gets NULL, reading as "no access", which is the honest answer.
63+
6264
## Three-part-name reach for metadata scalars
6365

6466
Probe-confirmed against SQL Server 2025 (2026-05-23):

0 commit comments

Comments
 (0)