So as the title says DML queries fail when the QUOTED_IDENTIFIER is set to OFF whilst using SqlDependancy to monitor a table. I get the following errors for INSERT/UPDATE/DELETE statements:

  • INSERT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
  • UPDATE failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
  • DELETE failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.

Here is code to replicate the issue, its as minimal as i have been able to create.

I used SQL 2008 R2 and Visual Studio 2013 (C# using .Net Framework v4.5.1)

First create a test DB, table and populate it.


USE [master]
GO

CREATE DATABASE [BUG0001]
GO

ALTER DATABASE [BUG0001] SET ENABLE_BROKER
GO

USE [BUG0001]
GO

CREATE TABLE [dbo].[Test]
(
    [RowVersion]
        ROWVERSION,

    [TestId]
        INT
        NOT NULL,

    [Flipper]
        BIT
        NOT NULL,

    CONSTRAINT [PK_dbo_Test_TestId] PRIMARY KEY ([TestId])
)
GO

INSERT INTO [dbo].[Test] ([TestId], [Flipper])
    VALUES (1, 0);

Now create a console application and run it (remember to set the connection string for your DB)


using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BUG0001
{
    class Program
    {
        const string CN_STRING = "Server=TESTSERVER;Database=BUG0001;Trusted_Connection=True;";
        const string SQL_DATA = "SELECT [RowVersion], [TestId], [Flipper] FROM [dbo].[Test] WHERE [RowVersion] > @LastRowVersion ORDER BY [RowVersion];";

        const int FIELD_ROW_VERSION = 0;
        const int FIELD_TEST_ID = 1;
        const int FIELD_FLIPPER = 2;

        static SqlConnection cn = null;
        static byte[] lastRowVersion = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };

        static void Main(string[] args)
        {
            SqlDependency.Start(CN_STRING);

            using (cn = new SqlConnection(CN_STRING))
            {
                cn.Open();

                Console.WriteLine("Press any key to exit.");
                Console.WriteLine();

                GetData();

                ConsoleKeyInfo keyInfo;

                do { keyInfo = Console.ReadKey(true); }
                while (keyInfo.Key == ConsoleKey.LeftWindows || keyInfo.Key == ConsoleKey.RightWindows);

                cn.Close();
            }

            SqlDependency.Stop(CN_STRING);
        }

        static void GetData()
        {
            using (var cmd = cn.CreateCommand())
            {
                cmd.CommandText = SQL_DATA;
                cmd.Parameters.Add(new SqlParameter("@LastRowVersion", lastRowVersion));

                /* The SqlDependency is a one event only mechignisum and must be reset each time the event is triggered. */
                var depenency = new SqlDependency(cmd);
                depenency.OnChange += depenency_OnChange;

                RenderResults(cmd);
            }
        }

        static void depenency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            Console.WriteLine(e.Info);
            Console.WriteLine(e.Source);
            Console.WriteLine(e.Type);
            Console.WriteLine();

            GetData();
        }

        static void RenderResults(SqlCommand cmd)
        {
            try
            {
                using (var reader = cmd.ExecuteReader())
                {
                    foreach (DbDataRecord record in reader)
                    {
                        var rowVersion = (byte[])record.GetValue(FIELD_ROW_VERSION);

                        Console.WriteLine(
                            "[RowVersion] = 0x{0}\r\n[TestId] = '{1}'\r\n[Flipper] = '{2}'\r\n",
                            BitConverter.ToString(rowVersion).Replace("-", string.Empty),
                            record.GetInt32(FIELD_TEST_ID),
                            record.GetBoolean(FIELD_FLIPPER)
                        );

                        lastRowVersion = rowVersion;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

Now run the following using SQL Management Studio to get the errors


--SET QUOTED_IDENTIFIER ON /* Without Error */
SET QUOTED_IDENTIFIER OFF /* With Error */
GO

INSERT INTO [dbo].[Test] ([TestId], [Flipper]) VALUES (2, 0);
GO

UPDATE [dbo].[Test] SET [Flipper] = ~[Flipper] WHERE [TestId] = 1
GO

SELECT * FROM [dbo].[Test]
GO

DELETE FROM [dbo].[Test] WHERE [TestId] = 2
GO

SELECT * FROM [dbo].[Test]
GO

So much like if you said to a doctor "it hurts when i touch it" and the doctor responds "well don't touch it" i know i can use "SET QUOTED_IDENTIFIER ON" in my application but i'm monitoring a table created and maintained by another application and can't control how they set the QUOTED_IDENTIFIER.

Is there a way to monitor the table without causing the issue demonstrated above?

Also can anybody explain why it happens?

I also found a similar question here (SQL Server: INSERT/UPDATE/DELETE failed because the following SET options have incorrect settings: ‘QUOTED_IDENTIFIER’) but it provided no clues on how to deal with this issue.

0

There are 0 answers