Data with Bert logo

How to Search Stored Procedures and Ad-Hoc Queries

louis-blythe-192936-1-e1510402200998

Have you ever wanted to find something that was referenced in the body of a SQL query?

Maybe you need to know what queries you will have to modify for an upcoming table rename.  Or maybe you want to see how many queries on your server are running [SELECT *]{.lang:default .highlight:0 .decode:true .crayon-inline}

Below are two templates you can use to search across the text of SQL queries on your server.

Watch this week's video on YouTube

1. Searching Stored Procedures, Functions, and Views

If the queries you are interested in are part of a stored procedure, function, or view, then you have to look no further than the [sys.sql_modules]{.lang:default .highlight:0 .decode:true .crayon-inline} view.

This view stores the query text of every module in your database, along with a number of other properties.

You can use something like the following as a template for searching through the query texts of these database objects:

USE [<database name>];
GO

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SELECT
    o.type_desc AS ObjectType,
    DB_NAME(o.parent_object_id) AS DatabaseName,
    s.name as SchemaName,
    o.name as ObjectName,
    r.Definition
FROM
    sys.sql_modules r
    INNER JOIN sys.objects o
        ON r.object_id = o.object_id
    INNER JOIN sys.schemas s
        ON o.schema_id = s.schema_id
WHERE
    -- put your search keyword here
    r.Definition LIKE '%SELECT%'

For example, I recently built a query for searching stored procedures and functions that might contain SQL injection vulnerabilities.

Using the starting template above, I added some filtering in the WHERE clause to limit my search to queries that follow common coding patterns that are vulnerable to SQL injection:

USE [<database name>];
GO

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SELECT
    o.type_desc AS ObjectType,
    DB_NAME(o.parent_object_id) AS DatabaseName,
    s.name as SchemaName,
    o.name as ObjectName,
    r.Definition
FROM
    sys.sql_modules r
    INNER JOIN sys.objects o
        ON r.object_id = o.object_id
    INNER JOIN sys.schemas s
        ON o.schema_id = s.schema_id
WHERE
    -- Remove white space from query texts
    REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        r.Definition,CHAR(0),''),CHAR(9),''),CHAR(10),''),CHAR(11),''),
        CHAR(12),''),CHAR(13),''),CHAR(14),''),CHAR(160),''),' ','')
    LIKE '%+@%'
    AND 
    ( -- Only if executes a dynamic string
        r.Definition LIKE '%EXEC(%'
        OR r.Definition LIKE '%EXECUTE%'
        OR r.Definition LIKE '%sp_executesql%'
    )

2. Searching Ad-Hoc SQL Queries

Searching across ad-hoc queries is a little tougher.  Unless you are proactively logging the query texts with extended events or some other tool, there is no way to definitively search every ad-hoc query text.

However, SQL Server does create (or reuse) an execution plan for each query that executes.  Most of those plans are then added to the execution plan cache.

Execution plans are eventually removed from the cache for various reasons, but while they exist we can easily search their contents, including searching through that plan's query text.

As a starting point, you can use the following code to retrieve SQL query texts that are currently stored in the plan cache:

USE [<database name>];
GO

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')

SELECT
   stmt.value('(@StatementText)[1]', 'varchar(max)') AS [Query],
   query_plan AS [QueryPlan]
FROM 
    sys.dm_exec_cached_plans AS cp
    CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
    CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
WHERE
    -- put your search keywords here
    stmt.value('(@StatementText)[1]', 'varchar(max)') LIKE '%SELECT%'

Although the template above searches for the query texts in our execution plans, you can also use it to search for other query plan elements, such as elements that indicate if you have non-sargable query.

I used this technique recently to search for ad-hoc queries that might be vulnerable to SQL injection.  I modified the template above to search the input parameter values instead of the query texts, flagging any values that look like they might have some injection code in them:

USE [<database name>];
GO

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')

SELECT
   stmt.value('(@StatementText)[1]', 'varchar(max)') AS [Query],
   query_plan AS [QueryPlan],
   stmt.value('(.//ColumnReference/@ParameterCompiledValue)[1]', 'varchar(1000)') AS [ParameterValue] 
FROM 
    sys.dm_exec_cached_plans AS cp
    CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
    CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
WHERE
    -- if single quotes exist in a parameter
    stmt.value('(.//ColumnReference/@ParameterCompiledValue)[1]', 'varchar(1000)') like '%''%'
    OR stmt.value('(.//ColumnReference/@ParameterCompiledValue)[1]', 'varchar(1000)') like '%sys.objects%'
    OR stmt.value('(.//ColumnReference/@ParameterCompiledValue)[1]', 'varchar(1000)') like '%[0-9]=[0-9]%'

So while using this technique won't allow you to search across 100% of ad-hoc queries, it should be able to search the ones that run most frequently and appear in your plan cache.

Intrigued by how I'm searching query texts for SQL injection vulnerabilities? Attend my online webcast on Tuesday November 14, 2017 at 1PM Eastern at the PASS Security Virtual Group to learn about these queries and how protect yourself from SQL injection.

4 Common Misconceptions About SQL Injection Attacks

jaanus-jagomagi-377699 Photo by Jaanus Jagomägi on Unsplash

Interested in learning more about SQL injection attacks, including how to prevent them?  Attend my online webcast on Tuesday November 14, 2017 at 1PM Eastern at the PASS Security Virtual Group.

SQL injection continues to be one of the biggest security risks that we face as database professionals.

Every year, millions of users' personal information is leaked due to poorly written queries exploited by SQL injection.  The sad truth is that SQL injection is completely preventable with the right knowledge.

My goal today is to cover four common misconceptions that people have about SQL injection in an effort to dissuade any illusions that an injection attack is not something that can happen to you.

Watch this week's video on YouTube

1. "My database information isn't public"

Let's see, without me knowing anything about your databases, I'm guessing you might have some tables with names like:

  • Users
  • Inventory
  • Products
  • Sales
  • etc...

Any of those sound familiar?

You might not be publicly publishing your database object names, but that doesn't mean they aren't easy to guess.

All a malicious user needs is a list of common database table names and they can iterate over the ones they are interested in until they find the ones that match in your system.

2. "But I obfuscate all of my table and column names!"

obscure-table-name

Oh jeez.  I hope you don't do this.

Some people do this for job security ("since only I can understand my naming conventions, I'm guaranteeing myself a job!") and that's a terrible reason in and of itself.

Doing it for security reasons is just as horrible though.  Why?  Well, have you ever heard of some system tables like sys.objects and sys.columns?

SELECT 
    t.name, c.name 
FROM 
    sys.objects t
    INNER JOIN sys.columns c 
        on t.object_id = c.object_id

A hacker wanting to get into your system can easily write queries like the ones above, revealing your "secure" naming conventions.

sys-objects-results

Security through obscurity doesn't work.  If you have table names that aren't common, that's perfectly fine, but don't use that as your only form of prevention.

3. "Injection is the developer's/dba's/somebody else's problem"

You're exactly right.  SQL injection is a problem that should be tackled by the developer/dba/other person.

But it's also a problem that benefits from multiple layers of security, meaning it's your problem to solve as well.

Preventing sql injection is hard.

Developers should be validating, sanitizing, parameterizing etc...  DBAs should be parameterizing, sanitizing, restricting access, etc..

Multiple layers of security in the app and in the database are the only way to confidently prevent an injection attack.

4. "I'm too small of a fish in a big pond - no one would go out of their way to attack me"

So you run a niche business making and selling bespoke garden gnomes.

You only have a few dozen/hundred customers, so who would bother trying to steal your data with SQL injection?

Well, most SQL injection attacks can be completely automated with tools like sqlmap.  Someone might not care about your business enough to handcraft some SQL injection code, but that won't stop them from stealing your handcrafted garden gnome customers' data through automated means.

No app, big or small, is protected from the wrath of automated SQL injection tooling.

Interested in learning more about SQL injection attacks, including how to prevent them?  Attend my online webcast on Tuesday November 14, 2017 at 1PM Eastern at the PASS Security Virtual Group.

The Quickest Way To Get SQL Command Help

jp-valery-200305-3

Every once in a while I discover a SQL Server Management Studio trick that's apparently been around forever but is completely new to me.

Today I want to point out one of those features that had me thinking "how did I not know about this before":

The F1 keyboard shortcut.

Watch this week's video on YouTube

To use it, highlight a command or function that you want to know more information about and then press F1.  Simple as that.

Pressing F1 brings up the Microsoft online documentation for that keyword/function, making it the fastest way of getting to Microsoft's online documentation.  You'll solve your own questions faster than a coworker can tell you "to google it."

2017-10-26_18-47-05

Most recently I've been using the F1 shortcut in the following scenarios:

  • Can't remember the date/time style formats when using CONVERT?  Highlight CONVERT and press F1: BOOM! All date and time style codes appear before you.
  • Need to use some option for CREATE INDEX and don't remember the syntax?  Just highlight CREATE INDEX and press F1!  Everything you need is there.
  • Do you remember if BETWEEN is inclusive or exclusive?  F1 knows.  Just press it.

You get the idea.

Assuming you use the online Microsoft docs 10 times per day, 250 days a year, and each time it takes you 10 seconds to open a browser and search for the doc...

( 10/day * 250/year * 10 sec ) / 60 sec / 60 min = 6.94 hours saved.  Your welcome.

10 Questions To Spark Conversation At Your Next SQL Event

rawpixel-com-411167-1-1 Photo by rawpixel.com on Unsplash

Here's a word for word transcription of a conversation I've had a hundred times over:

"Hi I'm Bert.  What do you do?"

"I'm ____ and I'm a SQL developer."

"That's cool, me too."

*crickets*

*I look down at phone because I don't know what to talk about*

Sound familiar?

In the next few weeks, you might find yourself at a conference like PASS Summit or SQLintersection.  If not a conference, then maybe a local user group, meetup, or SQL Saturday.

Inevitably you will find yourself surrounded by strangers.  Strangers who you know share common interests with you (SQL, duh!).

But if you are like me, starting a meaningful conversation with those strangers can be uncomfortable.  Most people have interesting stories to share, the challenge is to get them to talk about them.

The good news is that I've developed an easy way to get a conversation started with the people you just met:

Come prepared with interesting open-ended questions.

Watch this week's video on YouTube

I keep a memorized list of open-ended questions that I can ask whenever I don't know how to keep the conversation going.  Try asking any of these questions the next time you don't know what to say (and reciprocate by sharing your own fun story); I guarantee these will spark some interesting conversations.

1. "What's your best SQL Server war story?"

We've all been in the trenches and have had to play the hero.

2. "What are your thoughts on EntityFramework/ORMs?"

If you ever want to get a table full of SQL DBAs going, this will do it.

3. "What's the oldest version of SQL Server you are still stuck supporting?"

Although this one elicits a one-word response, the next easy follow-up is "why/how!?"

4. "What was your biggest "oops" moment?"

Backups were corrupt?  Yeahhhhh....

5. "What's the most recent feature you started using in SQL Server 2014/2016/2017? How is it?"

I love hearing people's answers to this because it's a good way to figure out what new features really add value and which ones are over-hyped/limited in functionality.

6. "Are you using ?  How is it?"

Similar to #5, this is a great way to get real-world feedback about certain features.

7. "What's your favorite session from today/this week?  What did you like most about it?"

I love finding out what sessions other people found useful - once again, real world reviews on what I should check out in the future.

8. "Have you been to before? Do you have any recommendations for what I should do/see/eat?"

Great way to get to know the surrounding area without having to read reviews online.

9. "Do you use PowerShell or any other software to automate/do dev ops?"

PowerShell is the future.  Start learning how others are incorporating it into their environments, what struggles they've had implementing automated processes, etc...

10. "Are there any other events going on tonight?"

Especially great if talking to people who have attended the event before.  Find out what's worth going to, if it's better to show up early or late, is there a "best seat" in the house, etc...

I hope this list of questions encourages you to become better acquainted with your fellow conference goers.  And if I see you at PASS Summit...don't be surprised if you hear me ask you one of these questions!

How to Make SQL Server Act Like A Human By Using WAITFOR

fischer-twins-396836 Photo by Fischer Twins on Unsplash

You probably tune your queries for maximum performance.  You take pride in knowing how to add indexes and refactor code in order to squeeze out every last drop your server's performance potential.  Speed is usually king.

That's why you probably don't use SQL Server's WAITFOR command regularly - it actually makes your overall query run slower.

However, slowness isn't always a bad thing.  Today I want to show you two of my favorite ways for using the WAITFOR command.

Watch this week's video on YouTube

You can also watch this week's content on my YouTube channel.

1. Building A Human

Modern day computers are fast.  CPUs perform billions of actions per second, the amount of RAM manufactures can cram onto a stick increases regularly, and SSDs are quickly making disk I/O concerns a thing of the past.

While all of those things are great for processing large workloads, they move computers further and further away from "human speed".

But "human speed" is sometimes what you want.  Maybe you want to simulate app usage on your database or the load created by analysts running ad hoc queries against your server.

This is where I love using WAITFOR DELAY - it can simulate humans executing queries extremely welll:

-- Run forever
WHILE (1=1)
BEGIN
    --Insert data to simulate an app action from our app
    EXEC dbo.BuyDonuts 12

    -- We usually average an order every 3 seconds
    WAITFOR DELAY '00:00:03'
END

Throw in some psuedo-random number generation and some IF statements, and you have a fake server load you can start using:

WHILE (1=1)
BEGIN
    -- Generate command values 1-24
    DECLARE @RandomDonutAmount int = ABS(CHECKSUM(NEWID()) % 25) + 1

    -- Generate a delay between 0 and 5 seconds
    DECLARE @RandomDelay int = ABS(CHECKSUM(NEWID()) % 6)

    EXEC dbo.BuyDonuts @RandomDonutAmount

    WAITFOR DELAY @RandomDelay
END

2. Poor Man's Service Broker

Service Broker is a great feature in SQL Server.  It handles messaging and queuing scenarios really well, but requires more setup time so I usually don't like using it in scenarios where I need something quick and dirty.

Instead of having to set up Service Broker to know when some data is available or a process is ready to be kicked off, I can do the same with a WHILE loop and a WAITFOR:

DECLARE @Quantity smallint = NULL

-- Keep checking our table data until we find the data we want
WHILE (@Quantity IS NULL)
BEGIN
    -- Delay each iteration of our loop for 10 seconds
    WAITFOR DELAY '00:00:03'

    -- Check to see if someone has bought a dozen donuts yet
    SELECT @Quantity = Quantity FROM dbo.Orders WHERE Quantity = 12
END

-- Run some other query now that our dozen donut purchase condition has been met
EXEC dbo.GenerateCoupon

Fancy? No.  Practical? Yes.

No longer do I need to keep checking a table for results before I run a query - I can have WAITFOR do that for me.

If you know there is a specific time you want to wait for until you start pinging some process, you can incorporate WAITFOR TIME to make your checking even more intelligent:

DECLARE @Quantity smallint = NULL

-- Let's not start checking until 6am...that's when the donut shop opens
WAITFOR TIME '06:00:00'
-- Keep checking our table data until we find the data we want
WHILE (@Quantity IS NULL)
BEGIN
    -- Delay each iteration of our loop for 10 seconds
    WAITFOR DELAY '00:00:03'

    -- Check to see if someone has bought a dozen donuts yet
    SELECT @Quantity = Quantity FROM dbo.Orders WHERE Quantity = 12
END

-- Run some other query now that our dozen donut purchase condition has been met
EXEC dbo.GenerateCoupon