MySQL :: MySQL 8.0 Reference Manual :: 14.11 XML Functions (original) (raw)

Table 14.16 XML Functions

Name Description
ExtractValue() Extract a value from an XML string using XPath notation
UpdateXML() Return replaced XML fragment

This section discusses XML and related functionality in MySQL.

Two functions providing basic XPath 1.0 (XML Path Language, version 1.0) capabilities are available. Some basic information about XPath syntax and usage is provided later in this section; however, an in-depth discussion of these topics is beyond the scope of this manual, and you should refer to theXML Path Language (XPath) 1.0 standard for definitive information. A useful resource for those new to XPath or who desire a refresher in the basics is the Zvon.org XPath Tutorial, which is available in several languages.

Note

These functions remain under development. We continue to improve these and other aspects of XML and XPath functionality in MySQL 8.0 and onwards. You may discuss these, ask questions about them, and obtain help from other users with them in the MySQL XML User Forum.

XPath expressions used with these functions support user variables and local stored program variables. User variables are weakly checked; variables local to stored programs are strongly checked (see also Bug #26518):

mysql> SET @xml = '<a><b>X</b><b>Y</b></a>';  
Query OK, 0 rows affected (0.00 sec)  
mysql> SET @i =1, @j = 2;  
Query OK, 0 rows affected (0.00 sec)  
mysql> SELECT @i, ExtractValue(@xml, '//b[$@i]');  
+------+--------------------------------+  
| @i   | ExtractValue(@xml, '//b[$@i]') |  
+------+--------------------------------+  
|    1 | X                              |  
+------+--------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT @j, ExtractValue(@xml, '//b[$@j]');  
+------+--------------------------------+  
| @j   | ExtractValue(@xml, '//b[$@j]') |  
+------+--------------------------------+  
|    2 | Y                              |  
+------+--------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT @k, ExtractValue(@xml, '//b[$@k]');  
+------+--------------------------------+  
| @k   | ExtractValue(@xml, '//b[$@k]') |  
+------+--------------------------------+  
| NULL |                                |  
+------+--------------------------------+  
1 row in set (0.00 sec)  
mysql> DELIMITER |  
mysql> CREATE PROCEDURE myproc ()  
    -> BEGIN  
    ->   DECLARE i INT DEFAULT 1;  
    ->   DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>';  
    ->  
    ->   WHILE i < 4 DO  
    ->     SELECT xml, i, ExtractValue(xml, '//a[$i]');  
    ->     SET i = i+1;  
    ->   END WHILE;  
    -> END |  
Query OK, 0 rows affected (0.01 sec)  
mysql> DELIMITER ;  
mysql> CALL myproc();  
+--------------------------+---+------------------------------+  
| xml                      | i | ExtractValue(xml, '//a[$i]') |  
+--------------------------+---+------------------------------+  
| <a>X</a><a>Y</a><a>Z</a> | 1 | X                            |  
+--------------------------+---+------------------------------+  
1 row in set (0.00 sec)  
+--------------------------+---+------------------------------+  
| xml                      | i | ExtractValue(xml, '//a[$i]') |  
+--------------------------+---+------------------------------+  
| <a>X</a><a>Y</a><a>Z</a> | 2 | Y                            |  
+--------------------------+---+------------------------------+  
1 row in set (0.01 sec)  
+--------------------------+---+------------------------------+  
| xml                      | i | ExtractValue(xml, '//a[$i]') |  
+--------------------------+---+------------------------------+  
| <a>X</a><a>Y</a><a>Z</a> | 3 | Z                            |  
+--------------------------+---+------------------------------+  
1 row in set (0.01 sec)  

Parameters. Variables used in XPath expressions inside stored routines that are passed in as parameters are also subject to strong checking.

Expressions containing user variables or variables local to stored programs must otherwise (except for notation) conform to the rules for XPath expressions containing variables as given in the XPath 1.0 specification.

Note

A user variable used to store an XPath expression is treated as an empty string. Because of this, it is not possible to store an XPath expression as a user variable. (Bug #32911)

mysql> SELECT ExtractValue('<a><b/></a>', '/a/b');  
+-------------------------------------+  
| ExtractValue('<a><b/></a>', '/a/b') |  
+-------------------------------------+  
|                                     |  
+-------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue('<a><c/></a>', '/a/b');  
+-------------------------------------+  
| ExtractValue('<a><c/></a>', '/a/b') |  
+-------------------------------------+  
|                                     |  
+-------------------------------------+  
1 row in set (0.00 sec)  

However, you can determine whether there was actually a matching element using the following:

mysql> SELECT ExtractValue('<a><b/></a>', 'count(/a/b)');  
+-------------------------------------+  
| ExtractValue('<a><b/></a>', 'count(/a/b)') |  
+-------------------------------------+  
| 1                                   |  
+-------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue('<a><c/></a>', 'count(/a/b)');  
+-------------------------------------+  
| ExtractValue('<a><c/></a>', 'count(/a/b)') |  
+-------------------------------------+  
| 0                                   |  
+-------------------------------------+  
1 row in set (0.01 sec)  

Important
ExtractValue() returns onlyCDATA, and does not return any tags that might be contained within a matching tag, nor any of their content (see the result returned as val1 in the following example).

mysql> SELECT  
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/a') AS val1,  
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/a/b') AS val2,  
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '//b') AS val3,  
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/b') AS val4,  
    ->   ExtractValue('<a>ccc<b>ddd</b><b>eee</b></a>', '//b') AS val5;  
+------+------+------+------+---------+  
| val1 | val2 | val3 | val4 | val5    |  
+------+------+------+------+---------+  
| ccc  | ddd  | ddd  |      | ddd eee |  
+------+------+------+------+---------+  

This function uses the current SQL collation for making comparisons with contains(), performing the same collation aggregation as other string functions (such asCONCAT()), in taking into account the collation coercibility of their arguments; seeSection 12.8.4, “Collation Coercibility in Expressions”, for an explanation of the rules governing this behavior.
(Previously, binary—that is, case-sensitive—comparison was always used.)
NULL is returned if_xmlfrag_ contains elements which are not properly nested or closed, and a warning is generated, as shown in this example:

mysql> SELECT ExtractValue('<a>c</a><b', '//a');  
+-----------------------------------+  
| ExtractValue('<a>c</a><b', '//a') |  
+-----------------------------------+  
| NULL                              |  
+-----------------------------------+  
1 row in set, 1 warning (0.00 sec)  
mysql> SHOW WARNINGS\G  
*************************** 1. row ***************************  
  Level: Warning  
   Code: 1525  
Message: Incorrect XML value: 'parse error at line 1 pos 11:  
         END-OF-INPUT unexpected ('>' wanted)'  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue('<a>c</a><b/>', '//a');  
+-------------------------------------+  
| ExtractValue('<a>c</a><b/>', '//a') |  
+-------------------------------------+  
| c                                   |  
+-------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT  
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/a', '<e>fff</e>') AS val1,  
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/b', '<e>fff</e>') AS val2,  
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '//b', '<e>fff</e>') AS val3,  
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val4,  
    ->   UpdateXML('<a><d></d><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val5  
    -> \G  
*************************** 1. row ***************************  
val1: <e>fff</e>  
val2: <a><b>ccc</b><d></d></a>  
val3: <a><e>fff</e><d></d></a>  
val4: <a><b>ccc</b><e>fff</e></a>  
val5: <a><d></d><b>ccc</b><d></d></a>  

Note

A discussion in depth of XPath syntax and usage are beyond the scope of this manual. Please see theXML Path Language (XPath) 1.0 specification for definitive information. A useful resource for those new to XPath or who are wishing a refresher in the basics is theZvon.org XPath Tutorial, which is available in several languages.

Descriptions and examples of some basic XPath expressions follow:

mysql> SET @xml = '<a><b><c>w</c><b>x</b><d>y</d>z</b></a>';  
Query OK, 0 rows affected (0.00 sec)  
mysql> SELECT @xml;  
+-----------------------------------------+  
| @xml                                    |  
+-----------------------------------------+  
| <a><b><c>w</c><b>x</b><d>y</d>z</b></a> |  
+-----------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue(@xml, '//b[1]');  
+------------------------------+  
| ExtractValue(@xml, '//b[1]') |  
+------------------------------+  
| x z                          |  
+------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue(@xml, '//b[2]');  
+------------------------------+  
| ExtractValue(@xml, '//b[2]') |  
+------------------------------+  
|                              |  
+------------------------------+  
1 row in set (0.01 sec)  
mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[1]');  
+---------------------------------------------------+  
| ExtractValue(@xml, '/descendant-or-self::*/b[1]') |  
+---------------------------------------------------+  
| x z                                               |  
+---------------------------------------------------+  
1 row in set (0.06 sec)  
mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[2]');  
+---------------------------------------------------+  
| ExtractValue(@xml, '/descendant-or-self::*/b[2]') |  
+---------------------------------------------------+  
|                                                   |  
+---------------------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[1]');  
+-------------------------------------------------+  
| ExtractValue(@xml, '/descendant-or-self::b[1]') |  
+-------------------------------------------------+  
| z                                               |  
+-------------------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[2]');  
+-------------------------------------------------+  
| ExtractValue(@xml, '/descendant-or-self::b[2]') |  
+-------------------------------------------------+  
| x                                               |  
+-------------------------------------------------+  
1 row in set (0.00 sec)  

XPath Limitations. The XPath syntax supported by these functions is currently subject to the following limitations:

mysql> SELECT ExtractValue(  
    ->   '<a><b c="1">X</b><b c="2">Y</b></a>',  
    ->    'a/b'  
    -> ) AS result;  
+--------+  
| result |  
+--------+  
| X Y    |  
+--------+  
1 row in set (0.03 sec)  

In this case, the locator a/b resolves to/a/b.
Relative locators are also supported within predicates. In the following example, d[../@c="1"] is resolved as /a/b[@c="1"]/d:

mysql> SELECT ExtractValue(  
    ->      '<a>  
    ->        <b c="1"><d>X</d></b>  
    ->        <b c="2"><d>X</d></b>  
    ->      </a>',  
    ->      'a/b/d[../@c="1"]')  
    -> AS result;  
+--------+  
| result |  
+--------+  
| X      |  
+--------+  
1 row in set (0.00 sec)  
mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b');  
+-------------------------------------------------------+  
| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b') |  
+-------------------------------------------------------+  
| x                                                     |  
+-------------------------------------------------------+  
1 row in set (0.02 sec)  
mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*');  
+-------------------------------------------------------+  
| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*') |  
+-------------------------------------------------------+  
| x y                                                   |  
+-------------------------------------------------------+  
1 row in set (0.01 sec)  

XPath expressions passed as arguments toExtractValue() andUpdateXML() may contain the colon character (:) in element selectors, which enables their use with markup employing XML namespaces notation. For example:

mysql> SET @xml = '<a>111<b:c>222<d>333</d><e:f>444</e:f></b:c></a>';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT ExtractValue(@xml, '//e:f');
+-----------------------------+
| ExtractValue(@xml, '//e:f') |
+-----------------------------+
| 444                         |
+-----------------------------+
1 row in set (0.00 sec)

mysql> SELECT UpdateXML(@xml, '//b:c', '<g:h>555</g:h>');
+--------------------------------------------+
| UpdateXML(@xml, '//b:c', '<g:h>555</g:h>') |
+--------------------------------------------+
| <a>111<g:h>555</g:h></a>                   |
+--------------------------------------------+
1 row in set (0.00 sec)

This is similar in some respects to what is permitted byApache Xalan and some other parsers, and is much simpler than requiring namespace declarations or the use of the namespace-uri() and local-name() functions.

Error handling. For both ExtractValue() andUpdateXML(), the XPath locator used must be valid and the XML to be searched must consist of elements which are properly nested and closed. If the locator is invalid, an error is generated:

mysql> SELECT ExtractValue('<a>c</a><b/>', '/&a');
ERROR 1105 (HY000): XPATH syntax error: '&a'

If xmlfrag does not consist of elements which are properly nested and closed,NULL is returned and a warning is generated, as shown in this example:

mysql> SELECT ExtractValue('<a>c</a><b', '//a');
+-----------------------------------+
| ExtractValue('<a>c</a><b', '//a') |
+-----------------------------------+
| NULL                              |
+-----------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1525
Message: Incorrect XML value: 'parse error at line 1 pos 11:
         END-OF-INPUT unexpected ('>' wanted)'
1 row in set (0.00 sec)

mysql> SELECT ExtractValue('<a>c</a><b/>', '//a');
+-------------------------------------+
| ExtractValue('<a>c</a><b/>', '//a') |
+-------------------------------------+
| c                                   |
+-------------------------------------+
1 row in set (0.00 sec)

Important

The replacement XML used as the third argument toUpdateXML() is_not_ checked to determine whether it consists solely of elements which are properly nested and closed.

XPath Injection. code injection occurs when malicious code is introduced into the system to gain unauthorized access to privileges and data. It is based on exploiting assumptions made by developers about the type and content of data input from users. XPath is no exception in this regard.

A common scenario in which this can happen is the case of application which handles authorization by matching the combination of a login name and password with those found in an XML file, using an XPath expression like this one:

//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id

This is the XPath equivalent of an SQL statement like this one:

SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';

A PHP application employing XPath might handle the login process like this:

<?php

  $file     =   "users.xml";

  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>l</mi><mi>o</mi><mi>g</mi><mi>i</mi><mi>n</mi><mo>=</mo></mrow><annotation encoding="application/x-tex">login    =   </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">in</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>POST["login"];
  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mi>a</mi><mi>s</mi><mi>s</mi><mi>w</mi><mi>o</mi><mi>r</mi><mi>d</mi><mo>=</mo></mrow><annotation encoding="application/x-tex">password =   </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">ss</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>POST["password"];

  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>p</mi><mi>a</mi><mi>t</mi><mi>h</mi><mo>=</mo><mi mathvariant="normal">&quot;</mi><mi mathvariant="normal">/</mi><mi mathvariant="normal">/</mi><mi>u</mi><mi>s</mi><mi>e</mi><mi>r</mi><mo stretchy="false">[</mo><mi>l</mi><mi>o</mi><mi>g</mi><mi>i</mi><mi>n</mi><mi mathvariant="normal">/</mi><mi>t</mi><mi>e</mi><mi>x</mi><mi>t</mi><mo stretchy="false">(</mo><mo stretchy="false">)</mo><mo>=</mo></mrow><annotation encoding="application/x-tex">xpath = &quot;//user[login/text()=</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">&quot;//</span><span class="mord mathnormal">u</span><span class="mord mathnormal" style="margin-right:0.02778em;">ser</span><span class="mopen">[</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">in</span><span class="mord">/</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">t</span><span class="mopen">(</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>login and password/text()=$password]/attribute::id";

  if( file_exists($file) )
  {
    <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>m</mi><mi>l</mi><mo>=</mo><mi>s</mi><mi>i</mi><mi>m</mi><mi>p</mi><mi>l</mi><mi>e</mi><mi>x</mi><mi>m</mi><msub><mi>l</mi><mi>l</mi></msub><mi>o</mi><mi>a</mi><msub><mi>d</mi><mi>f</mi></msub><mi>i</mi><mi>l</mi><mi>e</mi><mo stretchy="false">(</mo></mrow><annotation encoding="application/x-tex">xml = simplexml_load_file(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">m</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">im</span><span class="mord mathnormal" style="margin-right:0.01968em;">pl</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0197em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">o</span><span class="mord mathnormal">a</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10764em;">f</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mopen">(</span></span></span></span>file);

    if($result = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>m</mi><mi>l</mi><mo>−</mo><mo>&gt;</mo><mi>x</mi><mi>p</mi><mi>a</mi><mi>t</mi><mi>h</mi><mo stretchy="false">(</mo></mrow><annotation encoding="application/x-tex">xml-&gt;xpath(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">m</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord">−</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mopen">(</span></span></span></span>xpath))
      echo "You are now logged in as user $result[0].";
    else
      echo "Invalid login name or password.";
  }
  else
    exit("Failed to open $file.");

?>

No checks are performed on the input. This means that a malevolent user can “short-circuit” the test by entering' or 1=1 for both the login name and password, resulting in $xpath being evaluated as shown here:

//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id

Since the expression inside the square brackets always evaluates as true, it is effectively the same as this one, which matches the id attribute of everyuser element in the XML document:

//user/attribute::id

One way in which this particular attack can be circumvented is simply by quoting the variable names to be interpolated in the definition of $xpath, forcing the values passed from a Web form to be converted to strings:

This is the same strategy that is often recommended for preventing SQL injection attacks. In general, the practices you should follow for preventing XPath injection attacks are the same as for preventing SQL injection:

Just as SQL injection attacks can be used to obtain information about database schemas, so can XPath injection be used to traverse XML files to uncover their structure, as discussed in Amit Klein's paperBlind XPath Injection (PDF file, 46KB).

It is also important to check the output being sent back to the client. Consider what can happen when we use the MySQLExtractValue() function:

mysql> SELECT ExtractValue(
    ->     LOAD_FILE('users.xml'),
    ->     '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
    -> ) AS id;
+-------------------------------+
| id                            |
+-------------------------------+
| 00327 13579 02403 42354 28570 |
+-------------------------------+
1 row in set (0.01 sec)

Because ExtractValue() returns multiple matches as a single space-delimited string, this injection attack provides every valid ID contained withinusers.xml to the user as a single row of output. As an extra safeguard, you should also test output before returning it to the user. Here is a simple example:

mysql> SELECT @id = ExtractValue(
    ->     LOAD_FILE('users.xml'),
    ->     '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT IF(
    ->     INSTR(@id, ' ') = 0,
    ->     @id,
    ->     'Unable to retrieve user ID')
    -> AS singleID;
+----------------------------+
| singleID                   |
+----------------------------+
| Unable to retrieve user ID |
+----------------------------+
1 row in set (0.00 sec)

In general, the guidelines for returning data to users securely are the same as for accepting user input. These can be summed up as: