Archive

Archive for the ‘Sql Server’ Category

Lost Database Master Key Password

November 3, 2011 1 comment

I had a situation occur recently where I needed to restore a DB to a new server but I didn’t have the ability the bring the Service Master Key over with it. Nor did I have the original password used to encrypt the Database Master Key (it was before my time).

What I did have was access to the box with the database still running and the associated key hierarchy still operational (symmetric keys and the like).

I’ve not done too much messing around with the database master key before, feeling it was best left well alone, however the need to migrate this DB was there and I needed to pull it across without loosing access to the encrypted data.

To prototype this activity out I created a database on my local DB server and utilized the services of another Sql Server 2008 Server elsewhere in the organization.

So, first off I needed to create the key hierarchy and a place to store my test encrypted data:

Create Master Key Encryption By Password = 'abc123!!!ORIGINAL';
Create Asymmetric Key AKey With Algorithm = RSA_2048;
Create Symmetric Key SKey With Algorithm = TRIPLE_DES_3KEY
    Encryption By Asymmetric Key AKey
Create Table dbo.ASaucerfulOfSecrets
    (
        TheSecret        VarBinary(MAX)
    )
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Insert Into dbo.ASaucerfulOfSecrets Values
    ( EncryptByKey(Key_Guid('SKey'), N'Ssssh! This is a BIG secret!'
                                   , 1, 0x12345));
Close Symmetric Key SKey;

I confirmed that encryption was working as expected by simply pulling the data back:

Select *
From dbo.ASaucerfulOfSecrets
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Select Cast(DecryptByKey(TheSecret, 1, 0x12345) As NVarChar(MAX))
  From dbo.ASaucerfulOfSecrets;
Close Symmetric Key SKey;

Which gave me the following results

TheSecret

0x00496C61F87C4941BF7CBE8C879146BA0100000043C4B3F8824C73EBF3C0…

(No column name)

Ssssh! This is a BIG secret!

Life so far was operating within the expected parameters. Now I backed up the DB and restored it to a different server.

Running the select again I got the expected exceptions raised due to the inability to open the Database Master Key:


(1 row(s) affected)

Msg 15581, Level 16, State 3, Line 3

Please create a master key in the database or open the master key in the session before performing this operation.

(1 row(s) affected)

Msg 15315, Level 16, State 1, Line 5

The key ‘SKey’ is not open. Please open the key before using it.

So, now came the exercise of correcting the issue. Given that we had supposedly lost the password to the original DB, it’s safe to assume that one would want to recreate it (and this time, write the bloody thing down somewhere!). So that’s the approach I took. Back on the source server I ran the following:

Alter Master Key Regenerate
    With Encryption By Password = 'abc123!!!New'

The Books OnLine warns that this can be an intensive process and I suppose it comes down to just how many keys you have defined in your hierarchy. For the purposes of this test I had but one key so I wasn’t worried. Now I had a Master Key password that I knew of so I performed another backup, moved the DB to the secondary server and restored it. Just for giggles I reran the select again and received (rather unsurprisingly) the same results.

Next I tried the select again but only after explicitly opening the Database Master Key (with the ‘new’ password):

Open Master Key Decryption By Password = 'abc123!!!New'
Select *
From dbo.ASaucerfulOfSecrets
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Select Cast(DecryptByKey(TheSecret, 1, 0x12345) As NVarChar(MAX))
  From dbo.ASaucerfulOfSecrets;
Close Symmetric Key SKey;

This did indeed pull back the decrypted data. Now for the final action – adding the Service Master Key to the Database Master Key:

Open Master Key Decryption By Password = 'abc123!!!New';
Alter Master Key Add Encryption By Service Master Key;

This enabled the encryption to use either an explicitly opened master key, or use the service master key – which is exactly what I was after.

I was able to confirm the results by performing the select again (without opening the master key first) and this gave me my encrypted data back. Tada!

Categories: Encryption, Sql Server, T-Sql

SSL certificate on a Sql Server 2005 instance failing with a 0x8009030d error

October 26, 2011 Leave a comment

This one had me stumped for days. Fortunately MS support came to my rescue with a simple answer.

