Archive for the 'SQL Server 2005' Category

Determining Active Directory group membership from SQL Server 2005

Have you ever needed to determine an authenticated user’s Active Directory (AD) group memberships from within SQL Server?

It makes sense if you are able to utilise AD to decide a user’s role or permissions in an application. This reduces the development time required to develop a custom permissions model, and allows the helpdesk, maintenance and operations team keep track of user privileges from one place.

It seems SQL Server 2005 does not have an out of box solution to this. Searching the various support forums and search engines lead me down the Managed .NET assembly route. Microsoft calls this building a database object with common language runtime (CLR) integration.

You may be asking yourselves… why not go down the SQL Server Integration Services route to gather an export from Active Directory… why would you query LDAP each time a query in SQL Server is executed? These are important design decisions that are really down to the required functionality.

You can find out another way to achieve this here: How To Get Active Directory Users and Groups Using SQL Server Integration Services SSIS 2005

Ok, so I am now going to build a CLR assembly in Visual Studio 2008 with the target framework set to .NET 3.5. SQL Server 2005 is actually .NET 2.0 targeted, but 3.5 uses the same CLR engine, so we are able to make use of things like LINQ and additional Active Directory components. [more on this later].

   1: namespace SpittingCAML.SQLCLR.DatabaseAssemblies 

   2: { 

   3:     using System; 

   4:     using System.Collections.Generic; 

   5:     using System.Data.SqlTypes; 

   6:     using System.DirectoryServices.AccountManagement; 

   7:     using System.Linq; 

   8:     using System.Text; 

   9:     using Microsoft.SqlServer.Server; 

  10:     using System.Data; 

  11:     /// <summary> 

  12:     /// this class contains the AD Integration methods (Stored Procedures) 

  13:     /// </summary> 

  14:     public class ADIntegration 

  15:     { 

  16:         /// <summary> 

  17:         /// this method determines if a user is a member of the supplied 

  18:         /// active directory group 

  19:         /// </summary> 

  20:         /// <param name="user">the user to search for</param> 

  21:         /// <param name="domain">the domain to search</param> 

  22:         /// <param name="groupName">the group to determine</param> 

  23:         /// <returns>true if access granted, false if not granted</returns> 

  24:         [Microsoft.SqlServer.Server.SqlFunction] 

  25:         public static bool IsGroupMember(string user, string domain, string groupName) 

  26:         { 

  27:             bool isGroupMember = false; 

  28:  

  29:             // can't proceed with lookup if supplied parameters are null 

  30:             if(string.IsNullOrEmpty(user) || string.IsNullOrEmpty(domain) || string.IsNullOrEmpty(groupName)) 

  31:             { 

  32:                 return isGroupMember; 

  33:             }           

  34:             using (var ctx = new PrincipalContext(ContextType.Domain, domain)) 

  35:             { 

  36:                 using (var grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, groupName)) 

  37:                 { 

  38:                     isGroupMember = grp != null && 

  39:                         grp 

  40:                         .GetMembers(true) 

  41:                         .Any(m => m.SamAccountName.ToLower() == user.ToLower()); 

  42:                 } 

  43:             } 

  44:             

  45:             return isGroupMember; 

  46:         } 

  47:     } 

  48: } 

Code Snippet 1 – the assembly that will be built into a DLL in Visual Studio 2008 (download a copy here: ADIntegration.cs)

The code snippet creates a class ‘ADIntegration’ in the ‘SpittingCAML.SQLCLR.DatabaseAssemblies’ namespace. This is important to remember, as it is required later when registering the assembly.

The functionality of this method is as follows:

  • User calls the method with the user name, domain and user group as parameters
  • System determines if supplied user is a member of the supplied user group for the supplied domain
  • System returns boolean true if user is a member and boolean false if not a member.
  • The method code is shamelessly stolen from StackOverflow, user huseyint. The rest is down to the style conventions of my organisation.

I have tagged the method ‘IsGroupMember’ with the ‘[Microsoft.SqlServer.Server.SqlFunction] tag to let SQL server know this is intended to be used in place of a typical SQL Server function.

