Why SQL Server stored procedure selecting encrypted data into variable fails - sql-server

Stored procedure that selects data from encrypted column (Always Encrypted) into variable fails with an error
Cannot continue the execution because the session is in the kill state
if XACT_ABORT is set to on.
Removing SET XACT_ABORT ON; line makes the stored procedure work perfectly, but it is unclear how it is related.
Completely removing variable also fixes an error.
Environment:
Microsoft SQL Server 2016 Enterprise (64-bit) Service Pack 2 with CU2 (13.0.5153.0): latest build at the moment.
Microsoft Windows NT 6.3 (15063)
Stored procedure:
CREATE PROCEDURE [SomeStoredProcedure]
WITH EXECUTE AS OWNER
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #EncryptedValue VARBINARY(4096);
SELECT TOP 1
#EncryptedValue = [someencryptedcolumn]
FROM
[sometable];
SELECT #EncryptedValue
RETURN 0;
END;
Table declaration:
CREATE TABLE [sometable]
(
[someencryptedcolumn] [varbinary](4096)
ENCRYPTED WITH
(
COLUMN_ENCRYPTION_KEY = [CEK1],
ENCRYPTION_TYPE = Randomized,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
)
NULL
)
Sample data:
INSERT INTO [sometable] ([someencryptedcolumn])
VALUES (NULL)
Call the stored procedure:
EXEC [SomeStoredProcedure];
Error:
Msg 596, Level 21, State 1, Line 29
Cannot continue the execution because the session is in the kill state.
Msg 0, Level 20, State 0, Line 29
A severe error occurred on the current command. The results, if any, should be discarded.
I found no references on how XACT_ABORT and Always Encrypted related.
I also checked for SQL Server logs but found no additional information on the issue.
Update:
Link to the registered SQL Server bug

