<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>The Last Pickle</title>
	<atom:link href="http://www.thelastpickle.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.thelastpickle.com</link>
	<description></description>
	<pubDate>Sun, 27 Jul 2008 16:22:45 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<item>
		<title>Pattern matching F# Lists</title>
		<link>http://www.thelastpickle.com/2008/07/27/pattern-matching-f-lists/</link>
		<comments>http://www.thelastpickle.com/2008/07/27/pattern-matching-f-lists/#comments</comments>
		<pubDate>Sun, 27 Jul 2008 16:22:45 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[f# lists patterns]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/07/27/pattern-matching-f-lists/</guid>
		<description><![CDATA[Here are some simple examples I&#8217;ve come up with for working with lists in F#. These follow on from previous work looking at finding a value in a list and again this is definitely not efficient F# code. I&#8217;m just playing around in the language and trying to understand things. 

First I tried walking a [...]]]></description>
			<content:encoded><![CDATA[<p>Here are some simple examples I&#8217;ve come up with for working with lists in F#. These follow on from previous work looking at finding a value in a list and again this is definitely not efficient F# code. I&#8217;m just playing around in the language and trying to understand things. </p>

<p>First I tried walking a list using a recursive function and a match <a href="http://research.microsoft.com/fsharp/manual/lexyacc.aspx#Patterns">pattern</a>. All I wanted to do was pop the head of the list, print it and process the remainder of the list.</p>

<p>The <code>printfn</code> statements in my example use the <a href="http://research.microsoft.com/fsharp/manual/FSharp.Core/Microsoft.FSharp.Text.html"><code>%A</code></a> format specifier which provides pretty printing for any value. Also the <code>;</code> symbol used to separate multiple expressions in a match rule causes the first expression to be evaluated and its result discarded before the second expression is evaluated and used as the result. This method can be used to execute side-affecting statements such as old fashioned debug printing. </p>

<pre>
#light;

let rec listWalk list =
  printfn "-----";
  match list with 
  | [] -> printfn "Empty List";
  | h::t -> printfn "Head is %A" h; printfn "Tail is %A" t; listWalk t;;

let s = "A string of words";;
let l = s |> String.split [' '];;
</pre>

<p>The list of words <code>l</code> is used in all the examples for this post. </p>

<pre>
> listWalk l;;
-----
Head is "A"
Tail is ["string"; "of"; "words"]
-----
Head is "string"
Tail is ["of"; "words"]
-----
Head is "of"
Tail is ["words"]
-----
Head is "words"
Tail is []
-----
Empty List
val it : unit = ()
>
</pre>

<p>The first match clause matches on an empty list (<code>[]</code>) and the second matches on a list construction. The head element is bound to the identifier <code>h</code> and the tail to the identifier <code>t</code>. Looking at the output from <code>printfn</code>, <code>h</code> is a value of type string (denoted by the surrounding double quotes) and <code>t</code> is a value of type list of string. Notice that when <code>t</code> is an empty list its string representation is the same as the pattern used to denote an empty list.</p>

<p>Next I wanted to a match rule to find the last element in the list, that is where there is a value for head and the tail is an empty list. So I added a rule that combined the previous two and matched on a list constructed with a head and an empty list.</p>

<pre>
let rec listWalk list =
 printfn "-----";
 match list with 
 | [] -> printfn "Empty List";
 | h::[] -> printfn "Last element is %A" h; 
 | h::t -> printfn "Head is %A" h; printfn "Tail is %A" t; listWalk t;; 
</pre>

<p>Using the same list of words as above.</p>

<pre>
> listWalk l;;
-----
Head is "A"
Tail is ["string"; "of"; "words"]
-----
Head is "string"
Tail is ["of"; "words"]
-----
Head is "of"
Tail is ["words"]
-----
Last element is "words"
val it : unit = ()
</pre>

<p>This time the rule for an empty list (<code>[]</code>) did not match because a recursive call was not made when a list with a single element was detected. </p>

<p>There is an easy mistake to make when using match patterns like this though. The patterns <code>h::t</code> and <code>h::[]</code> will both match the pattern of a list with one remaining element, as <code>[]</code> just means an empty list. So if the rules are sequenced incorrectly my new rule to detect the last element in the list will not work. </p>

<pre>
let rec listWalk list =
  printfn "-----";
  match list with 
  | [] -> printfn "Empty List";
  | h::t -> printfn "Head is %A" h; printfn "Tail is %A" t; listWalk t;
  | h::[] -> printfn "Last element is %A" h;;
</pre>

<p>Results in&#8230;</p>

<pre>
> listWalk l;;
-----
Head is "A"
Tail is ["string"; "of"; "words"]
-----
Head is "string"
Tail is ["of"; "words"]
-----
Head is "of"
Tail is ["words"]
-----
Head is "words"
Tail is []
-----
Empty List
val it : unit = ()
>
</pre>

<p>In fact the compiler is able to work this out outputs a warning when compiling the function. </p>

<p><em>warning FS0026: This rule will never be matched.</em></p>

<p>Of course if all I wanted to do was iterate over a list there are many functions in the <a href="http://research.microsoft.com/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.List.html">List Module</a> that would be a better choice. For example using one of the four iter(ation) functions.</p>

<pre>
let listWalk list =
  printfn "-----";
  list |> List.iter (fun e -> printfn "Element is %A" e);;
</pre>

<pre>
> > listWalk l;;
-----
Element is "A"
Element is "string"
Element is "of"
Element is "words"
val it : unit = ()
>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/07/27/pattern-matching-f-lists/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Matching patterns in F#</title>
		<link>http://www.thelastpickle.com/2008/07/16/matching-patterns-in-f/</link>
		<comments>http://www.thelastpickle.com/2008/07/16/matching-patterns-in-f/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 10:36:22 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[f#]]></category>

		<category><![CDATA[patterns]]></category>

		<category><![CDATA[recursion]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/07/16/matching-patterns-in-f/</guid>
		<description><![CDATA[Patterns in F# are kind of like switch statements in C#, but much more powerful. For my first experiment with them I decided to see if I could find a word in a sentence. This is definitely the wrong way to go about solving this problem, calling String.Contains() would be easier. But I am still [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://research.microsoft.com/fsharp/manual/quicktour.aspx#QuickTourPatterns">Patterns</a> in F# are kind of like <code>switch</code> statements in C#, but much more powerful. For my first experiment with them I decided to see if I could find a word in a sentence. This is definitely the wrong way to go about solving this problem, calling String.Contains() would be easier. But I am still very much learning the language and this is the first step in investigating whether patterns would be of use solving another problem I have in mind.</p>

<p>So with that in mind lets get started in F# interactive mode and the <a href="http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383943"><code>#light</code></a> syntax. </p>

<p>My first draft was not pretty, but it worked and got my head into the game. The algorithm involves examining the elements of a list one by one and comparing the value to the value of a variable.</p>

<pre>
#light;

> let sentence = "there is a red dog";;
val sentence : string

> let tokens = sentence |> String.split [' '];;
val tokens : string list

> tokens;;
val it : string list = ["there"; "is"; "a"; "red"; "dog"]

> let find = "red";;
val find : string

> let rec search tokens find= 
 match tokens with
 | [] -> "no match"
 | h::t when h = find -> "match"
 | h::t -> search t find;;

val search : 'a list -> 'a -> string
</pre>

<p>First I defined and initialized the value <code>sentence</code> with the string I wanted to search, letting the compiler correctly infer the type to be <code>string</code>. The value of <code>sentence</code> was then passed as the last argument to the <code>String.split</code> function using the <a href="http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383708">pipeline operator (|>)</a>. The first argument passed was a <a href="http://research.microsoft.com/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.type_List.html">list</a> containing the delimiters to split the string on, in this case just the space character. The result of calling split is another list containing the words split from the sentence; executing <code>tokens;;</code> in the interactive session returns the value.</p>

<p>The value to search for was stored in the variable &#8216;find&#8217;. I&#8217;m trying to avoid calling variables &#8216;variables&#8217; as by default they are immutable in F# and so cannot be changed. So I tend to think of them more like values with a name, but this seems to make things a little confusing at times. </p>

<p>Then I defined the function <code>search</code> using the <code>rec</code> modifier to instruct the compiler to allow recursive calls. The function takes two parameters, the first is the list to search and the second is the value to search for. The <code>match</code> statement has three patterns, one of which makes a (<a href="http://en.wikipedia.org/wiki/Tail_recursion">tail</a>) recursive call to continue processing the list of words. Recursion was probably not the best construct to use but I wanted to see how it works in F#. The three <a href="http://research.microsoft.com/fsharp/manual/lexyacc.aspx#Patterns">patterns</a> are evaluated in order and work as follows.</p>

<ol>
<li>Match on an empty list, execute the expression to return the string &#8220;no match&#8221;</li>
<li>Match on a list with at least one element, bind the head of the list to <code>h</code> and the remainder of the list to <code>t</code>. If the value of h is equivalent to the value of &#8216;find&#8217; execute the expression to return the string &#8220;match&#8221;</li>
<li>Match on a list with at least one element, bind the head of the list to <code>h</code> and the remainder of the list to <code>t</code>. Make a recursive call to search and pass the tail (t) of the list.</li>
</ol>

<p>Some simple tests showed to function to be working as expected. </p>

<pre>
> search tokens find;;
val it : string = "match"

> let find = "blue";;
val find : string

> search tokens find;;
val it : string = "no match"
</pre>

<h2>Function Values</h2>

<p>That worked as a first draft but it was not very nice, so I set about it with my refactoring stick. </p>

<p>The first thing I did was to modify the search function to return a boolean. I did not have to explicitly say it returned a boolean value, just change the string return values to the boolean constants and the compiler worked it out. Otherwise the search function itself was fine.</p>

<pre>
> let rec search tokens find = 
 match tokens with
 | [] -> false 
 | h::t when h = find -> true
 | h::t -> search t find;;
val search : 'a list -> 'a -> bool
</pre>

<p>As well as inferring the return type the compiler was also able to automatically generalize the function. There was nothing in the search function that required the value of <code>find</code> or the list of <code>tokens</code> to be a strings. So the compiler created a function that accepts a list of a generic type (&#8221;&#8216;a list&#8221;), a single value of the same generic type (&#8221;&#8216;a&#8221;) and returns a boolean value (bool).</p>

<p>Next I used partial function application / currying to create a function that accepts a string sentence to search in and returns an anonymous function. The code in the <code>wordSearch</code> function to split the sentence is only executed once, and only when the anonymous function is executed. At the moment I don&#8217;t understand the language enough to explain why, but the key element to achieving this result is the anonymous function requiring a parameter to be passed. </p>

<pre>
> let wordSearch s = 
 let tokens = String.split [' '] s
 fun (word: string) -> search tokens word;;
val wordSearch : string -> (string -> bool)

> let s = wordSearch "there is a red dog";;
val s : (string -> bool)

> s "red";;
val it : bool = true

> s "blue";;
val it : bool = false
</pre>

<p>Next I hope to modify the function to search the list of tokens for any element from a list.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/07/16/matching-patterns-in-f/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Initialization In F and C #</title>
		<link>http://www.thelastpickle.com/2008/07/09/initialization-in-f-and-c/</link>
		<comments>http://www.thelastpickle.com/2008/07/09/initialization-in-f-and-c/#comments</comments>
		<pubDate>Thu, 10 Jul 2008 06:41:14 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[c#]]></category>

		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/07/09/initialization-in-f-and-c/</guid>
		<description><![CDATA[There are some common compiler tricks in F# and C#, for example object initialization. 

In C# 3.0 we have Object Initializers 
which allow for an object to be instantiated via an explicit constructor call and properties to be set in one statement.


Form f = new Form1()
{
    Text = "Hello"
};
Application.Run(f);


And in F# we [...]]]></description>
			<content:encoded><![CDATA[<p>There are some common compiler tricks in F# and C#, for example object initialization. </p>

<p>In C# 3.0 we have <a href="http://msdn.microsoft.com/en-us/library/bb384062.aspx">Object Initializers</a> 
which allow for an object to be instantiated via an explicit constructor call and properties to be set in one statement.</p>

<pre>
Form f = new Form1()
{
    Text = "Hello"
};
Application.Run(f);
</pre>

<p>And in F# we have <em>initial property settings</em> where named values are first mapped to constructor parameters and the unused values used to set properties.</p>

<pre>
> open System.Windows.Forms;;

> let f = new Form(Text = "Hello");;

val f : Form

> f.Show();;
val it : unit = ()
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/07/09/initialization-in-f-and-c/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Inferred Generics in F#</title>
		<link>http://www.thelastpickle.com/2008/07/06/inferred-generics-in-f/</link>
		<comments>http://www.thelastpickle.com/2008/07/06/inferred-generics-in-f/#comments</comments>
		<pubDate>Sun, 06 Jul 2008 09:43:11 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[f#]]></category>

		<category><![CDATA[generics]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/07/06/inferred-generics-in-f/</guid>
		<description><![CDATA[The first hundred or so pages of Expert F# have been really interesting, the compiler feels so much more advanced than C#. Although I think it has more to do with the nature of functional programming than any deficiency in the C# compiler. The code does not tell the machine how to do something rather [...]]]></description>
			<content:encoded><![CDATA[<p>The first hundred or so pages of <a href="http://www.amazon.co.uk/Expert-F-Experts-Voice-Net/dp/1590598504">Expert F#</a> have been really interesting, the compiler feels so much more advanced than C#. Although I think it has more to do with the nature of functional programming than any deficiency in the C# compiler. The code does not tell the machine how to do something rather it says what needs to be done. Which means the compiler is free to do as it wishes and may compile things to have a broader meaning, so long as the intention of the code is honored.</p>

<p>One of the neat tricks is when a function is <em>automatically inferred to be generic</em> through a process known as <a href="http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383934">Generalization</a>. This occurs when a function does not constrain or use a parameter is such a way that the value must be of a specific type. </p>

<p>Lets start with something that is not Generalized. A function that takes a <a href="http://research.microsoft.com/fsharp/manual/quicktour.aspx#QuickTourTuples">tuple</a> of two values and subtracts 1 from each value.</p>

<pre>
> let sub (a,b) = (a-1, b-1);;
val sub : int * int -> int * int

> sub (2,3);;
val it : int * int = (1, 2)
</pre>

<p>While I cannot explain why the sub function is not generalizable in terms of the rules laid out in the language specification of Generalization, it&#8217;s pretty clear what&#8217;s happened. Subtracting the integer 1 from each of the tuple values requires that the tuple be of type <code>int * int</code>, i.e. two integers. So the function sub takes a tuple of two integers and returns a tuple of two integers. </p>

<p>Now something that can be generalized, returning the first value in a tuple of two values. </p>

<pre>
> let getFirst (a,b) = a;;
val getFirst : 'a * 'b -> 'a

> getFirst (1,2);;
val it : int = 1

> getFirst ("a", "b");;
val it : string = "a"
</pre>

<p>Types are denoted by the <code>token, so the tuple</code>a * &#8220;b is a tuple of a value of type a and a value of type b. Because of the generalization getFirst can be called with two integers (1,2) or two strings (&#8221;a&#8221;, &#8220;b&#8221;). This is just how the built in <a href="http://research.microsoft.com/fsharp/manual/FSharp.Core/Microsoft.FSharp.Core.Operators.html">fst</a> operator works. It takes a tuple of two values and returns the first (there is also a snd to return the second value).</p>

<pre>
>fst;;
val it : ('a * 'b -> 'a) = &lt;fun:clo@0_6&gt;

> fst (1,2);;
val it : int = 1

> fst ("a", "b");;
val it : string = "a"
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/07/06/inferred-generics-in-f/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Persisting Computed XML</title>
		<link>http://www.thelastpickle.com/2008/07/05/persisting-computed-xml/</link>
		<comments>http://www.thelastpickle.com/2008/07/05/persisting-computed-xml/#comments</comments>
		<pubDate>Sat, 05 Jul 2008 17:42:19 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sqlserver]]></category>

		<category><![CDATA[xml]]></category>

		<category><![CDATA[xquery]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/07/05/persisting-computed-xml/</guid>
		<description><![CDATA[It&#8217;s possible to define an XML column as a computed column. And in most cases I think it would make sense to persist the column to save doing all that work again. As Bob Beauchemin points out there&#8217;s a trick to doing it. 

The first thing you need is a user defined scalar function to [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s possible to define an XML column as a computed column. And in most cases I think it would make sense to persist the column to save doing all that work again. As <a href="http://www.sqlskills.com/blogs/bobb/2006/03/03/XQueryMethodsAndDeterminism.aspx">Bob Beauchemin</a> points out there&#8217;s a trick to doing it. </p>

<p>The first thing you need is a user defined scalar function to that returns an XML type. As with all <a href="http://technet.microsoft.com/en-us/library/ms191250.aspx">persisted computed columns</a> the function must be deterministic and as Bob points out that&#8217;s fine because all XQuery functions are deterministic. However the UDF must also be marked with <a href="http://technet.microsoft.com/en-us/library/aa258261(SQL.80).aspx">SCHEMABINDING</a> to ensure it&#8217;s determinism. </p>

<p>SCHEMABINDING guarantees that the dependancies of the function cannot be changed without first altering the function which has two effects. First the function will continue to execute without an exception (e.g. bad column name), which could be considered a type of determinism. Second column types and definitions cannot change; changes could lead to truncation, overflow or a dependancy on a column changed to a non deterministic computed definition.</p>

<p>So first the function, you&#8217;ll need to pass all the column values into the function.</p>

<pre>
create function dbo.udf_event_data
(
    @start_time as smalldatetime, 
    @end_time as smalldatetime  
)
returns xml 
with schemabinding
as
begin

-- times must be cast to formatted strings for XQuery
declare @start_time_char varchar(128)
set @start_time_char = convert(varchar(128), @start_time, 126) + 'Z'    

declare @end_time_char varchar(128)
set @end_time_char = convert(varchar(128), @end_time, 126) + 'Z'    

declare @x xml
set @x = '<r />';

set @x = 
    @x.query('
        declare namespace xsi="http://www.w3.org/2001/XMLSchema-instance";

        <event_data>
            <start_time>        
                {   
                    if ( empty(sql:variable("@start_time_char") cast as xs:dateTime? )) then                    
                        attribute xsi:nil {"true"}  
                    else () 
                }
                {
                    sql:variable("@start_time_char") cast as xs:dateTime?
                }
            </start_time>
            <end_time>      
                {   
                    if ( empty(sql:variable("@end_time_char") cast as xs:dateTime? )) then                  
                        attribute xsi:nil {"true"}  
                    else () 
                }
                {
                    sql:variable("@end_time_char") cast as xs:dateTime?
                }
            </end_time>
        </event_data>
    ');     
return @x
end     
</pre>  

<p>The next step is to create a computed column and mark the XML column as PERSISTED. All the columns values needed by the function must be passed in the definition which should look something like. </p>

<pre>
...
[event_data]  AS ([dbo].[udf_event_data]([start_time],[end_time])) PERSISTED,
</pre>

<p>The UDF above uses the mechanism for handling SQL NULL <a href="http://www.thelastpickle.com/2008/06/29/detecting-sql-null-in-xquery/">discussed previously</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/07/05/persisting-computed-xml/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Detecting SQL NULL in XQuery</title>
		<link>http://www.thelastpickle.com/2008/06/29/detecting-sql-null-in-xquery/</link>
		<comments>http://www.thelastpickle.com/2008/06/29/detecting-sql-null-in-xquery/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 15:02:52 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sql server]]></category>

		<category><![CDATA[xquery]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/06/30/detecting-sql-null-in-xquery/</guid>
		<description><![CDATA[When using XQuery inside SQL Server the value of SQL variables and columns can be referenced using the sql:column and sql:variable functions respectively. When a SQL value is NULL you may want to mark the XML element as xsi:nil, like the following.


&#60;event_data xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34; xsi:nil=&#34;true&#34; /&#62;


The semantic equivalent of SQL NULL in XQuery is an empty [...]]]></description>
			<content:encoded><![CDATA[<p>When using XQuery inside SQL Server the value of SQL variables and columns can be referenced using the <code>sql:column</code> and <code>sql:variable</code> functions respectively. When a SQL value is NULL you may want to mark the XML element as xsi:nil, like the following.</p>

<pre>
&lt;event_data xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:nil=&quot;true&quot; /&gt;
</pre>

<p>The semantic equivalent of SQL NULL in XQuery is an empty sequence. To convert a SQL value to a sequence the XQuery <code>cast</code> operator can be used. This will first <a href="http://technet.microsoft.com/en-us/library/ms191150.aspx">atomize</a> the value, then according to the <a href="http://www.w3.org/TR/xquery/#id-cast">XQuery spec</a> if the result is an empty sequence the cast operation will succeed if the target type has the optional occurrence indicator (&#8217;?&#8217; meaning zero or one).</p>

<p>Meaning that the cast must fail and throw an error unless the occurrence indicator is ? present when casting an empty sequence. However the SQL implementation is a little different; from the documentation on <a href="http://technet.microsoft.com/en-us/library/ms190179.aspx">SequenceType Expressions</a>&#8230;</p>

<p><em>&#8220;In SQL Server 2005, the instance of and cast as XQuery expressions on SequenceTypes are partially supported&#8230; In SQL Server, the question mark (?) is required after the AtomicType.&#8221;</em></p>

<p>I imagine SQL added this requirement to their implementation to avoid runtime errors and possibly to continue to the idea that emptyness / NULLness is not a bad thing. </p>

<p>So to detect NULL in a SQL value and optionally create an element with <code>xsi:nil</code> is used the following.</p>

<pre>
declare @data varchar(128)

declare @x xml
set @x = '<r />';

set @x = 
    @x.query('
        declare namespace xsi="http://www.w3.org/2001/XMLSchema-instance";
        
        <event_data>    
                {   
                    if ( empty(sql:variable("@data") cast as xs:string? )) then                 
                        attribute xsi:nil {"true"}  
                    else () 
                }
                {
                    sql:variable("@data") cast as xs:string?
                }
        </event_data>
    ');     
select  @x
</pre>

<p>If the <code>else</code> clause of the <code>if</code> is used to construct an xsi:string node the following error is raised.</p>

<pre>
XQuery [query()]: Heterogeneous sequences are not allowed: found 'xs:string ?' and
'attribute(xsi{http://www.w3.org/2001/XMLSchema-instance}:nil,xdt:untypedAtomic)'
</pre>

<p>The Atomic Constructor Functions can also be used to convert values, such as <code>xs:string(@data)</code> however I prefer to use cast when casting variables.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/06/29/detecting-sql-null-in-xquery/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Heap locking under Serializable Transactions</title>
		<link>http://www.thelastpickle.com/2008/05/29/heap-locking-under-serializable-transactions/</link>
		<comments>http://www.thelastpickle.com/2008/05/29/heap-locking-under-serializable-transactions/#comments</comments>
		<pubDate>Thu, 29 May 2008 21:52:15 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[locks]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sqlserver]]></category>

		<category><![CDATA[transactions]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/05/29/heap-locking-under-serializable-transactions/</guid>
		<description><![CDATA[Tables without a clustered index store their data in a Heap data structure. The (in row) data is added to the heap as its encountered without any ordering, rather than ordering by the columns in a clustered index. This makes finding particular rows a little tricky and a lot slower as there is normally no [...]]]></description>
			<content:encoded><![CDATA[<p>Tables without a clustered index store their data in a <a href="http://msdn.microsoft.com/en-us/library/ms188270.aspx">Heap data structure</a>. The (in row) data is added to the heap as its encountered without any ordering, rather than ordering by the columns in a clustered index. This makes finding particular rows a little tricky and a lot slower as there is normally no way of knowing if there are more rows in the table that satisfy the query. </p>

<p>It also has an effect on the locking strategy employed under different isolation levels that can result in dramatically reduced concurrency on a table. Of course in the general case a table with a clustered index is preferred to a heap so this should not be a day to day issue, but its a moderately interesting situation. </p>

<p>To test this I created a DB with two tables, one a heap and one a b-tree, and a view that shows the locks for a connection. I also referred to the <a href="http://msdn.microsoft.com/en-us/library/ms186396.aspx">Lock Compatibility</a> page on more than one occasion. </p>

<pre>
create database locks;
go

use locks
go

create table noindex 
(
    col1 int ,
    col2 varchar(2)
)
go

insert into noindex
values
    (1,'a');
insert into noindex
values
    (2,'b');

create table indexed
(
    col1 int primary key,
    col2 varchar(2)
)
go

insert into 
    indexed
select
    *
from
    noindex;
go

create view
    vwLocks
as
    select 
        cast(request_mode as varchar(5)) as request_mode ,
        cast(request_status as varchar(10)) as request_status ,
        cast(resource_type as varchar(15)) as resource_type,
        case 
            when resource_type = 'OBJECT' then
                cast(object_name(resource_associated_entity_id) as varchar(20)) 
            else
                cast(resource_associated_entity_id as varchar(20))
        end as resource_name,
        cast(resource_description as varchar(15)) as resource_description
    from 
        sys.dm_tran_locks
    where
        request_session_id = @@spid
go
</pre>

<p>Each test used the same basic query below, changing the table (from &#8216;noindex&#8217; to &#8216;indexed&#8217;) and the isolation level. </p>

<pre>
set transaction isolation level read committed
begin transaction

select
    *
from
    noindex
where
    col1 = 1

print 'after select'

select  
    *
from
    vwLocks

insert into
    noindex
values
    (3,'c')

print 'after insert'

select  
    *
from
    vwLocks

update 
    noindex
set 
    col2 = 'x' 
where 
    col1 = 2

print 'after update'

select  
    *
from
    vwLocks

rollback
</pre>

<h3>Heap and READ COMMITTED isolation</h3>

<p>Under READ COMMITTED isolation no locks were maintained after the select. After the insert however an Exclusive (X) lock is held against the newly inserted row identified by its Row ID (RID) 1:152:2. The first number in the RID is the File number, the next the Page number and the last the &#8220;slot&#8221; number in the page which can be though of as the row index. There are also Intent the Exclusive (IX) locks that were taken on the Table and then the Page before the row was inserted.</p>

<p>After the update the only additional locking is an Exclusive lock on the updated row, again identified by its RID (1:152:1).  </p>

<pre>
after select

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             

after insert

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
X            GRANT          RID             72057594038321152    1:152:2        
IX           GRANT          PAGE            72057594038321152    1:152          
IS           GRANT          OBJECT          vwLocks                             
IX           GRANT          OBJECT          noindex                             

after update

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
X            GRANT          RID             72057594038321152    1:152:2        
IX           GRANT          PAGE            72057594038321152    1:152          
IS           GRANT          OBJECT          vwLocks                             
IX           GRANT          OBJECT          noindex                             
X            GRANT          RID             72057594038321152    1:152:1        
   
</pre>

<p>If the previous query was run and the transaction left running (remove the <code>rollback</code> statement) another query could still read from the table under certain circumstances. The query would need to be constructed so that it did not to access the locked rows, however all rows may need to  be accessed to check if they satisfy the query. For example the next query will not block behind the open transaction. </p>

<pre>
select top 1 * from noindex where col1 = 1
</pre>

<p>The first row in the table has col1 equal to 1, and the <code>top 1</code> operator tells the query engine it only needs to return one row so there is no need to scan for further rows. The next query will block as it must access all rows to test if col1 equals 1, if the rows were ordered by col 1 as soon as it found the row with col2 = 2 the query could stop (as 2 > 1 so any value >= 2 must also be > 1). </p>

<pre>
select * from noindex where col1 = 1
</pre>

<p>The point here is that there is still room for other queries to read from the table. If there were Non Clustered indexes on the table they would identify the rows in the heap using the RID and would not need to scan the heap to find candidate rows. Instead the non clustered index would be scanned and the RID values used to index directly to the row in the page. Yes there would be locking on the NC index that could block the scan, but I&#8217;ll look at that another day. </p>

<h3>Heap and SERIALIZABLE isolation</h3>

<p>Under a serializable transaction a table level Shared (S) lock is held after the select that prevents all updates on the table. Under serializable isolation the results of the select need to be protected from Phantom Reads and Unrepeatable Reads. As there is no index on col1 another record could be inserted or modified to have a value of 1, which would invalidate the result of the select. So the only way to support the isolation level is to prevent all changes on the table by holding the shared lock. Other connections can still read from the table, but they will block when trying to insert or update any row.</p>

<p>The insert statement leaves an Exclusive lock on the RID the new row is added to and the accompanying Intent Exclusive lock taken on the Page the row was added to. The Shared lock left in place by the select is promoted to a Shared Intent Exclusive (SIX) lock, this seems to act like having both a S and an IX lock on the table. Other connections may still take an Intent Shared lock on the table and then read rows that do not have Exclusive locks on them. However other connections may not take an Intent Exclusive lock on the table that would be the first step in locking down to the row (RID) level to insert or update a row. Thats may understanding, it&#8217;s covered in the <a href="http://msdn.microsoft.com/en-us/library/ms175519.aspx">Lock Modes</a> page.</p>

<p>So after the select and the insert other connections may still read from the table, but any form of update is prohibited. This is the same as the read committed transaction, however running the update changes things somewhat. </p>

<p>The update promotes the Intent Exclusive lock on the table up to a full Exclusive lock preventing all access from other connections. Right now I cannot crack the logic of why this would happen. If only the update query is run in the transaction the the Exclusive lock is also taken. </p>

<pre>
after select

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             
S            GRANT          OBJECT          noindex                             

after insert

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
X            GRANT          RID             72057594038321152    1:152:2        
IX           GRANT          PAGE            72057594038321152    1:152          
IS           GRANT          OBJECT          vwLocks                             
SIX          GRANT          OBJECT          noindex                             

after update

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
X            GRANT          RID             72057594038321152    1:152:2        
IX           GRANT          PAGE            72057594038321152    1:152          
IS           GRANT          OBJECT          vwLocks                             
X            GRANT          OBJECT          noindex                                            
</pre>

<h3>B-Tree and READ COMMITTED isolation</h3>

<p>As a contrast below is the locks taken when using the <code>indexed</code> table under read committed isolation. </p>

<p>At all times other connections may read from the <code>indexed</code> table so long as they do not attempt to read data inserted or updated by the transaction. This is because the highest lock taken on the table and page is an Intent Exclusive. Which is compatible with the Intent Shared lock a query would take on the same object before attempting to lock down to the key level. </p>

<pre>
after select

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             

after insert

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IX           GRANT          PAGE            72057594038386688    1:154          
IS           GRANT          OBJECT          vwLocks                             
X            GRANT          KEY             72057594038386688    (03000d8f0ecc) 
IX           GRANT          OBJECT          indexed                             

after update

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IX           GRANT          PAGE            72057594038386688    1:154          
IS           GRANT          OBJECT          vwLocks                             
X            GRANT          KEY             72057594038386688    (03000d8f0ecc) 
IX           GRANT          OBJECT          indexed                             
X            GRANT          KEY             72057594038386688    (020068e8b274) 
</pre>

<h3>B-Tree and SERIALIZABLE isolation</h3>

<p>The locks taken below under a serializable isolation level are slightly different owing to the additional isolation guarantees. However the effect on the ability of another connection to read from the table is the same.</p>

<pre>
after select

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          PAGE            72057594038386688    1:154          
IS           GRANT          OBJECT          vwLocks                             
IS           GRANT          OBJECT          indexed                             
S            GRANT          KEY             72057594038386688    (010086470766) 

after insert

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IX           GRANT          PAGE            72057594038386688    1:154          
IS           GRANT          OBJECT          vwLocks                             
X            GRANT          KEY             72057594038386688    (03000d8f0ecc) 
IX           GRANT          OBJECT          indexed                             
S            GRANT          KEY             72057594038386688    (010086470766) 

after update

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IX           GRANT          PAGE            72057594038386688    1:154          
IS           GRANT          OBJECT          vwLocks                             
X            GRANT          KEY             72057594038386688    (03000d8f0ecc) 
IX           GRANT          OBJECT          indexed                             
X            GRANT          KEY             72057594038386688    (020068e8b274) 
S            GRANT          KEY             72057594038386688    (010086470766) 
</pre>

<h3>Conclusion</h3>

<p>So if there is a Heap table and its modified inside a Serializable transaction you may well end up with the entire table locked for all other connections. Another reason I guess to avoid Heaps.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/05/29/heap-locking-under-serializable-transactions/feed/</wfw:commentRss>
		</item>
		<item>
		<title>How long do READ COMMITTED transactions hold shared locks</title>
		<link>http://www.thelastpickle.com/2008/05/23/how-long-do-read-committed-transactions-hold-shared-locks/</link>
		<comments>http://www.thelastpickle.com/2008/05/23/how-long-do-read-committed-transactions-hold-shared-locks/#comments</comments>
		<pubDate>Fri, 23 May 2008 17:51:09 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[locking]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sqlserver]]></category>

		<category><![CDATA[transaction]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/05/23/how-long-do-read-committed-transactions-hold-shared-locks/</guid>
		<description><![CDATA[This was one of the questions I did not answer in my previous look at Shared locks in standard transaction isolations. In that post the lowest level lock the READ COMMITTED test took was an Intent Shared (IS) lock on the single data page in the table. But I do remember reading somewhere that READ [...]]]></description>
			<content:encoded><![CDATA[<p>This was one of the questions I did not answer in my previous look at <a href="http://www.thelastpickle.com/2008/05/06/shared-locks-in-standard-transaction-isolations/">Shared locks in standard transaction isolations</a>. In that post the lowest level lock the READ COMMITTED test took was an Intent Shared (IS) lock on the single data page in the table. But I do remember reading somewhere that READ COMMITTED transactions only hold Shared locks on a page while data is read from the page, not for the duration of the query. The effects of this were hinted at with the Phantom Reads experienced during the READ COMMITTED query <a href="http://www.thelastpickle.com/2008/05/09/locking-without-a-transaction/">without a transaction</a>.</p>

<p>To look into this further I tried a similar approach used previously, setting up the environment as follows.</p>

<pre>
create database locks;
go

use locks;
go

create table data
(
    col1 char(1) primary key,
    col2 char(4500) not null
)

insert into
    data
values
    ('a', 'a');
insert into
    data
values
    ('b', 'b');
go

--drop view vwLocks
create view
    vwLocks
as
    select 
        cast(request_mode as varchar(5)) as request_mode ,
        cast(request_status as varchar(10)) as request_status ,
        cast(resource_type as varchar(15)) as resource_type,
        case 
            when resource_type = 'OBJECT' then
                cast(object_name(resource_associated_entity_id) as varchar(20)) 
            else
                cast(resource_associated_entity_id as varchar(20))
        end as resource_name,
        cast(resource_description as varchar(15)) as resource_description
    from 
        sys.dm_tran_locks
    where
        request_session_id = @@spid
go

-- drop and re-establish the connection to close any locks 
-- hanging around from creating the db.
</pre>

<p>This created a table where only a single row can fit on a data page, so each of the two rows added must be stored on a different data page. To verify the number of data pages in the table I used <code>DBCC IND</code>. It&#8217;s undocumented but there is some info out there, especially in <a href="http://www.amazon.com/Inside-Microsoft-SQL-Server-2005/dp/0735621055">Inside Microsoft SQL Server 2005: The Storage Engine</a>. </p>

<p>Pages of type 1 are the in row data pages, in my DB the first data page is 143 and the NextPagePID column shows that the next page is 154. To view the contents of the pages use <code>DBCC PAGE</code> after first turing on the trace flag 3604, I&#8217;ve used these undocumented methods <a href="http://www.thelastpickle.com/2008/04/20/snapshot-isolation-levels-and-phantom-reads/">previously</a>.</p>

<pre>
dbcc ind (locks, data, -1)

-- to view the contents of the pages
dbcc traceon(3604)
dbcc page (locks, 1,143,3)
</pre>

<p>The next step was to read from the data table and observe the locks taken.</p>

<pre>
set transaction isolation level read committed
begin transaction
select data.col1, l.* from data outer apply vwLocks as l
commit 

/*
col1 request_mode request_status resource_type   resource_name        resource_description
---- ------------ -------------- --------------- -------------------- --------------------
a    S            GRANT          DATABASE        0                                   
a    IS           GRANT          PAGE            72057594038321152    1:143          
a    IS           GRANT          OBJECT          data                                
a    IS           GRANT          OBJECT          vwLocks                             
b    S            GRANT          DATABASE        0                                   
b    IS           GRANT          PAGE            72057594038321152    1:143          
b    IS           GRANT          OBJECT          data                                
b    IS           GRANT          OBJECT          vwLocks                             
*/
</pre>

<p>When reading both of the records the view showed locks on page 143, this was not what I expected. I thought it would show an Intent Shared lock on page 143 when reading row &#8220;a&#8221; and an Intent Shared lock on page 154 when reading row &#8220;b&#8221;. </p>

<p>The query plan was helpful in solving this mystery. The results of the query against vwLocks are cached by a Table Spool operation and used to perform the join for both rows from the data table.</p>

<p><img src="/wp-content/uploads/2008/05/read_committed_shared_locks_query_plan.png" alt="read committed query plan" /></p>

<p>The Remote Scan in the lower right corner is the read against sys.dm&#95;tran&#95;locks.</p>

<p><img src="/wp-content/uploads/2008/05/read_committed_shared_locks_remote_scan.png" alt="remote scan operation" /></p>

<p>As seen in the properties of the Remote Scan operation reading from sys.dm&#95;tran&#95;locks occurs once and the results are used once (Actual Rebinds = 0 and Actual Rewinds = 0). However the Table Spool operation is evaluated twice (Actual Rebinds = 1 and Actual Rewinds =1). </p>

<p><img src="/wp-content/uploads/2008/05/read_committed_shared_locks_table_spool.png" alt="table spool operation" /></p>

<p>From the help page on <a href="http://msdn.microsoft.com/en-us/library/ms191158.aspx">Logical and Physical Operators</a>&#8230;</p>

<p><strong>&#8220;If an operator is on the inner side of a loop join, the sum of the number of rebinds and rewinds should equal the number of rows processed on the outer side of the join. A rebind means that one or more of the correlated parameters of the join changed and the inner side must be reevaluated. A rewind means that none of the correlated parameters changed and the prior inner result set may be reused.&#8221;</strong></p>

<p>So I needed to remove the caching effect of the Table Spool to see the lock state for each row read from the table. I tried using join predicates and table valued functions that took parameters but they all included spooling and returned the same values. Finally I settled on a c# table valued function that queried vwLocks; the query engine did not dare cache the results from something so wacky as a clr routine. </p>

<p>The c# function queries the view using the context connection, this is the connection the request is running on. It then reads the results into  objects so it can dispose of the <code>SqlDataReader</code> and associated objects before returning the <code>IEnumerabl</code>e collection. The <code>Fill</code> method is then called by SQL to project the data in each object in the collection to the scalar column values of the table. </p>

<p>The full c# code was.</p>

<pre>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{

    private readonly static string _sql = "select * from vwLocks"; 

    //the table definition property has been re-formated to fit on the page
    //it should be on a single line
    
    [Microsoft.SqlServer.Server.SqlFunction(
        DataAccess=DataAccessKind.Read, 
        FillRowMethodName="Fill", 
        IsDeterministic=false, 
        Name="tvf_c_locks", 
        SystemDataAccess=SystemDataAccessKind.Read, 
        TableDefinition="request_mode nvarchar(5), request_status nvarchar(10), 
            resource_type nvarchar(15), resource_name nvarchar(20), resource_description nvarchar(25)")]
    public static IEnumerable tvc_c_locks()
    {
        using (SqlConnection conn = new SqlConnection("context connection = true"))
        {
            conn.Open();
            using (SqlCommand cmd = new SqlCommand(_sql, conn))
            {
                using (SqlDataReader r = cmd.ExecuteReader(CommandBehavior.SingleResult))
                {
                    List<row> result = new List</row><row>();

                    while (r.Read())
                    {
                        result.Add(new Row(
                            safeString(r, 0), safeString(r, 1),safeString(r, 2),
                            safeString(r, 3),safeString(r, 4)));
                    }
                    return result;
                }
            }
        }
    }

    private static SqlString safeString(SqlDataReader reader, int index)
    {
        if (reader.IsDBNull(index))
        {
            return SqlString.Null;
        }
        return new SqlString(reader.GetString(index));
    }

    public static void Fill(object row, out SqlString request_mode, out SqlString request_status, 
        out SqlString resource_type, out SqlString resource_name, out SqlString resource_description)
    {
        Row r = row as Row;

        request_mode = r._request_mode;
        request_status = r._request_status;
        resource_type = r._resource_type;
        resource_name = r._resource_name;
        resource_description = r._resource_description;
    }

    public class Row
    {
        public readonly SqlString _request_mode;
        public readonly SqlString _request_status;
        public readonly SqlString _resource_type;
        public readonly SqlString _resource_name;
        public readonly SqlString _resource_description;

        public Row(SqlString request_mode, SqlString request_status, 
                    SqlString resource_type, SqlString resource_name, SqlString resource_description)
        {
            _request_mode = request_mode;
            _request_status = request_status;
            _resource_type = resource_type;
            _resource_name = resource_name;
            _resource_description = resource_description;
        }
    }
};
</row></pre>

<p>With the udf deployed to SQL Server using Visual Studio I queried it as follows.</p>

<pre>
set transaction isolation level read committed
begin transaction
select data.col1,l.*  from data outer apply dbo.tvf_c_locks() as l
commit

/*
col1 request_mode request_status resource_type   resource_name        resource_description
---- ------------ -------------- --------------- -------------------- -------------------------
a    S            GRANT          DATABASE        0                                   
a    Sch-S        GRANT          METADATA        0                    assembly_id = 6
a    IS           GRANT          PAGE            72057594038321152    1:143          
a    Sch-S        GRANT          OBJECT          tvf_c_locks                         
a    IS           GRANT          OBJECT          data                                
a    IS           GRANT          OBJECT          vwLocks                             
b    S            GRANT          DATABASE        0                                   
b    Sch-S        GRANT          METADATA        0                    assembly_id = 6
b    IS           GRANT          PAGE            72057594038321152    1:154          
b    Sch-S        GRANT          OBJECT          tvf_c_locks                         
b    IS           GRANT          OBJECT          data                                
b    IS           GRANT          OBJECT          vwLocks                             
*/
</pre>

<p>A number of new locks taken during the query to maintain the stability of the c# function. But the results show that when reading row &#8220;a&#8221; a Page level Intent Shared lock was taken on page 143 only; and when reading row &#8220;b&#8221; an IS lock was taken on page 154 only. This looks like the result I wanted; page level locking taken for the duration of the read from the page and not for the duration of the query.</p>

<p>If the page level locks were maintained I would expect to see the lock on page 143 also present when row &#8220;b&#8221; was read. Such as under a REPEATABLE READ transaction where the contents of page 143 must be protected from change for the duration of the query / transaction as shown below. </p>

<pre>
set transaction isolation level repeatable read
begin transaction
select data.col1,l.*  from data outer apply dbo.tvf_c_locks() as l
commit

col1 request_mode request_status resource_type   resource_name        resource_description
---- ------------ -------------- --------------- -------------------- -------------------------
a    S            GRANT          DATABASE        0                                   
a    Sch-S        GRANT          METADATA        0                    assembly_id = 6
a    IS           GRANT          PAGE            72057594038321152    1:143          
a    Sch-S        GRANT          OBJECT          tvf_c_locks                         
a    S            GRANT          KEY             72057594038321152    (6100bc414817) 
a    IS           GRANT          OBJECT          data                                
a    IS           GRANT          OBJECT          vwLocks                             
b    S            GRANT          DATABASE        0                                   
b    Sch-S        GRANT          METADATA        0                    assembly_id = 6
b    IS           GRANT          PAGE            72057594038321152    1:143          
b    IS           GRANT          PAGE            72057594038321152    1:154          
b    Sch-S        GRANT          OBJECT          tvf_c_locks                         
b    S            GRANT          KEY             72057594038321152    (6100bc414817) 
b    IS           GRANT          OBJECT          data                                
b    S            GRANT          KEY             72057594038321152    (62000610418e) 
b    IS           GRANT          OBJECT          vwLocks                             
</pre>

<p>In these results the lock on page 143 is still present when reading row &#8220;b&#8221; from page 154. As seen in <a href="http://www.thelastpickle.com/2008/05/06/shared-locks-in-standard-transaction-isolations/">previous posts</a> these locks will be maintained to the end of the transaction.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/05/23/how-long-do-read-committed-transactions-hold-shared-locks/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Discovering the Isolation Level</title>
		<link>http://www.thelastpickle.com/2008/05/22/discovering-the-isolation-level/</link>
		<comments>http://www.thelastpickle.com/2008/05/22/discovering-the-isolation-level/#comments</comments>
		<pubDate>Fri, 23 May 2008 12:14:36 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[dbcc]]></category>

		<category><![CDATA[dmv]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sqlserver]]></category>

		<category><![CDATA[transaction]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/05/23/discovering-the-isolation-level/</guid>
		<description><![CDATA[A few times I&#8217;ve wondered how to detect the ISOLATION level and have discovered a few. 

DBCC USEROPTIONS


DBCC USEROPTIONS

Set Option                           Value
-------------------------------------------------------------------------------
textsize         [...]]]></description>
			<content:encoded><![CDATA[<p>A few times I&#8217;ve wondered how to detect the ISOLATION level and have discovered a few. </p>

<h3><a href="http://msdn.microsoft.com/en-us/library/ms180065.aspx">DBCC USEROPTIONS</a></h3>

<pre>
DBCC USEROPTIONS

Set Option                           Value
-------------------------------------------------------------------------------
textsize                             2147483647
language                             us_english
dateformat                           mdy
datefirst                            7
lock_timeout                         -1
quoted_identifier                    SET
arithabort                           SET
ansi_null_dflt_on                    SET
ansi_warnings                        SET
ansi_padding                         SET
ansi_nulls                           SET
concat_null_yields_null              SET
isolation level                      read committed
</pre>

<h3><a href="http://technet.microsoft.com/en-us/library/ms176013.aspx">sys.dm&#95;exec&#95;sessions</a></h3>

<pre>
select
    session_id, 
    case (transaction_isolation_level)
        when 0 then 'Unspecified'
        when 1 then 'Read Uncommitted'
        when 2 then 'Read Committed'
        when 3 then 'Repeatable'
        when 4 then 'Serializable'
        when 5 then 'Snapshot'
    end as transaction_isolation_level_desc
from
    sys.dm\_exec\_sessions 
where
    session_id = @@spid

/*
session_id transaction_isolation_level_desc
---------- --------------------------------
55         Read Committed
*/
</pre>

<h3><a href="http://msdn.microsoft.com/en-us/library/ms177648.aspx">sys.dm&#95;exec&#95;requests</a></h3>

<pre>
select
    session_id, 
    case (transaction_isolation_level)
        when 0 then 'Unspecified'
        when 1 then 'Read Uncommitted'
        when 2 then 'Read Committed'
        when 3 then 'Repeatable'
        when 4 then 'Serializable'
        when 5 then 'Snapshot'
    end as transaction_isolation_level_desc
from
    sys.dm_exec_requests 
where
    session_id = @@spid
    
/*
session_id transaction_isolation_level_desc
---------- --------------------------------
55         Read Committed
*/
</pre>

<p>Remember that the isolation level can be modified for query / statement through the use of <a href="">table hints</a> to be different to the level in place when the transaction started. This is supported by the sys.dm&#95;exec&#95;requests view as it has a many to one relationship with a transaction and represents a substring of the <a href="http://msdn.microsoft.com/en-us/library/ms181929.aspx">sys.dm&#95;exec&#95;sql&#95;text</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/05/22/discovering-the-isolation-level/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Locking without a Transaction</title>
		<link>http://www.thelastpickle.com/2008/05/09/locking-without-a-transaction/</link>
		<comments>http://www.thelastpickle.com/2008/05/09/locking-without-a-transaction/#comments</comments>
		<pubDate>Sat, 10 May 2008 08:19:59 +0000</pubDate>
		<dc:creator>aaron</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[locking]]></category>

		<category><![CDATA[sql 2005]]></category>

		<category><![CDATA[sqlserver]]></category>

		<category><![CDATA[transaction]]></category>

		<guid isPermaLink="false">http://www.thelastpickle.com/2008/05/10/locking-without-without-a-transaction/</guid>
		<description><![CDATA[Well, not really without a transaction. But I was wondering what locking schemes are used when a query is run outside of an explicit (BEGIN / COMMIT) transaction. What impact does changes to the isolation level have?

The simple answer is its the same when compared to the locking examples I previously identified when running under [...]]]></description>
			<content:encoded><![CDATA[<p>Well, not really without a transaction. But I was wondering what locking schemes are used when a query is run outside of an explicit (BEGIN / COMMIT) transaction. What impact does changes to the isolation level have?</p>

<p>The simple answer is its the same when compared to the <a href="http://www.thelastpickle.com/2008/05/07/shared-locks-in-standard-transaction-isolations/">locking examples</a> I previously identified when running under different isolation levels. Without an explicit transaction in place the query will run under an <a href="http://msdn.microsoft.com/en-us/library/ms187878.aspx">Auto Commit Transaction</a>, which sound a lot like what used to be called &#8220;system transactions&#8221;. </p>

<p>A quick execution of some modified code from the previous post shows the same locks taken under the SERIALIZABLE isolation level when an explicit transaction is not in place. </p>

<pre>
set transaction isolation level  serializable

select * from vwLocks
select * from data cross apply vwLocks
select * from vwLocks 

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             

col1 request_mode request_status resource_type   resource_name        resource_description
---- ------------ -------------- --------------- -------------------- --------------------
a    S            GRANT          DATABASE        0                                   
a    IS           GRANT          PAGE            72057594038321152    1:143          
a    RangeS-S     GRANT          KEY             72057594038321152    (6100bc414817) 
a    IS           GRANT          OBJECT          data                                
a    IS           GRANT          OBJECT          vwLocks                             


request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             
</pre>

<p>And the same is seen for the READ COMMITTED isolation level.</p>

<pre>
request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             


col1 request_mode request_status resource_type   resource_name        resource_description
---- ------------ -------------- --------------- -------------------- --------------------
a    S            GRANT          DATABASE        0                                   
a    IS           GRANT          PAGE            72057594038321152    1:143          
a    IS           GRANT          OBJECT          data                                
a    IS           GRANT          OBJECT          vwLocks                             

request_mode request_status resource_type   resource_name        resource_description
------------ -------------- --------------- -------------------- --------------------
S            GRANT          DATABASE        0                                   
IS           GRANT          OBJECT          vwLocks                             
</pre>

<p>The query must still take locks in order to protect against reading data that is currently being modified. And the absence of a transaction means there is no delineated time span in which future modifications must be prevented. So locks are not held after the query has executed.</p>

<p>That got me thinking; if the guarantees about future changes (Repeatable Reads and Phantom Reads) don&#8217;t apply because there is no transaction it seems odd that SERIALIZABLE needs to take stronger locks than READ COMMITTED. After all they are both implementing the same guarantee about current modifications (no Dirty Reads).</p>

<p>But its not just a transaction that has duration, a query can as well. What if the query involved multiple reads from the same table; or spooled from a non clustered index and then performed lookups on a heap or clustered index. In those cases would the guarantees of the isolation level apply for the duration of the query?</p>

<p>I could not find a way to show the locks taken during the various phases of a query as I could with the simple examples in the <a href="http://www.thelastpickle.com/2008/05/07/shared-locks-in-standard-transaction-isolations/">previous post on locks</a>. But I was able to create a test that demonstrated different isolation levels. </p>

<p>First I created a table of numbers so I could create a long running query. The code below uses the technique from chapter 4 of <a href="http://www.amazon.co.uk/Inside-Microsoft-SQL-Server-2005/dp/0735623139/ref=sr_1_4?ie=UTF8&amp;s=books&amp;qid=1210434074&amp;sr=8-4">Inside SQL Server 2005: T-SQL Querying</a> which can be found <a href="http://www.sql.co.il/books/insidetsql2005/">here</a>. I&#8217;ve removed the primary key to ensure the queries take a long time to complete; you should adjust the number of rows (using the @max variable) in the table so there is enough time to run the second query. </p>

<pre>
IF OBJECT_ID('dbo.Nums') IS NOT NULL
  DROP TABLE dbo.Nums;
GO
CREATE TABLE dbo.Nums(n INT NOT NULL);
DECLARE @max AS INT, @rc AS INT;
SET @max = 10000;
SET @rc = 1;

INSERT INTO Nums VALUES(1);
WHILE @rc * 2 < = @max
BEGIN
  INSERT INTO dbo.Nums SELECT n + @rc FROM dbo.Nums;
  SET @rc = @rc * 2;
END

INSERT INTO dbo.Nums 
  SELECT n + @rc FROM dbo.Nums WHERE n + @rc <= @max;
GO
</pre>

<p>Next I modified the vwLocks view created in the previous post to show locks from all connections as follows.</p>

</pre><pre>
ALTER view
    [dbo].[vwLocks]
as
    select 
        cast(request_mode as varchar(10)) as request_mode ,
        cast(request_status as varchar(10)) as request_status ,
        cast(resource_type as varchar(15)) as resource_type,
        case 
            when resource_type = 'OBJECT' then
                cast(object_name(resource_associated_entity_id) as varchar(20)) 
            else
                cast(resource_associated_entity_id as varchar(20))
        end as resource_name,
        cast(resource_description as varchar(15)) as resource_description
    from
        sys.dm_tran_locks
</pre>

<p>I then created a query that took 7 seconds to complete; it finds numbers in the table that have 0 or 1 numbers higher than them by counting all of the numbers lower than the first.</p>

<pre>
set transaction isolation level read committed

select 
    parent.n
from 
    Nums as parent
where
    1 >= 
    (
        select 
            count(child.n)
        from
            Nums as child
        where
            child.n > parent.n
    )
</pre>

<p>The query plan for this is nasty; on my machine it involved scanning the Parent alias and scanning then spooling the Child alias 10001 times. When run by it returns two numbers: 9999 and 10000. The next time I ran it I quickly switched to another connection and inserted row into the table as follows:</p>

<pre>
insert into Nums
values(9999999)
</pre>

<p>Every time I did this the Insert completed immediately and the select query returned the new row as well as the previous values.</p>

<pre>
n
-----------
9999
10000
9999999

(3 row(s) affected) 
</pre>

<p>If the Select query is run after the new row is inserted only two numbers are returned: 10000 and 9999999. Inserting the row while the Select query is running results in a Phantom Read; the new row was not present when the Parent number 9999 was considered otherwise it would not have been included. It is was present and included in the result set later in the execution of the query. </p>

<p>When the test is run under SERIALIZABLE isolation level the Insert statement blocks until the Select has completed. The select returns the numbers 9999 and 10000 as it should, when run again after the Insert has completed it returns 10000 and 9999999. </p>

<p>While I cannot demonstrate the locks during the query it seems clear that the SERIALIZABLE query took and held locks which prevented Phantom Reads occurring, and so prevented the Insert from completing. And that the READ COMMITTED query either did not take or did not hold its locks long enough to prevent the Phantom Reads as it does not guard against them; and so the Insert was able to add a phantom record that was sucked into the query.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thelastpickle.com/2008/05/09/locking-without-a-transaction/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.977 seconds -->