One your assembly is built, it is almost ready to be put into a database of your choice, as an object. You’ll notice we make use of LINQ and Directory Services. However, since SQL Server 2005 is targeting .NET 2.0 it is necessary for you to install some pre-requisites in order to gain access to .NET 3.5 features. It should also be noted that not all .NET 2.0 features are available in SQL Server 2005, as the code has to meet certain criteria to be included in the ‘safe’ category.

Read more about safe, unsafe and external access assemblies: here.

The pre-requisites required are:

  • CLR enabled must be set on your database server – this enables CLR assemblies
  • The target database must be set to TRUSTWORTHY to allow UNSAFE assemblies to be run against it.
  • System.Core [.NET 3.5 core components]
  • System.DirectoryServices.AccountManagement [.NET 3.5 Active Directory specific methods]
  • System.DirectoryServices.Protocols [.NET 2.0 dependancy of System.DirectoryServices.AccountManagement]
  • System.DirectoryServices [.NET 2.0 dependancy of System.DirectoryServices.AccountManagement]

The pre-requisites are typically found in the install folders for .NET 2.0 and .NET 3.5

   1: -- must enable CLR on the database

   2:  

   3: EXEC sp_configure 'show advanced options' , '1';

   4: go

   5: reconfigure;

   6: go

   7: EXEC sp_configure 'clr enabled' , '1'

   8: go

   9: reconfigure;

  10:  

  11: DECLARE @dbName NVARCHAR(255)

  12: SET @dbName = '<database name here>'

  13: DECLARE @sql NVARCHAR(4000)

  14:  

  15: SET @sql = 'USE ' + @dbName;

  16:  

  17: -- must make database trustworthy

  18:  

  19: EXEC sp_executesql @sql

  20: EXEC sp_changedbowner 'sa'  

  21: SET @sql = 'ALTER DATABASE ' + @dbName + ' SET TRUSTWORTHY ON;'

  22: EXEC sp_executesql @sql

  23:  

  24: GO

  25:  

  26: DECLARE @DotNet2Folder NVARCHAR(1000)

  27: DECLARE @DotNet35Folder NVARCHAR(1000)

  28: DECLARE @path NVARCHAR(4000)

  29:  

  30: SET @DotNet2Folder = 'C:\Windows\Microsoft.NET\Framework\v2.0.50727'

  31: SET @DotNet35Folder = 'C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5'

  32:  

  33:  

  34: --if the [System.Core] assembly exists then drop it 

  35: IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'System.Core') 

  36:         DROP ASSEMBLY [System.Core] 

  37:  

  38: --if the [System.DirectoryServices.AccountManagement] assembly exists then drop it 

  39: IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'System.DirectoryServices.AccountManagement') 

  40:         DROP ASSEMBLY [System.DirectoryServices.AccountManagement] 

  41:  

  42: --if the [System.DirectoryServices.Protocols] assembly exists then drop it 

  43: IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'System.DirectoryServices.Protocols') 

  44:         DROP ASSEMBLY [System.DirectoryServices.Protocols] 

  45:  

  46: --if the [System.DirectoryServices] assembly exists then drop it 

  47: IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'System.DirectoryServices') 

  48:         DROP ASSEMBLY [System.DirectoryServices] 

  49:  

  50: CREATE ASSEMBLY [System.Core]  

  51: FROM @DotNet35Folder + '\system.core.dll'  

  52: WITH permission_set = UNSAFE  

  53:  

  54: CREATE ASSEMBLY [System.DirectoryServices.Protocols]  

  55: FROM @DotNet2Folder + '\System.DirectoryServices.Protocols.dll'  

  56: WITH PERMISSION_SET = UNSAFE  

  57:  

  58: CREATE ASSEMBLY [System.DirectoryServices.AccountManagement]

  59: FROM @DotNet35Folder + '\System.DirectoryServices.AccountManagement.dll'

  60: WITH permission_set = UNSAFE  

  61:  

  62: CREATE ASSEMBLY [System.DirectoryServices]  

  63: FROM @DotNet2Folder + '\System.DirectoryServices.dll'  

  64: WITH PERMISSION_SET = UNSAFE  

  65:  

  66: GO  

