4.1. 词汇结构

SQLImporting 由一系列* command 组成。命令由一系列令牌*组成,该令牌以分号(“;”)结尾。Importing 流的末尾还会终止命令。哪些令牌有效,取决于特定命令的语法。

令牌可以是关键字标识符带引号的标识符Literals(或常数)或特殊字符符号。令牌通常由空格(空格,制表符,换行符)分隔,但如果不存在歧义则不需要(通常只有特殊字符与某些其他令牌类型相邻时才是这种情况)。

例如,以下是(在语法上)有效的 SQLImporting:

SELECT * FROM MY_TABLE;
UPDATE MY_TABLE SET A = 5;
INSERT INTO MY_TABLE VALUES (3, 'hi there');

这是三个命令的序列,每行一个(尽管这不是必需的;一行上可以有多个命令,并且可以在行之间有效地分割命令)。

此外,SQLImporting 中可能会出现“Comments”。它们不是令牌,实际上等效于空格。

关于哪些令牌标识命令以及哪些是操作数或参数,SQL 语法不一致。前几个标记通常是命令名称,因此在上面的示例中,我们通常会说“ SELECT”,“ UPDATE”和“ INSERT”命令。但是例如UPDATE命令始终要求SET令牌出现在某个位置,并且INSERT的这种特殊变化也需要VALUES才能完整。 Part VI中描述了每个命令的精确语法规则。

4 .1.1. 标识符和关键词

上例中的SELECTUPDATEVALUES之类的标记是关键字的示例,即在 SQL 语言中具有固定含义的单词。标记MY_TABLEA是* identifiers *的示例。它们根据使用的命令来标识表,列或其他数据库对象的名称。因此,有时将它们简称为“名称”。关键字和标识符具有相同的词法结构,这意味着在不了解语言的情况下无法知道令牌是标识符还是关键字。关键字的完整列表可以在Appendix C中找到。

SQL 标识符和关键字必须以字母(a-z,但也可以带有变音符号和非拉丁字母)或下划线(_)开头。标识符或关键字中的后续字符可以是字母,下划线,数字(0-9)或美元符号($)。请注意,根据 SQL 标准字母,不允许在标识符中使用美元符号,因此使用美元符号可能会使应用程序的可移植性降低。 SQL 标准不会定义包含数字或以下划线开头或结尾的关键字,因此,此格式的标识符可以避免与将来的标准 extensions 发生冲突。

系统使用的标识符不超过NAMEDATALEN -1 个字节;较长的名称可以写在命令中,但是会被截断。默认情况下,NAMEDATALEN为 64,因此最大标识符长度为 63 个字节。如果此限制有问题,可以通过更改src/include/pg_config_manual.h中的NAMEDATALEN常量来提高此限制。

关键字和未引用的标识符不区分大小写。因此:

UPDATE MY_TABLE SET A = 5;

可以等效地写成:

uPDaTE my_TabLE SeT a = 5;

经常使用的约定是用大写字母写关键词,用小写字母写名字,例如:

UPDATE my_table SET a = 5;

还有第二种标识符:分隔标识符带引号的标识符。它是通过将任意字符序列括在双引号(")中形成的。定界的标识符始终是标识符,而不是关键字。因此,"select"可用于引用名为“ select”的列或表,而未引用的select将作为关键字,因此在需要使用表或列名的地方使用时会引发解析错误。该示例可以用带引号的标识符编写,如下所示:

UPDATE "my_table" SET "a" = 5;

带引号的标识符可以包含任何字符,但代码零的字符除外。 (要包含双引号,请写两个双引号.)这允许构造原本不可能的表或列名称,例如包含空格或&的名称。长度限制仍然适用。

带引号的标识符的变体允许包含由其代码点标识的转义 Unicode 字符。此变体以双引号之前的U&(大写或小写字母 U,后跟与号)开头,中间没有空格,例如U&"foo"。 (请注意,这会导致与运算符&产生歧义.请在运算符周围使用空格以避免出现此问题.)在引号内,可以通过写反斜杠后跟四位数的十六进制代码点号或反斜杠来指定 Unicode 字符。或者,反斜杠后跟加号,后跟六位十六进制代码点编号。例如,标识符"data"可以写为

U&"d\0061t\+000061"

以下不那么简单的示例用西里尔字母写俄语单词“ slon”(大象):

U&"\0441\043B\043E\043D"

如果需要与反斜杠不同的转义字符,则可以在字符串后使用UESCAPE 子句来指定它,例如:

U&"d!0061t!+000061" UESCAPE '!'

转义字符可以是十六进制数字,加号,单引号,双引号或空格字符以外的任何单个字符。请注意,转义符用单引号而不是双引号书写。

要在标识符中实际包含转义字符,请将其写入两次。

Unicode 转义语法仅在服务器编码为UTF8时有效。使用其他服务器编码时,只能指定 ASCII 范围(最大\007F)中的代码点。 4 位和 6 位格式均可用于指定 UTF-16 代理对,以组成代码点大于 U FFFF 的字符,尽管从 6 位格式的可用性上讲,这是不必要的。 (代理对不直接存储,而是组合成一个代码点,然后以 UTF-8 编码.)

引用标识符也使其区分大小写,而未引用的名称总是折叠为小写。例如,PostgreSQL 认为标识符FOOfoo"foo"相同,但是"Foo""FOO"彼此不同。 (在 PostgreSQL 中将未加引号的名称折叠成小写与 SQL 标准不兼容,即说未加引号的名称应折叠成大写.因此,根据标准,foo应该等效于"FOO"而不是"foo".如果要编写,建议您始终在便携式应用程序中使用特定名称,也不要使用任何特定名称.)

4.1.2. Constants

PostgreSQL 中有 3 种“隐式类型的常量”:字符串,位字符串和数字。常量也可以用显式类型指定,这可以使系统实现更准确的表示和更有效的处理。以下各节将讨论这些替代方案。

4 .1.2.1. 字符串常量

SQL 中的字符串常量是由单引号(')限制的任意字符序列,例如'This is a string'。要在字符串常量中包含单引号字符,请写两个相邻的单引号,例如'Dianne''s horse'。注意,这与双引号字符(")不同。

将仅用空格和至少一个换行符隔开的两个字符串常量连接起来并有效地对待,就好像该字符串已被写为一个常量一样。例如:

SELECT 'foo'
'bar';

等效于:

SELECT 'foobar';

but:

SELECT 'foo'      'bar';

是无效的语法。 (这种有点奇怪的行为由 SQL 指定; PostgreSQL 遵循该标准.)

4 .1.2.2. 具有 C 样式转义符的字符串常量

PostgreSQL 还接受“转义”字符串常量,这是 SQL 标准的扩展。通过在开头的单引号(例如E'foo')之前写字母E(大写或小写)来指定转义字符串常量。 (当跨行 continue 使用转义字符串常量时,请仅在第一个引号前写E.)在转义字符串中,反斜杠字符(\)开始一个类似 C 的反斜杠转义序列,其中反斜杠和其后的组合字符表示一个特殊的字节值,如Table 4.1所示。

表 4.1. 反斜杠转义序列

反斜杠转义序列Interpretation
\bbackspace
\fform feed
\nnewline
\rcarriage return
\ttab
\o , \oo , \ooo ( o = 0 - 7)八进制字节值
\xh\xhh(* h * = 0-9,A-F)十六进制字节值
\uxxxx\Uxxxxxxxx(* x * = 0-9,A-F)16 或 32 位十六进制 Unicode 字符值

反斜杠后的所有其他字符均按字面意义使用。因此,要包含反斜杠字符,请写两个反斜杠(\\)。另外,除了''的常规方式外,还可以通过写\'将单引号包含在转义字符串中。

您要负责创建的字节序列(尤其是在使用八进制或十六进制转义符时)在服务器字符集编码中组成有效字符。如果服务器编码为 UTF-8,则应改用 Unicode 转义或Section 4.1.2.3中说明的替代 Unicode 转义语法。 (另一种方法是手工进行 UTF-8 编码并写出字节,这将非常麻烦.)

仅当服务器编码为UTF8时,Unicode 转义语法才能完全起作用。使用其他服务器编码时,只能指定 ASCII 范围(最大\u007F)中的代码点。 4 位和 8 位格式均可用于指定 UTF-16 代理对,以组成代码点大于 U FFFF 的字符,尽管从技术上讲 8 位格式的可用性使此操作不必要。 (当服务器编码为UTF8时使用代理对时,它们首先被组合成一个代码点,然后以 UTF-8 编码.)

Caution

如果配置参数standard_conforming_stringsoff,则 PostgreSQL 可以在常规和转义字符串常量中识别反斜杠转义。但是,从 PostgreSQL 9.1 开始,默认值为on,这意味着仅在转义字符串常量中识别反斜杠转义。此行为更符合标准,但是可能会破坏依赖历史行为的应用程序,在该行为中始终识别出反斜杠转义符。解决方法是,可以将此参数设置为off,但是最好不要使用反斜杠转义符来进行迁移。如果您需要使用反斜杠转义符来表示特殊字符,请使用E编写字符串常量。

除了standard_conforming_strings之外,配置参数escape_string_warningbackslash_quote还控制字符串常量中反斜杠的处理。

代码为零的字符不能为字符串常量。

4 .1.2.3. 带 Unicode 转义符的字符串常量

PostgreSQL 还支持字符串的另一种转义语法,允许按代码点指定任意 Unicode 字符。 Unicode 转义字符串常量紧接在开头引号之前以U&(大写或小写字母 U,后跟与号)开头,中间没有空格,例如U&'foo'。 (请注意,这会导致与运算符&产生歧义.请在运算符周围使用空格以避免出现此问题.)在引号内,可以通过写反斜杠后跟四位数的十六进制代码点号或反斜杠来指定 Unicode 字符。或者,反斜杠后跟加号,后跟六位十六进制代码点编号。例如,字符串'data'可以写为

U&'d\0061t\+000061'

以下不那么简单的示例用西里尔字母写俄语单词“ slon”(大象):

U&'\0441\043B\043E\043D'

如果需要与反斜杠不同的转义字符,则可以在字符串后使用UESCAPE 子句来指定它,例如:

U&'d!0061t!+000061' UESCAPE '!'

转义字符可以是十六进制数字,加号,单引号,双引号或空格字符以外的任何单个字符。

Unicode 转义语法仅在服务器编码为UTF8时有效。使用其他服务器编码时,只能指定 ASCII 范围(最大\007F)中的代码点。 4 位和 6 位格式均可用于指定 UTF-16 代理对,以组成代码点大于 U FFFF 的字符,尽管从 6 位格式的可用性上讲,这是不必要的。 (当服务器编码为UTF8时使用代理对时,它们首先被组合成一个代码点,然后以 UTF-8 编码.)

另外,仅在打开配置参数standard_conforming_strings时,字符串常量的 Unicode 转义语法才起作用。这是因为否则该语法可能会使解析 SQL 语句的 Client 端感到困惑,以至于可能导致 SQL 注入和类似的安全问题。如果参数设置为 off,则此语法将被拒绝并显示一条错误消息。

要从字面上包括转义字符,请将其写入两次。

4 .1.2.4. 以美元报价的字符串常量

虽然指定字符串常量的标准语法通常很方便,但是当所需的字符串包含许多单引号或反斜杠时,可能很难理解,因为每个引号或反斜杠必须加倍。为了在这种情况下允许更具可读性的查询,PostgreSQL 提供了另一种称为“美元引号”的方式来编写字符串常量。以美元报价的字符串常量包含一个美元符号($),一个零个或多个字符的可选“标记”,另一个美元符号,构成字符串内容的任意字符序列,一个美元符号以及与之相同的标记开始用这美元报价和一个美元符号。例如,以下两种使用美元引号指定字符串“ Dianne's horse”的方法:

$$Dianne's horse$$
$SomeTag$Dianne's horse$SomeTag$

注意,在用美元报价的字符串中,可以使用单引号而不需要对其进行转义。确实,用美元引号引起的字符串中的任何字符都不会转义:字符串内容始终按字面意义编写。反斜杠不是特别的,也不是美元符号,除非它们是与开始标记匹配的序列的一部分。

通过在每个嵌套级别选择不同的标签,可以嵌套用美元报价的字符串常量。这在编写函数定义中最常用。例如:

$function$
BEGIN
    RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
END;
$function$

在这里,序列$q$[\t\r\n\v\\]$q$代表用美元报价的 Literals 字符串[\t\r\n\v\\],当函数主体由 PostgreSQL 执行时将被识别。但是由于该序列与外部美元引号定界符$function$不匹配,因此就外部字符串而言,它只是常量中的更多字符。

用美元引号的字符串的标签(如果有)遵循与未引用标识符相同的规则,不同之处在于它不能包含美元符号。标签区分大小写,因此$tag$String content$tag$是正确的,而$TAG$String content$tag$不是。

关键字或标识符后面用美元报价的字符串必须用空格分隔;否则,美元报价定界符将被视为先前标识符的一部分。

美元引号不是 SQL 标准的一部分,但是,与标准兼容的单引号语法相比,美元引号通常是一种更方便的写复杂字符串 Literals 的方法。当在过程常量定义中经常需要在其他常量中表示字符串常量时,它特别有用。使用单引号语法时,上例中的每个反斜杠必须写为四个反斜杠,在解析原始字符串常量时将其减少为两个反斜杠,然后在函数期间重新解析内部字符串常量时将其减少为一个反斜杠。执行。

4 .1.2.5. 位串常量

位字符串常量看起来像常规字符串常量,紧接在开头引号(没有中间空格)之前为B(大写或小写),例如B'1001'。位字符串常量中仅允许使用01字符。

或者,可以使用前导的X(大写或小写),例如X'1FF',以十六进制表示法指定位字符串常量。此表示法等效于每个十六进制数具有四个二进制数字的位字符串常量。

两种形式的位字符串常量都可以以与常规字符串常量相同的方式跨行 continue。美元引用不能在位字符串常量中使用。

4 .1.2.6. 数值常数

可以以下列一般形式接受数字常数:

digits
digits.[digits][e[+-]digits]
[digits].digits[e[+-]digits]
digitse[+-]digits

其中* digits *是一个或多个十进制数字(0 到 9)。如果使用小数点,则必须在小数点之前或之后至少有一位数字。如果存在指数标记(e),则必须至少有一位数字。常量中不能嵌入任何空格或其他字符。请注意,任何前导的正号或负号实际上并未视为常量的一部分;它是应用于常量的运算符。

这些是有效数字常量的一些示例:

42
3.5
4.
.001
5e2
1.925e-3

一个既不包含小数点也不包含指数的数字常量,如果其值适合integer类型(32 位),则最初假定为integer类型。否则,如果其值适合bigint类型(64 位),则假定它为bigint类型;否则,将其视为numeric。最初总是假定包含小数点和/或指数的常量为numeric类型。

最初分配的数值常量的数据类型只是类型解析算法的起点。在大多数情况下,常量将根据上下文自动强制为最合适的类型。必要时,可以通过强制转换数值将其解释为特定的数据类型。 例如,您可以通过编写以下代码来强制将数值视为real(float4)类型:

REAL '1.23'  -- string style
1.23::REAL   -- PostgreSQL (historical) style

这些实际上只是接下来讨论的一般转换符号的特殊情况。

4 .1.2.7. 其他类型的常数

可以使用以下任一符号 Importing任意类型的常量:

type 'string'
'string'::type
CAST ( 'string' AS type )

字符串常量的文本将传递给名为* type *的类型的 Importing 转换例程。结果是所指示类型的常数。如果在常量类型上没有任何歧义(例如,当直接将其分配给表列时),则可以省略显式类型强制转换,在这种情况下,它将被自动强制。

可以使用常规 SQL 表示法或美元引号编写字符串常量。

也可以使用类似函数的语法来指定类型强制:

typename ( 'string' )

但并非所有类型名称都可以这种方式使用;有关详情,请参见Section 4.2.9

Section 4.2.9CAST()和函数调用语法也可以用于指定任意表达式的运行时类型转换。为避免语法歧义,type 'string'语法只能用于指定简单 Literals 常量的类型。 type 'string'语法的另一个限制是它不适用于数组类型;使用::CAST()指定数组常量的类型。

CAST()语法符合 SQL。 type 'string'语法是标准的概括:SQL 仅针对少数数据类型指定此语法,而 PostgreSQL 允许所有类型使用该语法。 ::的语法是 PostgreSQL 的历史用法,函数调用的语法也是如此。

4.1.3. Operators

操作员名称是以下列表中最多NAMEDATALEN -1(默认为 63)个字符的序列:

  • - * / < > = ~ ! @ # % ^ & | ` ?

但是,对操作员名称有一些限制:

  • --/*不能出现在操作员名称的任何位置,因为它们将被视为 Comments 的开头。

  • 多字符运算符名称不能以+-结尾,除非该名称还包含以下至少一个字符:

~ ! @ # % ^ & | ` ?

例如,@-是允许的运算符名称,而*-不是。此限制使 PostgreSQL 可以解析 SQL 兼容查询,而无需在标记之间使用空格。

使用非 SQL 标准的运算符名称时,通常需要用空格分隔相邻的运算符,以避免产生歧义。例如,如果您定义了一个名为@的左一元运算符,则不能编写X*@Y;您必须编写X* @Y以确保 PostgreSQL 将其读取为两个运算符名称,而不是一个。

4 .1.4. 特殊的角色

某些不是字母数字的字符具有特殊的含义,不同于成为运算符。有关用法的详细信息可以在描述相应语法元素的位置找到。本部分仅用于建议存在和总结这些字符的目的。

  • 美元符号($)后跟数字,用于表示函数定义或准备好的语句主体中的位置参数。在其他情况下,美元符号可以是标识符的一部分,也可以是美元引用的字符串常量。

  • 括号(())具有将表达式分组和强制执行优先级的通常含义。在某些情况下,括号是特定 SQL 命令的固定语法的一部分。

  • 方括号([])用于选择数组的元素。有关阵列的更多信息,请参见Section 8.15

  • 逗号(,)在某些语法构造中用于分隔列表的元素。

  • 分号(;)终止 SQL 命令。除了字符串常量或带引号的标识符外,它不能出现在命令中的任何位置。

  • 冒号(:)用于从数组中选择“切片”。 (请参见Section 8.15。)在某些 SQL 方言(例如 Embedded SQL)中,冒号用于为变量名添加前缀。

  • 在某些情况下,星号(*)用于表示表行或复合值的所有字段。当用作聚合函数的参数时,它还具有特殊含义,即该聚合不需要任何显式参数。

  • 句点(.)用于数字常量,并用于分隔模式,表和列名称。

4.1.5. Comments

Comments 是由双破折号开始并延伸到行尾的一系列字符,例如:

-- This is a standard SQL comment

或者,可以使用 C 样式的块 Comments:

/* multiline comment
 * with nesting: /* nested block comment */
 */

其中 Comment 以/*开头,并扩展到匹配的*/。这些块 Comments 按照 SQL 标准中的规定嵌套,但与 C 不同,因此可以 Comments 掉可能包含现有块 Comments 的较大代码块。

在进行进一步的语法分析之前,已从 Importing 流中删除 Comments,并有效地将其替换为空格。

4 .1.6. 运算符优先级

Table 4.2显示了 PostgreSQL 中运算符的优先级和关联性。大多数运算符具有相同的优先级,并且是左关联的。运算符的优先级和关联性硬连接到解析器中。

当使用二进制和一元运算符的组合时,有时需要添加括号。例如:

SELECT 5 ! - 6;

将被解析为:

SELECT 5 ! (- 6);

因为解析器不知道(直到为时已晚)将!定义为后缀运算符,而不是后缀运算符。为了在这种情况下获得所需的行为,您必须编写:

SELECT (5 !) - 6;

这是人们为扩展性付出的代价。

表 4.2. 运算符优先级(从高到低)

Operator/ElementAssociativityDescription
.left表/列名称分隔符
::leftPostgreSQL-style typecast
[ ]left数组元素选择
+ -right一元加号,一元减号
^leftexponentiation
* / %left乘法,除法,模
+ -leftaddition, subtraction
(任何其他运算符)left所有其他本机和用户定义的运算符
BETWEEN IN LIKE ILIKE SIMILAR 范围包含,设置成员资格,字符串匹配
< > = <= >= <> comparison operators
IS ISNULL NOTNULL IS TRUEIS FALSEIS NULLIS DISTINCT FROM
NOTrightlogical negation
ANDleftlogical conjunction
ORleftlogical disjunction

请注意,运算符优先级规则也适用于与上述内置运算符同名的用户定义的运算符。例如,如果您为某些自定义数据类型定义一个“”运算符,则无论您做什么,它都将与内置“”运算符具有相同的优先级。

OPERATOR语法中使用模式限定的运算符名称时,例如:

SELECT 3 OPERATOR(pg_catalog.+) 4;

OPERATOR构造具有“ Table 4.2”所示的“其他任何运算符”的默认优先级。不管OPERATOR()中出现哪个特定运算符,都是如此。

Note

9.5 之前的 PostgreSQL 版本使用略有不同的运算符优先级规则。特别是<= >=<>过去被视为通用运算符; IS测试曾经具有更高的优先级;和NOT BETWEEN及其相关构造的行为不一致,在某些情况下被认为具有NOT而不是BETWEEN的优先级。更改这些规则是为了更好地符合 SQL 标准,并减少对逻辑等效结构的不一致处理所造成的混淆。在大多数情况下,这些更改不会导致任何行为更改,或者可能导致“没有此类操作符”失败,可以通过添加括号来解决。但是,在某些极端情况下,查询可能会更改行为而没有报告任何解析错误。如果您担心这些更改是否在默默地破坏了某些内容,则可以在打开配置参数operator_precedence_warning的情况下测试您的应用程序,以查看是否记录了任何警告。