Error Handling - Determine whether sp_executesql was the source - sql-server

When an error message is returned by sp_executesql, is there any built-in error handling method/mechanism that can be used to identify that this error was returned by this procedure vs. directly from the containing script?
In the case of the below, the error details identify the error as occurring on line 1 but they don't indicate that the line 1 being referred to is not line 1 from the script itself but rather line 1 in the query passed by the script to sp_executesql.
I'm looking for some way to identify the source so that I can log it accordingly. I'd like to log something like Script x - Call to inner query errored on that query's line 1 instead of the generic (and misleading) Script x errored on line 1.
Demo
-- do other things first
BEGIN TRY
EXECUTE sp_executesql #stmt = N'SELECT 1/0';
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage
END CATCH;
Returns:
ErrorNumber ErrorSeverity ErrorState ErrorProcedure
----------- ------------- ----------- ---------------
8134 16 1 NULL

Unfortunately, the call stack isn't available with T-SQL error handling. Consider upvoting this feature request to facilitate capturing T-SQL stack details.
The example below uses a nested TRY/CATCH to raise a user-defined error (message number 50000) when the inner script errors, capturing the available details along with context description ("inner script"). When errors occur in the outer script, the original error is simply re-thrown. Absence of a context and a system error number indicates the outermost script erred, although you could build and raise a user-defined error there instead, including the outer script context description.
BEGIN TRY
BEGIN TRY
EXECUTE sp_executesql #stmt = N'SELECT 1/0;';
END TRY
BEGIN CATCH
DECLARE
#ErrorNumber int
,#ErrorMessage nvarchar(2048)
,#ErrorSeverity int
,#ErrorState int
,#ErrorLine int;
SELECT
#ErrorNumber =ERROR_NUMBER()
,#ErrorMessage =ERROR_MESSAGE()
,#ErrorSeverity = ERROR_SEVERITY()
,#ErrorState =ERROR_STATE()
,#ErrorLine =ERROR_LINE();
RAISERROR('Error %d caught in inner script at line %d: %s'
,#ErrorSeverity
,#ErrorState
,#ErrorNumber
,#ErrorLine
,#ErrorMessage);
END CATCH;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO

you can use sp_executeSQL's OUTPUT parameter:
DECLARE #ErrorLine NVARCHAR(32)
DECLARE #Params NVARCHAR(150) = '#Return INT OUTPUT'
DECLARE #SQL NVARCHAR(MAX) = ''
SET #SQL = #SQL + ' '
SET #SQL = #SQL + ' BEGIN TRY '
SET #SQL = #SQL + ' SELECT 100 AS First '
SET #SQL = #SQL + ' SELECT 1/0 AS Second '
SET #SQL = #SQL + ' END TRY '
SET #SQL = #SQL + ' BEGIN CATCH '
SET #SQL = #SQL + ' SELECT #Return = ERROR_LINE() '
SET #SQL = #SQL + ' END CATCH '
SET #SQL = #SQL + ' '
EXEC sp_executeSQL #SQL, #Params, #Return = #ErrorLine OUTPUT
SELECT #ErrorLine
this code will show #ErrorLine = 1 regardless of where the error is because technically, the entire SQL is on a single line and it makes the whole thing more complex, but you get the idea...
EDIT: if #ErrorLine is NULL, there's no error in sp_executeSQL.

Here is the solution that returns the line number. We are in a SPROC that takes parameters. Essentially, as a tSQL developer, you will need to guess where issues will occur, normally around arguments to formal parameter inputs.
-- Preamble
CREATE PROCEDURE [Meta].[ValidateTable]
#DatabaseNameInput VARCHAR(100), -- = 'DatabaseNameInput',
#TableNameInput VARCHAR(100), -- = 'TableNameInput',
#SchemaNameInput VARCHAR(100), -- = 'SchemaNameInput',
AS
BEGIN
DECLARE #crlf CHAR(2) = CHAR(13) + CHAR(10),
-----------Database Validity------------------
#IsDatabaseValid BIT,
#DatabaseNumber INTEGER,
#DatabaseNamePredicate NVARCHAR(100),
#CurrentExecutingContext NVARCHAR(40),
#DatabaseValidityExecutable NVARCHAR(100),
#DatabaseParameterString NVARCHAR(50),
-----------Table Validity------------------
#TableObjectIdentity INTEGER,
#TableString NVARCHAR(500),
#TableParameterString NVARCHAR(50),
#TableValidityExecutable NVARCHAR(200),
-----------Error Handling------------------
#ErrorState INTEGER = 0,
#ErrorNumber INTEGER = 0,
#ErrorSeverity INTEGER = 0,
#MyErrorMessage NVARCHAR(150),
#SetMessageText NVARCHAR(1024) = 'No Error Message Text for sys.messages.',
#ErrorDescription NVARCHAR(1024) = 'No error description was given.';
-- Be aware of SQL Injection Risk with no semi-colons at the line tails
SET #TableString = 'N' + '''' + #DatabaseNameInput + '.' + #SchemaNameInput + '.' + #TableNameInput + '''';
SET #DatabaseParameterString = N'#DatabaseNumber INTEGER OUTPUT ';
SET #TableParameterString = N'#TableObjectIdentity INTEGER OUTPUT';
-- Phase 0.0, testing for database existence.
PRINT 'Table Validity Executable: ' + #TableValidityExecutable;
EXECUTE sp_executesql #DatabaseValidityExecutable, #DatabaseParameterString, #DatabaseNumber = #DatabaseNumber OUTPUT;
IF #DatabaseNumber IS NULL
BEGIN
SET #MyErrorMessage = 'The #DatabaseNameInput parameter: "%s" specified by the caller does not exist on this SQL Server - ' + ##SERVERNAME;
EXECUTE sys.sp_addmessage #msgnum = 59802, #severity = 16, #msgtext = #MyErrorMessage, #replace = 'replace', #lang = 'us_english';
RAISERROR(59802, 15, 1, #DatabaseNamePredicate);
END;
-- Phase 0.1, testing for table existence.
PRINT 'Table Validity Executable: ' + #TableValidityExecutable;
EXECUTE sp_executesql #TableValidityExecutable, #TableParameterString, #TableObjectIdentity = #TableObjectIdentity OUTPUT;
IF #TableObjectIdentity IS NULL
BEGIN
SET #MyErrorMessage = 'The #TableNameInput parameter: "%s" specified by the caller does not exist in this database - ' + DB_NAME() +';';
EXECUTE sys.sp_addmessage #msgnum = 59803, #severity = 16, #msgtext = #MyErrorMessage, #replace = 'replace', #lang = 'us_english';
RAISERROR(59803, 15, 1, #TableString);
END;

Related

RaiseError not returning to caller as expected

I am using SQL Server 2106.
I have a test stored procedure with a just TRY CATCH that raises an error within it using RAISERROR.
It goes to the CATCH where I attempt to log the error with a call to a logging stored procedure before returning to the client caller. It too is encased in a TRY CATCH. I purposely incorrectly named an input argument to the logging stored procedure so as to male it fail.
As expected the call to the logging store procedure fails and the code drops into the CATCH. There
I do another RAISERROR expecting to then return immediately to the caller.
However, it does not and drops to the next command which is another RAISERROR. This one does a RAISERROR to send back to the caller the initial error (when the logging does not fail).
Why is it it dropping threw AFTER the first logging CATCH error was caught? It should return to the caller at this point.
Stored proc:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[TestRaiseErrorModified]
AS
BEGIN
DECLARE #CurrentDateTime DATETIME,
#Message VARCHAR(MAX) = ''
SELECT #CurrentDateTime = GETDATE()
-- For the CATCH.
DECLARE #ErrorLine INT;
DECLARE #ErrorMessage VARCHAR(2048);
DECLARE #ErrorNumber INT;
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
DECLARE #DatabaseName VARCHAR(255);
DECLARE #ServerName VARCHAR(255);
DECLARE #ErrorDescription VARCHAR(MAX);
DECLARE #CRLF VARCHAR(2);
BEGIN TRY
SET NOCOUNT ON;
SELECT #Message = 'Critical Error - procedure TestRaiseErrorModified during the
select.'
RAISERROR (#Message, 16, 1)
-- Returns success.
RETURN 0
END TRY
BEGIN CATCH
-- Gather these first.
SELECT
#ErrorLine = ERROR_LINE()
-- ERROR_MESSAGE() contains the RAISERROR message raised above.
, #ErrorMessage = ERROR_MESSAGE()
, #ErrorNumber = ERROR_NUMBER()
, #ErrorSeverity = ERROR_SEVERITY()
, #ErrorState = ERROR_STATE()
, #DatabaseName = CAST(DB_NAME() AS VARCHAR)
, #ServerName = CAST(SERVERPROPERTY ( 'ServerName' ) AS VARCHAR)
, #CRLF = CHAR(13) + CHAR(10)
-- Build for a critical error type message.
SET #ErrorDescription = 'From stored procedure: ' + ERROR_PROCEDURE()
+ '. Error Line: ' + CAST(#ErrorLine AS VARCHAR)
+ '. Error Message: ' + #ErrorMessage
+ ' Error Number: ' + CAST(#ErrorNumber AS VARCHAR)
+ '. Error Severity: ' + CAST(#ErrorSeverity AS
VARCHAR)
+ '. Error State: ' + CAST(#ErrorState AS VARCHAR)
+ '. Database Name: ' + #DatabaseName
+ '. Server Name: ' + #ServerName
IF (XACT_STATE() <> 0)
BEGIN
ROLLBACK TRANSACTION
END
IF (#ErrorSeverity = 16) AND (#ErrorState = 2)
BEGIN
-- Return the validation error to display to the User.
RAISERROR(#ErrorMessage, #ErrorSeverity, #ErrorState)
END
ELSE
BEGIN
-- Log the critical error.
BEGIN TRY
EXEC dbo.InsertBlogErrorLog
#a_LogDateTime = #CurrentDateTime,
#a_UserName = 'Admin',
#a_UserIpAddress = '32.211.50.62',
#a_ObjectID = ##PROCID,
#a_MessagexxxType = 'S/P Critical Error',
#a_LogMessage = #ErrorDescription
END TRY
BEGIN CATCH
-- Stack the messages. Return the logging error to display to the User.
SELECT #Message = 'Critical Error - procedure InsertBlogErrorLog failed. A
log entry CANNOT be made. Do not continue. Contact the Admin. Initial error
message: ' + #ErrorMessage
RAISERROR(#Message, 16, 1)
END CATCH
-- Why is it it dropping threw AFTER the first logging CATCH error was caught?
-- It should return to the caller allready.
-- So now it will cause 2 messages to be returned. When only 1 should be
-- returned.
-- Return the original error as the logging was successful.
-- This message will be passed back to display to the User.
SELECT #Message = 'Critical Error - do not continue. Contact the Admin and
provide this log date: ' + Convert(VARCHAR, #CurrentDateTime,21)
RAISERROR(#Message, 16, 1)
END
-- Returns failure.
RETURN 1
END CATCH
END

Must declare the scalar variable error for stored procedure in nopcommerce 4.3

I want to search GTIN option in admin product list. So, for that I am providing GTIN value in ProductLoadAllPaged store procedure. Now, when I search GTIN value from product list at that time throw datatable error and from console application get message that System.Data.SqlClient.SqlException (0x80131904): Must declare the scalar variable "#GTIN"..
Here is store procedure added code,
ALTER PROCEDURE [dbo].[ProductLoadAllPaged]
(
#GTIN nvarchar(50) = null--AWAZ
)
AS
BEGIN
...........
--SKU (exact match)
IF #SearchSku = 1
BEGIN
SET #sql = #sql + 'OR p.[Sku] = #OriginalKeywords '
END
--NEW ADDED CODE FOR GTIN SEARCH
IF #GTIN is not null
BEGIN
SET #sql = #sql + 'AND p.Gtin = #GTIN'
END
--localized product name
SET #sql = #sql + '
UNION
SELECT lp.EntityId
FROM LocalizedProperty lp with (NOLOCK)
WHERE
lp.LocaleKeyGroup = N''Product''
AND lp.LanguageId = ' + ISNULL(CAST(#LanguageId AS nvarchar(max)), '0') + '
AND ( (lp.LocaleKey = N''Name'''
..........
END
In the stored procedure change this line of code:
EXEC sp_executesql #sql, N'#Keywords nvarchar(4000), #OriginalKeywords nvarchar(4000)', #Keywords, #OriginalKeywords
To this:
EXEC sp_executesql #sql, N'#Keywords nvarchar(4000), #OriginalKeywords nvarchar(4000), #GTIN nvarchar(50)', #Keywords, #OriginalKeywords, #GTIN
This will add your new parameter when executing the dynamic SQL query.

Prevent print from generating a warning in TSQL

-- EDIT --
I have answered my question but the solution is
more of a work-around. If someone can provide a
real answer I will award the solution to them.
----
I have searched around for a solution to prevent MS SQL from generating a warning when using print statements in a stored procedure without success.
I am executing this procedure from our ETL application and I need the stored procedure to exit without errors or warnings.
FYI - The print statements are not needed when the stored procedure is executed by the ETL process. Those are there for when it is executed manually.
I have tried SET ANSI WARNINGS OFF and Try/Catch but I am still getting warnings on the output.
This is the stored procedure:
CREATE PROCEDURE [dbo].[spBackupSomeTables]
AS
BEGIN TRY
SET ANSI_WARNINGS OFF
PRINT 'Copying SomeTable1 to Z_Backup_SomeTable1' + CHAR(10)
SET ANSI_WARNINGS ON
TRUNCATE TABLE Z_Backup_SomeTable1
INSERT INTO Z_Backup_SomeTable1
SELECT *
FROM SomeTable1
SET ANSI_WARNINGS OFF
PRINT 'Copying SomeTable2 to Z_Backup_SomeTable2' + CHAR(10)
SET ANSI_WARNINGS ON
TRUNCATE TABLE Z_Backup_SomeTable2
INSERT INTO Z_Backup_SomeTable2
SELECT *
FROM SomeTable2
SET ANSI_WARNINGS OFF
PRINT 'All Done' + CHAR(10)
SET ANSI_WARNINGS ON
END TRY
BEGIN CATCH
DECLARE
#ErrorNumber VARCHAR(1000),
#ErrorSeverity VARCHAR(1000),
#ErrorState VARCHAR(1000),
#ErrorProcedure VARCHAR(1000),
#ErrorLine VARCHAR(1000),
#ErrorMessage VARCHAR(1000)
SET #ErrorNumber = (SELECT ERROR_NUMBER() AS ErrorNumber)
SET #ErrorSeverity = (SELECT ERROR_SEVERITY() AS ErrorSeverity)
SET #ErrorState = (SELECT ERROR_STATE() AS ErrorState)
SET #ErrorProcedure = (SELECT ERROR_PROCEDURE() AS ErrorProcedure)
SET #ErrorLine = (SELECT ERROR_LINE() AS ErrorLine)
SET #ErrorMessage = (SELECT ERROR_MESSAGE() AS ErrorMessage)
PRINT 'ErrorNumber: ' + #ErrorNumber + CHAR(10)
PRINT 'ErrorSeverity: ' + #ErrorSeverity + CHAR(10)
PRINT 'ErrorState: ' + #ErrorState + CHAR(10)
PRINT 'ErrorProcedure: ' + #ErrorProcedure + CHAR(10)
PRINT 'ErrorLine: ' + #ErrorLine + CHAR(10)
PRINT 'ErrorMessage: ' + #ErrorMessage + CHAR(10)
END CATCH
GO
This is the execution result:
Warnings: --->
W (1): Copying SomeTable1 to Z_Backup_SomeTable1
W (2): Copying SomeTable2 to Z_Backup_SomeTable2
W (3): All Done
Using suggestions/links in the comments, this is the solution I came up with..
CREATE PROCEDURE [dbo].[spBackupSomeTables] #Debug tinyint =1
AS
BEGIN
IF #Debug IS NULL
SET #Debug = 1;
IF #Debug > 0
RAISERROR( 'Copying SomeTable1 to Z_Backup_SomeTable1', 0, 1 ) WITH NOWAIT
TRUNCATE TABLE Z_Backup_SomeTable1
INSERT INTO Z_Backup_SomeTable1
SELECT *
FROM SomeTable1
IF #Debug > 0
RAISERROR( 'Copying SomeTable2 to Z_Backup_SomeTable2', 0, 1 ) WITH NOWAIT
TRUNCATE TABLE Z_Backup_SomeTable2
INSERT INTO Z_Backup_SomeTable2
SELECT *
FROM SomeTable2
IF #Debug > 0
RAISERROR( 'All Done', 0, 1 ) WITH NOWAIT
END
GO
The #Debug paramter is defaulted to a value of 1 so that humans don't have to worry about passing any parameters in order to view the printed status messages. The ETL job will simply pass a value of 0 for that parameter to suppress the print statements allowing the job to complete without any (false) warnings.

Msg 102, Level 15, State 1, Line 34 Incorrect syntax near '.'

I am getting above error while executing query as following, which is a stored procedure command line execution
exec sp_Bind_Division_Store_Camera_On_Filter_In_Liveview
null, null, null, null
Stored procedure is as following
ALTER PROCEDURE [dbo].[sp_Bind_Division_Store_Camera_On_Filter_In_Liveview]
#division varchar(45),
#store varchar(45),
#camera varchar(68),
#group varchar(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE
#BaseQuery nvarchar(max) = 'select
distinct tblcameradetails.name as CameraName,
tblcameradetails.IsDeviceEnabled,
tblcameradetails.cameraID,
tblcameradetails.deviceIP,
tblmediasourcedetails.StreamName,
tblmediasourcedetails.streamID,
tblcameradetails.Model_ID,
tblcameradetails.IsHidden,
tblcameradetails.hasPTZcapability,
tblcameradetails.CameraModelNo,
tblcameradetails.username as CameraUserName,
tblcameradetails.hasPTZCycle,
tblcameradetails.hasPreset,
tblcameradetails.password as CameraPassword,
tblmediasourcedetails.isRecordingStarted as IsRecordingOn,
tblcameradetails.IsCovert,
tblcameradetails.constCameraName,
tblmediasourcedetails.constStreamName,
tblstoredetails.Store_ID,
tblsystemlocationdetails.division,
tblstoredetails.Store_Name,
tblstoredetails.Store_IP,
tblstoreconfiguration.Liveview_Session_Timeout
from
tblmediasourcedetails,
tblcameradetails,
tblcameragroupdetails,
tblgroupdetails,
tblvssaddsourcedetails,
tblsystemlocationdetails,
tblstoredetails,
tblstoreconfiguration'
, #ParamList nvarchar(max) = N'#p1 varchar(45), #p2 varchar(45), #p3 varchar(68), #p4 varchar(100)'
, #WhereClause nvarchar(max) = N'where
tblmediasourcedetails.cameraID=tblcameradetails.cameraID
and tblmediasourcedetails.streamID=tblvssaddsourcedetails.streamID
and tblcameradetails.cameraID = tblcameragroupdetails.cameraID
and tblcameragroupdetails.groupID=tblgroupdetails.groupID
and tblstoredetails.Store_ID= tblcameradetails.Store_ID
and tblsystemlocationdetails.Store_ID= tblstoredetails.Store_ID
and tblstoredetails.Store_ID=tblstoreconfiguration.Store_ID and 1=1'
, #OrderByClause nvarchar(100) = 'order by CameraName';
IF #division IS NOT NULL
BEGIN
SET #WhereClause = #WhereClause + ' and division like (#p1)';
END
IF #store IS NOT NULL
BEGIN
SET #WhereClause = #WhereClause + ' and (store like (#p2))';
END
IF #camera IS NOT NULL
BEGIN
SET #WhereClause = #WhereClause + ' and tblcameradetails.name like (#p3)';
END
IF #group IS NOT NULL
BEGIN
SET #WhereClause = #WhereClause + ' and tblgroupdetails.name in (#p4)';
END
SET #BaseQuery = #BaseQuery + #WhereClause + #OrderByClause;
EXECUTE sp_executesql #BaseQuery, #ParamList, #p1 = #division, #p2 = #store, #p3 = #camera, #p4 = #group;
END
Now error shows me that on line 34 error at syntax near '.', but I found no problem at all, because I am run this query form command prompt very well same applied in stored procedure, it will raised error. Please help me to solved out this.
You can easily print your variable #BaseQuery to check the resulting query.
the query in the variable #BaseQuery looks like this
select
distinct tblcameradetails.name as CameraName,
tblcameradetails.IsDeviceEnabled,
tblcameradetails.cameraID,
tblcameradetails.deviceIP,
tblmediasourcedetails.StreamName,
tblmediasourcedetails.streamID,
tblcameradetails.Model_ID,
tblcameradetails.IsHidden,
tblcameradetails.hasPTZcapability,
tblcameradetails.CameraModelNo,
tblcameradetails.username as CameraUserName,
tblcameradetails.hasPTZCycle,
tblcameradetails.hasPreset,
tblcameradetails.password as CameraPassword,
tblmediasourcedetails.isRecordingStarted as IsRecordingOn,
tblcameradetails.IsCovert,
tblcameradetails.constCameraName,
tblmediasourcedetails.constStreamName,
tblstoredetails.Store_ID,
tblsystemlocationdetails.division,
tblstoredetails.Store_Name,
tblstoredetails.Store_IP,
tblstoreconfiguration.Liveview_Session_Timeout
from
tblmediasourcedetails,
tblcameradetails,
tblcameragroupdetails,
tblgroupdetails,
tblvssaddsourcedetails,
tblsystemlocationdetails,
tblstoredetails,
tblstoreconfigurationwhere
tblmediasourcedetails.cameraID=tblcameradetails.cameraID
and tblmediasourcedetails.streamID=tblvssaddsourcedetails.streamID
and tblcameradetails.cameraID = tblcameragroupdetails.cameraID
and tblcameragroupdetails.groupID=tblgroupdetails.groupID
and tblstoredetails.Store_ID= tblcameradetails.Store_ID
and tblsystemlocationdetails.Store_ID= tblstoredetails.Store_ID
and tblstoredetails.Store_ID=tblstoreconfiguration.Store_ID and 1=1order by CameraName
You can see that you are missing a space before the WHERE and ORDER BY clause
You need to add a space in the starting of your WHERE and ORDER BY variables like this
#WhereClause nvarchar(max) = N' where
#OrderByClause nvarchar(100) = ' order by CameraName';
Try to put some PRINT in the middle of the query.
For example PRINT #BaseQuery after SET #BaseQuery = #BaseQuery + #WhereClause + #OrderByClause;
Because maybe you need a space in some variable (for example N' where...1=1' instead of N'where...1=1' )

SQL print line number in comment of dynamically created stored procedure?

I have written a script that is several thousands of lines long that I am using to generate some stored procedures dynamically.
I want to reference the script that generated the stored procedures in the comments in the stored procedures, and would like to be able to refer to the line in the script file by inserting the line number of the script file into the comments in the stored procedure file.
So for example if ##line_number gave the line number I want in the code bellow then ##line_number should be 5
1| declare #job varchar(max)
2| SET #job = '/* this is generated dynamicly by _______ */'
3| SET #job = #job + 'SELECT *' + CHAR(10)
4| SET #job = #job + 'FROM ' + #Table_Name + CHAR(10)
5| SET #job = #job + '/* ' + ##line_number + ' */'
You can use TRY / CATCH with a forced error as the CATCH block can return the line number that the error occurred on via the ERROR_LINE() function. The full construct, formatted for readability, is:
BEGIN TRY
;THROW 50000, 'Line#', 1 -- all 3 values are arbitrary, but required
END TRY
BEGIN CATCH
SET #LineNumber = ERROR_LINE()
END CATCH
Now, to get the #LineNumber variable to populate with the line number that it is being set on, you can reduce that construct to a single line as follows:
BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET #Line=ERROR_LINE();END CATCH
Here is a full example of it working:
SET ANSI_NULLS ON
SET NOCOUNT ON
GO
-- Line #1 (of current batch, not of the entire script if GOs are used)
DECLARE #CRLF NCHAR(2) = NCHAR(13) + NCHAR(10),
#SQL1 NVARCHAR(MAX) = '',
#SQL2 NVARCHAR(MAX) = '', -- Line #5
#Line INT = -1 -- default to an invalid line #
SET #SQL1 += N'/********************' + #CRLF
SET #SQL1 += N' *' + #CRLF
SET #SQL1 += N' * Test Auto-' + #CRLF -- Line #10
SET #SQL1 += N' * Generated Proc 1' + #CRLF
BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET #Line=ERROR_LINE();END CATCH
SET #SQL1 += N' * Line #:' + CONVERT(NVARCHAR(10), #Line) + #CRLF
SET #SQL1 += N' *' + #CRLF
SET #SQL1 += N' ********************/' + #CRLF -- Line #15
-- more code here
SET #SQL2 += N'/********************' + #CRLF
SET #SQL2 += N' *' + #CRLF -- Line #20
SET #SQL2 += N' * Test Auto-' + #CRLF
SET #SQL2 += N' * Generated Proc 2' + #CRLF
BEGIN TRY;THROW 50000,'',1;END TRY BEGIN CATCH;SET #Line=ERROR_LINE();END CATCH
SET #SQL2 += N' * Line #:' + CONVERT(NVARCHAR(10), #Line) + #CRLF
SET #SQL2 += N' *' + #CRLF -- Line #25
SET #SQL2 += N' ********************/' + #CRLF
PRINT #SQL1
PRINT #SQL2
GO
The line numbers returned for Proc 1 and Proc 2 are 12 and 23 respectively, which is correct for both.
Please note that the THROW command started in SQL Server 2012. If you are using SQL Server 2005, 2008, or 2008 R2, then you need to use RAISERROR() function instead of THROW.
I changed SolomonRutzky's answer a bit to get is to work in SQL Server versions prior to 2012:
DECLARE #Line INT
SET #Line = 0 BEGIN TRY RAISERROR ('Line#', 11, 1)WITH NOWAIT END TRY BEGIN CATCH SET #Line=ERROR_LINE() END CATCH
PRINT('/* testing ... I messed up somewhere near line: ' + CONVERT(varchar(10), ISNULL(#Line, 0)) + ' */')
Haven't found a built in function to return the current line number so I have started making a function to find the line number for me.
If I get the text of the current running query at the top and declare some variable, and then copy and past the function call and the #LineCounter increment code, I can get the current line number.
DECLARE #var1 NVARCHAR(MAX)
SELECT #var1 = sqltext.TEXT
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
WHERE req.session_id = ##SPID
DECLARE #LineCounter int
SET #LineCounter = 0
DECLARE #Current_Line_Number int
SET #Current_Line_Number = 0
SET #LineCounter = #LineCounter + 1
SELECT #Current_Line_Number = [MSMS].[dbo].[ReturnLineNumber] (#var1, #LineCounter)
PRINT #Current_Line_Number
This is the function
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: James J
-- Create date: 11/11/2013
-- Description: Function to return the line number for
-- where the query was called from when passed the query
-- an the count of the times it has already been used.
-- =============================================
ALTER FUNCTION ReturnLineNumber
(
#CurrentQuery nvarchar(max),
#Count int
)
RETURNS int
AS
BEGIN
DECLARE #var1 NVARCHAR(MAX)
DECLARE #functionName nvarchar(30)
SET #functionName = 'ReturnLineNumber'
SET #var1 = #CurrentQuery
DECLARE #LineCount int
SET #LineCount = 0
IF (CHARINDEX(CHAR(13), #var1) > 0)
BEGIN
DECLARE #queryString nvarchar(max)
SET #queryString = #var1
DECLARE #LineIndex int
SET #LineIndex = 1
DECLARE #LineLength int
DECLARE #linestring nvarchar(max)
DECLARE #functioncount int
SET #functioncount = 0
WHILE (#LineIndex > 0)
BEGIN
SET #LineIndex = CHARINDEX(CHAR(13), #queryString)
SET #LineLength = LEN(#queryString) - CHARINDEX(CHAR(13), #queryString)
SET #linestring = SUBSTRING(#queryString, 0, #LineIndex + 1)
SET #queryString = SUBSTRING(#queryString, #LineIndex + 1, #LineLength)
SET #LineCount = #LineCount + 1
IF (CHARINDEX(#functionName, #linestring) > 0)
BEGIN
SET #functioncount = #functioncount + 1
IF (#functioncount = #Count)
BEGIN
RETURN #LineCount
END
END
END
END
RETURN 0
END
GO
This is not a great way to get the line number and I probably should add some more checks to make sure I don't have commented out function calls but this is the closest I have got for the moment.

Resources