Code Snippet 2: running the above T-SQL script will enable CLR on the database server, ensure the database you are going to be using assemblies in is trustworthy and registers the pre-requisite components. (download a copy here: ActiveDirectory_PreReqs.sql)

If you make use of the code snippet you’ll need to supply your database name and the locations of the .NET 2.0 and .NET 3.5 DLLs. The pre-requisites are installed in UNSAFE mode since they contain code that is not certified safe. If you try to register the assemblies as SAFE you will be presented with an error showing the areas of the code that are not certified safe.

We are now ready to deploy our assembly that contains the code in Code Snippet 1.

   1: DECLARE @dbName NVARCHAR(255)

   2: SET @dbName = '<database name here>'

   3:  

   4: DECLARE @assemFolder NVARCHAR(1000)

   5: SET @assemFolder = '<assembly folder here>'

   6:  

   7: -- have to drop the function first because you can't delete the assembly

   8: -- when the function is dependant on it 

   9: IF EXISTS (SELECT * 

  10:        FROM   sysobjects 

  11:        WHERE  name = N'udf_IsGroupMember')

  12:     DROP FUNCTION dbo.[udf_IsGroupMember]

  13:  

  14: --if the [SpittingCAML.SQLCLR.DatabaseAssemblies] assembly exists then drop it 

  15: IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'SpittingCAML.SQLCLR.DatabaseAssemblies') 

  16:         DROP ASSEMBLY [SpittingCAML.SQLCLR.DatabaseAssemblies] 

  17:  

  18: CREATE ASSEMBLY [SpittingCAML.SQLCLR.DatabaseAssemblies]  

  19: FROM @assemFolder + '\SpittingCAML.SQLCLR.DatabaseAssemblies.dll'  

  20: WITH permission_set = unsafe  

  21:  

  22: GO  

  23:  

  24: /********************************************************************************

  25: *

  26: * Name: [udf_IsGroupMember]

  27: *

  28: *

  29: * Description: 

  30: *

  31: *       Function determines if the supplied user is in an active directory group

  32: *     

  33: *        NB: this is a MANAGED CODE function utilising the 

  34: *                   SpittingCAML.SQLCLR.DatabaseAssemblies assembly class

  35: *

  36: * Revision History:

  37: *

  38: * Version Date         Author(s)        Description

  39: * 1.0     08 Dec 2009  Martin Allen        Intial Release

  40: *

  41: ********************************************************************************/

  42: CREATE FUNCTION [udf_IsGroupMember]

  43: (

  44:  @user nvarchar(1000), 

  45:  @domain nvarchar(1000), 

  46:  @groupName nvarchar(1000)

  47: )

  48: RETURNS BIT

  49: AS EXTERNAL NAME [SpittingCAML.SQLCLR.DatabaseAssemblies].[SpittingCAML.SQLCLR.DatabaseAssemblies.ADIntegration].[IsGroupMember]

  50:  

  51: GO

Code Snippet 3: Deploying our assembly and creating a stub that will allow standard SQL stored procedures and functions call our assembly method as if it was a standard SQL function. (download a copy here: Deploy_Assembly.sql)

You will need to edit code snippet 3 with the database name and the folder that you have built the assembly [created by Code Snippet 1] to.

Once you have run this script, you are in the position to test it.

   1: DECLARE @user nvarchar(1000)

   2: DECLARE @domain nvarchar(1000)

   3: DECLARE @groupName nvarchar(1000)

   4:  

   5: -- TODO: Set parameter values here.

   6:  

   7: set @user = 'testuser'

   8: set @domain = 'mydomain'

   9: set @groupName = 'mygroup'

  10:  

  11: SELECT [dbo].[udf_IsGroupMember](@user, @domain, @groupName)

Code Snippet 4: testing the deployment was successful. (download a copy here: Test_Assembly.sql)

Set the parameters to something more meaningful to your Active Directory, and supply a valid domain and give it a go.

Now we have a useful function to utilise alongside standard T-SQL functions. I use it to replace legacy function calls to proprietary permission models to reduce maintenance costs!

Hope this helps you out! Enjoy Smile

SC

Further reading on SQL Server Assemblies



Report Viewer Control fails to render correctly in IE8

One of our more widely used application uses the Report Viewer Control to render SQL Server Reporting Services 2005 reports.