The problem I was having was getting Sql Server to load an SSL certificate to encrypt communications between client and server on a clustered instance. No matter how many times I tried, Sql refused to run issuing a 0x8009030d error code when attempting to load the certificate. I was always getting the following logged in my ErrorLog:

Server The server could not load the certificate it needs to initiate an SSL connection. It returned the following error: 0x8009030d. Check certificates to make sure they are valid.

Server Error: 26014, Severity: 16, State: 1.

Server Unable to load user-specified certificate. The server will not accept a connection. You should verify that the certificate is correctly installed. See "Configuring Certificate for Use by SSL" in Books Online.

Server Error: 17182, Severity: 16, State: 1.

Server TDSSNIClient initialization failed with error 0×80092004, status code 0×80.

Server Error: 17182, Severity: 16, State: 1. 2011-10-26 07:26:42.75 Server TDSSNIClient initialization failed with error 0×80092004, status code 0×1.

Server Error: 17826, Severity: 18, State: 3.

Server Could not start the network library because of an internal error in the network library. To determine the cause, review the errors immediately preceding this one in the error log.

Server Error: 17120, Severity: 16, State: 1.

Server SQL Server could not spawn FRunCM thread. Check the SQL Server error log and the Windows event logs for information about possible related problems.

I’d come across a few posts that mentioned this error and that Sql was having issues with the Private Key but they suggested using commands from the Resource Kit to fix the issue – something I could not do. To make matters worse (as my previous post described) Checkpointing kept on getting in my way of removing the certificate, just to get Sql to start. I’d even tried things like adding the Service Account in the Administrators group in the vain hope that it was a permissions issue.

So after spending several days of tearing my hair out (and running out of time to get this fixed) I came to the conclusion I needed to bring in outside help – MS Support to the rescue!

And the answer? It was as simple as deleting the previously imported certificate then re-importing it using the domain account that runs Sql. Yup, it was that simple…

Thanks Gary!

Categories: Cluster, Sql Server, SSL

Sql Server secretly replicating registry keys on startup

October 20, 2011 Leave a comment

Not my find this but such a useful post I thought I’d repost it.

In my situation I had added a certificate’s thumbprint to the SuperSocketNetLib key on a new cluster which, for some reason Sql took a total and utter dislike to.

I thought (in my apparent naïveté) that I could simple remove the thumbprint from the registry, restart Sql and I’d be off and running.

Not so fast though. Every time I’d restart Sql Server it would repopulate the thumbprint back into the Certificate value then fail to start as a result. No matter how many times I rebooted, took the value out of the registry and twisted on my left foot whilst swinging a chicken in the air, Sql Server stubbornly and abjectly refused to stop repopulating the value with the bad certificates thumbprint.

User baby_cheeses on SqlServerCentral.com was able to find the magic formula for stopping Sql Server from magically repopulating the registry.

I amended baby_cheeses answer slightly to fit my situation, and it is…

  1. Remove the Certificate’s thumbprint from the registry.

  2. Start Sql Server using a Net Start MSSqlServer.

  3. Check the registry again and ensure thumbprint hasn’t returned.

  4. Start up Sql Server from within Cluster Administrator

  5. Check registry a third time and remove the thumbprint from the Certificate value.

  6. Move the cluster to another node and watch it start normally.

Like I say, I can’t take credit for this discovery.

Original post is here.

 

UPDATE

Apparently what is happening here is known as Checkpointing, where a copy of the registry is stored on the Quorum. MS gives an official method of avoiding the problem here, although I still think baby_cheeses solution is simpler and involves far less hacking around!

SHOWPLAN permission denied in database ‘master’.

Tricky lil’ bugger this one.

We hit this error when trying to run a Estimated Query Plan on a users’ query. Didn’t make any sense since we’re SysAdmin’s on the server;  doubly-so since we could run a showplan elsewhere.

Turns out the offender in question was a call to msdb.dbo.sp_send_dbmail. Apparently bad funky things happen there if one tries a showplan on it. To solve it we simply commented out the call to the sendmail procedure out and then, the sky’s did doth part and our estimated query plan presented itself unto us..

Good to know…

Categories: Grumbles, Sql Server

Target.com: Inventory like it’s 1999!

January 22, 2011 Leave a comment

