I'm trying to send data to my kinesis stream from SQL but I'm only able to get back the error.
{
"__type": "InvalidSignatureException",
"message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
}
My key & secret are correct as I've done a successful test in Postman.
Here is my function for creating the Authentication header...
ALTER FUNCTION [dbo].[CreateAuth]
(
#awsAccessKey NVARCHAR(MAX),
#awsSecretKey NVARCHAR(MAX),
#content NVARCHAR(MAX),
#dateTime Datetime
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #awsRegion NVARCHAR(MAX) = 'eu-west-2';
DECLARE #awsService NVARCHAR(MAX) = 'kinesis';
DECLARE #timeStamp NVARCHAR(16) = FORMAT(#dateTime, 'yyyyMMddTHHmmssZ');
DECLARE #scope NVARCHAR(MAX) = FORMAT(#dateTime, 'yyyyMMdd') + '/'+#awsRegion+'/'+#awsService+'/aws4_request'
DECLARE #x_amz_content_sha256 NVARCHAR(MAX);
DECLARE #hexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',#content);
SET #x_amz_content_sha256 = LOWER(CONVERT([varchar](512), #hexbin,2))
-- CANONICAL REQUEST
DECLARE #CanonicalRequest NVARCHAR(MAX) = '';
-- HTTP verb
SET #CanonicalRequest += 'POST' + CHAR(13)
-- URL
SET #CanonicalRequest += 'kinesis.eu-west-2.amazonaws.com' + CHAR(13)
-- QUERYSTRING (must be sorted alphbetically)
SET #CanonicalRequest += '' + CHAR(13)
-- HEADERS (must be sorted alphbetically)
SET #CanonicalRequest += 'content-type:application/x-amz-json-1.1' + CHAR(13)
SET #CanonicalRequest += 'host:kinesis.eu-west-2.amazonaws.com' + CHAR(13)
SET #CanonicalRequest += 'x-amz-content-sha256:' + #x_amz_content_sha256 + CHAR(13)
SET #CanonicalRequest += 'x-amz-date:' + #timeStamp + CHAR(13)
SET #CanonicalRequest += 'x-amz-target:' + 'Kinesis_20131202.PutRecord' + CHAR(13)
-- SIGNED HEADERS
DECLARE #signedheaders NVARCHAR(MAX) = 'content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target'
SET #CanonicalRequest += #signedheaders + CHAR(13)
-- HASHED PAYLOAD
SET #CanonicalRequest += #x_amz_content_sha256
DECLARE #CanonicalRequestHexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',#CanonicalRequest);
-- STRING TO SIGN
DECLARE #stringToSign NVARCHAR(MAX) = '';
SET #stringToSign += 'AWS4-HMAC-SHA256' + CHAR(13)
SET #stringToSign += #timeStamp + CHAR(13)
SET #stringToSign += #scope + CHAR(13)
SET #stringToSign += LOWER(CONVERT([varchar](512), #CanonicalRequestHexbin,2))
-- CALCULATE SIGNATURE
DECLARE #DateKey VARBINARY(64);
DECLARE #DateRegionKey VARBINARY(64);
DECLARE #DateRegionServiceKey VARBINARY(64);
DECLARE #SigningKey VARBINARY(64);
DECLARE #Signature VARBINARY(64);
SET #DateKey = dbo.HMAC('SHA2_256',CONVERT(VARBINARY(MAX), 'AWS4'+#awsSecretKey),CONVERT(VARBINARY(MAX),FORMAT(#dateTime, 'yyyyMMdd')))
SET #DateRegionKey = dbo.HMAC('SHA2_256', #DateKey, CONVERT(VARBINARY(MAX),#awsRegion))
SET #DateRegionServiceKey = dbo.HMAC('SHA2_256', #DateRegionKey, CONVERT(VARBINARY(MAX),#awsService))
SET #SigningKey = dbo.HMAC('SHA2_256', #DateRegionServiceKey, CONVERT(VARBINARY(MAX),'aws4_request'))
SET #Signature = dbo.HMAC('SHA2_256',#SigningKey,CONVERT(VARBINARY(MAX),#stringToSign));
--BUILD Authorization
DECLARE #AuthValue NVARCHAR(MAX) = '';
SET #AuthValue += 'AWS4-HMAC-SHA256 Credential=' + #awsAccessKey + '/'+ #scope
SET #AuthValue += ',SignedHeaders=' + #signedheaders
SET #AuthValue += ',Signature=' + LOWER(CONVERT([varchar](512), #Signature,2))
RETURN #AuthValue
And here is the call to kinesis
-- Open the connection.
EXEC #ret = sp_OACreate 'MSXML2.ServerXMLHTTP', #token OUT;
IF #ret <> 0 RAISERROR('Unable to open HTTP connection.', 10, 1);
-- Send the request.
EXEC #ret = sp_OAMethod #token, 'open', NULL, 'POST', #url, 'false';
SET #auth = dbo.CreateAuth('MYACCESSKEY','MYSECRETKEY',#postData, #datetime)
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'Authorization', #auth
PRINT #auth
PRINT ''
DECLARE #hexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',#postData); --hash data
DECLARE #x_amz_content_sha256 NVARCHAR(MAX) = LOWER(CONVERT([varchar](512), #hexbin,2)) -- get hex of hashed data
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'content-type', #contentType;
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'host', 'kinesis.eu-west-2.amazonaws.com';
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'x-amz-content-sha256', #x_amz_content_sha256;
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'x-amz-date', #xAmzDate;
EXEC #ret = sp_OAMethod #token, 'setRequestHeader', NULL, 'x-amz-target', #xAmzTarget;
EXEC #ret = sp_OAMethod #token, 'send', NULL, #postData;
-- Handle the response.
EXEC #ret = sp_OAGetProperty #token, 'status', #status OUT;
EXEC #ret = sp_OAGetProperty #token, 'statusText', #statusText OUT;
EXEC #ret = sp_OAGetProperty #token, 'responseText', #responseText OUT;
-- Show the response.
PRINT 'Status: ' + #status + ' (' + #statusText + ')';
PRINT 'Response text: ' + #responseText;
-- Close the connection.
EXEC #ret = sp_OADestroy #token;
IF #ret <> 0 RAISERROR('Unable to close HTTP connection.', 10, 1);
The HMAC function I'm using is this one... https://gist.github.com/rmalayter/3130462
I've verified that the results produced with the SHA2_256 & HMAC functions are correct using https://codebeautify.org/
I'm pretty sure I'm doing everything according to... https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html and https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
I'm lost as to what to look try next.
I've solved it now. There were a number of problems aside from my lazy hard coding.
CanonicalURI should just be '/'. - thanks #AlwayLearning.
Changed all string variables to VARCHAR rather than NVARCHAR, this was causing incorrect hashing.
The date used for the #datekey also needs to be a VARCHAR CONVERT(VARCHAR(MAX),FORMAT(#dateTime, 'yyyyMMdd')) as the FORMAT() function returns a NVARCHAR.
And I found this site which was very helpful http://aws-signature.com.s3-website-us-west-2.amazonaws.com/
Successful function now looks like this...
ALTER FUNCTION [dbo].[CreateAuth]
(
#awsAccessKey VARCHAR(MAX),
#awsSecretKey VARCHAR(MAX),
#content VARCHAR(MAX),
#dateTime Datetime,
#awsRegion VARCHAR(MAX),
#awsService VARCHAR(MAX),
#CanonicalRequestURI VARCHAR(MAX),
#host VARCHAR(MAX),
#xAmzTarget VARCHAR(MAX),
#contentType VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #date VARCHAR(8) = FORMAT(#dateTime, 'yyyyMMdd')
DECLARE #timeStamp VARCHAR(16) = FORMAT(#dateTime, 'yyyyMMddTHHmmssZ');
DECLARE #scope VARCHAR(MAX) = #date + '/'+#awsRegion+'/'+#awsService+'/aws4_request'
DECLARE #x_amz_content_sha256 VARCHAR(MAX);
DECLARE #hexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',#content);
SET #x_amz_content_sha256 = LOWER(CONVERT([varchar](MAX), #hexbin,2))
-- CANONICAL REQUEST
DECLARE #CanonicalRequest VARCHAR(MAX) = '';
-- HTTP verb
SET #CanonicalRequest += 'POST' + CHAR(10)
-- URL
SET #CanonicalRequest += #CanonicalRequestURI + CHAR(10)
-- QUERYSTRING (must be sorted alphbetically)
SET #CanonicalRequest += '' + CHAR(10)
-- HEADERS (must be sorted alphbetically)
SET #CanonicalRequest += 'content-type:' + #contentType + CHAR(10)
SET #CanonicalRequest += 'host:' + #host + CHAR(10)
SET #CanonicalRequest += 'x-amz-content-sha256:' + #x_amz_content_sha256 + CHAR(10)
SET #CanonicalRequest += 'x-amz-date:' + #timeStamp + CHAR(10)
SET #CanonicalRequest += 'x-amz-target:' + #xAmzTarget + CHAR(10)
SET #CanonicalRequest += CHAR(10)
-- SIGNED HEADERS
DECLARE #signedheaders VARCHAR(MAX) = 'content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target'
SET #CanonicalRequest += #signedheaders + CHAR(10)
-- HASHED PAYLOAD
SET #CanonicalRequest += #x_amz_content_sha256
DECLARE #CanonicalRequestHexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',#CanonicalRequest);
-- STRING TO SIGN
DECLARE #stringToSign VARCHAR(MAX) = '';
SET #stringToSign += 'AWS4-HMAC-SHA256' + CHAR(10)
SET #stringToSign += #timeStamp + CHAR(10)
SET #stringToSign += #scope + CHAR(10)
SET #stringToSign += LOWER(CONVERT([varchar](MAX), #CanonicalRequestHexbin,2))
-- CALCULATE SIGNATURE
DECLARE #DateKey VARBINARY(MAX);
DECLARE #DateRegionKey VARBINARY(MAX);
DECLARE #DateRegionServiceKey VARBINARY(MAX);
DECLARE #SigningKey VARBINARY(MAX);
DECLARE #Signature VARBINARY(MAX);
SET #DateKey = dbo.HMAC('SHA2_256',CONVERT(VARBINARY(MAX), 'AWS4'+#awsSecretKey),CONVERT(VARBINARY(MAX),#date))
SET #DateRegionKey = dbo.HMAC('SHA2_256', #DateKey, CONVERT(VARBINARY(MAX),#awsRegion))
SET #DateRegionServiceKey = dbo.HMAC('SHA2_256', #DateRegionKey, CONVERT(VARBINARY(MAX),#awsService))
SET #SigningKey = dbo.HMAC('SHA2_256', #DateRegionServiceKey, CONVERT(VARBINARY(MAX),'aws4_request'))
SET #Signature = dbo.HMAC('SHA2_256',#SigningKey,CONVERT(VARBINARY(MAX),#stringToSign));
--BUILD Authorization
DECLARE #AuthValue VARCHAR(MAX) = '';
SET #AuthValue += 'AWS4-HMAC-SHA256 Credential=' + #awsAccessKey + '/'+ #scope
SET #AuthValue += ',SignedHeaders=' + #signedheaders
SET #AuthValue += ',Signature=' + LOWER(CONVERT([varchar](MAX), #Signature,2))
RETURN #AuthValue
END
Related
Maps API was working, but has suddenly stopped. Getting ??? ??? for Address and NULL for everything else. This was working in the past.
Using this:
DECLARE #Address as varchar(100),
#City as varchar(25),
#State as varchar(2),
#PostalCode as varchar(10),
#Country varchar(40),
#County varchar(40),
#GPSLatitude numeric(18,9),
#GPSLongitude numeric(18,9),
#MapURL varchar(1024)
SET #Address = '333 W 35th st'
SET #City = 'Chicago'
SET #State = 'IL'
--Build the web API URL
DECLARE #URL varchar(MAX)
SET #URL = 'https://www.google.com/maps/api/geocode/xml?sensor=false&address='+
CASE WHEN #Address IS NOT NULL THEN #Address ELSE '' END +
CASE WHEN #City IS NOT NULL THEN ', ' + #City ELSE '' END +
CASE WHEN #State IS NOT NULL THEN ', '+ #State ELSE '' END +
CASE WHEN #PostalCode IS NOT NULL THEN ', ' + #PostalCode ELSE '' END +
CASE WHEN #Country IS NOT NULL THEN ', ' + #Country ELSE '' END+
'&key=I_HAVE_A_VALID_API'
SET #URL = REPLACE(#Url, ' ', '+')
---CREATE THE OATH REQUEST
DECLARE #Response varchar(8000)
DECLARE #XML xml
DECLARE #Obj int
DECLARE #Result int
DECLARE #HTTPStatus int
DECLARE #ErrorMsg varchar(MAX)
Exec #Result = sp_OACreate 'MSXML2.ServerXMLHttp',#Obj OUT
BEGIN TRY
EXEC #Result = sp_OAMethod #Obj, 'open', NULL, 'GET', #URL, false
EXEC #Result = sp_OAMethod #Obj, 'setRequestHeader', NULL, 'Content-Type', 'application/x-www-form-urlencoded'
EXEC #Result = sp_OAMethod #Obj, send, NULL, ''
EXEC #Result = sp_OAGetProperty #Obj, 'status', #HTTPStatus OUT
EXEC #Result = sp_OAGetProperty #Obj, 'responseXML.xml', #Response OUT
END TRY
BEGIN CATCH
SET #ErrorMsg = ERROR_MESSAGE()
END CATCH
EXEC #Result = sp_OADestroy #Obj
IF (#ErrorMsg IS NOT NULL) OR (#HTTPStatus <> 200)
BEGIN
SET #ErrorMsg = 'Error in spGeocode: ' + ISNULL(#ErrorMsg, 'Http result is: ' + CAST(#HTTPStatus AS varchar (10)))
RAISERROR (#ErrorMsg, 16,1, #HTTPStatus)
RETURN
END
--CAPTURE THE RESPONSE
BEGIN
SET #XML = CAST(#Response AS XML)
SET #GPSLatitude =#XML.value('(/GeocodeResponse/result/geometry/location/lat) [1]', 'numeric(18,9)')
SET #GPSLongitude = #XML.value('(/GeocodeResponse/result/geometry/location/lng) [1]', 'numeric(18,9)')
SET #City = #XML.value('(/GeocodeResponse/result/address_component[type="locality"]/long_name) [1]','varchar(40)')
SET #State = #XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_1"]/short_name) [1]','varchar(40)')
SET #PostalCode = #XML.value('(/GeocodeResponse/result/address_component[type="postal_code"]/long_name) [1]','varchar(20)')
SET #Country = #XML.value('(/GeocodeResponse/result/address_component[type="country"]/short_name) [1]','varchar(40)')
SET #County = #XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_2"]/short_name) [1]','varchar(40)')
SET #Address =
ISNULL(#XML.value('(/GeocodeResponse/result/address_component[type="street_number"]/long_name) [1]','varchar(40)'), '???') + ' '+
ISNULL (#XML.value('(/GeocodeResponse/result/address_component[type="route"]/long_name) [1]','varchar(40)'), '???')
SET #MapURL = 'https://maps.google.com/maps?f=q&hl=en&q=' + CAST (#GPSLatitude AS varchar(20)) + '+' + CAST(#GPSLongitude AS varchar(20))
END
BEGIN
SELECT #Address as Address,
#City as City,
#State as State,
#PostalCode as Zip,
#Country as Country,
#County as County,
#GPSLatitude as Lat,
#GPSLongitude as lon,
#MapURL as map
END
Output was Address, Latitude and Longitude and MapURL. Will eventually develop into a Stored Procedure in an ERP.
This worked a few weeks ago then had to shut it off for security reasons.
I'm trying to send out csv files through sp_send_dbmail and one of the fields (outcome) has commas in it. When running the code below, it will automatically separate some of the outcomes as two columns.
I've tried changing #query_result_separator, but then it wouldn't separate anything at all, except for the outcome field, which eventually gives me two columns.
declare #filename NVARCHAR(MAX)
DECLARE #prefix varchar(50)
DECLARE #q varchar(max)
DECLARE #body NVARCHAR(MAX)
SET #body = 'abc'
SET #filename = concat('abc_',replace(cast(cast(getdate() as date) as varchar),'-','_'),'.csv')
SET #prefix = '[sep=,' + CHAR(13) + CHAR(10) + 'ID]'
SET #q ='set nocount on; select right(''"=""'' + cast(ID as nvarchar) + ''"""'', 24)' + #prefix + ',Outcome, isnull(cast(Outcomedate as varchar),'''') as OutcomeDate,isnull(cast(Alerts as varchar),'''') as Alerts
from database.abc.defg'
EXEC msdb.dbo.sp_send_dbmail
#body = #body,
#body_format ='HTML',
#recipients = 'abc#defg.com',
#subject = 'abc',
#query = #q ,
#attach_query_result_as_file = 1,
#query_attachment_filename = #filename,
#query_result_separator = ',',
#query_result_no_padding = 1,
#query_result_width = 500;
I am trying to write the following stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spInsertAudit]
(#val1 VARCHAR(100),
#val2 VARCHAR(200))
AS
BEGIN TRY
DECLARE #return_value varchar(max)
/************************************/
/* The Query */
/************************************/
EXEC [dbo].[SerializeJSON]
'INSERT INTO tLogin (EMP, LoginInTime)
VALUES (#val1, #val2)'
END TRY
BEGIN CATCH
/************************************/
/* Get Error results back */
/************************************/
EXEC [dbo].[SerializeJSON]
'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
The SerializeJSON stored procedure (found here) is this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SerializeJSON]
(#ParameterSQL AS VARCHAR(MAX))
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #XMLString VARCHAR(MAX)
DECLARE #XML XML
DECLARE #Paramlist NVARCHAR(1000)
SET #Paramlist = N'#XML XML OUTPUT'
SET #SQL = 'WITH PrepareTable (XMLString)'
SET #SQL = #SQL + 'AS('
SET #SQL = #SQL + #ParameterSQL + 'FOR XML RAW,TYPE,ELEMENTS'
SET #SQL = #SQL + ')'
SET #SQL = #SQL + 'SELECT #XML=[XMLString]FROM[PrepareTable]'
EXEC sp_executesql #SQL
, #Paramlist
, #XML = #XML OUTPUT
SET #XMLString = CAST(#XML AS VARCHAR(MAX))
DECLARE #JSON VARCHAR(MAX)
DECLARE #Row VARCHAR(MAX)
DECLARE #RowStart INT
DECLARE #RowEnd INT
DECLARE #FieldStart INT
DECLARE #FieldEnd INT
DECLARE #KEY VARCHAR(MAX)
DECLARE #Value VARCHAR(MAX)
DECLARE #StartRoot VARCHAR(100);
SET #StartRoot = '<row>'
DECLARE #EndRoot VARCHAR(100);
SET #EndRoot = '</row>'
DECLARE #StartField VARCHAR(100);
SET #StartField = '<'
DECLARE #EndField VARCHAR(100);
SET #EndField = '>'
SET #RowStart = CharIndex(#StartRoot, #XMLString, 0)
SET #JSON = ''
WHILE #RowStart > 0
BEGIN
SET #RowStart = #RowStart + Len(#StartRoot)
SET #RowEnd = CharIndex(#EndRoot, #XMLString, #RowStart)
SET #Row = SubString(#XMLString, #RowStart, #RowEnd - #RowStart)
SET #JSON = #JSON + '{'
-- for each row
SET #FieldStart = CharIndex(#StartField, #Row, 0)
WHILE #FieldStart > 0
BEGIN
-- parse node key
SET #FieldStart = #FieldStart + Len(#StartField)
SET #FieldEnd = CharIndex(#EndField, #Row, #FieldStart)
SET #KEY = SubString(#Row, #FieldStart, #FieldEnd - #FieldStart)
SET #JSON = #JSON + '"' + #KEY + '":'
-- parse node value
SET #FieldStart = #FieldEnd + 1
SET #FieldEnd = CharIndex('</', #Row, #FieldStart)
SET #Value = SubString(#Row, #FieldStart, #FieldEnd - #FieldStart)
SET #JSON = #JSON + '"' + #Value + '",'
SET #FieldStart = #FieldStart + Len(#StartField)
SET #FieldEnd = CharIndex(#EndField, #Row, #FieldStart)
SET #FieldStart = CharIndex(#StartField, #Row, #FieldEnd)
END
IF LEN(#JSON) > 0
SET #JSON = SubString(#JSON, 0, LEN(#JSON))
SET #JSON = #JSON + '},'
--/ for each row
SET #RowStart = CharIndex(#StartRoot, #XMLString, #RowEnd)
END
IF LEN(#JSON) > 0
SET #JSON = SubString(#JSON, 0, LEN(#JSON))
SET #JSON = '[' + #JSON + ']'
SELECT #JSON
END
When I execute the spInsertAudit stored procedure, I get this error:
Msg 102, Level 15, State 1, Line 8
Incorrect syntax near 'XML'.
So it did write to that table but for some reason it's not returning the JSON and having that error above....
I was trying to execute one of my Stored procedure but i am getting an syntax error and i am unable to understand why.
here is the sproc:
ALTER PROCEDURE [dbo].[HotlinePlusAdministration_ArticleMigrator]
#ArticleKey AS INT,
--#CategoryID AS INT,
--#Title AS Varchar(200),
--#ArticleDate AS datetime,
#DestLinkServer AS VARCHAR(50),
#UserID AS VARCHAR(8),
#ReturnMsg AS VARCHAR(1000) OUTPUT
AS
BEGIN
DECLARE #Query AS NVARCHAR(4000)
DECLARE #Log AS VARCHAR(8000)
DECLARE #ArticleID as int
DECLARE #NewArticleID as int
DECLARE #ArticleKeyExists as int
DECLARE #Title as varchar(200)
DECLARE #CategoryID as INT
DECLARE #ArticleDate as varchar(30)
DECLARE #ParmDefinition nvarchar(500);
SET XACT_ABORT ON -- Required for nested transaction
BEGIN TRAN
-- Check if ArticleID exists in Destination Server
SET #Query = N' SELECT #ArticleKeyExists = COUNT(*)
FROM ' + #DestLinkServer + '.HL2_61.dbo.Article' + ' where ArticleKey = ' + str(#ArticleKey)
SET #ParmDefinition = N'#ArticleKey int, #ArticleKeyExists int OUTPUT';
EXECUTE sp_executesql #Query , #ParmDefinition, #ArticleKey , #ArticleKeyExists OUTPUT;
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
SET #ReturnMsg = #Log + '<span style="color:red;">ERROR: <br></span>'
RETURN -1
END
--Delete existing Articles for select page
set #Query = 'DELETE FROM ' + #DestLinkServer +
'.HL2_61.dbo.Article ' +
'WHERE ArticleKey = ' + CONVERT(VARCHAR, #ArticleKey)
--'WHERE CategoryID = ' + CONVERT(VARCHAR, #CategoryID) + ' and Title = ''' + #Title + ''' and ArticleDate = ''' + #ArticleDate + ''''
Print #Query
EXEC(#Query)
when i am trying to execute it i am getting an error here:
SELECT #ArticleKeyExists = COUNT(*)
FROM BRWSQLDC.HL2_61.dbo.Article where ArticleKey = 1591276581
Can some body please help me on this,
Thanks.
SET #ArticleKeyExists = (SELECT COUNT(*) FROM BRWSQLDC.HL2_61.dbo.Article where ArticleKey = 1591276581)
The following stored procedure works correctly execpt when I pass in the #NameSubstring parameter. I know I am not dynamically building the like clause properly. How can I build the like clause when this parameter also needs to be passed as a parameter in the EXEC sp_executesql call near the bottom of the procedure?
ALTER PROCEDURE [dbo].[spGetAutoCompleteList]
(
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int,
#ReturnMappings bit,
#ReturnData bit
)
AS
DECLARE #ErrorCode int,
#GetMappings nvarchar(500),
#Debug bit,
#Select AS NVARCHAR(4000),
#From AS NVARCHAR(4000),
#Where AS NVARCHAR(4000),
#Sql AS NVARCHAR(4000),
#Parms AS NVARCHAR(4000)
SET #ErrorCode = 0
SET #Debug = 1
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF #AutoCompleteID IS NOT NULL OR #StatusFlag IS NOT NULL OR #NameSubstring IS NOT NULL
BEGIN
SET #Select = '
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments'
SET #GetMappings = '
Select ac.AutoCompleteID'
IF #ReturnData = 1
BEGIN
SET #Select = #Select + '
, ac.AutoCompleteData'
END
SET #From = '
FROM tbAutoComplete ac'
SET #Where = '
WHERE 1=1'
IF #AutoCompleteID IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar)'
END
IF #StatusFlag IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.StatusFlag = CAST(#StatusFlag AS nvarchar)'
END
IF #NameSubstring IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteName like #NameSubstring' + '%'
END
SET #Where = #Where + '
AND ac.CompanyID = + CAST(#CompanyID AS nvarchar)'
SET #Sql = #Select + #From + #Where
SET #Parms = '
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int'
EXEC sp_executesql #Sql,
#Parms,
#AutoCompleteID,
#StatusFlag,
#NameSubstring,
#CompanyID
IF #ReturnMappings = 1
BEGIN
SET #GetMappings = 'Select * FROM tbAutoCompleteMap acm WHERE acm.AutoCompleteID IN(' + #GetMappings + #From + #Where + ')'
--EXEC sp_executesql #GetMappings
END
IF #Debug = 1
BEGIN
PRINT #GetMappings
PRINT #Sql
END
END
SELECT #ErrorCode = #ErrorCode + ##ERROR
IF #ErrorCode <> 0
BEGIN
SELECT '<FaultClass>1</FaultClass><FaultCode>1</FaultCode>'
+ '<FaultDesc>Internal Database Error.</FaultDesc>'
+ '<FaultDebugInfo>(spGetAutoCompleteList): There was an error while trying to SELECT from tbAutoComplete.</FaultDebugInfo>'
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
#NameString needs to be outside of the quotes. To get #NameString% enclosed in quotes, you use two single quotes to escape the quote character as a literal.
SET #Where = #Where + '
AND ac.AutoCompleteName like ''' + #NameSubstring + '%'''
To avoid SQL injection, do not use concatenation when adding the parameter to your SQL statement. I strongly recommend that you use this format:
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE #NameSubstring + char(37)'
END
By using char(37) instead of '%' you avoid having to escape the apostrophes around the string literal
If you wanted to put a wildcard at either side, then you would use
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE char(37) + #NameSubstring + char(37)'
END
-----------------------------------------------------------------------------
In case someone believes I am wrong, here's proof that concatenation is a risk.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestInjection]') AND type in (N'U')) BEGIN
create table TestInjection(ID int, Value nvarchar(10))
insert into TestInjection (ID,Value)
Values
(1,'Tom'),
(2,'Fred'),
(3,'Betty'),
(4,'Betty2'),
(5,'Betty3'),
(6,'George')
END
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] like ''' + #NameSubstring + '%'''
Declare #params nvarchar(100) = '#ID int'
exec sp_executesql #sql, #params, #ID
select * from TestInjection
Run it the first time and you will get a resultset with 3 records, and another with all 6 records.
Now swap the declaration of #NameSubstring to the alternative, and re-run. All data in the table has been deleted.
If on the other hand you write your code like:
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] LIKE #NameSubstring + char(37)'
Declare #params nvarchar(100) = '#ID int, #NameSubstring nvarchar(1000)'
exec sp_executesql #sql, #params, #ID, #NameSubstring
select * from TestInjection
Then you still get the 3 records returned the first time, but you don't lose your data when you change the declaration.
SET #Where = #Where + 'AND ac.AutoCompleteName like ''%' + #NameSubstring + '%'''
So, you are asking how to specify parameters when you use dynamic queries and sp_executesql ?
It can be done, like this:
DECLARE /* ... */
SET #SQLString = N'SELECT #LastlnameOUT = max(lname) FROM pubs.dbo.employee WHERE job_lvl = #level'
SET #ParmDefinition = N'#level tinyint, #LastlnameOUT varchar(30) OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #LastlnameOUT=#Lastlname OUTPUT
You can read more about it here: http://support.microsoft.com/kb/262499
Perhaps this wouldn't be an issue if you weren't using dynamic SQL. It looks to me like a vanilla query would work just as well and be much more straightforward to read and debug. Consider the following:
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments
FROM tbAutoComplete ac
WHERE ((ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar) OR (#AutoCompleteID IS NULL))
AND ((ac.StatusFlag = CAST(#StatusFlag AS nvarchar)) OR (#StatusFlag IS NULL))
AND ((ac.AutoCompleteName like #NameSubstring + '%') OR (#NameSubstring IS NULL))
AND ((ac.CompanyID = CAST(#CompanyID AS nvarchar)) OR (#CompanyID IS NULL))
This is much simpler, clearer etc. Good luck!