1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875
|
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
-
- This file is part of the OpenLink Software Virtuoso Open-Source (VOS)
- project.
-
- Copyright (C) 1998-2018 OpenLink Software
-
- This project is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; only version 2 of the License, dated June 1991.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-->
<chapter label="webandxml.xml" id="webandxml">
<title>XML Support</title>
<abstract>
<para>This chapter covers Virtuoso's XML, full text retrieval and related functions.</para>
<para>Virtuoso provides free text indexing capabilities for
textual data, including XML data. Virtuoso supports extraction of
XML documents from SQL datasets. It also supports <link linkend="fn_xpath_eval">XPath</link>,
<link linkend="xq">XQuery</link>,
<link linkend="xslttrans">XSLT</link> and XML Schema validation.</para>
<para>
An SQL long varchar, long xml or xmltype column in a database table can contain XML data as text or in a binary serialized format, respectively. If
a column value is a well-formed XML fragment there are
special operations that can be performed on the value.
There is an SQL data type that represents an XML entity.
A string representing a
well-formed XML entity can be converted into an entity object
representing the root node. XPath expressions can then be applied to
the entity in order to retrieve other entities or sets of entities.
Returning an XML entity to a client application or printing it out on
a dynamic web page will produce the text representation of the entity,
complete with start and end tags, attributes, namespaces, and so forth.
</para>
<para>
An entity object can be stored as a value of a long varchar or varchar
column. This will produce and store the text corresponding to the
entity. Storing the same into a long xml or xmltype column will provide a more compact representation and will guarantee well-formedness and optionally schema validity, even if the data comes in as text.
</para>
<para>
A long varchar column can contain huge XML documents as special
"persistent XML entity" objects. XML entities of this sort
consume only a little amount of memory and load small portions of data from
disk to memory on demand; an application can handle XML documents that
are order of magnitude larger than the amount of available memory.
<link linkend="sqlrefxmldatatype">LONG XML</link> column type is convenient
if a column contains only valid XML documents. An application can save
XML entities to LONG XML columns and retrieve them back without calling any
type conversion functions.
A special user-defined type <link linkend="xmltypeudt">XMLType</link>
allows the use of an XML entity as a base for deriving user-defined types from it.
</para>
<para>
The <link linkend="xpathcontainspredicate"><function>xpath_contains</function></link> SQL predicate can be used to
test whether a column value matches a given XPath expression. If the
XPath expression specifies a node set and that set is non-empty for a
given column value, it is possible to bind a result variable to each
element of the set. In this way a single row of data in a table can
produce multiple rows in an SQL result set, one for each entity
selected by the XPath predicate.
</para>
<para>
If there is a free text index on a column it is possible to define
that the content be checked for well-formedness. In this case both
<link linkend="containspredicate"><function>contains</function></link> and
<link linkend="xpathcontainspredicate"><function>xpath_contains</function></link> predicates can be applied to the
same column in the same query. You should create a free text index on
your XML data if you expect any content-based searches. The free text
index will recognize specific features of XML and allow their use in
searches.
</para>
<para>
The <link linkend="xcontainspredicate"><function>xcontains</function></link> SQL predicate is a combination of
XPath and free text, making automatic use of the free text index for
evaluating an XPath expression. This predicate also allows you to
test text values of entities for complex free text conditions such as
proximity.
</para>
<para>
There is a user interface for using this feature on DAV resources.
The use of the <link linkend="xpathcontainspredicate"><function>xpath_contains</function></link> predicate is not
limited to DAV resources though. The use of XPath with XML values is
independent of free text indexing and of XML views.
</para>
<para>
Virtuoso offers functions for XPath processing of well-formed XML
strings in SQL. Together with the Virtuoso free text support, these
functions offer a powerful free-form and structured content retrieval
system. You can search for XPath matches in any XML-populated column.
One practical example would be the RES_CONTENT column of the
WS.WS.SYS_DAV_RES table, which contains the contents of documents
stored in the WebDAV repository. As with the sample database, which
contains the XML sources for this documentation, you may store XML
documents directly in the WebDAV repository.
</para>
<para>
An SQL statement can return complex XML documents based on relational data.
Virtuoso supports both Microsoft's "FOR XML" syntax and the standard set of SQLX XML
composing functions like
<link linkend="fn_XMLELEMENT"><function>XMLELEMENT</function></link> and
<link linkend="fn_XMLAGG"><function>XMLAGG</function></link>.
Very complicated processing can be done in a single statement that combines
XML composing functions, <link linkend="fn_xquery_eval"><function>xquery_eval</function></link>
and <link linkend="fn_xslt"><function>xslt</function></link>.
</para>
<para>
Virtuoso/PL routines can modify XML entities in DOM style (functions like
<link linkend="fn_XMLAppendChildren"><function>XMLAppendChildren</function></link>
and
<link linkend="fn_XMLReplace"><function>XMLReplace</function></link>). Doing local changes this way
can be simpler than via XSLT or XQuery; DOM modifications also help speed-critical applications
to avoid unnecessary copying of data.
</para>
<para>
Virtuoso's XML parser can read XML documents of any complexity.
It can validate source documents against DTD and XML schema,
load compound documents or their fragments,
recover from errors in ill-formed HTML documents like popular browsers can.
</para>
</abstract>
<sect1 id="forxmlforsql">
<title>Rendering SQL Queries as XML (FOR XML Clause)</title>
<para>
Virtuoso extends SQL-92 with the FOR XML clause that allows any SQL
result set to be turned into XML according to some simple rules. The
notation and functionality are similar to those offered by Microsoft
SQL Server and IIS.
</para>
<para>
The FOR XML clause has 3 variants:
</para>
<formalpara><title>RAW</title>
<para>
Make an XML entity from each row of the result set; do not attempt to
construct hierarchies. Each row's data is enclosed in a <ROW/>
element and each column is either an attribute or child element.
</para>
</formalpara>
<formalpara><title>AUTO</title>
<para>
A hierarchy is constructed with one level for each table of the join
for which at least one column is selected. The table whose column is
first mentioned in the selection will be the topmost element, the next
table its child, etc. Each level of the tree will consist of one type
of element. A parent element will have multiple children if
consecutive rows do not differ in the column values coming from the
parent element. When a table's column values differ from the previous
row, the element and all children thereof are closed and a new element
is started, with children filled out from other columns of the result
set.
</para>
</formalpara>
<formalpara><title>EXPLICIT</title>
<para>
This mode gives more control on the resulting tree's structure while
requiring a more elaborate query structure. In this mode, the query
will be a UNION ALL of many joins and each row will specify exactly
one element. Which type of element this is and where in the tree it
will be placed are determined by the values of the first two columns,
TAG and PARENT.
</para>
</formalpara>
<para>
In all modes, columns may either be attributes or sub-elements. The
<emphasis>ELEMENT</emphasis> keyword after the FOR XML clause forces
all columns to be rendered as sub-elements; attribute are the
default.
</para>
<para>
In all modes except explicit, the names of elements are the unprefixed
table names and the names of attributes are the columns' names in the
result set. If tables have correlation names the correlation names
are used in the output instead of the table names. Expressions are
allowed in the selections but these should be named using AS. In AUTO
mode Virtuoso assumes expressions belong to the topmost element.
</para>
<para>
The FOR XML clause is generally allowed in SELECT statements in place
of the FOR UPDATE clause. However it only has an effect when the
statement is executed through the <link
linkend="fn_xml_auto"><function>xml_auto()</function></link> function.
</para>
<tip><title>See Also:</title> <para>The <link
linkend="sqlxmlstmts">SQL-XML Statements</link> page described in the
Visual Server Administration Interface section provides a fast
graphical way of supplying an SQL statement to Virtuoso and saving the
view as a resource accessible from the WebDAV store.</para></tip>
<sect2 id="forxmlexplicmode">
<title>FOR XML EXPLICIT Mode</title>
<para>
This mode gives the developer the most control over the generated
result tree but requires a verbose query formulation. Each row must
begin with two integer columns, the first identifying the element
represented by the row and the second the parent element type of this
element. Consider:
</para>
<programlisting>
select 1 as tag, null as parent,
"CategoryID" as [category!1!cid],
"CategoryName" as [category!1!name],
NULL as [product!2!pid],
NULL as [product!2!name!element]
from "Demo".."Categories"
union all
select 2, 1, "category" ."CategoryID", NULL, "ProductID", "ProductName"
from "Demo".."Categories" "category", "Demo".."Products" as "product"
where "product"."CategoryID" = "category"."CategoryID"
order by [category!1!cid], 5
for xml explicit;
</programlisting>
<para>
This query makes a two level tree where Categories have Product
children. The selection in the first UNION term specifies the element
types in the result set. The two first columns, TAG and PARENT are
required in all EXPLICIT queries. Subsequent columns have an extended
AS declaration that specifies which element they belong to, what that
element is called in XML and what the column will be called. A row
where TAG has a value of 1 will pick the columns which has [xxx!1!yyy]
as their alias; rows with a TAG of 2 will pick columns with an alias
with [xxx!2!yyy] and so on.
</para>
<para>
If consecutive rows have a different TAG but the same PARENT, these
will be siblings of different types. This possibility does not exist
with the other FOR XML modes.
</para>
<para>
If the PARENT is 0 or NULL, then any previously open elements in the
result are closed and the element of the row becomes a top-level
element. When PARENT refers to the TAG of a presently open element in
the set, all children of that element are closed and the row's element
is inserted as the next child of the last element with the TAG equal
to the new row's PARENT. All open tags are closed at the end of the
result set.
</para>
<note>
<title>Note</title>
<para>Since each level of the tree is generated by a different term in
the UNION ALL, an ORDER BY will invariably be needed to group the
children after their parents. If the parent rows have NULLs in
place of the child row's key values, the parent gets sorted first
because NULL collates first.
</para>
</note>
</sect2>
<sect2 id="examplesofforxml"><title>Examples of FOR XML</title>
<para>This section gives one example of each mode of FOR XML combined
with the <function>xml_auto()</function> function to help us display
the results simply. First we create a procedure that enables us to
supply SQL and return XML using the <function>xml_auto()</function>
function.</para>
<programlisting>
create procedure xmla (in q varchar)
{
declare st any;
st := string_output ();
xml_auto (q, vector (), st);
result_names (q);
result (string_output_string (st));
}
</programlisting>
<para>Now we can apply this to a couple of examples:</para>
<example><title>XML RAW</title>
<programlisting>
xmla ('select "category"."CategoryID", "CategoryName",
"ProductName", "ProductID"
from "Demo".."Categories" "category", "Demo".."Products" as "product"
where "product"."CategoryID" = "category"."CategoryID" FOR XML RAW');
</programlisting>
<screen>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Chai" ProductID="1">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Chang" ProductID="2">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Guaraná Fantástica" ProductID="24">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Sasquatch Ale" ProductID="34">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Steeleye Stout" ProductID="35">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Côte de Blaye" ProductID="38">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Chartreuse verte" ProductID="39">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Ipoh Coffee" ProductID="43">
</ROW>
<ROW CategoryID="1" CategoryName="Beverages" ProductName="Laughing Lumberjack Lager" ProductID="67">
</ROW>
.....
</screen>
</example>
<para>As we can see, RAW mode produces a simple row-by-row account
of the data encased within the <ROW.../> tags. This is the simplest
mode.</para>
<example>
<title>XML AUTO</title>
<programlisting>
xmla ('select "category"."CategoryID", "CategoryName",
"ProductName", "ProductID"
from "Demo".."Categories" "category", "Demo".."Products" as "product"
where "product"."CategoryID" = "category"."CategoryID" FOR XML AUTO ELEMENT');
</programlisting>
<screen>
<category>
<CategoryID>1</CategoryID> <CategoryName>Beverages</CategoryName><product>
<ProductName>Chai</ProductName> <ProductID>1</ProductID></product>
<product>
<ProductName>Chang</ProductName> <ProductID>2</ProductID></product>
<product>
<ProductName>Guaraná Fantástica</ProductName> <ProductID>24</ProductID></product>
<product>
<ProductName>Sasquatch Ale</ProductName> <ProductID>34</ProductID></product>
<product>
<ProductName>Steeleye Stout</ProductName> <ProductID>35</ProductID></product>
<product>
<ProductName>Côte de Blaye</ProductName> <ProductID>38</ProductID></product>
<product>
<ProductName>Chartreuse verte</ProductName> <ProductID>39</ProductID></product>
<product>
<ProductName>Ipoh Coffee</ProductName> <ProductID>43</ProductID></product>
<product>
<ProductName>Laughing Lumberjack Lager</ProductName> <ProductID>67</ProductID></product>
<product>
.....
</screen>
</example>
<para>In contrast to RAW mode, AUTO produces results that are more
tree-like. Only one category element is used for each category, and
that contains all the children of the category.</para>
<example>
<title>XML EXPLICIT</title>
<programlisting>
xmla ('
select 1 as tag, null as parent,
"CategoryID" as [category!1!cid],
"CategoryName" as [category!1!name],
NULL as [product!2!pid],
NULL as [product!2!name!element]
from "Demo".."Categories"
union all
select 2, 1, "category" ."CategoryID", NULL, "ProductID", "ProductName"
from "Demo".."Categories" "category", "Demo".."Products" as "product"
where "product"."CategoryID" = "category"."CategoryID"
order by [category!1!cid], 5
FOR XML EXPLICIT');
</programlisting>
<screen>
<CATEGORY CID="1" NAME="Beverages">
<PRODUCT PID="1">
<NAME>Chai</NAME></PRODUCT>
<PRODUCT PID="2">
<NAME>Chang</NAME></PRODUCT>
<PRODUCT PID="24">
<NAME>Guaraná Fantástica</NAME></PRODUCT>
<PRODUCT PID="34">
<NAME>Sasquatch Ale</NAME></PRODUCT>
<PRODUCT PID="35">
<NAME>Steeleye Stout</NAME></PRODUCT>
<PRODUCT PID="38">
<NAME>Côte de Blaye</NAME></PRODUCT>
<PRODUCT PID="39">
<NAME>Chartreuse verte</NAME></PRODUCT>
<PRODUCT PID="43">
<NAME>Ipoh Coffee</NAME></PRODUCT>
<PRODUCT PID="67">
<NAME>Laughing Lumberjack Lager</NAME></PRODUCT>
<PRODUCT PID="70">
<NAME>Outback Lager</NAME></PRODUCT>
<PRODUCT PID="75">
<NAME>Rhönbräu Klosterbier</NAME></PRODUCT>
<PRODUCT PID="76">
<NAME>Lakkalikööri</NAME></PRODUCT>
</CATEGORY>
<CATEGORY CID="2" NAME="Condiments">
<PRODUCT PID="3">
.....
</screen>
</example>
<para>In this example, we specify precisely the tree structure we
wish, and construct the EXPLICIT query to produce that tree. Many
times programmers know what the resulting XML should look
like but do not know how to get exactly what they want. FOR XML
EXPLICIT can be very useful in these cases.</para>
</sect2>
<sect2 id="forxmlfunc">
<title>Functions</title>
<link linkend="fn_xml_auto"><function>xml_auto()</function></link>
</sect2>
<sect2 id="forxmlsyntax">
<title>FOR XML Syntax</title>
<programlisting>
for__xml ::= FOR XML <mode> [ ELEMENT ]
<mode> ::= RAW | AUTO | EXPLICIT
<explicit column> ::= scalar_exp AS '[' <element> '!' <tag no> '!'
<column name> [ '!' <option> ] ']'
<tag no> ::= INTNUM
<column name> ::= IDENTIFIER
<element> ::= IDENTIFIER
<option> ::= IDENTIFIER
</programlisting>
<para>
The <explicit column> should be used in the selection list of
the first term of the UNION ALL construct in a FOR XML EXPLICIT query.
Virtuoso provides this functionality separately from any Web server
context, although these are principally expected to be used inside VSP
pages.
</para>
<para>
The text of <option> part of the <explicit column> is
ignored but if it is present then the value is placed into
a sub-element of the element for the row, not into an attribute.
</para>
</sect2>
</sect1>
<sect1 id="composingxmlinsql">
<title>XML Composing Functions in SQL Statements (SQLX)</title>
<para>
The preferred means of constructing XML data from SQL is to use the standard SQLX SQL extension.
</para>
<para>SQLX is a collection of functions added for creating XML entities from standard
relational queries. Basically, you write a SQL statement with calls to SQLX functions in
the selection and a piece of XML is created.
</para>
<para>
There are five XML composing functions:
</para>
<simplelist>
<member><link linkend="fn_XMLELEMENT">
<function>XMLELEMENT()</function>
</link>
creates a single XML element that can contain an arbitrary number of attributes and sub-elements.</member>
<member><link linkend="fn_XMLATTRIBUTES">
<function>XMLATTRIBUTES()</function>
</link>
lists XML attributes to be placed in the XML element created by an enclosing call of <function>XMLELEMENT()</function>.</member>
<member><link linkend="fn_XMLCONCAT">
<function>XMLCONCAT()</function>
</link>
returns a forest of XML values by concatenating a list of XML values or forests.</member>
<member><link linkend="fn_XMLAGG">
<function>XMLAGG()</function>
</link>
is an aggregate function that creates a forest of XML values by concatenating the XML values that are returned from multiple rows.</member>
<member><link linkend="fn_XMLFOREST">
<function>XMLFOREST()</function>
</link>
is similar to <function>XMLATTRIBUTES()</function> but returns a forest of elements instead of list of attributes.</member>
</simplelist>
<para>These functions belong
to the SQLX standard, an emerging SQL standard for XML. All the functions take a variable number of arguments.
</para>
<para>
<function>XMLELEMENT</function> is used to construct XML elements from relational data.
It takes as parameters the element name, an optional collection of attributes for the element
(returned by <function>XMLATTRIBUTES</function> call),
column names, strings, <function>XMLELEMENT</function>, <function>XMLFOREST</function>,
<function>XMLCONCAT</function>, and <function>XMLAGG</function> calls, and an entity objects (returned by
corresponding functions, e.g. <function>xtree_doc</function>, <function>xpath_eval</function>,
<function>xquery_eval</function>) which will make up the content of the element (an exception from this is
an attribute entity returned by <function>xquery_eval</function> - in this case it is joined to the list of the
element's attributes).
Column names, strings and attribute entities returned by <function>xpath_eval</function> will make up the text content
of the element.
The others will make up the children of the element.
</para>
<para>
In the <function>XMLATTRIBUTES</function> clause, the value expressions are evaluated to get the values for the
attributes.
</para>
<para>
<function>XMLFOREST</function> produces a forest of XML elements from a given list of arguments. It accepts a list of
SQL value expressions as its arguments, and produces an XML element from each value returned.
</para>
<para>
<function>XMLCONCAT</function> produces a forest of elements by concatenating a list of XML values. XMLCONCAT accepts
a list of XML value expressions as its arguments, and produces a forest of elements by concatenating the XML values
that are returned from the same row to make one value. If an argument of the <function>XMLCONCAT</function> is an
entity object returned by <function>xquery_eval</function> or <function>path_eval</function>, it must not be an
attribute.
</para>
<para>
The following statements create the same result sets:
</para>
<programlisting><![CDATA[
select XMLELEMENT ('Person',
XMLELEMENT ('firstname', "FirstName"),
XMLELEMENT ('lastname', "LastName"),
xquery_eval('//country', xtree_doc('<a><country>USA</country></a>')))
from "Demo"."demo"."Employees";
select
XMLELEMENT ('Person',
XMLFOREST ("FirstName"as "firstname", "LastName" as "lastname"),
xquery_eval('//country', xtree_doc('<a><country>USA</country></a>')))
from "Demo"."demo"."Employees";
select
XMLELEMENT ('Person',
XMLCONCAT (
XMLELEMENT ('firstname', "FirstName"),
XMLELEMENT ('lastname', "LastName"),
xquery_eval('//country', xtree_doc('<a><country>USA</country></a>'))))
from "Demo"."demo"."Employees";
]]></programlisting>
<note>
<title>Note:</title>
<para>
The second statement is more effective than the others.
</para>
</note>
<para>
In order to return more than one row of values, you can use <function>XMLAGG</function>. <function>XMLAGG</function>
is an aggregate function that produces a forest of XML elements from a collection of XML elements. It concatenates
the values returned from one column of multiple rows, unlike XMLCONCAT, which concatenates the values
returned from multiple columns in the same row.
</para>
<para>
The parameters that would be used as element names (in the <function>XMLELEMENT</function> and in the
'AS clause ' of the <function>XMLFOREST</function> and <function>XMLATTRIBUTES</function>)
must be valid XML names. If the 'AS clause ' is absent in a list of the parameters of the
<function>XMLFOREST</function> or <function>XMLATTRIBUTES</function>, Virtuoso uses the partially escaped form of
the column name as the element or attribute name. The partially escaped form means that SQL <identifier>
characters that are valid characters in a XML NAME are not changed, SQL <identifier>
character that is not valid XML NAME character is replaced with "_xHHHH_", where HHHH is character's upper case
hexadecimal code. For example, "first_name" is replaced with "first_x005F_name", "last name" is replaced with
"last_x0020_name".
</para>
<para>The following example creates an 'FullAddress' element with
<itemizedlist mark="bullet">
<listitem> four attributes, three of them ('PostalCode', 'Address', 'City') are
produced by <function>XMLATTRIBUTES</function>, and the fourth attribute - 'country' is calculated by
<function>xquery_eval</function> </listitem>
<listitem>'Region' subelement, that is produced by <function>xtree_doc</function></listitem>
<listitem>text content, that is produced by <function>xpath_eval</function> </listitem>
<listitem>'emp' subelement with text content from the column "LastName", that is created by nested
<function>XMLELEMENT</function></listitem>
</itemizedlist>
</para>
<programlisting>
<![CDATA[select XMLELEMENT ('FullAddress',
XMLATTRIBUTES ( "PostalCode", "Address", "City"),
xtree_doc ('<Region>WA</Region>'),
xquery_eval('//@country', xtree_doc('<a country="USA"/>')),
xpath_eval('//@Phone', xtree_doc('<a Phone="(206) 555-9857"/>')),
XMLELEMENT('emp', "LastName"))
from "Demo"."demo"."Employees"
----------------------------
<FullAddress PostalCode="98122" Address="507 - 20th Ave. E. Apt. 2A" City="Seattle" country="USA">
<Region>WA</Region>
(206) 555-9857
<emp>Davolio</emp>
</FullAddress>
<FullAddress PostalCode="98401" Address="908 W. Capital Way" City="Tacoma" country="USA">
<Region>WA</Region>
(206) 555-9857
<emp>Fuller</emp>
</FullAddress>
. . .
]]>
</programlisting>
<tip>
<title>See Also:</title>
<para>
<link linkend="fn_XMLAGG">XMLAGG()</link>
</para>
<para>
<link linkend="fn_XMLATTRIBUTES">XMLATTRIBUTE()</link>
</para>
<para>
<link linkend="fn_XMLCONCAT">XMLCONCAT()</link>
</para>
<para>
<link linkend="fn_XMLELEMENT">XMLELEMENT()</link>
</para>
<para>
<link linkend="fn_XMLFOREST">XMLFOREST()</link>
</para>
</tip>
<para>
XML composing functions deal with arguments of arbitrary type, but the result is always an XML entity that can contain only elements and strings.
Hence there is a set of type casting rules. These rules are quite common for any XML DOM model, so they're similar to those listed for
<link linkend="xmldomtypecasting">DOM function arguments</link>:
</para>
<para>
If an instance of XMLType is passed then its internal XML entity is used.
</para>
<para>
If an array representation of an XML tree entity is passed then it is used exactly like XML entity.
</para>
<para>
If an argument is NULL then it is fully ignored, as if there is no such argument at all.
</para>
<para>
If an argument is not of a type listed above and not a string then it is cast to a string first.
</para>
<para>
A root node of some document (or of some generic XML entity) can not appear in the middle of the resulting tree.
So if a root node is passed then all child nodes of the root (i.e. every top-level node of the document) will be added.
</para>
<para>
SQL/XML standards introduce a special name "forest of XML elements" for an ordered list of XML elements,
like one returned by <function>XMLFOREST()</function>.
In Virtuoso, forest can contain XML nodes of any sort, not only XML elements, so it can also contain strings, processing instructions and comments.
Virtuoso processes any non-empty "forest" as if it were the root node of a "generic XML entity",
and items of the forest were top-level nodes of that entity.
Hence, a forest can be passed to any function that accepts an value of type "XML entity".
The only potential problem is that this entity is well-formed if and only if the forest is non-empty.
If an empty forest is serialized to an XML text then the result is an empty string that is not an acceptable input for an XML parser.
</para>
<para>
It is important to remember that the XML document can not contain two neighbour text nodes and that the text node can not be an empty string.
If two consequent strings appear in the list of values of a forest or in the list of children of an new element then
they are replaced with a single node that is a concatenation of these string.
Similarly, if an empty string appears in the list of values of a forest or in the list of children of an new element then it is removed from the list.
</para>
</sect1>
<!-- ####################### -->
<!-- &xmltableview; removed by Kingsley until further notice -->
<!-- ####################### -->
&xmlview;
<!-- ################### ~~~~~~~~~~ ################### -->
<sect1 id="queryingxmldata"><title>Querying Stored XML Data</title>
<sect2 id="xpathcontainsSQLPred">
<title>XPATH_CONTAINS SQL Predicate</title>
<para>
XPath expressions can be used in SQL statements to decompose and match
XML data stored in columns. The <parameter>xpath_contains</parameter>
SQL predicate can be used either to test for an XML value matching a
path expression or to extract one or more entities from the XML value.
These values can then be used later in the query as contexts for other
XPath expressions.
</para>
<programlisting>
xpath_contains (xml_column, xp_expression[, query_variable]);
</programlisting>
<para>
The first argument, <parameter>xml_column</parameter> is the name of the column on which to perform the XPath search.
The second argument, <parameter>xp_expression</parameter>, takes an XPath
expression.
</para>
<para>
The third argument is an optional query variable that gets bound to
each result entity value of the xpath expression. If this variable is
omitted the xpath_contains predicate will qualify the query by
returning true for matches. In this case the result will only return
one row per match. If the variable is present, the result set could
contain multiple rows per result set row of the base table, one row for
each match.
</para>
<para>
Consider the example:
</para>
<programlisting>
select xt_file, t from xml_text
where xpath_contains (xt_text, '//chapter/title[position () = 1]', t);
</programlisting>
<para>
This SQL statement will select the first title child of any chapter
entities in the XML documents in the xt_text column of the table
<parameter>xml_text</parameter>. There can be several matching
entities per row of xml_text. The result set will contain a row for
each matching entity.
</para>
<para>
In XPath terms the path expression of
<parameter>xpath_contains</parameter> is evaluated with the context
node set to the root node of the XML tree represented by the value of
the column that is the first argument of xpath_contains. This node is
the only element of the context node set.
</para>
<note><title>Note:</title>
<para>
The 't' variable in the above example gets bound to XML entities, not
to their string values or other representations. One can thus use
these values as context nodes for other expressions.
</para>
</note>
<para>
The XPATH expression can have a list of options in the beginning.
The list of options is surrounded by square brackets.
Options in the list are delimited by spaces.
The most popular option is <parameter>__quiet</parameter> that allows to
process a set of rows if not all stored documents are valid XMLs;
if an error is signalled by the XML parser when it prepares a content document for the XPATH in question
and the XPATH contains <parameter>__quiet</parameter> then the error is suppressed and
the row is silently ignored as if XPATH found nothing.
One can configure the DTD validator of the parser by placing its <link linkend="dtd_config">configuration parameters</link> in
the list of XPATH options.
</para>
<para>
The following example is almost identical to the previous one but it works even if
not all values of <parameter>xt_text</parameter> are valid XMLs, and the resulting values of the
't' variable are standalone entities even if source documents in xt_text contain external generic entities.
</para>
<programlisting>
select xt_file, t from xml_text
where xpath_contains (xt_text, '[__quiet BuildStandalone=ENABLE]//chapter/title[position () = 1]', t);
</programlisting>
</sect2>
<sect2 id="qryusingxpath_eval">
<title>Using xpath_eval()</title>
<para>The <function>xpath_eval()</function> function is used to filter
out parts of an XML fragment that match a given XPATH expression. It
can be used to retrieve multiple-node answers to queries, as it is often
the case that more than one node-set matches.
Consider the following statements that create a table with XML stored inside.</para>
<programlisting><![CDATA[
CREATE TABLE t_articles (
article_id int NOT NULL,
article_title varchar(255) NOT NULL,
article_xml long varchar
);
insert into t_articles (article_id, article_title) values (1, 'a');
insert into t_articles (article_id, article_title) values (2, 'b');
UPDATE t_articles SET article_xml = '
<beatles id = "b1">
<beatle instrument = "guitar" alive = "no">john lennon</beatle>
<beatle instrument = "guitar" alive = "no">george harrison</beatle>
</beatles>'
WHERE article_id = 1;
UPDATE t_articles SET article_xml = '
<beatles id = "b2">
<beatle instrument = "bass" alive = "yes">paul mccartney</beatle>
<beatle instrument = "drums" alive = "yes">ringo starr</beatle>
</beatles>'
WHERE article_id = 2;
]]></programlisting>
<para>Now we make a query that will return a vector of results, each
vector element corresponding to a node-set of the result.</para>
<programlisting><![CDATA[
SELECT xpath_eval('//beatle/@instrument', xml_tree_doc (article_xml), 0)
AS beatle_instrument FROM t_articles WHERE article_id = 2;
]]></programlisting>
<para>The repeating nodes are returned as part of a vector, the third argument
to <function>xpath_eval()</function> is set to 0, which means that it is to return all nodes.</para>
<para>Otherwise, we can select the first node-set by supplying 1 as the third
parameter to <function>xpath_eval()</function>: </para>
<programlisting><![CDATA[
SELECT xpath_eval('//beatle/@instrument', xml_tree_doc (article_xml), 1)
AS beatle_instrument FROM t_articles WHERE article_id = 2;
]]></programlisting>
<tip><title>See Also:</title>
<para><link linkend="fn_xpath_eval"><function>xpath_eval()</function></link></para>
<para><link linkend="fn_xquery_eval"><function>xquery_eval()</function></link></para>
<para><link linkend="fn_xmlupdate"><function>xmlupdate()</function></link></para>
</tip>
</sect2>
<sect2 id="wxmlextentrefinxml">
<title>External Entity References in Stored XML</title>
<para>
When an XML document is stored as either text or in persistent XML
format it can contain references to external parsed entities with the
<!entity ...> declaration and the &xx; syntax. These are
stored as references and not expanded at storage time if the entity is
external. Such references are transparently followed by XPath and
XSLT. A run-time error occurs if the referenced resource cannot be
accessed when needed. The reference is only followed if the actual
subtree is selected by XPath or XSLT. The resource is retrieved at
most once for each XPath or XSLT operation referencing it, regardless
of the number of times the link is traversed. This is transparent, so
that the document node of the referenced entity appears as if it were
in the place of the reference.
</para>
<para>
External entity references have an associated URI, which is either
absolute, with protocol identifier and full path, or relative.
Virtuoso resolves relative references with respect to the base URI of
the referencing document. If the document is stored as a column value
in a table it does not have a natural base URI; therefore, the
application must supply one if relative references are to be
supported. This is done by specifying an extra column of the same
table to contain a path, in the form of collections delimited by
slashes, like the path of a DAV resource or a Unix file system path.
This base URI is associated with an XML column with the IDENTIFIED BY
declaration:
</para>
<programlisting>
create table XML_TEXT (
XT_ID integer,
XT_FILE varchar,
XT_TEXT long varchar identified by xt_file,
primary key (XT_ID)
);
create index XT_FILE on XML_TEXT (XT_FILE);
</programlisting>
<para>
Thus, each time the value of <parameter>xt_text</parameter> is
retrieved for XML processing by <function>xpath_contains()</function>
or <function>xcontains()</function> the base URI is taken from
<parameter>xt_file</parameter>. The complete URI for the
<parameter>xt_text</parameter> of a column of the sample table would
be:
</para>
<programlisting>
virt://<qualified table name>.<uri column>.<text column>:<uri column value>
</programlisting>
<para>
An example would be:
</para>
<programlisting>
"virt://DB.DBA.XML_TEXT.XT_FILE.XT_TEXT:sqlreference.xml"
</programlisting>
<para>
The '..' and '.' in relative paths are treated like file names when
combining relative references to base URIs. A relative reference
without a path just replaces the last part of the path in the base
URI.
</para>
<tip><title>See Also:</title>
<para>
<link linkend="fn_xml_uri_get"><function>xml_uri_get()</function> and
<function>xml_uri_merge()</function></link> for more details.
</para>
</tip>
</sect2>
<sect2 id="wamlschmdtdfuncs">
<title>XML Schema & DTD Functions</title>
<para>The following functions can be used to generate XML Schema or
DTD information about a given SQL query:</para>
<simplelist>
<member><link linkend="fn_xml_auto_schema"><function>xml_auto_schema()</function></link></member>
<member><link linkend="fn_xml_auto_dtd"><function>xml_auto_dtd()</function></link></member>
</simplelist>
<example id="ex_webandxmlautoschdtd"><title>Generating XML Schema and DTD Data</title>
<para>This example shows trivial use of the two functions
<function>xml_auto_schema()</function> and <function>xml_auto_dtd()</function>.
</para>
<programlisting><![CDATA[
SQL> select xml_auto_schema('select u_name from sys_users', 'root');
callret
VARCHAR
_______________________________________________________________________________
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation>
Schema for output of the following SQL statement:
]]>
<![CDATA[select u_name from sys_users]]>
<![CDATA[
</xsd:documentation>
</xsd:annotation>
<xsd:element name="root" type="root__Type"/>
<xsd:complexType name="root__Type">
<xsd:sequence>
<xsd:element name="SYS_USERS" type="SYS_USERS_Type" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SYS_USERS_Type">
<xsd:attribute name="U_NAME" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
1 Rows. -- 1843 msec.
SQL> select xml_auto_dtd('select u_name from sys_users', 'root');
callret
VARCHAR
_______________________________________________________________________________
<!-- dtd for output of the following SQL statement:
select u_name from sys_users
-->
<!ELEMENT root (#PCDATA | SYS_USERS)* >
<!ELEMENT SYS_USERS (#PCDATA)* >
<!ATTLIST SYS_USERS
U_NAME CDATA #IMPLIED >
1 Rows. -- 411 msec.
]]></programlisting>
</example>
</sect2>
<sect2 id="usingxmlfreetext">
<title>Using XML and Free Text</title>
<para>
Virtuoso integrates classic free text retrieval and XML
semi-structured query features to offer a smart, scalable XML
repository. When a column is declared as indexed XML with the CREATE
TEXT XML INDEX statement the text is checked for well-formedness at
time of storage. The specific XML structure of the text is also
considered when making the free text index entries. This XML-aware
free text index is used for processing XPath queries in the
<parameter>xcontains</parameter> SQL predicate. This predicate is
only applicable to columns for which there is an XML free text index.
</para>
<para>
Arbitrary free text criteria can appear inside the XPath expression of
<parameter>xcontains</parameter>. These are introduced by the XPath
extension function <function>text-contains()</function>, which may
only be used within <parameter>xcontains</parameter> as it relies on
the underlying free text index.
</para>
<note><title>Note</title>
<para><function>xpath_contains()</function> does not require the
existence of a free text index and can thus apply to any well-formed
XML content.</para>
</note>
</sect2>
<sect2 id="xcontainspredicate">
<title>XCONTAINS predicate</title>
<para>This predicate is used in a SQL statement, it returns "true" if a free text
indexed column with XML content matches an XPATH expression. Optionally
produces the matching node set as a result set.</para>
<para>
Syntax
</para>
<programlisting>
xcontains_pred:
xcontains (column, expr [, result_var [, opt_or_value ...]])
opt_or_value:
DESCENDING
| START_ID ',' scalar_exp
| END_ID ',' scalar_exp
| SCORE_LIMIT ',' scalar_exp
| OFFBAND column
result_var:
IDENTIFIER
| NULL
</programlisting>
<para>
The <emphasis>column</emphasis> must refer to a column for which there exists a free text index.
</para>
<para>
The <emphasis>expr</emphasis> must be a narrow or wide string expression whose syntax matches the
rules in 'XPATH Query Syntax'.
</para>
<para>
The <emphasis>result_var</emphasis> variable is a query variable which, if present, will be
successively bound to each element of the node set selected by the XPATH
expression. if the value is not a node set and is true, the variable will
be once bound to this value. The scope of the variable is the containing
select and its value is a scalar or an XML entity.
The <emphasis>result_var</emphasis> can be not an identifier but a NULL keyword to explicitly indicate that no query variable is required.
</para>
<para>
The <emphasis>START_ID</emphasis> is the first allowed document ID to be selected by the
expression in its traversal order, e.g. least or equal for ascending and
greatest or equal for descending.
</para>
<para>
<emphasis>END_ID</emphasis> is the last allowed id in the traversal order. For descending order
the START_ID must be >= END_ID for hits to be able to exist. For ascending
order the START_ID must be <= END_ID for hits to be able to exist.
</para>
<para>
<emphasis>DESCENDING</emphasis> specifies that the search will produce the
hit with the greatest ID first, as defined by integer or composite collation. This
has nothing to do with a possible ORDER BY of the
enclosing statement. Even if there is an ORDER BY in the enclosing
statement the DESCENDING keyword of xcontains has an effect in the
interpretation of the STRT_ID and END_ID xcontains options.
</para>
<para>
<emphasis>RANGES</emphasis> specifies that the query variable following the RANGES keyword will
be bound to the word position ranges of the hits of the expression inside
the document. The variable is in scope inside the enclosing SELECT
statement.
</para>
<para>
<emphasis>SCORE_LIMIT</emphasis> specifies a minimum score that hits must have or exceed to be
considered matches of the predicate.
</para>
<para>
<emphasis>OFFBAND</emphasis> specifies that the following column will be retrieved from the free
text index instead of the actual table. For this to be possible the column
must have been declared as offband with the CLUSTERED WITH option of the
<link linkend="createtxtidxstmt">CREATE TEXT INDEX</link> statement.
</para>
<para>
If the select statement containing
the xcontains predicate does not specify an exact match of the primary key of
the table having the xcontains predicate, then the contains predicate will be
the 'driving' condition, meaning that rows come in ascending or descending
order of the free text document ID. If there is a full equality match of the primary
key of the table, this will be the driving predicate and xcontains will only be used
to check if the text expression matches the single row identified by the full match
of the primary key.
</para>
<para>
The xcontains predicate may not appear outside of a select statement and may
only reference a column for which a free text index has been declared. The
first argument must be a column for which there is such an index. The text
expression may be variable and computed, although it must be constant during
the evaluation of the select statement containing it.</para>
<para>
The xcontains predicate must be a part of the top level AND of the WHERE
clause of the containing select. It may not for example be a term of an OR
predicate in the select but can be AND'ed with an OR expression.
</para>
<example>
<title>Selecting Title Elements Called 'Key'</title>
<programlisting>
select xt_file from xml_text2 where
xcontains (xt_text, '//title = "Key"');
</programlisting>
<para>
The query retrieves the <parameter>xt_file</parameter> for rows
whose <parameter>xt_text</parameter> is an XML document containing
'Key' as the text value of a title element.
</para>
<para>If not all values in <parameter>xt_text</parameter> are valid XMLs then
'__quiet' option can be useful to disable error signalling. It is unusual to get
an incorrect XML stored in a column that has free text XML index because
both on insert and on update the text is parsed by an free text indexing routine,
but the error is possible if e.g. a non-standalone document is stored and
an important external entity was available at indexing time but disappeared later.
Thus a modified example might be better for a column with non-standalone documents;
<programlisting>
select xt_file from xml_text2 where
xcontains (xt_text, '[__quiet] //title = "Key"');
</programlisting>
</para>
<title>Selecting Title Element that Contains a Specified Text</title>
<programlisting>
select n from xml_text2 where
xcontains(xt_text,
'//title[. = "AS Declaration - Column Aliasing"]',0,n);
</programlisting>
<para>
The query retrieves each title element from each row of
<parameter>xml_text2</parameter> where the
<parameter>xt_text</parameter> contains title elements with the text
value "AS Declaration - Column Aliasing."
</para>
<note><title>Note</title> <para>The equality test is case- and
whitespace-sensitive, as normal in XPath. The free text index is used
for the search but the final test is done according to XPath
rules.</para>
</note>
</example>
<tip><title>See Also:</title>
<para>The <link linkend="containspredicate">CONTAINS</link> Predicate.</para></tip>
</sect2>
<sect2 id="textcontainsxpath">
<title>text-contains XPath Predicate</title>
<programlisting>
text-contains (node-set, text-expression)
</programlisting>
<para>
This XPath predicate is true if any of the nodes in
<parameter>node-set</parameter> have text values matching the
<parameter>text-expression</parameter>. The
<parameter>text-expression</parameter> should be a constant string
whose syntax corresponds to the top production of the free text syntax
for patterns in <function>contains()</function>. The string also may
not consist exclusively of spaces or noise words.
</para>
<tip><title>See Also:</title>
<para>"Noise Words" in the <link linkend="freetext">Free Text Search
chapter</link>.</para></tip>
<example>
<title>Selecting All Titles About Aliasing</title>
<programlisting>
select n from xml_text2 where
xcontains (xt_text,
'//title[text-contains (., "Aliasing")]', 0, n);
</programlisting>
</example>
<para>
This selects all title elements that contain the word
"Aliasing" using free text match rules: case insensitive and
whole word.
</para>
<example>
<title>Select All Trees with Elements Containing "sql reference"</title>
<programlisting>
select n from xml_text2 where
xcontains (xt_text,
'//*[text-contains (., ''"sql reference"'')]',
0, n);
</programlisting>
</example>
<para>
This selects all elements whose text value contains the phrase
"sql reference". Free text matching rules apply. This
produces all nodes in document order for all documents which contains
the phrase, starting with the document node and following downward
including all paths to the innermost element(s) whose text contains
the phrase.
</para>
</sect2>
<sect2 id="xmlfreetextrules">
<title>XML Free Text Indexing Rules</title>
<para>
XML documents are inserted into the free text index as follows:
</para>
<simplelist>
<member>The process works on the parsed XML tree; therefore character
and local entity references are expanded.</member>
<member>Whole words of text content, bounded by delimiters used for
free text, are each assigned an ordinal number. Noise words defined
in the noise.txt file used by free text indexing are not
counted.</member>
<member>Attribute names and values are not indexed.</member>
<member>Element start and end tags are indexed using the expanded
names — that is, prefixed with the namespace URI + ':'.</member>
<member>An element start tag's ordinal number is one less than
the ordinal number of the first whole word in the text value.</member>
<member>A close tag's ordinal number is one greater than that of
the last word in the text value.</member>
</simplelist>
<para>
From these rules follows that:
</para>
<programlisting>
<html>
<body>
<title>Title of Document</title>
<p>Some <b>bold</b> text </p>
</body>
</html>
</programlisting>
<para>
will be indexed as follows:
</para>
<programlisting>
<html> 0
<body> 0
<title> 0
Title 1
of - no number, noise word
Document 2
</title> 3
<p> 3
Some 4
<b> 4
bold 5
</b> 6
text 6
</p> 6
</body> 6
</html> 6
</programlisting>
<para>
As a result, the phrase "some bold text" is the string value
of the <p> tag and will match the free text expression
"some bold text" even though there is mark-up in it.
Conversely, the phrase "Document some bold" does not match.
Words will not considered adjacent if there is a mix of opening and
closing tags. They will only be considered adjacent if there are
solely one or more either opening or closing tags between them. This
can be circumvented by using the <parameter>NEAR</parameter> connective
instead of the phrase construct.
</para>
<para>
A free text condition will only be true of an element if all the words
needed to satisfy the condition are part of the element's string
value. This string value includes text children of
descendants.</para>
</sect2>
<sect2 id="xmlencoding">
<title>XML Processing & Free Text Encoding Issues</title>
<para>
XML document may be written in a variety of encodings, and it may cause errors
if an incorrect encoding is used for reading a document. Most common errors
can easily be eliminated by writing proper XML prologs in documents, but this
is not always possible, e.g. if documents are composed by third-party
applications. Virtuoso provides various tools to support different types of
encodings and to specify encodings to use if a given document has no XML prolog.
</para>
<sect3 id="encodingsvscharsets">
<title>Encodings: The Difference Between Encodings & Character Sets</title>
<para>
Not all documents may be converted to Unicode by using simple character sets.
Some of them are stored in so-called "multibyte" encodings.
It means that every letter (or ideograph) is represented as a sequence of
one or more bytes, not by exactly one byte. The conversion from such representation
to Unicode and back is usually significantly slower than simple transformation
via character sets, so these representations are supported by data import operations only, but not by internal RDBMS
routines.</para>
<para>
The Virtuoso Server "knows" some number of built-in encodings, such as UTF-8,
UTF-16BE and UTF-16LE. It can load additional encoding descriptions from
a "UCM" file, and can automatically create a new encoding from a known charset with
the same name. See the <link linkend="ucmencodings">UCM Encodings</link> section
for more details.</para>
<para>
An encoding may be used in the following places:
</para>
<simplelist>
<member>The XML/HTML parser to convert source text to Unicode.</member>
<member>The free-text indexing engine to convert plain-text or XML documents
to Unicode during the indexing.</member>
<member>It may be used by the compiler of free-text search expressions
to convert string constants of the expression to Unicode.</member>
<member>It may be used to convert string constants of XPath/XQuery expressions.</member>
</simplelist>
<para>You can only use character sets, not encodings as an ODBC connection
character set, as a character set attribute of a column of a database table,
as an output encoding of the built-in XSLT processor (it is for future versions).
UTF-8 is an exception, it is supported in many places where other encodings are not.</para>
<!-- not clear -->
<tip><title>Security Note:</title>
<para>Two strings converted to Unicode may be identical, but this does not
guarantee that their source strings were equal byte-by-byte due to the
nature of some encodings. For this reason you should avoid processing
authorization data that are neither in Unicode nor in one of the standard
character sets (single-byte encodings). Multibyte encodings and user-defined
character sets may be unsafe for such purposes.
</para></tip>
<sect4 id="ucmencodings">
<title>UCM Encodings</title>
<para>
The description of a multibyte encodings is much longer than the description
of a character set. It is inconvenient to keep such amounts of data inside
the executable. Virtuoso can load descriptions of required encodings
from external files in UCM format. Every UCM file describes one encoding.</para>
<para>
Virtuoso loads UCM files at system initialization. The list of UCM files
is kept in the <link linkend="VIRTINI">Virtuoso INI file</link> under a
section called [Ucms]. This section should contain a UcmPath parameter and
one or more parameters with names Ucm1, Ucm2, Ucm3 and so on (up to Ucm99).</para>
<para>
The UcmPath parameter specifies the directory where UCM files are located,
and every UcmNN parameter specifies the name of a UCM file to load and
a list of names that the encoding can be identified by the
<?xml ... encoding="..." ?> XML preamble. A vertical bar character is
used to delimit names in the list.</para>
<example id="ucmdefininifile">
<title>Sample [Ucms] Section</title>
<programlisting>
[Ucms]
UcmPath = /usr/local/javalib/ucm
Ucm1 = java-Cp933-1.3-P.ucm,Cp933
Ucm2 = java-Cp949-1.3-P.ucm,Cp949|Korean
</programlisting>
<para>This section describes two UCM files located in /usr/local/javalib/ucm
directory: data from java-Cp933-1.3-P.ucm will be used for documents in the
'Cp933' encoding; data from java-Cp949-1.3-P.ucm will be used for documents
in the 'Cp949' encoding and for documents in the 'Korean' encoding
(because these two names refers to the same encoding). </para>
</example>
<note><title>Note:</title>
<para>The encoding name specified inside the UCM file itself is not used.</para>
</note>
<para>
The Virtuoso server will log the results of processing each UCM file specified
in the Virtuoso INI file. If a UCM file specified is not found or contains
syntax errors, the error is logged, otherwise only the type and name(s) of
the encoding are logged.</para>
<note><title>Note:</title>
<para>If the virtuoso.ini contains a misspelled name of a parameter or section,
the parameter (or a whole section) is ignored without being reported as an error.
It is always wise to verify that the log contains a record about the encoding(s)
you load.</para></note>
<tip><title>See Also:</title>
<para>UCM files can be found freely from various sites concerning the "International Components
for Unicode" project, such as: <ulink url="http://www-124.ibm.com/icu">IBM ICU Homepage</ulink>
or the <ulink url="http://oss.software.ibm.com/cvs/icu/charset/data/ucm/">IBM UCM files directory</ulink>.</para>
<para>The <link linkend="cinterface">C Interface</link> chapter contains
further information regarding user customizable support for new encodings and
languages. For almost all tasks, it is enough to define a new charset or to
load an additional UCM file, but some special tasks may require writing
additional C code.</para>
</tip>
</sect4>
</sect3>
<sect3 id="encodingattr"><title>The <parameter>Encoding</parameter> Attribute</title>
<para>If an XML document contains the <parameter>encoding</parameter>
parameter in its</para>
<programlisting><?xml ... ?></programlisting>
<para>prolog declaration, it will be properly decoded and converted into
UTF-8, so the application code is free from encoding problems. If the
value of this attribute is the name of a pre-set or user-defined character
set, that character set will be used. Virtuoso will recognize names such as
<parameter>UTF-8</parameter> and <parameter>UTF8</parameter> as multi-character
or special encodings. Virtuoso recognizes both official names and aliases.</para>
<para>If an encoding is not specified in an XML prolog, or if the document
contains no prolog, the default encoding will be used to read the
document. If a built-in SQL function invokes the XML parser, it will
have an optional argument <parameter>parser_mode</parameter> to
specify whether source text should be parsed as strict XML or as HTML.
If the source text is 8-bit, then UTF-8 will be used as the default
encoding for "XML mode", and ISO-8859-1 (Latin-1) will be the
default for "HTML mode". If the source text is of some
wide-character type, Unicode is the default. To make another encoding
the default, you may specify its official or alias name as the
<parameter>content_encoding</parameter> argument of a built-in
function you call.</para>
</sect3>
<sect3 id="encodingxpathexp">
<title>Encoding in XPath Expressions</title>
<para>
Sometimes applications should perform XPath queries using the encoding specified
by a client. For example, a search engine may ask a user to specify a pattern to
search and use the browser's current encoding as a hint to parse the pattern
properly. In such cases you may wish to use the <parameter>__enc</parameter>
XPath option to specify the encoding used for the rest of XPath string:</para>
<example><title>Specifying Search Encodings in XPath</title>
<para>Create a sample table and store an XML with non-Latin-1 characters</para>
<programlisting>
create table ENC_XML_SAMPLE (
ID integer,
XPER long varchar,
primary key (ID)
);
insert into ENC_XML_SAMPLE (ID, XPER)<!-- values (1, xml_persistent ('<?xml version="1.0" encoding="WINDOWS-1251" ?><book><cit> , (.)</cit></book>')); -->
values (
1,
xml_persistent ('<?xml version="1.0" encoding="WINDOWS-1251" ?>
<book><cit>Îí äîáàâèë
êàðòîøêè,
ïîñîëèë è
ïîñòàâèë
àêâàðèóì íà
îãîíü
(Ì.Æâàíåöêèé
)</cit></book>'
)
);
...
</programlisting>
<para>Find the IDs of all XML documents whose texts contain a specified
phrase. Note that there are pairs of single quotes (not double
quotes) around <parameter>KOI8-R</parameter>. The encoding name should
be in single quotes, but because it is inside a string constant the
quotes must be duplicated.
</para>
<programlisting>
select ID from ENC_XML_SAMPLE where
xcontains (XPER, '[__enc ''KOI8-R''] //cit[text-contains(.,
"''ÐÏÓÔÁ×ÉÌ
ÁË×ÁÒÉÕÍ
ÎÁ ÏÇÏÎØ''")]');
<!-- select ID from ENC_XML_SAMPLE where xcontains (XPER, '[__enc ''KOI8-R''] //cit[text-contains(., "'' ''")]'); -->
</programlisting>
</example>
</sect3>
<sect3 id="encodinginfttsp">
<title>Encoding in Free Text Search Indexes & Patterns</title>
<para>Like XML applications, free text searching may have encoding problems,
and Virtuoso offers a similar solution for them.</para>
<para>Both the CREATE TEXT INDEX statement and vt_create_text_index()
Virtuoso/PL procedure
have an optional argument to specify the encoding of the indexed data.
The specified encoding will be applied to all source text documents
(if the TEXT INDEX was created), or to all XML documents that have no encoding
attribute of the sort <?xml ... encoding="..." ?>
(if the TEXT XML INDEX was created).</para>
<para>
The option <parameter>__enc</parameter> may be specified at the
beginning of free text search pattern, even if the pattern is inside
an XPath statement:</para>
<example>
<title>Specifying an Encoding for Free Text Searching</title>
<para>
Create a sample table and store a sample of text with non-Latin-1 characters
(assuming that client encoding is Windows-1251)
</para>
<programlisting>
create table ENC_TEXT_SAMPLE (
ID integer,
TEXT long nvarchar,
primary key (ID)
);
insert into ENC_TEXT_SAMPLE (ID, XPER)<!-- values (1, N' , (.)')); -->
values (
1,
'<?xml version="1.0" encoding="WINDOWS-1251" ?>
Îí äîáàâèë
êàðòîøêè,
ïîñîëèë è
ïîñòàâèë
àêâàðèóì
íà îãîíü
(Ì.Æâàíåöêèé')
);
...
</programlisting>
<para>Find the IDs of all text documents whose texts contain a specified phrase.
</para>
<!-- select ID from ENC_TEXT_SAMPLE where contains (TEXT, '[__enc ''KOI8-R''] " "'); -->
<programlisting>
select ID from ENC_SAMPLE where
contains (TEXT, '[__enc ''KOI8-R'']
"ÐÏÓÔÁ×ÉÌ
ÁË×ÁÒÉÕÍ
ÎÁ ÏÇÏÎØ"'
);
</programlisting>
<para>Encoding may be applied locally to an argument of the text-search predicate.
It may be used if the document contains citations in different encodings or if
the XML document contains non-ASCII characters in names of tags or attributes,
or if the encoding affects character codes of ASCII symbols such as '/' or '['.
</para>
<programlisting>
select ID from ENC_XML_SAMPLE where
xcontains (XPER, '//cit[text-contains(., "[__enc ''KOI8-R'']
''ÐÏÓÔÁ×ÉÌ
ÁË×ÁÒÉÕÍ ÎÁ
ÏÇÏÎØ''")]'
);
</programlisting>
</example>
<note><title>Note:</title>
<para>
You may have free-text a expression written as a literal constant:
e.g. if the argument of text-contains XPath function is a literal constant.
Be careful to not declare the __enc twice, once in the beginning of the whole
XPath expression and then again in the beginning of the free-text expression
constant, because words of the text expression will thus be converted twice.</para></note>
</sect3>
</sect2>
</sect1>
<sect1 id="updategrams">
<title>Using UpdateGrams to Modify Data</title>
<para>Updategrams allow database updates to be defined as XML. This
is ultimately achieved by mapping the XML nodes against corresponding
database columns. Updategrams can be used to replace existing data
access components in a middle tier. A typical application will
include a middle tier consisting of business logic and data access
code. The data access code will interact with the database using
disconnected recordsets and command objects calling stored procedures.
Most of the data access section of the middle tier can be replaced
with updategrams.</para>
<para>Most data access tiers (both middle tier code and stored
procedures) will deal individually with specific database tables or
groups of related tables. This can inhibit performance and often
several round trips to the database are required to complete a
transaction. Updategrams solve this problem by including all the data
in an XML document that is then mapped to database tables and columns.
The entire database update can then be accomplished at once. This
update can include inserting, updating and deleting data.</para>
<para>The <function>xmlsql_update()</function> function supports
XML-based insert, update, and delete operations performed on an
existing table in the database.</para>
<link linkend="fn_xmlsql_update"><function>xmlsql_update()</function></link>
<sect2 id="updategrambasics">
<title>Updategrams Basics</title>
<para>
The general format of an updategram is:
</para>
<programlisting>
<sql:sync xmlns:sql="xml-sql">
<sql:before>
<TABLENAME [sql:id="value"] col="value" col="value"?../>
</sql:before>
<sql:after>
<TABLENAME [sql:id="value"] [sql:at-identity="value"]
col="value" col="value"?../>
</sql:after>
</sql:sync>
</programlisting>
<para>
or
</para>
<programlisting>
<sql:sync xmlns:sql="xml-sql">
<sql:before>
<TABLENAME [sql:id="value"]>
<col>"value"</col>
<col>"value"</col>
...
</TABLENAME>
...
</sql:before>
<sql:after>
<TABLENAME [sql:id="value"] [sql:at-identity="value"]>
<col>"value"</col>
<col>"value"</col>
...
</TABLENAME>
...
</sql:after>
</sql:sync>
</programlisting>
</sect2>
<sect2 id="elementsdesc">
<title>Elements Description</title>
<para>
The <parameter><sync></parameter> tag of the updategram
signifies the beginning of an operation(s) The rows specified in the
<parameter><before></parameter> refer to existing records in the
database. The rows specified in the
<parameter><after></parameter> block refer to what the user
wants in the database. <parameter><TABLENAME.../></parameter>
identifies target table.
</para>
<para>
The <parameter>sql:at-identity</parameter> attribute stores the last
identity value added by the system (if possible). This identity value
can then be used in subsequent operations.
</para>
<para>
The <parameter>sql:id</parameter> attribute is used to mark rows. This
forces an association between the record specified in the
<parameter><before></parameter> and
<parameter><after></parameter> block in the update gram. When
there are multiple instances specified, it is recommended that
<parameter>sql:id</parameter> attribute be used for all the instances.
</para>
<para>
Each <parameter><TABLENAME.../></parameter> refers to a single
table. Multiple <parameter><TABLENAME.../></parameter> entries
are allowed in the same <parameter><before></parameter> or
<parameter><after></parameter> tags, or in both
<parameter><before></parameter> and
<parameter><after></parameter> tags; however, nesting is not
allowed. The <parameter><before></parameter> and
<parameter><after></parameter> tags are optional. A missing tag
is the same as having a tag with no content.
</para>
</sect2>
<sect2 id="determiningactions">
<title>Determining Actions</title>
<para>
If only the <parameter><after></parameter> block is specified,
the rows specified in the <parameter><after></parameter> block
are inserted in the table(s). If both the
<parameter><before></parameter> and
<parameter><after></parameter> blocks are specified, then rows
specified in the <parameter><after></parameter> block for which
there are no corresponding rows in the
<parameter><before></parameter> block are inserted in the
table(s).
</para>
<para>
In an update operation, the rows specified in the
<parameter><before></parameter> block refer to existing rows in
the database. The corresponding rows in the
<parameter><after></parameter> block reflect what the user wants
in the database. A row update operation is performed if there is a row
in both the <parameter><before></parameter> and
<parameter><after></parameter> sections with the same set of
values for the attributes that uniquely identify a row in a
table. Rows specified in the <parameter><before></parameter>
block must be valid in the database for the updategram to successfully
update the rows.
</para>
<para>
In a delete operation, if only the
<parameter><before></parameter> block is specified in the update
gram, the rows specified in the <parameter><before></parameter>
block are deleted from the table(s). If both the
<parameter><before></parameter> and
<parameter><after></parameter> blocks are specified, the rows
for which there are no corresponding rows in the
<parameter><after></parameter> block are deleted from the
table(s).
</para>
</sect2>
<sect2 id="usinginparams">
<title>Using Input Parameters</title>
<para>
Parameters declarations should be described in the <header>
section of the updategram. There should be one
<parameter><param></parameter> row for each parameter.
</para>
<para>
General syntax:
</para>
<programlisting>
<sql:header xmlns:sql="xml-sql">
<sql:param name="PARAM_NAME" [default="DEFAULT_VALUE"]/>
...
</sql:header>
</programlisting>
<para>
Where <parameter>PARAM_NAME</parameter> is the name of the parameter
and <parameter>DEFAULT_VALUE</parameter> is optional default of
parameter Parameters in updategram should have
<parameter>$PARAM_NAME</parameter> instead of a value. On processing,
Virtuoso replaces <parameter>$PARAM_NAME</parameter> with the
corresponding value from the
<parameter><input_parameters></parameter> given to the function
<function>xmlsql_update()</function>.
</para>
</sect2>
<sect2 id="examples">
<title>Examples</title>
<para>
Given the following tables:
</para>
<programlisting>
CREATE TABLE Orders (
OrderID int identity,
CustomerID varchar(10),
EmpID int,
PRIMARY KEY (OrderID));
CREATE TABLE OrderDetails (
OrderID int,
ProductID int,
Quantity int);
</programlisting>
<para>
A. Update Gram to Insert a Record
</para>
<programlisting>
xmlsql_update (xml_tree_doc (xml_tree (
'<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:sync>
<sql:after>
<Orders CustomerID="TEST" EmpID="99"/>
</sql:after>
</sql:sync>
</ROOT>')));
</programlisting>
<para>
B. Updategram with an <parameter>at-identity</parameter> Attribute
</para>
<programlisting>
xmlsql_update (xml_tree_doc (xml_tree (
'<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:sync>
<sql:after>
<Orders sql:at-identity="x" CustomerID="VINET" EmpID="10"/>
<OrderDetails OrderID="x" ProductID="1" Quantity="50"/>
<OrderDetails OrderID="x" ProductID="2" Quantity="20"/>
<Orders sql:at-identity="x" CustomerID="HANAR" EmpID="11"/>
<OrderDetails OrderID="x" ProductID="1" Quantity="30"/>
<OrderDetails OrderID="x" ProductID="4" Quantity="25"/>
</sql:after>
</sql:sync>
</ROOT>')));
</programlisting>
<para>
C. Updategram to Delete a Record
</para>
<programlisting>
xmlsql_update (xml_tree_doc (xml_tree (
'<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:sync>
<sql:before>
<Orders CustomerID="HANAR" EmpID="11"/>
</sql:before>
</sql:sync>
</ROOT>')));
</programlisting>
<para>
D. Updategram to Update a Record
</para>
<programlisting>
xmlsql_update (xml_tree_doc (xml_tree (
'<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:sync>
<sql:before>
<Orders sql:id="1" CustomerID="VINET" EmpID="10"/>
</sql:before>
<sql:after>
<Orders sql:id="1" CustomerID="VINET_NEW" EmpID="11"/>
</sql:after>
</sql:sync>
</ROOT>')));
</programlisting>
<para>
E: Using a different syntax for updategrams — entities in place
of attributes — example D can be transformed to:
</para>
<programlisting>
xmlsql_update (xml_tree_doc (xml_tree (
'<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:sync>
<sql:before>
<Orders sql:id="1">
<CustomerID>VINET</CustomerID>
<EmpID>10</EmpID>
</Orders>
</sql:before>
<sql:after>
<Orders sql:id="1">
<CustomerID>VINET_NEW</CustomerID>
<EmpID>11</EmpID>
</Orders>
</sql:after>
</sql:sync>
</ROOT>')));
</programlisting>
<para>
Note that two syntaxes cannot be mixed in one document.
</para>
<para>
F: Using input parameters
</para>
<para>
Assume the following table:
</para>
<programlisting>
CREATE TABLE Shippers(
ShipperID INTEGER,
CompanyName VARCHAR(40),
Phone VARCHAR(24),
PRIMARY KEY (ShipperID));
xmlsql_update (xml_tree_doc (xml_tree (
'<DocumentElement xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:header>
<sql:param name="ShipperID" default="2"/>
<sql:param name="CompanyName" default="United Package New"/>
<sql:param name="Phone" default="(503) 555-3199 (new)"/>
</sql:header>
<sql:sync>
<sql:before>
</sql:before>
<sql:after>
<Shippers sql:id="1" ShipperID="\$ShipperID"
CompanyName="\$CompanyName" Phone="\$Phone"/>
</sql:after>
</sql:sync>
</DocumentElement>')),
vector ('ShipperID','10','CompanyName','DHL','Phone','+359 32 144'));
-- <- this is a array with input parameters
</programlisting>
<para>
This will add one record to the Shippers table with the data in the array.
Note that the slash/dollar sign pair '\$' transforms to dollar sign '$' only
</para>
</sect2>
</sect1>
<!-- ################### ~~~~~~~~~~ ################### -->
&xmltemplates;
<!-- ################### ~~~~~~~~~~ ################### -->
&xmlschema;
<!-- ################### ~~~~~~~~~~ ################### -->
&xquery;
<!-- ################### ~~~~~~~~~~ ################### -->
&xslttrans;
<!-- ################### ~~~~~~~~~~ ################### -->
&XMLType;
&XMLDOM;
</chapter>
|