August
17th 2009
dbo.InputBuffer (Function)

Posted under SQL

Background

Most people who use SQL Server regularly, know about the wonderful DBCC InputBuffer command. If you’re not a SQL aficionado, you can easily find out what other connections “are doing” when one of them starts causing problems. Checking the input buffer - how commands are received by the server - returns the SQL code that was most recently sent from that connection.

For example, if spid ( Server Process Identifier ) #72 is blocking other transactions, or chewing up an alarming amount of disc I/O ( especially if it’s an ad hoc query ), you can find out what it’s up to by calling:

DBCC InputBuffer (72)

Which returns something like this:

EventType Parameters EventInfo
Language Event 0 declare @spid int set @spid = 72 Declare @plan_handle binary(32), @code xml Select @plan_handle = plan_handle From sys.dm_exec_requests Where session_id = @spid; Select @code = query_plan From sys.dm_exec_query_plan(@plan_handle)

Beyond the Basics

This is a good start, but has some issues. Most importantly, the results simply aren’t available to T-SQL code. You can write an application to call DBCC externally - the Activity Monitor does this, giving you the SQL involved when you double click a process - but this limits the utility. For example, if you run sp_who2 and find a dozen questionable spids, you will have to check them individually.

On the other hand, a user-defined function ( UDF ) opens many doors. Logging comes immediately to mind, and will be the subject of a future post. A more straight-forward use for a SQL function is to simply ask what’s running on the server:

Select
	dbo.InputBuffer(spid) As RunningCode,
	*
From
	sys.sysProcesses
Where
	blocked != 0
	Or spid In (Select blocked From sys.sysProcesses)

Instead of simply getting a dump of the spid/kpid values involved, the CPU and I/O numbers, etc, this query returns at-a-glance what each connection is doing. The where clause in the query above will return only connections that are involved in a blocking chain - which is useful when debugging concurrency issues - but the concept applies no matter what you’re doing. You could rewrite the query “… Where dbo.InputBuffer(spid) Like ‘%My_Table_Name%‘ …” to see which transactions are trying to use a table, and what they’re doing with it.

The Code

The T-SQL batch below creates a function named dbo.InputBuffer to the database. This could of course be deployed to any schema; dbo is used for simplicy, as it exists by default in all databases.

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO

If Exists (Select * From sys.objects Where object_id = object_id(N'dbo.InputBuffer') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
	Drop Function dbo.InputBuffer
GO

Create Function dbo.InputBuffer(@spid int) Returns varChar(max) As Begin
	Declare @code varChar(max)

	Select
		@code = SubString(
			st.text,
			(er.statement_start_offset / 2)+1,
			((Case er.statement_end_offset When -1 Then DataLength(st.text) Else er.statement_end_offset End - er.statement_start_offset) / 2) + 1
		)
	From
		sys.dm_exec_requests As er Cross Apply
		sys.dm_exec_sql_text(er.sql_handle) As st
	Where
		er.session_id = @spid;

	Return @code
End

Caveat Emptor (sys.dm_exec_requests)

There is one important difference between the code presented here, and DBCC. Checking the input buffer through the database command console gives you the last command received on any open connection. On the other hand, sys.dm_exec_requests DMV only tracks queries that are currently executing. This is a subtle distinction, but can be a very important one.

If somebody opens a query window in SSMS or Query Analyzer, runs an update query, and then doesn’t close the window once the transaction commits, DBCC will return the update query, but the function I’m presenting will not.

Alternate Version

Often, it’s useful to see NULL returned as an indicator that a given connection is dormant, but, sometimes what’s needed is the behavior from DBCC. In those cases, the code below is a better fit.

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO

If Exists (Select * From sys.objects Where object_id = object_id(N'dbo.InputBuffer2') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
	Drop Function dbo.InputBuffer2
GO

Create Function dbo.InputBuffer2(@spid int) Returns varChar(max) As Begin
	Declare @code varChar(max)

	Select
		@code = st.text
	From
		sys.dm_exec_connections As ec Cross Apply
		sys.dm_exec_sql_text(ec.most_recent_sql_handle) As st
	Where
		ec.session_id = @spid;

	Return @code
End

You might instead want to use a nullable input parameter (@spid int, @exactBehavior bit = 1), or simply to deploy only whichever version of the code does what you want.

In the next episode, we’ll look at how to get the query plan for a transaction running against the server.

4 Responses to “dbo.InputBuffer (Function)”

  1. Joseph on 18 Aug 2009 at 11:01 pm #

    Very clever - I like it.

    Minor nitpick, though. You can’t capture the results of a dbcc call, like you said, but you can record the output of a stored procedure into a temp table. So if you have a sproc that calls dbcc for you, you can catch the results. Like this:

    if object_id(’tempdb.dbo.#inputbuffer’,'U’) is not null
    drop table #inputbuffer
    create table #inputbuffer
    (EventType varchar(20)
    , Parameter int
    , EventInfo varchar(255))

    insert #inputbuffer exec sp_executesql N’dbcc inputbuffer(@spid)’,N’@spid int’,@spid

    What this is doing is easy enough to understand. It still isn’t as nifty as what you’ve presented, because all it does is dump the code ( and metadata ) into a temp table. Your function returns a scalar value, which means that your function can be used in a select list to add a column to a result set or to filter. This code only shows how to grab the info.

    I borrowed the above code from: http://www.nyx.net/~bwunder/SQL2000/Scripts/admin/Procedures/dbo.ExpungeUsers.PRC.txt

  2. Software Development Blog » dbo.QueryPlan (Function) on 19 Aug 2009 at 6:25 pm #

    [...] last post described how to see what another connection to a SQL Server is doing, with a user-defined function, dbo.InputBuffer. Given a spid, the function returns the SQL text being run on that connection - or the last command [...]

  3. Forrest on 19 Aug 2009 at 7:08 pm #

    @Joseph - It’s true that you can write a stored procedure to call DBCC and then “pipe” the output into a temp table, as you’ve shown.

    But that can’t be wrapped into a function, because functions can’t write to temp ( or permanent ) tables. A UDF is desirable here for two reasons: you can see what a spid is doing when you query tables like sysProcesses, and also because putting a function call in the select list has that function run within the implicit transaction the select query uses; this guarantees that you’ll get consistent results. Calling DBCC immediately after querying sys.sysLockInfo opens up the possibility that one command will finish and a connection will send a new command to the database, in between the time you run the two queries.

    It all depends what you need to do…

  4. Joseph on 19 Aug 2009 at 8:50 pm #

    Gotcha.

    Why not use fn_get_sql() for that?

    http://msdn.microsoft.com/en-us/library/ms189451.aspx

    You need to have at least SQL Server 2000 SP-3 for that function to be available, and you need VIEW SERVER STATE permission. But you’re looking up the handle anyway, so you can piggyback off Microsoft. Actually I bet this is why.

    Important:
    This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature. Use sys.dm_exec_sql_text instead. For more information, see sys.dm_exec_sql_text (Transact-SQL).

Trackback URI | Comments RSS

Leave a Reply