I like online shopping – I do it a lot. I’ll admit that Amazon.Com has me hooked and I’d spend even more money there if the wife wouldn’t cut my fingers off at the core as a result. Target.Com is a new one for me to visit though. Done the retail store (a lot) and like it a lot (beats Wally World any day of the week), but their online inventory leaves something to be desired.

They try hard hard – real hard. When an item’s in stock they not only tell you that but also which Aisle it’s in (or endcap thereof). Real nice and useful however a total FAIL when it comes to reliability.

For me if your data’s not 100% correct it’s not worth pushing. And in Target’s case they seem to have an issue with the number of items on hand bit. Depending on whom we spoke to (wither store or via the phone) that figure could be 24 hours or even more out of date ; The web site continued to proudly inform us that our local Target had a particular item in stock even when it didn’t. Even checking over 24 hours later it still told us it was available even though it wasn’t.

This effect reminds me of a crowd I did some Sql Work for late in the last c. whilst I was still in the UK. Their name shall remain known only to me and them, however the story’s true. This crowd wanted an online system to punt their wares to Joe Consumer – all wizzywzggy flash driven utilizing Sql Server on the front and back end with replication to handle to the orders. Real nice right up to the point when there order’s then ended up on a printer somewhere awaiting an operator to actually input the order to the mainframe ‘at some later point’. This of course meant the at any point in time the inventory could could vary from accurate to ‘just a stab in the dark’.

I was reminded of this antiquated hand-cranked this system with my experience at Target; if you’re going to have an inventory indicator then it’d had better be accurate to, I don’t know, the last 60 minutes at least or else it devalues the web site forcing the consumer to have to resort to the phone to double check the inventory prior to (in our case) traipsing out in the bloody snow and cold).

Categories: Grumbles, Inventory, Sql Server

Resilient and unstoppable

December 30, 2010 Leave a comment

This post is a little on the late side, however better late than never…

We’ve been using Idera’s Sql Compliance Manager at work for some time to provide a good healthy audit trail of all user database changes, good or bad, on the production DB’s. As our use grew so did it’s unsuitability to the server it was located on (needed a TB more data than the few hundred GB currently on offer would allow!). So that led me to make the biggest move yet when I had to copy nearly a year’s worth of audit data (took about 24 hours across a 1GB pipe!), followed by a total reinstalled and repointing from the client DB Servers.

I’m extremely proud to report that throughout this downtime (nearly 2 days in total), Idera didn’t miss a beat and ALL activity that took place during the move was picked up as soon as the new server came online and within a day you’d have never known there had even been the potential of a blip.

Nice work Idera – Mark me down as extremely bloody impressed.

Categories: Auditing, Idera, Sql Server

Quest Spotlight 7.0 and “Collection: ‘IndexLatchWaits’ failed : Violation of UNIQUE KEY Constraint…” error

If you’ve just installed or upgraded to Quest Spotlight 7.0 and and getting this odd error for this counter then get Support to send you version 7.5.0.4648 of QS_IndexLatchWaitTimes.sql.

The reason for the exception is because it’s not taking Schema’s into account when inserting unique values into a temporary table, thus the 7.0 RTM version currently errors out if you have the same constraint or index name for the same table for tables under different schemas.

The same script affects both 2005 and 2008 so the patch will need to be installed in two locations – Quest will (should) provide all the details…

Categories: Quest, Sql Server

Deploying signed assemblies in a single script

November 18, 2009 Leave a comment

I’ve been writing a set of routines to control my database backups recently and one part of this was a CLR User Defined Data Type I’d created that allows some rudimentary file handling (with the appropriate security). By it’s very nature this assembly required the EXTERNAL ACCESS privilege and this is where the head-scratching came about.

Liking clean install scripts with minimal payloads (single scripts are easier to check into Source Code and deploy across multiple environments) I needed some method of encapsulating the deploy and ensuring the appropriate asymmetric key, users etc. were created. However normally this would require either the key file or the assembly DLL to be copied along with the .Sql script – and this would break my 1 script rule! So next idea was to create the asymmetric key from the assembly directly within Sql server – but I couldn’t do that until I’d installed the assembly which would fail because it required the EXTERNAL ACCESS privilege; Thus leading to another game of Chicken and Egg.

The solution was remarkably obvious – use a temporary SAFE assembly to create the asymmetric key then dispose of it directly afterwards – after all the assembly is only required at this juncture to create the asymmetric key.

The first section of the script deals with previous execution cleanups etc:

  1. Use RandomThunks
  2.  
  3. ————/ Start stage 1 : Drop previous objects /————————————————–
  4.  
  5. If Exists ( Select 1 From sys.assembly_types As T Inner Join sys.schemas As S On T.schema_id = S.schema_id Where T.name = N'SqlFile' And S.Name = N'Maintenance.Database.Archives')
  6.     Drop Type [Maintenance.Database.Archives].SqlFile;
  7.  
  8. If Exists (Select 1 From sys.assemblies Where name = N'RandomThunks.CLR.SqlServerFileUDT' and is_user_defined = 1)
  9.     Drop Assembly [Skipjack.CLR.SqlServerFileUDT];
  10.  
  11. – This next key should never exist when we run this as it's only use to create the asymmetric key before we Grant the External Access right to it.
  12.  
  13. If Exists (Select 1 From sys.assemblies Where name = N'TEMPORARY. DO NOT USE!' and is_user_defined = 1)
  14.     Drop Assembly [TEMPORARY. DO NOT USE!];
  15.     
  16. If Exists ( Select 1 From sys.server_principals Where name = 'SqlServerFileUDTUser' )
  17.     Drop Login SqlServerFileUDTUser;
  18.     
  19. If Exists ( Select 1 From sys.asymmetric_keys Where name = 'SqlServerFileUDTKey' )
  20.     Drop Asymmetric Key SqlServerFileUDTKey;
  21.  
  22. ————/ End stage 1 : Drop previous objects /—————————————————-

And the second part deploys the assembly with the requisite privilege:

  1. ————/ Start stage 2 : Object Creation /——————————————————–
  2.  
  3. Declare @Assembly VarBinary(MAX);
  4. Set @Assembly = 0x4D5A9000030000000
  5.  
  6. If Not Exists ( Select 1 From sys.schemas Where Name = 'Maintenance.Database.Archives')
  7.     Exec sp_ExecuteSql N'Create Schema [Maintenance.Database.Archives]';
  8.  
  9. – Create a temporary assembly that we'll use to build the Asymmetric Key from
  10.  
  11. Create Assembly [TEMPORARY. DO NOT USE!] Authorization dbo
  12.     From @Assembly
  13.     With Permission_Set = Safe;
  14.  
  15. Create Asymmetric Key SqlServerFileUDTKey
  16.     From Assembly [TEMPORARY. DO NOT USE!];
  17.  
  18. – Drop the temporary assembly – it's use is over.
  19.  
  20. Drop Assembly [TEMPORARY. DO NOT USE!];
  21.  
  22. Create Login SqlServerFileUDTUser
  23.     From Asymmetric Key SqlServerFileUDTKey;
  24.  
  25. – This needs to be run at the Server level – encapsulating it in a sp_ExecuteSql allows the script to be DB name agnostic.
  26.  
  27. Exec sp_executeSql N'
  28.     Use master;
  29.  
  30.     Grant External Access Assembly
  31.         To SqlServerFileUDTUser;';
  32.  
  33. – This is the real assembly
  34.  
  35. Create Assembly [Skipjack.CLR.SqlServerFileUDT] Authorization dbo
  36.     From @Assembly
  37.     With Permission_Set = External_Access;
  38.  
  39. Create Type [Maintenance.Database.Archives].SqlFile
  40.     External Name [RandomThunks.CLR.SqlServerFileUDT].SqlFile;
  41.  
  42. ————/ End stage 2 : Object Creation /———————————————————-

Categories: CLR, Sql Server, Workarounds

Sql Agent Session Storage

November 10, 2009 Leave a comment

I’ve been fiddling around with some jobs recently that required values to be kept from one job step to another. Not finding anything much exciting I went for plan Z – roll my own.

They’re simple procedures but do work nicely.

First I create a Schema that everything will be bundled under:

  1. If Not Exists ( Select 1 From sys.schemas Where Name = 'Aracs.Utilities.Jobs')
  2.     Exec sp_ExecuteSql N'Create Schema [Aracs.Utilities.Jobs]';

 

