XML函数
表 XML函数
名称 | 描述 |
---|---|
ExtractValue() | 使用XPath表示法从XML字符串中提取值 |
UpdateXML() | 返回替换的XML片段 |
本节讨论MySQL中的XML和相关功能。
注意通过使用选项调用mysql和mysqldump客户端,可以从MySQL获取XML格式的输出
--xml
。请参见“mysql-MySQL命令行客户端”和“mysqldump-数据库备份程序”。
提供了两个提供基本XPath 1.0(XML路径语言,版本1.0)功能的功能。本节稍后将提供有关XPath语法和用法的一些基本信息。但是,对这些主题的深入讨论超出了本手册的范围,您应该参考 XML路径语言(XPath)1.0标准以获取确定的信息。Zvon.org XPath教程对XPath的新手或需要基础知识的人有用的资源,该语言提供多种语言。
注意这些功能仍在开发中。我们将继续改进MySQL 8.0及更高版本中XML和XPath功能的这些方面和其他方面。您可以在MySQL XML用户论坛中讨论这些问题,提出有关它们的问题,并从其他用户那里获得帮助。
这些函数使用的XPath表达式支持用户变量和本地存储的程序变量。用户变量被弱检查;强烈检查存储程序本地的变量(另请参见Bug#26518):
用户变量(弱检查)。不检查使用语法的变量(即用户变量)。如果变量的类型错误或先前未分配值,则服务器不会发出警告或错误。这也意味着用户应对印刷错误承担全部责任,因为如果(例如)在原定位置使用,则不会给出任何警告。
$@variable_name
$@myvariable
$@myvariable
例:
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
例:
mysql>
DELIMITER | mysql>CREATE PROCEDURE myproc () ->BEGIN ->DECLARE i INTDEFAULT 1; ->DECLARE xml VARCHAR(25)DEFAULT '<a>X</a><a>Y</a><a>Z</a>'; -> ->WHILE i < 4DO ->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表达式中使用的变量也要经过严格检查。
包含用户变量或存储程序本地变量的表达式必须(除符号外)必须符合XPath 1.0规范中给出的包含变量的XPath表达式的规则。
注意用于存储XPath表达式的用户变量被视为空字符串。因此,不可能将XPath表达式存储为用户变量。(缺陷#32911)
ExtractValue(xml_frag,xpath_expr)
ExtractValue()
带有两个字符串参数,一个XML标记的片段xml_frag
和一个XPath表达式xpath_expr
(也称为 locator);它返回CDATA
第一个文本节点的text(),它是XPath表达式匹配的一个或多个元素的子元素。使用此功能等效于使用
xpath_expr
after附加进行匹配/text()
。换言之,ExtractValue('<a><b>Sakila</b></a>','/a/b')
并且ExtractValue('<a><b>Sakila</b></a>','/a/b/text()')
产生相同的结果。如果找到多个匹配项,则每个匹配元素的第一个子文本节点的内容(以匹配的顺序)作为单个以空格分隔的字符串返回。
如果找不到表达式(包括隐式
/text()
)的匹配文本节点(无论何种原因,只要xpath_expr
是有效的,并且xml_frag
由正确嵌套和封闭的元素组成),则将返回一个空字符串。在空元素上的匹配与完全不匹配之间没有区别。这是设计使然。如果需要确定是否在其中找不到匹配的元素,
xml_frag
或者是否找到了此类元素但不包含子文本节点,则应测试使用XPathcount()
函数的表达式的结果。例如,这两个语句都返回一个空字符串,如下所示: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)但是,您可以使用以下方法确定实际上是否存在匹配的元素:
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)重要
ExtractValue()
仅返回CDATA
,并且不返回匹配标签中可能包含的任何标签,也不返回其任何内容(请参见val1
以下示例中返回的结果)。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()
)执行相同的校验规则聚合;有关控制此行为的规则的说明,请参见“表达式中的校验规则强制性”。(以前,始终使用二进制(即区分大小写)比较。)
NULL
如果xml_frag
包含未正确嵌套或关闭的元素,则返回,并生成警告,如下例所示: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_target
新的XML片段替换XML标记的给定片段的单个部分new_xml
,然后返回更改的XML。xml_target
被替换的部分与xpath_expr
用户提供的XPath表达式匹配。如果找不到表达式匹配
xpath_expr
项,或者找到多个匹配项,则该函数返回原始xml_target
XML片段。所有三个参数都应为字符串。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>
注意有关XPath语法和用法的深入讨论超出了本手册的范围。请参阅 XML路径语言(XPath)1.0规范以获取确定的信息。Zvon.org XPath教程对XPath的新手或希望复习基础的人有用的资源。
一些基本XPath表达式的描述和示例如下:
/tag
当且仅当是根元素时才匹配。
<tag/>
<tag/>
示例:
/a
具有匹配项,<a><b/></a>
因为它与最外面的(根)标记匹配。它与内部a
元素不匹配,<b><a/></b>
因为在这种情况下,它是另一个元素的子元素。/tag1/tag2
当且仅当它是的子元素并且是根元素时才匹配。
<tag2/>
<tag1/>
<tag1/>
示例:
/a/b
匹配b
XML片段中的元素,<a><b/></a>
因为它是root元素的子元素a
。它没有匹配项,<b><a/></b>
因为在这种情况下,b
它是根元素(因此是其他任何元素的子元素)。XPath表达式中也没有匹配项<a><c><b/></c></a>
;这里b
是的后代a
,但实际上不是的后代a
。该构造可扩展到三个或更多元素。例如,XPath表达式
/a/b/c
与c
fragment 中的元素匹配<a><b><c/></b></a>
。//tag
匹配的任何实例。
<tag>
例如:
//a
所述匹配a
元件在任何以下的:<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>
。注意
//tag
等于。一个常见的错误是将其与混淆,尽管后面的表达式实际上可能导致非常不同的结果,如此处所示:/descendant-or-self::*/tag
/descendant-or-self::tag
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
匹配b
XML片段<a><b/></a>
或中的元素<c><b/></c>
。但是,该表达式不会在片段中产生匹配项,<b><a/></b>
因为它b
必须是某些其他元素的子代。通配符可以在任何位置使用:表达式/*/b/*
将匹配b
本身不是根元素的元素的任何子元素。- 您可以使用
|
(UNION
)运算符来匹配多个定位符中的任何一个。例如,表达式//b|//c
匹配 XML目标中的全部b
和c
元素。 也可以根据一个或多个属性的值来匹配一个元素。这是使用语法完成的。例如,表达式匹配fragment 中的第二个元素。要与任何具有的元素匹配,请使用XPath表达式。
tag[@attribute="value"]
//b[@]
b
<a><b /><c/><b /></a>
attribute="value"
//*[attribute="value"]
要过滤多个属性值,只需连续使用多个属性比较子句。例如,表达式
//b[@c="x"][@d="y"]
匹配<b c="x" d="y"/>
给定XML片段中任意位置出现的元素。要查找具有相同属性与多个值中的任何一个匹配的元素,可以使用由
|
运算符连接的多个定位符。例如,要匹配属性值为23或17的所有b
元素c
,请使用expression//b[@c="23"]|//b[@c="17"]
。您也可以or
为此使用逻辑运算符://b[@c="23" or @c="17"]
。注意
or
和之间的区别|
是or
联接条件,而|
联接结果集。
XPath限制。这些功能支持的XPath语法当前受到以下限制:
'/a/b[@c=@d]'
不支持节点集到节点集的比较(例如)。- 支持所有标准XPath比较运算符。错误22228)
相对定位器表达式在根节点的上下文中解析。例如,考虑以下查询和结果:
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
。谓词中也支持相对定位符。在以下示例中,
d[../@c="1"]
解析为/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)- 不允许使用以标量值(包括变量引用,文字,数字和标量函数调用)为标量的表达式作为前缀的定位符,它们的使用会导致错误。
::
结合以下类型的节点类型,不支持该运算符:axis::comment()
axis::text()
axis::processing-instructions()
axis::node()
但是,支持名称测试(例如和),如以下示例所示:
axis::name
axis::*
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)。
以下XPath函数不受支持,或存在指示的已知问题:
id()
lang()
local-name()
name()
namespace-uri()
normalize-space()
starts-with()
string()
substring-after()
substring-before()
translate()
不支持以下轴:
following-sibling
following
preceding-sibling
preceding
XPath表达式作为参数传递ExtractValue()
和UpdateXML()
可以含有冒号(:
在元素选择),使它们与标记采用XML命名空间表示法的使用。例如:
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()
and local-name()
函数要简单得多。
错误处理。对于ExtractValue()
和UpdateXML()
,使用的XPath定位器必须有效,并且要搜索的XML必须包含正确嵌套和关闭的元素。如果定位器无效,则会生成错误:
mysql>SELECT ExtractValue('<a>c</a><b/>', '/&a'); ERROR 1105 (HY000): XPATH syntax error: '&a'
如果xml_frag
不包含正确嵌套和关闭的元素,NULL
则返回并生成警告,如本示例所示:
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)
重要作为第三个参数替换XML
UpdateXML()
是不检查,以确定它是否是只由被正确地嵌套和关闭元件。
XPath注入。将恶意代码引入系统以获得未经授权的特权和数据访问权时,就会发生代码注入。它基于开发人员对用户输入数据的类型和内容的假设。XPath在这方面也不例外。
发生这种情况的一种常见情况是应用程序通过使用如下所示的XPath表达式将登录名和密码的组合与XML文件中找到的那些进行匹配来处理授权的情况:
//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id
这是XPath的等效SQL语句,如下所示:
SELECT idFROM usersWHERE login='neapolitan' ANDpassword ='1c3cr34m';
使用XPath的PHP应用程序可能会这样处理登录过程:
<?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
得到评估,如下所示:
//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id
由于方括号内的表达式始终以表示true
,因此它实际上与该表达式相同,它与XML文档id
中每个user
元素的属性都匹配:
//user/attribute::id
可以避免这种特殊攻击的一种方法是,简单地引用在的定义中要插值的变量名$xpath
,强制从Web表单传递的值转换为字符串:
$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";
经常建议采用与防止SQL注入攻击相同的策略。通常,防止XPath注入攻击应遵循的做法与防止SQL注入相同:
- 从不接受应用程序中用户的未经测试的数据。
- 检查所有用户提交的数据的类型;拒绝或转换错误类型的数据
- 测试数值数据是否超出范围;截断,舍入或拒绝超出范围的值。测试字符串中的非法字符,然后将其删除或拒绝包含它们的输入。
- 不要输出明显的错误消息,这些错误消息可能会为未经授权的用户提供可能用于破坏系统的线索;将它们记录到文件或数据库表中。
正如SQL注入攻击可用于获取有关数据库模式的信息一样,XPath注入也可用于遍历XML文件以揭示其结构,如Amit Klein的论文 Blind XPath Injection(PDF文件,46KB)所述。
检查将输出发送回客户端的输出也很重要。考虑一下当我们使用MySQL ExtractValue()
函数时会发生什么:
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()
以单个空格分隔的字符串返回多个匹配项,因此这种注入攻击将包含在其中的每个有效ID users.xml
作为单行输出提供给用户。作为额外的保护措施,还应该在将输出返回给用户之前测试输出。这是一个简单的示例:
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)
通常,安全地将数据返回给用户的准则与接受用户输入的准则相同。这些可以总结为:
- 始终测试传出数据的类型和允许值。
- 切勿允许未经授权的用户参见错误消息,这些消息可能会提供有关可用于利用它的应用程序的信息。