navigation
 Wednesday, February 28, 2007

While I was writing a stored procedure that did some string manipulation and was debugging some unexpected behavior, I came across a fascinating, infurating, hidden-in-plain-sight "feature" of the LEN function: it returns the length of a string expression excluding trailing blanks. So, for example:

declare @string varchar(10)
set @string = '123456789 '

declare @len int
set @len = len( @string )

print @len
print right( @string, @len - 5 )

returns 9 and '789 ', not the expected 10 and '6789 '. Also, with this code:

declare @string varchar(10)
set @string = '123456789 '

declare @len int, @reverse_len int
set @len = len( @string )
set @reverse_len = len( reverse( @string ) )

print @len
print @reverse_len

One might expect that the length of a string would be the same forwards and backwards, but it isn't! @len = 9 and @reverse_len = 10!

A workaround for non-Unicode character data is to use the DATALENGTH function, which returns the size of an expression in bytes. If you choose to use DATALENGTH with Unicode data, you must divide by 2 to get the actual string length. Another workaround would be to append a non-blank character to the end of the string, get the length, then subtract one. Neither is very nice. If we had wanted to exclude trailing blanks, we could have called RTRIM before LEN ourselves.

posted on February 28, 2007  #    by Adam Anderson  Comments [1] Trackback

The T-SQL PRINT statement is the way to output plain text from SQL scripts and stored procedures. Unfortunately, it has a built-in limitation of printing 8000 non-Unicode or 4000 Unicode characters, and the rest is truncated. I was writing some SQL that generated lengthy SQL scripts that exceeded this limitation, and I wanted to create my own version of PRINT that would split my string at the nearest CRLF, print the part preceding the CRLF, then repeat on the rest of the string to the right of the CRLF. The CRLF itself would be omitted, since each PRINT statement appends its own to the output. However, my own string would sometimes contain two consecutive CRLF sequences for formatting purposes. My printing procedure would correctly strip the second one and leave the first, passing it to PRINT. But instead of my CRLF plus PRINT's, I only got one, spoiling my formatting. I ran the following test:

declare @cr char(1), @lf char(1)
set @cr = char(13)
set @lf = char(10)

print 'cr + lf' + @cr + @lf
print '---'

print 'cr only' + @cr
print '---'

print 'lf only' + @lf
print '---'

print 'lf + cr' + @lf + @cr
print '---'

And got the following output:

cr + lf
---
cr only

---
lf only

---
lf + cr

---

Interesting results! Notice that when a string is followed by CRLF, there is only one, not one plus one from PRINT. A solitary CR or LF will result in a new line plus one from PRINT, and LFCR results in two new lines plus one from PRINT. Since SQL Server honors a single CR or LF and they work as I expected, I updated my SQL generation code to use LF only as new line characters.

posted on February 28, 2007  #    by Adam Anderson  Comments [0] Trackback

I've been writing a lot of dynamic SQL that generates other SQL lately. While SQL isn't the best string manipulation language, it's got great native access to database schema information. However, my latest project was resulting in some very long scripts that coudn't be printed with PRINT, because it truncates the output after 8000 bytes, which is 8000 chars or 4000 nchars. One solution to this would have been to execute dynamic SQL to create a stored procedure containing the text, but in this case, I wanted to append multiple scripts together, so I chose to use PRINT as a simple way to output them all. The concept was simple: For each 4000 character Unicode block, find the last occurrance of CR, LF, or both, print the left part, and repeat on the right part. However, I ran into a few obstacles on the way; see my last two T-SQL Gotchas. Here is the final result, a general-purpose PRINT replacement that can handle any Unicode string, assuming that it contains at least one CR or LF every 4000 characters. The delimiter(s) could be parameterized, but I'll leave that as an exercise for you.

-- Works around the 4000/8000 character limit of the print statement
CREATE PROCEDURE dbo.LongPrint( @string nvarchar(max) )
AS
SET NOCOUNT ON

set @string = rtrim( @string )

declare @cr char(1), @lf char(1)
set @cr = char(13)
set @lf = char(10)

declare @len int, @cr_index int, @lf_index int, @crlf_index int, @has_cr_and_lf bit, @left nvarchar(4000), @reverse nvarchar(4000)
set @len = 4000

while ( len( @string ) > @len )
begin
   set @left = left( @string, @len )
   set @reverse = reverse( @left )
   set @cr_index = @len - charindex( @cr, @reverse ) + 1
   set @lf_index = @len - charindex( @lf, @reverse ) + 1
   set @crlf_index = case when @cr_index < @lf_index then @cr_index else @lf_index end
   set @has_cr_and_lf = case when @cr_index < @len and @lf_index < @len then 1 else 0 end
   print left( @string, @crlf_index - 1 )
   set @string = right( @string, len( @string ) - @crlf_index - @has_cr_and_lf )
end

print @string

