Reputation: 838
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 + ' */'
Upvotes: 4
Views: 9428
Reputation: 838
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)) + ' */')
Upvotes: 1
Reputation: 48864
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.
Upvotes: 8
Reputation: 838
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.
Upvotes: 0