An XQuery Programming Scenario

We present here a scenario of programming in XQuery inspired by the relational use case in [XQ-UC]. We follow the steps of a programmer who is writing a query that transforms data about an auction into an XHTML document. Each step in the scenario reveals an error in the query, explains how the programmer found the error, and explains how to correct the error.

We start with a description of the input data and schema and the output schema. The input data in Listing 4.1 is information about an auction that has been exported from a relational source in XML. The XML data conforms to the XML Schema in Listing 4.2. An auction contains a set of users, articles, and bids. A user has a name , a rating, and a unique identifier. An article is offered by a user, is available for sale between a start and end date, and has a reserve price and description. A bid associates a user with an article and a bid price.

Listing 4.1 Relational Auction Data in XML
 <auction xmlns="http://www.example.com/auction">   <users>     <user id="U01">       <name><first>Tom</first><last>Jones</last></name>       <rating>B</rating>     </user>     <user id="U02">       <name><first>Mary</first><last>Doe</last></name>       <rating>A</rating>     </user>     <user id="U03">       <name><first>Dee</first><last>Linquent</last></name>       <rating>D</rating>     </user>     <user id="U04">       <name><first>Roger</first><last>Smith</last></name>       <rating>C</rating>     </user>     <user id="U05">       <name><first>Jack</first><last>Sprat</last></name>       <rating>B</rating>     </user>   </users>   <articles>     <article id="1001">       <name>Red Bicycle</name>       <seller idref="U01"/>       <start_date>1999-01-05</start_date>       <end_date>1999-01-20</end_date>       <reserve_price>40</reserve_price>     </article>     <article id="1002">       <name>Motorcycle</name>       <seller idref="U02"/>       <start_date>1999-02-11</start_date>       <end_date>1999-03-15</end_date>       <reserve_price>500</reserve_price>       <description>In <em>really</em> good condition!</description>     </article>     <article id="1003">       <name>Old Bicycle</name>       <seller idref="U02"/>       <start_date>1999-01-10</start_date>       <end_date>1999-02-20</end_date>       <reserve-price>10</reserve-price>       <description>I bought it from Jack last month.</description>     </article>   </articles>   <bids>     <bid>       <userid idref="U02"/>       <articleno idref="1001"/>       <amount>45</amount>       <date>1999-01-11</date>     </bid>     <bid>       <userid idref="U04"/>       <articleno idref="1001"/>       <amount>50</amount>       <date>1999-01-13</date>     </bid>     <bid>       <userid idref="U02"/>       <articleno idref="1001"/>       <amount>55</amount>       <date>1999-01-15</date>     </bid>     <bid>       <userid idref="U01"/>       <articleno idref="1002"/>       <amount>400</amount>       <date>1999-02-14</date>     </bid>     <bid>       <userid idref="U02"/>       <articleno idref="1002"/>       <amount>600</amount>       <date>1999-02-16</date>     </bid>     <bid>       <userid idref="U04"/>       <articleno idref="1002"/>       <amount>1000</amount>       <date>1999-02-25</date>     </bid>     <bid>       <userid idref="U02"/>       <articleno idref="1002"/>       <amount>1200</amount>       <date>1999-03-02</date>     </bid>     <bid>       <userid idref="U05"/>       <articleno idref="1003"/>       <amount>20</amount>       <date>1999-02-03</date>     </bid>   </bids> </auction> 