We are migrating to IE8 in the near future, so it was up to the various development teams to ensure compatibility. It should have been oh so easy :-)

Figure 1 shows the Report Viewer Control correctly rendering, a report is produced with twenty pages. Scrolling to the bottom of the control works as expected.

reportviewerissue003
Figure 1- Application renders correctly in IE6

Figure 2 shows the same page and content in IE8. All looks good until you scroll to the bottom of the report (shown in Figure 3).

reportviewerissue001
Figure 2 - All looks okay in IE8…. however….

reportviewerissue002
Figure 3 - Scroll bar not rendered correctly [circled in red], page number footer missing (it is visible in Figure 1 when rendered in IE6)

There is a workaround for this particular issue. It involves the DOCTYPE markup that you may have in your master page or each individual page.

You can read more information about DOCTYPES at Holly Bergevin’s page: DOCTYPE at Community MX

Removing it completely from your page will make the browser work in ‘quirks’ mode. You’ll find that enabling ‘quirk’ mode will ensure the report is rendered correctly. See MSDN social.

If like me, you’ve got your DOCTYPE in your master page, and you’d really like to keep your XHTML compliant markup in place for the majority of your pages, it is a little more tricky. Either code the DOCTYPE in each page (removing it from your Master page! yuck!)… or find another workaround.

I search in vain for a solution. If you can help, please comment on this article. It seems it is a known bug!

SC



Delivery of an important project, thanks to some clever shortcuts

This blog has been quiet for the last couple of weeks because I’ve been leading a development team in the final stages of delivery of a pretty large asset/stock tracking system.

Its a long story as to why we decided to do it all ‘in house’ rather than to buy a COTS product. Lets just say, we’d already tried buying off the shelf and that’s the reason why we started doing our own.

The budget was very challenging and the development team grew large in the space of a few weeks when we realised that the estimated man hours required to do it was going to take more than four full time developers.

Communication

With a large development team, in disparate locations around the UK it is important to get the communication channels right. My organisation had rolled out text based communication similar to Live Messenger (Office Communicator). This proved the first clever shortcut. This tool allowed us to collaborate in real time, using virtual whiteboards.

WAN based software configuration management. This was very important, it meant we could all see the same page, and nothing was stuck on Fred’s laptop when he went on leave :-)

Hudson – continuous integration testing, ensured that we never dirtied our source code, so we didn’t hear any of the usual ‘why does it not build when I check it out?’ type crying from the team. I really liked the fact you could schedule builds and output the successful build number to SharePoint using it’s RSS feeds. This little gem saved the bacon many a time… although until the developers got used to not treating our source code control system as a backup for their work in progress, the automatic emails telling everyone (including the PM) that the build failed was a little annoying ;-)… I loved it, as there was no excuse for checking in ‘work in progress’  :-D

SharePoint 2007 for project documentation. For much the same reason why we had WAN based software configuration management. SharePoint helps anyone on the internal intranet see what we are up to … and the project manager can stick the GANTT and meeting minutes somewhere too :-)

Process

Those of you who have read my blog before will remember my ramblings about which software development methodology should you choose. We chose to use OpenUP, which is the open source version of the Rational Unified Process. I was a little sceptical about its adoption in the embryonic stages of the project, but it certainly helped us stay on course. It also ensured we followed a process that industry knows about. The iterative nature of these kinds of processes ensure that higher management get to see what us technical types are up to, which keeps the pressure off, because at the end of each iteration there is a defined output and testing stratagem.

Technology

We had to keep it all web based, so it could work over the intranet… and we were limited to IE6 because my organisation hates upgrading and is very risk adverse. I can hear you all groaning… but what about all the security patches etc. etc… yeah I know… I’ve sat across the table from the infrastructure guys and have tried to explain that, but we are only developing for our internal intranet… what could go wrong with that ;-) … basically it is out of my pay bracket to argue with the people that make that kind of decision.

As my department is well versed in C# and ASP.NET (3.5) this was our chosen technology base… we paired this with SQL Server, as we are also well versed in that.

During the design phase it was glaringly obvious that the standard AJAX toolkit combined with the standard ASP.NET controls was not going to cut the mustard with the requirements that had been elicited… and we needed rapid application development, and slick results.