Now we’ll need a backing store:

  1. Create Table [Aracs.Utilities.Jobs].SessionSaverStore
  2.     (
  3.          JobId                UniqueIdentifier        Not Null
  4.         ,ParameterName        NVarChar(128)            Not Null
  5.         ,Value                Sql_Variant                Null
  6.     );

 

Next I create the SessionDestroyer Procedure (since it can be called from the next one this keeps the dependencies happy and calm):

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionDestroyer') Is Null
  2.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionDestroyer As';
  3.  
  4. Go
  5.  
  6. Alter Procedure [Aracs.Utilities.Jobs].SessionDestroyer
  7.  
  8. As
  9.  
  10.     Declare     @JobHex                    NVarChar(32)    
  11.             ,@JobId                        UniqueIdentifier
  12.     ;
  13.     
  14.     Select    @JobHex = Substring(Program_Name, 32, 32)
  15.       From    sys.dm_Exec_Sessions
  16.       Where    Session_Id = @@Spid;
  17.  
  18.     Delete    [Aracs.Utilities.Jobs].SessionSaverStore
  19.       Where    JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');

 

Along comes the SessionSaverStore next:

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionSaverStore') Is Null
  2.     Create Table [Aracs.Utilities.Jobs].SessionSaverStore
  3.         (
  4.              JobId                        UniqueIdentifier
  5.             ,ParameterName                NVarChar(128)
  6.             ,Value                        Sql_Variant
  7.         );
  8.         
  9. If Object_Id('[Aracs.Utilities.Jobs].SessionSaver') Is Null
  10.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionSaver As';
  11.  
  12. Go
  13.  
  14. Alter Procedure [Aracs.Utilities.Jobs].SessionSaver
  15.              @ParameterName                NVarChar(128)
  16.             ,@Value                        Sql_Variant
  17.             ,@StartFresh                Bit = 0
  18.  
  19. As
  20.  
  21.     Declare     @JobHex                    NVarChar(32)
  22.             ,@JobId                        UniqueIdentifier
  23.     ;
  24.     
  25.     Select    @JobHex = Substring(Program_Name, 32, 32)
  26.       From    sys.dm_Exec_Sessions
  27.       Where    Session_Id = @@Spid;
  28.  
  29.     Set @JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');      
  30.     If Exists ( Select 1 From MSDB.dbo.sysJobs Where Job_Id = @JobId )
  31.     Begin
  32.         If @StartFresh = 1
  33.             Exec [Aracs.Utilities.Jobs].SessionDestroyer;
  34.             
  35.         If Exists (
  36.             Select    1
  37.               From    [Aracs.Utilities.Jobs].SessionSaverStore
  38.               Where JobId = @JobId
  39.                 And ParameterName = @ParameterName
  40.         )
  41.             Update    [Aracs.Utilities.Jobs].SessionSaverStore
  42.               Set    Value = @Value
  43.               Where    JobId = @JobId
  44.                 And    ParameterName = @ParameterName;
  45.         
  46.         Else
  47.             Insert
  48.               Into    [Aracs.Utilities.Jobs].SessionSaverStore
  49.                     (
  50.                          JobId
  51.                         ,ParameterName
  52.                         ,Value
  53.                     )
  54.                 Values
  55.                     (
  56.                          @JobId
  57.                         ,@ParameterName
  58.                         ,@Value
  59.                     );
  60.     End

 

Finally the SessionRetriever:

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionRetriever') Is Null
  2.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionRetriever As';
  3.  
  4. Go
  5.  
  6. Alter Procedure [Aracs.Utilities.Jobs].SessionRetriever
  7.              @ParameterName                NVarChar(128)
  8.             ,@Value                        Sql_Variant        Output
  9. As
  10.  
  11.     Declare     @JobHex                    NVarChar(32)    
  12.             ,@JobId                        UniqueIdentifier
  13.             ,@Valuei                    Sql_Variant
  14.             ,@DataType                    NVarChar(128)
  15.     ;
  16.     
  17.     Select    @JobHex = Substring(Program_Name, 32, 32)
  18.       From    sys.dm_Exec_Sessions
  19.       Where    Session_Id = @@Spid;
  20.  
  21.     Set @JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');      
  22.     If Exists ( Select 1 From MSDB.dbo.sysJobs Where Job_Id = @JobId )
  23.     Begin
  24.         Select    @Value = Value
  25.           From    [Aracs.Utilities.Jobs].SessionSaverStore
  26.           Where    JobId = @JobId
  27.             And    ParameterName = @ParameterName;
  28.  
  29.     End

 

Use is pretty straightforward. As an Proof of concept I created a Sql Agent Job with two steps – Store’ and ‘Fetch’

Store contained the following:

  1. Exec [Aracs.Utilities.Jobs].SessionSaver 'Name', 'Fred', 1
  2. Exec [Aracs.Utilities.Jobs].SessionSaver 'Age', 37

 

And Fetch:

  1. Declare @Name Sql_Variant
  2. Declare @Age Sql_Variant
  3.  
  4.   Exec[Aracs.Utilities.Jobs].SessionRetriever 'Name', @Name Output;
  5.   Exec[Aracs.Utilities.Jobs].SessionRetriever 'Age', @Age Output;
  6.  
  7. Print Cast(@Name As VarChar(128)) + ' ' + Cast( @Age As VarChar(4));

 

Granted it’s missing some bells and whistles (like exception handling) and I’d wager I could stuff the Retriever in as a function. However as a first hit I was pretty chuffed with it!

Categories: Sql Server, T-Sql

Bulk Copy within a Sql Server CLR Stored Procedure

October 19, 2009 2 comments

Whilst it’s not immediately obvious, one can perform a Bulk Copy operation as part of a Sql Server CLR Stored Procedure. There are however some gotcha’s.

First off is that you need to stay away from Context Connections for the actual operation. Whilst you can technically use a Context Connection for the first part, I’d personally not recommend you do for reasons that will become apparent shortly.

The code below is taken from one of my procedures using a homebrew class that provides me with information regarding a table – including the various elements of a fully qualified 4 part name (Server.Database.Schema.Table).

The first part of the code handles the bulk import:

  1. using (SqlConnection sourceCx = new SqlConnection(string.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI;", sourceTable.ServerName, sourceTable.DatabaseName)))
  2.             {
  3.                 sourceCx.Open();
  4.  
  5.                 using (SqlCommand command = new SqlCommand(string.Format("Select * From {0};", sourceTable.FqTableName2Part), sourceCx))
  6.                 {
  7.                     using (SqlDataReader reader = command.ExecuteReader())
  8.                     {
  9.                         using (SqlBulkCopy bulkCopy = new SqlBulkCopy(string.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI;", destinationTable.ServerName, destinationTable.DatabaseName)))
  10.                         {
  11.                             bulkCopy.DestinationTableName = destinationTable.FqTableName2Part;
  12.                             bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
  13.                             bulkCopy.DestinationTableName = destination;
  14.                             bulkCopy.BatchSize = 10000;
  15.                             bulkCopy.NotifyAfter = bulkCopy.BatchSize;
  16.                             bulkCopy.WriteToServer(reader);
  17.                             bulkCopy.Close();
  18.                         }
  19.                     }
  20.                 }
  21.             }

 

As you can see I’m using two SSPI connections in place of the Context based Connection that’s normally provided. I decided to use SSPI to avoid needing to store user names and passwords. Your mileage and methodology may vary.

The next section is the reason why I advise not using a Context Connection for the DataReader:

  1. static void bulkCopy_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
  2.      {
  3.          string call = string.Format("RaisError('{0} rows copied', 0, 0) With NoWait", e.RowsCopied);
  4.          SqlContext.Pipe.ExecuteAndSend(new SqlCommand(call));  
  5.      }

 

In order for this event to work correctly the Context Connection must be free – which will not be the case if you use it for the DataReader. Running the RaisError via ExecuteAndSend ensures the results are not gobbled up by the any special pipe that SqlCommand.ExecuteNoQuery creates (see Vadim Tryshev’s excellent post on RAISERROR in CLR Routines). Finally I use a 0 for both the State and Severity to avoid any subsequent messages that RaisError would otherwise produce.

Sql Server 2008, SP1

Categories: C#, CLR, Code, Sql Server
Follow

Get every new post delivered to your Inbox.