Listing 4.2 XML Schema for the Relational Auction Data
 <xs:schema targetNamespace="http://example.com/auction"            xmlns:xs="http://www.w3.org/2001/XMLSchema"            xmlns="http://www.example.com/auction">   <xs:element name="auction">     <xs:complexType>        <xs:sequence>           <xs:element name="users"/>           <xs:element name="articles"/>           <xs:element name="bids"/>        </xs:sequence>     </xs:complexType>   </xs:element>   <xs:element name="users">     <xs:complexType>        <xs:element ref="user" maxOccurs="unbounded"/>     </xs:complexType>   </xs:element>   <xs:element name="articles">     <xs:complexType>        <xs:element ref="article" maxOccurs="unbounded"/>     </xs:complexType>   </xs:element>   <xs:element name="bids">     <xs:complexType>        <xs:element ref="bid" maxOccurs="unbounded"/>     </xs:complexType>   </xs:element>   <xs:element name="rating" type="xs:string"/>   <xs:element name="user" type="User"/>   <xs:complexType name="User">      <xs:sequence>         <xs:element name="name">           <xs:complexType>             <xs:element name="first" type="xs:string" minOccurs="0"/>             <xs:element name="last" type="xs:string"/>           </xs:complexType>         </xs:element>         <xs:element ref="rating" minOccurs="0"/>      </xs:sequence>      <xs:attribute name="id" type="xs:ID" use="required"/>   </xs:complexType>   <xs:attribute name="idref" type="xs:IDREF"/>   <xs:element name="article" type="Article"/>   <xs:complexType name="Article">      <xs:sequence>         <xs:element name="name" type="xs:string"/>         <xs:element name="seller">            <xs:complexType>              <xs:attribute ref="idref" use="required"/>            </xs:complexType>         </xs:element>         <xs:element name="start_date" type="xs:date"/>         <xs:element name="end_date" type="xs:date"/>         <xs:element name="reserve_price" type="xs:decimal"/>         <xs:element name="description" type="Freeform" minOccurs="0"/>      </xs:sequence>      <xs:attribute name="id" type="xs:ID" use="required"/>   </xs:complexType>   <xs:complexType name="Freeform" mixed="true">     <xs:anyAttribute processContents="skip">     <xs:any processContents="skip" minOccurs="0"              maxOccurs="unbounded"/>   </xs:complexType>   <xs:element name="bid" type="Bid"/>   <xs:complexType name="Bid">      <xs:sequence>         <xs:element name="userid">            <xs:complexType>              <xs:attribute ref="idref" use="required"/>            </xs:complexType>         </xs:element>         <xs:element name="articleno">            <xs:complexType>              <xs:attribute ref="idref" use="required"/>            </xs:complexType>         </xs:element>         <xs:element name="amount" type="xs:decimal"/>      </xs:sequence>   </xs:complexType> </xs:schema> 

The output is an XHTML document that can be rendered by a browser. It contains a table of all of the currently active auctions. For each auctioned article, the output table contains the name of the person selling the article, the last (i.e., most recent) bid on the article, and the expiration date for the article. The desired output page is shown in Figure 4.1 in the Mozilla browser. The output XHTML document must conform to the schema in Listing 4.3. This schema is a subset of the complete XHTML schema and includes headings, tables, paragraphs, and links.

Listing 4.3 XML Schema for XHTML
 <xs:schema targetNamespace="http://www.w3.org/1999/xhtml"            xmlns:xs="http://www.w3.org/2001/XMLSchema"            xmlns="http://www.w3.org/1999/xhtml">   <xs:element name="html" type="html.type"/>   <xs:complexType name="html.type">     <xs:sequence>       <xs:element ref="head"/>       <xs:element ref="body"/>     </xs:sequence>   </xs:complexType>   <xs:element name="head" type="head.type"/>   <xs:complexType name="head.type">     <xs:element ref="title"/>   </xs:complexType>   <xs:element name="title" type="title.type"/>   <xs:complexType name="title.type" mixed="true"/>   <xs:element name="body" type="Block.type"/>   <xs:complexType name="Block.type" mixed="true">     <xs:choice minOccurs="0" maxOccurs="unbounded">        <xs:element ref="h1"/>        <xs:element ref="p"/>        <xs:element ref="table"/>     </xs:choice>   </xs:complexType>   <xs:element name="h1" type="heading.type"/>   <xs:complexType name="heading.type" mixed="true">       <xs:element ref="a" minOccurs="0" maxOccurs="unbounded"/>   </xs:complexType>   <xs:element name="p" type="p.type"/>   <xs:complexType name="p.type" mixed="true">       <xs:element ref="a" minOccurs="0" maxOccurs="unbounded"/>   </xs:complexType>   <xs:element name="a" type="a.type"/>   <xs:complexType name="a.type" mixed="true">      <xs:attribute name="href" type="xs:anyURI"/>   </xs:complexType>   <xs:element name="table" type="table.type"/>   <xs:complexType name="table.type" mixed="true">       <xs:element ref="tr" maxOccurs="unbounded"/>   </xs:complexType>   <xs:element name="tr" type="tr.type"/>   <xs:complexType name="tr.type">       <xs:choice maxOccurs="unbounded">           <xs:element ref="th"/>           <xs:element ref="td"/>       </xs:choice>   </xs:complexType>   <xs:element name="th" type="Block.type"/>   <xs:element name="td" type="Block.type"/> </xs:schema> 