We turned to Telerik’s ASP.NET AJAX control library and it saved us a small fortune. I’m sceptical about most third party products… and I was very sceptical about using it in such a ‘flagship’ project, however my fears were unfounded. It seems you can sell a product with these controls in, and you get all the source code for them… and you can modify the source code for them providing you let Telerik know what you did. They also provide an excellent forum for dealing with problems. If you use a search engine and look up ‘RadGrid’ the Telerik equivalent of a GridView or DataGrid, you still get hundreds of hits :-)

We used the windows style dialog manager (RadWindow and RadAlert), menu control (RadMenu), grid view (RadGrid), combo box (RadComboBox), numeric only text boxes with extras (RadNumericTextBox) and many of their date pickers to name a few. Not only did we use them, we also used them alongside existing AJAX and standard .NET controls with no issue. I think if we did not buy this toolset, we would have spent hundreds of hours doing what they have already done for us, and I bet ours wouldn’t have been as slick, or as tested as theirs. The development team and I weren’t sad that they’d taken the raw control building away from us (and I thought it would incite a riot by the coffee machine!). One of the most pleasing aspects about it is that the customer is happy that we have delivered a sophisticated interface that is user friendly. The window manager allows users to resize, drag and minimise functionality on our web pages much the same as they do on their windows desktop machines.

Our next cost saving came came with the ReportViewer control. We’d been bitten badly in previous projects when using Reporting Services. Is it just me, or does it not feel like a finished product? Problems we had in the past were cross domain access / the classic double hop problem (impersonation) and unstructured website navigation (that one was probably more our fault). The ReportViewer control gives us a little more freedom, we can construct dynamic object data sources in our business layer, and create reports on the fly, on the actual page rather than firing off our requests to the Reporting Services engine running on another site collection in IIS. This eliminates authentication issues and allows you to put the report that is generated anywhere on your page. Our user’s like it a lot :-).

SQL Server Integration Services (SSIS) is looking like it will save us money once we’ve worked out how to use it properly ;-). The COTS product that we’re replacing has a database backend that we can connect to using SSIS, and the customer is providing us data in excel. This is a useful took to help shape and data cleanse on the way into the new system

Final thoughts

User training is now underway and the User acceptance test is around the corner, and I think it will be a big success for the team.

SpittingCAML



InfoPath and SharePoint verses ASP.NET and a Traditional Database verses ASP.NET and using SharePoint as a database technology

I was recently asked by a colleague

“I’ve got to build a new application to support x (an anonymous set of requirements that I cannot divulge here!), I’ve not got long to do it, and my developer resources are thin on the ground. I’ve heard you talk about SharePoint and InfoPath, and need to call on your experience, do you think I could develop my application using those two technologies? It requires a complex interface layer and needs to be able to provide neat looking reports.”

Okay I said, I’ll give you my experiences in the form of some potential solutions and potential pros and cons. I realise by posting this I’m likely to anger the gods and provoke some really large debate… but that was my plan all along :-)

 

So your decision basically is between three development strategies/options

  1. InfoPath and SharePoint 2007 (MOSS)
  2. ASP.NET and MOSS
  3. ASP.NET and SQL Server 2005

This means the first step is to consider the requirements for the interface layer (IL)… ask yourself: will the user want to do anything fancy on the front end? e.g. sorting data grids, combo boxes, interface with external system. If the answer to that is yes, then you’ll probably want to consider an ASP.NET front end.

If the user really only requires a simple form, then InfoPath is a good choice for the IL… but to make the waters even more murky you’ll need to consider the storage/reporting requirements as InfoPath on it’s own will only offer XML based storage, either on disk, email or SharePoint forms library. ASP.NET forms are more flexible and can enable you to store the data in a SharePoint list, database or if you really wanted, and XML file.

InfoPath pros and cons
Pros

  • Forms can be produced by pretty much anyone with no training
  • Simple to build prototypes (quick and cheap)
  • Easy for user’s to use and understand
  • Allows offline editing (by saving the form to local hard drive)
  • Doesn’t need to be designed in detail before development can be started