posted on February 28, 2007  #    by Adam Anderson  Comments [1] Trackback
 Wednesday, February 21, 2007

Using is one of those dual use keywords in C#. Everybody uses using at the top of your code file so that you don’t have to type all of those long namespaces. The other use is for taking control over the closing and disposing of objects that support the IDisposable interface. Sometimes a little using goes a long way. One IDisposable class that is a good candidate for using is SqlConnection. Without using code to access a database, you would need to use a try-finally block to make sure you free up the resource.

 

SqlConnection conn = new SqlConnection(ConnectionString);

try

{

            ….

}

finally

{

      conn.Close();

}

 

Using using the code becomes much cleaner and logical.

 

using (SqlConnection conn = new SqlConnection(ConnectionString))

{

….

}

 

Next time you consider using resources, consider using using and clean your code up.

posted on February 21, 2007  #    by Bary Nusz  Comments [0] Trackback
 Tuesday, February 20, 2007

...and with it a long missed friend returns!

When I first switched from 2000 to 2005, I was dismayed to find that the scripting options were drastically reduced in functionality. I am glad to announce that in SP2, the old 2000 options are all back!

The service pack contains a lot of new functionality, read all about it here. The service pack can be downloaded here.

posted on February 20, 2007  #    by John Waters  Comments [0] Trackback
 Wednesday, February 14, 2007

How do you find if a table is referenced in a stored proc?  That can be quite the Easter egg hunt without some sort of tool.  The following SQL uses the syscomments table (contains the stored proc text) and joins to the sysobjects table to determine which syscomment records belong to stored procs.  CHARINDEX returns the starting position of an expression in a character string, so the fact that it returns greater than zero signals a hit.

DECLARE @SEARCHSTRING VARCHAR(255)

SELECT @SEARCHSTRING = 'MyTable'

SELECT DISTINCT sysobjects.name
FROM sysobjects,syscomments
WHERE sysobjects.id = syscomments.id
-- look for stored procs only
AND sysobjects.type = 'P'
AND sysobjects.category = 0
-- what you are looking for, what you're looking in
AND CHARINDEX(@SEARCHSTRING,syscomments.text)>0

This is a reduced version taken from an example at:

http://wiki.ittoolbox.com/index.php/HOWTO:Search_the_text_of_triggers_and_stored_procedures

Another handy related query from Adam Anderson's toolbox of SQL techniques finds references to column names.  This one lists the table names where tables contain "Created" and "Modified" users and dates:

select distinct so.name
from sysobjects so
where exists (
select *
from syscolumns sc
where sc.id = so.id
and sc.name in ( 'CreatedBy', 'CreatedOn', 'ModifiedBy', 'ModifiedOn' )
)

More info about CHARINDEX at http://msdn2.microsoft.com/en-us/library/ms186323.aspx.

posted on February 14, 2007  #    by Noel Rice  Comments [0] Trackback
 Wednesday, February 07, 2007

This morning I discovered I could enter code to VS but couldn't backspace over it.  This would require me to code perfectly the first time, and for me that's a non-starter.  It turns out that none of the command buttons work.  The short story on the fix for this oddity is to either disable Auto-Hide or open one of the hidden windows, then click the X to close.  More info at the msdn blog site: Tabs, Backspace, Delete and other commands not working in the editor.

posted on February 7, 2007  #    by Noel Rice  Comments [0] Trackback
 Tuesday, February 06, 2007

If you use Windows Vista and are like me, you have already turned off those annoying prompts generated by User Account Control (UAC). But can you really just turn it off?

Well, it turns out that you can't. I went to install Adobe Acrobat Reader 8.0, which is Vista compatible. After downloading the file I set about installing it, only to be informed that "The Temp folder is on a drive that is full or is inaccessible."

Some googling turned up this Tech Note on Adobes site. According to this note "On Vista, with User Account Control disabled, there is no write and execute access to the %TEMP% folder". Now isnt that amazing? Microsoft actually turns off access to the TEMP folder if you dont want to use its lousy UAC! I can't believe it!

There are some extremely annoying time consuming work arounds that are detailed in the Tech Note, which entail interrupting the installation half way through, copying files to another location, then running from there... The easist fix is to turn on UAC, reboot (required), run the install, turn off UAC, reboot (required) and there you go!

So thanks Microsoft for adding UAC and then crippling the machine when you turn it off. Very thoughtful.

posted on February 6, 2007  #    by John Waters  Comments [0] Trackback
 Monday, February 05, 2007

The full text of the error reads: "Commit failed. (details follow): 'pre-commit' hook failed with error output: all was unexpected at this time." I've run into this error when trying to commit files in Subversion (with TortoiseSVN) at least twice. At first, I thought it was caused by some obscure state error. I tried doing a cleanup on the directory, and then on the parent directory, with no success. I tried deleting the entire project and downloading it afresh from the repository. Nothing worked, until my associate Noel asked me what comment I'd used. It was then I realized that my comment contained double quotes. I replaced the double quotes with single quotes, and the commit worked without a hitch. In a way, it's a relief to know that I don't have to worry about any of my projects getting into a weird state where I can't commit anything, but on the other hand, it's a little troubling to think that my comment text apparently isn't getting escaped.

posted on February 5, 2007  #    by Adam Anderson  Comments [0] Trackback
 Tuesday, January 30, 2007

First of all, that title was a mouthful, so let me describe what I meant. A custom DataSource is something that you can assign (or databind) to the DataSource property of any bindable control. In programming terms, this means that the DataSource must implement IListSource, IDataSource, or IEnumerable. Well, I don't know about you, but I don't really want to spend a lot of time implementing all the methods of any of those interfaces. That's where Iterators come in; C# 2.0 added a new feature called Iterator blocks that makes it extremely easy to implement the IEnumerator interface, which is the only thing that you must implement in order to fully implement IEnumerable. In this article, I'll solve a real-world problem using this technique.

In application development, it's very common to need to fill a list control such as a DropDownList with a list of valid items, plus a blank item which represents a null value in a data store. However, while it is very easy to simply set some properties on a DropDownList to databind its items to the contents of a DataTable, there is no simple way to add that blank value without dropping into the code-behind. This is no big deal, but it just doesn't feel very clean to fill some DropDownLists declaratively, and have to resort to imperative code for other DropDownLists. One way to solve this problem would be to create a custom control descending from each list control with properties that control the extra blank item, but first of all, you'd have to write a custom control for each list control, and secondly, if we did that, then we couldn't talk about Iterators, could we? So instead, I'm going to create a custom DataSource that returns the contents of a DataTable, plus a blank item to represent null.

Let's proceed with writing the custom DataSource. Using Iterators to implement IEnumerable is as easy as declaring a class that implements it and using the yield keyword to build the return value. Let's declare our ListSource class and let Visual Studio create the method stubs to implement the interface:

using System;
using System.Collections.Generic;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion
  }
}

Assume the ListData class is a simple data class with two string properties: Text and Value. The first thing to notice is that the parameter list for GetEnumerator is empty, so the class itself will need to be initialized with properties that control the output of GetEnumerator. Let's add some private fields and a constructor to initialize them. New code is in italics:

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    private DataTable _Table;
    private string _TextField;
    private string _ValueField;

    public ListDataSource( DataTable table, string textField, string valueField )
    {
      _Table = table;
      _TextField = textField;
      _ValueField = valueField;
    }


    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion
  }
}

Of course, you could add code to expose the private fields as properties, but since that's not the focus of the article, let's just assume everyone knows how to do that so we can get to the interesting part. Now that we can create an instance of the class with a reference to a DataTable and some field names, we can implement GetEnumerator:

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    private DataTable _Table;
    private string _TextField;
    private string _ValueField;

    public ListDataSource( DataTable table, string textField, string valueField )
    {
      _Table = table;
      _TextField = textField;
      _ValueField = valueField;
    }

    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      yield return new ListData( String.Empty, String.Empty );
      foreach ( DataRow row in _Table.Rows )
        yield return new ListData( (string) row[ _TextField ], (string) row[ _ValueField ] );

    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return ( (IEnumerable<ListData>) this ).GetEnumerator();
    }

    #endregion
  }
}

Let's pause for a minute and contemplate the power of what those two lines have. The return type of GetEnumerator is IEnumerator<ListData>, but nowhere are we implementing any of the methods of the IEnumerator interface. Instead, we are simply iterating through a collection and returning each value with yield return. The C# 2.0 compiler does the work of implementing the IEnumerable interface for you.

Well, you could add any bells and whistles to the class that you wanted to, of course. Maybe you want to be able to specify the text and value of the null item, or maybe you want access to those private members through properties. Let's fast-forward past all that and demonstrate the final application of this class: the ability to bind to the DataSource declaratively. To do so, open any aspx page in Source View, locate a list control such as a DropDownList, and add the following attributes to the opening tag:

DataSource='<%# new ListDataSource( ds.MyTable, "MyTextField", "MyValueField" ) %>' DataTextField="Text" DataValueField="Value"

Now, when the control is databound, it will bind to our custom DataSource and fill its items using the ListData objects yielded by GetEnumerator.

In this article, I took two concepts: that all it takes to implement a DataSource is to implement IEnumerable, and that all it takes to implement IEnumerable is to write a little code using the yield keyword. I combined these concepts to demonstrate how to quickly and easily create a custom DataSource that could implement any custom logic desired and still integrate declaratively with all existing ASP.NET controls. If you are in need of .NET training or consulting, please contact us here at Falafel Software.

posted on January 30, 2007  #    by Adam Anderson  Comments [0] Trackback