8.4 中文镜像

MySQL 8.4 参考手册 · 中文镜像

正文为中文,英文原文默认隐藏。点击任意中文段落即可展开 / 收起对应英文原文。

打开官方英文原文 ↗

译文由机器翻译生成,仅供理解参考;有疑问请以官方英文原文为准。

中文点击中文段落可显示 / 隐藏英文原文本页译文覆盖 88%

14.11 XML 函数14.11 XML Functions

表 14.16 XML 函数Table 14.16 XML Functions

名称Name 说明Description
ExtractValue() 使用 XPath 表达式从 XML 字符串中提取值Extract a value from an XML string using XPath notation
UpdateXML() 返回替换后的 XML 片段Return replaced XML fragment

本节讨论 MySQL 中的 XML 及相关功能。This section discusses XML and related functionality in MySQL.

Note

可以通过在 mysql 和 mysqldump 客户端中使用 --xml 选项调用它们来获取 XML 格式的输出。请参见 第 6.5.1 节,“mysql — MySQL 命令行客户端”,以及 第 6.5.4 节,“mysqldump — 数据库备份程序”。It is possible to obtain XML-formatted output from MySQL in the mysql and mysqldump clients by invoking them with the --xml option. See Section 6.5.1, “mysql — The MySQL Command-Line Client”, and Section 6.5.4, “mysqldump — A Database Backup Program”.

提供了两个提供基本 XPath 1.0(XML 路径语言,第 1.0 版)功能的函数。本节稍后提供了一些关于 XPath 语法和使用的基本信息;然而,对这些主题的深入讨论超出了本手册的范围,您应参考XML 路径语言(XPath)1.0 标准以获取权威信息。对于 XPath 新手或希望重新学习基础知识的人来说,一个有用的资源是Zvon.org XPath 教程,该教程提供多种语言版本。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 the XML 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

这些功能仍在开发中。我们将继续改进 MySQL 8.4 及以后的版本中 XML 和 XPath 功能的这些及其他方面。您可以在 MySQL XML 用户论坛 中讨论这些功能、提出问题并获得其他用户的帮助。These functions remain under development. We continue to improve these and other aspects of XML and XPath functionality in MySQL 8.4 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 表达式支持用户变量和本地存储程序变量。用户变量检查较弱;存储程序的本地变量检查较严格(另见 Bug #26518):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):

  • 用户变量(弱检查)。 使用语法 $@variable_name(即用户变量)的变量不会被检查。如果变量类型错误或之前未被赋值,服务器不会发出任何警告或错误。这也意味着用户需自行承担任何拼写错误的责任,因为如果使用了 $@myvairable 而原本意图是 $@myvariable,不会发出任何警告。User variables (weak checking).  Variables using the syntax $@variable_name (that is, user variables) are not checked. No warnings or errors are issued by the server if a variable has the wrong type or has previously not been assigned a value. This also means the user is fully responsible for any typographical errors, since no warnings are given if (for example) $@myvairable is used where $@myvariable was intended.

    示例:Example:

    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)
    
  • 存储程序中的变量(强类型检查)。 使用语法 $variable_name 的变量可以在这些函数在存储程序内部调用时声明和使用。此类变量仅在定义它们的存储程序中是局部的,并且会进行类型和值的严格检查。Variables in stored programs (strong checking).  Variables using the syntax $variable_name can be declared and used with these functions when they are called inside stored programs. Such variables are local to the stored program in which they are defined, and are strongly checked for type and value.

    示例:Example:

    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)
    

    参数。 在存储例程中作为参数传入的、用于 XPath 表达式的变量也需要进行严格检查。Parameters.  Variables used in XPath expressions inside stored routines that are passed in as parameters are also subject to strong checking.

包含用户变量或存储程序本地变量的表达式,除了符号外,必须符合 XPath 1.0 规范中关于包含变量的 XPath 表达式的规则。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

用于存储 XPath 表达式的用户变量被视为空字符串。因此,不可能将 XPath 表达式存储为用户变量。(漏洞 #32911)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)

  • ExtractValue(xml_frag, xpath_expr)

    ExtractValue() 接受两个字符串参数,一个 XML 标记片段 xml_frag 和一个 XPath 表达式 xpath_expr(也称为 定位器);它返回由 XPath 表达式匹配的元素的第一个子文本节点的文本 (CDATA)。ExtractValue() takes two string arguments, a fragment of XML markup xml_frag and an XPath expression xpath_expr (also known as a locator); it returns the text (CDATA) of the first text node which is a child of the element or elements matched by the XPath expression.

    使用此函数等同于在附加了 /text() 之后使用 xpath_expr 进行匹配。换句话说,ExtractValue('<a><b>Sakila</b></a>', '/a/b') 和 ExtractValue('<a><b>Sakila</b></a>', '/a/b/text()') 产生相同的结果。如果 xml_frag 或 xpath_expr 为 NULL,该函数返回 NULL。Using this function is the equivalent of performing a match using the xpath_expr after appending /text(). In other words, ExtractValue('<a><b>Sakila</b></a>', '/a/b') and ExtractValue('<a><b>Sakila</b></a>', '/a/b/text()') produce the same result. If xml_frag or xpath_expr is NULL, the function returns NULL.

    如果找到多个匹配项,则返回每个匹配元素的第一个子文本节点的内容(按匹配顺序)作为单个以空格分隔的字符串。If multiple matches are found, the content of the first child text node of each matching element is returned (in the order matched) as a single, space-delimited string.

    如果没有找到与表达式匹配的文本节点(包括隐式的 /text())——无论出于何种原因,只要 xpath_expr 是有效的,并且 xml_frag 由正确嵌套和闭合的元素组成——则返回空字符串。对于空元素的匹配和根本没有匹配,不会做区分。这是设计使然。If no matching text node is found for the expression (including the implicit /text())—for whatever reason, as long as xpath_expr is valid, and xml_frag consists of elements which are properly nested and closed—an empty string is returned. No distinction is made between a match on an empty element and no match at all. This is by design.

    如果您需要确定在 xml_frag 中是否未找到匹配的元素,或者找到了这样的元素但其中不包含子文本节点,您应该测试使用 XPath count() 函数的表达式的结果。例如,这两个语句都返回一个空字符串,如下所示:If you need to determine whether no matching element was found in xml_frag or such an element was found but contained no child text nodes, you should test the result of an expression that uses the XPath count() function. For example, both of these statements return an empty string, as shown here:

    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() 仅返回 CDATA,不会返回匹配标签中可能包含的任何标签,也不会返回它们的内容(参见以下示例中作为 val1 返回的结果)。ExtractValue() returns only CDATA, 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 |
    +------+------+------+------+---------+
    

    该函数使用当前的 SQL 排序规则来与 contains() 进行比较,执行与其他字符串函数(例如 CONCAT())相同的排序规则聚合,同时考虑其参数的排序规则强制性;有关控制此行为规则的解释,请参见 第 12.8.4 节,“表达式中的排序规则强制性”。This function uses the current SQL collation for making comparisons with contains(), performing the same collation aggregation as other string functions (such as CONCAT()), in taking into account the collation coercibility of their arguments; see Section 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 会被返回,如果 xml_frag 包含未正确嵌套或未闭合的元素,并且会生成一个警告,如下例所示:NULL is returned if xml_frag 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)
    
  • UpdateXML(xml_target, xpath_expr, new_xml)

    此函数用一个新的 XML 片段 new_xml 替换给定 XML 片段 xml_target 的单个部分,然后返回更改后的 XML。被替换的 xml_target 部分匹配用户提供的 XPath 表达式 xpath_expr。This function replaces a single portion of a given fragment of XML markup xml_target with a new XML fragment new_xml, and then returns the changed XML. The portion of xml_target that is replaced matches an XPath expression xpath_expr supplied by the user.

    如果没有找到与 xpath_expr 匹配的表达式,或者找到多个匹配项,则函数返回原始的 xml_target XML 片段。三个参数都应为字符串。如果 UpdateXML() 的任何参数为 NULL,函数将返回 NULL。If no expression matching xpath_expr is found, or if multiple matches are found, the function returns the original xml_target XML fragment. All three arguments should be strings. If any of the arguments to UpdateXML() are NULL, the function returns NULL.

    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

对 XPath 语法和使用的深入讨论超出了本手册的范围。请参见 XML 路径语言 (XPath) 1.0 规范 以获取权威信息。对于 XPath 新手或希望复习基础知识的人来说,一个有用的资源是 Zvon.org XPath 教程,该教程提供多种语言版本。A discussion in depth of XPath syntax and usage are beyond the scope of this manual. Please see the XML 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 the Zvon.org XPath Tutorial, which is available in several languages.

以下是一些基本 XPath 表达式的描述和示例:Descriptions and examples of some basic XPath expressions follow:

  • /tag

    仅当 <tag/> 是根元素时才匹配 <tag/>。Matches <tag/> if and only if <tag/> is the root element.

    示例:/a 在 <a><b/></a> 中有匹配,因为它匹配最外层(根)标签。在 <b><a/></b> 中,它不会匹配内层 a 元素,因为在这种情况下它是另一个元素的子元素。Example: /a has a match in <a><b/></a> because it matches the outermost (root) tag. It does not match the inner a element in <b><a/></b> because in this instance it is the child of another element.

  • /tag1/tag2

    当且仅当它是<tag2/>的子项时<tag1/>,以及<tag1/>是根元素。Matches <tag2/> if and only if it is a child of <tag1/>, and <tag1/> is the root element.

    示例:/a/b 匹配 XML 片段 <a><b/></a> 中的 b 元素,因为它是根元素 a 的子元素。在 <b><a/></b> 中没有匹配,因为在这种情况下,b 是根元素(因此不是任何其他元素的子元素)。XPath 表达式在 <a><c><b/></c></a> 中也没有匹配;在这里,b 是 a 的后代,但实际上不是 a 的子元素。Example: /a/b matches the b element in the XML fragment <a><b/></a> because it is a child of the root element a. It does not have a match in <b><a/></b> because in this case, b is the root element (and hence the child of no other element). Nor does the XPath expression have a match in <a><c><b/></c></a>; here, b is a descendant of a, but not actually a child of a.

    这个结构可以扩展到三个或更多元素。例如,XPath 表达式 /a/b/c 匹配片段 <a><b><c/></b></a> 中的 c 元素。This construct is extendable to three or more elements. For example, the XPath expression /a/b/c matches the c element in the fragment <a><b><c/></b></a>.

  • //tag

    匹配任何 <tag> 的实例。Matches any instance of <tag>.

    示例://a 匹配以下任意情况中的 a 元素:<a><b><c/></b></a>;<c><a><b/></a></b>;<c><b><a/></b></c>。Example: //a matches the a element in any of the following: <a><b><c/></b></a>; <c><a><b/></a></b>; <c><b><a/></b></c>.

    // 可以与 / 结合使用。例如,//a/b 匹配两个片段中的 b 元素:<a><b/></a> 或 <c><a><b/></a></c>。// can be combined with /. For example, //a/b matches the b element in either of the fragments <a><b/></a> or <c><a><b/></a></c>.

    Note

    //tag 相当于 /descendant-or-self::*/tag。一个常见的错误是将其与 /descendant-or-self::tag 混淆,尽管后者表达式实际上可能导致非常不同的结果,如下所示://tag is the equivalent of /descendant-or-self::*/tag. A common error is to confuse this with /descendant-or-self::tag, although the latter expression can actually lead to very different results, as can be seen here:

    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)
    
  • * 运算符充当一个 “通配符”,可以匹配任何元素。例如,表达式 /*/b 匹配 XML 片段 <a><b/></a> 或 <c><b/></c> 中的 b 元素。然而,该表达式在片段 <b><a/></b> 中不会匹配,因为 b 必须是其他元素的子元素。通配符可以用于任何位置:表达式 /*/b/* 匹配任何不是根元素的 b 元素的子元素。The * operator acts as a “wildcard” that matches any element. For example, the expression /*/b matches the b element in either of the XML fragments <a><b/></a> or <c><b/></c>. However, the expression does not produce a match in the fragment <b><a/></b> because b must be a child of some other element. The wildcard may be used in any position: The expression /*/b/* matches any child of a b element that is itself not the root element.

  • 你可以使用 | (UNION) 运算符匹配任意多个定位器。例如,表达式 //b|//c 会匹配 XML 目标中的所有 b 和 c 元素。You can match any of several locators using the | (UNION) operator. For example, the expression //b|//c matches all b and c elements in the XML target.

  • 也可以根据一个或多个属性的值来匹配元素。这可以使用语法 tag[@attribute="value"] 来完成。例如,表达式 //b[@id="idB"] 会匹配片段 b 中的第二个 <a><b id="idA"/><c/><b id="idB"/></a> 元素。要匹配具有 任意 属性值为 attribute="value" 的元素,可以使用 XPath 表达式 //*[attribute="value"]。It is also possible to match an element based on the value of one or more of its attributes. This done using the syntax tag[@attribute="value"]. For example, the expression //b[@id="idB"] matches the second b element in the fragment <a><b id="idA"/><c/><b id="idB"/></a>. To match against any element having attribute="value", use the XPath expression //*[attribute="value"].

    要筛选多个属性值,只需连续使用多个属性比较子句。例如,表达式 //b[@c="x"][@d="y"] 匹配出现在给定 XML 片段中的元素 <b c="x" d="y"/>。To filter multiple attribute values, simply use multiple attribute-comparison clauses in succession. For example, the expression //b[@c="x"][@d="y"] matches the element <b c="x" d="y"/> occurring anywhere in a given XML fragment.

    要查找同一属性匹配多个值的元素,可以使用由 | 运算符连接的多个定位器。例如,要匹配所有 b 元素,其 c 属性的值为 23 或 17,可以使用表达式 //b[@c="23"]|//b[@c="17"]。你也可以使用逻辑 or 运算符来实现此目的://b[@c="23" or @c="17"]。To find elements for which the same attribute matches any of several values, you can use multiple locators joined by the | operator. For example, to match all b elements whose c attributes have either of the values 23 or 17, use the expression //b[@c="23"]|//b[@c="17"]. You can also use the logical or operator for this purpose: //b[@c="23" or @c="17"].

    Note

    or 和 | 之间的区别在于,or 连接条件,而 | 连接结果集。The difference between or and | is that or joins conditions, while | joins result sets.

XPath 限制。 这些函数支持的 XPath 语法目前受以下限制:XPath Limitations.  The XPath syntax supported by these functions is currently subject to the following limitations:

  • 不支持节点集到节点集的比较(例如 '/a/b[@c=@d]')。Nodeset-to-nodeset comparison (such as '/a/b[@c=@d]') is not supported.

  • 支持所有标准的 XPath 比较运算符。(错误 #22823)All of the standard XPath comparison operators are supported. (Bug #22823)

  • 相对定位器表达式是在根节点的上下文中解析的。例如,考虑以下查询和结果:Relative locator expressions are resolved in the context of the root node. For example, consider the following query and result:

    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)
    

    在这种情况下,定位器 a/b 解析为 /a/b。In this case, the locator a/b resolves to /a/b.

    相对定位器在谓词中也受支持。在以下示例中,d[../@c="1"] 被解析为 /a/b[@c="1"]/d: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)
    
  • 带有求值为标量值的表达式前缀的定位器——包括变量引用、字面量、数字和标量函数调用——是不允许的,使用它们会导致错误。Locators prefixed with expressions that evaluate as scalar values—including variable references, literals, numbers, and scalar function calls—are not permitted, and their use results in an error.

  • :: 运算符不支持与以下节点类型组合使用:The :: operator is not supported in combination with node types such as the following:

    • axis::comment()

    • axis::text()axis::text()

    • axis::processing-instructions()axis::processing-instructions()

    • axis::node()axis::node()

    然而,名称测试(例如 axis::name 和 axis::*)是支持的,如以下示例所示:However, name tests (such as axis::name and axis::*) are supported, as shown in these examples:

    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)
    
  • “上下”导航在路径会导致“超出”根元素的情况下不被支持。也就是说,你不能使用匹配给定元素的祖先的子孙的表达式,其中当前元素的一个或多个祖先也是根元素的祖先(见 Bug #16321)。“Up-and-down” navigation is not supported in cases where the path would lead “above” the root element. That is, you cannot use expressions which match on descendants of ancestors of a given element, where one or more of the ancestors of the current element is also an ancestor of the root element (see Bug #16321).

  • 以下 XPath 函数不受支持,或存在已知问题,如所示:The following XPath functions are not supported, or have known issues as indicated:

    • id()

    • lang()lang()

    • local-name()local-name()

    • name()name()

    • namespace-uri()namespace-uri()

    • normalize-space()normalize-space()

    • starts-with()starts-with()

    • string()string()

    • substring-after()substring-after()

    • substring-before()substring-before()

    • translate()translate()

  • 以下轴不受支持:The following axes are not supported:

    • following-sibling

    • following

    • preceding-sibling

    • preceding

传递给 ExtractValue() 和 UpdateXML() 的 XPath 表达式可能在元素选择器中包含冒号字符 (:),这使它们可以与使用 XML 命名空间标记的内容一起使用。例如:XPath expressions passed as arguments to ExtractValue() and UpdateXML() 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)

在某些方面,这与Apache Xalan和一些其他解析器允许的方式类似,并且比要求命名空间声明或使用namespace-uri()和local-name()函数要简单得多。This is similar in some respects to what is permitted by Apache Xalan and some other parsers, and is much simpler than requiring namespace declarations or the use of the namespace-uri() and local-name() functions.

错误处理。 对于ExtractValue()和UpdateXML(),所使用的 XPath 定位器必须有效,而且要搜索的 XML 必须由正确嵌套和关闭的元素组成。如果定位器无效,将生成错误:Error handling.  For both ExtractValue() and UpdateXML(), 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'

如果xml_frag不包含正确嵌套和闭合的元素,则返回NULL并生成警告,如下例所示:If xml_frag 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

作为UpdateXML()第三个参数使用的替换 XML 不会检查其是否完全由正确嵌套和闭合的元素组成。The replacement XML used as the third argument to UpdateXML() is not checked to determine whether it consists solely of elements which are properly nested and closed.

XPath 注入。 代码注入发生在恶意代码被引入系统以获取未经授权的权限和数据时。其原理是基于开发者对来自用户的数据输入类型和内容所做的假设。XPath 在这方面也不例外。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.

一个常见的可能发生这种情况的场景是应用程序通过将登录名和密码的组合与 XML 文件中找到的内容匹配来处理授权的情况,使用如下 XPath 表达式: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

这是相当于如下 SQL 语句的 XPath 表达式:This is the XPath equivalent of an SQL statement like this one:

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

一个使用 XPath 的 PHP 应用程序可能会这样处理登录过程:A PHP application employing XPath might handle the login process like this:

<?php

  $file     =   "users.xml";

  $login    =   $POST["login"];
  $password =   $POST["password"];

  $xpath = "//user[login/text()=$login and password/text()=$password]/attribute::id";

  if( file_exists($file) )
  {
    $xml = simplexml_load_file($file);

    if($result = $xml->xpath($xpath))
      echo "You are now logged in as user $result[0].";
    else
      echo "Invalid login name or password.";
  }
  else
    exit("Failed to open $file.");

?>

输入不会进行任何检查。这意味着恶意用户可以“绕过”测试,通过为登录名和密码输入' or 1=1,导致$xpath被按如下方式评估: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

由于方括号内的表达式总是评估为 true,它实际上等同于这个表达式,它会匹配 XML 文档中每个 user 元素的 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 every user element in the XML document:

//user/attribute::id

针对这种特定攻击的一种规避方法是,在定义 $xpath 时对要插入的变量名使用引号,从而强制从 Web 表单传入的值被转换为字符串: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:

$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";

这是通常推荐用于防止 SQL 注入攻击的同一策略。一般来说,防止 XPath 注入攻击的做法与防止 SQL 注入攻击的做法相同: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:

  • - 永远不要在应用程序中接受未经测试的用户数据。Never accepted untested data from users in your application.

  • - 检查所有用户提交的数据类型;拒绝或转换类型错误的数据。Check all user-submitted data for type; reject or convert data that is of the wrong type

  • - 测试数值数据是否超出范围;截断、四舍五入或拒绝超出范围的值。测试字符串是否含有非法字符,并将其去掉或拒绝包含非法字符的输入。Test numeric data for out of range values; truncate, round, or reject values that are out of range. Test strings for illegal characters and either strip them out or reject input containing them.

  • - 不要输出可能为未授权用户提供可用于破坏系统线索的明确错误信息;将这些错误记录到文件或表中。Do not output explicit error messages that might provide an unauthorized user with clues that could be used to compromise the system; log these to a file or database table instead.

正如 SQL 注入攻击可以用来获取数据库模式的信息一样,XPath 注入也可以用来遍历 XML 文件以发现其结构,正如 Amit Klein 的论文 Blind XPath Injection(PDF 文件,46KB)中讨论的那样。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 paper Blind XPath Injection (PDF file, 46KB).

同时检查发送回客户端的输出也很重要。考虑在使用 MySQL ExtractValue() 函数时可能发生的情况:It is also important to check the output being sent back to the client. Consider what can happen when we use the MySQL ExtractValue() 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)

因为 ExtractValue() 会将多个匹配项作为单个以空格分隔的字符串返回,所以此注入攻击会将 users.xml 中包含的每个有效 ID 提供给用户,作为单行输出。作为额外的安全措施,你还应该在将输出返回给用户之前进行测试。以下是一个简单示例:Because ExtractValue() returns multiple matches as a single space-delimited string, this injection attack provides every valid ID contained within users.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:

  • 始终测试输出数据的类型和允许的值。Always test outgoing data for type and permissible values.

  • 绝不允许未经授权的用户查看可能提供有关应用程序信息的错误信息,这些信息可能被用于利用应用程序。Never permit unauthorized users to view error messages that might provide information about the application that could be used to exploit it.