Cons

  • Which version of InfoPath does your corporate desktop/laptop build support? InfoPath 2003 is getting a little tired now (this means it’s old, and wont support newer controls, and will limit the ‘code behind’ that you can produce)
  • InfoPath does not allow you to build flexible, custom interfaces
  • Can’t reuse rules from other forms without having to recreate them
  • Rules are difficult to navigate/debug
  • Difficult to migrate (without reworking the forms)
  • If used in conjunction with an SharePoint form library, the coupling is very tight, so if you move the site/rename it you might have to alter the form

ASP.NET pros and cons
Pros

  • Can do whatever you like (within reason) as you have access to .NET 3.5. [this includes things like sending email etc.]
  • Can produce flexible interfaces
  • Easy to debug using Visual Studio
  • Can reuse code and layouts using classes and master pages
  • Can interface with SharePoint, SQL Server, Oracle, XML and lots of other ODBC compliant technologies

Cons

  • Requires that the developers have ASP.NET training
  • Prototypes take longer to build than in InfoPath
  • Does not allow offline use, without extensive development of a side by side offline system
  • Users may require training if something is ’specialised’
  • You need to design the pages (if you want a sensible solution)

You can also have a read of my blog: http://blog.mgallen.com/?p=206, where I’ve linked to Jason Apergis’ blog who explains the pros and cons in a workflow context, but he decides that InfoPath is better for his organisation.

Now you can compare traditional databases and SharePoint

SharePoint pros and cons
Pros

  • Easy to build sites and site collections (quick and cheapish)
  • Has plethora of web parts that can be dragged and dropped by novice users to create dynamic content
  • Links well with InfoPath
  • List items can be produced via MOSS API and Web Services from other technologies such as ASP.NET
  • Sites can be generated through the MOSS API
  • Does rudimentary version control (albeit not in the best possible way… perhaps this isn’t a pro after all :-))
  • Can create production level sites/storage facilities without a detailed design

Cons

  • It should not be used like a traditional database (… and can’t really be used like one either as it can’t do joins between lists)
  • Difficult to report from MOSS lists and libraries, although you can used Reporting Services to query lists it is generally more difficult compared to SQL queries
  • Uses lots of hard drive space (the MOSS database grows quite large)
  • It is not straight forward to migrate from a dev environment to a live environment

Traditional Database (e.g. SQL Server 2005)
Pros

  • Very flexible
  • Can use proper joins, sorts
  • Links very well with Reporting Services to produce powerful outputs
  • Links very well with ASP.NET and other .NET technologies

Cons

  • Requires a detailed design (or not… but don’t do that to yourself!)
  • Can’t be used directly with InfoPath
  • Requires a production and dev server in an ideal world

Okay, so if you read between the lines… I think you should go for options 2 or 3… preferably 3.

The perception is that as its quick and cheap to use InfoPath and SharePoint… and that perception is right 90% of the way…. You’ll find that once you’ve done 90%… The last 10% will take you an absolute age, and will probably consist of workarounds, squirming out of meeting requirements and swearing at the computer.

The decision is yours, so be pragmatic, and assess the requirements in front of you, and ask difficult questions to try to ascertain whether any potential requirements creep puts you in the ASP.NET frame or the InfoPath frame. If reporting is a major player, I would urge you to think about using SQL Server and Reporting Services.

I hope this has helped you a little bit anyway, good luck :-)

SpittingCAML



Stuff we should know, but always have to look up: SQL Server 2005 - setting the default directory for the mdf/ldf

Technology Yogi has done an easy to follow article on it: here

Essentially, run the following commands, assuming ‘D:\SQL_Data’ is where you want to place the mdf and ldf files.

USE [master]
GO
EXEC xp_instance_regwrite N’HKEY_LOCAL_MACHINE’, N’Software\Microsoft\MSSQLServer\MSSQLServer’, N’DefaultData’, REG_SZ, N’D:\SQL_Data’
GO
EXEC xp_instance_regwrite N’HKEY_LOCAL_MACHINE’, N’Software\Microsoft\MSSQLServer\MSSQLServer’, N’DefaultLog’, REG_SZ, N’D:\SQL_Data’
GO

SpittingCAML



