MySQL 8.4 参考手册  /  ...  /  函数名解析和决议

11.2.5 函数名解析和决议

MySQL 支持内置(原生)函数、可加载函数和存储函数。本节介绍服务器如何识别内置函数的名称是作为函数调用还是标识符使用,以及在存在具有给定名称的不同类型的函数时,服务器如何确定使用哪个函数。

内置函数名解析

解析器使用默认规则来解析内置函数的名称。可以通过启用 IGNORE_SPACE SQL 模式来更改这些规则。

当解析器遇到作为内置函数名称的单词时,它必须确定该名称是表示函数调用还是对标识符(例如表名或列名)的非表达式引用。例如,在以下语句中,对 count 的第一个引用是函数调用,而第二个引用是表名。

SELECT COUNT(*) FROM mytable;
CREATE TABLE count (i INT);

解析器应该仅在解析预期为表达式的部分时,才将内置函数的名称识别为函数调用。也就是说,在非表达式上下文中,函数名允许用作标识符。

但是,某些内置函数在解析或实现方面有特殊注意事项,因此默认情况下,解析器使用以下规则来区分它们的名称是在非表达式上下文中用作函数调用还是标识符。

  • 要在表达式中将名称用作函数调用,则名称和后面的 ( 左括号之间不能有空格。

  • 相反,要将函数名用作标识符,则它后面不能紧跟括号。

函数调用必须在名称和括号之间没有空格的要求仅适用于具有特殊注意事项的内置函数。 COUNT 就是这样一个名称。 sql/lex.h 源文件列出了这些特殊函数的名称,对于这些函数,后面的空格决定了它们的解释:由 SYM_FN() 宏在 symbols[] 数组中定义的名称。

以下列出了 MySQL 8.4 中受 IGNORE_SPACE 设置影响并在 sql/lex.h 源文件中列为特殊的函数。您可能会发现最简单的做法是将无空格要求视为适用于所有函数调用。

  • ADDDATE

  • BIT_AND

  • BIT_OR

  • BIT_XOR

  • CAST

  • COUNT

  • CURDATE

  • CURTIME

  • DATE_ADD

  • DATE_SUB

  • EXTRACT

  • GROUP_CONCAT

  • MAX

  • MID

  • MIN

  • NOW

  • POSITION

  • SESSION_USER

  • STD

  • STDDEV

  • STDDEV_POP

  • STDDEV_SAMP

  • SUBDATE

  • SUBSTR

  • SUBSTRING

  • SUM

  • SYSDATE

  • SYSTEM_USER

  • TRIM

  • VARIANCE

  • VAR_POP

  • VAR_SAMP

对于未在 sql/lex.h 中列为特殊的函数,空格无关紧要。仅当在表达式上下文中使用时,它们才会被解释为函数调用,否则可以自由地用作标识符。 ASCII 就是这样一个名称。但是,对于这些不受影响的函数名,在表达式上下文中解释可能会有所不同:如果存在具有给定名称的内置函数,则 func_name () 将被解释为内置函数;否则,如果存在具有该名称的可加载函数或存储函数,则 func_name () 将被解释为可加载函数或存储函数。

IGNORE_SPACE SQL 模式可用于修改解析器处理对空格敏感的函数名称的方式。

  • 如果禁用 IGNORE_SPACE,则当名称和后面的括号之间没有空格时,解析器会将该名称解释为函数调用。即使在非表达式上下文中使用函数名时也是如此。

    mysql> CREATE TABLE count(i INT);
    ERROR 1064 (42000): You have an error in your SQL syntax ...
    near 'count(i INT)'

    要消除错误并使名称被视为标识符,请在名称后使用空格或将其写成带引号的标识符(或两者都使用)。

    CREATE TABLE count (i INT);
    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);
  • 如果启用 IGNORE_SPACE,则解析器会放宽函数名称和后面的括号之间不能有空格的要求。这为编写函数调用提供了更大的灵活性。例如,以下任一函数调用都是合法的。

    SELECT COUNT(*) FROM mytable;
    SELECT COUNT (*) FROM mytable;

    但是,启用 IGNORE_SPACE 也会产生副作用,即解析器会将受影响的函数名称视为保留字(请参阅 第 11.3 节“关键字和保留字”)。这意味着名称后面的空格不再表示将其用作标识符。该名称可以在函数调用中使用,无论后面是否跟有空格,但在非表达式上下文中会导致语法错误,除非将其用引号引起来。例如,如果启用了 IGNORE_SPACE,则以下两个语句都会因语法错误而失败,因为解析器会将 count 解释为保留字。

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);

    要在非表达式上下文中使用函数名,请将其写成带引号的标识符。

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);

要启用 IGNORE_SPACE SQL 模式,请使用以下语句。

SET sql_mode = 'IGNORE_SPACE';

某些其他组合模式(例如 ANSI)在其值中包含 IGNORE_SPACE,也会启用 IGNORE_SPACE

SET sql_mode = 'ANSI';

请查看 第 7.1.11 节“服务器 SQL 模式”,以了解哪些组合模式启用了 IGNORE_SPACE

要最大限度地减少 SQL 代码对 IGNORE_SPACE 设置的依赖性,请遵循以下准则。

  • 避免创建与内置函数同名的可加载函数或存储函数。

  • 避免在非表达式上下文中使用函数名。例如,以下语句使用 count(受 IGNORE_SPACE 影响的函数名之一),因此如果启用了 IGNORE_SPACE,则无论名称后面是否跟有空格,它们都会失败。

    CREATE TABLE count(i INT);
    CREATE TABLE count (i INT);

    如果必须在非表达式上下文中使用函数名,请将其写成带引号的标识符。

    CREATE TABLE `count`(i INT);
    CREATE TABLE `count` (i INT);

函数名决议

以下规则描述了服务器如何解析对函数名的引用以进行函数创建和调用。

  • 内置函数和可加载函数

    如果您尝试创建与内置函数同名的可加载函数,则会发生错误。

    在这种情况下,IF NOT EXISTS 不起作用。有关详细信息,请参阅 第 15.7.4.1 节“CREATE FUNCTION 语句(用于可加载函数)”

  • 内置函数和存储函数

    可以创建与内置函数同名的存储函数,但要调用存储函数,必须使用模式名称对其进行限定。例如,如果您在 test 模式中创建了一个名为 PI 的存储函数,则将其作为 test.PI() 调用,因为服务器会将没有限定符的 PI() 解析为对内置函数的引用。如果存储函数名与内置函数名冲突,服务器会生成警告。可以使用 SHOW WARNINGS 显示警告。

    在这种情况下,IF NOT EXISTS 不起作用;请参阅 第 15.1.17 节“CREATE PROCEDURE 和 CREATE FUNCTION 语句”

  • 可加载函数和存储函数

    可以创建与现有可加载函数同名的存储函数,反之亦然。如果建议的存储函数名与现有可加载函数名冲突,或者建议的可加载函数名与现有存储函数名相同,则服务器会生成警告。在这两种情况下,一旦两个函数都存在,则在调用存储函数时必须使用模式名称对其进行限定;在这种情况下,服务器假定未限定的名称指的是可加载函数。

    MySQL 8.4 支持在 CREATE FUNCTION 语句中使用 IF NOT EXISTS,但在这种情况下不起作用。

上述函数名称解析规则对升级到实现新内置函数的 MySQL 版本有影响。

  • 如果您已经创建了一个具有给定名称的可加载函数,并将 MySQL 升级到一个实现了具有相同名称的新内置函数的版本,则该可加载函数将变得不可访问。要解决此问题,请使用 DROP FUNCTION 删除可加载函数,并使用 CREATE FUNCTION 使用不同的非冲突名称重新创建可加载函数。然后修改任何受影响的代码以使用新名称。

  • 如果新版本的 MySQL 实现了一个与现有存储函数同名的内置函数或可加载函数,您有两个选择:重命名存储函数以使用非冲突名称,或将对该函数的任何调用(如果尚未这样做)更改为使用架构限定符(schema_name.func_name() 语法)。无论哪种情况,都要相应地修改任何受影响的代码。