Figure 4.1. Desired Result

graphics/04fig01.gif

The XQuery in Listing 4.4 takes as input the data in Listing 4.1 and generates an XHTML document containing the table of articles available for auction. The external global variable $input is bound to the input document in the query evaluation environment. Line [1] specifies that the auction schema namespace is the default element namespace for elements constructed by the query, and line [2] declares the namespace prefix 'x' for the XHTML output document. Line [3] defines a global variable $base containing the base URI for the output document. Lines [4 “6] define the function makeURI , which converts a string to a URI and is called on lines [25] and [28]. Lines [9 “18] construct an HTML element that contains the body of the page, which in turn contains a table that contains the headers for each column in the table. Lines [20 “34] construct one row in the table for each article that is still available for auction. Each row contains the article's name (with a link to the article's description), the seller's name, the last bid, and the expiration date for the article.

Listing 4.4 XQuery
 [1] default element namespace = "http://www.example.com/auction"  [2] declare namespace x = "http://www.w3.org/1999/xhtml"   [3] define variable $base :=                          xs:anyURI("http://www.example.com/auction/")  [4] define function makeURI ($resource as xs:string) as xs:anyURI {  [5]   xs:anyURI(fn:concat(xs:string($base), $resource))  [6] }  [7] let $auction := $input/auction  [8] return   [9] <x:html> [10]   <x:body> [11]     <x:h1>Auctions</x:h1> [12]     <x:table> [13]       <x:td> [14]         <x:th>Item Name</th> [15]         <x:th>Seller</x:th> [16]         <x:th>Last Bid</x:th> [17]         <x:th>Closes On</x:th> [18]       </x:td> [19]       {  [20]         for $article in $auction/articles/article[start_date <=                                                                date()] [21]         let $last_bid := $auction/bids/bid[articleno/@idref =                                                  $article/@id][last()] [22]         return [23]           <x:td> [24]             <x:tr> [25]               <x:a href="{ makeURI($article/@id) }">{                                           data($article/anme) }</x:a> [26]             </x:tr> [27]             <x:tr> [28]               <x:a href="{ makeURI($article//@idref) }">{ [29]                fn:data($auction/users/user[@id =                                                $article//@idref]/name) [30]               }</x:a> [31]             </x:tr> [32]             <x:tr>{ fn:data($last_bid/amount) }</x:tr> [33]             <x:tr>{ fn:data($last_bid/date) }</x:tr> [34]           </x:td>  [35]       } [36]     </x:table> [37]   </x:body> [38] </x:html> 

Debugging

Running the query on the input auction document results in the web page in Figure 4.2. As you can see, the table is a mess. The query apparently contains some errors. A closer look at the query reveals that the tr and td tags are inverted on lines [13 “18] and [23 “34]. Fixing that problem results in the Web page in Figure 4.3. On our second try, we see that the article names in the first column are missing. This results from an error on line [25], in which the article's element name is misspelled as anme instead of name . This expression always yields empty content, because an article element does not contain any anme elements. Fixing this problem results in the Web page in Figure 4.4.

Figure 4.2. First Try

graphics/04fig02.gif

Figure 4.3. Second Try

graphics/04fig03.gif

Figure 4.4. Third Try

graphics/04fig04.gif

On our third try, the output looks identical to the desired output in Figure 4.1, so we are almost done. It turns out, however, that the query still has an error that is not tripped by our example input data but might be exercised by other data. The link to the seller is computed by this expression on line [28] of Listing 4.4:

 <x:a href="{ makeURI($article//@idref) }"> 

This expression accesses the idref attribute of some descendant of the article. In Listing 4.2, it appears that the idref attribute must be in the seller element, but we also see that the description element has type xs:anyType and, therefore, might contain idref attributes. If we apply this query to data in which some description element contains an idref attribute, the query will generate a run-time type error because the function makeURI expects one value, not multiple values, as input. When we evaluate the query on data of this kind (our fourth try), we get the error message shown below:

[View full width]
 
[View full width]
Line [28]: Run-time type error. Argument to function 'makeURI' contains more than one graphics/ccc.gif value.

We can correct the error by replacing $article//@idref with $article/seller/@idref .

After four cycles of debugging by changing and then running the query, we believe that we have a query that will generate the correct output for any input auction data. Next, we consider how document validation and static typing can help to shorten this debugging cycle.

Validation

The XML community (and before that the SGML community) have long understood the importance of validating the formats of the input and output documents. This was first done using DTDs and more recently using XML Schema. A typical query-processing model validates the input against a specified input DTD or XML Schema, then evaluates the query on the input that generates the output, and finally validates the output against a specified output DTD or XML Schema.

To enable validation in our example, we rewrite the query to explicitly import the schemas of the input and output and to explicitly validate the input data. We do not need to explicitly validate the output, because element constructors implicitly validate the elements that they construct. The following code segment shows the changes to lines [1 “2, 7] in the query shown in Listing 4.4 and adds line [2a]. (The other lines are identical to those in Listing 4.4, after correcting for the errors discovered in our first four tries .)

 [1] import schema default element namespace =                      "http://www.example.com/auction" at "auction.xsd" [2] import schema namespace x = "http://www.w3.org/1999/xhtml" at "xhtml.xsd" [2a] default validation strict      ... [7] let $auction := validate { $input } 

XML Schema provides three modes of validation (strict , skip , and lax) . Line [2a] defines that the default validation mode for the query is strict . This query will raise an error at run-time if the input document does not conform to the auction schema or if the output document does not conform to the XHTML schema.

Validation aids the debugging cycle because it flags errors that we previously had to identify by examining the output. When we run the query, we get the following error message:

 Validation error:  Missing 'x:head' element in 'x:html' element. 

This error is raised because the XHTML schema requires a head element containing a title element. We did not detect this error during our debugging process because browsers try to produce output whenever they can, even if validation would fail. If we look closely, we see that the desired output in Figure 4.1 is not identical to the actual output in Figure 4.4, because the former contains the title "Auctions" in the title bar, whereas the latter contains only the name of the browser.

How would validation affect our earlier debugging cycles? Validation detects the first error in which we inverted the tr and td elements. But validation fails to detect the second error, in which a misspelled element name leads to empty content, because the XHTML schema permits empty content in the "a" (anchor) element. Validation also fails to detect the third error, which occurs for input data in which description elements contain idref attributes. This happens because the input data that triggers the bug is perfectly valid against the given input schema, and if the input does trigger the bug, there will be no output data to validate against the output schema, instead the makeURI function will raise an error.

Static Typing

So far, our debugging cycle has consisted of running the query, which may raise a validation error or produce output, possibly observing an error in the output, identifying and correcting the bug in the query, and then running the query again. Static typing can help us to shorten this cycle.

Static typing differs from validation in that validation detects the presence of certain kinds of errors in the format of the data, whereas static typing guarantees their absence. In particular, static typing performs a conservative analysis of the query and raises a static type error when the analysis cannot guarantee that the structure of the result conforms to the structure required by the output schema.

Static typing can greatly accelerate the debugging cycle. First, it can spot errors during static analysis rather than at run-time. Second, it can detect many errors at once without requiring multiple runs of the query. Third, it can spot errors without the need for a tester to concoct data that will exercise those errors.

The result of static typing the query in Listing 4.4 (after the revisions above) is shown in Listing 4.5.

Listing 4.5 Static Errors
 Line [10]: Element 'x:body' encountered where element 'x:head' expected. Line [13]: Element 'x:td' encountered where element 'x:tr' expected. Line [23]: Element 'x:td' encountered where element 'x:tr' expected. Line [25]: Expression '$article/anme' is always empty. Line [28]: Argument to function 'makeURI' has actual type            ( attribute(@idref)  attribute(@idref,xs:anySimpleType) )+             instead of expected type 'xs:string'. 

The error on line [10] is discovered because the static type inferred for the content of the x:html element does not conform to the static type declared for the content of the html element in the XHTML schema in Listing 4.3. Similarly, the errors on lines [13] and [23] are discovered because the static type inferred for the content of the table element does not conform to the static type declared for the content of the table element in Listing 4.3. The error on line [25] is discovered because the static type inferred for the expression $article/anme is the empty sequence, since the static type of an article element does not contain any anme elements. The error on line [28] is discovered because the static type inferred for the expression $article//@idref is

 ( attribute(@idref)  attribute(@idref,xs:anySimpleType) )+ 

but the static type declared for argument to the function makeURI is xs:string . The type above is a mouthful. Where does it come from? There is one idref attribute declared in $article/seller , and there may be zero or more idref attributes of any simple type in $article/description ; hence, there are one or more idref attributes in total.

Correcting all of these type errors yields the query shown in Listing 4.6. (The corrections are in bold italics.) This query passes static type-checking and yields the desired result shown in Figure 4.1. In our example, all of the errors were type errors and were detected by the static type system. This may not always be the case, however; for example, a query may contain other kinds of errors that can only be detected during evaluation.

Listing 4.6 Corrected Query
 [1] default element namespace = "http://www.example.com/auction"  [2] declare namespace x = "http://www.w3.org/1999/xhtml"  [2a] default validation strict  [3] define variable $base :=                          xs:anyURI("http://www.example.com/auction/")  [4] define function makeURI ($resource as xs:string) as xs:anyURI {  [5]   xs:anyURI(fn:concat(xs:string($base), $resource))  [6] }  [7] let $auction := $input/auction  [8] return   [9] <x:html> [9a]   <x:head><x:title>Auctions</x:title></x:head> [10]   <x:body> [11]     <x:h1>Auctions</x:h1> [12]     <x:table> [13]       <x:tr> [14]         <x:th>Item Name</th> [15]         <x:th>Seller</x:th> [16]         <x:th>Last Bid</x:th> [17]         <x:th>Closes On</x:th> [18]       </x:tr> [19]       {  [20]         for $article in $auction/articles/article[start_date <=                                                                date()] [21]         let $last_bid :=              $auction/bids/bid[articleno/@idref=$article/@id][last()] [22]         return [23]           <x:tr> [24]             <x:td> [25]               <x:a href="{xs:anyURI($article/@id)}">{                                            data($article/name) }</x:a> [26]             </x:td> [27]             <x:td> [28]               <x:a href="{xs:anyURI($article/seller/@idref)}">{ [29]                fn:data($auction/users/user[@id =                                                $article//@idref]/name) [30]               }</x:a> [31]             </x:td> [32]             <x:td>{ fn:data($last_bid/amount) }</x:td> [33]             <x:td>{ fn:data($last_bid/date) }</x:td> [34]           </x:tr> [35]       } [36]     </x:table> [37]   </x:body> [38] </x:html> 


XQuery from the Experts(c) A Guide to the W3C XML Query Language
Beginning ASP.NET Databases Using VB.NET
ISBN: N/A
EAN: 2147483647
Year: 2005
Pages: 102

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net