This is a bug in the product. You should first try installing the latest SP/CU to see if it has already been fixed and if not report it to Microsoft.
I can also reproduce this on SQL Server 2017 RTM. I haven't tried installing the latest CU to see if that improves things.
It isn't specific to XACT_ABORT. You also see the same with other set options. Such as
SET DATEFIRST 5
SET ANSI_NULLS OFF.
When these are present it ends up calling sqllang.dll!CEnvColEncryptionKey::XretSchemaChanged twice and the second time around it ends up trying to dereference a null pointer and fails with an Access violation reading location 0x0000000000000000.
The call stack when the error is thrown (by SELECT #EncryptedValue) is as follows.

Related

SQL Server "CREATE PROCEDURE" not executing when procedure contains a join to a non-existent SEQUENCE?

Does anyone know why the following CREATE PROCEDURE statement does not work (it says "Commands completed successfully", but the procedure is not being created:
CREATE PROCEDURE [dbo].[TestProc]
AS
BEGIN
DECLARE #TV TABLE (lKey int null)
UPDATE #TV
SET lKey = 0 --a.lKey
FROM #TV
CROSS JOIN
-- Joining to select from non-existant sequence.
(SELECT NEXT VALUE FOR TestSeqDoesntExist AS lKey) a
END
GO
It appears to be something to do with the sequence not existing in the database - substituting an existing sequence object causes the procedure to be created as expected.
I would have expected the procedure to be created, but to fail on executing with an error about the sequence not existing.
I'm using SSMS v17.9 connecting to a SQL Server 2017 database (v14.0.1000.169).
It appears that this issue has been fixed by Cumulative Update 19 for SQL Server 2017 https://support.microsoft.com/en-us/help/4535007/cumulative-update-19-for-sql-server-2017
Although it is not completely obvious to me which of the documented fixes relate (if any), I updated my local Developer installation and the procedure is now created as expected.
Thanks to #Larnu and #[Jeroen Mostert] for pointing me in the right direction.

snapshot isolation error using sp_setapprole on a read-only Availability Group replica

I am investigating SQL Server Always On Availability Groups and ran into a problem when setting an application role on the read-only replica database. What really irritates me is the behavior, and I don't know how to interpret the error message.
All I do is call
DECLARE #cookie varbinary(8000);
EXEC sys.sp_setapprole
#rolename = 'TestRole', -- sysname
#password = 'password', -- sysname
#fCreateCookie = 1, -- bit
#cookie = #cookie OUTPUT; -- varbinary(8000)
EXEC sys.sp_unsetapprole #cookie = #cookie; -- varbinary(8000)
which works fine for the first attempt. On the second and all following tries, I receive the following error:
Msg 3961, Level 16, State 1, Procedure sp_setapprole, Line 44 [Batch
Start Line 25]
Snapshot isolation transaction failed in database
'AGTest' because the object accessed by the statement has been
modified by a DDL statement in another concurrent transaction since
the start of this transaction. It is disallowed because the metadata
is not versioned. A concurrent update to metadata can lead to
inconsistency if mixed with snapshot isolation.
When I execute the same statement on the primary database, I can set the approle on the replica again - once.
I tested different isolation level settings (although I wouldn't be willing to change it for the later productive database), which didn't work. I currently have no further approach to the problem, and google has almost no info for me.
Just to sum this up (for those who may run into similar problems): It was a SQL Server bug, which was fixed by Microsoft after I filed a support request (SP2 CU4). Unfortunately the fix seems to be available for SQL Server 2016 only, 2017 didn't inherit it - I hope it will be part of 2019, else I will have to create a new support case.
https://support.microsoft.com/en-us/help/4469908/error-3961-when-you-use-application-roles-read-only-secondary-replicas

Execute Stored Procedure with multiple result sets

I am using SSIS 2016. I need to execute a stored procedure that returns 4 result sets. I only need to keep the first result set and write this to a table. I can not modify the stored procedure. I do not care about any of the data returned in the other result sets. The stored procedure is in a SQL Server 2016 database. Results will also reside in SQL Server 2016.
I currently have this process running in SSIS 2008 using the "SQL Command" data access mode in an OLE DB Source like below. I have this in a For Each Loop Container to pass a series of param values to the stored procedure as I execute it multiple times for different param values on a daily basis.
SET FMTONLY OFF;
EXEC myProc
#Param1 = ?,
#Param2 =?,
#Param3 = ?;
By default SSIS 2008 is only returning the first result set, which has worked for me as I only care about the first result set.
I am using the Native OLEDB SQL Server client. From what I have read, it has changed the way it handles multiple result sets. I have used the WITH RESULT SETS to define the first result set but if I execute SSIS will fail indicating other result sets need to be defined.
In short, what is the best approach to duplicate what works in SSIS 2008 in SSIS 2016?
Solution Overview
I made 2 Experiments on that issue, the first experiments showed that in case of stored procedures with no parameters, nothing changed in SQL Server 2016 and SSIS 2016, the first Result Set is returned and others are ignored.
The second experiment showed that when using parameters, this will throw an exception, so you have to define metadata using WITH RESULT SETS option, then remove this option.
Detailed Solution
Experiment 1
The following experiment are made using SQL Server 2016 and Visual Studio 2015 with SSDT 2016
First i created this stored procedure
CREATE PROCEDURE sp_Test
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP 10 PersonType,NameStyle,Title
FROM [AdventureWorks2016CTP3].[Person].[Person]
SELECT TOP 10 PersonType,Firstname,Lastname
FROM [AdventureWorks2016CTP3].[Person].[Person_json]
END
GO
Then i added a Data flow task to SSIS package
Added an OLEDB Source, Recordset destination
In OLEDB source i select the Data access mode to SQL command
an use the following commnad
EXEC sp_Test
When clicking on Columns Tab it shows the first ResultSet structure
And we i executed the package it runs succesfully
Experiment 2
I changed the stored procedures to the following:
ALTER PROCEDURE [dbo].[sp_Test]
#param1 varchar(10),
#param2 varchar(10),
#param3 varchar(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP 10 PersonType,NameStyle,Title ,#param2 as 'Param'
FROM [AdventureWorks2016CTP3].[Person].[Person]
SELECT TOP 10 PersonType,Firstname,Lastname,#param3 as 'Param'
FROM [AdventureWorks2016CTP3].[Person].[Person_json]
END
And i used the following SQL Command in the OLEDB Source:
EXEC sp_Test ?,?,?
WITH RESULT SETS (
(
PersonType NVarchar(10),
NameStyle NVarchar(10),
Title NVarchar(10),
Param Varchar(10)
)
)
And i mapped the parameters correctly.
When running the package it throws the following exception.
[OLE DB Source 2] Error: SSIS Error Code DTS_E_OLEDBERROR. An OLE DB error has occurred. Error code: 0x80040E14.
An OLE DB record is available. Source: "Microsoft SQL Server Native Client 11.0" Hresult: 0x80040E14 Description: "EXECUTE statement failed because its WITH RESULT SETS clause specified 1 result set(s), and the statement tried to send more result sets than this.".
After that i tried to remove the With RESULT SETS option, so the command is :
EXEC sp_Test ?,?,?
I tried to execute the package again, so it is executed with no errors.
Conclusion
Try to use the WITH RESULT SETs option to define the OLEDB Source metadata, after that the metadata is defined, just remove this option and run the package, so it will just take the first Result Set succesfully.
I know it's not exactly what you are asking for, but maybe you could create another stored proc that will return only the first result set for you? (Without touching the first stored proc.) Or insert the data into a table for you and then you just read the data?

Find SQL query errors without executing the SQL query in SQL server

Is there any way to find the SQL query errors without executing the query in SQL server?
For example,
Use TestDB
Select * from Employee1
Consider TestDB doesn’t contain any table named “Employee1”. In this case, while we running this query, we will get the “Invalid object name Employee1” SQL error.
My question is that, after writing the query, is there any better approach to find these errors without executing?
I got a comment from my colleague is that use the sp_describe_first_result_set System stored procedure.
If you have 2012 and above you can use the sp that your colleague has mentioned.
If you use 2008 R2 and less then use
SET FMTONLY ON;
Select * from MyTabl;
SET FMTONLY OFF;
Msg 208, Level 16, State 1, Line 3
Invalid object name 'MyTabl'.
This will execute the query but not process any data. NOEXEC only parses the query so any objects that don't exist will not be flagged. NOEXEC checks syntax only. So what you want is either sp_describe_first_result_set or FMTONLY.
Alternatively if you have a select then you can also append to your where 1=2. For example
SELECT *
FROM MyTabl
WHERE 1=2
which gives them same result
Msg 208, Level 16, State 1, Line 3
Invalid object name 'MyTabl'

Stop Running a Procedure in Batch File

I am creating a deployment package which will run a stored procedure that's in VSS. I am able to do this but my problem is this. I have more or less 30 databases which I need to deploy to and I only have two databases which doesn't need to get the update. With that, I included the following codes to the .sql file which the batch file runs:
IF OBJECT_ID('CONFIG') IS NULL OR DB_NAME() LIKE '%SampleDB%'
BEGIN
PRINT 'This is not a store database. Skipping this database.'
SET NOEXEC ON
RETURN
END
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE ID = OBJECT_ID(N'[dbo].[sp_ThisIsMySampleProcedure]'))
DROP PROCEDURE [dbo].[sp_ThisIsMySampleProcedure]
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
CREATE PROCEDURE [dbo].[sp_ThisIsMySampleProcedure]
-- Everything else follows...
This sql code runs perfectly on SQL Server. Whenever I run it against Sample DB, it automatically skips proceeding to the checking if the procedure exists and everything after that.
But whenever I try to run it against SampleDB using my batch file, it prompts the error message but continues to run the procedure disregarding my RETURN keyword. So the message would look like this:
This is not a store database. Skipping this database.
Msg 3701, Level 11, State 5, Line 8
Cannot drop the procedure 'sp_ThisIsMySampleProcedure', because it does not exist or you do not have permission.
I understand that the error message is correct because the procedure really doesn't exist on my SampleDB. But why does it still keep running when I have the RETURN keyword there after it satisfied the condition to skip running the entire script?
Thank you!
EDIT:
Okay, I think people doesn't understand me completely. This is the scenario: My procedure (see above) works well on SQL Server Management Studio. What I mean is that whenever I try to run it on SampleDB, it gives me the message This is not a store database. Skipping this database.
But whenever I try to run my Batch-File which executes this stored procedure, I get this message on the command prompt:
This is not a store database. Skipping this database.
Msg 3701, Level 11, State 5, Line 8
Cannot drop the procedure 'sp_ThisIsMySampleProcedure', because it does not exist or you do not have permission.
Which basically means that the batch-file continued executing the whole SQL Script regardless that the first condition was satisfied.
My question is how do I make the batch-file know that whenever SQL Server throws the message This is not a store database. Skipping this database. the batch-file will immediately stop the execution of the sql file.
First. The keyword GO divides the file into separate requests. Each request separately processed by the server. RETURN exits only from first request, other requests will be run.
Try this:
select 1
RETURN
select 2
go
select 3
go
Second, SET NOEXEC ON is dangerous thing, it blocks all subsequent execution. Try this:
select 1
SET NOEXEC ON
RETURN
select 2
go
select 3
go
SET NOEXEC OFF
go
You can create procedure on all servers, but return from it in the beginig if database name like something. Or you can remove GO and create stored proc with dynamic SQL:
IF DB_NAME() like '%mydb%'
BEGIN
EXEC dbo.sp_executesql #statement = N'
CREATE PROCEDURE [dbo].[my proc]
AS
BEGIN
select 1
END'
END

Resources