@@ -104,6 +104,92 @@ internal override string DebugDisplay() => this.arg is null
104104 : $ "{ ( this . isSidVariant ? "SUSER_SNAME" : "SUSER_NAME" ) } ({ this . arg . DebugDisplay ( ) } )";
105105}
106106
107+ /// <summary>
108+ /// SQL <c>SUSER_SID([login [, Param2]])</c>: returns the binary SID for a
109+ /// server login — the calling session's login with no argument. Mirrors the
110+ /// <c>sys.server_principals</c> sid surface: <c>sa</c> is the well-known
111+ /// single byte <c>0x01</c>, registry logins (<c>CREATE LOGIN</c>) get their
112+ /// deterministic 16-byte synthetic sid, and an unknown name returns NULL.
113+ /// The no-argument form returns <c>0x01</c>, matching the
114+ /// <c>sys.dm_exec_sessions.security_id</c> placeholder for the simulator's
115+ /// fixed session principal. The optional <c>Param2</c> (real's
116+ /// skip-name-validation flag) parses and is ignored. Result type is
117+ /// <c>varbinary(85)</c>.
118+ /// </summary>
119+ internal sealed class SUserSid : Expression
120+ {
121+ private readonly Expression ? loginArg ;
122+
123+ public SUserSid ( ParserContext context )
124+ {
125+ if ( context . Token is Tokens . Operator { Character : ')' } )
126+ return ;
127+ this . loginArg = Parse ( context ) ;
128+ if ( context . Token is Tokens . Operator { Character : ',' } )
129+ {
130+ context . MoveNextRequired ( ) ;
131+ _ = Parse ( context ) ;
132+ }
133+ if ( context . Token is not Tokens . Operator { Character : ')' } )
134+ throw SimulatedSqlException . SyntaxErrorNear ( context ) ;
135+ }
136+
137+ public override SqlValue Run ( RuntimeContext runtime )
138+ {
139+ if ( this . loginArg is null )
140+ return SqlValue . FromVarbinary ( [ 0x01 ] ) ;
141+ var nameValue = this . loginArg . Run ( runtime ) ;
142+ if ( nameValue . IsNull )
143+ return SqlValue . Null ( SqlType . Varbinary ) ;
144+ var name = nameValue . CoerceTo ( SqlType . SystemName ) . AsString ;
145+ return Collation . Baseline . Equals ( name , "sa" )
146+ ? SqlValue . FromVarbinary ( [ 0x01 ] )
147+ : runtime . Batch . Connection . Simulation . Logins . ContainsKey ( name )
148+ ? SqlValue . FromVarbinary ( BuiltInResources . DeriveLoginSid ( name ) )
149+ : SqlValue . Null ( SqlType . Varbinary ) ;
150+ }
151+
152+ public override SqlType GetSqlType ( BatchContext batch , Func < MultiPartName , SqlType > resolveColumnType ) => SqlType . Varbinary ;
153+
154+ internal override bool ResultIsNullable ( Func < MultiPartName , bool > resolveColumnNullable ) => true ;
155+
156+ internal override string DebugDisplay ( ) => this . loginArg is null ? "SUSER_SID()" : $ "SUSER_SID({ this . loginArg . DebugDisplay ( ) } )";
157+ }
158+
159+ /// <summary>
160+ /// SQL <c>SID_BINARY(name)</c>: resolves a Windows / Entra-ID principal
161+ /// name to its binary SID. Probe-confirmed against SQL Server 2025: it
162+ /// returns NULL even for existing SQL-auth logins (<c>sid_binary(N'sa')</c>
163+ /// is NULL) — it only resolves directory principals, which the simulator
164+ /// never hosts — so a constant NULL <c>varbinary(85)</c> is faithful for
165+ /// every input the simulator can see. The argument is still parsed and
166+ /// evaluated (one required argument). SSMS's Select-Top-1000
167+ /// server-properties batch calls it on the service's Windows group name.
168+ /// </summary>
169+ internal sealed class SidBinary : Expression
170+ {
171+ private readonly Expression arg ;
172+
173+ public SidBinary ( ParserContext context )
174+ {
175+ this . arg = Parse ( context ) ;
176+ if ( context . Token is not Tokens . Operator { Character : ')' } )
177+ throw SimulatedSqlException . SyntaxErrorNear ( context ) ;
178+ }
179+
180+ public override SqlValue Run ( RuntimeContext runtime )
181+ {
182+ _ = this . arg . Run ( runtime ) ;
183+ return SqlValue . Null ( SqlType . Varbinary ) ;
184+ }
185+
186+ public override SqlType GetSqlType ( BatchContext batch , Func < MultiPartName , SqlType > resolveColumnType ) => SqlType . Varbinary ;
187+
188+ internal override bool ResultIsNullable ( Func < MultiPartName , bool > resolveColumnNullable ) => true ;
189+
190+ internal override string DebugDisplay ( ) => $ "SID_BINARY({ this . arg . DebugDisplay ( ) } )";
191+ }
192+
107193/// <summary>
108194/// SQL <c>ORIGINAL_LOGIN()</c>: returns the original login of the session
109195/// before any <c>EXECUTE AS</c> impersonation. The simulator doesn't model
0 commit comments