SQL Server Agent and StartServiceCtrlDispatcher (error 6)

Well, the aftermath from the issue we had earlier in the week continues. To mitigate any further log expansion problems, we decided to migrate all the logs and error outputs to another partition.

This went smoothly and all the SQL Server error logs now appear on the other partition, however the SQL Server Agent now fails to start…

To be more precise, it starts and then immediately stops. To diagnose the issue I copied the command that was being run as a service to the command line to see if anything more useful was displayed. No errors were present in the application or system event logs.


Figure 1: SQL Agent Service startup from the command line

A quick google for StartServiceCtrDispatcher though lots of potential solution, however none that could have helped in this case.

The issue should have been obvious, we had just moved the location of the logs, including the logs created by the SQL Server Agent.

You can set the log, either though the registry editor, or through SQL Server Configuration Manager.


Figure 2: Registry key to check


Figure 3: Permissions to allow on your new log location(s)

As you can see, under the SQL Server instance, is the error log path. If this log path is incorrect, or the user (or service account) starting the SQL Server Agent cannot write to this path you will get the StartServiceCtrlDispatcher (error 6).

I hope this helps in your diagnosis if it happens to you.

SpittingCAML



Dead SQL Server 2005 box… could it be down to a cursor?

Today was an odd one… a colleague and I were doing some eXtreme Programming (XP)…. i.e. we needed to solve an issue with one of our many ASP.NET applications quickly!!

We were looking at a stored procedure that contained a cursor. We then fiddled a bit and commented out lots of it out to aid our debug… In our excitement to solve the problem we made the schoolboy error of not un-commenting the CLOSE and DEALLOCATE steps in the cursor when we reinstated it.

The web page linked to the stored procedure was loading in the browser… it took an age to appear… alarm bells were ringing! What had we done… was there an infinite loop cursor in progress?

I logged into SQL Server Management Studio to run a diagnostic query. The diagnostic query I was running was very similar to the excellent one Glenn Berry has published. It checked for obvious issues such as

  1. High CPU load operations
  2. Blocking queries
  3. Transaction log size
  4. Network and I/O issues

I was not unsurprised to see that the transaction log on tempdb was full. Not being a SQL expert in the slightest, I called the Operations Manager over for assistance. Remote Desktop to the machine was also non functioning… what had we done! Could a messed up cursor really cause all these issues?

Well, the investigations continue. If you experience similar issues to this, definitely look at Glen Berry’s SQL script and the following Microsoft support articles

  1. Causes of Transaction log expansion
  2. Unexpected transaction log growth

enjoy

SpittingCAML



Investigate and debug your SharePoint application…. with the help of some nifty tools

Have you ever trawled through megabytes of log files using a text editor and a search keyword and thought to yourself…. there must be a better way! Well it turns out that there are FREE utilities out there to be used!

Log Parser, can parse your logs and enable you to perform SQL like queries on them.

LogParser 2.2
Figure 1: Log Parser 2.2 by Microsoft 

See more information about Log Parser at  the University of Delaware’s Cyber Crime Unit pages

SQL Nexus can analyse your SQL Server performance and produce funky graphical displays.

sql_nexus
Figure 2: SQL Nexus (requires Log Parser 2.2)

See more information on SQL Nexus at "There’s something about SQL blog".

PAL Tool can analyse your performance monitors.

paltool
Figure 3: PAL (Performance Analysis of Logs) Tool

More information on the PAL tool can be found at the Management Technologies blog

This is just a sample of utilities to help the SharePoint administrator or developer… there are many more as posted on the Debugging Toolbox blog

SpittingCAML



T-SQL Helper Tables… and generating dynamic email bodies… and lots more

While searching for a neat way to build the body of my emails for a particular application from database data I stumbled across a fantastic article by Robyn Page and Phil Factor (aka Database Mole). The article is entitled ‘The Helper Table Workbench’ and details how using a simple table filled with numbers from 1 to 10,000 can really help reduce the use of CURSOR and other slow T-SQL commands.

The section that interested me the most is by using this helper table it makes building the body of your email using T-SQL a piece of cake.

The function below that uses these principles (of the helper table) to do macro substitution of values into a string. It will work for replacing XHTML placeholders with a value, or producing error messages in a variety of languages. In fact, uses keep popping up for this sort of function. In this version, you are able to specify what strings are used for substitutions (the default is %1, %2, %3 etc,) and what you use as the delimiter of your list of macros and values.

CREATE FUNCTION [dbo].[ufsSubstitute]
(
@Template VARCHAR(8000),
@macroList VARCHAR(8000),
@valueList VARCHAR(8000),
@Delimiter VARCHAR(255)
)
RETURNS
VARCHAR(8000)
AS
BEGIN
DECLARE @macros
    TABLE
     (MyID INT IDENTITY(1,1),
    variable VARCHAR(80))
DECLARE @values
    TABLE
    (MyID INT IDENTITY(1,1),
    [value] VARCHAR(8000))
--get all the variables
INSERT INTO @macros (variable)
    SELECT
    SUBSTRING
    (@MacroList+@Delimiter,
     number,
    CHARINDEX(@Delimiter,
        @MacroList+@Delimiter,
             number) - number)
FROM Numbers
WHERE number <= LEN(@MacroList)
    AND SUBSTRING(@Delimiter + @MacroList,
         number, LEN(@delimiter))
             = @Delimiter
ORDER BY number 

INSERT INTO @values ([value])
    SELECT SUBSTRING(@ValueList+@Delimiter, number,
        CHARINDEX(@Delimiter,
         @ValueList+@Delimiter, number)
             - number)
FROM Numbers
WHERE number <= LEN(@ValueList)
AND SUBSTRING(@Delimiter + @ValueList, number,
        LEN(@delimiter)) = @Delimiter
ORDER BY number 

SELECT @Template=
    REPLACE(@Template,COALESCE(variable,
                '%'+CAST(v.MyID AS VARCHAR)),
                [value])
FROM @values v
LEFT OUTER JOIN @macros m ON v.MyID=m.MyID
    WHERE CHARINDEX(COALESCE(variable,'%'+
        CAST(v.MyID AS VARCHAR))
                    ,@Template)>0 

RETURN (@Template) 

END

It is possible to call this T-SQL function in many ways, using templates, macro lists, value lists and delimiters. See the examples below:

SELECT dbo.ufsSubstitute (NULL,NULL,'',',')
SELECT dbo.ufsSubstitute ('','','',',')
SELECT dbo.ufsSubstitute (
    ' views','','6',',') 

DECLARE @K2String VARCHAR(1000)
SET @K2String = 'Spitting,' + 'CAML,'
+ @ApproveDocument
+ ',' + @DestinationUser + ','
 + GETDATE() + ',' + @TaskListURL + ','
+ @ViewFlowURL + ',' + @MOSSUrl

SELECT dbo.ufsSubstitute ('
Dear $1 $2,
Your K2 task $3, has been actioned by $4
to on £$5.
Please check your K2 tasklist, $6, view flow $7 or
 MOSS Site $8 which shows all open tasks
that are available to you',
    '$1,$2,$3,$4,$5,$6,$7,$8',
    @K2String,
    ',')
SELECT dbo.ufsSubstitute ('To @Destination;
Report dated @Date
The @table table is now @rows long.
please @action'
,'@Destination|@Date|@Table|@rows|@action',
'Phil Factor|12 Apr 2007|Log|1273980|truncate','|')
SELECT dbo.ufsSubstitute (
'I thought that your present of a %1 was %2.
Thank you very much.
The %1 will come in handy for %3'
    ,''
    ,'trowel|absolutely wonderful|gardening','|') 

This is going to prove very valuable to allow me to build emails on the fly for many applications including hopefully the K2 [blackpearl] email bodies.

Enjoy

SpittingCAML



Backing up the K2 [blackpearl] SQL Server Database

I was about to write a long and detailed article on how to backup all the databases associated with a K2 [blackpearl] install… but then I discovered that someone had beat me to it!

So, for the Operations team in my department, here’s all you need to know: Database script to backup K2 databases from http://www.k2thought.com

It should also be noted here that it is essential that you back these databases up at the same frequency as you back up MOSS or Reporting Services databases to maintain the synchronisation if your server farm fails.

SpittingCAML




You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.