Preface: I am not referring to queries.
There are numerous places in SSMS that allow for filtering, such as the Object Explorer and Profiler, but these all treat filters as case-sensitive with no visible option otherwise, so if you search for contains 'ASDF'
then values like "ASDF_MyEntity" are included but "asdf_MyEntity" are omitted.
For example, we have a lot of SQL Agent jobs on a large server, and I'm trying to use the Object Explorer to filter them by project name, which we always prefix the job names with. However, these appear as both uppercase and lowercase variants.
Another use case is searching for related stored procedure to a module among thousands of entries. If one is named inconsistently (e.g. PascalCase versus camelCase), the filtered search will omit it. This seems like quite an unnecessary hazard of phantoms for debugging.
Furthermore, the sorting in the Object Explorer places uppercase variants before lowercase (so uppercase Z
comes before lowercase a
) for every type of entity (e.g. table names, stored procedures, job names, etc.), so I either have to scroll around a lot, or check two different filters (or more if it's not all uppercase or all lowercase letters — 2len(name) times to be precise).
I realize I can just query jobs, tables, and other entities manually, but doing this is nonsensical given that SSMS exists, as its name is "SQL Server Management Studio."
Is there something I can do to make SSMS (and SQL Server Profiler) ignore case like every other Windows application? Perhaps I can change a collation setting locally?
Also, why was this decision made by Microsoft when implementing SSMS? I have found it to be only detrimental.
Furthermore, the loading splash screen for SSMS says "built on Visual Studio" which ignores case in Solution Explorer.
P.S. Using SSMS 2014 (version 12.0.5214.6). I cannot make any server-side changes, and my local development environment should match the target server environment for testing purposes.
Best Answer
It is technically possible to do this case-insensitive filtering, though only due to a bug in SSMS. I don't have SSMS 2014 (version 12.x), but I did get it working in SSMS 17.9.1 and 18.0 Preview 6, connected to an instance with a default collation of
Hebrew_100_BIN2
.Attempt Uno (no)
Seeing that the query uses
LIKE
, I figured I would try using single-character range wildcards (i.e.[
and]
). Passing in a pattern of[Tt][Ee][Ss][Tt]
did not work, however, due to certain characters getting escaped. It seems that[
,%
, and_
all get wrapped in[
and]
to turn them into literals (i.e. no longer having special meaning).Attempt Dos (Yes)
Seeing that the value used with the
LIKE
operator (i.e. the value entered into the UI) was concatenated into the query instead of passed in as a variable (i.e.tbl.name like N''%Test%''
instead oftbl.name like N''%'' + @_msparam_XX + N''%''
), I started thinking, "Isn't there a reason why it's dangerous to do this with user input? Hmm, I wonder ..." And so I passed in a'
and it was escaped into''
. Normally this is exactly what should happen with a single-quote. However, this is being used in Dynamic SQL, so it's nested one-level deeper than where''
is properly escaped. A string within a string needs a single-quote to be escaped as''''
, or double-escaped, because''
merely ends the string within a string.For example, when filtering on "Tables", the generated code (the one line that matters) is:
If I had passed in "Te'st", the generated code would have been:
and yes, it does get an "unclosed quotation mark" error.
I tried adding a
COLLATE
clause, but all collations have underscores, soLatin1_General_100_CI_AS
becameLatin1[_]General[_]100[_]CI[_]AS
. D'oh!We can't pass in either
[
or%
without those being wrapped in[
and]
, BUT, we can concatenate stuff onto theN'%
string, such as the output of a function. And, we can bypass the escaping of the[
and%
characters by passing them in as hex /VARBINARY
values, and thenCONVERT
them back intoNVARCHAR
once inside.So, when filtering on Stored Procedures, we are starting with normally generated code of:
In order to make this work, we need to pass in a string having the following structure (in place of "Test"):
We could use
--
at the end instead ofAND 'x' <> '
, but theAND
construct ensures that if there is additional parts of the query that following this part, and are on the same line, that it continues to function as expected.To generate our "escaped" filter, we can use the following:
Pasting that hex value into our SQL Injection work-around, we get:
If we set our filter to the value directly above, the generated code will be:
And the generated code is passed into
sp_executesql
. The statement that is executed shows up in Profiler as:This technique works because
0xYYYY....
is not something that the parser is looking for. It's only looking for the literal values of[
,_
, and%
, and we aren't passing those in. We are just passing in something that will be translated into those once the Dynamic SQL is executed.But it doesn't need to be a
VARBINARY
string that is passed in. Any function that passes back a string will work. You just need to concatenate the ending ofN'%
with something, followed by what comes just before%')
, hence:' + {something_ending_with_%'} AND 'x' <> '
. I could have used+ NCHAR(91) + N'Tt' + NCHAR(93) +
to represent[Tt]
, but that seemed a lot bulkier than a continuousVARBINARY
literal. However, if you just want a%
, then doing+ NCHAR(37) + N'
would be just fine. Remember to useNCHAR()
and prefix literals withN
since this is allNVARCHAR
data.Attempt Tres (Mucho Yes)
So, you don't want to have to create the
[Tt][Ee][Ss][Tt]
string each time, and convert it toVARBINARY
. I get that. Oh, and you have no access to add / change objects in SQL Server. Ok. But you do have access totempdb
, right? If you can create a function intempdb
, then this can be even easier. Just execute the following:Then set your filter to:
' AND tempdb.dbo.ObjectFilter(sp.name, N'Test') = 1 AND 'x' <> '
Generated code will be:
You will have to adjust the column alias since tables uses
tbl.name
instead ofsp.name
. But now you can pass in any string and it will compare using a case-insensitive collation!!And, this function will stick around until the instance is restarted. So you will only need to execute the above T-SQL every once in a while.
{ drops mic ... walks off stage } ?
Additional Notes:
Keep in mind that this technique works only because there is a bug in SSMS. If that bug is ever fixed, then this work-around will likely stop working. Please see the "UPDATE" section at the bottom for links to the bug report I filed about this, as well as the enhancement suggestion I filed requesting a "case-insensitive" check-box, per filterable property.
From Randi's answer:
There is no point in looking for the queries. They are coming from the DLLs that make up SSMS. I think C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Microsoft.SqlServer.Management.Sdk.Sfc.dll might be one of them, but not sure.
From Bob's comment on the question:
Well, it depends. There are two levels of collations — instance-level and database-level — and the one that matters depends on what meta-data someone is trying to filter. Instance-level collation affects filtering instance-level meta data (e.g. Logins, Linked Servers, etc) and meta data in system databases. Database-level collation affects filtering user database meta data (e.g. Users, Tables, etc). In a Contained / Partially-Contained database, filtering the database-level meta data will always use the collation:
Latin1_General_100_CI_AS_WS_KS_SC
.This separation of levels explains the following finding noted in Randi's answer:
From Elaskanator's comment on the question:
Just FYI: this is, unfortunately, a very common misconception, but No, Binary Collations are not Case-Sensitive
UPDATE
I have submitted the following feedback items to Microsoft: